Compare commits

...
Author SHA1 Message Date
Rob Bygrave 47d55a2f29 [maven-release-plugin] prepare release ebean-11.11.1 2018-02-23 12:50:45 +13:00
Rob Bygrave b037be5d3e No Effective change - bump agent in pom (with query bean enhancement of interface default methods) 2018-02-23 12:48:06 +13:00
Rob Bygrave 60a3c24cba No Effective change - Tidy pom removing commented out old sqlserver jdbc driver 2018-02-23 10:31:22 +13:00
Rob Bygrave 4a243a76a7 #1270 - Multi-tenant - partition mode - Delete by id does not include tenantId value in predicate 2018-02-23 10:26:47 +13:00
Rob Bygrave 9aee7c892d Test only - copy/add multi-tenant partition tests 2018-02-23 10:14:27 +13:00
Rob Bygrave 18d944f228 Test only - copy/add multi-tenant partition tests 2018-02-23 10:13:57 +13:00
Rob Bygrave 29bb9d3b48 #1268 - SQL Server fix for "for update" plus Sequences support plus refactor of sequences 2018-02-22 22:46:17 +13:00
Rob Bygrave f8ea5619d0 #1268 - SQL Server fix for "for update" plus Sequences support plus refactor of sequences 2018-02-22 22:45:44 +13:00
Rob Bygrave e3fac7c622 #1269 - Paging queries should not ALWAYS append an order by ID ... but only when the order by is empty 2018-02-22 22:43:14 +13:00
Rob Bygrave 8ee68ac5e1 #1269 - Paging queries should not ALWAYS append an order by ID ... but only when the order by is empty 2018-02-22 22:40:22 +13:00
Rob Bygrave ec7b376612 Test only change - fix test assert for sql server 2018-02-22 22:12:30 +13:00
Rob Bygrave f2d438f21a Test only change - fix test assert for sql server 2018-02-22 22:11:56 +13:00
Rob Bygrave 20bd2241e4 Test only change - fix test assert to support sequence and identity insert 2018-02-22 22:11:36 +13:00
Rob Bygrave dd907cc4bc Test only change - fix keys for testing against Oracle 2018-02-22 22:10:59 +13:00
Rob Bygrave 4d122ea05b #1267 - DDL issue on @OneToMany @JoinTable ... extra unnecessary index created on fk column 2018-02-22 22:09:53 +13:00
Rob Bygrave 83384c6f0e #323 - DDL Generation support for foreign key cascade options
Tidy up, no GLOBAL_DEFAULT.
2018-02-21 11:04:04 +13:00
Rob Bygrave eaa48468cd #1174 - ENH: Support mapping @TenantId to an entity bean via @ManyToOne 2018-02-21 09:44:37 +13:00
Rob Bygrave 0b1c3a4233 Bump version to 11.11.1-SNAPSHOT 2018-02-21 09:44:07 +13:00
Rob Bygrave 3e8b06e38e #994 - @OneToOne cacade does not honor orphanRemoval = true 2018-02-20 16:59:55 +13:00
Rob Bygrave 81d48d67a7 #123 - Mapping - Add support for OneToMany JoinTables 2018-02-20 14:40:21 +13:00
Rob Bygrave 8d0df6541e Merge branch 'issue/1263-transaction-api' 2018-02-19 22:47:42 +13:00
Rob Bygrave 2be3099d55 #1228 - Add support to not generate FK Constraint when @JoinColumn(foreignKey=@ForeignKey(ConstraintMode.NO_CONSTRAINT)) 2018-02-19 22:45:47 +13:00
Rob Bygrave 183d079b58 #323 - DDL Generation support for foreign key cascade options 2018-02-19 22:24:51 +13:00
Rob Bygrave 40634412f5 #323 - DDL Generation support for foreign key cascade options 2018-02-19 22:24:37 +13:00
Roland PramlandRob Bygrave b8aa822a30 [utf8mb4-support] limited the size of all indexed string columns, as max key length on mysql/mariadb is 767 bytes (which are <= 191 chars on utf8mb4) (#1264) 2018-02-19 16:15:28 +13:00
Rob Bygrave a1d0fbb194 [maven-release-plugin] prepare for next development iteration 2018-02-16 14:45:29 +13:00
Rob Bygrave 70ccc6588d [maven-release-plugin] prepare release ebean-11.10.6 2018-02-16 14:45:16 +13:00
Rob Bygrave 3d62e84d59 #1263 - ENH: Make Transaction API Fluid such that we can set multiple thing like batchSize and label in a fluid fashion 2018-02-16 14:40:35 +13:00
Rob Bygrave ac36172cc6 #997 - TypeReflectHelper does not resolve type variables correctly in non-trivial cases
Part 2 - Fix the failing test, swap in use of TypeResolver and remove all the unused features.
2018-02-16 12:47:10 +13:00
Rob Bygrave 1829c74694 #997 - TypeReflectHelper does not resolve type variables correctly in non-trivial cases
Initial failing test ... and TypeResolver prior to cleanup
2018-02-16 12:25:07 +13:00
Rob Bygrave cc7e22014d #997 - TypeReflectHelper does not resolve type variables correctly in non-trivial cases
Initial failing test ... and TypeResolver prior to cleanup
2018-02-16 12:24:57 +13:00
Rob Bygrave a5c6f483b0 #1258 - ENH: Add ServerConfig notifyL2CacheInForeground option ... was ServerCachePlugin BackgroundExecutor problem 2018-02-16 11:18:17 +13:00
Rob Bygrave 5237e8ba66 #1261 - ENH: Support use of getGeneratedKeys with SqlUpdate 2018-02-16 09:45:49 +13:00
Rob Bygrave 9926cad7f1 #1262 - ENH: Support firstRow maxRows with findNative() 2018-02-16 00:11:27 +13:00
Rob Bygrave 6c1b295d41 [maven-release-plugin] prepare for next development iteration 2018-02-15 22:32:56 +13:00
Rob Bygrave 49b568e7d4 [maven-release-plugin] prepare release ebean-11.10.5 2018-02-15 22:32:43 +13:00
Rob Bygrave 628af31ddd #1142 - Mapping - Add support for @OrderColumn (Ebean automatically maintains an order column) 2018-02-15 22:31:41 +13:00
Rob Bygrave 31cac60393 #107 - Mapping - Add support for @PrimaryKeyJoinColumn/@PrimaryKeyJoinColumns
Adds Bi-directional support
2018-02-15 00:49:51 +13:00
Rob Bygrave 6a067c0cf7 #107 - Mapping - Add support for @PrimaryKeyJoinColumn/@PrimaryKeyJoinColumns
Extra test with UUID and optional OneToOne so has left join
2018-02-14 23:09:10 +13:00
Rob Bygrave 842292ef6b #107 - Mapping - Add support for @PrimaryKeyJoinColumn/@PrimaryKeyJoinColumns 2018-02-14 22:59:57 +13:00
Rob Bygrave 6db9a60f4f #107 - Fix existing test - Ah ??? ... this PrimaryKeyJoinColumn was ignored and should never have been in this test. 2018-02-14 17:24:14 +13:00
Rob Bygrave d9125db957 [maven-release-plugin] prepare for next development iteration 2018-02-14 15:29:26 +13:00
197 changed files with 5304 additions and 1000 deletions
+5 -12
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.10.4</version>
<version>11.11.1</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -17,7 +17,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.10.4</tag>
<tag>ebean-11.11.1</tag>
</scm>
<profiles>
@@ -103,7 +103,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>3.6</version>
<version>3.8</version>
</dependency>
<dependency>
@@ -191,13 +191,6 @@
<!--<scope>test</scope>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>microsoft</groupId>-->
<!--<artifactId>sqlserver-jdbc</artifactId>-->
<!--<version>4.2</version>-->
<!--<scope>test</scope>-->
<!--</dependency>-->
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-docker-run</artifactId>
@@ -222,7 +215,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-agent</artifactId>
<version>11.9.1</version>
<version>11.10.1</version>
<scope>test</scope>
</dependency>
@@ -295,7 +288,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>11.9.1</version>
<version>11.10.1</version>
<executions>
<execution>
<id>test</id>
+24
View File
@@ -54,6 +54,25 @@ public interface SqlUpdate {
*/
int execute();
/**
* Return the generated key value.
*/
Object getGeneratedKey();
/**
* Execute and return the generated key. This is effectively a short cut for:
*
* <pre>{@code
*
* sqlUpdate.execute();
* Object key = sqlUpdate.getGeneratedKey();
*
* }</pre>
*
* @return The generated key value
*/
Object executeGetKey();
/**
* Return true if eBean should automatically deduce the table modification
* information and process it.
@@ -88,6 +107,11 @@ public interface SqlUpdate {
*/
SqlUpdate setLabel(String label);
/**
* Set to true when we want to use getGeneratedKeys with this statement.
*/
SqlUpdate setGetGeneratedKeys(boolean getGeneratedKeys);
/**
* Return the sql statement.
*/
+18 -18
View File
@@ -57,7 +57,7 @@ public interface Transaction extends AutoCloseable {
/**
* Register a TransactionCallback with this transaction.
*/
void register(TransactionCallback callback);
Transaction register(TransactionCallback callback);
/**
* Set a label on the transaction.
@@ -65,7 +65,7 @@ public interface Transaction extends AutoCloseable {
* This label is used to group transaction execution times for performance metrics reporting.
* </p>
*/
void setLabel(String label);
Transaction setLabel(String label);
/**
* Return true if this transaction is read only.
@@ -75,7 +75,7 @@ public interface Transaction extends AutoCloseable {
/**
* Set whether this transaction should be readOnly.
*/
void setReadOnly(boolean readOnly);
Transaction setReadOnly(boolean readOnly);
/**
* Commits the transaction at this point with the expectation that another
@@ -139,7 +139,7 @@ public interface Transaction extends AutoCloseable {
/**
* Mark the transaction for rollback only.
*/
void setRollbackOnly();
Transaction setRollbackOnly();
/**
* Return true if the transaction is marked as rollback only.
@@ -172,7 +172,7 @@ public interface Transaction extends AutoCloseable {
* to control how that is sent to the document store.
* </p>
*/
void setDocStoreMode(DocStoreMode mode);
Transaction setDocStoreMode(DocStoreMode mode);
/**
* Set the batch size to use for sending messages to the document store.
@@ -184,7 +184,7 @@ public interface Transaction extends AutoCloseable {
* Setting this overrides the default of {@link DocStoreConfig#getBulkBatchSize()}
* </p>
*/
void setDocStoreBatchSize(int batchSize);
Transaction setDocStoreBatchSize(int batchSize);
/**
* Explicitly turn off or on the cascading nature of save() and delete(). This
@@ -199,7 +199,7 @@ public interface Transaction extends AutoCloseable {
* that do not support getGeneratedKeys.
* </p>
*/
void setPersistCascade(boolean persistCascade);
Transaction setPersistCascade(boolean persistCascade);
/**
* Set to true when you want all loaded properties to be included in the update
@@ -210,7 +210,7 @@ public interface Transaction extends AutoCloseable {
* and hence better JDBC batch performance.
* </p>
*/
void setUpdateAllLoadedProperties(boolean updateAllLoadedProperties);
Transaction setUpdateAllLoadedProperties(boolean updateAllLoadedProperties);
/**
* Set if the L2 cache should be skipped for "find by id" and "find by natural key" queries.
@@ -269,7 +269,7 @@ public interface Transaction extends AutoCloseable {
*
* @see ServerConfig#isSkipCacheAfterWrite()
*/
void setSkipCache(boolean skipCache);
Transaction setSkipCache(boolean skipCache);
/**
* Return true if the L2 cache should be skipped. More accurately if true then find by id
@@ -344,7 +344,7 @@ public interface Transaction extends AutoCloseable {
*
* }</pre>
*/
void setBatchMode(boolean useBatch);
Transaction setBatchMode(boolean useBatch);
/**
* The JDBC batch mode to use for this transaction.
@@ -356,7 +356,7 @@ public interface Transaction extends AutoCloseable {
* @param persistBatchMode the batch mode to use for this transaction
* @see io.ebean.config.ServerConfig#setPersistBatch(PersistBatch)
*/
void setBatch(PersistBatch persistBatchMode);
Transaction setBatch(PersistBatch persistBatchMode);
/**
* Return the batch mode at the transaction level.
@@ -377,7 +377,7 @@ public interface Transaction extends AutoCloseable {
* @param batchOnCascadeMode the batch mode to use per save(), insert(), update() or delete()
* @see io.ebean.config.ServerConfig#setPersistBatchOnCascade(PersistBatch)
*/
void setBatchOnCascade(PersistBatch batchOnCascadeMode);
Transaction setBatchOnCascade(PersistBatch batchOnCascadeMode);
/**
* Return the batch mode at the request level (for each save(), insert(), update() or delete()).
@@ -387,7 +387,7 @@ public interface Transaction extends AutoCloseable {
/**
* Specify the number of statements before a batch is flushed automatically.
*/
void setBatchSize(int batchSize);
Transaction setBatchSize(int batchSize);
/**
* Return the current batch size.
@@ -405,7 +405,7 @@ public interface Transaction extends AutoCloseable {
* number of objects and you don't care about getting back the ids.
* </p>
*/
void setBatchGetGeneratedKeys(boolean getGeneratedKeys);
Transaction setBatchGetGeneratedKeys(boolean getGeneratedKeys);
/**
* By default when mixing UpdateSql (or CallableSql) with Beans the batch is
@@ -422,7 +422,7 @@ public interface Transaction extends AutoCloseable {
* have a 2 step process (delayed binding).
* </p>
*/
void setBatchFlushOnMixed(boolean batchFlushOnMixed);
Transaction setBatchFlushOnMixed(boolean batchFlushOnMixed);
/**
* By default executing a query will automatically flush any batched
@@ -432,7 +432,7 @@ public interface Transaction extends AutoCloseable {
* execute a query and the batch will not be automatically flushed.
* </p>
*/
void setBatchFlushOnQuery(boolean batchFlushOnQuery);
Transaction setBatchFlushOnQuery(boolean batchFlushOnQuery);
/**
* Return true if the batch (of persisted beans or executed UpdateSql etc)
@@ -498,14 +498,14 @@ public interface Transaction extends AutoCloseable {
* caches in synch and maintain text indexes.
* </p>
*/
void addModification(String tableName, boolean inserts, boolean updates, boolean deletes);
Transaction addModification(String tableName, boolean inserts, boolean updates, boolean deletes);
/**
* Add an arbitrary user object to the transaction. The objects added have no
* impact on any internals of ebean and are solely meant as a convenient
* method push user information (although somewhat replaced by TransactionCallback).
*/
void putUserObject(String name, Object value);
Transaction putUserObject(String name, Object value);
/**
* Get an object added with {@link #putUserObject(String, Object)}.
@@ -191,6 +191,11 @@ public interface BeanCollection<E> extends Serializable {
*/
void setModifyListening(ModifyListenMode modifyListenMode);
/**
* Return the current modify listening mode. Can be null for on newly created beans.
*/
ModifyListenMode getModifyListening();
/**
* Add an object to the additions list.
* <p>
@@ -227,6 +232,11 @@ public interface BeanCollection<E> extends Serializable {
*/
void modifyReset();
/**
* Has been modified by an addition or removal.
*/
boolean wasTouched();
/**
* Return a shallow copy of this collection that is modifiable.
*/
@@ -90,6 +90,7 @@ public final class EntityBeanIntercept implements Serializable {
private int lazyLoadProperty = -1;
private Object ownerId;
private int sortOrder;
/**
* Create a intercept with a given entity.
@@ -693,6 +694,9 @@ public final class EntityBeanIntercept implements Serializable {
* Add and return a dirty property hash recursing into embedded beans.
*/
private void addDirtyPropertyKey(StringBuilder sb) {
if (sortOrder > 0) {
sb.append("s,");
}
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if (changedProps != null && changedProps[i]) {
@@ -1050,4 +1054,18 @@ public final class EntityBeanIntercept implements Serializable {
public void setOldValue(int propertyIndex, Object oldValue) {
setChangedPropertyValue(propertyIndex, true, oldValue);
}
/**
* Return the sort order value for an order column.
*/
public int getSortOrder() {
return sortOrder;
}
/**
* Set the sort order value for an order column.
*/
public void setSortOrder(int sortOrder) {
this.sortOrder = sortOrder;
}
}
@@ -140,6 +140,11 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
// Support for modify additions deletions etc - ManyToMany
// ---------------------------------------------------------
@Override
public ModifyListenMode getModifyListening() {
return modifyListenMode;
}
/**
* set modifyListening to be on or off.
*/
@@ -213,7 +218,12 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
boolean holdsModifications() {
return modifyHolder != null && modifyHolder.hasModifications();
}
@Override
public boolean wasTouched() {
return modifyHolder != null && modifyHolder.wasTouched();
}
/**
* Copies all relevant properties for a clone. See {@link #getShallowCopy()}
* @param other
@@ -28,7 +28,10 @@ class ModifyHolder<E> implements Serializable {
*/
private Set<E> modifyAdditions = new LinkedHashSet<>();
private boolean touched;
void reset() {
touched = false;
modifyDeletions = new LinkedHashSet<>();
modifyAdditions = new LinkedHashSet<>();
}
@@ -50,6 +53,7 @@ class ModifyHolder<E> implements Serializable {
void modifyAddition(E bean) {
if (bean != null) {
touched = true;
// If it is to delete then just remove the deletion
if (!undoDeletion(bean)) {
// Insert
@@ -65,6 +69,7 @@ class ModifyHolder<E> implements Serializable {
@SuppressWarnings("unchecked")
void modifyRemoval(Object bean) {
if (bean != null) {
touched = true;
// If it is to be added then just remove the addition
if (!undoAddition(bean)) {
modifyDeletions.add((E) bean);
@@ -80,6 +85,14 @@ class ModifyHolder<E> implements Serializable {
return modifyDeletions;
}
/**
* Return true if the collection was touched in some way. This is still true even if
* a bean was removed and then added (which is not held as a modification).
*/
boolean wasTouched() {
return touched;
}
/**
* Return true if there additions or removals.
*/
@@ -12,6 +12,7 @@ import io.ebean.cache.ServerCachePlugin;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbEncrypt;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.event.BeanFindController;
import io.ebean.event.BeanPersistController;
import io.ebean.event.BeanPersistListener;
@@ -181,6 +182,11 @@ public class ServerConfig {
*/
private DatabasePlatform databasePlatform;
/**
* The preferred IdType (to override the default Platform type).
*/
private IdType idType;
/**
* For DB's using sequences this is the number of sequence values prefetched.
*/
@@ -463,6 +469,12 @@ public class ServerConfig {
*/
private boolean disableL2Cache;
/**
* Generally we want to perform L2 cache notification in the background and not impact
* the performance of executing transactions.
*/
private boolean notifyL2CacheInForeground;
/**
* The time in millis used to determine when a query is alerted for being slow.
*/
@@ -473,7 +485,6 @@ public class ServerConfig {
*/
private SlowQueryListener slowQueryListener;
private ProfilingConfig profilingConfig = new ProfilingConfig();
/**
@@ -1716,6 +1727,20 @@ public class ServerConfig {
this.databasePlatform = databasePlatform;
}
/**
* Return the preferred DB platform IdType.
*/
public IdType getIdType() {
return idType;
}
/**
* Set the preferred DB platform IdType.
*/
public void setIdType(IdType idType) {
this.idType = idType;
}
/**
* Return the EncryptKeyManager.
*/
@@ -2680,6 +2705,7 @@ public class ServerConfig {
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground);
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
@@ -2716,6 +2742,7 @@ public class ServerConfig {
throw new IllegalArgumentException("Property 'batch.mode' or 'persistBatching' is being set but no longer used. Please change to use 'persistBatchMode'");
}
idType = p.getEnum(IdType.class, "idType", idType);
persistBatch = p.getEnum(PersistBatch.class, "persistBatch", persistBatch);
persistBatchOnCascade = p.getEnum(PersistBatch.class, "persistBatchOnCascade", persistBatchOnCascade);
@@ -2900,6 +2927,25 @@ public class ServerConfig {
this.disableL2Cache = disableL2Cache;
}
/**
* Return true if L2 cache notification should run in the foreground.
*/
public boolean isNotifyL2CacheInForeground() {
return notifyL2CacheInForeground;
}
/**
* Set this to true to run L2 cache notification in the foreground.
* <p>
* In general we don't want to do that as when we use a distributed cache (like Ignite, Hazelcast etc)
* we are making network calls and we prefer to do this in background and not impact the response time
* of the executing transaction.
* </p>
*/
public void setNotifyL2CacheInForeground(boolean notifyL2CacheInForeground) {
this.notifyL2CacheInForeground = notifyL2CacheInForeground;
}
/**
* Return the query plan time to live.
*/
@@ -6,6 +6,8 @@ import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.config.CustomDbTypeMapping;
import io.ebean.config.DbTypeConfig;
import io.ebean.config.ServerConfig;
import io.ebeaninternal.util.JdbcClose;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -98,6 +100,10 @@ public class DatabasePlatform {
*/
protected DbIdentity dbIdentity = new DbIdentity();
protected boolean sequenceBatchMode = true;
protected int sequenceBatchSize = 20;
/**
* The history support for this database platform.
*/
@@ -206,10 +212,19 @@ public class DatabasePlatform {
return exceptionTranslator.translate(message, e);
}
/**
* Configure the platform given the server configuration.
*/
public void configure(ServerConfig config) {
this.sequenceBatchSize = config.getDatabaseSequenceBatchSize();
configureIdType(config.getIdType());
configure(config.getDbTypeConfig(), config.isAllQuotedIdentifiers());
}
/**
* Configure UUID Storage etc based on ServerConfig settings.
*/
public void configure(DbTypeConfig config, boolean allQuotedIdentifiers) {
protected void configure(DbTypeConfig config, boolean allQuotedIdentifiers) {
this.allQuotedIdentifiers = allQuotedIdentifiers;
addGeoTypes(config.getGeometrySRID());
configureIdType(config.getIdType());
@@ -263,6 +278,20 @@ 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.
*/
@@ -304,10 +333,10 @@ public class DatabasePlatform {
* @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 batchSize the number of sequences that should be loaded
*/
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return null;
}
@@ -586,6 +615,14 @@ public class DatabasePlatform {
return sql;
}
/**
* For update hint on the FROM clause (SQL server only).
*/
public String fromForUpdate(Query.ForUpdate forUpdateMode) {
// return null except for sql server
return null;
}
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
// silently assume the database does not support the "for update" clause.
logger.info("it seems your database does not support the 'for update' clause");
@@ -618,18 +655,7 @@ public class DatabasePlatform {
try {
return tables.next();
} finally {
close(tables);
}
}
/**
* Close the resultSet.
*/
protected void close(ResultSet resultSet) {
try {
resultSet.close();
} catch (SQLException e) {
logger.error("Error closing resultSet", e);
JdbcClose.close(tables);
}
}
@@ -0,0 +1,56 @@
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.
*/
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;
}
}
@@ -2,6 +2,7 @@ package io.ebean.config.dbplatform;
import io.ebean.BackgroundExecutor;
import io.ebean.Transaction;
import io.ebeaninternal.util.JdbcClose;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -12,13 +13,15 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Database sequence based IdGenerator.
*/
public abstract class SequenceIdGenerator implements PlatformIdGenerator {
private static final Logger logger = LoggerFactory.getLogger(SequenceIdGenerator.class);
protected static final Logger logger = LoggerFactory.getLogger("io.ebean.SEQ");
/**
* Used to synchronise the idList access.
@@ -41,18 +44,18 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
protected final ArrayList<Long> idList = new ArrayList<>(50);
protected final int batchSize;
protected final int allocationSize;
protected int currentlyBackgroundLoading;
protected boolean currentlyBackgroundLoading;
/**
* Construct given a dataSource and sql to return the next sequence value.
*/
public SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
protected SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int allocationSize) {
this.backgroundExecutor = be;
this.dataSource = ds;
this.seqName = seqName;
this.batchSize = batchSize;
this.allocationSize = allocationSize;
}
public abstract String getSql(int batchSize);
@@ -81,26 +84,8 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
* </p>
*/
@Override
public void preAllocateIds(int allocateSize) {
if (batchSize > 1 && allocateSize > batchSize) {
// only bother if allocateSize is bigger than
// the normal loading batchSize
if (allocateSize > 100) {
// max out at 100 for now
allocateSize = 100;
}
loadLargeAllocation(allocateSize);
}
}
/**
* Called by preAllocateIds when we know that a large number of Id's is going
* to be needed shortly.
*/
protected void loadLargeAllocation(final int allocateSize) {
// preAllocateIds was called with a relatively large batchSize
// so we will just go ahead and load those anyway in background
backgroundExecutor.execute(() -> loadMoreIds(allocateSize, null));
public void preAllocateIds(int requestSize) {
// do nothing by default
}
/**
@@ -113,80 +98,80 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
public Object nextId(Transaction t) {
synchronized (monitor) {
if (idList.isEmpty()) {
loadMoreIds(batchSize, t);
}
Long nextId = idList.remove(0);
if (batchSize > 1) {
if (idList.size() <= batchSize / 2) {
loadBatchInBackground();
}
int size = idList.size();
if (size > 0) {
maybeLoadMoreInBackground(size);
} else {
loadMore(allocationSize);
}
return nextId;
return idList.remove(0);
}
}
/**
* Load another batch of Id's using a background thread.
*/
protected void loadBatchInBackground() {
// single threaded processing...
synchronized (backgroundLoadMonitor) {
if (currentlyBackgroundLoading > 0) {
// skip as already background loading
logger.debug("... skip background sequence load (another load in progress)");
return;
private void maybeLoadMoreInBackground(int currentSize) {
if (allocationSize > 1) {
if (currentSize <= allocationSize / 2) {
loadInBackground(allocationSize);
}
currentlyBackgroundLoading = batchSize;
backgroundExecutor.execute(() -> {
loadMoreIds(batchSize, null);
synchronized (backgroundLoadMonitor) {
currentlyBackgroundLoading = 0;
}
});
}
}
protected void loadMoreIds(final int numberToLoad, Transaction t) {
ArrayList<Long> newIds = getMoreIds(numberToLoad, t);
if (logger.isDebugEnabled()) {
logger.debug("... seq:" + seqName + " loaded:" + numberToLoad + " ids:" + newIds);
}
private void loadMore(int requestSize) {
List<Long> newIds = getMoreIds(requestSize);
synchronized (monitor) {
idList.addAll(newIds);
}
}
/**
* Load another batch of Id's using a background thread.
*/
protected void loadInBackground(final int requestSize) {
// single threaded processing...
synchronized (backgroundLoadMonitor) {
if (currentlyBackgroundLoading) {
// skip as already background loading
logger.debug("... skip background sequence load (another load in progress)");
return;
}
currentlyBackgroundLoading = true;
backgroundExecutor.execute(() -> {
loadMore(requestSize);
synchronized (backgroundLoadMonitor) {
currentlyBackgroundLoading = false;
}
});
}
}
/**
* Read the resultSet returning the list of Id values.
*/
protected abstract List<Long> readIds(ResultSet resultSet, int loadSize) throws SQLException;
/**
* Get more Id's by executing a query and reading the Id's returned.
*/
protected ArrayList<Long> getMoreIds(int loadSize, Transaction t) {
protected List<Long> getMoreIds(int requestSize) {
String sql = getSql(loadSize);
String sql = getSql(requestSize);
ArrayList<Long> newIds = new ArrayList<>(loadSize);
boolean useTxnConnection = t != null;
Connection c = null;
PreparedStatement pstmt = null;
ResultSet rset = null;
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
c = useTxnConnection ? t.getConnection() : dataSource.getConnection();
connection = dataSource.getConnection();
pstmt = c.prepareStatement(sql);
rset = pstmt.executeQuery();
while (rset.next()) {
newIds.add(rset.getLong(1));
statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
List<Long> newIds = readIds(resultSet, requestSize);
if (logger.isTraceEnabled()) {
logger.trace("seq:{} loaded:{} sql:{}", seqName, newIds.size(), sql);
}
if (newIds.isEmpty()) {
throw new PersistenceException("Always expecting more than 1 row from " + sql);
@@ -197,46 +182,24 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
} catch (SQLException e) {
if (e.getMessage().contains("Database is already closed")) {
String msg = "Error getting SEQ when DB shutting down " + e.getMessage();
logger.info(msg);
logger.error(msg);
System.out.println(msg);
return newIds;
return Collections.emptyList();
} else {
throw new PersistenceException("Error getting sequence nextval", e);
}
} finally {
if (useTxnConnection) {
closeResources(null, pstmt, rset);
} else {
closeResources(c, pstmt, rset);
}
closeResources(connection, statement, resultSet);
}
}
/**
* Close the JDBC resources.
*/
protected void closeResources(Connection c, PreparedStatement pstmt, ResultSet rset) {
try {
if (rset != null) {
rset.close();
}
} catch (SQLException e) {
logger.error("Error closing ResultSet", e);
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
logger.error("Error closing PreparedStatement", e);
}
try {
if (c != null) {
c.close();
}
} catch (SQLException e) {
logger.error("Error closing Connection", e);
}
private void closeResources(Connection connection, PreparedStatement statement, ResultSet resultSet) {
JdbcClose.close(resultSet);
JdbcClose.close(statement);
JdbcClose.close(connection);
}
}
@@ -0,0 +1,38 @@
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 (e.g. step 50).
*/
public abstract class SequenceStepIdGenerator extends SequenceIdGenerator {
/**
* Construct with stepSize (typically 50).
*/
protected SequenceStepIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize);
}
/**
* Add the next set of Ids as the next value plus all the following numbers up to the step size.
*/
protected List<Long> readIds(ResultSet resultSet, int ignoreRequestSize) throws SQLException {
List<Long> newIds = new ArrayList<>(allocationSize);
if (resultSet.next()) {
long start = resultSet.getLong(1);
for (int i = 0; i < allocationSize; i++) {
newIds.add(start + i);
}
}
return newIds;
}
}
@@ -55,10 +55,9 @@ public class DB2Platform extends DatabasePlatform {
* sequence values.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be,
DataSource ds, String seqName, int batchSize) {
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new DB2SequenceIdGenerator(be, ds, seqName, batchSize);
return new DB2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
}
}
@@ -1,14 +1,14 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceIdGenerator;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* DB2 specific sequence Id Generator.
*/
public class DB2SequenceIdGenerator extends SequenceIdGenerator {
public class DB2SequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
private final String unionBaseSql;
@@ -46,10 +46,8 @@ public class H2Platform extends DatabasePlatform {
* sequence values.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds,
String seqName, int batchSize) {
return new H2SequenceIdGenerator(be, ds, seqName, batchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new H2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
}
@Override
@@ -1,14 +1,14 @@
package io.ebean.config.dbplatform.h2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceIdGenerator;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* H2 specific sequence Id Generator.
*/
public class H2SequenceIdGenerator extends SequenceIdGenerator {
public class H2SequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
private final String unionBaseSql;
@@ -31,9 +31,8 @@ public class HsqldbPlatform extends DatabasePlatform {
}
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
return new H2SequenceIdGenerator(be, ds, seqName, batchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new H2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
}
}
@@ -1,16 +1,13 @@
package io.ebean.config.dbplatform.mysql;
import io.ebean.BackgroundExecutor;
import io.ebean.Query;
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.SqlErrorCodes;
import javax.sql.DataSource;
import java.sql.Types;
/**
@@ -64,16 +61,6 @@ public class MySqlPlatform extends DatabasePlatform {
dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("varbinary", 255));
}
/**
* Return null in case there is a sequence annotation.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be,
DataSource ds, String seqName, int batchSize) {
return null;
}
@Override
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
// NOWAIT and SKIP LOCKED currently not supported with MySQL
@@ -70,9 +70,8 @@ public class OraclePlatform extends DatabasePlatform {
}
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
return new OracleSequenceIdGenerator(be, ds, seqName, batchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new OracleSequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
}
@Override
@@ -1,14 +1,14 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceIdGenerator;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* Oracle specific sequence Id Generator.
*/
public class OracleSequenceIdGenerator extends SequenceIdGenerator {
public class OracleSequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
@@ -90,9 +90,9 @@ public class PostgresPlatform extends DatabasePlatform {
* Create a Postgres specific sequence IdGenerator.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new PostgresSequenceIdGenerator(be, ds, seqName, batchSize);
return new PostgresSequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
}
@Override
@@ -1,14 +1,14 @@
package io.ebean.config.dbplatform.postgres;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceIdGenerator;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* Postgres specific sequence Id Generator.
*/
public class PostgresSequenceIdGenerator extends SequenceIdGenerator {
public class PostgresSequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
@@ -13,7 +13,7 @@ public class SqlServerBasicSqlLimiter implements BasicSqlLimiter {
StringBuilder sb = new StringBuilder(50 + dbSql.length());
sb.append(dbSql);
if (!dbSql.toLowerCase().contains("order by")) {
sb.append(" order by 1 ");
sb.append(" order by 1");
}
sb.append(" ").append("offset");
sb.append(" ").append(firstRow).append(" rows");
@@ -1,13 +1,18 @@
package io.ebean.config.dbplatform.sqlserver;
import io.ebean.BackgroundExecutor;
import io.ebean.Query;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.config.ServerConfig;
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.SqlErrorCodes;
import javax.sql.DataSource;
import java.sql.Types;
/**
@@ -18,8 +23,8 @@ public class SqlServerPlatform extends DatabasePlatform {
public SqlServerPlatform() {
super();
this.platform = Platform.SQLSERVER;
// effectively disable persistBatchOnCascade mode for SQL Server
// due to lack of support for getGeneratedKeys in batch mode
// disable persistBatchOnCascade mode for
// SQL Server unless we are using sequences
this.persistBatchOnCascade = PersistBatch.NONE;
this.idInExpandedForm = true;
this.selectCountWithAlias = true;
@@ -29,12 +34,14 @@ public class SqlServerPlatform extends DatabasePlatform {
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsIdentity(true);
this.dbIdentity.setSupportsSequence(true);
this.sequenceBatchMode = false;
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("1222")
.addDuplicateKey("2601", "2627")
.addDataIntegrity("544", "8114", "8115")
.addDataIntegrity("544", "547", "8114", "8115")
.build();
this.openQuote = "[";
@@ -67,8 +74,45 @@ public class SqlServerPlatform extends DatabasePlatform {
}
@Override
public void configure(ServerConfig config) {
super.configure(config);
if (dbIdentity.getIdType() == IdType.SEQUENCE) {
this.persistBatchOnCascade = PersistBatch.ALL;
}
}
@Override
protected void escapeLikeCharacter(char ch, StringBuilder sb) {
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);
}
/**
* For update is part of the FROM clause on the base table for sql server.
*/
@Override
public String fromForUpdate(Query.ForUpdate forUpdateMode) {
switch (forUpdateMode) {
case SKIPLOCKED:
return "with (updlock,readpast)";
case NOWAIT:
return "with (updlock,nowait)";
default:
return "with (updlock)";
}
}
@Override
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
// for update are hints on from clause of base table
return sql;
}
}
@@ -0,0 +1,24 @@
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;
}
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.api;
import io.ebean.Transaction;
import io.ebeaninternal.server.transaction.TransactionScopeManager;
import io.ebeaninternal.server.util.ArrayStack;
@@ -93,8 +94,9 @@ public class ScopedTransaction extends SpiTransactionProxy {
}
@Override
public void setRollbackOnly() {
public Transaction setRollbackOnly() {
current.setRollbackOnly();
return this;
}
/**
@@ -588,6 +588,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
*/
boolean isRawSql();
/**
* Return true if the query should have an order by appended automatically.
*/
boolean checkPagingOrderBy();
/**
* Return the Order By clause or null if there is none defined.
*/
@@ -4,7 +4,23 @@ import io.ebean.SqlUpdate;
public interface SpiSqlUpdate extends SqlUpdate {
/**
* Return the Bind parameters.
*/
BindParams getBindParams();
/**
* Set the final sql being executed with named parameters replaced etc.
*/
void setGeneratedSql(String sql);
/**
* Return true if we are using getGeneratedKeys.
*/
boolean isGetGeneratedKeys();
/**
* Set the generated key value.
*/
void setGeneratedKey(Object idValue);
}
@@ -1,6 +1,7 @@
package io.ebeaninternal.api;
import io.ebean.ProfileLocation;
import io.ebean.Transaction;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
@@ -31,8 +32,9 @@ abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setLabel(String label) {
public Transaction setLabel(String label) {
transaction.setLabel(label);
return this;
}
@Override
@@ -101,8 +103,9 @@ abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setDocStoreMode(DocStoreMode mode) {
public Transaction setDocStoreMode(DocStoreMode mode) {
transaction.setDocStoreMode(mode);
return this;
}
@Override
@@ -111,8 +114,9 @@ abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setDocStoreBatchSize(int batchSize) {
public Transaction setDocStoreBatchSize(int batchSize) {
transaction.setDocStoreBatchSize(batchSize);
return this;
}
@Override
@@ -141,8 +145,9 @@ abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setSkipCache(boolean skipCache) {
public Transaction setSkipCache(boolean skipCache) {
transaction.setSkipCache(skipCache);
return this;
}
@Override
@@ -196,8 +201,9 @@ abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void register(TransactionCallback callback) {
public Transaction register(TransactionCallback callback) {
transaction.register(callback);
return this;
}
@Override
@@ -206,8 +212,9 @@ abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setReadOnly(boolean readOnly) {
public Transaction setReadOnly(boolean readOnly) {
transaction.setReadOnly(readOnly);
return this;
}
@Override
@@ -216,13 +223,15 @@ abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setPersistCascade(boolean persistCascade) {
public Transaction setPersistCascade(boolean persistCascade) {
transaction.setPersistCascade(persistCascade);
return this;
}
@Override
public void setUpdateAllLoadedProperties(boolean updateAllLoaded) {
public Transaction setUpdateAllLoadedProperties(boolean updateAllLoaded) {
transaction.setUpdateAllLoadedProperties(updateAllLoaded);
return this;
}
@Override
@@ -231,13 +240,15 @@ abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setBatchMode(boolean useBatch) {
public Transaction setBatchMode(boolean useBatch) {
transaction.setBatchMode(useBatch);
return this;
}
@Override
public void setBatch(PersistBatch persistBatchMode) {
public Transaction setBatch(PersistBatch persistBatchMode) {
transaction.setBatch(persistBatchMode);
return this;
}
@Override
@@ -246,8 +257,9 @@ abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setBatchOnCascade(PersistBatch batchOnCascadeMode) {
public Transaction setBatchOnCascade(PersistBatch batchOnCascadeMode) {
transaction.setBatchOnCascade(batchOnCascadeMode);
return this;
}
@Override
@@ -256,8 +268,9 @@ abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setBatchSize(int batchSize) {
public Transaction setBatchSize(int batchSize) {
transaction.setBatchSize(batchSize);
return this;
}
@Override
@@ -266,8 +279,9 @@ abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
public Transaction setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
transaction.setBatchGetGeneratedKeys(getGeneratedKeys);
return this;
}
@Override
@@ -276,13 +290,15 @@ abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
public Transaction setBatchFlushOnMixed(boolean batchFlushOnMixed) {
transaction.setBatchFlushOnMixed(batchFlushOnMixed);
return this;
}
@Override
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
public Transaction setBatchFlushOnQuery(boolean batchFlushOnQuery) {
transaction.setBatchFlushOnQuery(batchFlushOnQuery);
return this;
}
@Override
@@ -306,13 +322,15 @@ abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void addModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
public Transaction addModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
transaction.addModification(tableName, inserts, updates, deletes);
return this;
}
@Override
public void putUserObject(String name, Object value) {
public Transaction putUserObject(String name, Object value) {
transaction.putUserObject(name, value);
return this;
}
@Override
@@ -389,14 +389,7 @@ public class BaseTableDdl implements TableDdl {
protected void writeInlineForeignKey(DdlWrite write, Column column) throws IOException {
String references = column.getReferences();
int pos = references.lastIndexOf('.');
if (pos == -1) {
throw new IllegalStateException("Expecting period '.' character for table.column split but not found in [" + references + "]");
}
String refTableName = references.substring(0, pos);
String refColumnName = references.substring(pos + 1);
String fkConstraint = platformDdl.tableInlineForeignKey(new String[]{column.getName()}, refTableName, new String[]{refColumnName});
String fkConstraint = platformDdl.tableInlineForeignKey(new WriteForeignKey(null, column));
write.apply().append(",").newLine().append(" ").append(fkConstraint);
}
@@ -404,11 +397,7 @@ public class BaseTableDdl implements TableDdl {
List<ForeignKey> foreignKey = createTable.getForeignKey();
for (ForeignKey key : foreignKey) {
String refTableName = key.getRefTableName();
String[] cols = toColumnNamesSplit(key.getColumnNames());
String[] refColumns = toColumnNamesSplit(key.getRefColumnNames());
String fkConstraint = platformDdl.tableInlineForeignKey(cols, refTableName, refColumns);
String fkConstraint = platformDdl.tableInlineForeignKey(new WriteForeignKey(null, key));
write.apply().append(",").newLine().append(" ").append(fkConstraint);
}
}
@@ -433,61 +422,42 @@ public class BaseTableDdl implements TableDdl {
List<ForeignKey> foreignKey = createTable.getForeignKey();
for (ForeignKey key : foreignKey) {
String refTableName = key.getRefTableName();
String fkName = key.getName();
String[] cols = toColumnNamesSplit(key.getColumnNames());
String[] refColumns = toColumnNamesSplit(key.getRefColumnNames());
writeForeignKey(write, fkName, tableName, cols, refTableName, refColumns, key.getIndexName());
writeForeignKey(write, new WriteForeignKey(tableName, key));
}
}
protected void writeForeignKey(DdlWrite write, String tableName, Column column) throws IOException {
String fkName = column.getForeignKeyName();
String references = column.getReferences();
int pos = references.lastIndexOf('.');
if (pos == -1) {
throw new IllegalStateException("Expecting period '.' character for table.column split but not found in [" + references + "]");
}
String refTableName = references.substring(0, pos);
String refColumnName = references.substring(pos + 1);
String[] cols = {column.getName()};
String[] refCols = {refColumnName};
writeForeignKey(write, fkName, tableName, cols, refTableName, refCols, column.getForeignKeyIndex());
writeForeignKey(write, new WriteForeignKey(tableName, column));
}
protected void writeForeignKey(DdlWrite write, String fkName, String tableName, String[] columns, String refTable, String[] refColumns, String indexName) throws IOException {
protected void writeForeignKey(DdlWrite write, WriteForeignKey request) throws IOException {
tableName = lowerTableName(tableName);
String tableName = lowerTableName(request.table());
DdlBuffer fkeyBuffer = write.applyForeignKeys();
alterTableAddForeignKey(fkeyBuffer, fkName, tableName, columns, refTable, refColumns);
alterTableAddForeignKey(fkeyBuffer, request);
if (indexName != null) {
if (request.indexName() != null) {
// no matching unique constraint so add the index
fkeyBuffer.append(platformDdl.createIndex(indexName, tableName, columns)).endOfStatement();
fkeyBuffer.append(platformDdl.createIndex(request.indexName(), tableName, request.cols())).endOfStatement();
}
fkeyBuffer.end();
write.dropAllForeignKeys()
.append(platformDdl.alterTableDropForeignKey(tableName, fkName)).endOfStatement();
.append(platformDdl.alterTableDropForeignKey(tableName, request.fkName())).endOfStatement();
if (indexName != null) {
if (request.indexName() != null) {
write.dropAllForeignKeys()
.append(platformDdl.dropIndex(indexName, tableName)).endOfStatement();
.append(platformDdl.dropIndex(request.indexName(), tableName)).endOfStatement();
}
write.dropAllForeignKeys().end();
}
protected void alterTableAddForeignKey(DdlBuffer buffer, String fkName, String tableName, String[] columns, String refTable, String[] refColumns) throws IOException {
protected void alterTableAddForeignKey(DdlBuffer buffer, WriteForeignKey request) throws IOException {
String fkConstraint = platformDdl.alterTableAddForeignKey(tableName, fkName, columns, refTable, refColumns);
String fkConstraint = platformDdl.alterTableAddForeignKey(request);
if (fkConstraint != null && !fkConstraint.isEmpty()) {
buffer.append(fkConstraint).endOfStatement();
}
@@ -554,7 +524,7 @@ public class BaseTableDdl implements TableDdl {
for (UniqueConstraint uniqueConstraint : uniqueConstraints) {
if (inlineUniqueWhenNull) {
String uqName = uniqueConstraint.getName();
String[] columns = toColumnNamesSplit(uniqueConstraint.getColumnNames());
String[] columns = SplitColumns.split(uniqueConstraint.getColumnNames());
apply.append(",").newLine();
apply.append(" constraint ").append(uqName).append(" unique");
appendColumns(columns, apply);
@@ -571,17 +541,14 @@ public class BaseTableDdl implements TableDdl {
boolean inlineUniqueWhenNullable = platformDdl.isInlineUniqueWhenNullable();
List<Column> columns = createTable.getColumn();
List<Column> columns = new WriteUniqueConstraint(createTable.getColumn()).uniqueKeys();
for (Column column : columns) {
if (!Boolean.TRUE.equals(column.isPrimaryKey())
&& (hasValue(column.getUnique()) || hasValue(column.getUniqueOneToOne()))) {
if (Boolean.TRUE.equals(column.isNotnull()) || inlineUniqueWhenNullable) {
// normal mechanism for adding unique constraint
inlineUniqueConstraintSingle(apply, column);
} else {
// MsSqlServer & DB2 specific mechanism for adding unique constraints (that allow nulls)
externalUnique.add(column);
}
if (Boolean.TRUE.equals(column.isNotnull()) || inlineUniqueWhenNullable) {
// normal mechanism for adding unique constraint
inlineUniqueConstraintSingle(apply, column);
} else {
// SqlServer & DB2 specific mechanism for adding unique constraints (that allow nulls)
externalUnique.add(column);
}
}
}
@@ -625,13 +592,6 @@ public class BaseTableDdl implements TableDdl {
return cols;
}
/**
* Return as an array of string column names.
*/
protected String[] toColumnNamesSplit(String columns) {
return columns.split(",");
}
/**
* Convert the table lower case.
*/
@@ -662,7 +622,7 @@ public class BaseTableDdl implements TableDdl {
@Override
public void generate(DdlWrite writer, CreateIndex createIndex) throws IOException {
String[] cols = toColumnNamesSplit(createIndex.getColumns());
String[] cols = SplitColumns.split(createIndex.getColumns());
writer.apply()
.append(platformDdl.createIndex(createIndex.getIndexName(), createIndex.getTableName(), cols))
.endOfStatement();
@@ -931,22 +891,9 @@ public class BaseTableDdl implements TableDdl {
}
}
protected void alterColumnAddForeignKey(DdlWrite writer, AlterColumn alterColumn) throws IOException {
String tableName = alterColumn.getTableName();
String fkName = alterColumn.getForeignKeyName();
String[] cols = {alterColumn.getColumnName()};
String references = alterColumn.getReferences();
int pos = references.lastIndexOf('.');
if (pos == -1) {
throw new IllegalStateException("Expecting period '.' character for table.column split but not found in [" + references + "]");
}
String refTableName = references.substring(0, pos);
String refColumnName = references.substring(pos + 1);
String[] refCols = {refColumnName};
alterTableAddForeignKey(writer.apply(), fkName, tableName, cols, refTableName, refCols);
alterTableAddForeignKey(writer.apply(), new WriteForeignKey(alterColumn));
}
protected void alterColumnDropForeignKey(DdlWrite writer, AlterColumn alter) throws IOException {
@@ -1,5 +1,6 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.annotation.ConstraintMode;
import io.ebean.config.dbplatform.DatabasePlatform;
/**
@@ -14,7 +15,6 @@ public class Oracle10Ddl extends PlatformDdl {
this.dropConstraintIfExists = "drop constraint";
this.dropIndexIfExists = "drop index ";
this.dropTableCascade = " cascade constraints purge";
this.foreignKeyRestrict = "";
this.alterColumn = "modify";
this.columnSetNotnull = "not null";
this.columnSetNull = "null";
@@ -22,4 +22,20 @@ public class Oracle10Ddl extends PlatformDdl {
this.identitySuffix = " generated always as identity";
}
@Override
protected void appendForeignKeyOnUpdate(StringBuilder buffer, ConstraintMode mode) {
// do nothing, no on update clause for oracle
}
@Override
protected void appendForeignKeyMode(StringBuilder buffer, String onMode, ConstraintMode mode) {
switch (mode) {
case SET_NULL:
case CASCADE:
super.appendForeignKeyMode(buffer, onMode, mode);
default:
// do nothing, defaults to RESTRICT effectively
}
}
}
@@ -1,11 +1,13 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.annotation.ConstraintMode;
import io.ebean.config.DbConstraintNaming;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbDefaultValue;
import io.ebean.config.dbplatform.DbIdentity;
import io.ebean.config.dbplatform.IdType;
import io.ebean.util.StringHelper;
import io.ebeaninternal.dbmigration.ddlgeneration.BaseDdlHandler;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
@@ -17,7 +19,6 @@ import io.ebeaninternal.dbmigration.migration.Column;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.migration.IdentityType;
import io.ebeaninternal.dbmigration.model.MTable;
import io.ebean.util.StringHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -62,7 +63,8 @@ public class PlatformDdl {
*/
protected String dropSequenceIfExists = "drop sequence if exists ";
protected String foreignKeyRestrict = "on delete restrict on update restrict";
protected String foreignKeyOnDelete = "on delete";
protected String foreignKeyOnUpdate = "on update";
protected String identitySuffix = " auto_increment";
@@ -306,9 +308,7 @@ public class PlatformDdl {
if (initialValue > 1) {
sb.append(" start with ").append(initialValue);
}
if (allocationSize > 0 && allocationSize != 50) {
// at this stage ignoring allocationSize 50 as this is the 'default' and
// not consistent with the way Ebean batch fetches sequence values
if (allocationSize > 1) {
sb.append(" increment by ").append(allocationSize);
}
sb.append(";");
@@ -351,37 +351,68 @@ public class PlatformDdl {
/**
* Return the foreign key constraint when used inline with create table.
*/
public String tableInlineForeignKey(String[] columns, String refTable, String[] refColumns) {
public String tableInlineForeignKey(WriteForeignKey request) {
StringBuilder buffer = new StringBuilder(90);
buffer.append("foreign key");
appendColumns(columns, buffer);
buffer.append(" references ").append(lowerTableName(refTable));
appendColumns(refColumns, buffer);
appendWithSpace(foreignKeyRestrict, buffer);
appendColumns(request.cols(), buffer);
buffer.append(" references ").append(lowerTableName(request.refTable()));
appendColumns(request.refCols(), buffer);
appendForeignKeySuffix(request, buffer);
return buffer.toString();
}
/**
* Add foreign key.
*/
public String alterTableAddForeignKey(String tableName, String fkName, String[] columns, String refTable, String[] refColumns) {
public String alterTableAddForeignKey(WriteForeignKey request) {
StringBuilder buffer = new StringBuilder(90);
buffer
.append("alter table ").append(tableName)
.append(" add constraint ").append(fkName)
.append("alter table ").append(lowerTableName(request.table()))
.append(" add constraint ").append(request.fkName())
.append(" foreign key");
appendColumns(columns, buffer);
appendColumns(request.cols(), buffer);
buffer
.append(" references ")
.append(lowerTableName(refTable));
appendColumns(refColumns, buffer);
appendWithSpace(foreignKeyRestrict, buffer);
.append(lowerTableName(request.refTable()));
appendColumns(request.refCols(), buffer);
appendForeignKeySuffix(request, buffer);
return buffer.toString();
}
protected void appendForeignKeySuffix(WriteForeignKey request, StringBuilder buffer) {
appendForeignKeyOnDelete(buffer, withDefault(request.onDelete()));
appendForeignKeyOnUpdate(buffer, withDefault(request.onDelete()));
}
protected ConstraintMode withDefault(ConstraintMode mode) {
return (mode == null) ? ConstraintMode.RESTRICT : mode;
}
protected void appendForeignKeyOnDelete(StringBuilder buffer, ConstraintMode mode) {
appendForeignKeyMode(buffer, foreignKeyOnDelete, mode);
}
protected void appendForeignKeyOnUpdate(StringBuilder buffer, ConstraintMode mode) {
appendForeignKeyMode(buffer, foreignKeyOnUpdate, mode);
}
protected void appendForeignKeyMode(StringBuilder buffer, String onMode, ConstraintMode mode) {
buffer.append(" ").append(onMode).append(" ").append(translate(mode));
}
protected String translate(ConstraintMode mode) {
switch(mode) {
case SET_NULL: return "set null";
case SET_DEFAULT: return "set default";
case RESTRICT: return "restrict";
case CASCADE: return "cascade";
default:
throw new IllegalStateException("Unknown mode "+mode);
}
}
/**
* Drop a unique constraint from the table (Sometimes this is an index).
*/
@@ -3,8 +3,6 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import java.io.IOException;
/**
* DB2 platform specific DDL.
*/
@@ -17,17 +15,17 @@ public class SQLiteDdl extends PlatformDdl {
}
@Override
public void addTableComment(DdlBuffer apply, String tableName, String tableComment) throws IOException {
public void addTableComment(DdlBuffer apply, String tableName, String tableComment) {
// not supported
}
@Override
public void addColumnComment(DdlBuffer apply, String table, String column, String comment) throws IOException {
public void addColumnComment(DdlBuffer apply, String table, String column, String comment) {
// not supported
}
@Override
public String alterTableAddForeignKey(String tableName, String fkName, String[] columns, String refTable, String[] refColumns) {
public String alterTableAddForeignKey(WriteForeignKey request) {
// not supported
return null;
}
@@ -0,0 +1,12 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
public class SplitColumns {
/**
* Return as an array of string column names.
*/
public static String[] split(String columns) {
return columns.split(",");
}
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.annotation.ConstraintMode;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
@@ -14,7 +15,6 @@ public class SqlServerDdl extends PlatformDdl {
public SqlServerDdl(DatabasePlatform platform) {
super(platform);
this.identitySuffix = " identity(1,1)";
this.foreignKeyRestrict = "";
this.alterTableIfExists = "";
this.addColumn = "add";
this.inlineUniqueWhenNullable = false;
@@ -23,6 +23,13 @@ public class SqlServerDdl extends PlatformDdl {
this.historyDdl = new SqlServerHistoryDdl();
}
@Override
protected void appendForeignKeyMode(StringBuilder buffer, String onMode, ConstraintMode mode) {
if (mode != ConstraintMode.RESTRICT) {
super.appendForeignKeyMode(buffer, onMode, mode);
}
}
@Override
public String dropTable(String tableName) {
StringBuilder buffer = new StringBuilder();
@@ -114,9 +121,7 @@ public class SqlServerDdl extends PlatformDdl {
} else {
sb.append(" start with 1 ");
}
if (allocationSize > 0 && allocationSize != 50) {
// at this stage ignoring allocationSize 50 as this is the 'default' and
// not consistent with the way Ebean batch fetches sequence values
if (allocationSize > 1) {
sb.append(" increment by ").append(allocationSize);
}
sb.append(";");
@@ -0,0 +1,100 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.annotation.ConstraintMode;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.Column;
import io.ebeaninternal.dbmigration.migration.ForeignKey;
class WriteForeignKey {
private final String fkName;
private final String tableName;
private final String[] cols;
private String refTableName;
private String[] refCols;
private final String indexName;
private final ConstraintMode onDelete;
private final ConstraintMode onUpdate;
WriteForeignKey(AlterColumn alterColumn) {
this.tableName = alterColumn.getTableName();
this.indexName = alterColumn.getForeignKeyIndex();
this.fkName = alterColumn.getForeignKeyName();
this.cols = new String[]{alterColumn.getColumnName()};
setReferences(alterColumn.getReferences());
this.onDelete = modeOf(alterColumn.getForeignKeyOnDelete());
this.onUpdate = modeOf(alterColumn.getForeignKeyOnUpdate());
}
WriteForeignKey(String tableName, ForeignKey key) {
this.tableName = tableName;
this.indexName = key.getIndexName();
this.fkName = key.getName();
this.cols = toCols(key.getColumnNames());
this.refTableName = key.getRefTableName();
this.refCols = toCols(key.getRefColumnNames());
this.onDelete = modeOf(key.getOnDelete());
this.onUpdate = modeOf(key.getOnUpdate());
}
WriteForeignKey(String tableName, Column column) {
this.tableName = tableName;
this.indexName = column.getForeignKeyIndex();
this.fkName = column.getForeignKeyName();
this.cols = new String[]{column.getName()};
setReferences(column.getReferences());
this.onDelete = modeOf(column.getForeignKeyOnDelete());
this.onUpdate = modeOf(column.getForeignKeyOnUpdate());
}
private void setReferences(String references) {
int pos = references.lastIndexOf('.');
if (pos == -1) {
throw new IllegalStateException("Expecting period '.' character for table.column split but not found in [" + references + "]");
}
this.refTableName = references.substring(0, pos);
String refColumnName = references.substring(pos + 1);
this.refCols = new String[]{refColumnName};
}
private String[] toCols(String columns) {
return SplitColumns.split(columns);
}
private ConstraintMode modeOf(String value) {
return (value == null) ? null : ConstraintMode.valueOf(value);
}
public String table() {
return tableName;
}
public String[] cols() {
return cols;
}
public String indexName() {
return indexName;
}
public String fkName() {
return fkName;
}
public String refTable() {
return refTableName;
}
public String[] refCols() {
return refCols;
}
public ConstraintMode onDelete() {
return onDelete;
}
public ConstraintMode onUpdate() {
return onUpdate;
}
}
@@ -0,0 +1,47 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebeaninternal.dbmigration.migration.Column;
import java.util.ArrayList;
import java.util.List;
class WriteUniqueConstraint {
private final List<Column> primaryKeys = new ArrayList<>();
private final List<Column> uniqueKeys = new ArrayList<>();
WriteUniqueConstraint(List<Column> columns) {
// filter for unique and primary keys
for (Column column : columns) {
if (Boolean.TRUE.equals(column.isPrimaryKey())) {
primaryKeys.add(column);
}
if (hasValue(column.getUnique()) || hasValue(column.getUniqueOneToOne())){
uniqueKeys.add(column);
}
}
}
/**
* Return true if null or trimmed string is empty.
*/
boolean hasValue(String value) {
return value != null && !value.trim().isEmpty();
}
/**
* Return the single columns with unique constraints (that are not the primary key).
*/
public List<Column> uniqueKeys() {
if (uniqueKeys.isEmpty() || primaryKeys.size() > 1) {
// all single column unique constraints are valid
return uniqueKeys;
}
// filter out PFK
for (Column primaryKey : primaryKeys) {
uniqueKeys.remove(primaryKey);
}
return uniqueKeys;
}
}
@@ -1,14 +1,13 @@
package io.ebeaninternal.dbmigration.migration;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import java.util.ArrayList;
import java.util.List;
/**
@@ -40,6 +39,8 @@ import javax.xml.bind.annotation.XmlType;
* &lt;attribute name="references" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="foreignKeyName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="foreignKeyIndex" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="foreignKeyOnDelete" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="foreignKeyOnUpdate" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="dropForeignKey" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="dropForeignKeyIndex" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
@@ -99,6 +100,10 @@ public class AlterColumn {
protected String foreignKeyName;
@XmlAttribute(name = "foreignKeyIndex")
protected String foreignKeyIndex;
@XmlAttribute(name = "foreignKeyOnDelete")
protected String foreignKeyOnDelete;
@XmlAttribute(name = "foreignKeyOnUpdate")
protected String foreignKeyOnUpdate;
@XmlAttribute(name = "dropForeignKey")
protected String dropForeignKey;
@XmlAttribute(name = "dropForeignKeyIndex")
@@ -504,6 +509,46 @@ public class AlterColumn {
this.foreignKeyIndex = value;
}
/**
* Gets the value of the foreignKeyOnDelete property.
*
* @return possible object is
* {@link String }
*/
public String getForeignKeyOnDelete() {
return foreignKeyOnDelete;
}
/**
* Sets the value of the foreignKeyOnDelete property.
*
* @param value allowed object is
* {@link String }
*/
public void setForeignKeyOnDelete(String value) {
this.foreignKeyOnDelete = value;
}
/**
* Gets the value of the foreignKeyOnUpdate property.
*
* @return possible object is
* {@link String }
*/
public String getForeignKeyOnUpdate() {
return foreignKeyOnUpdate;
}
/**
* Sets the value of the foreignKeyOnUpdate property.
*
* @param value allowed object is
* {@link String }
*/
public void setForeignKeyOnUpdate(String value) {
this.foreignKeyOnUpdate = value;
}
/**
* Gets the value of the dropForeignKey property.
*
@@ -1,14 +1,13 @@
package io.ebeaninternal.dbmigration.migration;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import java.util.ArrayList;
import java.util.List;
/**
@@ -34,6 +33,8 @@ import javax.xml.bind.annotation.XmlType;
* &lt;attribute name="references" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="foreignKeyName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="foreignKeyIndex" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="foreignKeyOnDelete" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="foreignKeyOnUpdate" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="comment" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
@@ -80,6 +81,10 @@ public class Column {
protected String foreignKeyName;
@XmlAttribute(name = "foreignKeyIndex")
protected String foreignKeyIndex;
@XmlAttribute(name = "foreignKeyOnDelete")
protected String foreignKeyOnDelete;
@XmlAttribute(name = "foreignKeyOnUpdate")
protected String foreignKeyOnUpdate;
@XmlAttribute(name = "comment")
protected String comment;
@@ -378,6 +383,46 @@ public class Column {
this.foreignKeyIndex = value;
}
/**
* Gets the value of the foreignOnDelete property.
*
* @return possible object is
* {@link String }
*/
public String getForeignKeyOnDelete() {
return foreignKeyOnDelete;
}
/**
* Sets the value of the foreignKeyOnDelete property.
*
* @param value allowed object is
* {@link String }
*/
public void setForeignKeyOnDelete(String value) {
this.foreignKeyOnDelete = value;
}
/**
* Gets the value of the foreignOnUpdate property.
*
* @return possible object is
* {@link String }
*/
public String getForeignKeyOnUpdate() {
return foreignKeyOnUpdate;
}
/**
* Sets the value of the foreignKeyOnUpdate property.
*
* @param value allowed object is
* {@link String }
*/
public void setForeignKeyOnUpdate(String value) {
this.foreignKeyOnUpdate = value;
}
/**
* Gets the value of the comment property.
*
@@ -21,6 +21,8 @@ import javax.xml.bind.annotation.XmlType;
* &lt;attribute name="refColumnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="refTableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="indexName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="onDelete" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="onUpdate" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
@@ -41,7 +43,10 @@ public class ForeignKey {
protected String refTableName;
@XmlAttribute(name = "indexName")
protected String indexName;
@XmlAttribute(name = "onDelete")
protected String onDelete;
@XmlAttribute(name = "onUpdate")
protected String onUpdate;
/**
* Gets the value of the name property.
*
@@ -142,4 +147,44 @@ public class ForeignKey {
this.indexName = value;
}
/**
* Gets the value of the onDelete property.
*
* @return possible object is
* {@link String }
*/
public String getOnDelete() {
return onDelete;
}
/**
* Sets the value of the onDelete property.
*
* @param value allowed object is
* {@link String }
*/
public void setOnDelete(String value) {
this.onDelete = value;
}
/**
* Gets the value of the onUpdate property.
*
* @return possible object is
* {@link String }
*/
public String getOnUpdate() {
return onUpdate;
}
/**
* Sets the value of the onUpdate property.
*
* @param value allowed object is
* {@link String }
*/
public void setOnUpdate(String value) {
this.onUpdate = value;
}
}
@@ -1,13 +1,14 @@
package io.ebeaninternal.dbmigration.model;
import java.util.List;
import io.ebean.annotation.ConstraintMode;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.DdlHelp;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.Column;
import io.ebeaninternal.dbmigration.migration.DdlScript;
import io.ebeaninternal.server.deploy.DbMigrationInfo;
import java.util.List;
/**
* A column in the logical model.
*/
@@ -21,6 +22,8 @@ public class MColumn {
private String references;
private String foreignKeyName;
private String foreignKeyIndex;
private ConstraintMode fkeyOnDelete;
private ConstraintMode fkeyOnUpdate;
private String comment;
private boolean historyExclude;
@@ -55,6 +58,8 @@ public class MColumn {
this.references = column.getReferences();
this.foreignKeyName = column.getForeignKeyName();
this.foreignKeyIndex = column.getForeignKeyIndex();
this.fkeyOnDelete = fkeyMode(column.getForeignKeyOnDelete());
this.fkeyOnUpdate = fkeyMode(column.getForeignKeyOnUpdate());
this.notnull = Boolean.TRUE.equals(column.isNotnull());
this.primaryKey = Boolean.TRUE.equals(column.isPrimaryKey());
this.identity = Boolean.TRUE.equals(column.isIdentity());
@@ -63,6 +68,10 @@ public class MColumn {
this.historyExclude = Boolean.TRUE.equals(column.isHistoryExclude());
}
private ConstraintMode fkeyMode(String mode) {
return (mode == null) ? null : ConstraintMode.valueOf(mode);
}
public MColumn(String name, String type) {
this.name = name;
this.type = type;
@@ -89,6 +98,8 @@ public class MColumn {
copy.comment = comment;
copy.foreignKeyName = foreignKeyName;
copy.foreignKeyIndex = foreignKeyIndex;
copy.fkeyOnUpdate = fkeyOnUpdate;
copy.fkeyOnDelete = fkeyOnDelete;
copy.historyExclude = historyExclude;
copy.notnull = notnull;
copy.primaryKey = primaryKey;
@@ -154,6 +165,11 @@ public class MColumn {
this.foreignKeyIndex = foreignKeyIndex;
}
public void setForeignKeyModes(ConstraintMode onDelete, ConstraintMode onUpdate) {
this.fkeyOnDelete = onDelete;
this.fkeyOnUpdate = onUpdate;
}
public String getDefaultValue() {
return defaultValue;
}
@@ -196,7 +212,7 @@ public class MColumn {
/**
* Set unique specifically for OneToOne mapping.
* We need special DDL for this case for MsSqlServer.
* We need special DDL for this case for SqlServer.
*/
public void setUniqueOneToOne(String uniqueOneToOne) {
this.uniqueOneToOne = uniqueOneToOne;
@@ -260,6 +276,8 @@ public class MColumn {
c.setReferences(references);
c.setForeignKeyName(foreignKeyName);
c.setForeignKeyIndex(foreignKeyIndex);
c.setForeignKeyOnDelete(fkeyModeOf(fkeyOnDelete));
c.setForeignKeyOnUpdate(fkeyModeOf(fkeyOnUpdate));
c.setDefaultValue(defaultValue);
c.setComment(comment);
c.setUnique(unique);
@@ -286,6 +304,10 @@ public class MColumn {
return c;
}
private String fkeyModeOf(ConstraintMode mode) {
return (mode == null) ? null : mode.name();
}
protected static boolean different(String val1, String val2) {
return (val1 == null) ? val2 != null : !val1.equals(val2);
}
@@ -408,7 +408,6 @@ public class MTable {
* Return all the columns (excluding columns marked as dropped).
*/
public Collection<MColumn> allColumns() {
return columns.values();
}
@@ -657,4 +656,12 @@ public class MTable {
return draftTableName + "." + references.substring(lastDot + 1);
}
/**
* Clear the indexes on the foreign keys as they are covered by unique constraints.
*/
public void clearForeignKeyIndexes() {
for (MCompoundForeignKey compoundKey : compoundKeys) {
compoundKey.setIndexName(null);
}
}
}
@@ -9,7 +9,6 @@ import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.TableJoinColumn;
/**
* Add the intersection table to the model.
*/
@@ -32,7 +31,7 @@ public class ModelBuildIntersectionTable {
this.tableJoin = manyProp.getTableJoin();
}
public void build() {
public MTable build() {
intersectionTable = createTable();
MTable existingTable = ctx.addTable(intersectionTable);
@@ -47,6 +46,7 @@ public class ModelBuildIntersectionTable {
ctx.createDraft(intersectionTable, false);
}
return intersectionTable;
}
private void buildFkConstraints() {
@@ -11,10 +11,13 @@ import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.IndexDefinition;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.PropertyForeignKey;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.TableJoinColumn;
import io.ebeaninternal.server.deploy.id.ImportedId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -118,7 +121,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
@Override
public void visitMany(BeanPropertyAssocMany<?> p) {
if (p.isManyToMany()) {
if (p.hasJoinTable()) {
if (p.getMappedBy() == null) {
// only create on other 'owning' side
@@ -128,7 +131,16 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
// build the create table and fkey constraints
// putting the DDL into ctx for later output as we are
// in the middle of rendering the create table DDL
new ModelBuildIntersectionTable(ctx, p).build();
MTable intersectionTable = new ModelBuildIntersectionTable(ctx, p).build();
if (p.isO2mJoinTable()) {
intersectionTable.clearForeignKeyIndexes();
Collection<MColumn> cols = intersectionTable.allColumns();
if (cols.size() == 2) {
// always the second column that we put the unique constraint on
MColumn col = new ArrayList<>(cols).get(1);
col.setUnique(determineUniqueConstraintName(col.getName()));
}
}
}
}
}
@@ -148,14 +160,15 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
TableJoinColumn[] columns = p.getTableJoin().columns();
if (columns.length == 0) {
String msg = "No join columns for " + p.getFullBeanName();
throw new RuntimeException(msg);
throw new RuntimeException("No join columns for " + p.getFullBeanName());
}
ImportedId importedId = p.getImportedId();
List<MColumn> modelColumns = new ArrayList<>(columns.length);
PropertyForeignKey foreignKey = p.getForeignKey();
MCompoundForeignKey compoundKey = null;
if (columns.length > 1) {
// compound foreign key
@@ -180,15 +193,22 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
col.setDbMigrationInfos(p.getDbMigrationInfos());
col.setDefaultValue(p.getDbColumnDefault());
if (columns.length == 1) {
// single references column (put it on the column)
String refTable = importedProperty.getBeanDescriptor().getBaseTable();
if (refTable == null) {
// odd case where an EmbeddedId only has 1 property
refTable = p.getTargetDescriptor().getBaseTable();
if (p.hasForeignKey()) {
// single references column (put it on the column)
String refTable = importedProperty.getBeanDescriptor().getBaseTable();
if (refTable == null) {
// odd case where an EmbeddedId only has 1 property
refTable = p.getTargetDescriptor().getBaseTable();
}
col.setReferences(refTable + "." + refColumn);
col.setForeignKeyName(determineForeignKeyConstraintName(col.getName()));
if (p.hasForeignKeyIndex()) {
col.setForeignKeyIndex(determineForeignKeyIndexName(col.getName()));
}
if (foreignKey != null) {
col.setForeignKeyModes(foreignKey.getOnDelete(), foreignKey.getOnUpdate());
}
}
col.setReferences(refTable + "." + refColumn);
col.setForeignKeyName(determineForeignKeyConstraintName(col.getName()));
col.setForeignKeyIndex(determineForeignKeyIndexName(col.getName()));
} else {
compoundKey.addColumnPair(dbCol, refColumn);
}
@@ -231,6 +251,12 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
if (p.getBeanDescriptor().isUseIdGenerator()) {
col.setIdentity(true);
}
TableJoin primaryKeyJoin = p.getBeanDescriptor().getPrimaryKeyJoin();
if (primaryKeyJoin != null) {
TableJoinColumn[] columns = primaryKeyJoin.columns();
col.setReferences(primaryKeyJoin.getTable() + "." + columns[0].getForeignDbColumn());
col.setForeignKeyName(determineForeignKeyConstraintName(col.getName()));
}
} else {
col.setDefaultValue(p.getDbColumnDefault());
col.setDbMigrationInfos(p.getDbMigrationInfos());
@@ -270,7 +270,7 @@ public class DefaultContainer implements SpiContainer {
config.setDatabasePlatform(platform);
}
logger.info("DatabasePlatform name:{} platform:{}", config.getName(), platform.getName());
platform.configure(config.getDbTypeConfig(), config.isAllQuotedIdentifiers());
platform.configure(config);
}
/**
@@ -65,6 +65,10 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
*/
private int addPos;
private boolean getGeneratedKeys;
private Object generatedKey;
/**
* Create with server sql and bindParams object.
* <p>
@@ -93,6 +97,12 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
this(null, sql, new BindParams());
}
@Override
public Object executeGetKey() {
execute();
return getGeneratedKey();
}
@Override
public int execute() {
if (server != null) {
@@ -103,6 +113,16 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
}
}
@Override
public Object getGeneratedKey() {
return generatedKey;
}
@Override
public void setGeneratedKey(Object idValue) {
this.generatedKey = idValue;
}
@Override
public boolean isAutoTableMod() {
return isAutoTableMod;
@@ -125,6 +145,17 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
return this;
}
@Override
public boolean isGetGeneratedKeys() {
return getGeneratedKeys;
}
@Override
public SqlUpdate setGetGeneratedKeys(boolean getGeneratedKeys) {
this.getGeneratedKeys = getGeneratedKeys;
return this;
}
@Override
public String getGeneratedSql() {
return generatedSql;
@@ -372,10 +372,10 @@ public class InternalConfiguration {
public TransactionManager createTransactionManager(DocStoreUpdateProcessor indexUpdateProcessor) {
TransactionScopeManager scopeManager = createTransactionScopeManager();
boolean localL2 = cacheManager.isLocalL2Caching();
boolean notifyL2CacheInForeground = cacheManager.isLocalL2Caching() || serverConfig.isNotifyL2CacheInForeground();
TransactionManagerOptions options =
new TransactionManagerOptions(localL2, serverConfig, scopeManager, clusterManager, backgroundExecutor,
new TransactionManagerOptions(notifyL2CacheInForeground, serverConfig, scopeManager, clusterManager, backgroundExecutor,
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler());
if (serverConfig.isExplicitTransactionBeginMode()) {
@@ -11,8 +11,8 @@ import io.ebean.event.BeanPersistRequest;
import io.ebean.event.changelog.BeanChange;
import io.ebeaninternal.api.ConcurrencyMode;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.api.SpiProfileTransactionEvent;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.api.TransactionEvent;
import io.ebeaninternal.server.cache.CacheChangeSet;
import io.ebeaninternal.server.deploy.BeanDescriptor;
@@ -642,7 +642,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.createReference(Boolean.FALSE, false, getBeanId(), null);
return beanDescriptor.createReference(getBeanId(), null);
}
/**
@@ -713,6 +713,20 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
return intercept.isLoadedProperty(prop.getPropertyIndex());
}
/**
* Return true if the property is dirty.
*/
public boolean isDirtyProperty(BeanProperty prop) {
return intercept.isDirtyProperty(prop.getPropertyIndex());
}
/**
* Return the original / old value for the given property.
*/
public Object getOrigValue(BeanProperty prop) {
return intercept.getOrigValue(prop.getPropertyIndex());
}
@Override
public int executeNow() {
if (getterCallback) {
@@ -73,6 +73,11 @@ public final class PersistRequestUpdateSql extends PersistRequest {
*/
@Override
public void setGeneratedKey(Object idValue) {
updateSql.setGeneratedKey(idValue);
}
public boolean isGetGeneratedKeys() {
return updateSql.isGetGeneratedKeys();
}
/**
@@ -22,7 +22,7 @@ public class BeanCascadeInfo {
}
}
private void setType(CascadeType type) {
public void setType(CascadeType type) {
switch (type) {
case ALL:
save = true;
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.deploy;
import io.ebean.OrderBy;
import io.ebean.PersistenceContextScope;
import io.ebean.ProfileLocation;
import io.ebean.Query;
@@ -179,6 +178,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
private final String baseTableAsOf;
private final String baseTableVersionsBetween;
private final boolean historySupport;
private final TableJoin primaryKeyJoin;
private final BeanProperty softDeleteProperty;
private final boolean softDelete;
@@ -307,6 +307,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
private final BeanPropertyAssocOne<?> unidirectional;
private final BeanProperty orderColumn;
/**
* list of properties that are Lists/Sets/Maps (Derived).
@@ -454,6 +455,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
this.draftTable = deploy.getDraftTable();
this.baseTable = InternString.intern(deploy.getBaseTable());
this.baseTableAsOf = deploy.getBaseTableAsOf();
this.primaryKeyJoin = deploy.getPrimaryKeyJoin();
this.baseTableVersionsBetween = deploy.getBaseTableVersionsBetween();
this.dependentTables = deploy.getDependentTables();
this.dbComment = deploy.getDbComment();
@@ -478,6 +480,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
this.propertiesLocal = listHelper.getLocal();
this.propertiesMutable = listHelper.getMutable();
this.unidirectional = listHelper.getUnidirectional();
this.orderColumn = listHelper.getOrderColumn();
this.propertiesOne = listHelper.getOnes();
this.propertiesOneExportedSave = listHelper.getOneExportedSave();
this.propertiesOneExportedDelete = listHelper.getOneExportedDelete();
@@ -1200,7 +1203,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
if (tenant != null && !query.isNativeSql()) {
Object tenantId = ebeanServer.currentTenantId();
if (tenantId != null) {
query.where().eq(tenant.getName(), tenantId);
tenant.addTenant(query, tenantId);
}
}
if (isDocStoreOnly()) {
@@ -1868,6 +1871,29 @@ public class BeanDescriptor<T> implements BeanType<T> {
}
}
/**
* Create a non read only reference bean without checking cacheSharableBeans.
*/
@SuppressWarnings("unchecked")
public T createReference(Object id, PersistenceContext pc) {
try {
EntityBean eb = createEntityBean();
id = convertSetId(id, eb);
EntityBeanIntercept ebi = eb._ebean_getIntercept();
ebi.setBeanLoader(ebeanServer);
ebi.setReference(idPropertyIndex);
if (pc != null) {
contextPut(pc, id, eb);
ebi.setPersistenceContext(pc);
}
return (T) eb;
} catch (Exception ex) {
throw new PersistenceException(ex);
}
}
/**
* Return the bean property traversing the object graph and taking into
* account inheritance.
@@ -1923,6 +1949,13 @@ public class BeanDescriptor<T> implements BeanType<T> {
return owner.getBeanDescriptor(otherType);
}
/**
* Return the order column property.
*/
public BeanProperty getOrderColumn() {
return orderColumn;
}
/**
* Return the "shadow" property to support unidirectional relationships.
* <p>
@@ -2714,7 +2747,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
*/
public void setTenantId(EntityBean entityBean, Object tenantId) {
if (tenant != null) {
tenant.setValue(entityBean, tenantId);
tenant.setTenantValue(entityBean, tenantId);
}
}
@@ -2888,6 +2921,10 @@ public class BeanDescriptor<T> implements BeanType<T> {
}
}
public TableJoin getPrimaryKeyJoin() {
return primaryKeyJoin;
}
@Override
public BeanProperty getIdProperty() {
return idProperty;
@@ -3017,14 +3054,10 @@ public class BeanDescriptor<T> implements BeanType<T> {
public void appendOrderById(SpiQuery<T> query) {
if (idProperty != null && !idProperty.isEmbedded()) {
OrderBy<T> orderBy = query.getOrderBy();
if (orderBy == null || orderBy.isEmpty()) {
SpiRawSql rawSql = query.getRawSql();
if (rawSql != null) {
query.order(rawSql.getSql().getOrderBy());
}
query.order().asc(idProperty.getName());
} else if (!orderBy.containsProperty(idProperty.getName())) {
SpiRawSql rawSql = query.getRawSql();
if (rawSql != null) {
query.order(rawSql.getSql().getOrderBy());
} else {
query.order().asc(idProperty.getName());
}
}
@@ -3122,6 +3155,13 @@ public class BeanDescriptor<T> implements BeanType<T> {
return versionProperty;
}
/**
* Return true if this type is tenant aware.
*/
public boolean isMultiTenant() {
return tenant != null;
}
/**
* Return the tenant property when multi-tenant partitioning support is used.
*/
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.deploy;
import io.ebean.BackgroundExecutor;
import io.ebean.Model;
import io.ebean.RawSqlBuilder;
import io.ebean.annotation.ConstraintMode;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.config.EncryptKey;
@@ -38,6 +39,7 @@ 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.DeployOrderColumn;
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
import io.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import io.ebeaninternal.server.deploy.parse.DeployCreateProperties;
@@ -50,6 +52,7 @@ import io.ebeaninternal.server.properties.BeanPropertiesReader;
import io.ebeaninternal.server.properties.BeanPropertyAccess;
import io.ebeaninternal.server.properties.EnhanceBeanPropertyAccess;
import io.ebeaninternal.server.query.CQueryPlan;
import io.ebeaninternal.server.type.ScalarTypeInteger;
import io.ebeaninternal.xmlmapping.XmlMappingReader;
import io.ebeaninternal.xmlmapping.model.XmAliasMapping;
import io.ebeaninternal.xmlmapping.model.XmColumnMapping;
@@ -170,8 +173,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final BackgroundExecutor backgroundExecutor;
private final int dbSequenceBatchSize;
private final EncryptKeyManager encryptKeyManager;
private final IdBinderFactory idBinderFactory;
@@ -203,7 +204,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
this.serverName = InternString.intern(serverConfig.getName());
this.cacheManager = config.getCacheManager();
this.docStoreFactory = config.getDocStoreFactory();
this.dbSequenceBatchSize = serverConfig.getDatabaseSequenceBatchSize();
this.backgroundExecutor = config.getBackgroundExecutor();
this.dataSource = serverConfig.getDataSource();
this.encryptKeyManager = serverConfig.getEncryptKeyManager();
@@ -797,8 +797,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
// We only perform 'circular' checks etc after we have
// all the DeployBeanDescriptors created and in the map.
List<DeployBeanPropertyAssocOne<?>> primaryKeyJoinCheck = new ArrayList<>();
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
checkMappedBy(info);
checkMappedBy(info, primaryKeyJoinCheck);
}
for (DeployBeanPropertyAssocOne<?> prop : primaryKeyJoinCheck) {
checkUniDirectionalPrimaryKeyJoin(prop);
}
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
@@ -868,12 +872,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
* relationships.
* </p>
*/
private void checkMappedBy(DeployBeanInfo<?> info) {
private void checkMappedBy(DeployBeanInfo<?> info, List<DeployBeanPropertyAssocOne<?>> primaryKeyJoinCheck) {
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
if (!oneProp.isTransient()) {
if (oneProp.getMappedBy() != null) {
checkMappedByOneToOne(oneProp);
} else if (oneProp.isPrimaryKeyJoin()) {
primaryKeyJoinCheck.add(oneProp);
}
}
}
@@ -973,6 +979,23 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
throw new PersistenceException(msg);
}
private void makeOrderColumn(DeployBeanPropertyAssocMany<?> oneToMany) {
DeployBeanDescriptor<?> targetDesc = getTargetDescriptor(oneToMany);
DeployOrderColumn orderColumn = oneToMany.getOrderColumn();
DeployBeanProperty orderProperty = new DeployBeanProperty(targetDesc, Integer.class, ScalarTypeInteger.INSTANCE, null);
orderProperty.setName(DeployOrderColumn.LOGICAL_NAME);
orderProperty.setDbColumn(orderColumn.getName());
orderProperty.setNullable(orderColumn.isNullable());
orderProperty.setDbInsertable(orderColumn.isInsertable());
orderProperty.setDbUpdateable(orderColumn.isUpdatable());
orderProperty.setDbRead(true);
targetDesc.setOrderColumn(orderProperty);
}
/**
* A OneToMany with no matching mappedBy property in the target so must be
* unidirectional.
@@ -1071,6 +1094,21 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin();
otherTableJoin.copyWithoutType(tableJoin, true, tableJoin.getTable());
}
if (mappedAssocOne.isPrimaryKeyJoin()) {
// bi-directional PrimaryKeyJoin ...
mappedAssocOne.setPrimaryKeyJoin(false);
prop.setPrimaryKeyExport();
addPrimaryKeyJoin(prop);
}
}
private void checkUniDirectionalPrimaryKeyJoin(DeployBeanPropertyAssocOne<?> prop) {
if (prop.isPrimaryKeyJoin()) {
// uni-directional PrimaryKeyJoin ...
prop.setPrimaryKeyExport();
addPrimaryKeyJoin(prop);
}
}
/**
@@ -1091,16 +1129,22 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
prop.getCascadeInfo().setSaveDelete(true, true);
}
if (prop.hasOrderColumn()) {
makeOrderColumn(prop);
}
if (prop.getMappedBy() == null) {
// if we are doc store only we are done
// this allowes the use of @OneToMany in @DocStore - Entities
// this allows the use of @OneToMany in @DocStore - Entities
if (info.getDescriptor().isDocStoreOnly()) {
prop.setUnidirectional();
return;
}
if (!findMappedBy(prop)) {
makeUnidirectional(info, prop);
if (!prop.isO2mJoinTable()) {
makeUnidirectional(info, prop);
}
return;
}
}
@@ -1112,7 +1156,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
// get the mappedBy property
DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy);
if (mappedProp == null) {
String m = "Error on " + prop.getFullBeanName();
m += " Can not find mappedBy property [" + mappedBy + "] ";
m += "in [" + targetDesc + "]";
@@ -1135,6 +1178,19 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
otherTableJoin.copyTo(tableJoin, true, tableJoin.getTable());
}
PropertyForeignKey foreignKey = mappedAssocOne.getForeignKey();
if (foreignKey != null) {
ConstraintMode onDelete = foreignKey.getOnDelete();
switch (onDelete) {
case SET_DEFAULT:
case SET_NULL:
case CASCADE: {
// turn off cascade delete when we are using the foreign
// key constraint to cascade the delete or set null
prop.getCascadeInfo().setDelete(false);
}
}
}
}
/**
@@ -1332,12 +1388,16 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
seqName = namingConvention.getSequenceName(desc.getBaseTable(), primaryKeyColumn);
}
// create the sequence based IdGenerator
desc.setIdGenerator(createSequenceIdGenerator(seqName));
if (databasePlatform.isSequenceBatchMode()) {
// use sequence next step 1 as we are going to batch fetch them instead
desc.setSequenceAllocationSize(1);
}
int stepSize = desc.getSequenceAllocationSize();
desc.setIdGenerator(createSequenceIdGenerator(seqName, stepSize));
}
private PlatformIdGenerator createSequenceIdGenerator(String seqName) {
return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, seqName, dbSequenceBatchSize);
private PlatformIdGenerator createSequenceIdGenerator(String seqName, int stepSize) {
return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, stepSize, seqName);
}
private void createByteCode(DeployBeanDescriptor<?> deploy) {
@@ -1546,6 +1606,17 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
return changeLogListener;
}
public void addPrimaryKeyJoin(DeployBeanPropertyAssocOne<?> prop) {
String baseTable = prop.getDesc().getBaseTable();
DeployTableJoin inverse = prop.getTableJoin().createInverse(baseTable);
TableJoin inverseJoin = new TableJoin(inverse);
DeployBeanInfo<?> target = deployInfoMap.get(prop.getTargetType());
target.setPrimaryKeyJoin(inverseJoin);
}
/**
* Comparator to sort the BeanDescriptors by name.
*/
@@ -9,8 +9,10 @@ import io.ebean.config.dbplatform.DbEncryptFunction;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.plugin.Property;
import io.ebean.text.StringParser;
import io.ebean.util.SplitName;
import io.ebean.util.StringHelper;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.core.InternString;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedWhenCreated;
@@ -20,7 +22,6 @@ import io.ebeaninternal.server.el.ElPropertyChainBuilder;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.properties.BeanPropertyGetter;
import io.ebeaninternal.server.properties.BeanPropertySetter;
import io.ebean.util.SplitName;
import io.ebeaninternal.server.query.SqlBeanLoad;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.text.json.ReadJson;
@@ -720,6 +721,20 @@ public class BeanProperty implements ElPropertyValue, Property {
bean._ebean_getIntercept().setChangedProperty(propertyIndex);
}
/**
* Add the tenantId predicate to the query.
*/
public void addTenant(SpiQuery<?> query, Object tenantId) {
query.where().eq(name, tenantId);
}
/**
* Set the tenantId onto the bean.
*/
public void setTenantValue(EntityBean entityBean, Object tenantId) {
setValue(entityBean, tenantId);
}
/**
* Set the value of the property without interception or
* PropertyChangeSupport.
@@ -58,6 +58,13 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
*/
private final boolean unidirectional;
private final boolean o2mJoinTable;
/**
* Flag to indicate that the target has a order column to auto populate.
*/
private final boolean hasOrderColumn;
/**
* Flag to indicate manyToMany relationship.
*/
@@ -109,6 +116,8 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
public BeanPropertyAssocMany(BeanDescriptor<?> descriptor, DeployBeanPropertyAssocMany<T> deploy) {
super(descriptor, deploy);
this.unidirectional = deploy.isUnidirectional();
this.o2mJoinTable = deploy.isO2mJoinTable();
this.hasOrderColumn = deploy.hasOrderColumn();
this.manyToMany = deploy.isManyToMany();
this.manyType = deploy.getManyType();
this.mapKey = deploy.getMapKey();
@@ -133,8 +142,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
if (!isTransient) {
this.help = BeanCollectionHelpFactory.create(this);
if (manyToMany) {
// only manyToMany's have imported properties
if (hasJoinTable()) {
importedId = createImportedId(this, targetDescriptor, tableJoin);
} else {
@@ -163,7 +171,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
sb.append(", ");
}
// these fk columns are either on the intersection (int_) or base table (t0)
String fkTableAlias = isManyToMany() ? "int_" : "t0";
String fkTableAlias = hasJoinTable() ? "int_" : "t0";
sb.append(fkTableAlias).append(".").append(exportedProperties[i].getForeignDbColumn());
}
sb.append(", ").append(fetchOrderBy);
@@ -172,7 +180,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
}
String delStmt;
if (manyToMany) {
if (hasJoinTable()) {
delStmt = "delete from " + inverseJoin.getTable() + " where ";
} else {
delStmt = "delete from " + targetDescriptor.getBaseTable() + " where ";
@@ -341,8 +349,8 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
*/
private void addWhereParentIdIn(SpiQuery<?> query, List<Object> parentIds) {
String tableAlias = manyToMany ? "int_." : "t0.";
if (manyToMany) {
String tableAlias = hasJoinTable() ? "int_." : "t0.";
if (hasJoinTable()) {
query.setM2MIncludeJoin(inverseJoin);
}
String rawWhere = deriveWhereParentIdSql(true, tableAlias);
@@ -469,12 +477,12 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
StringBuilder sb = new StringBuilder(50);
SpiQuery<?> query = request.getQueryRequest().getQuery();
if (manyToMany) {
if (hasJoinTable()) {
sb.append(query.isAsDraft() ? intersectionDraftTable : intersectionPublishTable);
} else {
sb.append(targetDescriptor.getBaseTable(query.getTemporalMode()));
}
if (softDelete && manyToMany) {
if (softDelete && hasJoinTable()) {
sb.append(" x join ");
sb.append(targetDescriptor.getBaseTable(query.getTemporalMode()));
sb.append(" x2 on ");
@@ -491,7 +499,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
exportedProperties[i].appendWhere(sb, "x.", path);
}
if (softDelete) {
String alias = (manyToMany) ? "x2" : "x";
String alias = hasJoinTable() ? "x2" : "x";
sb.append(" and ").append(targetDescriptor.getSoftDeletePredicate(alias));
}
return sb.toString();
@@ -534,6 +542,10 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
return true;
}
public boolean hasOrderColumn() {
return hasOrderColumn;
}
@Override
public boolean isAssocMany() {
return true;
@@ -564,6 +576,20 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
return manyType;
}
/**
* Return true if this is many to many.
*/
public boolean hasJoinTable() {
return manyToMany || o2mJoinTable;
}
/**
* Return true if this is a one to many with a join table.
*/
public boolean isO2mJoinTable() {
return o2mJoinTable;
}
/**
* Return true if this is many to many.
*/
@@ -651,7 +677,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
public void addSelectExported(DbSqlContext ctx, String tableAlias) {
String alias = manyToMany ? "int_" : tableAlias;
String alias = hasJoinTable() ? "int_" : tableAlias;
if (alias == null) {
alias = "t0";
}
@@ -684,45 +710,6 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
return sb.toString();
}
// public void setPredicates(SpiQuery<?> query, EntityBean parentBean) {
//
// if (manyToMany){
// // for ManyToMany lazy loading we need to include a
// // join to the intersection table. The predicate column
// // is not on the 'destination many table'.
// query.setIncludeTableJoin(inverseJoin);
// }
//
// if (embeddedExportedProperties) {
// // use the EmbeddedId object instead of the parentBean
// BeanProperty idProp = descriptor.getIdProperty();
// parentBean = (EntityBean)idProp.getValue(parentBean);
// }
//
// for (int i = 0; i < exportedProperties.length; i++) {
// Object val = exportedProperties[i].getValue(parentBean);
// String fkColumn = exportedProperties[i].getForeignDbColumn();
// if (!manyToMany){
// fkColumn = targetDescriptor.getBaseTableAlias()+"."+fkColumn;
// } else {
// // use hard coded alias for intersection table
// fkColumn = "int_."+fkColumn;
// }
// query.where().eq(fkColumn, val);
// }
//
// if (extraWhere != null){
// // replace the table alias place holder
// String ta = targetDescriptor.getBaseTableAlias();
// String where = StringHelper.replaceString(extraWhere, "${ta}", ta);
// query.where().raw(where);
// }
//
// if (fetchOrderBy != null){
// query.order(fetchOrderBy);
// }
// }
/**
* Create the array of ExportedProperty used to build reference objects.
*/
@@ -766,7 +753,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
String searchTable;
TableJoinColumn[] columns;
if (manyToMany) {
if (hasJoinTable()) {
// look for column going to intersection
columns = intersectionJoin.columns();
searchTable = intersectionJoin.getTable();
@@ -980,7 +967,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
if (isManyToMany()) {
if (liveBean == null) {
// add new relationship (Map not allowed here)
liveVal.addBean(targetDescriptor.createReference(Boolean.FALSE, false, id, null));
liveVal.addBean(targetDescriptor.createReference(id, null));
}
} else {
@@ -1014,4 +1001,14 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
return liveMap;
}
public boolean isIncludeCascadeSave() {
// Note ManyToMany always included as we always 'save'
// the relationship via insert/delete of intersection table
// REMOVALS means including PrivateOwned relationships
return cascadeInfo.isSave() || hasJoinTable() || ModifyListenMode.REMOVALS == modifyListenMode;
}
public boolean isIncludeCascadeDelete() {
return cascadeInfo.isDelete() || o2mJoinTable || ModifyListenMode.REMOVALS == modifyListenMode;
}
}
@@ -8,6 +8,7 @@ import io.ebean.ValuePair;
import io.ebean.bean.EntityBean;
import io.ebean.bean.PersistenceContext;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.cache.CacheChangeSet;
import io.ebeaninternal.server.cache.CachedBeanData;
import io.ebeaninternal.server.core.DefaultSqlUpdate;
@@ -37,17 +38,20 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
private final boolean oneToOneExported;
private final boolean orphanRemoval;
private final boolean importedPrimaryKey;
private final boolean primaryKeyExport;
private final PropertyForeignKey foreignKey;
private AssocOneHelp localHelp;
protected final BeanProperty[] embeddedProps;
private final HashMap<String, BeanProperty> embeddedPropsMap;
/**
* The information for Imported foreign Keys.
*/
protected ImportedId importedId;
private String deleteByParentIdSql;
@@ -70,9 +74,12 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
super(descriptor, deploy);
foreignKey = deploy.getForeignKey();
primaryKeyExport = deploy.isPrimaryKeyExport();
importedPrimaryKey = deploy.isImportedPrimaryKey();
oneToOne = deploy.isOneToOne();
oneToOneExported = deploy.isOneToOneExported();
orphanRemoval = deploy.isOrphanRemoval();
if (embedded) {
// Overriding of the columns and use table alias of owning BeanDescriptor
@@ -309,6 +316,18 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
}
public PropertyForeignKey getForeignKey() {
return foreignKey;
}
public boolean hasForeignKey() {
return foreignKey == null || !foreignKey.isNoConstraint();
}
public boolean hasForeignKeyIndex() {
return foreignKey == null || !foreignKey.isNoIndex();
}
/**
* Return true if this a OneToOne property. Otherwise assumed ManyToOne.
*/
@@ -323,6 +342,10 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
return oneToOneExported;
}
public boolean isOrphanRemoval() {
return orphanRemoval;
}
/**
* If true this bean maps to the primary key.
*/
@@ -397,7 +420,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
// cacheData is the id value, maybe already in persistence context
Object assocBean = targetDescriptor.contextGet(context, cacheData);
if (assocBean == null) {
assocBean = targetDescriptor.createReference(Boolean.FALSE, false, cacheData, context);
assocBean = targetDescriptor.createReference(cacheData, context);
}
setValue(bean, assocBean);
}
@@ -550,13 +573,17 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
@Override
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
if (!isTransient) {
localHelp.appendSelect(ctx, subQuery);
if (primaryKeyExport) {
descriptor.getIdProperty().appendSelect(ctx, subQuery);
} else {
localHelp.appendSelect(ctx, subQuery);
}
}
}
@Override
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
if (!isTransient) {
if (!isTransient && !primaryKeyExport) {
localHelp.appendFrom(ctx, joinType);
if (sqlFormulaJoin != null) {
ctx.appendFormulaJoin(sqlFormulaJoin, joinType);
@@ -579,6 +606,16 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
return localHelp.read(ctx);
}
public void addTenant(SpiQuery<?> query, Object tenantId) {
T refBean = targetDescriptor.createReference(tenantId, null);
query.where().eq(name, refBean);
}
public void setTenantValue(EntityBean entityBean, Object tenantId) {
T refBean = targetDescriptor.createReference(tenantId, null);
setValue(entityBean, refBean);
}
@Override
public void setValue(EntityBean bean, Object value) {
super.setValue(bean, value);
@@ -734,6 +771,11 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
*/
public void setParentBeanToChild(EntityBean parent, EntityBean child) {
if (primaryKeyExport) {
Object parentId = descriptor.getId(parent);
targetDescriptor.convertSetId(parentId, child);
}
if (mappedBy != null) {
BeanProperty beanProperty = targetDescriptor.getBeanProperty(mappedBy);
if (beanProperty != null && beanProperty.getValue(child) == null) {
@@ -0,0 +1,36 @@
package io.ebeaninternal.server.deploy;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
/**
* Bean property for synthetic sort order value / order column.
*
* The value of which is held on the entity bean intercept.
*/
public class BeanPropertyOrderColumn extends BeanProperty {
public BeanPropertyOrderColumn(BeanDescriptor<?> descriptor, DeployBeanProperty deploy) {
super(descriptor, deploy);
}
@Override
public Object getValue(EntityBean bean) {
return bean._ebean_getIntercept().getSortOrder();
}
@Override
public Object getValueIntercept(EntityBean bean) {
return bean._ebean_getIntercept().getSortOrder();
}
@Override
public void setValue(EntityBean bean, Object value) {
bean._ebean_getIntercept().setSortOrder((int)value);
}
@Override
public void setValueIntercept(EntityBean bean, Object value) {
bean._ebean_getIntercept().setSortOrder((int)value);
}
}
@@ -121,4 +121,13 @@ public class BeanTable {
}
/**
* Return the primary key DB column.
*/
public String getIdColumn() {
if (idProperties.length != 1) {
throw new IllegalStateException("Expecting only one Id column to join to on "+beanType);
}
return idProperties[0].dbColumn;
}
}
@@ -129,4 +129,9 @@ public interface DbSqlContext {
* Start group by clause.
*/
void startGroupBy();
/**
* Append 'for update' lock hints on FROM clause (sql server only).
*/
void appendFromForUpdate();
}
@@ -0,0 +1,48 @@
package io.ebeaninternal.server.deploy;
import io.ebean.annotation.ConstraintMode;
import io.ebean.annotation.DbForeignKey;
public class PropertyForeignKey {
private final boolean noIndex;
private final boolean noConstraint;
private final ConstraintMode onDelete;
private final ConstraintMode onUpdate;
/**
* Construct for "No Constraint".
*/
public PropertyForeignKey() {
this.noConstraint = true;
this.noIndex = false;
this.onDelete = ConstraintMode.RESTRICT;
this.onUpdate = ConstraintMode.RESTRICT;
}
/**
* Construct for the mapping annotation.
*/
public PropertyForeignKey(DbForeignKey dbForeignKey) {
this.noIndex = dbForeignKey.noIndex();
this.noConstraint = dbForeignKey.noConstraint();
this.onDelete = dbForeignKey.onDelete();
this.onUpdate = dbForeignKey.onUpdate();
}
public boolean isNoIndex() {
return noIndex;
}
public boolean isNoConstraint() {
return noConstraint;
}
public ConstraintMode getOnDelete() {
return onDelete;
}
public ConstraintMode getOnUpdate() {
return onUpdate;
}
}
@@ -27,6 +27,7 @@ import io.ebeaninternal.server.deploy.ChainedBeanPostLoad;
import io.ebeaninternal.server.deploy.ChainedBeanQueryAdapter;
import io.ebeaninternal.server.deploy.IndexDefinition;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import io.ebeaninternal.server.idgen.UuidIdGenerator;
import io.ebeaninternal.server.rawsql.SpiRawSql;
@@ -55,10 +56,7 @@ public class DeployBeanDescriptor<T> {
@Override
public int compare(DeployBeanProperty o1, DeployBeanProperty o2) {
int v2 = o1.getSortOrder();
int v1 = o2.getSortOrder();
return (v1 < v2 ? -1 : (v1 == v2 ? 0 : 1));
return Integer.compare(o2.getSortOrder(), o1.getSortOrder());
}
}
@@ -83,6 +81,8 @@ public class DeployBeanDescriptor<T> {
private DeployBeanPropertyAssocOne<?> unidirectional;
private DeployBeanProperty orderColumn;
/**
* Type of Identity generation strategy used.
*/
@@ -107,7 +107,7 @@ public class DeployBeanDescriptor<T> {
private int sequenceInitialValue;
private int sequenceAllocationSize;
private int sequenceAllocationSize = 50;
/**
* Used with Identity columns but no getGeneratedKeys support.
@@ -203,6 +203,7 @@ public class DeployBeanDescriptor<T> {
private DocStoreMode docStoreDelete;
private List<DeployBeanProperty> idProperties;
private TableJoin primaryKeyJoin;
private short profileId;
@@ -215,6 +216,20 @@ public class DeployBeanDescriptor<T> {
this.beanType = beanType;
}
/**
* PK is also a FK.
*/
public void setPrimaryKeyJoin(TableJoin join) {
this.primaryKeyJoin = join;
this.idType = IdType.EXTERNAL;
this.idGeneratorName = null;
this.idGenerator = null;
}
public TableJoin getPrimaryKeyJoin() {
return primaryKeyJoin;
}
/**
* Return the DeployBeanInfo for the given bean class.
*/
@@ -433,6 +448,14 @@ public class DeployBeanDescriptor<T> {
this.unidirectional = unidirectional;
}
public void setOrderColumn(DeployBeanProperty orderColumn) {
this.orderColumn = orderColumn;
}
public DeployBeanProperty getOrderColumn() {
return orderColumn;
}
/**
* Return the concurrency mode used for beans of this type.
*/
@@ -17,8 +17,8 @@ import io.ebean.config.dbplatform.DbEncryptFunction;
import io.ebean.util.AnnotationUtil;
import io.ebeaninternal.server.core.InternString;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.DeployDocPropertyOptions;
import io.ebeaninternal.server.deploy.DbMigrationInfo;
import io.ebeaninternal.server.deploy.DeployDocPropertyOptions;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.properties.BeanPropertyGetter;
@@ -284,6 +284,10 @@ public class DeployBeanProperty {
return desc.getFullName() + "." + name;
}
public DeployBeanDescriptor<?> getDesc() {
return desc;
}
/**
* Return the DB column length for character columns.
* <p>
@@ -16,7 +16,7 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
/**
* Persist settings.
*/
private final BeanCascadeInfo cascadeInfo = new BeanCascadeInfo();
protected final BeanCascadeInfo cascadeInfo = new BeanCascadeInfo();
/**
* The join table information.
@@ -12,35 +12,39 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
/**
* The type of the many, set, list or map.
*/
final ManyType manyType;
private final ManyType manyType;
ModifyListenMode modifyListenMode = ModifyListenMode.NONE;
/**
* Flag to indicate manyToMany relationship.
*/
boolean manyToMany;
private boolean manyToMany;
private boolean o2mJoinTable;
/**
* Flag to indicate this is a unidirectional relationship.
*/
boolean unidirectional;
private boolean unidirectional;
/**
* Join for manyToMany intersection table.
*/
DeployTableJoin intersectionJoin;
private DeployTableJoin intersectionJoin;
/**
* For ManyToMany this is the Inverse join used to build reference queries.
*/
DeployTableJoin inverseJoin;
private DeployTableJoin inverseJoin;
String fetchOrderBy;
private String fetchOrderBy;
String mapKey;
private String mapKey;
String intersectionDraftTable;
private String intersectionDraftTable;
private DeployOrderColumn orderColumn;
/**
* Create this property.
@@ -206,4 +210,26 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
public void setIntersectionDraftTable() {
this.intersectionDraftTable = intersectionJoin.getTable() + "_draft";
}
public void setOrderColumn(DeployOrderColumn orderColumn) {
this.orderColumn = orderColumn;
}
public DeployOrderColumn getOrderColumn() {
return orderColumn;
}
public boolean hasOrderColumn() {
return orderColumn != null;
}
public boolean isO2mJoinTable() {
return o2mJoinTable;
}
public void setO2mJoinTable() {
this.o2mJoinTable = true;
setModifyListenMode(ModifyListenMode.ALL);
}
}
@@ -1,5 +1,9 @@
package io.ebeaninternal.server.deploy.meta;
import io.ebeaninternal.server.deploy.PropertyForeignKey;
import javax.persistence.CascadeType;
/**
* Property mapped to a joined bean.
*/
@@ -9,12 +13,20 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
private boolean oneToOneExported;
private boolean primaryKeyJoin;
private boolean primaryKeyExport;
private boolean importedPrimaryKey;
private DeployBeanEmbedded deployEmbedded;
private String columnPrefix;
private PropertyForeignKey foreignKey;
private boolean orphanRemoval;
/**
* Create the property.
*/
@@ -115,4 +127,47 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
public String getColumnPrefix() {
return columnPrefix;
}
/**
* Mark as PrimaryKeyJoin (we don't know which side is the export side initially).
*/
public void setPrimaryKeyJoin(boolean primaryKeyJoin) {
this.primaryKeyJoin = primaryKeyJoin;
}
public boolean isPrimaryKeyJoin() {
return primaryKeyJoin;
}
public boolean isPrimaryKeyExport() {
return primaryKeyExport;
}
/**
* Set as export side of OneToOne with PrimaryKeyJoin.
*/
public void setPrimaryKeyExport() {
this.primaryKeyExport = true;
this.oneToOneExported = true;
if (!cascadeInfo.isSave()) {
// we pretty much need to cascade save so turning that on automatically ...
cascadeInfo.setType(CascadeType.ALL);
}
}
public void setForeignKey(PropertyForeignKey foreignKey) {
this.foreignKey = foreignKey;
}
public PropertyForeignKey getForeignKey() {
return foreignKey;
}
public void setOrphanRemoval(boolean orphanRemoval) {
this.orphanRemoval = orphanRemoval;
}
public boolean isOrphanRemoval() {
return orphanRemoval;
}
}
@@ -1,11 +1,11 @@
package io.ebeaninternal.server.deploy.meta;
import io.ebean.bean.BeanCollection.ModifyListenMode;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanDescriptorMap;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.BeanPropertyOrderColumn;
import io.ebeaninternal.server.deploy.BeanPropertySimpleCollection;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.TableJoin;
@@ -64,6 +64,7 @@ public class DeployBeanPropertyLists {
private final TableJoin[] tableJoins;
private final BeanPropertyAssocOne<?> unidirectional;
private final BeanProperty orderColumn;
@SuppressWarnings({"unchecked", "rawtypes"})
public DeployBeanPropertyLists(BeanDescriptorMap owner, BeanDescriptor<?> desc, DeployBeanDescriptor<?> deploy) {
@@ -71,12 +72,11 @@ public class DeployBeanPropertyLists {
setImportedPrimaryKeys(deploy);
DeployBeanProperty deployOrderColumn = deploy.getOrderColumn();
this.orderColumn = deployOrderColumn != null ? new BeanPropertyOrderColumn(desc, deployOrderColumn) : null;
DeployBeanPropertyAssocOne<?> deployUnidirectional = deploy.getUnidirectional();
if (deployUnidirectional == null) {
unidirectional = null;
} else {
unidirectional = new BeanPropertyAssocOne(owner, desc, deployUnidirectional);
}
this.unidirectional = deployUnidirectional == null ? null : new BeanPropertyAssocOne(owner, desc, deployUnidirectional);
this.propertyMap = new LinkedHashMap<>();
@@ -89,7 +89,7 @@ public class DeployBeanPropertyLists {
// Create a BeanProperty for the discriminator column to support
// using RawSql queries with inheritance
discriminatorColumn = inheritInfo.getDiscriminatorColumn();
DeployBeanProperty discDeployProp = new DeployBeanProperty(deploy, String.class, new ScalarTypeString(), null);
DeployBeanProperty discDeployProp = new DeployBeanProperty(deploy, String.class, ScalarTypeString.INSTANCE, null);
discDeployProp.setDiscriminator();
discDeployProp.setName(discriminatorColumn);
discDeployProp.setDbColumn(discriminatorColumn);
@@ -115,6 +115,12 @@ public class DeployBeanPropertyLists {
allocateToList(prop);
}
if (orderColumn != null) {
orderColumn.setDeployOrder(order++);
allocateToList(orderColumn);
propertyMap.put(orderColumn.getName(), orderColumn);
}
if (discProperty != null) {
// put the discriminator property into the property map only
// (after the real properties have been organised into their lists)
@@ -160,6 +166,13 @@ public class DeployBeanPropertyLists {
return unidirectional;
}
/**
* Return the order column property.
*/
public BeanProperty getOrderColumn() {
return orderColumn;
}
/**
* Allocate the property to a list.
*/
@@ -194,6 +207,9 @@ public class DeployBeanPropertyLists {
} else {
nonManys.add(prop);
if (prop.isTenantId()) {
tenant = prop;
}
if (prop instanceof BeanPropertyAssocOne<?>) {
BeanPropertyAssocOne<?> assocOne = (BeanPropertyAssocOne<?>) prop;
if (prop.isEmbedded()) {
@@ -214,8 +230,6 @@ public class DeployBeanPropertyLists {
}
} else if (prop.isDraftDirty()) {
draftDirty = prop;
} else if (prop.isTenantId()) {
tenant = prop;
}
if (!prop.isAggregation()) {
baseScalar.add(prop);
@@ -423,17 +437,12 @@ public class DeployBeanPropertyLists {
for (BeanPropertyAssocMany<?> prop : manys) {
switch (mode) {
case Save:
if (prop.getCascadeInfo().isSave() || prop.isManyToMany()
|| ModifyListenMode.REMOVALS == prop.getModifyListenMode()) {
// Note ManyToMany always included as we always 'save'
// the relationship via insert/delete of intersection table
// REMOVALS means including PrivateOwned relationships
if (prop.isIncludeCascadeSave()) {
list.add(prop);
}
break;
case Delete:
if (prop.getCascadeInfo().isDelete() || ModifyListenMode.REMOVALS == prop.getModifyListenMode()) {
// REMOVALS means including PrivateOwned relationships
if (prop.isIncludeCascadeDelete()) {
list.add(prop);
}
break;
@@ -0,0 +1,39 @@
package io.ebeaninternal.server.deploy.meta;
import javax.persistence.OrderColumn;
public class DeployOrderColumn {
/**
* Logical property name used for order by and available for expression language.
*/
public static final String LOGICAL_NAME = "orderColumn";
private final String name;
private final boolean insertable;
private final boolean updatable;
private final boolean nullable;
public DeployOrderColumn(OrderColumn orderColumn) {
this.name = orderColumn.name();
this.insertable = orderColumn.insertable();
this.updatable = orderColumn.updatable();
this.nullable = orderColumn.nullable();
}
public String getName() {
return name;
}
public boolean isInsertable() {
return insertable;
}
public boolean isUpdatable() {
return updatable;
}
public boolean isNullable() {
return nullable;
}
}
@@ -13,16 +13,19 @@ import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanTable;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.meta.DeployOrderColumn;
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
import io.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
import io.ebeaninternal.server.query.SqlJoinType;
import javax.persistence.CascadeType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.MapKey;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.OrderColumn;
import java.util.Set;
/**
@@ -66,6 +69,14 @@ class AnnotationAssocManys extends AnnotationParser {
prop.setModifyListenMode(ModifyListenMode.REMOVALS);
prop.getCascadeInfo().setDelete(privateOwned.cascadeRemove());
}
OrderColumn orderColumn = get(prop, OrderColumn.class);
if (orderColumn != null) {
// need to cascade as we set the order on cascade
prop.setOrderColumn(new DeployOrderColumn(orderColumn));
prop.setFetchOrderBy(DeployOrderColumn.LOGICAL_NAME);
prop.getCascadeInfo().setType(CascadeType.ALL);
prop.setModifyListenMode(ModifyListenMode.ALL);
}
}
ManyToMany manyToMany = get(prop, ManyToMany.class);
if (manyToMany != null) {
@@ -111,8 +122,10 @@ class AnnotationAssocManys extends AnnotationParser {
readJoinTable(joinTable, prop);
} else {
// OneToMany in theory
prop.getTableJoin().addJoinColumn(true, joinTable.joinColumns(), beanTable);
// OneToMany with @JoinTable
prop.setO2mJoinTable();
readJoinTable(joinTable, prop);
manyToManyDefaultJoins(prop);
}
}
@@ -1,22 +1,32 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.annotation.DbForeignKey;
import io.ebean.annotation.FetchPreference;
import io.ebean.annotation.TenantId;
import io.ebean.annotation.Where;
import io.ebean.config.NamingConvention;
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
import io.ebeaninternal.server.deploy.BeanTable;
import io.ebeaninternal.server.deploy.PropertyForeignKey;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
import io.ebeaninternal.server.query.SqlJoinType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.validation.constraints.NotNull;
/**
@@ -24,12 +34,14 @@ import javax.validation.constraints.NotNull;
*/
public class AnnotationAssocOnes extends AnnotationParser {
private static final Logger log = LoggerFactory.getLogger(AnnotationAssocOnes.class);
private final BeanDescriptorManager factory;
/**
* Create with the deploy Info.
*/
public AnnotationAssocOnes(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
AnnotationAssocOnes(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
super(info, javaxValidationAnnotations);
this.factory = factory;
}
@@ -52,6 +64,9 @@ public class AnnotationAssocOnes extends AnnotationParser {
ManyToOne manyToOne = get(prop, ManyToOne.class);
if (manyToOne != null) {
readManyToOne(manyToOne, prop);
if (get(prop, TenantId.class) != null) {
prop.setTenantId();
}
}
OneToOne oneToOne = get(prop, OneToOne.class);
if (oneToOne != null) {
@@ -82,12 +97,22 @@ public class AnnotationAssocOnes extends AnnotationParser {
prop.setNullable(false);
}
DbForeignKey dbForeignKey = get(prop, DbForeignKey.class);
if (dbForeignKey != null){
prop.setForeignKey(new PropertyForeignKey(dbForeignKey));
}
Where where = get(prop, Where.class);
if (where != null) {
// not expecting this to be used on assoc one properties
prop.setExtraWhere(where.clause());
}
PrimaryKeyJoinColumn primaryKeyJoin = get(prop, PrimaryKeyJoinColumn.class);
if (primaryKeyJoin != null) {
readPrimaryKeyJoin(primaryKeyJoin, prop);
}
FetchPreference fetchPreference = get(prop, FetchPreference.class);
if (fetchPreference != null) {
prop.setFetchPreference(fetchPreference.value());
@@ -112,6 +137,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
if (!joinColumn.nullable()) {
prop.setNullable(false);
}
checkForNoConstraint(prop, joinColumn);
}
@@ -152,22 +178,32 @@ public class AnnotationAssocOnes extends AnnotationParser {
}
}
private void checkForNoConstraint(DeployBeanPropertyAssocOne<?> prop, JoinColumn joinColumn) {
ForeignKey foreignKey = joinColumn.foreignKey();
if (foreignKey != null && foreignKey.value() == ConstraintMode.NO_CONSTRAINT) {
prop.setForeignKey(new PropertyForeignKey());
}
}
private String errorMsgMissingBeanTable(Class<?> type, String from) {
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered?";
}
private BeanTable beanTable(DeployBeanPropertyAssoc<?> prop) {
BeanTable assoc = factory.getBeanTable(prop.getPropertyType());
if (assoc == null) {
throw new RuntimeException(errorMsgMissingBeanTable(prop.getPropertyType(), prop.getFullBeanName()));
}
return assoc;
}
private void readManyToOne(ManyToOne propAnn, DeployBeanProperty prop) {
DeployBeanPropertyAssocOne<?> beanProp = (DeployBeanPropertyAssocOne<?>) prop;
setCascadeTypes(propAnn.cascade(), beanProp.getCascadeInfo());
BeanTable assoc = factory.getBeanTable(beanProp.getPropertyType());
if (assoc == null) {
String msg = errorMsgMissingBeanTable(beanProp.getPropertyType(), prop.getFullBeanName());
throw new RuntimeException(msg);
}
beanProp.setBeanTable(assoc);
beanProp.setBeanTable(beanTable(beanProp));
beanProp.setDbInsertable(true);
beanProp.setDbUpdateable(true);
beanProp.setNullable(propAnn.optional());
@@ -184,17 +220,33 @@ public class AnnotationAssocOnes extends AnnotationParser {
prop.setMappedBy(propAnn.mappedBy());
if (!"".equals(propAnn.mappedBy())) {
prop.setOneToOneExported();
prop.setOrphanRemoval(propAnn.orphanRemoval());
}
setCascadeTypes(propAnn.cascade(), prop.getCascadeInfo());
prop.setBeanTable(beanTable(prop));
}
BeanTable assoc = factory.getBeanTable(prop.getPropertyType());
if (assoc == null) {
String msg = errorMsgMissingBeanTable(prop.getPropertyType(), prop.getFullBeanName());
throw new RuntimeException(msg);
private void readPrimaryKeyJoin(PrimaryKeyJoinColumn primaryKeyJoin, DeployBeanPropertyAssocOne<?> prop) {
if (!prop.isOneToOne()) {
throw new IllegalStateException("Expecting property " + prop.getFullBeanName() + " with PrimaryKeyJoinColumn to be a OneToOne?");
}
prop.setPrimaryKeyJoin(true);
if (!primaryKeyJoin.name().isEmpty()) {
log.warn("Automatically determining join columns and ignoring PrimaryKeyJoinColumn.name {} on {}", primaryKeyJoin.name(), prop.getFullBeanName());
}
if (!primaryKeyJoin.referencedColumnName().isEmpty()) {
log.warn("Automatically determining join columns and Ignoring PrimaryKeyJoinColumn.referencedColumnName {} on {}", primaryKeyJoin.referencedColumnName(), prop.getFullBeanName());
}
prop.setBeanTable(assoc);
BeanTable baseBeanTable = factory.getBeanTable(info.getDescriptor().getBeanType());
String localPrimaryKey = baseBeanTable.getIdColumn();
String foreignColumn = beanTable(prop).getIdColumn();
prop.getTableJoin().addJoinColumn(new DeployTableJoinColumn(localPrimaryKey, foreignColumn, false, false));
}
private void readEmbedded(DeployBeanPropertyAssocOne<?> prop, Embedded embedded) {
@@ -1,11 +1,12 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.RawSql;
import io.ebeaninternal.server.rawsql.SpiRawSql;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.rawsql.SpiRawSql;
import java.util.HashMap;
@@ -91,4 +92,11 @@ public class DeployBeanInfo<T> {
public void addNamedQuery(String name, String query) {
descriptor.addNamedQuery(name, query);
}
/**
* Set that the PK is also a foreign key.
*/
public void setPrimaryKeyJoin(TableJoin join) {
descriptor.setPrimaryKeyJoin(join);
}
}
@@ -8,7 +8,6 @@ import io.ebean.Update;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.BeanCollection.ModifyListenMode;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.PersistenceContext;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiTransaction;
@@ -21,7 +20,6 @@ import io.ebeaninternal.server.core.PersistRequestCallableSql;
import io.ebeaninternal.server.core.PersistRequestOrmUpdate;
import io.ebeaninternal.server.core.PersistRequestUpdateSql;
import io.ebeaninternal.server.core.Persister;
import io.ebeaninternal.server.deploy.BeanCollectionUtil;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
import io.ebeaninternal.server.deploy.BeanManager;
@@ -29,7 +27,6 @@ import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.IntersectionRow;
import io.ebeaninternal.server.deploy.ManyType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -413,7 +410,7 @@ public final class DefaultPersister implements Persister {
}
}
private void saveRecurse(EntityBean bean, Transaction t, Object parentBean, boolean insertMode, boolean publish) {
void saveRecurse(EntityBean bean, Transaction t, Object parentBean, boolean insertMode, boolean publish) {
// determine insert or update taking into account stateless updates
PersistRequestBean<?> request = createRequestRecurse(bean, t, parentBean, insertMode, publish);
@@ -581,6 +578,16 @@ public final class DefaultPersister implements Persister {
}
BeanDescriptor<?> descriptor = beanDescriptorManager.getBeanDescriptor(beanType);
boolean softDelete = !permanent && descriptor.isSoftDelete();
if (descriptor.isMultiTenant()) {
// convert to a delete by bean
for (Object id : ids) {
EntityBean bean = descriptor.createEntityBean();
descriptor.convertSetId(id, bean);
deleteRecurse(bean, transaction, permanent);
}
return ids.size();
}
ArrayList<Object> idList = new ArrayList<>(ids.size());
for (Object id : ids) {
@@ -588,7 +595,6 @@ public final class DefaultPersister implements Persister {
idList.add(descriptor.convertId(id));
}
boolean softDelete = !permanent && descriptor.isSoftDelete();
return delete(descriptor, null, idList, transaction, softDelete);
}
@@ -598,6 +604,14 @@ public final class DefaultPersister implements Persister {
@Override
public int delete(Class<?> beanType, Object id, Transaction transaction, boolean permanent) {
BeanDescriptor<?> descriptor = beanDescriptorManager.getBeanDescriptor(beanType);
if (descriptor.isMultiTenant()) {
// convert to a delete by bean
EntityBean bean = descriptor.createEntityBean();
descriptor.convertSetId(id, bean);
delete(bean, transaction, permanent);
return 1;
}
id = descriptor.convertId(id);
boolean softDelete = !permanent && descriptor.isSoftDelete();
return delete(descriptor, id, null, transaction, softDelete);
@@ -826,89 +840,9 @@ public final class DefaultPersister implements Persister {
}
}
/**
* Helper to wrap the details when saving a OneToMany or ManyToMany
* relationship.
*/
private static class SaveManyPropRequest {
private final boolean insertedParent;
private final BeanPropertyAssocMany<?> many;
private final EntityBean parentBean;
private final SpiTransaction transaction;
private final boolean cascade;
private final boolean deleteMissingChildren;
private final boolean publish;
private SaveManyPropRequest(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
this.insertedParent = insertedParent;
this.many = many;
this.cascade = many.getCascadeInfo().isSave();
this.parentBean = parentBean;
this.transaction = request.getTransaction();
this.deleteMissingChildren = request.isDeleteMissingChildren();
this.publish = request.isPublish();
}
public boolean isSaveIntersection() {
return transaction.isSaveAssocManyIntersection(many.getIntersectionTableJoin().getTable(), many.getBeanDescriptor().getName());
}
private Object getValue() {
return many.getValue(parentBean);
}
private boolean isModifyListenMode() {
return ModifyListenMode.REMOVALS == many.getModifyListenMode();
}
private boolean isDeleteMissingChildren() {
return deleteMissingChildren;
}
private boolean isInsertedParent() {
return insertedParent;
}
private BeanPropertyAssocMany<?> getMany() {
return many;
}
private EntityBean getParentBean() {
return parentBean;
}
private SpiTransaction getTransaction() {
return transaction;
}
private boolean isCascade() {
return cascade;
}
public void modifyListenReset(BeanCollection<?> c) {
if (insertedParent) {
// after insert set the modify listening mode
// for private owned etc
c.setModifyListening(many.getModifyListenMode());
}
c.modifyReset();
}
public boolean isPublish() {
return publish;
}
private void resetModifyState() {
Object details = getValue();
if (details instanceof BeanCollection<?>) {
modifyListenReset((BeanCollection<?>) details);
}
}
}
private void saveMany(SaveManyPropRequest saveMany, boolean insertMode) {
if (saveMany.getMany().isManyToMany()) {
if (saveMany.getMany().hasJoinTable()) {
// check if we can save the m2m intersection in this direction
// we only allow one direction based on first traversed basis
@@ -967,107 +901,7 @@ public final class DefaultPersister implements Persister {
*/
private void saveAssocManyDetails(SaveManyPropRequest saveMany, boolean deleteMissingChildren, boolean insertMode) {
BeanPropertyAssocMany<?> prop = saveMany.getMany();
Object details = saveMany.getValue();
// check that the list is not null and if it is a BeanCollection
// check that is has been populated (don't trigger lazy loading)
// For a Map this is a collection of Map.Entry objects and not beans
Collection<?> collection = BeanCollectionUtil.getActualEntries(details);
if (collection == null) {
// nothing to do here
return;
}
BeanDescriptor<?> targetDescriptor = prop.getTargetDescriptor();
if (saveMany.isInsertedParent()) {
// performance optimisation for large collections
targetDescriptor.preAllocateIds(collection.size());
}
SpiTransaction t = saveMany.getTransaction();
boolean isMap = ManyType.MAP == prop.getManyType();
EntityBean parentBean = saveMany.getParentBean();
if (deleteMissingChildren) {
// collect the Id's (to exclude from deleteManyDetails)
List<Object> detailIds = collectIds(collection, targetDescriptor, isMap);
// deleting missing children - children not in our collected detailIds
deleteManyDetails(t, prop.getBeanDescriptor(), parentBean, prop, detailIds, false);
}
// increase depth for batching order
t.depth(+1);
// if a map, then we get the key value and
// set it to the appropriate property on the
// detail bean before we save it
Object mapKeyValue = null;
boolean saveSkippable = prop.isSaveRecurseSkippable();
boolean skipSavingThisBean;
for (Object detailBean : collection) {
if (isMap) {
// its a map so need the key and value
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) detailBean;
mapKeyValue = entry.getKey();
detailBean = entry.getValue();
}
if (detailBean instanceof EntityBean) {
EntityBean detail = (EntityBean) detailBean;
EntityBeanIntercept ebi = detail._ebean_getIntercept();
if (prop.isManyToMany()) {
skipSavingThisBean = targetDescriptor.isReference(ebi);
} else {
if (targetDescriptor.isReference(ebi)) {
// we can skip this one
skipSavingThisBean = true;
} else if (ebi.isNewOrDirty()) {
skipSavingThisBean = false;
// set the parent bean to detailBean
prop.setJoinValuesToChild(parentBean, detail, mapKeyValue);
} else {
// unmodified so skip depending on prop.isSaveRecurseSkippable();
skipSavingThisBean = saveSkippable;
}
}
if (!skipSavingThisBean) {
saveRecurse(detail, t, parentBean, insertMode, saveMany.isPublish());
}
}
}
t.depth(-1);
}
/**
* Collect the Id values of the details to remove 'missing children' for stateless updates.
*/
private List<Object> collectIds(Collection<?> collection, BeanDescriptor<?> targetDescriptor, boolean isMap) {
List<Object> detailIds = new ArrayList<>();
// stateless update with deleteMissingChildren so first
// collect the Id values to remove the 'missing children'
for (Object detailBean : collection) {
if (isMap) {
detailBean = ((Map.Entry<?, ?>) detailBean).getValue();
}
if (detailBean instanceof EntityBean) {
Object id = targetDescriptor.getId((EntityBean) detailBean);
if (!DmlUtil.isNullOrZero(id)) {
// remember the Id (other details not in the collection) will be removed
detailIds.add(id);
}
}
}
return detailIds;
saveMany.saveDetails(this, deleteMissingChildren, insertMode);
}
/**
@@ -1108,13 +942,19 @@ public final class DefaultPersister implements Persister {
throw new PersistenceException(msg);
}
if (!vanillaCollection) {
((BeanCollection<?>) value).modifyReset();
BeanCollection<?> manyValue = (BeanCollection<?>) value;
setListenMode(manyValue, prop);
manyValue.modifyReset();
}
} else {
// BeanCollection so get the additions/deletions
BeanCollection<?> manyValue = (BeanCollection<?>) value;
additions = manyValue.getModifyAdditions();
deletions = manyValue.getModifyRemovals();
if (setListenMode(manyValue, prop)) {
additions = manyValue.getActualDetails();
} else {
additions = manyValue.getModifyAdditions();
deletions = manyValue.getModifyRemovals();
}
// reset so the changes are only processed once
manyValue.modifyReset();
}
@@ -1169,6 +1009,19 @@ public final class DefaultPersister implements Persister {
t.depth(-1);
}
/**
* Check if we need to set the listen mode (on new collections persisted for the first time).
*/
private boolean setListenMode(BeanCollection<?> manyValue, BeanPropertyAssocMany<?> prop) {
ModifyListenMode mode = manyValue.getModifyListening();
if (mode == null) {
// new collection persisted for the first time
manyValue.setModifyListening(prop.getModifyListenMode());
return true;
}
return false;
}
private int deleteAssocManyIntersection(EntityBean bean, BeanPropertyAssocMany<?> many, Transaction t, boolean publish) {
// delete all intersection rows for this bean
@@ -1222,7 +1075,7 @@ public final class DefaultPersister implements Persister {
// Many's with delete cascade
BeanPropertyAssocMany<?>[] manys = desc.propertiesManyDelete();
for (BeanPropertyAssocMany<?> many : manys) {
if (many.isManyToMany()) {
if (many.hasJoinTable()) {
if (!softDelete) {
// delete associated rows from intersection table (but not during soft delete)
deleteAssocManyIntersection(parentBean, many, t, request.isPublish());
@@ -1266,7 +1119,7 @@ public final class DefaultPersister implements Persister {
* collection (and should not be deleted).
* </p>
*/
private void deleteManyDetails(SpiTransaction t, BeanDescriptor<?> desc, EntityBean parentBean,
void deleteManyDetails(SpiTransaction t, BeanDescriptor<?> desc, EntityBean parentBean,
BeanPropertyAssocMany<?> many, List<Object> excludeDetailIds, boolean softDelete) {
if (many.getCascadeInfo().isDelete()) {
@@ -1302,7 +1155,7 @@ public final class DefaultPersister implements Persister {
// convert into a list of reference objects and perform delete by object
List<Object> refList = new ArrayList<>(childIds.size());
for (Object id : childIds) {
refList.add(targetDesc.createReference(null, false, id, null));
refList.add(targetDesc.createReference(id, null));
}
deleteList(refList, t, softDelete);
@@ -1337,6 +1190,15 @@ public final class DefaultPersister implements Persister {
}
}
}
for (BeanPropertyAssocOne<?> prop : desc.propertiesOneExportedSave()) {
if (prop.isOrphanRemoval() && request.isDirtyProperty(prop)) {
Object origValue = request.getOrigValue(prop);
if (origValue instanceof EntityBean) {
delete((EntityBean) origValue, request.getTransaction(), true);
}
}
}
}
/**
@@ -16,7 +16,7 @@ import java.sql.SQLException;
/**
* Executes the UpdateSql requests.
*/
public class ExeOrmUpdate {
class ExeOrmUpdate {
private static final Logger logger = LoggerFactory.getLogger(ExeOrmUpdate.class);
@@ -27,7 +27,7 @@ public class ExeOrmUpdate {
/**
* Create with a given binder.
*/
public ExeOrmUpdate(Binder binder) {
ExeOrmUpdate(Binder binder) {
this.pstmtFactory = new PstmtFactory();
this.binder = binder;
}
@@ -107,7 +107,7 @@ public class ExeOrmUpdate {
if (logSql) {
t.logSql(sql);
}
pstmt = pstmtFactory.getPstmt(t, sql);
pstmt = pstmtFactory.getPstmt(t, sql, false);
}
String bindLog = null;
@@ -6,17 +6,19 @@ import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.core.PersistRequestUpdateSql;
import io.ebeaninternal.server.core.PersistRequestUpdateSql.SqlType;
import io.ebeaninternal.server.util.BindParamsParser;
import io.ebeaninternal.util.JdbcClose;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Executes the UpdateSql requests.
*/
public class ExeUpdateSql {
class ExeUpdateSql {
private static final Logger logger = LoggerFactory.getLogger(ExeUpdateSql.class);
@@ -27,7 +29,7 @@ public class ExeUpdateSql {
/**
* Create with a given binder.
*/
public ExeUpdateSql(Binder binder) {
ExeUpdateSql(Binder binder) {
this.binder = binder;
this.pstmtFactory = new PstmtFactory();
}
@@ -51,6 +53,9 @@ public class ExeUpdateSql {
} else {
int rowCount = pstmt.executeUpdate();
request.checkRowCount(rowCount);
if (request.isGetGeneratedKeys()) {
readGeneratedKeys(pstmt, request);
}
request.postExecute();
return rowCount;
}
@@ -68,6 +73,22 @@ public class ExeUpdateSql {
}
}
private void readGeneratedKeys(PreparedStatement stmt, PersistRequestUpdateSql request) {
ResultSet resultSet = null;
try {
resultSet = stmt.getGeneratedKeys();
if (resultSet.next()) {
request.setGeneratedKey(resultSet.getObject(1));
}
} catch (SQLException ex) {
throw new PersistenceException(ex);
} finally {
JdbcClose.close(resultSet);
}
}
private PreparedStatement bindStmt(PersistRequestUpdateSql request, boolean batchThisRequest) throws SQLException {
SpiSqlUpdate updateSql = request.getUpdateSql();
@@ -90,7 +111,7 @@ public class ExeUpdateSql {
if (logSql) {
t.logSql(TrimLogSql.trim(sql));
}
pstmt = pstmtFactory.getPstmt(t, sql);
pstmt = pstmtFactory.getPstmt(t, sql, request.isGetGeneratedKeys());
}
if (updateSql.getTimeout() > 0) {
@@ -6,6 +6,7 @@ import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
/**
* Factory for creating Statements.
@@ -14,9 +15,9 @@ import java.sql.SQLException;
* getGeneratedKeys.
* </p>
*/
public class PstmtFactory {
class PstmtFactory {
public PstmtFactory() {
PstmtFactory() {
}
/**
@@ -30,9 +31,13 @@ public class PstmtFactory {
/**
* Get a prepared statement without any batching.
*/
public PreparedStatement getPstmt(SpiTransaction t, String sql) throws SQLException {
public PreparedStatement getPstmt(SpiTransaction t, String sql, boolean getGeneratedKeys) throws SQLException {
Connection conn = t.getInternalConnection();
return conn.prepareStatement(sql);
if (getGeneratedKeys) {
return conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
} else {
return conn.prepareStatement(sql);
}
}
/**
@@ -0,0 +1,247 @@
package io.ebeaninternal.server.persist;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.deploy.BeanCollectionUtil;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.ManyType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Helper to wrap the details when saving a OneToMany or ManyToMany relationship.
*/
class SaveManyPropRequest {
private final boolean insertedParent;
private final BeanPropertyAssocMany<?> many;
private final EntityBean parentBean;
private final SpiTransaction transaction;
private final boolean cascade;
private final boolean deleteMissingChildren;
private final boolean publish;
private final Object value;
private final BeanDescriptor<?> targetDescriptor;
private final boolean isMap;
private final boolean saveRecurseSkippable;
private Collection<?> collection;
private DefaultPersister persister;
private boolean deleteMissing;
private boolean insertMode;
private int sortOrder;
SaveManyPropRequest(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
this.insertedParent = insertedParent;
this.many = many;
this.cascade = many.getCascadeInfo().isSave();
this.parentBean = parentBean;
this.transaction = request.getTransaction();
this.deleteMissingChildren = request.isDeleteMissingChildren();
this.publish = request.isPublish();
this.value = many.getValue(parentBean);
this.targetDescriptor = many.getTargetDescriptor();
this.isMap = ManyType.MAP == many.getManyType();
this.saveRecurseSkippable = many.isSaveRecurseSkippable();
}
public boolean isSaveIntersection() {
return transaction.isSaveAssocManyIntersection(many.getIntersectionTableJoin().getTable(), many.getBeanDescriptor().getName());
}
Object getValue() {
return value;
}
boolean isModifyListenMode() {
return BeanCollection.ModifyListenMode.REMOVALS == many.getModifyListenMode();
}
boolean isDeleteMissingChildren() {
return deleteMissingChildren;
}
boolean isInsertedParent() {
return insertedParent;
}
BeanPropertyAssocMany<?> getMany() {
return many;
}
EntityBean getParentBean() {
return parentBean;
}
SpiTransaction getTransaction() {
return transaction;
}
boolean isCascade() {
return cascade;
}
boolean isPublish() {
return publish;
}
void modifyListenReset(BeanCollection<?> c) {
if (insertedParent) {
// after insert set the modify listening mode for private owned etc
c.setModifyListening(many.getModifyListenMode());
}
c.modifyReset();
}
void resetModifyState() {
if (value instanceof BeanCollection<?>) {
modifyListenReset((BeanCollection<?>) value);
}
}
void saveDetails(DefaultPersister persister, boolean deleteMissing, boolean insertMode) {
this.persister = persister;
this.deleteMissing = deleteMissing;
this.insertMode = insertMode;
// check that the list is not null and if it is a BeanCollection
// check that is has been populated (don't trigger lazy loading)
// For a Map this is a collection of Map.Entry objects and not beans
collection = BeanCollectionUtil.getActualEntries(value);
if (collection != null) {
processDetails();
}
}
private void processDetails() {
BeanProperty orderColumn = null;
boolean hasOrderColumn = many.hasOrderColumn();
if (hasOrderColumn) {
if (!insertedParent && canSkipForOrderColumn()) {
return;
}
orderColumn = targetDescriptor.getOrderColumn();
}
if (insertedParent) {
// performance optimisation for large collections
targetDescriptor.preAllocateIds(collection.size());
}
if (deleteMissing) {
// collect the Id's (to exclude from deleteManyDetails)
List<Object> detailIds = collectIds(collection, targetDescriptor, isMap);
// deleting missing children - children not in our collected detailIds
persister.deleteManyDetails(transaction, many.getBeanDescriptor(), parentBean, many, detailIds, false);
}
transaction.depth(+1);
saveAllBeans(orderColumn);
if (hasOrderColumn) {
resetModifyState();
}
transaction.depth(-1);
}
private void saveAllBeans(BeanProperty orderColumn) {
// if a map, then we get the key value and
// set it to the appropriate property on the
// detail bean before we save it
Object mapKeyValue = null;
boolean skipSavingThisBean;
for (Object detailBean : collection) {
sortOrder++;
if (isMap) {
// its a map so need the key and value
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) detailBean;
mapKeyValue = entry.getKey();
detailBean = entry.getValue();
}
if (detailBean instanceof EntityBean) {
EntityBean detail = (EntityBean) detailBean;
EntityBeanIntercept ebi = detail._ebean_getIntercept();
if (many.hasJoinTable()) {
skipSavingThisBean = targetDescriptor.isReference(ebi);
} else {
if (orderColumn != null) {
orderColumn.setValue(detail, sortOrder);
ebi.setDirty(true);
}
if (targetDescriptor.isReference(ebi)) {
// we can skip this one
skipSavingThisBean = true;
} else if (ebi.isNewOrDirty()) {
skipSavingThisBean = false;
// set the parent bean to detailBean
many.setJoinValuesToChild(parentBean, detail, mapKeyValue);
} else {
// unmodified so skip depending on prop.isSaveRecurseSkippable();
skipSavingThisBean = saveRecurseSkippable;
}
}
if (!skipSavingThisBean) {
persister.saveRecurse(detail, transaction, parentBean, insertMode, publish);
}
}
}
}
/**
* Return true if we can skip based on .. no modifications to the collection and no beans are dirty.
*/
private boolean canSkipForOrderColumn() {
return value instanceof BeanCollection
&& !((BeanCollection<?>) value).wasTouched()
&& noDirtyBeans();
}
private boolean noDirtyBeans() {
for (Object bean : collection) {
if (bean instanceof EntityBean && ((EntityBean) bean)._ebean_getIntercept().isDirty()) {
return false;
}
}
return true;
}
/**
* Collect the Id values of the details to remove 'missing children' for stateless updates.
*/
private List<Object> collectIds(Collection<?> collection, BeanDescriptor<?> targetDescriptor, boolean isMap) {
List<Object> detailIds = new ArrayList<>();
// stateless update with deleteMissingChildren so first
// collect the Id values to remove the 'missing children'
for (Object detailBean : collection) {
if (isMap) {
detailBean = ((Map.Entry<?, ?>) detailBean).getValue();
}
if (detailBean instanceof EntityBean) {
Object id = targetDescriptor.getId((EntityBean) detailBean);
if (!DmlUtil.isNullOrZero(id)) {
// remember the Id (other details not in the collection) will be removed
detailIds.add(id);
}
}
}
return detailIds;
}
}
@@ -24,6 +24,8 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
private static final Logger logger = LoggerFactory.getLogger(DmlHandler.class);
private static final int[] GENERATED_KEY_COLUMNS = new int[]{1};
/**
* The originating request.
*/
@@ -260,8 +262,7 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
// the Id generated is always the first column
// Required to stop Oracle10 giving us Oracle rowId??
// Other jdbc drivers seem fine without this hint.
int[] columns = {1};
return conn.prepareStatement(sql, columns);
return conn.prepareStatement(sql, GENERATED_KEY_COLUMNS);
} else {
return conn.prepareStatement(sql);
@@ -3,10 +3,12 @@ package io.ebeaninternal.server.persist.dml;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbEncrypt;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.persist.dmlbind.Bindable;
import io.ebeaninternal.server.persist.dmlbind.BindableId;
import io.ebeaninternal.server.persist.dmlbind.BindableList;
import io.ebeaninternal.server.persist.dmlbind.BindableOrderColumn;
import io.ebeaninternal.server.persist.dmlbind.BindableUnidirectional;
import io.ebeaninternal.server.persist.dmlbind.FactoryAssocOnes;
import io.ebeaninternal.server.persist.dmlbind.FactoryBaseProperties;
@@ -62,6 +64,11 @@ public class MetaFactory {
embeddedFact.create(setList, desc, DmlMode.UPDATE, includeLobs);
assocOneFact.create(setList, desc, DmlMode.UPDATE);
BeanProperty orderColumn = desc.getOrderColumn();
if (orderColumn != null) {
setList.add(new BindableOrderColumn(orderColumn));
}
BindableId id = idFact.createId(desc);
Bindable version = versionFact.create(desc);
Bindable tenantId = versionFact.createTenantId(desc);
@@ -66,15 +66,4 @@ class BindableAssocOne implements Bindable {
}
}
/**
* Cast to an EntityBean allowing null.
*/
EntityBean castToEntityBean(Object objectValue) {
if (objectValue instanceof EntityBean || objectValue == null) {
return (EntityBean) objectValue;
} else {
throw new IllegalStateException("Bean " + objectValue.getClass() + " is not enhanced?");
}
}
}
@@ -0,0 +1,20 @@
package io.ebeaninternal.server.persist.dmlbind;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import java.sql.SQLException;
class BindableAssocTenant extends BindableAssocOne {
BindableAssocTenant(BeanPropertyAssocOne<?> assocOne) {
super(assocOne);
}
@Override
public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
EntityBean assocBean = (EntityBean) assocOne.getValue(bean);
importedId.bind(request, assocBean);
}
}
@@ -0,0 +1,37 @@
package io.ebeaninternal.server.persist.dmlbind;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.deploy.BeanProperty;
import java.sql.SQLException;
import java.util.List;
/**
* Bindable for the synthetic order column.
*/
public class BindableOrderColumn extends BindableProperty {
public BindableOrderColumn(BeanProperty prop) {
super(prop);
}
@Override
public void addToUpdate(PersistRequestBean<?> request, List<Bindable> list) {
int sortOrder = request.getEntityBeanIntercept().getSortOrder();
if (sortOrder > 0) {
list.add(this);
}
}
/**
* Normal binding of a property value from the bean.
*/
@Override
public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
int sortOrder = bean._ebean_getIntercept().getSortOrder();
request.bind(sortOrder, prop);
}
}
@@ -54,15 +54,4 @@ public class BindableProperty implements Bindable {
request.bind(value, prop);
}
/**
* For compound types bind one of the underlying scalar values for a compound type.
*/
public void dmlBindObject(BindableRequest request, Object bean) throws SQLException {
Object value = null;
if (bean != null) {
value = prop.getValueObject(bean);
}
request.bind(value, prop);
}
}
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.persist.dmlbind;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
/**
* Creates a Bindable to support version concurrency where clauses.
@@ -36,6 +37,12 @@ public class FactoryVersion {
public Bindable createTenantId(BeanDescriptor<?> desc) {
BeanProperty tenant = desc.getTenantProperty();
return (tenant == null) ? null : new BindableProperty(tenant);
if (tenant == null) {
return null;
} else if (tenant instanceof BeanPropertyAssocOne) {
return new BindableAssocTenant((BeanPropertyAssocOne<?>) tenant);
} else {
return new BindableProperty(tenant);
}
}
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.query;
import io.ebean.Query;
import io.ebean.RawSql;
import io.ebean.RawSqlBuilder;
import io.ebean.annotation.Platform;
@@ -366,6 +367,10 @@ class CQueryBuilder {
return new SqlTreeBuilder(this, request, predicates).build();
}
private String nativeQueryPaging(SpiQuery<?> query, String sql) {
return dbPlatform.getBasicSqlLimiter().limit(sql, query.getFirstRow(), query.getMaxRows());
}
/**
* Create the SqlTree by reading the ResultSetMetaData and mapping table/columns to bean property paths.
*/
@@ -375,6 +380,9 @@ class CQueryBuilder {
// parse named parameters returning the final sql to execute
String sql = predicates.parseBindParams(query.getNativeSql());
if (query.hasMaxRowsOrFirstRow()) {
sql = nativeQueryPaging(query, sql);
}
query.setGeneratedSql(sql);
Connection connection = request.getTransaction().getConnection();
@@ -664,4 +672,16 @@ class CQueryBuilder {
boolean isPlatformDistinctOn() {
return dbPlatform.isPlatform(Platform.POSTGRES);
}
/**
* Return the 'for update' FROM hint (sql server).
*/
public String fromForUpdate(SpiQuery<?> query) {
Query.ForUpdate mode = query.getForUpdateMode();
if (mode == null) {
return null;
} else {
return dbPlatform.fromForUpdate(mode);
}
}
}
@@ -333,7 +333,7 @@ public class CQueryEngine {
*/
private <T> void prepareForPaging(OrmQueryRequest<T> request) {
SpiQuery<T> query = request.getQuery();
if (!query.isDistinct() && (query.getMaxRows() > 1 || query.getFirstRow() > 0)) {
if (query.checkPagingOrderBy()) {
request.getBeanDescriptor().appendOrderById(query);
}
}
@@ -26,6 +26,8 @@ class DefaultDbSqlContext implements DbSqlContext {
private final ArrayStack<String> prefixStack = new ArrayStack<>();
private final String fromForUpdate;
private boolean useColumnAlias;
private int columnIndex;
@@ -55,7 +57,8 @@ class DefaultDbSqlContext implements DbSqlContext {
* Construct for SELECT clause (with column alias settings).
*/
DefaultDbSqlContext(SqlTreeAlias alias, CQueryBuilder builder,
boolean alwaysUseColumnAlias, CQueryHistorySupport historySupport, CQueryDraftSupport draftSupport) {
boolean alwaysUseColumnAlias, CQueryHistorySupport historySupport,
CQueryDraftSupport draftSupport, String fromForUpdate) {
this.alias = alias;
this.tableAliasPlaceHolder = builder.tableAliasPlaceHolder;
@@ -64,6 +67,14 @@ class DefaultDbSqlContext implements DbSqlContext {
this.draftSupport = draftSupport;
this.historySupport = historySupport;
this.historyQuery = (historySupport != null);
this.fromForUpdate = fromForUpdate;
}
@Override
public void appendFromForUpdate() {
if (fromForUpdate != null) {
append(" ").append(fromForUpdate);
}
}
@Override
@@ -119,9 +119,10 @@ public final class SqlTreeBuilder {
this.alias = new SqlTreeAlias(request.getBaseTableAlias());
this.distinctOnPlatform = builder.isPlatformDistinctOn();
String fromForUpdate = builder.fromForUpdate(query);
CQueryHistorySupport historySupport = builder.getHistorySupport(query);
CQueryDraftSupport draftSupport = builder.getDraftSupport(query);
this.ctx = new DefaultDbSqlContext(alias, builder, !subQuery, historySupport, draftSupport);
this.ctx = new DefaultDbSqlContext(alias, builder, !subQuery, historySupport, draftSupport, fromForUpdate);
}
/**
@@ -12,8 +12,8 @@ import io.ebeaninternal.api.SpiQuery.Mode;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.DbReadContext;
import io.ebeaninternal.server.deploy.DbSqlContext;
import io.ebeaninternal.server.deploy.InheritInfo;
@@ -597,7 +597,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
if (nodeBeanProp instanceof BeanPropertyAssocMany<?>) {
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>) nodeBeanProp;
if (manyProp.isManyToMany()) {
if (manyProp.hasJoinTable()) {
String alias = ctx.getTableAlias(prefix);
String[] split = SplitName.split(prefix);
@@ -105,7 +105,7 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
if (assocBeanProperty instanceof BeanPropertyAssocMany<?>) {
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>) assocBeanProperty;
if (manyProp.isManyToMany()) {
if (manyProp.hasJoinTable()) {
manyToMany = true;
@@ -96,7 +96,7 @@ class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
} else {
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>) nodeBeanProp;
if (!manyProp.isManyToMany()) {
if (!manyProp.hasJoinTable()) {
manyProp.addJoin(joinType, parentAlias, alias, ctx);
} else {
@@ -68,6 +68,7 @@ final class SqlTreeNodeRoot extends SqlTreeNodeBean {
ctx.append(desc.getBaseTable(temporalMode));
ctx.append(" ").append(baseTableAlias);
ctx.appendFromForUpdate();
if (includeJoin != null) {
String a1 = baseTableAlias;
@@ -996,7 +996,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
CQueryPlanKey createQueryPlanKey() {
if (isNativeSql()) {
queryPlanKey = new NativeSqlQueryPlanKey(nativeSql);
queryPlanKey = new NativeSqlQueryPlanKey(nativeSql + "-" + firstRow + "-" + maxRows);
} else {
queryPlanKey = new OrmQueryPlanKey(beanDescriptor.getDiscValue(), m2mIncludeJoin, type, detail, maxRows, firstRow,
disableLazyLoading, orderBy,
@@ -1372,6 +1372,15 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return this;
}
@Override
public boolean checkPagingOrderBy() {
return (maxRows > 1 || firstRow > 0) && !distinct && orderByIsEmpty();
}
private boolean orderByIsEmpty() {
return orderBy == null || orderBy.isEmpty();
}
@Override
public OrderBy<T> getOrderBy() {
return orderBy;
@@ -1,5 +1,7 @@
package io.ebeaninternal.server.transaction;
import io.ebean.Transaction;
import java.sql.Connection;
import java.sql.SQLException;
@@ -21,8 +23,9 @@ public class DocStoreOnlyTransaction extends JdbcTransaction {
}
@Override
public void setReadOnly(boolean readOnly) {
public Transaction setReadOnly(boolean readOnly) {
// do nothing
return this;
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.transaction;
import io.ebean.ProfileLocation;
import io.ebean.Transaction;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
@@ -85,8 +86,9 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public void setLabel(String label) {
public Transaction setLabel(String label) {
// do nothing
return this;
}
@Override
@@ -130,7 +132,8 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public void setSkipCache(boolean skipCache) {
public Transaction setSkipCache(boolean skipCache) {
return this;
}
@Override
@@ -149,7 +152,7 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public void register(TransactionCallback callback) {
public Transaction register(TransactionCallback callback) {
throw new IllegalStateException(notExpectedMessage);
}
@@ -159,7 +162,7 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public void setDocStoreBatchSize(int docStoreBatchSize) {
public Transaction setDocStoreBatchSize(int docStoreBatchSize) {
throw new IllegalStateException(notExpectedMessage);
}
@@ -169,7 +172,7 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public void setDocStoreMode(DocStoreMode docStoreMode) {
public Transaction setDocStoreMode(DocStoreMode docStoreMode) {
throw new IllegalStateException(notExpectedMessage);
}
@@ -244,19 +247,21 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public void setReadOnly(boolean readOnly) {
public Transaction setReadOnly(boolean readOnly) {
if (!isActive()) {
throw new IllegalStateException(illegalStateMessage);
}
try {
connection.setReadOnly(readOnly);
return this;
} catch (SQLException e) {
throw new PersistenceException(e);
}
}
@Override
public void setUpdateAllLoadedProperties(boolean updateAllLoadedProperties) {
public Transaction setUpdateAllLoadedProperties(boolean updateAllLoadedProperties) {
return this;
}
@Override
@@ -265,13 +270,13 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public void setBatchMode(boolean batchMode) {
public Transaction setBatchMode(boolean batchMode) {
return this;
}
@Override
public void setBatch(PersistBatch batchMode) {
public Transaction setBatch(PersistBatch batchMode) {
return this;
}
@Override
@@ -280,7 +285,8 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public void setBatchOnCascade(PersistBatch batchOnCascadeMode) {
public Transaction setBatchOnCascade(PersistBatch batchOnCascadeMode) {
return this;
}
@Override
@@ -294,11 +300,13 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
public Transaction setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
return this;
}
@Override
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
public Transaction setBatchFlushOnMixed(boolean batchFlushOnMixed) {
return this;
}
/**
@@ -313,7 +321,8 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public void setBatchSize(int batchSize) {
public Transaction setBatchSize(int batchSize) {
return this;
}
@Override
@@ -322,7 +331,8 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
public Transaction setBatchFlushOnQuery(boolean batchFlushOnQuery) {
return this;
}
/**
@@ -536,7 +546,7 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
* Mark the transaction as rollback only.
*/
@Override
public void setRollbackOnly() {
public Transaction setRollbackOnly() {
// expect AutoCommit so we can't really support rollbackOnly
throw new IllegalStateException(notExpectedMessage);
}
@@ -586,11 +596,12 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public void setPersistCascade(boolean persistCascade) {
public Transaction setPersistCascade(boolean persistCascade) {
return this;
}
@Override
public void addModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
public Transaction addModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
throw new IllegalStateException(notExpectedMessage);
}
@@ -600,11 +611,12 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
@Override
public void putUserObject(String name, Object value) {
public Transaction putUserObject(String name, Object value) {
if (userObjects == null) {
userObjects = new HashMap<>();
}
userObjects.put(name, value);
return this;
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.transaction;
import io.ebean.ProfileLocation;
import io.ebean.Transaction;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
@@ -217,8 +218,9 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void setLabel(String label) {
public Transaction setLabel(String label) {
this.label = label;
return this;
}
@Override
@@ -288,8 +290,9 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void setSkipCache(boolean skipCache) {
public Transaction setSkipCache(boolean skipCache) {
this.skipCache = skipCache;
return this;
}
@Override
@@ -318,11 +321,12 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void register(TransactionCallback callback) {
public Transaction register(TransactionCallback callback) {
if (callbackList == null) {
callbackList = new ArrayList<>(4);
}
callbackList.add(callback);
return this;
}
protected void firePreRollback() {
@@ -385,8 +389,9 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void setDocStoreBatchSize(int docStoreBatchSize) {
public Transaction setDocStoreBatchSize(int docStoreBatchSize) {
this.docStoreBatchSize = docStoreBatchSize;
return this;
}
@Override
@@ -395,8 +400,9 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void setDocStoreMode(DocStoreMode docStoreMode) {
public Transaction setDocStoreMode(DocStoreMode docStoreMode) {
this.docStoreMode = docStoreMode;
return this;
}
@Override
@@ -529,21 +535,23 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void setReadOnly(boolean readOnly) {
public Transaction setReadOnly(boolean readOnly) {
if (!isActive()) {
throw new IllegalStateException(illegalStateMessage);
}
try {
localReadOnly = readOnly;
connection.setReadOnly(readOnly);
return this;
} catch (SQLException e) {
throw new PersistenceException(e);
}
}
@Override
public void setUpdateAllLoadedProperties(boolean updateAllLoadedProperties) {
public Transaction setUpdateAllLoadedProperties(boolean updateAllLoadedProperties) {
this.updateAllLoadedProperties = updateAllLoadedProperties;
return this;
}
@Override
@@ -552,19 +560,21 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void setBatchMode(boolean batchMode) {
public Transaction setBatchMode(boolean batchMode) {
if (!isActive()) {
throw new IllegalStateException(illegalStateMessage);
}
this.batchMode = (batchMode) ? PersistBatch.ALL : PersistBatch.NONE;
return this;
}
@Override
public void setBatch(PersistBatch batchMode) {
public Transaction setBatch(PersistBatch batchMode) {
if (!isActive()) {
throw new IllegalStateException(illegalStateMessage);
}
this.batchMode = batchMode;
return this;
}
@Override
@@ -573,11 +583,12 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void setBatchOnCascade(PersistBatch batchOnCascadeMode) {
public Transaction setBatchOnCascade(PersistBatch batchOnCascadeMode) {
if (!isActive()) {
throw new IllegalStateException(illegalStateMessage);
}
this.batchOnCascadeMode = batchOnCascadeMode;
return this;
}
@Override
@@ -591,19 +602,21 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
public Transaction setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
this.batchGetGeneratedKeys = getGeneratedKeys;
if (batchControl != null) {
batchControl.setGetGeneratedKeys(getGeneratedKeys);
}
return this;
}
@Override
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
public Transaction setBatchFlushOnMixed(boolean batchFlushOnMixed) {
this.batchFlushOnMixed = batchFlushOnMixed;
if (batchControl != null) {
batchControl.setBatchFlushOnMixed(batchFlushOnMixed);
}
return this;
}
/**
@@ -618,11 +631,12 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void setBatchSize(int batchSize) {
public Transaction setBatchSize(int batchSize) {
this.batchSize = batchSize;
if (batchControl != null) {
batchControl.setBatchSize(batchSize);
}
return this;
}
@Override
@@ -631,8 +645,9 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
public Transaction setBatchFlushOnQuery(boolean batchFlushOnQuery) {
this.batchFlushOnQuery = batchFlushOnQuery;
return this;
}
/**
@@ -1113,8 +1128,9 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
* Mark the transaction as rollback only.
*/
@Override
public void setRollbackOnly() {
public Transaction setRollbackOnly() {
this.rollbackOnly = true;
return this;
}
/**
@@ -1182,13 +1198,15 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void setPersistCascade(boolean persistCascade) {
public Transaction setPersistCascade(boolean persistCascade) {
this.persistCascade = persistCascade;
return this;
}
@Override
public void addModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
public Transaction addModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
getEvent().add(tableName, inserts, updates, deletes);
return this;
}
@Override
@@ -1201,11 +1219,12 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public void putUserObject(String name, Object value) {
public Transaction putUserObject(String name, Object value) {
if (userObjects == null) {
userObjects = new HashMap<>();
}
userObjects.put(name, value);
return this;
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.transaction;
import io.ebean.ProfileLocation;
import io.ebean.Transaction;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
@@ -28,8 +29,8 @@ class NoTransaction implements SpiTransaction {
static final NoTransaction INSTANCE = new NoTransaction();
@Override
public void setLabel(String label) {
// do nothing
public Transaction setLabel(String label) {
return this;
}
@Override
@@ -150,8 +151,8 @@ class NoTransaction implements SpiTransaction {
}
@Override
public void register(TransactionCallback callback) {
public Transaction register(TransactionCallback callback) {
return this;
}
@Override
@@ -160,13 +161,13 @@ class NoTransaction implements SpiTransaction {
}
@Override
public void setReadOnly(boolean readOnly) {
public Transaction setReadOnly(boolean readOnly) {
return this;
}
@Override
public void setRollbackOnly() {
public Transaction setRollbackOnly() {
return this;
}
@Override
@@ -176,28 +177,28 @@ class NoTransaction implements SpiTransaction {
@Override
public void setDocStoreMode(DocStoreMode mode) {
public Transaction setDocStoreMode(DocStoreMode mode) {
return this;
}
@Override
public void setDocStoreBatchSize(int batchSize) {
public Transaction setDocStoreBatchSize(int batchSize) {
return this;
}
@Override
public void setPersistCascade(boolean persistCascade) {
public Transaction setPersistCascade(boolean persistCascade) {
return this;
}
@Override
public void setUpdateAllLoadedProperties(boolean updateAllLoadedProperties) {
public Transaction setUpdateAllLoadedProperties(boolean updateAllLoadedProperties) {
return this;
}
@Override
public void setSkipCache(boolean skipCache) {
public Transaction setSkipCache(boolean skipCache) {
return this;
}
@Override
@@ -206,13 +207,13 @@ class NoTransaction implements SpiTransaction {
}
@Override
public void setBatchMode(boolean useBatch) {
public Transaction setBatchMode(boolean useBatch) {
return this;
}
@Override
public void setBatch(PersistBatch persistBatchMode) {
public Transaction setBatch(PersistBatch persistBatchMode) {
return this;
}
@Override
@@ -221,8 +222,8 @@ class NoTransaction implements SpiTransaction {
}
@Override
public void setBatchOnCascade(PersistBatch batchOnCascadeMode) {
public Transaction setBatchOnCascade(PersistBatch batchOnCascadeMode) {
return this;
}
@Override
@@ -231,8 +232,8 @@ class NoTransaction implements SpiTransaction {
}
@Override
public void setBatchSize(int batchSize) {
public Transaction setBatchSize(int batchSize) {
return this;
}
@Override
@@ -241,18 +242,18 @@ class NoTransaction implements SpiTransaction {
}
@Override
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
public Transaction setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
return this;
}
@Override
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
public Transaction setBatchFlushOnMixed(boolean batchFlushOnMixed) {
return this;
}
@Override
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
public Transaction setBatchFlushOnQuery(boolean batchFlushOnQuery) {
return this;
}
@Override
@@ -276,13 +277,13 @@ class NoTransaction implements SpiTransaction {
}
@Override
public void addModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
public Transaction addModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
return this;
}
@Override
public void putUserObject(String name, Object value) {
public Transaction putUserObject(String name, Object value) {
return this;
}
@Override
@@ -86,7 +86,7 @@ public final class PostCommitProcessing {
*/
void notifyLocalCache() {
processTableEvents(event.getEventTables());
if (manager.localL2Caching) {
if (manager.notifyL2CacheInForeground) {
// process l2 cache changes in foreground
processCacheChanges(event.buildCacheChanges(manager.viewInvalidation));
} else {
@@ -120,7 +120,7 @@ public class TransactionManager implements SpiTransactionManager {
*/
private final boolean changeLogAsync;
protected final boolean localL2Caching;
protected final boolean notifyL2CacheInForeground;
protected final boolean viewInvalidation;
@@ -144,7 +144,7 @@ public class TransactionManager implements SpiTransactionManager {
this.databasePlatform = options.config.getDatabasePlatform();
this.skipCacheAfterWrite = options.config.isSkipCacheAfterWrite();
this.localL2Caching = options.localL2Caching;
this.notifyL2CacheInForeground = options.notifyL2CacheInForeground;
this.persistBatch = options.config.getPersistBatch();
this.persistBatchOnCascade = options.config.appliedPersistBatchOnCascade();
this.rollbackOnChecked = options.config.isTransactionRollbackOnChecked();
@@ -12,7 +12,7 @@ import io.ebeanservice.docstore.api.DocStoreUpdateProcessor;
*/
public class TransactionManagerOptions {
final boolean localL2Caching;
final boolean notifyL2CacheInForeground;
final ServerConfig config;
final ClusterManager clusterManager;
final BackgroundExecutor backgroundExecutor;
@@ -23,11 +23,11 @@ public class TransactionManagerOptions {
final SpiProfileHandler profileHandler;
final TransactionScopeManager scopeManager;
public TransactionManagerOptions(boolean localL2Caching, ServerConfig config, TransactionScopeManager scopeManager, ClusterManager clusterManager,
public TransactionManagerOptions(boolean notifyL2CacheInForeground, ServerConfig config, TransactionScopeManager scopeManager, ClusterManager clusterManager,
BackgroundExecutor backgroundExecutor, DocStoreUpdateProcessor docStoreUpdateProcessor,
BeanDescriptorManager descMgr, DataSourceSupplier dataSourceSupplier, SpiProfileHandler profileHandler) {
this.localL2Caching = localL2Caching;
this.notifyL2CacheInForeground = notifyL2CacheInForeground;
this.config = config;
this.scopeManager = scopeManager;
this.clusterManager = clusterManager;
@@ -109,7 +109,7 @@ public final class DefaultTypeManager implements TypeManager {
private final ScalarType<?> shortType = new ScalarTypeShort();
private final ScalarType<?> integerType = new ScalarTypeInteger();
private final ScalarType<?> integerType = ScalarTypeInteger.INSTANCE;
private final ScalarType<?> longType = new ScalarTypeLong();
@@ -130,7 +130,7 @@ public final class DefaultTypeManager implements TypeManager {
private final ScalarType<?> currencyType = new ScalarTypeCurrency();
private final ScalarType<?> timeZoneType = new ScalarTypeTimeZone();
private final ScalarType<?> stringType = new ScalarTypeString();
private final ScalarType<?> stringType = ScalarTypeString.INSTANCE;
private final ScalarType<?> classType = new ScalarTypeClass();
@@ -1,10 +1,10 @@
package io.ebeaninternal.server.type;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import io.ebean.text.TextException;
import io.ebeaninternal.server.core.BasicTypeConverter;
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import java.io.DataInput;
import java.io.DataOutput;
@@ -17,7 +17,9 @@ import java.sql.Types;
*/
public class ScalarTypeInteger extends ScalarTypeBase<Integer> {
public ScalarTypeInteger() {
public static ScalarTypeInteger INSTANCE = new ScalarTypeInteger();
private ScalarTypeInteger() {
super(Integer.class, true, Types.INTEGER);
}
@@ -1,9 +1,9 @@
package io.ebeaninternal.server.type;
import io.ebeaninternal.server.core.BasicTypeConverter;
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import io.ebeaninternal.server.core.BasicTypeConverter;
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
import java.io.DataInput;
import java.io.DataOutput;
@@ -16,7 +16,9 @@ import java.sql.Types;
*/
public class ScalarTypeString extends ScalarTypeBase<String> {
public ScalarTypeString() {
public static final ScalarTypeString INSTANCE = new ScalarTypeString();
private ScalarTypeString() {
super(String.class, true, Types.VARCHAR);
}

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