mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
131
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a698bd71e | ||
|
|
baa0594501 | ||
|
|
e82b8c80f0 | ||
|
|
f8e54be9a6 | ||
|
|
d3137be984 | ||
|
|
8f432aa0a9 | ||
|
|
4aa4137abe | ||
|
|
f4bb4e1b6a | ||
|
|
7af4e166a4 | ||
|
|
1fa488d870 | ||
|
|
37a3a51b6b | ||
|
|
6a6e8f906f | ||
|
|
55bc498f9d | ||
|
|
4ee784c68f | ||
|
|
fc757c087a | ||
|
|
884dd939b7 | ||
|
|
6fd2f556db | ||
|
|
4e088a40d9 | ||
|
|
8ba66af878 | ||
|
|
441f51ed79 | ||
|
|
9ee891dc66 | ||
|
|
87ffcbf23e | ||
|
|
adfc54200f | ||
|
|
3c36b43449 | ||
|
|
9be1dcff6c | ||
|
|
37dff62fa9 | ||
|
|
4fd462b1c8 | ||
|
|
33ac1923ff | ||
|
|
0b87ae5eac | ||
|
|
678743cc10 | ||
|
|
c8345ce093 | ||
|
|
f3af43a9ce | ||
|
|
1df2ccb1c6 | ||
|
|
3add4472ef | ||
|
|
4613f63c66 | ||
|
|
612295fb9d | ||
|
|
64405ade52 | ||
|
|
ce5f1b25f5 | ||
|
|
ae4f50e7b3 | ||
|
|
c9120be347 | ||
|
|
b189e67b4c | ||
|
|
5d5c275c79 | ||
|
|
52f31a7a8e | ||
|
|
6697aecd48 | ||
|
|
348c075a14 | ||
|
|
bf2420702c | ||
|
|
16832ac67a | ||
|
|
47bd2ff03d | ||
|
|
850f1c1469 | ||
|
|
622a90c7d5 | ||
|
|
4c317f11ae | ||
|
|
01bce5cfa6 | ||
|
|
2158925d3c | ||
|
|
2aff6403a2 | ||
|
|
3c4d600187 | ||
|
|
f60ccb9b90 | ||
|
|
2a6108087c | ||
|
|
0da86a82ae | ||
|
|
643b0499c4 | ||
|
|
676bac390e | ||
|
|
79740accfa | ||
|
|
77c6f1cb9f | ||
|
|
538361d5fd | ||
|
|
b17593ba3c | ||
|
|
eb00690b30 | ||
|
|
a772e65d32 | ||
|
|
66f7f69ec7 | ||
|
|
ad52711fe1 | ||
|
|
d6fdb4ee34 | ||
|
|
74c7268d2f | ||
|
|
42fc419759 | ||
|
|
4fb3b9cddc | ||
|
|
c60077174e | ||
|
|
5e893dd2ad | ||
|
|
9eb54da2dc | ||
|
|
bb6ff866c7 | ||
|
|
2d719c5b37 | ||
|
|
9d54b264ad | ||
|
|
65b282a056 | ||
|
|
fc174c8988 | ||
|
|
06802896ef | ||
|
|
7f13ac9e5e | ||
|
|
4a1374d22d | ||
|
|
4ee8d647b7 | ||
|
|
59793f3c01 | ||
|
|
f7f383d8f3 | ||
|
|
562a3959b6 | ||
|
|
bb1275f4f9 | ||
|
|
7fde6b9331 | ||
|
|
3e97a4005a | ||
|
|
c271760230 | ||
|
|
f7c1845f0c | ||
|
|
9b04f20ed3 | ||
|
|
62b6b6293b | ||
|
|
6d8a1c9398 | ||
|
|
303039735f | ||
|
|
6bcf5c6881 | ||
|
|
0527f45f5a | ||
|
|
ba397bbb0c | ||
|
|
a617d58902 | ||
|
|
7fc4a229ae | ||
|
|
f53d31aa2e | ||
|
|
b889b6a6b0 | ||
|
|
f9b55a06b8 | ||
|
|
09d772a5e5 | ||
|
|
f7035eb228 | ||
|
|
1bf09ddd38 | ||
|
|
a8bf27da55 | ||
|
|
ee8bb5ef75 | ||
|
|
bcfd9cab1d | ||
|
|
2a5fd7f3a9 | ||
|
|
f8c37772d4 | ||
|
|
d988dbe69d | ||
|
|
5cb9b9b237 | ||
|
|
819d391495 | ||
|
|
1f0ed629a7 | ||
|
|
91044d0a75 | ||
|
|
57736185d0 | ||
|
|
8992980689 | ||
|
|
f341ff0167 | ||
|
|
abfa841ed7 | ||
|
|
e68adf53bc | ||
|
|
562d175de1 | ||
|
|
b5cef354ad | ||
|
|
09a887aeb7 | ||
|
|
53c903fa48 | ||
|
|
548454c90d | ||
|
|
94bab5f83b | ||
|
|
78965c1ac4 | ||
|
|
00b0fcaeb3 | ||
|
|
f7905c8e94 |
+2
-2
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean api</name>
|
||||
@@ -50,7 +50,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>6.15</version>
|
||||
<version>7.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -78,7 +78,7 @@ public class DatabaseFactory {
|
||||
Database server = createInternal(config);
|
||||
if (config.isRegister()) {
|
||||
if (config.isDefaultServer()) {
|
||||
if (defaultServerName != null) {
|
||||
if (defaultServerName != null && !defaultServerName.equals(config.getName())) {
|
||||
throw new IllegalStateException("Registering [" + config.getName() + "] as the default server but [" + defaultServerName + "] is already registered as the default");
|
||||
}
|
||||
defaultServerName = config.getName();
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Query for performing native SQL queries that return DTO Bean's.
|
||||
@@ -45,6 +46,26 @@ public interface DtoQuery<T> {
|
||||
@Nonnull
|
||||
List<T> findList();
|
||||
|
||||
/**
|
||||
* Execute the query iterating a row at a time.
|
||||
* <p>
|
||||
* Note that the QueryIterator holds resources related to the underlying
|
||||
* resultSet and potentially connection and MUST be closed. We should use
|
||||
* QueryIterator in a <em>try with resource block</em>.
|
||||
*/
|
||||
@Nonnull
|
||||
QueryIterator<T> findIterate();
|
||||
|
||||
/**
|
||||
* Execute the query returning a Stream.
|
||||
* <p>
|
||||
* Note that the Stream holds resources related to the underlying
|
||||
* resultSet and potentially connection and MUST be closed. We should use
|
||||
* the Stream in a <em>try with resource block</em>.
|
||||
*/
|
||||
@Nonnull
|
||||
Stream<T> findStream();
|
||||
|
||||
/**
|
||||
* Execute the query iterating a row at a time.
|
||||
* <p>
|
||||
|
||||
@@ -1671,7 +1671,7 @@ public interface ExpressionList<T> {
|
||||
ExpressionList<T> endAnd();
|
||||
|
||||
/**
|
||||
* End a AND junction - synonym for endJunction().
|
||||
* End a OR junction - synonym for endJunction().
|
||||
*/
|
||||
ExpressionList<T> endOr();
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ public class FetchConfig implements Serializable {
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to one of the static factory methods like {@link FetchConfig#ofQuery()}
|
||||
*
|
||||
* <p>
|
||||
* Construct using default JOIN mode.
|
||||
*/
|
||||
@Deprecated
|
||||
@@ -89,7 +89,7 @@ public class FetchConfig implements Serializable {
|
||||
* Return FetchConfig to lazily load the relationship.
|
||||
*/
|
||||
public static FetchConfig ofLazy() {
|
||||
return new FetchConfig(LAZY_MODE, 10);
|
||||
return new FetchConfig(LAZY_MODE, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,8 +110,8 @@ public class FetchConfig implements Serializable {
|
||||
* We want to migrate away from mutating FetchConfig to a fully immutable FetchConfig.
|
||||
*/
|
||||
private FetchConfig mutate(int mode, int batchSize) {
|
||||
if (batchSize < 1) {
|
||||
throw new IllegalArgumentException("batch size "+batchSize+" must be > 0");
|
||||
if (batchSize < 0) {
|
||||
throw new IllegalArgumentException("batch size " + batchSize + " must be > 0");
|
||||
}
|
||||
this.mode = mode;
|
||||
this.batchSize = batchSize;
|
||||
@@ -124,7 +124,7 @@ public class FetchConfig implements Serializable {
|
||||
*/
|
||||
@Deprecated
|
||||
public FetchConfig lazy() {
|
||||
return mutate(LAZY_MODE, 10);
|
||||
return mutate(LAZY_MODE, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,7 +137,7 @@ public class FetchConfig implements Serializable {
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to FetchConfig.ofQuery().
|
||||
*
|
||||
* <p>
|
||||
* Eagerly fetch the beans in this path as a separate query (rather than as
|
||||
* part of the main query).
|
||||
* <p>
|
||||
@@ -150,17 +150,15 @@ public class FetchConfig implements Serializable {
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to FetchConfig.ofQuery(batchSize).
|
||||
*
|
||||
* <p>
|
||||
* Eagerly fetch the beans in this path as a separate query (rather than as
|
||||
* part of the main query).
|
||||
* <p>
|
||||
* The queryBatchSize is the number of parent id's that this separate query
|
||||
* will load per batch.
|
||||
* </p>
|
||||
* <p>
|
||||
* This will load all beans on this path eagerly unless a {@link #lazy(int)}
|
||||
* is also used.
|
||||
* </p>
|
||||
*
|
||||
* @param batchSize the batch size used to load beans on this path
|
||||
*/
|
||||
@@ -171,13 +169,12 @@ public class FetchConfig implements Serializable {
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to FetchConfig.ofQuery(batchSize).
|
||||
*
|
||||
* <p>
|
||||
* Eagerly fetch the first batch of beans on this path.
|
||||
* This is similar to {@link #query(int)} but only fetches the first batch.
|
||||
* <p>
|
||||
* If there are more parent beans than the batch size then they will not be
|
||||
* loaded eagerly but instead use lazy loading.
|
||||
* </p>
|
||||
*
|
||||
* @param batchSize the number of parent beans this path is populated for
|
||||
*/
|
||||
@@ -188,7 +185,7 @@ public class FetchConfig implements Serializable {
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to FetchConfig.ofCache().
|
||||
*
|
||||
* <p>
|
||||
* Eagerly fetch the beans fetching the beans from the L2 bean cache
|
||||
* and using the DB for beans not in the cache.
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.util.Objects;
|
||||
* on the Query object.
|
||||
* </p>
|
||||
*/
|
||||
public final class OrderBy<T> implements Serializable {
|
||||
public class OrderBy<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 9157089257745730539L;
|
||||
|
||||
@@ -69,7 +69,6 @@ public final class OrderBy<T> implements Serializable {
|
||||
* Add a property with ascending order to this OrderBy.
|
||||
*/
|
||||
public Query<T> asc(String propertyName) {
|
||||
|
||||
list.add(new Property(propertyName, true));
|
||||
return query;
|
||||
}
|
||||
@@ -98,7 +97,6 @@ public final class OrderBy<T> implements Serializable {
|
||||
return query;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the property is known to be contained in the order by clause.
|
||||
*/
|
||||
@@ -207,7 +205,6 @@ public final class OrderBy<T> implements Serializable {
|
||||
if (!(obj instanceof OrderBy<?>)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OrderBy<?> e = (OrderBy<?>) obj;
|
||||
return e.list.equals(list);
|
||||
}
|
||||
@@ -249,7 +246,7 @@ public final class OrderBy<T> implements Serializable {
|
||||
/**
|
||||
* A property and its ascending descending order.
|
||||
*/
|
||||
public static final class Property implements Serializable {
|
||||
public static class Property implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1546009780322478077L;
|
||||
|
||||
@@ -415,13 +412,10 @@ public final class OrderBy<T> implements Serializable {
|
||||
}
|
||||
|
||||
private void parse(String orderByClause) {
|
||||
|
||||
if (orderByClause == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] chunks = orderByClause.split(",");
|
||||
for (String chunk : chunks) {
|
||||
for (String chunk : orderByClause.split(",")) {
|
||||
Property p = parseProperty(chunk);
|
||||
if (p != null) {
|
||||
list.add(p);
|
||||
@@ -467,8 +461,7 @@ public final class OrderBy<T> implements Serializable {
|
||||
if (s.startsWith("desc")) {
|
||||
return false;
|
||||
}
|
||||
String m = "Expecting [" + s + "] to be asc or desc?";
|
||||
throw new RuntimeException(m);
|
||||
throw new RuntimeException("Expecting [" + s + "] to be asc or desc?");
|
||||
}
|
||||
|
||||
private boolean isEmptyString(String s) {
|
||||
|
||||
@@ -56,7 +56,7 @@ import java.util.Iterator;
|
||||
*
|
||||
* @param <T> the type of entity bean in the iteration
|
||||
*/
|
||||
public interface QueryIterator<T> extends Iterator<T>, java.io.Closeable {
|
||||
public interface QueryIterator<T> extends Iterator<T>, AutoCloseable {
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if the iteration has more elements.
|
||||
@@ -70,12 +70,6 @@ public interface QueryIterator<T> extends Iterator<T>, java.io.Closeable {
|
||||
@Override
|
||||
T next();
|
||||
|
||||
/**
|
||||
* Remove is not allowed.
|
||||
*/
|
||||
@Override
|
||||
void remove();
|
||||
|
||||
/**
|
||||
* Close the underlying resources held by this iterator.
|
||||
*/
|
||||
|
||||
@@ -365,5 +365,10 @@ public interface SqlQuery extends Serializable {
|
||||
* Return the list of values.
|
||||
*/
|
||||
List<T> findList();
|
||||
|
||||
/**
|
||||
* Find streaming the result effectively consuming a row at a time.
|
||||
*/
|
||||
void findEach(Consumer<T> consumer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,14 @@ public interface Transaction extends AutoCloseable {
|
||||
*/
|
||||
void register(TransactionCallback callback);
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL - turn on automatic persistence of dirty beans and batchMode true.
|
||||
* <p>
|
||||
* With this turned on beans that are dirty in the persistence context
|
||||
* are automatically persisted on flush() and commit().
|
||||
*/
|
||||
void setAutoPersistUpdates(boolean autoPersistUpdates);
|
||||
|
||||
/**
|
||||
* Set a label on the transaction.
|
||||
* <p>
|
||||
@@ -339,15 +347,12 @@ public interface Transaction extends AutoCloseable {
|
||||
* The batch is automatically flushed when it hits the batch size and also when we
|
||||
* execute queries or when we mix UpdateSql and CallableSql with save and delete of
|
||||
* beans.
|
||||
* </p>
|
||||
* <p>
|
||||
* We use {@link #flush()} to explicitly flush the batch and we can use
|
||||
* {@link #setFlushOnQuery(boolean)} and {@link #setFlushOnMixed(boolean)}
|
||||
* to control the automatic flushing behaviour.
|
||||
* </p>
|
||||
* <p>
|
||||
* Example: batch processing of CallableSql executing every 10 rows
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -392,14 +397,11 @@ public interface Transaction extends AutoCloseable {
|
||||
* <p>
|
||||
* This only takes effect when batch mode on the transaction has not already meant that
|
||||
* JDBC batch mode is being used.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is useful when the single save() or delete() cascades. For example, inserting a 'master' cascades
|
||||
* and inserts a collection of 'detail' beans. The detail beans can be inserted using JDBC batch.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is effectively already turned on for all platforms apart from older Sql Server.
|
||||
* </p>
|
||||
*
|
||||
* @param batchMode the batch mode to use per save(), insert(), update() or delete()
|
||||
* @see io.ebean.config.DatabaseConfig#setPersistBatchOnCascade(PersistBatch)
|
||||
@@ -422,15 +424,19 @@ public interface Transaction extends AutoCloseable {
|
||||
int getBatchSize();
|
||||
|
||||
/**
|
||||
* Specify if you want batched inserts to use getGeneratedKeys.
|
||||
* Specify if we want batched inserts to use getGeneratedKeys.
|
||||
* <p>
|
||||
* By default batched inserts will try to use getGeneratedKeys if it is
|
||||
* supported by the underlying jdbc driver and database.
|
||||
* </p>
|
||||
* <p>
|
||||
* You may want to turn getGeneratedKeys off when you are inserting a large
|
||||
* number of objects and you don't care about getting back the ids.
|
||||
* </p>
|
||||
* We want to turn off getGeneratedKeys when we are inserting a large
|
||||
* number of objects and we don't care about getting back the ids. In this
|
||||
* way we avoid the extra cost of getting back the generated id values
|
||||
* from the database.
|
||||
* <p>
|
||||
* Note that when we do turn off getGeneratedKeys then we have the limitation
|
||||
* that after a bean has been inserted we are unable to then mutate the bean
|
||||
* and update it in the same transaction as we have not obtained it's id value.
|
||||
*/
|
||||
void setGetGeneratedKeys(boolean getGeneratedKeys);
|
||||
|
||||
@@ -449,13 +455,11 @@ public interface Transaction extends AutoCloseable {
|
||||
* <p>
|
||||
* If you want to execute both WITHOUT having the batch automatically flush
|
||||
* you need to call this with batchFlushOnMixed = false.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that UpdateSql and CallableSql are ALWAYS executed first (before the
|
||||
* beans are executed). This is because the UpdateSql and CallableSql have
|
||||
* already been bound to their PreparedStatements. The beans on the other hand
|
||||
* have a 2 step process (delayed binding).
|
||||
* </p>
|
||||
*/
|
||||
void setFlushOnMixed(boolean batchFlushOnMixed);
|
||||
|
||||
@@ -473,7 +477,6 @@ public interface Transaction extends AutoCloseable {
|
||||
* <p>
|
||||
* Calling this method with batchFlushOnQuery = false means that you can
|
||||
* execute a query and the batch will not be automatically flushed.
|
||||
* </p>
|
||||
*/
|
||||
void setFlushOnQuery(boolean batchFlushOnQuery);
|
||||
|
||||
@@ -490,7 +493,6 @@ public interface Transaction extends AutoCloseable {
|
||||
* should be flushed prior to executing a query.
|
||||
* <p>
|
||||
* The default is for this to be true.
|
||||
* </p>
|
||||
*/
|
||||
boolean isFlushOnQuery();
|
||||
|
||||
@@ -507,7 +509,6 @@ public interface Transaction extends AutoCloseable {
|
||||
* flush the batch if you like.
|
||||
* <p>
|
||||
* Flushing occurs automatically when:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>the batch size is reached</li>
|
||||
* <li>A query is executed on the same transaction</li>
|
||||
@@ -533,11 +534,9 @@ public interface Transaction extends AutoCloseable {
|
||||
* commit() rollback() and end() methods on the Transaction should still be
|
||||
* used. Calling these methods on the Connection would be a big no no unless
|
||||
* you know what you are doing.
|
||||
* </p>
|
||||
* <p>
|
||||
* Examples of when a developer may wish to use the connection directly are:
|
||||
* Savepoints, advanced CLOB BLOB use and advanced stored procedure calls.
|
||||
* </p>
|
||||
*/
|
||||
Connection getConnection();
|
||||
|
||||
@@ -545,17 +544,14 @@ public interface Transaction extends AutoCloseable {
|
||||
* Add table modification information to the TransactionEvent.
|
||||
* <p>
|
||||
* Use this in conjunction with getConnection() and raw JDBC.
|
||||
* </p>
|
||||
* <p>
|
||||
* This effectively informs Ebean of the data that has been changed by the
|
||||
* transaction and this information is normally automatically handled by Ebean
|
||||
* when you save entity beans or use UpdateSql etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* If you use raw JDBC then you can use this method to inform Ebean for the
|
||||
* tables that have been modified. Ebean uses this information to keep its
|
||||
* caches in synch and maintain text indexes.
|
||||
* </p>
|
||||
*/
|
||||
void addModification(String tableName, boolean inserts, boolean updates, boolean deletes);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebean;
|
||||
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.annotation.TxIsolation;
|
||||
import io.ebean.annotation.TxOption;
|
||||
import io.ebean.annotation.TxType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -33,6 +34,8 @@ public final class TxScope {
|
||||
|
||||
private TxIsolation isolation;
|
||||
|
||||
private TxOption autoPersistUpdates;
|
||||
|
||||
private PersistBatch batch;
|
||||
|
||||
private PersistBatch batchOnCascade;
|
||||
@@ -123,6 +126,13 @@ public final class TxScope {
|
||||
+ "] serverName[" + serverName + "] rollbackFor[" + rollbackFor + "] noRollbackFor[" + noRollbackFor + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the AutoPersistUpdates mode as a nullable Boolean.
|
||||
*/
|
||||
public Boolean getAutoPersistUpdates() {
|
||||
return autoPersistUpdates == null ? null : autoPersistUpdates.asBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if PersistBatch has been set.
|
||||
*/
|
||||
@@ -176,6 +186,14 @@ public final class TxScope {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the autoPersistUpdates mode.
|
||||
*/
|
||||
public TxScope setAutoPersistUpdates(TxOption autoPersistUpdates) {
|
||||
this.autoPersistUpdates = autoPersistUpdates;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction profile id.
|
||||
*/
|
||||
|
||||
@@ -13,12 +13,10 @@ import java.util.Set;
|
||||
* from the Map Set or List. The purpose of gathering the additions and removals
|
||||
* is to support persisting ManyToMany objects. The additions and removals
|
||||
* become inserts and deletes from the intersection table.
|
||||
* </p>
|
||||
* <p>
|
||||
* Technically this is <em>NOT</em> an extension of
|
||||
* <em>java.util.Collection</em>. The reason being that java.util.Map is not a
|
||||
* Collection. I realise this makes this name confusing so I apologise for that.
|
||||
* </p>
|
||||
*/
|
||||
public interface BeanCollection<E> extends Serializable {
|
||||
|
||||
@@ -68,7 +66,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
* Return true if the collection is uninitialised or is empty without any held modifications.
|
||||
* <p>
|
||||
* Returning true means can safely skip cascade save for this bean collection.
|
||||
* </p>
|
||||
*/
|
||||
boolean isSkipSave();
|
||||
|
||||
@@ -93,7 +90,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
* <p>
|
||||
* That is, if the collection was not loaded due to filterMany predicates etc
|
||||
* then make sure the collection is set to empty.
|
||||
* </p>
|
||||
*/
|
||||
boolean checkEmptyLazyLoad();
|
||||
|
||||
@@ -136,10 +132,7 @@ public interface BeanCollection<E> extends Serializable {
|
||||
boolean isReadOnly();
|
||||
|
||||
/**
|
||||
* Add the bean to the collection.
|
||||
* <p>
|
||||
* This is disallowed for BeanMap.
|
||||
* </p>
|
||||
* Add the bean to the collection. This is disallowed for BeanMap.
|
||||
*/
|
||||
void internalAdd(Object bean);
|
||||
|
||||
@@ -168,7 +161,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
* Map.Entry.
|
||||
* <p>
|
||||
* For maps this returns the entrySet as we need the keys of the map.
|
||||
* </p>
|
||||
*/
|
||||
Collection<?> getActualEntries();
|
||||
|
||||
@@ -185,6 +177,11 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
boolean isReference();
|
||||
|
||||
/**
|
||||
* Return true if the collection is modify listening and has modifications.
|
||||
*/
|
||||
boolean hasModifications();
|
||||
|
||||
/**
|
||||
* Set modify listening on or off. This is used to keep track of objects that
|
||||
* have been added to or removed from the list set or map.
|
||||
@@ -192,7 +189,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
* This is required only for ManyToMany collections. The additions and
|
||||
* deletions are used to insert or delete entries from the intersection table.
|
||||
* Otherwise modifyListening is false.
|
||||
* </p>
|
||||
*/
|
||||
void setModifyListening(ModifyListenMode modifyListenMode);
|
||||
|
||||
@@ -206,7 +202,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
* <p>
|
||||
* This will potentially end up as an insert into a intersection table for a
|
||||
* ManyToMany.
|
||||
* </p>
|
||||
*/
|
||||
void modifyAddition(E bean);
|
||||
|
||||
@@ -215,7 +210,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
* <p>
|
||||
* This will potentially end up as an delete from an intersection table for a
|
||||
* ManyToMany.
|
||||
* </p>
|
||||
*/
|
||||
void modifyRemoval(Object bean);
|
||||
|
||||
|
||||
@@ -32,39 +32,47 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
private static final int STATE_REFERENCE = 1;
|
||||
private static final int STATE_LOADED = 2;
|
||||
|
||||
/**
|
||||
* Used when a bean is partially filled.
|
||||
*/
|
||||
private static final byte FLAG_LOADED_PROP = 1;
|
||||
private static final byte FLAG_CHANGED_PROP = 2;
|
||||
private static final byte FLAG_CHANGEDLOADED_PROP = 3;
|
||||
/**
|
||||
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
|
||||
* between an embedded bean being completely overwritten and one of its
|
||||
* embedded properties being made dirty.
|
||||
*/
|
||||
private static final byte FLAG_EMBEDDED_DIRTY = 4;
|
||||
/**
|
||||
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
|
||||
* between an embedded bean being completely overwritten and one of its
|
||||
* embedded properties being made dirty.
|
||||
*/
|
||||
private static final byte FLAG_ORIG_VALUE_SET = 8;
|
||||
|
||||
private transient final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
private transient NodeUsageCollector nodeUsageCollector;
|
||||
|
||||
private transient PersistenceContext persistenceContext;
|
||||
|
||||
private transient BeanLoader beanLoader;
|
||||
|
||||
private transient PreGetterCallback preGetterCallback;
|
||||
|
||||
private String ebeanServerName;
|
||||
|
||||
private boolean deletedFromCollection;
|
||||
|
||||
/**
|
||||
* The actual entity bean that 'owns' this intercept.
|
||||
*/
|
||||
private final EntityBean owner;
|
||||
|
||||
private EntityBean embeddedOwner;
|
||||
private int embeddedOwnerIndex;
|
||||
|
||||
/**
|
||||
* One of NEW, REF, UPD.
|
||||
*/
|
||||
private int state;
|
||||
|
||||
private boolean forceUpdate;
|
||||
|
||||
private boolean readOnly;
|
||||
|
||||
private boolean dirty;
|
||||
|
||||
/**
|
||||
* Flag set to disable lazy loading - typically for SQL "report" type entity beans.
|
||||
*/
|
||||
@@ -74,35 +82,9 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Flag set when lazy loading failed due to the underlying bean being deleted in the DB.
|
||||
*/
|
||||
private boolean lazyLoadFailure;
|
||||
|
||||
/**
|
||||
* Used when a bean is partially filled.
|
||||
*/
|
||||
private static final byte FLAG_LOADED_PROP = 1;
|
||||
|
||||
/**
|
||||
* Set of changed properties.
|
||||
*/
|
||||
private static final byte FLAG_CHANGED_PROP = 2;
|
||||
|
||||
/**
|
||||
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
|
||||
* between an embedded bean being completely overwritten and one of its
|
||||
* embedded properties being made dirty.
|
||||
*/
|
||||
private static final byte FLAG_EMBEDDED_DIRTY = 4;
|
||||
|
||||
/**
|
||||
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
|
||||
* between an embedded bean being completely overwritten and one of its
|
||||
* embedded properties being made dirty.
|
||||
*/
|
||||
private static final byte FLAG_ORIG_VALUE_SET = 8;
|
||||
|
||||
private final byte[] flags;
|
||||
|
||||
private boolean fullyLoadedBean;
|
||||
private boolean loadedFromCache;
|
||||
private final byte[] flags;
|
||||
private Object[] origValues;
|
||||
private Exception[] loadErrors;
|
||||
private int lazyLoadProperty = -1;
|
||||
@@ -569,6 +551,10 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
flags[propertyIndex] |= FLAG_CHANGED_PROP;
|
||||
}
|
||||
|
||||
private void setChangeLoaded(int propertyIndex) {
|
||||
flags[propertyIndex] |= FLAG_CHANGEDLOADED_PROP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set that an embedded bean has had one of its properties changed.
|
||||
*/
|
||||
@@ -937,7 +923,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
if (readOnly) {
|
||||
throw new IllegalStateException("This bean is readOnly");
|
||||
}
|
||||
setChangedProperty(propertyIndex);
|
||||
setChangeLoaded(propertyIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -977,7 +963,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check for primitive boolean.
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,6 @@ package io.ebean.bean;
|
||||
* <p>
|
||||
* This is used to ensure only one instance for a given entity type and id is
|
||||
* used to build object graphs from queries and lazy loading.
|
||||
* </p>
|
||||
*/
|
||||
public interface PersistenceContext {
|
||||
|
||||
@@ -20,7 +19,6 @@ public interface PersistenceContext {
|
||||
* <p>
|
||||
* Returns an existing entity bean (if one is already there) and otherwise
|
||||
* returns null.
|
||||
* </p>
|
||||
*/
|
||||
Object putIfAbsent(Class<?> rootType, Object id, Object bean);
|
||||
|
||||
@@ -82,7 +80,6 @@ public interface PersistenceContext {
|
||||
* <p>
|
||||
* If a bean has been deleted then for the same persistence context is should
|
||||
* not be able to be fetched from persistence context or L2 cache.
|
||||
* </p>
|
||||
*/
|
||||
class WithOption {
|
||||
|
||||
|
||||
@@ -135,6 +135,11 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
// Support for modify additions deletions etc - ManyToMany
|
||||
// ---------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean hasModifications() {
|
||||
return modifyHolder != null && modifyHolder.hasModifications();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModifyListenMode getModifyListening() {
|
||||
return modifyListenMode;
|
||||
@@ -145,7 +150,6 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
*/
|
||||
@Override
|
||||
public void setModifyListening(ModifyListenMode mode) {
|
||||
|
||||
this.modifyListenMode = mode;
|
||||
this.modifyListening = mode != null && ModifyListenMode.NONE != mode;
|
||||
if (modifyListening) {
|
||||
|
||||
@@ -198,10 +198,9 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
}
|
||||
if (list == null) {
|
||||
sb.append("deferred ");
|
||||
|
||||
} else {
|
||||
sb.append("size[").append(list.size()).append("] ");
|
||||
sb.append("list").append(list).append("");
|
||||
sb.append("list").append(list);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -175,10 +175,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
|
||||
/**
|
||||
* Returns the map entrySet.
|
||||
* <p>
|
||||
* This is because the key values may need to be set against the details (so
|
||||
* they don't need to be set twice).
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public Collection<?> getActualEntries() {
|
||||
@@ -194,7 +190,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
}
|
||||
if (map == null) {
|
||||
sb.append("deferred ");
|
||||
|
||||
} else {
|
||||
sb.append("size[").append(map.size()).append("]");
|
||||
sb.append(" map").append(map);
|
||||
@@ -243,17 +238,12 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public Set<Entry<K, E>> entrySet() {
|
||||
init();
|
||||
if (isReadOnly()) {
|
||||
return Collections.unmodifiableSet(map.entrySet());
|
||||
}
|
||||
if (modifyListening) {
|
||||
Set<Entry<K, E>> s = map.entrySet();
|
||||
return new ModifySet(this, s);
|
||||
}
|
||||
return map.entrySet();
|
||||
return modifyListening ? new ModifyEntrySet<>(this, map.entrySet()) : map.entrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -274,8 +264,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
if (isReadOnly()) {
|
||||
return Collections.unmodifiableSet(map.keySet());
|
||||
}
|
||||
// we don't really care about modifications to the ketSet?
|
||||
return map.keySet();
|
||||
return modifyListening ? new ModifyKeySet<>(this, map.keySet()) : map.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -346,11 +335,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
if (isReadOnly()) {
|
||||
return Collections.unmodifiableCollection(map.values());
|
||||
}
|
||||
if (modifyListening) {
|
||||
Collection<E> c = map.values();
|
||||
return new ModifyCollection<>(this, c);
|
||||
}
|
||||
return map.values();
|
||||
return modifyListening ? new ModifyCollection<>(this, map.values()) : map.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -176,7 +176,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
}
|
||||
if (set == null) {
|
||||
sb.append("deferred ");
|
||||
|
||||
} else {
|
||||
sb.append("size[").append(set.size()).append("]");
|
||||
sb.append(" set").append(set);
|
||||
|
||||
@@ -25,7 +25,7 @@ class ModifyCollection<E> implements Collection<E> {
|
||||
* The owner is notified of the additions and removals.
|
||||
* </p>
|
||||
*/
|
||||
public ModifyCollection(BeanCollection<E> owner, Collection<E> c) {
|
||||
ModifyCollection(BeanCollection<E> owner, Collection<E> c) {
|
||||
this.owner = owner;
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package io.ebean.common;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Handles the Entry Set for BeanMap.
|
||||
*/
|
||||
class ModifyEntrySet<K, E> implements Set<Map.Entry<K, E>> {
|
||||
|
||||
private final BeanMap<K, E> owner;
|
||||
private final Set<Map.Entry<K, E>> entrySet;
|
||||
|
||||
ModifyEntrySet(BeanMap<K, E> owner, Set<Map.Entry<K, E>> entrySet) {
|
||||
this.owner = owner;
|
||||
this.entrySet = entrySet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return entrySet.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return entrySet.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return entrySet.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return entrySet.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return entrySet.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> entries) {
|
||||
return entrySet.containsAll(entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
owner.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Map.Entry<K, E> entry) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends Map.Entry<K, E>> c) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
if (o instanceof Map.Entry) {
|
||||
Map.Entry entry = (Map.Entry) o;
|
||||
final E val = owner.get(entry.getKey());
|
||||
if (Objects.equals(val, entry.getValue())) {
|
||||
owner.remove(entry.getKey());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> entries) {
|
||||
boolean modified = false;
|
||||
final Iterator<Map.Entry<K, E>> it = iterator();
|
||||
while (it.hasNext()) {
|
||||
if (!entries.contains(it.next())) {
|
||||
it.remove();
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> entries) {
|
||||
boolean modified = false;
|
||||
for (Object entry : entries) {
|
||||
modified |= remove(entry);
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<K, E>> iterator() {
|
||||
return new EntrySetIterator(new ArrayList<>(entrySet).iterator());
|
||||
}
|
||||
|
||||
class EntrySetIterator implements Iterator<Map.Entry<K, E>> {
|
||||
|
||||
private final Iterator<Map.Entry<K, E>> iterator;
|
||||
private Map.Entry<K, E> entry;
|
||||
|
||||
EntrySetIterator(Iterator<Map.Entry<K, E>> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<K, E> next() {
|
||||
entry = iterator.next();
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
owner.remove(entry.getKey());
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package io.ebean.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Handle the Key Set for BeanMap.
|
||||
*/
|
||||
class ModifyKeySet<E> implements Set<E> {
|
||||
|
||||
private final Set<E> keySet;
|
||||
private final BeanMap<E, ?> owner;
|
||||
|
||||
ModifyKeySet(BeanMap<E, ?> owner, Set<E> keySet) {
|
||||
this.owner = owner;
|
||||
this.keySet = keySet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return keySet.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return keySet.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return keySet.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return keySet.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return keySet.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends E> keys) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return owner.remove(o) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> keys) {
|
||||
return keySet.containsAll(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
owner.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new KeySetIterator<>(new ArrayList<>(keySet).iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> keys) {
|
||||
return keysMatch(keys, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> keys) {
|
||||
return keysMatch(keys, true);
|
||||
}
|
||||
|
||||
private boolean keysMatch(Collection<?> keys, boolean containsMatch) {
|
||||
boolean changed = false;
|
||||
final Iterator<E> iterator = iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final E key = iterator.next();
|
||||
if (keys.contains(key) == containsMatch) {
|
||||
iterator.remove();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
class KeySetIterator<K> implements Iterator<K> {
|
||||
|
||||
private final Iterator<K> iterator;
|
||||
private K key;
|
||||
|
||||
KeySetIterator(Iterator<K> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K next() {
|
||||
key = iterator.next();
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
owner.remove(key);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package io.ebean.common;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Wraps a Set for the purposes of notifying removals and additions to the
|
||||
* BeanCollection owner.
|
||||
* <p>
|
||||
* This is required for persisting ManyToMany objects. Additions and removals
|
||||
* become inserts and deletes to the intersection table.
|
||||
* </p>
|
||||
*/
|
||||
class ModifySet<E> extends ModifyCollection<E> implements Set<E> {
|
||||
|
||||
/**
|
||||
* Create with an Owner that is notified of any additions or deletions.
|
||||
*/
|
||||
public ModifySet(BeanCollection<E> owner, Set<E> s) {
|
||||
super(owner, s);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -193,6 +193,12 @@ public class DatabaseConfig {
|
||||
*/
|
||||
private JsonConfig.Include jsonInclude = JsonConfig.Include.ALL;
|
||||
|
||||
/**
|
||||
* When true then by default DbJson beans are assumed to be dirty.
|
||||
* I believe we want to change this default to false in the future.
|
||||
*/
|
||||
private boolean jsonDirtyByDefault = true;
|
||||
|
||||
/**
|
||||
* The database platform name. Used to imply a DatabasePlatform to use.
|
||||
*/
|
||||
@@ -232,6 +238,12 @@ public class DatabaseConfig {
|
||||
*/
|
||||
private String historyTableSuffix = "_history";
|
||||
|
||||
/**
|
||||
* When true explicit transactions beans that have been made dirty will be
|
||||
* automatically persisted via update on flush.
|
||||
*/
|
||||
private boolean autoPersistUpdates;
|
||||
|
||||
/**
|
||||
* Use for transaction scoped batch mode.
|
||||
*/
|
||||
@@ -498,14 +510,23 @@ public class DatabaseConfig {
|
||||
private boolean notifyL2CacheInForeground;
|
||||
|
||||
/**
|
||||
* Set to true to support query plan capture.
|
||||
* Set to true to enable bind capture required for query plan capture.
|
||||
*/
|
||||
private boolean collectQueryPlans;
|
||||
private boolean queryPlanEnable;
|
||||
|
||||
/**
|
||||
* The default threshold in micros for collecting query plans.
|
||||
*/
|
||||
private long collectQueryPlanThresholdMicros = Long.MAX_VALUE;
|
||||
private long queryPlanThresholdMicros = Long.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* Set to true to enable automatic periodic query plan capture.
|
||||
*/
|
||||
private boolean queryPlanCapture;
|
||||
private long queryPlanCapturePeriodSecs = 60 * 10; // 10 minutes
|
||||
private long queryPlanCaptureMaxTimeMillis = 10_000; // 10 seconds
|
||||
private int queryPlanCaptureMaxCount = 10;
|
||||
private QueryPlanListener queryPlanListener;
|
||||
|
||||
/**
|
||||
* The time in millis used to determine when a query is alerted for being slow.
|
||||
@@ -722,6 +743,26 @@ public class DatabaseConfig {
|
||||
this.jsonInclude = jsonInclude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if DbJson beans are assumed dirty by default.
|
||||
* <p>
|
||||
* That is, when true beans that do not implement ModifyAwareType are by
|
||||
* default assumed to be dirty and included in updates.
|
||||
*/
|
||||
public boolean isJsonDirtyByDefault() {
|
||||
return jsonDirtyByDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to false if we want DbJson beans to not be assumed to be dirty.
|
||||
* <p>
|
||||
* That is, when true beans that do not implement ModifyAwareType are by
|
||||
* default assumed to be dirty and included in updates.
|
||||
*/
|
||||
public void setJsonDirtyByDefault(boolean jsonDirtyByDefault) {
|
||||
this.jsonDirtyByDefault = jsonDirtyByDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the Database.
|
||||
*/
|
||||
@@ -896,6 +937,20 @@ public class DatabaseConfig {
|
||||
this.tenantCatalogProvider = tenantCatalogProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if dirty beans are automatically persisted.
|
||||
*/
|
||||
public boolean isAutoPersistUpdates() {
|
||||
return autoPersistUpdates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if dirty beans are automatically persisted.
|
||||
*/
|
||||
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
|
||||
this.autoPersistUpdates = autoPersistUpdates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the PersistBatch mode to use by default at the transaction level.
|
||||
* <p>
|
||||
@@ -1045,7 +1100,6 @@ public class DatabaseConfig {
|
||||
* This is a performance optimisation to reduce the number times Ebean
|
||||
* requests a sequence to be used as an Id for a bean (aka reduce network
|
||||
* chatter).
|
||||
|
||||
*/
|
||||
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
|
||||
platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
|
||||
@@ -2693,7 +2747,10 @@ public class DatabaseConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings from ebean.properties.
|
||||
* Load settings from application.properties, application.yaml and other sources.
|
||||
* <p>
|
||||
* Uses <code>avaje-config</code> to load configuration properties. Goto https://avaje.io/config
|
||||
* for detail on how and where properties are loaded from.
|
||||
*/
|
||||
public void loadFromProperties() {
|
||||
this.properties = Config.asProperties();
|
||||
@@ -2796,14 +2853,19 @@ public class DatabaseConfig {
|
||||
loadDocStoreSettings(p);
|
||||
|
||||
defaultServer = p.getBoolean("defaultServer", defaultServer);
|
||||
autoPersistUpdates = p.getBoolean("autoPersistUpdates", autoPersistUpdates);
|
||||
loadModuleInfo = p.getBoolean("loadModuleInfo", loadModuleInfo);
|
||||
maxCallStack = p.getInt("maxCallStack", maxCallStack);
|
||||
dumpMetricsOnShutdown = p.getBoolean("dumpMetricsOnShutdown", dumpMetricsOnShutdown);
|
||||
dumpMetricsOptions = p.get("dumpMetricsOptions", dumpMetricsOptions);
|
||||
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
|
||||
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
|
||||
collectQueryPlans = p.getBoolean("collectQueryPlans", collectQueryPlans);
|
||||
collectQueryPlanThresholdMicros = p.getLong("collectQueryPlanThresholdMicros", collectQueryPlanThresholdMicros);
|
||||
queryPlanEnable = p.getBoolean("queryPlan.enable", queryPlanEnable);
|
||||
queryPlanThresholdMicros = p.getLong("queryPlan.thresholdMicros", queryPlanThresholdMicros);
|
||||
queryPlanCapture = p.getBoolean("queryPlan.capture", queryPlanCapture);
|
||||
queryPlanCapturePeriodSecs = p.getLong("queryPlan.capturePeriodSecs", queryPlanCapturePeriodSecs);
|
||||
queryPlanCaptureMaxTimeMillis = p.getLong("queryPlan.captureMaxTimeMillis", queryPlanCaptureMaxTimeMillis);
|
||||
queryPlanCaptureMaxCount = p.getInt("queryPlan.captureMaxCount", queryPlanCaptureMaxCount);
|
||||
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
|
||||
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
|
||||
localOnlyL2Cache = p.getBoolean("localOnlyL2Cache", localOnlyL2Cache);
|
||||
@@ -2873,6 +2935,7 @@ public class DatabaseConfig {
|
||||
jsonInclude = p.getEnum(JsonConfig.Include.class, "jsonInclude", jsonInclude);
|
||||
jsonDateTime = p.getEnum(JsonConfig.DateTime.class, "jsonDateTime", jsonDateTime);
|
||||
jsonDate = p.getEnum(JsonConfig.Date.class, "jsonDate", jsonDate);
|
||||
jsonDirtyByDefault = p.getBoolean("jsonDirtyByDefault", jsonDirtyByDefault);
|
||||
|
||||
runMigration = p.getBoolean("migration.run", runMigration);
|
||||
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
|
||||
@@ -3093,14 +3156,17 @@ public class DatabaseConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query plan time to live.
|
||||
* Return the time to live for ebean's internal query plan.
|
||||
*/
|
||||
public int getQueryPlanTTLSeconds() {
|
||||
return queryPlanTTLSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the query plan time to live.
|
||||
* Set the time to live for ebean's internal query plan.
|
||||
* <p>
|
||||
* This is the plan that knows how to execute the query, read the result
|
||||
* and collects execution metrics. By default this is set to 5 mins.
|
||||
*/
|
||||
public void setQueryPlanTTLSeconds(int queryPlanTTLSeconds) {
|
||||
this.queryPlanTTLSeconds = queryPlanTTLSeconds;
|
||||
@@ -3173,29 +3239,110 @@ public class DatabaseConfig {
|
||||
/**
|
||||
* Return true if query plan capture is enabled.
|
||||
*/
|
||||
public boolean isCollectQueryPlans() {
|
||||
return collectQueryPlans;
|
||||
public boolean isQueryPlanEnable() {
|
||||
return queryPlanEnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to enable query plan capture.
|
||||
*/
|
||||
public void setCollectQueryPlans(boolean collectQueryPlans) {
|
||||
this.collectQueryPlans = collectQueryPlans;
|
||||
public void setQueryPlanEnable(boolean queryPlanEnable) {
|
||||
this.queryPlanEnable = queryPlanEnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query plan collection threshold in microseconds.
|
||||
*/
|
||||
public long getCollectQueryPlanThresholdMicros() {
|
||||
return collectQueryPlanThresholdMicros;
|
||||
public long getQueryPlanThresholdMicros() {
|
||||
return queryPlanThresholdMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the query plan collection threshold in microseconds.
|
||||
* <p>
|
||||
* Queries executing slower than this will have bind values captured such that later
|
||||
* the query plan can be captured and reported.
|
||||
*/
|
||||
public void setCollectQueryPlanThresholdMicros(long collectQueryPlanThresholdMicros) {
|
||||
this.collectQueryPlanThresholdMicros = collectQueryPlanThresholdMicros;
|
||||
public void setQueryPlanThresholdMicros(long queryPlanThresholdMicros) {
|
||||
this.queryPlanThresholdMicros = queryPlanThresholdMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if periodic capture of query plans is enabled.
|
||||
*/
|
||||
public boolean isQueryPlanCapture() {
|
||||
return queryPlanCapture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to turn on periodic capture of query plans.
|
||||
*/
|
||||
public void setQueryPlanCapture(boolean queryPlanCapture) {
|
||||
this.queryPlanCapture = queryPlanCapture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the frequency to capture query plans.
|
||||
*/
|
||||
public long getQueryPlanCapturePeriodSecs() {
|
||||
return queryPlanCapturePeriodSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frequency in seconds to capture query plans.
|
||||
*/
|
||||
public void setQueryPlanCapturePeriodSecs(long queryPlanCapturePeriodSecs) {
|
||||
this.queryPlanCapturePeriodSecs = queryPlanCapturePeriodSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time after which a capture query plans request will
|
||||
* stop capturing more query plans.
|
||||
* <p>
|
||||
* Effectively this controls the amount of load/time we want to
|
||||
* allow for query plan capture.
|
||||
*/
|
||||
public long getQueryPlanCaptureMaxTimeMillis() {
|
||||
return queryPlanCaptureMaxTimeMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time after which a capture query plans request will
|
||||
* stop capturing more query plans.
|
||||
* <p>
|
||||
* Effectively this controls the amount of load/time we want to
|
||||
* allow for query plan capture.
|
||||
*/
|
||||
public void setQueryPlanCaptureMaxTimeMillis(long queryPlanCaptureMaxTimeMillis) {
|
||||
this.queryPlanCaptureMaxTimeMillis = queryPlanCaptureMaxTimeMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the max number of query plans captured per request.
|
||||
*/
|
||||
public int getQueryPlanCaptureMaxCount() {
|
||||
return queryPlanCaptureMaxCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max number of query plans captured per request.
|
||||
*/
|
||||
public void setQueryPlanCaptureMaxCount(int queryPlanCaptureMaxCount) {
|
||||
this.queryPlanCaptureMaxCount = queryPlanCaptureMaxCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listener used to process captured query plans.
|
||||
*/
|
||||
public QueryPlanListener getQueryPlanListener() {
|
||||
return queryPlanListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listener used to process captured query plans.
|
||||
*/
|
||||
public void setQueryPlanListener(QueryPlanListener queryPlanListener) {
|
||||
this.queryPlanListener = queryPlanListener;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.ebean.config;
|
||||
|
||||
import io.ebean.Database;
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The captured query plans.
|
||||
*/
|
||||
public class QueryPlanCapture {
|
||||
|
||||
private final Database database;
|
||||
private final List<MetaQueryPlan> plans;
|
||||
|
||||
public QueryPlanCapture(Database database, List<MetaQueryPlan> plans) {
|
||||
this.database = database;
|
||||
this.plans = plans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the database the plans were captured for.
|
||||
*/
|
||||
public Database getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the captured query plans.
|
||||
*/
|
||||
public List<MetaQueryPlan> getPlans() {
|
||||
return plans;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.ebean.config;
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL: Listener for captured query plans.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface QueryPlanListener {
|
||||
|
||||
/**
|
||||
* Process the captured query plans.
|
||||
*/
|
||||
void process(QueryPlanCapture capture);
|
||||
}
|
||||
@@ -9,9 +9,6 @@ import java.sql.Types;
|
||||
* functions for varchar, date and timestamp. If they are left null then that is
|
||||
* treated as though that data type can not be encrypted in the DB and will
|
||||
* instead use java client encryption.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public abstract class AbstractDbEncrypt implements DbEncrypt {
|
||||
|
||||
@@ -47,13 +44,10 @@ public abstract class AbstractDbEncrypt implements DbEncrypt {
|
||||
case Types.CHAR:
|
||||
case Types.LONGVARCHAR:
|
||||
return varcharEncryptFunction;
|
||||
|
||||
case Types.DATE:
|
||||
return dateEncryptFunction;
|
||||
|
||||
case Types.TIMESTAMP:
|
||||
return timestampEncryptFunction;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import io.ebean.config.dbplatform.DbEncryptFunction;
|
||||
|
||||
/**
|
||||
* H2 encryption support via encrypt decrypt function.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class H2DbEncrypt extends AbstractDbEncrypt {
|
||||
|
||||
|
||||
@@ -4,11 +4,7 @@ import org.h2.api.Trigger;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.sql.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
@@ -43,7 +39,6 @@ public class H2HistoryTrigger implements Trigger {
|
||||
|
||||
@Override
|
||||
public void init(Connection conn, String schemaName, String triggerName, String tableName, boolean before, int type) throws SQLException {
|
||||
|
||||
// get the columns for the table
|
||||
ResultSet rs = conn.getMetaData().getColumns(null, schemaName, tableName, null);
|
||||
|
||||
@@ -79,7 +74,6 @@ public class H2HistoryTrigger implements Trigger {
|
||||
|
||||
@Override
|
||||
public void fire(Connection connection, Object[] oldRow, Object[] newRow) throws SQLException {
|
||||
|
||||
if (oldRow != null) {
|
||||
// a delete or update event
|
||||
Timestamp now = new Timestamp(System.currentTimeMillis());
|
||||
@@ -99,7 +93,6 @@ public class H2HistoryTrigger implements Trigger {
|
||||
* Insert the data into the history table.
|
||||
*/
|
||||
private void insertIntoHistory(Connection connection, Object[] oldRow) throws SQLException {
|
||||
|
||||
try (PreparedStatement stmt = connection.prepareStatement(insertHistorySql)) {
|
||||
for (int i = 0; i < oldRow.length; i++) {
|
||||
stmt.setObject(i + 1, oldRow[i]);
|
||||
@@ -110,11 +103,11 @@ public class H2HistoryTrigger implements Trigger {
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() throws SQLException {
|
||||
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import io.ebean.config.dbplatform.DbEncryptFunction;
|
||||
|
||||
/**
|
||||
* MySql aes_encrypt aes_decrypt based encryption support.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class MySqlDbEncrypt extends AbstractDbEncrypt {
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ public class Oracle11Platform extends OraclePlatform {
|
||||
public Oracle11Platform() {
|
||||
super();
|
||||
this.platform = Platform.ORACLE11;
|
||||
this.columnAliasPrefix = "c";
|
||||
this.sqlLimiter = new OracleRownumSqlLimiter();
|
||||
this.basicSqlLimiter = new OracleRownumBasicLimiter();
|
||||
dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
|
||||
@@ -60,7 +60,6 @@ public class OracleDbEncrypt extends AbstractDbEncrypt {
|
||||
* @param decryptFunction the decrypt stored procedure
|
||||
*/
|
||||
public OracleDbEncrypt(String encryptFunction, String decryptFunction) {
|
||||
|
||||
this.varcharEncryptFunction = new OraVarcharFunction(encryptFunction, decryptFunction);
|
||||
this.dateEncryptFunction = new OraDateFunction(encryptFunction, decryptFunction);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public class OraclePlatform extends DatabasePlatform {
|
||||
public OraclePlatform() {
|
||||
super();
|
||||
this.platform = Platform.ORACLE;
|
||||
this.columnAliasPrefix = "c";
|
||||
this.supportsDeleteTableAlias = true;
|
||||
this.maxTableNameLength = 30;
|
||||
this.maxConstraintNameLength = 30;
|
||||
|
||||
+1
@@ -25,6 +25,7 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
|
||||
this.platform = Platform.SQLSERVER;
|
||||
// disable persistBatchOnCascade mode for
|
||||
// SQL Server unless we are using sequences
|
||||
this.dbEncrypt = new SqlServerDbEncrypt();
|
||||
this.persistBatchOnCascade = PersistBatch.NONE;
|
||||
this.idInExpandedForm = true;
|
||||
this.selectCountWithAlias = true;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package io.ebean.config.dbplatform.sqlserver;
|
||||
|
||||
import io.ebean.config.dbplatform.AbstractDbEncrypt;
|
||||
import io.ebean.config.dbplatform.DbEncryptFunction;
|
||||
|
||||
/**
|
||||
* SQL Server EncryptByPassPhrase DecryptByPassPhrase based encryption support.
|
||||
*/
|
||||
public class SqlServerDbEncrypt extends AbstractDbEncrypt {
|
||||
|
||||
public SqlServerDbEncrypt() {
|
||||
this.varcharEncryptFunction = new VarcharFunction();
|
||||
this.dateEncryptFunction = new DateFunction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBindEncryptDataFirst() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class VarcharFunction implements DbEncryptFunction {
|
||||
|
||||
@Override
|
||||
public String getDecryptSql(String columnWithTableAlias) {
|
||||
return "convert(nvarchar,DecryptByPassPhrase(?," + columnWithTableAlias + "))";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEncryptBindSql() {
|
||||
return "EncryptByPassPhrase(?,?)";
|
||||
}
|
||||
}
|
||||
|
||||
private static class DateFunction implements DbEncryptFunction {
|
||||
|
||||
@Override
|
||||
public String getDecryptSql(String columnWithTableAlias) {
|
||||
return "cast(convert(nvarchar,DecryptByPassPhrase(?," + columnWithTableAlias + ")) as date)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEncryptBindSql() {
|
||||
return "EncryptByPassPhrase(?,format(?,'yyyy-MM-dd'))";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,9 @@ public class QueryPlanRequest {
|
||||
|
||||
/**
|
||||
* Set the maximum number of plans to capture.
|
||||
* <p>
|
||||
* Use this to limit how much query plan capturing is done as query
|
||||
* plan capture is actual database load.
|
||||
*/
|
||||
public void setMaxCount(int maxCount) {
|
||||
this.maxCount = maxCount;
|
||||
@@ -58,7 +61,9 @@ public class QueryPlanRequest {
|
||||
/**
|
||||
* Set the maximum amount of time we want to use to capture plans.
|
||||
* <p>
|
||||
* Query plan collection will stop once this time is exceeded.
|
||||
* Query plan collection will stop once this time is exceeded. We use
|
||||
* this to ensure the query plan capture does not use excessive amount
|
||||
* of time - put too much load on the database.
|
||||
*/
|
||||
public void setMaxTimeMillis(long maxTimeMillis) {
|
||||
this.maxTimeMillis = maxTimeMillis;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</parent>
|
||||
<!-- <parent>-->
|
||||
<!-- <groupId>org.avaje</groupId>-->
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-parent-12.8.0</tag>
|
||||
<tag>ebean-parent-12.9.3</tag>
|
||||
</scm>
|
||||
|
||||
<name>ebean autotune</name>
|
||||
@@ -26,7 +26,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
+2
-2
@@ -113,7 +113,7 @@ public class AutoTuneDiffCollection {
|
||||
|
||||
diffCount++;
|
||||
|
||||
Origin origin = createOrigin(entry, point, tuneDetail.toString());
|
||||
Origin origin = createOrigin(entry, point, tuneDetail.asString());
|
||||
ProfileDiff diff = document.getProfileDiff();
|
||||
if (diff == null) {
|
||||
diff = new ProfileDiff();
|
||||
@@ -147,7 +147,7 @@ public class AutoTuneDiffCollection {
|
||||
Origin origin = new Origin();
|
||||
origin.setKey(point.getKey());
|
||||
origin.setBeanType(point.getBeanType());
|
||||
origin.setDetail(entry.getDetail().toString());
|
||||
origin.setDetail(entry.getDetail().asString());
|
||||
origin.setCallStack(point.getCallOrigin().getFullDescription());
|
||||
origin.setOriginal(query);
|
||||
|
||||
|
||||
+1
-9
@@ -46,7 +46,6 @@ public class ProfileManager implements ProfilingListener {
|
||||
|
||||
@Override
|
||||
public boolean isProfileRequest(ObjectGraphNode origin, SpiQuery<?> query) {
|
||||
|
||||
ProfileOrigin profileOrigin = profileMap.get(origin.getOriginQueryPoint().getKey());
|
||||
if (profileOrigin == null) {
|
||||
profileMap.put(origin.getOriginQueryPoint().getKey(), createProfileOrigin(origin, query));
|
||||
@@ -61,12 +60,11 @@ public class ProfileManager implements ProfilingListener {
|
||||
* <p>
|
||||
* For new profiling entries it is useful to compare the profiling against the current
|
||||
* query detail that is specified in the code (as the query might already be manually optimised).
|
||||
* </p>
|
||||
*/
|
||||
private ProfileOrigin createProfileOrigin(ObjectGraphNode origin, SpiQuery<?> query) {
|
||||
ProfileOrigin profileOrigin = new ProfileOrigin(origin.getOriginQueryPoint(), queryTuningAddVersion, profilingBase, profilingRate);
|
||||
// set the current query detail (fetch group) so that we can compare against profiling for new entries
|
||||
profileOrigin.setOriginalQuery(query.getDetail().toString());
|
||||
profileOrigin.setOriginalQuery(query.getDetail().asString());
|
||||
return profileOrigin;
|
||||
}
|
||||
|
||||
@@ -77,7 +75,6 @@ public class ProfileManager implements ProfilingListener {
|
||||
*/
|
||||
@Override
|
||||
public void collectQueryInfo(ObjectGraphNode node, long beans, long micros) {
|
||||
|
||||
if (node != null) {
|
||||
ObjectGraphOrigin origin = node.getOriginQueryPoint();
|
||||
if (origin != null) {
|
||||
@@ -92,11 +89,9 @@ public class ProfileManager implements ProfilingListener {
|
||||
* <p>
|
||||
* This is sent to use from a EntityBeanIntercept when the finalise method
|
||||
* is called on the bean.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public void collectNodeUsage(NodeUsageCollector usageCollector) {
|
||||
|
||||
ProfileOrigin profileOrigin = getProfileOrigin(usageCollector.getNode().getOriginQueryPoint());
|
||||
profileOrigin.collectUsageInfo(usageCollector);
|
||||
}
|
||||
@@ -114,16 +109,13 @@ public class ProfileManager implements ProfilingListener {
|
||||
* Collect all the profiling information.
|
||||
*/
|
||||
public AutoTuneCollection profilingCollection(boolean reset) {
|
||||
|
||||
AutoTuneCollection req = new AutoTuneCollection();
|
||||
|
||||
for (ProfileOrigin origin : profileMap.values()) {
|
||||
BeanDescriptor<?> desc = server.getBeanDescriptorById(origin.getOrigin().getBeanType());
|
||||
if (desc != null) {
|
||||
origin.profilingCollection(desc, req, reset);
|
||||
}
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ public class TunedQueryInfo implements Serializable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return tunedDetail.toString();
|
||||
return tunedDetail.asString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-5
@@ -27,7 +27,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asStringDebug().trim()).isEqualTo("fetch customer (name)");
|
||||
assertThat(detail.asString().trim()).isEqualTo("fetch customer (name)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -46,7 +46,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asStringDebug()).isEqualTo("select (orderDate) fetch customer (name)");
|
||||
assertThat(detail.asString()).isEqualTo("select (orderDate) fetch customer (name)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -64,7 +64,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asStringDebug().trim()).isEqualTo("select (orderDate,customer)");
|
||||
assertThat(detail.asString().trim()).isEqualTo("select (orderDate,customer)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -87,7 +87,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asStringDebug()).isEqualTo("select (orderDate) fetch customer (billingAddress)");
|
||||
assertThat(detail.asString()).isEqualTo("select (orderDate) fetch customer (billingAddress)");
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
po.collectUsageInfo(c);
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
assertThat(detail.asStringDebug()).isEqualTo("fetch customer (name,note) fetch customer.billingAddress (line1)");
|
||||
assertThat(detail.asString()).isEqualTo("fetch customer (name,note) fetch customer.billingAddress (line1)");
|
||||
}
|
||||
|
||||
private NodeUsageCollector node(String path) {
|
||||
|
||||
+17
-17
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean bom</name>
|
||||
@@ -18,8 +18,8 @@
|
||||
<ebean-migration.version>12.4.0</ebean-migration.version>
|
||||
<ebean-test-docker.version>4.1</ebean-test-docker.version>
|
||||
<ebean-datasource.version>7.0</ebean-datasource.version>
|
||||
<ebean-agent.version>12.6.6</ebean-agent.version>
|
||||
<ebean-maven-plugin.version>12.6.6</ebean-maven-plugin.version>
|
||||
<ebean-agent.version>12.9.0</ebean-agent.version>
|
||||
<ebean-maven-plugin.version>12.9.1</ebean-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -81,88 +81,88 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-xml</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-autotune</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>kotlin-querybean-generator</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-redis</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
@@ -16,7 +16,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -11,8 +11,6 @@ public interface DataReader {
|
||||
|
||||
boolean next() throws SQLException;
|
||||
|
||||
void resetColumnPosition();
|
||||
|
||||
void incrementPos(int increment);
|
||||
|
||||
byte[] getBinaryBytes() throws SQLException;
|
||||
|
||||
+18
-18
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core</artifactId>
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-parent-12.8.0</tag>
|
||||
<tag>ebean-parent-12.9.3</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -72,7 +72,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.9.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -87,19 +87,19 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.9.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -119,6 +119,15 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Provided scope so that the H2HistoryTrigger can live in Ebean core
|
||||
and not require a separate module for it -->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.4.199</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.transaction</groupId>
|
||||
<artifactId>jta</artifactId>
|
||||
@@ -173,7 +182,7 @@
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.2.10</version>
|
||||
<version>42.2.20</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -214,15 +223,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Provided scope so that the H2HistoryTrigger can live in Ebean core
|
||||
and not require a separate module for it -->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.4.199</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
@@ -282,7 +282,7 @@
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.5</version>
|
||||
<version>2.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -302,7 +302,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>12.6.6</version>
|
||||
<version>12.9.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
|
||||
@@ -155,7 +155,6 @@ public class BindParams implements Serializable {
|
||||
* Set an In Out parameter using position.
|
||||
*/
|
||||
public void setParameter(int position, Object value, int outType) {
|
||||
|
||||
Param p = getParam(position);
|
||||
p.setInValue(value);
|
||||
p.setOutType(outType);
|
||||
@@ -178,8 +177,8 @@ public class BindParams implements Serializable {
|
||||
* Using position set the In value of a parameter. Note that for nulls you
|
||||
* must use setNullParameter.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void setParameter(int position, Object value) {
|
||||
|
||||
Param p = getParam(position);
|
||||
if (value instanceof Collection) {
|
||||
// use of postgres ANY with positioned parameter
|
||||
@@ -214,7 +213,6 @@ public class BindParams implements Serializable {
|
||||
* Set a named In Out parameter.
|
||||
*/
|
||||
public void setParameter(String name, Object value, int outType) {
|
||||
|
||||
Param p = getParam(name);
|
||||
p.setInValue(value);
|
||||
p.setOutType(outType);
|
||||
@@ -232,7 +230,6 @@ public class BindParams implements Serializable {
|
||||
* Set a named In parameter that is not null.
|
||||
*/
|
||||
public Param setParameter(String name, Object value) {
|
||||
|
||||
Param p = getParam(name);
|
||||
p.setInValue(value);
|
||||
return p;
|
||||
@@ -299,7 +296,6 @@ public class BindParams implements Serializable {
|
||||
* Return true if the bind hash and count has not changed.
|
||||
*/
|
||||
public boolean isSameBindHash() {
|
||||
|
||||
if (bindHash == null) {
|
||||
bindHash = calcQueryPlanHash();
|
||||
return false;
|
||||
@@ -330,10 +326,6 @@ public class BindParams implements Serializable {
|
||||
|
||||
private final StringBuilder preparedSql;
|
||||
|
||||
public OrderedList() {
|
||||
this(new ArrayList<>());
|
||||
}
|
||||
|
||||
public OrderedList(List<Param> paramList) {
|
||||
this.paramList = paramList;
|
||||
this.preparedSql = new StringBuilder();
|
||||
|
||||
@@ -1,52 +1,25 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.expression.IdInExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used for bean cache lookup with where ids in expression.
|
||||
* Process Cache lookup by Id(s).
|
||||
*/
|
||||
public class CacheIdLookup<T> {
|
||||
|
||||
private final IdInExpression idInExpression;
|
||||
|
||||
private int remaining;
|
||||
|
||||
public CacheIdLookup(IdInExpression idInExpression) {
|
||||
this.idInExpression = idInExpression;
|
||||
}
|
||||
public interface CacheIdLookup<T> {
|
||||
|
||||
/**
|
||||
* Return the Id values for the in expression.
|
||||
* Return the Id values to lookup against the L2 cache.
|
||||
*/
|
||||
public Collection<?> idValues() {
|
||||
return idInExpression.idValues();
|
||||
}
|
||||
Collection<?> idValues();
|
||||
|
||||
/**
|
||||
* Process the hits returning the beans fetched from cache and
|
||||
* adjusting the in expression (to not fetch the hits).
|
||||
* Remove the hits returning the beans fetched from L2 cache.
|
||||
*/
|
||||
public List<T> removeHits(BeanCacheResult<T> cacheResult) {
|
||||
List<T> removeHits(BeanCacheResult<T> cacheResult);
|
||||
|
||||
Set<Object> hitIds = new HashSet<>();
|
||||
List<T> beans = new ArrayList<>(hitIds.size());
|
||||
|
||||
for (BeanCacheResult.Entry<T> hit : cacheResult.hits()) {
|
||||
hitIds.add(hit.getKey());
|
||||
beans.add(hit.getBean());
|
||||
}
|
||||
|
||||
this.remaining = idInExpression.removeIds(hitIds);
|
||||
return beans;
|
||||
}
|
||||
|
||||
public boolean allHits() {
|
||||
return remaining == 0;
|
||||
}
|
||||
/**
|
||||
* Return true if all beans where found in L2 cache.
|
||||
*/
|
||||
boolean allHits();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.expression.IdInExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used for bean cache lookup with where ids in expression.
|
||||
*/
|
||||
public class CacheIdLookupMany<T> implements CacheIdLookup<T> {
|
||||
|
||||
private final IdInExpression idInExpression;
|
||||
|
||||
private int remaining;
|
||||
|
||||
public CacheIdLookupMany(IdInExpression idInExpression) {
|
||||
this.idInExpression = idInExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Id values for the in expression.
|
||||
*/
|
||||
@Override
|
||||
public Collection<?> idValues() {
|
||||
return idInExpression.idValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the hits returning the beans fetched from cache and
|
||||
* adjusting the in expression (to not fetch the hits).
|
||||
*/
|
||||
@Override
|
||||
public List<T> removeHits(BeanCacheResult<T> cacheResult) {
|
||||
|
||||
Set<Object> hitIds = new HashSet<>();
|
||||
List<T> beans = new ArrayList<>(hitIds.size());
|
||||
|
||||
for (BeanCacheResult.Entry<T> hit : cacheResult.hits()) {
|
||||
hitIds.add(hit.getKey());
|
||||
beans.add(hit.getBean());
|
||||
}
|
||||
|
||||
this.remaining = idInExpression.removeIds(hitIds);
|
||||
return beans;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allHits() {
|
||||
return remaining == 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Used for bean cache lookup with a single id value.
|
||||
*/
|
||||
public class CacheIdLookupSingle<T> implements CacheIdLookup<T> {
|
||||
|
||||
private final Object idValue;
|
||||
private boolean found;
|
||||
|
||||
public CacheIdLookupSingle(Object idValue) {
|
||||
this.idValue = idValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> idValues() {
|
||||
return Collections.singleton(idValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> removeHits(BeanCacheResult<T> cacheResult) {
|
||||
final List<BeanCacheResult.Entry<T>> hits = cacheResult.hits();
|
||||
if (hits.size() == 1) {
|
||||
found = true;
|
||||
return Collections.singletonList(hits.get(0).getBean());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allHits() {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public interface LoadContext {
|
||||
/**
|
||||
* Return the minimum batch size when using QueryIterator with query joins.
|
||||
*/
|
||||
int getSecondaryQueriesMinBatchSize(int defaultQueryBatch);
|
||||
int getSecondaryQueriesMinBatchSize();
|
||||
|
||||
/**
|
||||
* Execute any secondary (+query) queries if there are any defined.
|
||||
|
||||
@@ -71,7 +71,6 @@ public class LoadManyRequest extends LoadRequest {
|
||||
* This for use when lazy loading is invoked on methods such as clear() and removeAll() where it
|
||||
* generally makes sense to only fetch the Id values as the other property information is not
|
||||
* used.
|
||||
* </p>
|
||||
*/
|
||||
private boolean isOnlyIds() {
|
||||
return onlyIds;
|
||||
@@ -91,18 +90,16 @@ public class LoadManyRequest extends LoadRequest {
|
||||
return loadContext.getBatchSize();
|
||||
}
|
||||
|
||||
private List<Object> getParentIdList() {
|
||||
|
||||
private List<Object> parentIdList(SpiEbeanServer server) {
|
||||
List<Object> idList = new ArrayList<>();
|
||||
|
||||
BeanPropertyAssocMany<?> many = getMany();
|
||||
for (BeanCollection<?> bc : batch) {
|
||||
idList.add(many.getParentId(bc.getOwnerBean()));
|
||||
bc.setLoader(server); // don't use the load buffer again
|
||||
}
|
||||
if (many.getTargetDescriptor().isPadInExpression()) {
|
||||
BindPadding.padIds(idList);
|
||||
}
|
||||
|
||||
return idList;
|
||||
}
|
||||
|
||||
@@ -111,9 +108,7 @@ public class LoadManyRequest extends LoadRequest {
|
||||
}
|
||||
|
||||
public SpiQuery<?> createQuery(SpiEbeanServer server) {
|
||||
|
||||
BeanPropertyAssocMany<?> many = getMany();
|
||||
|
||||
SpiQuery<?> query = many.newQuery(server);
|
||||
String orderBy = many.getLazyFetchOrderBy();
|
||||
if (orderBy != null) {
|
||||
@@ -128,7 +123,7 @@ public class LoadManyRequest extends LoadRequest {
|
||||
}
|
||||
|
||||
query.setLazyLoadForParents(many);
|
||||
many.addWhereParentIdIn(query, getParentIdList(), loadContext.isUseDocStore());
|
||||
many.addWhereParentIdIn(query, parentIdList(server), loadContext.isUseDocStore());
|
||||
query.setPersistenceContext(loadContext.getPersistenceContext());
|
||||
|
||||
String mode = isLazy() ? "+lazy" : "+query";
|
||||
@@ -146,7 +141,6 @@ public class LoadManyRequest extends LoadRequest {
|
||||
// override to just select the Id values
|
||||
query.select(many.getTargetIdProperty());
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
@@ -154,10 +148,8 @@ public class LoadManyRequest extends LoadRequest {
|
||||
* After the query execution check for empty collections and load L2 cache if desired.
|
||||
*/
|
||||
public void postLoad() {
|
||||
|
||||
BeanDescriptor<?> desc = loadContext.getBeanDescriptor();
|
||||
BeanPropertyAssocMany<?> many = getMany();
|
||||
|
||||
// check for BeanCollection's that where never processed
|
||||
// in the +query or +lazy load due to no rows (predicates)
|
||||
for (BeanCollection<?> bc : batch) {
|
||||
@@ -172,6 +164,5 @@ public class LoadManyRequest extends LoadRequest {
|
||||
desc.cacheManyPropPut(many, bc, parentId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,11 @@ import java.util.List;
|
||||
|
||||
class NoopQueryPlanManager implements QueryPlanManager {
|
||||
|
||||
@Override
|
||||
public void setDefaultThreshold(long thresholdMicros) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiQueryBindCapture createBindCapture(SpiQueryPlan queryPlan) {
|
||||
return SpiQueryBindCapture.NOOP;
|
||||
|
||||
@@ -12,6 +12,11 @@ public interface QueryPlanManager {
|
||||
|
||||
QueryPlanManager NOOP = new NoopQueryPlanManager();
|
||||
|
||||
/**
|
||||
* Update the global default threshold used when new query plans are created.
|
||||
*/
|
||||
void setDefaultThreshold(long thresholdMicros);
|
||||
|
||||
/**
|
||||
* Create the bind capture for the given query plan.
|
||||
*/
|
||||
|
||||
@@ -73,6 +73,10 @@ public class ScopeTrans {
|
||||
restoreBatchGeneratedKeys = transaction.getBatchGetGeneratedKeys();
|
||||
restoreBatchFlushOnQuery = transaction.isFlushOnQuery();
|
||||
}
|
||||
Boolean autoPersistUpdates = txScope.getAutoPersistUpdates();
|
||||
if (autoPersistUpdates != null) {
|
||||
transaction.setAutoPersistUpdates(autoPersistUpdates);
|
||||
}
|
||||
if (txScope.isBatchSet()) {
|
||||
transaction.setBatchMode(txScope.isBatchMode());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* SPI interface for underlying BeanDescriptor.
|
||||
*/
|
||||
public interface SpiBeanType {
|
||||
|
||||
/**
|
||||
* Return true if the bean contains a many property that has modifications.
|
||||
* <p>
|
||||
* That is a ManyToMany or a OneToMany with orphan removal with additions
|
||||
* or removals from the collection.
|
||||
*/
|
||||
boolean isToManyDirty(EntityBean bean);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Manager of SpiBeanTypes.
|
||||
*/
|
||||
public interface SpiBeanTypeManager {
|
||||
|
||||
/**
|
||||
* Return the bean type for the given entity class.
|
||||
*/
|
||||
SpiBeanType getBeanType(Class<?> entityType);
|
||||
|
||||
}
|
||||
@@ -1,14 +1,6 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.DtoQuery;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.ExtendedServer;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.RowConsumer;
|
||||
import io.ebean.RowMapper;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.TxScope;
|
||||
import io.ebean.*;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.CallOrigin;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
@@ -25,6 +17,7 @@ import io.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Service Provider extension to EbeanServer.
|
||||
@@ -234,6 +227,11 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
|
||||
*/
|
||||
<T> List<T> findSingleAttributeList(SpiSqlQuery query, Class<T> cls);
|
||||
|
||||
/**
|
||||
* SqlQuery find single attribute streaming the result to a consumer.
|
||||
*/
|
||||
<T> void findSingleAttributeEach(SpiSqlQuery query, Class<T> cls, Consumer<T> consumer);
|
||||
|
||||
/**
|
||||
* SqlQuery find one with mapper.
|
||||
*/
|
||||
@@ -249,6 +247,16 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
|
||||
*/
|
||||
void findEachRow(SpiSqlQuery query, RowConsumer consumer);
|
||||
|
||||
/**
|
||||
* DTO findIterate query.
|
||||
*/
|
||||
<T> QueryIterator<T> findDtoIterate(SpiDtoQuery<T> query);
|
||||
|
||||
/**
|
||||
* DTO findStream query.
|
||||
*/
|
||||
<T> Stream<T> findDtoStream(SpiDtoQuery<T> query);
|
||||
|
||||
/**
|
||||
* DTO findList query.
|
||||
*/
|
||||
|
||||
@@ -105,4 +105,9 @@ public interface SpiExpression extends Expression {
|
||||
* Check for match to a natural key query returning false if it doesn't match.
|
||||
*/
|
||||
boolean naturalKey(NaturalKeyQueryData<?> data);
|
||||
|
||||
/**
|
||||
* Apply property prefix when filterMany expressions included into main query.
|
||||
*/
|
||||
void prefixProperty(String path);
|
||||
}
|
||||
|
||||
@@ -43,4 +43,9 @@ public interface SpiExpressionList<T> extends ExpressionList<T>, SpiExpression {
|
||||
default void applyRowLimits(SpiQuery<?> query) {
|
||||
// do nothing by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply property prefix when filterMany expressions included in main query.
|
||||
*/
|
||||
void prefixProperty(String path);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* SPI extension to PersistenceContext.
|
||||
*/
|
||||
public interface SpiPersistenceContext extends PersistenceContext {
|
||||
|
||||
/**
|
||||
* Return the list of dirty beans held by this persistence context.
|
||||
*/
|
||||
List<Object> dirtyBeans(SpiBeanTypeManager manager);
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.FetchConfig;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@@ -19,4 +20,8 @@ public interface SpiQueryFetch {
|
||||
*/
|
||||
void fetchProperties(String name, Set<String> properties, FetchConfig config);
|
||||
|
||||
/**
|
||||
* Add a nested fetch graph.
|
||||
*/
|
||||
void addNested(String name, OrmQueryDetail nestedDetail, FetchConfig config);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.ebeaninternal.api;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
@@ -147,6 +146,11 @@ public interface SpiTransaction extends Transaction {
|
||||
*/
|
||||
int depth();
|
||||
|
||||
/**
|
||||
* Return true if dirty beans are automatically persisted.
|
||||
*/
|
||||
boolean isAutoPersistUpdates();
|
||||
|
||||
/**
|
||||
* Return true if this transaction was created explicitly via
|
||||
* <code>Ebean.beginTransaction()</code>.
|
||||
@@ -191,7 +195,7 @@ public interface SpiTransaction extends Transaction {
|
||||
* later. This is along the lines of 'extended persistence context'
|
||||
* behaviour.
|
||||
*/
|
||||
PersistenceContext getPersistenceContext();
|
||||
SpiPersistenceContext getPersistenceContext();
|
||||
|
||||
/**
|
||||
* Set the persistence context to this transaction.
|
||||
@@ -203,7 +207,7 @@ public interface SpiTransaction extends Transaction {
|
||||
* and setPersistenceContext() enable a developer to reuse a single
|
||||
* PersistenceContext with multiple transactions.
|
||||
*/
|
||||
void setPersistenceContext(PersistenceContext context);
|
||||
void setPersistenceContext(SpiPersistenceContext context);
|
||||
|
||||
/**
|
||||
* Return the underlying Connection for internal use.
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.ebeaninternal.api;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.TransactionCallback;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
@@ -43,6 +42,16 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
return transaction.getLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
|
||||
transaction.setAutoPersistUpdates(autoPersistUpdates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoPersistUpdates() {
|
||||
return transaction.isAutoPersistUpdates();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commitAndContinue() {
|
||||
transaction.commitAndContinue();
|
||||
@@ -369,12 +378,12 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistenceContext getPersistenceContext() {
|
||||
public SpiPersistenceContext getPersistenceContext() {
|
||||
return transaction.getPersistenceContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPersistenceContext(PersistenceContext context) {
|
||||
public void setPersistenceContext(SpiPersistenceContext context) {
|
||||
transaction.setPersistenceContext(context);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiSqlBinding;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.lib.Str;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.server.util.Str;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
import io.ebeaninternal.server.persist.TrimLogSql;
|
||||
import io.ebeaninternal.server.util.BindParamsParser;
|
||||
@@ -83,11 +78,6 @@ public abstract class AbstractSqlQueryRequest {
|
||||
return transaction.isLogSql();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resultSet and associated query plan if known.
|
||||
*/
|
||||
abstract void setResultSet(ResultSet resultSet, Object queryPlanKey) throws SQLException;
|
||||
|
||||
/**
|
||||
* Return the bindLog for this request.
|
||||
*/
|
||||
@@ -95,12 +85,15 @@ public abstract class AbstractSqlQueryRequest {
|
||||
return bindLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resultSet and associated query plan if known.
|
||||
*/
|
||||
abstract void setResultSet(ResultSet resultSet, Object queryPlanKey) throws SQLException;
|
||||
|
||||
/**
|
||||
* Return true if we can navigate to the next row.
|
||||
*/
|
||||
public boolean next() throws SQLException {
|
||||
return resultSet.next();
|
||||
}
|
||||
public abstract boolean next() throws SQLException;
|
||||
|
||||
protected abstract void requestComplete();
|
||||
|
||||
@@ -113,7 +106,6 @@ public abstract class AbstractSqlQueryRequest {
|
||||
JdbcClose.close(pstmt);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare the SQL taking into account named bind parameters.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.config.QueryPlanCapture;
|
||||
import io.ebean.config.QueryPlanListener;
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class DefaultQueryPlanListener implements QueryPlanListener {
|
||||
|
||||
static final QueryPlanListener INSTANT = new DefaultQueryPlanListener();
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger("io.ebean.QUERYPLAN");
|
||||
|
||||
@Override
|
||||
public void process(QueryPlanCapture capture) {
|
||||
// better to log this in JSON form?
|
||||
String dbName = capture.getDatabase().getName();
|
||||
for (MetaQueryPlan plan : capture.getPlans()) {
|
||||
log.info("queryPlan db:{} label:{} queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}",
|
||||
dbName, plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(),
|
||||
plan.getSql(), plan.getBind(), plan.getPlan());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,12 +45,7 @@ import io.ebean.bean.PersistenceContext.WithOption;
|
||||
import io.ebean.bean.SingleBeanLoader;
|
||||
import io.ebean.cache.ServerCacheManager;
|
||||
import io.ebean.common.CopyOnFirstWriteList;
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.EncryptKeyManager;
|
||||
import io.ebean.config.SlowQueryEvent;
|
||||
import io.ebean.config.SlowQueryListener;
|
||||
import io.ebean.config.TenantMode;
|
||||
import io.ebean.config.*;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.event.BeanPersistController;
|
||||
import io.ebean.event.ShutdownManager;
|
||||
@@ -125,6 +120,7 @@ import io.ebeanservice.docstore.api.DocStoreIntegration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
@@ -137,7 +133,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -145,6 +140,7 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.Spliterator;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
@@ -196,7 +192,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
private final SpiLogManager logManager;
|
||||
private final PersistenceContextScope defaultPersistenceContextScope;
|
||||
private final int lazyLoadBatchSize;
|
||||
private final int queryBatchSize;
|
||||
private final boolean updateAllPropertiesInBatch;
|
||||
private final long slowQueryMicros;
|
||||
private final SlowQueryListener slowQueryListener;
|
||||
@@ -217,7 +212,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
this.extraMetrics = config.getExtraMetrics();
|
||||
this.serverName = this.config.getName();
|
||||
this.lazyLoadBatchSize = this.config.getLazyLoadBatchSize();
|
||||
this.queryBatchSize = this.config.getQueryBatchSize();
|
||||
this.cqueryEngine = config.getCQueryEngine();
|
||||
this.expressionFactory = config.getExpressionFactory();
|
||||
this.encryptKeyManager = this.config.getEncryptKeyManager();
|
||||
@@ -242,7 +236,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
this.clockService = config.getClockService();
|
||||
|
||||
DocStoreIntegration docStoreComponents = config.createDocStoreIntegration(this);
|
||||
this.transactionManager = config.createTransactionManager(docStoreComponents.updateProcessor());
|
||||
this.transactionManager = config.createTransactionManager(this, docStoreComponents.updateProcessor());
|
||||
this.documentStore = docStoreComponents.documentStore();
|
||||
this.queryPlanManager = config.initQueryPlanManager(transactionManager);
|
||||
this.metaInfoManager = new DefaultMetaInfoManager(this);
|
||||
@@ -305,10 +299,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return lazyLoadBatchSize;
|
||||
}
|
||||
|
||||
public int getQueryBatchSize() {
|
||||
return queryBatchSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object currentTenantId() {
|
||||
return currentTenantProvider == null ? null : currentTenantProvider.currentId();
|
||||
@@ -410,6 +400,31 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
migrationRunner.loadProperties(config.getProperties());
|
||||
migrationRunner.run(config.getDataSource());
|
||||
}
|
||||
startQueryPlanCapture();
|
||||
}
|
||||
|
||||
private void startQueryPlanCapture() {
|
||||
if (config.isQueryPlanCapture()) {
|
||||
long secs = config.getQueryPlanCapturePeriodSecs();
|
||||
if (secs > 10) {
|
||||
logger.info("capture query plan enabled, every {}secs", secs);
|
||||
backgroundExecutor.scheduleWithFixedDelay(this::collectQueryPlans, secs, secs, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void collectQueryPlans() {
|
||||
QueryPlanRequest request = new QueryPlanRequest();
|
||||
request.setMaxCount(config.getQueryPlanCaptureMaxCount());
|
||||
request.setMaxTimeMillis(config.getQueryPlanCaptureMaxTimeMillis());
|
||||
|
||||
// obtains query explain plans ...
|
||||
List<MetaQueryPlan> plans = metaInfoManager.queryPlanCollectNow(request);
|
||||
QueryPlanListener listener = config.getQueryPlanListener();
|
||||
if (listener == null) {
|
||||
listener = DefaultQueryPlanListener.INSTANT;
|
||||
}
|
||||
listener.process(new QueryPlanCapture(this, plans));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1150,6 +1165,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction) {
|
||||
return Optional.ofNullable(findOne(query, transaction));
|
||||
@@ -1180,6 +1196,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public <T> Set<T> findSet(Query<T> query, Transaction t) {
|
||||
@@ -1196,6 +1213,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public <K, T> Map<K, T> findMap(Query<T> query, Transaction t) {
|
||||
@@ -1217,6 +1235,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <A, T> List<A> findSingleAttributeList(Query<T> query, Transaction t) {
|
||||
@@ -1227,7 +1246,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return (List<A>) request.findSingleAttributeList();
|
||||
return request.findSingleAttributeList();
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1277,6 +1296,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <A, T> List<A> findIds(Query<T> query, Transaction t) {
|
||||
return findIdsWithCopy(((SpiQuery<T>) query).copy(), t);
|
||||
@@ -1337,26 +1357,29 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> FutureRowCount<T> findFutureCount(Query<T> q, Transaction t) {
|
||||
SpiQuery<T> copy = ((SpiQuery<T>) q).copy();
|
||||
copy.setFutureFetch(true);
|
||||
Transaction newTxn = createTransaction();
|
||||
QueryFutureRowCount<T> queryFuture = new QueryFutureRowCount<>(new CallableQueryCount<T>(this, copy, newTxn));
|
||||
QueryFutureRowCount<T> queryFuture = new QueryFutureRowCount<>(new CallableQueryCount<>(this, copy, newTxn));
|
||||
backgroundExecutor.execute(queryFuture.getFutureTask());
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction t) {
|
||||
SpiQuery<T> copy = ((SpiQuery<T>) query).copy();
|
||||
copy.setFutureFetch(true);
|
||||
Transaction newTxn = createTransaction();
|
||||
QueryFutureIds<T> queryFuture = new QueryFutureIds<>(new CallableQueryIds<T>(this, copy, newTxn));
|
||||
QueryFutureIds<T> queryFuture = new QueryFutureIds<>(new CallableQueryIds<>(this, copy, newTxn));
|
||||
backgroundExecutor.execute(queryFuture.getFutureTask());
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> FutureList<T> findFutureList(Query<T> query, Transaction t) {
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
@@ -1369,11 +1392,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
// Create a new transaction solely to execute the findList() at some future time
|
||||
Transaction newTxn = createTransaction();
|
||||
QueryFutureList<T> queryFuture = new QueryFutureList<>(new CallableQueryList<T>(this, spiQuery, newTxn));
|
||||
QueryFutureList<T> queryFuture = new QueryFutureList<>(new CallableQueryList<>(this, spiQuery, newTxn));
|
||||
backgroundExecutor.execute(queryFuture.getFutureTask());
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction) {
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
@@ -1387,6 +1411,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return new LimitOffsetPagedList<>(this, spiQuery);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> QueryIterator<T> findIterate(Query<T> query, Transaction t) {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
|
||||
@@ -1399,39 +1424,20 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> Stream<T> findLargeStream(Query<T> query, Transaction transaction) {
|
||||
return findStream(query, transaction);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> Stream<T> findStream(Query<T> query, Transaction transaction) {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, transaction);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return toStream(request.findIterate());
|
||||
} catch (RuntimeException ex) {
|
||||
request.endTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
return toStream(findIterate(query, transaction));
|
||||
}
|
||||
|
||||
private <T> Stream<T> toStream(QueryIterator<T> queryIterator) {
|
||||
return stream(spliteratorUnknownSize(queryIterator, Spliterator.ORDERED), false)
|
||||
.onClose(new QueryIteratorClose(queryIterator));
|
||||
}
|
||||
|
||||
private static class QueryIteratorClose implements Runnable {
|
||||
private final QueryIterator<?> iterator;
|
||||
|
||||
private QueryIteratorClose(QueryIterator<?> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
iterator.close();
|
||||
}
|
||||
return stream(spliteratorUnknownSize(queryIterator, Spliterator.ORDERED), false).onClose(queryIterator::close);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1470,6 +1476,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
// no try finally - findEachWhile guarantee's cleanup of the transaction if required
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction) {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, transaction);
|
||||
@@ -1481,6 +1488,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> List<T> findList(Query<T> query, Transaction t) {
|
||||
return findList(query, t, false);
|
||||
@@ -1540,6 +1548,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<SqlRow> findList(SqlQuery query, Transaction t) {
|
||||
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t);
|
||||
@@ -1576,6 +1585,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return executeSqlQuery((req) -> req.findOneMapper(mapper), query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void findSingleAttributeEach(SpiSqlQuery query, Class<T> cls, Consumer<T> consumer) {
|
||||
executeSqlQuery((req) -> req.findSingleAttributeEach(cls, consumer), query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findSingleAttributeList(SpiSqlQuery query, Class<T> cls) {
|
||||
return executeSqlQuery((req) -> req.findSingleAttributeList(cls), query);
|
||||
@@ -1619,6 +1633,23 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> QueryIterator<T> findDtoIterate(SpiDtoQuery<T> query) {
|
||||
DtoQueryRequest<T> request = new DtoQueryRequest<>(this, dtoQueryEngine, query);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findIterate();
|
||||
} catch (RuntimeException ex) {
|
||||
request.endTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Stream<T> findDtoStream(SpiDtoQuery<T> query) {
|
||||
return toStream(findDtoIterate(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findDtoList(SpiDtoQuery<T> query) {
|
||||
DtoQueryRequest<T> request = new DtoQueryRequest<>(this, dtoQueryEngine, query);
|
||||
@@ -2241,6 +2272,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return checkUniqueness(bean, null);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<Property> checkUniqueness(Object bean, Transaction transaction) {
|
||||
EntityBean entityBean = checkEntityBean(bean);
|
||||
@@ -2252,14 +2284,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
Object id = idProperty.getVal(entityBean);
|
||||
if (entityBean._ebean_getIntercept().isNew() && id != null) {
|
||||
// Primary Key is changeable only on new models - so skip check if we are not
|
||||
// new.
|
||||
// Primary Key is changeable only on new models - so skip check if we are not new
|
||||
Query<?> query = new DefaultOrmQuery<>(beanDesc, this, expressionFactory);
|
||||
query.setId(id);
|
||||
if (findCount(query, transaction) > 0) {
|
||||
Set<Property> ret = new HashSet<>();
|
||||
ret.add(idProperty);
|
||||
return ret;
|
||||
return Collections.singleton(idProperty);
|
||||
}
|
||||
}
|
||||
for (BeanProperty[] props : beanDesc.getUniqueProps()) {
|
||||
@@ -2322,6 +2351,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
List<MetaQueryPlan> queryPlanInit(QueryPlanInit initRequest) {
|
||||
if (initRequest.isAll()) {
|
||||
queryPlanManager.setDefaultThreshold(initRequest.getThresholdMicros());
|
||||
}
|
||||
return beanDescriptorManager.queryPlanInit(initRequest);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.QueryIterator;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebeaninternal.api.SpiDtoQuery;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
@@ -23,6 +24,9 @@ import java.util.function.Predicate;
|
||||
*/
|
||||
public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
|
||||
private static final String ENC_PREFIX = EncryptAlias.PREFIX;
|
||||
private static final String ENC_PREFIX_UPPER = EncryptAlias.PREFIX.toUpperCase();
|
||||
|
||||
private final SpiDtoQuery<T> query;
|
||||
|
||||
private final DtoQueryEngine queryEngine;
|
||||
@@ -87,6 +91,11 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
}
|
||||
}
|
||||
|
||||
public QueryIterator<T> findIterate() {
|
||||
flushJdbcBatchOnQuery();
|
||||
return queryEngine.findIterate(this);
|
||||
}
|
||||
|
||||
public void findEach(Consumer<T> consumer) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findEach(this, consumer);
|
||||
@@ -107,9 +116,12 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
return queryEngine.findList(this);
|
||||
}
|
||||
|
||||
public boolean next() throws SQLException {
|
||||
return dataReader.next();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T readNextBean() throws SQLException {
|
||||
dataReader.resetColumnPosition();
|
||||
return (T) plan.readRow(dataReader);
|
||||
}
|
||||
|
||||
@@ -133,9 +145,9 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
}
|
||||
|
||||
static String parseColumn(String columnLabel) {
|
||||
if (columnLabel.startsWith("_e_") || columnLabel.startsWith("_E_")) {
|
||||
if (columnLabel.startsWith(ENC_PREFIX) || columnLabel.startsWith(ENC_PREFIX_UPPER)) {
|
||||
// encrypted column alias in the form _e_<tableAlias>_<column>
|
||||
final int pos = columnLabel.indexOf("_", 3);
|
||||
final int pos = columnLabel.indexOf("_", 4);
|
||||
if (pos > -1) {
|
||||
return columnLabel.substring(pos + 1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
/**
|
||||
* Used to create column alias for encrypted columns.
|
||||
*/
|
||||
public interface EncryptAlias {
|
||||
String PREFIX = "zx__";
|
||||
}
|
||||
@@ -423,13 +423,13 @@ public class InternalConfiguration {
|
||||
/**
|
||||
* Create the TransactionManager taking into account autoCommit mode.
|
||||
*/
|
||||
TransactionManager createTransactionManager(DocStoreUpdateProcessor indexUpdateProcessor) {
|
||||
TransactionManager createTransactionManager(SpiServer server, DocStoreUpdateProcessor indexUpdateProcessor) {
|
||||
|
||||
TransactionScopeManager scopeManager = createTransactionScopeManager();
|
||||
boolean notifyL2CacheInForeground = cacheManager.isLocalL2Caching() || config.isNotifyL2CacheInForeground();
|
||||
|
||||
TransactionManagerOptions options =
|
||||
new TransactionManagerOptions(notifyL2CacheInForeground, config, scopeManager, clusterManager, backgroundExecutor,
|
||||
new TransactionManagerOptions(server, notifyL2CacheInForeground, config, scopeManager, clusterManager, backgroundExecutor,
|
||||
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler(), logManager,
|
||||
tableModState, cacheNotify, clockService);
|
||||
|
||||
@@ -617,10 +617,10 @@ public class InternalConfiguration {
|
||||
}
|
||||
|
||||
public QueryPlanManager initQueryPlanManager(TransactionManager transactionManager) {
|
||||
if (!config.isCollectQueryPlans()) {
|
||||
if (!config.isQueryPlanEnable()) {
|
||||
return QueryPlanManager.NOOP;
|
||||
}
|
||||
long threshold = config.getCollectQueryPlanThresholdMicros();
|
||||
long threshold = config.getQueryPlanThresholdMicros();
|
||||
return new CQueryPlanManager(transactionManager, threshold, queryPlanLogger(databasePlatform.getPlatform()), extraMetrics);
|
||||
}
|
||||
|
||||
|
||||
@@ -173,8 +173,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
* iteration is fine.
|
||||
* </p>
|
||||
*/
|
||||
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
|
||||
return loadContext.getSecondaryQueriesMinBatchSize(defaultQueryBatch);
|
||||
public int getSecondaryQueriesMinBatchSize() {
|
||||
return loadContext.getSecondaryQueriesMinBatchSize();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,10 +219,10 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
*/
|
||||
@Override
|
||||
public void prepareQuery() {
|
||||
secondaryQueries = query.convertJoins();
|
||||
beanDescriptor.prepareQuery(query);
|
||||
adapterPreQuery();
|
||||
this.secondaryQueries = query.convertJoins();
|
||||
this.queryPlanKey = query.prepare(this);
|
||||
queryPlanKey = query.prepare(this);
|
||||
}
|
||||
|
||||
public boolean isNativeSql() {
|
||||
|
||||
@@ -379,9 +379,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
public void setBatched() {
|
||||
batched = true;
|
||||
if (type == Type.INSERT || type == Type.UPDATE) {
|
||||
// used to trigger automatic jdbc batch flush
|
||||
intercept.registerGetterCallback(this);
|
||||
getterCallback = true;
|
||||
if (beanDescriptor.hasSingleIdProperty()) {
|
||||
// used to trigger automatic jdbc batch flush
|
||||
intercept.registerGetterCallback(this);
|
||||
getterCallback = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,6 +889,14 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove deleted beans from the persistence context early.
|
||||
*/
|
||||
public void removeFromPersistenceContext() {
|
||||
idValue = beanDescriptor.getId(entityBean);
|
||||
beanDescriptor.contextDeleted(transaction.getPersistenceContext(), idValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggressive L1 and L2 cache cleanup for deletes.
|
||||
*/
|
||||
@@ -1030,6 +1040,17 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
if (!publish) {
|
||||
beanDescriptor.setDraft(entityBean);
|
||||
}
|
||||
if (transaction.isAutoPersistUpdates() && idValue != null) {
|
||||
// with getGeneratedKeys off we will not have a idValue
|
||||
beanDescriptor.contextPut(transaction.getPersistenceContext(), idValue, entityBean);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if persist can be skipped on the reference only bean.
|
||||
*/
|
||||
public boolean isSkipReference() {
|
||||
return intercept.isReference() || (Flags.isRecurse(flags) && beanDescriptor.referenceIdPropertyOnly(intercept));
|
||||
}
|
||||
|
||||
public boolean isReference() {
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.ebeaninternal.server.core;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.lib.Str;
|
||||
import io.ebeaninternal.server.util.Str;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.persist.PersistExecute;
|
||||
import io.ebeaninternal.server.persist.TrimLogSql;
|
||||
|
||||
@@ -20,17 +20,22 @@ public interface RelationalQueryEngine {
|
||||
/**
|
||||
* Find a list of beans using relational query.
|
||||
*/
|
||||
List<SqlRow> findList(RelationalQueryRequest request);
|
||||
|
||||
/**
|
||||
* Find each query using relational query.
|
||||
*/
|
||||
void findEach(RelationalQueryRequest request, Consumer<SqlRow> consumer);
|
||||
<T> List<T> findList(RelationalQueryRequest request, RowReader<T> reader);
|
||||
|
||||
/**
|
||||
* Find each while query using relational query.
|
||||
*/
|
||||
void findEach(RelationalQueryRequest request, Predicate<SqlRow> consumer);
|
||||
<T> void findEach(RelationalQueryRequest request, RowReader<T> reader, Predicate<T> consumer);
|
||||
|
||||
/**
|
||||
* Find each via raw consumer.
|
||||
*/
|
||||
void findEach(RelationalQueryRequest request, RowConsumer mapper);
|
||||
|
||||
/**
|
||||
* Find one via mapper.
|
||||
*/
|
||||
<T> T findOne(RelationalQueryRequest request, RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* Find single attribute.
|
||||
@@ -43,19 +48,9 @@ public interface RelationalQueryEngine {
|
||||
<T> List<T> findSingleAttributeList(RelationalQueryRequest request, Class<T> cls);
|
||||
|
||||
/**
|
||||
* Find one via mapper.
|
||||
* Find single attribute streaming the result to a consumer.
|
||||
*/
|
||||
<T> T findOneMapper(RelationalQueryRequest request, RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* Find list via mapper.
|
||||
*/
|
||||
<T> List<T> findListMapper(RelationalQueryRequest request, RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* Find each via raw consumer.
|
||||
*/
|
||||
void findEachRow(RelationalQueryRequest request, RowConsumer mapper);
|
||||
<T> void findSingleAttributeEach(RelationalQueryRequest request, Class<T> cls, Consumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Collect SQL query execution statistics.
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.RowConsumer;
|
||||
import io.ebean.RowMapper;
|
||||
import io.ebean.SqlQuery;
|
||||
import io.ebean.SqlRow;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.*;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiSqlBinding;
|
||||
|
||||
@@ -57,18 +53,24 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
|
||||
boolean findEachRow(RowConsumer mapper) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findEachRow(this, mapper);
|
||||
queryEngine.findEach(this, mapper);
|
||||
return true;
|
||||
}
|
||||
|
||||
<T> List<T> findListMapper(RowMapper<T> mapper) {
|
||||
flushJdbcBatchOnQuery();
|
||||
return queryEngine.findListMapper(this, mapper);
|
||||
return queryEngine.findList(this, () -> mapper.map(resultSet, rows++));
|
||||
}
|
||||
|
||||
<T> T findOneMapper(RowMapper<T> mapper) {
|
||||
flushJdbcBatchOnQuery();
|
||||
return queryEngine.findOneMapper(this, mapper);
|
||||
return queryEngine.findOne(this, mapper);
|
||||
}
|
||||
|
||||
public <T> boolean findSingleAttributeEach(Class<T> cls, Consumer<T> consumer) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findSingleAttributeEach(this, cls, consumer);
|
||||
return true;
|
||||
}
|
||||
|
||||
public <T> List<T> findSingleAttributeList(Class<T> cls) {
|
||||
@@ -83,17 +85,17 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
|
||||
public void findEach(Consumer<SqlRow> consumer) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findEach(this, consumer);
|
||||
queryEngine.findEach(this, (resultSet, rowNum) -> consumer.accept(createNewRow()));
|
||||
}
|
||||
|
||||
public void findEachWhile(Predicate<SqlRow> consumer) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findEach(this, consumer);
|
||||
queryEngine.findEach(this, this::createNewRow, consumer);
|
||||
}
|
||||
|
||||
public List<SqlRow> findList() {
|
||||
flushJdbcBatchOnQuery();
|
||||
return queryEngine.findList(this);
|
||||
return queryEngine.findList(this, this::createNewRow);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,16 +137,14 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
public void incrementRows() {
|
||||
rows++;
|
||||
}
|
||||
|
||||
public <T> List<T> mapList(RowMapper<T> mapper) throws SQLException {
|
||||
List<T> list = new ArrayList<>();
|
||||
while (next()) {
|
||||
list.add(mapper.map(resultSet, rows++));
|
||||
@Override
|
||||
public boolean next() throws SQLException {
|
||||
if (!resultSet.next()) {
|
||||
return false;
|
||||
} else {
|
||||
rows++;
|
||||
return true;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public <T> T mapOne(RowMapper<T> mapper) throws SQLException {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Read a row building a result for that row.
|
||||
*/
|
||||
public interface RowReader<T> {
|
||||
|
||||
/**
|
||||
* Build and return a result for a row.
|
||||
*/
|
||||
T read() throws SQLException;
|
||||
}
|
||||
@@ -38,15 +38,7 @@ import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.ExpressionPath;
|
||||
import io.ebean.plugin.Property;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.BeanCacheResult;
|
||||
import io.ebeaninternal.api.CQueryPlanKey;
|
||||
import io.ebeaninternal.api.ConcurrencyMode;
|
||||
import io.ebeaninternal.api.LoadBeanContext;
|
||||
import io.ebeaninternal.api.LoadContext;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiUpdatePlan;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.api.TransactionEventTable.TableIUD;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
@@ -119,7 +111,7 @@ import static io.ebeaninternal.server.persist.DmlUtil.isNullOrZero;
|
||||
/**
|
||||
* Describes Beans including their deployment information.
|
||||
*/
|
||||
public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptor.class);
|
||||
|
||||
@@ -1755,6 +1747,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return idProperty != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return false for IdClass case with multiple @Id properties.
|
||||
*/
|
||||
public boolean hasSingleIdProperty() {
|
||||
return idPropertyIndex != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this type has a simple Id and the platform supports mutli-value binding.
|
||||
*/
|
||||
@@ -1880,7 +1879,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
if (refBean == null) {
|
||||
refBean = createReference(readOnly, false, id, pc);
|
||||
}
|
||||
return (EntityBean)refBean;
|
||||
return (EntityBean) refBean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2473,7 +2472,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Return the property path given the db table and column.
|
||||
*/
|
||||
public String findBeanPath(String schemaName, String tableName, String columnName) {
|
||||
if (matchBaseTable(schemaName, tableName)) {
|
||||
if (matchBaseTable(tableName)) {
|
||||
return columnPath.get(columnName);
|
||||
}
|
||||
BeanPropertyAssoc<?> assocProperty = tablePath.get(tableName);
|
||||
@@ -2489,10 +2488,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean matchBaseTable(String schemaName, String tableName) {
|
||||
boolean matchBaseTable(String tableName) {
|
||||
return tableName.isEmpty()
|
||||
|| baseTable.equalsIgnoreCase(tableName)
|
||||
|| baseTable.equalsIgnoreCase(schemaName + "." + tableName);
|
||||
|| baseTable.endsWith("." + tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2937,6 +2936,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToManyDirty(EntityBean bean) {
|
||||
final EntityBeanIntercept ebi = bean._ebean_getIntercept();
|
||||
for (BeanPropertyAssocMany<?> many : propertiesManySave) {
|
||||
if (ebi.isLoadedProperty(many.getPropertyIndex())) {
|
||||
final BeanCollection<?> value = (BeanCollection<?>) many.getValue(bean);
|
||||
if (value != null && value.hasModifications()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the bean is draftable and considered a 'live' instance.
|
||||
*/
|
||||
@@ -3134,12 +3147,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return ebi.isReference() || referenceIdPropertyOnly(ebi);
|
||||
}
|
||||
|
||||
boolean referenceIdPropertyOnly(EntityBeanIntercept ebi) {
|
||||
public boolean referenceIdPropertyOnly(EntityBeanIntercept ebi) {
|
||||
return idOnlyReference && ebi.hasIdOnly(idPropertyIndex);
|
||||
}
|
||||
|
||||
public boolean isIdLoaded(EntityBeanIntercept ebi) {
|
||||
return ebi.isLoadedProperty(idPropertyIndex);
|
||||
// assume id loaded for IdClass case with idPropertyIndex == -1
|
||||
return idPropertyIndex == -1 ? true : ebi.isLoadedProperty(idPropertyIndex);
|
||||
}
|
||||
|
||||
boolean hasIdValue(EntityBean bean) {
|
||||
@@ -3179,7 +3193,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
int propertyIndex = beanProperty.getPropertyIndex();
|
||||
if (!ebi.isDirtyProperty(propertyIndex) && ebi.isLoadedProperty(propertyIndex)) {
|
||||
Object value = beanProperty.getValue(ebi.getOwner());
|
||||
if (value == null || beanProperty.isDirtyValue(value)) {
|
||||
if (value != null && beanProperty.isDirtyValue(value)) {
|
||||
// mutable scalar value which is considered dirty so mark
|
||||
// it as such so that it is included in an update
|
||||
ebi.markPropertyAsChanged(propertyIndex);
|
||||
|
||||
@@ -26,9 +26,7 @@ import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanInit;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.util.AnnotationUtil;
|
||||
import io.ebeaninternal.api.ConcurrencyMode;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.TransactionEventTable;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import io.ebeaninternal.server.cache.SpiCacheManager;
|
||||
import io.ebeaninternal.server.core.InternString;
|
||||
@@ -88,11 +86,12 @@ import java.util.concurrent.TimeUnit;
|
||||
/**
|
||||
* Creates BeanDescriptors.
|
||||
*/
|
||||
public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
public class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTypeManager {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptorManager.class);
|
||||
|
||||
private static final BeanDescComparator beanDescComparator = new BeanDescComparator();
|
||||
public static final String JAVA_LANG_RECORD = "java.lang.Record";
|
||||
|
||||
private final ReadAnnotations readAnnotations;
|
||||
private final TransientProperties transientProperties;
|
||||
@@ -262,6 +261,11 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return descQueueMap.get(queueId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiBeanType getBeanType(Class<?> entityType) {
|
||||
return getBeanDescriptor(entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> BeanDescriptor<T> getBeanDescriptor(Class<T> entityType) {
|
||||
@@ -1418,14 +1422,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
*/
|
||||
private void checkInheritedClasses(Class<?> beanClass) {
|
||||
Class<?> superclass = beanClass.getSuperclass();
|
||||
if (Object.class.equals(superclass)) {
|
||||
if (Object.class.equals(superclass) || Model.class.equals(superclass) || JAVA_LANG_RECORD.equals(superclass.getName())) {
|
||||
// we got to the top of the inheritance
|
||||
return;
|
||||
}
|
||||
if (Model.class.equals(superclass)) {
|
||||
// top of the inheritance. Not enhancing Model at this stage
|
||||
return;
|
||||
}
|
||||
if (!EntityBean.class.isAssignableFrom(superclass)) {
|
||||
if (isMappedSuperWithNoProperties(superclass)) {
|
||||
// ok to stop and treat just the same as Object.class
|
||||
|
||||
@@ -18,6 +18,7 @@ import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.core.EncryptAlias;
|
||||
import io.ebeaninternal.server.core.InternString;
|
||||
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
|
||||
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedWhenCreated;
|
||||
@@ -55,8 +56,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static io.ebean.util.StringHelper.replace;
|
||||
|
||||
/**
|
||||
* Description of a property of a bean. Includes its deployment information such
|
||||
* as database column mapping information.
|
||||
@@ -65,6 +64,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanProperty.class);
|
||||
|
||||
private static final String ENC_PREFIX = " " + EncryptAlias.PREFIX;
|
||||
|
||||
/**
|
||||
* Flag to mark this is the id property.
|
||||
*/
|
||||
@@ -353,13 +354,13 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
}
|
||||
|
||||
private String tableAliasIntern(BeanDescriptor<?> descriptor, String s, boolean dbEncrypted, String dbColumn) {
|
||||
if (descriptor != null) {
|
||||
s = replace(s, "${ta}.", "${}");
|
||||
s = replace(s, "${ta}", "${}");
|
||||
if (s != null && descriptor != null) {
|
||||
s = s.replace("${ta}.", "${}");
|
||||
s = s.replace("${ta}", "${}");
|
||||
if (dbEncrypted) {
|
||||
s = dbEncryptFunction.getDecryptSql(s);
|
||||
String namedParam = ":encryptkey_" + descriptor.getBaseTable() + "___" + dbColumn;
|
||||
s = replace(s, "?", namedParam);
|
||||
s = s.replace("?", namedParam);
|
||||
}
|
||||
}
|
||||
return InternString.intern(s);
|
||||
@@ -531,7 +532,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
* Return the SQL for the column including decryption function and column alias.
|
||||
*/
|
||||
private String getDecryptSqlWithColumnAlias(String tableAlias) {
|
||||
return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()) + " _e_" + tableAlias + "_" + this.getDbColumn();
|
||||
return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()) + ENC_PREFIX + tableAlias + "_" + this.getDbColumn();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-3
@@ -81,18 +81,18 @@ class BeanPropertyAssocManySqlHelp<T> {
|
||||
|
||||
@Override
|
||||
public void visitOneImported(BeanPropertyAssocOne<?> p) {
|
||||
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitScalar(BeanProperty p) {
|
||||
public void visitScalar(BeanProperty p, boolean allowNonNull) {
|
||||
sb.append(",").append(p.getDbColumn());
|
||||
colCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-9
@@ -54,7 +54,6 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
|
||||
}
|
||||
|
||||
private void readAssocOne(DeployBeanPropertyAssocOne<?> prop) {
|
||||
|
||||
ManyToOne manyToOne = get(prop, ManyToOne.class);
|
||||
if (manyToOne != null) {
|
||||
readManyToOne(manyToOne, prop);
|
||||
@@ -183,7 +182,6 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
|
||||
}
|
||||
|
||||
private void readManyToOne(ManyToOne propAnn, DeployBeanPropertyAssocOne<?> beanProp) {
|
||||
|
||||
setCascadeTypes(propAnn.cascade(), beanProp.getCascadeInfo());
|
||||
setTargetType(propAnn.targetEntity(), beanProp);
|
||||
setBeanTable(beanProp);
|
||||
@@ -194,7 +192,6 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
|
||||
}
|
||||
|
||||
private void readOneToOne(OneToOne propAnn, DeployBeanPropertyAssocOne<?> prop) {
|
||||
|
||||
prop.setOneToOne();
|
||||
prop.setDbInsertable(true);
|
||||
prop.setDbUpdateable(true);
|
||||
@@ -223,21 +220,19 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
|
||||
}
|
||||
|
||||
private void readPrimaryKeyJoin(PrimaryKeyJoinColumn primaryKeyJoin, DeployBeanPropertyAssocOne<?> prop) {
|
||||
|
||||
if (!prop.isOneToOne()) {
|
||||
throw new IllegalStateException("Expecting property " + prop.getFullBeanName() + " with PrimaryKeyJoinColumn to be a OneToOne?");
|
||||
}
|
||||
prop.setPrimaryKeyJoin(true);
|
||||
|
||||
if (!primaryKeyJoin.name().isEmpty()) {
|
||||
log.warn("Automatically determining join columns and ignoring PrimaryKeyJoinColumn.name {} on {}", primaryKeyJoin.name(), prop.getFullBeanName());
|
||||
log.info("Automatically determining join columns for @PrimaryKeyJoinColumn - ignoring PrimaryKeyJoinColumn.name attribute [{}] on {}", primaryKeyJoin.name(), prop.getFullBeanName());
|
||||
}
|
||||
if (!primaryKeyJoin.referencedColumnName().isEmpty()) {
|
||||
log.warn("Automatically determining join columns and Ignoring PrimaryKeyJoinColumn.referencedColumnName {} on {}", primaryKeyJoin.referencedColumnName(), prop.getFullBeanName());
|
||||
log.info("Automatically determining join columns for @PrimaryKeyJoinColumn - Ignoring PrimaryKeyJoinColumn.referencedColumnName attribute [{}] on {}", primaryKeyJoin.referencedColumnName(), prop.getFullBeanName());
|
||||
}
|
||||
|
||||
BeanTable baseBeanTable = factory.getBeanTable(info.getDescriptor().getBeanType());
|
||||
|
||||
String localPrimaryKey = baseBeanTable.getIdColumn();
|
||||
String foreignColumn = getBeanTable(prop).getIdColumn();
|
||||
|
||||
@@ -245,7 +240,6 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
|
||||
}
|
||||
|
||||
private void readEmbedded(DeployBeanPropertyAssocOne<?> prop, Embedded embedded) {
|
||||
|
||||
if (descriptor.isDocStoreOnly() && prop.getDocStoreDoc() == null) {
|
||||
prop.setDocStoreEmbedded("");
|
||||
}
|
||||
@@ -257,7 +251,6 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
|
||||
} catch (NoSuchMethodError e) {
|
||||
// using standard JPA API without prefix option, maybe in EE container
|
||||
}
|
||||
|
||||
readEmbeddedAttributeOverrides(prop);
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -44,9 +44,11 @@ public abstract class BaseTablePropertyVisitor implements BeanPropertyVisitor {
|
||||
public abstract void visitOneImported(BeanPropertyAssocOne<?> p);
|
||||
|
||||
/**
|
||||
* Override this method for normal scalar property.
|
||||
* Override this method for normal scalar property. With allowNonNull
|
||||
* used with properties of Embedded beans that can only be Nonnull when
|
||||
* the Embedded bean is Nonnull.
|
||||
*/
|
||||
@Override
|
||||
public abstract void visitScalar(BeanProperty p);
|
||||
public abstract void visitScalar(BeanProperty p, boolean allowNonNull);
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -40,8 +40,8 @@ public interface BeanPropertyVisitor {
|
||||
void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded);
|
||||
|
||||
/**
|
||||
* Visit a scalar property.
|
||||
* Visit a scalar property specify allowing non-null (e.g. embedded bean scalar properties).
|
||||
*/
|
||||
void visitScalar(BeanProperty p);
|
||||
void visitScalar(BeanProperty p, boolean allowNonNull);
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ public class VisitProperties {
|
||||
|
||||
} else {
|
||||
// simple scalar type
|
||||
pv.visitScalar(p);
|
||||
pv.visitScalar(p, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,17 @@ import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
*/
|
||||
public abstract class AbstractExpression implements SpiExpression {
|
||||
|
||||
protected final String propName;
|
||||
protected String propName;
|
||||
|
||||
protected AbstractExpression(String propName) {
|
||||
this.propName = propName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prefixProperty(String path) {
|
||||
this.propName = path + "." + propName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean naturalKey(NaturalKeyQueryData<?> data) {
|
||||
// by default can't use naturalKey cache
|
||||
|
||||
+8
-2
@@ -17,8 +17,8 @@ class BetweenPropertyExpression extends NonPrepareExpression {
|
||||
|
||||
private static final String BETWEEN = " between ";
|
||||
|
||||
private final String lowProperty;
|
||||
private final String highProperty;
|
||||
private String lowProperty;
|
||||
private String highProperty;
|
||||
private final Object value;
|
||||
|
||||
BetweenPropertyExpression(String lowProperty, String highProperty, Object value) {
|
||||
@@ -27,6 +27,12 @@ class BetweenPropertyExpression extends NonPrepareExpression {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prefixProperty(String path) {
|
||||
this.lowProperty = path + "." + lowProperty;
|
||||
this.highProperty = path + "." + highProperty;
|
||||
}
|
||||
|
||||
protected String name(String propName) {
|
||||
return propName;
|
||||
}
|
||||
|
||||
+7
@@ -88,6 +88,13 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prefixProperty(String path) {
|
||||
for (SpiExpression exp : list) {
|
||||
exp.prefixProperty(path);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean naturalKey(NaturalKeyQueryData<?> data) {
|
||||
// can't use naturalKey cache
|
||||
|
||||
@@ -126,6 +126,13 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prefixProperty(String path) {
|
||||
for (SpiExpression exp : list) {
|
||||
exp.prefixProperty(path);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Junction<T> toJunction() {
|
||||
return new JunctionExpression<>(Junction.Type.FILTER, this);
|
||||
|
||||
@@ -36,6 +36,11 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress
|
||||
this.subQuery = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prefixProperty(String path) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean naturalKey(NaturalKeyQueryData<?> data) {
|
||||
// can't use naturalKey cache
|
||||
|
||||
@@ -19,6 +19,11 @@ class IdExpression extends NonPrepareExpression implements SpiExpression {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prefixProperty(String path) {
|
||||
throw new IllegalStateException("Not allowed?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDocQuery(DocQueryContext context) throws IOException {
|
||||
context.writeId(value);
|
||||
|
||||
@@ -66,6 +66,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
this.exprList = exprList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prefixProperty(String path) {
|
||||
exprList.prefixProperty(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean naturalKey(NaturalKeyQueryData<?> data) {
|
||||
// can't use naturalKey cache
|
||||
|
||||
@@ -57,6 +57,12 @@ abstract class LogicExpression implements SpiExpression {
|
||||
this.expTwo = (SpiExpression) expTwo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prefixProperty(String path) {
|
||||
expOne.prefixProperty(path);
|
||||
expTwo.prefixProperty(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean naturalKey(NaturalKeyQueryData<?> data) {
|
||||
// can't use naturalKey cache
|
||||
|
||||
+5
@@ -24,6 +24,11 @@ class NestedPathWrapperExpression implements SpiExpression {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prefixProperty(String path) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean naturalKey(NaturalKeyQueryData<?> data) {
|
||||
// can't use naturalKey cache
|
||||
|
||||
@@ -9,6 +9,11 @@ import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
*/
|
||||
abstract class NonPrepareExpression implements SpiExpression {
|
||||
|
||||
@Override
|
||||
public void prefixProperty(String path) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean naturalKey(NaturalKeyQueryData<?> data) {
|
||||
// can't use naturalKey cache
|
||||
|
||||
@@ -17,6 +17,11 @@ class NoopExpression implements SpiExpression {
|
||||
|
||||
protected static final NoopExpression INSTANCE = new NoopExpression();
|
||||
|
||||
@Override
|
||||
public void prefixProperty(String path) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean naturalKey(NaturalKeyQueryData<?> data) {
|
||||
// can't use naturalKey cache
|
||||
|
||||
@@ -22,6 +22,11 @@ final class NotExpression implements SpiExpression {
|
||||
this.exp = (SpiExpression) exp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prefixProperty(String path) {
|
||||
exp.prefixProperty(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean naturalKey(NaturalKeyQueryData<?> data) {
|
||||
// can't use naturalKey cache
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
|
||||
<TITLE>AvajeLib</TITLE>
|
||||
</HEAD>
|
||||
<Body BGCOLOR="#ffffff">
|
||||
Core services for an application.
|
||||
<P>
|
||||
Core services including Logging, Deployment properties and Background thread for
|
||||
running frequent tasks.
|
||||
</P>
|
||||
</Body>
|
||||
</HTML>
|
||||
@@ -33,7 +33,7 @@ abstract class DLoadBaseContext {
|
||||
|
||||
final boolean queryFetch;
|
||||
|
||||
DLoadBaseContext(DLoadContext parent, BeanDescriptor<?> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
|
||||
DLoadBaseContext(DLoadContext parent, BeanDescriptor<?> desc, String path, OrmQueryProperties queryProps) {
|
||||
this.parent = parent;
|
||||
this.serverName = parent.getEbeanServer().getName();
|
||||
this.desc = desc;
|
||||
@@ -42,11 +42,7 @@ abstract class DLoadBaseContext {
|
||||
this.hitCache = parent.isBeanCacheGet() && desc.isBeanCaching();
|
||||
this.objectGraphNode = parent.getObjectGraphNode(path);
|
||||
this.queryFetch = queryProps != null && queryProps.isQueryFetch();
|
||||
this.batchSize = initBatchSize(defaultBatchSize, queryProps);
|
||||
}
|
||||
|
||||
private int initBatchSize(int batchSize, OrmQueryProperties queryProps) {
|
||||
return queryProps == null ? batchSize : queryProps.getBatchSize();
|
||||
this.batchSize = parent.batchSize(queryProps);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,8 +31,8 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
|
||||
|
||||
private LoadBuffer currentBuffer;
|
||||
|
||||
DLoadBeanContext(DLoadContext parent, BeanDescriptor<?> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
|
||||
super(parent, desc, path, defaultBatchSize, queryProps);
|
||||
DLoadBeanContext(DLoadContext parent, BeanDescriptor<?> desc, String path, OrmQueryProperties queryProps) {
|
||||
super(parent, desc, path, queryProps);
|
||||
// bufferList only required when using query joins (queryFetch)
|
||||
this.bufferList = (!queryFetch) ? null : new ArrayList<>();
|
||||
this.currentBuffer = createBuffer(batchSize);
|
||||
|
||||
@@ -90,7 +90,7 @@ public class DLoadContext implements LoadContext {
|
||||
this.planLabel = null;
|
||||
this.profileLocation = null;
|
||||
this.profilingListener = null;
|
||||
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
|
||||
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, null);
|
||||
}
|
||||
|
||||
private ObjectGraphOrigin initOrigin() {
|
||||
@@ -128,7 +128,7 @@ public class DLoadContext implements LoadContext {
|
||||
}
|
||||
|
||||
// initialise rootBeanContext after origin and relativePath have been set
|
||||
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
|
||||
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, null);
|
||||
registerSecondaryQueries(secondaryQueries);
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ public class DLoadContext implements LoadContext {
|
||||
* Return the minimum batch size when using QueryIterator with query joins.
|
||||
*/
|
||||
@Override
|
||||
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
|
||||
public int getSecondaryQueriesMinBatchSize() {
|
||||
if (secQuery == null) {
|
||||
return -1;
|
||||
}
|
||||
@@ -190,7 +190,7 @@ public class DLoadContext implements LoadContext {
|
||||
for (OrmQueryProperties aSecQuery : secQuery) {
|
||||
int batchSize = aSecQuery.getBatchSize();
|
||||
if (batchSize == 0) {
|
||||
batchSize = defaultQueryBatch;
|
||||
batchSize = 100;
|
||||
}
|
||||
maxBatch = Math.max(maxBatch, batchSize);
|
||||
}
|
||||
@@ -287,51 +287,55 @@ public class DLoadContext implements LoadContext {
|
||||
getManyContext(path, many).register(bc);
|
||||
}
|
||||
|
||||
int batchSize(OrmQueryProperties props) {
|
||||
if (props == null) {
|
||||
return defaultBatchSize;
|
||||
}
|
||||
int batchSize = props.getBatchSize();
|
||||
return batchSize == 0 ? defaultBatchSize : batchSize;
|
||||
}
|
||||
|
||||
DLoadBeanContext getBeanContext(String path) {
|
||||
if (path == null) {
|
||||
return rootBeanContext;
|
||||
}
|
||||
return beanMap.computeIfAbsent(path, p -> createBeanContext(p, defaultBatchSize, null));
|
||||
return beanMap.computeIfAbsent(path, p -> createBeanContext(p, null));
|
||||
}
|
||||
|
||||
DLoadBeanContext getBeanContextWithInherit(String path, BeanPropertyAssocOne<?> property) {
|
||||
String key = path + ":" + property.getTargetDescriptor().getName();
|
||||
return beanMap.computeIfAbsent(key, p -> createBeanContext(property, path, defaultBatchSize, null));
|
||||
return beanMap.computeIfAbsent(key, p -> createBeanContext(property, path, null));
|
||||
}
|
||||
|
||||
private void registerSecondaryNode(boolean many, OrmQueryProperties props) {
|
||||
int batchSize = props.getBatchSize();
|
||||
if (batchSize == 0) {
|
||||
batchSize = defaultBatchSize;
|
||||
}
|
||||
String path = props.getPath();
|
||||
if (many) {
|
||||
manyMap.put(path, createManyContext(path, batchSize, props));
|
||||
manyMap.put(path, createManyContext(path, props));
|
||||
} else {
|
||||
beanMap.put(path, createBeanContext(path, batchSize, props));
|
||||
beanMap.put(path, createBeanContext(path, props));
|
||||
}
|
||||
}
|
||||
|
||||
DLoadManyContext getManyContext(String path, BeanPropertyAssocMany<?> many) {
|
||||
return manyMap.computeIfAbsent(path, p -> createManyContext(p, many, defaultBatchSize));
|
||||
return manyMap.computeIfAbsent(path, p -> createManyContext(p, many));
|
||||
}
|
||||
|
||||
private DLoadManyContext createManyContext(String path, BeanPropertyAssocMany<?> many, int batchSize) {
|
||||
return new DLoadManyContext(this, many, path, batchSize, null);
|
||||
private DLoadManyContext createManyContext(String path, BeanPropertyAssocMany<?> many) {
|
||||
return new DLoadManyContext(this, many, path, null);
|
||||
}
|
||||
|
||||
private DLoadManyContext createManyContext(String path, int batchSize, OrmQueryProperties queryProps) {
|
||||
private DLoadManyContext createManyContext(String path, OrmQueryProperties queryProps) {
|
||||
BeanPropertyAssocMany<?> p = (BeanPropertyAssocMany<?>) getBeanProperty(rootDescriptor, path);
|
||||
return new DLoadManyContext(this, p, path, batchSize, queryProps);
|
||||
return new DLoadManyContext(this, p, path, queryProps);
|
||||
}
|
||||
|
||||
private DLoadBeanContext createBeanContext(String path, int batchSize, OrmQueryProperties queryProps) {
|
||||
private DLoadBeanContext createBeanContext(String path, OrmQueryProperties queryProps) {
|
||||
BeanPropertyAssoc<?> p = (BeanPropertyAssoc<?>) getBeanProperty(rootDescriptor, path);
|
||||
return new DLoadBeanContext(this, p.getTargetDescriptor(), path, batchSize, queryProps);
|
||||
return new DLoadBeanContext(this, p.getTargetDescriptor(), path, queryProps);
|
||||
}
|
||||
|
||||
private DLoadBeanContext createBeanContext(BeanPropertyAssoc<?> property, String path, int batchSize, OrmQueryProperties queryProps) {
|
||||
return new DLoadBeanContext(this, property.getTargetDescriptor(), path, batchSize, queryProps);
|
||||
private DLoadBeanContext createBeanContext(BeanPropertyAssoc<?> property, String path, OrmQueryProperties queryProps) {
|
||||
return new DLoadBeanContext(this, property.getTargetDescriptor(), path, queryProps);
|
||||
}
|
||||
|
||||
private BeanProperty getBeanProperty(BeanDescriptor<?> desc, String path) {
|
||||
|
||||
@@ -31,11 +31,8 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
|
||||
|
||||
private LoadBuffer currentBuffer;
|
||||
|
||||
DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany<?> property,
|
||||
String path, int defaultBatchSize, OrmQueryProperties queryProps) {
|
||||
|
||||
super(parent, property.getBeanDescriptor(), path, defaultBatchSize, queryProps);
|
||||
|
||||
DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany<?> property, String path, OrmQueryProperties queryProps) {
|
||||
super(parent, property.getBeanDescriptor(), path, queryProps);
|
||||
this.property = property;
|
||||
this.docStoreMapped = property.isTargetDocStoreMapped();
|
||||
// bufferList only required when using query joins (queryFetch)
|
||||
@@ -204,7 +201,6 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
|
||||
|
||||
@Override
|
||||
public void loadMany(BeanCollection<?> bc, boolean onlyIds) {
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
boolean useCache = !onlyIds && context.hitCache && context.property.isUseCache();
|
||||
@@ -218,6 +214,7 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
|
||||
// find it using instance equality - avoiding equals() and potential deadlock issue
|
||||
if (list.get(i) == bc) {
|
||||
list.remove(i);
|
||||
bc.setLoader(context.parent.getEbeanServer());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -225,10 +222,9 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
|
||||
}
|
||||
}
|
||||
|
||||
// Should reduce the list by checking each beanCollection in the L2 first before executing the query
|
||||
|
||||
LoadManyRequest req = new LoadManyRequest(this, onlyIds, useCache);
|
||||
context.parent.getEbeanServer().loadMany(req);
|
||||
context.parent.getEbeanServer().loadMany(new LoadManyRequest(this, onlyIds, useCache));
|
||||
// clear the buffer as all entries have been loaded
|
||||
list.clear();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ public final class BatchControl {
|
||||
*/
|
||||
private int bufferMax;
|
||||
|
||||
private Queue[] queues = new Queue[3];
|
||||
private final Queue[] queues = new Queue[3];
|
||||
|
||||
/**
|
||||
* Create for a given transaction, PersistExecute, default size and getGeneratedKeys.
|
||||
|
||||
@@ -447,7 +447,7 @@ public final class DefaultPersister implements Persister {
|
||||
public void insert(EntityBean bean, Transaction t) {
|
||||
|
||||
PersistRequestBean<?> req = createRequest(bean, t, PersistRequest.Type.INSERT);
|
||||
if (req.isReference()) {
|
||||
if (req.isSkipReference()) {
|
||||
// skip insert on reference bean
|
||||
return;
|
||||
}
|
||||
@@ -881,6 +881,7 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
|
||||
int count = request.executeOrQueue();
|
||||
request.removeFromPersistenceContext();
|
||||
|
||||
if (request.isPersistCascade()) {
|
||||
deleteAssocOne(request);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user