Compare commits

..
Author SHA1 Message Date
Rob Bygrave 65ba231fc0 [maven-release-plugin] prepare release ebean-11.1.1 2017-10-03 23:20:35 +13:00
Rob Bygrave 1da2382e45 No effective change - javadoc only 2017-10-03 15:38:17 +13:00
Rob Bygrave a86af94a8c Reduce logging level only - no functional change 2017-10-03 15:35:21 +13:00
Rob Bygrave 8301cb634e #1027 - ENH: Add a Slow query event listener ... to log or listen to queries that exceed some defined execution time 2017-10-03 15:04:30 +13:00
Rob Bygrave 721654d033 #1140 - Error loading embedded instance
The issue was due to the loaded status not being recursively set on embedded
beans after inserts. The test case invoked lazy loading due to that which
lead to the issue.
2017-10-03 12:47:29 +13:00
Rob Bygrave fe1817ef02 No effective change - update test 2017-10-03 12:34:56 +13:00
Rob Bygrave fc996659f2 #1139 - NoSuchMethodError: javax.persistence.Embedded.prefix()Ljava/lang/String; 2017-10-03 11:02:38 +13:00
rob bygrave 368d22baa6 #1145 - Refactor @ChangeLog based on JSON documents for 'new values' and 'old values' 2017-10-03 01:31:37 +13:00
rob bygrave daf3d97266 #1144 - Refactor internals - move generated property setting into PersistRequestBean 2017-10-03 00:30:34 +13:00
rob bygrave f2179fca2d #1123 - 11.1.1 API change - moved PersistBatch, Platform, TxIsolation and TxType to io.ebean.annotation package 2017-09-15 21:48:53 +12:00
rob bygrave 2c436c3af5 #1123 - 11.1.1 API change - moved PersistBatch, Platform, TxIsolation and TxType to io.ebean.annotation package 2017-09-15 21:48:37 +12:00
Roland PramlandRob Bygrave 8d334ff2e8 Feature/contitional test runner (#1132)
* FIX: warnings (add generics, overrides, fix imports) - should be no effecive code change

* replaced deprecated methods by default methods

* FIX: more compiler warnings

* FIX more warnings - no code changes

* FIX: deprecated call to JsonParseException

* Remove unused code, check unused variables

* ADD: ContitonaltestRunner to skip tests for certain platforms

* Use annotations to control on which platforms a test shoud run

* FIX: compile errors
2017-09-15 21:03:03 +12:00
rob bygrave f3e4696498 [maven-release-plugin] prepare for next development iteration 2017-09-14 01:03:21 +12:00
rob bygrave 221260fa44 [maven-release-plugin] prepare release ebean-10.4.7 2017-09-14 01:03:07 +12:00
rob bygrave f8e7a79e9a #1131 - Migration broken in 10.4.6 due different classpath-scanner version 2017-09-14 00:56:37 +12:00
Roland PramlandRob Bygrave b66f18bfd6 Fix warnings 2 (#1130)
* FIX: warnings (add generics, overrides, fix imports) - should be no effecive code change

* replaced deprecated methods by default methods

* FIX: more compiler warnings

* FIX more warnings - no code changes

* FIX: deprecated call to JsonParseException

* Remove unused code, check unused variables
2017-09-14 00:32:48 +12:00
Roland PramlandRob Bygrave ac6708e0d0 Fix warnings (#1129)
* FIX: warnings (add generics, overrides, fix imports) - should be no effecive code change

* replaced deprecated methods by default methods

* FIX: more compiler warnings

* FIX more warnings - no code changes
2017-09-14 00:29:01 +12:00
rob bygrave a5f46c095d [maven-release-plugin] prepare for next development iteration 2017-09-13 23:02:39 +12:00
190 changed files with 1754 additions and 1216 deletions
+6 -6
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>10.4.6</version>
<version>11.1.1</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -17,7 +17,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-10.4.6</tag>
<tag>ebean-11.1.1</tag>
</scm>
<profiles>
@@ -87,7 +87,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>2.4</version>
<version>3.1</version>
</dependency>
<dependency>
@@ -117,7 +117,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>10.2.1</version>
<version>10.3.1</version>
</dependency>
<dependency>
@@ -210,7 +210,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-agent</artifactId>
<version>10.4.1</version>
<version>11.1.1</version>
<scope>test</scope>
</dependency>
@@ -283,7 +283,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>10.4.1</version>
<version>11.1.1</version>
<executions>
<execution>
<id>test</id>
+1
View File
@@ -1,5 +1,6 @@
package io.ebean;
import io.ebean.annotation.TxIsolation;
import io.ebean.cache.ServerCacheManager;
import io.ebean.config.ServerConfig;
import io.ebean.text.csv.CsvReader;
+10 -5
View File
@@ -1,5 +1,6 @@
package io.ebean;
import io.ebean.annotation.TxIsolation;
import io.ebean.cache.ServerCacheManager;
import io.ebean.config.ServerConfig;
import io.ebean.meta.MetaInfoManager;
@@ -561,8 +562,8 @@ public interface EbeanServer {
* Start a transaction typically specifying REQUIRES_NEW or REQUIRED semantics.
* <p>
* <p>
* Note that this provides an try finally alternative to using {@link #execute(TxScope, TxCallable)} or
* {@link #execute(TxScope, TxRunnable)}.
* Note that this provides an try finally alternative to using {@link #executeCall(TxScope, Callable)} or
* {@link #execute(TxScope, Runnable)}.
* </p>
* <p>
* <h3>REQUIRES_NEW example:</h3>
@@ -1668,10 +1669,12 @@ public interface EbeanServer {
* Deprecated - please migrate to executeCall().
*/
@Deprecated
<T> T execute(TxScope scope, TxCallable<T> callable);
default <T> T execute(TxScope scope, TxCallable<T> callable) {
return executeCall(scope, callable);
}
/**
* Execute a Callable in a Transaction with the default scope.
* Execute a TxCallable in a Transaction with the default scope.
* <p>
* The default scope runs with REQUIRED and by default will rollback on any
* exception (checked or runtime).
@@ -1702,7 +1705,9 @@ public interface EbeanServer {
* Deprecated - please migrate to executeCall().
*/
@Deprecated
<T> T execute(TxCallable<T> callable);
default <T> T execute(TxCallable<T> callable) {
return executeCall(null, callable);
}
/**
* Return the manager of the server cache ("L2" cache).
+1
View File
@@ -1,6 +1,7 @@
package io.ebean;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
import io.ebean.config.DocStoreConfig;
import io.ebean.config.ServerConfig;
+1
View File
@@ -46,5 +46,6 @@ public interface TxCallable<T> extends Callable<T> {
* instead.
* </p>
*/
@Override
T call();
}
+1
View File
@@ -37,5 +37,6 @@ public interface TxRunnable extends Runnable {
/**
* Run the method in a transaction sope.
*/
@Override
void run();
}
+30 -7
View File
@@ -1,24 +1,27 @@
package io.ebean;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.TxIsolation;
import io.ebean.annotation.TxType;
import java.util.ArrayList;
import java.util.concurrent.Callable;
/**
* Holds the definition of how a transactional method should run.
* <p>
* This information matches the features of the Transactional annotation. You
* can use it directly with TxRunnable or TxCallable via
* {@link Ebean#execute(TxScope, TxCallable)} or
* {@link Ebean#execute(TxScope, TxRunnable)}.
* can use it directly with Runnable or Callable via
* {@link Ebean#execute(TxScope, Runnable)} or
* {@link Ebean#executeCall(TxScope, Callable)}.
* </p>
* <p>
* This object is used internally with the enhancement of a method with
* Transactional annotation.
* </p>
*
* @see TxCallable
* @see TxRunnable
* @see Ebean#execute(TxScope, TxCallable)
* @see Ebean#execute(TxScope, TxRunnable)
* @see Ebean#execute(TxScope, Runnable)
* @see Ebean#executeCall(TxScope, Callable)
*/
public final class TxScope {
@@ -38,6 +41,11 @@ public final class TxScope {
boolean readOnly;
/**
* Set this to false if the JDBC batch should not be automatically be flushed when a query is executed.
*/
boolean flushOnQuery = true;
ArrayList<Class<? extends Throwable>> rollbackFor;
ArrayList<Class<? extends Throwable>> noRollbackFor;
@@ -235,6 +243,21 @@ public final class TxScope {
return this;
}
/**
* Return false if the JDBC batch buffer should not be flushed automatically when a query is executed.
*/
public boolean isFlushOnQuery() {
return flushOnQuery;
}
/**
* Set flushOnQuery to be false to stop automatically flushing the JDBC batch buffer when a query is executed.
*/
public TxScope setFlushOnQuery(boolean flushOnQuery) {
this.flushOnQuery = flushOnQuery;
return this;
}
/**
* Return the Isolation level this transaction should run with.
*/
@@ -0,0 +1,22 @@
package io.ebean.bean;
/**
* Visitor for collecting new/old values for a bean update.
*/
public interface BeanDiffVisitor {
/**
* Collect a new/old value pair.
*/
void visit(int position, Object newVal, Object oldVal);
/**
* Start processing an associated bean.
*/
void visitPush(int position);
/**
* Stop processing an associated bean.
*/
void visitPop();
}
@@ -507,6 +507,15 @@ public final class EntityBeanIntercept implements Serializable {
loadedProps[propertyIndex] = true;
}
/**
* Set all properties to be loaded (post insert).
*/
public void setLoadedPropertyAll() {
for (int i = 0; i < loadedProps.length; i++) {
loadedProps[i] = true;
}
}
/**
* Return true if the property is loaded.
*/
@@ -694,6 +703,28 @@ public final class EntityBeanIntercept implements Serializable {
}
}
/**
* Recursively add dirty properties.
*/
public void addDirtyPropertyValues(BeanDiffVisitor visitor) {
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if (changedProps != null && changedProps[i]) {
// the property has been changed on this bean
Object newVal = owner._ebean_getField(i);
Object oldVal = getOrigValue(i);
visitor.visit(i, newVal, oldVal);
} else if (embeddedDirty != null && embeddedDirty[i]) {
// an embedded property has been changed - recurse
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
visitor.visitPush(i);
embeddedBean._ebean_getIntercept().addDirtyPropertyValues(visitor);
visitor.visitPop();
}
}
}
/**
* Return a dirty property hash taking into account embedded beans.
*/
@@ -1124,4 +1155,11 @@ public final class EntityBeanIntercept implements Serializable {
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* Explicitly set an old value.
*/
public void setOldValue(int propertyIndex,Object oldValue) {
setChangedPropertyValue(propertyIndex, true, oldValue);
}
}
@@ -1,7 +1,7 @@
package io.ebean.config;
import io.ebean.config.dbplatform.DbType;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
/**
* Custom mappings for DB types that override the default.
@@ -1,6 +1,6 @@
package io.ebean.config;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.migration.MigrationConfig;
import io.ebean.migration.MigrationRunner;
import org.slf4j.Logger;
@@ -2,7 +2,7 @@ package io.ebean.config;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import java.util.ArrayList;
import java.util.List;
@@ -2,9 +2,9 @@ package io.ebean.config;
import com.fasterxml.jackson.core.JsonFactory;
import io.ebean.EbeanServerFactory;
import io.ebean.PersistBatch;
import io.ebean.annotation.PersistBatch;
import io.ebean.PersistenceContextScope;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.Query;
import io.ebean.Transaction;
import io.ebean.annotation.Encrypted;
@@ -439,6 +439,16 @@ public class ServerConfig {
*/
private boolean disableL2Cache;
/**
* The time in millis used to determine when a query is alerted for being slow.
*/
private long slowQueryMillis;
/**
* The listener for processing slow query events.
*/
private SlowQueryListener slowQueryListener;
/**
* Construct a Server Configuration for programmatically creating an EbeanServer.
*/
@@ -446,6 +456,34 @@ public class ServerConfig {
}
/**
* Return the slow query time in millis.
*/
public long getSlowQueryMillis() {
return slowQueryMillis;
}
/**
* Set the slow query time in millis.
*/
public void setSlowQueryMillis(long slowQueryMillis) {
this.slowQueryMillis = slowQueryMillis;
}
/**
* Return the slow query event listener.
*/
public SlowQueryListener getSlowQueryListener() {
return slowQueryListener;
}
/**
* Set the slow query event listener.
*/
public void setSlowQueryListener(SlowQueryListener slowQueryListener) {
this.slowQueryListener = slowQueryListener;
}
/**
* Put a service object into configuration such that it can be passed to a plugin.
* <p>
@@ -2534,6 +2572,7 @@ public class ServerConfig {
dbTypeConfig.setGeometrySRID(srid);
}
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
@@ -0,0 +1,59 @@
package io.ebean.config;
import io.ebean.bean.ObjectGraphNode;
/**
* Slow query event.
*/
public class SlowQueryEvent {
private final String sql;
private final long timeMillis;
private final int rowCount;
private final ObjectGraphNode originNode;
/**
* Construct with the SQL and execution time in millis.
*/
public SlowQueryEvent(String sql, long timeMillis, int rowCount, ObjectGraphNode originNode) {
this.sql = sql;
this.timeMillis = timeMillis;
this.rowCount = rowCount;
this.originNode = originNode;
}
/**
* Return the SQL for the slow query.
*/
public String getSql() {
return sql;
}
/**
* Return the execution time in millis.
*/
public long getTimeMillis() {
return timeMillis;
}
/**
* Return the total row count associated with the query.
*/
public int getRowCount() {
return rowCount;
}
/**
* Return the origin point for the root query.
* <p>
* Typically the <code>originNode.getOriginQueryPoint().getFirstStackElement()</code> provides the stack line that
* shows the code that invoked the query.
* </p>
*/
public ObjectGraphNode getOriginNode() {
return originNode;
}
}
@@ -0,0 +1,13 @@
package io.ebean.config;
/**
* Listener for slow query events.
*/
@FunctionalInterface
public interface SlowQueryListener {
/**
* Process a slow query event.
*/
void process(SlowQueryEvent event);
}
@@ -4,8 +4,8 @@ import io.ebean.BackgroundExecutor;
import io.ebean.Query;
import io.ebean.config.CustomDbTypeMapping;
import io.ebean.config.DbTypeConfig;
import io.ebean.PersistBatch;
import io.ebean.Platform;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.config.ServerConfig;
import io.ebean.dbmigration.ddlgeneration.DdlHandler;
import io.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
@@ -1,8 +1,8 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.BackgroundExecutor;
import io.ebean.PersistBatch;
import io.ebean.Platform;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
@@ -44,7 +44,7 @@ public class DB2Platform extends DatabasePlatform {
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("bigint", false));
dbTypeMap.put(DbType.REAL, new DbPlatformType("real"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 15));
persistBatchOnCascade = PersistBatch.NONE;
}
@@ -1,7 +1,7 @@
package io.ebean.config.dbplatform.h2;
import io.ebean.BackgroundExecutor;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.Query;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
@@ -1,7 +1,7 @@
package io.ebean.config.dbplatform.hsqldb;
import io.ebean.BackgroundExecutor;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
@@ -1,7 +1,7 @@
package io.ebean.config.dbplatform.mysql;
import io.ebean.BackgroundExecutor;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.Query;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
@@ -1,7 +1,7 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.BackgroundExecutor;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.Query;
import io.ebean.config.dbplatform.BasicSqlAnsiLimiter;
import io.ebean.config.dbplatform.DatabasePlatform;
@@ -1,7 +1,7 @@
package io.ebean.config.dbplatform.postgres;
import io.ebean.BackgroundExecutor;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.Query;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
@@ -1,6 +1,6 @@
package io.ebean.config.dbplatform.sqlanywhere;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
@@ -1,6 +1,6 @@
package io.ebean.config.dbplatform.sqlite;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
@@ -1,7 +1,7 @@
package io.ebean.config.dbplatform.sqlserver;
import io.ebean.PersistBatch;
import io.ebean.Platform;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
@@ -4,7 +4,7 @@ import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.config.DbConstraintNaming;
import io.ebean.config.DbMigrationConfig;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
@@ -539,7 +539,7 @@ public class DbMigration {
return new SQLitePlatform();
case GENERIC:
return new DatabasePlatform();
default:
throw new IllegalArgumentException("Platform missing? " + platform);
}
@@ -1,6 +1,6 @@
package io.ebean.dbmigration;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -4,7 +4,6 @@ import io.ebean.config.DbConstraintNaming;
import io.ebean.config.NamingConvention;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DbHistorySupport;
import io.ebean.config.dbplatform.DbIdentity;
import io.ebean.config.dbplatform.IdType;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
@@ -17,6 +17,7 @@ public class DB2Ddl extends PlatformDdl {
this.inlineUniqueWhenNullable = false;
}
@Override
public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns, boolean notNull) {
if (notNull) {
return super.alterTableAddUniqueConstraint(tableName, uqName, columns, true);
@@ -82,6 +82,7 @@ public class SqlServerDdl extends PlatformDdl {
return sb.toString();
}
@Override
public String alterTableDropConstraint(String tableName, String constraintName) {
StringBuilder sb = new StringBuilder();
sb.append("IF (OBJECT_ID('").append(constraintName).append("', 'C') IS NOT NULL) ");
@@ -1,6 +1,5 @@
package io.ebean.dbmigration.ddlgeneration.platform;
import io.ebean.config.DbConstraintNaming;
import io.ebean.config.ServerConfig;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
@@ -1,9 +1,5 @@
package io.ebean.event.changelog;
import io.ebean.ValuePair;
import java.util.Map;
/**
* A bean insert, update or delete change sent as part of a ChangeSet.
*/
@@ -12,68 +8,68 @@ public class BeanChange {
/**
* The underling base table name.
*/
String table;
private final String type;
/**
* The tenantId value.
*/
Object tenantId;
private final Object tenantId;
/**
* The id value.
*/
Object id;
private final Object id;
/**
* The INSERT, UPDATE or DELETE change type.
*/
ChangeType type;
private final ChangeType event;
/**
* The time the bean change was created.
*/
long eventTime;
private final long eventTime;
/**
* The values for insert or update. Note that null values are not included for insert.
* The change in JSON form.
*/
Map<String, ValuePair> values;
private final String data;
/**
* Construct with all the details.
* The change in JSON form.
*/
public BeanChange(String table, Object tenantId, Object id, ChangeType type, Map<String, ValuePair> values) {
this.table = table;
private final String oldData;
/**
* Construct with change as JSON.
*/
public BeanChange(String type, Object tenantId, Object id, ChangeType event, String data, String oldData) {
this.type = type;
this.tenantId = tenantId;
this.id = id;
this.type = type;
this.event = event;
this.eventTime = System.currentTimeMillis();
this.values = values;
this.data = data;
this.oldData = oldData;
}
/**
* Default constructor for JSON tools.
* Construct with change as JSON.
*/
public BeanChange() {
public BeanChange(String table, Object tenantId, Object id, ChangeType event, String data) {
this(table, tenantId , id , event , data , null);
}
@Override
public String toString() {
return "table:" + table + " tenantId: " + tenantId + " id:" + id + " values:" + values;
return "type:" + type + " tenantId: " + tenantId + " id:" + id + " data:" + data;
}
/**
* Return the object type (typically table name).
*/
public String getTable() {
return table;
}
/**
* Set the object type (for JSON tools).
*/
public void setTable(String table) {
this.table = table;
public String getType() {
return type;
}
/**
@@ -83,13 +79,6 @@ public class BeanChange {
return tenantId;
}
/**
* Set the bean id (for JSON tools).
*/
public void setTenantId(Object tenantId) {
this.tenantId = tenantId;
}
/**
* Return the object id.
*/
@@ -97,25 +86,11 @@ public class BeanChange {
return id;
}
/**
* Set the bean id (for JSON tools).
*/
public void setId(Object id) {
this.id = id;
}
/**
* Return the change type (INSERT, UPDATE or DELETE).
*/
public ChangeType getType() {
return type;
}
/**
* Set the type (for JSON tools).
*/
public void setType(ChangeType type) {
this.type = type;
public ChangeType getEvent() {
return event;
}
/**
@@ -126,23 +101,16 @@ public class BeanChange {
}
/**
* Set the event time in epoch millis.
* Return the change data in JSON form.
*/
public void setEventTime(long eventTime) {
this.eventTime = eventTime;
public String getData() {
return data;
}
/**
* Return the value pairs. For inserts the ValuePair oldValue is always null.
* Return the old data in JSON form.
*/
public Map<String, ValuePair> getValues() {
return values;
}
/**
* Set the value pairs (for JSON tools).
*/
public void setValues(Map<String, ValuePair> values) {
this.values = values;
public String getOldData() {
return oldData;
}
}
+9 -9
View File
@@ -96,7 +96,7 @@ public class EJson {
/**
* Parse the json and return as a modify aware List.
*/
public static List<Object> parseList(String json, boolean modifyAware) throws IOException {
public static <T> List<T> parseList(String json, boolean modifyAware) throws IOException {
return EJsonReader.parseList(json, modifyAware);
}
@@ -125,8 +125,8 @@ public class EJson {
* Parse the json returning as a List taking into account the current token.
*/
@SuppressWarnings("unchecked")
public static List<Object> parseList(JsonParser parser, JsonToken currentToken) throws IOException {
return (List<Object>) EJsonReader.parse(parser, currentToken, false);
public static <T> List<T> parseList(JsonParser parser, JsonToken currentToken) throws IOException {
return (List<T>) EJsonReader.parse(parser, currentToken, false);
}
/**
@@ -153,23 +153,23 @@ public class EJson {
/**
* Parse the json returning a Set that might be modify aware.
*/
public static Set parseSet(String json, boolean modifyAware) throws IOException {
List<Object> list = parseList(json, modifyAware);
public static <T> Set<T> parseSet(String json, boolean modifyAware) throws IOException {
List<T> list = parseList(json, modifyAware);
if (list == null) {
return null;
}
if (modifyAware) {
return ((ModifyAwareList) list).asSet();
return ((ModifyAwareList<T>) list).asSet();
} else {
return new LinkedHashSet<>(list);
return new LinkedHashSet<T>(list);
}
}
/**
* Parse the json returning as a Set taking into account the current token.
*/
public static Set<Object> parseSet(JsonParser parser, JsonToken currentToken) throws IOException {
return new LinkedHashSet<>(parseList(parser, currentToken));
public static <T> Set<T> parseSet(JsonParser parser, JsonToken currentToken) throws IOException {
return new LinkedHashSet<T>(parseList(parser, currentToken));
}
}
@@ -52,8 +52,8 @@ class EJsonReader {
}
@SuppressWarnings("unchecked")
static List<Object> parseList(String json, boolean modifyAware) throws IOException {
return (List<Object>) parse(json, modifyAware);
static <T> List<T> parseList(String json, boolean modifyAware) throws IOException {
return (List<T>) parse(json, modifyAware);
}
@SuppressWarnings("unchecked")
@@ -1,7 +1,7 @@
package io.ebeaninternal.api;
import io.ebean.TxScope;
import io.ebean.PersistBatch;
import io.ebean.annotation.PersistBatch;
import java.util.ArrayList;
@@ -53,6 +53,8 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
private Boolean restoreBatchGeneratedKeys;
private boolean restoreBatchFlushOnQuery;
/**
* Flag set when a rollback has occurred.
*/
@@ -77,10 +79,14 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
restoreBatchOnCascade = transaction.getBatchOnCascade();
restoreBatchSize = transaction.getBatchSize();
restoreBatchGeneratedKeys = transaction.getBatchGetGeneratedKeys();
restoreBatchFlushOnQuery = transaction.isBatchFlushOnQuery();
}
if (txScope.isBatchSet()) {
transaction.setBatch(txScope.getBatch());
}
if (!txScope.isFlushOnQuery()) {
transaction.setBatchFlushOnQuery(false);
}
if (txScope.isBatchOnCascadeSet()) {
transaction.setBatchOnCascade(txScope.getBatchOnCascade());
}
@@ -158,6 +164,7 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
if (created) {
transaction.commit();
} else {
transaction.setBatchFlushOnQuery(restoreBatchFlushOnQuery);
if (restoreBatch != null) {
transaction.setBatch(restoreBatch);
}
@@ -1,6 +1,6 @@
package io.ebeaninternal.api;
import io.ebean.PersistBatch;
import io.ebean.annotation.PersistBatch;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.bean.PersistenceContext;
@@ -25,6 +25,11 @@ import java.util.List;
*/
public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionLoader {
/**
* Return the server extended Json context.
*/
SpiJsonContext jsonExtended();
/**
* For internal use, shutdown of the server invoked by JVM Shutdown.
*/
@@ -200,4 +205,9 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
* Return the DataTimeZone to use when reading/writing timestamps via JDBC.
*/
DataTimeZone getDataTimeZone();
/**
* Check for slow query event.
*/
void slowQueryCheck(long executionTimeMicros, int rowCount, SpiQuery<?> query);
}
@@ -0,0 +1,24 @@
package io.ebeaninternal.api;
import com.fasterxml.jackson.core.JsonGenerator;
import io.ebean.text.json.JsonContext;
import io.ebean.text.json.JsonWriteOptions;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import java.io.Writer;
/**
* Extended Json Context for internal server use.
*/
public interface SpiJsonContext extends JsonContext {
/**
* Create a Json Writer for writing beans as JSON.
*/
SpiJsonWriter createJsonWriter(JsonGenerator gen, JsonWriteOptions options);
/**
* Create a Json Writer for writing beans as JSON supplying a writer.
*/
SpiJsonWriter createJsonWriter(Writer writer);
}
@@ -1,6 +1,5 @@
package io.ebeaninternal.extraddl.model;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -1,12 +1,11 @@
package io.ebeaninternal.server.changelog;
import io.ebean.ValuePair;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeSet;
import io.ebean.event.changelog.ChangeType;
import io.ebean.text.json.JsonContext;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import java.io.IOException;
import java.io.Writer;
@@ -15,25 +14,23 @@ import java.util.Map;
/**
* Builds JSON document for a bean change.
*/
public class ChangeJsonBuilder {
class ChangeJsonBuilder {
protected final JsonFactory jsonFactory = new JsonFactory();
protected final JsonContext json;
protected ChangeJsonBuilder(JsonContext json) {
ChangeJsonBuilder(JsonContext json) {
this.json = json;
}
/**
* Write the bean change as JSON.
*/
public void writeBeanJson(Writer writer, BeanChange bean, ChangeSet changeSet, int position) throws IOException {
void writeBeanJson(Writer writer, BeanChange bean, ChangeSet changeSet) throws IOException {
try (JsonGenerator generator = jsonFactory.createGenerator(writer)) {
writeBeanChange(generator, bean, changeSet, position);
writeBeanChange(generator, bean, changeSet);
generator.flush();
}
}
@@ -41,34 +38,29 @@ public class ChangeJsonBuilder {
/**
* Write the bean change as JSON document containing the transaction header details.
*/
protected void writeBeanChange(JsonGenerator gen, BeanChange bean, ChangeSet changeSet, int position) throws IOException {
private void writeBeanChange(JsonGenerator gen, BeanChange bean, ChangeSet changeSet) throws IOException {
gen.writeStartObject();
writeBeanTransactionDetails(gen, changeSet, position);
gen.writeStringField("object", bean.getTable());
gen.writeNumberField("ts", bean.getEventTime());
gen.writeStringField("change", bean.getEvent().getCode());
gen.writeStringField("type", bean.getType());
gen.writeStringField("id", bean.getId().toString());
if (bean.getTenantId() != null) {
gen.writeStringField("tenantId", bean.getTenantId().toString());
}
gen.writeStringField("objectId", bean.getId().toString());
gen.writeStringField("change", bean.getType().getCode());
gen.writeNumberField("eventTime", bean.getEventTime());
writeBeanTransactionDetails(gen, changeSet);
writeBeanValues(gen, bean);
gen.writeEndObject();
}
/**
* Denormalise by writing the transaction header details.
*/
protected void writeBeanTransactionDetails(JsonGenerator gen, ChangeSet changeSet, int position) throws IOException {
private void writeBeanTransactionDetails(JsonGenerator gen, ChangeSet changeSet) throws IOException {
gen.writeStringField("txnId", changeSet.getTxnId());
gen.writeStringField("txnState", changeSet.getTxnState().getCode());
gen.writeNumberField("txnBatch", changeSet.getTxnBatch());
gen.writeNumberField("txnPosition", position);
String source = changeSet.getSource();
if (source != null) {
gen.writeStringField("source", source);
@@ -94,39 +86,18 @@ public class ChangeJsonBuilder {
/**
* For insert and update write the new/old values.
*/
protected void writeBeanValues(JsonGenerator gen, BeanChange bean) throws IOException {
private void writeBeanValues(JsonGenerator gen, BeanChange bean) throws IOException {
if (bean.getType() != ChangeType.DELETE) {
gen.writeFieldName("values");
gen.writeStartObject();
writeValuePairs(bean, gen);
gen.writeEndObject();
}
}
if (bean.getEvent() != ChangeType.DELETE) {
gen.writeFieldName("data");
gen.writeRaw(":");
gen.writeRaw(bean.getData());
/**
* Write all the value pairs suppressing null values.
* <p>
* We are intentionally keeping the same new/old structure for both inserts and updates.
* </p>
*/
protected void writeValuePairs(BeanChange bean, JsonGenerator gen) throws IOException {
for (Map.Entry<String, ValuePair> entry : bean.getValues().entrySet()) {
gen.writeFieldName(entry.getKey());
gen.writeStartObject();
ValuePair value = entry.getValue();
Object newValue = value.getNewValue();
if (newValue != null) {
gen.writeFieldName("new");
json.writeScalar(gen, newValue);
String oldData = bean.getOldData();
if (oldData != null) {
gen.writeRaw(",\"oldData\":");
gen.writeRaw(oldData);
}
Object oldValue = value.getOldValue();
if (oldValue != null) {
gen.writeFieldName("old");
json.writeScalar(gen, oldValue);
}
gen.writeEndObject();
}
}
@@ -10,15 +10,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.StringWriter;
import java.util.List;
import java.util.Properties;
/**
* Logs the change sets in JSON to logger named <code>io.ebean.ChangeLog</code>.
* <p>
* The logged entries duplicate/denormalise the transaction details so that each bean change
* is fully contained with the transaction information.
* </p>
* Simply logs the change sets in JSON form to logger named <code>io.ebean.ChangeLog</code>.
*/
public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
@@ -30,22 +25,17 @@ public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
/**
* The named logger we send the change set payload to. Can be externally configured as desired.
*/
protected static final Logger changeLog = LoggerFactory.getLogger("io.ebean.ChangeLog");
private static final Logger changeLog = LoggerFactory.getLogger("io.ebean.ChangeLog");
/**
* Used to build the JSON.
*/
protected ChangeJsonBuilder jsonBuilder;
private ChangeJsonBuilder jsonBuilder;
/**
* A bigger default buffer for bean inserts and updates (that have value pairs).
*/
protected int defaultBufferSize = 400;
/**
* Expected to be a reasonable buffer size for deletes (which do not have value pairs).
*/
protected int defaultDeleteBufferSize = 250;
private int defaultBufferSize = 400;
public DefaultChangeLogListener() {
}
@@ -79,13 +69,11 @@ public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
@Override
public void log(ChangeSet changeSet) {
List<BeanChange> changes = changeSet.getChanges();
for (int i = 0; i < changes.size(); i++) {
for (BeanChange beanChange : changeSet.getChanges()) {
// log each bean change as a separate log entry
BeanChange beanChange = changes.get(i);
try {
StringWriter writer = new StringWriter(getBufferSize(beanChange));
jsonBuilder.writeBeanJson(writer, beanChange, changeSet, i);
jsonBuilder.writeBeanJson(writer, beanChange, changeSet);
changeLog.info(writer.toString());
} catch (Exception e) {
logger.error("Exception logging beanChange " + beanChange.toString(), e);
@@ -96,9 +84,9 @@ public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
/**
* Return a decent buffer size based on the bean change.
*/
protected int getBufferSize(BeanChange beanChange) {
private int getBufferSize(BeanChange beanChange) {
return ChangeType.DELETE == beanChange.getType() ? defaultDeleteBufferSize : defaultBufferSize;
return ChangeType.DELETE == beanChange.getEvent() ? 250 : defaultBufferSize;
}
}
@@ -134,8 +134,6 @@ public class DatabasePlatformFactory {
String dbProductName = metaData.getDatabaseProductName();
dbProductName = dbProductName.toLowerCase();
int majorVersion = metaData.getDatabaseMajorVersion();
if (dbProductName.contains("oracle")) {
return new OraclePlatform();
} else if (dbProductName.contains("microsoft")) {
@@ -20,8 +20,6 @@ import io.ebean.SqlRow;
import io.ebean.SqlUpdate;
import io.ebean.Transaction;
import io.ebean.TransactionCallback;
import io.ebean.TxCallable;
import io.ebean.TxRunnable;
import io.ebean.TxScope;
import io.ebean.Update;
import io.ebean.UpdateQuery;
@@ -43,6 +41,8 @@ import io.ebean.config.TenantMode;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.dbmigration.DdlGenerator;
import io.ebean.event.BeanPersistController;
import io.ebean.config.SlowQueryEvent;
import io.ebean.config.SlowQueryListener;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetaInfoManager;
@@ -51,16 +51,8 @@ import io.ebean.plugin.Plugin;
import io.ebean.plugin.SpiServer;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.json.JsonContext;
import io.ebeaninternal.api.LoadBeanRequest;
import io.ebeaninternal.api.LoadManyRequest;
import io.ebeaninternal.api.ScopeTrans;
import io.ebeaninternal.api.ScopedTransaction;
import io.ebeaninternal.api.SpiBackgroundExecutor;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.*;
import io.ebeaninternal.api.SpiQuery.Type;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.api.TransactionEventTable;
import io.ebeaninternal.server.autotune.AutoTuneService;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
import io.ebeaninternal.server.deploy.BeanDescriptor;
@@ -91,8 +83,8 @@ import io.ebeaninternal.server.transaction.TransactionScopeManager;
import io.ebeaninternal.util.ParamTypeHelper;
import io.ebeaninternal.util.ParamTypeHelper.TypeInfo;
import io.ebeanservice.docstore.api.DocStoreIntegration;
import io.ebean.TxIsolation;
import io.ebean.TxType;
import io.ebean.annotation.TxIsolation;
import io.ebean.annotation.TxType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -179,7 +171,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final EncryptKeyManager encryptKeyManager;
private final JsonContext jsonContext;
private final SpiJsonContext jsonContext;
private final DocumentStore documentStore;
@@ -213,6 +205,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final boolean collectQueryStatsByNode;
private final long slowQueryMicros;
private final SlowQueryListener slowQueryListener;
/**
* Cache used to collect statistics based on ObjectGraphNode (used to highlight lazy loading origin points).
*/
@@ -238,6 +234,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.encryptKeyManager = serverConfig.getEncryptKeyManager();
this.defaultPersistenceContextScope = serverConfig.getPersistenceContextScope();
this.currentTenantProvider = serverConfig.getCurrentTenantProvider();
this.slowQueryMicros = config.getSlowQueryMicros();
this.slowQueryListener = config.getSlowQueryListener();
this.beanDescriptorManager = config.getBeanDescriptorManager();
beanDescriptorManager.setEbeanServer(this);
@@ -661,16 +659,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return transactionManager.createTransaction(true, isolation.getLevel());
}
@Override
public <T> T execute(TxCallable<T> c) {
return execute(null, c);
}
@Override
public <T> T execute(TxScope scope, TxCallable<T> c) {
return executeCall(scope, c);
}
@Override
public <T> T executeCall(Callable<T> c) {
return executeCall(null, c);
@@ -2219,6 +2207,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return jsonContext;
}
@Override
public SpiJsonContext jsonExtended() {
return jsonContext;
}
@Override
public void collectQueryStats(ObjectGraphNode node, long loadedBeanCount, long timeMicros) {
@@ -2234,4 +2227,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Override
public void slowQueryCheck(long timeMicros, int rowCount, SpiQuery<?> query) {
if (timeMicros > slowQueryMicros) {
if (slowQueryListener != null) {
slowQueryListener.process(new SlowQueryEvent(query.getGeneratedSql(), timeMicros / 1000L, rowCount, query.getParentNode()));
}
}
}
}
@@ -0,0 +1,26 @@
package io.ebeaninternal.server.core;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.config.SlowQueryEvent;
import io.ebean.config.SlowQueryListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default slow query listener implementation that logs a warning message.
*/
class DefaultSlowQueryListener implements SlowQueryListener {
private static final Logger log = LoggerFactory.getLogger("io.ebean.SlowQuery");
@Override
public void process(SlowQueryEvent event) {
String firstStack = "";
ObjectGraphNode node = event.getOriginNode();
if (node != null) {
firstStack = node.getOriginQueryPoint().getFirstStackElement();
}
log.warn("Slow query warning - millis:{} rows:{} caller[{}] sql[{}]", event.getTimeMillis(), event.getRowCount(), firstStack, event.getSql());
}
}
@@ -3,12 +3,7 @@ package io.ebeaninternal.server.core;
import io.ebean.ValuePair;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
/**
@@ -54,51 +49,4 @@ public class DiffHelp {
return desc.diff((EntityBean) newBean, (EntityBean) oldBean);
}
/**
* Flattens an existing diff map converting assoc one beans into the associated id changes.
*/
public static Map<String, ValuePair> flatten(Map<String, ValuePair> values, BeanDescriptor<?> desc) {
Map<String, ValuePair> flattened = null;
Iterator<Map.Entry<String, ValuePair>> iterator = values.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, ValuePair> entry = iterator.next();
BeanProperty beanProperty = desc.getBeanProperty(entry.getKey());
if (beanProperty instanceof BeanPropertyAssocMany) {
// filter out assoc many bean properties
iterator.remove();
} else if (beanProperty instanceof BeanPropertyAssocOne) {
BeanPropertyAssocOne<?> assoc = (BeanPropertyAssocOne<?>) beanProperty;
if (!assoc.isEmbedded()) {
// flatten for assoc one beans
if (flattened == null) {
flattened = new LinkedHashMap<>();
}
flattenToId(flattened, entry, beanProperty, assoc);
iterator.remove();
}
}
}
if (flattened != null) {
values.putAll(flattened);
}
return values;
}
private static void flattenToId(Map<String, ValuePair> flattened, Map.Entry<String, ValuePair> entry, BeanProperty beanProperty, BeanPropertyAssocOne<?> assoc) {
BeanDescriptor<?> oneDesc = assoc.getTargetDescriptor();
ValuePair value = entry.getValue();
Object newId = value.getNewValue() == null ? null : oneDesc.getId((EntityBean) value.getNewValue());
Object oldId = value.getOldValue() == null ? null : oneDesc.getId((EntityBean) value.getOldValue());
String propName = beanProperty.getName() + "." + oneDesc.getIdProperty().getName();
flattened.put(propName, new ValuePair(newId, oldId));
}
}
@@ -1,12 +1,14 @@
package io.ebeaninternal.server.core;
import com.fasterxml.jackson.core.JsonFactory;
import io.ebean.ExpressionFactory;
import io.ebean.annotation.Platform;
import io.ebean.cache.ServerCacheManager;
import io.ebean.config.ExternalTransactionManager;
import io.ebean.Platform;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbHistorySupport;
import io.ebean.config.SlowQueryListener;
import io.ebean.event.changelog.ChangeLogListener;
import io.ebean.event.changelog.ChangeLogPrepare;
import io.ebean.event.changelog.ChangeLogRegister;
@@ -14,9 +16,9 @@ import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.plugin.Plugin;
import io.ebean.plugin.SpiServer;
import io.ebean.text.json.JsonContext;
import io.ebeaninternal.api.SpiBackgroundExecutor;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiJsonContext;
import io.ebeaninternal.server.autotune.AutoTuneService;
import io.ebeaninternal.server.autotune.service.AutoTuneServiceFactory;
import io.ebeaninternal.server.cache.DefaultCacheAdapter;
@@ -60,7 +62,6 @@ import io.ebeanservice.docstore.api.DocStoreFactory;
import io.ebeanservice.docstore.api.DocStoreIntegration;
import io.ebeanservice.docstore.api.DocStoreUpdateProcessor;
import io.ebeanservice.docstore.none.NoneDocStoreFactory;
import com.fasterxml.jackson.core.JsonFactory;
import org.avaje.datasource.DataSourcePool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -261,7 +262,7 @@ public class InternalConfiguration {
}
}
public JsonContext createJsonContext(SpiEbeanServer server) {
public SpiJsonContext createJsonContext(SpiEbeanServer server) {
return new DJsonContext(server, jsonFactory, typeManager);
}
@@ -432,4 +433,30 @@ public class InternalConfiguration {
public ServerCacheManager cache() {
return new DefaultCacheAdapter(cacheManager);
}
/**
* Return the slow query warning limit in micros.
*/
long getSlowQueryMicros() {
long millis = serverConfig.getSlowQueryMillis();
return (millis < 1) ? Long.MAX_VALUE : millis * 1000L;
}
/**
* Return the SlowQueryListener with a default that logs a warning message.
*/
SlowQueryListener getSlowQueryListener() {
long millis = serverConfig.getSlowQueryMillis();
if (millis < 1) {
return null;
}
SlowQueryListener listener = serverConfig.getSlowQueryListener();
if (listener == null) {
listener = serverConfig.service(SlowQueryListener.class);
if (listener == null) {
listener = new DefaultSlowQueryListener();
}
}
return listener;
}
}
@@ -385,7 +385,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
*/
@Override
@SuppressWarnings("unchecked")
public Set<?> findSet() {
public Set<T> findSet() {
return (Set<T>) queryEngine.findMany(this);
}
@@ -393,7 +393,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
* Execute the query as findMap.
*/
@Override
public Map<?, ?> findMap() {
@SuppressWarnings("unchecked")
public <K> Map<K, T> findMap() {
String mapKey = query.getMapKey();
if (mapKey == null) {
BeanProperty idProp = beanDescriptor.getIdProperty();
@@ -403,7 +404,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
throw new PersistenceException("No mapKey specified for query");
}
}
return (Map<?, ?>) queryEngine.findMany(this);
return (Map<K, T>) queryEngine.findMany(this);
}
/**
@@ -480,7 +481,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
} else {
cacheKey = query.queryHash();
}
if (!query.getUseQueryCache().isGet()) {
return null;
}
@@ -504,11 +505,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
if (cached instanceof BeanCollection) {
cached = ((BeanCollection<?>)cached).getShallowCopy();
} else if (cached instanceof List) {
cached = new CopyOnFirstWriteList<>((List)cached);
cached = new CopyOnFirstWriteList<>((List<?>)cached);
} else if (cached instanceof Set) {
cached = new LinkedHashSet<>((Set)cached);
cached = new LinkedHashSet<>((Set<?>)cached);
} else if (cached instanceof Map) {
cached = new LinkedHashMap<>((Map)cached);
cached = new LinkedHashMap<>((Map<?,?>)cached);
}
}
return cached;
@@ -596,4 +597,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
public Object getTenantId() {
return (transaction == null) ? null : transaction.getTenantId();
}
/**
* Check for slow query event.
*/
public void slowQueryCheck(long executionTimeMicros, int rowCount) {
ebeanServer.slowQueryCheck(executionTimeMicros, rowCount, query);
}
}
@@ -18,6 +18,7 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanManager;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import io.ebeaninternal.server.deploy.id.ImportedId;
import io.ebeaninternal.server.persist.BatchControl;
import io.ebeaninternal.server.persist.BatchedSqlException;
@@ -179,6 +180,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
beanDescriptor.setDraftDirty(entityBean, true);
}
this.dirty = intercept.isDirty();
initGeneratedProperties();
}
/**
@@ -225,6 +227,52 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
persistCascade = transaction.isPersistCascade();
}
private void initGeneratedProperties() {
switch (type) {
case INSERT:
onInsertGeneratedProperties();
break;
case UPDATE:
if (!beanDescriptor.isReference(intercept) && (dirty || statelessUpdate)) {
onUpdateGeneratedProperties();
}
break;
case SOFT_DELETE:
onUpdateGeneratedProperties();
break;
}
}
private void onUpdateGeneratedProperties() {
for (BeanProperty prop : beanDescriptor.propertiesGenUpdate()) {
GeneratedProperty generatedProperty = prop.getGeneratedProperty();
if (prop.isVersion()) {
if (isLoadedProperty(prop)) {
// @Version property must be loaded to be involved
Object value = generatedProperty.getUpdateValue(prop, entityBean, now());
Object oldVal = prop.getValue(entityBean);
setVersionValue(value);
intercept.setOldValue(prop.getPropertyIndex(), oldVal);
}
} else {
// @WhenModified set without invoking interception
Object oldVal = prop.getValue(entityBean);
Object value = generatedProperty.getUpdateValue(prop, entityBean, now());
prop.setValueChanged(entityBean, value);
intercept.setOldValue(prop.getPropertyIndex(), oldVal);
}
}
}
private void onInsertGeneratedProperties() {
for (BeanProperty prop : beanDescriptor.propertiesGenInsert()) {
Object value = prop.getGeneratedProperty().getInsertValue(prop, entityBean, now());
prop.setValueChanged(entityBean, value);
}
}
/**
* If using batch on cascade flush if required.
*/
@@ -525,7 +573,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Prepare the update after potential modifications in a BeanPersistController.
*/
private void postControllerPrepareUpdate() {
if (intercept.isNew() && controller != null) {
if (statelessUpdate && controller != null) {
// 'stateless update' - set dirty properties modified in controller preUpdate
intercept.setNewBeanForUpdate();
}
@@ -780,17 +828,20 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
@Override
public void postExecute() {
changeLog();
if (controller != null) {
controllerPost();
}
setNotifyCache();
if (type == Type.UPDATE && (notifyCache || docStoreMode == DocStoreMode.UPDATE)) {
boolean isChangeLog = beanDescriptor.isChangeLog();
if (type == Type.UPDATE && (isChangeLog || notifyCache || docStoreMode == DocStoreMode.UPDATE)) {
// get the dirty properties for update notification to the doc store
dirtyProperties = intercept.getDirtyProperties();
}
if (isChangeLog) {
changeLog();
}
// if bean persisted again then should result in an update
intercept.setLoaded();
if (isInsert()) {
@@ -857,27 +908,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
}
/**
* Determine the concurrency mode depending on fully/partially populated bean.
* <p>
* Specifically with version concurrency we want to check that the version property was one of the
* loaded properties.
* </p>
*/
public ConcurrencyMode determineConcurrencyMode() {
// 'partial bean' update/delete...
if (concurrencyMode.equals(ConcurrencyMode.VERSION)) {
// check the version property was loaded
BeanProperty prop = beanDescriptor.getVersionProperty();
if (prop == null || !intercept.isLoadedProperty(prop.getPropertyIndex())) {
concurrencyMode = ConcurrencyMode.NONE;
}
}
return concurrencyMode;
}
/**
* Return true if the update DML/SQL must be dynamically generated.
* <p>
@@ -908,11 +938,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
private void postInsert() {
// mark all properties as loaded after an insert to support immediate update
int len = intercept.getPropertyLength();
for (int i = 0; i < len; i++) {
intercept.setLoadedProperty(i);
}
beanDescriptor.setEmbeddedOwner(entityBean);
beanDescriptor.setAllLoaded(entityBean);
if (!publish) {
beanDescriptor.setDraft(entityBean);
}
@@ -1147,4 +1173,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
return now;
}
/**
* Return true if this is a stateless update request (in which case it doesn't really have 'old values').
*/
public boolean isStatelessUpdate() {
return statelessUpdate;
}
}
@@ -101,12 +101,12 @@ public interface SpiOrmQueryRequest<T> extends DocQueryRequest<T> {
/**
* Execute the query as findSet.
*/
Set<?> findSet();
Set<T> findSet();
/**
* Execute the query as findMap.
*/
Map<?, ?> findMap();
<K> Map<K, T> findMap();
/**
* Execute the findSingleAttributeList query.
@@ -61,7 +61,7 @@ public class BootupClassPathSearch {
}
long searchTime = System.currentTimeMillis() - st;
logger.info("Classpath search entities[{}] searchTime[{}] in packages[{}]", bc.getEntities().size(), searchTime, packages);
logger.debug("Classpath search entities[{}] searchTime[{}] in packages[{}]", bc.getEntities().size(), searchTime, packages);
return bc;
} catch (Exception ex) {
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.core.bootup;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -0,0 +1,108 @@
package io.ebeaninternal.server.deploy;
import io.ebean.PersistenceIOException;
import io.ebean.bean.BeanDiffVisitor;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import io.ebeaninternal.server.util.ArrayStack;
import java.io.IOException;
import java.io.StringWriter;
/**
* Builds the 'new values' and 'old values' in JSON form for ChangeLog.
*/
class BeanChangeJson implements BeanDiffVisitor {
private final StringWriter newData;
private final StringWriter oldData;
private final SpiJsonWriter newJson;
private final SpiJsonWriter oldJson;
private final ArrayStack<BeanDescriptor<?>> stack = new ArrayStack<>();
private BeanDescriptor<?> descriptor;
BeanChangeJson(BeanDescriptor<?> descriptor, boolean statelessUpdate) {
this.descriptor = descriptor;
this.newData = new StringWriter(200);
this.newJson = descriptor.createJsonWriter(newData);
newJson.writeStartObject();
if (statelessUpdate) {
this.oldJson = null;
this.oldData = null;
} else {
this.oldData = new StringWriter(200);
this.oldJson = descriptor.createJsonWriter(oldData);
oldJson.writeStartObject();
}
}
@Override
public void visit(int position, Object newVal, Object oldVal) {
try {
BeanProperty prop = descriptor.propertiesIndex[position];
if (prop.isDbUpdatable()) {
prop.jsonWriteValue(newJson, newVal);
if (oldJson != null) {
prop.jsonWriteValue(oldJson, oldVal);
}
}
} catch (IOException e) {
throw new PersistenceIOException(e);
}
}
@Override
public void visitPush(int position) {
stack.push(descriptor);
BeanPropertyAssocOne<?> embedded = (BeanPropertyAssocOne<?>)descriptor.propertiesIndex[position];
descriptor = embedded.getTargetDescriptor();
newJson.writeStartObject(embedded.getName());
if (oldJson != null) {
oldJson.writeStartObject(embedded.getName());
}
}
@Override
public void visitPop() {
newJson.writeEndObject();
if (oldJson != null) {
oldJson.writeEndObject();
}
descriptor = stack.pop();
}
/**
* Flush the buffers.
*/
void flush() {
try {
newJson.writeEndObject();
newJson.gen().flush();
if (oldJson != null) {
oldJson.writeEndObject();
oldJson.gen().flush();
}
} catch (IOException e) {
throw new PersistenceIOException(e);
}
}
/**
* Return the new values JSON.
*/
String newJson() {
return newData.toString();
}
/**
* Return the old values JSON.
*/
String oldJson() {
return oldData == null ? null : oldData.toString();
}
}
@@ -7,7 +7,7 @@ import io.ebean.bean.BeanCollection;
import io.ebean.bean.BeanCollectionAdd;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import java.io.IOException;
@@ -62,6 +62,6 @@ public interface BeanCollectionHelp<T> {
/**
* Write the collection out as json.
*/
void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException;
void jsonWrite(SpiJsonWriter ctx, String name, Object collection, boolean explicitInclude) throws IOException;
}
@@ -9,8 +9,10 @@ import io.ebeaninternal.server.core.OrmQueryRequest;
*/
public class BeanCollectionHelpFactory {
@SuppressWarnings("rawtypes")
static final BeanListHelp LIST_HELP = new BeanListHelp();
@SuppressWarnings("rawtypes")
static final BeanSetHelp SET_HELP = new BeanSetHelp();
/**
@@ -47,7 +47,6 @@ import io.ebeaninternal.server.cache.CachedBeanData;
import io.ebeaninternal.server.cache.CachedManyIds;
import io.ebeaninternal.server.core.CacheOptions;
import io.ebeaninternal.server.core.DefaultSqlUpdate;
import io.ebeaninternal.server.core.DiffHelp;
import io.ebeaninternal.server.core.InternString;
import io.ebeaninternal.server.core.PersistRequest;
import io.ebeaninternal.server.core.PersistRequestBean;
@@ -67,7 +66,7 @@ import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
import io.ebeaninternal.server.query.SplitName;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
import io.ebeaninternal.server.text.json.ReadJson;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import io.ebeaninternal.server.type.DataBind;
import io.ebeaninternal.util.SortByClause;
import io.ebeaninternal.util.SortByClauseParser;
@@ -83,12 +82,12 @@ import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Modifier;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -340,6 +339,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
*/
private final BeanProperty[] propertiesNonTransient;
protected final BeanProperty[] propertiesIndex;
private final BeanProperty[] propertiesGenInsert;
private final BeanProperty[] propertiesGenUpdate;
/**
* The bean class name or the table name for MapBeans.
@@ -478,6 +479,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
this.propertiesManySave = listHelper.getManySave();
this.propertiesManyDelete = listHelper.getManyDelete();
this.propertiesManyToMany = listHelper.getManyToMany();
this.propertiesGenInsert = listHelper.getGeneratedInsert();
this.propertiesGenUpdate = listHelper.getGeneratedUpdate();
this.derivedTableJoins = listHelper.getTableJoin();
@@ -797,16 +800,15 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return ebeanServer.getReadAuditPrepare();
}
public boolean isChangeLog() {
return changeLogFilter != null;
}
/**
* Return true if this request should be included in the change log.
*/
public BeanChange getChangeLogBean(PersistRequestBean<T> request) {
if (changeLogFilter == null) {
return null;
}
PersistRequest.Type type = request.getType();
switch (type) {
switch (request.getType()) {
case INSERT:
return changeLogFilter.includeInsert(request) ? insertBeanChange(request) : null;
case UPDATE:
@@ -815,34 +817,79 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
case DELETE:
return changeLogFilter.includeDelete(request) ? deleteBeanChange(request) : null;
default:
throw new IllegalStateException("Unhandled request type " + type);
throw new IllegalStateException("Unhandled request type " + request.getType());
}
}
private BeanChange beanChange(ChangeType type, Object id, String data, String oldData) {
Object tenantId = ebeanServer.currentTenantId();
return new BeanChange(name, tenantId, id, type, data, oldData);
}
/**
* Return the bean change for a delete.
*/
private BeanChange deleteBeanChange(PersistRequestBean<T> request) {
return newBeanChange(request.getBeanId(), ChangeType.DELETE, Collections.<String, ValuePair>emptyMap());
return beanChange(ChangeType.DELETE, request.getBeanId(), null, null);
}
/**
* Return the bean change for an update.
* Return the bean change for an update generating 'new values' and 'old values' in JSON form.
*/
private BeanChange updateBeanChange(PersistRequestBean<T> request) {
return newBeanChange(request.getBeanId(), ChangeType.UPDATE, diffFlatten(request.getEntityBeanIntercept().getDirtyValues()));
try {
BeanChangeJson changeJson = new BeanChangeJson(this, request.isStatelessUpdate());
request.getEntityBeanIntercept().addDirtyPropertyValues(changeJson);
changeJson.flush();
return beanChange(ChangeType.UPDATE, request.getBeanId(), changeJson.newJson(), changeJson.oldJson());
} catch (RuntimeException e) {
logger.error("Failed to write ChangeLog entry for update", e);
return null;
}
}
/**
* Return the bean change for an insert.
*/
private BeanChange insertBeanChange(PersistRequestBean<T> request) {
return newBeanChange(request.getBeanId(), ChangeType.INSERT, diffForInsert(request.getEntityBean()));
try {
StringWriter writer = new StringWriter(200);
SpiJsonWriter jsonWriter = createJsonWriter(writer);
jsonWriteForInsert(jsonWriter, request.getEntityBean());
jsonWriter.gen().flush();
return beanChange(ChangeType.INSERT, request.getBeanId(), writer.toString(), null);
} catch (IOException e) {
logger.error("Failed to write ChangeLog entry for insert", e);
return null;
}
}
private BeanChange newBeanChange(Object id, ChangeType changeType, Map<String, ValuePair> values) {
Object tenantId = ebeanServer.currentTenantId();
return new BeanChange(getBaseTable(), tenantId, id, changeType, values);
SpiJsonWriter createJsonWriter(StringWriter writer) {
return ebeanServer.jsonExtended().createJsonWriter(writer);
}
/**
* Populate the diff for inserts with flattened non-null property values.
*/
protected void jsonWriteForInsert(SpiJsonWriter jsonWriter, EntityBean newBean) throws IOException {
jsonWriter.writeStartObject();
for (BeanProperty prop : propertiesBaseScalar) {
prop.jsonWriteForInsert(jsonWriter, newBean);
}
for (BeanPropertyAssocOne<?> prop : propertiesOne) {
prop.jsonWriteForInsert(jsonWriter, newBean);
}
for (BeanPropertyAssocOne<?> prop : propertiesEmbedded) {
prop.jsonWriteForInsert(jsonWriter, newBean);
}
jsonWriter.writeEndObject();
}
public SqlUpdate deleteById(Object id, List<Object> idList, boolean softDelete) {
@@ -1747,7 +1794,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* account inheritance.
*/
public BeanProperty getBeanPropertyFromPath(String path) {
BeanDescriptor other = this;
BeanDescriptor<?> other = this;
while (true) {
String[] split = SplitName.splitBegin(path);
@@ -1771,7 +1818,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* Return the BeanDescriptor for a given path of Associated One or Many beans.
*/
public BeanDescriptor<?> getBeanDescriptor(String path) {
BeanDescriptor result = this;
BeanDescriptor<?> result = this;
while (true) {
if (path == null) {
return result;
@@ -1806,7 +1853,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* </p>
*/
public BeanPropertyAssocOne<?> getUnidirectional() {
BeanDescriptor other = this;
BeanDescriptor<?> other = this;
while (true) {
if (other.unidirectional != null) {
return other.unidirectional;
@@ -2029,8 +2076,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
public ElComparator<T> getElComparator(String propNameOrSortBy) {
ElComparator<T> c = comparatorCache.computeIfAbsent(propNameOrSortBy, this::createComparator);
return c;
return comparatorCache.computeIfAbsent(propNameOrSortBy, this::createComparator);
}
/**
@@ -2747,11 +2793,12 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
/**
* Set the embedded owner on any embedded bean properties.
* Set all properties to be loaded (recurse to embedded beans).
*/
public void setEmbeddedOwner(EntityBean bean) {
for (BeanPropertyAssocOne<?> aPropertiesEmbedded : propertiesEmbedded) {
aPropertiesEmbedded.setEmbeddedOwner(bean);
public void setAllLoaded(EntityBean bean) {
bean._ebean_getIntercept().setLoadedPropertyAll();
for (BeanPropertyAssocOne<?> embedded : propertiesEmbedded) {
embedded.setAllLoadedEmbedded(bean);
}
}
@@ -2848,45 +2895,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
}
/**
* Flatten the diff that comes from the entity bean intercept.
*/
Map<String, ValuePair> diffFlatten(Map<String, ValuePair> diff) {
return DiffHelp.flatten(diff, this);
}
/**
* Return a map of the differences between a and b.
* <p>
* A and B must be of the same type. B can be null, in which case the 'dirty
* diff' of a is returned.
* </p>
* <p>
* This intentionally does not include as OneToMany or ManyToMany properties.
* </p>
*/
public Map<String, ValuePair> diffForInsert(EntityBean newBean) {
Map<String, ValuePair> map = new LinkedHashMap<>();
diffForInsert(null, map, newBean);
return map;
}
/**
* Populate the diff for inserts with flattened non-null property values.
*/
public void diffForInsert(String prefix, Map<String, ValuePair> map, EntityBean newBean) {
for (BeanProperty aPropertiesBaseScalar : propertiesBaseScalar) {
aPropertiesBaseScalar.diffForInsert(prefix, map, newBean);
}
for (BeanPropertyAssocOne<?> aPropertiesOne : propertiesOne) {
aPropertiesOne.diffForInsert(prefix, map, newBean);
}
for (BeanPropertyAssocOne<?> aPropertiesEmbedded : propertiesEmbedded) {
aPropertiesEmbedded.diffForInsert(prefix, map, newBean);
}
}
/**
* Return the diff comparing the bean values.
*/
@@ -3049,23 +3057,37 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return propertiesLocal;
}
public void jsonWriteDirty(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
/**
* Return the properties set as generated values on insert.
*/
public BeanProperty[] propertiesGenInsert() {
return propertiesGenInsert;
}
/**
* Return the properties set as generated values on update.
*/
public BeanProperty[] propertiesGenUpdate() {
return propertiesGenUpdate;
}
public void jsonWriteDirty(SpiJsonWriter writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
jsonHelp.jsonWriteDirty(writeJson, bean, dirtyProps);
}
protected void jsonWriteDirtyProperties(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
protected void jsonWriteDirtyProperties(SpiJsonWriter writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
jsonHelp.jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
}
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
jsonHelp.jsonWrite(writeJson, bean, null);
}
public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) throws IOException {
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean, String key) throws IOException {
jsonHelp.jsonWrite(writeJson, bean, key);
}
protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) throws IOException {
protected void jsonWriteProperties(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
jsonHelp.jsonWriteProperties(writeJson, bean);
}
@@ -6,8 +6,7 @@ import com.fasterxml.jackson.core.JsonToken;
import io.ebean.bean.EntityBean;
import io.ebean.text.json.EJson;
import io.ebeaninternal.server.text.json.ReadJson;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.WriteJson.WriteBean;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import java.io.IOException;
import java.util.LinkedHashMap;
@@ -24,7 +23,7 @@ public class BeanDescriptorJsonHelp<T> {
this.inheritInfo = desc.inheritInfo;
}
public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) throws IOException {
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean, String key) throws IOException {
writeJson.writeStartObject(key);
@@ -43,13 +42,12 @@ public class BeanDescriptorJsonHelp<T> {
writeJson.writeEndObject();
}
protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) throws IOException {
protected void jsonWriteProperties(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
WriteBean writeBean = writeJson.createWriteBean(desc, bean);
writeBean.write(writeJson);
writeJson.writeBean(desc, bean);
}
public void jsonWriteDirty(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
public void jsonWriteDirty(SpiJsonWriter writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
if (inheritInfo == null) {
jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
@@ -58,7 +56,7 @@ public class BeanDescriptorJsonHelp<T> {
}
}
protected void jsonWriteDirtyProperties(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
protected void jsonWriteDirtyProperties(SpiJsonWriter writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
writeJson.writeStartObject(null);
// render the dirty properties
@@ -85,7 +83,7 @@ public class BeanDescriptorJsonHelp<T> {
return null;
}
if (JsonToken.START_OBJECT != token) {
throw new JsonParseException("Unexpected token " + token + " - expecting start_object", parser.getCurrentLocation());
throw new JsonParseException(parser, "Unexpected token " + token + " - expecting start_object", parser.getCurrentLocation());
}
}
@@ -98,7 +96,7 @@ public class BeanDescriptorJsonHelp<T> {
if (parser.nextToken() != JsonToken.FIELD_NAME) {
String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?";
throw new JsonParseException(msg, parser.getCurrentLocation());
throw new JsonParseException(parser, msg, parser.getCurrentLocation());
}
String propName = parser.getCurrentName();
@@ -111,7 +109,7 @@ public class BeanDescriptorJsonHelp<T> {
return jsonReadProperties(jsonRead, bean, path);
}
String msg = "Error reading inheritance discriminator, expected property [" + discColumn + "] but got [" + propName + "] ?";
throw new JsonParseException(msg, parser.getCurrentLocation());
throw new JsonParseException(parser, msg, parser.getCurrentLocation());
}
String discValue = parser.nextTextValue();
@@ -8,7 +8,7 @@ import io.ebean.bean.BeanCollectionAdd;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.EntityBean;
import io.ebean.common.BeanList;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import java.io.IOException;
import java.util.ArrayList;
@@ -125,7 +125,7 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
}
@Override
public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException {
public void jsonWrite(SpiJsonWriter ctx, String name, Object collection, boolean explicitInclude) throws IOException {
List<?> list;
if (collection instanceof BeanCollection<?>) {
@@ -8,7 +8,7 @@ import io.ebean.bean.BeanCollectionAdd;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.EntityBean;
import io.ebean.common.BeanMap;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import java.io.IOException;
import java.util.LinkedHashMap;
@@ -160,7 +160,7 @@ public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
}
@Override
public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException {
public void jsonWrite(SpiJsonWriter ctx, String name, Object collection, boolean explicitInclude) throws IOException {
Map<?, ?> map;
if (collection instanceof BeanCollection<?>) {
@@ -24,7 +24,7 @@ import io.ebeaninternal.server.query.SplitName;
import io.ebeaninternal.server.query.SqlBeanLoad;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.text.json.ReadJson;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import io.ebeaninternal.server.type.DataBind;
import io.ebeaninternal.server.type.ScalarType;
import io.ebeaninternal.server.type.ScalarTypeBoolean;
@@ -1360,21 +1360,55 @@ public class BeanProperty implements ElPropertyValue, Property {
return jsonSerialize;
}
@SuppressWarnings(value = "unchecked")
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
/**
* JSON write the property for 'insert only depth'.
*/
@SuppressWarnings("unchecked")
public void jsonWriteForInsert(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
if (!jsonSerialize) {
return;
}
Object value = getValueIntercept(bean);
Object value = getValue(bean);
if (value != null) {
jsonWriteScalar(writeJson, value);
}
}
/**
* JSON write the property value.
*/
public void jsonWriteValue(SpiJsonWriter writeJson, Object value) throws IOException {
if (!jsonSerialize) {
return;
}
jsonWriteVal(writeJson, value);
}
/**
* JSON write the bean property.
*/
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
if (!jsonSerialize) {
return;
}
jsonWriteVal(writeJson, getValueIntercept(bean));
}
@SuppressWarnings("unchecked")
private void jsonWriteVal(SpiJsonWriter writeJson, Object value) throws IOException {
if (value == null) {
writeJson.writeNullField(name);
} else {
if (scalarType != null) {
writeJson.writeFieldName(name);
scalarType.jsonWrite(writeJson.gen(), value);
} else {
writeJson.writeValueUsingObjectMapper(name, value);
}
jsonWriteScalar(writeJson, value);
}
}
private void jsonWriteScalar(SpiJsonWriter writeJson, Object value) throws IOException {
if (scalarType != null) {
writeJson.writeFieldName(name);
scalarType.jsonWrite(writeJson.gen(), value);
} else {
writeJson.writeValueUsingObjectMapper(name, value);
}
}
@@ -1407,17 +1441,6 @@ public class BeanProperty implements ElPropertyValue, Property {
}
}
/**
* Populate diff map for insert if the property is not null.
*/
public void diffForInsert(String prefix, Map<String, ValuePair> map, EntityBean newBean) {
Object newVal = (newBean == null) ? null : getValue(newBean);
if (newVal != null) {
String propName = (prefix == null) ? name : prefix + "." + name;
map.put(propName, new ValuePair(newVal, null));
}
}
/**
* Populate diff map comparing the property values between the beans.
*/
@@ -20,7 +20,7 @@ import io.ebeaninternal.server.el.ElPropertyChainBuilder;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.query.SqlBeanLoad;
import io.ebeaninternal.server.text.json.ReadJson;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -263,7 +263,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
Object value = getValue(bean);
if (value instanceof BeanCollection) {
// reset the collection back to empty
((BeanCollection) value).reset(bean, name);
((BeanCollection<?>) value).reset(bean, name);
} else {
createReference(bean);
}
@@ -962,8 +962,15 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
return null != targetDescriptor.getId(otherBean);
}
/**
* Skip JSON write value for ToMany property.
*/
public void jsonWriteValue(SpiJsonWriter writeJson, Object value) throws IOException {
// do nothing, exclude ToMany properties
}
@Override
public void jsonWrite(WriteJson ctx, EntityBean bean) throws IOException {
public void jsonWrite(SpiJsonWriter ctx, EntityBean bean) throws IOException {
if (!this.jsonSerialize) {
return;
}
@@ -49,7 +49,7 @@ public class BeanPropertyAssocManyJsonHelp {
return;
}
if (JsonToken.START_ARRAY != event) {
throw new JsonParseException("Unexpected token " + event + " - expecting start_array ", parser.getCurrentLocation());
throw new JsonParseException(parser, "Unexpected token " + event + " - expecting start_array ");
}
if (many.isTransient()) {
@@ -18,7 +18,7 @@ import io.ebeaninternal.server.query.SplitName;
import io.ebeaninternal.server.query.SqlBeanLoad;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.text.json.ReadJson;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import javax.persistence.PersistenceException;
import java.io.IOException;
@@ -351,22 +351,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
return importedPrimaryKey;
}
@Override
public void diffForInsert(String prefix, Map<String, ValuePair> map, EntityBean newBean) {
Object newEmb = (newBean == null) ? null : getValue(newBean);
if (newEmb != null) {
prefix = (prefix == null) ? name : prefix + "." + name;
if (embedded) {
getTargetDescriptor().diffForInsert(prefix, map, (EntityBean) newEmb);
} else {
// we are only interested in the Id value
BeanDescriptor<T> targetDescriptor = getTargetDescriptor();
BeanProperty idProperty = targetDescriptor.getIdProperty();
idProperty.diffForInsert(prefix, map, (EntityBean) newEmb);
}
}
}
@Override
public void diff(String prefix, Map<String, ValuePair> map, EntityBean newBean, EntityBean oldBean) {
@@ -625,13 +609,14 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
/**
* Set the owner on the embedded bean property.
* For embedded bean set the owner and all properties to be loaded (recursively).
*/
void setEmbeddedOwner(EntityBean owner) {
void setAllLoadedEmbedded(EntityBean owner) {
Object emb = getValue(owner);
if (emb != null) {
setEmbeddedOwner(owner, emb);
EntityBean embeddedBean = (EntityBean) emb;
embeddedBean._ebean_getIntercept().setEmbeddedOwner(owner, propertyIndex);
targetDescriptor.setAllLoaded(embeddedBean);
}
}
@@ -676,8 +661,57 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
}
/**
* JSON write property (non-recursive to other beans).
*/
@Override
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
public void jsonWriteForInsert(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
if (!jsonSerialize) {
return;
}
jsonWriteBean(writeJson, getValue(bean));
}
/**
* JSON write property value (non-recursive to other beans).
*/
@Override
public void jsonWriteValue(SpiJsonWriter writeJson, Object value) throws IOException {
if (!jsonSerialize) {
return;
}
jsonWriteBean(writeJson, value);
}
private void jsonWriteBean(SpiJsonWriter writeJson, Object value) throws IOException {
if (value instanceof EntityBean) {
if (embedded) {
writeJson.writeFieldName(name);
BeanDescriptor<?> refDesc = descriptor.getBeanDescriptor(value.getClass());
refDesc.jsonWriteForInsert(writeJson, (EntityBean)value);
} else {
jsonWriteTargetId(writeJson, (EntityBean)value);
}
}
}
/**
* Just write the Id property of the ToOne property.
*/
private void jsonWriteTargetId(SpiJsonWriter writeJson, EntityBean childBean) throws IOException {
BeanProperty idProperty = targetDescriptor.getIdProperty();
if (idProperty != null) {
writeJson.writeStartObject(name);
idProperty.jsonWriteForInsert(writeJson, childBean);
writeJson.writeEndObject();
}
}
@Override
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
if (!jsonSerialize) {
return;
@@ -688,7 +722,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
writeJson.writeNullField(name);
} else {
//noinspection StatementWithEmptyBody
if (writeJson.isParentBean(value)) {
// bi-directional and already rendered parent
@@ -8,7 +8,7 @@ import io.ebean.bean.BeanCollectionAdd;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.EntityBean;
import io.ebean.common.BeanSet;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import java.io.IOException;
import java.util.LinkedHashSet;
@@ -124,7 +124,7 @@ public final class BeanSetHelp<T> implements BeanCollectionHelp<T> {
}
@Override
public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException {
public void jsonWrite(SpiJsonWriter ctx, String name, Object collection, boolean explicitInclude) throws IOException {
Set<?> set;
if (collection instanceof BeanCollection<?>) {
@@ -1,15 +1,13 @@
package io.ebeaninternal.server.deploy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
/**
* Class to hold the DDL-migration information that is needed to do correct alters.
*
*
* @author Roland Praml, FOCONIS AG
*/
public class DbMigrationInfo {
@@ -18,8 +16,8 @@ public class DbMigrationInfo {
private final List<String> postAdd;
private final List<String> preAlter;
private final List<String> postAlter;
private final List<Platform> platforms;
private final List<Platform> platforms;
public DbMigrationInfo(String[] preAdd, String[] postAdd, String[] preAlter, String[] postAlter, Platform[] platforms) {
this.preAdd = toList(preAdd);
this.postAdd = toList(postAdd);
@@ -35,7 +33,7 @@ public class DbMigrationInfo {
return Collections.unmodifiableList(Arrays.asList(scripts));
}
}
public List<String> getPreAdd() {
return preAdd;
}
@@ -51,7 +49,7 @@ public class DbMigrationInfo {
public List<Platform> getPlatforms() {
return platforms;
}
public String joinPlatforms() {
if (platforms.isEmpty()) {
return null;
@@ -9,6 +9,7 @@ import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.BeanPropertySimpleCollection;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import io.ebeaninternal.server.type.ScalarTypeString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -345,6 +346,36 @@ public class DeployBeanPropertyLists {
return tenant;
}
/**
* Return the properties set via generated values on insert.
*/
public BeanProperty[] getGeneratedInsert() {
List<BeanProperty> list = new ArrayList<>();
for (BeanProperty prop : nonTransients) {
GeneratedProperty gen = prop.getGeneratedProperty();
if (gen != null && gen.includeInInsert()) {
list.add(prop);
}
}
return list.toArray(new BeanProperty[list.size()]);
}
/**
* Return the properties set via generated values on update.
*/
public BeanProperty[] getGeneratedUpdate() {
List<BeanProperty> list = new ArrayList<>();
for (BeanProperty prop : nonTransients) {
GeneratedProperty gen = prop.getGeneratedProperty();
if (gen != null && gen.includeInUpdate()) {
list.add(prop);
}
}
return list.toArray(new BeanProperty[list.size()]);
}
/**
* Mode used to determine which BeanPropertyAssoc to include.
*/
@@ -199,7 +199,11 @@ public class AnnotationAssocOnes extends AnnotationParser {
prop.setEmbedded();
prop.setDbInsertable(true);
prop.setDbUpdateable(true);
prop.setColumnPrefix(embedded.prefix());
try {
prop.setColumnPrefix(embedded.prefix());
} catch (NoSuchMethodError e) {
// using standard JPA API without prefix option, maybe in EE container
}
readEmbeddedAttributeOverrides(prop);
}
@@ -1,6 +1,6 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.annotation.Formula;
import io.ebean.annotation.Where;
import io.ebean.config.NamingConvention;
@@ -21,28 +21,28 @@ import java.util.concurrent.ConcurrentMap;
/**
* Provides some base methods for processing deployment annotations. All findAnnotation* methods
* are capable to search for meta-annotations (annotation that has an other annotation)
*
* are capable to search for meta-annotations (annotation that has an other annotation)
*
* <p>search algorithm for ONE annotation:</p>
* <ul>
* <li>Check if annotation is direct on the property</li>
* <li>if not found: Check all annotations at the annotateElement
* <li>if not found: Check all annotations at the annotateElement
* if they have the needed annotation as meta annotation</li>
* <li>if not found: go up to super class and try again
* <li>if not found: go up to super class and try again
* (only findAnnotationRecursive)</li>
* </ul>
* DFS (Depth-First-Search) is used. The algorithm is the same as it is used in Spring-Framework,
* DFS (Depth-First-Search) is used. The algorithm is the same as it is used in Spring-Framework,
* as the code is taken from there.
*
* <p>search algoritm for a Set&lt;Annotation&gt; works a litte bit different, as it does not stop
*
* <p>search algoritm for a Set&lt;Annotation&gt; works a litte bit different, as it does not stop
* on the first match, but continues searching down to the last corner to find all annotations.</p>
*
*
* <p>To prevent endless recursion, the search algoritm tracks all visited annotations</p>
*
*
* <p>Supports also "java 1.6 repeatable containers" like{@link JoinColumn} / {@link JoinColumns}.</p>
*
* <p>This means, searching for <code>JoinColumn</code> will find them also if they are inside a
* <code>JoinColumn<b>s</b></code> annotation</p>
*
* <p>This means, searching for <code>JoinColumn</code> will find them also if they are inside a
* <code>JoinColumn<b>s</b></code> annotation</p>
*/
public abstract class AnnotationBase {
@@ -218,10 +218,10 @@ public abstract class AnnotationBase {
while (clazz != null && clazz != Object.class) {
findMetaAnnotations(clazz, annotationType, ret, visited);
clazz = clazz.getSuperclass();
}
}
return ret;
}
/**
* Perform the search algorithm avoiding endless recursion by tracking which
* annotations have already been visited.
@@ -305,7 +305,7 @@ public abstract class AnnotationBase {
}
private static final ConcurrentMap<Annotation, Method> valueMethods = new ConcurrentHashMap<>();
// only a non-null-marker the valueMethods - Cache
// only a non-null-marker the valueMethods - Cache
private static final Method nullMethod = getNullMethod();
@@ -49,8 +49,6 @@ public class DeployUtil {
private static final int DEFAULT_JSON_VARCHAR_LENGTH = 3000;
private static final int DEFAULT_ARRAY_VARCHAR_LENGTH = 1000;
private final NamingConvention namingConvention;
private final TypeManager typeManager;
@@ -59,6 +59,7 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
List<SpiExpression> list = exprList.list;
if (list.size() == 1 && list.get(0) instanceof JunctionExpression) {
@SuppressWarnings("rawtypes")
JunctionExpression nested = (JunctionExpression) list.get(0);
if (type == Type.AND && !nested.type.isText()) {
// and (and (a, b, c)) -> and (a, b, c)
@@ -204,7 +205,7 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public boolean isSameByBind(SpiExpression other) {
JunctionExpression that = (JunctionExpression) other;
JunctionExpression<?> that = (JunctionExpression<?>) other;
return type == that.type && exprList.isSameByBind(that.exprList);
}
@@ -92,7 +92,7 @@ public final class DeleteMeta {
}
boolean publish = request.isPublish();
switch (request.determineConcurrencyMode()) {
switch (request.getConcurrencyMode()) {
case NONE:
return publish ? sqlNone : sqlDraftNone;
@@ -100,7 +100,7 @@ public final class DeleteMeta {
return publish ? sqlVersion : sqlDraftVersion;
default:
throw new RuntimeException("Invalid mode " + request.determineConcurrencyMode());
throw new RuntimeException("Invalid mode " + request.getConcurrencyMode());
}
}
@@ -77,7 +77,7 @@ public class MetaFactory {
DeleteMeta createDelete(BeanDescriptor<?> desc) {
BindableId id = idFact.createId(desc);
Bindable version = versionFact.create(desc);
Bindable version = versionFact.createForDelete(desc);
Bindable tenantId = versionFact.createTenantId(desc);
return new DeleteMeta(emptyStringAsNull, desc, id, version, tenantId);
@@ -93,9 +93,7 @@ public final class UpdateMeta {
return getDynamicUpdatePlan(request);
}
// 'full bean' update...
ConcurrencyMode mode = request.determineConcurrencyMode();
switch (mode) {
switch (request.getConcurrencyMode()) {
case NONE:
return modeNoneUpdatePlan;
@@ -103,7 +101,7 @@ public final class UpdateMeta {
return modeVersionUpdatePlan;
default:
throw new RuntimeException("Invalid mode " + mode);
throw new RuntimeException("Invalid mode " + request.getConcurrencyMode());
}
}
@@ -125,7 +123,7 @@ public final class UpdateMeta {
set.addToUpdate(persistRequest, list);
BindableList bindableList = new BindableList(list);
ConcurrencyMode mode = persistRequest.determineConcurrencyMode();
ConcurrencyMode mode = persistRequest.getConcurrencyMode();
// build the SQL for this update statement
String sql = genSql(mode, bindableList, persistRequest.getUpdateTable());
@@ -1,37 +0,0 @@
package io.ebeaninternal.server.persist.dmlbind;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import java.sql.SQLException;
import java.util.List;
/**
* Bindable for generated ManyToOne - likely 'who created'.
*/
class BindableAssocOneGeneratedInsert extends BindableAssocOne {
private final GeneratedProperty generatedProperty;
BindableAssocOneGeneratedInsert(BeanPropertyAssocOne<?> assocOne) {
super(assocOne);
this.generatedProperty = assocOne.getGeneratedProperty();
}
@Override
public void addToUpdate(PersistRequestBean<?> request, List<Bindable> list) {
throw new RuntimeException("never called");
}
@Override
public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
Object objectValue = generatedProperty.getInsertValue(assocOne, bean, request.now());
EntityBean generatedValue = castToEntityBean(objectValue);
assocOne.setValue(bean, generatedValue);
registerDeferred(request, bean, generatedValue);
}
}
@@ -1,39 +0,0 @@
package io.ebeaninternal.server.persist.dmlbind;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import java.sql.SQLException;
import java.util.List;
/**
* Bindable for generated ManyToOne - likely 'who modified'.
*/
class BindableAssocOneGeneratedUpdate extends BindableAssocOne {
private final GeneratedProperty generatedProperty;
BindableAssocOneGeneratedUpdate(BeanPropertyAssocOne<?> assocOne) {
super(assocOne);
this.generatedProperty = assocOne.getGeneratedProperty();
}
@Override
public void addToUpdate(PersistRequestBean<?> request, List<Bindable> list) {
if (generatedProperty.includeInAllUpdates() || request.isAddToUpdate(assocOne)) {
list.add(this);
}
}
@Override
public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
Object objectValue = generatedProperty.getUpdateValue(assocOne, bean, request.now());
EntityBean generatedValue = castToEntityBean(objectValue);
assocOne.setValueChanged(bean, generatedValue);
registerDeferred(request, bean, generatedValue);
}
}
@@ -1,47 +0,0 @@
package io.ebeaninternal.server.persist.dmlbind;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import io.ebeaninternal.server.persist.dml.GenerateDmlRequest;
import java.sql.SQLException;
/**
* Bindable for insert on a property with a GeneratedProperty.
* <p>
* This is typically a 'insert timestamp', 'update timestamp' or 'counter'.
* </p>
*/
public class BindablePropertyInsertGenerated extends BindableProperty {
private final GeneratedProperty gen;
public BindablePropertyInsertGenerated(BeanProperty prop, GeneratedProperty gen) {
super(prop);
this.gen = gen;
}
@Override
public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
Object value = gen.getInsertValue(prop, bean, request.now());
// generated value should be the correct type
if (bean != null) {
// support PropertyChangeSupport
//prop.setValueIntercept(bean, value);
prop.setValue(bean, value);
}
request.bind(value, prop);
}
/**
* Always bind on Insert SET.
*/
@Override
public void dmlAppend(GenerateDmlRequest request) {
request.appendColumn(prop.getDbColumn());
}
}
@@ -1,64 +0,0 @@
package io.ebeaninternal.server.persist.dmlbind;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import io.ebeaninternal.server.persist.dml.GenerateDmlRequest;
import java.sql.SQLException;
import java.util.List;
/**
* Bindable for update on a property with a GeneratedProperty.
* <p>
* This is typically a 'update timestamp' or 'counter'.
* </p>
*/
public class BindablePropertyUpdateGenerated extends BindableProperty {
private final GeneratedProperty gen;
public BindablePropertyUpdateGenerated(BeanProperty prop, GeneratedProperty gen) {
super(prop);
this.gen = gen;
}
/**
* Add BindablePropertyUpdateGenerated if the property is loaded.
*/
@Override
public void addToUpdate(PersistRequestBean<?> request, List<Bindable> list) {
if (gen.includeInAllUpdates() || request.isLoadedProperty(prop)) {
list.add(this);
}
}
@Override
public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
Object value = gen.getUpdateValue(prop, bean, request.now());
// generated value should be the correct type
request.bind(value, prop);
if (prop.isVersion()) {
if (request.getPersistRequest().isLoadedProperty(prop)) {
// set to the bean after the where clause has been generated
request.registerGeneratedVersion(value);
}
} else {
// @WhenModified set without invoking interception
prop.setValueChanged(bean, value);
}
}
/**
* Always bind on Insert SET.
*/
@Override
public void dmlAppend(GenerateDmlRequest request) {
request.appendColumn(prop.getDbColumn());
}
}
@@ -0,0 +1,54 @@
package io.ebeaninternal.server.persist.dmlbind;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.persist.dml.GenerateDmlRequest;
import java.sql.SQLException;
import java.util.List;
/**
* Bindable for a Version BeanProperty. Obtains value from 'old values'.
*/
public class BindablePropertyVersion implements Bindable {
protected final BeanProperty prop;
public BindablePropertyVersion(BeanProperty prop) {
this.prop = prop;
}
@Override
public String toString() {
return prop.toString();
}
@Override
public boolean isDraftOnly() {
return prop.isDraftOnly();
}
@Override
public void addToUpdate(PersistRequestBean<?> request, List<Bindable> list) {
if (request.isAddToUpdate(prop)) {
list.add(this);
}
}
@Override
public void dmlAppend(GenerateDmlRequest request) {
request.appendColumn(prop.getDbColumn());
}
/**
* Normal binding of a property value from the bean.
*/
@Override
public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
// get prior version value from 'old values'
Object value = bean._ebean_getIntercept().getOrigValue(prop.getPropertyIndex());
request.bind(value, prop);
}
}
@@ -35,24 +35,7 @@ public class FactoryAssocOnes {
}
break;
}
if (one.getGeneratedProperty() == null) {
list.add(new BindableAssocOne(one));
} else {
// typically generated 'who' created/modified properties
switch (mode) {
case INSERT:
if (one.getGeneratedProperty().includeInInsert()) {
list.add(new BindableAssocOneGeneratedInsert(one));
}
break;
case UPDATE:
if (one.getGeneratedProperty().includeInUpdate()) {
// A 'Who Created property' is never updated
list.add(new BindableAssocOneGeneratedUpdate(one));
}
break;
}
}
list.add(new BindableAssocOne(one));
}
}
}
@@ -40,26 +40,6 @@ public class FactoryProperty {
}
}
GeneratedProperty gen = prop.getGeneratedProperty();
if (gen != null) {
if (DmlMode.INSERT.equals(mode)) {
if (gen.includeInInsert()) {
return new BindablePropertyInsertGenerated(prop, gen);
} else {
return null;
}
}
if (DmlMode.UPDATE.equals(mode)) {
if (gen.includeInUpdate()) {
return new BindablePropertyUpdateGenerated(prop, gen);
} else {
// An 'Insert Timestamp' is never updated
return null;
}
}
}
return prop.isDbEncrypted() ? new BindableEncryptedProperty(prop, bindEncryptDataFirst) : new BindableProperty(prop);
}
}
@@ -17,6 +17,15 @@ public class FactoryVersion {
*/
public Bindable create(BeanDescriptor<?> desc) {
BeanProperty versionProperty = desc.getVersionProperty();
return (versionProperty == null) ? null : new BindablePropertyVersion(versionProperty);
}
/**
* Create a Bindable for the version property(s) for a bean type.
*/
public Bindable createForDelete(BeanDescriptor<?> desc) {
BeanProperty versionProperty = desc.getVersionProperty();
return (versionProperty == null) ? null : new BindableProperty(versionProperty);
}
@@ -36,7 +36,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* An object that represents a SqlSelect statement.
@@ -561,16 +560,28 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
return collection;
}
/**
* Update execution stats and check for slow query.
*/
void updateExecutionStatistics() {
try {
long exeNano = System.nanoTime() - startNano;
executionTimeMicros = TimeUnit.NANOSECONDS.toMicros(exeNano);
updateStatistics();
request.slowQueryCheck(executionTimeMicros, rowCount);
}
/**
* Update execution stats but skip slow query check as expected large query.
*/
void updateExecutionStatisticsIterator() {
updateStatistics();
}
private void updateStatistics() {
try {
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
if (autoTuneProfiling) {
profilingListener.collectQueryInfo(objectGraphNode, loadedBeanCount, executionTimeMicros);
}
queryPlan.executionTime(loadedBeanCount, executionTimeMicros, objectGraphNode);
} catch (Exception e) {
logger.error("Error updating execution statistics", e);
}
@@ -1,6 +1,6 @@
package io.ebeaninternal.server.query;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.RawSql;
import io.ebean.RawSql.ColumnMapping;
import io.ebean.RawSql.ColumnMapping.Column;
@@ -50,7 +50,7 @@ class CQueryFetchSingleAttribute {
private String bindLog;
private int executionTimeMicros;
private long executionTimeMicros;
private int rowCount;
@@ -91,20 +91,17 @@ class CQueryFetchSingleAttribute {
long startNano = System.nanoTime();
try {
prepareExecute();
List<Object> result = new ArrayList<>();
while (dataReader.next()) {
result.add(scalarType.read(dataReader));
dataReader.resetColumnPosition();
rowCount++;
}
long exeNano = System.nanoTime() - startNano;
executionTimeMicros = (int) exeNano / 1000;
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
request.slowQueryCheck(executionTimeMicros, rowCount);
return result;
} finally {
@@ -38,7 +38,7 @@ class CQueryIteratorSimple<T> implements QueryIterator<T> {
@Override
public void close() {
cquery.updateExecutionStatistics();
cquery.updateExecutionStatisticsIterator();
cquery.close();
request.endTransIfRequired();
}
@@ -58,7 +58,7 @@ class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
@Override
public void close() {
cquery.updateExecutionStatistics();
cquery.updateExecutionStatisticsIterator();
cquery.close();
request.endTransIfRequired();
}
@@ -47,7 +47,7 @@ class CQueryRowCount {
private String bindLog;
private int executionTimeMicros;
private long executionTimeMicros;
private int rowCount;
@@ -99,7 +99,6 @@ class CQueryRowCount {
long startNano = System.nanoTime();
try {
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
pstmt = conn.prepareStatement(sql);
@@ -110,16 +109,14 @@ class CQueryRowCount {
bindLog = predicates.bind(pstmt, conn);
rset = pstmt.executeQuery();
if (!rset.next()) {
throw new PersistenceException("Expecting 1 row but got none?");
}
rowCount = rset.getInt(1);
long exeNano = System.nanoTime() - startNano;
executionTimeMicros = (int) exeNano / 1000;
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
request.slowQueryCheck(executionTimeMicros, rowCount);
return rowCount;
} finally {
@@ -39,7 +39,7 @@ class CQueryUpdate {
private String bindLog;
private int executionTimeMicros;
private long executionTimeMicros;
private int rowCount;
@@ -90,7 +90,6 @@ class CQueryUpdate {
long startNano = System.nanoTime();
try {
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
pstmt = conn.prepareStatement(sql);
@@ -102,9 +101,8 @@ class CQueryUpdate {
bindLog = predicates.bind(pstmt, conn);
rowCount = pstmt.executeUpdate();
long exeNano = System.nanoTime() - startNano;
executionTimeMicros = (int) exeNano / 1000;
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
request.slowQueryCheck(executionTimeMicros, rowCount);
return rowCount;
} finally {
@@ -147,7 +147,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
return children[0].getSingleAttributeScalarType();
}
if (properties[0] instanceof BeanPropertyAssocOne<?>) {
BeanPropertyAssocOne assocOne = (BeanPropertyAssocOne<?>)properties[0];
BeanPropertyAssocOne<?> assocOne = (BeanPropertyAssocOne<?>)properties[0];
if (assocOne.isAssocId()) {
return assocOne.getTargetDescriptor().getIdProperty().getScalarType();
}
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.querydefn;
import io.ebeaninternal.api.HashQueryPlanBuilder;
import io.ebeaninternal.server.deploy.DeployParser;
import io.ebeaninternal.server.persist.Binder;
import io.ebeaninternal.server.type.DataBind;
@@ -190,17 +189,4 @@ public class OrmUpdateProperties {
return sb.toString();
}
/**
* Return a logical set clause to use for isSameByPlan() use.
*/
private String logicalSetClause() {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Value> entry : values.entrySet()) {
sb.append(", ");
sb.append(entry.getKey()).append(entry.getValue().bindClause());
}
return sb.toString();
}
}
@@ -1,26 +1,26 @@
package io.ebeaninternal.server.text.json;
import io.ebean.FetchPath;
import io.ebean.bean.EntityBean;
import io.ebean.config.JsonConfig;
import io.ebean.plugin.BeanType;
import io.ebean.text.json.EJson;
import io.ebean.text.json.JsonContext;
import io.ebean.text.json.JsonIOException;
import io.ebean.text.json.JsonReadOptions;
import io.ebean.text.json.JsonWriteBeanVisitor;
import io.ebean.text.json.JsonWriteOptions;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.type.TypeManager;
import io.ebeaninternal.util.ParamTypeHelper;
import io.ebeaninternal.util.ParamTypeHelper.ManyType;
import io.ebeaninternal.util.ParamTypeHelper.TypeInfo;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import io.ebean.FetchPath;
import io.ebean.bean.EntityBean;
import io.ebean.config.JsonConfig;
import io.ebean.plugin.BeanType;
import io.ebean.text.json.EJson;
import io.ebean.text.json.JsonIOException;
import io.ebean.text.json.JsonReadOptions;
import io.ebean.text.json.JsonWriteBeanVisitor;
import io.ebean.text.json.JsonWriteOptions;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiJsonContext;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.type.TypeManager;
import io.ebeaninternal.util.ParamTypeHelper;
import io.ebeaninternal.util.ParamTypeHelper.ManyType;
import io.ebeaninternal.util.ParamTypeHelper.TypeInfo;
import java.io.IOException;
import java.io.Reader;
@@ -39,7 +39,7 @@ import java.util.Set;
/**
* Default implementation of JsonContext.
*/
public class DJsonContext implements JsonContext {
public class DJsonContext implements SpiJsonContext {
private final SpiEbeanServer server;
@@ -179,7 +179,7 @@ public class DJsonContext implements JsonContext {
if (currentToken != JsonToken.START_ARRAY) {
JsonToken event = src.nextToken();
if (event != JsonToken.START_ARRAY) {
throw new JsonParseException("Expecting start_array event but got " + event, src.getCurrentLocation());
throw new JsonParseException(src, "Expecting start_array event but got " + event);
}
}
@@ -336,9 +336,23 @@ public class DJsonContext implements JsonContext {
BeanDescriptor<?> d = getDescriptor(value.getClass());
WriteJson writeJson = createWriteJson(gen, options);
d.jsonWrite(writeJson, (EntityBean) value, null);
} else {
jsonScalar.write(gen, value);
}
}
@Override
public SpiJsonWriter createJsonWriter(Writer writer) {
JsonGenerator generator = createGenerator(writer);
return createJsonWriter(generator, null);
}
@Override
public SpiJsonWriter createJsonWriter(JsonGenerator gen, JsonWriteOptions options) {
return createWriteJson(gen, options);
}
private WriteJson createWriteJson(JsonGenerator gen, JsonWriteOptions options) {
FetchPath pathProps = (options == null) ? null : options.getPathProperties();
Map<String, JsonWriteBeanVisitor<?>> visitors = (options == null) ? null : options.getVisitorMap();
@@ -0,0 +1,68 @@
package io.ebeaninternal.server.text.json;
import io.ebean.bean.EntityBean;
import io.ebean.text.json.JsonWriter;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import java.util.Collection;
/**
* Internal API extensions for JSON writing of Bean properties.
*/
public interface SpiJsonWriter extends JsonWriter {
/**
* Return true if the value is a parent bean.
*/
boolean isParentBean(Object value);
/**
* Start an assoc one path.
*/
void beginAssocOne(String name, EntityBean bean);
/**
* End an assoc one path.
*/
void endAssocOne();
/**
* Return true if the many property should be included.
*/
Boolean includeMany(String name);
/**
* Push the parent bean of a ToMany.
*/
void pushParentBeanMany(EntityBean bean);
/**
* Pop the parent of a ToMany.
*/
void popParentBeanMany();
/**
* Write the collection.
*/
void toJson(String name, Collection<?> collection);
/**
* Start a Many.
*/
void beginAssocMany(String name);
/**
* End a Many.
*/
void endAssocMany();
/**
* Write value using underlying Jaskson object mapper if available.
*/
void writeValueUsingObjectMapper(String name, Object value);
/**
* Write the bean properties.
*/
<T> void writeBean(BeanDescriptor<T> desc, EntityBean bean);
}
@@ -1,18 +1,17 @@
package io.ebeaninternal.server.text.json;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.ebean.FetchPath;
import io.ebean.bean.EntityBean;
import io.ebean.config.JsonConfig;
import io.ebean.text.json.EJson;
import io.ebean.text.json.JsonIOException;
import io.ebean.text.json.JsonWriteBeanVisitor;
import io.ebean.text.json.JsonWriter;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.util.ArrayStack;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.InputStream;
@@ -21,7 +20,7 @@ import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class WriteJson implements JsonWriter {
public class WriteJson implements SpiJsonWriter {
private final SpiEbeanServer server;
@@ -347,7 +346,7 @@ public class WriteJson implements JsonWriter {
return !parentBeans.isEmpty() && parentBeans.contains(bean);
}
public void pushParentBeanMany(Object parentBean) {
public void pushParentBeanMany(EntityBean parentBean) {
parentBeans.push(parentBean);
}
@@ -355,7 +354,7 @@ public class WriteJson implements JsonWriter {
parentBeans.pop();
}
public void beginAssocOne(String key, Object bean) {
public void beginAssocOne(String key, EntityBean bean) {
parentBeans.push(bean);
pathStack.pushPathKey(key);
}
@@ -384,10 +383,15 @@ public class WriteJson implements JsonWriter {
}
}
public WriteBean createWriteBean(BeanDescriptor<?> desc, EntityBean bean) {
@Override
public <T> void writeBean(BeanDescriptor<T> desc, EntityBean bean) {
createWriteBean(desc, bean).write(this);
}
private <T> WriteBean createWriteBean(BeanDescriptor<T> desc, EntityBean bean) {
String path = pathStack.peekWithNull();
JsonWriteBeanVisitor visitor = (visitors == null) ? null : visitors.get(path);
JsonWriteBeanVisitor<?> visitor = (visitors == null) ? null : visitors.get(path);
if (fetchPath == null) {
return new WriteBean(desc, bean, visitor);
}
@@ -407,10 +411,10 @@ public class WriteJson implements JsonWriter {
if (!isIncludeEmpty()) {
// check for suppression of empty collection or map
if (value instanceof Collection && ((Collection) value).isEmpty()) {
if (value instanceof Collection && ((Collection<?>) value).isEmpty()) {
// suppress empty collection
return;
} else if (value instanceof Map && ((Map) value).isEmpty()) {
} else if (value instanceof Map && ((Map<?,?>) value).isEmpty()) {
// suppress empty map
return;
}
@@ -436,13 +440,15 @@ public class WriteJson implements JsonWriter {
final Set<String> currentIncludeProps;
final BeanDescriptor<?> desc;
final EntityBean currentBean;
@SuppressWarnings("rawtypes")
final JsonWriteBeanVisitor visitor;
WriteBean(BeanDescriptor<?> desc, EntityBean currentBean, JsonWriteBeanVisitor visitor) {
WriteBean(BeanDescriptor<?> desc, EntityBean currentBean, JsonWriteBeanVisitor<?> visitor) {
this(desc, false, null, currentBean, visitor);
}
WriteBean(BeanDescriptor<?> desc, boolean explicitAllProps, Set<String> currentIncludeProps, EntityBean currentBean, JsonWriteBeanVisitor visitor) {
WriteBean(BeanDescriptor<?> desc, boolean explicitAllProps, Set<String> currentIncludeProps, EntityBean currentBean, JsonWriteBeanVisitor<?> visitor) {
super();
this.desc = desc;
this.currentBean = currentBean;
@@ -3,7 +3,7 @@ package io.ebeaninternal.server.transaction;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.bean.PersistenceContext;
import io.ebean.PersistBatch;
import io.ebean.annotation.PersistBatch;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform.OnQueryOnly;
import io.ebean.event.changelog.BeanChange;
@@ -2,7 +2,7 @@ package io.ebeaninternal.server.transaction;
import io.ebean.BackgroundExecutor;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.PersistBatch;
import io.ebean.annotation.PersistBatch;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DatabasePlatform.OnQueryOnly;
import io.ebean.event.changelog.ChangeLogListener;
@@ -5,7 +5,7 @@ import io.ebean.annotation.DbEnumType;
import io.ebean.annotation.DbEnumValue;
import io.ebean.annotation.EnumValue;
import io.ebean.config.JsonConfig;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.config.ScalarTypeConverter;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
@@ -239,8 +239,8 @@ public final class DefaultTypeManager implements TypeManager {
if (iterator.hasNext()) {
// use the cacheFactory (via classpath service loader)
ExtraTypeFactory plugin = iterator.next();
List<? extends ScalarType> types = plugin.createTypes(config, objectMapper);
for (ScalarType type : types) {
List<? extends ScalarType<?>> types = plugin.createTypes(config, objectMapper);
for (ScalarType<?> type : types) {
logger.debug("adding ScalarType {}", type.getClass());
addCustomType(type);
}
@@ -280,10 +280,10 @@ public final class DefaultTypeManager implements TypeManager {
}
private void logAdd(ScalarType<?> scalarType) {
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
String msg = "ScalarType register [" + scalarType.getClass().getName() + "]";
msg += " for [" + scalarType.getType().getName() + "]";
logger.debug(msg);
logger.trace(msg);
}
}
@@ -19,13 +19,13 @@ import java.util.UUID;
/**
* Type mapped for DB ARRAY type (Postgres only effectively).
*/
public class ScalarTypeArraySet extends ScalarTypeJsonCollection<Set> implements ScalarTypeArray {
public class ScalarTypeArraySet<T> extends ScalarTypeJsonCollection<Set<T>> implements ScalarTypeArray {
private static ScalarTypeArraySet UUID = new ScalarTypeArraySet("uuid", DocPropertyType.UUID, ArrayElementConverter.UUID);
private static ScalarTypeArraySet LONG = new ScalarTypeArraySet("bigint", DocPropertyType.LONG, ArrayElementConverter.LONG);
private static ScalarTypeArraySet INTEGER = new ScalarTypeArraySet("integer", DocPropertyType.INTEGER, ArrayElementConverter.INTEGER);
private static ScalarTypeArraySet DOUBLE = new ScalarTypeArraySet("float", DocPropertyType.DOUBLE, ArrayElementConverter.DOUBLE);
private static ScalarTypeArraySet STRING = new ScalarTypeArraySet("varchar", DocPropertyType.TEXT, ArrayElementConverter.STRING);
private static final ScalarTypeArraySet<UUID> UUID = new ScalarTypeArraySet<>("uuid", DocPropertyType.UUID, ArrayElementConverter.UUID);
private static final ScalarTypeArraySet<Long> LONG = new ScalarTypeArraySet<>("bigint", DocPropertyType.LONG, ArrayElementConverter.LONG);
private static final ScalarTypeArraySet<Integer> INTEGER = new ScalarTypeArraySet<>("integer", DocPropertyType.INTEGER, ArrayElementConverter.INTEGER);
private static final ScalarTypeArraySet<Double> DOUBLE = new ScalarTypeArraySet<>("float", DocPropertyType.DOUBLE, ArrayElementConverter.DOUBLE);
private static final ScalarTypeArraySet<String> STRING = new ScalarTypeArraySet<>("varchar", DocPropertyType.TEXT, ArrayElementConverter.STRING);
static PlatformArrayTypeFactory factory() {
return new Factory();
@@ -37,7 +37,7 @@ public class ScalarTypeArraySet extends ScalarTypeJsonCollection<Set> implements
* Return the ScalarType to use based on the List's generic parameter type.
*/
@Override
public ScalarTypeArraySet typeFor(Type valueType) {
public ScalarTypeArraySet<?> typeFor(Type valueType) {
if (valueType.equals(UUID.class)) {
return UUID;
}
@@ -59,10 +59,11 @@ public class ScalarTypeArraySet extends ScalarTypeJsonCollection<Set> implements
private final String arrayType;
private final ArrayElementConverter converter;
private final ArrayElementConverter<T> converter;
public ScalarTypeArraySet(String arrayType, DocPropertyType docPropertyType, ArrayElementConverter converter) {
super(Set.class, Types.ARRAY, docPropertyType);
@SuppressWarnings({ "unchecked", "rawtypes" })
public ScalarTypeArraySet(String arrayType, DocPropertyType docPropertyType, ArrayElementConverter<T> converter) {
super((Class)Set.class, Types.ARRAY, docPropertyType);
this.arrayType = arrayType;
this.converter = converter;
}
@@ -80,21 +81,20 @@ public class ScalarTypeArraySet extends ScalarTypeJsonCollection<Set> implements
return arrayType + "[]";
}
@SuppressWarnings("unchecked")
private Set fromArray(Object[] array1) {
Set set = new LinkedHashSet();
private Set<T> fromArray(Object[] array1) {
Set<T> set = new LinkedHashSet<>();
for (Object element : array1) {
set.add(converter.toElement(element));
}
return new ModifyAwareSet(set);
return new ModifyAwareSet<>(set);
}
protected Object[] toArray(Set value) {
protected Object[] toArray(Set<T> value) {
return value.toArray();
}
@Override
public Set read(DataReader reader) throws SQLException {
public Set<T> read(DataReader reader) throws SQLException {
Array array = reader.getArray();
if (array == null) {
return null;
@@ -104,7 +104,7 @@ public class ScalarTypeArraySet extends ScalarTypeJsonCollection<Set> implements
}
@Override
public void bind(DataBind bind, Set value) throws SQLException {
public void bind(DataBind bind, Set<T> value) throws SQLException {
if (value == null) {
bind.setNull(Types.ARRAY);
} else {
@@ -113,7 +113,7 @@ public class ScalarTypeArraySet extends ScalarTypeJsonCollection<Set> implements
}
@Override
public String formatValue(Set value) {
public String formatValue(Set<T> value) {
try {
return EJson.write(value);
} catch (IOException e) {
@@ -122,7 +122,7 @@ public class ScalarTypeArraySet extends ScalarTypeJsonCollection<Set> implements
}
@Override
public Set parse(String value) {
public Set<T> parse(String value) {
try {
return EJson.parseSet(value, false);
} catch (IOException e) {
@@ -131,12 +131,12 @@ public class ScalarTypeArraySet extends ScalarTypeJsonCollection<Set> implements
}
@Override
public Set jsonRead(JsonParser parser) throws IOException {
public Set<T> jsonRead(JsonParser parser) throws IOException {
return EJson.parseSet(parser, parser.getCurrentToken());
}
@Override
public void jsonWrite(JsonGenerator writer, Set value) throws IOException {
public void jsonWrite(JsonGenerator writer, Set<T> value) throws IOException {
EJson.write(value, writer);
}
@@ -6,17 +6,17 @@ import java.lang.reflect.Type;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Set;
import java.util.UUID;
/**
* H2 database support for DB ARRAY.
*/
class ScalarTypeArraySetH2 extends ScalarTypeArraySet {
class ScalarTypeArraySetH2<T> extends ScalarTypeArraySet<T> {
private static ScalarTypeArraySetH2 UUID = new ScalarTypeArraySetH2("uuid", DocPropertyType.UUID, ArrayElementConverter.UUID);
private static ScalarTypeArraySetH2 LONG = new ScalarTypeArraySetH2("bigint", DocPropertyType.LONG, ArrayElementConverter.LONG);
private static ScalarTypeArraySetH2 INTEGER = new ScalarTypeArraySetH2("integer", DocPropertyType.INTEGER, ArrayElementConverter.INTEGER);
private static ScalarTypeArraySetH2 DOUBLE = new ScalarTypeArraySetH2("double", DocPropertyType.DOUBLE, ArrayElementConverter.DOUBLE);
private static ScalarTypeArraySetH2 STRING = new ScalarTypeArraySetH2("varchar", DocPropertyType.TEXT, ArrayElementConverter.STRING);
private static final ScalarTypeArraySetH2<UUID> UUID = new ScalarTypeArraySetH2<>("uuid", DocPropertyType.UUID, ArrayElementConverter.UUID);
private static final ScalarTypeArraySetH2<Long> LONG = new ScalarTypeArraySetH2<>("bigint", DocPropertyType.LONG, ArrayElementConverter.LONG);
private static final ScalarTypeArraySetH2<Integer> INTEGER = new ScalarTypeArraySetH2<>("integer", DocPropertyType.INTEGER, ArrayElementConverter.INTEGER);
private static final ScalarTypeArraySetH2<Double> DOUBLE = new ScalarTypeArraySetH2<>("double", DocPropertyType.DOUBLE, ArrayElementConverter.DOUBLE);
private static final ScalarTypeArraySetH2<String> STRING = new ScalarTypeArraySetH2<>("varchar", DocPropertyType.TEXT, ArrayElementConverter.STRING);
static PlatformArrayTypeFactory factory() {
return new ScalarTypeArraySetH2.Factory();
@@ -28,7 +28,7 @@ class ScalarTypeArraySetH2 extends ScalarTypeArraySet {
* Return the ScalarType to use based on the List's generic parameter type.
*/
@Override
public ScalarTypeArraySetH2 typeFor(Type valueType) {
public ScalarTypeArraySetH2<?> typeFor(Type valueType) {
if (valueType.equals(java.util.UUID.class)) {
return UUID;
}
@@ -48,12 +48,12 @@ class ScalarTypeArraySetH2 extends ScalarTypeArraySet {
}
}
private ScalarTypeArraySetH2(String arrayType, DocPropertyType docPropertyType, ArrayElementConverter converter) {
private ScalarTypeArraySetH2(String arrayType, DocPropertyType docPropertyType, ArrayElementConverter<T> converter) {
super(arrayType, docPropertyType, converter);
}
@Override
public void bind(DataBind bind, Set value) throws SQLException {
public void bind(DataBind bind, Set<T> value) throws SQLException {
if (value == null) {
bind.setNull(Types.ARRAY);
} else {
@@ -17,6 +17,7 @@ public interface TypeManager {
/**
* Register a ScalarType for an Enum with can have multiple classes.
*/
@SuppressWarnings("rawtypes")
void addEnumType(ScalarType<?> type, Class<? extends Enum> myEnumClass);
/**
+8 -1
View File
@@ -1,16 +1,19 @@
package io.ebean;
import io.ebean.annotation.Platform;
import io.ebean.util.StringHelper;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import org.tests.model.basic.Country;
import org.avaje.agentloader.AgentLoader;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Types;
public class BaseTestCase {
@RunWith(ConditionalTestRunner.class)
public abstract class BaseTestCase {
protected static Logger logger = LoggerFactory.getLogger(BaseTestCase.class);
@@ -75,6 +78,10 @@ public class BaseTestCase {
return Platform.ORACLE == platform();
}
public boolean isDb2() {
return Platform.DB2 == platform();
}
public boolean isPostgres() {
return Platform.POSTGRES == platform();
}
@@ -0,0 +1,48 @@
package io.ebean;
import io.ebean.annotation.ForPlatform;
import io.ebean.annotation.IgnorePlatform;
import io.ebean.annotation.Platform;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
/**
* This testrunner checks for an {@link IgnorePlatform} annotation and ignores the test.
*
* @author Roland Praml, FOCONIS AG
*/
public class ConditionalTestRunner extends BlockJUnit4ClassRunner {
public ConditionalTestRunner(Class<?> klass) throws InitializationError {
super(klass);
}
@Override
public void runChild(FrameworkMethod method, RunNotifier notifier) {
ForPlatform forPlatform = method.getAnnotation(ForPlatform.class);
if (forPlatform != null) {
if (!platformMath(forPlatform.value())) {
notifier.fireTestIgnored(describeChild(method));
return;
}
}
IgnorePlatform ignore = method.getAnnotation(IgnorePlatform.class);
if (ignore == null || !platformMath(ignore.value())) {
super.runChild(method, notifier);
} else {
notifier.fireTestIgnored(describeChild(method));
}
}
private boolean platformMath(Platform[] platforms) {
Platform current = Ebean.getDefaultServer().getPluginApi().getDatabasePlatform().getPlatform();
for (Platform p : platforms) {
if (p.equals(current)) {
return true;
}
}
return false;
}
}

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