mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
42
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47d55a2f29 | ||
|
|
b037be5d3e | ||
|
|
60a3c24cba | ||
|
|
4a243a76a7 | ||
|
|
9aee7c892d | ||
|
|
18d944f228 | ||
|
|
29bb9d3b48 | ||
|
|
f8ea5619d0 | ||
|
|
e3fac7c622 | ||
|
|
8ee68ac5e1 | ||
|
|
ec7b376612 | ||
|
|
f2d438f21a | ||
|
|
20bd2241e4 | ||
|
|
dd907cc4bc | ||
|
|
4d122ea05b | ||
|
|
83384c6f0e | ||
|
|
eaa48468cd | ||
|
|
0b1c3a4233 | ||
|
|
3e8b06e38e | ||
|
|
81d48d67a7 | ||
|
|
8d0df6541e | ||
|
|
2be3099d55 | ||
|
|
183d079b58 | ||
|
|
40634412f5 | ||
|
|
b8aa822a30 | ||
|
|
a1d0fbb194 | ||
|
|
70ccc6588d | ||
|
|
3d62e84d59 | ||
|
|
ac36172cc6 | ||
|
|
1829c74694 | ||
|
|
cc7e22014d | ||
|
|
a5c6f483b0 | ||
|
|
5237e8ba66 | ||
|
|
9926cad7f1 | ||
|
|
6c1b295d41 | ||
|
|
49b568e7d4 | ||
|
|
628af31ddd | ||
|
|
31cac60393 | ||
|
|
6a067c0cf7 | ||
|
|
842292ef6b | ||
|
|
6db9a60f4f | ||
|
|
d9125db957 |
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
+24
-77
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+49
-18
@@ -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(";");
|
||||
|
||||
+100
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+47
@@ -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;
|
||||
* <attribute name="references" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="foreignKeyName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="foreignKeyIndex" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="foreignKeyOnDelete" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="foreignKeyOnUpdate" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="dropForeignKey" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="dropForeignKeyIndex" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* </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;
|
||||
* <attribute name="references" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="foreignKeyName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="foreignKeyIndex" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="foreignKeyOnDelete" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="foreignKeyOnUpdate" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="comment" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* </restriction>
|
||||
* </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;
|
||||
* <attribute name="refColumnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="refTableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="indexName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="onDelete" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="onUpdate" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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() {
|
||||
|
||||
+38
-12
@@ -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
|
||||
|
||||
+32
-20
@@ -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
Reference in New Issue
Block a user