Compare commits

..
Author SHA1 Message Date
Rob Bygrave 4ba5702211 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.3 2014-07-16 23:17:06 +12:00
Rob Bygrave 2e7aeae004 Bump version in README 2014-07-16 23:14:52 +12:00
rbygrave 3f2676ab49 Fix for #168 - ENH: DataSource - add support for configuring autocommit=true 2014-07-16 21:58:32 +12:00
rbygrave 3f29acb25c Fix for #169 - Caused by: java.lang.ClassCastException: org.example.Order cannot be cast to org.example.Customer 2014-07-15 23:09:08 +12:00
rbygrave 6d1a41f979 Fix for #167 - Ebean try to do update child nodes instead of inserting them on cascading save resulting in OptimisticLockException 2014-07-15 22:05:56 +12:00
Rob Bygrave aee46955a7 Add test which saves parent and child using same pk value 2014-07-10 23:27:18 +12:00
rbygrave 38a93b9814 Update README with new version 2014-07-10 22:41:38 +12:00
rbygrave 5f2ad0dc62 [maven-release-plugin] prepare for next development iteration 2014-07-10 21:41:01 +12:00
rbygrave ece7f481bc [maven-release-plugin] prepare release avaje-ebeanorm-4.1.2 2014-07-10 21:40:39 +12:00
rbygrave fa4d0380e5 Add extra test - TestQueryRowCountWithMany 2014-07-10 21:29:44 +12:00
rbygrave 713ba4889d Clean up of test for #162 2014-07-10 21:23:45 +12:00
rbygrave 0543745e18 Test case for #162 - no issue reproduced, no assert failing 2014-07-10 19:48:06 +12:00
rbygrave 4e2bc6cce6 Fix for #166 - Incorrect join with @OneToOne optional on one side and
NOT optional on other side
2014-07-10 03:05:52 +12:00
rbygrave f2d422ff5a Test case for #165 - Incorrect join used when query language with where
expression has a many property and ends with a foreign key column
2014-07-10 01:20:51 +12:00
rbygrave 176197513d No effective change - format only 2014-07-09 23:51:19 +12:00
rbygrave 1e3035c558 Fix for #164 - Incorrect join used when where expression has a many
property and ends with a foreign key column
2014-07-09 23:31:15 +12:00
rbygrave 17f7143447 Fix for #163 - Remove detection of autocommit=true from JdbcTransaction 2014-07-09 22:24:28 +12:00
rbygrave 394dfc96d1 Update Test - cleanup and add clarification test to TestManyWhereJoin 2014-07-09 22:23:31 +12:00
Rob Bygrave 452ce78c3d Fix for #161 - DDL Generation - @OneToOne (foreign key column / JoinColumn) should default to having a unique constraint on it 2014-07-08 23:23:46 +12:00
Rob Bygrave 4cb13760c2 Fix for #160 - DDL generation throwing NPE at com.avaje.ebeaninternal.server.ddl.CreateTableVisitor.isDbColumnWritten(CreateTableVisitor.java:60) 2014-07-08 23:04:27 +12:00
Rob Bygrave 5cc2eff0d4 Fix for #158 - Add support for using setDistinct (by excluding id property from generated sql) 2014-07-07 23:29:48 +12:00
Rob Bygrave af9a01be77 No effective change - update test 2014-07-05 23:08:09 +12:00
Rob Bygrave cceb7c035c Fix for #159 - Refactor - cleanup clode when closing query only transaction + change default option 2014-07-05 22:10:53 +12:00
Rob Bygrave 1ddc103582 Fix for #157 - Regression for @OrderBy on a @ManyToMany property due to change from issue 154 2014-07-02 23:56:42 +12:00
Rob Bygrave b973cc3b86 No effective change - imports of Assert in tests + formatting 2014-07-02 23:29:54 +12:00
Rob Bygrave 10f0ea9bae No effective change - code format and tidy up 2014-07-02 23:10:53 +12:00
Rob Bygrave c49dd54500 Improve exception throw in BeanReflectProperties when _ebean_props not found 2014-07-02 21:23:28 +12:00
Rob Bygrave bf0944bb2a [maven-release-plugin] prepare for next development iteration 2014-06-30 22:06:04 +12:00
Rob Bygrave 59ba6244c1 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.1 2014-06-30 22:05:43 +12:00
Rob Bygrave f88524b34c Fix for #156 - NPE in DDL Generation when Embedded bean as column with unique constraint 2014-06-30 22:03:31 +12:00
Rob Bygrave 6838b4df4f Fix for #155 Add markAsDirty() method 2014-06-27 23:08:36 +12:00
Rob Bygrave 5db7ae504e [maven-release-plugin] prepare for next development iteration 2014-06-26 19:39:13 +12:00
162 changed files with 4854 additions and 3476 deletions
+1 -4
View File
@@ -1,15 +1,12 @@
[![Stories in Ready](https://badge.waffle.io/ebean-orm/avaje-ebeanorm.png?label=ready&title=Ready)](https://waffle.io/ebean-orm/avaje-ebeanorm)
avaje-ebeanorm
==============
- Release - 4.0.2 - May 19th: https://github.com/ebean-orm/avaje-ebeanorm/wiki/4.0.2-Release
- Release - 4.0.3 and 4.0.4 contained some bug fixes plus the Model and Finder objects.
Maven Dependency
----------------
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>4.0.4</version>
<version>4.1.3</version>
</dependency>
+1 -1
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>4.1.0</version>
<version>4.1.3</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
+25 -1
View File
@@ -475,7 +475,31 @@ public final class Ebean {
public static void insert(Collection<?> beans) {
serverMgr.getPrimaryServer().insert(beans);
}
/**
* Marks the entity bean as dirty.
* <p>
* This is used so that when a bean that is otherwise unmodified is updated with the version
* property updated.
* <p>
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
* dirty so that it is not skipped.
*
* <pre class="code">
*
* Customer customer = Ebean.find(Customer, id);
*
* // mark the bean as dirty so that a save() or update() will
* // increment the version property
* Ebean.markAsDirty(customer);
* Ebean.save(customer);
*
* </pre>
*/
public static void markAsDirty(Object bean) throws OptimisticLockException {
serverMgr.getPrimaryServer().markAsDirty(bean);
}
/**
* Saves the bean using an update. If you know you are updating a bean then it is preferrable to
* use this update() method rather than save().
@@ -837,6 +837,28 @@ public interface EbeanServer {
*/
public int save(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
/**
* Marks the entity bean as dirty.
* <p>
* This is used so that when a bean that is otherwise unmodified is updated the version
* property is updated.
* <p>
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
* dirty so that it is not skipped.
*
* <pre class="code">
*
* Customer customer = ebeanServer.find(Customer, id);
*
* // mark the bean as dirty so that a save() or update() will
* // increment the version property
* ebeanServer.markAsDirty(customer);
* ebeanServer.save(customer);
*
* </pre>
*/
public void markAsDirty(Object bean);
/**
* Saves the bean using an update. If you know you are updating a bean then it is preferrable to
* use this update() method rather than save().
+24
View File
@@ -85,6 +85,30 @@ public abstract class Model {
return Ebean.getServer(server);
}
/**
* Marks the entity bean as dirty.
* <p>
* This is used so that when a bean that is otherwise unmodified is updated the version
* property is updated.
* <p>
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
* dirty so that it is not skipped.
*
* <pre class="code">
*
* Customer customer = Customer.find.byId(id);
*
* // mark the bean as dirty so that a save() or update() will
* // increment the version property
* customer.markAsDirty();
* customer.save();
*
* </pre>
*/
public void markAsDirty() {
db().markAsDirty(this);
}
/**
* Insert or update this entity depending on its state.
*
@@ -32,6 +32,8 @@ public class DataSourceConfig {
private int isolationLevel = Transaction.READ_COMMITTED;
private boolean autoCommit;
private String heartbeatSql;
private int heartbeatFreqSecs = 30;
@@ -131,6 +133,20 @@ public class DataSourceConfig {
public void setIsolationLevel(int isolationLevel) {
this.isolationLevel = isolationLevel;
}
/**
* Return autoCommit setting.
*/
public boolean isAutoCommit() {
return autoCommit;
}
/**
* Set to true to turn on autoCommit.
*/
public void setAutoCommit(boolean autoCommit) {
this.autoCommit = autoCommit;
}
/**
* Return the minimum number of connections the pool should maintain.
@@ -450,6 +466,7 @@ public class DataSourceConfig {
String dbUrl = properties.get(prefix + "databaseUrl", null);
this.url = properties.get(prefix + "url", dbUrl);
this.autoCommit = properties.getBoolean(prefix + "autoCommit", false);
this.captureStackTrace = properties.getBoolean(prefix + "captureStackTrace", false);
this.maxStackTraceSize = properties.getInt(prefix + "maxStackTraceSize", 5);
this.leakTimeMinutes = properties.getInt(prefix + "leakTimeMinutes", 30);
@@ -175,6 +175,13 @@ public class ServerConfig {
*/
private DataSourceConfig dataSourceConfig = new DataSourceConfig();
/**
* Set to true if the DataSource uses autoCommit.
* <p>
* Indicates that Ebean should use autoCommit friendly Transactions and TransactionManager.
*/
private boolean autoCommitMode;
/**
* The data source JNDI name if using a JNDI DataSource.
*/
@@ -582,6 +589,20 @@ public class ServerConfig {
public void setDataSourceJndiName(String dataSourceJndiName) {
this.dataSourceJndiName = dataSourceJndiName;
}
/**
* Return true if autoCommit mode is on. This indicates to Ebean to use autoCommit friendly Transactions and TransactionManager.
*/
public boolean isAutoCommitMode() {
return autoCommitMode;
}
/**
* Set to true if autoCommit mode is on and Ebean should use autoCommit friendly Transactions and TransactionManager.
*/
public void setAutoCommitMode(boolean autoCommitMode) {
this.autoCommitMode = autoCommitMode;
}
/**
* Return a value used to represent TRUE in the database.
@@ -1281,6 +1302,7 @@ public class ServerConfig {
loadDataSourceSettings(p);
autoCommitMode = p.getBoolean("autoCommitMode", false);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", false);
namingConvention = createNamingConvention(p);
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform");
@@ -11,7 +11,7 @@ public class DbDdlSyntax {
private boolean inlinePrimaryKeyConstraint = false;
private boolean addOneToOneUniqueContraint = false;
private boolean addOneToOneUniqueContraint = true;
private int maxConstraintNameLength = 32;
@@ -53,13 +53,15 @@ public class ManyWhereJoins implements Serializable {
String join = elProp.getElPrefix();
BeanProperty p = elProp.getBeanProperty();
if (p instanceof BeanPropertyAssocMany<?>){
join = addManyToJoin(join, p.getName());
join = addManyToJoin(join, p.getName());
}
if (join != null){
addJoin(join);
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
if (secondaryTableJoinPrefix != null) {
addJoin(join+"."+secondaryTableJoinPrefix);
if (p != null) {
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
if (secondaryTableJoinPrefix != null) {
addJoin(join+"."+secondaryTableJoinPrefix);
}
}
addParentJoins(join);
}
@@ -1,13 +1,12 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebean.ExpressionFactory;
import com.avaje.ebeaninternal.server.expression.FilterExprPath;
public interface SpiExpressionFactory extends ExpressionFactory {
/**
* Create another expression factory with a given sub path.
*/
public ExpressionFactory createExpressionFactory();//FilterExprPath prefix);
/**
* Create another expression factory with a given sub path.
*/
public ExpressionFactory createExpressionFactory();
}
@@ -532,12 +532,24 @@ public interface SpiQuery<T> extends Query<T> {
* Return the index of the first row to return in the query.
*/
public int getFirstRow();
/**
* Internally set by Ebean when this query must use the DISTINCT keyword.
* <p>
* This does not exclude/remove the use of the id property.
*/
public Query<T> setSqlDistinct(boolean sqlDistinct);
/**
* return true if this query uses DISTINCT.
* Return true if this query has been specified by a user or internally by Ebean to use DISTINCT.
*/
public boolean isDistinctQuery();
/**
* Return true if this query has been specified by a user to use DISTINCT.
*/
public boolean isDistinct();
/**
* Set default select clauses where none have been explicitly defined.
*/
@@ -15,6 +15,11 @@ import com.avaje.ebeaninternal.server.persist.BatchControl;
*/
public interface SpiTransaction extends Transaction {
/**
* End the transaction when had query only use.
*/
public void endQueryOnly();
/**
* Return the string prefix with the transactin id and label used in logging.
*/
@@ -32,8 +32,6 @@ public abstract class BeanRequest {
protected boolean createdTransaction;
protected boolean readOnly;
public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) {
this.ebeanServer = ebeanServer;
this.serverName = ebeanServer.getName();
@@ -62,29 +60,19 @@ public abstract class BeanRequest {
if (transaction == null || !transaction.isActive()) {
// create an implicit transaction to execute this query
transaction = ebeanServer.createServerTransaction(false, -1);
// commented out for performance reasons...
// TODO: review performance of trans.setReadOnly(true)
//if (readOnlyTransaction) {
// readOnly = true;
// transaction.setReadOnly(true);
//}
createdTransaction = true;
}
}
}
/**
* Commit this transaction if it was created for this request.
*/
public void commitTransIfRequired() {
if (createdTransaction) {
if (readOnly) {
transaction.rollback();
} else {
transaction.commit();
}
}
}
/**
* Commit this transaction if it was created for this request.
*/
public void commitTransIfRequired() {
if (createdTransaction) {
transaction.commit();
}
}
/**
* Rollback the transaction if it was created for this request.
@@ -1112,7 +1112,7 @@ public final class DefaultServer implements SpiEbeanServer {
// predicates on *ToMany properties
if (query.initManyWhereJoins()) {
// we need a sql distinct now
query.setDistinct(true);
query.setSqlDistinct(true);
}
boolean allowOneManyFetch = true;
@@ -1198,18 +1198,12 @@ public final class DefaultServer implements SpiEbeanServer {
}
SpiOrmQueryRequest<T> request = createQueryRequest(desc, spiQuery, t);
try {
request.initTransIfRequired();
return (T) request.findId();
T bean = (T) request.findId();
} finally {
request.endTransIfRequired();
return bean;
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1256,15 +1250,10 @@ public final class DefaultServer implements SpiEbeanServer {
try {
request.initTransIfRequired();
Set<T> set = (Set<T>) request.findSet();
return (Set<T>) request.findSet();
} finally {
request.endTransIfRequired();
return set;
} catch (RuntimeException ex) {
// String stackTrace = throwablePrinter.print(ex);
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1280,15 +1269,10 @@ public final class DefaultServer implements SpiEbeanServer {
try {
request.initTransIfRequired();
Map<?, T> map = (Map<?, T>) request.findMap();
return (Map<?, T>) request.findMap();
} finally {
request.endTransIfRequired();
return map;
} catch (RuntimeException ex) {
// String stackTrace = throwablePrinter.print(ex);
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1303,14 +1287,10 @@ public final class DefaultServer implements SpiEbeanServer {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ROWCOUNT, query, t);
try {
request.initTransIfRequired();
int rowCount = request.findRowCount();
return request.findRowCount();
} finally {
request.endTransIfRequired();
return rowCount;
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1326,14 +1306,10 @@ public final class DefaultServer implements SpiEbeanServer {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ID_LIST, query, t);
try {
request.initTransIfRequired();
List<Object> list = request.findIds();
return request.findIds();
} finally {
request.endTransIfRequired();
return list;
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1434,10 +1410,8 @@ public final class DefaultServer implements SpiEbeanServer {
try {
request.initTransIfRequired();
request.findVisit(visitor);
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
} finally {
// do nothing - findVisit garuntee's cleanup of the transaction if required
}
}
@@ -1448,10 +1422,9 @@ public final class DefaultServer implements SpiEbeanServer {
try {
request.initTransIfRequired();
return request.findIterate();
// request.endTransIfRequired();
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
request.endTransIfRequired();
throw ex;
}
}
@@ -1468,14 +1441,10 @@ public final class DefaultServer implements SpiEbeanServer {
try {
request.initTransIfRequired();
List<T> list = request.findList();
return request.findList();
} finally {
request.endTransIfRequired();
return list;
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1518,14 +1487,10 @@ public final class DefaultServer implements SpiEbeanServer {
try {
request.initTransIfRequired();
List<SqlRow> list = request.findList();
return request.findList();
} finally {
request.endTransIfRequired();
return list;
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1535,14 +1500,10 @@ public final class DefaultServer implements SpiEbeanServer {
try {
request.initTransIfRequired();
Set<SqlRow> set = request.findSet();
return request.findSet();
} finally {
request.endTransIfRequired();
return set;
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1551,14 +1512,10 @@ public final class DefaultServer implements SpiEbeanServer {
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t);
try {
request.initTransIfRequired();
Map<?, SqlRow> map = request.findMap();
return request.findMap();
} finally {
request.endTransIfRequired();
return map;
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1576,6 +1533,16 @@ public final class DefaultServer implements SpiEbeanServer {
persister.save(checkEntityBean(bean), t);
}
@Override
public void markAsDirty(Object bean) {
if (bean instanceof EntityBean == false) {
throw new IllegalArgumentException("This bean is not an EntityBean?");
}
// mark the bean as dirty (so that an update will not get skipped)
((EntityBean)bean)._ebean_getIntercept().setDirty(true);
}
/**
* Update the bean using the default 'updatesDeleteMissingChildren' setting.
*/
@@ -1,5 +1,7 @@
package com.avaje.ebeaninternal.server.core;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -22,6 +24,7 @@ import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
@@ -31,6 +34,7 @@ import com.avaje.ebeaninternal.server.resource.ResourceManager;
import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory;
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter;
import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager;
import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager;
@@ -42,8 +46,6 @@ import com.avaje.ebeaninternal.server.type.TypeManager;
/**
* Used to extend the ServerConfig with additional objects used to configure and
* construct an EbeanServer.
*
* @author rbygrave
*/
public class InternalConfiguration {
@@ -113,8 +115,7 @@ public class InternalConfiguration {
this.beanDescriptorManager = new BeanDescriptorManager(this);
beanDescriptorManager.deploy();
this.transactionManager = new TransactionManager(clusterManager, backgroundExecutor,
serverConfig, beanDescriptorManager, this.getBootupClasses());
this.transactionManager = createTransactionManager();
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder);
@@ -131,6 +132,34 @@ public class InternalConfiguration {
}
}
/**
* Create the TransactionManager taking into account autoCommit mode.
*/
private TransactionManager createTransactionManager() {
if (isAutoCommitMode()) {
return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
}
return new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
}
/**
* Return true if autoCommit mode is on.
*/
private boolean isAutoCommitMode() {
if (serverConfig.isAutoCommitMode()) {
// explicitly set
return true;
}
DataSource dataSource = serverConfig.getDataSource();
if (dataSource instanceof DataSourcePool && ((DataSourcePool)dataSource).getAutoCommit()) {
// We know the DataSourcePool is using autoCommit
return true;
}
return false;
}
public JsonContext createJsonContext(SpiEbeanServer server) {
@@ -193,14 +193,12 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
/**
* Will end a locally created transaction.
* <p>
* It ends the transaction by using a rollback() as the transaction is known
* to be readOnly.
* It ends the query only transaction.
* </p>
*/
public void endTransIfRequired() {
if (createdTransaction) {
// we can rollback as readOnly transaction
transaction.rollback();
transaction.endQueryOnly();
}
}
@@ -92,7 +92,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
private List<BeanPropertyAssocMany<?>> updatedManys;
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
PersistExecute persistExecute, PersistRequest.Type type) {
PersistExecute persistExecute, PersistRequest.Type type, boolean saveRecurse) {
super(server, t, persistExecute);
this.entityBean = (EntityBean) bean;
@@ -103,12 +103,9 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
this.bean = bean;
this.parentBean = parentBean;
this.controller = beanDescriptor.getPersistController();
if (PersistRequest.Type.DETERMINE != type) {
this.type = type;
} else {
// determine mode during cascade save (supporting stateless update)
this.type = beanDescriptor.isInsertMode(intercept) ? Type.INSERT : Type.UPDATE;
this.type = type;
if (saveRecurse) {
this.persistCascade = t.isPersistCascade();
}
@@ -40,15 +40,6 @@ public final class RelationalQueryRequest {
this.trans = (SpiTransaction) t;
}
/**
* Rollback the transaction if it was created for this request.
*/
public void rollbackTransIfRequired() {
if (createdTransaction) {
trans.rollback();
}
}
/**
* Create a transaction if none currently exists.
*/
@@ -58,10 +49,6 @@ public final class RelationalQueryRequest {
if (trans == null || !trans.isActive()) {
// create a local readOnly transaction
trans = ebeanServer.createServerTransaction(false, -1);
// commented out for performance reasons...
// TODO: review performance of trans.setReadOnly(true)
// trans.setReadOnly(true);
createdTransaction = true;
}
}
@@ -72,8 +59,7 @@ public final class RelationalQueryRequest {
*/
public void endTransIfRequired() {
if (createdTransaction) {
// we can rollback as a readOnly transaction.
trans.rollback();
trans.endQueryOnly();
}
}
@@ -45,8 +45,6 @@ public interface SpiOrmQueryRequest<T> {
*/
public void endTransIfRequired();
public void rollbackTransIfRequired();
/**
* Execute the query as findById.
*/
@@ -30,6 +30,8 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
private final CreateTableVisitor parent;
private BeanPropertyAssocOne<?> embedded;
public CreateTableColumnVisitor(CreateTableVisitor parent, DdlGenContext ctx) {
this.parent = parent;
this.ctx = ctx;
@@ -68,6 +70,7 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
this.embedded = embedded;
visitScalar(p);
}
@@ -184,7 +187,12 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
private String createUniqueConstraint(BeanProperty p) {
StringBuilder expr = createUniqueConstraintBuffer(p.getBeanDescriptor().getBaseTable(), p.getDbColumn());
String baseTable = p.getBeanDescriptor().getBaseTable();
if (baseTable == null) {
// Embedded bean property that has unique constraint on it
baseTable = embedded.getBeanDescriptor().getBaseTable();
}
StringBuilder expr = createUniqueConstraintBuffer(baseTable, p.getDbColumn());
expr.append(p.getDbColumn()).append(")");
return expr.toString();
@@ -1531,7 +1531,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
if (propertyDeploy && chain != null) {
BeanFkeyProperty fk = fkeyMap.get(propName);
if (fk != null) {
return fk.create(chain.getExpression());
return fk.create(chain.getExpression(), chain.isContainsMany());
}
}
@@ -1886,21 +1886,31 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return idProperty;
}
public boolean isInsertMode(EntityBeanIntercept ebi) {
/**
* Return true if this bean should be inserted rather than updated.
*
* @param ebi
* The entity bean intercept
* @param insertMode
* true if the 'root request' was an insert rather than an update
*/
public boolean isInsertMode(EntityBeanIntercept ebi, boolean insertMode) {
if (ebi.isLoaded()) {
// must be an update as the bean is loaded
return false;
}
// determine based on Id property
if (idProperty.isEmbedded()) {
// not using Id generator so just base on isLoaded()
return !ebi.isLoaded();
}
//if (idGenerator == null) {
// return !ebi.isLoaded();
//} else {
return !hasIdProperty(ebi);
//}
if (!hasIdProperty(ebi)) {
// No Id property means it must be an insert
return true;
}
// same as the 'root request'
return insertMode;
}
public boolean isReference(EntityBeanIntercept ebi) {
@@ -877,7 +877,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
if (!tableJoin.hasJoinColumns()) {
// define Join as the inverse of the mappedBy property
DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin();
otherTableJoin.copyTo(tableJoin, true, tableJoin.getTable());
otherTableJoin.copyWithoutType(tableJoin, true, tableJoin.getTable());
}
}
@@ -1019,6 +1019,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
// set bean controller, finder and listener
setBeanControllerFinderListener(desc);
deplyInherit.process(desc);
desc.checkInheritanceMapping();
createProperties.createProperties(desc);
@@ -10,182 +10,188 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue;
*/
public final class BeanFkeyProperty implements ElPropertyValue {
private final String placeHolder;
private final String prefix;
private final String name;
private final String dbColumn;
private final String placeHolder;
private final String prefix;
private final String name;
private final String dbColumn;
private final boolean containsMany;
private int deployOrder;
private int deployOrder;
public BeanFkeyProperty(String prefix, String name, String dbColumn, int deployOrder) {
this.prefix = prefix;
this.name = name;
this.dbColumn = dbColumn;
this.deployOrder = deployOrder;
this.placeHolder = calcPlaceHolder(prefix, dbColumn);
}
public BeanFkeyProperty(String prefix, String name, String dbColumn, int deployOrder) {
this(prefix, name, dbColumn, deployOrder, false);
}
private BeanFkeyProperty(String prefix, String name, String dbColumn, int deployOrder, boolean containsMany) {
this.prefix = prefix;
this.name = name;
this.dbColumn = dbColumn;
this.deployOrder = deployOrder;
this.containsMany = containsMany;
this.placeHolder = calcPlaceHolder(prefix, dbColumn);
}
public int getDeployOrder() {
return deployOrder;
}
public int getDeployOrder() {
return deployOrder;
}
private String calcPlaceHolder(String prefix, String dbColumn) {
if (prefix != null) {
return "${" + prefix + "}" + dbColumn;
} else {
return ROOT_ELPREFIX + dbColumn;
}
private String calcPlaceHolder(String prefix, String dbColumn) {
if (prefix != null) {
return "${" + prefix + "}" + dbColumn;
} else {
return ROOT_ELPREFIX + dbColumn;
}
}
public BeanFkeyProperty create(String expression) {
int len = expression.length() - name.length() - 1;
String prefix = expression.substring(0, len);
public BeanFkeyProperty create(String expression, boolean containsMany) {
int len = expression.length() - name.length() - 1;
String prefix = expression.substring(0, len);
return new BeanFkeyProperty(prefix, name, dbColumn, deployOrder);
}
return new BeanFkeyProperty(prefix, name, dbColumn, deployOrder, containsMany);
}
/**
* Returns false for keys.
*/
public boolean isDbEncrypted() {
return false;
}
/**
* Returns false for keys.
*/
public boolean isDbEncrypted() {
return false;
}
/**
* Returns false for keys.
*/
public boolean isLocalEncrypted() {
return false;
}
/**
* Returns false for keys.
*/
public boolean isLocalEncrypted() {
return false;
}
/**
* Only usable as ElPropertyDeploy.
*/
public boolean isDeployOnly() {
return true;
}
@Override
public boolean containsFormulaWithJoin() {
return false;
}
/**
* Only usable as ElPropertyDeploy.
*/
public boolean isDeployOnly() {
return true;
}
/**
* Returns false.
*/
public boolean containsMany() {
return false;
}
public boolean containsManySince(String sinceProperty) {
return containsMany();
}
@Override
public boolean containsFormulaWithJoin() {
return false;
}
public String getDbColumn() {
return dbColumn;
}
/**
* Returns false.
*/
public boolean containsMany() {
return containsMany;
}
public String getName() {
return name;
}
public boolean containsManySince(String sinceProperty) {
return containsMany();
}
public String getElName() {
return name;
}
public String getDbColumn() {
return dbColumn;
}
/**
* Returns null as not an AssocOne.
*/
public Object[] getAssocOneIdValues(EntityBean value) {
return null;
}
public String getName() {
return name;
}
/**
* Returns null as not an AssocOne.
*/
public String getAssocOneIdExpr(String prefix, String operator) {
return null;
}
public String getElName() {
return name;
}
/**
* Returns null as not an AssocOne.
*/
public String getAssocIdInExpr(String prefix) {
return null;
}
/**
* Returns null as not an AssocOne.
*/
public Object[] getAssocOneIdValues(EntityBean value) {
return null;
}
/**
* Returns null as not an AssocOne.
*/
public String getAssocIdInValueExpr(int size) {
return null;
}
/**
* Returns null as not an AssocOne.
*/
public String getAssocOneIdExpr(String prefix, String operator) {
return null;
}
/**
* Returns false as not an AssocOne.
*/
public boolean isAssocId() {
return false;
}
public boolean isAssocProperty() {
return false;
}
/**
* Returns null as not an AssocOne.
*/
public String getAssocIdInExpr(String prefix) {
return null;
}
public String getElPlaceholder(boolean encrypted) {
return placeHolder;
}
/**
* Returns null as not an AssocOne.
*/
public String getAssocIdInValueExpr(int size) {
return null;
}
public String getElPrefix() {
return prefix;
}
/**
* Returns false as not an AssocOne.
*/
public boolean isAssocId() {
return false;
}
public boolean isDateTimeCapable() {
return false;
}
public int getJdbcType() {
return 0;
}
public boolean isAssocProperty() {
return false;
}
public Object parseDateTime(long systemTimeMillis) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public String getElPlaceholder(boolean encrypted) {
return placeHolder;
}
public StringFormatter getStringFormatter() {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public String getElPrefix() {
return prefix;
}
public StringParser getStringParser() {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public boolean isDateTimeCapable() {
return false;
}
public void elSetReference(EntityBean bean) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public int getJdbcType() {
return 0;
}
public Object elConvertType(Object value) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public BeanProperty getBeanProperty() {
return null;
}
public void elSetValue(EntityBean bean, Object value, boolean populate) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public Object parseDateTime(long systemTimeMillis) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public Object elGetValue(EntityBean bean) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public StringFormatter getStringFormatter() {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public Object elGetReference(EntityBean bean) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public StringParser getStringParser() {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public BeanProperty getBeanProperty() {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public void elSetReference(EntityBean bean) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public String getDeployProperty() {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public Object elConvertType(Object value) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public void elSetValue(EntityBean bean, Object value, boolean populate) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public Object elGetValue(EntityBean bean) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public Object elGetReference(EntityBean bean) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public String getDeployProperty() {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
}
@@ -153,8 +153,9 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
if (i > 0) {
sb.append(", ");
}
// these fkcolumns always on base table hence t0 as alias
sb.append("t0.").append(exportedProperties[i].getForeignDbColumn());
// these fk columns are either on the intersection (int_) or base table (t0)
String fkTableAlias = isManyToMany() ? "int_" : "t0";
sb.append(fkTableAlias).append(".").append(exportedProperties[i].getForeignDbColumn());
}
if (fetchOrderBy != null) {
sb.append(", ").append(fetchOrderBy);
@@ -1,19 +1,15 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection;
import com.avaje.ebeaninternal.server.type.ScalarType;
public class BeanPropertySimpleCollection<T> extends BeanPropertyAssocMany<T> {
private final ScalarType<T> collectionScalarType;
public BeanPropertySimpleCollection(BeanDescriptorMap owner, BeanDescriptor<?> descriptor, DeployBeanPropertySimpleCollection<T> deploy) {
super(owner, descriptor, deploy);
this.collectionScalarType = deploy.getCollectionScalarType();
}
public BeanPropertySimpleCollection(BeanDescriptorMap owner, BeanDescriptor<?> descriptor, DeployBeanPropertySimpleCollection<T> deploy) {
super(owner, descriptor, deploy);
}
public void initialise() {
super.initialise();
}
public void initialise() {
super.initialise();
}
}
@@ -17,10 +17,6 @@ import com.avaje.ebeaninternal.server.query.SqlJoinType;
*/
public final class TableJoin {
public static final String LEFT_OUTER = "left outer join";
public static final String JOIN = "join";
/**
* Flag set when the imported key maps to the primary key. This occurs for
* intersection tables (ManyToMany).
@@ -13,18 +13,17 @@ import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
import com.avaje.ebeaninternal.server.deploy.IntersectionRow;
import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
import com.avaje.ebeaninternal.server.persist.dmlbind.BindableRequest;
import com.avaje.ebeaninternal.util.ValueUtil;
/**
* Imported Embedded id.
*/
public class ImportedIdEmbedded implements ImportedId {
final BeanPropertyAssoc<?> owner;
private final BeanPropertyAssoc<?> owner;
final BeanPropertyAssocOne<?> foreignAssocOne;
private final BeanPropertyAssocOne<?> foreignAssocOne;
final ImportedIdSimple[] imported;
private final ImportedIdSimple[] imported;
public ImportedIdEmbedded(BeanPropertyAssoc<?> owner, BeanPropertyAssocOne<?> foreignAssocOne, ImportedIdSimple[] imported) {
this.owner = owner;
@@ -13,6 +13,9 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -905,4 +908,32 @@ public class DeployBeanDescriptor<T> {
return list;
}
/**
* Check the mapping for class inheritance
*/
public void checkInheritanceMapping() {
if (inheritInfo == null) {
checkInheritance(getBeanType());
}
}
/**
* Check valid mapping annotations on the class hierarchy.
*/
private void checkInheritance(Class<?> beanType) {
Class<?> parent = beanType.getSuperclass();
if (parent == null || Object.class.equals(parent)) {
// all good
return;
}
if (parent.isAnnotationPresent(Entity.class)) {
String msg = "Checking "+getBeanType()+" and found "+parent+" that has @Entity annotation rather than MappedSuperclass?";
throw new IllegalStateException(msg);
}
if (parent.isAnnotationPresent(MappedSuperclass.class)) {
// continue checking
checkInheritance(parent);
}
}
}
@@ -2,40 +2,28 @@ package com.avaje.ebeaninternal.server.deploy.meta;
import com.avaje.ebean.bean.BeanCollection.ModifyListenMode;
import com.avaje.ebeaninternal.server.deploy.ManyType;
import com.avaje.ebeaninternal.server.type.ScalarType;
public class DeployBeanPropertySimpleCollection<T> extends DeployBeanPropertyAssocMany<T> {
private final ScalarType<T> collectionScalarType;
public DeployBeanPropertySimpleCollection(DeployBeanDescriptor<?> desc, Class<T> targetType, ScalarType<T> scalarType, ManyType manyType) {
super(desc, targetType, manyType);
this.collectionScalarType = scalarType;
this.modifyListenMode = ModifyListenMode.ALL;
}
public DeployBeanPropertySimpleCollection(DeployBeanDescriptor<?> desc, Class<T> targetType, ManyType manyType) {
super(desc, targetType, manyType);
this.modifyListenMode = ModifyListenMode.ALL;
}
/**
* Return the scalarType of the collection elements.
*/
public ScalarType<T> getCollectionScalarType() {
return collectionScalarType;
}
/**
* Returns false as never a ManyToMany.
*/
@Override
public boolean isManyToMany() {
return false;
}
/**
* Returns false as never a ManyToMany.
*/
@Override
public boolean isManyToMany() {
return false;
}
/**
* Returns true as always Unidirectional.
*/
@Override
public boolean isUnidirectional() {
return true;
}
/**
* Returns true as always Unidirectional.
*/
@Override
public boolean isUnidirectional() {
return true;
}
}
@@ -4,11 +4,9 @@ import java.util.ArrayList;
import javax.persistence.JoinColumn;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo;
import com.avaje.ebeaninternal.server.deploy.BeanTable;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
@@ -19,205 +17,196 @@ import com.avaje.ebeaninternal.server.query.SqlJoinType;
*/
public class DeployTableJoin {
/**
* Flag set when the imported key maps to the primary key.
* This occurs for intersection tables (ManyToMany).
*/
private boolean importedPrimaryKey;
/**
* The joined table.
*/
private String table;
/**
* The type of join. LEFT OUTER etc.
*/
private SqlJoinType type = SqlJoinType.INNER;
/**
* Flag set when the imported key maps to the primary key. This occurs for intersection tables
* (ManyToMany).
*/
private boolean importedPrimaryKey;
/**
* The list of properties mapped to this joined table.
*/
private ArrayList<DeployBeanProperty> properties = new ArrayList<DeployBeanProperty>();
/**
* The joined table.
*/
private String table;
/**
* The list of join column pairs. Used to generate the on clause.
*/
private ArrayList<DeployTableJoinColumn> columns = new ArrayList<DeployTableJoinColumn>();
/**
* The type of join. LEFT OUTER etc.
*/
private SqlJoinType type = SqlJoinType.INNER;
/**
* The persist cascade info.
*/
private BeanCascadeInfo cascadeInfo = new BeanCascadeInfo();
private InheritInfo inheritInfo;
/**
* The list of properties mapped to this joined table.
*/
private ArrayList<DeployBeanProperty> properties = new ArrayList<DeployBeanProperty>();
/**
* Create a DeployTableJoin.
*/
public DeployTableJoin() {
}
public String toString() {
return type + " " + table + " " + columns;
/**
* The list of join column pairs. Used to generate the on clause.
*/
private ArrayList<DeployTableJoinColumn> columns = new ArrayList<DeployTableJoinColumn>(4);
/**
* The persist cascade info.
*/
private BeanCascadeInfo cascadeInfo = new BeanCascadeInfo();
private InheritInfo inheritInfo;
/**
* Create a DeployTableJoin.
*/
public DeployTableJoin() {
}
public String toString() {
return type + " " + table + " " + columns;
}
/**
* Return true if the imported foreign key maps to the primary key.
*/
public boolean isImportedPrimaryKey() {
return importedPrimaryKey;
}
/**
* Flag set when the imported key maps to the primary key. This occurs for intersection tables
* (ManyToMany).
*/
public void setImportedPrimaryKey(boolean importedPrimaryKey) {
this.importedPrimaryKey = importedPrimaryKey;
}
/**
* Return true if the JoinOnPair have been set.
*/
public boolean hasJoinColumns() {
return columns.size() > 0;
}
/**
* Return the persist info.
*/
public BeanCascadeInfo getCascadeInfo() {
return cascadeInfo;
}
/**
* Copy all the columns to this join potentially reversing the columns.
*/
public void setColumns(DeployTableJoinColumn[] cols, boolean reverse) {
columns = new ArrayList<DeployTableJoinColumn>();
for (int i = 0; i < cols.length; i++) {
addJoinColumn(cols[i].copy(reverse));
}
}
/**
* Return true if the imported foreign key maps to the primary key.
*/
public boolean isImportedPrimaryKey() {
return importedPrimaryKey;
}
/**
* Add a join pair
*/
public void addJoinColumn(DeployTableJoinColumn pair) {
columns.add(pair);
}
/**
* Flag set when the imported key maps to the primary key.
* This occurs for intersection tables (ManyToMany).
*/
public void setImportedPrimaryKey(boolean importedPrimaryKey) {
this.importedPrimaryKey = importedPrimaryKey;
}
/**
* Add a JoinColumn
* <p>
* The order is generally true for OneToMany and false for ManyToOne relationships.
* </p>
*/
public void addJoinColumn(boolean order, JoinColumn jc, BeanTable beanTable) {
if (!"".equals(jc.table())) {
setTable(jc.table());
}
if (!"".equals(jc.name()) || !"".equals(jc.referencedColumnName())) {
// only add the join column details when name or referencedColumnName is specified
addJoinColumn(new DeployTableJoinColumn(order, jc, beanTable));
}
}
/**
* Return true if the JoinOnPair have been set.
*/
public boolean hasJoinColumns() {
return columns.size() > 0;
/**
* Add a JoinColumn array.
*/
public void addJoinColumn(boolean order, JoinColumn[] jcArray, BeanTable beanTable) {
for (int i = 0; i < jcArray.length; i++) {
addJoinColumn(order, jcArray[i], beanTable);
}
}
/**
* Return the persist info.
*/
public BeanCascadeInfo getCascadeInfo() {
return cascadeInfo;
}
/**
* Return the join columns.
*/
public DeployTableJoinColumn[] columns() {
return (DeployTableJoinColumn[]) columns.toArray(new DeployTableJoinColumn[columns.size()]);
}
/**
* Copy all the columns to this join potentially reversing the columns.
*/
public void setColumns(DeployTableJoinColumn[] cols, boolean reverse) {
columns = new ArrayList<DeployTableJoinColumn>();
for (int i = 0; i < cols.length; i++) {
addJoinColumn(cols[i].copy(reverse));
}
}
/**
* Add a join pair
*/
public void addJoinColumn(DeployTableJoinColumn pair) {
columns.add(pair);
}
/**
* For secondary table joins returns the properties mapped to that table.
*/
public DeployBeanProperty[] properties() {
return (DeployBeanProperty[]) properties.toArray(new DeployBeanProperty[properties.size()]);
}
/**
* Add a JoinColumn
* <p>
* The order is generally true for OneToMany and false for ManyToOne relationships.
* </p>
*/
public void addJoinColumn(boolean order, JoinColumn jc, BeanTable beanTable) {
if (!"".equals(jc.table())) {
setTable(jc.table());
}
addJoinColumn(new DeployTableJoinColumn(order, jc, beanTable));
}
/**
* Add a JoinColumn array.
*/
public void addJoinColumn(boolean order, JoinColumn[] jcArray, BeanTable beanTable) {
for (int i = 0; i < jcArray.length; i++) {
addJoinColumn(order, jcArray[i], beanTable);
}
}
/**
* Return the join columns.
*/
public DeployTableJoinColumn[] columns() {
return (DeployTableJoinColumn[])columns.toArray(new DeployTableJoinColumn[columns.size()]);
}
/**
* Return the joined table name.
*/
public String getTable() {
return table;
}
/**
* For secondary table joins returns the properties mapped to that table.
*/
public DeployBeanProperty[] properties() {
return (DeployBeanProperty[])properties.toArray(new DeployBeanProperty[properties.size()]);
}
/**
* set the joined table name.
*/
public void setTable(String table) {
this.table = table;
}
/**
* Return the joined table name.
*/
public String getTable() {
return table;
}
/**
* Return the type of join. LEFT OUTER JOIN etc.
*/
public SqlJoinType getType() {
return type;
}
/**
* set the joined table name.
*/
public void setTable(String table) {
this.table = table;
}
/**
* Return true if this join is a left outer join.
*/
public boolean isOuterJoin() {
return type == SqlJoinType.OUTER;
}
/**
* Return the type of join. LEFT OUTER JOIN etc.
*/
public SqlJoinType getType() {
return type;
}
public void setType(SqlJoinType type) {
this.type = type;
}
/**
* Return true if this join is a left outer join.
*/
public boolean isOuterJoin() {
return type == SqlJoinType.OUTER;
}
private void setType(SqlJoinType type) {
this.type = type;
}
/**
* Set the type of join.
*/
public void setType(String joinType) {
joinType = joinType.toUpperCase();
if (joinType.equalsIgnoreCase(TableJoin.JOIN)) {
type = SqlJoinType.INNER;
} else if (joinType.indexOf("LEFT") > -1) {
type = SqlJoinType.OUTER;
} else if (joinType.indexOf("OUTER") > -1) {
type = SqlJoinType.OUTER;
} else if (joinType.indexOf("INNER") > -1) {
type = SqlJoinType.INNER;
} else {
throw new RuntimeException(Message.msg("join.type.unknown", joinType));
}
}
public DeployTableJoin createInverse(String tableName) {
DeployTableJoin inverse = new DeployTableJoin();
public DeployTableJoin createInverse(String tableName) {
return copyTo(inverse, true, tableName);
DeployTableJoin inverse = new DeployTableJoin();
return copyInternal(inverse, true, tableName, true);
}
public DeployTableJoin copyTo(DeployTableJoin destJoin, boolean reverse, String tableName) {
return copyInternal(destJoin, reverse, tableName, true);
}
public DeployTableJoin copyWithoutType(DeployTableJoin destJoin, boolean reverse, String tableName) {
return copyInternal(destJoin, reverse, tableName, false);
}
private DeployTableJoin copyInternal(DeployTableJoin destJoin, boolean reverse, String tableName, boolean withType) {
destJoin.setTable(tableName);
if (withType) {
destJoin.setType(type);
}
public DeployTableJoin copyTo(DeployTableJoin destJoin, boolean reverse, String tableName) {
destJoin.setTable(tableName);
destJoin.setType(type);
destJoin.setColumns(columns(), reverse);
return destJoin;
}
destJoin.setColumns(columns(), reverse);
public InheritInfo getInheritInfo() {
return inheritInfo;
}
return destJoin;
}
public void setInheritInfo(InheritInfo inheritInfo) {
this.inheritInfo = inheritInfo;
}
public InheritInfo getInheritInfo() {
return inheritInfo;
}
public void setInheritInfo(InheritInfo inheritInfo) {
this.inheritInfo = inheritInfo;
}
}
@@ -18,12 +18,12 @@ import com.avaje.ebean.config.TableName;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanTable;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Read the deployment annotation for Assoc Many beans.
@@ -160,7 +160,7 @@ public class AnnotationAssocManys extends AnnotationParser {
DeployTableJoin destJoin = prop.getTableJoin();
destJoin.addJoinColumn(false, joinTable.inverseJoinColumns(), prop.getBeanTable());
intJoin.setType(TableJoin.LEFT_OUTER);
intJoin.setType(SqlJoinType.OUTER);
// reverse join from dest back to intersection
DeployTableJoin inverseDest = destJoin.createInverse(intTableName);
@@ -216,7 +216,7 @@ public class AnnotationAssocManys extends AnnotationParser {
intTableName = getM2MJoinTableName(localTable, otherTable);
intJoin.setTable(intTableName);
intJoin.setType(TableJoin.LEFT_OUTER);
intJoin.setType(SqlJoinType.OUTER);
}
DeployTableJoin destJoin = prop.getTableJoin();
@@ -282,7 +282,7 @@ public class AnnotationAssocManys extends AnnotationParser {
manyProp.setManyToMany(true);
manyProp.setModifyListenMode(ModifyListenMode.ALL);
manyProp.setBeanTable(assoc);
manyProp.getTableJoin().setType(TableJoin.LEFT_OUTER);
manyProp.getTableJoin().setType(SqlJoinType.OUTER);
}
private void readToOne(OneToMany propAnn, DeployBeanPropertyAssocMany<?> manyProp) {
@@ -307,7 +307,7 @@ public class AnnotationAssocManys extends AnnotationParser {
}
manyProp.setBeanTable(assoc);
manyProp.getTableJoin().setType(TableJoin.LEFT_OUTER);
manyProp.getTableJoin().setType(SqlJoinType.OUTER);
}
@@ -19,10 +19,10 @@ import com.avaje.ebean.annotation.Where;
import com.avaje.ebean.config.NamingConvention;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.BeanTable;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Read the deployment annotations for Associated One beans.
@@ -99,7 +99,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
if (notNull != null) {
prop.setNullable(false);
// overrides optional attribute of ManyToOne etc
prop.getTableJoin().setType(TableJoin.JOIN);
prop.getTableJoin().setType(SqlJoinType.INNER);
}
}
@@ -9,34 +9,33 @@ import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta;
*/
public class AnnotationSql extends AnnotationParser {
public AnnotationSql(DeployBeanInfo<?> info) {
super(info);
}
public AnnotationSql(DeployBeanInfo<?> info) {
super(info);
}
public void parse() {
Class<?> cls = descriptor.getBeanType();
Sql sql = cls.getAnnotation(Sql.class);
if (sql != null){
setSql(sql);
}
SqlSelect sqlSelect = cls.getAnnotation(SqlSelect.class);
if (sqlSelect != null){
setSqlSelect(sqlSelect);
}
}
private void setSql(Sql sql) {
SqlSelect[] select = sql.select();
for (int i = 0; i < select.length; i++) {
setSqlSelect(select[i]);
}
}
public void parse() {
Class<?> cls = descriptor.getBeanType();
Sql sql = cls.getAnnotation(Sql.class);
if (sql != null) {
setSql(sql);
}
private void setSqlSelect(SqlSelect sqlSelect) {
SqlSelect sqlSelect = cls.getAnnotation(SqlSelect.class);
if (sqlSelect != null) {
setSqlSelect(sqlSelect);
}
}
DRawSqlMeta rawSqlMeta = new DRawSqlMeta(sqlSelect);
descriptor.add(rawSqlMeta);
}
private void setSql(Sql sql) {
SqlSelect[] select = sql.select();
for (int i = 0; i < select.length; i++) {
setSqlSelect(select[i]);
}
}
private void setSqlSelect(SqlSelect sqlSelect) {
DRawSqlMeta rawSqlMeta = new DRawSqlMeta(sqlSelect);
descriptor.add(rawSqlMeta);
}
}
@@ -2,10 +2,10 @@ package com.avaje.ebeaninternal.server.deploy.parse;
import java.util.HashMap;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Wraps information about a bean during deployment parsing.
@@ -58,7 +58,7 @@ public class DeployBeanInfo<T> {
if (tableJoin == null) {
tableJoin = new DeployTableJoin();
tableJoin.setTable(tableName);
tableJoin.setType(TableJoin.JOIN);
tableJoin.setType(SqlJoinType.INNER);
descriptor.addTableJoin(tableJoin);
tableJoinMap.put(key, tableJoin);
@@ -71,13 +71,8 @@ public class DeployBeanInfo<T> {
*/
public void setBeanJoinType(DeployBeanPropertyAssocOne<?> beanProp, boolean outerJoin) {
String joinType = TableJoin.JOIN;
if (outerJoin){// && util.isUseOneToOneOptional()) {
joinType = TableJoin.LEFT_OUTER;
}
DeployTableJoin tableJoin = beanProp.getTableJoin();
tableJoin.setType(joinType);
tableJoin.setType(outerJoin ? SqlJoinType.OUTER : SqlJoinType.INNER);
}
}
@@ -264,7 +264,7 @@ public class DeployCreateProperties {
try {
ScalarType<?> scalarType = typeManager.getScalarType(targetType);
if (scalarType != null) {
return new DeployBeanPropertySimpleCollection(desc, targetType, scalarType, manyType);
return new DeployBeanPropertySimpleCollection(desc, targetType, manyType);
}
} catch (NullPointerException e) {
logger.debug("expected non-scalar type" + e.getMessage());
@@ -176,7 +176,7 @@ public class DataSourcePool implements DataSource {
this.name = name;
this.poolListener = createPoolListener(params.getPoolListener());
this.autoCommit = false;
this.autoCommit = params.isAutoCommit();
this.transactionIsolation = params.getIsolationLevel();
this.maxInactiveMillis = 1000 * params.getMaxInactiveTimeSecs();
@@ -32,6 +32,7 @@ import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate;
import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql;
import com.avaje.ebeaninternal.server.core.Persister;
import com.avaje.ebeaninternal.server.core.PstmtBatch;
import com.avaje.ebeaninternal.server.core.PersistRequest.Type;
import com.avaje.ebeaninternal.server.deploy.BeanCollectionUtil;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
@@ -172,7 +173,7 @@ public final class DefaultPersister implements Persister {
if (req.isReference()) {
// its a reference so see if there are manys to save...
if (req.isPersistCascade()) {
saveAssocMany(false, req);
saveAssocMany(false, req, false);
}
req.checkUpdatedManysOnly();
@@ -218,16 +219,16 @@ public final class DefaultPersister implements Persister {
}
}
private void saveRecurse(EntityBean bean, Transaction t, Object parentBean) {
private void saveRecurse(EntityBean bean, Transaction t, Object parentBean, boolean insertMode) {
// determine insert or update taking into account stateless updates
PersistRequestBean<?> request = createRequest(bean, t, parentBean, PersistRequest.Type.DETERMINE);
PersistRequestBean<?> request = createRequest(bean, t, parentBean, insertMode);
if (request.isReference()) {
// its a reference...
if (request.isPersistCascade()) {
// save any associated List held beans
saveAssocMany(false, request);
saveAssocMany(false, request, insertMode);
}
request.checkUpdatedManysOnly();
@@ -253,7 +254,7 @@ public final class DefaultPersister implements Persister {
try {
if (request.isPersistCascade()) {
// save associated One beans recursively first
saveAssocOne(request);
saveAssocOne(request, true);
}
// set the IDGenerated value if required
@@ -262,7 +263,7 @@ public final class DefaultPersister implements Persister {
if (request.isPersistCascade()) {
// save any associated List held beans
saveAssocMany(true, request);
saveAssocMany(true, request, true);
}
} finally {
request.unRegisterBean();
@@ -282,7 +283,7 @@ public final class DefaultPersister implements Persister {
try {
if (request.isPersistCascade()) {
// save associated One beans recursively first
saveAssocOne(request);
saveAssocOne(request, false);
}
if (request.isDirty()) {
@@ -297,7 +298,7 @@ public final class DefaultPersister implements Persister {
if (request.isPersistCascade()) {
// save all the beans in assocMany's after
saveAssocMany(false, request);
saveAssocMany(false, request, false);
}
request.checkUpdatedManysOnly();
@@ -539,7 +540,7 @@ public final class DefaultPersister implements Persister {
* bean to the child beans.
* </p>
*/
private void saveAssocMany(boolean insertedParent, PersistRequestBean<?> request) {
private void saveAssocMany(boolean insertedParent, PersistRequestBean<?> request, boolean insertMode) {
EntityBean parentBean = request.getEntityBean();
BeanDescriptor<?> desc = request.getBeanDescriptor();
@@ -559,7 +560,7 @@ public final class DefaultPersister implements Persister {
} else {
t.depth(+1);
prop.setParentBeanToChild(parentBean, detailBean);
saveRecurse(detailBean, t, parentBean);
saveRecurse(detailBean, t, parentBean, insertMode);
t.depth(-1);
}
}
@@ -571,7 +572,7 @@ public final class DefaultPersister implements Persister {
for (int i = 0; i < manys.length; i++) {
// check that property is loaded and not empty uninitialised collection
if (request.isLoadedProperty(manys[i]) && !manys[i].isEmptyBeanCollection(parentBean)) {
saveMany(new SaveManyPropRequest(insertedParent, manys[i], parentBean, request));
saveMany(new SaveManyPropRequest(insertedParent, manys[i], parentBean, request), insertMode);
if (!insertedParent) {
request.addUpdatedManyProperty(manys[i]);
}
@@ -646,7 +647,7 @@ public final class DefaultPersister implements Persister {
}
}
private void saveMany(SaveManyPropRequest saveMany) {
private void saveMany(SaveManyPropRequest saveMany, boolean insertMode) {
if (saveMany.getMany().isManyToMany()) {
@@ -654,7 +655,7 @@ public final class DefaultPersister implements Persister {
boolean saveIntersectionFromThisDirection = saveMany.isSaveIntersection();
if (saveMany.isCascade()) {
// Need explicit Cascade to save the beans on other side
saveAssocManyDetails(saveMany, false);
saveAssocManyDetails(saveMany, false, insertMode);
}
// for ManyToMany save the 'relationship' via inserts/deletes
// into/from the intersection table
@@ -670,7 +671,7 @@ public final class DefaultPersister implements Persister {
}
if (saveMany.isCascade()) {
// potentially deletes 'missing children' for 'stateless update'
saveAssocManyDetails(saveMany, saveMany.isDeleteMissingChildren());
saveAssocManyDetails(saveMany, saveMany.isDeleteMissingChildren(), insertMode);
}
}
}
@@ -708,7 +709,7 @@ public final class DefaultPersister implements Persister {
/**
* Save the details from a OneToMany collection.
*/
private void saveAssocManyDetails(SaveManyPropRequest saveMany, boolean deleteMissingChildren) {
private void saveAssocManyDetails(SaveManyPropRequest saveMany, boolean deleteMissingChildren, boolean insertMode) {
BeanPropertyAssocMany<?> prop = saveMany.getMany();
@@ -791,7 +792,7 @@ public final class DefaultPersister implements Persister {
} else {
// normal save recurse
saveRecurse(detail, t, parentBean);
saveRecurse(detail, t, parentBean, insertMode);
}
if (detailIds != null) {
// remember the Id (other details not in the collection) will be removed
@@ -839,7 +840,7 @@ public final class DefaultPersister implements Persister {
if (prop instanceof BeanPropertyAssocMany<?>) {
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>) prop;
saveMany(new SaveManyPropRequest(manyProp, parentBean, (SpiTransaction) t));
saveMany(new SaveManyPropRequest(manyProp, parentBean, (SpiTransaction) t), true);
} else if (prop instanceof BeanPropertyAssocOne<?>) {
BeanPropertyAssocOne<?> oneProp = (BeanPropertyAssocOne<?>) prop;
@@ -849,7 +850,7 @@ public final class DefaultPersister implements Persister {
int revertDepth = -1 * depth;
trans.depth(depth);
saveRecurse(assocBean, t, parentBean);
saveRecurse(assocBean, t, parentBean, true);
trans.depth(revertDepth);
} else {
@@ -1087,7 +1088,7 @@ public final class DefaultPersister implements Persister {
/**
* Save any associated one beans.
*/
private void saveAssocOne(PersistRequestBean<?> request) {
private void saveAssocOne(PersistRequestBean<?> request, boolean insertMode) {
BeanDescriptor<?> desc = request.getBeanDescriptor();
@@ -1111,7 +1112,7 @@ public final class DefaultPersister implements Persister {
} else {
SpiTransaction t = request.getTransaction();
t.depth(-1);
saveRecurse(detailBean, t, null);
saveRecurse(detailBean, t, null, insertMode);
t.depth(+1);
}
}
@@ -1196,38 +1197,52 @@ public final class DefaultPersister implements Persister {
}
}
/**
* Create the Persist Request Object that wraps all the objects used to
* perform an insert, update or delete.
*/
@SuppressWarnings("unchecked")
private <T> PersistRequestBean<T> createRequest(T bean, Transaction t, Object parentBean, PersistRequest.Type type) {
BeanManager<T> mgr = getBeanManager(bean);
if (mgr == null) {
throw new PersistenceException(errNotRegistered(bean.getClass()));
}
return (PersistRequestBean<T>) createRequest(bean, t, parentBean, mgr, type);
return createRequest(bean, t, parentBean, mgr, type, false);
}
private String errNotRegistered(Class<?> beanClass) {
String msg = "The type [" + beanClass + "] is not a registered entity?";
msg += " If you don't explicitly list the entity classes to use Ebean will search for them in the classpath.";
msg += " If the entity is in a Jar check the ebean.search.jars property in ebean.properties file or check ServerConfig.addJar().";
return msg;
}
/**
* Create an Insert or Update PersistRequestBean when cascading.
* <p>
* This call determines the PersistRequest.Type based on bean state and the insert flag (root persist type).
*/
private <T> PersistRequestBean<T> createRequest(T bean, Transaction t, Object parentBean, boolean insertMode) {
BeanManager<T> mgr = getBeanManager(bean);
if (mgr == null) {
throw new PersistenceException(errNotRegistered(bean.getClass()));
}
BeanDescriptor<T> desc = mgr.getBeanDescriptor();
EntityBean entityBean = (EntityBean)bean;
// determine Insert or Update based on bean state and insert flag
PersistRequest.Type type = desc.isInsertMode(entityBean._ebean_getIntercept(), insertMode) ? Type.INSERT : Type.UPDATE;
return createRequest(bean, t, parentBean, mgr, type, true);
}
/**
* Create the Persist Request Object that wraps all the objects used to
* perform an insert, update or delete.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private PersistRequestBean<?> createRequest(Object bean, Transaction t, Object parentBean, BeanManager<?> mgr, PersistRequest.Type type) {
private <T> PersistRequestBean<T> createRequest(T bean, Transaction t, Object parentBean, BeanManager<?> mgr, PersistRequest.Type type, boolean saveRecurse) {
return new PersistRequestBean(server, bean, parentBean, mgr, (SpiTransaction) t, persistExecute, type);
return new PersistRequestBean(server, bean, parentBean, mgr, (SpiTransaction) t, persistExecute, type, saveRecurse);
}
private String errNotRegistered(Class<?> beanClass) {
String msg = "The type [" + beanClass + "] is not a registered entity?";
msg += " If you don't explicitly list the entity classes to use Ebean will search for them in the classpath.";
msg += " If the entity is in a Jar check the ebean.search.jars property in ebean.properties file or check ServerConfig.addJar().";
return msg;
}
/**
* Return the BeanDescriptor for a bean that is being persisted.
* <p>
@@ -144,7 +144,7 @@ public class CQueryBuilder implements Constants {
String sqlSelect = "select count(*)";
if (hasMany) {
// need to count distinct id's ...
query.setDistinct(true);
query.setSqlDistinct(true);
sqlSelect = null;
}
@@ -320,13 +320,13 @@ public class CQueryBuilder implements Constants {
if (!useSqlLimiter) {
sb.append("select ");
if (query.isDistinct()) {
if (query.isDistinctQuery()) {
sb.append("distinct ");
}
}
sb.append(select.getSelectSql());
if (query.isDistinct() && dbOrderBy != null) {
if (query.isDistinctQuery() && dbOrderBy != null) {
// add the orderby columns to the select clause (due to distinct)
sb.append(", ").append(convertDbOrderByForSelect(dbOrderBy));
}
@@ -258,7 +258,8 @@ public class SqlTreeBuilder {
// Optional many property for lazy loading query
BeanPropertyAssocMany<?> lazyLoadMany = (query == null) ? null : query.getLazyLoadForParentsProperty();
return new SqlTreeNodeRoot(desc, props, myList, !subQuery, includeJoin, lazyLoadMany);
boolean withId = !subQuery && (query == null || !query.isDistinct());
return new SqlTreeNodeRoot(desc, props, myList, withId, includeJoin, lazyLoadMany);
} else if (prop instanceof BeanPropertyAssocMany<?>) {
return new SqlTreeNodeManyRoot(prefix, (BeanPropertyAssocMany<?>) prop, props, myList);
@@ -312,7 +313,7 @@ public class SqlTreeBuilder {
// as we are now going to join to the many then we need
// to add the distinct to the sql query to stop duplicate
// rows...
query.setDistinct(true);
query.setSqlDistinct(true);
}
}
}
@@ -194,7 +194,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
Mode queryMode = ctx.getQueryMode();
PersistenceContext persistenceContext = ctx.getPersistenceContext();
PersistenceContext persistenceContext = !readId ? null : ctx.getPersistenceContext();
Object id = null;
if (!readId) {
@@ -274,8 +274,9 @@ public class SqlTreeNodeBean implements SqlTreeNode {
} else if (localBean != null) {
ctx.setCurrentPrefix(prefix, pathMap);
createListProxies(localDesc, ctx, localBean);
if (readId) {
createListProxies(localDesc, ctx, localBean);
}
localDesc.postLoad(localBean, null);
if (localBean instanceof EntityBean) {
@@ -290,7 +291,10 @@ public class SqlTreeNodeBean implements SqlTreeNode {
}
if (partialObject) {
ctx.register(null, ebi);
if (readId) {
// register for lazy loading
ctx.register(null, ebi);
}
} else {
ebi.setFullyLoadedBean(true);
}
File diff suppressed because it is too large Load Diff
@@ -71,8 +71,7 @@ public final class DefaultOrmUpdate<T> implements SpiUpdate<T>, Serializable {
this.name = namedUpdate.getName();
this.notifyCache = namedUpdate.isNotifyCache();
// named updates are always converted to sql as part
// of the initialisation
// named updates are always converted to sql as part of the initialisation
this.updateStatement = namedUpdate.getSqlUpdateStatement();
this.type = deriveType(updateStatement);
}
@@ -223,7 +223,5 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
return cancelled;
}
}
}
@@ -2,21 +2,21 @@ package com.avaje.ebeaninternal.server.querydefn;
public class NaturalKeyBindParam {
private final String name;
private final Object value;
public NaturalKeyBindParam(String name, Object value) {
this.name = name;
this.value = value;
}
private final String name;
public String getName() {
return name;
}
private final Object value;
public NaturalKeyBindParam(String name, Object value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
public Object getValue() {
return value;
}
}
@@ -27,461 +27,456 @@ import com.avaje.ebeaninternal.server.query.SplitName;
* Holds the select() and join() details of a ORM query.
* </p>
* <p>
* It is worth noting that for autoFetch a "tuned fetch info" builds an instance
* of OrmQueryDetail. Tuning a query is a matter of replacing an instance of
* this class with one that has been tuned with select() and join() set.
* It is worth noting that for autoFetch a "tuned fetch info" builds an instance of OrmQueryDetail.
* Tuning a query is a matter of replacing an instance of this class with one that has been tuned
* with select() and join() set.
* </p>
*/
public class OrmQueryDetail implements Serializable {
private static final long serialVersionUID = -2510486880141461807L;
private static final long serialVersionUID = -2510486880141461807L;
/**
* Root level properties.
*/
private OrmQueryProperties baseProps = new OrmQueryProperties();
/**
* Root level properties.
*/
private OrmQueryProperties baseProps = new OrmQueryProperties();
/**
* Contains the fetch/lazy/query joins and their properties.
*/
private LinkedHashMap<String, OrmQueryProperties> fetchPaths = new LinkedHashMap<String, OrmQueryProperties>(8);
/**
* Contains the fetch/lazy/query joins and their properties.
*/
private LinkedHashMap<String, OrmQueryProperties> fetchPaths = new LinkedHashMap<String, OrmQueryProperties>(8);
private LinkedHashSet<String> includes = new LinkedHashSet<String>(8);
private LinkedHashSet<String> includes = new LinkedHashSet<String>(8);
/**
* Return a deep copy of the OrmQueryDetail.
*/
public OrmQueryDetail copy() {
OrmQueryDetail copy = new OrmQueryDetail();
copy.baseProps = baseProps.copy();
for (Map.Entry<String, OrmQueryProperties> entry : fetchPaths.entrySet()) {
copy.fetchPaths.put(entry.getKey(), entry.getValue().copy());
}
copy.includes = new LinkedHashSet<String>(includes);
return copy;
/**
* Return a deep copy of the OrmQueryDetail.
*/
public OrmQueryDetail copy() {
OrmQueryDetail copy = new OrmQueryDetail();
copy.baseProps = baseProps.copy();
for (Map.Entry<String, OrmQueryProperties> entry : fetchPaths.entrySet()) {
copy.fetchPaths.put(entry.getKey(), entry.getValue().copy());
}
copy.includes = new LinkedHashSet<String>(includes);
return copy;
}
/**
* Calculate the hash for the query plan.
*/
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
if (baseProps == null) {
builder.add(false);
} else {
builder.add(true);
baseProps.queryPlanHash(request, builder);
}
/**
* Calculate the hash for the query plan.
*/
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
if (baseProps == null) {
builder.add(false);
if (fetchPaths != null) {
for (OrmQueryProperties p : fetchPaths.values()) {
p.queryPlanHash(request, builder);
}
}
}
/**
* Return true if equal in terms of autofetch (select and joins).
*/
public boolean isAutoFetchEqual(OrmQueryDetail otherDetail) {
return autofetchPlanHash() == otherDetail.autofetchPlanHash();
}
/**
* Calculate the hash for the query plan.
*/
private int autofetchPlanHash() {
int hc = (baseProps == null ? 1 : baseProps.autofetchPlanHash());
if (fetchPaths != null) {
for (OrmQueryProperties p : fetchPaths.values()) {
hc = hc * 31 + p.autofetchPlanHash();
}
}
return hc;
}
public String toString() {
StringBuilder sb = new StringBuilder();
if (baseProps != null) {
sb.append("select ").append(baseProps);
}
if (fetchPaths != null) {
for (OrmQueryProperties join : fetchPaths.values()) {
sb.append(" fetch ").append(join);
}
}
return sb.toString();
}
public int hashCode() {
throw new RuntimeException("should not use");
}
/**
* set the properties to include on the base / root entity.
*/
public void select(String columns) {
baseProps = new OrmQueryProperties(null, columns);
}
public boolean containsProperty(String property) {
if (baseProps == null) {
return true;
} else {
return baseProps.isIncluded(property);
}
}
/**
* Set the base / root query properties.
*/
public void setBase(OrmQueryProperties baseProps) {
this.baseProps = baseProps;
}
public List<OrmQueryProperties> removeSecondaryQueries() {
return removeSecondaryQueries(false);
}
public List<OrmQueryProperties> removeSecondaryLazyQueries() {
return removeSecondaryQueries(true);
}
private List<OrmQueryProperties> removeSecondaryQueries(boolean lazyQuery) {
ArrayList<String> matchingPaths = new ArrayList<String>(2);
for (OrmQueryProperties chunk : fetchPaths.values()) {
boolean match = lazyQuery ? chunk.isLazyFetch() : chunk.isQueryFetch();
if (match) {
matchingPaths.add(chunk.getPath());
}
}
if (matchingPaths.size() == 0) {
return null;
}
// sort into depth order to remove
Collections.sort(matchingPaths);
// the list of secondary queries
ArrayList<OrmQueryProperties> props = new ArrayList<OrmQueryProperties>(2);
for (int i = 0; i < matchingPaths.size(); i++) {
String path = matchingPaths.get(i);
includes.remove(path);
OrmQueryProperties secQuery = fetchPaths.remove(path);
if (secQuery == null) {
// the path has already been removed by another
// secondary query
} else {
builder.add(true);
baseProps.queryPlanHash(request, builder);
}
if (fetchPaths != null) {
for (OrmQueryProperties p : fetchPaths.values()) {
p.queryPlanHash(request, builder);
props.add(secQuery);
// remove any child properties for this path
Iterator<OrmQueryProperties> pass2It = fetchPaths.values().iterator();
while (pass2It.hasNext()) {
OrmQueryProperties pass2Prop = pass2It.next();
if (secQuery.isChild(pass2Prop)) {
// remove join to secondary query from the main query
// and add to this secondary query
pass2It.remove();
includes.remove(pass2Prop.getPath());
secQuery.add(pass2Prop);
}
}
}
}
/**
* Return true if equal in terms of autofetch (select and joins).
*/
public boolean isAutoFetchEqual(OrmQueryDetail otherDetail) {
return autofetchPlanHash() == otherDetail.autofetchPlanHash();
// Add the secondary queries as select properties
// to the parent chunk to ensure the foreign keys
// are included in the query
for (int i = 0; i < props.size(); i++) {
String path = props.get(i).getPath();
// split into parent and property
String[] split = SplitName.split(path);
// add property to parent chunk
OrmQueryProperties chunk = getChunk(split[0], true);
chunk.addSecondaryQueryJoin(split[1]);
}
/**
* Calculate the hash for the query plan.
*/
private int autofetchPlanHash() {
return props;
}
int hc = (baseProps == null ? 1 : baseProps.autofetchPlanHash());
public boolean tuneFetchProperties(OrmQueryDetail tunedDetail) {
if (fetchPaths != null) {
for (OrmQueryProperties p : fetchPaths.values()) {
hc = hc * 31 + p.autofetchPlanHash();
}
}
boolean tuned = false;
return hc;
}
OrmQueryProperties tunedRoot = tunedDetail.getChunk(null, false);
if (tunedRoot != null && tunedRoot.hasProperties()) {
tuned = true;
baseProps.setTunedProperties(tunedRoot);
public String toString() {
StringBuilder sb = new StringBuilder();
if (baseProps != null) {
sb.append("select ").append(baseProps);
}
if (fetchPaths != null) {
for (OrmQueryProperties join : fetchPaths.values()) {
sb.append(" fetch ").append(join);
}
}
return sb.toString();
}
public int hashCode() {
throw new RuntimeException("should not use");
}
/**
* set the properties to include on the base / root entity.
*/
public void select(String columns) {
baseProps = new OrmQueryProperties(null, columns);
}
public boolean containsProperty(String property) {
if (baseProps == null) {
return true;
for (OrmQueryProperties tunedChunk : tunedDetail.fetchPaths.values()) {
OrmQueryProperties chunk = getChunk(tunedChunk.getPath(), false);
if (chunk != null) {
// set the properties to select
chunk.setTunedProperties(tunedChunk);
} else {
return baseProps.isIncluded(property);
// add a missing join
putFetchPath(tunedChunk.copy());
}
}
}
return tuned;
}
/**
* Matches a join() method of the query.
*/
public void putFetchPath(OrmQueryProperties chunk) {
String path = chunk.getPath();
fetchPaths.put(path, chunk);
includes.add(path);
}
/**
* Remove all joins and properties.
* <p>
* Typically for the row count query.
* </p>
*/
public void clear() {
includes.clear();
fetchPaths.clear();
}
/**
* Set the fetch properties and configuration for a given path.
*
* @param path
* the property to join
* @param partialProps
* the properties on the join property to include
*/
public OrmQueryProperties addFetch(String path, String partialProps, FetchConfig fetchConfig) {
OrmQueryProperties chunk = getChunk(path, true);
chunk.setProperties(partialProps);
chunk.setFetchConfig(fetchConfig);
return chunk;
}
public void sortFetchPaths(BeanDescriptor<?> d) {
LinkedHashMap<String, OrmQueryProperties> sorted = new LinkedHashMap<String, OrmQueryProperties>(fetchPaths.size());
for (OrmQueryProperties p : fetchPaths.values()) {
sortFetchPaths(d, p, sorted);
}
/**
* Set the base / root query properties.
*/
public void setBase(OrmQueryProperties baseProps) {
this.baseProps = baseProps;
}
fetchPaths = sorted;
}
public List<OrmQueryProperties> removeSecondaryQueries() {
return removeSecondaryQueries(false);
}
private void sortFetchPaths(BeanDescriptor<?> d, OrmQueryProperties p,
LinkedHashMap<String, OrmQueryProperties> sorted) {
public List<OrmQueryProperties> removeSecondaryLazyQueries() {
return removeSecondaryQueries(true);
}
private List<OrmQueryProperties> removeSecondaryQueries(boolean lazyQuery) {
ArrayList<String> matchingPaths = new ArrayList<String>(2);
for (OrmQueryProperties chunk : fetchPaths.values()) {
boolean match = lazyQuery ? chunk.isLazyFetch() : chunk.isQueryFetch();
if (match) {
matchingPaths.add(chunk.getPath());
}
String path = p.getPath();
if (!sorted.containsKey(path)) {
String parentPath = p.getParentPath();
if (parentPath == null || sorted.containsKey(parentPath)) {
// off root path or parent already ahead in fetch order
sorted.put(path, p);
} else {
OrmQueryProperties parentProp = fetchPaths.get(parentPath);
if (parentProp == null) {
ElPropertyValue el = d.getElGetValue(parentPath);
if (el == null) {
String msg = "Path [" + parentPath + "] not valid from " + d.getFullName();
throw new PersistenceException(msg);
}
// add a missing parent path just fetching the Id property
BeanPropertyAssoc<?> assocOne = (BeanPropertyAssoc<?>) el.getBeanProperty();
parentProp = new OrmQueryProperties(parentPath, assocOne.getTargetIdProperty());
}
if (matchingPaths.size() == 0) {
return null;
if (parentProp != null) {
sortFetchPaths(d, parentProp, sorted);
}
sorted.put(path, p);
}
}
}
// sort into depth order to remove
Collections.sort(matchingPaths);
/**
* Convert 'fetch joins' to 'many' properties over to 'query joins'.
*/
public void convertManyFetchJoinsToQueryJoins(BeanDescriptor<?> beanDescriptor, String lazyLoadManyPath,
boolean allowOne, int queryBatch) {
// the list of secondary queries
ArrayList<OrmQueryProperties> props = new ArrayList<OrmQueryProperties>(2);
ArrayList<OrmQueryProperties> manyChunks = new ArrayList<OrmQueryProperties>(3);
for (int i = 0; i < matchingPaths.size(); i++) {
String path = matchingPaths.get(i);
includes.remove(path);
OrmQueryProperties secQuery = fetchPaths.remove(path);
if (secQuery == null) {
// the path has already been removed by another
// secondary query
// the name of the many fetch property if there is one
String manyFetchProperty = null;
} else {
props.add(secQuery);
// flag that is set once the many fetch property is chosen
boolean fetchJoinFirstMany = allowOne;
// remove any child properties for this path
Iterator<OrmQueryProperties> pass2It = fetchPaths.values().iterator();
while (pass2It.hasNext()) {
OrmQueryProperties pass2Prop = pass2It.next();
if (secQuery.isChild(pass2Prop)) {
// remove join to secondary query from the main query
// and add to this secondary query
pass2It.remove();
includes.remove(pass2Prop.getPath());
secQuery.add(pass2Prop);
}
}
}
sortFetchPaths(beanDescriptor);
for (String fetchPath : fetchPaths.keySet()) {
ElPropertyDeploy elProp = beanDescriptor.getElPropertyDeploy(fetchPath);
if (elProp.containsManySince(manyFetchProperty)) {
// this is a join to a *ToMany
OrmQueryProperties chunk = fetchPaths.get(fetchPath);
if (chunk.isFetchJoin() && !isLazyLoadManyRoot(lazyLoadManyPath, chunk)
&& !hasParentSecJoin(lazyLoadManyPath, chunk)) {
// this is a 'fetch join' (included in main query)
if (fetchJoinFirstMany) {
// letting the first one remain a 'fetch join'
fetchJoinFirstMany = false;
manyFetchProperty = fetchPath;
} else {
// convert this one over to a 'query join'
manyChunks.add(chunk);
}
}
// Add the secondary queries as select properties
// to the parent chunk to ensure the foreign keys
// are included in the query
for (int i = 0; i < props.size(); i++) {
String path = props.get(i).getPath();
// split into parent and property
String[] split = SplitName.split(path);
// add property to parent chunk
OrmQueryProperties chunk = getChunk(split[0], true);
chunk.addSecondaryQueryJoin(split[1]);
}
return props;
}
}
public boolean tuneFetchProperties(OrmQueryDetail tunedDetail) {
boolean tuned = false;
OrmQueryProperties tunedRoot = tunedDetail.getChunk(null, false);
if (tunedRoot != null && tunedRoot.hasProperties()) {
tuned = true;
baseProps.setTunedProperties(tunedRoot);
for (OrmQueryProperties tunedChunk : tunedDetail.fetchPaths.values()) {
OrmQueryProperties chunk = getChunk(tunedChunk.getPath(), false);
if (chunk != null) {
// set the properties to select
chunk.setTunedProperties(tunedChunk);
} else {
// add a missing join
putFetchPath(tunedChunk.copy());
}
}
}
return tuned;
for (int i = 0; i < manyChunks.size(); i++) {
// convert 'fetch joins' over to 'query joins'
manyChunks.get(i).setQueryFetch(queryBatch, true);
}
}
/**
* Matches a join() method of the query.
*/
public void putFetchPath(OrmQueryProperties chunk) {
String path = chunk.getPath();
fetchPaths.put(path, chunk);
includes.add(path);
/**
* Return true if this is actually the root level of a +query/+lazy loading query.
*/
private boolean isLazyLoadManyRoot(String lazyLoadManyPath, OrmQueryProperties chunk) {
if (lazyLoadManyPath != null && lazyLoadManyPath.equals(chunk.getPath())) {
return true;
}
return false;
}
/**
* Remove all joins and properties.
* <p>
* Typically for the row count query.
* </p>
*/
public void clear() {
includes.clear();
fetchPaths.clear();
}
/**
* Set the fetch properties and configuration for a given path.
*
* @param path
* the property to join
* @param partialProps
* the properties on the join property to include
*/
public OrmQueryProperties addFetch(String path, String partialProps, FetchConfig fetchConfig) {
OrmQueryProperties chunk = getChunk(path, true);
chunk.setProperties(partialProps);
chunk.setFetchConfig(fetchConfig);
return chunk;
}
public void sortFetchPaths(BeanDescriptor<?> d) {
LinkedHashMap<String, OrmQueryProperties> sorted = new LinkedHashMap<String, OrmQueryProperties>(fetchPaths.size());
for (OrmQueryProperties p : fetchPaths.values()) {
sortFetchPaths(d, p, sorted);
}
fetchPaths = sorted;
}
private void sortFetchPaths(BeanDescriptor<?> d, OrmQueryProperties p,
LinkedHashMap<String, OrmQueryProperties> sorted) {
String path = p.getPath();
if (!sorted.containsKey(path)) {
String parentPath = p.getParentPath();
if (parentPath == null || sorted.containsKey(parentPath)) {
// off root path or parent already ahead in fetch order
sorted.put(path, p);
} else {
OrmQueryProperties parentProp = fetchPaths.get(parentPath);
if (parentProp == null) {
ElPropertyValue el = d.getElGetValue(parentPath);
if (el == null) {
String msg = "Path [" + parentPath + "] not valid from " + d.getFullName();
throw new PersistenceException(msg);
}
// add a missing parent path just fetching the Id property
BeanPropertyAssoc<?> assocOne = (BeanPropertyAssoc<?>) el.getBeanProperty();
parentProp = new OrmQueryProperties(parentPath, assocOne.getTargetIdProperty());
}
if (parentProp != null) {
sortFetchPaths(d, parentProp, sorted);
}
sorted.put(path, p);
}
}
}
/**
* Convert 'fetch joins' to 'many' properties over to 'query joins'.
*/
public void convertManyFetchJoinsToQueryJoins(BeanDescriptor<?> beanDescriptor, String lazyLoadManyPath,
boolean allowOne, int queryBatch) {
ArrayList<OrmQueryProperties> manyChunks = new ArrayList<OrmQueryProperties>(3);
// the name of the many fetch property if there is one
String manyFetchProperty = null;
// flag that is set once the many fetch property is chosen
boolean fetchJoinFirstMany = allowOne;
sortFetchPaths(beanDescriptor);
for (String fetchPath : fetchPaths.keySet()) {
ElPropertyDeploy elProp = beanDescriptor.getElPropertyDeploy(fetchPath);
if (elProp.containsManySince(manyFetchProperty)) {
// this is a join to a *ToMany
OrmQueryProperties chunk = fetchPaths.get(fetchPath);
if (chunk.isFetchJoin()
&& !isLazyLoadManyRoot(lazyLoadManyPath, chunk)
&& !hasParentSecJoin(lazyLoadManyPath, chunk)) {
// this is a 'fetch join' (included in main query)
if (fetchJoinFirstMany) {
// letting the first one remain a 'fetch join'
fetchJoinFirstMany = false;
manyFetchProperty = fetchPath;
} else {
// convert this one over to a 'query join'
manyChunks.add(chunk);
}
}
}
}
for (int i = 0; i < manyChunks.size(); i++) {
// convert 'fetch joins' over to 'query joins'
manyChunks.get(i).setQueryFetch(queryBatch, true);
}
}
/**
* Return true if this is actually the root level of a +query/+lazy loading
* query.
*/
private boolean isLazyLoadManyRoot(String lazyLoadManyPath, OrmQueryProperties chunk) {
if (lazyLoadManyPath != null && lazyLoadManyPath.equals(chunk.getPath())) {
return true;
}
/**
* If the chunk has a parent that is a query or lazy join. In this case it does not need to be
* converted.
*/
private boolean hasParentSecJoin(String lazyLoadManyPath, OrmQueryProperties chunk) {
OrmQueryProperties parent = getParent(chunk);
if (parent == null) {
return false;
} else {
if (lazyLoadManyPath != null && lazyLoadManyPath.equals(parent.getPath())) {
return false;
} else if (!parent.isFetchJoin()) {
return true;
} else {
return hasParentSecJoin(lazyLoadManyPath, parent);
}
}
}
/**
* Return the parent chunk.
*/
private OrmQueryProperties getParent(OrmQueryProperties chunk) {
String parentPath = chunk.getParentPath();
return parentPath == null ? null : fetchPaths.get(parentPath);
}
/**
* Set any default select clauses for the main bean and any joins that have not explicitly defined
* a select clause.
* <p>
* That is this will use FetchType.LAZY to exclude some properties by default.
* </p>
*/
public void setDefaultSelectClause(BeanDescriptor<?> desc) {
if (desc.hasDefaultSelectClause() && !hasSelectClause()) {
if (baseProps == null) {
baseProps = new OrmQueryProperties();
}
baseProps.setDefaultProperties(desc.getDefaultSelectClause(), desc.getDefaultSelectClauseSet());
}
/**
* If the chunk has a parent that is a query or lazy join. In this case it
* does not need to be converted.
*/
private boolean hasParentSecJoin(String lazyLoadManyPath, OrmQueryProperties chunk) {
OrmQueryProperties parent = getParent(chunk);
if (parent == null) {
return false;
} else {
if (lazyLoadManyPath != null && lazyLoadManyPath.equals(parent.getPath())) {
return false;
} else if (!parent.isFetchJoin()) {
return true;
} else {
return hasParentSecJoin(lazyLoadManyPath, parent);
}
for (OrmQueryProperties joinProps : fetchPaths.values()) {
if (!joinProps.hasSelectClause()) {
BeanDescriptor<?> assocDesc = desc.getBeanDescriptor(joinProps.getPath());
if (assocDesc.hasDefaultSelectClause()) {
// use the default select clause
joinProps.setDefaultProperties(assocDesc.getDefaultSelectClause(), assocDesc.getDefaultSelectClauseSet());
}
}
}
}
/**
* Return the parent chunk.
*/
private OrmQueryProperties getParent(OrmQueryProperties chunk) {
String parentPath = chunk.getParentPath();
return parentPath == null ? null : fetchPaths.get(parentPath);
public boolean hasSelectClause() {
return (baseProps != null && baseProps.hasSelectClause());
}
/**
* Return true if the query detail has neither select properties specified or any joins defined.
*/
public boolean isEmpty() {
return fetchPaths.isEmpty() && (baseProps == null || !baseProps.hasProperties());
}
/**
* Return true if there are no joins.
*/
public boolean isJoinsEmpty() {
return fetchPaths.isEmpty();
}
/**
* Add the explicit bean join.
* <p>
* This is also used to Exclude the matching property from the parent select (aka remove the
* foreign key) because it is now included in it's on node in the SqlTree.
* </p>
*/
public void includeBeanJoin(String parentPath, String propertyName) {
OrmQueryProperties parentChunk = getChunk(parentPath, true);
parentChunk.includeBeanJoin(propertyName);
}
public OrmQueryProperties getChunk(String path, boolean create) {
if (path == null) {
return baseProps;
}
OrmQueryProperties props = fetchPaths.get(path);
if (create && props == null) {
props = new OrmQueryProperties(path);
putFetchPath(props);
return props;
/**
* Set any default select clauses for the main bean and any joins that have
* not explicitly defined a select clause.
* <p>
* That is this will use FetchType.LAZY to exclude some properties by
* default.
* </p>
*/
public void setDefaultSelectClause(BeanDescriptor<?> desc) {
if (desc.hasDefaultSelectClause() && !hasSelectClause()) {
if (baseProps == null) {
baseProps = new OrmQueryProperties();
}
baseProps.setDefaultProperties(desc.getDefaultSelectClause(), desc.getDefaultSelectClauseSet());
}
for (OrmQueryProperties joinProps : fetchPaths.values()) {
if (!joinProps.hasSelectClause()) {
BeanDescriptor<?> assocDesc = desc.getBeanDescriptor(joinProps.getPath());
if (assocDesc.hasDefaultSelectClause()) {
// use the default select clause
joinProps.setDefaultProperties(assocDesc.getDefaultSelectClause(), assocDesc.getDefaultSelectClauseSet());
}
}
}
} else {
return props;
}
}
public boolean hasSelectClause() {
return (baseProps != null && baseProps.hasSelectClause());
}
/**
* Return true if the property is included.
*/
public boolean includes(String path) {
/**
* Return true if the query detail has neither select properties specified
* or any joins defined.
*/
public boolean isEmpty() {
return fetchPaths.isEmpty() && (baseProps == null || !baseProps.hasProperties());
}
OrmQueryProperties chunk = fetchPaths.get(path);
/**
* Return true if there are no joins.
*/
public boolean isJoinsEmpty() {
return fetchPaths.isEmpty();
}
// may not have fetch properties if just +cache etc
return chunk != null && !chunk.isCache();
}
/**
* Add the explicit bean join.
* <p>
* This is also used to Exclude the matching property from the parent select
* (aka remove the foreign key) because it is now included in it's on node
* in the SqlTree.
* </p>
*/
public void includeBeanJoin(String parentPath, String propertyName) {
OrmQueryProperties parentChunk = getChunk(parentPath, true);
parentChunk.includeBeanJoin(propertyName);
}
public OrmQueryProperties getChunk(String path, boolean create) {
if (path == null) {
return baseProps;
}
OrmQueryProperties props = fetchPaths.get(path);
if (create && props == null) {
props = new OrmQueryProperties(path);
putFetchPath(props);
return props;
} else {
return props;
}
}
/**
* Return true if the property is included.
*/
public boolean includes(String path) {
OrmQueryProperties chunk = fetchPaths.get(path);
// may not have fetch properties if just +cache etc
return chunk != null && !chunk.isCache();
}
/**
* Return the property includes for this detail.
*/
public HashSet<String> getIncludes() {
return includes;
}
/**
* Return the property includes for this detail.
*/
public HashSet<String> getIncludes() {
return includes;
}
}
@@ -3,198 +3,196 @@ package com.avaje.ebeaninternal.server.querydefn;
import javax.persistence.PersistenceException;
/**
* Parses a Object relational query statement into a OrmQueryDetail and
* OrmQueryAttributes.
* Parses a Object relational query statement into a OrmQueryDetail and OrmQueryAttributes.
* <p>
* The reason they are split into detail and attributes is that the autoFetch
* feature is used to replace the OrmQueryDetail leaving the attributes
* unchanged.
* The reason they are split into detail and attributes is that the autoFetch feature is used to
* replace the OrmQueryDetail leaving the attributes unchanged.
* </p>
*/
public class OrmQueryDetailParser {
private final OrmQueryDetail detail = new OrmQueryDetail();
private final OrmQueryDetail detail = new OrmQueryDetail();
private int maxRows;
private int maxRows;
private int firstRow;
private int firstRow;
private String rawWhereClause;
private String rawWhereClause;
private String rawOrderBy;
private String rawOrderBy;
private final SimpleTextParser parser;
private final SimpleTextParser parser;
public OrmQueryDetailParser(String oql) {
this.parser = new SimpleTextParser(oql);
public OrmQueryDetailParser(String oql) {
this.parser = new SimpleTextParser(oql);
}
public void parse() throws PersistenceException {
parser.nextWord();
processInitial();
}
protected void assign(DefaultOrmQuery<?> query) {
query.setOrmQueryDetail(detail);
query.setFirstRow(firstRow);
query.setMaxRows(maxRows);
query.setRawWhereClause(rawWhereClause);
query.order(rawOrderBy);
}
private void processInitial() {
if (parser.isMatch("find")) {
OrmQueryProperties props = readFindFetch();
detail.setBase(props);
} else {
process();
}
while (!parser.isFinished()) {
process();
}
}
public void parse() throws PersistenceException {
private boolean isFetch() {
return parser.isMatch("fetch") || parser.isMatch("join");
}
private void process() {
if (isFetch()) {
OrmQueryProperties props = readFindFetch();
detail.putFetchPath(props);
} else if (parser.isMatch("where")) {
readWhere();
} else if (parser.isMatch("order", "by")) {
readOrderBy();
} else if (parser.isMatch("limit")) {
readLimit();
} else {
throw new PersistenceException("Query expected 'fetch', 'where','order by' or 'limit' keyword but got ["
+ parser.getWord() + "] \r " + parser.getOql());
}
}
private void readLimit() {
try {
String maxLimit = parser.nextWord();
maxRows = Integer.parseInt(maxLimit);
String offsetKeyword = parser.nextWord();
if (offsetKeyword != null) {
if (!parser.isMatch("offset")) {
throw new PersistenceException("expected offset keyword but got " + parser.getWord());
}
String firstRowLimit = parser.nextWord();
firstRow = Integer.parseInt(firstRowLimit);
parser.nextWord();
processInitial();
}
} catch (NumberFormatException e) {
String msg = "Expected an integer for maxRows or firstRows in limit offset clause";
throw new PersistenceException(msg, e);
}
}
protected void assign(DefaultOrmQuery<?> query) {
query.setOrmQueryDetail(detail);
query.setFirstRow(firstRow);
query.setMaxRows(maxRows);
query.setRawWhereClause(rawWhereClause);
query.order(rawOrderBy);
}
private void readOrderBy() {
// read the by
parser.nextWord();
private void processInitial() {
if (parser.isMatch("find")) {
OrmQueryProperties props = readFindFetch();
detail.setBase(props);
} else {
process();
}
while (!parser.isFinished()) {
process();
StringBuilder sb = new StringBuilder();
while (parser.nextWord() != null) {
if (parser.isMatch("limit")) {
break;
} else {
String w = parser.getWord();
if (!w.startsWith("(")) {
sb.append(" ");
}
sb.append(w);
}
}
rawOrderBy = sb.toString().trim();
if (!parser.isFinished()) {
readLimit();
}
}
private void readWhere() {
int nextMode = 0;
StringBuilder sb = new StringBuilder();
while ((parser.nextWord()) != null) {
if (parser.isMatch("order", "by")) {
nextMode = 1;
break;
} else if (parser.isMatch("limit")) {
nextMode = 2;
break;
} else {
sb.append(" ").append(parser.getWord());
}
}
String whereClause = sb.toString().trim();
if (whereClause.length() > 0) {
rawWhereClause = whereClause;
}
private boolean isFetch() {
return parser.isMatch("fetch") || parser.isMatch("join");
if (nextMode == 1) {
readOrderBy();
} else if (nextMode == 2) {
readLimit();
}
}
private void process() {
if (isFetch()) {
OrmQueryProperties props = readFindFetch();
detail.putFetchPath(props);
private OrmQueryProperties readFindFetch() {
} else if (parser.isMatch("where")) {
readWhere();
boolean readAlias = false;
} else if (parser.isMatch("order", "by")) {
readOrderBy();
} else if (parser.isMatch("limit")) {
readLimit();
} else {
throw new PersistenceException("Query expected 'fetch', 'where','order by' or 'limit' keyword but got ["
+ parser.getWord() + "] \r " + parser.getOql());
}
}
private void readLimit() {
try {
String maxLimit = parser.nextWord();
maxRows = Integer.parseInt(maxLimit);
String offsetKeyword = parser.nextWord();
if (offsetKeyword != null) {
if (!parser.isMatch("offset")) {
throw new PersistenceException("expected offset keyword but got " + parser.getWord());
}
String firstRowLimit = parser.nextWord();
firstRow = Integer.parseInt(firstRowLimit);
parser.nextWord();
}
} catch (NumberFormatException e) {
String msg = "Expected an integer for maxRows or firstRows in limit offset clause";
throw new PersistenceException(msg, e);
}
}
private void readOrderBy() {
// read the by
String props = null;
String path = parser.nextWord();
String token = null;
while ((token = parser.nextWord()) != null) {
if (!readAlias && parser.isMatch("as")) {
// next token is alias
parser.nextWord();
readAlias = true;
StringBuilder sb = new StringBuilder();
while (parser.nextWord() != null) {
if (parser.isMatch("limit")) {
break;
} else {
String w = parser.getWord();
if (!w.startsWith("(")) {
sb.append(" ");
}
sb.append(w);
}
}
rawOrderBy = sb.toString().trim();
} else if ('(' == token.charAt(0)) {
props = token;
parser.nextWord();
break;
if (!parser.isFinished()) {
readLimit();
}
} else if (isFindFetchEnd()) {
break;
} else if (!readAlias) {
readAlias = true;
} else {
throw new PersistenceException("Expected (props) or new 'fetch' 'where' but got " + token);
}
}
private void readWhere() {
int nextMode = 0;
StringBuilder sb = new StringBuilder();
while ((parser.nextWord()) != null) {
if (parser.isMatch("order", "by")) {
nextMode = 1;
break;
} else if (parser.isMatch("limit")) {
nextMode = 2;
break;
} else {
sb.append(" ").append(parser.getWord());
}
}
String whereClause = sb.toString().trim();
if (whereClause.length() > 0) {
rawWhereClause = whereClause;
}
if (nextMode == 1) {
readOrderBy();
} else if (nextMode == 2) {
readLimit();
}
if (props != null) {
props = props.substring(1, props.length() - 1);
}
return new OrmQueryProperties(path, props);
}
private OrmQueryProperties readFindFetch() {
boolean readAlias = false;
String props = null;
String path = parser.nextWord();
String token = null;
while ((token = parser.nextWord()) != null) {
if (!readAlias && parser.isMatch("as")) {
// next token is alias
parser.nextWord();
readAlias = true;
} else if ('(' == token.charAt(0)) {
props = token;
parser.nextWord();
break;
} else if (isFindFetchEnd()) {
break;
} else if (!readAlias) {
readAlias = true;
} else {
throw new PersistenceException("Expected (props) or new 'fetch' 'where' but got " + token);
}
}
if (props != null) {
props = props.substring(1, props.length() - 1);
}
return new OrmQueryProperties(path, props);
private boolean isFindFetchEnd() {
if (isFetch()) {
return true;
}
private boolean isFindFetchEnd() {
if (isFetch()) {
return true;
}
if (parser.isMatch("where")) {
return true;
}
if (parser.isMatch("order", "by")) {
return true;
}
return false;
if (parser.isMatch("where")) {
return true;
}
if (parser.isMatch("order", "by")) {
return true;
}
return false;
}
}
@@ -6,40 +6,40 @@ import com.avaje.ebeaninternal.api.SpiQuery;
public class OrmQueryLimitRequest implements SqlLimitRequest {
private final SpiQuery<?> ormQuery;
private final SpiQuery<?> ormQuery;
private final DatabasePlatform dbPlatform;
private final String sql;
private final String sqlOrderBy;
private final String sql;
private final String sqlOrderBy;
public OrmQueryLimitRequest(String sql, String sqlOrderBy, SpiQuery<?> ormQuery, DatabasePlatform dbPlatform) {
this.sql = sql;
this.sqlOrderBy = sqlOrderBy;
this.ormQuery = ormQuery;
this.dbPlatform = dbPlatform;
}
public String getDbOrderBy() {
return sqlOrderBy;
}
public String getDbSql() {
return sql;
}
public String getDbOrderBy() {
return sqlOrderBy;
}
public int getFirstRow() {
return ormQuery.getFirstRow();
}
public String getDbSql() {
return sql;
}
public int getMaxRows() {
return ormQuery.getMaxRows();
}
public int getFirstRow() {
return ormQuery.getFirstRow();
}
public boolean isDistinct() {
return ormQuery.isDistinct();
}
public int getMaxRows() {
return ormQuery.getMaxRows();
}
public boolean isDistinct() {
return ormQuery.isDistinctQuery();
}
public SpiQuery<?> getOrmQuery() {
return ormQuery;
File diff suppressed because it is too large Load Diff
@@ -2,176 +2,176 @@ package com.avaje.ebeaninternal.server.querydefn;
public class SimpleTextParser {
private final String oql;
private final char[] chars;
private final int eof;
private final String oql;
private final char[] chars;
private final int eof;
private int pos;
private String word;
private String lowerWord;
private int pos;
private String word;
private String lowerWord;
private int openParenthesisCount;
private int openParenthesisCount;
public SimpleTextParser(String oql) {
this.oql = oql;
this.chars = oql.toCharArray();
this.eof = oql.length();
}
public SimpleTextParser(String oql) {
this.oql = oql;
this.chars = oql.toCharArray();
this.eof = oql.length();
}
public int getPos() {
public int getPos() {
return pos;
}
public String getOql() {
return oql;
}
return oql;
}
public String getWord() {
return word;
}
public String getWord() {
return word;
}
public String peekNextWord() {
int origPos = pos;
String nw = nextWordInternal();
pos = origPos;
return nw;
}
public String peekNextWord() {
int origPos = pos;
String nw = nextWordInternal();
pos = origPos;
return nw;
}
/**
* Match the current and the next word.
*/
public boolean isMatch(String lowerMatch, String nextWordMatch) {
/**
* Match the current and the next word.
*/
public boolean isMatch(String lowerMatch, String nextWordMatch) {
if (isMatch(lowerMatch)) {
String nw = peekNextWord();
if (nw != null) {
nw = nw.toLowerCase();
return nw.equals(nextWordMatch);
}
}
return false;
}
if (isMatch(lowerMatch)) {
String nw = peekNextWord();
if (nw != null) {
nw = nw.toLowerCase();
return nw.equals(nextWordMatch);
}
}
return false;
}
public boolean isFinished() {
return word == null;
}
public boolean isFinished() {
return word == null;
}
public int findWordLower(String lowerMatch, int afterPos) {
this.pos = afterPos;
return findWordLower(lowerMatch);
}
public int findWordLower(String lowerMatch, int afterPos) {
this.pos = afterPos;
return findWordLower(lowerMatch);
}
public int findWordLower(String lowerMatch) {
do {
if (nextWord() == null) {
return -1;
}
if (lowerMatch.equals(lowerWord)) {
return pos - lowerWord.length();
}
} while (true);
}
public int findWordLower(String lowerMatch) {
do {
if (nextWord() == null) {
return -1;
}
if (lowerMatch.equals(lowerWord)) {
return pos - lowerWord.length();
}
} while (true);
}
/**
* Match the current word.
*/
public boolean isMatch(String lowerMatch) {
return lowerMatch.equals(lowerWord);
}
/**
* Match the current word.
*/
public boolean isMatch(String lowerMatch) {
return lowerMatch.equals(lowerWord);
}
public String nextWord() {
word = nextWordInternal();
if (word != null) {
lowerWord = word.toLowerCase();
}
return word;
}
public String nextWord() {
word = nextWordInternal();
if (word != null) {
lowerWord = word.toLowerCase();
}
return word;
}
private String nextWordInternal() {
trimLeadingWhitespace();
if (pos >= eof) {
return null;
}
int start = pos;
if (chars[pos] == '(') {
moveToClose();
} else {
moveToEndOfWord();
}
return oql.substring(start, pos);
}
private String nextWordInternal() {
trimLeadingWhitespace();
if (pos >= eof) {
return null;
}
int start = pos;
if (chars[pos] == '(') {
moveToClose();
} else {
moveToEndOfWord();
}
return oql.substring(start, pos);
}
private void moveToClose() {
private void moveToClose() {
pos++;
openParenthesisCount = 0;
pos++;
openParenthesisCount = 0;
for (; pos < eof; pos++) {
char c = chars[pos];
if (c == '(') {
// count nested parenthesis
openParenthesisCount++;
for (; pos < eof; pos++) {
char c = chars[pos];
if (c == '(') {
// count nested parenthesis
openParenthesisCount++;
} else if (c == ')') {
if (openParenthesisCount > 0) {
// still in nested parenthesis
--openParenthesisCount;
} else {
// we have found the end
pos++;
return;
}
}
}
}
} else if (c == ')') {
if (openParenthesisCount > 0) {
// still in nested parenthesis
--openParenthesisCount;
} else {
// we have found the end
pos++;
return;
}
}
}
}
private void moveToEndOfWord() {
char c = chars[pos];
boolean isOperator = isOperator(c);
for (; pos < eof; pos++) {
c = chars[pos];
if (isWordTerminator(c, isOperator)) {
return;
}
}
}
private void moveToEndOfWord() {
char c = chars[pos];
boolean isOperator = isOperator(c);
for (; pos < eof; pos++) {
c = chars[pos];
if (isWordTerminator(c, isOperator)) {
return;
}
}
}
private boolean isWordTerminator(char c, boolean isOperator) {
if (Character.isWhitespace(c)) {
return true;
}
if (isOperator(c)) {
return !isOperator;
}
if (c == '('){
return true;
}
private boolean isWordTerminator(char c, boolean isOperator) {
if (Character.isWhitespace(c)) {
return true;
}
if (isOperator(c)) {
return !isOperator;
}
if (c == '(') {
return true;
}
return isOperator;
}
return isOperator;
}
private boolean isOperator(char c) {
switch (c) {
case '<':
return true;
case '>':
return true;
case '=':
return true;
case '!':
return true;
private boolean isOperator(char c) {
switch (c) {
case '<':
return true;
case '>':
return true;
case '=':
return true;
case '!':
return true;
default:
return false;
}
}
default:
return false;
}
}
private void trimLeadingWhitespace() {
for (; pos < eof; pos++) {
char c = chars[pos];
if (!Character.isWhitespace(c)) {
break;
}
}
}
private void trimLeadingWhitespace() {
for (; pos < eof; pos++) {
char c = chars[pos];
if (!Character.isWhitespace(c)) {
break;
}
}
}
}
@@ -36,7 +36,7 @@ public class BeanReflectProperties {
return (String[]) field.get(null);
} catch (Exception e) {
throw new IllegalStateException(e);
throw new IllegalStateException("Error getting _ebean_props field on type "+clazz, e);
}
}
}
@@ -0,0 +1,27 @@
package com.avaje.ebeaninternal.server.transaction;
import java.sql.Connection;
import java.sql.SQLException;
/**
* AutoCommit friendly Transaction.
* <p>
* Skips actual commit and rollback as these are performed automatically.
*/
public class AutoCommitJdbcTransaction extends JdbcTransaction {
public AutoCommitJdbcTransaction(String id, boolean explicit, Connection connection, TransactionManager manager) {
super(id, explicit, connection, manager);
}
@Override
protected void performRollback() throws SQLException {
// do nothing as autoCommit
}
@Override
protected void performCommit() throws SQLException {
// do nothing as autoCommit
}
}
@@ -0,0 +1,36 @@
package com.avaje.ebeaninternal.server.transaction;
import java.sql.Connection;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
/**
* AutoCommit based TransactionManager.
* <p>
* Intended to be used if when autoCommit mode is desired.
*/
public class AutoCommitTransactionManager extends TransactionManager {
public AutoCommitTransactionManager(ClusterManager clusterManager, BackgroundExecutor backgroundExecutor,
ServerConfig config, BeanDescriptorManager descMgr, BootupClasses bootupClasses) {
super(clusterManager, backgroundExecutor, config, descMgr, bootupClasses);
}
/**
* Create an autoCommit based Transaction.
*/
@Override
protected SpiTransaction createTransaction(boolean explicit, Connection c, long id) {
return new AutoCommitJdbcTransaction(prefix + id, explicit, c, this);
}
}
@@ -50,11 +50,6 @@ public class JdbcTransaction implements SpiTransaction {
*/
protected final boolean explicit;
/**
* Set to true if the connection has autoCommit=true initially.
*/
protected final boolean autoCommit;
/**
* Behaviour for ending query only transactions.
*/
@@ -136,14 +131,10 @@ public class JdbcTransaction implements SpiTransaction {
try {
this.active = true;
this.id = id;
this.logPrefix = deriveLogPrefix(id,null);
this.logPrefix = deriveLogPrefix(id);
this.explicit = explicit;
this.manager = manager;
this.connection = connection;
this.autoCommit = connection.getAutoCommit();
if (this.autoCommit) {
connection.setAutoCommit(false);
}
this.onQueryOnly = manager == null ? OnQueryOnly.ROLLBACK : manager.getOnQueryOnly();
this.persistenceContext = new DefaultPersistenceContext();
@@ -152,23 +143,17 @@ public class JdbcTransaction implements SpiTransaction {
}
}
private static String deriveLogPrefix(String id, String label) {
private static String deriveLogPrefix(String id) {
StringBuilder sb = new StringBuilder();
sb.append("txn[");
if (id != null) {
sb.append(id);
}
sb.append("] ");
if (label != null) {
sb.append("label[").append(label).append("] ");
}
return sb.toString();
}
public void setLabel(String label) {
this.logPrefix = deriveLogPrefix(id,label);
}
public String getLogPrefix() {
return logPrefix;
}
@@ -518,14 +503,6 @@ public class JdbcTransaction implements SpiTransaction {
} catch (SQLException e) {
logger.error("Error setting to readOnly?", e);
}
try {
if (this.autoCommit) {
// reset the autoCommit status prior to returning to pool
connection.setAutoCommit(true);
}
} catch (SQLException e) {
logger.error("Error setting to readOnly?", e);
}
try {
connection.close();
} catch (Exception ex) {
@@ -541,16 +518,21 @@ public class JdbcTransaction implements SpiTransaction {
* Notify the transaction manager.
*/
protected void notifyCommit() {
if (manager == null) {
return;
}
if (queryOnly) {
manager.notifyOfQueryOnly(true, this, null);
} else {
manager.notifyOfCommit(this);
if (manager != null) {
if (queryOnly) {
manager.notifyOfQueryOnly(true, this, null);
} else {
manager.notifyOfCommit(this);
}
}
}
protected void notifyQueryOnly() {
if (manager != null) {
manager.notifyOfQueryOnly(true, this, null);
}
}
/**
* Rollback, Commit or Close for query only transaction.
* <p>
@@ -558,28 +540,56 @@ public class JdbcTransaction implements SpiTransaction {
* rollback or just close the connection for performance.
* </p>
*/
private void commitQueryOnly() {
protected void connectionEndForQueryOnly() {
try {
switch (onQueryOnly) {
case ROLLBACK:
connection.rollback();
performRollback();
break;
case COMMIT:
connection.commit();
performCommit();
break;
case CLOSE_ON_READCOMMITTED:
// Connection is closed via deactivate() which follows
// This optimisation is only available at READ COMMITTED Isolation
// valid at READ COMMITTED Isolation
break;
default:
connection.rollback();
performRollback();
}
} catch (SQLException e) {
String m = "Error when ending a query only transaction via " + onQueryOnly;
logger.error(m, e);
logger.error("Error when ending a query only transaction via " + onQueryOnly, e);
}
}
/**
* Perform the actual rollback on the connection.
*/
protected void performRollback() throws SQLException {
connection.rollback();
}
/**
* Perform the actual commit on the connection.
*/
protected void performCommit() throws SQLException {
connection.commit();
}
/**
* End the transaction on a query only request.
*/
public void endQueryOnly() {
if (!isActive()) {
throw new IllegalStateException(illegalStateMessage);
}
try {
connectionEndForQueryOnly();
} finally {
// these will not throw an exception
deactivate();
notifyQueryOnly();
}
}
/**
* Commit the transaction.
*/
@@ -590,13 +600,13 @@ public class JdbcTransaction implements SpiTransaction {
try {
if (queryOnly) {
// can rollback or just close for performance
commitQueryOnly();
connectionEndForQueryOnly();
} else {
// commit
if (batchControl != null && !batchControl.isEmpty()) {
batchControl.flush();
}
connection.commit();
performCommit();
}
} catch (Exception e) {
@@ -613,13 +623,12 @@ public class JdbcTransaction implements SpiTransaction {
* Notify the transaction manager.
*/
protected void notifyRollback(Throwable cause) {
if (manager == null) {
return;
}
if (queryOnly) {
manager.notifyOfQueryOnly(false, this, cause);
} else {
manager.notifyOfRollback(this, cause);
if (manager != null) {
if (queryOnly) {
manager.notifyOfQueryOnly(false, this, cause);
} else {
manager.notifyOfRollback(this, cause);
}
}
}
@@ -639,7 +648,7 @@ public class JdbcTransaction implements SpiTransaction {
throw new IllegalStateException(illegalStateMessage);
}
try {
connection.rollback();
performRollback();
} catch (Exception ex) {
throw new PersistenceException(ex);
@@ -42,7 +42,7 @@ public class TransactionManager {
public static final Logger TXN_LOGGER = LoggerFactory.getLogger("org.avaje.ebean.TXN");
/**
* The behaviour desired when ending a query only transaction.
* The behavior desired when ending a query only transaction.
*/
public enum OnQueryOnly {
@@ -62,49 +62,47 @@ public class TransactionManager {
COMMIT
}
private final BeanDescriptorManager beanDescriptorManager;
protected final BeanDescriptorManager beanDescriptorManager;
/**
* Prefix for transaction id's (logging).
*/
private final String prefix;
protected final String prefix;
private final String externalTransPrefix;
protected final String externalTransPrefix;
/**
* The dataSource of connections.
*/
private final DataSource dataSource;
protected final DataSource dataSource;
/**
* Flag to indicate the default Isolation is READ COMMITTED. This enables us
* to close queryOnly transactions rather than commit or rollback them.
*/
private final OnQueryOnly onQueryOnly;
protected final OnQueryOnly onQueryOnly;
/**
* The default batchMode for transactions.
*/
private final boolean defaultBatchMode;
protected final boolean defaultBatchMode;
private final BackgroundExecutor backgroundExecutor;
protected final BackgroundExecutor backgroundExecutor;
private final ClusterManager clusterManager;
protected final ClusterManager clusterManager;
//private final int commitDebugLevel;
private final String serverName;
protected final String serverName;
/**
* Id's for transaction logging.
*/
private AtomicLong transactionCounter = new AtomicLong(1000);
protected AtomicLong transactionCounter = new AtomicLong(1000);
private int clusterDebugLevel;
private final BulkEventListenerMap bulkEventListenerMap;
protected int clusterDebugLevel;
private TransactionEventListener[] transactionEventListeners;
protected final BulkEventListenerMap bulkEventListenerMap;
protected TransactionEventListener[] transactionEventListeners;
/**
* Create the TransactionManager
@@ -130,7 +128,7 @@ public class TransactionManager {
this.prefix = GlobalProperties.get("transaction.prefix", "");
this.externalTransPrefix = GlobalProperties.get("transaction.prefix", "e");
String value = GlobalProperties.get("transaction.onqueryonly", "ROLLBACK").toUpperCase().trim();
String value = GlobalProperties.get("transaction.onqueryonly", "CLOSE").toUpperCase().trim();
this.onQueryOnly = getOnQueryOnly(value, dataSource);
initialiseHeartbeat();
@@ -148,14 +146,14 @@ public class TransactionManager {
((DataSourcePool)dataSource).shutdown(deregisterDriver);
}
}
public BeanDescriptorManager getBeanDescriptorManager() {
return beanDescriptorManager;
}
public BulkEventListenerMap getBulkEventListenerMap() {
return bulkEventListenerMap;
}
public BeanDescriptorManager getBeanDescriptorManager() {
return beanDescriptorManager;
}
public BulkEventListenerMap getBulkEventListenerMap() {
return bulkEventListenerMap;
}
/**
* Return the behaviour to use when a query only transaction is committed.
@@ -171,7 +169,6 @@ public class TransactionManager {
*/
private OnQueryOnly getOnQueryOnly(String onQueryOnly, DataSource ds) {
if (onQueryOnly.equals("COMMIT")){
return OnQueryOnly.COMMIT;
}
@@ -222,26 +219,26 @@ public class TransactionManager {
return dataSource;
}
/**
* Return the cluster debug level.
*/
public int getClusterDebugLevel() {
return clusterDebugLevel;
}
/**
* Return the cluster debug level.
*/
public int getClusterDebugLevel() {
return clusterDebugLevel;
}
/**
* Set the cluster debug level.
*/
public void setClusterDebugLevel(int clusterDebugLevel) {
this.clusterDebugLevel = clusterDebugLevel;
}
/**
* Set the cluster debug level.
*/
public void setClusterDebugLevel(int clusterDebugLevel) {
this.clusterDebugLevel = clusterDebugLevel;
}
/**
* Defines the type of behaviour to use when closing a transaction that was used to query data only.
*/
public OnQueryOnly getOnQueryOnly() {
return onQueryOnly;
}
/**
* Defines the type of behavior to use when closing a transaction that was used to query data only.
*/
public OnQueryOnly getOnQueryOnly() {
return onQueryOnly;
}
/**
* Wrap the externally supplied Connection.
@@ -276,7 +273,7 @@ public class TransactionManager {
c = dataSource.getConnection();
long id = transactionCounter.incrementAndGet();
JdbcTransaction t = new JdbcTransaction(prefix + id, explicit, c, this);
SpiTransaction t = createTransaction(explicit, c, id);
// set the default batch mode. This can be on for
// jdbc drivers that support getGeneratedKeys
@@ -312,7 +309,7 @@ public class TransactionManager {
c = dataSource.getConnection();
long id = transactionCounter.incrementAndGet();
JdbcTransaction t = new JdbcTransaction(prefix + id, false, c, this);
SpiTransaction t = createTransaction(false, c, id);
// set the default batch mode. Can be true for
// jdbc drivers that support getGeneratedKeys
@@ -339,6 +336,13 @@ public class TransactionManager {
}
}
/**
* Create a new transaction.
*/
protected SpiTransaction createTransaction(boolean explicit, Connection c, long id) {
return new JdbcTransaction(prefix + id, explicit, c, this);
}
/**
* Process a local rolled back transaction.
*/
@@ -429,9 +433,6 @@ public class TransactionManager {
logger.error(m, ex);
}
}
/**
* Process a Transaction that comes from another framework or local code.
@@ -452,34 +453,33 @@ public class TransactionManager {
backgroundExecutor.execute(postCommit.notifyPersistListeners());
}
/**
* Notify local BeanPersistListeners etc of events from another server in the cluster.
*/
public void remoteTransactionEvent(RemoteTransactionEvent remoteEvent) {
if (clusterDebugLevel > 0 || logger.isDebugEnabled()){
logger.info("Cluster Received: "+remoteEvent.toString());
}
List<TableIUD> tableIUDList = remoteEvent.getTableIUDList();
if (tableIUDList != null){
for (int i = 0; i < tableIUDList.size(); i++) {
TableIUD tableIUD = tableIUDList.get(i);
beanDescriptorManager.cacheNotify(tableIUD);
}
}
List<BeanPersistIds> beanPersistList = remoteEvent.getBeanPersistList();
if (beanPersistList != null){
for (int i = 0; i < beanPersistList.size(); i++) {
BeanPersistIds beanPersist = beanPersistList.get(i);
beanPersist.notifyCacheAndListener();
}
}
/**
* Notify local BeanPersistListeners etc of events from another server in the cluster.
*/
public void remoteTransactionEvent(RemoteTransactionEvent remoteEvent) {
if (clusterDebugLevel > 0 || logger.isDebugEnabled()) {
logger.info("Cluster Received: " + remoteEvent.toString());
}
List<TableIUD> tableIUDList = remoteEvent.getTableIUDList();
if (tableIUDList != null) {
for (int i = 0; i < tableIUDList.size(); i++) {
TableIUD tableIUD = tableIUDList.get(i);
beanDescriptorManager.cacheNotify(tableIUD);
}
}
List<BeanPersistIds> beanPersistList = remoteEvent.getBeanPersistList();
if (beanPersistList != null) {
for (int i = 0; i < beanPersistList.size(); i++) {
BeanPersistIds beanPersist = beanPersistList.get(i);
beanPersist.notifyCacheAndListener();
}
}
}
}
@@ -3,8 +3,7 @@ package com.avaje.ebean;
import java.util.Map;
import java.util.Set;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.bean.EntityBean;
@@ -2,16 +2,18 @@ package com.avaje.ebeaninternal.server.querydefn;
import java.util.Set;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
import com.avaje.tests.model.basic.Order;
public class TestQueryLanguage extends TestCase {
public class TestQueryLanguage extends BaseTestCase {
@Test
public void test() {
DefaultOrmQuery<Order> q = check("find order join customer (id, name)");
@@ -0,0 +1,55 @@
package com.avaje.ebeaninternal.server.transaction;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.EbeanServerFactory;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.tests.model.basic.UTDetail;
public class TestAutoCommitDataSource extends BaseTestCase {
@Test
public void test() {
ServerConfig config = new ServerConfig();
config.setName("h2autocommit");
config.loadFromProperties();
config.addClass(UTDetail.class);
config.setDdlGenerate(true);
config.setDdlRun(true);
config.setAutoCommitMode(true);
GlobalProperties.setSkipPrimaryServer(true);
EbeanServer ebeanServer = EbeanServerFactory.create(config);
UTDetail detail1 = new UTDetail("one", 12, 30D);
UTDetail detail2 = new UTDetail("two", 11, 30D);
UTDetail detail3 = new UTDetail("three", 8, 30D);
Transaction txn = ebeanServer.beginTransaction();
try {
txn.setBatchMode(true);
ebeanServer.save(detail1);
ebeanServer.save(detail2);
ebeanServer.save(detail3);
txn.commit();
} finally {
txn.end();
}
List<UTDetail> details = ebeanServer.find(UTDetail.class).findList();
Assert.assertEquals(3, details.size());
}
}
@@ -1,9 +1,5 @@
package com.avaje.ebeaninternal.server.util;
import junit.framework.Assert;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -14,6 +10,10 @@ import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.Test;
/**
* This ensures that the ClassPathSearch supports normal file:file.jar files as well as jar/war files
* with bang paths. Bang paths typically look like this as a url:
@@ -2,7 +2,7 @@ package com.avaje.tests.basic;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.EbeanServerFactory;
@@ -1,176 +0,0 @@
package com.avaje.tests.basic;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.Future;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.EbeanServerFactory;
import com.avaje.ebean.FutureList;
import com.avaje.ebean.Query;
import com.avaje.ebean.SqlFutureList;
import com.avaje.ebean.SqlQuery;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.config.DataSourceConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.tests.model.basic.Order;
import com.avaje.tests.model.basic.ResetBasicData;
import com.avaje.tests.model.basic.TOne;
public class MainFutureList {
public static void main(String[] args) throws Exception {
checkFutureRowCount(true);
//testSqlQueryFuture();
//testOrmFuture();
}
public void executeDDL(EbeanServer server, String ddl) {
Transaction t = server.createTransaction();
try {
Connection connection = t.getConnection()
;
} finally {
t.end();
}
}
private void executeStmt(Connection c, String ddl) throws SQLException {
java.sql.PreparedStatement pstmt = null;
try {
pstmt = c.prepareStatement(ddl);
pstmt.execute();
} finally {
if (pstmt != null) {
pstmt.close();
}
}
}
private static EbeanServer createEbeanServer(boolean primary) {
if (primary){
return Ebean.getServer(null);
}
ServerConfig c = new ServerConfig();
c.setName("pgtest");
// // requires postgres driver in class path
// DataSourceConfig postgresDb = new DataSourceConfig();
// postgresDb.setDriver("org.postgresql.Driver");
// postgresDb.setUsername("test");
// postgresDb.setPassword("test");
// postgresDb.setUrl("jdbc:postgresql://127.0.0.1:5432/test");
// postgresDb.setHeartbeatSql("select count(*) from t_one");
// requires oracle driver in class path
DataSourceConfig oraDb = new DataSourceConfig();
oraDb.setDriver("oracle.jdbc.driver.OracleDriver");
oraDb.setUsername("junk");
oraDb.setPassword("junk");
oraDb.setUrl("jdbc:oracle:thin:junk/junk@localhost:1521:XE");
oraDb.setHeartbeatSql("select count(*) from dual");
c.loadFromProperties();
c.setDdlGenerate(true);
c.setDdlRun(true);
c.setDefaultServer(false);
c.setRegister(false);
// c.setDataSourceConfig(postgresDb);
c.setDataSourceConfig(oraDb);
//c.setDatabaseBooleanTrue("1");
//c.setDatabaseBooleanFalse("0");
//c.setDatabaseBooleanTrue("T");
//c.setDatabaseBooleanFalse("F");
//c.setDatabasePlatform(new Postgres83Platform());
c.addClass(TOne.class);
return EbeanServerFactory.create(c);
}
public static void checkFutureRowCount(boolean primay) throws Exception {
ResetBasicData.reset();
EbeanServer server = createEbeanServer(primay);
Query<Order> query = server.find(Order.class);
Future<Integer> futureRowCount = server.findFutureRowCount(query, null);
boolean done = futureRowCount.isDone();
System.out.println("done: "+done);
Integer rowCount = futureRowCount.get();
System.out.println("got rc:"+rowCount);
}
public static void checkSqlQueryFuture(boolean primay) throws Exception {
EbeanServer server = createEbeanServer(primay);
String sql = "select o.* from all_tables o";
SqlQuery sqlQuery = server.createSqlQuery(sql);
SqlFutureList list = server.findFutureList(sqlQuery, null);
System.out.println("start done:"+list.isDone());
Thread.sleep(200);
if (!list.isDone()){
list.cancel(true);
}
if (!list.isCancelled()){
List<SqlRow> list2 = list.get();
System.out.println("got "+list2.size());
}
Thread.sleep(3000);
System.out.println("done sleeping");
}
public static void checkOrmFuture() throws Exception {
ResetBasicData.reset();
//EbeanServer server = Ebean.getServer(null);
Query<Order> query = Ebean.find(Order.class);
FutureList<Order> futureList = query.findFutureList();
Thread.sleep(3000);
System.out.println("end of sleep");
if (!futureList.isDone()){
futureList.cancel(true);
}
System.out.println("and... done:"+futureList.isDone());
if (!futureList.isCancelled()){
//List<Order> l0 = futureList.get(30, TimeUnit.SECONDS);
List<Order> list = futureList.get();
System.out.println("list:"+list);
}
System.out.println("done");
}
}
@@ -1,7 +1,6 @@
package com.avaje.tests.basic;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -2,8 +2,7 @@ package com.avaje.tests.basic;
import java.sql.Date;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -21,7 +21,6 @@ public class TestErrorBindLog extends BaseTestCase {
} catch (PersistenceException e) {
String msg = e.getMessage();
e.printStackTrace();
Assert.assertTrue(msg.contains("Bind values:"));
}
}
@@ -3,8 +3,7 @@ package com.avaje.tests.basic;
import java.util.List;
import java.util.concurrent.ExecutionException;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -2,8 +2,7 @@ package com.avaje.tests.basic;
import java.sql.Timestamp;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -2,8 +2,7 @@ package com.avaje.tests.basic;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -2,8 +2,7 @@ package com.avaje.tests.basic;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -1,7 +1,6 @@
package com.avaje.tests.basic;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -4,8 +4,7 @@ import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -3,8 +3,7 @@ package com.avaje.tests.basic;
import java.util.Map;
import java.util.Set;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -2,8 +2,7 @@ package com.avaje.tests.basic;
import java.util.Map;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -7,7 +7,6 @@ import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.Transaction;
import com.avaje.tests.model.basic.Customer;
import com.avaje.tests.model.basic.EBasicVer;
import com.avaje.tests.model.basic.Order;
@@ -20,9 +19,9 @@ public class TestLogTransLogOnError extends BaseTestCase {
ResetBasicData.reset();
Transaction t = Ebean.beginTransaction();
Ebean.beginTransaction();
try {
// t.log("--- hello");
Ebean.find(Customer.class).findList();
Ebean.find(Order.class).where().gt("id", 1).findList();
@@ -52,9 +51,8 @@ public class TestLogTransLogOnError extends BaseTestCase {
ResetBasicData.reset();
Transaction t = Ebean.beginTransaction();
Ebean.beginTransaction();
try {
// t.log("--- hello testPersistError");
Ebean.find(Customer.class).findList();
EBasicVer newBean = new EBasicVer();
@@ -3,8 +3,7 @@ package com.avaje.tests.basic;
import java.util.ArrayList;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -3,8 +3,7 @@ package com.avaje.tests.basic;
import java.sql.Date;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -2,8 +2,7 @@ package com.avaje.tests.basic;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -2,8 +2,7 @@ package com.avaje.tests.basic;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -2,8 +2,7 @@ package com.avaje.tests.basic;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -3,8 +3,7 @@ package com.avaje.tests.basic;
import java.util.List;
import java.util.Random;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -1,7 +1,6 @@
package com.avaje.tests.basic;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -1,7 +1,6 @@
package com.avaje.tests.basic;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -1,7 +1,6 @@
package com.avaje.tests.basic;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -2,8 +2,7 @@ package com.avaje.tests.basic;
import java.sql.Timestamp;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -1,7 +1,6 @@
package com.avaje.tests.basic;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -4,8 +4,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -1,7 +1,6 @@
package com.avaje.tests.basic;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -1,7 +1,6 @@
package com.avaje.tests.basic;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -1,7 +1,6 @@
package com.avaje.tests.basic;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -2,8 +2,7 @@ package com.avaje.tests.basic;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -2,8 +2,7 @@ package com.avaje.tests.basic.encrypt;
import java.sql.Timestamp;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -1,7 +1,6 @@
package com.avaje.tests.basic.event;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -1,7 +1,6 @@
package com.avaje.tests.basic.one2one;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -3,8 +3,7 @@ package com.avaje.tests.batchload;
import java.util.ArrayList;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -1,12 +1,10 @@
package com.avaje.tests.batchload;
import java.util.List;
import java.util.UUID;
import javax.persistence.EntityNotFoundException;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -57,7 +55,7 @@ public class TestBatchLazyWithDeleted extends BaseTestCase {
Assert.assertEquals(1, deletedCount);
for (UUTwo u : list) {
UUOne master = u.getMaster();
u.getMaster();
//BeanState beanState = Ebean.getBeanState(master);
//Assert.assertTrue(beanState.isReference());
}
@@ -2,8 +2,7 @@ package com.avaje.tests.batchload;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -2,8 +2,7 @@ package com.avaje.tests.batchload;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -4,8 +4,7 @@ import java.util.UUID;
import javax.persistence.EntityNotFoundException;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -2,8 +2,7 @@ package com.avaje.tests.batchload;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -2,8 +2,7 @@ package com.avaje.tests.batchload;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -1,7 +1,6 @@
package com.avaje.tests.batchload;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
+2 -2
View File
@@ -1,7 +1,6 @@
package com.avaje.tests.cache;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -31,6 +30,7 @@ public class TestCacheBasic extends BaseTestCase {
ServerCacheStatistics statistics = countryCache.getStatistics(false);
int hc = statistics.getHitCount();
Assert.assertEquals(1, hc);
Assert.assertNotNull(c0);
// Country c1 = Ebean.getReference(Country.class, "NZ");
// Assert.assertEquals(2, countryCache.getStatistics(false).getHitCount());

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