Compare commits

..
Author SHA1 Message Date
rob bygrave 08d241d946 #1966 Remove sequence batching - postgres opps 2020-03-05 00:34:05 +13:00
rob bygrave 1c24ebf6dc Remove Sequence batching in favour of JPA increment 50
Also remove h2sqldb in favour of always using H2
2020-03-05 00:22:20 +13:00
179 changed files with 2656 additions and 5959 deletions
+11 -11
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>12.3.1</version>
<version>12.1.14-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -22,7 +22,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-12.3.1</tag>
<tag>HEAD</tag>
</scm>
<profiles>
@@ -64,7 +64,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>config</artifactId>
<version>1.2</version>
<version>1.0</version>
</dependency>
<!--
@@ -72,22 +72,22 @@
to assist with IDE auto-completion with Ebean API
-->
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-jsr305</artifactId>
<version>1.0</version>
<groupId>io.ebean</groupId>
<artifactId>ebean-jsr305</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>persistence-api</artifactId>
<version>2.2.4</version>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>6.11</version>
<version>6.9</version>
</dependency>
<dependency>
@@ -99,7 +99,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-datasource</artifactId>
<version>4.9.1</version>
<version>4.7.3</version>
</dependency>
<dependency>
@@ -200,7 +200,7 @@
<dependency>
<groupId>io.ebean.test</groupId>
<artifactId>ebean-test-docker</artifactId>
<version>3.1.5</version>
<version>3.1.3</version>
<scope>test</scope>
</dependency>
@@ -292,7 +292,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>12.2.4</version>
<version>12.1.12</version>
<executions>
<execution>
<id>test</id>
-16
View File
@@ -101,22 +101,6 @@ public class DB {
// return originalPrimaryServer;
// }
/**
* Return the ScriptRunner for the default database.
* <p>
* Useful to run SQL scripts that are resources. For example a test script
* for inserting seed data for a particular test.
*
* <pre>{@code
*
* DB.script().run("/scripts/test-script.sql")
*
* }</pre>
*/
public static ScriptRunner script() {
return getDefault().script();
}
/**
* Return the ExpressionFactory from the default database.
* <p>
+6 -38
View File
@@ -313,7 +313,7 @@ public interface Transaction extends AutoCloseable {
* <h3>getGeneratedKeys</h3>
* <p>
* Often with large batch inserts we want to turn off getGeneratedKeys. We do
* this via {@link #setGetGeneratedKeys(boolean)}.
* this via {@link #setBatchGetGeneratedKeys(boolean)}.
* Also note that some JDBC drivers do not support getGeneratedKeys in JDBC batch mode.
* </p>
* <pre>{@code
@@ -342,7 +342,7 @@ public interface Transaction extends AutoCloseable {
* </p>
* <p>
* We use {@link #flush()} to explicitly flush the batch and we can use
* {@link #setFlushOnQuery(boolean)} and {@link #setFlushOnMixed(boolean)}
* {@link #setBatchFlushOnQuery(boolean)} and {@link #setBatchFlushOnMixed(boolean)}
* to control the automatic flushing behaviour.
* </p>
* <p>
@@ -432,15 +432,7 @@ public interface Transaction extends AutoCloseable {
* number of objects and you don't care about getting back the ids.
* </p>
*/
void setGetGeneratedKeys(boolean getGeneratedKeys);
/**
* Deprecated renamed to setGetGeneratedKeys().
*/
@Deprecated
default void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
setGetGeneratedKeys(getGeneratedKeys);
}
void setBatchGetGeneratedKeys(boolean getGeneratedKeys);
/**
* By default when mixing UpdateSql (or CallableSql) with Beans the batch is
@@ -457,15 +449,7 @@ public interface Transaction extends AutoCloseable {
* have a 2 step process (delayed binding).
* </p>
*/
void setFlushOnMixed(boolean batchFlushOnMixed);
/**
* Deprecated renamed to setFlushOnMixed().
*/
@Deprecated
default void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
setFlushOnMixed(batchFlushOnMixed);
}
void setBatchFlushOnMixed(boolean batchFlushOnMixed);
/**
* By default executing a query will automatically flush any batched
@@ -475,15 +459,7 @@ public interface Transaction extends AutoCloseable {
* execute a query and the batch will not be automatically flushed.
* </p>
*/
void setFlushOnQuery(boolean batchFlushOnQuery);
/**
* Deprecated renamed to setFlushOnQuery().
*/
@Deprecated
default void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
setFlushOnQuery(batchFlushOnQuery);
}
void setBatchFlushOnQuery(boolean batchFlushOnQuery);
/**
* Return true if the batch (of persisted beans or executed UpdateSql etc)
@@ -492,15 +468,7 @@ public interface Transaction extends AutoCloseable {
* The default is for this to be true.
* </p>
*/
boolean isFlushOnQuery();
/**
* Deprecated renamed to isFlushOnQuery().
*/
@Deprecated
default boolean isBatchFlushOnQuery() {
return isFlushOnQuery();
}
boolean isBatchFlushOnQuery();
/**
* The batch will be flushing automatically but you can use this to explicitly
@@ -40,8 +40,6 @@ public final class EntityBeanIntercept implements Serializable {
private String ebeanServerName;
private boolean deletedFromCollection;
/**
* The actual entity bean that 'owns' this intercept.
*/
@@ -915,7 +913,6 @@ public final class EntityBeanIntercept implements Serializable {
}
private void preGetterCallback(int propertyIndex) {
PreGetterCallback preGetterCallback = this.preGetterCallback;
if (preGetterCallback != null) {
preGetterCallback.preGetterTrigger(propertyIndex);
}
@@ -1140,17 +1137,6 @@ public final class EntityBeanIntercept implements Serializable {
this.sortOrder = sortOrder;
}
/**
* Set if the entity was deleted from a BeanCollection.
*/
public void setDeletedFromCollection(final boolean deletedFromCollection) {
this.deletedFromCollection = deletedFromCollection;
}
public boolean isOrphanDelete() {
return deletedFromCollection && !isNew();
}
/**
* Set the load error that happened on this property.
*/
@@ -1,7 +1,5 @@
package io.ebean.common;
import io.ebean.bean.EntityBean;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashSet;
@@ -56,11 +54,6 @@ class ModifyHolder<E> implements Serializable {
void modifyAddition(E bean) {
if (bean != null) {
touched = true;
if (bean instanceof EntityBean) {
((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(false);
}
// If it is to delete then just remove the deletion
if (!undoDeletion(bean)) {
// Insert
@@ -77,11 +70,6 @@ class ModifyHolder<E> implements Serializable {
void modifyRemoval(Object bean) {
if (bean != null) {
touched = true;
if (bean instanceof EntityBean) {
((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(true);
}
// If it is to be added then just remove the addition
if (!undoAddition(bean)) {
modifyDeletions.add((E) bean);
@@ -222,8 +222,8 @@ public abstract class AbstractNamingConvention implements NamingConvention {
* Return true if this class is part of entity inheritance.
*/
protected boolean hasInheritance(Class<?> supCls) {
return AnnotationUtil.typeHas(supCls, Inheritance.class)
|| AnnotationUtil.has(supCls, DiscriminatorValue.class);
return AnnotationUtil.findAnnotationRecursive(supCls, Inheritance.class) != null
|| AnnotationUtil.findAnnotation(supCls, DiscriminatorValue.class) != null;
}
@@ -255,12 +255,16 @@ public abstract class AbstractNamingConvention implements NamingConvention {
* Gets the table name from annotation.
*/
protected TableName getTableNameFromAnnotation(Class<?> beanClass) {
final Table table = AnnotationUtil.typeGet(beanClass, Table.class);
if (table != null && !isEmpty(table.name())) {
final Table t = AnnotationUtil.findAnnotationRecursive(beanClass, Table.class);
// Take the annotation if defined
if (t != null && !isEmpty(t.name())) {
// Note: empty catalog and schema are converted to null
// Only need to convert quoted identifiers from annotations
return new TableName(quoteIdentifiers(table.catalog()), quoteIdentifiers(table.schema()), quoteIdentifiers(table.name()));
return new TableName(quoteIdentifiers(t.catalog()), quoteIdentifiers(t.schema()), quoteIdentifiers(t.name()));
}
// No annotation
return null;
}
@@ -34,11 +34,6 @@ public class PlatformConfig {
*/
private String databaseBooleanFalse;
/**
* For DB's using sequences this is the number of sequence values prefetched.
*/
private int databaseSequenceBatchSize = 20;
/**
* Set for DB's that support both Sequence and Identity (and the default choice is not desired).
*/
@@ -79,7 +74,6 @@ public class PlatformConfig {
public PlatformConfig(PlatformConfig platformConfig) {
this.databaseBooleanFalse = platformConfig.databaseBooleanFalse;
this.databaseBooleanTrue = platformConfig.databaseBooleanTrue;
this.databaseSequenceBatchSize = platformConfig.databaseSequenceBatchSize;
this.idType = platformConfig.idType;
this.geometrySRID = platformConfig.geometrySRID;
this.dbUuid = platformConfig.dbUuid;
@@ -173,20 +167,6 @@ public class PlatformConfig {
this.databaseBooleanFalse = databaseBooleanFalse;
}
/**
* Return the number of DB sequence values that should be preallocated.
*/
public int getDatabaseSequenceBatchSize() {
return databaseSequenceBatchSize;
}
/**
* Set the number of DB sequence values that should be preallocated.
*/
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
this.databaseSequenceBatchSize = databaseSequenceBatchSize;
}
/**
* Return the Geometry SRID.
*/
@@ -294,7 +274,6 @@ public class PlatformConfig {
public void loadSettings(PropertiesWrapper p) {
idType = p.getEnum(IdType.class, "idType", idType);
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
databaseInetAddressVarchar = p.getBoolean("databaseInetAddressVarchar", databaseInetAddressVarchar);
+52 -71
View File
@@ -139,7 +139,7 @@ public class ServerConfig {
* Having this on and registering entity classes means we don't need to manually
* write that code or use classpath scanning to find entity classes.
*/
private boolean loadModuleInfo = true;
private boolean loadModuleInfo;
/**
* List of interesting classes such as entities, embedded, ScalarTypes,
@@ -331,6 +331,18 @@ public class ServerConfig {
*/
private ClassLoadConfig classLoadConfig = new ClassLoadConfig();
/**
* Set to true if the DataSource uses autoCommit.
* <p>
* Indicates that Ebean should use autoCommit friendly Transactions and TransactionManager.
*/
private boolean autoCommitMode;
/**
* Set to true if transaction begin should be started with explicit statement.
*/
private boolean explicitTransactionBeginMode;
/**
* The data source JNDI name if using a JNDI DataSource.
*/
@@ -463,11 +475,6 @@ public class ServerConfig {
private String enabledL2Regions;
/**
* Set to true to effectively disable L2 cache plugins.
*/
private boolean localOnlyL2Cache;
/**
* Should the javax.validation.constraints.NotNull enforce a notNull column in DB.
* If set to false, use io.ebean.annotation.NotNull or Column(nullable=true).
@@ -1043,18 +1050,6 @@ public class ServerConfig {
this.lazyLoadBatchSize = lazyLoadBatchSize;
}
/**
* Set the number of sequences to fetch/preallocate when using DB sequences.
* <p>
* 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).
* </p>
*/
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
}
/**
* Return the default JDBC fetchSize hint for findList queries.
*/
@@ -1733,6 +1728,42 @@ public class ServerConfig {
this.dataSourceJndiName = dataSourceJndiName;
}
/**
* Return true if autoCommit mode is on. This indicates to Ebean to use autoCommit friendly Transactions and TransactionManager.
*/
public boolean isAutoCommitMode() {
return autoCommitMode;
}
/**
* Set to true if autoCommit mode is on and Ebean should use autoCommit friendly Transactions and TransactionManager.
*/
public void setAutoCommitMode(boolean autoCommitMode) {
this.autoCommitMode = autoCommitMode;
}
/**
* Return true if transaction begin should be started with explicit statement.
*/
public boolean isExplicitTransactionBeginMode() {
return explicitTransactionBeginMode;
}
/**
* Set to true if transaction begin should be started with explicit statement.
* <p>
* This works for H2 and Postgres but not for Oracle - only use this if you first name
* is Daryl or you have explicitly talked to Rob about this feature.
* </p>
* <p>
* This is generally not expected to be turned on but instead allow transactions to start
* implicitly which is generally the standard approach.
* </p>
*/
public void setExplicitTransactionBeginMode(boolean explicitTransactionBeginMode) {
this.explicitTransactionBeginMode = explicitTransactionBeginMode;
}
/**
* Return a value used to represent TRUE in the database.
* <p>
@@ -1785,32 +1816,6 @@ public class ServerConfig {
this.platformConfig.setDatabaseBooleanFalse(databaseFalse);
}
/**
* Return the number of DB sequence values that should be preallocated.
*/
public int getDatabaseSequenceBatchSize() {
return platformConfig.getDatabaseSequenceBatchSize();
}
/**
* Set the number of DB sequence values that should be preallocated and cached
* by Ebean.
* <p>
* This is only used for DB's that use sequences and is a performance
* optimisation. This reduces the number of times Ebean needs to get a
* sequence value from the Database reducing network chatter.
* </p>
* <p>
* By default this value is 10 so when we need another Id (and don't have one
* in our cache) Ebean will fetch 10 id's from the database. Note that when
* the cache drops to have full (which is 5 by default) Ebean will fetch
* another batch of Id's in a background thread.
* </p>
*/
public void setDatabaseSequenceBatch(int databaseSequenceBatchSize) {
this.platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
}
/**
* Return the database platform name (can be null).
* <p>
@@ -2756,7 +2761,7 @@ public class ServerConfig {
*/
protected void loadDataSourceSettings(PropertiesWrapper p) {
dataSourceConfig.loadSettings(p.properties, name);
readOnlyDataSourceConfig.loadSettings(p.properties, name + "-ro");
readOnlyDataSourceConfig.loadSettings(p.properties, name);
}
/**
@@ -2817,9 +2822,10 @@ public class ServerConfig {
collectQueryPlanThresholdMicros = p.getLong("collectQueryPlanThresholdMicros", collectQueryPlanThresholdMicros);
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
localOnlyL2Cache = p.getBoolean("localOnlyL2Cache", localOnlyL2Cache);
enabledL2Regions = p.get("enabledL2Regions", enabledL2Regions);
notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground);
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
useJavaxValidationNotNull = p.getBoolean("useJavaxValidationNotNull", useJavaxValidationNotNull);
autoReadOnlyDataSource = p.getBoolean("autoReadOnlyDataSource", autoReadOnlyDataSource);
@@ -3056,20 +3062,6 @@ public class ServerConfig {
this.disableL2Cache = disableL2Cache;
}
/**
* Return true to use local only L2 cache. Effectively ignore l2 cache plugin like ebean-redis etc.
*/
public boolean isLocalOnlyL2Cache() {
return localOnlyL2Cache;
}
/**
* Force the use of local only L2 cache. Effectively ignore l2 cache plugin like ebean-redis etc.
*/
public void setLocalOnlyL2Cache(boolean localOnlyL2Cache) {
this.localOnlyL2Cache = localOnlyL2Cache;
}
/**
* Returns if we use javax.validation.constraints.NotNull
*/
@@ -3268,17 +3260,6 @@ public class ServerConfig {
return loadModuleInfo && classes.isEmpty();
}
/**
* Set false to turn off automatic registration of entity beans.
* <p>
* When using query beans that also generates a module info class that
* can register the entity bean classes (to avoid classpath scanning).
* This is on by default and setting this to false turns it off.
*/
public void setLoadModuleInfo(boolean loadModuleInfo) {
this.loadModuleInfo = loadModuleInfo;
}
public enum UuidVersion {
VERSION4,
VERSION1,
@@ -110,10 +110,6 @@ public class DatabasePlatform {
*/
protected DbIdentity dbIdentity = new DbIdentity();
protected boolean sequenceBatchMode = true;
protected int sequenceBatchSize = 20;
/**
* The history support for this database platform.
*/
@@ -146,7 +142,7 @@ public class DatabasePlatform {
protected String truncateTable = "truncate table %s";
protected String columnAliasPrefix;
protected String columnAliasPrefix = "c";
/**
* Use a BackTick ` at the beginning and end of table or column names that you
@@ -235,7 +231,6 @@ public class DatabasePlatform {
* Configure the platform given the server configuration.
*/
public void configure(PlatformConfig config) {
this.sequenceBatchSize = config.getDatabaseSequenceBatchSize();
this.caseSensitiveCollation = config.isCaseSensitiveCollation();
configureIdType(config.getIdType());
configure(config, config.isAllQuotedIdentifiers());
@@ -298,20 +293,6 @@ public class DatabasePlatform {
return platform.name().toLowerCase();
}
/**
* Return true if we are using Sequence batch mode rather than STEP.
*/
public boolean isSequenceBatchMode() {
return sequenceBatchMode;
}
/**
* Set to false to not use sequence batch mode but instead STEP mode.
*/
public void setSequenceBatchMode(boolean sequenceBatchMode) {
this.sequenceBatchMode = sequenceBatchMode;
}
/**
* Return true if this database platform supports native ILIKE expression.
*/
@@ -381,13 +362,13 @@ public class DatabasePlatform {
/**
* Return a DB Sequence based IdGenerator.
*
* @param be the BackgroundExecutor that can be used to load the sequence if
* desired
* @param ds the DataSource
* @param stepSize the sequence allocation size as defined by mapping (defaults to 50)
* @param seqName the name of the sequence
* @param be the BackgroundExecutor that can be used to load the sequence if
* desired
* @param ds the DataSource
* @param increment the sequence allocation size as defined by mapping (defaults to 50)
* @param seqName the name of the sequence
*/
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return null;
}
@@ -116,7 +116,7 @@ public class DbPlatformTypeMapping {
} else {
put(DbType.VARCHAR, new DbPlatformType("varchar", 255));
put(DbType.DECIMAL, new DbPlatformType("decimal", 16, 3));
put(DbType.DECIMAL, new DbPlatformType("decimal", 38));
put(DbType.VARBINARY, new DbPlatformType("varbinary", 255));
put(DbType.BINARY, new DbPlatformType("binary", 255));
put(DbType.CHAR, new DbPlatformType("char", 1));
@@ -1,57 +0,0 @@
package io.ebean.config.dbplatform;
import io.ebean.BackgroundExecutor;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* Database sequence based IdGenerator using Sequence Step 1 but batch fetch many sequence values.
*/
public abstract class SequenceBatchIdGenerator extends SequenceIdGenerator {
/**
* Construct where batchSize is the sequence step size.
*
*/
public SequenceBatchIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
}
/**
* If allocateSize is large load some sequences in a background thread.
* <p>
* For example, when inserting a bean with a cascade on a OneToMany with many
* beans Ebean can call this to ensure .
* </p>
*/
@Override
public void preAllocateIds(int requestSize) {
if (allocationSize > 1 && requestSize > allocationSize) {
// only bother if allocateSize is bigger than
// the normal loading batchSize
if (requestSize > 100) {
// max out at 100 for now
requestSize = 100;
}
loadInBackground(requestSize);
}
}
/**
* Add the next set of Ids as the next value plus all the following numbers up to the step size.
*/
@Override
protected List<Long> readIds(ResultSet resultSet, int loadSize) throws SQLException {
List<Long> newIds = new ArrayList<>(loadSize);
while (resultSet.next()) {
newIds.add(resultSet.getLong(1));
}
return newIds;
}
}
@@ -59,7 +59,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
this.allocationSize = allocationSize;
}
public abstract String getSql(int batchSize);
public abstract String getSql();
/**
* Returns the sequence name.
@@ -102,7 +102,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
if (size > 0) {
maybeLoadMoreInBackground(size);
} else {
loadMore(allocationSize);
loadMore();
}
return idList.pollFirst();
}
@@ -116,8 +116,8 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
}
}
private void loadMore(int requestSize) {
List<Long> newIds = getMoreIds(requestSize);
private void loadMore() {
List<Long> newIds = getMoreIds();
synchronized (monitor) {
idList.addAll(newIds);
}
@@ -139,7 +139,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
currentlyBackgroundLoading = true;
backgroundExecutor.execute(() -> {
loadMore(requestSize);
loadMore();
synchronized (backgroundLoadMonitor) {
currentlyBackgroundLoading = false;
}
@@ -150,14 +150,14 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
/**
* Read the resultSet returning the list of Id values.
*/
protected abstract List<Long> readIds(ResultSet resultSet, int loadSize) throws SQLException;
protected abstract List<Long> readIds(ResultSet resultSet) throws SQLException;
/**
* Get more Id's by executing a query and reading the Id's returned.
*/
protected List<Long> getMoreIds(int requestSize) {
protected List<Long> getMoreIds() {
String sql = getSql(requestSize);
String sql = getSql();
Connection connection = null;
PreparedStatement statement = null;
@@ -168,7 +168,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
List<Long> newIds = readIds(resultSet, requestSize);
List<Long> newIds = readIds(resultSet);
if (logger.isTraceEnabled()) {
logger.trace("seq:{} loaded:{} sql:{}", seqName, newIds.size(), sql);
}
@@ -13,19 +13,25 @@ import java.util.List;
*/
public abstract class SequenceStepIdGenerator extends SequenceIdGenerator {
private final String nextSql;
/**
* Construct with stepSize (typically 50).
*/
protected SequenceStepIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
protected SequenceStepIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int stepSize, String nextSql) {
super(be, ds, seqName, stepSize);
this.nextSql = nextSql;
}
@Override
public String getSql() {
return nextSql;
}
/**
* Add the next set of Ids as the next value plus all the following numbers up to the step size.
*/
@Override
protected List<Long> readIds(ResultSet resultSet, int ignoreRequestSize) throws SQLException {
protected List<Long> readIds(ResultSet resultSet) throws SQLException {
List<Long> newIds = new ArrayList<>(allocationSize);
if (resultSet.next()) {
long start = resultSet.getLong(1);
@@ -16,8 +16,10 @@ public class ClickHousePlatform extends DatabasePlatform {
//this.dbEncrypt =
//this.historySupport =
//this.exceptionTranslator =
this.nativeUuidType = true;
this.dbDefaultValue.setNow("now()");
this.columnAliasPrefix = null;
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(false);
@@ -32,7 +34,7 @@ public class ClickHousePlatform extends DatabasePlatform {
dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("UInt16", false));
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("UInt32", false));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("UInt64", false));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("Decimal", 16, 3));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("Decimal", 16,4));
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("Float64", false));
dbTypeMap.put(DbType.DATE, new DbPlatformType("Date", false));
@@ -42,7 +42,8 @@ public class DB2Platform extends DatabasePlatform {
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("bigint", false));
dbTypeMap.put(DbType.REAL, new DbPlatformType("real"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 16, 3));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 15));
persistBatchOnCascade = PersistBatch.NONE;
}
@@ -51,9 +52,8 @@ public class DB2Platform extends DatabasePlatform {
* sequence values.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new DB2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new DB2Sequence(be, ds, seqName, increment);
}
}
@@ -0,0 +1,17 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
/**
* DB2 specific sequence Id Generator.
*/
class DB2Sequence extends SequenceStepIdGenerator {
DB2Sequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "values nextval for " + seqName);
}
}
@@ -1,35 +0,0 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* DB2 specific sequence Id Generator.
*/
public class DB2SequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
private final String unionBaseSql;
/**
* Construct given a dataSource and sql to return the next sequence value.
*/
public DB2SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
this.baseSql = "values nextval for " + seqName;
this.unionBaseSql = " union " + baseSql;
}
@Override
public String getSql(int batchSize) {
StringBuilder sb = new StringBuilder();
sb.append(baseSql);
for (int i = 1; i < batchSize; i++) {
sb.append(unionBaseSql);
}
return sb.toString();
}
}
@@ -0,0 +1,14 @@
package io.ebean.config.dbplatform.h2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
class H2DbSequence extends SequenceStepIdGenerator {
H2DbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select " + seqName + ".nextval");
}
}
@@ -26,6 +26,7 @@ public class H2Platform extends DatabasePlatform {
this.supportsDeleteTableAlias = true;
this.inlineSqlUpdateLimit = true;
this.dbDefaultValue.setNow("now()");
this.columnAliasPrefix = null;
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("50200","HYT00")
@@ -41,13 +42,9 @@ public class H2Platform extends DatabasePlatform {
dbTypeMap.put(DbType.UUID, new DbPlatformType("uuid", false));
}
/**
* Return a H2 specific sequence IdGenerator that supports batch fetching
* sequence values.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new H2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new H2DbSequence(be, ds, seqName, increment);
}
@Override
@@ -1,35 +0,0 @@
package io.ebean.config.dbplatform.h2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* H2 specific sequence Id Generator.
*/
public class H2SequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
private final String unionBaseSql;
/**
* Construct given a dataSource and sql to return the next sequence value.
*/
public H2SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
this.baseSql = "select " + seqName + ".nextval";
this.unionBaseSql = " union " + baseSql;
}
@Override
public String getSql(int batchSize) {
StringBuilder sb = new StringBuilder();
sb.append(baseSql);
for (int i = 1; i < batchSize; i++) {
sb.append(unionBaseSql);
}
return sb.toString();
}
}
@@ -17,6 +17,7 @@ public class HanaPlatform extends DatabasePlatform {
this.sqlLimiter = new HanaSqlLimiter();
this.persistBatchOnCascade = PersistBatch.NONE;
this.supportsResultSetConcurrencyModeUpdatable = false;
this.columnAliasPrefix = null;
this.historySupport = new HanaHistorySupport();
this.basicSqlLimiter = new HanaBasicSqlLimiter();
@@ -1,38 +0,0 @@
package io.ebean.config.dbplatform.hsqldb;
import io.ebean.BackgroundExecutor;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.h2.H2DbEncrypt;
import io.ebean.config.dbplatform.h2.H2SequenceIdGenerator;
import javax.sql.DataSource;
/**
* H2 specific platform.
*/
public class HsqldbPlatform extends DatabasePlatform {
public HsqldbPlatform() {
super();
this.platform = Platform.HSQLDB;
this.dbEncrypt = new H2DbEncrypt();
this.truncateTable = "delete from %s";
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
this.dbIdentity.setSupportsIdentity(true);
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
}
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new H2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
}
}
@@ -1,4 +0,0 @@
/**
* Hsqldb specific support.
*/
package io.ebean.config.dbplatform.hsqldb;
@@ -31,6 +31,7 @@ public class MySqlPlatform extends DatabasePlatform {
this.inlineSqlUpdateLimit = true;
this.dbEncrypt = new MySqlDbEncrypt();
this.historySupport = new MySqlHistorySupport();
this.columnAliasPrefix = null;
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsIdentity(true);
@@ -23,6 +23,7 @@ public class NuoDbPlatform extends DatabasePlatform {
this.idInExpandedForm = true;
this.supportsResultSetConcurrencyModeUpdatable = false;
this.supportsDeleteTableAlias = true;
this.columnAliasPrefix = null;
this.likeClauseRaw = "like ?";
this.historySupport = new NuoDbHistorySupport();
//this.dbEncrypt = ...
@@ -43,8 +44,8 @@ public class NuoDbPlatform extends DatabasePlatform {
}
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new NuoDbSequence(be, ds, seqName, stepSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new NuoDbSequence(be, ds, seqName, increment);
}
@Override
@@ -5,20 +5,10 @@ import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
public class NuoDbSequence extends SequenceStepIdGenerator {
class NuoDbSequence extends SequenceStepIdGenerator {
private final String nextSql;
/**
* Construct where batchSize is the sequence step size.
*/
public NuoDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize);
this.nextSql = "select next value for " + seqName + " from dual";
NuoDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select next value for " + seqName + " from dual");
}
@Override
public String getSql(int batchSize) {
return nextSql;
}
}
@@ -1,7 +1,6 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.IdType;
/**
* Oracle 11 platform using rownum sql limiting.
@@ -13,6 +12,5 @@ public class Oracle11Platform extends OraclePlatform {
this.platform = Platform.ORACLE11;
this.columnAliasPrefix = "c";
this.sqlLimiter = new OracleRownumSqlLimiter();
dbIdentity.setIdType(IdType.SEQUENCE);
}
}
@@ -0,0 +1,14 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
class OracleDbSequence extends SequenceStepIdGenerator {
OracleDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select " + seqName + ".nextval from dual");
}
}
@@ -22,6 +22,7 @@ public class OraclePlatform extends DatabasePlatform {
public OraclePlatform() {
super();
this.platform = Platform.ORACLE;
this.columnAliasPrefix = null;
this.supportsDeleteTableAlias = true;
this.maxTableNameLength = 30;
this.maxConstraintNameLength = 30;
@@ -30,7 +31,7 @@ public class OraclePlatform extends DatabasePlatform {
this.basicSqlLimiter = new BasicSqlAnsiLimiter();
this.historySupport = new OracleDbHistorySupport();
this.truncateTable = "truncate table %s cascade";
dbIdentity.setIdType(IdType.IDENTITY);
dbIdentity.setIdType(IdType.SEQUENCE);
dbIdentity.setSupportsSequence(true);
dbIdentity.setSupportsIdentity(true);
dbIdentity.setSupportsGetGeneratedKeys(true);
@@ -62,7 +63,8 @@ public class OraclePlatform extends DatabasePlatform {
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("number", 19, 4));
dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("number", 5));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("number", 3));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("number", 16, 3));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("number", 38));
dbTypeMap.put(DbType.VARCHAR, new DbPlatformType("varchar2", 255));
dbTypeMap.put(DbType.LONGVARBINARY, new DbPlatformType("blob"));
@@ -74,8 +76,8 @@ public class OraclePlatform extends DatabasePlatform {
}
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new OracleSequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new OracleDbSequence(be, ds, seqName, increment);
}
@Override
@@ -1,27 +0,0 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* Oracle specific sequence Id Generator.
*/
public class OracleSequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
/**
* Construct given a dataSource and sql to return the next sequence value.
*/
public OracleSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
this.baseSql = "select " + seqName + ".nextval, a from (select level as a FROM dual CONNECT BY level <= ";
}
@Override
public String getSql(int batchSize) {
return baseSql + batchSize + ")";
}
}
@@ -0,0 +1,14 @@
package io.ebean.config.dbplatform.postgres;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
class PostgresDbSequence extends SequenceStepIdGenerator {
PostgresDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select nextval('" + seqName + "')");
}
}
@@ -34,6 +34,7 @@ public class PostgresPlatform extends DatabasePlatform {
this.blobDbType = Types.LONGVARBINARY;
this.clobDbType = Types.VARCHAR;
this.nativeUuidType = true;
this.columnAliasPrefix = null;
this.truncateTable = "truncate table %s cascade";
this.dbEncrypt = new PostgresDbEncrypt();
this.historySupport = new PostgresHistorySupport();
@@ -70,6 +71,7 @@ public class PostgresPlatform extends DatabasePlatform {
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("float"));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 38));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("timestamptz"));
dbTypeMap.put(DbType.BINARY, dbBytea);
@@ -107,9 +109,8 @@ public class PostgresPlatform extends DatabasePlatform {
* Create a Postgres specific sequence IdGenerator.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new PostgresSequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new PostgresDbSequence(be, ds, seqName, increment);
}
@Override
@@ -1,27 +0,0 @@
package io.ebean.config.dbplatform.postgres;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* Postgres specific sequence Id Generator.
*/
public class PostgresSequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
/**
* Construct given a dataSource and sql to return the next sequence value.
*/
public PostgresSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
this.baseSql = "select nextval('" + seqName + "'), s.generate_series from (select generate_series from generate_series(1,";
}
@Override
public String getSql(int batchSize) {
return baseSql + batchSize + ") ) as s";
}
}
@@ -32,7 +32,8 @@ public class SqlAnywherePlatform extends DatabasePlatform {
dbTypeMap.put(DbType.REAL, new DbPlatformType("float(16)"));
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("float(32)"));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 16, 3));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 28));
dbTypeMap.put(DbType.BLOB, new DbPlatformType("binary(4500)"));
dbTypeMap.put(DbType.CLOB, new DbPlatformType("long varchar"));
dbTypeMap.put(DbType.LONGVARBINARY, new DbPlatformType("long binary"));
@@ -12,6 +12,7 @@ public class SqlServer17Platform extends SqlServerBasePlatform {
public SqlServer17Platform() {
super();
this.platform = Platform.SQLSERVER17;
this.columnAliasPrefix = null;
this.nativeUuidType = true;
this.dbTypeMap.put(DbType.UUID, new DbPlatformType("uniqueidentifier", false));
}
@@ -36,7 +36,6 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsIdentity(true);
this.dbIdentity.setSupportsSequence(true);
this.sequenceBatchMode = false;
this.exceptionTranslator =
new SqlErrorCodes()
@@ -62,7 +61,9 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
dbTypeMap.put(DbType.REAL, new DbPlatformType("float(16)"));
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("float(32)"));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 16, 3));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 28));
dbTypeMap.put(DbType.DATE, new DbPlatformType("date"));
dbTypeMap.put(DbType.TIME, new DbPlatformType("time"));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime2"));
@@ -93,12 +94,9 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
sb.append('[').append(ch).append(']');
}
/**
* Create a Postgres specific sequence IdGenerator.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new SqlServerStepSequence(be, ds, seqName, stepSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new SqlServerDbSequence(be, ds, seqName, increment);
}
/**
@@ -0,0 +1,13 @@
package io.ebean.config.dbplatform.sqlserver;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
class SqlServerDbSequence extends SequenceStepIdGenerator {
SqlServerDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select next value for " + seqName);
}
}
@@ -1,24 +0,0 @@
package io.ebean.config.dbplatform.sqlserver;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
public class SqlServerStepSequence extends SequenceStepIdGenerator {
private final String nextSql;
/**
* Construct where batchSize is the sequence step size.
*/
public SqlServerStepSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize);
this.nextSql = "select next value for "+seqName;
}
@Override
public String getSql(int batchSize) {
return nextSql;
}
}
@@ -171,7 +171,7 @@ public class DefaultCsvCallback<T> implements CsvCallback<T> {
logger.info("Creating transaction, batchSize[" + persistBatchSize + "]");
transaction.setBatchMode(true);
transaction.setBatchSize(persistBatchSize);
transaction.setGetGeneratedKeys(false);
transaction.setBatchGetGeneratedKeys(false);
} else {
// explicitly turn off JDBC batching in case
+265 -59
View File
@@ -1,98 +1,304 @@
package io.ebean.util;
import io.ebean.annotation.Formula;
import io.ebean.annotation.Platform;
import io.ebean.annotation.Where;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Annotation utility methods to find annotations.
* Annotation utility methods to find annotations recursively - taken from the spring framework.
*/
public class AnnotationUtil {
/**
* Determine if the supplied {@link Annotation} is defined in the core JDK {@code java.lang.annotation} package.
*/
public static boolean notJavaLang(Annotation annotation) {
return !annotation.annotationType().getName().startsWith("java.lang.annotation");
public static boolean isInJavaLangAnnotationPackage(Annotation annotation) {
return annotation.annotationType().getName().startsWith("java.lang.annotation");
}
/**
* Simple get on field or method with no meta-annotations or platform filtering.
* Find a single {@link Annotation} of {@code annotationType} on the supplied {@link AnnotatedElement}.
* <p>
* Meta-annotations will be searched if the annotation is not <em>directly present</em> on the supplied element.
* <p>
* <strong>Warning</strong>: this method operates generically on annotated elements. In other words, this method
* does not execute specialized search algorithms for classes or methods. It only traverses through Annotations!
* It also does not filter out platform dependent annotations!
*/
public static <A extends Annotation> A get(AnnotatedElement element, Class<A> annotation) {
return element.getAnnotation(annotation);
}
@SuppressWarnings("unchecked")
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
// check if directly present, if not, start search for meta-annotations.
Annotation[] anns = annotatedElement.getAnnotations();
if (anns.length == 0) {
return null; // no annotations present, so searching for meta annotations not required
}
/**
* Simple has with no meta-annotations or platform filtering.
*/
public static <A extends Annotation> boolean has(AnnotatedElement element, Class<A> annotation) {
return get(element, annotation) != null;
}
/**
* On class get the annotation - includes inheritance.
*/
public static <A extends Annotation> A typeGet(Class<?> clazz, Class<A> annotationType) {
while (clazz != null && clazz != Object.class) {
final A val = clazz.getAnnotation(annotationType);
if (val != null) {
return val;
// As we need the anns array anyway, we iterate over this instead
// of using annotatedElement.getAnnotation(...) which is synchronized internally
for (Annotation ann : anns) {
if (ann.annotationType() == annotationType) {
return (A) ann;
}
}
return findAnnotation(anns, annotationType, new HashSet<>());
}
/**
* Find a single {@link Annotation} of {@code annotationType} on the supplied class.
* <p>Meta-annotations will be searched if the annotation is not directly present on
* the supplied element.
* <p><strong>Note</strong>: this method searches for annotations at class & superClass(es)!
*/
@SuppressWarnings("unchecked")
public static <A extends Annotation> A findAnnotationRecursive(Class<?> clazz, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
while (clazz != null && clazz != Object.class) {
// check if directly present, if not, start search for meta-annotations.
Annotation[] anns = clazz.getAnnotations();
if (anns.length != 0) {
for (Annotation ann : anns) {
if (ann.annotationType() == annotationType) {
return (A) ann;
}
}
A ann = findAnnotation(anns, annotationType, new HashSet<>());
if (ann != null) {
return ann;
}
}
// no meta-annotation present at this class - traverse to superclass
clazz = clazz.getSuperclass();
}
return null;
}
/**
* Finds the first annotation of a type for this platform. (if annotation is platform specific, otherwise first
* found annotation is returned)
*/
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType, Platform platform) {
if (annotationType == null) {
return null;
}
Set<A> anns = findAnnotations(annotatedElement, annotationType);
return getPlatformMatchingAnnotation(anns, platform);
}
/**
* Finds all annotations recusively for a class and its superclasses or interfaces.
*/
public static <A extends Annotation> Set<A> findAnnotationsRecursive(Class<?> clazz, Class<A> annotationType) {
Objects.requireNonNull(annotationType);
Set<A> ret = new LinkedHashSet<>();
Set<Annotation> visited = new HashSet<>();
Set<Class<?>> visitedInterfaces = new HashSet<>();
while (clazz != null && !clazz.getName().startsWith("java.lang.")) {
findMetaAnnotationsRecursive(clazz, annotationType, ret, visited, visitedInterfaces);
clazz = clazz.getSuperclass();
}
return ret;
}
/**
* Searches the interfaces for annotations.
*/
private static <A extends Annotation> void findMetaAnnotationsRecursive(Class<?> clazz,
Class<A> annotationType, Set<A> ret,
Set<Annotation> visited, Set<Class<?>> visitedInterfaces) {
findMetaAnnotations(clazz, annotationType, ret, visited);
for (Class<?> iface : clazz.getInterfaces()) {
if (!iface.getName().startsWith("java.lang.") && visitedInterfaces.add(iface)) {
findMetaAnnotationsRecursive(iface, annotationType, ret, visited, visitedInterfaces);
}
}
}
/**
* Perform the search algorithm avoiding endless recursion by tracking which
* annotations have already been visited.
*/
@SuppressWarnings("unchecked")
private static <A extends Annotation> A findAnnotation(Annotation[] anns, Class<A> annotationType, Set<Annotation> visited) {
for (Annotation ann : anns) {
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
Annotation[] metaAnns = ann.annotationType().getAnnotations();
for (Annotation metaAnn : metaAnns) {
if (metaAnn.annotationType() == annotationType) {
return (A) metaAnn;
}
}
if (metaAnns.length > 0) {
A annotation = findAnnotation(metaAnns, annotationType, visited);
if (annotation != null) {
return annotation;
}
}
}
}
return null;
}
/**
* On class get all the annotations - includes inheritance.
* Find all {@link Annotation}s of {@code annotationType} on the supplied {@link AnnotatedElement}.
* <p>
* Meta-annotations will be searched if the annotation is not <em>directly present</em> on the supplied element.
* <p>
* <strong>Warning</strong>: this method operates generically on annotated elements. In other words, this method
* does not execute specialized search algorithms for classes or methods. It only traverses through Annotations!
*/
public static <A extends Annotation> Set<A> typeGetAll(Class<?> clazz, Class<A> annotationType) {
Set<A> result = new LinkedHashSet<>();
typeGetAllCollect(clazz, annotationType, result);
return result;
}
private static <A extends Annotation> void typeGetAllCollect(Class<?> clazz, Class<A> annotationType, Set<A> result) {
while (clazz != null && clazz != Object.class) {
final A val = clazz.getAnnotation(annotationType);
if (val != null) {
result.add(val);
}
clazz = clazz.getSuperclass();
public static <A extends Annotation> Set<A> findAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
Set<A> ret = new LinkedHashSet<>();
findMetaAnnotations(annotatedElement, annotationType, ret, new HashSet<>());
return ret;
}
/**
* On class simple check for annotation - includes inheritance.
* Perform the search algorithm avoiding endless recursion by tracking which
* annotations have already been visited.
*/
public static <A extends Annotation> boolean typeHas(Class<?> clazz, Class<A> annotation) {
return typeGet(clazz, annotation) != null;
}
@SuppressWarnings("unchecked")
private static <A extends Annotation> void findMetaAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType, Set<A> ret, Set<Annotation> visited) {
/**
* Find all the annotations for the filter searching meta-annotations.
*/
public static Set<Annotation> metaFindAllFor(AnnotatedElement element, Set<Class<?>> filter) {
Set<Annotation> visited = new HashSet<>();
Set<Annotation> result = new LinkedHashSet<>();
for (Annotation ann : element.getAnnotations()) {
metaAdd(ann, filter, visited, result);
}
return result;
}
private static void metaAdd(Annotation ann, Set<Class<?>> filter, Set<Annotation> visited, Set<Annotation> result) {
if (notJavaLang(ann) && visited.add(ann)) {
if (filter.contains(ann.annotationType())) {
result.add(ann);
} else {
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
metaAdd(metaAnn, filter, visited, result);
Annotation[] anns = annotatedElement.getAnnotations();
for (Annotation ann : anns) {
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
if (ann.annotationType() == annotationType) {
ret.add((A) ann);
} else {
Method repeatableValueMethod = getRepeatableValueMethod(ann, annotationType);
if (repeatableValueMethod != null) {
try {
A[] repeatedAnns = (A[]) repeatableValueMethod.invoke(ann);
for (Annotation repeatedAnn : repeatedAnns) {
ret.add((A) repeatedAnn);
findMetaAnnotations(repeatedAnn.annotationType(), annotationType, ret, visited);
}
} catch (Exception e) { // catch all exceptions (thrown by invoke)
throw new RuntimeException(e);
}
} else {
findMetaAnnotations(ann.annotationType(), annotationType, ret, visited);
}
}
}
}
}
// caches for getRepeatableValueMethod
private static Method getNullMethod() {
try {
return AnnotationUtil.class.getDeclaredMethod("getNullMethod");
} catch (NoSuchMethodException e) {
return null;
}
}
private static final ConcurrentMap<Annotation, Method> valueMethods = new ConcurrentHashMap<>();
// only a non-null-marker the valueMethods - Cache
private static final Method nullMethod = getNullMethod();
/**
* Returns the <code>value()</code> method for a possible containerAnnotation.
* Method is retuned only, if its signature is <code>array of containingType</code>.
*/
private static <A extends Annotation> Method getRepeatableValueMethod(
Annotation containerAnnotation, Class<A> containingType) {
Method method = valueMethods.get(containerAnnotation);
if (method == null) {
try {
method = containerAnnotation.annotationType().getMethod("value");
} catch (NoSuchMethodException e) {
method = nullMethod;
} catch (Exception e) {
throw new RuntimeException(e);
}
Method prev = valueMethods.putIfAbsent(containerAnnotation, method);
method = prev == null ? method : prev;
}
if (method != nullMethod) {
Class<?> retType = method.getReturnType();
if (retType.isArray() && retType.getComponentType() == containingType) {
return method;
}
}
return null;
}
/**
* Finds a suitable annotation from <code>Set<T> anns</code> for this platform.
* To distinguish between platforms, annotation type <code>T</code> must define
* a method withthis signature:
* <p>
* <code>Class<? extends DatabasePlatform>[] platforms() default {};</code>
* </p>
* The finding rules are:
* <ol>
* <li>Check if T has method "platforms" if not, return <code>ann[0]</code></code>
* <li>find the annotation that is defined for <code>databasePlatform</code></li>
* <li>otherwise return the annotation for default platform (platforms = {})</li>
* <li>return null
* </ol>
* (This mechanism is currently used by {@link Where} and {@link Formula})
*/
public static <T extends Annotation> T getPlatformMatchingAnnotation(Set<T> anns, Platform matchPlatform) {
if (anns.isEmpty()) {
return null;
}
Method getPlatformsMethod = null;
T fallback = null;
for (T ann : anns) {
try {
if (getPlatformsMethod == null) {
getPlatformsMethod = ann.getClass().getMethod("platforms");
}
if (!Platform[].class.isAssignableFrom(getPlatformsMethod.getReturnType())) {
return ann;
}
Platform[] platforms = (Platform[]) getPlatformsMethod.invoke(ann);
if (platforms.length == 0) {
fallback = ann;
} else {
for (Platform platform : platforms) {
if (matchPlatform == platform) {
return ann;
}
}
}
} catch (NoSuchMethodException e) {
return ann; // not platform specific - return first one
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return fallback;
}
}
@@ -71,13 +71,13 @@ public class ScopeTrans {
restoreBatchOnCascade = transaction.isBatchOnCascade();
restoreBatchSize = transaction.getBatchSize();
restoreBatchGeneratedKeys = transaction.getBatchGetGeneratedKeys();
restoreBatchFlushOnQuery = transaction.isFlushOnQuery();
restoreBatchFlushOnQuery = transaction.isBatchFlushOnQuery();
}
if (txScope.isBatchSet()) {
transaction.setBatchMode(txScope.isBatchMode());
}
if (!txScope.isFlushOnQuery()) {
transaction.setFlushOnQuery(false);
transaction.setBatchFlushOnQuery(false);
}
if (txScope.isBatchOnCascadeSet()) {
transaction.setBatchOnCascade(txScope.isBatchOnCascade());
@@ -86,7 +86,7 @@ public class ScopeTrans {
transaction.setBatchSize(txScope.getBatchSize());
}
if (txScope.isSkipGeneratedKeys()) {
transaction.setGetGeneratedKeys(false);
transaction.setBatchGetGeneratedKeys(false);
}
}
@@ -136,7 +136,7 @@ public class ScopeTrans {
transaction.commit();
} else {
nestedCommit = true;
transaction.setFlushOnQuery(restoreBatchFlushOnQuery);
transaction.setBatchFlushOnQuery(restoreBatchFlushOnQuery);
if (restoreBatch != null) {
transaction.setBatchMode(restoreBatch);
}
@@ -147,7 +147,7 @@ public class ScopeTrans {
transaction.setBatchSize(restoreBatchSize);
}
if (restoreBatchGeneratedKeys != null) {
transaction.setGetGeneratedKeys(restoreBatchGeneratedKeys);
transaction.setBatchGetGeneratedKeys(restoreBatchGeneratedKeys);
}
}
}
@@ -147,7 +147,7 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
*
* @param tenantId For multi-tenant lazy loading provide the tenantId to use.
*/
SpiTransaction createReadOnlyTransaction(Object tenantId);
SpiTransaction createQueryTransaction(Object tenantId);
/**
* An event from another server in the cluster used to notify local
@@ -269,8 +269,8 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setGetGeneratedKeys(boolean getGeneratedKeys) {
transaction.setGetGeneratedKeys(getGeneratedKeys);
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
transaction.setBatchGetGeneratedKeys(getGeneratedKeys);
}
@Override
@@ -279,18 +279,18 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setFlushOnMixed(boolean batchFlushOnMixed) {
transaction.setFlushOnMixed(batchFlushOnMixed);
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
transaction.setBatchFlushOnMixed(batchFlushOnMixed);
}
@Override
public void setFlushOnQuery(boolean batchFlushOnQuery) {
transaction.setFlushOnQuery(batchFlushOnQuery);
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
transaction.setBatchFlushOnQuery(batchFlushOnQuery);
}
@Override
public boolean isFlushOnQuery() {
return transaction.isFlushOnQuery();
public boolean isBatchFlushOnQuery() {
return transaction.isBatchFlushOnQuery();
}
@Override
@@ -14,7 +14,6 @@ import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.mysql.MySql55Platform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
@@ -814,8 +813,6 @@ public class DefaultDbMigration implements DbMigration {
switch (platform) {
case H2:
return new H2Platform();
case HSQLDB:
return new HsqldbPlatform();
case POSTGRES9:
return new Postgres9Platform();
case POSTGRES:
@@ -6,9 +6,9 @@ import io.ebean.config.dbplatform.DatabasePlatform;
/**
* Oracle platform specific DDL.
*/
public class OracleDdl extends PlatformDdl {
public class Oracle10Ddl extends PlatformDdl {
public OracleDdl(DatabasePlatform platform) {
public Oracle10Ddl(DatabasePlatform platform) {
super(platform);
this.dropTableIfExists = "drop table ";
this.dropSequenceIfExists = "drop sequence ";
@@ -48,12 +48,4 @@ public class OracleDdl extends PlatformDdl {
}
}
/**
* Modify and return the column definition for autoincrement or identity definition.
*/
@Override
public String asIdentityColumn(String columnDefn, DdlIdentity identity) {
return asIdentityStandardOptions(columnDefn, identity);
}
}
@@ -195,6 +195,7 @@ public class PlatformDdl {
* SQl2003 standard identity definition.
*/
protected String asIdentityStandardOptions(String columnDefn, DdlIdentity identity) {
StringBuilder sb = new StringBuilder(columnDefn.length() + 60);
sb.append(columnDefn).append(identity.optionGenerated());
sb.append(identity.identityOptions(identityStartWith, identityIncrementBy, identityCache));
@@ -58,7 +58,7 @@ public class PlatformTypeConverter {
} else {
// scale - varchar(10)
int scale = Integer.parseInt(columnDefinition.substring(open + 1, close));
return dbType.renderType(scale, -1) + suffix;
return dbType.renderType(scale, 0) + suffix;
}
} catch (IllegalArgumentException e) {
@@ -55,7 +55,7 @@ public class MCompoundUniqueConstraint {
public MCompoundUniqueConstraint(AddUniqueConstraint change) {
this.name = change.getConstraintName();
this.columns = split(change.getColumnNames());
this.oneToOne = Boolean.TRUE.equals(change.isOneToOne());
this.oneToOne = change.isOneToOne();
this.platforms = change.getPlatforms();
this.nullableColumns = splitWithNull(change.getNullableColumns());
}
@@ -63,7 +63,7 @@ public class MCompoundUniqueConstraint {
public MCompoundUniqueConstraint(UniqueConstraint uq) {
this.name = uq.getName();
this.columns = split(uq.getColumnNames());
this.oneToOne = Boolean.TRUE.equals(uq.isOneToOne());
this.oneToOne = uq.isOneToOne();
this.platforms = uq.getPlatforms();
this.nullableColumns = splitWithNull(uq.getNullableColumns());
}
@@ -1,54 +0,0 @@
package io.ebeaninternal.server.cache;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* Bean Id value plus discriminator type.
* <p>
* Put into L2 cache such that we know the type of a bean with inheritance.
*/
public class CachedBeanId implements Externalizable {
private String discValue;
private Object id;
public CachedBeanId(String discValue, Object id) {
this.discValue = discValue;
this.id = id;
}
/**
* Construct from serialisation.
*/
public CachedBeanId() {
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(discValue);
out.writeObject(id);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
discValue = in.readUTF();
id = in.readObject();
}
@Override
public String toString() {
return discValue + ":" + id;
}
public String getDiscValue() {
return discValue;
}
public Object getId() {
return id;
}
}
@@ -134,7 +134,7 @@ class DefaultCacheHolder {
}
private ServerCacheOptions getQueryOptions(Class<?> cls) {
CacheQueryTuning tuning = AnnotationUtil.get(cls, CacheQueryTuning.class);
CacheQueryTuning tuning = AnnotationUtil.findAnnotation(cls, CacheQueryTuning.class);
if (tuning != null) {
return new ServerCacheOptions(tuning).applyDefaults(queryDefault);
}
@@ -52,7 +52,7 @@ public class DefaultChangeLogRegister implements ChangeLogRegister {
* Find and return the ChangeLog annotation in the inheritance hierarchy.
*/
private ChangeLog getChangeLog(Class<?> beanType) {
return AnnotationUtil.typeGet(beanType, ChangeLog.class);
return AnnotationUtil.findAnnotationRecursive(beanType, ChangeLog.class);
}
/**
@@ -58,7 +58,7 @@ public abstract class AbstractSqlQueryRequest {
trans = server.currentServerTransaction();
if (trans == null || !trans.isActive()) {
// create a local readOnly transaction
trans = server.createReadOnlyTransaction(null);
trans = server.createQueryTransaction(null);
createdTransaction = true;
}
}
@@ -7,7 +7,6 @@ import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.mysql.MySql55Platform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
@@ -164,8 +163,6 @@ public class DatabasePlatformFactory {
return mysqlVersion(majorVersion, minorVersion);
} else if (dbProductName.contains("h2")) {
return new H2Platform();
} else if (dbProductName.contains("hsql database engine")) {
return new HsqldbPlatform();
} else if (dbProductName.contains("postgres")) {
return readPostgres(connection, majorVersion);
} else if (dbProductName.contains("nuo")) {
@@ -170,7 +170,7 @@ class DefaultBeanLoader {
private List<?> executeQuery(LoadRequest loadRequest, SpiQuery<?> query) {
if (onIterateUseExtraTxn && loadRequest.isParentFindIterate()) {
// MySql - we need a different transaction to execute the secondary query
SpiTransaction extraTxn = server.createReadOnlyTransaction(query.getTenantId());
SpiTransaction extraTxn = server.createQueryTransaction(query.getTenantId());
try {
return server.findList(query, extraTxn);
} finally {
@@ -8,6 +8,10 @@ import io.ebean.config.TenantMode;
import io.ebean.config.UnderscoreNamingConvention;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.datasource.DataSourceAlertFactory;
import io.ebean.datasource.DataSourceConfig;
import io.ebean.datasource.DataSourceFactory;
import io.ebean.datasource.DataSourcePoolListener;
import io.ebean.service.SpiContainer;
import io.ebeaninternal.api.SpiBackgroundExecutor;
import io.ebeaninternal.api.SpiEbeanServer;
@@ -20,6 +24,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
@@ -34,8 +39,13 @@ public class DefaultContainer implements SpiContainer {
private final ClusterManager clusterManager;
private final JndiDataSourceLookup jndiDataSourceFactory;
public DefaultContainer(ContainerConfig containerConfig) {
this.clusterManager = new ClusterManager(containerConfig);
this.jndiDataSourceFactory = new JndiDataSourceLookup();
// register so that we can shutdown any Ebean wide
// resources such as clustering
ShutdownManager.registerContainer(this);
@@ -220,10 +230,75 @@ public class DefaultContainer implements SpiContainer {
* Set the DataSource if it has not already been set.
*/
private void setDataSource(ServerConfig config) {
if (config.getDataSource() == null) {
config.setDataSource(getDataSourceFromConfig(config, false));
}
if (config.getReadOnlyDataSource() == null && config.isAutoReadOnlyDataSource()) {
config.setReadOnlyDataSource(getDataSourceFromConfig(config, true));
}
}
private DataSource getDataSourceFromConfig(ServerConfig config, boolean readOnly) {
if (isOfflineMode(config)) {
logger.debug("... DbOffline using platform [{}]", DbOffline.getPlatform());
} else {
InitDataSource.init(config);
return null;
}
if (!readOnly && config.getDataSourceJndiName() != null) {
DataSource ds = jndiDataSourceFactory.lookup(config.getDataSourceJndiName());
if (ds == null) {
throw new PersistenceException("JNDI lookup for DataSource " + config.getDataSourceJndiName() + " returned null.");
} else {
return ds;
}
}
DataSourceConfig dsConfig = (readOnly) ? config.getReadOnlyDataSourceConfig() : config.getDataSourceConfig();
if (dsConfig == null) {
throw new PersistenceException("No DataSourceConfig defined for " + config.getName());
}
if (dsConfig.isOffline()) {
if (config.getDatabasePlatformName() == null) {
throw new PersistenceException("You MUST specify a DatabasePlatformName on ServerConfig when offline");
}
return null;
}
DataSourceFactory factory = DataSourceFactory.get();
if (factory == null) {
throw new IllegalStateException("No DataSourceFactory service implementation found in class path."
+ " Probably missing dependency to avaje-datasource?");
}
DataSourceAlertFactory alertFactory = config.service(DataSourceAlertFactory.class);
if (alertFactory != null) {
dsConfig.setAlert(alertFactory.createAlert());
}
attachListener(config, dsConfig);
if (readOnly) {
// setup to use AutoCommit such that we skip explicit commit
dsConfig.setAutoCommit(true);
//dsConfig.setReadOnly(true);
dsConfig.setDefaults(config.getDataSourceConfig());
dsConfig.setIsolationLevel(config.getDataSourceConfig().getIsolationLevel());
}
String poolName = config.getName() + (readOnly ? "-ro" : "");
return factory.createPool(poolName, dsConfig);
}
/**
* Create and attach a DataSourcePoolListener if it has been specified via properties and there is not one already attached.
*/
private void attachListener(ServerConfig config, DataSourceConfig dsConfig) {
if (dsConfig.getListener() == null) {
String poolListener = dsConfig.getPoolListener();
if (poolListener != null) {
dsConfig.setListener((DataSourcePoolListener) config.getClassLoadConfig().newInstance(poolListener));
}
}
}
@@ -256,7 +331,7 @@ public class DefaultContainer implements SpiContainer {
}
try (Connection connection = serverConfig.getDataSource().getConnection()) {
if (connection.getAutoCommit()) {
if (!serverConfig.isAutoCommitMode() && connection.getAutoCommit()) {
logger.warn("DataSource [{}] has autoCommit defaulting to true!", serverConfig.getName());
}
return true;
@@ -2322,8 +2322,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
@Override
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
return transactionManager.createReadOnlyTransaction(tenantId);
public SpiTransaction createQueryTransaction(Object tenantId) {
return transactionManager.createQueryTransaction(tenantId);
}
/**
@@ -1,149 +0,0 @@
package io.ebeaninternal.server.core;
import io.ebean.config.ServerConfig;
import io.ebean.datasource.DataSourceAlertFactory;
import io.ebean.datasource.DataSourceConfig;
import io.ebean.datasource.DataSourceFactory;
import io.ebean.datasource.DataSourcePoolListener;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
/**
* Initialise the main DataSource and read-only DataSource.
*/
class InitDataSource {
private final JndiDataSourceLookup jndiDataSourceFactory = new JndiDataSourceLookup();
private final ServerConfig config;
/**
* Create and set the main DataSource and read-only DataSource.
*/
static void init(ServerConfig config) {
new InitDataSource(config).initialise();
}
InitDataSource(ServerConfig config) {
this.config = config;
}
private void initialise() {
if (config.getDataSource() == null) {
config.setDataSource(initDataSource());
}
if (config.getReadOnlyDataSource() == null) {
config.setReadOnlyDataSource(initReadOnlyDataSource());
}
}
/**
* Initialise the "main" read write DataSource from configuration.
*/
private DataSource initDataSource() {
final String jndiName = config.getDataSourceJndiName();
if (jndiName != null) {
return jndiDataSource(jndiName);
}
return createFromConfig(config.getDataSourceConfig(), false);
}
private DataSource jndiDataSource(String jndiName) {
DataSource ds = jndiDataSourceFactory.lookup(jndiName);
if (ds == null) {
throw new PersistenceException("JNDI lookup for DataSource " + jndiName + " returned null.");
}
return ds;
}
/**
* Initialise the "read only" DataSource from configuration.
*/
private DataSource initReadOnlyDataSource() {
DataSourceConfig roConfig = readOnlyConfig();
return roConfig == null ? null : createFromConfig(roConfig, true);
}
DataSourceConfig readOnlyConfig() {
DataSourceConfig roConfig = config.getReadOnlyDataSourceConfig();
if (roConfig == null) {
// it has explicitly been set to null, not expected but ok
return null;
}
if (urlSet(roConfig.getUrl())) {
return roConfig;
}
// convenient alternate place to set the read-only url
final String readOnlyUrl = config.getDataSourceConfig().getReadOnlyUrl();
if (urlSet(readOnlyUrl)) {
roConfig.setUrl(readOnlyUrl);
return roConfig;
}
if (config.isAutoReadOnlyDataSource()) {
roConfig.setUrl(null); // blank out in case it is "none"
return roConfig;
} else {
return null;
}
}
private boolean urlSet(String url) {
return url != null && !"none".equalsIgnoreCase(url) && !url.trim().isEmpty();
}
private DataSource createFromConfig(DataSourceConfig dsConfig, boolean readOnly) {
if (dsConfig == null) {
throw new PersistenceException("No DataSourceConfig defined for " + config.getName());
}
if (dsConfig.isOffline()) {
if (config.getDatabasePlatformName() == null) {
throw new PersistenceException("You MUST specify a DatabasePlatformName on ServerConfig when offline");
}
return null;
}
attachAlert(dsConfig);
attachListener(dsConfig);
if (readOnly) {
// setup to use AutoCommit such that we skip explicit commit
dsConfig.setAutoCommit(true);
dsConfig.setReadOnly(true);
dsConfig.setDefaults(config.getDataSourceConfig());
dsConfig.setIsolationLevel(config.getDataSourceConfig().getIsolationLevel());
}
return create(dsConfig, readOnly);
}
private DataSource create(DataSourceConfig dsConfig, boolean readOnly) {
DataSourceFactory factory = DataSourceFactory.get();
if (factory == null) {
throw new IllegalStateException("No DataSourceFactory service implementation found in class path. Missing dependency to ebean-datasource?");
}
String poolName = config.getName() + (readOnly ? "-ro" : "");
return factory.createPool(poolName, dsConfig);
}
/**
* Attach DataSourceAlert via service loader if present.
*/
private void attachAlert(DataSourceConfig dsConfig) {
DataSourceAlertFactory alertFactory = config.service(DataSourceAlertFactory.class);
if (alertFactory != null) {
dsConfig.setAlert(alertFactory.createAlert());
}
}
/**
* Create and attach a DataSourcePoolListener if it has been specified via properties and there is not one already attached.
*/
private void attachListener(DataSourceConfig dsConfig) {
if (dsConfig.getListener() == null) {
String poolListener = dsConfig.getPoolListener();
if (poolListener != null) {
dsConfig.setListener((DataSourcePoolListener) config.getClassLoadConfig().newInstance(poolListener));
}
}
}
}
@@ -15,6 +15,7 @@ import io.ebean.config.ServerConfig;
import io.ebean.config.SlowQueryListener;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbHistorySupport;
import io.ebean.datasource.DataSourcePool;
import io.ebean.event.changelog.ChangeLogListener;
import io.ebean.event.changelog.ChangeLogPrepare;
import io.ebean.event.changelog.ChangeLogRegister;
@@ -44,10 +45,10 @@ import io.ebeaninternal.server.changelog.DefaultChangeLogPrepare;
import io.ebeaninternal.server.changelog.DefaultChangeLogRegister;
import io.ebeaninternal.server.cluster.ClusterManager;
import io.ebeaninternal.server.core.bootup.BootupClasses;
import io.ebeaninternal.server.core.timezone.CloneDataTimeZone;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
import io.ebeaninternal.server.core.timezone.MySqlDataTimeZone;
import io.ebeaninternal.server.core.timezone.LocalDataTimeZone;
import io.ebeaninternal.server.core.timezone.NoDataTimeZone;
import io.ebeaninternal.server.core.timezone.OracleDataTimeZone;
import io.ebeaninternal.server.core.timezone.SimpleDataTimeZone;
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
@@ -77,10 +78,12 @@ import io.ebeaninternal.server.query.QueryPlanLoggerSqlServer;
import io.ebeaninternal.server.readaudit.DefaultReadAuditLogger;
import io.ebeaninternal.server.readaudit.DefaultReadAuditPrepare;
import io.ebeaninternal.server.text.json.DJsonContext;
import io.ebeaninternal.server.transaction.AutoCommitTransactionManager;
import io.ebeaninternal.server.transaction.DataSourceSupplier;
import io.ebeaninternal.server.transaction.DefaultProfileHandler;
import io.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
import io.ebeaninternal.server.transaction.DocStoreTransactionManager;
import io.ebeaninternal.server.transaction.ExplicitTransactionManager;
import io.ebeaninternal.server.transaction.ExternalTransactionScopeManager;
import io.ebeaninternal.server.transaction.JtaTransactionManager;
import io.ebeaninternal.server.transaction.NoopProfileHandler;
@@ -97,6 +100,7 @@ import io.ebeanservice.docstore.none.NoneDocStoreFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -446,6 +450,12 @@ public class InternalConfiguration {
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler(), logManager,
tableModState, cacheNotify, clockService);
if (serverConfig.isExplicitTransactionBeginMode()) {
return new ExplicitTransactionManager(options);
}
if (isAutoCommitMode()) {
return new AutoCommitTransactionManager(options);
}
if (serverConfig.isDocStoreOnly()) {
return new DocStoreTransactionManager(options);
}
@@ -482,6 +492,18 @@ public class InternalConfiguration {
}
}
/**
* Return true if autoCommit mode is on.
*/
private boolean isAutoCommitMode() {
if (serverConfig.isAutoCommitMode()) {
// explicitly set
return true;
}
DataSource dataSource = serverConfig.getDataSource();
return dataSource instanceof DataSourcePool && ((DataSourcePool) dataSource).isAutoCommit();
}
/**
* Create the TransactionScopeManager taking into account JTA or external transaction manager.
*/
@@ -507,12 +529,12 @@ public class InternalConfiguration {
String tz = serverConfig.getDataTimeZone();
if (tz == null) {
if (isMySql(getPlatform())) {
return new MySqlDataTimeZone();
return new LocalDataTimeZone();
}
return new NoDataTimeZone();
}
if (getPlatform().base() == Platform.ORACLE) {
return new OracleDataTimeZone(tz);
return new CloneDataTimeZone(tz);
} else {
return new SimpleDataTimeZone(tz);
}
@@ -572,10 +594,7 @@ public class InternalConfiguration {
}
private ServerCachePlugin initServerCachePlugin() {
if (serverConfig.isLocalOnlyL2Cache()) {
localL2Caching = true;
return new DefaultServerCachePlugin();
}
ServerCachePlugin plugin = serverConfig.getServerCachePlugin();
if (plugin == null) {
ServiceLoader<ServerCachePlugin> cacheFactories = ServiceLoader.load(ServerCachePlugin.class);
@@ -286,7 +286,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
} else {
// create an implicit transaction to execute this query
// potentially using read-only DataSource with autoCommit
transaction = ebeanServer.createReadOnlyTransaction(query.getTenantId());
transaction = ebeanServer.createQueryTransaction(query.getTenantId());
}
createdTransaction = true;
}
@@ -699,7 +699,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Create and return a new reference bean matching this beans Id value.
*/
public T createReference() {
return beanDescriptor.createRef(getBeanId(), null);
return beanDescriptor.createReference(getBeanId(), null);
}
/**
@@ -1399,8 +1399,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
changes.put(property.getName(), val);
if (property.isNaturalKey()) {
updateNaturalKey = true;
String valStr = (val == null) ? null : val.toString();
changeSet.addNaturalKeyPut(beanDescriptor, key, valStr);
changeSet.addNaturalKeyPut(beanDescriptor, key, val.toString());
}
}
}
@@ -9,7 +9,7 @@ import io.ebeaninternal.dbmigration.ddlgeneration.platform.HanaColumnStoreDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.HsqldbDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.MySqlDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.NuoDbDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.OracleDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.Oracle10Ddl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.PlatformDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.Postgres9Ddl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.PostgresDdl;
@@ -40,7 +40,7 @@ public class PlatformDdlBuilder {
return new NuoDbDdl(platform);
case ORACLE:
case ORACLE11:
return new OracleDdl(platform);
return new Oracle10Ddl(platform);
case SQLITE:
return new SQLiteDdl(platform);
case POSTGRES9:
@@ -473,6 +473,6 @@ public class BootupClasses implements ClassFilter {
* Returns true if this class has the annotation (or meta annotation). Does not search recursively.
*/
private boolean has(Class<?> cls, Class<? extends Annotation> ann) {
return AnnotationUtil.has(cls, ann);
return AnnotationUtil.findAnnotation(cls, ann) != null;
}
}
@@ -8,9 +8,9 @@ import java.util.Calendar;
* Used with Oracle JDBC driver as that wants to mutate the Calender.
* </p>
*/
public class OracleDataTimeZone extends SimpleDataTimeZone {
public class CloneDataTimeZone extends SimpleDataTimeZone {
public OracleDataTimeZone(String zoneId) {
public CloneDataTimeZone(String zoneId) {
super(zoneId);
}
@@ -5,11 +5,11 @@ import java.util.Calendar;
/**
* Implementation of DataTimeZone when single Calendar instance is used with local timezone.
*/
public class MySqlDataTimeZone implements DataTimeZone {
public class LocalDataTimeZone implements DataTimeZone {
protected final Calendar zone;
public MySqlDataTimeZone() {
public LocalDataTimeZone() {
this.zone = Calendar.getInstance();
}
@@ -19,7 +19,7 @@ public class MySqlDataTimeZone implements DataTimeZone {
}
@Override
public Calendar getDateTimeZone() {
return zone;
}
public Calendar getDateTimeZone() {
return zone; // workaround for MySQL. TODO: rename class to MySqlDataTimeZone!?
}
}
@@ -2053,16 +2053,14 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
/**
* Create a non read only reference bean without checking cacheSharableBeans.
*/
public T createReference(Object id, PersistenceContext pc) {
if (inheritInfo != null && !inheritInfo.isConcrete()) {
return findReferenceBean(id, pc);
}
return createRef(id, pc);
}
@SuppressWarnings("unchecked")
public T createRef(Object id, PersistenceContext pc) {
public T createReference(Object id, PersistenceContext pc) {
try {
if (inheritInfo != null && !inheritInfo.isConcrete()) {
return findReferenceBean(id, pc);
}
EntityBean eb = createEntityBean();
id = convertSetId(id, eb);
EntityBeanIntercept ebi = eb._ebean_getIntercept();
@@ -2073,6 +2071,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
ebi.setPersistenceContext(pc);
}
return (T) eb;
} catch (Exception ex) {
throw new PersistenceException(ex);
}
@@ -44,9 +44,9 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.meta.DeployBeanTable;
import io.ebeaninternal.server.deploy.meta.DeployIdentityMode;
import io.ebeaninternal.server.deploy.meta.DeployOrderColumn;
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
import io.ebeaninternal.server.deploy.meta.DeployIdentityMode;
import io.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import io.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import io.ebeaninternal.server.deploy.parse.DeployInherit;
@@ -357,6 +357,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
* Deploy returning the asOfTableMap (which is required by the SQL builders).
*/
public Map<String, String> deploy(List<XmEbean> mappings) {
try {
createListeners();
readEntityDeploymentInitial();
@@ -364,6 +365,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
readEntityBeanTable();
readEntityDeploymentAssociations();
readInheritedIdGenerators();
setProfileIds();
// creates the BeanDescriptors
readEntityRelationships();
@@ -373,7 +375,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
initialiseAll();
readForeignKeys();
readTableToDescriptor();
logStatus();
// clear collections we no longer need
@@ -774,6 +778,18 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
}
/**
* Set profileIds based on descriptor full name order.
*/
private void setProfileIds() {
List<? extends DeployBeanDescriptor<?>> deployDescriptors = deployInfoMap.values().stream()
.map(DeployBeanInfo::getDescriptor)
.collect(Collectors.toList());
deployDescriptors.sort(Comparator.comparing(DeployBeanDescriptor::getFullName));
}
/**
* Create the BeanTable from the deployment information gathered so far.
*/
@@ -986,15 +1002,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
orderProperty.setDbInsertable(orderColumn.isInsertable());
orderProperty.setDbUpdateable(orderColumn.isUpdatable());
orderProperty.setDbRead(true);
orderProperty.setOwningType(targetDesc.getBeanType());
final InheritInfo targetInheritInfo = targetDesc.getInheritInfo();
if (targetInheritInfo != null) {
for (InheritInfo child : targetInheritInfo.getChildren()) {
final DeployBeanDescriptor<?> childDescriptor = deployInfoMap.get(child.getType()).getDescriptor();
childDescriptor.setOrderColumn(orderProperty);
}
}
targetDesc.setOrderColumn(orderProperty);
}
@@ -1394,13 +1401,13 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
String primaryKeyColumn = desc.getSinglePrimaryKeyColumn();
seqName = namingConvention.getSequenceName(desc.getBaseTable(), primaryKeyColumn);
}
int stepSize = desc.setIdentitySequenceBatchMode(databasePlatform.isSequenceBatchMode());
desc.setIdGenerator(createSequenceIdGenerator(seqName, stepSize));
int increment = desc.setIdentitySequenceDefaultIncrement();
desc.setIdGenerator(createSequenceIdGenerator(seqName, increment));
}
}
private PlatformIdGenerator createSequenceIdGenerator(String seqName, int stepSize) {
return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, stepSize, seqName);
private PlatformIdGenerator createSequenceIdGenerator(String seqName, int increment) {
return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, increment, seqName);
}
private void createByteCode(DeployBeanDescriptor<?> deploy) {
@@ -1584,7 +1591,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private boolean isMappedSuperWithNoProperties(Class<?> beanClass) {
// do not search recursive here
MappedSuperclass annotation = AnnotationUtil.get(beanClass, MappedSuperclass.class);
MappedSuperclass annotation = AnnotationUtil.findAnnotation(beanClass, MappedSuperclass.class);
if (annotation == null) {
return false;
}
@@ -13,7 +13,6 @@ import io.ebeaninternal.api.json.SpiJsonReader;
import io.ebeaninternal.api.json.SpiJsonWriter;
import io.ebeaninternal.server.cache.CacheChangeSet;
import io.ebeaninternal.server.cache.CachedBeanData;
import io.ebeaninternal.server.cache.CachedBeanId;
import io.ebeaninternal.server.core.DefaultSqlUpdate;
import io.ebeaninternal.server.deploy.id.ImportedId;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
@@ -434,19 +433,11 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
}
if (embedded) {
return targetDescriptor.cacheEmbeddedBeanExtract((EntityBean) ap);
} else if (targetInheritInfo != null) {
return createCacheBeanId(ap);
} else {
return targetDescriptor.getIdProperty().getCacheDataValue((EntityBean) ap);
}
}
private Object createCacheBeanId(Object bean) {
final BeanDescriptor<?> desc = targetDescriptor.descOf(bean.getClass());
final Object id = desc.getIdProperty().getCacheDataValue((EntityBean) bean);
return new CachedBeanId(desc.getDiscValue(), id);
}
@Override
public String format(Object value) {
return targetDescriptor.getIdBinder().cacheKey(value);
@@ -460,31 +451,19 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
if (embedded) {
setValue(bean, targetDescriptor.cacheEmbeddedBeanLoad((CachedBeanData) cacheData, context));
} else {
if (cacheData instanceof CachedBeanId) {
setValue(bean, refInheritBean((CachedBeanId) cacheData, context));
} else {
setValue(bean, refBean(targetDescriptor, cacheData, context));
if (cacheData instanceof String) {
cacheData = targetDescriptor.getIdProperty().scalarType.parse((String) cacheData);
}
// cacheData is the id value, maybe already in persistence context
Object assocBean = targetDescriptor.contextGet(context, cacheData);
if (assocBean == null) {
assocBean = targetDescriptor.createReference(cacheData, context);
}
setValue(bean, assocBean);
}
}
}
private Object refInheritBean(CachedBeanId cacheId, PersistenceContext context) {
final InheritInfo rowInheritInfo = targetInheritInfo.readType(cacheId.getDiscValue());
return refBean(rowInheritInfo.desc(), cacheId.getId(), context);
}
private Object refBean(BeanDescriptor<?> desc, Object id, PersistenceContext context) {
if (id instanceof String) {
id = desc.getIdProperty().scalarType.parse((String) id);
}
Object bean = desc.contextGet(context, id);
if (bean == null) {
bean = desc.createRef(id, context);
}
return bean;
}
@Override
public ScalarDataReader<?> getIdReader() {
return targetDescriptor.getIdProperty();
@@ -661,12 +640,14 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
@Override
public void addTenant(SpiQuery<?> query, Object tenantId) {
query.where().eq(name, targetDescriptor.createRef(tenantId, null));
T refBean = targetDescriptor.createReference(tenantId, null);
query.where().eq(name, refBean);
}
@Override
public void setTenantValue(EntityBean entityBean, Object tenantId) {
setValue(entityBean, targetDescriptor.createRef(tenantId, null));
T refBean = targetDescriptor.createReference(tenantId, null);
setValue(entityBean, refBean);
}
@Override
@@ -36,6 +36,7 @@ public final class IdBinderSimple implements IdBinder {
@SuppressWarnings("rawtypes")
private final ScalarType scalarType;
public IdBinderSimple(BeanProperty idProperty, MultiValueBind multiValueBind) {
this.idProperty = idProperty;
this.scalarType = idProperty.getScalarType();
@@ -70,6 +71,7 @@ public final class IdBinderSimple implements IdBinder {
@Override
public void buildRawSqlSelectChain(String prefix, List<String> selectChain) {
idProperty.buildRawSqlSelectChain(prefix, selectChain);
}
@@ -216,6 +218,7 @@ public final class IdBinderSimple implements IdBinder {
@Override
public String getAssocOneIdExpr(String prefix, String operator) {
StringBuilder sb = new StringBuilder();
if (prefix != null) {
sb.append(prefix);
@@ -228,6 +231,7 @@ public final class IdBinderSimple implements IdBinder {
@Override
public String getAssocIdInExpr(String prefix) {
StringBuilder sb = new StringBuilder();
if (prefix != null) {
sb.append(prefix);
@@ -239,6 +243,7 @@ public final class IdBinderSimple implements IdBinder {
@Override
public Object convertId(Object idValue) {
if (!idValue.getClass().equals(expectedType)) {
return scalarType.toBeanType(idValue);
}
@@ -247,10 +252,12 @@ public final class IdBinderSimple implements IdBinder {
@Override
public Object convertSetId(Object idValue, EntityBean bean) {
if (!idValue.getClass().equals(expectedType)) {
idValue = scalarType.toBeanType(idValue);
}
if (bean != null) {
// support PropertyChangeSupport
idProperty.setValueIntercept(bean, idValue);
}
return idValue;
@@ -407,11 +407,8 @@ public class DeployBeanDescriptor<T> {
identityMode.setSequenceGenerator(genName);
}
/**
* Return the sequence increment to use given sequence batch mode.
*/
public int setIdentitySequenceBatchMode(boolean sequenceBatchMode) {
return identityMode.setSequenceBatchMode(sequenceBatchMode);
public int setIdentitySequenceDefaultIncrement() {
return identityMode.setSequenceDefaultIncrement();
}
public void setIdentityType(IdType type) {
@@ -4,13 +4,10 @@ import io.ebean.annotation.CreatedTimestamp;
import io.ebean.annotation.DocCode;
import io.ebean.annotation.DocProperty;
import io.ebean.annotation.DocSortable;
import io.ebean.annotation.Formula;
import io.ebean.annotation.Platform;
import io.ebean.annotation.SoftDelete;
import io.ebean.annotation.UpdatedTimestamp;
import io.ebean.annotation.WhenCreated;
import io.ebean.annotation.WhenModified;
import io.ebean.annotation.Where;
import io.ebean.annotation.WhoCreated;
import io.ebean.annotation.WhoModified;
import io.ebean.config.ScalarTypeConverter;
@@ -34,13 +31,11 @@ import javax.persistence.EmbeddedId;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.Version;
import javax.validation.constraints.Size;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -195,6 +190,11 @@ public class DeployBeanProperty {
private final DeployDocPropertyOptions docMapping = new DeployDocPropertyOptions();
/**
* The method used to read the property.
*/
private Method readMethod;
private int propertyIndex;
private BeanPropertyGetter getter;
@@ -232,8 +232,6 @@ public class DeployBeanProperty {
private List<DbMigrationInfo> dbMigrationInfos;
private Set<Annotation> metaAnnotations;
public DeployBeanProperty(DeployBeanDescriptor<?> desc, Class<?> propertyType, ScalarType<?> scalarType, ScalarTypeConverter<?, ?> typeConverter) {
this.desc = desc;
this.propertyType = propertyType;
@@ -263,29 +261,29 @@ public class DeployBeanProperty {
if (field == null) {
return 0;
}
if (AnnotationUtil.get(field, Id.class) != null) {
if (AnnotationUtil.findAnnotation(field, Id.class) != null) {
return ID_ORDER;
} else if (AnnotationUtil.get(field, EmbeddedId.class) != null) {
} else if (AnnotationUtil.findAnnotation(field, EmbeddedId.class) != null) {
return ID_ORDER;
} else if (undirectionalShadow) {
return UNIDIRECTIONAL_ORDER;
} else if (isAuditProperty()) {
return AUDITCOLUMN_ORDER;
} else if (AnnotationUtil.get(field, Version.class) != null) {
} else if (AnnotationUtil.findAnnotation(field, Version.class) != null) {
return VERSIONCOLUMN_ORDER;
} else if (AnnotationUtil.get(field, SoftDelete.class) != null) {
} else if (AnnotationUtil.findAnnotation(field, SoftDelete.class) != null) {
return VERSIONCOLUMN_ORDER;
}
return 0;
}
private boolean isAuditProperty() {
return (AnnotationUtil.has(field, WhenCreated.class)
|| AnnotationUtil.has(field, WhenModified.class)
|| AnnotationUtil.has(field, WhoModified.class)
|| AnnotationUtil.has(field, WhoCreated.class)
|| AnnotationUtil.has(field, UpdatedTimestamp.class)
|| AnnotationUtil.has(field, CreatedTimestamp.class));
return (AnnotationUtil.findAnnotation(field, WhenCreated.class) != null
|| AnnotationUtil.findAnnotation(field, WhenModified.class) != null
|| AnnotationUtil.findAnnotation(field, WhoModified.class) != null
|| AnnotationUtil.findAnnotation(field, WhoCreated.class) != null
|| AnnotationUtil.findAnnotation(field, UpdatedTimestamp.class) != null
|| AnnotationUtil.findAnnotation(field, CreatedTimestamp.class) != null);
}
public String getFullBeanName() {
@@ -455,6 +453,13 @@ public class DeployBeanProperty {
}
}
/**
* Return the getter method.
*/
public Method getReadMethod() {
return readMethod;
}
/**
* Set to the owning type form a Inheritance heirarchy.
*/
@@ -667,7 +672,7 @@ public class DeployBeanProperty {
*/
private String aggregationJoin(int pos, String dbColumn) {
String p0 = aggregation.substring(0, pos + 1);
aggregationParsed = p0 + "${ta}." + dbColumn + aggregation.substring(aggregation.length() - 1);
aggregationParsed = p0 + "${ta}." + dbColumn + aggregation.substring(aggregation.length() - 1);
return aggregationParsed;
}
@@ -901,6 +906,17 @@ public class DeployBeanProperty {
this.isTransient = true;
}
/**
* Set the bean read method.
* <p>
* NB: That a BeanReflectGetter is used to actually perform the getting of
* property values from a bean. This is due to performance considerations.
* </p>
*/
public void setReadMethod(Method readMethod) {
this.readMethod = readMethod;
}
/**
* Return the property type.
*/
@@ -1106,7 +1122,7 @@ public class DeployBeanProperty {
*/
public Object /*AnnotatedField*/ getJacksonField() {
com.fasterxml.jackson.databind.introspect.AnnotatedClass jac =
(com.fasterxml.jackson.databind.introspect.AnnotatedClass) getDesc().getJacksonAnnotatedClass();
(com.fasterxml.jackson.databind.introspect.AnnotatedClass) getDesc().getJacksonAnnotatedClass();
for (com.fasterxml.jackson.databind.introspect.AnnotatedField candidate : jac.fields()) {
if (candidate.getName().equals(getName())) {
return candidate;
@@ -1114,102 +1130,4 @@ public class DeployBeanProperty {
}
return null;
}
public void initMetaAnnotations(Set<Class<?>> metaAnnotationsFilter) {
metaAnnotations = AnnotationUtil.metaFindAllFor(field, metaAnnotationsFilter);
}
@SuppressWarnings("unchecked")
public <A extends Annotation> A getMetaAnnotation(Class<A> annotationType) {
for (Annotation ann : metaAnnotations) {
if (ann.annotationType() == annotationType) {
return (A) ann;
}
}
return null;
}
@SuppressWarnings("unchecked")
public <A extends Annotation> List<A> getMetaAnnotations(Class<A> annotationType) {
List<A> result = new ArrayList<>();
for (Annotation ann : metaAnnotations) {
if (ann.annotationType() == annotationType) {
result.add((A) ann);
}
}
return result;
}
public List<Size> getMetaAnnotationSize() {
final List<Size> size = getMetaAnnotations(Size.class);
final List<Size.List> lists = getMetaAnnotations(Size.List.class);
for (Size.List list : lists) {
Collections.addAll(size, list.value());
}
return size;
}
public Formula getMetaAnnotationFormula(Platform platform) {
Formula fallback = null;
for (Annotation ann : metaAnnotations) {
if (ann.annotationType() == Formula.class) {
Formula formula = (Formula) ann;
final Platform[] platforms = formula.platforms();
if (platforms.length == 0) {
fallback = formula;
} else if (matchPlatform(platforms, platform)) {
return formula;
}
} else if (ann.annotationType() == Formula.List.class) {
Formula.List formulaList = (Formula.List) ann;
for (Formula formula : formulaList.value()) {
final Platform[] platforms = formula.platforms();
if (platforms.length == 0) {
fallback = formula;
} else if (matchPlatform(platforms, platform)) {
return formula;
}
}
}
}
return fallback;
}
public Where getMetaAnnotationWhere(Platform platform) {
Where fallback = null;
for (Annotation ann : metaAnnotations) {
if (ann.annotationType() == Where.class) {
Where where = (Where) ann;
final Platform[] platforms = where.platforms();
if (platforms.length == 0) {
fallback = where;
} else if (matchPlatform(where.platforms(), platform)) {
return where;
}
} else if (ann.annotationType() == Where.List.class) {
Where.List whereList = (Where.List) ann;
for (Where where : whereList.value()) {
final Platform[] platforms = where.platforms();
if (platforms.length == 0) {
fallback = where;
} else if (matchPlatform(where.platforms(), platform)) {
return where;
}
}
}
}
return fallback;
}
private boolean matchPlatform(Platform[] platforms, Platform match) {
for (Platform platform : platforms) {
if (platform == match) {
return true;
}
}
return false;
}
}
@@ -54,11 +54,8 @@ public class DeployIdentityMode {
}
}
public int setSequenceBatchMode(boolean sequenceBatchMode) {
if (sequenceBatchMode) {
// Ebean batch fetching multiple sequence values
increment = 1;
} else if (increment == 0) {
public int setSequenceDefaultIncrement() {
if (increment == 0) {
// Use JPA default of 50
increment = 50;
}
@@ -125,7 +125,7 @@ class AnnotationAssocManys extends AnnotationParser {
prop.setMapKey(mapKey.name());
}
Where where = prop.getMetaAnnotationWhere(platform);
Where where = get(prop, Where.class);
if (where != null) {
prop.setExtraWhere(where.clause());
}
@@ -138,8 +138,8 @@ class AnnotationAssocManys extends AnnotationParser {
// check for manually defined joins
BeanTable beanTable = prop.getBeanTable();
Set<JoinColumn> joinColumns = annotationJoinColumns(prop);
if (!joinColumns.isEmpty()) {
Set<JoinColumn> joinColumns = getAll(prop, JoinColumn.class);
if (joinColumns != null) {
prop.getTableJoin().addJoinColumn(util, true, joinColumns, beanTable);
}
@@ -194,7 +194,7 @@ class AnnotationAssocManys extends AnnotationParser {
if (!elementCollection.targetClass().equals(void.class)) {
prop.setTargetType(elementCollection.targetClass());
}
Column column = prop.getMetaAnnotation(Column.class);
Column column = get(prop, Column.class);
if (column != null) {
prop.setDbColumn(column.name());
prop.setDbLength(column.length());
@@ -82,7 +82,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
prop.setId();
prop.setNullable(false);
}
Column column = prop.getMetaAnnotation(Column.class);
Column column = get(prop, Column.class);
if (column != null && !isEmpty(column.name())) {
// have this in for AssocOnes used on
// Sql based beans...
@@ -100,7 +100,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
prop.setForeignKey(new PropertyForeignKey(dbForeignKey));
}
Where where = prop.getMetaAnnotationWhere(platform);
Where where = get(prop, Where.class);
if (where != null) {
// not expecting this to be used on assoc one properties
prop.setExtraWhere(where.clause());
@@ -131,11 +131,12 @@ public class AnnotationAssocOnes extends AnnotationParser {
// check for manually defined joins
BeanTable beanTable = prop.getBeanTable();
for (JoinColumn joinColumn : annotationJoinColumns(prop)) {
for (JoinColumn joinColumn : getAll(prop, JoinColumn.class)) {
setFromJoinColumn(prop, beanTable, joinColumn);
checkForNoConstraint(prop, joinColumn);
}
JoinTable joinTable = get(prop, JoinTable.class);
if (joinTable != null) {
for (JoinColumn joinColumn : joinTable.joinColumns()) {
@@ -1,27 +1,16 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.annotation.Aggregation;
import io.ebean.annotation.Avg;
import io.ebean.annotation.DbMigration;
import io.ebean.annotation.Index;
import io.ebean.annotation.Indices;
import io.ebean.annotation.Max;
import io.ebean.annotation.Min;
import io.ebean.annotation.Platform;
import io.ebean.annotation.Sum;
import io.ebean.config.NamingConvention;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.util.AnnotationUtil;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import javax.persistence.AttributeOverride;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Collections;
import java.lang.reflect.Method;
import java.util.Set;
/**
@@ -52,7 +41,7 @@ import java.util.Set;
abstract class AnnotationBase {
final DatabasePlatform databasePlatform;
protected final Platform platform;
private final Platform platform;
final NamingConvention namingConvention;
final DeployUtil util;
@@ -75,43 +64,68 @@ abstract class AnnotationBase {
return s == null || s.trim().isEmpty();
}
/**
* Return the annotation for the property.
* <p>
* Looks first at the field and then at the getter method. It searches for meta-annotations, but not
* recursively in the class hierarchy.
* </p>
* <p>
* If a <code>repeatable</code> annotation class is specified and the annotation is platform
* specific then the platform specific annotation is returned. Otherwise the first annotation
* is returned. Note that you need no longer handle "java 1.6 repeatable containers"
* like {@link JoinColumn} / {@link JoinColumns} yourself.
* </p>
* <p>
*/
<T extends Annotation> T get(DeployBeanProperty prop, Class<T> annClass) {
return AnnotationUtil.get(prop.getField(), annClass);
}
<T extends Annotation> boolean has(DeployBeanProperty prop, Class<T> annClass) {
return AnnotationUtil.has(prop.getField(), annClass);
}
Set<JoinColumn> annotationJoinColumns(DeployBeanProperty prop) {
return AnnotationFind.joinColumns(prop.getField());
}
Set<AttributeOverride> annotationAttributeOverrides(DeployBeanProperty prop) {
return AnnotationFind.attributeOverrides(prop.getField());
}
Set<Index> annotationIndexes(DeployBeanProperty prop) {
return AnnotationFind.indexes(prop.getField());
}
Set<DbMigration> annotationDbMigrations(DeployBeanProperty prop) {
return AnnotationFind.dbMigrations(prop.getField());
}
Set<Index> annotationClassIndexes(Class<?> cls) {
Set<Index> result = AnnotationUtil.typeGetAll(cls, Index.class);
for (Indices index : AnnotationUtil.typeGetAll(cls, Indices.class)) {
Collections.addAll(result, index.value());
T a = null;
Field field = prop.getField();
if (field != null) {
a = AnnotationUtil.findAnnotation(field, annClass, platform);
}
return result;
if (a == null) {
Method method = prop.getReadMethod();
if (method != null) {
a = AnnotationUtil.findAnnotation(method, annClass, platform);
}
}
return a;
}
Set<NamedQuery> annotationClassNamedQuery(Class<?> cls) {
Set<NamedQuery> result = AnnotationUtil.typeGetAll(cls, NamedQuery.class);
for (NamedQueries queries : AnnotationUtil.typeGetAll(cls, NamedQueries.class)) {
Collections.addAll(result, queries.value());
/**
* Return all annotations for this property. Annotations are not filtered by platfrom and you'll get
* really all annotations that are directly, indirectly or meta-present.
*/
<T extends Annotation> Set<T> getAll(DeployBeanProperty prop, Class<T> annClass) {
Set<T> ret = null;
Field field = prop.getField();
if (field != null) {
ret = AnnotationUtil.findAnnotations(field, annClass);
}
return result;
Method method = prop.getReadMethod();
if (method != null) {
if (ret != null) {
ret.addAll(AnnotationUtil.findAnnotations(method, annClass));
} else {
ret = AnnotationUtil.findAnnotations(method, annClass);
}
}
return ret;
}
/**
* Return the annotation for the property.
* <p>
* Looks first at the field and then at the getter method. then at class level.
* (This is used for SequenceGenerator e.g.)
* </p>
*/
<T extends Annotation> T find(DeployBeanProperty prop, Class<T> annClass) {
T a = get(prop, annClass);
if (a == null) {
a = AnnotationUtil.findAnnotation(prop.getOwningType(), annClass, platform);
}
return a;
}
}
@@ -31,7 +31,8 @@ import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import static io.ebean.util.AnnotationUtil.typeGet;
import static io.ebean.util.AnnotationUtil.findAnnotationRecursive;
import static io.ebean.util.AnnotationUtil.findAnnotationsRecursive;
/**
* Read the class level deployment annotations.
@@ -61,8 +62,9 @@ public class AnnotationClass extends AnnotationParser {
* Parse any AttributeOverride set on the class.
*/
void parseAttributeOverride() {
Class<?> cls = descriptor.getBeanType();
AttributeOverride override = typeGet(cls, AttributeOverride.class);
AttributeOverride override = findAnnotationRecursive(cls, AttributeOverride.class);
if (override != null) {
String propertyName = override.name();
Column column = override.column();
@@ -102,15 +104,16 @@ public class AnnotationClass extends AnnotationParser {
}
private void read(Class<?> cls) {
// maybe doc store only so check for this before @Entity
DocStore docStore = typeGet(cls, DocStore.class);
DocStore docStore = findAnnotationRecursive(cls, DocStore.class);
if (docStore != null) {
descriptor.readDocStore(docStore);
descriptor.setEntityType(EntityType.DOC);
descriptor.setName(cls.getSimpleName());
}
Entity entity = typeGet(cls, Entity.class);
Entity entity = findAnnotationRecursive(cls, Entity.class);
if (entity != null) {
descriptor.setEntityType(EntityType.ORM);
if (entity.name().isEmpty()) {
@@ -120,37 +123,37 @@ public class AnnotationClass extends AnnotationParser {
}
}
Identity identity = typeGet(cls, Identity.class);
Identity identity = findAnnotationRecursive(cls, Identity.class);
if (identity != null) {
descriptor.setIdentityMode(identity);
}
IdClass idClass = typeGet(cls, IdClass.class);
IdClass idClass = findAnnotationRecursive(cls, IdClass.class);
if (idClass != null) {
descriptor.setIdClass(idClass.value());
}
Embeddable embeddable = typeGet(cls, Embeddable.class);
Embeddable embeddable = findAnnotationRecursive(cls, Embeddable.class);
if (embeddable != null) {
descriptor.setEntityType(EntityType.EMBEDDED);
descriptor.setName("Embeddable:" + cls.getSimpleName());
}
for (Index index : annotationClassIndexes(cls)) {
for (Index index : findAnnotationsRecursive(cls, Index.class)) {
descriptor.addIndex(new IndexDefinition(convertColumnNames(index.columnNames()), index.name(),
index.unique(), index.platforms(), index.concurrent(), index.definition()));
}
UniqueConstraint uc = typeGet(cls, UniqueConstraint.class);
UniqueConstraint uc = findAnnotationRecursive(cls, UniqueConstraint.class);
if (uc != null) {
descriptor.addIndex(new IndexDefinition(convertColumnNames(uc.columnNames())));
}
View view = typeGet(cls, View.class);
View view = findAnnotationRecursive(cls, View.class);
if (view != null) {
descriptor.setView(view.name(), view.dependentTables());
}
Table table = typeGet(cls, Table.class);
Table table = findAnnotationRecursive(cls, Table.class);
if (table != null) {
UniqueConstraint[] uniqueConstraints = table.uniqueConstraints();
for (UniqueConstraint c : uniqueConstraints) {
@@ -158,54 +161,54 @@ public class AnnotationClass extends AnnotationParser {
}
}
StorageEngine storage = typeGet(cls, StorageEngine.class);
StorageEngine storage = findAnnotationRecursive(cls, StorageEngine.class);
if (storage != null) {
descriptor.setStorageEngine(storage.value());
}
DbPartition partition = typeGet(cls, DbPartition.class);
DbPartition partition = findAnnotationRecursive(cls, DbPartition.class);
if (partition != null) {
descriptor.setPartitionMeta(new PartitionMeta(partition.mode(), partition.property()));
}
Draftable draftable = typeGet(cls, Draftable.class);
Draftable draftable = findAnnotationRecursive(cls, Draftable.class);
if (draftable != null) {
descriptor.setDraftable();
}
DraftableElement draftableElement = typeGet(cls, DraftableElement.class);
DraftableElement draftableElement = findAnnotationRecursive(cls, DraftableElement.class);
if (draftableElement != null) {
descriptor.setDraftableElement();
}
ReadAudit readAudit = typeGet(cls, ReadAudit.class);
ReadAudit readAudit = findAnnotationRecursive(cls, ReadAudit.class);
if (readAudit != null) {
descriptor.setReadAuditing();
}
History history = typeGet(cls, History.class);
History history = findAnnotationRecursive(cls, History.class);
if (history != null) {
descriptor.setHistorySupport();
}
DbComment comment = typeGet(cls, DbComment.class);
DbComment comment = findAnnotationRecursive(cls, DbComment.class);
if (comment != null) {
descriptor.setDbComment(comment.value());
}
if (!disableL2Cache) {
Cache cache = typeGet(cls, Cache.class);
Cache cache = findAnnotationRecursive(cls, Cache.class);
if (cache != null) {
descriptor.setCache(cache);
} else {
InvalidateQueryCache invalidateQueryCache = typeGet(cls, InvalidateQueryCache.class);
InvalidateQueryCache invalidateQueryCache = findAnnotationRecursive(cls, InvalidateQueryCache.class);
if (invalidateQueryCache != null) {
descriptor.setInvalidateQueryCache(invalidateQueryCache.region());
}
}
}
for (NamedQuery namedQuery : annotationClassNamedQuery(cls)) {
for (NamedQuery namedQuery : findAnnotationsRecursive(cls, NamedQuery.class)) {
descriptor.addNamedQuery(namedQuery.name(), namedQuery.query());
}
}
@@ -106,8 +106,8 @@ public class AnnotationFields extends AnnotationParser {
*/
@Override
public void parse() {
for (DeployBeanProperty prop : descriptor.propertiesAll()) {
prop.initMetaAnnotations(readConfig.getMetaAnnotations());
if (prop instanceof DeployBeanPropertyAssoc<?>) {
readAssocOne((DeployBeanPropertyAssoc<?>) prop);
} else {
@@ -123,11 +123,13 @@ public class AnnotationFields extends AnnotationParser {
readJsonAnnotations(prop);
if (has(prop, Id.class)) {
Id id = get(prop, Id.class);
if (id != null) {
readIdAssocOne(prop);
}
if (has(prop, EmbeddedId.class)) {
EmbeddedId embeddedId = get(prop, EmbeddedId.class);
if (embeddedId != null) {
prop.setId();
prop.setNullable(false);
prop.setEmbedded();
@@ -138,7 +140,7 @@ public class AnnotationFields extends AnnotationParser {
if (docEmbedded != null) {
prop.setDocStoreEmbedded(docEmbedded.doc());
if (descriptor.isDocStoreOnly()) {
if (has(prop, ManyToOne.class)) {
if (get(prop, ManyToOne.class) == null) {
prop.setEmbedded();
prop.setDbInsertable(true);
prop.setDbUpdateable(true);
@@ -153,20 +155,20 @@ public class AnnotationFields extends AnnotationParser {
readEmbeddedAttributeOverrides((DeployBeanPropertyAssocOne<?>) prop);
}
Formula formula = prop.getMetaAnnotationFormula(platform);
Formula formula = get(prop, Formula.class);
if (formula != null) {
prop.setSqlFormula(formula.select(), formula.join());
}
initWhoProperties(prop);
initDbMigration(prop);
readDbMigration(prop);
}
private void initWhoProperties(DeployBeanProperty prop) {
if (has(prop, WhoModified.class)) {
if (get(prop, WhoModified.class) != null) {
generatedPropFactory.setWhoModified(prop);
}
if (has(prop, WhoCreated.class)) {
if (get(prop, WhoCreated.class) != null) {
generatedPropFactory.setWhoCreated(prop);
}
}
@@ -186,7 +188,7 @@ public class AnnotationFields extends AnnotationParser {
prop.setDbInsertable(true);
prop.setDbUpdateable(true);
Column column = prop.getMetaAnnotation(Column.class);
Column column = get(prop, Column.class);
if (column != null) {
readColumn(column, prop);
}
@@ -198,32 +200,6 @@ public class AnnotationFields extends AnnotationParser {
prop.setDbColumn(namingConvention.getColumnFromProperty(beanType, prop.getName()));
}
initIdentity(prop);
initTenantId(prop);
initDbJson(prop);
initFormula(prop);
initVersion(prop);
initWhen(prop);
initWhoProperties(prop);
initDbMigration(prop);
// Want to process last so we can use with @Formula
if (has(prop, Transient.class)) {
// it is not a persistent property.
prop.setDbRead(false);
prop.setDbInsertable(false);
prop.setDbUpdateable(false);
prop.setTransient();
}
initEncrypt(prop);
for (Index index : annotationIndexes(prop)) {
addIndex(prop, index);
}
}
private void initIdentity(DeployBeanProperty prop) {
Id id = get(prop, Id.class);
GeneratedValue gen = get(prop, GeneratedValue.class);
if (gen != null) {
@@ -243,7 +219,7 @@ public class AnnotationFields extends AnnotationParser {
if (temporal != null) {
readTemporal(temporal, prop);
} else if (has(prop, Lob.class)) {
} else if (get(prop, Lob.class) != null) {
util.setLobType(prop);
}
@@ -252,58 +228,53 @@ public class AnnotationFields extends AnnotationParser {
prop.setDbLength(length.value());
}
if (has(prop, io.ebean.annotation.NotNull.class)) {
prop.setNullable(false);
}
}
private void initValidation(DeployBeanProperty prop) {
NotNull notNull = get(prop, NotNull.class);
if (notNull != null && isEbeanValidationGroups(notNull.groups())) {
// Not null on all validation groups so enable
// DDL generation of Not Null Constraint
io.ebean.annotation.NotNull nonNull = get(prop, io.ebean.annotation.NotNull.class);
if (nonNull != null) {
prop.setNullable(false);
}
if (!prop.isLob()) {
// take the max size of all @Size annotations
int maxSize = -1;
for (Size size : prop.getMetaAnnotationSize()) {
if (size.max() < Integer.MAX_VALUE) {
maxSize = Math.max(maxSize, size.max());
if (validationAnnotations) {
NotNull notNull = get(prop, NotNull.class);
if (notNull != null && isEbeanValidationGroups(notNull.groups())) {
// Not null on all validation groups so enable
// DDL generation of Not Null Constraint
prop.setNullable(false);
}
if (!prop.isLob()) {
// take the max size of all @Size annotations
int maxSize = -1;
for (Size size : getAll(prop, Size.class)) {
if (size.max() < Integer.MAX_VALUE) {
maxSize = Math.max(maxSize, size.max());
}
}
if (maxSize != -1) {
prop.setDbLength(maxSize);
}
}
if (maxSize != -1) {
prop.setDbLength(maxSize);
}
}
}
private void initTenantId(DeployBeanProperty prop) {
if (validationAnnotations) {
initValidation(prop);
}
if (has(prop, TenantId.class)) {
if (get(prop, TenantId.class) != null) {
prop.setTenantId();
}
if (has(prop, Draft.class)) {
if (get(prop, Draft.class) != null) {
prop.setDraft();
}
if (has(prop, DraftOnly.class)) {
if (get(prop, DraftOnly.class) != null) {
prop.setDraftOnly();
}
if (has(prop, DraftDirty.class)) {
if (get(prop, DraftDirty.class) != null) {
prop.setDraftDirty();
}
if (has(prop, DraftReset.class)) {
if (get(prop, DraftReset.class) != null) {
prop.setDraftReset();
}
if (has(prop, SoftDelete.class)) {
SoftDelete softDelete = get(prop, SoftDelete.class);
if (softDelete != null) {
prop.setSoftDelete();
}
}
private void initDbJson(DeployBeanProperty prop) {
DbComment comment = get(prop, DbComment.class);
if (comment != null) {
prop.setDbComment(comment.value());
@@ -325,9 +296,7 @@ public class AnnotationFields extends AnnotationParser {
if (dbArray != null) {
util.setDbArray(prop, dbArray);
}
}
private void initFormula(DeployBeanProperty prop) {
DocCode docCode = get(prop, DocCode.class);
if (docCode != null) {
prop.setDocCode(docCode);
@@ -340,19 +309,19 @@ public class AnnotationFields extends AnnotationParser {
if (docProperty != null) {
prop.setDocProperty(docProperty);
}
Formula formula = prop.getMetaAnnotationFormula(platform);
Formula formula = get(prop, Formula.class);
if (formula != null) {
prop.setSqlFormula(formula.select(), formula.join());
}
final Aggregation aggregation = prop.getMetaAnnotation(Aggregation.class);
Aggregation aggregation = get(prop, Aggregation.class);
if (aggregation != null) {
prop.setAggregation(aggregation.value().replace("$1", prop.getName()));
}
}
private void initVersion(DeployBeanProperty prop) {
if (has(prop, Version.class)) {
Version version = get(prop, Version.class);
if (version != null) {
// explicitly specify a version column
prop.setVersionColumn();
generatedPropFactory.setVersion(prop);
@@ -368,19 +337,35 @@ public class AnnotationFields extends AnnotationParser {
// use the default Lob fetchType
prop.setFetchType(defaultLobFetchType);
}
}
private void initWhen(DeployBeanProperty prop) {
if (has(prop, WhenCreated.class) || has(prop, CreatedTimestamp.class)) {
if (get(prop, WhenCreated.class) != null || get(prop, CreatedTimestamp.class) != null) {
generatedPropFactory.setInsertTimestamp(prop);
}
if (has(prop, WhenModified.class) || has(prop, UpdatedTimestamp.class)) {
if (get(prop, WhenModified.class) != null || get(prop, UpdatedTimestamp.class) != null) {
generatedPropFactory.setUpdateTimestamp(prop);
}
}
private void initEncrypt(DeployBeanProperty prop) {
initWhoProperties(prop);
if (get(prop, HistoryExclude.class) != null) {
prop.setExcludedFromHistory();
}
readDbMigration(prop);
// Want to process last so we can use with @Formula
Transient t = get(prop, Transient.class);
if (t != null) {
// it is not a persistent property.
prop.setDbRead(false);
prop.setDbInsertable(false);
prop.setDbUpdateable(false);
prop.setTransient();
}
if (!prop.isTransient()) {
EncryptDeploy encryptDeploy = util.getEncryptDeploy(info.getDescriptor().getBaseTableFull(), prop.getDbColumn());
if (encryptDeploy == null || encryptDeploy.getMode() == Mode.MODE_ANNOTATION) {
Encrypted encrypted = get(prop, Encrypted.class);
@@ -391,22 +376,24 @@ public class AnnotationFields extends AnnotationParser {
setEncryption(prop, encryptDeploy.isDbEncrypt(), encryptDeploy.getDbLength());
}
}
Set<Index> indices = getAll(prop, Index.class);
for (Index index : indices) {
addIndex(prop, index);
}
}
private void readIdentity(Identity identity) {
descriptor.setIdentityMode(identity);
}
private void initDbMigration(DeployBeanProperty prop) {
if (has(prop, HistoryExclude.class)) {
prop.setExcludedFromHistory();
}
private void readDbMigration(DeployBeanProperty prop) {
DbDefault dbDefault = get(prop, DbDefault.class);
if (dbDefault != null) {
prop.setDbColumnDefault(dbDefault.value());
}
Set<DbMigration> dbMigration = annotationDbMigrations(prop);
Set<DbMigration> dbMigration = getAll(prop, DbMigration.class);
dbMigration.forEach(ann -> prop.addDbMigrationInfo(
new DbMigrationInfo(ann.preAdd(), ann.postAdd(), ann.preAlter(), ann.postAlter(), ann.platforms())));
}
@@ -458,13 +445,15 @@ public class AnnotationFields extends AnnotationParser {
prop.setJsonSerialize(jsonIgnore.serialize());
prop.setJsonDeserialize(jsonIgnore.deserialize());
}
if (has(prop, UnmappedJson.class)) {
if (get(prop, UnmappedJson.class) != null) {
prop.setUnmappedJson();
}
}
private boolean hasRelationshipItem(DeployBeanProperty prop) {
return has(prop, OneToMany.class) || has(prop, ManyToOne.class) || has(prop, OneToOne.class);
return get(prop, OneToMany.class) != null ||
get(prop, ManyToOne.class) != null ||
get(prop, OneToOne.class) != null;
}
private void setEncryption(DeployBeanProperty prop, boolean dbEncString, int dbLen) {
@@ -539,7 +528,7 @@ public class AnnotationFields extends AnnotationParser {
}
descriptor.setIdGeneratedValue();
SequenceGenerator seq = get(prop, SequenceGenerator.class);
SequenceGenerator seq = find(prop, SequenceGenerator.class);
if (seq != null) {
String seqName = seq.sequenceName();
if (seqName.isEmpty()) {
@@ -1,75 +0,0 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.annotation.DbMigration;
import io.ebean.annotation.Index;
import io.ebean.annotation.Indices;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import static io.ebean.util.AnnotationUtil.get;
public class AnnotationFind {
public static Set<JoinColumn> joinColumns(Field field) {
final JoinColumn col = get(field, JoinColumn.class);
if (col != null) {
return Collections.singleton(col);
}
final JoinColumns cols = get(field, JoinColumns.class);
if (cols != null) {
Set<JoinColumn> result = new LinkedHashSet<>();
Collections.addAll(result, cols.value());
return result;
}
return Collections.emptySet();
}
public static Set<AttributeOverride> attributeOverrides(Field field) {
final AttributeOverride ann = get(field, AttributeOverride.class);
if (ann != null) {
return Collections.singleton(ann);
}
final AttributeOverrides collection = get(field, AttributeOverrides.class);
if (collection != null) {
Set<AttributeOverride> result = new LinkedHashSet<>();
Collections.addAll(result, collection.value());
return result;
}
return Collections.emptySet();
}
public static Set<Index> indexes(Field field) {
final Index ann = get(field, Index.class);
if (ann != null) {
return Collections.singleton(ann);
}
final Indices collection = get(field, Indices.class);
if (collection != null) {
Set<Index> result = new LinkedHashSet<>();
Collections.addAll(result, collection.value());
return result;
}
return Collections.emptySet();
}
public static Set<DbMigration> dbMigrations(Field field) {
final DbMigration ann = get(field, DbMigration.class);
if (ann != null) {
return Collections.singleton(ann);
}
final DbMigration.List collection = get(field, DbMigration.List.class);
if (collection != null) {
Set<DbMigration> result = new LinkedHashSet<>();
Collections.addAll(result, collection.value());
return result;
}
return Collections.emptySet();
}
}
@@ -87,7 +87,7 @@ public abstract class AnnotationParser extends AnnotationBase {
*/
void readEmbeddedAttributeOverrides(DeployBeanPropertyAssocOne<?> prop) {
Set<AttributeOverride> attrOverrides = annotationAttributeOverrides(prop);
Set<AttributeOverride> attrOverrides = getAll(prop, AttributeOverride.class);
if (!attrOverrides.isEmpty()) {
HashMap<String, String> propMap = new HashMap<>(attrOverrides.size());
for (AttributeOverride attrOverride : attrOverrides) {
@@ -95,6 +95,7 @@ public abstract class AnnotationParser extends AnnotationBase {
}
prop.getDeployEmbedded().putAll(propMap);
}
}
void readColumn(Column columnAnn, DeployBeanProperty prop) {
@@ -16,7 +16,8 @@ class AnnotationSql extends AnnotationParser {
@Override
public void parse() {
Class<?> cls = descriptor.getBeanType();
if (AnnotationUtil.typeHas(cls, Sql.class)) {
Sql sql = AnnotationUtil.findAnnotationRecursive(cls, Sql.class);
if (sql != null) {
descriptor.setEntityType(BeanDescriptor.EntityType.SQL);
}
}
@@ -23,6 +23,7 @@ import javax.persistence.ManyToOne;
import javax.persistence.PersistenceException;
import javax.persistence.Transient;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
@@ -52,6 +53,7 @@ public class DeployCreateProperties {
* Create the appropriate properties for a bean.
*/
public void createProperties(DeployBeanDescriptor<?> desc) {
createProperties(desc, desc.getBeanType(), 0);
desc.sortProperties();
}
@@ -87,14 +89,22 @@ public class DeployCreateProperties {
// ignore all fields on model (_$dbName)
return;
}
boolean scalaObject = desc.isScalaObject();
try {
Method[] declaredMethods = beanType.getDeclaredMethods();
Field[] fields = beanType.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (!ignoreField(field)) {
DeployBeanProperty prop = createProp(desc, field, beanType);
String fieldName = getFieldName(field, beanType);
String initFieldName = initCap(fieldName);
Method getter = findGetter(field, initFieldName, declaredMethods, scalaObject);
DeployBeanProperty prop = createProp(desc, field, beanType, getter);
if (prop != null) {
// set a order that gives priority to inherited properties
// push Id/EmbeddedId up and CreatedTimestamp/UpdatedTimestamp down
@@ -127,6 +137,68 @@ public class DeployCreateProperties {
}
}
/**
* Make the first letter of the string upper case.
*/
private String initCap(String str) {
if (str.length() > 1) {
return Character.toUpperCase(str.charAt(0)) + str.substring(1);
} else {
// only a single char
return str.toUpperCase();
}
}
/**
* Return the bean spec field name (trim of "is" from boolean types)
*/
private String getFieldName(Field field, Class<?> beanType) {
String name = field.getName();
if ((Boolean.class.equals(field.getType()) || boolean.class.equals(field.getType()))
&& name.startsWith("is") && name.length() > 2) {
// it is a boolean type field starting with "is"
char c = name.charAt(2);
if (Character.isUpperCase(c)) {
String msg = "trimming off 'is' from boolean field name " + name + " in class " + beanType.getName();
logger.info(msg);
return name.substring(2);
}
}
return name;
}
/**
* Find a public non-static getter method that matches this field (according to bean-spec rules).
*/
private Method findGetter(Field field, String initFieldName, Method[] declaredMethods, boolean scalaObject) {
String methGetName = "get" + initFieldName;
String methIsName = "is" + initFieldName;
String scalaGet = field.getName();
for (Method m : declaredMethods) {
if ((scalaObject && m.getName().equals(scalaGet)) || m.getName().equals(methGetName)
|| m.getName().equals(methIsName)) {
Class<?>[] params = m.getParameterTypes();
if (params.length == 0) {
if (field.getType().equals(m.getReturnType())) {
int modifiers = m.getModifiers();
if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) {
// we find it...
return m;
}
}
}
}
}
return null;
}
@SuppressWarnings({"unchecked"})
private DeployBeanProperty createManyType(DeployBeanDescriptor<?> desc, Class<?> targetType, ManyType manyType) {
@@ -146,7 +218,7 @@ public class DeployCreateProperties {
Class<?> propertyType = field.getType();
ManyToOne manyToOne = AnnotationUtil.get(field, ManyToOne.class);
ManyToOne manyToOne = AnnotationUtil.findAnnotation(field, ManyToOne.class);
if (manyToOne != null) {
Class<?> tt = manyToOne.targetEntity();
if (!tt.equals(void.class)) {
@@ -163,7 +235,8 @@ public class DeployCreateProperties {
// List, Set or Map based object
Class<?> targetType = determineTargetType(field);
if (targetType == null) {
if (AnnotationUtil.has(field, Transient.class)) {
Transient transAnnotation = AnnotationUtil.findAnnotation(field, Transient.class);
if (transAnnotation != null) {
// not supporting this field (generic type used)
return null;
}
@@ -198,18 +271,21 @@ public class DeployCreateProperties {
* Return true if the field has one of the special mappings.
*/
private boolean isSpecialScalarType(Field field) {
return (AnnotationUtil.has(field, DbJson.class))
|| (AnnotationUtil.has(field, DbJsonB.class))
|| (AnnotationUtil.has(field, DbArray.class))
|| (AnnotationUtil.has(field, DbMap.class))
|| (AnnotationUtil.has(field, UnmappedJson.class));
return (AnnotationUtil.findAnnotation(field, DbJson.class) != null)
|| (AnnotationUtil.findAnnotation(field, DbJsonB.class) != null)
|| (AnnotationUtil.findAnnotation(field, DbArray.class) != null)
|| (AnnotationUtil.findAnnotation(field, DbMap.class) != null)
|| (AnnotationUtil.findAnnotation(field, UnmappedJson.class) != null);
}
private boolean isTransientField(Field field) {
return AnnotationUtil.has(field, Transient.class);
Transient t = AnnotationUtil.findAnnotation(field, Transient.class);
return (t != null);
}
private DeployBeanProperty createProp(DeployBeanDescriptor<?> desc, Field field, Class<?> beanType) {
private DeployBeanProperty createProp(DeployBeanDescriptor<?> desc, Field field, Class<?> beanType, Method getter) {
DeployBeanProperty prop = createProp(desc, field);
if (prop == null) {
// transient annotation on unsupported type
@@ -217,6 +293,9 @@ public class DeployCreateProperties {
} else {
prop.setOwningType(beanType);
prop.setName(field.getName());
// interested in the getter for reading annotations
prop.setReadMethod(getter);
prop.setField(field);
return prop;
}
@@ -8,6 +8,7 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Inheritance;
import java.lang.annotation.Annotation;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -109,11 +110,11 @@ public class DeployInherit {
info.setParent(parent);
}
Inheritance ia = AnnotationUtil.typeGet(cls, Inheritance.class);
Inheritance ia = AnnotationUtil.findAnnotationRecursive(cls, Inheritance.class);
if (ia != null) {
ia.strategy();
}
DiscriminatorColumn da = AnnotationUtil.typeGet(cls, DiscriminatorColumn.class);
DiscriminatorColumn da = AnnotationUtil.findAnnotationRecursive(cls, DiscriminatorColumn.class);
if (da != null) {
// lowercase the discriminator column for RawSql and JSON
info.setColumnName(da.name().toLowerCase());
@@ -123,7 +124,7 @@ public class DeployInherit {
}
if (!info.isAbstract()) {
DiscriminatorValue dv = AnnotationUtil.get(cls, DiscriminatorValue.class); // do not search recursive
DiscriminatorValue dv = AnnotationUtil.findAnnotation(cls, DiscriminatorValue.class); // do not search recursive
if (dv != null) {
info.setDiscriminatorValue(dv.value());
} else {
@@ -143,7 +144,17 @@ public class DeployInherit {
}
private boolean isInheritanceClass(Class<?> cls) {
return AnnotationUtil.typeHas(cls, Inheritance.class);
while (true) {
if (cls.equals(Object.class)) {
return false;
}
Annotation a = AnnotationUtil.findAnnotationRecursive(cls, Inheritance.class);
if (a != null) {
return true;
}
// search up the inheritance heirarchy
cls = cls.getSuperclass();
}
}
}
@@ -1,8 +0,0 @@
package io.ebeaninternal.server.deploy.parse;
class InitMetaJacksonAnnotation {
static void init(ReadAnnotationConfig readConfig) {
readConfig.addMetaAnnotation(com.fasterxml.jackson.annotation.JacksonAnnotation.class);
}
}
@@ -1,11 +0,0 @@
package io.ebeaninternal.server.deploy.parse;
import javax.validation.constraints.Size;
class InitMetaValidationAnnotation {
static void init(ReadAnnotationConfig readConfig) {
readConfig.addMetaAnnotation(Size.class);
readConfig.addMetaAnnotation(Size.List.class);
}
}
@@ -1,15 +1,8 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.annotation.Aggregation;
import io.ebean.annotation.Formula;
import io.ebean.annotation.Where;
import io.ebean.config.ServerConfig;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
import javax.persistence.Column;
import java.util.HashSet;
import java.util.Set;
/**
* Configuration used when reading the deployment annotations.
*/
@@ -24,8 +17,6 @@ class ReadAnnotationConfig {
private final boolean jacksonAnnotations;
private final boolean idGeneratorAutomatic;
private final Set<Class<?>> metaAnnotations = new HashSet<>();
ReadAnnotationConfig(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, ServerConfig serverConfig) {
this.generatedPropFactory = generatedPropFactory;
@@ -37,16 +28,6 @@ class ReadAnnotationConfig {
this.javaxValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJavaxValidationAnnotationsPresent();
this.jacksonAnnotations = generatedPropFactory.getClassLoadConfig().isJacksonAnnotationsPresent();
this.metaAnnotations.add(Column.class);
this.metaAnnotations.add(Formula.class);
this.metaAnnotations.add(Formula.List.class);
this.metaAnnotations.add(Where.class);
this.metaAnnotations.add(Where.List.class);
this.metaAnnotations.add(Aggregation.class);
}
public void addMetaAnnotation(Class<?> annotation) {
metaAnnotations.add(annotation);
}
GeneratedPropertyFactory getGeneratedPropFactory() {
@@ -80,8 +61,4 @@ class ReadAnnotationConfig {
boolean isJacksonAnnotations() {
return jacksonAnnotations;
}
public Set<Class<?>> getMetaAnnotations() {
return metaAnnotations;
}
}
@@ -14,12 +14,6 @@ public class ReadAnnotations {
public ReadAnnotations(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, ServerConfig serverConfig) {
this.readConfig = new ReadAnnotationConfig(generatedPropFactory, asOfViewSuffix, versionsBetweenSuffix, serverConfig);
if (readConfig.isJavaxValidationAnnotations()) {
InitMetaValidationAnnotation.init(readConfig);
}
if (readConfig.isJacksonAnnotations()) {
InitMetaJacksonAnnotation.init(readConfig);
}
}
/**
@@ -104,18 +104,16 @@ public class IdInExpression extends NonPrepareExpression {
@Override
public void addSql(SpiExpressionRequest request) {
BeanDescriptor<?> descriptor = request.getBeanDescriptor();
DefaultExpressionRequest r = (DefaultExpressionRequest) request;
BeanDescriptor<?> descriptor = r.getBeanDescriptor();
IdBinder idBinder = descriptor.getIdBinder();
if (idCollection.isEmpty()) {
request.append(SQL_FALSE); // append false for this stage
} else {
if (idBinder.isComplexId()) {
request.append(descriptor.getIdBinderInLHSSql());
request.append(idBinder.getIdInValueExpr(false, idCollection.size()));
} else {
request.append(idBinder.getBeanProperty().getName());
request.appendInExpression(false, idCollection);
}
request.append(descriptor.getIdBinderInLHSSql());
String inClause = idBinder.getIdInValueExpr(false, idCollection.size());
request.append(inClause);
}
}
@@ -18,7 +18,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static io.ebeaninternal.server.persist.DmlUtil.isNullOrZero;
@@ -69,9 +68,10 @@ public class SaveManyBeans extends SaveManyBase {
resetModifyState();
}
} else {
if (isModifyListenMode() || many.hasOrderColumn()) {
// delete any removed beans / orphans
removeAssocManyOrphans();
if (isModifyListenMode()) {
// delete any removed beans via private owned. Needs to occur before
// a 'deleteMissingChildren' statement occurs
removeAssocManyPrivateOwned();
}
if (cascade) {
// potentially deletes 'missing children' for 'stateless update'
@@ -108,7 +108,7 @@ public class SaveManyBeans extends SaveManyBase {
BeanProperty orderColumn = null;
boolean hasOrderColumn = many.hasOrderColumn();
if (hasOrderColumn) {
if (!insertedParent && canSkipForOrderColumn() && saveRecurseSkippable) {
if (!insertedParent && canSkipForOrderColumn()) {
return;
}
orderColumn = targetDescriptor.getOrderColumn();
@@ -157,7 +157,7 @@ public class SaveManyBeans extends SaveManyBase {
if (many.hasJoinTable()) {
skipSavingThisBean = targetDescriptor.isReference(ebi);
} else {
if (orderColumn != null && !Objects.equals(sortOrder, orderColumn.getValue(detail))) {
if (orderColumn != null) {
orderColumn.setValue(detail, sortOrder);
ebi.setDirty(true);
}
@@ -178,11 +178,6 @@ public class SaveManyBeans extends SaveManyBase {
if (!skipSavingThisBean) {
persister.saveRecurse(detail, transaction, parentBean, request.getFlags());
if (many.hasOrderColumn()) {
// Clear the bean from the PersistenceContext (L1 cache), because the order of referenced beans might have changed
final BeanDescriptor<?> beanDescriptor = many.getBeanDescriptor();
beanDescriptor.contextClear(transaction.getPersistenceContext(), beanDescriptor.getId(parentBean));
}
}
}
}
@@ -339,25 +334,20 @@ public class SaveManyBeans extends SaveManyBase {
transaction.depth(-1);
}
private void removeAssocManyOrphans() {
private void removeAssocManyPrivateOwned() {
// check that the list is not null and if it is a BeanCollection
// check that is has been populated (don't trigger lazy loading)
if (value instanceof BeanCollection<?>) {
BeanCollection<?> c = (BeanCollection<?>) value;
Set<?> modifyRemovals = c.getModifyRemovals();
if (insertedParent) {
// after insert set the modify listening mode for private owned etc
c.setModifyListening(many.getModifyListenMode());
}
// We must not reset when we still have to update other entities in the collection and set their new orderColumn value
if (!many.hasOrderColumn()) {
c.modifyReset();
}
modifyListenReset(c);
if (modifyRemovals != null && !modifyRemovals.isEmpty()) {
for (Object removedBean : modifyRemovals) {
if (removedBean instanceof EntityBean) {
EntityBean eb = (EntityBean) removedBean;
if (eb._ebean_intercept().isOrphanDelete()) {
if (!eb._ebean_getIntercept().isNew()) {
// only delete if the bean was loaded meaning that it is known to exist in the DB
persister.deleteRequest(persister.createDeleteRemoved(removedBean, transaction, request.getFlags()));
}
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.persist.dml;
import io.ebean.annotation.Platform;
import io.ebean.bean.EntityBean;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.server.core.PersistRequestBean;
@@ -45,10 +44,7 @@ final class InsertMeta {
private final String[] identityDbColumns;
private final Platform platform;
InsertMeta(DatabasePlatform dbPlatform, BeanDescriptor<?> desc, Bindable shadowFKey, BindableId id, BindableList all) {
this.platform = dbPlatform.getPlatform();
this.discriminator = getDiscriminator(desc);
this.id = id;
this.all = all;
@@ -166,11 +162,7 @@ final class InsertMeta {
request.append("insert into ").append(table);
if (nullId && noColumnsForInsert(draftTable)) {
if (this.platform.base() == Platform.MYSQL) {
request.append(" values (default)");
} else {
request.append(" default values");
}
request.append(" default values");
return request.toString();
}
@@ -54,7 +54,7 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
private <T> void flushJdbcBatchOnQuery(OrmQueryRequest<T> request) {
SpiTransaction t = request.getTransaction();
if (t.isFlushOnQuery()) {
if (t.isBatchFlushOnQuery()) {
// before we perform a query, we need to flush any
// previous persist requests that are queued/batched.
// The query may read data affected by those requests.
@@ -0,0 +1,31 @@
package io.ebeaninternal.server.transaction;
import java.sql.Connection;
/**
* AutoCommit friendly Transaction.
* <p>
* Skips actual commit and rollback as these are performed automatically.
*/
public class AutoCommitJdbcTransaction extends JdbcTransaction {
public AutoCommitJdbcTransaction(String id, boolean explicit, Connection connection, TransactionManager manager) {
super(id, explicit, connection, manager);
}
@Override
protected void checkAutoCommit(Connection connection) {
// do nothing as autoCommit
}
@Override
protected void performRollback() {
// do nothing as autoCommit
}
@Override
protected void performCommit() {
// do nothing as autoCommit
}
}
@@ -0,0 +1,26 @@
package io.ebeaninternal.server.transaction;
import io.ebeaninternal.api.SpiTransaction;
import java.sql.Connection;
/**
* AutoCommit based TransactionManager.
* <p>
* Intended to be used if when autoCommit mode is desired.
*/
public class AutoCommitTransactionManager extends TransactionManager {
public AutoCommitTransactionManager(TransactionManagerOptions options) {
super(options);
}
/**
* Create an autoCommit based Transaction.
*/
@Override
protected SpiTransaction createTransaction(boolean explicit, Connection c) {
return new AutoCommitJdbcTransaction(nextTxnId(), explicit, c, this);
}
}
@@ -24,7 +24,7 @@ public class DocStoreTransactionManager extends TransactionManager {
}
@Override
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
public SpiTransaction createQueryTransaction(Object tenantId) {
return new DocStoreOnlyTransaction("", false, this);
}
@@ -0,0 +1,42 @@
package io.ebeaninternal.server.transaction;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* This only works for Postgres and H2 (and doesn't work for Oracle).
* <p>
* Uses explicit begin statement to start the transactions.
*/
public class ExplicitJdbcTransaction extends JdbcTransaction {
public ExplicitJdbcTransaction(String id, boolean explicit, Connection connection, TransactionManager manager) {
super(id, explicit, connection, manager);
}
@Override
protected void checkAutoCommit(Connection connection) throws SQLException {
// begin the transaction explicitly
executeStatement("begin");
}
@Override
protected void performRollback() throws SQLException {
// Postgres needs this explicit rollback statement when used with AutoCommit=true
executeStatement("rollback");
}
@Override
protected void performCommit() throws SQLException {
// Postgres needs this explicit commit statement when used with AutoCommit=true
executeStatement("commit");
}
private void executeStatement(String statement) throws SQLException {
try (PreparedStatement stmt = connection.prepareStatement(statement)) {
stmt.execute();
}
}
}
@@ -0,0 +1,40 @@
package io.ebeaninternal.server.transaction;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.api.SpiTransaction;
import java.sql.Connection;
/**
* TransactionManager where the transactions start with explicit "begin" statement.
*/
public class ExplicitTransactionManager extends TransactionManager {
public ExplicitTransactionManager(TransactionManagerOptions options) {
super(options);
}
/**
* Create a ExplicitJdbcTransaction.
*/
@Override
protected SpiTransaction createTransaction(boolean explicit, Connection c) {
return new ExplicitJdbcTransaction(nextTxnId(), explicit, c, this);
}
/**
* Override the initialise of OnQueryOnly with the intention not to use CLOSE with ExplicitJdbcTransaction.
*/
@Override
protected DatabasePlatform.OnQueryOnly initOnQueryOnly(DatabasePlatform.OnQueryOnly dbPlatformOnQueryOnly) {
// first check for a system property 'override'
String systemPropertyValue = System.getProperty("ebean.transaction.onqueryonly");
if (systemPropertyValue != null) {
return DatabasePlatform.OnQueryOnly.valueOf(systemPropertyValue.trim().toUpperCase());
}
// default to rollback if not defined on the platform
return dbPlatformOnQueryOnly == null ? DatabasePlatform.OnQueryOnly.ROLLBACK : dbPlatformOnQueryOnly;
}
}
@@ -66,8 +66,6 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
private final long startNanos;
private ProfileLocation profileLocation;
/**
* Create without a tenantId.
*/
@@ -127,12 +125,12 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
@Override
public void setProfileLocation(ProfileLocation profileLocation) {
this.profileLocation = profileLocation;
throw new IllegalStateException(notExpectedMessage);
}
@Override
public ProfileLocation getProfileLocation() {
return profileLocation;
return null;
}
@Override
@@ -304,11 +302,11 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public void setGetGeneratedKeys(boolean getGeneratedKeys) {
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
}
@Override
public void setFlushOnMixed(boolean batchFlushOnMixed) {
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
}
/**
@@ -327,12 +325,12 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public boolean isFlushOnQuery() {
public boolean isBatchFlushOnQuery() {
return false;
}
@Override
public void setFlushOnQuery(boolean batchFlushOnQuery) {
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
}
/**
@@ -589,7 +589,7 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void setGetGeneratedKeys(boolean getGeneratedKeys) {
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
this.batchGetGeneratedKeys = getGeneratedKeys;
if (batchControl != null) {
batchControl.setGetGeneratedKeys(getGeneratedKeys);
@@ -597,7 +597,7 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void setFlushOnMixed(boolean batchFlushOnMixed) {
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
this.batchFlushOnMixed = batchFlushOnMixed;
if (batchControl != null) {
batchControl.setBatchFlushOnMixed(batchFlushOnMixed);
@@ -624,12 +624,12 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public boolean isFlushOnQuery() {
public boolean isBatchFlushOnQuery() {
return batchFlushOnQuery;
}
@Override
public void setFlushOnQuery(boolean batchFlushOnQuery) {
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
this.batchFlushOnQuery = batchFlushOnQuery;
}
@@ -233,19 +233,19 @@ class NoTransaction implements SpiTransaction {
}
@Override
public void setGetGeneratedKeys(boolean getGeneratedKeys) {
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
}
@Override
public void setFlushOnMixed(boolean batchFlushOnMixed) {
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
}
@Override
public void setFlushOnQuery(boolean batchFlushOnQuery) {
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
}
@Override
public boolean isFlushOnQuery() {
public boolean isBatchFlushOnQuery() {
return false;
}
@@ -23,7 +23,7 @@ abstract class TransactionFactory {
*
* @param tenantId The tenantId for lazy loading queries.
*/
abstract SpiTransaction createReadOnlyTransaction(Object tenantId);
abstract SpiTransaction createQueryTransaction(Object tenantId);
/**
* Return a new transaction.
@@ -21,7 +21,7 @@ class TransactionFactoryBasic extends TransactionFactory {
}
@Override
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
public SpiTransaction createQueryTransaction(Object tenantId) {
Connection connection = null;
try {
@@ -26,7 +26,7 @@ class TransactionFactoryBasicWithRead extends TransactionFactoryBasic {
}
@Override
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
public SpiTransaction createQueryTransaction(Object tenantId) {
Connection connection = null;
try {

Some files were not shown because too many files have changed in this diff Show More