Compare commits

...
Author SHA1 Message Date
Rob Bygrave b8ae443074 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.7 2014-09-10 00:02:31 +12:00
Rob Bygrave ab9762bbd9 Fix for #190 - javax.persistence.PersistenceException: Server [null] was not found? at EntityBeanIntercept.loadBean(EntityBeanIntercept.java:693) 2014-09-10 00:00:49 +12:00
Rob Bygrave 2c90b239cd [maven-release-plugin] prepare for next development iteration 2014-08-22 00:45:30 +12:00
Rob Bygrave 13806767a3 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.6 2014-08-22 00:45:20 +12:00
Rob Bygrave 40e43b9d5f update agent and maven plugin versions to 4.1.6 2014-08-22 00:44:08 +12:00
Rob Bygrave 29f1e63146 Fix for #186 - Fix for Lazy load when fetched optional ManyToOne bean is null in database 2014-08-22 00:41:12 +12:00
rbygrave 5cf008b521 Fix for #182 Modify pom removing provided scope from slf4j-api dependency 2014-08-12 23:30:41 +12:00
rbygrave 3e788d4598 [maven-release-plugin] prepare for next development iteration 2014-08-01 21:43:53 +12:00
rbygrave 8a29454893 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.5 2014-08-01 21:43:31 +12:00
rbygrave 62c083a6ea Add LongMaxUpdater, change CQueryPlanStats to use LongMaxUpdater 2014-07-29 23:48:38 +12:00
rbygrave 7f8e4be396 Merge branch 'master' of github.com:ebean-orm/avaje-ebeanorm 2014-07-29 23:44:38 +12:00
rbygrave 2ad39974e8 No effective change - code format and javadoc tidy 2014-07-29 23:44:03 +12:00
Rob Bygrave 664e95d87b Merge pull request #180 from rvbiljouw/master
Index annotation for generating single-column indexes in DDL
2014-07-29 22:45:39 +12:00
Rick van Biljouw 5cf668f65b Disallow Index annotation on fields that have a relationship annotation (OneToMany, ManyToOne or OneToOne) 2014-07-28 19:44:25 +01:00
Rick van Biljouw 31f56621ff Added @Index annotation to generate single-column indexes 2014-07-28 18:43:50 +01:00
rbygrave 11a6cd04ea Update README with latest version 2014-07-26 00:12:41 +12:00
rbygrave fdb573bfe2 Fix for #177 - EbeanServer.refresh() not loading/refreshing @Lob property unless it is annotated with @Basic(fetch=FetchType.EAGER) 2014-07-26 00:09:26 +12:00
rbygrave ae0e1fe691 No effective change - remove some incorrect javadoc 2014-07-25 23:21:14 +12:00
rbygrave 0511e01273 [maven-release-plugin] prepare for next development iteration 2014-07-24 19:45:56 +12:00
rbygrave f3bd38536c [maven-release-plugin] prepare release avaje-ebeanorm-4.1.4 2014-07-24 19:45:30 +12:00
rbygrave 139a558165 Add test TestPropertyChangeListener 2014-07-21 22:59:31 +12:00
rbygrave 53b40f7ce0 Update .gitignore for h2 db directory 2014-07-21 22:32:05 +12:00
rbygrave e50656871d Fix for #175 - Change to use System.getProperty("ebean.serverfactory")
rather than GlobalProperties
2014-07-21 22:31:01 +12:00
rbygrave f7ece0f13c Fix for #174 - Incorrect SQL generated with syntax error at the "order by" clause 2014-07-21 21:49:31 +12:00
rbygrave 562c6fafc9 Update maven-enhancer to 4.1.5, improve test - TestNotEnhancedMappedSuper 2014-07-19 19:41:57 +12:00
rbygrave 63e9f603ec Code cleanup - move old iterator/while loops to for loops 2014-07-19 19:10:13 +12:00
rbygrave 0c4fc4eea3 Fix for #173 - Ability to use a MappedSuperClass without being enhanced as long as it doesn't have persistent fields 2014-07-19 17:49:03 +12:00
rbygrave 2dca1bdf5b Fix for #172 - Regression: @Where predicates not applied in eager fetch (but are on lazy load) 2014-07-18 23:35:14 +12:00
rbygrave 6403a83e0e No effective change - format only 2014-07-18 22:52:49 +12:00
Rob Bygrave b57b91ffef [maven-release-plugin] prepare for next development iteration 2014-07-16 23:18:02 +12:00
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
Rob Bygrave 723ae6bffc [maven-release-plugin] prepare release avaje-ebeanorm-4.1.0 2014-06-26 19:38:48 +12:00
Rob Bygrave 33b173dd55 Bump pom to 4.1.0-SNAPSHOT 2014-06-26 19:37:15 +12:00
Rob Bygrave 7966d74eb3 Just tweaking some very common string appending with buffer size prediction 2014-06-26 01:15:48 +12:00
Rob Bygrave 28cb2e210d Merge branch 'master' of github.com:ebean-orm/avaje-ebeanorm 2014-06-26 00:37:40 +12:00
Rob Bygrave 1109d412e7 Fix for #154 - @OrderBy on a @OneToMany property is not used when lazy loading 2014-06-26 00:37:31 +12:00
Rob Bygrave 3e33ece65a No effective change - add protected modified 2014-06-26 00:12:55 +12:00
Rob Bygrave 1dd200fba2 Merge pull request #153 from waffle-iron/master
waffle.io Badge
2014-06-25 09:16:10 +12:00
Making GitHub Delicious. f0ea977f91 add waffle.io badge 2014-06-24 15:13:51 -06:00
Rob Bygrave e830d8efc7 Additional test in TestOrderByWithMany 2014-06-24 00:17:11 +12:00
Rob Bygrave 0b2ba2a82e Fix for #151 - IllegalArgumentException using enum in filter - toBeanType() throws 2014-06-24 00:04:32 +12:00
Rob Bygrave df526b8f3c Fix for #149 - OneToMany with nested ManyToMany deletion bug 2014-06-23 23:34:22 +12:00
Rob Bygrave 8fe1f32e78 No effective change - code reformat on PersistRequestBean 2014-06-21 00:03:28 +12:00
Rob Bygrave c5ea93fb8e Fix for #150 - Stateless update - property changed in BeanPersistAdapter
preUpdate not included in update
2014-06-20 23:49:29 +12:00
Rob Bygrave dd83bc4d26 Test BeanPersistAdapter with stateless update 2014-06-19 02:03:59 +12:00
Rob Bygrave 9b4b7a1a01 Opps, part of #108 2014-06-19 02:03:14 +12:00
Rob Bygrave 548fa745d6 Fix for #108 - ENH (443) : Postgres HStore support 2014-06-19 01:35:07 +12:00
Rob Bygrave 7d83e3f49e Fix for #97 - ENH: (406) Add support for Postgres Serial type 2014-06-13 23:48:45 +12:00
Rob Bygrave 974284bf8a Addition to fix for #139 - DDL constraint generation when table with schema 2014-06-13 02:38:14 +12:00
Rob Bygrave b446f00cd8 Addition to fix for #139 - DDL constraint generation when table with schema 2014-06-12 23:48:40 +12:00
Rob Bygrave a4095b4ae5 Fix for #142 - ENH: Add support for using RawSql with user supplied ResultSet 2014-06-12 21:11:14 +12:00
Rob Bygrave af6dbd0dbe Fix for #140 - Add some support for RawSql findRowCount, remove BeanCollection.hasMoreRows() 2014-06-10 23:52:05 +12:00
Rob Bygrave db17ddd069 No effective change - tidy up test for findPagedList 2014-06-10 23:10:27 +12:00
Rob Bygrave b6b85225e7 Fix for #139 - DDL constraint generation when table with schema 2014-06-10 21:13:16 +12:00
Rob Bygrave b57abfa732 Update README.md 2014-05-30 03:28:25 +12:00
Rob Bygrave 4a4927b1f3 Update README.md 2014-05-30 03:27:38 +12:00
Rob Bygrave 6ac34bec97 Update README.md 2014-05-30 03:26:59 +12:00
Rob Bygrave 82725e6a98 [maven-release-plugin] prepare for next development iteration 2014-05-30 03:07:28 +12:00
285 changed files with 9557 additions and 5552 deletions
+1
View File
@@ -6,6 +6,7 @@
target/
logs/
log/
/db
/mydb.db
+2 -2
View File
@@ -1,12 +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
Maven Dependency
----------------
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>4.0.2</version>
<version>4.1.4</version>
</dependency>
+9 -10
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>4.0.4</version>
<version>4.1.7</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -48,6 +48,12 @@
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
@@ -77,13 +83,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-agentloader</artifactId>
@@ -94,7 +93,7 @@
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-agent</artifactId>
<version>4.1.2</version>
<version>4.1.6</version>
<scope>test</scope>
</dependency>
@@ -186,7 +185,7 @@
<plugin>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
<version>4.1.2</version>
<version>4.1.6</version>
<executions>
<!-- Not going to enhance Model bean -->
<execution>
+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().
@@ -6,8 +6,6 @@ import com.avaje.ebean.common.BootupEbeanManager;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.util.ClassUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Creates EbeanServer instances.
@@ -26,14 +24,9 @@ import org.slf4j.LoggerFactory;
* methods on the Ebean singleton such as {@link Ebean#find(Class)} are just a
* convenient way of using the 'default/primary' EbeanServer.
* </p>
*
* @author Rob Bygrave
*
*/
public class EbeanServerFactory {
private static final Logger logger = LoggerFactory.getLogger(EbeanServerFactory.class);
private static BootupEbeanManager serverFactory = createServerFactory();
/**
@@ -69,27 +62,10 @@ public class EbeanServerFactory {
private static BootupEbeanManager createServerFactory() {
// String d___ =
// com.avaje.ebean.server.core.DefaultServerFactory.class.getName();
String dflt = "com.avaje.ebeaninternal.server.core.DefaultServerFactory";
String implClassName = GlobalProperties.get("ebean.serverfactory", dflt);
String implClassName = System.getProperty("ebean.serverfactory", dflt);
int delaySecs = GlobalProperties.getInt("ebean.start.delay", 0);
if (delaySecs > 0) {
try {
// perhaps useful to delay the startup to give time to
// attach a debugger when running in a server like tomcat.
String m = "Ebean sleeping " + delaySecs + " seconds due to ebean.start.delay";
logger.info(m);
Thread.sleep(delaySecs * 1000);
} catch (InterruptedException e) {
String m = "Interrupting debug.start.delay of " + delaySecs;
logger.error(m, e);
}
}
try {
// use a client side implementation?
return (BootupEbeanManager) ClassUtil.newInstance(implClassName);
} catch (Exception ex) {
throw new RuntimeException(ex);
+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.
*
+51 -3
View File
@@ -1,6 +1,7 @@
package com.avaje.ebean;
import java.io.Serializable;
import java.sql.ResultSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -135,18 +136,33 @@ import com.avaje.ebean.util.CamelCaseHelper;
* Note that lazy loading also works with object graphs built with RawSql.
* </p>
*
* @author rbygrave
*
*/
public final class RawSql implements Serializable {
private static final long serialVersionUID = 1L;
private final ResultSet resultSet;
private final Sql sql;
private final ColumnMapping columnMapping;
protected RawSql(Sql sql, ColumnMapping columnMapping) {
/**
* Construct with a ResultSet and properties that the columns map to.
* <p>
* The properties listed in the propertyNames must be in the same order as the columns in the
* resultSet.
* <p>
* When a query executes this RawSql object then it will close the resultSet.
*/
public RawSql(ResultSet resultSet, String... propertyNames) {
this.resultSet = resultSet;
this.sql = null;
this.columnMapping = new ColumnMapping(propertyNames);
}
protected RawSql(ResultSet resultSet, Sql sql, ColumnMapping columnMapping) {
this.resultSet = resultSet;
this.sql = sql;
this.columnMapping = columnMapping;
}
@@ -158,6 +174,14 @@ public final class RawSql implements Serializable {
return sql;
}
/**
* Return the resultSet if this is a ResultSet based RawSql.
*/
public ResultSet getResultSet() {
return resultSet;
}
/**
* Return the column mapping for the SQL columns to bean properties.
*/
@@ -169,6 +193,9 @@ public final class RawSql implements Serializable {
* Return the hash for this query.
*/
public int queryHash() {
if (resultSet != null) {
return 31 * columnMapping.queryHash();
}
return 31 * sql.queryHash() + columnMapping.queryHash();
}
@@ -329,6 +356,7 @@ public final class RawSql implements Serializable {
private final LinkedHashMap<String, Column> dbColumnMap;
private final Map<String, String> propertyMap;
private final Map<String, Column> propertyColumnMap;
private final boolean parsed;
@@ -364,6 +392,26 @@ public final class RawSql implements Serializable {
this.propertyColumnMap = null;
this.dbColumnMap = new LinkedHashMap<String, Column>();
}
/**
* Construct for ResultSet use.
*/
protected ColumnMapping(String... propertyNames) {
this.immutable = false;
this.parsed = false;
this.propertyMap = null;
//this.propertyColumnMap = null;
this.dbColumnMap = new LinkedHashMap<String, Column>();
int hc = 31;
int pos = 0;
for (String prop : propertyNames) {
hc = 31 * hc + prop.hashCode();
dbColumnMap.put(prop, new Column(pos++, prop, null, prop));
}
propertyColumnMap = dbColumnMap;
this.queryHashCode = hc;
}
/**
* Construct an immutable ColumnMapping based on collected information.
@@ -1,5 +1,7 @@
package com.avaje.ebean;
import java.sql.ResultSet;
import com.avaje.ebean.RawSql.ColumnMapping;
import com.avaje.ebean.RawSql.Sql;
@@ -10,8 +12,6 @@ import com.avaje.ebean.RawSql.Sql;
* named query.
* </p>
*
* @author rbygrave
*
* @see RawSql
*/
public class RawSqlBuilder {
@@ -21,10 +21,23 @@ public class RawSqlBuilder {
*/
public static final String IGNORE_COLUMN = "$$_IGNORE_COLUMN_$$";
private final ResultSet resultSet;
private final Sql sql;
private final ColumnMapping columnMapping;
/**
* Create and return a RawSql object based on the resultSet and list of properties the columns in
* the resultSet map to.
* <p>
* The properties listed in the propertyNames must be in the same order as the columns in the
* resultSet.
*/
public static RawSql resultSet(ResultSet resultSet, String... propertyNames) {
return new RawSql(resultSet, propertyNames);
}
/**
* Return an unparsed RawSqlBuilder. Unlike a parsed one this query can not be
* modified - so no additional WHERE or HAVING expressions can be added to
@@ -58,9 +71,17 @@ public class RawSqlBuilder {
return new RawSqlBuilder(sql2, mapping);
}
private RawSqlBuilder(ResultSet resultSet, ColumnMapping columnMapping) {
this.resultSet = resultSet;
this.columnMapping = columnMapping;
this.sql = null;
}
private RawSqlBuilder(Sql sql, ColumnMapping columnMapping) {
this.sql = sql;
this.columnMapping = columnMapping;
this.resultSet = null;
}
/**
@@ -92,7 +113,7 @@ public class RawSqlBuilder {
* has been defined.
*/
public RawSql create() {
return new RawSql(sql, columnMapping.createImmutableCopy());
return new RawSql(resultSet, sql, columnMapping.createImmutableCopy());
}
/**
@@ -101,4 +122,6 @@ public class RawSqlBuilder {
protected Sql getSql() {
return sql;
}
}
@@ -0,0 +1,15 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ColumnHstore {
}
@@ -0,0 +1,24 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An annotation for declaring an index on a single column.
*
* @author rvbiljouw
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Index {
/**
* Name of the index
*
* @return index name
*/
String value() default "";
}
@@ -55,21 +55,6 @@ import com.avaje.ebean.TxType;
* Ebean.save(order);
* }
* </pre>
*
* <p>
* During development and testing you can set a debug level which will log the
* transaction begin, commit and rollback events so that you can easily confirm
* it is behaving as you would expect.
* </p>
*
* <pre class="code">
* ## in ebean.properties file
*
* ## Log transaction begins and ends etc
* ## (0=NoLogging 1=minimal ... 9=logAll)
* ebean.debug.transaction=3
*
* </pre>
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@@ -137,22 +137,6 @@ public interface BeanCollection<E> extends Serializable {
*/
public Collection<?> getActualEntries();
/**
* Set to true if maxRows was hit and there are actually more rows available.
* <p>
* Can be used by client code that is paging through results using
* setFirstRow() setMaxRows(). If this returns true then the client can
* display a 'next' button etc.
* </p>
*/
public boolean hasMoreRows();
/**
* Set to true when maxRows is hit but there are actually more rows available.
* This is set so that client code knows that there is more data available.
*/
public void setHasMoreRows(boolean hasMoreRows);
/**
* return true if there are real rows held. Return false is this is using
* Deferred fetch to lazy load the rows and the rows have not yet been
@@ -39,8 +39,6 @@ public final class EntityBeanIntercept implements Serializable {
private transient PersistenceContext persistenceContext;
private transient BeanLoader beanLoader;
private int beanLoaderIndex;
private String ebeanServerName;
@@ -200,34 +198,22 @@ public final class EntityBeanIntercept implements Serializable {
}
/**
* Return the index position for batch loading via BeanLoader.
* Set the BeanLoader with PersistenceContext.
*/
public int getBeanLoaderIndex() {
return beanLoaderIndex;
}
/**
* Set Lazy Loading by ebeanServerName.
* <p>
* This is for reference beans created by themselves.
* </p>
*/
public void setBeanLoaderByServerName(String ebeanServerName) {
this.beanLoaderIndex = 0;
this.beanLoader = null;
this.ebeanServerName = ebeanServerName;
}
/**
* Set the BeanLoader for general lazy loading.
*/
public void setBeanLoader(int index, BeanLoader beanLoader, PersistenceContext ctx) {
this.beanLoaderIndex = index;
public void setBeanLoader(BeanLoader beanLoader, PersistenceContext ctx) {
this.beanLoader = beanLoader;
this.persistenceContext = ctx;
this.ebeanServerName = beanLoader.getName();
}
/**
* Set the BeanLoader.
*/
public void setBeanLoader(BeanLoader beanLoader) {
this.beanLoader = beanLoader;
this.ebeanServerName = beanLoader.getName();
}
public boolean isFullyLoadedBean() {
return fullyLoadedBean;
}
@@ -45,12 +45,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
*/
protected final String propertyName;
/**
* Flag set to true if rows are limited by firstRow maxRows and more rows
* exist. For use by client to enable 'next' for paging.
*/
protected boolean hasMoreRows;
protected ModifyHolder<E> modifyHolder;
protected ModifyListenMode modifyListenMode;
@@ -151,26 +145,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
this.readOnly = readOnly;
}
/**
* Set to true if maxRows was hit and there are actually more rows available.
* <p>
* Can be used by client code that is paging through results using
* setFirstRow() setMaxRows(). If this returns true then the client can
* display a 'next' button etc.
* </p>
*/
public boolean hasMoreRows() {
return hasMoreRows;
}
/**
* Set to true when maxRows is hit but there are actually more rows available.
* This is set so that client code knows that there is more data available.
*/
public void setHasMoreRows(boolean hasMoreRows) {
this.hasMoreRows = hasMoreRows;
}
protected void checkReadOnly() {
if (readOnly) {
String msg = "This collection is in ReadOnly mode";
@@ -153,7 +153,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
}
public String toString() {
StringBuffer sb = new StringBuffer();
StringBuffer sb = new StringBuffer(50);
sb.append("BeanList ");
if (isReadOnly()) {
sb.append("readOnly ");
@@ -163,7 +163,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
} else {
sb.append("size[").append(list.size()).append("] ");
sb.append("hasMoreRows[").append(hasMoreRows).append("] ");
sb.append("list").append(list).append("");
}
return sb.toString();
@@ -154,7 +154,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
}
public String toString() {
StringBuffer sb = new StringBuffer();
StringBuffer sb = new StringBuffer(50);
sb.append("BeanMap ");
if (isReadOnly()) {
sb.append("readOnly ");
@@ -164,7 +164,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
} else {
sb.append("size[").append(map.size()).append("]");
sb.append(" hasMoreRows[").append(hasMoreRows).append("]");
sb.append(" map").append(map);
}
return sb.toString();
@@ -143,7 +143,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
}
public String toString() {
StringBuffer sb = new StringBuffer();
StringBuffer sb = new StringBuffer(50);
sb.append("BeanSet ");
if (isReadOnly()) {
sb.append("readOnly ");
@@ -153,7 +153,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
} else {
sb.append("size[").append(set.size()).append("]");
sb.append(" hasMoreRows[").append(hasMoreRows).append("]");
sb.append(" set").append(set);
}
return sb.toString();
@@ -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);
@@ -1,11 +1,10 @@
package com.avaje.ebean.config;
import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.Set;
/**
* A map like structure of properties.
@@ -68,9 +67,8 @@ final class PropertyMap implements Serializable {
}
synchronized void putAll(Map<String, String> keyValueMap) {
Iterator<Entry<String, String>> it = keyValueMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
@@ -1,20 +1,18 @@
package com.avaje.ebean.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import javax.servlet.ServletContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Helper used to load the PropertyMap.
*/
@@ -81,9 +79,7 @@ final class PropertyMapLoader {
}
// put values in initially without any evaluation
Iterator<Entry<Object, Object>> it = props.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Object, Object> entry = it.next();
for (Map.Entry<Object, Object> entry : props.entrySet()) {
String key = ((String) entry.getKey()).toLowerCase();
String val = ((String) entry.getValue());
if (val != null) {
@@ -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");
@@ -57,6 +57,13 @@ public final class TableName {
this.name = split[len - 1];
}
/**
* Parse a qualifiedTableName that might include a catalog and schema and just return the table name.
*/
public static String parse(String qualifiedTableName) {
return new TableName(qualifiedTableName).getName();
}
public String toString() {
return getQualifiedName();
}
@@ -121,6 +128,18 @@ public final class TableName {
return buffer.toString();
}
/**
* Append a catalog and schema prefix if they exist to the string builder.
*/
public void appendCatalogAndSchema(StringBuilder buffer) {
if (catalog != null) {
buffer.append(catalog).append(".");
}
if (schema != null) {
buffer.append(schema).append(".");
}
}
/**
* Checks if is table name is valid i.e. it has at least a name.
*
@@ -1,5 +1,7 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.TableName;
/**
* Used to support DB specific syntax for DDL generation.
*/
@@ -9,7 +11,7 @@ public class DbDdlSyntax {
private boolean inlinePrimaryKeyConstraint = false;
private boolean addOneToOneUniqueContraint = false;
private boolean addOneToOneUniqueContraint = true;
private int maxConstraintNameLength = 32;
@@ -43,6 +45,17 @@ public class DbDdlSyntax {
return pk;
}
/**
* Return the column definition for an identity column.
*/
public String getIdentityColumnDefn(String columnDefn) {
String identity = getIdentity();
if (identity != null && identity.length() > 0) {
return columnDefn+" "+identity;
}
return columnDefn;
}
/**
* Return the identity clause for DB's that have identities.
*/
@@ -248,26 +261,29 @@ public class DbDdlSyntax {
this.inlinePrimaryKeyConstraint = inlinePrimaryKeyConstraint;
}
/**
* Builds and returns a fully index name.
*/
public String getIndexName(String table, String propName, int ixCount) {
StringBuilder buffer = new StringBuilder();
buffer.append("ix_");
buffer.append(table);
buffer.append("_");
buffer.append(propName);
StringBuilder buffer = new StringBuilder(30);
buffer.append("ix_").append(TableName.parse(table));
buffer.append("_").append(propName);
addSuffix(buffer, ixCount);
return buffer.toString();
}
/**
* Builds and returns a fully qualified foreign key constraint name.
*/
public String getForeignKeyName(String table, String propName, int fkCount) {
StringBuilder buffer = new StringBuilder();
buffer.append("fk_");
buffer.append(table);
buffer.append("_");
buffer.append(propName);
StringBuilder buffer = new StringBuilder(30);
buffer.append("fk_").append(TableName.parse(table));
buffer.append("_").append(propName);
addSuffix(buffer, fkCount);
@@ -276,15 +292,10 @@ public class DbDdlSyntax {
/**
* Adds the suffix.
*
* @param buffer
* the buffer
* @param count
* the count
*/
protected void addSuffix(StringBuilder buffer, int count) {
final String suffixNr = Integer.toString(count);
final int suffixLen = suffixNr.length() + 1;
String suffixNr = Integer.toString(count);
int suffixLen = suffixNr.length() + 1;
if (buffer.length() + suffixLen > maxConstraintNameLength) {
buffer.setLength(maxConstraintNameLength - suffixLen);
@@ -17,22 +17,21 @@ public class LimitOffsetSqlLimiter implements SqlLimiter {
public SqlLimitResponse limit(SqlLimitRequest request) {
StringBuilder sb = new StringBuilder(512);
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(50 + dbSql.length());
sb.append("select ");
if (request.isDistinct()) {
sb.append("distinct ");
}
sb.append(request.getDbSql());
sb.append(dbSql);
int firstRow = request.getFirstRow();
int maxRows = request.getMaxRows();
if (maxRows > 0) {
maxRows = maxRows + 1;
}
if (maxRows > 0 || firstRow > 0) {
sb.append(" ").append(NEW_LINE).append(LIMIT).append(" ").append(maxRows);
sb.append(" ").append(LIMIT).append(" ").append(maxRows);
if (firstRow > 0) {
sb.append(" ").append(OFFSET).append(" ");
sb.append(firstRow);
@@ -26,9 +26,7 @@ public class MsSqlServer2005SqlLimiter implements SqlLimiter {
int lastRow = request.getMaxRows();
if (lastRow > 0) {
// fetch 1 more than we return so that
// we know if more rows are available
lastRow = lastRow + firstRow + 1;
lastRow = lastRow + firstRow;
}
if (firstRow < 1) {
@@ -0,0 +1,73 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.GlobalProperties;
import javax.sql.DataSource;
import java.sql.Types;
/**
* Postgres v8.3 specific platform.
* <p>
* No support for getGeneratedKeys.
* </p>
*/
public class Postgres8Platform extends DatabasePlatform {
public Postgres8Platform() {
super();
this.name = "postgres";
this.selectCountWithAlias = true;
this.blobDbType = Types.LONGVARBINARY;
this.clobDbType = Types.VARCHAR;
this.dbEncrypt = new PostgresDbEncrypt();
this.dbIdentity.setSupportsGetGeneratedKeys(false);
this.dbIdentity.setIdType(IdType.SEQUENCE);
this.dbIdentity.setSupportsSequence(true);
String colAlias = GlobalProperties.get("ebean.columnAliasPrefix", null);
if (colAlias == null) {
// Postgres requires the "as" keyword for column alias
GlobalProperties.put("ebean.columnAliasPrefix", "as c");
}
this.openQuote = "\"";
this.closeQuote = "\"";
// dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
dbTypeMap.put(Types.DECIMAL, new DbType("decimal", 38));
dbTypeMap.put(Types.BINARY, new DbType("bytea", false));
dbTypeMap.put(Types.VARBINARY, new DbType("bytea", false));
dbTypeMap.put(Types.BLOB, new DbType("bytea", false));
dbTypeMap.put(Types.CLOB, new DbType("text"));
dbTypeMap.put(Types.LONGVARBINARY, new DbType("bytea", false));
dbTypeMap.put(Types.LONGVARCHAR, new DbType("text"));
dbDdlSyntax.setDropTableCascade("cascade");
dbDdlSyntax.setDropIfExists("if exists");
}
/**
* Create a Postgres specific sequence IdGenerator.
*/
@Override
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds,
String seqName, int batchSize) {
return new PostgresSequenceIdGenerator(be, ds, seqName, batchSize);
}
@Override
protected String withForUpdate(String sql) {
return sql + " for update";
}
}
@@ -0,0 +1,25 @@
package com.avaje.ebean.config.dbplatform;
public class PostgresDdlSyntax extends DbDdlSyntax {
/**
* Map bigint, integer and smallint into their equivilent serial types.
*/
@Override
public String getIdentityColumnDefn(String columnDefn) {
//smallserial, serial and bigserial
if ("bigint".equalsIgnoreCase(columnDefn)) {
return "bigserial";
}
if ("integer".equalsIgnoreCase(columnDefn)) {
return "serial";
}
if ("smallint".equalsIgnoreCase(columnDefn)) {
return "smallserial";
}
return columnDefn;
}
}
@@ -4,27 +4,37 @@ import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.GlobalProperties;
import javax.sql.DataSource;
import java.sql.Types;
/**
* Postgres v8.3 specific platform.
* Postgres v9 specific platform.
* <p>
* No support for getGeneratedKeys.
* Uses serial types and getGeneratedKeys.
* </p>
*/
public class PostgresPlatform extends DatabasePlatform {
/**
* Unique jdbc type id defined for hstore type.
*/
public static final int TYPE_HSTORE = 4001;
public PostgresPlatform() {
super();
this.name = "postgres";
this.dbDdlSyntax = new PostgresDdlSyntax();
this.selectCountWithAlias = true;
this.blobDbType = Types.LONGVARBINARY;
this.clobDbType = Types.VARCHAR;
this.dbEncrypt = new PostgresDbEncrypt();
this.dbIdentity.setSupportsGetGeneratedKeys(false);
this.dbIdentity.setIdType(IdType.SEQUENCE);
// Use Identity and getGeneratedKeys
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
String colAlias = GlobalProperties.get("ebean.columnAliasPrefix", null);
@@ -36,8 +46,8 @@ public class PostgresPlatform extends DatabasePlatform {
this.openQuote = "\"";
this.closeQuote = "\"";
// dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
dbTypeMap.put(TYPE_HSTORE, new DbType("hstore"));
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
@@ -15,7 +15,7 @@ public class RowNumberSqlLimiter implements SqlLimiter {
*/
private static final String ROW_NUMBER_AS = ") as rn, ";
final String rowNumberWindowAlias;
private final String rowNumberWindowAlias;
/**
* Specify the name of the rowNumberWindowAlias.
@@ -30,16 +30,18 @@ public class RowNumberSqlLimiter implements SqlLimiter {
public SqlLimitResponse limit(SqlLimitRequest request) {
StringBuilder sb = new StringBuilder(500);
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(60 + dbSql.length());
int firstRow = request.getFirstRow();
int lastRow = request.getMaxRows();
if (lastRow > 0) {
lastRow = lastRow + firstRow + 1;
lastRow = lastRow + firstRow;
}
sb.append("select * from (").append(NEW_LINE);
sb.append("select * from ( ");
sb.append("select ");
if (request.isDistinct()) {
@@ -50,9 +52,9 @@ public class RowNumberSqlLimiter implements SqlLimiter {
sb.append(request.getDbOrderBy());
sb.append(ROW_NUMBER_AS);
sb.append(request.getDbSql());
sb.append(dbSql);
sb.append(NEW_LINE).append(") ");
sb.append(" ) ");
sb.append(rowNumberWindowAlias);
sb.append(" where ");
if (firstRow > 0) {
@@ -35,38 +35,40 @@ public class RownumSqlLimiter implements SqlLimiter {
// :MAX_ROW_TO_FETCH )
// where rnum >= :MIN_ROW_TO_FETCH;
StringBuilder sb = new StringBuilder(500);
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(60 + dbSql.length());
int firstRow = request.getFirstRow();
int lastRow = request.getMaxRows();
if (lastRow > 0) {
lastRow = lastRow + firstRow + 1;
lastRow = lastRow + firstRow;
}
sb.append("select * ").append(NEW_LINE).append("from ( ");
sb.append("select * from ( ");
sb.append("select ");
if (useFirstRowsHint && request.getMaxRows() > 0) {
sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows() + 1).append(") */ ");
sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows()).append(") */ ");
}
sb.append("rownum ").append(rnum).append(", a.* ").append(NEW_LINE);
sb.append(" from (");//
sb.append("rownum ").append(rnum).append(", a.* ");
sb.append(" from (");
sb.append(" select ");
if (request.isDistinct()) {
sb.append("distinct ");
}
sb.append(request.getDbSql());
sb.append(dbSql);
sb.append(NEW_LINE).append(" ) a ");
sb.append(NEW_LINE).append(" ) a ");
if (lastRow > 0) {
sb.append(NEW_LINE).append(" where rownum <= ").append(lastRow);
sb.append(" where rownum <= ").append(lastRow);
}
sb.append(NEW_LINE).append(" ) ");
sb.append(" ) ");
if (firstRow > 0) {
sb.append(NEW_LINE).append("where ");
sb.append(" where ");
sb.append(rnum).append(" > ").append(firstRow);
}
@@ -8,15 +8,12 @@ public class SqlAnywhereLimiter implements SqlLimiter {
public SqlLimitResponse limit(SqlLimitRequest request) {
StringBuilder sb = new StringBuilder(500);
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(60 + dbSql.length());
int firstRow = request.getFirstRow();
int maxRows = request.getMaxRows();
if (maxRows > 0) {
// fetch 1 more than we return so that
// we know if more rows are available
maxRows = maxRows + 1;
}
/*
* SELECT TOP xx START AT xx ... FROM ...
@@ -31,7 +28,7 @@ public class SqlAnywhereLimiter implements SqlLimiter {
if (firstRow > 0) {
sb.append("start at ").append(firstRow + 1).append(" ");
}
sb.append(request.getDbSql());
sb.append(dbSql);
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
@@ -5,8 +5,6 @@ import com.avaje.ebean.Query;
/**
* The request object for the query that can have sql limiting applied to it
* (such as a LIMIT OFFSET clause).
*
* @author rob
*/
public interface SqlLimitRequest {
@@ -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.
*/
@@ -1,7 +1,6 @@
package com.avaje.ebeaninternal.server.cache;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import com.avaje.ebean.annotation.CacheTuning;
@@ -89,9 +88,7 @@ public class DefaultCacheHolder {
}
public void clearAll() {
Iterator<ServerCache> it = concMap.values().iterator();
while (it.hasNext()) {
ServerCache serverCache = it.next();
for (ServerCache serverCache : concMap.values()) {
serverCache.clear();
}
}
@@ -111,9 +111,7 @@ public class DefaultServerCache implements ServerCache {
int hc = reset ? removedHitCount.getAndSet(0) : removedHitCount.get();
Iterator<CacheEntry> it = map.values().iterator();
while (it.hasNext()) {
CacheEntry cacheEntry = it.next();
for (CacheEntry cacheEntry : map.values()) {
hc += cacheEntry.getHitCount(reset);
}
@@ -268,10 +266,6 @@ public class DefaultServerCache implements ServerCache {
return map.size();
}
private Iterator<CacheEntry> cacheEntries() {
return map.values().iterator();
}
/**
* The task used to periodically trim the cache.
*/
@@ -296,7 +290,7 @@ public class DefaultServerCache implements ServerCache {
long idleExpire = System.currentTimeMillis() - (maxIdleSecs*1000);
long ttlExpire = System.currentTimeMillis() - (maxSecsToLive*1000);
Iterator<CacheEntry> it = cacheEntries();
Iterator<CacheEntry> it = map.values().iterator();
while (it.hasNext()) {
CacheEntry cacheEntry = it.next();
if (maxIdleSecs > 0 && idleExpire > cacheEntry.getLastAccessTime()) {
@@ -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.
@@ -128,17 +116,17 @@ public abstract class BeanRequest {
return transaction.getInternalConnection();
}
/**
* Return true if SQL should be logged for this transaction.
*/
public boolean isLogSql() {
return transaction.isLogSql();
}
/**
* Return true if SUMMARY information should be logged for this transaction.
*/
public boolean isLogSummary() {
return transaction.isLogSummary();
}
/**
* Return true if SQL should be logged for this transaction.
*/
public boolean isLogSql() {
return transaction.isLogSql();
}
/**
* Return true if SUMMARY information should be logged for this transaction.
*/
public boolean isLogSummary() {
return transaction.isLogSummary();
}
}
@@ -2,7 +2,6 @@ package com.avaje.ebeaninternal.server.core;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.persistence.Embeddable;
@@ -11,6 +10,7 @@ import javax.persistence.Table;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.config.CompoundType;
import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebean.config.ServerConfig;
@@ -65,7 +65,9 @@ public class BootupClasses implements ClassPathSearchMatcher {
public BootupClasses(List<Class<?>> list) {
if (list != null) {
process(list.iterator());
for (Class<?> cls : list) {
isMatch(cls);
}
}
}
@@ -83,13 +85,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
this.serverConfigStartupList.addAll(parent.serverConfigStartupList);
}
private void process(Iterator<Class<?>> it) {
while (it.hasNext()) {
Class<?> cls = it.next();
isMatch(cls);
}
}
/**
* Create a copy of this object so that classes can be added to it.
*/
@@ -297,13 +292,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
return beanListenerList;
}
public void add(Iterator<Class<?>> it) {
while (it.hasNext()) {
Class<?> clazz = it.next();
isMatch(clazz);
}
}
public boolean isMatch(Class<?> cls) {
if (isEmbeddable(cls)) {
@@ -53,16 +53,16 @@ public class DatabasePlatformFactory {
private DatabasePlatform byDatabaseName(String dbName) throws SQLException {
dbName = dbName.toLowerCase();
if (dbName.equals("postgres83")) {
if (dbName.equals("postgres") || dbName.equals("postgres9")) {
return new PostgresPlatform();
}
if (dbName.equals("postgres8") || dbName.equals("postgres83")) {
return new Postgres8Platform();
}
if (dbName.equals("oracle9")) {
return new Oracle9Platform();
}
if (dbName.equals("oracle10")) {
return new Oracle10Platform();
}
if (dbName.equals("oracle")) {
if (dbName.equals("oracle") || dbName.equals("oracle10")) {
return new Oracle10Platform();
}
if (dbName.equals("sqlserver2005")) {
@@ -24,6 +24,7 @@ import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
/**
@@ -108,7 +109,19 @@ public class DefaultBeanLoader {
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(many.getTargetType());
String orderBy = many.getLazyFetchOrderBy();
if (orderBy != null) {
query.orderBy(orderBy);
}
String extraWhere = many.getExtraWhere();
if (extraWhere != null) {
// replace special ${ta} placeholder with the base table alias
// which is always t0 and add the extra where clause
String ew = StringHelper.replaceString(extraWhere, "${ta}", "t0");
query.where().raw(ew);
}
query.setLazyLoadForParents(idList, many);
many.addWhereParentIdIn(query, idList);
@@ -402,6 +415,13 @@ public class DefaultBeanLoader {
if (ebi.isReadOnly()) {
query.setReadOnly(true);
}
if (SpiQuery.Mode.REFRESH_BEAN.equals(mode)) {
// explicitly state to load all properties on REFRESH.
// Lobs default to fetch lazy so this forces lobs to be
// included in a 'refresh' query
query.select("*");
}
Object dbBean = query.findUnique();
if (dbBean == null) {
@@ -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,22 +1,3 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.sql.Connection;
@@ -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();
}
}
@@ -362,9 +360,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
* Log the SQL if the logLevel is appropriate.
*/
public void logSql(String sql) {
if (transaction.isLogSql()) {
transaction.logSql(sql);
}
transaction.logSql(sql);
}
/**
@@ -34,106 +34,101 @@ import com.avaje.ebeaninternal.server.transaction.BeanPersistIdMap;
*/
public final class PersistRequestBean<T> extends PersistRequest implements BeanPersistRequest<T> {
private final BeanManager<T> beanManager;
private final BeanManager<T> beanManager;
private final BeanDescriptor<T> beanDescriptor;
private final BeanDescriptor<T> beanDescriptor;
private final BeanPersistListener<T> beanPersistListener;
private final BeanPersistListener<T> beanPersistListener;
/**
* For per post insert update delete control.
*/
private final BeanPersistController controller;
/**
* For per post insert update delete control.
*/
private final BeanPersistController controller;
/**
/**
* The bean being persisted.
*/
private final T bean;
private final EntityBean entityBean;
/**
* The associated intercept.
*/
private final EntityBeanIntercept intercept;
private final T bean;
/**
* The parent bean for unidirectional save.
*/
private final Object parentBean;
private final EntityBean entityBean;
private final boolean dirty;
/**
* The associated intercept.
*/
private final EntityBeanIntercept intercept;
private ConcurrencyMode concurrencyMode;
/**
* The parent bean for unidirectional save.
*/
private final Object parentBean;
/**
* The unique id used for logging summary.
*/
private Object idValue;
private final boolean dirty;
/**
* Hash value used to handle cascade delete both ways in a relationship.
*/
private Integer beanHash;
private ConcurrencyMode concurrencyMode;
private boolean notifyCache;
/**
* The unique id used for logging summary.
*/
private Object idValue;
private boolean deleteMissingChildren;
private final Set<String> dirtyPropertyNames;
/**
* Hash value used to handle cascade delete both ways in a relationship.
*/
private Integer beanHash;
private boolean notifyCache;
private boolean deleteMissingChildren;
/**
* Flag used to detect when only many properties where updated via a cascade. Used to ensure
* appropriate caches are updated in that case.
*/
private boolean updatedManysOnly;
/**
* Many properties that were cascade saved (and hence might need caches updated later).
*/
private boolean updatedManysOnly;
/**
* Many properties that were cascade saved (and hence might need caches updated later).
*/
private List<BeanPropertyAssocMany<?>> updatedManys;
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr,
SpiTransaction t, PersistExecute persistExecute, PersistRequest.Type type) {
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
PersistExecute persistExecute, PersistRequest.Type type, boolean saveRecurse) {
super(server, t, persistExecute);
this.entityBean = (EntityBean)bean;
super(server, t, persistExecute);
this.entityBean = (EntityBean) bean;
this.intercept = entityBean._ebean_getIntercept();
this.beanManager = mgr;
this.beanDescriptor = mgr.getBeanDescriptor();
this.beanPersistListener = beanDescriptor.getPersistListener();
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.beanManager = mgr;
this.beanDescriptor = mgr.getBeanDescriptor();
this.beanPersistListener = beanDescriptor.getPersistListener();
this.bean = bean;
this.parentBean = parentBean;
this.controller = beanDescriptor.getPersistController();
this.type = type;
if (saveRecurse) {
this.persistCascade = t.isPersistCascade();
}
if (this.type == Type.UPDATE && intercept.isNew() ) {
// 'stateless update' - set bean up for doing update
intercept.setNewBeanForUpdate();
if (this.type == Type.UPDATE) {
if (intercept.isNew()) {
// 'stateless update' - set loaded properties as dirty
intercept.setNewBeanForUpdate();
}
// Mark Mutable scalar properties (like Hstore) as dirty where necessary
beanDescriptor.checkMutableProperties(intercept);
}
// derive the set of property names now as we will pass them to a beanPersistListener later
this.dirtyPropertyNames = (beanPersistListener == null) ? null : intercept.getDirtyPropertyNames();
this.bean = bean;
this.parentBean = parentBean;
this.controller = beanDescriptor.getPersistController();
this.concurrencyMode = beanDescriptor.getConcurrencyMode(intercept);
this.dirty = intercept.isDirty();
}
this.dirty = intercept.isDirty();
}
/**
* Return true if this is an insert request.
*/
/**
* Return true if this is an insert request.
*/
public boolean isInsert() {
return Type.INSERT == type;
}
@Override
@Override
public Set<String> getLoadedProperties() {
return intercept.getLoadedPropertyNames();
}
@@ -148,18 +143,18 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
return intercept.getDirtyValues();
}
public boolean isNotify(TransactionEvent txnEvent) {
this.notifyCache = beanDescriptor.isCacheNotify();
return notifyCache || isNotifyPersistListener();
}
public boolean isNotify(TransactionEvent txnEvent) {
this.notifyCache = beanDescriptor.isCacheNotify();
return notifyCache || isNotifyPersistListener();
}
public boolean isNotifyPersistListener() {
return beanPersistListener != null;
}
public boolean isNotifyPersistListener() {
return beanPersistListener != null;
}
/**
* Notify/Update the local L2 cache after the transaction has successfully committed.
*/
/**
* Notify/Update the local L2 cache after the transaction has successfully committed.
*/
public void notifyCache() {
if (notifyCache) {
switch (type) {
@@ -178,414 +173,416 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
}
public void addToPersistMap(BeanPersistIdMap beanPersistMap) {
public void addToPersistMap(BeanPersistIdMap beanPersistMap) {
beanPersistMap.add(beanDescriptor, type, idValue);
}
beanPersistMap.add(beanDescriptor, type, idValue);
}
public boolean notifyLocalPersistListener() {
if (beanPersistListener == null) {
return false;
public boolean notifyLocalPersistListener() {
if (beanPersistListener == null) {
return false;
} else {
switch (type) {
case INSERT:
return beanPersistListener.inserted(bean);
} else {
switch (type) {
case INSERT:
return beanPersistListener.inserted(bean);
case UPDATE:
return beanPersistListener.updated(bean, dirtyPropertyNames);
case UPDATE:
return beanPersistListener.updated(bean, intercept.getDirtyPropertyNames());
case DELETE:
return beanPersistListener.deleted(bean);
case DELETE:
return beanPersistListener.deleted(bean);
default:
return false;
}
}
}
default:
return false;
}
}
}
public boolean isParent(Object o) {
return o == parentBean;
}
public boolean isParent(Object o) {
return o == parentBean;
}
/**
* Return true if this bean has been already been persisted
* (inserted or updated) in this transaction.
*/
public boolean isRegisteredBean() {
return transaction.isRegisteredBean(bean);
}
public void unRegisterBean() {
transaction.unregisterBean(bean);
}
/**
* The hash used to register the bean with the transaction.
* <p>
* Takes into account the class type and id value.
* </p>
*/
private Integer getBeanHash() {
if (beanHash == null) {
Object id = beanDescriptor.getId(entityBean);
int hc = 31 * bean.getClass().getName().hashCode();
if (id != null) {
hc += id.hashCode();
}
beanHash = Integer.valueOf(hc);
}
return beanHash;
}
public void registerDeleteBean() {
Integer hash = getBeanHash();
transaction.registerDeleteBean(hash);
}
public void unregisterDeleteBean() {
Integer hash = getBeanHash();
transaction.unregisterDeleteBean(hash);
}
public boolean isRegisteredForDeleteBean() {
if (transaction == null){
return false;
} else {
Integer hash = getBeanHash();
return transaction.isRegisteredDeleteBean(hash);
}
}
/**
* Return true if this bean has been already been persisted (inserted or updated) in this
* transaction.
*/
public boolean isRegisteredBean() {
return transaction.isRegisteredBean(bean);
}
public BeanManager<T> getBeanManager() {
return beanManager;
}
public void unRegisterBean() {
transaction.unregisterBean(bean);
}
/**
* Return the BeanDescriptor for the associated bean.
*/
public BeanDescriptor<T> getBeanDescriptor() {
return beanDescriptor;
}
/**
* The hash used to register the bean with the transaction.
* <p>
* Takes into account the class type and id value.
* </p>
*/
private Integer getBeanHash() {
if (beanHash == null) {
Object id = beanDescriptor.getId(entityBean);
int hc = 31 * bean.getClass().getName().hashCode();
if (id != null) {
hc += id.hashCode();
}
beanHash = Integer.valueOf(hc);
}
return beanHash;
}
/**
* Return true if a stateless update should also delete any missing details
* beans.
*/
public boolean isDeleteMissingChildren() {
return deleteMissingChildren;
}
public void registerDeleteBean() {
Integer hash = getBeanHash();
transaction.registerDeleteBean(hash);
}
/**
* Set if deleteMissingChildren occurs on cascade save to OneToMany or ManyToMany.
*/
public void setDeleteMissingChildren(boolean deleteMissingChildren) {
this.deleteMissingChildren = deleteMissingChildren;
}
public void unregisterDeleteBean() {
Integer hash = getBeanHash();
transaction.unregisterDeleteBean(hash);
}
/**
* Used to skip updates if we know the bean is not dirty. This is the case
* for EntityBeans that have not been modified.
*/
public boolean isDirty() {
return dirty;
}
public boolean isRegisteredForDeleteBean() {
if (transaction == null) {
return false;
} else {
Integer hash = getBeanHash();
return transaction.isRegisteredDeleteBean(hash);
}
}
/**
* Return the concurrency mode used for this persist.
*/
public ConcurrencyMode getConcurrencyMode() {
return concurrencyMode;
}
public BeanManager<T> getBeanManager() {
return beanManager;
}
/**
* Returns a description of the request. This is typically the bean class
* name or the base table for MapBeans.
* <p>
* Used to determine common persist requests for queueing and statement
* batching.
* </p>
*/
public String getFullName() {
return beanDescriptor.getFullName();
}
/**
* Return the BeanDescriptor for the associated bean.
*/
public BeanDescriptor<T> getBeanDescriptor() {
return beanDescriptor;
}
/**
* Return the bean associated with this request.
*/
public T getBean() {
return bean;
}
/**
* Return true if a stateless update should also delete any missing details beans.
*/
public boolean isDeleteMissingChildren() {
return deleteMissingChildren;
}
public EntityBean getEntityBean() {
/**
* Set if deleteMissingChildren occurs on cascade save to OneToMany or ManyToMany.
*/
public void setDeleteMissingChildren(boolean deleteMissingChildren) {
this.deleteMissingChildren = deleteMissingChildren;
}
/**
* Prepare the update after potential modifications in a BeanPersistController.
*/
public void postControllerPrepareUpdate() {
if (intercept.isNew() && controller != null) {
// 'stateless update' - set dirty properties modified in controller preUpdate
intercept.setNewBeanForUpdate();
}
}
/**
* Used to skip updates if we know the bean is not dirty. This is the case for EntityBeans that
* have not been modified.
*/
public boolean isDirty() {
return dirty;
}
/**
* Return the concurrency mode used for this persist.
*/
public ConcurrencyMode getConcurrencyMode() {
return concurrencyMode;
}
/**
* Returns a description of the request. This is typically the bean class name or the base table
* for MapBeans.
* <p>
* Used to determine common persist requests for queueing and statement batching.
* </p>
*/
public String getFullName() {
return beanDescriptor.getFullName();
}
/**
* Return the bean associated with this request.
*/
public T getBean() {
return bean;
}
public EntityBean getEntityBean() {
return entityBean;
}
/**
* Return the Id value for the bean.
*/
public Object getBeanId() {
return beanDescriptor.getId(entityBean);
}
* Return the Id value for the bean.
*/
public Object getBeanId() {
return beanDescriptor.getId(entityBean);
}
public BeanDelta createDeltaBean() {
return new BeanDelta(beanDescriptor, getBeanId());
}
public BeanDelta createDeltaBean() {
return new BeanDelta(beanDescriptor, getBeanId());
}
/**
* Return the parent bean for cascading save with unidirectional
* relationship.
*/
public Object getParentBean() {
return parentBean;
}
/**
* Return the parent bean for cascading save with unidirectional relationship.
*/
public Object getParentBean() {
return parentBean;
}
/**
* Return the controller if there is one associated with this type of bean.
* This returns null if there is no controller associated.
*/
public BeanPersistController getBeanController() {
return controller;
}
/**
* Return the controller if there is one associated with this type of bean. This returns null if
* there is no controller associated.
*/
public BeanPersistController getBeanController() {
return controller;
}
/**
* Return the intercept if there is one.
*/
public EntityBeanIntercept getEntityBeanIntercept() {
return intercept;
}
/**
* Return the intercept if there is one.
*/
public EntityBeanIntercept getEntityBeanIntercept() {
return intercept;
}
/**
* Return true if this property is loaded (full bean or included in partial
* bean).
*/
public boolean isLoadedProperty(BeanProperty prop) {
return intercept.isLoadedProperty(prop.getPropertyIndex());
}
/**
* Return true if this property is loaded (full bean or included in partial bean).
*/
public boolean isLoadedProperty(BeanProperty prop) {
return intercept.isLoadedProperty(prop.getPropertyIndex());
}
@Override
public int executeNow() {
switch (type) {
case INSERT:
persistExecute.executeInsertBean(this);
return -1;
@Override
public int executeNow() {
switch (type) {
case INSERT:
persistExecute.executeInsertBean(this);
return -1;
case UPDATE:
persistExecute.executeUpdateBean(this);
return -1;
case UPDATE:
persistExecute.executeUpdateBean(this);
return -1;
case DELETE:
persistExecute.executeDeleteBean(this);
return -1;
case DELETE:
persistExecute.executeDeleteBean(this);
return -1;
default:
throw new RuntimeException("Invalid type " + type);
}
}
default:
throw new RuntimeException("Invalid type " + type);
}
}
@Override
public int executeOrQueue() {
@Override
public int executeOrQueue() {
boolean batch = transaction.isBatchThisRequest();
boolean batch = transaction.isBatchThisRequest();
BatchControl control = transaction.getBatchControl();
if (control != null) {
return control.executeOrQueue(this, batch);
}
if (batch) {
control = persistExecute.createBatchControl(transaction);
return control.executeOrQueue(this, batch);
BatchControl control = transaction.getBatchControl();
if (control != null) {
return control.executeOrQueue(this, batch);
}
if (batch) {
control = persistExecute.createBatchControl(transaction);
return control.executeOrQueue(this, batch);
} else {
return executeNow();
}
}
} else {
return executeNow();
}
}
/**
* Set the generated key back to the bean. Only used for inserts with
* getGeneratedKeys.
*/
public void setGeneratedKey(Object idValue) {
if (idValue != null) {
// remember it for logging summary
this.idValue = beanDescriptor.convertSetId(idValue, entityBean);
}
}
/**
* Set the generated key back to the bean. Only used for inserts with getGeneratedKeys.
*/
public void setGeneratedKey(Object idValue) {
if (idValue != null) {
// remember it for logging summary
this.idValue = beanDescriptor.convertSetId(idValue, entityBean);
}
}
/**
* Set the Id value that was bound. Used for the purposes of logging summary
* information on this request.
*/
public void setBoundId(Object idValue) {
this.idValue = idValue;
}
/**
* Set the Id value that was bound. Used for the purposes of logging summary information on this
* request.
*/
public void setBoundId(Object idValue) {
this.idValue = idValue;
}
/**
* Check for optimistic concurrency exception.
*/
public final void checkRowCount(int rowCount) throws SQLException {
if (rowCount != 1) {
String m = Message.msg("persist.conc2", "" + rowCount);
throw new OptimisticLockException(m, null, bean);
}
}
/**
* Check for optimistic concurrency exception.
*/
public final void checkRowCount(int rowCount) throws SQLException {
if (rowCount != 1) {
String m = Message.msg("persist.conc2", "" + rowCount);
throw new OptimisticLockException(m, null, bean);
}
}
public void postDelete() {
// Delete the bean from the PersistenceContent
transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue);
// Delete from cache early even if transaction fails
beanDescriptor.cacheHandleDelete(idValue, this);
}
/**
* Post processing.
*/
public void postExecute() throws SQLException {
public void postDelete() {
if (controller != null) {
controllerPost();
}
// Delete the bean from the PersistenceContent
transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue);
// Delete from cache early even if transaction fails
beanDescriptor.cacheHandleDelete(idValue, this);
}
if (intercept != null) {
// if bean persisted again then should result in an update
intercept.setLoaded();
}
/**
* Post processing.
*/
public void postExecute() throws SQLException {
addEvent();
if (controller != null) {
controllerPost();
}
if (isLogSummary()) {
logSummary();
}
}
if (intercept != null) {
// if bean persisted again then should result in an update
intercept.setLoaded();
}
private void controllerPost() {
switch (type) {
case INSERT:
controller.postInsert(this);
break;
case UPDATE:
controller.postUpdate(this);
break;
case DELETE:
controller.postDelete(this);
break;
default:
break;
}
}
addEvent();
private void logSummary() {
if (isLogSummary()) {
logSummary();
}
}
String name = beanDescriptor.getName();
switch (type) {
case INSERT:
transaction.logSummary("Inserted [" + name + "] [" + idValue + "]");
break;
case UPDATE:
transaction.logSummary("Updated [" + name + "] [" + idValue + "]");
break;
case DELETE:
transaction.logSummary("Deleted [" + name + "] [" + idValue + "]");
break;
default:
break;
}
}
private void controllerPost() {
switch (type) {
case INSERT:
controller.postInsert(this);
break;
case UPDATE:
controller.postUpdate(this);
break;
case DELETE:
controller.postDelete(this);
break;
default:
break;
}
}
/**
* Add the bean to the TransactionEvent. This will be used by
* TransactionManager to synch Cache, Cluster and text indexes.
*/
private void addEvent() {
private void logSummary() {
TransactionEvent event = transaction.getEvent();
if (event != null) {
event.add(this);
}
}
String name = beanDescriptor.getName();
switch (type) {
case INSERT:
transaction.logSummary("Inserted [" + name + "] [" + idValue + "]");
break;
case UPDATE:
transaction.logSummary("Updated [" + name + "] [" + idValue + "]");
break;
case DELETE:
transaction.logSummary("Deleted [" + name + "] [" + idValue + "]");
break;
default:
break;
}
}
/**
* Determine the concurrency mode depending on fully/partially populated
* bean.
* <p>
* Specifically with version concurrency we want to check that the version
* property was one of the loaded properties.
* </p>
*/
public ConcurrencyMode determineConcurrencyMode() {
// 'partial bean' update/delete...
if (concurrencyMode.equals(ConcurrencyMode.VERSION)) {
// check the version property was loaded
BeanProperty prop = beanDescriptor.getVersionProperty();
if (prop != null && intercept.isLoadedProperty(prop.getPropertyIndex())) {
// OK to use version property
} else {
concurrencyMode = ConcurrencyMode.NONE;
}
}
return concurrencyMode;
}
/**
* Add the bean to the TransactionEvent. This will be used by TransactionManager to synch Cache,
* Cluster and text indexes.
*/
private void addEvent() {
/**
* Return true if the update DML/SQL must be dynamically generated.
* <p>
* This is the case for updates/deletes of partially populated beans.
* </p>
*/
public boolean isDynamicUpdateSql() {
return beanDescriptor.isUpdateChangesOnly() || !intercept.isFullyLoadedBean();
}
TransactionEvent event = transaction.getEvent();
if (event != null) {
event.add(this);
}
}
/**
* Create a GenerateDmlRequest used to generate the DML.
* <p>
* Will used changed properties or loaded properties depending on the
* BeanDescriptor.isUpdateChangesOnly() value.
* </p>
*/
public GenerateDmlRequest createGenerateDmlRequest(boolean emptyStringAsNull) {
return new GenerateDmlRequest(emptyStringAsNull, intercept, beanDescriptor.isUpdateChangesOnly());
}
/**
* Determine the concurrency mode depending on fully/partially populated bean.
* <p>
* Specifically with version concurrency we want to check that the version property was one of the
* loaded properties.
* </p>
*/
public ConcurrencyMode determineConcurrencyMode() {
/**
* Test if the property value has changed and if so include it in the
* update.
*/
public boolean isAddToUpdate(BeanProperty prop) {
return intercept.isDirtyProperty(prop.getPropertyIndex());
}
// 'partial bean' update/delete...
if (concurrencyMode.equals(ConcurrencyMode.VERSION)) {
// check the version property was loaded
BeanProperty prop = beanDescriptor.getVersionProperty();
if (prop != null && intercept.isLoadedProperty(prop.getPropertyIndex())) {
// OK to use version property
} else {
concurrencyMode = ConcurrencyMode.NONE;
}
}
return concurrencyMode;
}
/**
* Return true if the update DML/SQL must be dynamically generated.
* <p>
* This is the case for updates/deletes of partially populated beans.
* </p>
*/
public boolean isDynamicUpdateSql() {
return beanDescriptor.isUpdateChangesOnly() || !intercept.isFullyLoadedBean();
}
/**
* Create a GenerateDmlRequest used to generate the DML.
* <p>
* Will used changed properties or loaded properties depending on the
* BeanDescriptor.isUpdateChangesOnly() value.
* </p>
*/
public GenerateDmlRequest createGenerateDmlRequest(boolean emptyStringAsNull) {
return new GenerateDmlRequest(emptyStringAsNull, intercept, beanDescriptor.isUpdateChangesOnly());
}
/**
* Test if the property value has changed and if so include it in the update.
*/
public boolean isAddToUpdate(BeanProperty prop) {
return intercept.isDirtyProperty(prop.getPropertyIndex());
}
public List<DerivedRelationshipData> getDerivedRelationships() {
return transaction.getDerivedRelationship(bean);
}
public void postInsert() {
// mark all properties as loaded after an insert to support immediate update
// mark all properties as loaded after an insert to support immediate update
int len = intercept.getPropertyLength();
for (int i = 0; i < len; i++) {
intercept.setLoadedProperty(i);
intercept.setLoadedProperty(i);
}
}
public boolean isReference() {
return beanDescriptor.isReference(intercept);
}
/**
* This many property has been cascade saved. Keep note of this and update the 'many property'
* cache on post commit.
*/
public void addUpdatedManyProperty(BeanPropertyAssocMany<?> updatedAssocMany) {
//if (notifyCache) {
if (updatedManys == null) {
updatedManys = new ArrayList<BeanPropertyAssocMany<?>>(5);
}
updatedManys.add(updatedAssocMany);
//}
// if (notifyCache) {
if (updatedManys == null) {
updatedManys = new ArrayList<BeanPropertyAssocMany<?>>(5);
}
updatedManys.add(updatedAssocMany);
// }
}
/**
@@ -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.
*/
@@ -0,0 +1,87 @@
package com.avaje.ebeaninternal.server.ddl;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
/**
* A visitor that creates indexes for columns annotated with ColumnIndex
*
* @author rvbiljouw
* @see com.avaje.ebean.annotation.Index
*/
public class CreateIndexVisitor extends AbstractBeanVisitor {
private final IndexPropertyVisitor iv;
public CreateIndexVisitor(DdlGenContext ctx) {
this.iv = new IndexPropertyVisitor(ctx);
}
@Override
public void visitBegin() {
}
@Override
public boolean visitBean(BeanDescriptor<?> descriptor) {
return descriptor.isInheritanceRoot();
}
@Override
public PropertyVisitor visitProperty(BeanProperty p) {
return iv;
}
@Override
public void visitBeanEnd(BeanDescriptor<?> descriptor) {
visitInheritanceProperties(descriptor, iv);
}
@Override
public void visitEnd() {
}
private static final class IndexPropertyVisitor extends BaseTablePropertyVisitor {
private final DdlGenContext ctx;
public IndexPropertyVisitor(DdlGenContext ctx) {
this.ctx = ctx;
}
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
}
@Override
public void visitOneImported(BeanPropertyAssocOne<?> p) {
}
@Override
public void visitScalar(BeanProperty p) {
String baseTable = p.getBeanDescriptor().getBaseTable();
if (p.isIndexed()) {
String indexName = p.getIndexName();
if (indexName.length() == 0) {
indexName = ctx.getDdlSyntax().getIndexName(baseTable, p.getDbColumn(), ctx.incrementIxCount());
}
ctx.write("create index ")
.write(indexName)
.write(" on ")
.write(baseTable)
.write("(")
.write(p.getDbColumn())
.write(");")
.writeNewLine();
}
}
@Override
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
}
}
}
@@ -2,6 +2,10 @@ package com.avaje.ebeaninternal.server.ddl;
import java.sql.Types;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.config.TableName;
import com.avaje.ebean.config.dbplatform.DbDdlSyntax;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
@@ -12,8 +16,6 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin;
import com.avaje.ebeaninternal.server.deploy.TableJoinColumn;
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Used as part of CreateTableVisitor to generated the create table DDL script.
@@ -28,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;
@@ -66,12 +70,13 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
this.embedded = embedded;
visitScalar(p);
}
private StringBuilder createUniqueConstraintBuffer(String table, String column) {
String uqConstraintName = "uq_" + table + "_" + column;
String uqConstraintName = "uq_"+TableName.parse(table)+"_"+column;
if (uqConstraintName.length() > ddl.getMaxConstraintNameLength()) {
uqConstraintName = uqConstraintName.substring(0, ddl.getMaxConstraintNameLength());
@@ -153,11 +158,12 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
parent.writeColumnName(p.getDbColumn(), p);
String columnDefn = ctx.getColumnDefn(p);
ctx.write(columnDefn);
boolean identity = isIdentity(p);
if (identity) {
writeIdentity();
ctx.write(ddl.getIdentityColumnDefn(columnDefn));
} else {
ctx.write(columnDefn);
}
if (p.isId() && ddl.isInlinePrimaryKeyConstraint()){
@@ -181,18 +187,16 @@ 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();
}
protected void writeIdentity() {
String identity = ddl.getIdentity();
if (identity != null && identity.length() > 0) {
ctx.write(" ").write(identity);
}
}
protected void writeIdentitySuffix() {
String identity = ddl.getIdentitySuffix();
@@ -4,6 +4,10 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.config.TableName;
import com.avaje.ebean.config.dbplatform.DbDdlSyntax;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
@@ -13,8 +17,6 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.parse.SqlReservedWords;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Used to generated the create table DDL script.
@@ -23,23 +25,22 @@ public class CreateTableVisitor extends AbstractBeanVisitor {
private static final Logger logger = LoggerFactory.getLogger(CreateTableVisitor.class);
final DdlGenContext ctx;
private final DdlGenContext ctx;
final PropertyVisitor pv;
private final PropertyVisitor pv;
final DbDdlSyntax ddl;
private final DbDdlSyntax ddl;
final int columnNameWidth;
private final int columnNameWidth;
// avoid writing columns twice, e.g. when used in associations with insertable=false and updateable=false
private final Set<String> wroteColumns = new HashSet<String>();
private ArrayList<String> checkConstraints = new ArrayList<String>();
private ArrayList<String> checkConstraints = new ArrayList<String>();
private ArrayList<String> uniqueConstraints = new ArrayList<String>();
private ArrayList<String> uniqueConstraints = new ArrayList<String>();
private String table;
private String schema;
private String table;
public Set<String> getWroteColumns() {
return wroteColumns;
@@ -53,235 +54,211 @@ public class CreateTableVisitor extends AbstractBeanVisitor {
this.pv = new CreateTableColumnVisitor(this, ctx);
}
public boolean isDbColumnWritten(String dbColumn) {
// Columns are not case sensitive - user lower case
// as e.g. @JoinColumn(s) may use a different case
return wroteColumns.contains(dbColumn.toLowerCase());
}
public boolean isDbColumnWritten(String dbColumn) {
// Columns are not case sensitive - user lower case
// as e.g. @JoinColumn(s) may use a different case
return wroteColumns.contains(dbColumn.toLowerCase());
}
public void addDbColumnWritten(String dbColumn){
// Column names are case insensitive
wroteColumns.add(dbColumn.toLowerCase());
}
public void addDbColumnWritten(String dbColumn) {
// Column names are case insensitive
wroteColumns.add(dbColumn.toLowerCase());
}
/**
* Write the table name including a check for SQL reserved words.
*/
protected void writeTableName(BeanDescriptor<?> descriptor) {
String tableName = descriptor.getBaseTable();
int dotPos = tableName.lastIndexOf('.');
if (dotPos > -1){
schema = tableName.substring(0, dotPos);
table = tableName.substring(dotPos+1);
} else {
table = tableName;
}
if (SqlReservedWords.isKeyword(table)) {
logger.warn("Table name ["+table+"] is a suspected SQL reserved word for bean "+descriptor.getFullName());
}
ctx.write(tableName);
}
protected String getTable() {
return table;
}
protected String getSchema() {
return schema;
}
/**
* Write the column name including a check for the SQL reserved words.
* @param p
*/
protected void writeColumnName(String columnName, BeanProperty p) {
addDbColumnWritten(columnName);
if (SqlReservedWords.isKeyword(columnName)) {
String propName = p == null ? "(Unknown)" : p.getFullBeanName();
logger.warn("Column name ["+columnName+"] is a suspected SQL reserved word for property "+propName);
}
ctx.write(" ").write(columnName, columnNameWidth).write(" ");
}
/**
* Build a check constraint for the property if required.
* <p>
* Typically check constraint based on Enum mapping values.
* </p>
*/
protected void addCheckConstraint(BeanProperty p, String prefix, String constraintExpression) {
if (p != null && constraintExpression != null){
// build constraint clause
String s = "constraint "+getConstraintName(prefix, p)+" "+constraintExpression;
// add to list as we render all check constraints just prior to primary key
checkConstraints.add(s);
}
}
protected String getConstraintName(String prefix, BeanProperty p) {
return prefix + table + "_" + p.getDbColumn();
}
protected void addUniqueConstraint(String constraintExpression) {
uniqueConstraints.add(constraintExpression);
}
/**
* Write the table name including a check for SQL reserved words.
*/
protected void writeTableName(BeanDescriptor<?> descriptor) {
protected void addCheckConstraint(String constraintExpression) {
checkConstraints.add(constraintExpression);
}
protected void addCheckConstraint(BeanProperty p) {
addCheckConstraint(p,"ck_", p.getDbConstraintExpression());
}
public boolean visitBean(BeanDescriptor<?> descriptor) {
wroteColumns.clear();
if (!descriptor.isInheritanceRoot()){
return false;
}
ctx.write("create table ");
writeTableName(descriptor);
ctx.write(" (").writeNewLine();
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null && inheritInfo.isRoot()){
String discColumn = inheritInfo.getDiscriminatorColumn();
int discType = inheritInfo.getDiscriminatorType();
int discLength = inheritInfo.getDiscriminatorLength();
DbType dbType = ctx.getDbTypeMap().get(discType);
String discDbType = dbType.renderType(discLength, 0);
writeColumnName(discColumn, null);
ctx.write(discDbType);
ctx.write(" not null,");
ctx.writeNewLine();
}
return true;
}
public void visitBeanEnd(BeanDescriptor<?> descriptor) {
// parse to remove any catalog or schema prefix on the table
table = TableName.parse(descriptor.getBaseTable());
ctx.write(descriptor.getBaseTable());
}
visitInheritanceProperties(descriptor, pv);
if (checkConstraints.size() > 0){
for (String checkConstraint : checkConstraints) {
ctx.write(" ").write(checkConstraint).write(",").writeNewLine();
}
checkConstraints = new ArrayList<String>();
}
if (uniqueConstraints.size() > 0){
for (String constraint : uniqueConstraints) {
ctx.write(" ").write(constraint).write(",").writeNewLine();
}
uniqueConstraints = new ArrayList<String>();
}
CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints();
if (compoundUniqueConstraints != null){
String table = descriptor.getBaseTable();
for (int i = 0; i < compoundUniqueConstraints.length; i++) {
String constraint = createUniqueConstraint(table, i, compoundUniqueConstraints[i]);
ctx.write(" ").write(constraint).write(",").writeNewLine();
}
}
BeanProperty idProp = descriptor.getIdProperty();
/**
* Write the column name including a check for the SQL reserved words.
*/
protected void writeColumnName(String columnName, BeanProperty p) {
if (idProp == null){
// No comma + new line
ctx.removeLast().removeLast();
} else if (ddl.isInlinePrimaryKeyConstraint()) {
// The Primary Key constraint was inlined with the column
// ... No comma + new line
ctx.removeLast().removeLast();
} else {
// Add the primay key constraint
String pkName = ddl.getPrimaryKeyName(table);
ctx.write(" constraint ").write(pkName).write(" primary key (");
VisitorUtil.visit(idProp, new AbstractPropertyVisitor() {
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
ctx.write(p.getDbColumn()).write(", ");
}
@Override
public void visitScalar(BeanProperty p) {
ctx.write(p.getDbColumn()).write(", ");
}
@Override
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
ctx.write(p.getDbColumn()).write(", ");
}
});
// remove the last comma, end of PK
ctx.removeLast().write(")");
}
// end of table
ctx.write(")").writeNewLine();
ctx.write(";").writeNewLine().writeNewLine();
ctx.flush();
}
private String createUniqueConstraint(String table, int idx, CompoundUniqueContraint uc) {
String uqConstraintName = "uq_"+table+"_"+(idx+1) ;
StringBuilder sb = new StringBuilder();
sb.append("constraint ")
.append(uqConstraintName)
.append(" unique (");
String[] columns = uc.getColumns();
for (int i = 0; i < columns.length; i++) {
if (i > 0){
sb.append(",");
}
sb.append(columns[i]);
}
sb.append(")");
return sb.toString();
addDbColumnWritten(columnName);
if (SqlReservedWords.isKeyword(columnName)) {
String propName = p == null ? "(Unknown)" : p.getFullBeanName();
logger.warn("Column name [" + columnName + "] is a suspected SQL reserved word for property " + propName);
}
ctx.write(" ").write(columnName, columnNameWidth).write(" ");
}
public void visitBeanDescriptorEnd() {
ctx.write(");").writeNewLine().writeNewLine();
}
/**
* Build a check constraint for the property if required.
* <p>
* Typically check constraint based on Enum mapping values.
* </p>
*/
protected void addCheckConstraint(BeanProperty p, String prefix, String constraintExpression) {
if (p != null && constraintExpression != null) {
public PropertyVisitor visitProperty(BeanProperty p) {
return pv;
}
// build constraint clause
String s = "constraint " + getConstraintName(prefix, p) + " " + constraintExpression;
public void visitBegin() {
}
// add to list as we render all check constraints just prior to primary key
checkConstraints.add(s);
}
}
public void visitEnd() {
ctx.addIntersectionCreateTables();
ctx.flush();
}
protected String getConstraintName(String prefix, BeanProperty p) {
return prefix + table + "_" + p.getDbColumn();
}
protected void addUniqueConstraint(String constraintExpression) {
uniqueConstraints.add(constraintExpression);
}
protected void addCheckConstraint(String constraintExpression) {
checkConstraints.add(constraintExpression);
}
protected void addCheckConstraint(BeanProperty p) {
addCheckConstraint(p, "ck_", p.getDbConstraintExpression());
}
public boolean visitBean(BeanDescriptor<?> descriptor) {
wroteColumns.clear();
if (!descriptor.isInheritanceRoot()) {
return false;
}
ctx.write("create table ");
writeTableName(descriptor);
ctx.write(" (").writeNewLine();
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null && inheritInfo.isRoot()) {
String discColumn = inheritInfo.getDiscriminatorColumn();
int discType = inheritInfo.getDiscriminatorType();
int discLength = inheritInfo.getDiscriminatorLength();
DbType dbType = ctx.getDbTypeMap().get(discType);
String discDbType = dbType.renderType(discLength, 0);
writeColumnName(discColumn, null);
ctx.write(discDbType);
ctx.write(" not null,");
ctx.writeNewLine();
}
return true;
}
public void visitBeanEnd(BeanDescriptor<?> descriptor) {
visitInheritanceProperties(descriptor, pv);
if (checkConstraints.size() > 0) {
for (String checkConstraint : checkConstraints) {
ctx.write(" ").write(checkConstraint).write(",").writeNewLine();
}
checkConstraints = new ArrayList<String>();
}
if (uniqueConstraints.size() > 0) {
for (String constraint : uniqueConstraints) {
ctx.write(" ").write(constraint).write(",").writeNewLine();
}
uniqueConstraints = new ArrayList<String>();
}
CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints();
if (compoundUniqueConstraints != null) {
String table = descriptor.getBaseTable();
for (int i = 0; i < compoundUniqueConstraints.length; i++) {
String constraint = createUniqueConstraint(table, i, compoundUniqueConstraints[i]);
ctx.write(" ").write(constraint).write(",").writeNewLine();
}
}
BeanProperty idProp = descriptor.getIdProperty();
if (idProp == null) {
// No comma + new line
ctx.removeLast().removeLast();
} else if (ddl.isInlinePrimaryKeyConstraint()) {
// The Primary Key constraint was inlined with the column
// ... No comma + new line
ctx.removeLast().removeLast();
} else {
// Add the primay key constraint
String pkName = ddl.getPrimaryKeyName(table);
ctx.write(" constraint ").write(pkName).write(" primary key (");
VisitorUtil.visit(idProp, new AbstractPropertyVisitor() {
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
ctx.write(p.getDbColumn()).write(", ");
}
@Override
public void visitScalar(BeanProperty p) {
ctx.write(p.getDbColumn()).write(", ");
}
@Override
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
ctx.write(p.getDbColumn()).write(", ");
}
});
// remove the last comma, end of PK
ctx.removeLast().write(")");
}
// end of table
ctx.write(")").writeNewLine();
ctx.write(";").writeNewLine().writeNewLine();
ctx.flush();
}
private String createUniqueConstraint(String table, int idx, CompoundUniqueContraint uc) {
StringBuilder sb = new StringBuilder(50);
sb.append("constraint ")
.append("uq_").append(TableName.parse(table)).append("_").append(idx + 1)
.append(" unique (");
String[] columns = uc.getColumns();
for (int i = 0; i < columns.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append(columns[i]);
}
sb.append(")");
return sb.toString();
}
public void visitBeanDescriptorEnd() {
ctx.write(");").writeNewLine().writeNewLine();
}
public PropertyVisitor visitProperty(BeanProperty p) {
return pv;
}
public void visitBegin() {
}
public void visitEnd() {
ctx.addIntersectionCreateTables();
ctx.flush();
}
}
@@ -98,17 +98,17 @@ public class DdlGenerator implements SpiEbeanPlugin {
}
}
protected void writeDrop(String dropFile) {
protected void writeDrop(String dropFile) {
try {
String c = generateDropDdl();
writeFile(dropFile, c);
try {
String c = generateDropDdl();
writeFile(dropFile, c);
} catch (IOException e) {
String msg = "Error generating Drop DDL";
throw new PersistenceException(msg, e);
}
}
} catch (IOException e) {
String msg = "Error generating Drop DDL";
throw new PersistenceException(msg, e);
}
}
protected void writeCreate(String createFile) {
@@ -149,6 +149,9 @@ public class DdlGenerator implements SpiEbeanPlugin {
AddForeignKeysVisitor fkeys = new AddForeignKeysVisitor(ctx);
VisitorUtil.visit(server, fkeys);
CreateIndexVisitor indexes = new CreateIndexVisitor(ctx);
VisitorUtil.visit(server, indexes);
ctx.flush();
createContent = ctx.getContent();
return createContent;
@@ -3,9 +3,9 @@ package com.avaje.ebeaninternal.server.deploy;
import java.lang.reflect.Modifier;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -220,6 +220,12 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
*/
private final BeanProperty[] propertiesLocal;
/**
* Scalar mutable properties (need to dirty check on update).
*/
private final BeanProperty[] propertiesMutable;
private final BeanPropertyAssocOne<?> unidirectional;
/**
@@ -391,6 +397,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
this.propertiesBaseCompound = listHelper.getBaseCompound();
this.propertiesEmbedded = listHelper.getEmbedded();
this.propertiesLocal = listHelper.getLocal();
this.propertiesMutable = listHelper.getMutable();
this.unidirectional = listHelper.getUnidirectional();
this.propertiesOne = listHelper.getOnes();
this.propertiesOneExported = listHelper.getOneExported();
@@ -596,9 +603,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
if (isEmbedded()) {
// initialise all the properties
Iterator<BeanProperty> it = propertiesAll();
while (it.hasNext()) {
BeanProperty prop = it.next();
for (BeanProperty prop : propertiesAll()) {
prop.initialise();
}
} else {
@@ -616,9 +621,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
if (!isEmbedded()) {
// initialise all the non-id properties
Iterator<BeanProperty> it = propertiesAll();
while (it.hasNext()) {
BeanProperty prop = it.next();
for (BeanProperty prop : propertiesAll()) {
if (!prop.isId()) {
prop.initialise();
}
@@ -1021,9 +1024,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
* Reset the statistics on all the query plans.
*/
public void clearQueryStatistics() {
Iterator<CQueryPlan> it = queryPlanCache.values().iterator();
while (it.hasNext()) {
CQueryPlan queryPlan = (CQueryPlan) it.next();
for (CQueryPlan queryPlan : queryPlanCache.values()) {
queryPlan.resetStatistics();
}
}
@@ -1039,13 +1040,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
}
}
/**
* Return the query plans for this BeanDescriptor.
*/
public Iterator<CQueryPlan> queryPlans() {
return queryPlanCache.values().iterator();
}
public CQueryPlan getQueryPlan(HashQueryPlan key) {
return queryPlanCache.get(key);
}
@@ -1216,8 +1210,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
convertSetId(id, eb);
EntityBeanIntercept ebi = eb._ebean_getIntercept();
ebi.setBeanLoaderByServerName(ebeanServer.getName());
ebi.setBeanLoader(ebeanServer);
// Note: not creating proxies for many's...
ebi.setReference(idPropertyIndex);
@@ -1524,7 +1517,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());
}
}
@@ -1847,10 +1840,10 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
}
/**
* Return an Iterator of all BeanProperty. This includes transient properties.
* Return a collection of all BeanProperty. This includes transient properties.
*/
public Iterator<BeanProperty> propertiesAll() {
return propMap.values().iterator();
public Collection<BeanProperty> propertiesAll() {
return propMap.values();
}
/**
@@ -1879,21 +1872,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) {
@@ -1918,7 +1921,27 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return false;
}
/**
* Check for mutable scalar types and mark as dirty if necessary.
*/
public void checkMutableProperties(EntityBeanIntercept ebi) {
for (int i = 0; i < propertiesMutable.length; i++) {
BeanProperty beanProperty = propertiesMutable[i];
if (ebi.isDirtyProperty(beanProperty.getPropertyIndex())) {
// already marked as dirty
} else if (ebi.isLoadedProperty(beanProperty.getPropertyIndex())) {
Object value = beanProperty.getValue(ebi.getOwner());
if (value == null || beanProperty.isDirtyValue(value)) {
// mutable scalar value which is considered dirty so mark
// it as such so that it is included in an update
ebi.markPropertyAsChanged(beanProperty.getPropertyIndex());
}
}
}
}
public ConcurrencyMode getConcurrencyMode(EntityBeanIntercept ebi) {
if (!hasVersionProperty(ebi)) {
return ConcurrencyMode.NONE;
} else {
@@ -429,18 +429,21 @@ public final class BeanDescriptorCacheHelp<T> {
EntityBean bean = desc.createEntityBean();
desc.convertSetId(id, bean);
EntityBeanIntercept ebi = bean._ebean_getIntercept();
ebi.setBeanLoader(desc.getEbeanServer());
if (Boolean.TRUE.equals(readOnly)) {
bean._ebean_getIntercept().setReadOnly(true);
ebi.setReadOnly(true);
}
beanLoadData(bean, data);
if (beanLog.isTraceEnabled()) {
beanLog.trace(" GET {}({}) - hit", cacheName, id);
}
return (T) bean;
}
/**
* Remove a bean from the cache given its Id.
*/
@@ -1,17 +1,20 @@
package com.avaje.ebeaninternal.server.deploy;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.MappedSuperclass;
import javax.persistence.PersistenceException;
import javax.persistence.Transient;
import javax.sql.DataSource;
import org.slf4j.Logger;
@@ -61,9 +64,7 @@ import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
import com.avaje.ebeaninternal.server.lib.util.Dnode;
import com.avaje.ebeaninternal.server.reflect.BeanReflect;
import com.avaje.ebeaninternal.server.reflect.BeanReflectFactory;
import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter;
import com.avaje.ebeaninternal.server.reflect.BeanReflectProperties;
import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter;
import com.avaje.ebeaninternal.server.reflect.EnhanceBeanReflectFactory;
import com.avaje.ebeaninternal.server.type.TypeManager;
@@ -510,9 +511,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void readEntityBeanTable() {
Iterator<DeployBeanInfo<?>> it = deplyInfoMap.values().iterator();
while (it.hasNext()) {
DeployBeanInfo<?> info = it.next();
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
BeanTable beanTable = createBeanTable(info);
beanTableMap.put(beanTable.getBeanType(), beanTable);
}
@@ -526,18 +525,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void readEntityDeploymentAssociations() {
Iterator<DeployBeanInfo<?>> it = deplyInfoMap.values().iterator();
while (it.hasNext()) {
DeployBeanInfo<?> info = it.next();
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
readDeployAssociations(info);
}
}
private void readInheritedIdGenerators() {
Iterator<DeployBeanInfo<?>> it = deplyInfoMap.values().iterator();
while (it.hasNext()) {
DeployBeanInfo<?> info = it.next();
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
DeployBeanDescriptor<?> descriptor = info.getDescriptor();
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null && !inheritInfo.isRoot()) {
@@ -613,10 +608,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
* @param info the new inheritance info
*/
private void setInheritanceInfo(DeployBeanInfo<?> info) {
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
if (!oneProp.isTransient()) {
DeployBeanInfo<?> assoc = deplyInfoMap.get(oneProp.getTargetType());
if (assoc != null){
oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
}
@@ -626,7 +621,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
if (!manyProp.isTransient()) {
DeployBeanInfo<?> assoc = deplyInfoMap.get(manyProp.getTargetType());
if (assoc != null){
manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
}
@@ -755,9 +749,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
String searchName = name.substring(0, p).toLowerCase();
// search for this in the possible matches
Iterator<String> it = matchSet.iterator();
while (it.hasNext()) {
String possibleMappedBy = it.next();
for (String possibleMappedBy : matchSet) {
String possibleLower = possibleMappedBy.toLowerCase();
if (possibleLower.indexOf(searchName) > -1) {
// we have a match..
@@ -877,7 +869,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 +1011,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
// set bean controller, finder and listener
setBeanControllerFinderListener(desc);
deplyInherit.process(desc);
desc.checkInheritanceMapping();
createProperties.createProperties(desc);
@@ -1153,12 +1146,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void setScalarType(DeployBeanDescriptor<?> deployDesc) {
Iterator<DeployBeanProperty> it = deployDesc.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
if (prop instanceof DeployBeanPropertyAssoc<?>) {
} else {
for (DeployBeanProperty prop : deployDesc.propertiesAll()) {
if (prop instanceof DeployBeanPropertyAssoc<?> == false) {
deployUtil.setScalarType(prop);
}
}
@@ -1312,30 +1301,39 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
desc.setBeanReflect(beanReflect);
desc.setProperties(reflectProps.getProperties());
Iterator<DeployBeanProperty> it = desc.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
for (DeployBeanProperty prop : desc.propertiesAll()) {
String propName = prop.getName();
Integer pos = reflectProps.getPropertyIndex(propName);
if (pos == null) {
throw new IllegalStateException("Property "+propName+" not found in "+reflectProps);
}
BeanReflectGetter getter = beanReflect.getGetter(propName, pos.intValue());
BeanReflectSetter setter = beanReflect.getSetter(propName, pos.intValue());
prop.setGetter(getter);
prop.setSetter(setter);
prop.setPropertyIndex(pos.intValue());
if (getter == null) {
String m = "BeanReflectGetter for " + prop.getFullBeanName() + " was not found?";
throw new RuntimeException(m);
if (isPersistentField(prop)) {
throw new IllegalStateException("Property "+propName+" not found in "+reflectProps);
}
} else {
int propertyIndex = pos.intValue();
prop.setPropertyIndex(propertyIndex);
prop.setGetter(beanReflect.getGetter(propName, propertyIndex));
prop.setSetter(beanReflect.getSetter(propName, propertyIndex));
}
}
}
/**
* Return true if this is a persistent field (not transient or static).
*/
private boolean isPersistentField(DeployBeanProperty prop) {
Field field = prop.getField();
int modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) {
return false;
}
if (field.isAnnotationPresent(Transient.class)) {
return false;
}
return true;
}
/**
* DevNote: It is assumed that Embedded can contain version properties. It is
* also assumed that Embedded beans do NOT themselves contain Embedded beans
@@ -1407,6 +1405,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
* enhanced or all dynamically subclassed).
*/
private void checkInheritedClasses(Class<?> beanClass) {
Class<?> superclass = beanClass.getSuperclass();
if (Object.class.equals(superclass)) {
// we got to the top of the inheritance
@@ -1417,14 +1416,40 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
return;
}
if (!EntityBean.class.isAssignableFrom(superclass)) {
if (isMappedSuperWithNoProperties(superclass)) {
// ok to stop and treat just the same as Object.class
return;
}
throw new IllegalStateException("Super type "+superclass+" is not enhanced?");
}
// recursively continue up the inheritance hierarchy
checkInheritedClasses(superclass);
}
/**
* Return true if this is a MappedSuperclass bean with no persistent properties.
* If so it is ok for it not to be enhanced.
*/
private boolean isMappedSuperWithNoProperties(Class<?> beanClass) {
MappedSuperclass annotation = beanClass.getAnnotation(MappedSuperclass.class);
if (annotation == null) {
return false;
}
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())) {
// ignore this field
} else if (field.isAnnotationPresent(Transient.class)) {
// ignore this field
} else {
return false;
}
}
return true;
}
/**
* Comparator to sort the BeanDescriptors by name.
*/
@@ -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");
}
}
@@ -78,7 +78,7 @@ public class BeanProperty implements ElPropertyValue {
* Flag set if this maps to the inheritance discriminator column
*/
final boolean discriminator;
/**
* Flag to mark the property as embedded. This could be on
* BeanPropertyAssocOne rather than here. Put it here for checking Id type
@@ -92,7 +92,7 @@ public class BeanProperty implements ElPropertyValue {
final boolean version;
final boolean naturalKey;
/**
* Set if this property is nullable.
*/
@@ -136,7 +136,7 @@ public class BeanProperty implements ElPropertyValue {
* True if the property is a Clob, Blob LongVarchar or LongVarbinary.
*/
final boolean lob;
final boolean fetchEager;
final boolean isTransient;
@@ -147,7 +147,7 @@ public class BeanProperty implements ElPropertyValue {
final String name;
final int propertyIndex;
/**
* The reflected field.
*/
@@ -262,6 +262,10 @@ public class BeanProperty implements ElPropertyValue {
final boolean jsonDeserialize;
final boolean indexed;
final String indexName;
public BeanProperty(DeployBeanProperty deploy) {
this(null, null, deploy);
}
@@ -271,7 +275,10 @@ public class BeanProperty implements ElPropertyValue {
this.descriptor = descriptor;
this.name = InternString.intern(deploy.getName());
this.propertyIndex = deploy.getPropertyIndex();
this.indexed = deploy.isIndexed();
this.indexName = deploy.getIndexName();
this.unidirectionalShadow = deploy.isUndirectionalShadow();
this.discriminator = deploy.isDiscriminator();
this.localEncrypted = deploy.isLocalEncrypted();
@@ -326,7 +333,7 @@ public class BeanProperty implements ElPropertyValue {
this.lob = isLobType(dbType);
this.propertyType = deploy.getPropertyType();
this.field = deploy.getField();
EntityType et = descriptor == null ? null : descriptor.getEntityType();
this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), false, null);
this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), dbEncrypted, dbColumn);
@@ -362,6 +369,9 @@ public class BeanProperty implements ElPropertyValue {
this.name = InternString.intern(source.getName());
this.propertyIndex = source.propertyIndex;
this.indexed = source.isIndexed();
this.indexName = source.getIndexName();
this.dbColumn = InternString.intern(override.getDbColumn());
this.sqlFormulaJoin = InternString.intern(override.getSqlFormulaJoin());
this.sqlFormulaSelect = InternString.intern(override.getSqlFormulaSelect());
@@ -410,7 +420,7 @@ public class BeanProperty implements ElPropertyValue {
this.lob = isLobType(dbType);
this.propertyType = source.getPropertyType();
this.field = source.getField();
this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn);
this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn);
@@ -446,7 +456,7 @@ public class BeanProperty implements ElPropertyValue {
}
public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain,
boolean propertyDeploy) {
boolean propertyDeploy) {
throw new PersistenceException("Not valid on scalar bean property " + getFullBeanName());
}
@@ -475,7 +485,17 @@ public class BeanProperty implements ElPropertyValue {
* Return true if this property maps to the inheritance discriminator column.
*/
public boolean isDiscriminator() {
return discriminator;
return discriminator;
}
/**
* Return true if the underlying type is mutable.
*/
public boolean isMutableScalarType() {
if (scalarType == null) {
return false;
}
return scalarType.isMutable();
}
public void copyProperty(EntityBean sourceBean, EntityBean destBean) {
@@ -715,14 +735,14 @@ public class BeanProperty implements ElPropertyValue {
private static Object[] NO_ARGS = new Object[0];
public Object getCacheDataValue(EntityBean bean){
return getValue(bean);
public Object getCacheDataValue(EntityBean bean) {
return getValue(bean);
}
public void setCacheDataValue(EntityBean bean, Object cacheData){
setValue(bean, cacheData);
public void setCacheDataValue(EntityBean bean, Object cacheData) {
setValue(bean, cacheData);
}
/**
* Return the value of the property method.
*/
@@ -735,12 +755,12 @@ public class BeanProperty implements ElPropertyValue {
throw new RuntimeException(msg, ex);
}
}
/**
* Explicitly use reflection to get value.
*/
public Object getValueViaReflection(Object bean) {
try {
try {
return readMethod.invoke(bean, NO_ARGS);
} catch (Exception ex) {
String beanType = bean == null ? "null" : bean.getClass().getName();
@@ -795,7 +815,7 @@ public class BeanProperty implements ElPropertyValue {
* Return the position of this property in the enhanced bean.
*/
public int getPropertyIndex() {
return propertyIndex;
return propertyIndex;
}
public String getElName() {
@@ -809,10 +829,10 @@ public class BeanProperty implements ElPropertyValue {
return false;
}
@Override
public boolean containsFormulaWithJoin() {
return formula && sqlFormulaJoin != null;
return formula && sqlFormulaJoin != null;
}
public boolean containsManySince(String sinceProperty) {
@@ -868,6 +888,14 @@ public class BeanProperty implements ElPropertyValue {
return descriptor.getFullName() + "." + name;
}
/**
* Return true if the mutable value is considered dirty.
* This is only used for 'mutable' scalar types like hstore etc.
*/
public boolean isDirtyValue(Object value) {
return scalarType.isDirty(value);
}
/**
* Return the scalarType.
*/
@@ -886,12 +914,12 @@ public class BeanProperty implements ElPropertyValue {
public boolean isDateTimeCapable() {
return scalarType != null && scalarType.isDateTimeCapable();
}
public int getJdbcType() {
return scalarType == null ? 0 : scalarType.getJdbcType();
return scalarType == null ? 0 : scalarType.getJdbcType();
}
public Object parseDateTime(long systemTimeMillis) {
public Object parseDateTime(long systemTimeMillis) {
return scalarType.parseDateTime(systemTimeMillis);
}
@@ -954,10 +982,10 @@ public class BeanProperty implements ElPropertyValue {
* Return true if this is the natural key property.
*/
public boolean isNaturalKey() {
return naturalKey;
return naturalKey;
}
/**
/**
* Return true if this property is mandatory.
*/
public boolean isNullable() {
@@ -990,9 +1018,9 @@ public class BeanProperty implements ElPropertyValue {
* Return true if this property is loadable from a resultSet.
*/
public boolean isLoadProperty() {
return !isTransient || formula;
return !isTransient || formula;
}
/**
* Return true if this is a version column used for concurrency checking.
*/
@@ -1033,10 +1061,10 @@ public class BeanProperty implements ElPropertyValue {
* Lob's usually default to fetch lazy.
*/
public boolean isFetchEager() {
return fetchEager;
return fetchEager;
}
/**
/**
* Return true if this is mapped to a Clob Blob LongVarchar or
* LongVarbinary.
*/
@@ -1046,17 +1074,17 @@ public class BeanProperty implements ElPropertyValue {
private boolean isLobType(int type) {
switch (type) {
case Types.CLOB:
return true;
case Types.BLOB:
return true;
case Types.LONGVARBINARY:
return true;
case Types.LONGVARCHAR:
return true;
case Types.CLOB:
return true;
case Types.BLOB:
return true;
case Types.LONGVARBINARY:
return true;
case Types.LONGVARCHAR:
return true;
default:
return false;
default:
return false;
}
}
@@ -1157,7 +1185,7 @@ public class BeanProperty implements ElPropertyValue {
@SuppressWarnings("unchecked")
public void jsonWrite(WriteJsonContext ctx, EntityBean bean) {
if(!jsonSerialize){
if (!jsonSerialize) {
return;
}
Object value = getValueIntercept(bean);
@@ -1169,15 +1197,15 @@ public class BeanProperty implements ElPropertyValue {
}
public void jsonRead(ReadJsonContext ctx, EntityBean bean) {
if(!jsonDeserialize){
if (!jsonDeserialize) {
return;
}
String jsonValue;
try {
jsonValue = ctx.readScalarValue();
} catch (TextException e){
throw new TextException("Error reading property "+getFullBeanName(), e);
}
String jsonValue;
try {
jsonValue = ctx.readScalarValue();
} catch (TextException e) {
throw new TextException("Error reading property " + getFullBeanName(), e);
}
Object objValue;
if (jsonValue == null) {
objValue = null;
@@ -1186,4 +1214,12 @@ public class BeanProperty implements ElPropertyValue {
}
setValue(bean, objValue);
}
public boolean isIndexed() {
return indexed;
}
public String getIndexName() {
return indexName;
}
}
@@ -3,7 +3,6 @@ package com.avaje.ebeaninternal.server.deploy;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.persistence.PersistenceException;
@@ -60,8 +59,16 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
*/
private final boolean manyToMany;
/**
* Order by used when fetch joining the associated many.
*/
private final String fetchOrderBy;
/**
* Order by used when lazy loading the associated many.
*/
private String lazyFetchOrderBy;
private final String mapKey;
/**
@@ -137,6 +144,23 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
if (exportedProperties.length > 0){
embeddedExportedProperties = exportedProperties[0].isEmbedded();
exportedPropertyBindProto = deriveExportedPropertyBindProto();
if (fetchOrderBy != null) {
// derive lazyFetchOrderBy
StringBuilder sb = new StringBuilder(50);
for (int i = 0; i < exportedProperties.length; i++) {
if (i > 0) {
sb.append(", ");
}
// 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);
}
lazyFetchOrderBy = sb.toString().trim();
}
}
String delStmt;
@@ -510,6 +534,13 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
}
/**
* Return the order by for use when lazy loading the associated collection.
*/
public String getLazyFetchOrderBy() {
return lazyFetchOrderBy;
}
/**
* Return the default mapKey when returning a Map.
*/
public String getMapKey() {
@@ -774,12 +805,8 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
private BeanProperty initMapKeyProperty() {
// search for the property
BeanDescriptor<?> targetDesc = getTargetDescriptor();
Iterator<BeanProperty> it = targetDesc.propertiesAll();
while (it.hasNext()){
BeanProperty prop = it.next();
for (BeanProperty prop : targetDesc.propertiesAll()) {
if (mapKey.equalsIgnoreCase(prop.getName())) {
return prop;
}
@@ -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();
}
}
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
@@ -8,33 +9,33 @@ import java.util.Set;
*/
public final class DeployPropertyParserMap extends DeployParser {
private final Map<String,String> map;
private final Map<String, String> map;
public DeployPropertyParserMap(Map<String,String> map) {
this.map = map;
}
public DeployPropertyParserMap(Map<String, String> map) {
this.map = map;
}
/**
* Returns null for raw sql queries.
*/
public Set<String> getIncludes() {
return null;
}
/**
* Returns null for raw sql queries.
*/
public Set<String> getIncludes() {
return Collections.emptySet();
}
public String convertWord() {
String r = getDeployWord(word);
return r == null ? word : r;
}
public String convertWord() {
String r = getDeployWord(word);
return r == null ? word : r;
}
@Override
public String getDeployWord(String expression) {
String deployExpr = map.get(expression);
if (deployExpr == null) {
return null;
} else {
return deployExpr;
}
@Override
public String getDeployWord(String expression) {
String deployExpr = map.get(expression);
if (deployExpr == null) {
return null;
} else {
return deployExpr;
}
}
}
@@ -1,10 +1,8 @@
package com.avaje.ebeaninternal.server.deploy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.SqlUpdate;
@@ -15,129 +13,113 @@ import com.avaje.ebeaninternal.util.DefaultExpressionRequest;
public class IntersectionRow {
private final String tableName;
private final String tableName;
private final LinkedHashMap<String,Object> values = new LinkedHashMap<String,Object>();
private final LinkedHashMap<String, Object> values = new LinkedHashMap<String, Object>();
private ArrayList<Object> excludeIds;
private BeanDescriptor<?> excludeDescriptor;
private ArrayList<Object> excludeIds;
private BeanDescriptor<?> excludeDescriptor;
public IntersectionRow(String tableName){
this.tableName = tableName;
}
/**
* Set Id's to exclude. This is for deleting non-attached detail Id's.
*/
public void setExcludeIds(ArrayList<Object> excludeIds, BeanDescriptor<?> excludeDescriptor){
this.excludeIds = excludeIds;
this.excludeDescriptor = excludeDescriptor;
}
public IntersectionRow(String tableName) {
this.tableName = tableName;
}
public void put(String key, Object value){
values.put(key, value);
}
/**
* Set Id's to exclude. This is for deleting non-attached detail Id's.
*/
public void setExcludeIds(ArrayList<Object> excludeIds, BeanDescriptor<?> excludeDescriptor) {
this.excludeIds = excludeIds;
this.excludeDescriptor = excludeDescriptor;
}
public void put(String key, Object value) {
values.put(key, value);
}
public SqlUpdate createInsert(EbeanServer server){
public SqlUpdate createInsert(EbeanServer server) {
BindParams bindParams = new BindParams();
BindParams bindParams = new BindParams();
StringBuilder sb = new StringBuilder();
sb.append("insert into ").append(tableName).append(" (");
StringBuilder sb = new StringBuilder();
sb.append("insert into ").append(tableName).append(" (");
int count = 0;
Iterator<Entry<String, Object>> it = values.entrySet().iterator();
while (it.hasNext()) {
if (count++ > 0){
sb.append(", ");
}
Map.Entry<String, Object> entry = it.next();
sb.append(entry.getKey());
bindParams.setParameter(count, entry.getValue());
}
sb.append(") values (");
for (int i = 0; i < count; i++) {
if (i > 0){
sb.append(", ");
}
sb.append("?");
}
sb.append(")");
return new DefaultSqlUpdate(server, sb.toString(), bindParams);
}
public SqlUpdate createDelete(EbeanServer server){
BindParams bindParams = new BindParams();
StringBuilder sb = new StringBuilder();
sb.append("delete from ").append(tableName).append(" where ");
int count = 0;
Iterator<Entry<String, Object>> it = values.entrySet().iterator();
while (it.hasNext()) {
if (count++ > 0){
sb.append(" and ");
}
Map.Entry<String, Object> entry = it.next();
sb.append(entry.getKey());
sb.append(" = ?");
bindParams.setParameter(count, entry.getValue());
}
if (excludeIds != null){
IdInExpression idIn = new IdInExpression(excludeIds);
DefaultExpressionRequest er = new DefaultExpressionRequest(excludeDescriptor);
idIn.addSqlNoAlias(er);
idIn.addBindValues(er);
sb.append(" and not ( ");
sb.append(er.getSql());
sb.append(" ) ");
ArrayList<Object> bindValues = er.getBindValues();
for (int i = 0; i < bindValues.size(); i++) {
bindParams.setParameter(++count, bindValues.get(i));
}
}
return new DefaultSqlUpdate(server, sb.toString(), bindParams);
}
public SqlUpdate createDeleteChildren(EbeanServer server) {
BindParams bindParams = new BindParams();
StringBuilder sb = new StringBuilder();
sb.append("delete from ").append(tableName).append(" where ");
int count = 0;
Iterator<Entry<String, Object>> it = values.entrySet().iterator();
while (it.hasNext()) {
if (count++ > 0) {
sb.append(" and ");
}
Map.Entry<String, Object> entry = it.next();
sb.append(entry.getKey());
sb.append(" = ?");
bindParams.setParameter(count, entry.getValue());
}
return new DefaultSqlUpdate(server, sb.toString(), bindParams);
int count = 0;
for (Map.Entry<String, Object> entry : values.entrySet()) {
if (count++ > 0) {
sb.append(", ");
}
sb.append(entry.getKey());
bindParams.setParameter(count, entry.getValue());
}
sb.append(") values (");
for (int i = 0; i < count; i++) {
if (i > 0) {
sb.append(", ");
}
sb.append("?");
}
sb.append(")");
return new DefaultSqlUpdate(server, sb.toString(), bindParams);
}
public SqlUpdate createDelete(EbeanServer server) {
BindParams bindParams = new BindParams();
StringBuilder sb = new StringBuilder();
sb.append("delete from ").append(tableName).append(" where ");
int count = 0;
for (Map.Entry<String, Object> entry : values.entrySet()) {
if (count++ > 0) {
sb.append(" and ");
}
sb.append(entry.getKey());
sb.append(" = ?");
bindParams.setParameter(count, entry.getValue());
}
if (excludeIds != null) {
IdInExpression idIn = new IdInExpression(excludeIds);
DefaultExpressionRequest er = new DefaultExpressionRequest(excludeDescriptor);
idIn.addSqlNoAlias(er);
idIn.addBindValues(er);
sb.append(" and not ( ");
sb.append(er.getSql());
sb.append(" ) ");
ArrayList<Object> bindValues = er.getBindValues();
for (int i = 0; i < bindValues.size(); i++) {
bindParams.setParameter(++count, bindValues.get(i));
}
}
return new DefaultSqlUpdate(server, sb.toString(), bindParams);
}
public SqlUpdate createDeleteChildren(EbeanServer server) {
BindParams bindParams = new BindParams();
StringBuilder sb = new StringBuilder();
sb.append("delete from ").append(tableName).append(" where ");
int count = 0;
for (Map.Entry<String, Object> entry : values.entrySet()) {
if (count++ > 0) {
sb.append(" and ");
}
sb.append(entry.getKey());
sb.append(" = ?");
bindParams.setParameter(count, entry.getValue());
}
return new DefaultSqlUpdate(server, sb.toString(), bindParams);
}
}
@@ -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;
@@ -6,13 +6,15 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
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;
@@ -226,9 +228,7 @@ public class DeployBeanDescriptor<T> {
boolean missingMethods = false;
Iterator<DeployBeanProperty> it = propMap.values().iterator();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
for (DeployBeanProperty prop : propMap.values()) {
if (!prop.isTransient()) {
String m = "";
if (prop.getReadMethod() == null) {
@@ -704,10 +704,10 @@ public class DeployBeanDescriptor<T> {
}
/**
* Return an Iterator of all BeanProperty.
* Return a collection of all BeanProperty deployment information.
*/
public Iterator<DeployBeanProperty> propertiesAll() {
return propMap.values().iterator();
public Collection<DeployBeanProperty> propertiesAll() {
return propMap.values();
}
/**
@@ -719,9 +719,7 @@ public class DeployBeanDescriptor<T> {
boolean hasLazyFetch = false;
Iterator<DeployBeanProperty> it = propMap.values().iterator();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
for (DeployBeanProperty prop : propMap.values()) {
if (prop.isTransient()) {
// ignore transient props etc
} else if (prop instanceof DeployBeanPropertyAssocMany<?>) {
@@ -794,9 +792,7 @@ public class DeployBeanDescriptor<T> {
ArrayList<DeployBeanProperty> list = new ArrayList<DeployBeanProperty>(2);
Iterator<DeployBeanProperty> it = propMap.values().iterator();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
for (DeployBeanProperty prop : propMap.values()) {
if (prop.isId()) {
list.add(prop);
}
@@ -825,9 +821,7 @@ public class DeployBeanDescriptor<T> {
ArrayList<DeployBeanPropertyAssocOne<?>> list = new ArrayList<DeployBeanPropertyAssocOne<?>>();
Iterator<DeployBeanProperty> it = propMap.values().iterator();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
for (DeployBeanProperty prop : propMap.values()) {
if (prop instanceof DeployBeanPropertyAssocOne<?>) {
if (!prop.isEmbedded()) {
list.add((DeployBeanPropertyAssocOne<?>) prop);
@@ -846,9 +840,7 @@ public class DeployBeanDescriptor<T> {
ArrayList<DeployBeanPropertyAssocMany<?>> list = new ArrayList<DeployBeanPropertyAssocMany<?>>();
Iterator<DeployBeanProperty> it = propMap.values().iterator();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
for (DeployBeanProperty prop : propMap.values()) {
if (prop instanceof DeployBeanPropertyAssocMany<?>) {
list.add((DeployBeanPropertyAssocMany<?>) prop);
}
@@ -866,13 +858,8 @@ public class DeployBeanDescriptor<T> {
ArrayList<DeployBeanProperty> list = new ArrayList<DeployBeanProperty>();
Iterator<DeployBeanProperty> it = propMap.values().iterator();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
if (prop instanceof DeployBeanPropertyAssoc<?>) {
} else {
for (DeployBeanProperty prop : propMap.values()) {
if (prop instanceof DeployBeanPropertyAssoc<?> == false) {
if (!prop.isId() && prop.isVersionColumn()) {
list.add(prop);
}
@@ -889,13 +876,8 @@ public class DeployBeanDescriptor<T> {
ArrayList<DeployBeanProperty> list = new ArrayList<DeployBeanProperty>();
Iterator<DeployBeanProperty> it = propMap.values().iterator();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
if (prop instanceof DeployBeanPropertyAssoc<?>) {
} else {
for (DeployBeanProperty prop : propMap.values()) {
if (prop instanceof DeployBeanPropertyAssoc<?> == false) {
if (!prop.isId()) {
list.add(prop);
}
@@ -905,4 +887,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);
}
}
}
@@ -217,6 +217,9 @@ public class DeployBeanProperty {
private int sortOrder;
private boolean indexed;
private String indexName;
public DeployBeanProperty(DeployBeanDescriptor<?> desc, Class<?> propertyType, ScalarType<?> scalarType, ScalarTypeConverter<?, ?> typeConverter) {
this.desc = desc;
this.propertyType = propertyType;
@@ -930,4 +933,19 @@ public class DeployBeanProperty {
return desc.getFullName() + "." + name;
}
public boolean isIndexed() {
return indexed;
}
public void setIndexed(boolean indexed) {
this.indexed = indexed;
}
public String getIndexName() {
return indexName;
}
public void setIndexName(String indexName) {
this.indexName = indexName;
}
}
@@ -1,7 +1,6 @@
package com.avaje.ebeaninternal.server.deploy.meta;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
@@ -37,6 +36,8 @@ public class DeployBeanPropertyLists {
private final ArrayList<BeanProperty> local = new ArrayList<BeanProperty>();
private final ArrayList<BeanProperty> mutable = new ArrayList<BeanProperty>();
private final ArrayList<BeanProperty> manys = new ArrayList<BeanProperty>();
private final ArrayList<BeanProperty> nonManys = new ArrayList<BeanProperty>();
@@ -74,18 +75,13 @@ public class DeployBeanPropertyLists {
this.propertyMap = new LinkedHashMap<String, BeanProperty>();
Iterator<DeployBeanProperty> deployIt = deploy.propertiesAll();
while (deployIt.hasNext()) {
DeployBeanProperty deployProp = deployIt.next();
BeanProperty beanProp = createBeanProperty(owner, deployProp);
for (DeployBeanProperty prop : deploy.propertiesAll()) {
BeanProperty beanProp = createBeanProperty(owner, prop);
propertyMap.put(beanProp.getName(), beanProp);
}
Iterator<BeanProperty> it = propertyMap.values().iterator();
int order = 0;
while (it.hasNext()) {
BeanProperty prop = it.next();
for (BeanProperty prop : propertyMap.values()) {
prop.setDeployOrder(order++);
allocateToList(prop);
}
@@ -135,6 +131,10 @@ public class DeployBeanPropertyLists {
nonTransients.add(prop);
}
if (prop.isMutableScalarType()) {
mutable.add(prop);
}
if (desc.getInheritInfo() != null && prop.isLocal()) {
local.add(prop);
}
@@ -197,7 +197,7 @@ public class DeployBeanPropertyLists {
public BeanProperty getId() {
if (ids.size() > 1) {
String msg = "Ebean does not support multiple @Id properties. You need to convert to using an @EmbeddedId."
String msg = "Issue with bean "+desc+". Ebean does not support multiple @Id properties. You need to convert to using an @EmbeddedId."
+" Please email the ebean google group if you need further clarification.";
throw new IllegalStateException(msg);
}
@@ -223,6 +223,10 @@ public class DeployBeanPropertyLists {
return (BeanProperty[]) local.toArray(new BeanProperty[local.size()]);
}
public BeanProperty[] getMutable() {
return (BeanProperty[]) mutable.toArray(new BeanProperty[mutable.size()]);
}
public BeanPropertyAssocOne<?>[] getEmbedded() {
return (BeanPropertyAssocOne[]) embedded.toArray(new BeanPropertyAssocOne[embedded.size()]);
}
@@ -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;
}
}
@@ -1,7 +1,5 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import java.util.Iterator;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
@@ -18,12 +16,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.
@@ -44,9 +42,7 @@ public class AnnotationAssocManys extends AnnotationParser {
* Parse the annotations.
*/
public void parse() {
Iterator<DeployBeanProperty> it = descriptor.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
for (DeployBeanProperty prop : descriptor.propertiesAll()) {
if (prop instanceof DeployBeanPropertyAssocMany<?>) {
read((DeployBeanPropertyAssocMany<?>) prop);
}
@@ -160,7 +156,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 +212,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 +278,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 +303,7 @@ public class AnnotationAssocManys extends AnnotationParser {
}
manyProp.setBeanTable(assoc);
manyProp.getTableJoin().setType(TableJoin.LEFT_OUTER);
manyProp.getTableJoin().setType(SqlJoinType.OUTER);
}
@@ -1,6 +1,5 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import java.util.Iterator;
import java.util.Map;
import javax.persistence.Column;
@@ -19,10 +18,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.
@@ -44,9 +43,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
*/
public void parse() {
Iterator<DeployBeanProperty> it = descriptor.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
for (DeployBeanProperty prop : descriptor.propertiesAll()) {
if (prop instanceof DeployBeanPropertyAssocOne<?>) {
readAssocOne((DeployBeanPropertyAssocOne<?>) prop);
}
@@ -99,7 +96,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);
}
}
@@ -1,40 +1,21 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import java.sql.Types;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.PersistenceException;
import javax.persistence.SequenceGenerator;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.persistence.Version;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import com.avaje.ebean.annotation.CreatedTimestamp;
import com.avaje.ebean.annotation.EmbeddedColumns;
import com.avaje.ebean.annotation.Encrypted;
import com.avaje.ebean.annotation.Expose;
import com.avaje.ebean.annotation.Formula;
import com.avaje.ebean.annotation.UpdatedTimestamp;
import com.avaje.ebean.annotation.*;
import com.avaje.ebean.config.EncryptDeploy;
import com.avaje.ebean.config.EncryptDeploy.Mode;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.dbplatform.DbEncrypt;
import com.avaje.ebean.config.dbplatform.DbEncryptFunction;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
@@ -66,7 +47,7 @@ public class AnnotationFields extends AnnotationParser {
if (GlobalProperties.getBoolean("ebean.lobEagerFetch", false)) {
defaultLobFetchType = FetchType.EAGER;
}
}
}
/**
@@ -74,15 +55,12 @@ public class AnnotationFields extends AnnotationParser {
*/
public void parse() {
Iterator<DeployBeanProperty> it = descriptor.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
for (DeployBeanProperty prop : descriptor.propertiesAll()) {
if (prop instanceof DeployBeanPropertyAssoc<?>) {
readAssocOne(prop);
} else {
readField(prop);
}
}
}
@@ -108,7 +86,7 @@ public class AnnotationFields extends AnnotationParser {
if (prop.isId() && !prop.isEmbedded()) {
prop.setEmbedded(true);
}
readEmbeddedAttributeOverrides((DeployBeanPropertyAssocOne<?>)prop);
readEmbeddedAttributeOverrides((DeployBeanPropertyAssocOne<?>) prop);
}
}
@@ -153,7 +131,7 @@ public class AnnotationFields extends AnnotationParser {
if (id != null) {
readId(id, prop);
}
// determine the JDBC type using Lob/Temporal
// otherwise based on the property Class
Lob lob = get(prop, Lob.class);
@@ -203,9 +181,9 @@ public class AnnotationFields extends AnnotationParser {
if (notNull != null && isNotNullOnAllValidationGroups(notNull.groups())) {
// Not null on all validation groups so enable
// DDL generation of Not Null Constraint
prop.setNullable(false);
prop.setNullable(false);
}
Size size = get(prop, Size.class);
if (size != null) {
if (size.max() < Integer.MAX_VALUE) {
@@ -233,7 +211,7 @@ public class AnnotationFields extends AnnotationParser {
} else {
throw new RuntimeException("Can't use EmbeddedColumns on ScalarType "
+ prop.getFullBeanName());
+ prop.getFullBeanName());
}
}
@@ -250,7 +228,7 @@ public class AnnotationFields extends AnnotationParser {
if (!prop.isTransient()) {
EncryptDeploy encryptDeploy = util.getEncryptDeploy(info.getDescriptor().getBaseTableFull(),
prop.getDbColumn());
prop.getDbColumn());
if (encryptDeploy == null || encryptDeploy.getMode().equals(Mode.MODE_ANNOTATION)) {
Encrypted encrypted = get(prop, Encrypted.class);
if (encrypted != null) {
@@ -261,6 +239,20 @@ public class AnnotationFields extends AnnotationParser {
}
}
Index index = get(prop, Index.class);
if (index != null) {
if(hasRelationshipItem(prop)) {
throw new RuntimeException("Can't use Index on foreign key relationships.");
}
prop.setIndexed(true);
prop.setIndexName(index.value());
}
}
private boolean hasRelationshipItem(DeployBeanProperty prop) {
return get(prop, OneToMany.class) != null ||
get(prop, ManyToOne.class) != null ||
get(prop, OneToOne.class) != null;
}
/**
@@ -317,7 +309,7 @@ public class AnnotationFields extends AnnotationParser {
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
private ScalarTypeEncryptedWrapper<?> createScalarType(DeployBeanProperty prop, ScalarType<?> st) {
// Use Java Encryptor wrapping the logical scalar type
@@ -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);
}
}
@@ -5,11 +5,14 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Iterator;
import javax.persistence.PersistenceException;
import javax.persistence.Transient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.annotation.ColumnHstore;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.deploy.DetermineManyType;
import com.avaje.ebeaninternal.server.deploy.ManyType;
@@ -21,10 +24,9 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection;
import com.avaje.ebeaninternal.server.type.CtCompoundType;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.server.type.ScalarTypePostgresHstore;
import com.avaje.ebeaninternal.server.type.TypeManager;
import com.avaje.ebeaninternal.server.type.reflect.CheckImmutableResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Create the properties for a bean.
@@ -55,10 +57,7 @@ public class DeployCreateProperties {
desc.sortProperties();
// check the transient properties...
Iterator<DeployBeanProperty> it = desc.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
for (DeployBeanProperty prop : desc.propertiesAll()) {
if (prop.isTransient()) {
if (prop.getWriteMethod() == null || prop.getReadMethod() == null) {
// Typically a helper method ... this is expected
@@ -261,7 +260,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());
@@ -276,6 +275,16 @@ public class DeployCreateProperties {
Class<?> propertyType = field.getType();
Class<?> innerType = propertyType;
String specialTypeKey = getSpecialScalarType(field);
if (specialTypeKey != null) {
ScalarType<?> scalarType = typeManager.getScalarTypeFromKey(specialTypeKey);
if (scalarType == null) {
logger.error("Could not find ScalarType to match key ["+specialTypeKey+"]");
} else {
return new DeployBeanProperty(desc, propertyType, scalarType, null);
}
}
// check for Collection type (list, set or map)
ManyType manyType = determineManyType.getManyType(propertyType);
@@ -336,6 +345,15 @@ public class DeployCreateProperties {
}
}
private String getSpecialScalarType(Field field) {
if (field.getAnnotation(ColumnHstore.class) != null) {
return ScalarTypePostgresHstore.KEY;
};
return null;
}
private boolean isTransientField(Field field) {
Transient t = field.getAnnotation(Transient.class);
@@ -2,7 +2,6 @@ package com.avaje.ebeaninternal.server.deploy.parse;
import java.lang.annotation.Annotation;
import java.sql.Types;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -51,9 +50,7 @@ public class DeployInherit {
private void findInheritClasses(List<Class<?>> entityList) {
// go through each class and initialise the info object...
Iterator<Class<?>> it = entityList.iterator();
while (it.hasNext()) {
Class<?> cls = (Class<?>) it.next();
for (Class<?> cls : entityList) {
if (isInheritanceClass(cls)) {
DeployInheritInfo info = createInfo(cls);
deployMap.put(cls, info);
@@ -62,9 +59,8 @@ public class DeployInherit {
}
private void buildDeployTree() {
Iterator<DeployInheritInfo> it = deployMap.values().iterator();
while (it.hasNext()) {
DeployInheritInfo info = it.next();
for (DeployInheritInfo info : deployMap.values()) {
if (!info.isRoot()) {
DeployInheritInfo parent = getInfo(info.getParent());
parent.addChild(info);
@@ -74,19 +70,15 @@ public class DeployInherit {
private void buildFinalTree() {
Iterator<DeployInheritInfo> it = deployMap.values().iterator();
while (it.hasNext()) {
DeployInheritInfo deploy = it.next();
for (DeployInheritInfo deploy : deployMap.values()) {
if (deploy.isRoot()) {
// build tree top down...
createFinalInfo(null, null, deploy);
}
}
}
private InheritInfo createFinalInfo(InheritInfo root, InheritInfo parent,
DeployInheritInfo deploy) {
private InheritInfo createFinalInfo(InheritInfo root, InheritInfo parent, DeployInheritInfo deploy) {
InheritInfo node = new InheritInfo(root, parent, deploy);
if (parent != null) {
@@ -99,12 +91,7 @@ public class DeployInherit {
}
// buildFinalChildren(root, child, deploy);
Iterator<DeployInheritInfo> it = deploy.children();
while (it.hasNext()) {
DeployInheritInfo childDeploy = it.next();
for (DeployInheritInfo childDeploy : deploy.children()) {
createFinalInfo(root, node, childDeploy);
}
@@ -2,7 +2,6 @@ package com.avaje.ebeaninternal.server.deploy.parse;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
@@ -80,8 +79,8 @@ public class DeployInheritInfo {
/**
* Return the child nodes.
*/
public Iterator<DeployInheritInfo> children() {
return children.iterator();
public List<DeployInheritInfo> children() {
return children;
}
/**
@@ -1,9 +1,7 @@
package com.avaje.ebeaninternal.server.expression;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
@@ -29,9 +27,7 @@ class AllEqualsExpression implements SpiExpression {
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
if (propMap != null) {
Iterator<String> it = propMap.keySet().iterator();
while (it.hasNext()) {
String propertyName = it.next();
for (String propertyName : propMap.keySet()) {
ElPropertyDeploy elProp = desc.getElPropertyDeploy(name(propertyName));
if (elProp != null && elProp.containsMany()) {
manyWhereJoin.add(elProp);
@@ -45,9 +41,7 @@ class AllEqualsExpression implements SpiExpression {
if (propMap.isEmpty()) {
return;
}
Iterator<Object> it = propMap.values().iterator();
while (it.hasNext()) {
Object value = it.next();
for (Object value : propMap.values()) {
if (value != null) {
request.addBindValue(value);
} else {
@@ -64,12 +58,9 @@ class AllEqualsExpression implements SpiExpression {
request.append("(");
Set<Entry<String, Object>> entries = propMap.entrySet();
Iterator<Entry<String, Object>> it = entries.iterator();
int count = 0;
while (it.hasNext()) {
Map.Entry<java.lang.String, java.lang.Object> entry = it.next();
for (Map.Entry<String,Object> entry : propMap.entrySet()) {
Object value = entry.getValue();
String propName = entry.getKey();
@@ -1,7 +1,6 @@
package com.avaje.ebeaninternal.server.expression;
import java.util.ArrayList;
import java.util.Iterator;
import com.avaje.ebean.ExampleExpression;
import com.avaje.ebean.LikeType;
@@ -207,10 +206,8 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
OrmQueryRequest<?> r = (OrmQueryRequest<?>) request;
BeanDescriptor<?> beanDescriptor = r.getBeanDescriptor();
Iterator<BeanProperty> propIter = beanDescriptor.propertiesAll();
while (propIter.hasNext()) {
BeanProperty beanProperty = propIter.next();
for (BeanProperty beanProperty : beanDescriptor.propertiesAll()) {
String propName = beanProperty.getName();
Object value = beanProperty.getValue(entity);
@@ -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();
@@ -1,7 +1,7 @@
package com.avaje.ebeaninternal.server.lib.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
@@ -60,9 +60,7 @@ public class Dnode {
sb = new StringBuilder();
}
sb.append("<").append(nodeName);
Iterator<String> it = attributeNames();
while (it.hasNext()) {
String attr = it.next();
for (String attr : attrList.keySet()) {
Object attrValue = getAttribute(attr);
sb.append(" ").append(attr).append("=\"");
if (attrValue != null) {
@@ -145,9 +143,7 @@ public class Dnode {
if (children.remove(node)) {
return true;
}
Iterator<Dnode> it = children.iterator();
while (it.hasNext()) {
Dnode child = it.next();
for (Dnode child : children) {
if (child.remove(node)) {
return true;
}
@@ -286,8 +282,8 @@ public class Dnode {
/**
* The attribute names as strings.
*/
public Iterator<String> attributeNames() {
return attrList.keySet().iterator();
public Collection<String> attributeNames() {
return attrList.keySet();
}
/**
@@ -2,8 +2,9 @@ package com.avaje.ebeaninternal.server.lib.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
* A simple test message that can be sent via smtp.
@@ -84,8 +85,8 @@ public class MailMessage {
/**
* Return the recipient list.
*/
public Iterator<MailAddress> getRecipientList() {
return recipientList.iterator();
public List<MailAddress> getRecipientList() {
return recipientList;
}
/**
@@ -119,15 +120,15 @@ public class MailMessage {
/**
* Return the body text.
*/
public Iterator<String> getBodyLines() {
return bodylines.iterator();
public List<String> getBodyLines() {
return bodylines;
}
/**
* Return the headers.
*/
public Iterator<String> getHeaderFields() {
return header.keySet().iterator();
public Collection<String> getHeaderFields() {
return header.keySet();
}
/**
@@ -140,16 +141,13 @@ public class MailMessage {
public String toString() {
StringBuilder sb = new StringBuilder(100);
sb.append("Sender: " + senderAddress + "\tRecipient: " + recipientList + "\n");
Iterator<String> hi = header.keySet().iterator();
while (hi.hasNext()) {
String key = hi.next();
for (String key : header.keySet()) {
String hline = key + ": " + header.get(key) + "\n";
sb.append(hline);
}
sb.append("\n");
Iterator<String> e = bodylines.iterator();
while (e.hasNext()) {
sb.append(e.next()).append("\n");
for (String line : bodylines) {
sb.append(line).append("\n");
}
return sb.toString();
}
@@ -1,8 +1,5 @@
package com.avaje.ebeaninternal.server.lib.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -10,206 +7,200 @@ import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Iterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Sends simple MailMessages via smtp.
* Sends simple MailMessages via smtp.
*/
public class MailSender implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(MailSender.class);
int traceLevel = 0;
Socket sserver;
String server;
BufferedReader in;
private static final Logger logger = LoggerFactory.getLogger(MailSender.class);
OutputStreamWriter out;
MailMessage message;
int traceLevel = 0;
MailListener listener = null;
Socket sserver;
String server;
private static final int SMTP_PORT = 25;
BufferedReader in;
/**
* Create for a given mail server.
*/
public MailSender(String server) {
this.server = server;
}
/**
* Set the listener to handle MessageEvents.
*/
public void setMailListener(MailListener listener){
this.listener = listener;
}
OutputStreamWriter out;
/**
* Send the message.
*/
public void run() {
send(message);
}
MailMessage message;
/**
* Send the message in a background thread.
*/
public void sendInBackground(MailMessage message){
this.message = message;
Thread thread = new Thread(this);
thread.start();
}
MailListener listener = null;
/**
* Send the message in the current thread.
*/
public void send(MailMessage message){
try {
Iterator<MailAddress> i = message.getRecipientList();
while (i.hasNext()) {
MailAddress recipientAddress = (MailAddress) i.next();
sserver = new Socket(server, SMTP_PORT);
send(message, sserver, recipientAddress);
sserver.close();
if (listener != null){
MailEvent event = new MailEvent(message, null);
listener.handleEvent(event);
}
}
} catch (Exception ex) {
if (listener != null){
MailEvent event = new MailEvent(message, ex);
listener.handleEvent(event);
} else {
logger.error(null, ex);
}
private static final int SMTP_PORT = 25;
/**
* Create for a given mail server.
*/
public MailSender(String server) {
this.server = server;
}
/**
* Set the listener to handle MessageEvents.
*/
public void setMailListener(MailListener listener) {
this.listener = listener;
}
/**
* Send the message.
*/
public void run() {
send(message);
}
/**
* Send the message in a background thread.
*/
public void sendInBackground(MailMessage message) {
this.message = message;
Thread thread = new Thread(this);
thread.start();
}
/**
* Send the message in the current thread.
*/
public void send(MailMessage message) {
try {
for (MailAddress recipientAddress : message.getRecipientList()) {
sserver = new Socket(server, SMTP_PORT);
send(message, sserver, recipientAddress);
sserver.close();
if (listener != null) {
MailEvent event = new MailEvent(message, null);
listener.handleEvent(event);
}
}
} catch (Exception ex) {
if (listener != null) {
MailEvent event = new MailEvent(message, ex);
listener.handleEvent(event);
} else {
logger.error(null, ex);
}
}
}
private void send(MailMessage message, Socket sserver, MailAddress recipientAddress) throws IOException {
// A bit convoluted, but doesn't depend on DNS in any way...
InetAddress localhost = sserver.getLocalAddress();
String localaddress = localhost.getHostAddress();
MailAddress sender = message.getSender();
message.setCurrentRecipient(recipientAddress);
// Mandatory header fields, Date and From
if (message.getHeader("Date") == null) {
message.addHeader("Date", new java.util.Date().toString());
}
if (message.getHeader("From") == null) {
message.addHeader("From", sender.getAlias() + " <" + sender.getEmailAddress() + ">");
}
private void send(MailMessage message, Socket sserver, MailAddress recipientAddress) throws IOException {
// A bit convoluted, but doesn't depend on DNS in any way...
InetAddress localhost = sserver.getLocalAddress();
String localaddress = localhost.getHostAddress();
MailAddress sender = message.getSender();
message.setCurrentRecipient(recipientAddress);
// Mandatory header fields, Date and From
if (message.getHeader("Date") == null){
message.addHeader("Date", new java.util.Date().toString());
}
if (message.getHeader("From") == null){
message.addHeader("From", sender.getAlias() + " <" + sender.getEmailAddress() + ">");
}
//if (message.getHeader("From") == null){
message.addHeader("To", recipientAddress.getAlias() + " <" + recipientAddress.getEmailAddress() + ">");
//}
out = new OutputStreamWriter(sserver.getOutputStream());
in = new BufferedReader(new InputStreamReader(sserver.getInputStream()));
String sintro = readln();
if (!sintro.startsWith("220")) { // 220
logger.debug("SmtpSender: intro==" + sintro);
return;
}
writeln("EHLO " + localaddress);
if (!expect250()) {
return;
}
writeln("MAIL FROM:<" + sender.getEmailAddress() + ">");
if (!expect250()) {
return;
}
writeln("RCPT TO:<" + recipientAddress.getEmailAddress() + ">");
if (!expect250()){
return;
}
writeln("DATA");
while (true) { // may be multiple 250 replies pending from server
String line = readln();
if (line.startsWith("3"))
break; // ready to send
if (!line.startsWith("2")) {
logger.debug("SmtpSender.send reponse to DATA: " + line);
return;
}
}
Iterator<String> hi = message.getHeaderFields();
while (hi.hasNext()) {
String key = (String) hi.next();
writeln(key + ": " + message.getHeader(key));
}
writeln(""); // end of header;
Iterator<String> e = message.getBodyLines();
while (e.hasNext()) {
String bline = (String) e.next();
if (bline.startsWith(".")) {
bline = "." + bline;
}
writeln(bline);
}
writeln(".");
expect250();
writeln("QUIT");
// if (message.getHeader("From") == null){
message.addHeader("To", recipientAddress.getAlias() + " <" + recipientAddress.getEmailAddress() + ">");
// }
out = new OutputStreamWriter(sserver.getOutputStream());
in = new BufferedReader(new InputStreamReader(sserver.getInputStream()));
String sintro = readln();
if (!sintro.startsWith("220")) { // 220
logger.debug("SmtpSender: intro==" + sintro);
return;
}
private boolean expect250() throws IOException {
String line = readln();
if (!line.startsWith("2")) {
logger.info("SmtpSender.expect250: " + line);
return false;
}
return true;
writeln("EHLO " + localaddress);
if (!expect250()) {
return;
}
private void writeln(String s) throws IOException {
if (traceLevel > 2){
logger.debug("From client: " + s);
}
out.write(s + "\r\n");
out.flush();
writeln("MAIL FROM:<" + sender.getEmailAddress() + ">");
if (!expect250()) {
return;
}
private String readln() throws IOException {
String line = in.readLine();
if (traceLevel > 1){
logger.debug("From server: " + line);
}
return line;
writeln("RCPT TO:<" + recipientAddress.getEmailAddress() + ">");
if (!expect250()) {
return;
}
/**
* Set the trace level.
*/
public void setTraceLevel(int traceLevel){
this.traceLevel = traceLevel;
writeln("DATA");
while (true) { // may be multiple 250 replies pending from server
String line = readln();
if (line.startsWith("3"))
break; // ready to send
if (!line.startsWith("2")) {
logger.debug("SmtpSender.send reponse to DATA: " + line);
return;
}
}
for (String key : message.getHeaderFields()) {
writeln(key + ": " + message.getHeader(key));
}
writeln(""); // end of header;
for (String bline : message.getBodyLines()) {
if (bline.startsWith(".")) {
bline = "." + bline;
}
writeln(bline);
}
writeln(".");
expect250();
writeln("QUIT");
/**
* Return the hostname of the local machine.
*/
public String getLocalHostName() {
try {
InetAddress ipaddress = InetAddress.getLocalHost();
String localHost = ipaddress.getHostName();
if (localHost == null) {
return "localhost";
} else {
return localHost;
}
} catch (UnknownHostException e) {
return "localhost";
}
}
}
private boolean expect250() throws IOException {
String line = readln();
if (!line.startsWith("2")) {
logger.info("SmtpSender.expect250: " + line);
return false;
}
return true;
}
private void writeln(String s) throws IOException {
if (traceLevel > 2) {
logger.debug("From client: " + s);
}
out.write(s + "\r\n");
out.flush();
}
private String readln() throws IOException {
String line = in.readLine();
if (traceLevel > 1) {
logger.debug("From server: " + line);
}
return line;
}
/**
* Set the trace level.
*/
public void setTraceLevel(int traceLevel) {
this.traceLevel = traceLevel;
}
/**
* Return the hostname of the local machine.
*/
public String getLocalHostName() {
try {
InetAddress ipaddress = InetAddress.getLocalHost();
String localHost = ipaddress.getHostName();
if (localHost == null) {
return "localhost";
} else {
return localHost;
}
} catch (UnknownHostException e) {
return "localhost";
}
}
}
@@ -0,0 +1,38 @@
package com.avaje.ebeaninternal.server.lib.util;
/**
* String utility for adding strings together.
* <p>
* Predicts a decent buffer size to append the strings into.
*/
public class Str {
/**
* Append strings together.
*/
public static String add(String s0, String s1, String ... args) {
// determine a decent buffer size
int len = 16 + s0.length() + s1.length();
for (int i = 0; i < args.length; i++) {
len += args[i].length();
}
// append all the strings into the buffer
StringBuilder sb = new StringBuilder(len);
sb.append(s0).append(s1);
for (int i = 0; i < args.length; i++) {
sb.append(args[i]);
}
return sb.toString();
}
/**
* Append two strings together.
*/
public static String add(String s0, String s1) {
StringBuilder sb = new StringBuilder(s0.length() + s1.length() + 5);
return sb.append(s0).append(s1).toString();
}
}
@@ -57,7 +57,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
currentBuffer = createBuffer(secondaryBatchSize);
}
// set the persistenceContext on the bean first
ebi.setBeanLoader(0, currentBuffer, getPersistenceContext());
ebi.setBeanLoader(currentBuffer, getPersistenceContext());
currentBuffer.add(ebi);
}
@@ -1,15 +1,14 @@
package com.avaje.ebeaninternal.server.persist;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import javax.persistence.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Used to hold BatchedPstmt objects for batch based execution.
* <p>
@@ -92,9 +91,7 @@ public class BatchedPstmtHolder {
// but still need to close PreparedStatements.
boolean isError = false;
Iterator<BatchedPstmt> it = stmtMap.values().iterator();
while (it.hasNext()) {
BatchedPstmt bs = it.next();
for (BatchedPstmt bs : stmtMap.values()) {
try {
if (!isError) {
bs.executeBatch(getGeneratedKeys);
@@ -119,7 +116,7 @@ public class BatchedPstmtHolder {
bs.close();
} catch (SQLException ex) {
// error closing PreparedStatement
logger.error(null, ex);
logger.error(null, ex);
}
}
}
@@ -64,8 +64,6 @@ public final class DefaultPersistExecute implements PersistExecute {
BeanPersistController controller = request.getBeanController();
if (controller == null || controller.preInsert(request)) {
persister.insert(request);
// NOTE: the persister fires the postInsert so that this
// occurs before ebeanIntercept.setLoaded(true)
}
}
@@ -79,9 +77,8 @@ public final class DefaultPersistExecute implements PersistExecute {
BeanPersistController controller = request.getBeanController();
if (controller == null || controller.preUpdate(request)) {
request.postControllerPrepareUpdate();
persister.update(request);
// NOTE: the persister fires the postUpdate so that this
// occurs before ebeanIntercept.setLoaded(true)
}
}
@@ -95,9 +92,7 @@ public final class DefaultPersistExecute implements PersistExecute {
BeanPersistController controller = request.getBeanController();
if (controller == null || controller.preDelete(request)) {
persister.delete(request);
// NOTE: the persister fires the postDelete
}
}
@@ -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();
@@ -413,14 +414,14 @@ public final class DefaultPersister implements Persister {
if (t.isPersistCascade()) {
// OneToOne exported side with delete cascade
BeanPropertyAssocOne<?>[] expOnes = descriptor.propertiesOneExportedDelete();
for (int i = 0; i < expOnes.length; i++) {
for (int i = 0; i < expOnes.length; i++) {
BeanDescriptor<?> targetDesc = expOnes[i].getTargetDescriptor();
if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isBeanCaching()) {
SqlUpdate sqlDelete = expOnes[i].deleteByParentId(id, idList);
executeSqlUpdate(sqlDelete, t);
} else {
List<Object> childIds = expOnes[i].findIdsByParentId(id, idList, t);
delete(targetDesc, null, childIds, t);
deleteChildrenById(t, targetDesc, childIds);
}
}
@@ -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 {
@@ -1057,16 +1058,37 @@ public final class DefaultPersister implements Persister {
Object parentId = desc.getId(parentBean);
List<Object> idsByParentId = many.findIdsByParentId(parentId, null, t, excludeDetailIds);
if (!idsByParentId.isEmpty()) {
delete(targetDesc, null, idsByParentId, t);
deleteChildrenById(t, targetDesc, idsByParentId);
}
}
}
}
/**
* Cascade delete child entities by Id.
* <p>
* Will use delete by object if the child entity has manyToMany relationships.
*/
private void deleteChildrenById(SpiTransaction t, BeanDescriptor<?> targetDesc, List<Object> childIds) {
if (targetDesc.propertiesManyToMany().length > 0) {
// convert into a list of reference objects and perform delete by object
List<Object> refList = new ArrayList<Object>(childIds.size());
for (Object id : childIds) {
refList.add(targetDesc.createReference(null, id));
}
deleteList(refList, t);
} else {
// perform delete by statement if possible
delete(targetDesc, null, childIds, t);
}
}
/**
* Save any associated one beans.
*/
private void saveAssocOne(PersistRequestBean<?> request) {
private void saveAssocOne(PersistRequestBean<?> request, boolean insertMode) {
BeanDescriptor<?> desc = request.getBeanDescriptor();
@@ -1090,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);
}
}
@@ -1175,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>
@@ -15,6 +15,7 @@ import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.core.PstmtBatch;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.lib.util.Str;
import com.avaje.ebeaninternal.server.persist.BatchedPstmt;
import com.avaje.ebeaninternal.server.persist.BatchedPstmtHolder;
import com.avaje.ebeaninternal.server.persist.dmlbind.BindableRequest;
@@ -56,7 +57,7 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
this.transaction = persistRequest.getTransaction();
this.logLevelSql = transaction.isLogSql();
if (logLevelSql) {
this.bindLog = new StringBuilder();
this.bindLog = new StringBuilder(50);
} else {
this.bindLog = null;
}
@@ -137,7 +138,7 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
protected void logSql(String sql) {
if (logLevelSql) {
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
sql += "; --bind(" + bindLog + ")";
sql = Str.add(sql, "; --bind(", bindLog.toString(), ")");
}
transaction.logSql(sql);
}
@@ -237,8 +238,7 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
/**
* Check with useGeneratedKeys to get appropriate PreparedStatement.
*/
protected PreparedStatement getPstmt(SpiTransaction t, String sql, boolean genKeys)
throws SQLException {
protected PreparedStatement getPstmt(SpiTransaction t, String sql, boolean genKeys) throws SQLException {
Connection conn = t.getInternalConnection();
if (genKeys) {
@@ -4,7 +4,6 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import javax.persistence.OptimisticLockException;
@@ -115,7 +114,7 @@ public class InsertHandler extends DmlHandler {
protected PreparedStatement getPstmt(SpiTransaction t, String sql, boolean useGeneratedKeys) throws SQLException {
Connection conn = t.getInternalConnection();
if (useGeneratedKeys) {
return conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
return conn.prepareStatement(sql, meta.getIdentityDbColumns());
} else {
return conn.prepareStatement(sql);
@@ -8,126 +8,121 @@ import com.avaje.ebeaninternal.api.SpiUpdatePlan;
import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
/**
* Cachable plan for executing bean updates for a given set of changed
* properties.
*
* @author rbygrave
* Plan for executing bean updates for a given set of changed properties.
*/
public class UpdatePlan implements SpiUpdatePlan {
/**
* Special plan used when there is nothing in the set clause and the update
* should in fact be skipped. Occurs when the updated properties have
* updatable=false in their deployment.
* Special plan used when there is nothing in the set clause and the update should in fact be
* skipped. Occurs when the updated properties have updatable=false in their deployment.
*/
public static final UpdatePlan EMPTY_SET_CLAUSE = new UpdatePlan();
private final Integer key;
private final ConcurrencyMode mode;
private final Integer key;
private final String sql;
private final ConcurrencyMode mode;
private final Bindable set;
private final String sql;
private final long timeCreated;
private final Bindable set;
private final boolean emptySetClause;
private Long timeLastUsed;
private final long timeCreated;
/**
* Create a non cachable UpdatePlan.
*/
public UpdatePlan(ConcurrencyMode mode, String sql, Bindable set) {
private final boolean emptySetClause;
this(null, mode, sql, set);
}
private Long timeLastUsed;
/**
* Create a cachable UpdatePlan with a given key.
*/
public UpdatePlan(Integer key, ConcurrencyMode mode, String sql, Bindable set) {
/**
* Create a non cached UpdatePlan.
*/
public UpdatePlan(ConcurrencyMode mode, String sql, Bindable set) {
this.emptySetClause = (sql == null);
this.key = key;
this.mode = mode;
this.sql = sql;
this.set = set;
this.timeCreated = System.currentTimeMillis();
}
this(null, mode, sql, set);
}
/**
* Special constructor for emptySetClause=true instance.
*/
private UpdatePlan(){
this.emptySetClause = true;
this.key = Integer.valueOf(0);
this.mode = ConcurrencyMode.NONE;
this.sql = null;
this.set = null;
this.timeCreated = 0;
}
public boolean isEmptySetClause() {
return emptySetClause;
}
/**
* Create a UpdatePlan with a given key.
*/
public UpdatePlan(Integer key, ConcurrencyMode mode, String sql, Bindable set) {
/**
* Run the prepared statement binding for the 'update set' properties.
*/
public void bindSet(DmlHandler bind, EntityBean bean) throws SQLException {
this.emptySetClause = (sql == null);
this.key = key;
this.mode = mode;
this.sql = sql;
this.set = set;
this.timeCreated = System.currentTimeMillis();
}
set.dmlBind(bind, bean);
/**
* Special constructor for emptySetClause=true instance.
*/
private UpdatePlan() {
this.emptySetClause = true;
this.key = Integer.valueOf(0);
this.mode = ConcurrencyMode.NONE;
this.sql = null;
this.set = null;
this.timeCreated = 0;
}
// not strictly 'thread safe' but object assignment is atomic
Long touched = Long.valueOf(System.currentTimeMillis());
this.timeLastUsed = touched;
}
public boolean isEmptySetClause() {
return emptySetClause;
}
/**
* Return the time this plan was created.
*/
public long getTimeCreated() {
return timeCreated;
}
/**
* Run the prepared statement binding for the 'update set' properties.
*/
public void bindSet(DmlHandler bind, EntityBean bean) throws SQLException {
/**
* Return the time this plan was last used.
*/
public Long getTimeLastUsed() {
set.dmlBind(bind, bean);
// not thread safe but atomic
return timeLastUsed;
}
// not strictly 'thread safe' but object assignment is atomic
Long touched = Long.valueOf(System.currentTimeMillis());
this.timeLastUsed = touched;
}
/**
* Return the hash key.
*/
public Integer getKey() {
return key;
}
/**
* Return the time this plan was created.
*/
public long getTimeCreated() {
return timeCreated;
}
/**
* Return the concurrency mode for this plan.
*/
public ConcurrencyMode getMode() {
return mode;
}
/**
* Return the time this plan was last used.
*/
public Long getTimeLastUsed() {
/**
* Return the DML statement.
*/
public String getSql() {
return sql;
}
// not thread safe but atomic
return timeLastUsed;
}
/**
* Return the Bindable properties for the update set.
*/
public Bindable getSet() {
return set;
}
/**
* Return the hash key.
*/
public Integer getKey() {
return key;
}
/**
* Return the concurrency mode for this plan.
*/
public ConcurrencyMode getMode() {
return mode;
}
/**
* Return the DML statement.
*/
public String getSql() {
return sql;
}
/**
* Return the Bindable properties for the update set.
*/
public Bindable getSet() {
return set;
}
}
@@ -357,6 +357,16 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
if (query.isRawSql()) {
ResultSet suppliedResultSet = query.getRawSql().getResultSet();
if (suppliedResultSet != null) {
// this is a user supplied ResultSet so use that
dataReader = queryPlan.createDataReader(suppliedResultSet);
bindLog = "";
return true;
}
}
if (forwardOnlyHint) {
// Use forward only hints for large resultset processing (Issue 56, MySql specific)
pstmt = conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
@@ -476,15 +486,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
}
}
private boolean hasMoreRows() throws SQLException {
synchronized (this) {
if (cancelled) {
return false;
}
return dataReader.next();
}
}
/**
* Read a row from the result set returning a bean.
* <p>
@@ -533,7 +534,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
private boolean readBeanInternal() throws SQLException {
if (loadedBeanCount >= maxRowsLimit) {
collection.setHasMoreRows(hasMoreRows());
return false;
}
@@ -125,6 +125,10 @@ public class CQueryBuilder implements Constants {
// always set the order by to null for row count query
query.setOrder(null);
if (query.isRawSql()) {
query.setFirstRow(0);
query.setMaxRows(0);
}
ManyWhereJoins manyWhereJoins = query.getManyWhereJoins();
@@ -140,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;
}
@@ -158,7 +162,7 @@ public class CQueryBuilder implements Constants {
SqlTree sqlTree = createSqlTree(request, predicates);
SqlLimitResponse s = buildSql(sqlSelect, request, predicates, sqlTree);
String sql = s.getSql();
if (hasMany) {
if (hasMany || query.isRawSql()) {
sql = "select count(*) from ( " + sql + ")";
if (selectCountWithAlias) {
sql += " as c";
@@ -316,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));
}
@@ -25,7 +25,12 @@ public class CQueryBuilderRawSql implements Constants {
* Build the full SQL Select statement for the request.
*/
public SqlLimitResponse buildSql(OrmQueryRequest<?> request, CQueryPredicates predicates, RawSql.Sql rsql) {
if (rsql == null) {
// this is a ResultSet based RawSql query - just use some placeholder for the SQL
return new SqlLimitResponse("--ResultSetBasedRawSql", false);
}
if (!rsql.isParsed()){
String sql = rsql.getUnparsedSql();
BindParams bindParams = request.getQuery().getBindParams();
@@ -14,6 +14,7 @@ import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.lib.util.Str;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
@@ -53,7 +54,7 @@ public class CQueryEngine {
if (request.isLogSql()) {
String logSql = rcQuery.getGeneratedSql();
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
logSql += "; --bind("+rcQuery.getBindLog()+")";
logSql = Str.add(logSql, "; --bind(", rcQuery.getBindLog(), ")");
}
request.logSql(logSql);
}
@@ -88,7 +89,7 @@ public class CQueryEngine {
if (request.isLogSql()) {
String logSql = rcQuery.getGeneratedSql();
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
logSql += "; --bind("+rcQuery.getBindLog()+")";
logSql= Str.add(logSql, "; --bind(", rcQuery.getBindLog(), ")");
}
request.logSql(logSql);
}
@@ -241,7 +242,7 @@ public class CQueryEngine {
String sql = query.getGeneratedSql();
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
sql += "; --bind("+query.getBindLog()+")";
sql= Str.add(sql, "; --bind(", query.getBindLog(), ")");
}
query.getTransaction().logSql(sql);
}
@@ -1,4 +1,3 @@
package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
@@ -12,6 +11,7 @@ import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.meta.MetaQueryPlanOriginCount;
import com.avaje.ebean.meta.MetaQueryPlanStatistic;
import com.avaje.ebeaninternal.server.util.LongAdder;
import com.avaje.ebeaninternal.server.util.LongMaxUpdater;
/**
* Statistics for a specific query plan that can accumulate.
@@ -26,7 +26,7 @@ public final class CQueryPlanStats {
private final LongAdder totalBeans = new LongAdder();
private final AtomicLong maxTime = new AtomicLong();
private final LongMaxUpdater maxTime = new LongMaxUpdater();
private final AtomicLong startTime = new AtomicLong(System.currentTimeMillis());
@@ -34,19 +34,25 @@ public final class CQueryPlanStats {
private final ConcurrentHashMap<ObjectGraphNode, LongAdder> origins;
/**
* Construct for a given query plan.
*/
public CQueryPlanStats(CQueryPlan queryPlan, boolean collectQueryOrigins) {
this.queryPlan = queryPlan;
this.origins = !collectQueryOrigins ? null : new ConcurrentHashMap<ObjectGraphNode, LongAdder>();
}
/**
* Add a query execution to the statistics.
*/
public void add(long loadedBeanCount, long timeMicros, ObjectGraphNode objectGraphNode) {
count.increment();
totalBeans.add(loadedBeanCount);
totalTime.add(timeMicros);
if (timeMicros > maxTime.get()) {
// effectively a high water mark
maxTime.set(timeMicros);
}
maxTime.update(timeMicros);
// not safe but should be atomic
lastQueryTime = System.currentTimeMillis();
@@ -64,12 +70,16 @@ public final class CQueryPlanStats {
}
}
/**
* Reset the internal statistics counters.
*/
public void reset() {
// Racey but near enough for our purposes as we don't want locks
count.reset();
totalBeans.reset();
totalTime.reset();
maxTime.set(0);
maxTime.reset();
startTime.set(System.currentTimeMillis());
if (origins != null) {
@@ -77,13 +87,18 @@ public final class CQueryPlanStats {
counter.reset();
}
}
}
/**
* Return the last time this query was executed.
*/
public long getLastQueryTime() {
return lastQueryTime;
}
/**
* Return a Snapshot of the query execution statistics potentially resetting the internal counters.
*/
public Snapshot getSnapshot(boolean reset) {
List<MetaQueryPlanOriginCount> origins = getOrigins(reset);
@@ -91,9 +106,9 @@ public final class CQueryPlanStats {
// not guaranteed to be consistent due to time gaps between getting each value out of LongAdders but can live with that
// relative to the cost of making sure count and totalTime etc are all guaranteed to be consistent
if (reset) {
return new Snapshot(queryPlan, count.sumThenReset(), totalTime.sumThenReset(), totalBeans.sumThenReset(), maxTime.getAndSet(0), startTime.getAndSet(System.currentTimeMillis()), lastQueryTime, origins);
return new Snapshot(queryPlan, count.sumThenReset(), totalTime.sumThenReset(), totalBeans.sumThenReset(), maxTime.maxThenReset(), startTime.getAndSet(System.currentTimeMillis()), lastQueryTime, origins);
}
return new Snapshot(queryPlan, count.sum(), totalTime.sum(), totalBeans.sum(), maxTime.get(), startTime.get(), lastQueryTime, origins);
return new Snapshot(queryPlan, count.sum(), totalTime.sum(), totalBeans.sum(), maxTime.max(), startTime.get(), lastQueryTime, origins);
}
/**
@@ -2,8 +2,12 @@ package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebeaninternal.api.BindParams;
import com.avaje.ebeaninternal.api.BindParams.OrderedList;
import com.avaje.ebeaninternal.api.SpiExpressionList;
@@ -17,8 +21,6 @@ import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.util.BindParamsParser;
import com.avaje.ebeaninternal.util.DefaultExpressionRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Compile Query Predicates.
@@ -121,6 +123,8 @@ public class CQueryPredicates {
* Includes from where and order by clauses.
*/
private Set<String> predicateIncludes;
private Set<String> orderByIncludes;
public CQueryPredicates(Binder binder, OrmQueryRequest<?> request) {
this.binder = binder;
@@ -316,16 +320,20 @@ public class CQueryPredicates {
*/
private void parsePropertiesToDbColumns(DeployParser deployParser) {
dbWhere = deriveWhere(deployParser);
dbFilterMany = deriveFilterMany(deployParser);
dbHaving = deriveHaving(deployParser);
// order by is dependent on the manyProperty (if there is one)
logicalOrderBy = deriveOrderByWithMany(request.getManyProperty());
if (logicalOrderBy != null) {
dbOrderBy = deployParser.parse(logicalOrderBy);
}
// create a copy of the includes required to support the orderBy
orderByIncludes = new HashSet<String>(deployParser.getIncludes());
dbWhere = deriveWhere(deployParser);
dbFilterMany = deriveFilterMany(deployParser);
dbHaving = deriveHaving(deployParser);
// all includes including ones for manyWhere clause
predicateIncludes = deployParser.getIncludes();
}
@@ -504,6 +512,13 @@ public class CQueryPredicates {
return predicateIncludes;
}
/**
* Return the orderBy includes.
*/
public Set<String> getOrderByIncludes() {
return orderByIncludes;
}
/**
* The where sql with named bind parameters converted to ?.
*/
@@ -22,6 +22,7 @@ import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.RelationalQueryEngine;
import com.avaje.ebeaninternal.server.core.RelationalQueryRequest;
import com.avaje.ebeaninternal.server.lib.util.Str;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.type.DataBind;
@@ -94,7 +95,7 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
if (request.isLogSql()) {
String logSql = sql;
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
logSql += "; --bind("+bindLog+")";
logSql = Str.add(logSql, "; --bind(", bindLog, ")");
}
t.logSql(logSql);
}
@@ -115,8 +116,6 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
maxRows = query.getMaxRows();
}
boolean hasHitMaxRows = false;
int loadRowCount = 0;
SqlQueryListener listener = query.getListener();
@@ -152,7 +151,6 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
if (loadRowCount == maxRows) {
// break, as we have hit the max rows to fetch...
hasHitMaxRows = true;
break;
}
}
@@ -160,13 +158,6 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
BeanCollection<?> beanColl = wrapper.getBeanCollection();
if (hasHitMaxRows) {
if (rset.next()) {
// there are more rows available after the maxRows limit
beanColl.setHasMoreRows(true);
}
}
if (request.isLogSummary()) {
long exeTime = System.currentTimeMillis() - startTime;
String msg = "SqlQuery rows[" + loadRowCount + "] time[" + exeTime + "] bind[" + bindLog + "]";
@@ -52,7 +52,7 @@ public class LimitOffsetPage<T> implements Page<T>, BeanCollectionTouched {
* Perform fetch ahead when the list is first accessed.
*/
public void notifyTouched(BeanCollection<?> c) {
if (c.hasMoreRows()) {
if (hasNext()) {
owner.fetchAheadIfRequired(pageIndex);
}
}
@@ -65,9 +65,8 @@ public class LimitOffsetPage<T> implements Page<T>, BeanCollectionTouched {
}
}
@SuppressWarnings("unchecked")
public boolean hasNext() {
return ((BeanCollection<T>) getList()).hasMoreRows();
return pageIndex < getTotalPageCount() - 1;
}
public boolean hasPrev() {

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