mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
29
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
624d08f46c | ||
|
|
14749daf9b | ||
|
|
2817875706 | ||
|
|
3f8d705439 | ||
|
|
e9faef2692 | ||
|
|
d459807af7 | ||
|
|
10ad9ef096 | ||
|
|
11e3649b85 | ||
|
|
ddc00fedb6 | ||
|
|
0aa9208dcc | ||
|
|
e3b1996dc9 | ||
|
|
24ca744e06 | ||
|
|
7f9ce5bdd4 | ||
|
|
23e5de0819 | ||
|
|
c23508819f | ||
|
|
af030e3da4 | ||
|
|
d8e67d09a9 | ||
|
|
66c08e663e | ||
|
|
9a096e272e | ||
|
|
21e87e12cb | ||
|
|
7c6d7dd4d0 | ||
|
|
c5329e9db0 | ||
|
|
bccb9f8ddb | ||
|
|
c57af29fca | ||
|
|
37b6a321ab | ||
|
|
896f7d9ea8 | ||
|
|
141a0d9358 | ||
|
|
5a7f24da5d | ||
|
|
8ea86d6c6d |
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.15.11</version>
|
||||
<version>11.17.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-11.15.11</tag>
|
||||
<tag>ebean-11.17.1</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
|
||||
@@ -167,6 +167,30 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
return db().deletePermanent(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this entity using the default merge options.
|
||||
* <p>
|
||||
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
|
||||
* insert or an update based on that.
|
||||
*
|
||||
* @see EbeanServer#merge(Object)
|
||||
*/
|
||||
public void merge(T bean) {
|
||||
db().merge(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this entity using the specified merge options.
|
||||
* <p>
|
||||
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
|
||||
* insert or an update based on that.
|
||||
*
|
||||
* @see EbeanServer#merge(Object, MergeOptions)
|
||||
*/
|
||||
public void merge(T bean, MergeOptions options) {
|
||||
db().merge(bean, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes this entity from the database.
|
||||
*
|
||||
|
||||
@@ -638,6 +638,15 @@ public final class Ebean {
|
||||
serverMgr.getDefaultServer().updateAll(beans);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the bean using the default merge options.
|
||||
*
|
||||
* @param bean The bean to merge
|
||||
*/
|
||||
public static void merge(Object bean) {
|
||||
serverMgr.getDefaultServer().merge(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the bean using the given merge options.
|
||||
*
|
||||
|
||||
@@ -1628,6 +1628,13 @@ public interface EbeanServer {
|
||||
*/
|
||||
void updateAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Merge the bean using the default merge options (no paths specified, default delete).
|
||||
*
|
||||
* @param bean The bean to merge
|
||||
*/
|
||||
void merge(Object bean);
|
||||
|
||||
/**
|
||||
* Merge the bean using the given merge options.
|
||||
*
|
||||
|
||||
@@ -8,11 +8,20 @@ import java.util.Set;
|
||||
*/
|
||||
public class MergeOptionsBuilder {
|
||||
|
||||
private static final MOptions DEFAULT_OPTIONS = new MOptions();
|
||||
|
||||
private Set<String> paths = new LinkedHashSet<>();
|
||||
|
||||
private boolean clientGeneratedIds;
|
||||
|
||||
private boolean deletePermanent = true;
|
||||
private boolean deletePermanent;
|
||||
|
||||
/**
|
||||
* Return the default options.
|
||||
*/
|
||||
public static MergeOptions defaultOptions() {
|
||||
return DEFAULT_OPTIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a path that will included in the merge.
|
||||
@@ -47,7 +56,7 @@ public class MergeOptionsBuilder {
|
||||
/**
|
||||
* Build and return the MergeOptions instance.
|
||||
*/
|
||||
public MOptions build() {
|
||||
public MergeOptions build() {
|
||||
return new MOptions(paths, clientGeneratedIds, deletePermanent);
|
||||
}
|
||||
|
||||
@@ -57,6 +66,12 @@ public class MergeOptionsBuilder {
|
||||
private final boolean deletePermanent;
|
||||
private final Set<String> paths;
|
||||
|
||||
private MOptions(){
|
||||
this.clientGeneratedIds = false;
|
||||
this.paths = new LinkedHashSet<>();
|
||||
this.deletePermanent = false;
|
||||
}
|
||||
|
||||
private MOptions(Set<String> paths, boolean clientGeneratedIds, boolean deletePermanent) {
|
||||
this.paths = paths;
|
||||
this.clientGeneratedIds = clientGeneratedIds;
|
||||
|
||||
@@ -19,15 +19,17 @@ package io.ebean;
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // example that uses 'named' parameters
|
||||
* String s = "UPDATE f_topic set post_count = :count where id = :id";
|
||||
* SqlUpdate update = Ebean.createSqlUpdate(s);
|
||||
* update.setParameter("id", 1);
|
||||
* update.setParameter("count", 50);
|
||||
* // example that uses 'named' parameters
|
||||
*
|
||||
* int modifiedCount = Ebean.execute(update);
|
||||
* String s = "UPDATE f_topic set post_count = :count where id = :id";
|
||||
*
|
||||
* String msg = "There were " + modifiedCount + " rows updated";
|
||||
* SqlUpdate update = Ebean.createSqlUpdate(s);
|
||||
* update.setParameter("id", 1);
|
||||
* update.setParameter("count", 50);
|
||||
*
|
||||
* int modifiedCount = update.execute();
|
||||
*
|
||||
* String msg = "There were " + modifiedCount + " rows updated";
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
@@ -57,6 +59,33 @@ package io.ebean;
|
||||
*
|
||||
* txn.commit();
|
||||
* }
|
||||
* }</pre>
|
||||
* <p>
|
||||
* An alternative to the batch mode on the transaction is to use addBatch() and executeBatch() like:
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* try (Transaction txn = Ebean.beginTransaction()) {
|
||||
*
|
||||
* insert.setNextParameter(10000);
|
||||
* insert.setNextParameter("hello");
|
||||
* insert.setNextParameter("rob");
|
||||
* insert.addBatch();
|
||||
*
|
||||
* insert.setNextParameter(10001);
|
||||
* insert.setNextParameter("goodbye");
|
||||
* insert.setNextParameter("rob");
|
||||
* insert.addBatch();
|
||||
*
|
||||
* insert.setNextParameter(10002);
|
||||
* insert.setNextParameter("chow");
|
||||
* insert.setNextParameter("bob");
|
||||
* insert.addBatch();
|
||||
*
|
||||
* int[] rows = insert.executeBatch();
|
||||
*
|
||||
* txn.commit();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
@@ -83,6 +112,18 @@ public interface SqlUpdate {
|
||||
*/
|
||||
int execute();
|
||||
|
||||
/**
|
||||
* Execute when addBatch() has been used to batch multiple bind executions.
|
||||
*
|
||||
* @return The row counts for each of the batched statements.
|
||||
*/
|
||||
int[] executeBatch();
|
||||
|
||||
/**
|
||||
* Add the statement to batch processing to then later execute via executeBatch().
|
||||
*/
|
||||
void addBatch();
|
||||
|
||||
/**
|
||||
* Return the generated key value.
|
||||
*/
|
||||
|
||||
@@ -493,6 +493,11 @@ public class ServerConfig {
|
||||
*/
|
||||
private List<String> mappingLocations = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* When true we do not need explicit GeneratedValue mapping.
|
||||
*/
|
||||
private boolean idGeneratorAutomatic = true;
|
||||
|
||||
/**
|
||||
* Construct a Server Configuration for programmatically creating an EbeanServer.
|
||||
*/
|
||||
@@ -1967,7 +1972,7 @@ public class ServerConfig {
|
||||
* This is the same as serverConfig.getMigrationConfig().setRunMigration(). We have added this method here
|
||||
* as it is often the only thing we need to configure for migrations.
|
||||
*/
|
||||
public void setRunMigration(boolean runMigration){
|
||||
public void setRunMigration(boolean runMigration) {
|
||||
migrationConfig.setRunMigration(runMigration);
|
||||
}
|
||||
|
||||
@@ -2772,6 +2777,7 @@ public class ServerConfig {
|
||||
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
|
||||
useJavaxValidationNotNull = p.getBoolean("useJavaxValidationNotNull", useJavaxValidationNotNull);
|
||||
autoReadOnlyDataSource = p.getBoolean("autoReadOnlyDataSource", autoReadOnlyDataSource);
|
||||
idGeneratorAutomatic = p.getBoolean("idGeneratorAutomatic", idGeneratorAutomatic);
|
||||
|
||||
backgroundExecutorSchedulePoolSize = p.getInt("backgroundExecutorSchedulePoolSize", backgroundExecutorSchedulePoolSize);
|
||||
backgroundExecutorShutdownSecs = p.getInt("backgroundExecutorShutdownSecs", backgroundExecutorShutdownSecs);
|
||||
@@ -3109,6 +3115,23 @@ public class ServerConfig {
|
||||
this.mappingLocations = mappingLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* When false we need explicit <code>@GeneratedValue</code> mapping to assign
|
||||
* Identity or Sequence generated values. When true Id properties are automatically
|
||||
* assigned Identity or Sequence without the GeneratedValue mapping.
|
||||
*/
|
||||
public boolean isIdGeneratorAutomatic() {
|
||||
return idGeneratorAutomatic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to false such that Id properties require explicit <code>@GeneratedValue</code>
|
||||
* mapping before they are assigned Identity or Sequence generation based on platform.
|
||||
*/
|
||||
public void setIdGeneratorAutomatic(boolean idGeneratorAutomatic) {
|
||||
this.idGeneratorAutomatic = idGeneratorAutomatic;
|
||||
}
|
||||
|
||||
public enum UuidVersion {
|
||||
VERSION4,
|
||||
VERSION1,
|
||||
|
||||
@@ -31,6 +31,11 @@ import java.util.function.Predicate;
|
||||
*/
|
||||
public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionLoader {
|
||||
|
||||
/**
|
||||
* Return the log manager.
|
||||
*/
|
||||
SpiLogManager log();
|
||||
|
||||
/**
|
||||
* Return the server extended Json context.
|
||||
*/
|
||||
@@ -268,4 +273,15 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
* Return true if a row for the bean type and id exists.
|
||||
*/
|
||||
boolean exists(Class<?> beanType, Object beanId, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Add to JDBC batch for later execution.
|
||||
*/
|
||||
void addBatch(SpiSqlUpdate defaultSqlUpdate, SpiTransaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the batched statement.
|
||||
*/
|
||||
int[] executeBatch(SpiSqlUpdate defaultSqlUpdate, SpiTransaction transaction);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Log manager for SQL, TXN and Summary logging.
|
||||
* <p>
|
||||
* In general at runtime this uses SLF4J Logger but this abstraction allows us to capture
|
||||
* the logged SQL during testing such that we can assert against the executed sql if desired.
|
||||
* </p>
|
||||
*/
|
||||
public interface SpiLogManager {
|
||||
|
||||
/**
|
||||
* Return the SQL logger.
|
||||
*/
|
||||
SpiLogger sql();
|
||||
|
||||
/**
|
||||
* Return the TXN logger.
|
||||
*/
|
||||
SpiLogger txn();
|
||||
|
||||
/**
|
||||
* Return the Summary logger.
|
||||
*/
|
||||
SpiLogger sum();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Logger for SQL, TXN and Summary logging.
|
||||
* <p>
|
||||
* In general at runtime this uses SLF4J Logger but this abstraction allows us to capture
|
||||
* the logged SQL during testing such that we can assert against the executed sql if desired.
|
||||
* </p>
|
||||
*/
|
||||
public interface SpiLogger {
|
||||
|
||||
/**
|
||||
* Is debug logging enabled.
|
||||
*/
|
||||
boolean isDebug();
|
||||
|
||||
/**
|
||||
* Is trace logging enabled.
|
||||
*/
|
||||
boolean isTrace();
|
||||
|
||||
/**
|
||||
* Log a debug level message.
|
||||
*/
|
||||
void debug(String msg);
|
||||
|
||||
/**
|
||||
* Log a trace level message.
|
||||
*/
|
||||
void trace(String msg);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* API for Plugins to define the logger for io.ebean.SQL, io.ebean.TXN and io.ebean.SUM.
|
||||
*/
|
||||
public interface SpiLoggerFactory {
|
||||
|
||||
/**
|
||||
* Create the logger given the name.
|
||||
*/
|
||||
SpiLogger create(String name);
|
||||
}
|
||||
@@ -644,9 +644,9 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
boolean isBeanCachePut();
|
||||
|
||||
/**
|
||||
* Return true if the bean cache is being explicitly loaded via RECACHE mode.
|
||||
* Return true if we must hit the DB (Cache reload or select for update).
|
||||
*/
|
||||
boolean isBeanCacheReload();
|
||||
boolean isForceHitDatabase();
|
||||
|
||||
/**
|
||||
* Return the cache mode for using the bean cache (Get and Put).
|
||||
|
||||
+7
-4
@@ -5,6 +5,7 @@ import io.ebeaninternal.dbmigration.model.MTable;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.PropertyForeignKey;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.deploy.TableJoinColumn;
|
||||
|
||||
@@ -48,10 +49,12 @@ class ModelBuildIntersectionTable {
|
||||
|
||||
private void buildFkConstraints() {
|
||||
|
||||
ctx.fkeyBuilder(intersectionTable)
|
||||
.addForeignKey(manyProp.getBeanDescriptor(), intersectionTableJoin, true)
|
||||
.addForeignKey(manyProp.getTargetDescriptor(), tableJoin, false);
|
||||
|
||||
PropertyForeignKey foreignKey = manyProp.getForeignKey();
|
||||
if (foreignKey == null || !foreignKey.isNoConstraint()) {
|
||||
ctx.fkeyBuilder(intersectionTable)
|
||||
.addForeignKey(manyProp.getBeanDescriptor(), intersectionTableJoin, true)
|
||||
.addForeignKey(manyProp.getTargetDescriptor(), tableJoin, false);
|
||||
}
|
||||
intersectionTable.checkDuplicateForeignKeys();
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ public class BaseQueryTuner {
|
||||
default:
|
||||
// not using autoTune when explicitly loading the l2 bean cache
|
||||
// or when using Versions query
|
||||
return !query.isBeanCacheReload() && SpiQuery.TemporalMode.VERSIONS != query.getTemporalMode();
|
||||
return !query.isForceHitDatabase() && SpiQuery.TemporalMode.VERSIONS != query.getTemporalMode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -170,11 +170,7 @@ public abstract class AbstractSqlQueryRequest {
|
||||
}
|
||||
|
||||
if (isLogSql()) {
|
||||
String logSql = TrimLogSql.trim(sql);
|
||||
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
|
||||
logSql = Str.add(logSql, "; --bind(", bindLog, ")");
|
||||
}
|
||||
trans.logSql(logSql);
|
||||
trans.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ")"));
|
||||
}
|
||||
|
||||
setResultSet(pstmt.executeQuery(), null);
|
||||
|
||||
@@ -13,6 +13,7 @@ import io.ebean.FutureIds;
|
||||
import io.ebean.FutureList;
|
||||
import io.ebean.FutureRowCount;
|
||||
import io.ebean.MergeOptions;
|
||||
import io.ebean.MergeOptionsBuilder;
|
||||
import io.ebean.PagedList;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.ProfileLocation;
|
||||
@@ -63,8 +64,10 @@ import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.api.SpiDtoQuery;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiJsonContext;
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiQuery.Type;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiTransactionManager;
|
||||
import io.ebeaninternal.api.TransactionEventTable;
|
||||
@@ -189,6 +192,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private final CurrentTenantProvider currentTenantProvider;
|
||||
|
||||
private final SpiLogManager logManager;
|
||||
|
||||
/**
|
||||
* The default PersistenceContextScope used if it is not explicitly set on a query.
|
||||
*/
|
||||
@@ -229,6 +234,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
public DefaultServer(InternalConfiguration config, ServerCacheManager cache) {
|
||||
|
||||
this.logManager = config.getLogManager();
|
||||
this.dtoBeanManager = config.getDtoBeanManager();
|
||||
this.serverConfig = config.getServerConfig();
|
||||
this.objectGraphStats = new ConcurrentHashMap<>();
|
||||
@@ -315,6 +321,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiLogManager log() {
|
||||
return logManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollectQueryOrigins() {
|
||||
return collectQueryOrigins;
|
||||
@@ -919,6 +930,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return new DefaultUpdateQuery<>(createQuery(beanType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(Object bean) {
|
||||
merge(bean, MergeOptionsBuilder.defaultOptions(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(Object bean, MergeOptions options) {
|
||||
merge(bean, options, null);
|
||||
@@ -1159,7 +1175,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
spiQuery.setType(Type.BEAN);
|
||||
if (SpiQuery.Mode.NORMAL == spiQuery.getMode() && !spiQuery.isBeanCacheReload()) {
|
||||
if (SpiQuery.Mode.NORMAL == spiQuery.getMode() && !spiQuery.isForceHitDatabase()) {
|
||||
// See if we can skip doing the fetch completely by getting the bean from the
|
||||
// persistence context or the bean cache
|
||||
T bean = findIdCheckPersistenceContextAndCache(t, spiQuery, spiQuery.getId());
|
||||
@@ -1961,6 +1977,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return persister.executeSqlUpdate(updSql, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction) {
|
||||
persister.addBatch(sqlUpdate, transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] executeBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction) {
|
||||
return persister.executeBatch(sqlUpdate, transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the updateSql.
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.Update;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@@ -26,7 +27,7 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
|
||||
private static final long serialVersionUID = -6493829438421253102L;
|
||||
|
||||
private transient final EbeanServer server;
|
||||
private transient final SpiEbeanServer server;
|
||||
|
||||
/**
|
||||
* The parameters used to bind to the sql.
|
||||
@@ -69,6 +70,16 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
|
||||
private Object generatedKey;
|
||||
|
||||
/**
|
||||
* Set when batching explicitly used.
|
||||
*/
|
||||
private boolean batched;
|
||||
|
||||
/**
|
||||
* Transaction used for addBatch() executeBatch() processing.
|
||||
*/
|
||||
private transient SpiTransaction transaction;
|
||||
|
||||
/**
|
||||
* Create with server sql and bindParams object.
|
||||
* <p>
|
||||
@@ -76,7 +87,7 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
* same time.
|
||||
* </p>
|
||||
*/
|
||||
public DefaultSqlUpdate(EbeanServer server, String sql, BindParams bindParams) {
|
||||
public DefaultSqlUpdate(SpiEbeanServer server, String sql, BindParams bindParams) {
|
||||
this.server = server;
|
||||
this.sql = sql;
|
||||
this.bindParams = bindParams;
|
||||
@@ -86,7 +97,7 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
* Create with a specific server. This means you can use the
|
||||
* SqlUpdate.execute() method.
|
||||
*/
|
||||
public DefaultSqlUpdate(EbeanServer server, String sql) {
|
||||
public DefaultSqlUpdate(SpiEbeanServer server, String sql) {
|
||||
this(server, sql, new BindParams());
|
||||
}
|
||||
|
||||
@@ -111,6 +122,10 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
@Override
|
||||
public int execute() {
|
||||
if (server != null) {
|
||||
if (batched) {
|
||||
server.executeBatch(this, transaction);
|
||||
return -1;
|
||||
}
|
||||
return server.execute(this);
|
||||
} else {
|
||||
// Hopefully this doesn't catch anyone out...
|
||||
@@ -118,6 +133,34 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] executeBatch() {
|
||||
if (server == null) {
|
||||
throw new IllegalStateException("No EbeanServer set?");
|
||||
}
|
||||
if (!batched) {
|
||||
throw new IllegalStateException("No prior addBatch() called?");
|
||||
}
|
||||
return server.executeBatch(this, transaction);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addBatch() {
|
||||
if (server == null) {
|
||||
throw new IllegalStateException("No EbeanServer set?");
|
||||
}
|
||||
if (transaction == null) {
|
||||
transaction = server.currentServerTransaction();
|
||||
if (transaction == null) {
|
||||
throw new IllegalStateException("No current transaction? Must have a transaction to use addBatch()");
|
||||
}
|
||||
}
|
||||
|
||||
batched = true;
|
||||
server.addBatch(this, transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneratedKey() {
|
||||
return generatedKey;
|
||||
|
||||
@@ -20,6 +20,9 @@ import io.ebean.plugin.SpiServer;
|
||||
import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiJsonContext;
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
import io.ebeaninternal.api.SpiLogger;
|
||||
import io.ebeaninternal.api.SpiLoggerFactory;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import io.ebeaninternal.dbmigration.DbOffline;
|
||||
import io.ebeaninternal.server.autotune.AutoTuneService;
|
||||
@@ -44,6 +47,8 @@ import io.ebeaninternal.server.dto.DtoBeanManager;
|
||||
import io.ebeaninternal.server.expression.DefaultExpressionFactory;
|
||||
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
|
||||
import io.ebeaninternal.server.expression.platform.DbExpressionHandlerFactory;
|
||||
import io.ebeaninternal.server.logger.DLogManager;
|
||||
import io.ebeaninternal.server.logger.DLoggerFactory;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
import io.ebeaninternal.server.persist.DefaultPersister;
|
||||
import io.ebeaninternal.server.persist.platform.MultiValueBind;
|
||||
@@ -134,16 +139,19 @@ public class InternalConfiguration {
|
||||
|
||||
private final MultiValueBind multiValueBind;
|
||||
|
||||
private final SpiLogManager logManager;
|
||||
|
||||
public InternalConfiguration(ClusterManager clusterManager,
|
||||
SpiCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor,
|
||||
ServerConfig serverConfig, BootupClasses bootupClasses) {
|
||||
|
||||
this.serverConfig = serverConfig;
|
||||
this.logManager = initLogManager();
|
||||
this.docStoreFactory = initDocStoreFactory(serverConfig.service(DocStoreFactory.class));
|
||||
this.jsonFactory = serverConfig.getJsonFactory();
|
||||
this.clusterManager = clusterManager;
|
||||
this.backgroundExecutor = backgroundExecutor;
|
||||
this.cacheManager = cacheManager;
|
||||
this.serverConfig = serverConfig;
|
||||
this.bootupClasses = bootupClasses;
|
||||
|
||||
this.databasePlatform = serverConfig.getDatabasePlatform();
|
||||
@@ -169,6 +177,20 @@ public class InternalConfiguration {
|
||||
this.cQueryEngine = new CQueryEngine(serverConfig, databasePlatform, binder, asOfTableMapping, draftTableMap);
|
||||
}
|
||||
|
||||
private SpiLogManager initLogManager() {
|
||||
|
||||
// allow plugin - i.e. capture executed SQL for testing/asserts
|
||||
SpiLoggerFactory loggerFactory = serverConfig.service(SpiLoggerFactory.class);
|
||||
if (loggerFactory == null) {
|
||||
loggerFactory = new DLoggerFactory();
|
||||
}
|
||||
|
||||
SpiLogger sql = loggerFactory.create("io.ebean.SQL");
|
||||
SpiLogger sum = loggerFactory.create("io.ebean.SUM");
|
||||
SpiLogger txn = loggerFactory.create("io.ebean.TXN");
|
||||
return new DLogManager(sql, sum, txn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return the ExpressionFactory based on configuration and database platform.
|
||||
*/
|
||||
@@ -262,9 +284,9 @@ public class InternalConfiguration {
|
||||
|
||||
DbHistorySupport historySupport = databasePlatform.getHistorySupport();
|
||||
if (historySupport == null) {
|
||||
return new Binder(typeManager, 0, false, jsonHandler, dataTimeZone, multiValueBind);
|
||||
return new Binder(typeManager, logManager, 0, false, jsonHandler, dataTimeZone, multiValueBind);
|
||||
}
|
||||
return new Binder(typeManager, historySupport.getBindCount(), historySupport.isStandardsBased(), jsonHandler, dataTimeZone, multiValueBind);
|
||||
return new Binder(typeManager, logManager, historySupport.getBindCount(), historySupport.isStandardsBased(), jsonHandler, dataTimeZone, multiValueBind);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,7 +400,7 @@ public class InternalConfiguration {
|
||||
|
||||
TransactionManagerOptions options =
|
||||
new TransactionManagerOptions(notifyL2CacheInForeground, serverConfig, scopeManager, clusterManager, backgroundExecutor,
|
||||
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler());
|
||||
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler(), logManager);
|
||||
|
||||
if (serverConfig.isExplicitTransactionBeginMode()) {
|
||||
return new ExplicitTransactionManager(options);
|
||||
@@ -511,4 +533,8 @@ public class InternalConfiguration {
|
||||
public DtoBeanManager getDtoBeanManager() {
|
||||
return dtoBeanManager;
|
||||
}
|
||||
|
||||
public SpiLogManager getLogManager() {
|
||||
return logManager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
@@ -27,15 +26,17 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
|
||||
private String description;
|
||||
|
||||
private boolean addBatch;
|
||||
|
||||
/**
|
||||
* Create.
|
||||
*/
|
||||
public PersistRequestUpdateSql(SpiEbeanServer server, SqlUpdate sqlUpdate,
|
||||
public PersistRequestUpdateSql(SpiEbeanServer server, SpiSqlUpdate sqlUpdate,
|
||||
SpiTransaction t, PersistExecute persistExecute) {
|
||||
|
||||
super(server, t, persistExecute, sqlUpdate.getLabel());
|
||||
this.type = Type.UPDATESQL;
|
||||
this.updateSql = (SpiSqlUpdate) sqlUpdate;
|
||||
this.updateSql = sqlUpdate;
|
||||
updateSql.reset();
|
||||
}
|
||||
|
||||
@@ -44,11 +45,24 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
profileBase(EVT_UPDATESQL, offset, (short)0, flushCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this statement to JDBC batch for later execution.
|
||||
*/
|
||||
public int addBatch() {
|
||||
this.addBatch = true;
|
||||
return executeOrQueue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeNow() {
|
||||
return persistExecute.executeSqlUpdate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatchThisRequest() {
|
||||
return addBatch || super.isBatchThisRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeOrQueue() {
|
||||
return executeStatement();
|
||||
|
||||
@@ -8,6 +8,7 @@ import io.ebean.Transaction;
|
||||
import io.ebean.Update;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
@@ -91,4 +92,14 @@ public interface Persister {
|
||||
* Visit the metrics.
|
||||
*/
|
||||
void visitMetrics(MetricVisitor visitor);
|
||||
|
||||
/**
|
||||
* Add the statement to JDBC batch for later execution via executeBatch.
|
||||
*/
|
||||
void addBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the associated batched statement.
|
||||
*/
|
||||
int[] executeBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction);
|
||||
}
|
||||
|
||||
@@ -152,6 +152,11 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
*/
|
||||
private final IdType idType;
|
||||
|
||||
/**
|
||||
* Set when Id property is marked with GeneratedValue annotation.
|
||||
*/
|
||||
private final boolean idGeneratedValue;
|
||||
|
||||
private final boolean idTypePlatformDefault;
|
||||
|
||||
private final PlatformIdGenerator idGenerator;
|
||||
@@ -450,6 +455,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
this.defaultSelectClause = deploy.getDefaultSelectClause();
|
||||
this.idType = deploy.getIdType();
|
||||
this.idGeneratedValue = deploy.isIdGeneratedValue();
|
||||
this.idTypePlatformDefault = deploy.isIdTypePlatformDefault();
|
||||
this.idGenerator = deploy.getIdGenerator();
|
||||
this.sequenceName = deploy.getSequenceName();
|
||||
@@ -631,9 +637,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
*/
|
||||
public void setEbeanServer(SpiEbeanServer ebeanServer) {
|
||||
this.ebeanServer = ebeanServer;
|
||||
for (BeanPropertyAssocMany<?> aPropertiesMany : propertiesMany) {
|
||||
for (BeanPropertyAssocMany<?> assocMany : propertiesMany) {
|
||||
// used for creating lazy loading lists etc
|
||||
aPropertiesMany.setLoader(ebeanServer);
|
||||
assocMany.setEbeanServer(ebeanServer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1922,6 +1928,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return createEntityBean();
|
||||
}
|
||||
|
||||
/**
|
||||
* We actually need to do a query because we don't know the type without the discriminator
|
||||
* value, just select the id property and discriminator column (auto added)
|
||||
*/
|
||||
private T findReferenceBean(Object id, PersistenceContext pc) {
|
||||
DefaultOrmQuery<T> query = new DefaultOrmQuery<>(this, ebeanServer, ebeanServer.getExpressionFactory());
|
||||
query.setPersistenceContext(pc);
|
||||
return query
|
||||
// .select(getIdProperty().getName())
|
||||
// we do not select the id because we
|
||||
// probably have to load the entire bean
|
||||
.setId(id).findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reference bean based on the id.
|
||||
*/
|
||||
@@ -1941,6 +1961,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (inheritInfo != null && !inheritInfo.isConcrete()) {
|
||||
return findReferenceBean(id, pc);
|
||||
}
|
||||
|
||||
EntityBean eb = createEntityBean();
|
||||
id = convertSetId(id, eb);
|
||||
|
||||
@@ -1974,10 +1998,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
try {
|
||||
if (inheritInfo != null && !inheritInfo.isConcrete()) {
|
||||
// we actually need to do a query because we don't know the type without the discriminator
|
||||
// value, just select the id property and discriminator column (auto added)
|
||||
DefaultOrmQuery<T> query = new DefaultOrmQuery<>(this, ebeanServer, ebeanServer.getExpressionFactory());
|
||||
return query.select(getIdProperty().getName()).setId(id).findOne();
|
||||
return findReferenceBean(id, pc);
|
||||
}
|
||||
|
||||
EntityBean eb = createEntityBean();
|
||||
@@ -2984,6 +3005,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return idType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the Id value is marked as a <code>@GeneratedValue</code>.
|
||||
*/
|
||||
public boolean isIdGeneratedValue() {
|
||||
return idGeneratedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the identity is the platform default (not explicitly set).
|
||||
*/
|
||||
|
||||
@@ -1369,9 +1369,15 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
desc.setIdType(IdType.EXTERNAL);
|
||||
return;
|
||||
}
|
||||
// use the default. IDENTITY or SEQUENCE.
|
||||
desc.setIdType(dbIdentity.getIdType());
|
||||
desc.setIdTypePlatformDefault();
|
||||
if (desc.isIdGeneratedValue() || serverConfig.isIdGeneratorAutomatic()) {
|
||||
// use IDENTITY or SEQUENCE based on platform
|
||||
desc.setIdType(dbIdentity.getIdType());
|
||||
desc.setIdTypePlatformDefault();
|
||||
} else {
|
||||
// externally/application supplied Id values
|
||||
desc.setIdType(IdType.EXTERNAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (desc.getBaseTable() == null) {
|
||||
@@ -1387,21 +1393,23 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return;
|
||||
}
|
||||
|
||||
String seqName = desc.getIdGeneratorName();
|
||||
if (seqName != null) {
|
||||
logger.debug("explicit sequence {} on {}", seqName, desc.getFullName());
|
||||
} else {
|
||||
String primaryKeyColumn = desc.getSinglePrimaryKeyColumn();
|
||||
// use namingConvention to define sequence name
|
||||
seqName = namingConvention.getSequenceName(desc.getBaseTable(), primaryKeyColumn);
|
||||
}
|
||||
if (IdType.SEQUENCE == desc.getIdType()) {
|
||||
String seqName = desc.getIdGeneratorName();
|
||||
if (seqName != null) {
|
||||
logger.debug("explicit sequence {} on {}", seqName, desc.getFullName());
|
||||
} else {
|
||||
String primaryKeyColumn = desc.getSinglePrimaryKeyColumn();
|
||||
// use namingConvention to define sequence name
|
||||
seqName = namingConvention.getSequenceName(desc.getBaseTable(), primaryKeyColumn);
|
||||
}
|
||||
|
||||
if (databasePlatform.isSequenceBatchMode()) {
|
||||
// use sequence next step 1 as we are going to batch fetch them instead
|
||||
desc.setSequenceAllocationSize(1);
|
||||
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));
|
||||
}
|
||||
int stepSize = desc.getSequenceAllocationSize();
|
||||
desc.setIdGenerator(createSequenceIdGenerator(seqName, stepSize));
|
||||
}
|
||||
|
||||
private PlatformIdGenerator createSequenceIdGenerator(String seqName, int stepSize) {
|
||||
|
||||
@@ -78,6 +78,11 @@ public final class BeanFkeyProperty implements ElPropertyValue {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object localEncrypt(Object value) {
|
||||
throw new IllegalArgumentException("Should not get here?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsFormulaWithJoin() {
|
||||
return false;
|
||||
|
||||
@@ -28,6 +28,7 @@ import io.ebeaninternal.server.query.STreeProperty;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.server.type.LocalEncryptedType;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.ScalarTypeBoolean;
|
||||
import io.ebeaninternal.server.type.ScalarTypeEnum;
|
||||
@@ -1246,6 +1247,11 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
return dbBind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object localEncrypt(Object value) {
|
||||
return ((LocalEncryptedType)scalarType).localEncrypt(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if DB encrypted.
|
||||
*/
|
||||
|
||||
@@ -65,6 +65,8 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
*/
|
||||
final TableJoin tableJoin;
|
||||
|
||||
final PropertyForeignKey foreignKey;
|
||||
|
||||
/**
|
||||
* The type of the joined bean.
|
||||
*/
|
||||
@@ -90,6 +92,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
*/
|
||||
public BeanPropertyAssoc(BeanDescriptor<?> descriptor, DeployBeanPropertyAssoc<T> deploy) {
|
||||
super(descriptor, deploy);
|
||||
this.foreignKey = deploy.getForeignKey();
|
||||
this.extraWhere = InternString.intern(deploy.getExtraWhere());
|
||||
this.beanTable = deploy.getBeanTable();
|
||||
this.mappedBy = InternString.intern(deploy.getMappedBy());
|
||||
@@ -128,6 +131,13 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
return fetchPreference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the extra configuration for the foreign key.
|
||||
*/
|
||||
public PropertyForeignKey getForeignKey() {
|
||||
return foreignKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ElPropertyValue for a *ToOne or *ToMany.
|
||||
*/
|
||||
|
||||
@@ -7,10 +7,10 @@ import io.ebean.Transaction;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.text.PathProperties;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
@@ -50,6 +50,8 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
private final String intersectionPublishTable;
|
||||
private final String intersectionDraftTable;
|
||||
|
||||
private IntersectionTable intersectionTable;
|
||||
|
||||
/**
|
||||
* For ManyToMany this is the Inverse join used to build reference queries.
|
||||
*/
|
||||
@@ -352,10 +354,30 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
* Set the lazy load server to help create reference collections (that lazy
|
||||
* load on demand).
|
||||
*/
|
||||
public void setLoader(BeanCollectionLoader loader) {
|
||||
public void setEbeanServer(SpiEbeanServer server) {
|
||||
if (help != null) {
|
||||
help.setLoader(loader);
|
||||
help.setLoader(server);
|
||||
}
|
||||
if (manyToMany) {
|
||||
intersectionTable = initIntersectionTable();
|
||||
}
|
||||
}
|
||||
|
||||
private IntersectionTable initIntersectionTable() {
|
||||
|
||||
IntersectionBuilder row = new IntersectionBuilder(intersectionPublishTable, intersectionDraftTable);
|
||||
for (ExportedProperty exportedProperty : exportedProperties) {
|
||||
row.addColumn(exportedProperty.getForeignDbColumn());
|
||||
}
|
||||
importedId.buildImport(row);
|
||||
return row.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the intersection table helper.
|
||||
*/
|
||||
public IntersectionTable intersectionTable() {
|
||||
return intersectionTable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -755,6 +777,20 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
return intersectionDraftTable != null && !intersectionDraftTable.equals(intersectionPublishTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the two side of a many to many to the given SqlUpdate.
|
||||
*/
|
||||
public void intersectionBind(SqlUpdate sql, EntityBean parentBean, EntityBean other) {
|
||||
if (embeddedExportedProperties) {
|
||||
BeanProperty idProp = descriptor.getIdProperty();
|
||||
parentBean = (EntityBean) idProp.getValue(parentBean);
|
||||
}
|
||||
for (ExportedProperty exportedProperty : exportedProperties) {
|
||||
sql.setNextParameter(exportedProperty.getValue(parentBean));
|
||||
}
|
||||
importedId.bindImport(sql, other);
|
||||
}
|
||||
|
||||
private void buildExport(IntersectionRow row, EntityBean parentBean) {
|
||||
|
||||
if (embeddedExportedProperties) {
|
||||
|
||||
@@ -46,15 +46,16 @@ class BeanPropertyAssocManyJsonHelp {
|
||||
if (JsonToken.VALUE_NULL == event) {
|
||||
return;
|
||||
}
|
||||
if (JsonToken.START_ARRAY != event) {
|
||||
throw new JsonParseException(parser, "Unexpected token " + event + " - expecting start_array ");
|
||||
}
|
||||
|
||||
if (many.isTransient()) {
|
||||
jsonReadTransientUsingObjectMapper(readJson, parentBean);
|
||||
return;
|
||||
}
|
||||
|
||||
if (JsonToken.START_ARRAY != event) {
|
||||
throw new JsonParseException(parser, "Unexpected token " + event + " - expecting start_array");
|
||||
}
|
||||
|
||||
many.setValue(parentBean, many.jsonReadCollection(readJson, parentBean));
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
|
||||
private final boolean primaryKeyExport;
|
||||
|
||||
private final PropertyForeignKey foreignKey;
|
||||
|
||||
private AssocOneHelp localHelp;
|
||||
|
||||
protected final BeanProperty[] embeddedProps;
|
||||
@@ -73,8 +71,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
DeployBeanPropertyAssocOne<T> deploy) {
|
||||
|
||||
super(descriptor, deploy);
|
||||
|
||||
foreignKey = deploy.getForeignKey();
|
||||
primaryKeyExport = deploy.isPrimaryKeyExport();
|
||||
oneToOne = deploy.isOneToOne();
|
||||
oneToOneExported = deploy.isOneToOneExported();
|
||||
@@ -323,10 +319,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
}
|
||||
}
|
||||
|
||||
public PropertyForeignKey getForeignKey() {
|
||||
return foreignKey;
|
||||
}
|
||||
|
||||
public boolean hasForeignKey() {
|
||||
return foreignKey == null || !foreignKey.isNoConstraint();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper to build IntersectionTable.
|
||||
*/
|
||||
public class IntersectionBuilder {
|
||||
|
||||
private final String publishTable;
|
||||
private final String draftTable;
|
||||
|
||||
private final List<String> columns = new ArrayList<>();
|
||||
|
||||
IntersectionBuilder(String publishTable, String draftTable) {
|
||||
this.publishTable = publishTable;
|
||||
this.draftTable = draftTable;
|
||||
}
|
||||
|
||||
public void addColumn(String column) {
|
||||
columns.add(column);
|
||||
}
|
||||
|
||||
public IntersectionTable build() {
|
||||
|
||||
String insertSql = insertSql(publishTable);
|
||||
String deleteSql = deleteSql(publishTable);
|
||||
|
||||
String draftInsertSql;
|
||||
String draftDeleteSql;
|
||||
if (publishTable.equals(draftTable)) {
|
||||
draftInsertSql = insertSql;
|
||||
draftDeleteSql = deleteSql;
|
||||
} else {
|
||||
draftInsertSql = insertSql(draftTable);
|
||||
draftDeleteSql = deleteSql(draftTable);
|
||||
}
|
||||
|
||||
return new IntersectionTable(insertSql, deleteSql, draftInsertSql, draftDeleteSql);
|
||||
}
|
||||
|
||||
private String insertSql(String tableName) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("insert into ").append(tableName).append(" (");
|
||||
|
||||
int count = 0;
|
||||
for (String column : columns) {
|
||||
if (count++ > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(column);
|
||||
}
|
||||
sb.append(") values (");
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append("?");
|
||||
}
|
||||
sb.append(")");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String deleteSql(String tableName) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("delete from ").append(tableName);
|
||||
sb.append(" where ");
|
||||
|
||||
int count = 0;
|
||||
for (String column : columns) {
|
||||
if (count++ > 0) {
|
||||
sb.append(" and ");
|
||||
}
|
||||
sb.append(column);
|
||||
sb.append(" = ?");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.expression.DefaultExpressionRequest;
|
||||
import io.ebeaninternal.server.expression.IdInExpression;
|
||||
@@ -22,12 +22,12 @@ public class IntersectionRow {
|
||||
private List<Object> excludeIds;
|
||||
private BeanDescriptor<?> excludeDescriptor;
|
||||
|
||||
public IntersectionRow(String tableName, BeanDescriptor<?> targetDescriptor) {
|
||||
IntersectionRow(String tableName, BeanDescriptor<?> targetDescriptor) {
|
||||
this.tableName = tableName;
|
||||
this.targetDescriptor = targetDescriptor;
|
||||
}
|
||||
|
||||
public IntersectionRow(String tableName) {
|
||||
IntersectionRow(String tableName) {
|
||||
this.tableName = tableName;
|
||||
this.targetDescriptor = null;
|
||||
}
|
||||
@@ -35,7 +35,7 @@ public class IntersectionRow {
|
||||
/**
|
||||
* Set Id's to exclude. This is for deleting non-attached detail Id's.
|
||||
*/
|
||||
public void setExcludeIds(List<Object> excludeIds, BeanDescriptor<?> excludeDescriptor) {
|
||||
void setExcludeIds(List<Object> excludeIds, BeanDescriptor<?> excludeDescriptor) {
|
||||
this.excludeIds = excludeIds;
|
||||
this.excludeDescriptor = excludeDescriptor;
|
||||
}
|
||||
@@ -44,7 +44,7 @@ public class IntersectionRow {
|
||||
values.put(key, value);
|
||||
}
|
||||
|
||||
public SqlUpdate createInsert(EbeanServer server) {
|
||||
public SqlUpdate createInsert(SpiEbeanServer server) {
|
||||
|
||||
BindParams bindParams = new BindParams();
|
||||
|
||||
@@ -72,7 +72,7 @@ public class IntersectionRow {
|
||||
return new DefaultSqlUpdate(server, sb.toString(), bindParams);
|
||||
}
|
||||
|
||||
public SqlUpdate createDelete(EbeanServer server, boolean softDelete) {
|
||||
public SqlUpdate createDelete(SpiEbeanServer server, boolean softDelete) {
|
||||
|
||||
BindParams bindParams = new BindParams();
|
||||
|
||||
@@ -107,7 +107,7 @@ public class IntersectionRow {
|
||||
return new DefaultSqlUpdate(server, sb.toString(), bindParams);
|
||||
}
|
||||
|
||||
public SqlUpdate createDeleteChildren(EbeanServer server) {
|
||||
public SqlUpdate createDeleteChildren(SpiEbeanServer server) {
|
||||
|
||||
BindParams bindParams = new BindParams();
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.SqlUpdate;
|
||||
|
||||
public class IntersectionTable {
|
||||
|
||||
private final String insertSql;
|
||||
private final String deleteSql;
|
||||
private final String draftInsertSql;
|
||||
private final String draftDeleteSql;
|
||||
|
||||
IntersectionTable(String insertSql, String deleteSql, String draftInsertSql, String draftDeleteSql) {
|
||||
this.insertSql = insertSql;
|
||||
this.deleteSql = deleteSql;
|
||||
this.draftInsertSql = draftInsertSql;
|
||||
this.draftDeleteSql = draftDeleteSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a SqlUpdate for inserting into the intersection table.
|
||||
*/
|
||||
public SqlUpdate insert(EbeanServer server, boolean draft) {
|
||||
return server.createSqlUpdate(draft ? draftInsertSql : insertSql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a SqlUpdate for deleting from the intersection table.
|
||||
*/
|
||||
public SqlUpdate delete(EbeanServer server, boolean draft) {
|
||||
return server.createSqlUpdate(draft ? draftDeleteSql : deleteSql);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import io.ebean.SqlUpdate;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.IntersectionBuilder;
|
||||
import io.ebeaninternal.server.deploy.IntersectionRow;
|
||||
import io.ebeaninternal.server.persist.dml.GenerateDmlRequest;
|
||||
import io.ebeaninternal.server.persist.dmlbind.BindableRequest;
|
||||
@@ -66,4 +67,14 @@ public interface ImportedId {
|
||||
* Return the set importedId clause.
|
||||
*/
|
||||
String importedIdClause();
|
||||
|
||||
/**
|
||||
* Add DB columns to the intersection builder.
|
||||
*/
|
||||
void buildImport(IntersectionBuilder row);
|
||||
|
||||
/**
|
||||
* Bind values to the intersection SqlUpdate.
|
||||
*/
|
||||
void bindImport(SqlUpdate sql, EntityBean other);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.IntersectionBuilder;
|
||||
import io.ebeaninternal.server.deploy.IntersectionRow;
|
||||
import io.ebeaninternal.server.persist.dml.GenerateDmlRequest;
|
||||
import io.ebeaninternal.server.persist.dmlbind.BindableRequest;
|
||||
@@ -130,15 +131,31 @@ public class ImportedIdEmbedded implements ImportedId {
|
||||
|
||||
EntityBean embeddedId = (EntityBean) foreignAssocOne.getValue(other);
|
||||
if (embeddedId == null) {
|
||||
String msg = "Foreign Key value null?";
|
||||
throw new PersistenceException(msg);
|
||||
throw new PersistenceException("Foreign Key value null?");
|
||||
}
|
||||
|
||||
for (ImportedIdSimple anImported : imported) {
|
||||
Object scalarValue = anImported.foreignProperty.getValue(embeddedId);
|
||||
row.put(anImported.localDbColumn, scalarValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildImport(IntersectionBuilder row) {
|
||||
for (ImportedIdSimple importedScalar : imported) {
|
||||
row.addColumn(importedScalar.localDbColumn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindImport(SqlUpdate sql, EntityBean other) {
|
||||
EntityBean embeddedId = (EntityBean) foreignAssocOne.getValue(other);
|
||||
if (embeddedId == null) {
|
||||
throw new PersistenceException("Foreign Key value null?");
|
||||
}
|
||||
for (ImportedIdSimple anImported : imported) {
|
||||
Object scalarValue = anImported.foreignProperty.getValue(embeddedId);
|
||||
sql.setNextParameter(scalarValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.ebeaninternal.server.deploy.BeanFkeyProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.IntersectionBuilder;
|
||||
import io.ebeaninternal.server.deploy.IntersectionRow;
|
||||
import io.ebeaninternal.server.persist.dml.GenerateDmlRequest;
|
||||
import io.ebeaninternal.server.persist.dmlbind.BindableRequest;
|
||||
@@ -98,6 +99,20 @@ public final class ImportedIdSimple implements ImportedId, Comparable<ImportedId
|
||||
return foreignProperty.getValue(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildImport(IntersectionBuilder row) {
|
||||
row.addColumn(localDbColumn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindImport(SqlUpdate sql, EntityBean other) {
|
||||
Object value = getIdValue(other);
|
||||
if (value == null) {
|
||||
throw new PersistenceException("Foreign Key value null?");
|
||||
}
|
||||
sql.setNextParameter(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildImport(IntersectionRow row, EntityBean other) {
|
||||
|
||||
|
||||
@@ -106,6 +106,11 @@ public class DeployBeanDescriptor<T> {
|
||||
|
||||
private PlatformIdGenerator idGenerator;
|
||||
|
||||
/**
|
||||
* Set true when explicit auto generated Id.
|
||||
*/
|
||||
private boolean idGeneratedValue;
|
||||
|
||||
/**
|
||||
* The database sequence name (optional).
|
||||
*/
|
||||
@@ -848,6 +853,20 @@ public class DeployBeanDescriptor<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true for automatic Id generation strategy.
|
||||
*/
|
||||
public boolean isIdGeneratedValue() {
|
||||
return idGeneratedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when GeneratedValue explicitly mapped on Id property.
|
||||
*/
|
||||
public void setIdGeneratedValue() {
|
||||
this.idGeneratedValue = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the standard UUID generator.
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import io.ebeaninternal.server.deploy.BeanCascadeInfo;
|
||||
import io.ebeaninternal.server.deploy.BeanTable;
|
||||
import io.ebeaninternal.server.deploy.PropertyForeignKey;
|
||||
|
||||
/**
|
||||
* Abstract base for properties mapped to an associated bean, list, set or map.
|
||||
@@ -16,7 +17,7 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
/**
|
||||
* Persist settings.
|
||||
*/
|
||||
protected final BeanCascadeInfo cascadeInfo = new BeanCascadeInfo();
|
||||
final BeanCascadeInfo cascadeInfo = new BeanCascadeInfo();
|
||||
|
||||
/**
|
||||
* The join table information.
|
||||
@@ -42,6 +43,8 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
|
||||
private int fetchPreference = 1000;
|
||||
|
||||
private PropertyForeignKey foreignKey;
|
||||
|
||||
/**
|
||||
* Construct the property.
|
||||
*/
|
||||
@@ -117,6 +120,13 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
return cascadeInfo;
|
||||
}
|
||||
|
||||
public void setForeignKey(PropertyForeignKey foreignKey) {
|
||||
this.foreignKey = foreignKey;
|
||||
}
|
||||
|
||||
public PropertyForeignKey getForeignKey() {
|
||||
return foreignKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mappedBy deployment attribute.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import io.ebeaninternal.server.deploy.PropertyForeignKey;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
@@ -22,8 +21,6 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
|
||||
private String columnPrefix;
|
||||
|
||||
private PropertyForeignKey foreignKey;
|
||||
|
||||
private boolean orphanRemoval;
|
||||
|
||||
/**
|
||||
@@ -148,14 +145,6 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public void setForeignKey(PropertyForeignKey foreignKey) {
|
||||
this.foreignKey = foreignKey;
|
||||
}
|
||||
|
||||
public PropertyForeignKey getForeignKey() {
|
||||
return foreignKey;
|
||||
}
|
||||
|
||||
public void setOrphanRemoval(boolean orphanRemoval) {
|
||||
this.orphanRemoval = orphanRemoval;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
import io.ebean.annotation.FetchPreference;
|
||||
import io.ebean.annotation.HistoryExclude;
|
||||
import io.ebean.annotation.PrivateOwned;
|
||||
@@ -12,6 +13,7 @@ import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanTable;
|
||||
import io.ebeaninternal.server.deploy.PropertyForeignKey;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
@@ -95,6 +97,12 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
readElementCollection(prop, elementCollection);
|
||||
}
|
||||
|
||||
// for ManyToMany typically to disable foreign keys from intersection table
|
||||
DbForeignKey dbForeignKey = get(prop, DbForeignKey.class);
|
||||
if (dbForeignKey != null){
|
||||
prop.setForeignKey(new PropertyForeignKey(dbForeignKey));
|
||||
}
|
||||
|
||||
if (get(prop, HistoryExclude.class) != null) {
|
||||
prop.setExcludedFromHistory();
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
|
||||
}
|
||||
|
||||
private String errorMsgMissingBeanTable(Class<?> type, String from) {
|
||||
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered?";
|
||||
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered? Does it have the @Entity annotation?";
|
||||
}
|
||||
|
||||
private BeanTable beanTable(DeployBeanPropertyAssoc<?> prop) {
|
||||
|
||||
@@ -322,7 +322,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
prop.setDbLength(length.value());
|
||||
}
|
||||
|
||||
io.ebean.annotation.NotNull nonNull = get(prop, io.ebean.annotation.NotNull.class);
|
||||
io.ebean.annotation.NotNull nonNull = get(prop, io.ebean.annotation.NotNull.class);
|
||||
if (nonNull != null) {
|
||||
prop.setNullable(false);
|
||||
}
|
||||
@@ -389,7 +389,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
|
||||
Set<DbMigration> dbMigration = getAll(prop, DbMigration.class);
|
||||
dbMigration.forEach(ann -> prop.addDbMigrationInfo(
|
||||
new DbMigrationInfo(ann.preAdd(), ann.postAdd(), ann.preAlter(), ann.postAlter(), ann.platforms())));
|
||||
new DbMigrationInfo(ann.preAdd(), ann.postAdd(), ann.preAlter(), ann.postAlter(), ann.platforms())));
|
||||
}
|
||||
|
||||
private void addIndex(DeployBeanProperty prop, Index index) {
|
||||
@@ -515,6 +515,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
|
||||
private void readGenValue(GeneratedValue gen, DeployBeanProperty prop) {
|
||||
|
||||
descriptor.setIdGeneratedValue();
|
||||
String genName = gen.generator();
|
||||
|
||||
SequenceGenerator sequenceGenerator = find(prop, SequenceGenerator.class);
|
||||
|
||||
@@ -174,6 +174,11 @@ public class ElPropertyChain implements ElPropertyValue {
|
||||
return lastElPropertyValue.isLocalEncrypted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object localEncrypt(Object value) {
|
||||
return lastElPropertyValue.localEncrypt(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAssocIsEmpty(SpiExpressionRequest request, String path) {
|
||||
return lastElPropertyValue.getAssocIsEmpty(request, path);
|
||||
|
||||
@@ -52,6 +52,11 @@ public interface ElPropertyValue extends ElPropertyDeploy, ExpressionPath {
|
||||
*/
|
||||
boolean isDbEncrypted();
|
||||
|
||||
/**
|
||||
* Encrypt the input value return the encrypted value.
|
||||
*/
|
||||
Object localEncrypt(Object value);
|
||||
|
||||
/**
|
||||
* Return the value ensuring objects prior to the top scalar property are
|
||||
* automatically populated.
|
||||
|
||||
@@ -84,10 +84,11 @@ public class SimpleExpression extends AbstractValueExpression {
|
||||
// bind the key as well as the value
|
||||
String encryptKey = prop.getBeanProperty().getEncryptKey().getStringValue();
|
||||
request.addBindEncryptKey(encryptKey);
|
||||
} else if (prop.isLocalEncrypted()) {
|
||||
Object bindVal = prop.localEncrypt(value());
|
||||
request.addBindEncryptKey(bindVal);
|
||||
return;
|
||||
}
|
||||
//else if (prop.isLocalEncrypted()) {
|
||||
// not supporting this for equals (but probably could)
|
||||
// prop.getBeanProperty().getScalarType();
|
||||
}
|
||||
|
||||
request.addBindValue(value());
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package io.ebeaninternal.server.logger;
|
||||
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
import io.ebeaninternal.api.SpiLogger;
|
||||
|
||||
public class DLogManager implements SpiLogManager {
|
||||
|
||||
private final SpiLogger sql;
|
||||
private final SpiLogger summary;
|
||||
private final SpiLogger txn;
|
||||
|
||||
public DLogManager(SpiLogger sql, SpiLogger summary, SpiLogger txn) {
|
||||
this.sql = sql;
|
||||
this.summary = summary;
|
||||
this.txn = txn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiLogger txn() {
|
||||
return txn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiLogger sql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiLogger sum() {
|
||||
return summary;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package io.ebeaninternal.server.logger;
|
||||
|
||||
import io.ebeaninternal.api.SpiLogger;
|
||||
import io.ebeaninternal.api.SpiLoggerFactory;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Default SpiLoggerFactory implementation.
|
||||
*/
|
||||
public class DLoggerFactory implements SpiLoggerFactory {
|
||||
|
||||
/**
|
||||
* Just use a standard slf4j Logger.
|
||||
*/
|
||||
@Override
|
||||
public SpiLogger create(String name) {
|
||||
return new DSpiLogger(LoggerFactory.getLogger(name));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package io.ebeaninternal.server.logger;
|
||||
|
||||
import io.ebeaninternal.api.SpiLogger;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class DSpiLogger implements SpiLogger {
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
public DSpiLogger(Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebug() {
|
||||
return logger.isDebugEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrace() {
|
||||
return logger.isTraceEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String msg) {
|
||||
logger.debug(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String msg) {
|
||||
logger.trace(msg);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@@ -357,4 +358,11 @@ public final class BatchControl {
|
||||
private BatchedBeanHolder[] getBeanHolderArray() {
|
||||
return beanHoldMap.values().toArray(new BatchedBeanHolder[beanHoldMap.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a batched statement.
|
||||
*/
|
||||
public int[] execute(String key, boolean getGeneratedKeys) throws SQLException {
|
||||
return pstmtHolder.execute(key, getGeneratedKeys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ public class BatchedPstmt implements SpiProfileTransactionEvent {
|
||||
|
||||
private long profileStart;
|
||||
|
||||
private int[] results;
|
||||
|
||||
/**
|
||||
* Create with a given statement.
|
||||
*/
|
||||
@@ -116,7 +118,7 @@ public class BatchedPstmt implements SpiProfileTransactionEvent {
|
||||
|
||||
private void executeAndCheckRowCounts() throws SQLException {
|
||||
|
||||
int[] results = pstmt.executeBatch();
|
||||
results = pstmt.executeBatch();
|
||||
if (results.length != list.size()) {
|
||||
String s = "results array error " + results.length + " " + list.size();
|
||||
throw new SQLException(s);
|
||||
@@ -140,4 +142,10 @@ public class BatchedPstmt implements SpiProfileTransactionEvent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the execution results (row counts).
|
||||
*/
|
||||
public int[] getResults() {
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.persist;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -73,6 +74,19 @@ public class BatchedPstmtHolder {
|
||||
return stmtMap.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute one of the batched statements returning the row counts.
|
||||
*/
|
||||
public int[] execute(String key, boolean getGeneratedKeys) throws SQLException {
|
||||
|
||||
BatchedPstmt batchedPstmt = stmtMap.remove(key);
|
||||
if (batchedPstmt == null) {
|
||||
throw new PersistenceException("No batched statement found for key " + key);
|
||||
}
|
||||
batchedPstmt.executeBatch(getGeneratedKeys);
|
||||
return batchedPstmt.getResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all batched PreparedStatements.
|
||||
*
|
||||
|
||||
@@ -2,11 +2,11 @@ package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
import io.ebeaninternal.server.core.Message;
|
||||
import io.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
|
||||
import io.ebeaninternal.server.persist.platform.MultiValueBind;
|
||||
import io.ebeaninternal.server.transaction.TransactionManager;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
@@ -48,7 +48,7 @@ public class Binder {
|
||||
/**
|
||||
* Set the PreparedStatement with which to bind variables to.
|
||||
*/
|
||||
public Binder(TypeManager typeManager, int asOfBindCount, boolean asOfStandardsBased,
|
||||
public Binder(TypeManager typeManager, SpiLogManager logManager, int asOfBindCount, boolean asOfStandardsBased,
|
||||
DbExpressionHandler dbExpressionHandler, DataTimeZone dataTimeZone, MultiValueBind multiValueBind) {
|
||||
|
||||
this.typeManager = typeManager;
|
||||
@@ -57,12 +57,7 @@ public class Binder {
|
||||
this.dbExpressionHandler = dbExpressionHandler;
|
||||
this.dataTimeZone = dataTimeZone;
|
||||
this.multiValueBind = multiValueBind;
|
||||
this.enableBindLog = enableBindLog();
|
||||
}
|
||||
|
||||
private boolean enableBindLog() {
|
||||
return TransactionManager.SQL_LOGGER.isDebugEnabled()
|
||||
|| TransactionManager.SUM_LOGGER.isDebugEnabled();
|
||||
this.enableBindLog = logManager.sql().isDebug();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,7 @@ import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.BeanPersistController;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiUpdate;
|
||||
import io.ebeaninternal.server.core.Message;
|
||||
@@ -34,6 +35,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -124,13 +126,28 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction) {
|
||||
new PersistRequestUpdateSql(server, sqlUpdate, transaction, persistExecute).addBatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] executeBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction) {
|
||||
BatchControl batchControl = transaction.getBatchControl();
|
||||
try {
|
||||
return batchControl.execute(sqlUpdate.getSql(), sqlUpdate.isGetGeneratedKeys());
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the updateSql.
|
||||
*/
|
||||
@Override
|
||||
public int executeSqlUpdate(SqlUpdate updSql, Transaction t) {
|
||||
|
||||
return executeOrQueue(new PersistRequestUpdateSql(server, updSql, (SpiTransaction) t, persistExecute));
|
||||
return executeOrQueue(new PersistRequestUpdateSql(server, (SpiSqlUpdate) updSql, (SpiTransaction) t, persistExecute));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,14 @@ class MergeContext {
|
||||
this.clientGeneratedIds = clientGeneratedIds;
|
||||
}
|
||||
|
||||
public SpiEbeanServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
public SpiTransaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the list of beans to delete.
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
class MergeHandler {
|
||||
|
||||
private final Pattern PATH_SPLIT = Pattern.compile("\\.");
|
||||
private static final Pattern PATH_SPLIT = Pattern.compile("\\.");
|
||||
|
||||
private final SpiEbeanServer server;
|
||||
private final BeanDescriptor<?> desc;
|
||||
@@ -51,7 +51,7 @@ class MergeHandler {
|
||||
List<EntityBean> merge() {
|
||||
|
||||
Set<String> paths = options.paths();
|
||||
if (paths.isEmpty() && !options.isClientGeneratedIds()) {
|
||||
if (desc.isIdGeneratedValue() && paths.isEmpty() && !options.isClientGeneratedIds()) {
|
||||
// just do a single insert or update based on Id value present
|
||||
Object id = desc.getId(bean);
|
||||
if (id != null) {
|
||||
@@ -139,7 +139,12 @@ class MergeHandler {
|
||||
throw new PersistenceException("merge path [" + path + "] is not a ToMany or ToOne property of " + targetDesc.getFullName());
|
||||
}
|
||||
if (prop instanceof BeanPropertyAssocMany<?>) {
|
||||
return new MergeNodeAssocMany(fullPath, (BeanPropertyAssocMany<?>) prop);
|
||||
BeanPropertyAssocMany<?> assocMany = (BeanPropertyAssocMany<?>) prop;
|
||||
if (assocMany.isManyToMany()) {
|
||||
return new MergeNodeAssocManyToMany(fullPath, assocMany);
|
||||
} else {
|
||||
return new MergeNodeAssocOneToMany(fullPath, assocMany);
|
||||
}
|
||||
} else {
|
||||
return new MergeNodeAssocOne(fullPath, (BeanPropertyAssocOne<?>) prop);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -50,6 +52,22 @@ abstract class MergeNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the outline beans as a map keyed by Id values.
|
||||
*/
|
||||
Map<Object, EntityBean> toMap(Collection outlines) {
|
||||
|
||||
Map<Object, EntityBean> outlineMap = new HashMap<>();
|
||||
if (outlines != null) {
|
||||
for (Object out : outlines) {
|
||||
EntityBean outlineBean = (EntityBean) out;
|
||||
Object outlineId = targetDescriptor.getId(outlineBean);
|
||||
outlineMap.put(outlineId, outlineBean);
|
||||
}
|
||||
}
|
||||
return outlineMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the query to fetch the Ids values for the foreign keys basically.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.IntersectionTable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Node for processing merge on ManyToMany properties.
|
||||
*/
|
||||
class MergeNodeAssocManyToMany extends MergeNode {
|
||||
|
||||
private final BeanPropertyAssocMany<?> many;
|
||||
|
||||
MergeNodeAssocManyToMany(String fullPath, BeanPropertyAssocMany<?> property) {
|
||||
super(fullPath, property);
|
||||
this.many = property;
|
||||
}
|
||||
|
||||
public void merge(MergeRequest request) {
|
||||
|
||||
EntityBean parentBean = request.getBean();
|
||||
|
||||
Collection beans = many.getRawCollection(parentBean);
|
||||
Collection outlines = many.getRawCollection(request.getOutline());
|
||||
|
||||
Map<Object, EntityBean> outlineIds = toMap(outlines);
|
||||
|
||||
List<EntityBean> additions = new ArrayList<>();
|
||||
if (beans != null) {
|
||||
for (Object bean : beans) {
|
||||
EntityBean entityBean = (EntityBean) bean;
|
||||
Object beanId = targetDescriptor.getId(entityBean);
|
||||
if (beanId != null) {
|
||||
if (outlineIds.remove(beanId) == null) {
|
||||
additions.add(entityBean);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// any remaining are considered deletes
|
||||
List<EntityBean> deletions = new ArrayList<>(outlineIds.values());
|
||||
|
||||
SpiEbeanServer server = request.getServer();
|
||||
SpiTransaction transaction = request.getTransaction();
|
||||
|
||||
IntersectionTable intersectionTable = many.intersectionTable();
|
||||
|
||||
if (!deletions.isEmpty()) {
|
||||
transaction.flushBatch();
|
||||
|
||||
SqlUpdate delete = intersectionTable.delete(server, false);
|
||||
for (EntityBean deletion : deletions) {
|
||||
many.intersectionBind(delete, parentBean, deletion);
|
||||
delete.addBatch();
|
||||
}
|
||||
delete.execute();
|
||||
}
|
||||
|
||||
if (!additions.isEmpty()) {
|
||||
transaction.flushBatch();
|
||||
|
||||
SqlUpdate insert = intersectionTable.insert(server, false);
|
||||
for (EntityBean addition : additions) {
|
||||
many.intersectionBind(insert, parentBean, addition);
|
||||
insert.addBatch();
|
||||
}
|
||||
insert.execute();
|
||||
}
|
||||
|
||||
many.resetMany(parentBean);
|
||||
}
|
||||
|
||||
}
|
||||
+3
-10
@@ -10,11 +10,11 @@ import java.util.Map;
|
||||
/**
|
||||
* Node for processing merge on ToMany properties.
|
||||
*/
|
||||
class MergeNodeAssocMany extends MergeNode {
|
||||
class MergeNodeAssocOneToMany extends MergeNode {
|
||||
|
||||
private final BeanPropertyAssocMany<?> many;
|
||||
|
||||
MergeNodeAssocMany(String fullPath, BeanPropertyAssocMany<?> property) {
|
||||
MergeNodeAssocOneToMany(String fullPath, BeanPropertyAssocMany<?> property) {
|
||||
super(fullPath, property);
|
||||
this.many = property;
|
||||
}
|
||||
@@ -24,14 +24,7 @@ class MergeNodeAssocMany extends MergeNode {
|
||||
Collection beans = many.getRawCollection(request.getBean());
|
||||
Collection outlines = many.getRawCollection(request.getOutline());
|
||||
|
||||
Map<Object, EntityBean> outlineIds = new HashMap<>();
|
||||
if (outlines != null) {
|
||||
for (Object outline : outlines) {
|
||||
EntityBean outlineBean = (EntityBean) outline;
|
||||
Object outlineId = targetDescriptor.getId(outlineBean);
|
||||
outlineIds.put(outlineId, outlineBean);
|
||||
}
|
||||
}
|
||||
Map<Object, EntityBean> outlineIds = toMap(outlines);
|
||||
|
||||
if (beans != null) {
|
||||
for (Object bean : beans) {
|
||||
@@ -1,6 +1,8 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
|
||||
/**
|
||||
* Request object used for processing the merge.
|
||||
@@ -18,6 +20,20 @@ class MergeRequest {
|
||||
this.outline = outline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated server.
|
||||
*/
|
||||
SpiEbeanServer getServer() {
|
||||
return context.getServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated transaction.
|
||||
*/
|
||||
public SpiTransaction getTransaction() {
|
||||
return context.getTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sub request with the given beans (to cascade the processing).
|
||||
*/
|
||||
|
||||
@@ -154,9 +154,7 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
|
||||
*/
|
||||
protected void logSql(String sql) {
|
||||
if (logLevelSql) {
|
||||
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
|
||||
sql = Str.add(sql, "; --bind(", bindLog.toString(), ")");
|
||||
}
|
||||
sql = Str.add(sql, "; --bind(", bindLog.toString(), ")");
|
||||
transaction.logSql(sql);
|
||||
}
|
||||
}
|
||||
@@ -187,7 +185,10 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
|
||||
@Override
|
||||
public void bindNoLog(Object value, int sqlType, String logPlaceHolder) throws SQLException {
|
||||
if (logLevelSql) {
|
||||
bindLog.append(logPlaceHolder).append(" ");
|
||||
if (bindLog.length() > 0) {
|
||||
bindLog.append(",");
|
||||
}
|
||||
bindLog.append(logPlaceHolder);
|
||||
}
|
||||
dataBind.setObject(value, sqlType);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import io.ebeaninternal.server.core.SpiResultSet;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.lib.util.Str;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
import io.ebeaninternal.server.transaction.TransactionManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -82,9 +81,7 @@ public class CQueryEngine {
|
||||
|
||||
if (request.isLogSql()) {
|
||||
String logSql = query.getGeneratedSql();
|
||||
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
|
||||
logSql = Str.add(logSql, "; --bind(", query.getBindLog(), ") rows:", String.valueOf(rows));
|
||||
}
|
||||
logSql = Str.add(logSql, "; --bind(", query.getBindLog(), ") rows:", String.valueOf(rows));
|
||||
request.logSql(logSql);
|
||||
}
|
||||
|
||||
@@ -151,11 +148,7 @@ public class CQueryEngine {
|
||||
}
|
||||
|
||||
private <T> void logGeneratedSql(OrmQueryRequest<T> request, String sql, String bindLog) {
|
||||
String logSql = sql;
|
||||
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
|
||||
logSql = Str.add(logSql, "; --bind(", bindLog, ")");
|
||||
}
|
||||
request.logSql(logSql);
|
||||
request.logSql(Str.add(sql, "; --bind(", bindLog, ")"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -458,9 +451,7 @@ public class CQueryEngine {
|
||||
private void logSql(CQuery<?> query) {
|
||||
|
||||
String sql = query.getGeneratedSql();
|
||||
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
|
||||
sql = Str.add(sql, "; --bind(", query.getBindLog(), ")");
|
||||
}
|
||||
sql = Str.add(sql, "; --bind(", query.getBindLog(), ")");
|
||||
query.getTransaction().logSql(sql);
|
||||
}
|
||||
|
||||
|
||||
@@ -1276,8 +1276,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBeanCacheReload() {
|
||||
return CacheMode.PUT == useBeanCache;
|
||||
public boolean isForceHitDatabase() {
|
||||
return forUpdate != null || CacheMode.PUT == useBeanCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -43,6 +43,9 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
|
||||
private final TransactionManager manager;
|
||||
|
||||
private final boolean logSql;
|
||||
private final boolean logSummary;
|
||||
|
||||
/**
|
||||
* The status of the transaction.
|
||||
*/
|
||||
@@ -70,6 +73,8 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
*/
|
||||
ImplicitReadOnlyTransaction(TransactionManager manager, Connection connection) {
|
||||
this.manager = manager;
|
||||
this.logSql = manager.isLogSql();
|
||||
this.logSummary = manager.isLogSummary();
|
||||
this.active = true;
|
||||
this.connection = connection;
|
||||
this.persistenceContext = new DefaultPersistenceContext();
|
||||
@@ -441,22 +446,22 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
|
||||
@Override
|
||||
public boolean isLogSql() {
|
||||
return TransactionManager.SQL_LOGGER.isDebugEnabled();
|
||||
return logSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogSummary() {
|
||||
return TransactionManager.SUM_LOGGER.isDebugEnabled();
|
||||
return logSummary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSql(String msg) {
|
||||
TransactionManager.SQL_LOGGER.debug(msg);
|
||||
manager.log().sql().debug(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSummary(String msg) {
|
||||
TransactionManager.SUM_LOGGER.debug(msg);
|
||||
manager.log().sum().debug(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -55,6 +55,9 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
*/
|
||||
protected final String id;
|
||||
|
||||
private final boolean logSql;
|
||||
private final boolean logSummary;
|
||||
|
||||
/**
|
||||
* The user defined label to group execution statistics.
|
||||
*/
|
||||
@@ -200,11 +203,15 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
this.startNanos = System.nanoTime();
|
||||
|
||||
if (manager == null) {
|
||||
this.logSql = false;
|
||||
this.logSummary = false;
|
||||
this.skipCacheAfterWrite = true;
|
||||
this.batchMode = PersistBatch.NONE;
|
||||
this.batchOnCascadeMode = PersistBatch.NONE;
|
||||
this.onQueryOnly = OnQueryOnly.ROLLBACK;
|
||||
} else {
|
||||
this.logSql = manager.isLogSql();
|
||||
this.logSummary = manager.isLogSummary();
|
||||
this.skipCacheAfterWrite = manager.isSkipCacheAfterWrite();
|
||||
this.batchMode = manager.getPersistBatch();
|
||||
this.batchOnCascadeMode = manager.getPersistBatchOnCascade();
|
||||
@@ -861,22 +868,22 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
|
||||
@Override
|
||||
public boolean isLogSql() {
|
||||
return TransactionManager.SQL_LOGGER.isDebugEnabled();
|
||||
return logSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogSummary() {
|
||||
return TransactionManager.SUM_LOGGER.isDebugEnabled();
|
||||
return logSummary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSql(String msg) {
|
||||
TransactionManager.SQL_LOGGER.debug(Str.add(logPrefix, msg));
|
||||
manager.log().sql().debug(Str.add(logPrefix, msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSummary(String msg) {
|
||||
TransactionManager.SUM_LOGGER.debug(Str.add(logPrefix, msg));
|
||||
manager.log().sum().debug(Str.add(logPrefix, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,6 +21,7 @@ class SavepointTransaction extends SpiTransactionProxy {
|
||||
private final Savepoint savepoint;
|
||||
private final Connection connection;
|
||||
private final String logPrefix;
|
||||
private final String spPrefix;
|
||||
|
||||
private boolean rollbackOnly;
|
||||
private int state;
|
||||
@@ -31,7 +32,8 @@ class SavepointTransaction extends SpiTransactionProxy {
|
||||
this.connection = transaction.getInternalConnection();
|
||||
this.savepoint = connection.setSavepoint();
|
||||
int savepointId = savepoint.getSavepointId();
|
||||
this.logPrefix = "txn[" + transaction.getId() + "-sp" + savepointId + "] ";
|
||||
this.spPrefix = "sp[" + savepointId + "] ";
|
||||
this.logPrefix = transaction.getLogPrefix() + spPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -41,12 +43,12 @@ class SavepointTransaction extends SpiTransactionProxy {
|
||||
|
||||
@Override
|
||||
public void logSql(String msg) {
|
||||
TransactionManager.SQL_LOGGER.debug(Str.add(logPrefix, msg));
|
||||
transaction.logSql(Str.add(spPrefix, msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSummary(String msg) {
|
||||
TransactionManager.SUM_LOGGER.debug(Str.add(logPrefix, msg));
|
||||
transaction.logSummary(Str.add(spPrefix, msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.Connection;
|
||||
@@ -14,8 +13,6 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
*/
|
||||
abstract class TransactionFactory {
|
||||
|
||||
static final Logger TXN_LOGGER = TransactionManager.TXN_LOGGER;
|
||||
|
||||
final AtomicLong counter = new AtomicLong(1000);
|
||||
|
||||
final TransactionManager manager;
|
||||
@@ -51,8 +48,8 @@ abstract class TransactionFactory {
|
||||
}
|
||||
}
|
||||
|
||||
if (explicit && TXN_LOGGER.isTraceEnabled()) {
|
||||
TXN_LOGGER.trace(t.getLogPrefix() + "Begin");
|
||||
if (explicit && manager.log().txn().isTrace()) {
|
||||
manager.log().txn().trace(t.getLogPrefix() + "Begin");
|
||||
}
|
||||
|
||||
return t;
|
||||
|
||||
@@ -15,6 +15,8 @@ import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.ScopeTrans;
|
||||
import io.ebeaninternal.api.ScopedTransaction;
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
import io.ebeaninternal.api.SpiLogger;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiTransactionManager;
|
||||
@@ -53,12 +55,6 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
|
||||
public static final Logger clusterLogger = LoggerFactory.getLogger("io.ebean.Cluster");
|
||||
|
||||
public static final Logger SQL_LOGGER = LoggerFactory.getLogger("io.ebean.SQL");
|
||||
|
||||
public static final Logger SUM_LOGGER = LoggerFactory.getLogger("io.ebean.SUM");
|
||||
|
||||
public static final Logger TXN_LOGGER = LoggerFactory.getLogger("io.ebean.TXN");
|
||||
|
||||
protected final BeanDescriptorManager beanDescriptorManager;
|
||||
|
||||
/**
|
||||
@@ -128,6 +124,9 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
|
||||
private final TransactionFactory transactionFactory;
|
||||
|
||||
private final SpiLogManager logManager;
|
||||
private final SpiLogger txnLogger;
|
||||
|
||||
private final DatabasePlatform databasePlatform;
|
||||
|
||||
private final SpiProfileHandler profileHandler;
|
||||
@@ -142,6 +141,8 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
*/
|
||||
public TransactionManager(TransactionManagerOptions options) {
|
||||
|
||||
this.logManager = options.logManager;
|
||||
this.txnLogger = logManager.txn();
|
||||
this.databasePlatform = options.config.getDatabasePlatform();
|
||||
this.skipCacheAfterWrite = options.config.isSkipCacheAfterWrite();
|
||||
this.notifyL2CacheInForeground = options.notifyL2CacheInForeground;
|
||||
@@ -355,12 +356,12 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
public void notifyOfRollback(SpiTransaction transaction, Throwable cause) {
|
||||
|
||||
try {
|
||||
if (TXN_LOGGER.isDebugEnabled()) {
|
||||
if (txnLogger.isDebug()) {
|
||||
String msg = transaction.getLogPrefix() + "Rollback";
|
||||
if (cause != null) {
|
||||
msg += " error: " + formatThrowable(cause);
|
||||
}
|
||||
TXN_LOGGER.debug(msg);
|
||||
txnLogger.debug(msg);
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
@@ -374,8 +375,8 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
public void notifyOfQueryOnly(SpiTransaction transaction) {
|
||||
|
||||
// Nothing that interesting here
|
||||
if (TXN_LOGGER.isTraceEnabled()) {
|
||||
TXN_LOGGER.trace(transaction.getLogPrefix() + "Commit - query only");
|
||||
if (txnLogger.isTrace()) {
|
||||
txnLogger.trace(transaction.getLogPrefix() + "Commit - query only");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,8 +410,8 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
public void notifyOfCommit(SpiTransaction transaction) {
|
||||
|
||||
try {
|
||||
if (TXN_LOGGER.isDebugEnabled()) {
|
||||
TXN_LOGGER.debug(transaction.getLogPrefix() + "Commit");
|
||||
if (txnLogger.isDebug()) {
|
||||
txnLogger.debug(transaction.getLogPrefix() + "Commit");
|
||||
}
|
||||
|
||||
PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, transaction);
|
||||
@@ -702,4 +703,16 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
throw new RuntimeException("Should never get here?");
|
||||
}
|
||||
}
|
||||
|
||||
public SpiLogManager log() {
|
||||
return logManager;
|
||||
}
|
||||
|
||||
public boolean isLogSql() {
|
||||
return logManager.sql().isDebug();
|
||||
}
|
||||
|
||||
public boolean isLogSummary() {
|
||||
return logManager.sum().isDebug();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import io.ebeaninternal.server.cluster.ClusterManager;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
@@ -22,10 +23,11 @@ public class TransactionManagerOptions {
|
||||
final DataSourceSupplier dataSourceSupplier;
|
||||
final SpiProfileHandler profileHandler;
|
||||
final TransactionScopeManager scopeManager;
|
||||
final SpiLogManager logManager;
|
||||
|
||||
public TransactionManagerOptions(boolean notifyL2CacheInForeground, ServerConfig config, TransactionScopeManager scopeManager, ClusterManager clusterManager,
|
||||
BackgroundExecutor backgroundExecutor, DocStoreUpdateProcessor docStoreUpdateProcessor,
|
||||
BeanDescriptorManager descMgr, DataSourceSupplier dataSourceSupplier, SpiProfileHandler profileHandler) {
|
||||
BeanDescriptorManager descMgr, DataSourceSupplier dataSourceSupplier, SpiProfileHandler profileHandler, SpiLogManager logManager) {
|
||||
|
||||
this.notifyL2CacheInForeground = notifyL2CacheInForeground;
|
||||
this.config = config;
|
||||
@@ -36,6 +38,7 @@ public class TransactionManagerOptions {
|
||||
this.descMgr = descMgr;
|
||||
this.dataSourceSupplier = dataSourceSupplier;
|
||||
this.profileHandler = profileHandler;
|
||||
this.logManager = logManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
/**
|
||||
* Scalar type that wraps a local/client side encrypted value.
|
||||
*/
|
||||
public interface LocalEncryptedType {
|
||||
|
||||
/**
|
||||
* Encrypt and return the un-encrypted value.
|
||||
*/
|
||||
Object localEncrypt(Object value);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class ScalarTypeEncryptedWrapper<T> implements ScalarType<T> {
|
||||
public class ScalarTypeEncryptedWrapper<T> implements ScalarType<T>, LocalEncryptedType {
|
||||
|
||||
private final ScalarType<T> wrapped;
|
||||
|
||||
@@ -23,6 +23,12 @@ public class ScalarTypeEncryptedWrapper<T> implements ScalarType<T> {
|
||||
this.dataEncryptSupport = dataEncryptSupport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object localEncrypt(Object value) {
|
||||
String formatValue = wrapped.format(value);
|
||||
return dataEncryptSupport.encryptObject(formatValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long asVersion(T value) {
|
||||
throw new RuntimeException("not supported");
|
||||
|
||||
@@ -66,6 +66,7 @@ public class ServerConfigTest {
|
||||
props.setProperty("idType", "SEQUENCE");
|
||||
props.setProperty("mappingLocations", "classpath:/foo;bar");
|
||||
props.setProperty("namingConvention", "io.ebean.config.MatchingNamingConvention");
|
||||
props.setProperty("idGeneratorAutomatic", "true");
|
||||
|
||||
|
||||
serverConfig.loadFromProperties(props);
|
||||
@@ -74,6 +75,8 @@ public class ServerConfigTest {
|
||||
assertTrue(serverConfig.isNotifyL2CacheInForeground());
|
||||
assertTrue(serverConfig.isDbOffline());
|
||||
assertTrue(serverConfig.isAutoReadOnlyDataSource());
|
||||
assertTrue(serverConfig.isIdGeneratorAutomatic());
|
||||
|
||||
assertThat(serverConfig.getNamingConvention()).isInstanceOf(MatchingNamingConvention.class);
|
||||
|
||||
assertEquals(IdType.SEQUENCE, serverConfig.getIdType());
|
||||
@@ -108,4 +111,14 @@ public class ServerConfigTest {
|
||||
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatch());
|
||||
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_defaults() {
|
||||
|
||||
ServerConfig serverConfig = new ServerConfig();
|
||||
assertTrue(serverConfig.isIdGeneratorAutomatic());
|
||||
|
||||
serverConfig.setIdGeneratorAutomatic(false);
|
||||
assertFalse(serverConfig.isIdGeneratorAutomatic());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,11 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiLogManager log() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownManaged() {
|
||||
|
||||
@@ -237,6 +242,11 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(Object bean) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(Object bean, MergeOptions options) {
|
||||
|
||||
@@ -800,6 +810,16 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] executeBatch(SpiSqlUpdate defaultSqlUpdate, SpiTransaction transaction) {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int execute(CallableSql callableSql, Transaction t) {
|
||||
return 0;
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.plugin.Property;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Animal;
|
||||
import org.tests.model.basic.AnimalShelter;
|
||||
import org.tests.model.basic.Cat;
|
||||
import org.tests.model.basic.Contact;
|
||||
import org.tests.model.basic.Country;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.Dog;
|
||||
import org.tests.model.basic.Order;
|
||||
import org.tests.model.bridge.BSite;
|
||||
import org.tests.model.bridge.BUser;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -13,7 +22,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class BeanDescriptorTest extends BaseTestCase {
|
||||
|
||||
BeanDescriptor<Customer> customerDesc = spiEbeanServer().getBeanDescriptor(Customer.class);
|
||||
private BeanDescriptor<Customer> customerDesc = spiEbeanServer().getBeanDescriptor(Customer.class);
|
||||
|
||||
@Test
|
||||
public void createReference() {
|
||||
@@ -47,6 +56,29 @@ public class BeanDescriptorTest extends BaseTestCase {
|
||||
assertThat(server().getBeanState(bean).isDisableLazyLoad()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createReference_with_inheritance() {
|
||||
Cat cat = new Cat();
|
||||
cat.setName("Puss");
|
||||
Ebean.save(cat);
|
||||
|
||||
Dog dog = new Dog();
|
||||
dog.setRegistrationNumber("DOGGIE");
|
||||
Ebean.save(dog);
|
||||
|
||||
AnimalShelter shelter = new AnimalShelter();
|
||||
shelter.setName("My Animal Shelter");
|
||||
shelter.getAnimals().add(cat);
|
||||
shelter.getAnimals().add(dog);
|
||||
|
||||
Ebean.save(shelter);
|
||||
|
||||
BeanDescriptor<Animal> animalDesc = spiEbeanServer().getBeanDescriptor(Animal.class);
|
||||
|
||||
Animal bean = animalDesc.createReference(Boolean.FALSE, false, dog.getId(), null);
|
||||
assertThat(bean.getId()).isEqualTo(dog.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allProperties() {
|
||||
|
||||
@@ -70,4 +102,37 @@ public class BeanDescriptorTest extends BaseTestCase {
|
||||
assertThat(to.getName()).isEqualTo("rob");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isIdTypeExternal_when_externalId() {
|
||||
|
||||
BeanDescriptor<Country> countryDesc = spiEbeanServer().getBeanDescriptor(Country.class);
|
||||
assertThat(countryDesc.isIdGeneratedValue()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isIdTypeExternal_when_platformGenerator_noGeneratedValueAnnotation() {
|
||||
|
||||
assertThat(customerDesc.isIdGeneratedValue()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isIdTypeExternal_when_explicitGeneratedValue() {
|
||||
|
||||
BeanDescriptor<Contact> desc = spiEbeanServer().getBeanDescriptor(Contact.class);
|
||||
assertThat(desc.isIdGeneratedValue()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isIdTypeExternal_when_uuidGenerator_and_generatedValue() {
|
||||
|
||||
BeanDescriptor<BSite> desc = spiEbeanServer().getBeanDescriptor(BSite.class);
|
||||
assertThat(desc.isIdGeneratedValue()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isIdTypeExternal_when_uuidGenerator_and_noGeneratedValue() {
|
||||
|
||||
BeanDescriptor<BUser> desc = spiEbeanServer().getBeanDescriptor(BUser.class);
|
||||
assertThat(desc.isIdGeneratedValue()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package io.ebeantest;
|
||||
|
||||
import io.ebeaninternal.api.SpiLogger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Capture the log messages (executed SQL) for testing.
|
||||
*/
|
||||
class CaptureLogger implements SpiLogger {
|
||||
|
||||
private final SpiLogger wrapped;
|
||||
|
||||
private List<String> messages = new ArrayList<>();
|
||||
|
||||
private boolean active;
|
||||
|
||||
CaptureLogger(SpiLogger wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebug() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrace() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String msg) {
|
||||
if (active) {
|
||||
messages.add(msg);
|
||||
}
|
||||
wrapped.debug(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String msg) {
|
||||
if (active) {
|
||||
messages.add(msg);
|
||||
}
|
||||
wrapped.trace(msg);
|
||||
}
|
||||
|
||||
List<String> start() {
|
||||
this.active = true;
|
||||
return collect();
|
||||
}
|
||||
|
||||
List<String> stop() {
|
||||
this.active = false;
|
||||
return collect();
|
||||
}
|
||||
|
||||
List<String> collect() {
|
||||
List<String> response = messages;
|
||||
messages = new ArrayList<>();
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package io.ebeantest;
|
||||
|
||||
import io.ebeaninternal.api.SpiLogger;
|
||||
import io.ebeaninternal.api.SpiLoggerFactory;
|
||||
import io.ebeaninternal.server.logger.DSpiLogger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Create a logger that captures the SQL and register it for later access in tests.
|
||||
*/
|
||||
public class CapturingLoggerFactory implements SpiLoggerFactory {
|
||||
|
||||
@Override
|
||||
public SpiLogger create(String name) {
|
||||
|
||||
DSpiLogger logger = new DSpiLogger(LoggerFactory.getLogger(name));
|
||||
if (name.equals("io.ebean.SQL")) {
|
||||
return LoggedSql.register(logger);
|
||||
}
|
||||
return logger;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package io.ebeantest;
|
||||
|
||||
import io.ebeaninternal.api.SpiLogger;
|
||||
import io.ebeaninternal.server.logger.DSpiLogger;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LoggedSql {
|
||||
|
||||
private static CaptureLogger sqlLogger;
|
||||
|
||||
static SpiLogger register(DSpiLogger logger) {
|
||||
if (sqlLogger == null) {
|
||||
sqlLogger = new CaptureLogger(logger);
|
||||
}
|
||||
return sqlLogger;
|
||||
}
|
||||
|
||||
public static List<String> start() {
|
||||
return sqlLogger.start();
|
||||
}
|
||||
|
||||
public static List<String> stop() {
|
||||
return sqlLogger.stop();
|
||||
}
|
||||
|
||||
public static List<String> collect() {
|
||||
return sqlLogger.collect();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +1,7 @@
|
||||
package org.ebeantest;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.UnsynchronizedAppenderBase;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import io.ebeantest.LoggedSql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -20,28 +14,11 @@ import java.util.List;
|
||||
*/
|
||||
public class LoggedSqlCollector {
|
||||
|
||||
static Logger logger = (Logger) LoggerFactory.getLogger("io.ebean.SQL");
|
||||
|
||||
private static BasicAppender basicAppender = new BasicAppender();
|
||||
|
||||
static {
|
||||
|
||||
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
|
||||
basicAppender.setContext(lc);
|
||||
Level level = logger.getEffectiveLevel();
|
||||
if (level.isGreaterOrEqual(Level.INFO)) {
|
||||
logger.setAdditive(false);
|
||||
}
|
||||
logger.addAppender(basicAppender);
|
||||
logger.setLevel(Level.TRACE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start collection of the logged SQL statements.
|
||||
*/
|
||||
public static List<String> start() {
|
||||
return basicAppender.collectStart();
|
||||
return LoggedSql.start();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,54 +26,11 @@ public class LoggedSqlCollector {
|
||||
* the SQL.
|
||||
*/
|
||||
public static List<String> stop() {
|
||||
return basicAppender.collectEnd();
|
||||
return LoggedSql.stop();
|
||||
}
|
||||
|
||||
public static List<String> current() {
|
||||
return basicAppender.collectContinue();
|
||||
return LoggedSql.collect();
|
||||
}
|
||||
|
||||
private static class BasicAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
|
||||
|
||||
List<String> messages = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected void append(ILoggingEvent eventObject) {
|
||||
if (started) {
|
||||
messages.add(eventObject.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start collection.
|
||||
*/
|
||||
List<String> collectStart() {
|
||||
List<String> tempMessages = messages;
|
||||
messages = new ArrayList<>();
|
||||
// set started flag
|
||||
start();
|
||||
return tempMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* End collection.
|
||||
*/
|
||||
List<String> collectEnd() {
|
||||
// set stopped state
|
||||
stop();
|
||||
List<String> tempMessages = messages;
|
||||
messages = new ArrayList<>();
|
||||
return tempMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the collected SQL and continue.
|
||||
*/
|
||||
List<String> collectContinue() {
|
||||
List<String> tempMessages = messages;
|
||||
messages = new ArrayList<>();
|
||||
return tempMessages;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,10 @@ import io.ebean.Query;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.annotation.ForPlatform;
|
||||
import io.ebean.annotation.Platform;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.EBasic;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
|
||||
import java.util.List;
|
||||
@@ -40,6 +42,42 @@ public class TestQueryForUpdate extends BaseTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ForPlatform({
|
||||
Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL
|
||||
})
|
||||
public void testForUpdate_when_alreadyInPC() {
|
||||
|
||||
EBasic basic = new EBasic("test PC cache");
|
||||
Ebean.save(basic);
|
||||
|
||||
try (Transaction transaction = Ebean.beginTransaction()) {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
EBasic basic0 = Ebean.find(EBasic.class, basic.getId());
|
||||
assertThat(basic0).isNotNull();
|
||||
|
||||
EBasic basic1 = Ebean.find(EBasic.class)
|
||||
.setId( basic.getId())
|
||||
.forUpdate()
|
||||
.findOne();
|
||||
|
||||
assertThat(basic1).isNotNull();
|
||||
assertThat(basic1).isSameAs(basic0);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(2);
|
||||
if (isH2() || isPostgres()) {
|
||||
assertThat(sql.get(0)).contains("from e_basic t0 where t0.id =");
|
||||
assertThat(sql.get(1)).contains("from e_basic t0 where t0.id =");
|
||||
assertThat(sql.get(1)).contains("for update");
|
||||
}
|
||||
|
||||
transaction.end();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ForPlatform({
|
||||
Platform.H2, Platform.POSTGRES, Platform.SQLSERVER, Platform.ORACLE
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.tests.basic.encrypt;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.EBasicEncryptClient;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestEncryptClientSide extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void insertUpdate() {
|
||||
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
EBasicEncryptClient bean = new EBasicEncryptClient();
|
||||
bean.setDescription("hello");
|
||||
bean.setStatus(EBasicEncryptClient.Status.ONE);
|
||||
bean.setDob(today);
|
||||
|
||||
Ebean.save(bean);
|
||||
|
||||
EBasicEncryptClient found = Ebean.find(EBasicEncryptClient.class)
|
||||
.where()
|
||||
.eq("description", "hello")
|
||||
.eq("status", EBasicEncryptClient.Status.ONE)
|
||||
.eq("dob", today)
|
||||
.findOne();
|
||||
|
||||
assertThat(found).isNotNull();
|
||||
assertThat(found.getDescription()).isEqualTo("hello");
|
||||
assertThat(found.getStatus()).isEqualTo(EBasicEncryptClient.Status.ONE);
|
||||
assertThat(found.getDob()).isEqualTo(today);
|
||||
|
||||
|
||||
found.setDescription("goodbye");
|
||||
found.setStatus(EBasicEncryptClient.Status.TWO);
|
||||
Ebean.save(found);
|
||||
|
||||
found = Ebean.find(EBasicEncryptClient.class)
|
||||
.where()
|
||||
.eq("description", "goodbye")
|
||||
.eq("status", EBasicEncryptClient.Status.TWO)
|
||||
.eq("dob", today)
|
||||
.findOne();
|
||||
|
||||
assertThat(found).isNotNull();
|
||||
assertThat(found.getDescription()).isEqualTo("goodbye");
|
||||
|
||||
Ebean.delete(found);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.tests.ddl;
|
||||
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
public class DfkNoneViaMtoM {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
String name;
|
||||
|
||||
/**
|
||||
* Have no Foreign key constraints on the intersection table.
|
||||
*/
|
||||
@ManyToMany(cascade = CascadeType.ALL)
|
||||
@DbForeignKey(noConstraint = true)
|
||||
List<DfkOne> ones = new ArrayList<>();
|
||||
|
||||
public DfkNoneViaMtoM(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<DfkOne> getOnes() {
|
||||
return ones;
|
||||
}
|
||||
|
||||
public void setOnes(List<DfkOne> ones) {
|
||||
this.ones = ones;
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,40 @@ public class TestForeignKeyModes extends BaseTestCase {
|
||||
assertThat(found.getOne()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noneViaManyToMany() {
|
||||
|
||||
DfkOne one = Ebean.getReference(DfkOne.class, 999L);
|
||||
|
||||
DfkNoneViaMtoM none = new DfkNoneViaMtoM("none2");
|
||||
none.getOnes().add(one);
|
||||
|
||||
// Would normally fail as DfkOne id:999 is not actually in Database
|
||||
// and with the foreign key constraint on the intersection table the
|
||||
// insert into the intersection table would fail
|
||||
Ebean.save(none);
|
||||
|
||||
DfkNoneViaMtoM found = Ebean.find(DfkNoneViaMtoM.class)
|
||||
.setId(none.getId())
|
||||
.fetch("ones", "id")
|
||||
.findOne();
|
||||
|
||||
assertThat(found).isNotNull();
|
||||
|
||||
// but we can't actually fetch it via ORM M2M ... as it joins across
|
||||
// to DfkOne and that doesn't exist
|
||||
assertThat(found.getOnes()).isNotNull();
|
||||
assertThat(found.getOnes()).hasSize(0);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
Ebean.delete(none);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from dfk_none_via_mto_m_dfk_one where dfk_none_via_mto_m_id = ?");
|
||||
assertThat(sql.get(1)).contains("delete from dfk_none_via_mto_m where id=?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setNullOnDelete() {
|
||||
|
||||
@@ -63,7 +97,6 @@ public class TestForeignKeyModes extends BaseTestCase {
|
||||
DfkSetNull found = Ebean.find(DfkSetNull.class, other.getId());
|
||||
assertThat(found).isNotNull();
|
||||
assertThat(found.getOne()).isNull();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.tests.inheritance;
|
||||
|
||||
import javax.persistence.DiscriminatorColumn;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import io.ebean.annotation.Index;
|
||||
|
||||
/**
|
||||
* Model class to reference an organization node.
|
||||
*
|
||||
* @author Christian Hartl, FOCONIS AG
|
||||
*/
|
||||
@Entity
|
||||
@MappedSuperclass
|
||||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
|
||||
@DiscriminatorColumn(name = "kind")
|
||||
@Index(unique = false, columnNames = "kind")
|
||||
public abstract class OrganizationNode {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@OneToOne(cascade = {})
|
||||
@NotNull
|
||||
private OrganizationTreeNode parentTreeNode;
|
||||
|
||||
public OrganizationTreeNode getParentTreeNode() {
|
||||
return parentTreeNode;
|
||||
}
|
||||
|
||||
public void setParentTreeNode(OrganizationTreeNode parentTreeNode) {
|
||||
this.parentTreeNode = parentTreeNode;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.tests.inheritance;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import io.ebean.annotation.PrivateOwned;
|
||||
|
||||
/**
|
||||
* Model class to reference an organization tree node.
|
||||
*
|
||||
* @author Christian Hartl, FOCONIS AG
|
||||
*/
|
||||
@Entity
|
||||
public class OrganizationTreeNode {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "parentTreeNode")
|
||||
@NotNull
|
||||
@PrivateOwned
|
||||
private OrganizationNode organizationNode;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public OrganizationNode getOrganizationNode() {
|
||||
return organizationNode;
|
||||
}
|
||||
|
||||
public void setOrganizationNode(OrganizationNode organizationNode) {
|
||||
this.organizationNode = organizationNode;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.tests.inheritance;
|
||||
|
||||
import javax.persistence.DiscriminatorValue;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
@Entity
|
||||
@DiscriminatorValue("Unit")
|
||||
public class OrganizationUnit extends OrganizationNode {
|
||||
|
||||
private String title;
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.tests.inheritance;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestTreeOrganisations extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
OrganizationTreeNode treeNode = new OrganizationTreeNode();
|
||||
treeNode.setName("tree");
|
||||
|
||||
OrganizationUnit node = new OrganizationUnit();
|
||||
node.setTitle("node");
|
||||
|
||||
treeNode.setOrganizationNode(node);
|
||||
Ebean.save(treeNode);
|
||||
|
||||
treeNode = Ebean.find(OrganizationTreeNode.class, treeNode.getId());
|
||||
assertEquals(node, treeNode.getOrganizationNode());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.tests.merge;
|
||||
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.Version;
|
||||
@@ -8,7 +9,7 @@ import java.util.UUID;
|
||||
@MappedSuperclass
|
||||
public class MBase {
|
||||
|
||||
@Id
|
||||
@Id @GeneratedValue
|
||||
private UUID id;
|
||||
|
||||
@Version
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.tests.merge;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
@Entity
|
||||
public class MGroup {
|
||||
|
||||
@Id
|
||||
private long id;
|
||||
|
||||
private String name;
|
||||
|
||||
public MGroup(long id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.tests.merge;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.Version;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
public class MMachine {
|
||||
|
||||
@Id
|
||||
private long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@ManyToMany
|
||||
private List<MGroup> groups = new ArrayList<>();
|
||||
|
||||
@Version
|
||||
private long version;
|
||||
|
||||
public MMachine(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public List<MGroup> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
|
||||
public void setGroups(List<MGroup> groups) {
|
||||
this.groups = groups;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,21 @@ public class TestMergeCustomer extends BaseTestCase {
|
||||
|
||||
private Random random = new Random();
|
||||
|
||||
@Test
|
||||
public void customerOnly_defaultOptions_expect_updateOnly() {
|
||||
|
||||
MCustomer mCustomer = partial("cust1", "(id,name,version)");
|
||||
mCustomer.setName("NotCust0");
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Ebean.merge(mCustomer);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("update mcustomer set name=?, version=? where id=? and version=?");
|
||||
}
|
||||
|
||||
/**
|
||||
* So this is effectively the same as a stateless update.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.tests.merge;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.MergeOptions;
|
||||
import io.ebean.MergeOptionsBuilder;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestMergeM2M extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void m2mMerge() {
|
||||
|
||||
MGroup group1 = new MGroup(1, "gone");
|
||||
MGroup group2 = new MGroup(2, "gtwo");
|
||||
MGroup group3 = new MGroup(3, "gthree");
|
||||
MGroup group4 = new MGroup(4, "gfour");
|
||||
MGroup group5 = new MGroup(5, "gfive");
|
||||
|
||||
Ebean.save(group1);
|
||||
Ebean.save(group2);
|
||||
Ebean.save(group3);
|
||||
Ebean.save(group4);
|
||||
Ebean.save(group5);
|
||||
|
||||
MMachine machine = new MMachine("mac1");
|
||||
machine.getGroups().add(group1);
|
||||
machine.getGroups().add(group2);
|
||||
machine.getGroups().add(group3);
|
||||
|
||||
MergeOptions options = new MergeOptionsBuilder().addPath("groups").build();
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Ebean.merge(machine, options);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(3);
|
||||
assertThat(sql.get(0)).contains("select");
|
||||
assertThat(sql.get(1)).contains("insert into mmachine");
|
||||
assertThat(sql.get(2)).contains("insert into mmachine_mgroup");
|
||||
|
||||
machine.setName("mac1-mod");
|
||||
machine.getGroups().remove(group2);
|
||||
machine.getGroups().remove(group3);
|
||||
machine.getGroups().add(group4);
|
||||
machine.getGroups().add(group5);
|
||||
|
||||
Ebean.merge(machine, options);
|
||||
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(4);
|
||||
assertThat(sql.get(0)).contains("select");
|
||||
assertThat(sql.get(1)).contains("delete from mmachine_mgroup");
|
||||
assertThat(sql.get(2)).contains("insert into mmachine_mgroup");
|
||||
assertThat(sql.get(3)).contains("update mmachine");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import io.ebean.annotation.Index;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
@@ -25,7 +26,7 @@ import java.util.List;
|
||||
@Cache(naturalKey = "email")
|
||||
public class Contact {
|
||||
|
||||
@Id
|
||||
@Id @GeneratedValue
|
||||
int id;
|
||||
|
||||
@Size(max=127)
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package org.tests.model.basic;
|
||||
|
||||
import io.ebean.annotation.Encrypted;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Version;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "e_basicenc_client")
|
||||
public class EBasicEncryptClient {
|
||||
|
||||
public enum Status {
|
||||
ONE,
|
||||
TWO
|
||||
}
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
String name;
|
||||
|
||||
@Encrypted(dbLength = 80, dbEncryption = false)
|
||||
String description;
|
||||
|
||||
@Encrypted(dbLength = 20, dbEncryption = false)
|
||||
LocalDate dob;
|
||||
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Encrypted(dbLength = 20, dbEncryption = false)
|
||||
Status status;
|
||||
|
||||
@Version
|
||||
long version;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public LocalDate getDob() {
|
||||
return dob;
|
||||
}
|
||||
|
||||
public void setDob(LocalDate dob) {
|
||||
this.dob = dob;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
package org.tests.model.bridge;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
public class BSite {
|
||||
|
||||
@Id
|
||||
@Id @GeneratedValue
|
||||
UUID id;
|
||||
|
||||
String name;
|
||||
|
||||
@@ -7,7 +7,9 @@ import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.Transient;
|
||||
import javax.persistence.Version;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -25,6 +27,9 @@ public class EcmPerson {
|
||||
@Column(name = "number", length = 10)
|
||||
Map<String, String> phoneNumbers = new LinkedHashMap<>();
|
||||
|
||||
@Transient
|
||||
Map<String, String> transientPhoneNumbers;
|
||||
|
||||
@Version
|
||||
long version;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.ebean.Ebean;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -137,6 +138,7 @@ public class TestElementCollectionBasicMap extends BaseTestCase {
|
||||
}
|
||||
|
||||
private void jsonToFrom(EcmPerson foundFirst) {
|
||||
foundFirst.transientPhoneNumbers = new HashMap<>();
|
||||
String asJson = Ebean.json().toJson(foundFirst);
|
||||
EcmPerson fromJson = Ebean.json().toBean(EcmPerson.class, asJson);
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import io.ebeaninternal.api.SpiTransaction;
|
||||
import org.junit.Test;
|
||||
import org.tests.idkeys.db.AuditLog;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class TestInsertSqlLogging extends BaseTestCase {
|
||||
@@ -39,6 +41,38 @@ public class TestInsertSqlLogging extends BaseTestCase {
|
||||
insert.setNextParameter("bob");
|
||||
insert.execute();
|
||||
|
||||
txn.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addBatch_executeBatch() {
|
||||
|
||||
Ebean.find(AuditLog.class).where().ge("id", 10000).delete();
|
||||
|
||||
String sql = "insert into audit_log (id, description, modified_description) values (?,?,?)";
|
||||
SqlUpdate insert = Ebean.createSqlUpdate(sql);
|
||||
|
||||
try (Transaction txn = Ebean.beginTransaction()) {
|
||||
|
||||
insert.setNextParameter(10000);
|
||||
insert.setNextParameter("hello");
|
||||
insert.setNextParameter("rob");
|
||||
insert.addBatch();
|
||||
|
||||
insert.setNextParameter(10001);
|
||||
insert.setNextParameter("goodbye");
|
||||
insert.setNextParameter("rob");
|
||||
insert.addBatch();
|
||||
|
||||
insert.setNextParameter(10002);
|
||||
insert.setNextParameter("chow");
|
||||
insert.setNextParameter("bob");
|
||||
insert.addBatch();
|
||||
|
||||
int[] rows = insert.executeBatch();
|
||||
System.out.println("Rows was " + Arrays.toString(rows));
|
||||
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
io.ebeantest.CapturingLoggerFactory
|
||||
Reference in New Issue
Block a user