diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBaseContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBaseContext.java
index 4b54bc78b..f3ab9fea5 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBaseContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBaseContext.java
@@ -1,105 +1,105 @@
-package com.avaje.ebeaninternal.server.loadcontext;
-
-import com.avaje.ebean.FetchConfig;
-import com.avaje.ebean.bean.ObjectGraphNode;
-import com.avaje.ebean.bean.PersistenceContext;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
-
-/**
- * Base class for Bean and BeanCollection loading (lazy loading and query join loading).
- */
-public abstract class DLoadBaseContext {
-
- protected final DLoadContext parent;
-
- protected final BeanDescriptor> desc;
-
- protected final String path;
-
- protected final String fullPath;
-
- protected final OrmQueryProperties queryProps;
-
- protected final boolean hitCache;
-
- protected final String serverName;
-
- protected final int firstBatchSize;
-
- protected final int secondaryBatchSize;
-
- protected final ObjectGraphNode objectGraphNode;
-
- protected final boolean queryFetch;
-
-
- public DLoadBaseContext(DLoadContext parent, BeanDescriptor> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
-
- this.parent = parent;
- this.serverName = parent.getEbeanServer().getName();
- this.desc = desc;
- this.queryProps = queryProps;
- this.path = path;
- this.fullPath = parent.getFullPath(path);
-
- this.hitCache = !parent.isExcludeBeanCache() && desc.isBeanCaching();
-
- this.objectGraphNode = parent.getObjectGraphNode(path);
-
- this.queryFetch = queryProps != null && queryProps.isQueryFetch();
- this.firstBatchSize = initFirstBatchSize(defaultBatchSize, queryProps);
- this.secondaryBatchSize = initSecondaryBatchSize(defaultBatchSize, firstBatchSize, queryProps);
- }
-
- private int initFirstBatchSize(int batchSize, OrmQueryProperties queryProps) {
- if (queryProps == null) {
- return batchSize;
- }
-
- int queryFetchBatch = queryProps.getQueryFetchBatch();
- if (queryFetchBatch > 0) {
- // property join was automatically set to a 'query join'
- return queryFetchBatch;
- }
-
- FetchConfig fetchConfig = queryProps.getFetchConfig();
- if (fetchConfig == null) {
- return batchSize;
- }
-
- int queryBatchSize = fetchConfig.getQueryBatchSize();
- if (queryBatchSize == -1) {
- // not eager query fetch, just lazy loading
- return batchSize;
-
- } else if (queryBatchSize == 0) {
- // default query fetch batch size is 100
- return 100;
-
- } else {
- return queryBatchSize;
- }
- }
-
- private int initSecondaryBatchSize(int defaultBatchSize, int firstBatchSize, OrmQueryProperties queryProps) {
- if (queryProps == null) {
- return defaultBatchSize;
- }
- FetchConfig fetchConfig = queryProps.getFetchConfig();
- if (fetchConfig == null) {
- return defaultBatchSize;
- }
- if (fetchConfig.isQueryAll()) {
- return firstBatchSize;
- }
-
- int lazyBatchSize = fetchConfig.getLazyBatchSize();
- return (lazyBatchSize > 1) ? lazyBatchSize : defaultBatchSize;
- }
-
- protected PersistenceContext getPersistenceContext() {
- return parent.getPersistenceContext();
- }
-
-}
+package com.avaje.ebeaninternal.server.loadcontext;
+
+import com.avaje.ebean.FetchConfig;
+import com.avaje.ebean.bean.ObjectGraphNode;
+import com.avaje.ebean.bean.PersistenceContext;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
+
+/**
+ * Base class for Bean and BeanCollection loading (lazy loading and query join loading).
+ */
+public abstract class DLoadBaseContext {
+
+ protected final DLoadContext parent;
+
+ protected final BeanDescriptor> desc;
+
+ protected final String path;
+
+ protected final String fullPath;
+
+ protected final OrmQueryProperties queryProps;
+
+ protected final boolean hitCache;
+
+ protected final String serverName;
+
+ protected final int firstBatchSize;
+
+ protected final int secondaryBatchSize;
+
+ protected final ObjectGraphNode objectGraphNode;
+
+ protected final boolean queryFetch;
+
+
+ public DLoadBaseContext(DLoadContext parent, BeanDescriptor> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
+
+ this.parent = parent;
+ this.serverName = parent.getEbeanServer().getName();
+ this.desc = desc;
+ this.queryProps = queryProps;
+ this.path = path;
+ this.fullPath = parent.getFullPath(path);
+
+ this.hitCache = !parent.isExcludeBeanCache() && desc.isBeanCaching();
+
+ this.objectGraphNode = parent.getObjectGraphNode(path);
+
+ this.queryFetch = queryProps != null && queryProps.isQueryFetch();
+ this.firstBatchSize = initFirstBatchSize(defaultBatchSize, queryProps);
+ this.secondaryBatchSize = initSecondaryBatchSize(defaultBatchSize, firstBatchSize, queryProps);
+ }
+
+ private int initFirstBatchSize(int batchSize, OrmQueryProperties queryProps) {
+ if (queryProps == null) {
+ return batchSize;
+ }
+
+ int queryFetchBatch = queryProps.getQueryFetchBatch();
+ if (queryFetchBatch > 0) {
+ // property join was automatically set to a 'query join'
+ return queryFetchBatch;
+ }
+
+ FetchConfig fetchConfig = queryProps.getFetchConfig();
+ if (fetchConfig == null) {
+ return batchSize;
+ }
+
+ int queryBatchSize = fetchConfig.getQueryBatchSize();
+ if (queryBatchSize == -1) {
+ // not eager query fetch, just lazy loading
+ return batchSize;
+
+ } else if (queryBatchSize == 0) {
+ // default query fetch batch size is 100
+ return 100;
+
+ } else {
+ return queryBatchSize;
+ }
+ }
+
+ private int initSecondaryBatchSize(int defaultBatchSize, int firstBatchSize, OrmQueryProperties queryProps) {
+ if (queryProps == null) {
+ return defaultBatchSize;
+ }
+ FetchConfig fetchConfig = queryProps.getFetchConfig();
+ if (fetchConfig == null) {
+ return defaultBatchSize;
+ }
+ if (fetchConfig.isQueryAll()) {
+ return firstBatchSize;
+ }
+
+ int lazyBatchSize = fetchConfig.getLazyBatchSize();
+ return (lazyBatchSize > 1) ? lazyBatchSize : defaultBatchSize;
+ }
+
+ protected PersistenceContext getPersistenceContext() {
+ return parent.getPersistenceContext();
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
index fab92203c..dcb611f54 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
@@ -1,208 +1,208 @@
-package com.avaje.ebeaninternal.server.loadcontext;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import com.avaje.ebean.bean.BeanLoader;
-import com.avaje.ebean.bean.EntityBeanIntercept;
-import com.avaje.ebean.bean.PersistenceContext;
-import com.avaje.ebeaninternal.api.LoadBeanBuffer;
-import com.avaje.ebeaninternal.api.LoadBeanContext;
-import com.avaje.ebeaninternal.api.LoadBeanRequest;
-import com.avaje.ebeaninternal.api.SpiQuery;
-import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
-
-/**
- * Default implementation of LoadBeanContext.
- */
-public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext{
-
- private List bufferList;
-
- private LoadBuffer currentBuffer;
-
- public DLoadBeanContext(DLoadContext parent, BeanDescriptor> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
-
- super(parent, desc, path, defaultBatchSize, queryProps);
-
- // bufferList only required when using query joins (queryFetch)
- this.bufferList = (!queryFetch) ? null : new ArrayList();
- this.currentBuffer = createBuffer(firstBatchSize);
- }
-
- /**
- * Reset the buffers after a query iterator reset.
- */
- public void clear() {
- if (bufferList != null) {
- bufferList.clear();
- }
- currentBuffer = createBuffer(secondaryBatchSize);
- }
-
- protected void configureQuery(SpiQuery> query, String lazyLoadProperty) {
-
- // propagate the readOnly state
- if (parent.isReadOnly() != null) {
- query.setReadOnly(parent.isReadOnly());
- }
- query.setParentNode(objectGraphNode);
- query.setLazyLoadProperty(lazyLoadProperty);
-
- if (queryProps != null) {
- queryProps.configureBeanQuery(query);
- }
- if (parent.isUseAutofetchManager()) {
- query.setAutofetch(true);
- }
- }
-
- protected void register(EntityBeanIntercept ebi){
-
- if (currentBuffer.isFull()) {
- currentBuffer = createBuffer(secondaryBatchSize);
- }
- // set the persistenceContext on the bean first
- ebi.setBeanLoader(currentBuffer, getPersistenceContext());
- currentBuffer.add(ebi);
- }
-
- private LoadBuffer createBuffer(int size) {
- LoadBuffer buffer = new LoadBuffer(this, size);
- if (bufferList != null) {
- bufferList.add(buffer);
- }
- return buffer;
- }
-
- public void loadSecondaryQuery(OrmQueryRequest> parentRequest) {
-
- if (!queryFetch) {
- throw new IllegalStateException("Not expecting loadSecondaryQuery() to be called?");
- }
- synchronized (this) {
-
- if (bufferList != null) {
- for (LoadBuffer loadBuffer : bufferList) {
- if (!loadBuffer.list.isEmpty()) {
- boolean loadCache = false;
- LoadBeanRequest req = new LoadBeanRequest(loadBuffer, parentRequest, false, null, loadCache);
-
- parent.getEbeanServer().loadBean(req);
- if (!queryProps.isQueryFetchAll()) {
- // Stop - only fetch the first batch ... the rest will be lazy loaded
- break;
- }
- }
- // this is only run once - secondary query is a one shot deal
- this.bufferList = null;
- }
- }
- }
- }
-
-
- /**
- * A buffer for batch loading beans on a given path.
- */
- public static class LoadBuffer implements BeanLoader, LoadBeanBuffer {
-
- private final DLoadBeanContext context;
- private final int batchSize;
- private final List list;
- private PersistenceContext persistenceContext;
-
- public LoadBuffer(DLoadBeanContext context, int batchSize) {
- this.context = context;
- this.batchSize = batchSize;
- this.list = new ArrayList(batchSize);
- }
-
- public int getBatchSize() {
- return batchSize;
- }
-
- /**
- * Return true if the buffer is full.
- */
- public boolean isFull() {
- return batchSize == list.size();
- }
-
- /**
- * Return true if the buffer is full.
- */
- public void add(EntityBeanIntercept ebi) {
- if (persistenceContext == null) {
- // get persistenceContext from first loaded bean into the buffer
- persistenceContext = ebi.getPersistenceContext();
- }
- list.add(ebi);
- }
-
- @Override
- public List getBatch() {
- return list;
- }
-
- @Override
- public String getName() {
- return context.serverName;
- }
-
- @Override
- public String getFullPath() {
- return context.fullPath;
- }
-
- @Override
- public BeanDescriptor> getBeanDescriptor() {
- return context.desc;
- }
-
- @Override
- public PersistenceContext getPersistenceContext() {
- return persistenceContext;
- }
-
- @Override
- public void configureQuery(SpiQuery> query, String lazyLoadProperty) {
- context.configureQuery(query, lazyLoadProperty);
- }
-
- @Override
- public void loadBean(EntityBeanIntercept ebi) {
- // A synchronized (this) is effectively held by EntityBeanIntercept.loadBean()
-
- if (context.desc.lazyLoadMany(ebi)) {
- // lazy load property was a Many
- return;
- }
-
- if (context.hitCache && context.desc.cacheBeanLoad(ebi)) {
- // successfully hit the L2 cache so don't invoke DB lazy loading
- list.remove(ebi);
- return;
- }
-
- if (context.hitCache) {
- // Check each of the beans in the batch to see if they are in the L2 cache.
- Iterator iterator = list.iterator();
- while (iterator.hasNext()) {
- EntityBeanIntercept bean = iterator.next();
- if (context.desc.cacheBeanLoad(bean)) {
- iterator.remove();
- }
- }
- }
-
- LoadBeanRequest req = new LoadBeanRequest(this, true, ebi.getLazyLoadProperty(), context.hitCache);
- context.desc.getEbeanServer().loadBean(req);
- }
-
- }
-
-}
+package com.avaje.ebeaninternal.server.loadcontext;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.avaje.ebean.bean.BeanLoader;
+import com.avaje.ebean.bean.EntityBeanIntercept;
+import com.avaje.ebean.bean.PersistenceContext;
+import com.avaje.ebeaninternal.api.LoadBeanBuffer;
+import com.avaje.ebeaninternal.api.LoadBeanContext;
+import com.avaje.ebeaninternal.api.LoadBeanRequest;
+import com.avaje.ebeaninternal.api.SpiQuery;
+import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
+
+/**
+ * Default implementation of LoadBeanContext.
+ */
+public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext{
+
+ private List bufferList;
+
+ private LoadBuffer currentBuffer;
+
+ public DLoadBeanContext(DLoadContext parent, BeanDescriptor> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
+
+ super(parent, desc, path, defaultBatchSize, queryProps);
+
+ // bufferList only required when using query joins (queryFetch)
+ this.bufferList = (!queryFetch) ? null : new ArrayList();
+ this.currentBuffer = createBuffer(firstBatchSize);
+ }
+
+ /**
+ * Reset the buffers after a query iterator reset.
+ */
+ public void clear() {
+ if (bufferList != null) {
+ bufferList.clear();
+ }
+ currentBuffer = createBuffer(secondaryBatchSize);
+ }
+
+ protected void configureQuery(SpiQuery> query, String lazyLoadProperty) {
+
+ // propagate the readOnly state
+ if (parent.isReadOnly() != null) {
+ query.setReadOnly(parent.isReadOnly());
+ }
+ query.setParentNode(objectGraphNode);
+ query.setLazyLoadProperty(lazyLoadProperty);
+
+ if (queryProps != null) {
+ queryProps.configureBeanQuery(query);
+ }
+ if (parent.isUseAutofetchManager()) {
+ query.setAutofetch(true);
+ }
+ }
+
+ protected void register(EntityBeanIntercept ebi){
+
+ if (currentBuffer.isFull()) {
+ currentBuffer = createBuffer(secondaryBatchSize);
+ }
+ // set the persistenceContext on the bean first
+ ebi.setBeanLoader(currentBuffer, getPersistenceContext());
+ currentBuffer.add(ebi);
+ }
+
+ private LoadBuffer createBuffer(int size) {
+ LoadBuffer buffer = new LoadBuffer(this, size);
+ if (bufferList != null) {
+ bufferList.add(buffer);
+ }
+ return buffer;
+ }
+
+ public void loadSecondaryQuery(OrmQueryRequest> parentRequest) {
+
+ if (!queryFetch) {
+ throw new IllegalStateException("Not expecting loadSecondaryQuery() to be called?");
+ }
+ synchronized (this) {
+
+ if (bufferList != null) {
+ for (LoadBuffer loadBuffer : bufferList) {
+ if (!loadBuffer.list.isEmpty()) {
+ boolean loadCache = false;
+ LoadBeanRequest req = new LoadBeanRequest(loadBuffer, parentRequest, false, null, loadCache);
+
+ parent.getEbeanServer().loadBean(req);
+ if (!queryProps.isQueryFetchAll()) {
+ // Stop - only fetch the first batch ... the rest will be lazy loaded
+ break;
+ }
+ }
+ // this is only run once - secondary query is a one shot deal
+ this.bufferList = null;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * A buffer for batch loading beans on a given path.
+ */
+ public static class LoadBuffer implements BeanLoader, LoadBeanBuffer {
+
+ private final DLoadBeanContext context;
+ private final int batchSize;
+ private final List list;
+ private PersistenceContext persistenceContext;
+
+ public LoadBuffer(DLoadBeanContext context, int batchSize) {
+ this.context = context;
+ this.batchSize = batchSize;
+ this.list = new ArrayList(batchSize);
+ }
+
+ public int getBatchSize() {
+ return batchSize;
+ }
+
+ /**
+ * Return true if the buffer is full.
+ */
+ public boolean isFull() {
+ return batchSize == list.size();
+ }
+
+ /**
+ * Return true if the buffer is full.
+ */
+ public void add(EntityBeanIntercept ebi) {
+ if (persistenceContext == null) {
+ // get persistenceContext from first loaded bean into the buffer
+ persistenceContext = ebi.getPersistenceContext();
+ }
+ list.add(ebi);
+ }
+
+ @Override
+ public List getBatch() {
+ return list;
+ }
+
+ @Override
+ public String getName() {
+ return context.serverName;
+ }
+
+ @Override
+ public String getFullPath() {
+ return context.fullPath;
+ }
+
+ @Override
+ public BeanDescriptor> getBeanDescriptor() {
+ return context.desc;
+ }
+
+ @Override
+ public PersistenceContext getPersistenceContext() {
+ return persistenceContext;
+ }
+
+ @Override
+ public void configureQuery(SpiQuery> query, String lazyLoadProperty) {
+ context.configureQuery(query, lazyLoadProperty);
+ }
+
+ @Override
+ public void loadBean(EntityBeanIntercept ebi) {
+ // A synchronized (this) is effectively held by EntityBeanIntercept.loadBean()
+
+ if (context.desc.lazyLoadMany(ebi)) {
+ // lazy load property was a Many
+ return;
+ }
+
+ if (context.hitCache && context.desc.cacheBeanLoad(ebi)) {
+ // successfully hit the L2 cache so don't invoke DB lazy loading
+ list.remove(ebi);
+ return;
+ }
+
+ if (context.hitCache) {
+ // Check each of the beans in the batch to see if they are in the L2 cache.
+ Iterator iterator = list.iterator();
+ while (iterator.hasNext()) {
+ EntityBeanIntercept bean = iterator.next();
+ if (context.desc.cacheBeanLoad(bean)) {
+ iterator.remove();
+ }
+ }
+ }
+
+ LoadBeanRequest req = new LoadBeanRequest(this, true, ebi.getLazyLoadProperty(), context.hitCache);
+ context.desc.getEbeanServer().loadBean(req);
+ }
+
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
index 5cc7b23c7..a24ccca51 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
@@ -1,297 +1,297 @@
-package com.avaje.ebeaninternal.server.loadcontext;
-
-import com.avaje.ebean.bean.*;
-import com.avaje.ebeaninternal.api.LoadContext;
-import com.avaje.ebeaninternal.api.LoadSecondaryQuery;
-import com.avaje.ebeaninternal.api.SpiEbeanServer;
-import com.avaje.ebeaninternal.api.SpiQuery;
-import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
-import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
-import com.avaje.ebeaninternal.server.el.ElPropertyValue;
-import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Default implementation of LoadContext.
- */
-public class DLoadContext implements LoadContext {
-
- private final SpiEbeanServer ebeanServer;
-
- private final BeanDescriptor> rootDescriptor;
-
- private final Map beanMap = new HashMap();
- private final Map manyMap = new HashMap();
-
- private final DLoadBeanContext rootBeanContext;
-
- private final Boolean readOnly;
- private final boolean excludeBeanCache;
- private final int defaultBatchSize;
-
- /**
- * The path relative to the root of the object graph.
- */
- private final String relativePath;
- private final ObjectGraphOrigin origin;
- private final boolean useAutofetchManager;
-
- private final Map nodePathMap = new HashMap();
-
- private PersistenceContext persistenceContext;
-
- private List secQuery;
-
- public DLoadContext(OrmQueryRequest> request) {
-
- this.persistenceContext = request.getPersistenceContext();
- this.ebeanServer = request.getServer();
- this.defaultBatchSize = request.getLazyLoadBatchSize();
- this.rootDescriptor = request.getBeanDescriptor();
-
- SpiQuery> query = request.getQuery();
- this.readOnly = query.isReadOnly();
- this.excludeBeanCache = Boolean.FALSE.equals(query.isUseBeanCache());
- this.useAutofetchManager = query.getAutoFetchManager() != null;
-
- ObjectGraphNode parentNode = query.getParentNode();
- if (parentNode != null){
- this.origin = parentNode.getOriginQueryPoint();
- this.relativePath = parentNode.getPath();
- } else {
- this.origin = null;
- this.relativePath = null;
- }
-
- // initialise rootBeanContext after origin and relativePath have been set
- this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
- }
-
- protected boolean isExcludeBeanCache() {
- return excludeBeanCache;
- }
-
- /**
- * Return the minimum batch size when using QueryIterator with query joins.
- */
- public int getSecondaryQueriesMinBatchSize(OrmQueryRequest> parentRequest, int defaultQueryBatch) {
-
- if (secQuery == null) {
- return -1;
- }
-
- int maxBatch = 0;
- for (int i = 0; i < secQuery.size(); i++) {
- int batchSize = secQuery.get(i).getQueryFetchBatch();
- if (batchSize == 0) {
- batchSize = defaultQueryBatch;
- }
- maxBatch = Math.max(maxBatch, batchSize);
- }
- return maxBatch;
- }
-
- /**
- * Execute all the secondary queries.
- */
- public void executeSecondaryQueries(OrmQueryRequest> parentRequest) {
-
- if (secQuery != null){
- for (int i = 0; i < secQuery.size(); i++) {
- OrmQueryProperties properties = secQuery.get(i);
- LoadSecondaryQuery load = getLoadSecondaryQuery(properties.getPath());
- load.loadSecondaryQuery(parentRequest);
- }
- }
- }
-
- /**
- * Return the LoadBeanContext or LoadManyContext for the given path.
- */
- private LoadSecondaryQuery getLoadSecondaryQuery(String path){
- LoadSecondaryQuery beanLoad = beanMap.get(path);
- if (beanLoad == null){
- beanLoad = manyMap.get(path);
- }
- return beanLoad;
- }
-
- /**
- * Remove the +query and +lazy secondary queries and
- * register them with their appropriate LoadBeanContext
- * or LoadManyContext.
- *
- * The parts of the secondary queries are removed and used
- * by LoadBeanContext/LoadManyContext to build the appropriate
- * queries.
- *
- */
- public void registerSecondaryQueries(SpiQuery> query) {
-
- secQuery = query.removeQueryJoins();
- if (secQuery != null){
- for (int i = 0; i < secQuery.size(); i++) {
- OrmQueryProperties props = secQuery.get(i);
- registerSecondaryQuery(props);
- }
- }
-
- List lazyQueries = query.removeLazyJoins();
- if (lazyQueries != null){
- for (int i = 0; i < lazyQueries.size(); i++) {
- OrmQueryProperties lazyProps = lazyQueries.get(i);
- registerSecondaryQuery(lazyProps);
- }
- }
- }
-
- /**
- * Setup the load context at this path with OrmQueryProperties which is
- * used to build the appropriate query for +query or +lazy loading.
- */
- private void registerSecondaryQuery(OrmQueryProperties props) {
-
- String propName = props.getPath();
- ElPropertyValue elGetValue = rootDescriptor.getElGetValue(propName);
-
- boolean many = elGetValue.getBeanProperty().containsMany();
- registerSecondaryNode(many, props);
- }
-
-
- public ObjectGraphNode getObjectGraphNode(String path) {
-
- ObjectGraphNode node = nodePathMap.get(path);
- if (node == null){
- node = createObjectGraphNode(path);
- nodePathMap.put(path, node);
- }
-
- return node;
- }
-
- private ObjectGraphNode createObjectGraphNode(String path) {
-
- if (relativePath != null){
- if (path == null){
- path = relativePath;
- } else {
- path = relativePath+"."+path;
- }
- }
- return new ObjectGraphNode(origin, path);
- }
-
- public boolean isUseAutofetchManager() {
- return useAutofetchManager;
- }
-
- protected String getFullPath(String path) {
- if (relativePath == null) {
- return path;
- } else {
- return relativePath + "." + path;
- }
- }
-
- protected SpiEbeanServer getEbeanServer() {
- return ebeanServer;
- }
-
- /**
- * Return the parent state which defines the sharedInstance and readOnly status
- * which needs to be propagated to other beans and collections.
- */
- protected Boolean isReadOnly() {
- return readOnly;
- }
-
- public PersistenceContext getPersistenceContext() {
- return persistenceContext;
- }
-
- public void resetPersistenceContext(PersistenceContext persistenceContext) {
- this.persistenceContext = persistenceContext;
- // clear the load contexts for beans and beanCollections
- for (DLoadBeanContext beanContext : beanMap.values()) {
- beanContext.clear();
- }
- for (DLoadManyContext manyContext : manyMap.values()) {
- manyContext.clear();
- }
- this.rootBeanContext.clear();
- }
-
- public void register(String path, EntityBeanIntercept ebi){
- getBeanContext(path).register(ebi);
- }
-
- public void register(String path, BeanCollection> bc){
- getManyContext(path).register(bc);
- }
-
- private DLoadBeanContext getBeanContext(String path) {
- if (path == null){
- return rootBeanContext;
- }
- DLoadBeanContext beanContext = beanMap.get(path);
- if (beanContext == null){
- beanContext = createBeanContext(path, defaultBatchSize, null);
- beanMap.put(path, beanContext);
- }
- return beanContext;
- }
-
- private void registerSecondaryNode(boolean many, OrmQueryProperties props) {
-
- String path = props.getPath();
- int lazyJoinBatch = props.getLazyFetchBatch();
- int batchSize = lazyJoinBatch > 0 ? lazyJoinBatch : defaultBatchSize;
-
- if (many){
- DLoadManyContext manyContext = createManyContext(path, batchSize, props);
- manyMap.put(path, manyContext);
- } else {
- DLoadBeanContext beanContext = createBeanContext(path, batchSize, props);
- beanMap.put(path, beanContext);
- }
- }
-
- private DLoadManyContext getManyContext(String path) {
- if (path == null){
- throw new RuntimeException("path is null?");
- }
- DLoadManyContext ctx = manyMap.get(path);
- if (ctx == null){
- ctx = createManyContext(path, defaultBatchSize, null);
- manyMap.put(path, ctx);
- }
- return ctx;
- }
-
- private DLoadManyContext createManyContext(String path, int batchSize, OrmQueryProperties queryProps) {
-
- BeanPropertyAssocMany> p = (BeanPropertyAssocMany>)getBeanProperty(rootDescriptor, path);
-
- return new DLoadManyContext(this, p, path, batchSize, queryProps);
- }
-
- private DLoadBeanContext createBeanContext(String path, int batchSize, OrmQueryProperties queryProps) {
-
- BeanPropertyAssoc> p = (BeanPropertyAssoc>)getBeanProperty(rootDescriptor, path);
- BeanDescriptor> targetDescriptor = p.getTargetDescriptor();
-
- return new DLoadBeanContext(this, targetDescriptor, path, batchSize, queryProps);
- }
-
- private BeanProperty getBeanProperty(BeanDescriptor> desc, String path){
- return desc.getBeanPropertyFromPath(path);
- }
-
-}
+package com.avaje.ebeaninternal.server.loadcontext;
+
+import com.avaje.ebean.bean.*;
+import com.avaje.ebeaninternal.api.LoadContext;
+import com.avaje.ebeaninternal.api.LoadSecondaryQuery;
+import com.avaje.ebeaninternal.api.SpiEbeanServer;
+import com.avaje.ebeaninternal.api.SpiQuery;
+import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
+import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
+import com.avaje.ebeaninternal.server.el.ElPropertyValue;
+import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Default implementation of LoadContext.
+ */
+public class DLoadContext implements LoadContext {
+
+ private final SpiEbeanServer ebeanServer;
+
+ private final BeanDescriptor> rootDescriptor;
+
+ private final Map beanMap = new HashMap();
+ private final Map manyMap = new HashMap();
+
+ private final DLoadBeanContext rootBeanContext;
+
+ private final Boolean readOnly;
+ private final boolean excludeBeanCache;
+ private final int defaultBatchSize;
+
+ /**
+ * The path relative to the root of the object graph.
+ */
+ private final String relativePath;
+ private final ObjectGraphOrigin origin;
+ private final boolean useAutofetchManager;
+
+ private final Map nodePathMap = new HashMap();
+
+ private PersistenceContext persistenceContext;
+
+ private List secQuery;
+
+ public DLoadContext(OrmQueryRequest> request) {
+
+ this.persistenceContext = request.getPersistenceContext();
+ this.ebeanServer = request.getServer();
+ this.defaultBatchSize = request.getLazyLoadBatchSize();
+ this.rootDescriptor = request.getBeanDescriptor();
+
+ SpiQuery> query = request.getQuery();
+ this.readOnly = query.isReadOnly();
+ this.excludeBeanCache = Boolean.FALSE.equals(query.isUseBeanCache());
+ this.useAutofetchManager = query.getAutoFetchManager() != null;
+
+ ObjectGraphNode parentNode = query.getParentNode();
+ if (parentNode != null){
+ this.origin = parentNode.getOriginQueryPoint();
+ this.relativePath = parentNode.getPath();
+ } else {
+ this.origin = null;
+ this.relativePath = null;
+ }
+
+ // initialise rootBeanContext after origin and relativePath have been set
+ this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
+ }
+
+ protected boolean isExcludeBeanCache() {
+ return excludeBeanCache;
+ }
+
+ /**
+ * Return the minimum batch size when using QueryIterator with query joins.
+ */
+ public int getSecondaryQueriesMinBatchSize(OrmQueryRequest> parentRequest, int defaultQueryBatch) {
+
+ if (secQuery == null) {
+ return -1;
+ }
+
+ int maxBatch = 0;
+ for (int i = 0; i < secQuery.size(); i++) {
+ int batchSize = secQuery.get(i).getQueryFetchBatch();
+ if (batchSize == 0) {
+ batchSize = defaultQueryBatch;
+ }
+ maxBatch = Math.max(maxBatch, batchSize);
+ }
+ return maxBatch;
+ }
+
+ /**
+ * Execute all the secondary queries.
+ */
+ public void executeSecondaryQueries(OrmQueryRequest> parentRequest) {
+
+ if (secQuery != null){
+ for (int i = 0; i < secQuery.size(); i++) {
+ OrmQueryProperties properties = secQuery.get(i);
+ LoadSecondaryQuery load = getLoadSecondaryQuery(properties.getPath());
+ load.loadSecondaryQuery(parentRequest);
+ }
+ }
+ }
+
+ /**
+ * Return the LoadBeanContext or LoadManyContext for the given path.
+ */
+ private LoadSecondaryQuery getLoadSecondaryQuery(String path){
+ LoadSecondaryQuery beanLoad = beanMap.get(path);
+ if (beanLoad == null){
+ beanLoad = manyMap.get(path);
+ }
+ return beanLoad;
+ }
+
+ /**
+ * Remove the +query and +lazy secondary queries and
+ * register them with their appropriate LoadBeanContext
+ * or LoadManyContext.
+ *
+ * The parts of the secondary queries are removed and used
+ * by LoadBeanContext/LoadManyContext to build the appropriate
+ * queries.
+ *
+ */
+ public void registerSecondaryQueries(SpiQuery> query) {
+
+ secQuery = query.removeQueryJoins();
+ if (secQuery != null){
+ for (int i = 0; i < secQuery.size(); i++) {
+ OrmQueryProperties props = secQuery.get(i);
+ registerSecondaryQuery(props);
+ }
+ }
+
+ List lazyQueries = query.removeLazyJoins();
+ if (lazyQueries != null){
+ for (int i = 0; i < lazyQueries.size(); i++) {
+ OrmQueryProperties lazyProps = lazyQueries.get(i);
+ registerSecondaryQuery(lazyProps);
+ }
+ }
+ }
+
+ /**
+ * Setup the load context at this path with OrmQueryProperties which is
+ * used to build the appropriate query for +query or +lazy loading.
+ */
+ private void registerSecondaryQuery(OrmQueryProperties props) {
+
+ String propName = props.getPath();
+ ElPropertyValue elGetValue = rootDescriptor.getElGetValue(propName);
+
+ boolean many = elGetValue.getBeanProperty().containsMany();
+ registerSecondaryNode(many, props);
+ }
+
+
+ public ObjectGraphNode getObjectGraphNode(String path) {
+
+ ObjectGraphNode node = nodePathMap.get(path);
+ if (node == null){
+ node = createObjectGraphNode(path);
+ nodePathMap.put(path, node);
+ }
+
+ return node;
+ }
+
+ private ObjectGraphNode createObjectGraphNode(String path) {
+
+ if (relativePath != null){
+ if (path == null){
+ path = relativePath;
+ } else {
+ path = relativePath+"."+path;
+ }
+ }
+ return new ObjectGraphNode(origin, path);
+ }
+
+ public boolean isUseAutofetchManager() {
+ return useAutofetchManager;
+ }
+
+ protected String getFullPath(String path) {
+ if (relativePath == null) {
+ return path;
+ } else {
+ return relativePath + "." + path;
+ }
+ }
+
+ protected SpiEbeanServer getEbeanServer() {
+ return ebeanServer;
+ }
+
+ /**
+ * Return the parent state which defines the sharedInstance and readOnly status
+ * which needs to be propagated to other beans and collections.
+ */
+ protected Boolean isReadOnly() {
+ return readOnly;
+ }
+
+ public PersistenceContext getPersistenceContext() {
+ return persistenceContext;
+ }
+
+ public void resetPersistenceContext(PersistenceContext persistenceContext) {
+ this.persistenceContext = persistenceContext;
+ // clear the load contexts for beans and beanCollections
+ for (DLoadBeanContext beanContext : beanMap.values()) {
+ beanContext.clear();
+ }
+ for (DLoadManyContext manyContext : manyMap.values()) {
+ manyContext.clear();
+ }
+ this.rootBeanContext.clear();
+ }
+
+ public void register(String path, EntityBeanIntercept ebi){
+ getBeanContext(path).register(ebi);
+ }
+
+ public void register(String path, BeanCollection> bc){
+ getManyContext(path).register(bc);
+ }
+
+ private DLoadBeanContext getBeanContext(String path) {
+ if (path == null){
+ return rootBeanContext;
+ }
+ DLoadBeanContext beanContext = beanMap.get(path);
+ if (beanContext == null){
+ beanContext = createBeanContext(path, defaultBatchSize, null);
+ beanMap.put(path, beanContext);
+ }
+ return beanContext;
+ }
+
+ private void registerSecondaryNode(boolean many, OrmQueryProperties props) {
+
+ String path = props.getPath();
+ int lazyJoinBatch = props.getLazyFetchBatch();
+ int batchSize = lazyJoinBatch > 0 ? lazyJoinBatch : defaultBatchSize;
+
+ if (many){
+ DLoadManyContext manyContext = createManyContext(path, batchSize, props);
+ manyMap.put(path, manyContext);
+ } else {
+ DLoadBeanContext beanContext = createBeanContext(path, batchSize, props);
+ beanMap.put(path, beanContext);
+ }
+ }
+
+ private DLoadManyContext getManyContext(String path) {
+ if (path == null){
+ throw new RuntimeException("path is null?");
+ }
+ DLoadManyContext ctx = manyMap.get(path);
+ if (ctx == null){
+ ctx = createManyContext(path, defaultBatchSize, null);
+ manyMap.put(path, ctx);
+ }
+ return ctx;
+ }
+
+ private DLoadManyContext createManyContext(String path, int batchSize, OrmQueryProperties queryProps) {
+
+ BeanPropertyAssocMany> p = (BeanPropertyAssocMany>)getBeanProperty(rootDescriptor, path);
+
+ return new DLoadManyContext(this, p, path, batchSize, queryProps);
+ }
+
+ private DLoadBeanContext createBeanContext(String path, int batchSize, OrmQueryProperties queryProps) {
+
+ BeanPropertyAssoc> p = (BeanPropertyAssoc>)getBeanProperty(rootDescriptor, path);
+ BeanDescriptor> targetDescriptor = p.getTargetDescriptor();
+
+ return new DLoadBeanContext(this, targetDescriptor, path, batchSize, queryProps);
+ }
+
+ private BeanProperty getBeanProperty(BeanDescriptor> desc, String path){
+ return desc.getBeanPropertyFromPath(path);
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
index cbf3da204..004727d5c 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
@@ -1,221 +1,221 @@
-package com.avaje.ebeaninternal.server.loadcontext;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import com.avaje.ebean.bean.BeanCollection;
-import com.avaje.ebean.bean.BeanCollectionLoader;
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebean.bean.ObjectGraphNode;
-import com.avaje.ebean.bean.PersistenceContext;
-import com.avaje.ebeaninternal.api.LoadManyBuffer;
-import com.avaje.ebeaninternal.api.LoadManyContext;
-import com.avaje.ebeaninternal.api.LoadManyRequest;
-import com.avaje.ebeaninternal.api.SpiQuery;
-import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
-import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
-
-public class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
-
- protected final BeanPropertyAssocMany> property;
-
- private List bufferList;
-
- private LoadBuffer currentBuffer;
-
- public DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany> property,
- String path, int defaultBatchSize, OrmQueryProperties queryProps) {
-
- super(parent, property.getBeanDescriptor(), path, defaultBatchSize, queryProps);
-
- this.property = property;
- // bufferList only required when using query joins (queryFetch)
- this.bufferList = (!queryFetch) ? null : new ArrayList();
- this.currentBuffer = createBuffer(firstBatchSize);
- }
-
- private LoadBuffer createBuffer(int size) {
- LoadBuffer buffer = new LoadBuffer(this, size);
- if (bufferList != null) {
- bufferList.add(buffer);
- }
- return buffer;
- }
-
- /**
- * Reset the buffers for a query iterator reset.
- */
- public void clear() {
- if (bufferList != null) {
- bufferList.clear();
- }
- currentBuffer = createBuffer(secondaryBatchSize);
- }
-
- public void configureQuery(SpiQuery> query){
-
- // propagate the readOnly state
- if (parent.isReadOnly() != null){
- query.setReadOnly(parent.isReadOnly());
- }
- query.setParentNode(objectGraphNode);
-
- if (queryProps != null){
- queryProps.configureBeanQuery(query);
- }
-
- if (parent.isUseAutofetchManager()){
- query.setAutofetch(true);
- }
- }
-
- public BeanPropertyAssocMany> getBeanProperty() {
- return property;
- }
-
- public BeanDescriptor> getBeanDescriptor() {
- return desc;
- }
-
-
- public String getName() {
- return parent.getEbeanServer().getName();
- }
-
- public void register(BeanCollection> bc){
-
- if (currentBuffer.isFull()) {
- currentBuffer = createBuffer(secondaryBatchSize);
- }
- currentBuffer.add(bc);
- bc.setLoader(currentBuffer);
- }
-
- public void loadSecondaryQuery(OrmQueryRequest> parentRequest) {
-
- if (!queryFetch) {
- throw new IllegalStateException("Not expecting loadSecondaryQuery() to be called?");
- }
- synchronized (this) {
- if (bufferList != null) {
- for (LoadBuffer loadBuffer : bufferList) {
- if (!loadBuffer.list.isEmpty()) {
- LoadManyRequest req = new LoadManyRequest(loadBuffer, parentRequest, false, false, false);
- parent.getEbeanServer().loadMany(req);
- if (!queryProps.isQueryFetchAll()) {
- // Stop - only fetch the first batch ... the rest will be lazy loaded
- break;
- }
- }
- }
-
- // this is only run once - secondary query is a one shot deal
- this.bufferList = null;
- }
- }
- }
-
- /**
- * A buffer for batch loading bean collections on a given path.
- * Supports batch lazy loading and secondary query loading.
- */
- public static class LoadBuffer implements BeanCollectionLoader, LoadManyBuffer {
-
- private final PersistenceContext persistenceContext;
- private final DLoadManyContext context;
- private final int batchSize;
- private final List> list;
-
- public LoadBuffer(DLoadManyContext context, int batchSize) {
- this.context = context;
- // set the persistence context as at this moment in
- // case it changes as part of a findIterate etc
- this.persistenceContext = context.getPersistenceContext();
- this.batchSize = batchSize;
- this.list = new ArrayList>(batchSize);
- }
-
- public int getBatchSize() {
- return batchSize;
- }
-
- /**
- * Return true if the buffer is full.
- */
- public boolean isFull() {
- return batchSize == list.size();
- }
-
- /**
- * Return true if the buffer is full.
- */
- public void add(BeanCollection> bc) {
- list.add(bc);
- }
-
- @Override
- public List> getBatch() {
- return list;
- }
-
- @Override
- public BeanPropertyAssocMany> getBeanProperty() {
- return context.property;
- }
-
- @Override
- public ObjectGraphNode getObjectGraphNode() {
- return context.objectGraphNode;
- }
-
- @Override
- public void configureQuery(SpiQuery> query){
- context.configureQuery(query);
- }
-
- @Override
- public String getName() {
- return context.serverName;
- }
-
- @Override
- public BeanDescriptor> getBeanDescriptor() {
- return context.desc;
- }
-
- @Override
- public PersistenceContext getPersistenceContext() {
- return persistenceContext;
- }
-
- @Override
- public String getFullPath() {
- return context.fullPath;
- }
-
- public void loadMany(BeanCollection> bc, boolean onlyIds) {
-
- synchronized (this) {
- boolean useCache = context.hitCache && !onlyIds;
- if (useCache) {
- EntityBean ownerBean = bc.getOwnerBean();
- BeanDescriptor> parentDesc = context.desc.getBeanDescriptor(ownerBean.getClass());
- Object parentId = parentDesc.getId(ownerBean);
- if (parentDesc.cacheManyPropLoad(context.property, bc, parentId, context.parent.isReadOnly())) {
- // we loaded the bean from cache
- list.remove(bc);
- return;
- }
- }
-
- // Should reduce the list by checking each beanCollection in the L2 first before executing the query
-
- LoadManyRequest req = new LoadManyRequest(this, true, onlyIds, useCache);
- context.parent.getEbeanServer().loadMany(req);
- }
- }
-
- }
-}
+package com.avaje.ebeaninternal.server.loadcontext;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.avaje.ebean.bean.BeanCollection;
+import com.avaje.ebean.bean.BeanCollectionLoader;
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebean.bean.ObjectGraphNode;
+import com.avaje.ebean.bean.PersistenceContext;
+import com.avaje.ebeaninternal.api.LoadManyBuffer;
+import com.avaje.ebeaninternal.api.LoadManyContext;
+import com.avaje.ebeaninternal.api.LoadManyRequest;
+import com.avaje.ebeaninternal.api.SpiQuery;
+import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
+import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
+
+public class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
+
+ protected final BeanPropertyAssocMany> property;
+
+ private List bufferList;
+
+ private LoadBuffer currentBuffer;
+
+ public DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany> property,
+ String path, int defaultBatchSize, OrmQueryProperties queryProps) {
+
+ super(parent, property.getBeanDescriptor(), path, defaultBatchSize, queryProps);
+
+ this.property = property;
+ // bufferList only required when using query joins (queryFetch)
+ this.bufferList = (!queryFetch) ? null : new ArrayList();
+ this.currentBuffer = createBuffer(firstBatchSize);
+ }
+
+ private LoadBuffer createBuffer(int size) {
+ LoadBuffer buffer = new LoadBuffer(this, size);
+ if (bufferList != null) {
+ bufferList.add(buffer);
+ }
+ return buffer;
+ }
+
+ /**
+ * Reset the buffers for a query iterator reset.
+ */
+ public void clear() {
+ if (bufferList != null) {
+ bufferList.clear();
+ }
+ currentBuffer = createBuffer(secondaryBatchSize);
+ }
+
+ public void configureQuery(SpiQuery> query){
+
+ // propagate the readOnly state
+ if (parent.isReadOnly() != null){
+ query.setReadOnly(parent.isReadOnly());
+ }
+ query.setParentNode(objectGraphNode);
+
+ if (queryProps != null){
+ queryProps.configureBeanQuery(query);
+ }
+
+ if (parent.isUseAutofetchManager()){
+ query.setAutofetch(true);
+ }
+ }
+
+ public BeanPropertyAssocMany> getBeanProperty() {
+ return property;
+ }
+
+ public BeanDescriptor> getBeanDescriptor() {
+ return desc;
+ }
+
+
+ public String getName() {
+ return parent.getEbeanServer().getName();
+ }
+
+ public void register(BeanCollection> bc){
+
+ if (currentBuffer.isFull()) {
+ currentBuffer = createBuffer(secondaryBatchSize);
+ }
+ currentBuffer.add(bc);
+ bc.setLoader(currentBuffer);
+ }
+
+ public void loadSecondaryQuery(OrmQueryRequest> parentRequest) {
+
+ if (!queryFetch) {
+ throw new IllegalStateException("Not expecting loadSecondaryQuery() to be called?");
+ }
+ synchronized (this) {
+ if (bufferList != null) {
+ for (LoadBuffer loadBuffer : bufferList) {
+ if (!loadBuffer.list.isEmpty()) {
+ LoadManyRequest req = new LoadManyRequest(loadBuffer, parentRequest, false, false, false);
+ parent.getEbeanServer().loadMany(req);
+ if (!queryProps.isQueryFetchAll()) {
+ // Stop - only fetch the first batch ... the rest will be lazy loaded
+ break;
+ }
+ }
+ }
+
+ // this is only run once - secondary query is a one shot deal
+ this.bufferList = null;
+ }
+ }
+ }
+
+ /**
+ * A buffer for batch loading bean collections on a given path.
+ * Supports batch lazy loading and secondary query loading.
+ */
+ public static class LoadBuffer implements BeanCollectionLoader, LoadManyBuffer {
+
+ private final PersistenceContext persistenceContext;
+ private final DLoadManyContext context;
+ private final int batchSize;
+ private final List> list;
+
+ public LoadBuffer(DLoadManyContext context, int batchSize) {
+ this.context = context;
+ // set the persistence context as at this moment in
+ // case it changes as part of a findIterate etc
+ this.persistenceContext = context.getPersistenceContext();
+ this.batchSize = batchSize;
+ this.list = new ArrayList>(batchSize);
+ }
+
+ public int getBatchSize() {
+ return batchSize;
+ }
+
+ /**
+ * Return true if the buffer is full.
+ */
+ public boolean isFull() {
+ return batchSize == list.size();
+ }
+
+ /**
+ * Return true if the buffer is full.
+ */
+ public void add(BeanCollection> bc) {
+ list.add(bc);
+ }
+
+ @Override
+ public List> getBatch() {
+ return list;
+ }
+
+ @Override
+ public BeanPropertyAssocMany> getBeanProperty() {
+ return context.property;
+ }
+
+ @Override
+ public ObjectGraphNode getObjectGraphNode() {
+ return context.objectGraphNode;
+ }
+
+ @Override
+ public void configureQuery(SpiQuery> query){
+ context.configureQuery(query);
+ }
+
+ @Override
+ public String getName() {
+ return context.serverName;
+ }
+
+ @Override
+ public BeanDescriptor> getBeanDescriptor() {
+ return context.desc;
+ }
+
+ @Override
+ public PersistenceContext getPersistenceContext() {
+ return persistenceContext;
+ }
+
+ @Override
+ public String getFullPath() {
+ return context.fullPath;
+ }
+
+ public void loadMany(BeanCollection> bc, boolean onlyIds) {
+
+ synchronized (this) {
+ boolean useCache = context.hitCache && !onlyIds;
+ if (useCache) {
+ EntityBean ownerBean = bc.getOwnerBean();
+ BeanDescriptor> parentDesc = context.desc.getBeanDescriptor(ownerBean.getClass());
+ Object parentId = parentDesc.getId(ownerBean);
+ if (parentDesc.cacheManyPropLoad(context.property, bc, parentId, context.parent.isReadOnly())) {
+ // we loaded the bean from cache
+ list.remove(bc);
+ return;
+ }
+ }
+
+ // Should reduce the list by checking each beanCollection in the L2 first before executing the query
+
+ LoadManyRequest req = new LoadManyRequest(this, true, onlyIds, useCache);
+ context.parent.getEbeanServer().loadMany(req);
+ }
+ }
+
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java
index 3bc6b1b3b..c76277461 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java
@@ -1,303 +1,303 @@
-package com.avaje.ebeaninternal.server.persist;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-
-import javax.persistence.PersistenceException;
-
-import com.avaje.ebeaninternal.api.SpiTransaction;
-import com.avaje.ebeaninternal.server.core.PersistRequest;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-
-/**
- * Controls the batch ordering of persist requests.
- *
- * Persist requests include bean inserts updates deletes and UpdateSql and
- * CallableSql requests.
- *
- *
- * This object queues up the requests into appropriate entries according to the
- * 'depth' and the 'type' of the requests. The depth relates to how saves and
- * deletes cascade following the associations of a bean. For saving Associated
- * One cascades reduce the depth (-1) and associated many's increase the depth.
- * The initial depth of a request is 0.
- *
- */
-public final class BatchControl {
-
- /**
- * Used to sort queue entries by depth.
- */
- private static final BatchDepthComparator depthComparator = new BatchDepthComparator();
-
- /**
- * Controls batching of the PreparedStatements. This should be flushed after
- * each 'depth'.
- */
- private final BatchedPstmtHolder pstmtHolder = new BatchedPstmtHolder();
-
- /**
- * Map of the BatchedBeanHolder objects. They each have a depth and are later
- * sorted by their depth to get the execution order.
- */
- private final HashMap beanHoldMap = new HashMap();
-
- private final SpiTransaction transaction;
-
- /**
- * The size at which the batch queue will flush. This should be close to the
- * number of statements that are batched into a single PreparedStatement. This
- * size relates to the size of a list in a BatchQueueEntry and not the total
- * number of request which could be more than that.
- */
- private int batchSize;
-
- /**
- * If true try to get generated keys from inserts.
- */
- private boolean getGeneratedKeys;
-
- private boolean batchFlushOnMixed = true;
-
- /**
- * Create for a given transaction, PersistExecute, default size and getGeneratedKeys.
- */
- public BatchControl(SpiTransaction t, int batchSize, boolean getGenKeys) {
- this.transaction = t;
- this.batchSize = batchSize;
- this.getGeneratedKeys = getGenKeys;
- transaction.setBatchControl(this);
- }
-
- /**
- * Set this flag to false to allow batching of a mix of Beans and UpdateSql
- * (or CallableSql). Normally if you mix the two this will result in an
- * automatic flush.
- *
- * Note that UpdateSql and CallableSql will ALWAYS flush first. This is due to
- * it already having been bound to a PreparedStatement where as the Beans go
- * through a 2 step process when they are flushed (delayed binding).
- *
- */
- public void setBatchFlushOnMixed(boolean flushBatchOnMixed) {
- this.batchFlushOnMixed = flushBatchOnMixed;
- }
-
- /**
- * Return the batchSize.
- */
- public int getBatchSize() {
- return batchSize;
- }
-
- /**
- * Set the size of batch execution.
- *
- * The user can set this via the Transaction.
- *
- */
- public void setBatchSize(int batchSize) {
- if (batchSize > 1) {
- this.batchSize = batchSize;
- }
- }
-
- /**
- * Set whether or not to use getGeneratedKeys for this batch execution.
- *
- * The user can set this via the transaction
- *
- */
- public void setGetGeneratedKeys(Boolean getGeneratedKeys) {
- if (getGeneratedKeys != null) {
- this.getGeneratedKeys = getGeneratedKeys;
- }
- }
-
- /**
- * Execute a Orm Update, SqlUpdate or CallableSql.
- *
- * These all go straight to jdbc and use addBatch(). Entity beans goto a queue
- * and wait there so that the jdbc is executed in the correct order according
- * to the depth.
- *
- */
- public int executeStatementOrBatch(PersistRequest request, boolean batch) {
- if (!batch || (batchFlushOnMixed && !isBeansEmpty())) {
- // flush when mixing beans and updateSql
- flush();
- }
- if (!batch) {
- // execute the request immediately without batching
- return request.executeNow();
- }
-
- if (pstmtHolder.getMaxSize() >= batchSize) {
- flush();
- }
- // for OrmUpdate, SqlUpdate, CallableSql there is no queue...
- // so straight to jdbc prepared statement and use addBatch().
- // aka executeNow() may use addBatch().
- request.executeNow();
- return -1;
- }
-
- /**
- * Entity Bean insert, update or delete. This will either execute the request
- * immediately or queue it for batch processing later. The queue is flushed
- * according to the depth (object graph depth).
- */
- public int executeOrQueue(PersistRequestBean> request, boolean batch) {
-
- if (!batch || (batchFlushOnMixed && !pstmtHolder.isEmpty())) {
- // flush when mixing beans and updateSql
- flush();
- }
- if (!batch) {
- return request.executeNow();
- }
- if (addToBatch(request)) {
- // flush as the top level has hit the batch size
- flush();
- }
- return -1;
- }
-
- /**
- * Add the request to the batch and return true if we should flush.
- */
- private boolean addToBatch(PersistRequestBean> request) {
-
- BatchedBeanHolder beanHolder = getBeanHolder(request);
- int bufferSize = beanHolder.append(request);
-
- // return true if top level has hit batch size
- return bufferSize == batchSize && beanHolder.getOrder() == 100;
- }
-
- /**
- * Return the actual batch of PreparedStatements.
- */
- public BatchedPstmtHolder getPstmtHolder() {
- return pstmtHolder;
- }
-
- /**
- * Return true if the queue is empty.
- */
- public boolean isEmpty() {
- return (isBeansEmpty() && pstmtHolder.isEmpty());
- }
-
- /**
- * Flush any batched PreparedStatements.
- */
- protected void flushPstmtHolder() {
- pstmtHolder.flush(getGeneratedKeys);
- }
-
- /**
- * Execute all the requests contained in the list.
- */
- protected void executeNow(ArrayList list) {
- for (int i = 0; i < list.size(); i++) {
- if (i % batchSize == 0) {
- // hit the batch size so flush
- flushPstmtHolder();
- }
- list.get(i).executeNow();
- }
- flushPstmtHolder();
- }
-
- /**
- * Flush without resetting the topOrder (maintains the depth info).
- */
- public void flush() throws PersistenceException {
- flush(false);
- }
-
- /**
- * Flush with a reset the topOrder (fully empty the batch).
- */
- public void flushReset() throws PersistenceException {
- flush(true);
- }
-
- /**
- * execute all the requests currently queued or batched.
- */
- private void flush(boolean resetTop) throws PersistenceException {
-
- if (!pstmtHolder.isEmpty()) {
- // Flush existing pstmts (updateSql or callableSql)
- flushPstmtHolder();
- }
- if (isEmpty()) {
- // Nothing in queue to flush
- return;
- }
-
- // convert entry map to array for sorting
- BatchedBeanHolder[] bsArray = getBeanHolderArray();
- // sort the entries by depth
- Arrays.sort(bsArray, depthComparator);
-
- if (transaction.isLogSummary()) {
- transaction.logSummary("BatchControl flush " + Arrays.toString(bsArray));
- }
- for (int i = 0; i < bsArray.length; i++) {
- bsArray[i].executeNow();
- }
-
- if (resetTop) {
- beanHoldMap.clear();
- }
- }
-
- /**
- * Return an entry for the given type description. The type description is
- * typically the bean class name (or table name for MapBeans).
- */
- private BatchedBeanHolder getBeanHolder(PersistRequestBean> request) {
-
- BeanDescriptor> beanDescriptor = request.getBeanDescriptor();
- BatchedBeanHolder batchBeanHolder = beanHoldMap.get(beanDescriptor.getFullName());
- if (batchBeanHolder == null) {
- int relativeDepth = transaction.depth();
- if (relativeDepth == 0 && !beanHoldMap.isEmpty()) {
- // flush and reset the batch as we are changing the type of our top level
- // bean so just keep it simple and flush and reset the top
- flushReset();
- }
-
- batchBeanHolder = new BatchedBeanHolder(this, beanDescriptor, 100 + relativeDepth);
- beanHoldMap.put(beanDescriptor.getFullName(), batchBeanHolder);
- }
- return batchBeanHolder;
- }
-
- /**
- * Return true if this holds no persist requests.
- */
- private boolean isBeansEmpty() {
- if (beanHoldMap.isEmpty()) {
- return true;
- }
- for (BatchedBeanHolder beanHolder : beanHoldMap.values()) {
- if (!beanHolder.isEmpty()) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Return the BatchedBeanHolder's ready for sorting and executing.
- */
- private BatchedBeanHolder[] getBeanHolderArray() {
- return beanHoldMap.values().toArray(new BatchedBeanHolder[beanHoldMap.size()]);
- }
-}
+package com.avaje.ebeaninternal.server.persist;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import javax.persistence.PersistenceException;
+
+import com.avaje.ebeaninternal.api.SpiTransaction;
+import com.avaje.ebeaninternal.server.core.PersistRequest;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+
+/**
+ * Controls the batch ordering of persist requests.
+ *
+ * Persist requests include bean inserts updates deletes and UpdateSql and
+ * CallableSql requests.
+ *
+ *
+ * This object queues up the requests into appropriate entries according to the
+ * 'depth' and the 'type' of the requests. The depth relates to how saves and
+ * deletes cascade following the associations of a bean. For saving Associated
+ * One cascades reduce the depth (-1) and associated many's increase the depth.
+ * The initial depth of a request is 0.
+ *
+ */
+public final class BatchControl {
+
+ /**
+ * Used to sort queue entries by depth.
+ */
+ private static final BatchDepthComparator depthComparator = new BatchDepthComparator();
+
+ /**
+ * Controls batching of the PreparedStatements. This should be flushed after
+ * each 'depth'.
+ */
+ private final BatchedPstmtHolder pstmtHolder = new BatchedPstmtHolder();
+
+ /**
+ * Map of the BatchedBeanHolder objects. They each have a depth and are later
+ * sorted by their depth to get the execution order.
+ */
+ private final HashMap beanHoldMap = new HashMap();
+
+ private final SpiTransaction transaction;
+
+ /**
+ * The size at which the batch queue will flush. This should be close to the
+ * number of statements that are batched into a single PreparedStatement. This
+ * size relates to the size of a list in a BatchQueueEntry and not the total
+ * number of request which could be more than that.
+ */
+ private int batchSize;
+
+ /**
+ * If true try to get generated keys from inserts.
+ */
+ private boolean getGeneratedKeys;
+
+ private boolean batchFlushOnMixed = true;
+
+ /**
+ * Create for a given transaction, PersistExecute, default size and getGeneratedKeys.
+ */
+ public BatchControl(SpiTransaction t, int batchSize, boolean getGenKeys) {
+ this.transaction = t;
+ this.batchSize = batchSize;
+ this.getGeneratedKeys = getGenKeys;
+ transaction.setBatchControl(this);
+ }
+
+ /**
+ * Set this flag to false to allow batching of a mix of Beans and UpdateSql
+ * (or CallableSql). Normally if you mix the two this will result in an
+ * automatic flush.
+ *
+ * Note that UpdateSql and CallableSql will ALWAYS flush first. This is due to
+ * it already having been bound to a PreparedStatement where as the Beans go
+ * through a 2 step process when they are flushed (delayed binding).
+ *
+ */
+ public void setBatchFlushOnMixed(boolean flushBatchOnMixed) {
+ this.batchFlushOnMixed = flushBatchOnMixed;
+ }
+
+ /**
+ * Return the batchSize.
+ */
+ public int getBatchSize() {
+ return batchSize;
+ }
+
+ /**
+ * Set the size of batch execution.
+ *
+ * The user can set this via the Transaction.
+ *
+ */
+ public void setBatchSize(int batchSize) {
+ if (batchSize > 1) {
+ this.batchSize = batchSize;
+ }
+ }
+
+ /**
+ * Set whether or not to use getGeneratedKeys for this batch execution.
+ *
+ * The user can set this via the transaction
+ *
+ */
+ public void setGetGeneratedKeys(Boolean getGeneratedKeys) {
+ if (getGeneratedKeys != null) {
+ this.getGeneratedKeys = getGeneratedKeys;
+ }
+ }
+
+ /**
+ * Execute a Orm Update, SqlUpdate or CallableSql.
+ *
+ * These all go straight to jdbc and use addBatch(). Entity beans goto a queue
+ * and wait there so that the jdbc is executed in the correct order according
+ * to the depth.
+ *
+ */
+ public int executeStatementOrBatch(PersistRequest request, boolean batch) {
+ if (!batch || (batchFlushOnMixed && !isBeansEmpty())) {
+ // flush when mixing beans and updateSql
+ flush();
+ }
+ if (!batch) {
+ // execute the request immediately without batching
+ return request.executeNow();
+ }
+
+ if (pstmtHolder.getMaxSize() >= batchSize) {
+ flush();
+ }
+ // for OrmUpdate, SqlUpdate, CallableSql there is no queue...
+ // so straight to jdbc prepared statement and use addBatch().
+ // aka executeNow() may use addBatch().
+ request.executeNow();
+ return -1;
+ }
+
+ /**
+ * Entity Bean insert, update or delete. This will either execute the request
+ * immediately or queue it for batch processing later. The queue is flushed
+ * according to the depth (object graph depth).
+ */
+ public int executeOrQueue(PersistRequestBean> request, boolean batch) {
+
+ if (!batch || (batchFlushOnMixed && !pstmtHolder.isEmpty())) {
+ // flush when mixing beans and updateSql
+ flush();
+ }
+ if (!batch) {
+ return request.executeNow();
+ }
+ if (addToBatch(request)) {
+ // flush as the top level has hit the batch size
+ flush();
+ }
+ return -1;
+ }
+
+ /**
+ * Add the request to the batch and return true if we should flush.
+ */
+ private boolean addToBatch(PersistRequestBean> request) {
+
+ BatchedBeanHolder beanHolder = getBeanHolder(request);
+ int bufferSize = beanHolder.append(request);
+
+ // return true if top level has hit batch size
+ return bufferSize == batchSize && beanHolder.getOrder() == 100;
+ }
+
+ /**
+ * Return the actual batch of PreparedStatements.
+ */
+ public BatchedPstmtHolder getPstmtHolder() {
+ return pstmtHolder;
+ }
+
+ /**
+ * Return true if the queue is empty.
+ */
+ public boolean isEmpty() {
+ return (isBeansEmpty() && pstmtHolder.isEmpty());
+ }
+
+ /**
+ * Flush any batched PreparedStatements.
+ */
+ protected void flushPstmtHolder() {
+ pstmtHolder.flush(getGeneratedKeys);
+ }
+
+ /**
+ * Execute all the requests contained in the list.
+ */
+ protected void executeNow(ArrayList list) {
+ for (int i = 0; i < list.size(); i++) {
+ if (i % batchSize == 0) {
+ // hit the batch size so flush
+ flushPstmtHolder();
+ }
+ list.get(i).executeNow();
+ }
+ flushPstmtHolder();
+ }
+
+ /**
+ * Flush without resetting the topOrder (maintains the depth info).
+ */
+ public void flush() throws PersistenceException {
+ flush(false);
+ }
+
+ /**
+ * Flush with a reset the topOrder (fully empty the batch).
+ */
+ public void flushReset() throws PersistenceException {
+ flush(true);
+ }
+
+ /**
+ * execute all the requests currently queued or batched.
+ */
+ private void flush(boolean resetTop) throws PersistenceException {
+
+ if (!pstmtHolder.isEmpty()) {
+ // Flush existing pstmts (updateSql or callableSql)
+ flushPstmtHolder();
+ }
+ if (isEmpty()) {
+ // Nothing in queue to flush
+ return;
+ }
+
+ // convert entry map to array for sorting
+ BatchedBeanHolder[] bsArray = getBeanHolderArray();
+ // sort the entries by depth
+ Arrays.sort(bsArray, depthComparator);
+
+ if (transaction.isLogSummary()) {
+ transaction.logSummary("BatchControl flush " + Arrays.toString(bsArray));
+ }
+ for (int i = 0; i < bsArray.length; i++) {
+ bsArray[i].executeNow();
+ }
+
+ if (resetTop) {
+ beanHoldMap.clear();
+ }
+ }
+
+ /**
+ * Return an entry for the given type description. The type description is
+ * typically the bean class name (or table name for MapBeans).
+ */
+ private BatchedBeanHolder getBeanHolder(PersistRequestBean> request) {
+
+ BeanDescriptor> beanDescriptor = request.getBeanDescriptor();
+ BatchedBeanHolder batchBeanHolder = beanHoldMap.get(beanDescriptor.getFullName());
+ if (batchBeanHolder == null) {
+ int relativeDepth = transaction.depth();
+ if (relativeDepth == 0 && !beanHoldMap.isEmpty()) {
+ // flush and reset the batch as we are changing the type of our top level
+ // bean so just keep it simple and flush and reset the top
+ flushReset();
+ }
+
+ batchBeanHolder = new BatchedBeanHolder(this, beanDescriptor, 100 + relativeDepth);
+ beanHoldMap.put(beanDescriptor.getFullName(), batchBeanHolder);
+ }
+ return batchBeanHolder;
+ }
+
+ /**
+ * Return true if this holds no persist requests.
+ */
+ private boolean isBeansEmpty() {
+ if (beanHoldMap.isEmpty()) {
+ return true;
+ }
+ for (BatchedBeanHolder beanHolder : beanHoldMap.values()) {
+ if (!beanHolder.isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return the BatchedBeanHolder's ready for sorting and executing.
+ */
+ private BatchedBeanHolder[] getBeanHolderArray() {
+ return beanHoldMap.values().toArray(new BatchedBeanHolder[beanHoldMap.size()]);
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchDepthComparator.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchDepthComparator.java
index d5414b4e8..ad6dba553 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchDepthComparator.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchDepthComparator.java
@@ -1,30 +1,30 @@
-package com.avaje.ebeaninternal.server.persist;
-
-import java.io.Serializable;
-import java.util.Comparator;
-
-/**
- * Used to sort BatchedBeanHolder by their depth.
- *
- * Beans are queued and put into BatchedBeanHolder along with their depth. This
- * delays the actually binding to PreparedStatements until the
- * BatchedBeanHolder's are flushed. This is so that we can get the generated
- * keys from inserts. These values are required to persist the 'detail' beans.
- *
- */
-public class BatchDepthComparator implements Comparator, Serializable {
-
- private static final long serialVersionUID = 264611821665757991L;
-
- public int compare(BatchedBeanHolder b1, BatchedBeanHolder b2) {
-
- if (b1.getOrder() < b2.getOrder()) {
- return -1;
- }
- if (b1.getOrder() == b2.getOrder()) {
- return 0;
- }
- return 1;
- }
-
-}
+package com.avaje.ebeaninternal.server.persist;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * Used to sort BatchedBeanHolder by their depth.
+ *
+ * Beans are queued and put into BatchedBeanHolder along with their depth. This
+ * delays the actually binding to PreparedStatements until the
+ * BatchedBeanHolder's are flushed. This is so that we can get the generated
+ * keys from inserts. These values are required to persist the 'detail' beans.
+ *
+ */
+public class BatchDepthComparator implements Comparator, Serializable {
+
+ private static final long serialVersionUID = 264611821665757991L;
+
+ public int compare(BatchedBeanHolder b1, BatchedBeanHolder b2) {
+
+ if (b1.getOrder() < b2.getOrder()) {
+ return -1;
+ }
+ if (b1.getOrder() == b2.getOrder()) {
+ return 0;
+ }
+ return 1;
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchPostExecute.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchPostExecute.java
index bd1afdb26..82acbadef 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchPostExecute.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchPostExecute.java
@@ -1,36 +1,36 @@
-package com.avaje.ebeaninternal.server.persist;
-
-import java.sql.SQLException;
-
-/**
- * Handles the processing required after batch execution.
- *
- * This includes concurrency checking, generated keys on inserts, transaction
- * logging, transaction event table modifcation and for beans resetting their
- * 'loaded' status.
- *
- */
-public interface BatchPostExecute {
-
-
- /**
- * Check that the rowCount is correct for this execute. This is for
- * performing concurrency checking in batch execution.
- */
- public void checkRowCount(int rowCount) throws SQLException;
-
- /**
- * For inserts with generated keys. Otherwise not used.
- */
- public void setGeneratedKey(Object idValue);
-
- /**
- * Execute the post execute processing.
- *
- * This includes transaction logging, transaction event table modification
- * and for beans resetting their 'loaded' status.
- *
- */
- public void postExecute() throws SQLException;
-
-}
+package com.avaje.ebeaninternal.server.persist;
+
+import java.sql.SQLException;
+
+/**
+ * Handles the processing required after batch execution.
+ *
+ * This includes concurrency checking, generated keys on inserts, transaction
+ * logging, transaction event table modifcation and for beans resetting their
+ * 'loaded' status.
+ *
+ */
+public interface BatchPostExecute {
+
+
+ /**
+ * Check that the rowCount is correct for this execute. This is for
+ * performing concurrency checking in batch execution.
+ */
+ public void checkRowCount(int rowCount) throws SQLException;
+
+ /**
+ * For inserts with generated keys. Otherwise not used.
+ */
+ public void setGeneratedKey(Object idValue);
+
+ /**
+ * Execute the post execute processing.
+ *
+ * This includes transaction logging, transaction event table modification
+ * and for beans resetting their 'loaded' status.
+ *
+ */
+ public void postExecute() throws SQLException;
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedBeanHolder.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedBeanHolder.java
index 28b5e21a9..88a60f359 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedBeanHolder.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedBeanHolder.java
@@ -1,166 +1,166 @@
-package com.avaje.ebeaninternal.server.persist;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
-
-import com.avaje.ebeaninternal.server.core.PersistRequest;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-
-/**
- * Holds lists of persist requests for beans of a given type.
- *
- * This is used to delay the actual binding of the bean to PreparedStatements.
- * The reason is that we don't have all the bind values yet in the case of inserts
- * with getGeneratedKeys.
- *
- *
- * Has a depth which is used to determine the order in which it should be
- * executed. The lowest depth is executed first.
- *
- */
-public class BatchedBeanHolder {
-
- private static final Object DUMMY = new Object();
-
- /**
- * The owning queue.
- */
- private final BatchControl control;
-
- private final String shortDesc;
-
- /**
- * The 'depth' which is used to determine the execution order.
- */
- private final int order;
-
- /**
- * The list of bean insert requests.
- */
- private ArrayList inserts;
-
- /**
- * The list of bean update requests.
- */
- private ArrayList updates;
-
- /**
- * The list of bean delete requests.
- */
- private ArrayList deletes;
-
- /**
- * Set of beans in this batch. This is used to ensure that a single bean instance is not included
- * in the batch twice (two separate insert requests etc).
- */
- private IdentityHashMap
- *
- * @see com.avaje.ebeaninternal.server.persist.DefaultPersistExecute
- */
-public final class DefaultPersister implements Persister {
-
- private static final Logger logger = LoggerFactory.getLogger(DefaultPersister.class);
-
- /**
- * Actually does the persisting work.
- */
- private final PersistExecute persistExecute;
-
- private final SpiEbeanServer server;
-
- private final BeanDescriptorManager beanDescriptorManager;
-
- private final boolean updatesDeleteMissingChildren;
-
- public DefaultPersister(SpiEbeanServer server, Binder binder, BeanDescriptorManager descMgr, PstmtBatch pstmtBatch) {
-
- this.server = server;
- this.updatesDeleteMissingChildren = server.getServerConfig().isUpdatesDeleteMissingChildren();
- this.beanDescriptorManager = descMgr;
- this.persistExecute = new DefaultPersistExecute(binder, pstmtBatch, server.getServerConfig().getPersistBatchSize());
- }
-
- /**
- * Execute the CallableSql.
- */
- public int executeCallable(CallableSql callSql, Transaction t) {
-
- PersistRequestCallableSql request = new PersistRequestCallableSql(server, callSql, (SpiTransaction) t, persistExecute);
- try {
- request.initTransIfRequired();
- int rc = request.executeOrQueue();
- request.commitTransIfRequired();
- return rc;
-
- } catch (RuntimeException e) {
- request.rollbackTransIfRequired();
- throw e;
- }
- }
-
- /**
- * Execute the orm update.
- */
- public int executeOrmUpdate(Update> update, Transaction t) {
-
- SpiUpdate> ormUpdate = (SpiUpdate>) update;
-
- BeanManager> mgr = beanDescriptorManager.getBeanManager(ormUpdate.getBeanType());
-
- if (mgr == null) {
- String msg = "No BeanManager found for type [" + ormUpdate.getBeanType() + "]. Is it an entity?";
- throw new PersistenceException(msg);
- }
-
- PersistRequestOrmUpdate request = new PersistRequestOrmUpdate(server, mgr, ormUpdate, (SpiTransaction) t, persistExecute);
- try {
- request.initTransIfRequired();
- int rc = request.executeOrQueue();
- request.commitTransIfRequired();
- return rc;
-
- } catch (RuntimeException e) {
- request.rollbackTransIfRequired();
- throw e;
- }
- }
-
- /**
- * Execute the updateSql.
- */
- public int executeSqlUpdate(SqlUpdate updSql, Transaction t) {
-
- PersistRequestUpdateSql request = new PersistRequestUpdateSql(server, updSql, (SpiTransaction) t, persistExecute);
- try {
- request.initTransIfRequired();
- int rc = request.executeOrQueue();
- request.commitTransIfRequired();
- return rc;
-
- } catch (RuntimeException e) {
- request.rollbackTransIfRequired();
- throw e;
- }
- }
-
- /**
- * Recursively delete the bean. This calls back to the EbeanServer.
- */
- private void deleteRecurse(Object detailBean, Transaction t) {
- // NB: a new PersistRequest is made
- server.delete(detailBean, t);
- }
-
- /**
- * Update the bean.
- */
- public void update(EntityBean entityBean, Transaction t) {
- update(entityBean, t, updatesDeleteMissingChildren);
- }
-
- /**
- * Update the bean specifying deleteMissingChildren.
- */
- public void update(EntityBean entityBean, Transaction t, boolean deleteMissingChildren) {
-
- PersistRequestBean> req = createRequest(entityBean, t, null, PersistRequest.Type.UPDATE);
- req.setDeleteMissingChildren(deleteMissingChildren);
- try {
- req.initTransIfRequiredWithBatchCascade();
- if (req.isReference()) {
- // its a reference so see if there are manys to save...
- if (req.isPersistCascade()) {
- saveAssocMany(false, req, false);
- }
- req.checkUpdatedManysOnly();
- } else {
- update(req);
- }
-
- req.commitTransIfRequired();
- req.flushBatchOnCascade();
-
- } catch (RuntimeException ex) {
- req.rollbackTransIfRequired();
- throw ex;
- }
- }
-
- /**
- * Insert or update the bean.
- */
- public void save(EntityBean bean, Transaction t) {
- if (bean._ebean_getIntercept().isLoaded()) {
- // deleteMissingChildren is false when using 'save' on 'loaded' beans
- update(bean, t, false);
- } else {
- insert(bean, t);
- }
- }
-
- /**
- * Insert this bean.
- */
- public void insert(EntityBean bean, Transaction t) {
-
- PersistRequestBean> req = createRequest(bean, t, null, PersistRequest.Type.INSERT);
- try {
- req.initTransIfRequiredWithBatchCascade();
- insert(req);
- req.commitTransIfRequired();
- req.flushBatchOnCascade();
-
- } catch (RuntimeException ex) {
- req.rollbackTransIfRequired();
- throw ex;
- }
- }
-
- 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, insertMode);
-
- if (request.isReference()) {
- // its a reference...
- if (request.isPersistCascade()) {
- // save any associated List held beans
- saveAssocMany(false, request, insertMode);
- }
- request.checkUpdatedManysOnly();
-
- } else {
- if (request.isInsert()) {
- insert(request);
- } else {
- update(request);
- }
- }
- }
-
- /**
- * Insert the bean.
- */
- private void insert(PersistRequestBean> request) {
-
- if (request.isRegisteredBean()){
- // skip as already inserted/updated in this request (recursive cascading)
- return;
- }
-
- try {
- if (request.isPersistCascade()) {
- // save associated One beans recursively first
- saveAssocOne(request, true);
- }
-
- // set the IDGenerated value if required
- setIdGenValue(request);
- request.executeOrQueue();
-
- if (request.isPersistCascade()) {
- // save any associated List held beans
- saveAssocMany(true, request, true);
- }
- } finally {
- request.unRegisterBean();
- }
- }
-
- /**
- * Update the bean.
- */
- private void update(PersistRequestBean> request) {
-
- if (request.isRegisteredBean()){
- // skip as already inserted/updated in this request (recursive cascading)
- return;
- }
-
- try {
- if (request.isPersistCascade()) {
- // save associated One beans recursively first
- saveAssocOne(request, false);
- }
-
- if (request.isDirty()) {
- request.executeOrQueue();
-
- } else {
- // skip validation on unchanged bean
- if (logger.isDebugEnabled()) {
- logger.debug(Message.msg("persist.update.skipped", request.getBean()));
- }
- }
-
- if (request.isPersistCascade()) {
- // save all the beans in assocMany's after
- saveAssocMany(false, request, false);
- }
-
- request.checkUpdatedManysOnly();
-
- } finally {
- request.unRegisterBean();
- }
- }
-
- /**
- * Delete the bean with the explicit transaction.
- */
- public void delete(EntityBean bean, Transaction t) {
-
- PersistRequestBean> req = createRequest(bean, t, null, PersistRequest.Type.DELETE);
- if (req.isRegisteredForDeleteBean()) {
- // skip deleting bean. Used where cascade is on
- // both sides of a relationship
- if (logger.isDebugEnabled()) {
- logger.debug("skipping delete on alreadyRegistered " + bean);
- }
- return;
- }
-
- try {
- req.initTransIfRequiredWithBatchCascade();
- delete(req);
- req.commitTransIfRequired();
- req.flushBatchOnCascade();
-
- } catch (RuntimeException ex) {
- req.rollbackTransIfRequired();
- throw ex;
- }
- }
-
- private void deleteList(List> beanList, Transaction t) {
- for (int i = 0; i < beanList.size(); i++) {
- EntityBean bean = (EntityBean)beanList.get(i);
- delete(bean, t);
- }
- }
-
- /**
- * Delete by a List of Id's.
- */
- public void deleteMany(Class> beanType, Collection> ids, Transaction transaction) {
-
- if (ids == null || ids.size() == 0) {
- return;
- }
-
- BeanDescriptor> descriptor = beanDescriptorManager.getBeanDescriptor(beanType);
-
- ArrayList idList = new ArrayList(ids.size());
- for (Object id : ids) {
- // convert to appropriate type if required
- idList.add(descriptor.convertId(id));
- }
-
- delete(descriptor, null, idList, transaction);
- }
-
- /**
- * Delete by Id.
- */
- public int delete(Class> beanType, Object id, Transaction transaction) {
-
- BeanDescriptor> descriptor = beanDescriptorManager.getBeanDescriptor(beanType);
-
- // convert to appropriate type if required
- id = descriptor.convertId(id);
- return delete(descriptor, id, null, transaction);
- }
-
- /**
- * Delete by Id or a List of Id's.
- */
- private int delete(BeanDescriptor> descriptor, Object id, List idList, Transaction transaction) {
-
- SpiTransaction t = (SpiTransaction) transaction;
- if (t.isPersistCascade()) {
- BeanPropertyAssocOne>[] propImportDelete = descriptor.propertiesOneImportedDelete();
- if (propImportDelete.length > 0) {
- // We actually need to execute a query to get the foreign key values
- // as they are required for the delete cascade. Query back just the
- // Id and the appropriate foreign key values
- Query> q = deleteRequiresQuery(descriptor, propImportDelete);
- if (idList != null) {
- q.where().idIn(idList);
- if (t.isLogSummary()) {
- t.logSummary("-- DeleteById of " + descriptor.getName() + " ids[" + idList + "] requires fetch of foreign key values");
- }
- List> beanList = server.findList(q, t);
- deleteList(beanList, t);
- return beanList.size();
-
- } else {
- q.where().idEq(id);
- if (t.isLogSummary()) {
- t.logSummary("-- DeleteById of " + descriptor.getName() + " id[" + id + "] requires fetch of foreign key values");
- }
- EntityBean bean = (EntityBean)server.findUnique(q, t);
- if (bean == null) {
- return 0;
- } else {
- delete(bean, t);
- return 1;
- }
- }
- }
- }
-
- if (t.isPersistCascade()) {
- // OneToOne exported side with delete cascade
- BeanPropertyAssocOne>[] expOnes = descriptor.propertiesOneExportedDelete();
- 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 childIds = expOnes[i].findIdsByParentId(id, idList, t);
- deleteChildrenById(t, targetDesc, childIds);
- }
- }
-
- // OneToMany's with delete cascade
- BeanPropertyAssocMany>[] manys = descriptor.propertiesManyDelete();
- for (int i = 0; i < manys.length; i++) {
- BeanDescriptor> targetDesc = manys[i].getTargetDescriptor();
- if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isBeanCaching()) {
- // we can just delete children with a single statement
- SqlUpdate sqlDelete = manys[i].deleteByParentId(id, idList);
- executeSqlUpdate(sqlDelete, t);
- } else {
- // we need to fetch the Id's to delete (recurse or notify L2 cache)
- List childIds = manys[i].findIdsByParentId(id, idList, t, null);
- if (!childIds.isEmpty()) {
- delete(targetDesc, null, childIds, t);
- }
- }
- }
- }
-
- // ManyToMany's ... delete from intersection table
- BeanPropertyAssocMany>[] manys = descriptor.propertiesManyToMany();
- for (int i = 0; i < manys.length; i++) {
- SqlUpdate sqlDelete = manys[i].deleteByParentId(id, idList);
- if (t.isLogSummary()) {
- t.logSummary("-- Deleting intersection table entries: " + manys[i].getFullBeanName());
- }
- executeSqlUpdate(sqlDelete, t);
- }
-
- // delete the bean(s)
- SqlUpdate deleteById = descriptor.deleteById(id, idList);
- if (t.isLogSummary()) {
- if (idList != null) {
- t.logSummary("-- Deleting " + descriptor.getName() + " Ids: " + idList);
- } else {
- t.logSummary("-- Deleting " + descriptor.getName() + " Id: " + id);
- }
- }
-
- // use Id's to update L2 cache rather than Bulk table event
- deleteById.setAutoTableMod(false);
- if (idList != null) {
- t.getEvent().addDeleteByIdList(descriptor, idList);
- } else {
- t.getEvent().addDeleteById(descriptor, id);
- }
- int rows = executeSqlUpdate(deleteById, t);
-
- // Delete from the persistence context so that it can't be fetched again later
- PersistenceContext persistenceContext = t.getPersistenceContext();
- if (idList != null) {
- for (Object idValue : idList) {
- persistenceContext.deleted(descriptor.getBeanType(), idValue);
- }
- } else {
- persistenceContext.deleted(descriptor.getBeanType(), id);
- }
- return rows;
- }
-
- /**
- * We need to create and execute a query to get the foreign key values as
- * the delete cascades to them (foreign keys).
- */
- private Query> deleteRequiresQuery(BeanDescriptor> desc, BeanPropertyAssocOne>[] propImportDelete) {
-
- Query> q = server.createQuery(desc.getBeanType());
- StringBuilder sb = new StringBuilder(30);
- for (int i = 0; i < propImportDelete.length; i++) {
- sb.append(propImportDelete[i].getName()).append(",");
- }
- q.setAutofetch(false);
- q.select(sb.toString());
- return q;
- }
-
- /**
- * Delete the bean.
- *
- * Note that preDelete fires before the deletion of children.
- *
- */
- private void delete(PersistRequestBean> request) {
-
- DeleteUnloadedForeignKeys unloadedForeignKeys = null;
-
- if (request.isPersistCascade()) {
- // delete children first ... register the
- // bean to handle bi-directional cascading
- request.registerDeleteBean();
- deleteAssocMany(request);
- request.unregisterDeleteBean();
-
- unloadedForeignKeys = getDeleteUnloadedForeignKeys(request);
- if (unloadedForeignKeys != null) {
- // there are foreign keys that we don't have on this partially
- // populated bean so we actually need to query them (to cascade delete)
- unloadedForeignKeys.queryForeignKeys();
- }
- }
-
- request.executeOrQueue();
-
- if (request.isPersistCascade()) {
- deleteAssocOne(request);
-
- if (unloadedForeignKeys != null) {
- unloadedForeignKeys.deleteCascade();
- }
- }
-
- }
-
- /**
- * Save the associated child beans contained in a List.
- *
- * This will automatically copy over any join properties from the parent
- * bean to the child beans.
- *
- */
- private void saveAssocMany(boolean insertedParent, PersistRequestBean> request, boolean insertMode) {
-
- EntityBean parentBean = request.getEntityBean();
- BeanDescriptor> desc = request.getBeanDescriptor();
- SpiTransaction t = request.getTransaction();
-
- // exported ones with cascade save
- BeanPropertyAssocOne>[] expOnes = desc.propertiesOneExportedSave();
- for (int i = 0; i < expOnes.length; i++) {
- BeanPropertyAssocOne> prop = expOnes[i];
-
- // check for partial beans
- if (request.isLoadedProperty(prop)) {
- EntityBean detailBean = prop.getValueAsEntityBean(parentBean);
- if (detailBean != null) {
- if (!prop.isSaveRecurseSkippable(detailBean)) {
- t.depth(+1);
- prop.setParentBeanToChild(parentBean, detailBean);
- saveRecurse(detailBean, t, parentBean, insertMode);
- t.depth(-1);
- }
- }
- }
- }
-
- // many's with cascade save
- BeanPropertyAssocMany>[] manys = desc.propertiesManySave();
- 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), insertMode);
- if (!insertedParent) {
- request.addUpdatedManyProperty(manys[i]);
- }
- }
- }
- }
-
- /**
- * Helper to wrap the details when saving a OneToMany or ManyToMany
- * relationship.
- */
- private static class SaveManyPropRequest {
- private final boolean insertedParent;
- private final BeanPropertyAssocMany> many;
- private final EntityBean parentBean;
- private final SpiTransaction transaction;
- private final boolean cascade;
- private final boolean deleteMissingChildren;
-
- private SaveManyPropRequest(boolean insertedParent, BeanPropertyAssocMany> many, EntityBean parentBean, PersistRequestBean> request) {
- this.insertedParent = insertedParent;
- this.many = many;
- this.cascade = many.getCascadeInfo().isSave();
- this.parentBean = parentBean;
- this.transaction = request.getTransaction();
- this.deleteMissingChildren = request.isDeleteMissingChildren();
- }
-
- private SaveManyPropRequest(BeanPropertyAssocMany> many, EntityBean parentBean, SpiTransaction t) {
- this.insertedParent = false;
- this.many = many;
- this.parentBean = parentBean;
- this.transaction = t;
- this.cascade = true;
- this.deleteMissingChildren = false;
- }
-
- public boolean isSaveIntersection() {
- return transaction.isSaveAssocManyIntersection(many.getIntersectionTableJoin().getTable(), many.getBeanDescriptor().getName());
- }
-
- private Object getValue() {
- return many.getValue(parentBean);
- }
-
- private boolean isModifyListenMode() {
- return ModifyListenMode.REMOVALS.equals(many.getModifyListenMode());
- }
-
- private boolean isDeleteMissingChildren() {
- return deleteMissingChildren;
- }
-
- private boolean isInsertedParent() {
- return insertedParent;
- }
-
- private BeanPropertyAssocMany> getMany() {
- return many;
- }
-
- private EntityBean getParentBean() {
- return parentBean;
- }
-
- private SpiTransaction getTransaction() {
- return transaction;
- }
-
- private boolean isCascade() {
- return cascade;
- }
- }
-
- private void saveMany(SaveManyPropRequest saveMany, boolean insertMode) {
-
- if (saveMany.getMany().isManyToMany()) {
-
- // check if we can save the m2m intersection in this direction
- boolean saveIntersectionFromThisDirection = saveMany.isSaveIntersection();
- if (saveMany.isCascade()) {
- // Need explicit Cascade to save the beans on other side
- saveAssocManyDetails(saveMany, false, insertMode);
- }
- // for ManyToMany save the 'relationship' via inserts/deletes
- // into/from the intersection table
- if (saveIntersectionFromThisDirection) {
- // only allowed on one direction of a m2m based on beanName
- saveAssocManyIntersection(saveMany, saveMany.isDeleteMissingChildren());
- }
- } else {
- if (saveMany.isModifyListenMode()) {
- // delete any removed beans via private owned. Needs to occur before
- // a 'deleteMissingChildren' statement occurs
- removeAssocManyPrivateOwned(saveMany);
- }
- if (saveMany.isCascade()) {
- // potentially deletes 'missing children' for 'stateless update'
- saveAssocManyDetails(saveMany, saveMany.isDeleteMissingChildren(), insertMode);
- }
- }
- }
-
- private void removeAssocManyPrivateOwned(SaveManyPropRequest saveMany) {
-
- Object details = saveMany.getValue();
-
- // check that the list is not null and if it is a BeanCollection
- // check that is has been populated (don't trigger lazy loading)
- if (details instanceof BeanCollection>) {
-
- BeanCollection> c = (BeanCollection>) details;
- Set> modifyRemovals = c.getModifyRemovals();
- if (modifyRemovals != null && !modifyRemovals.isEmpty()) {
-
- SpiTransaction t = saveMany.getTransaction();
- // increase depth for batching order
- t.depth(+1);
- for (Object removedBean : modifyRemovals) {
- if (removedBean instanceof EntityBean) {
- EntityBean eb = (EntityBean)removedBean;
- if (eb._ebean_getIntercept().isLoaded()) {
- // only delete if the bean was loaded meaning that
- // it is know to exist in the DB
- deleteRecurse(removedBean, t);
- }
- }
- }
- t.depth(-1);
- }
- }
- }
-
- /**
- * Save the details from a OneToMany collection.
- */
- private void saveAssocManyDetails(SaveManyPropRequest saveMany, boolean deleteMissingChildren, boolean insertMode) {
-
- BeanPropertyAssocMany> prop = saveMany.getMany();
-
- Object details = saveMany.getValue();
-
- // check that the list is not null and if it is a BeanCollection
- // check that is has been populated (don't trigger lazy loading)
- // For a Map this is a collection of Map.Entry objects and not beans
- Collection> collection = BeanCollectionUtil.getActualEntries(details);
-
- if (collection == null) {
- // nothing to do here
- return;
- }
-
- BeanDescriptor> targetDescriptor = prop.getTargetDescriptor();
- if (saveMany.isInsertedParent()) {
- // performance optimisation for large collections
- targetDescriptor.preAllocateIds(collection.size());
- }
-
- ArrayList detailIds = null;
- if (deleteMissingChildren) {
- // collect the Id's (to exclude from deleteManyDetails)
- detailIds = new ArrayList();
- }
-
- // increase depth for batching order
- SpiTransaction t = saveMany.getTransaction();
- t.depth(+1);
-
- // if a map, then we get the key value and
- // set it to the appropriate property on the
- // detail bean before we save it
- boolean isMap = ManyType.JAVA_MAP.equals(prop.getManyType());
- EntityBean parentBean = saveMany.getParentBean();
- Object mapKeyValue = null;
-
- boolean saveSkippable = prop.isSaveRecurseSkippable();
- boolean skipSavingThisBean;
-
- for (Object detailBean : collection) {
- if (isMap) {
- // its a map so need the key and value
- Map.Entry, ?> entry = (Map.Entry, ?>) detailBean;
- mapKeyValue = entry.getKey();
- detailBean = entry.getValue();
- }
-
- if (detailBean instanceof EntityBean) {
- EntityBean detail = (EntityBean)detailBean;
- EntityBeanIntercept ebi = detail._ebean_getIntercept();
- if (prop.isManyToMany()) {
- skipSavingThisBean = targetDescriptor.isReference(ebi);
- } else {
- if (targetDescriptor.isReference(ebi)) {
- // we can skip this one
- skipSavingThisBean = true;
-
- } else if (ebi.isNewOrDirty()) {
- skipSavingThisBean = false;
- // set the parent bean to detailBean
- prop.setJoinValuesToChild(parentBean, detail, mapKeyValue);
-
- } else {
- // unmodified so skip depending on prop.isSaveRecurseSkippable();
- skipSavingThisBean = saveSkippable;
- }
- }
-
- if (!skipSavingThisBean) {
- saveRecurse(detail, t, parentBean, insertMode);
- }
- if (detailIds != null) {
- // remember the Id (other details not in the collection) will be removed
- Object id = targetDescriptor.getId(detail);
- if (!DmlUtil.isNullOrZero(id)) {
- detailIds.add(id);
- }
- }
- }
- }
-
- if (detailIds != null) {
- // deleteMissingChildren is true so deleting children that were not just processed
- deleteManyDetails(t, prop.getBeanDescriptor(), parentBean, prop, detailIds);
- }
-
- t.depth(-1);
- }
-
- public int deleteManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t) {
-
- BeanDescriptor> descriptor = beanDescriptorManager.getBeanDescriptor(ownerBean.getClass());
- BeanPropertyAssocMany> prop = (BeanPropertyAssocMany>) descriptor.getBeanProperty(propertyName);
- return deleteAssocManyIntersection(ownerBean, prop, t);
- }
-
- public void saveManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t) {
-
- BeanDescriptor> descriptor = beanDescriptorManager.getBeanDescriptor(ownerBean.getClass());
- BeanPropertyAssocMany> prop = (BeanPropertyAssocMany>) descriptor.getBeanProperty(propertyName);
-
- saveAssocManyIntersection(new SaveManyPropRequest(prop, ownerBean, (SpiTransaction) t), false);
- }
-
- public void saveAssociation(EntityBean parentBean, String propertyName, Transaction t) {
-
- BeanDescriptor> descriptor = beanDescriptorManager.getBeanDescriptor(parentBean.getClass());
- SpiTransaction trans = (SpiTransaction) t;
-
- BeanProperty prop = descriptor.getBeanProperty(propertyName);
- if (prop == null) {
- String msg = "Could not find property [" + propertyName + "] on bean " + parentBean.getClass();
- throw new PersistenceException(msg);
- }
-
- if (prop instanceof BeanPropertyAssocMany>) {
- BeanPropertyAssocMany> manyProp = (BeanPropertyAssocMany>) prop;
- saveMany(new SaveManyPropRequest(manyProp, parentBean, (SpiTransaction) t), true);
-
- } else if (prop instanceof BeanPropertyAssocOne>) {
- BeanPropertyAssocOne> oneProp = (BeanPropertyAssocOne>) prop;
- EntityBean assocBean = oneProp.getValueAsEntityBean(parentBean);
-
- int depth = oneProp.isOneToOneExported() ? 1 : -1;
- int revertDepth = -1 * depth;
-
- trans.depth(depth);
- saveRecurse(assocBean, t, parentBean, true);
- trans.depth(revertDepth);
-
- } else {
- String msg = "Expecting [" + prop.getFullBeanName() + "] to be a OneToMany, OneToOne, ManyToOne or ManyToMany property?";
- throw new PersistenceException(msg);
- }
-
- }
-
- /**
- * Save the additions and removals from a ManyToMany collection as inserts
- * and deletes from the intersection table.
- *
- * This is done via MapBeans.
- *
- */
- private void saveAssocManyIntersection(SaveManyPropRequest saveManyPropRequest, boolean deleteMissingChildren) {
-
- BeanPropertyAssocMany> prop = saveManyPropRequest.getMany();
- Object value = prop.getValue(saveManyPropRequest.getParentBean());
- if (value == null) {
- return;
- }
-
- SpiTransaction t = saveManyPropRequest.getTransaction();
- boolean vanillaCollection = !(value instanceof BeanCollection>);
-
- if (vanillaCollection || deleteMissingChildren) {
- // delete all intersection rows and then treat all
- // beans in the collection as additions
- deleteAssocManyIntersection(saveManyPropRequest.getParentBean(), prop, t);
- }
-
- Collection> deletions = null;
- Collection> additions;
-
- if (saveManyPropRequest.isInsertedParent() || vanillaCollection || deleteMissingChildren) {
- // treat everything in the list/set/map as an intersection addition
- if (value instanceof Map, ?>) {
- additions = ((Map, ?>) value).values();
- } else if (value instanceof Collection>) {
- additions = (Collection>) value;
- } else {
- String msg = "Unhandled ManyToMany type " + value.getClass().getName() + " for " + prop.getFullBeanName();
- throw new PersistenceException(msg);
- }
- if (!vanillaCollection) {
- ((BeanCollection>) value).modifyReset();
- }
- } else {
- // BeanCollection so get the additions/deletions
- BeanCollection> manyValue = (BeanCollection>) value;
- additions = manyValue.getModifyAdditions();
- deletions = manyValue.getModifyRemovals();
- // reset so the changes are only processed once
- manyValue.modifyReset();
- }
-
- t.depth(+1);
-
- if (additions != null && !additions.isEmpty()) {
- for (Object other : additions) {
- EntityBean otherBean = (EntityBean)other;
- // the object from the 'other' side of the ManyToMany
- if (deletions != null && deletions.remove(otherBean)) {
- String m = "Inserting and Deleting same object? " + otherBean;
- if (t.isLogSummary()) {
- t.logSummary(m);
- }
- logger.warn(m);
-
- } else {
- if (!prop.hasImportedId(otherBean)) {
- String msg = "ManyToMany bean " + otherBean + " does not have an Id value.";
- throw new PersistenceException(msg);
-
- } else {
- // build a intersection row for 'insert'
- IntersectionRow intRow = prop.buildManyToManyMapBean(saveManyPropRequest.getParentBean(), otherBean);
- SqlUpdate sqlInsert = intRow.createInsert(server);
- executeSqlUpdate(sqlInsert, t);
- }
- }
- }
- }
- if (deletions != null && !deletions.isEmpty()) {
- for (Object other : deletions) {
- EntityBean otherDelete = (EntityBean)other;
- // the object from the 'other' side of the ManyToMany
- // build a intersection row for 'delete'
- IntersectionRow intRow = prop.buildManyToManyMapBean(saveManyPropRequest.getParentBean(), otherDelete);
- SqlUpdate sqlDelete = intRow.createDelete(server);
- executeSqlUpdate(sqlDelete, t);
- }
- }
-
- // decrease the depth back to what it was
- t.depth(-1);
- }
-
- private int deleteAssocManyIntersection(EntityBean bean, BeanPropertyAssocMany> many, Transaction t) {
-
- // delete all intersection rows for this bean
- IntersectionRow intRow = many.buildManyToManyDeleteChildren(bean);
- SqlUpdate sqlDelete = intRow.createDeleteChildren(server);
-
- return executeSqlUpdate(sqlDelete, t);
- }
-
- /**
- * Delete beans in any associated many.
- *
- * This is called prior to deleting the parent bean.
- *
- */
- private void deleteAssocMany(PersistRequestBean> request) {
-
- SpiTransaction t = request.getTransaction();
- t.depth(-1);
-
- BeanDescriptor> desc = request.getBeanDescriptor();
- EntityBean parentBean = request.getEntityBean();
-
- BeanPropertyAssocOne>[] expOnes = desc.propertiesOneExportedDelete();
- if (expOnes.length > 0) {
-
- DeleteUnloadedForeignKeys unloaded = null;
- for (int i = 0; i < expOnes.length; i++) {
- BeanPropertyAssocOne> prop = expOnes[i];
- if (request.isLoadedProperty(prop)) {
- Object detailBean = prop.getValue(parentBean);
- if (detailBean != null) {
- deleteRecurse(detailBean, t);
- }
- } else {
- if (unloaded == null) {
- unloaded = new DeleteUnloadedForeignKeys(server, request);
- }
- unloaded.add(prop);
- }
- }
- if (unloaded != null) {
- unloaded.queryForeignKeys();
- unloaded.deleteCascade();
- }
- }
-
- // Many's with delete cascade
- BeanPropertyAssocMany>[] manys = desc.propertiesManyDelete();
- for (int i = 0; i < manys.length; i++) {
- if (manys[i].isManyToMany()) {
- // delete associated rows from intersection table
- deleteAssocManyIntersection(parentBean, manys[i], t);
-
- } else {
-
- if (ModifyListenMode.REMOVALS.equals(manys[i].getModifyListenMode())) {
- // PrivateOwned ...
- Object details = manys[i].getValue(parentBean);
- if (details instanceof BeanCollection>) {
- Set> modifyRemovals = ((BeanCollection>) details).getModifyRemovals();
- if (modifyRemovals != null && !modifyRemovals.isEmpty()) {
-
- // delete the orphans that have been removed from the collection
- for (Object detail : modifyRemovals) {
- EntityBean detailBean = (EntityBean)detail;
- if (manys[i].hasId(detailBean)) {
- deleteRecurse(detailBean, t);
- }
- }
- }
- }
- }
-
- deleteManyDetails(t, desc, parentBean, manys[i], null);
- }
- }
-
- // restore the depth
- t.depth(+1);
- }
-
- /**
- * Delete the 'many' detail beans for a given parent bean.
- *
- * For stateless updates this deletes details beans that are no longer in
- * the many - the excludeDetailIds holds the detail beans that are in the
- * collection (and should not be deleted).
- *
- */
- private void deleteManyDetails(SpiTransaction t, BeanDescriptor> desc, EntityBean parentBean,
- BeanPropertyAssocMany> many, ArrayList excludeDetailIds) {
-
- if (many.getCascadeInfo().isDelete()) {
- // cascade delete the beans in the collection
- BeanDescriptor> targetDesc = many.getTargetDescriptor();
- if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isBeanCaching()) {
- // Just delete all the children with one statement
- IntersectionRow intRow = many.buildManyDeleteChildren(parentBean, excludeDetailIds);
- SqlUpdate sqlDelete = intRow.createDelete(server);
- executeSqlUpdate(sqlDelete, t);
-
- } else {
- // Delete recurse using the Id values of the children
- Object parentId = desc.getId(parentBean);
- List idsByParentId = many.findIdsByParentId(parentId, null, t, excludeDetailIds);
- if (!idsByParentId.isEmpty()) {
- deleteChildrenById(t, targetDesc, idsByParentId);
- }
- }
- }
- }
-
- /**
- * Cascade delete child entities by Id.
- *
- * Will use delete by object if the child entity has manyToMany relationships.
- */
- private void deleteChildrenById(SpiTransaction t, BeanDescriptor> targetDesc, List childIds) {
-
- if (targetDesc.propertiesManyToMany().length > 0) {
- // convert into a list of reference objects and perform delete by object
- List refList = new ArrayList(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, boolean insertMode) {
-
- BeanDescriptor> desc = request.getBeanDescriptor();
-
- // imported ones with save cascade
- BeanPropertyAssocOne>[] ones = desc.propertiesOneImportedSave();
-
- for (int i = 0; i < ones.length; i++) {
- BeanPropertyAssocOne> prop = ones[i];
-
- // check for partial objects
- if (request.isLoadedProperty(prop)) {
- EntityBean detailBean = prop.getValueAsEntityBean(request.getEntityBean());
- if (detailBean != null
- && !prop.isSaveRecurseSkippable(detailBean)
- && !prop.isReference(detailBean)
- && !request.isParent(detailBean)) {
- SpiTransaction t = request.getTransaction();
- t.depth(-1);
- saveRecurse(detailBean, t, null, insertMode);
- t.depth(+1);
- }
- }
- }
- }
-
- /**
- * Support for loading any Imported Associated One properties that are not
- * loaded but required for Delete cascade.
- */
- private DeleteUnloadedForeignKeys getDeleteUnloadedForeignKeys(PersistRequestBean> request) {
-
- DeleteUnloadedForeignKeys fkeys = null;
-
- BeanPropertyAssocOne>[] ones = request.getBeanDescriptor().propertiesOneImportedDelete();
- for (int i = 0; i < ones.length; i++) {
- if (!request.isLoadedProperty(ones[i])) {
- // we have cascade Delete on a partially populated bean and
- // this property was not loaded (so we are going to have to fetch it)
- if (fkeys == null) {
- fkeys = new DeleteUnloadedForeignKeys(server, request);
- }
- fkeys.add(ones[i]);
- }
- }
-
- return fkeys;
- }
-
- /**
- * Delete any associated one beans.
- */
- private void deleteAssocOne(PersistRequestBean> request) {
-
- BeanDescriptor> desc = request.getBeanDescriptor();
- BeanPropertyAssocOne>[] ones = desc.propertiesOneImportedDelete();
-
- for (int i = 0; i < ones.length; i++) {
- BeanPropertyAssocOne> prop = ones[i];
- if (request.isLoadedProperty(prop)) {
- Object detailBean = prop.getValue(request.getEntityBean());
- if (detailBean != null) {
- EntityBean detail = (EntityBean)detailBean;
- if (prop.hasId(detail)) {
- deleteRecurse(detail, request.getTransaction());
- }
- }
- }
- }
- }
-
- /**
- * Set Id Generated value for insert.
- */
- private void setIdGenValue(PersistRequestBean> request) {
-
- BeanDescriptor> desc = request.getBeanDescriptor();
- if (!desc.isUseIdGenerator()) {
- return;
- }
-
- BeanProperty idProp = desc.getIdProperty();
- if (idProp == null || idProp.isEmbedded()) {
- // not supporting IdGeneration for concatenated or Embedded
- return;
- }
-
- EntityBean bean = request.getEntityBean();
- Object uid = idProp.getValue(bean);
-
- if (DmlUtil.isNullOrZero(uid)) {
-
- // generate the nextId and set it to the property
- Object nextId = desc.nextId(request.getTransaction());
-
- // cast the data type if required and set it
- desc.convertSetId(nextId, bean);
- }
- }
-
- /**
- * Create the Persist Request Object that wraps all the objects used to
- * perform an insert, update or delete.
- */
- private PersistRequestBean createRequest(T bean, Transaction t, Object parentBean, PersistRequest.Type type) {
- BeanManager mgr = getBeanManager(bean);
- if (mgr == null) {
- throw new PersistenceException(errNotRegistered(bean.getClass()));
- }
- return createRequest(bean, t, parentBean, mgr, type, false);
- }
-
- /**
- * Create an Insert or Update PersistRequestBean when cascading.
- *
- * This call determines the PersistRequest.Type based on bean state and the insert flag (root persist type).
- */
- private PersistRequestBean createRequest(T bean, Transaction t, Object parentBean, boolean insertMode) {
- BeanManager mgr = getBeanManager(bean);
- if (mgr == null) {
- throw new PersistenceException(errNotRegistered(bean.getClass()));
- }
- BeanDescriptor 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(T bean, Transaction t, Object parentBean, BeanManager> mgr, PersistRequest.Type type, boolean saveRecurse) {
-
- 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.
- *
- * Note that this checks to see if the bean is a MapBean with a tableName.
- * If so it will return the table based BeanDescriptor.
- *
+ * This object uses DmlPersistExecute to perform the actual persist execution.
+ *
+ *
+ * This object:
+ *
+ *
Determines insert or update for saved beans
+ *
Determines the concurrency mode
+ *
Handles cascading of save and delete
+ *
Handles the batching and queueing
+ *
+ *
+ * @see com.avaje.ebeaninternal.server.persist.DefaultPersistExecute
+ */
+public final class DefaultPersister implements Persister {
+
+ private static final Logger logger = LoggerFactory.getLogger(DefaultPersister.class);
+
+ /**
+ * Actually does the persisting work.
+ */
+ private final PersistExecute persistExecute;
+
+ private final SpiEbeanServer server;
+
+ private final BeanDescriptorManager beanDescriptorManager;
+
+ private final boolean updatesDeleteMissingChildren;
+
+ public DefaultPersister(SpiEbeanServer server, Binder binder, BeanDescriptorManager descMgr, PstmtBatch pstmtBatch) {
+
+ this.server = server;
+ this.updatesDeleteMissingChildren = server.getServerConfig().isUpdatesDeleteMissingChildren();
+ this.beanDescriptorManager = descMgr;
+ this.persistExecute = new DefaultPersistExecute(binder, pstmtBatch, server.getServerConfig().getPersistBatchSize());
+ }
+
+ /**
+ * Execute the CallableSql.
+ */
+ public int executeCallable(CallableSql callSql, Transaction t) {
+
+ PersistRequestCallableSql request = new PersistRequestCallableSql(server, callSql, (SpiTransaction) t, persistExecute);
+ try {
+ request.initTransIfRequired();
+ int rc = request.executeOrQueue();
+ request.commitTransIfRequired();
+ return rc;
+
+ } catch (RuntimeException e) {
+ request.rollbackTransIfRequired();
+ throw e;
+ }
+ }
+
+ /**
+ * Execute the orm update.
+ */
+ public int executeOrmUpdate(Update> update, Transaction t) {
+
+ SpiUpdate> ormUpdate = (SpiUpdate>) update;
+
+ BeanManager> mgr = beanDescriptorManager.getBeanManager(ormUpdate.getBeanType());
+
+ if (mgr == null) {
+ String msg = "No BeanManager found for type [" + ormUpdate.getBeanType() + "]. Is it an entity?";
+ throw new PersistenceException(msg);
+ }
+
+ PersistRequestOrmUpdate request = new PersistRequestOrmUpdate(server, mgr, ormUpdate, (SpiTransaction) t, persistExecute);
+ try {
+ request.initTransIfRequired();
+ int rc = request.executeOrQueue();
+ request.commitTransIfRequired();
+ return rc;
+
+ } catch (RuntimeException e) {
+ request.rollbackTransIfRequired();
+ throw e;
+ }
+ }
+
+ /**
+ * Execute the updateSql.
+ */
+ public int executeSqlUpdate(SqlUpdate updSql, Transaction t) {
+
+ PersistRequestUpdateSql request = new PersistRequestUpdateSql(server, updSql, (SpiTransaction) t, persistExecute);
+ try {
+ request.initTransIfRequired();
+ int rc = request.executeOrQueue();
+ request.commitTransIfRequired();
+ return rc;
+
+ } catch (RuntimeException e) {
+ request.rollbackTransIfRequired();
+ throw e;
+ }
+ }
+
+ /**
+ * Recursively delete the bean. This calls back to the EbeanServer.
+ */
+ private void deleteRecurse(Object detailBean, Transaction t) {
+ // NB: a new PersistRequest is made
+ server.delete(detailBean, t);
+ }
+
+ /**
+ * Update the bean.
+ */
+ public void update(EntityBean entityBean, Transaction t) {
+ update(entityBean, t, updatesDeleteMissingChildren);
+ }
+
+ /**
+ * Update the bean specifying deleteMissingChildren.
+ */
+ public void update(EntityBean entityBean, Transaction t, boolean deleteMissingChildren) {
+
+ PersistRequestBean> req = createRequest(entityBean, t, null, PersistRequest.Type.UPDATE);
+ req.setDeleteMissingChildren(deleteMissingChildren);
+ try {
+ req.initTransIfRequiredWithBatchCascade();
+ if (req.isReference()) {
+ // its a reference so see if there are manys to save...
+ if (req.isPersistCascade()) {
+ saveAssocMany(false, req, false);
+ }
+ req.checkUpdatedManysOnly();
+ } else {
+ update(req);
+ }
+
+ req.commitTransIfRequired();
+ req.flushBatchOnCascade();
+
+ } catch (RuntimeException ex) {
+ req.rollbackTransIfRequired();
+ throw ex;
+ }
+ }
+
+ /**
+ * Insert or update the bean.
+ */
+ public void save(EntityBean bean, Transaction t) {
+ if (bean._ebean_getIntercept().isLoaded()) {
+ // deleteMissingChildren is false when using 'save' on 'loaded' beans
+ update(bean, t, false);
+ } else {
+ insert(bean, t);
+ }
+ }
+
+ /**
+ * Insert this bean.
+ */
+ public void insert(EntityBean bean, Transaction t) {
+
+ PersistRequestBean> req = createRequest(bean, t, null, PersistRequest.Type.INSERT);
+ try {
+ req.initTransIfRequiredWithBatchCascade();
+ insert(req);
+ req.commitTransIfRequired();
+ req.flushBatchOnCascade();
+
+ } catch (RuntimeException ex) {
+ req.rollbackTransIfRequired();
+ throw ex;
+ }
+ }
+
+ 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, insertMode);
+
+ if (request.isReference()) {
+ // its a reference...
+ if (request.isPersistCascade()) {
+ // save any associated List held beans
+ saveAssocMany(false, request, insertMode);
+ }
+ request.checkUpdatedManysOnly();
+
+ } else {
+ if (request.isInsert()) {
+ insert(request);
+ } else {
+ update(request);
+ }
+ }
+ }
+
+ /**
+ * Insert the bean.
+ */
+ private void insert(PersistRequestBean> request) {
+
+ if (request.isRegisteredBean()){
+ // skip as already inserted/updated in this request (recursive cascading)
+ return;
+ }
+
+ try {
+ if (request.isPersistCascade()) {
+ // save associated One beans recursively first
+ saveAssocOne(request, true);
+ }
+
+ // set the IDGenerated value if required
+ setIdGenValue(request);
+ request.executeOrQueue();
+
+ if (request.isPersistCascade()) {
+ // save any associated List held beans
+ saveAssocMany(true, request, true);
+ }
+ } finally {
+ request.unRegisterBean();
+ }
+ }
+
+ /**
+ * Update the bean.
+ */
+ private void update(PersistRequestBean> request) {
+
+ if (request.isRegisteredBean()){
+ // skip as already inserted/updated in this request (recursive cascading)
+ return;
+ }
+
+ try {
+ if (request.isPersistCascade()) {
+ // save associated One beans recursively first
+ saveAssocOne(request, false);
+ }
+
+ if (request.isDirty()) {
+ request.executeOrQueue();
+
+ } else {
+ // skip validation on unchanged bean
+ if (logger.isDebugEnabled()) {
+ logger.debug(Message.msg("persist.update.skipped", request.getBean()));
+ }
+ }
+
+ if (request.isPersistCascade()) {
+ // save all the beans in assocMany's after
+ saveAssocMany(false, request, false);
+ }
+
+ request.checkUpdatedManysOnly();
+
+ } finally {
+ request.unRegisterBean();
+ }
+ }
+
+ /**
+ * Delete the bean with the explicit transaction.
+ */
+ public void delete(EntityBean bean, Transaction t) {
+
+ PersistRequestBean> req = createRequest(bean, t, null, PersistRequest.Type.DELETE);
+ if (req.isRegisteredForDeleteBean()) {
+ // skip deleting bean. Used where cascade is on
+ // both sides of a relationship
+ if (logger.isDebugEnabled()) {
+ logger.debug("skipping delete on alreadyRegistered " + bean);
+ }
+ return;
+ }
+
+ try {
+ req.initTransIfRequiredWithBatchCascade();
+ delete(req);
+ req.commitTransIfRequired();
+ req.flushBatchOnCascade();
+
+ } catch (RuntimeException ex) {
+ req.rollbackTransIfRequired();
+ throw ex;
+ }
+ }
+
+ private void deleteList(List> beanList, Transaction t) {
+ for (int i = 0; i < beanList.size(); i++) {
+ EntityBean bean = (EntityBean)beanList.get(i);
+ delete(bean, t);
+ }
+ }
+
+ /**
+ * Delete by a List of Id's.
+ */
+ public void deleteMany(Class> beanType, Collection> ids, Transaction transaction) {
+
+ if (ids == null || ids.size() == 0) {
+ return;
+ }
+
+ BeanDescriptor> descriptor = beanDescriptorManager.getBeanDescriptor(beanType);
+
+ ArrayList idList = new ArrayList(ids.size());
+ for (Object id : ids) {
+ // convert to appropriate type if required
+ idList.add(descriptor.convertId(id));
+ }
+
+ delete(descriptor, null, idList, transaction);
+ }
+
+ /**
+ * Delete by Id.
+ */
+ public int delete(Class> beanType, Object id, Transaction transaction) {
+
+ BeanDescriptor> descriptor = beanDescriptorManager.getBeanDescriptor(beanType);
+
+ // convert to appropriate type if required
+ id = descriptor.convertId(id);
+ return delete(descriptor, id, null, transaction);
+ }
+
+ /**
+ * Delete by Id or a List of Id's.
+ */
+ private int delete(BeanDescriptor> descriptor, Object id, List idList, Transaction transaction) {
+
+ SpiTransaction t = (SpiTransaction) transaction;
+ if (t.isPersistCascade()) {
+ BeanPropertyAssocOne>[] propImportDelete = descriptor.propertiesOneImportedDelete();
+ if (propImportDelete.length > 0) {
+ // We actually need to execute a query to get the foreign key values
+ // as they are required for the delete cascade. Query back just the
+ // Id and the appropriate foreign key values
+ Query> q = deleteRequiresQuery(descriptor, propImportDelete);
+ if (idList != null) {
+ q.where().idIn(idList);
+ if (t.isLogSummary()) {
+ t.logSummary("-- DeleteById of " + descriptor.getName() + " ids[" + idList + "] requires fetch of foreign key values");
+ }
+ List> beanList = server.findList(q, t);
+ deleteList(beanList, t);
+ return beanList.size();
+
+ } else {
+ q.where().idEq(id);
+ if (t.isLogSummary()) {
+ t.logSummary("-- DeleteById of " + descriptor.getName() + " id[" + id + "] requires fetch of foreign key values");
+ }
+ EntityBean bean = (EntityBean)server.findUnique(q, t);
+ if (bean == null) {
+ return 0;
+ } else {
+ delete(bean, t);
+ return 1;
+ }
+ }
+ }
+ }
+
+ if (t.isPersistCascade()) {
+ // OneToOne exported side with delete cascade
+ BeanPropertyAssocOne>[] expOnes = descriptor.propertiesOneExportedDelete();
+ 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 childIds = expOnes[i].findIdsByParentId(id, idList, t);
+ deleteChildrenById(t, targetDesc, childIds);
+ }
+ }
+
+ // OneToMany's with delete cascade
+ BeanPropertyAssocMany>[] manys = descriptor.propertiesManyDelete();
+ for (int i = 0; i < manys.length; i++) {
+ BeanDescriptor> targetDesc = manys[i].getTargetDescriptor();
+ if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isBeanCaching()) {
+ // we can just delete children with a single statement
+ SqlUpdate sqlDelete = manys[i].deleteByParentId(id, idList);
+ executeSqlUpdate(sqlDelete, t);
+ } else {
+ // we need to fetch the Id's to delete (recurse or notify L2 cache)
+ List childIds = manys[i].findIdsByParentId(id, idList, t, null);
+ if (!childIds.isEmpty()) {
+ delete(targetDesc, null, childIds, t);
+ }
+ }
+ }
+ }
+
+ // ManyToMany's ... delete from intersection table
+ BeanPropertyAssocMany>[] manys = descriptor.propertiesManyToMany();
+ for (int i = 0; i < manys.length; i++) {
+ SqlUpdate sqlDelete = manys[i].deleteByParentId(id, idList);
+ if (t.isLogSummary()) {
+ t.logSummary("-- Deleting intersection table entries: " + manys[i].getFullBeanName());
+ }
+ executeSqlUpdate(sqlDelete, t);
+ }
+
+ // delete the bean(s)
+ SqlUpdate deleteById = descriptor.deleteById(id, idList);
+ if (t.isLogSummary()) {
+ if (idList != null) {
+ t.logSummary("-- Deleting " + descriptor.getName() + " Ids: " + idList);
+ } else {
+ t.logSummary("-- Deleting " + descriptor.getName() + " Id: " + id);
+ }
+ }
+
+ // use Id's to update L2 cache rather than Bulk table event
+ deleteById.setAutoTableMod(false);
+ if (idList != null) {
+ t.getEvent().addDeleteByIdList(descriptor, idList);
+ } else {
+ t.getEvent().addDeleteById(descriptor, id);
+ }
+ int rows = executeSqlUpdate(deleteById, t);
+
+ // Delete from the persistence context so that it can't be fetched again later
+ PersistenceContext persistenceContext = t.getPersistenceContext();
+ if (idList != null) {
+ for (Object idValue : idList) {
+ persistenceContext.deleted(descriptor.getBeanType(), idValue);
+ }
+ } else {
+ persistenceContext.deleted(descriptor.getBeanType(), id);
+ }
+ return rows;
+ }
+
+ /**
+ * We need to create and execute a query to get the foreign key values as
+ * the delete cascades to them (foreign keys).
+ */
+ private Query> deleteRequiresQuery(BeanDescriptor> desc, BeanPropertyAssocOne>[] propImportDelete) {
+
+ Query> q = server.createQuery(desc.getBeanType());
+ StringBuilder sb = new StringBuilder(30);
+ for (int i = 0; i < propImportDelete.length; i++) {
+ sb.append(propImportDelete[i].getName()).append(",");
+ }
+ q.setAutofetch(false);
+ q.select(sb.toString());
+ return q;
+ }
+
+ /**
+ * Delete the bean.
+ *
+ * Note that preDelete fires before the deletion of children.
+ *
+ */
+ private void delete(PersistRequestBean> request) {
+
+ DeleteUnloadedForeignKeys unloadedForeignKeys = null;
+
+ if (request.isPersistCascade()) {
+ // delete children first ... register the
+ // bean to handle bi-directional cascading
+ request.registerDeleteBean();
+ deleteAssocMany(request);
+ request.unregisterDeleteBean();
+
+ unloadedForeignKeys = getDeleteUnloadedForeignKeys(request);
+ if (unloadedForeignKeys != null) {
+ // there are foreign keys that we don't have on this partially
+ // populated bean so we actually need to query them (to cascade delete)
+ unloadedForeignKeys.queryForeignKeys();
+ }
+ }
+
+ request.executeOrQueue();
+
+ if (request.isPersistCascade()) {
+ deleteAssocOne(request);
+
+ if (unloadedForeignKeys != null) {
+ unloadedForeignKeys.deleteCascade();
+ }
+ }
+
+ }
+
+ /**
+ * Save the associated child beans contained in a List.
+ *
+ * This will automatically copy over any join properties from the parent
+ * bean to the child beans.
+ *
+ */
+ private void saveAssocMany(boolean insertedParent, PersistRequestBean> request, boolean insertMode) {
+
+ EntityBean parentBean = request.getEntityBean();
+ BeanDescriptor> desc = request.getBeanDescriptor();
+ SpiTransaction t = request.getTransaction();
+
+ // exported ones with cascade save
+ BeanPropertyAssocOne>[] expOnes = desc.propertiesOneExportedSave();
+ for (int i = 0; i < expOnes.length; i++) {
+ BeanPropertyAssocOne> prop = expOnes[i];
+
+ // check for partial beans
+ if (request.isLoadedProperty(prop)) {
+ EntityBean detailBean = prop.getValueAsEntityBean(parentBean);
+ if (detailBean != null) {
+ if (!prop.isSaveRecurseSkippable(detailBean)) {
+ t.depth(+1);
+ prop.setParentBeanToChild(parentBean, detailBean);
+ saveRecurse(detailBean, t, parentBean, insertMode);
+ t.depth(-1);
+ }
+ }
+ }
+ }
+
+ // many's with cascade save
+ BeanPropertyAssocMany>[] manys = desc.propertiesManySave();
+ 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), insertMode);
+ if (!insertedParent) {
+ request.addUpdatedManyProperty(manys[i]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper to wrap the details when saving a OneToMany or ManyToMany
+ * relationship.
+ */
+ private static class SaveManyPropRequest {
+ private final boolean insertedParent;
+ private final BeanPropertyAssocMany> many;
+ private final EntityBean parentBean;
+ private final SpiTransaction transaction;
+ private final boolean cascade;
+ private final boolean deleteMissingChildren;
+
+ private SaveManyPropRequest(boolean insertedParent, BeanPropertyAssocMany> many, EntityBean parentBean, PersistRequestBean> request) {
+ this.insertedParent = insertedParent;
+ this.many = many;
+ this.cascade = many.getCascadeInfo().isSave();
+ this.parentBean = parentBean;
+ this.transaction = request.getTransaction();
+ this.deleteMissingChildren = request.isDeleteMissingChildren();
+ }
+
+ private SaveManyPropRequest(BeanPropertyAssocMany> many, EntityBean parentBean, SpiTransaction t) {
+ this.insertedParent = false;
+ this.many = many;
+ this.parentBean = parentBean;
+ this.transaction = t;
+ this.cascade = true;
+ this.deleteMissingChildren = false;
+ }
+
+ public boolean isSaveIntersection() {
+ return transaction.isSaveAssocManyIntersection(many.getIntersectionTableJoin().getTable(), many.getBeanDescriptor().getName());
+ }
+
+ private Object getValue() {
+ return many.getValue(parentBean);
+ }
+
+ private boolean isModifyListenMode() {
+ return ModifyListenMode.REMOVALS.equals(many.getModifyListenMode());
+ }
+
+ private boolean isDeleteMissingChildren() {
+ return deleteMissingChildren;
+ }
+
+ private boolean isInsertedParent() {
+ return insertedParent;
+ }
+
+ private BeanPropertyAssocMany> getMany() {
+ return many;
+ }
+
+ private EntityBean getParentBean() {
+ return parentBean;
+ }
+
+ private SpiTransaction getTransaction() {
+ return transaction;
+ }
+
+ private boolean isCascade() {
+ return cascade;
+ }
+ }
+
+ private void saveMany(SaveManyPropRequest saveMany, boolean insertMode) {
+
+ if (saveMany.getMany().isManyToMany()) {
+
+ // check if we can save the m2m intersection in this direction
+ boolean saveIntersectionFromThisDirection = saveMany.isSaveIntersection();
+ if (saveMany.isCascade()) {
+ // Need explicit Cascade to save the beans on other side
+ saveAssocManyDetails(saveMany, false, insertMode);
+ }
+ // for ManyToMany save the 'relationship' via inserts/deletes
+ // into/from the intersection table
+ if (saveIntersectionFromThisDirection) {
+ // only allowed on one direction of a m2m based on beanName
+ saveAssocManyIntersection(saveMany, saveMany.isDeleteMissingChildren());
+ }
+ } else {
+ if (saveMany.isModifyListenMode()) {
+ // delete any removed beans via private owned. Needs to occur before
+ // a 'deleteMissingChildren' statement occurs
+ removeAssocManyPrivateOwned(saveMany);
+ }
+ if (saveMany.isCascade()) {
+ // potentially deletes 'missing children' for 'stateless update'
+ saveAssocManyDetails(saveMany, saveMany.isDeleteMissingChildren(), insertMode);
+ }
+ }
+ }
+
+ private void removeAssocManyPrivateOwned(SaveManyPropRequest saveMany) {
+
+ Object details = saveMany.getValue();
+
+ // check that the list is not null and if it is a BeanCollection
+ // check that is has been populated (don't trigger lazy loading)
+ if (details instanceof BeanCollection>) {
+
+ BeanCollection> c = (BeanCollection>) details;
+ Set> modifyRemovals = c.getModifyRemovals();
+ if (modifyRemovals != null && !modifyRemovals.isEmpty()) {
+
+ SpiTransaction t = saveMany.getTransaction();
+ // increase depth for batching order
+ t.depth(+1);
+ for (Object removedBean : modifyRemovals) {
+ if (removedBean instanceof EntityBean) {
+ EntityBean eb = (EntityBean)removedBean;
+ if (eb._ebean_getIntercept().isLoaded()) {
+ // only delete if the bean was loaded meaning that
+ // it is know to exist in the DB
+ deleteRecurse(removedBean, t);
+ }
+ }
+ }
+ t.depth(-1);
+ }
+ }
+ }
+
+ /**
+ * Save the details from a OneToMany collection.
+ */
+ private void saveAssocManyDetails(SaveManyPropRequest saveMany, boolean deleteMissingChildren, boolean insertMode) {
+
+ BeanPropertyAssocMany> prop = saveMany.getMany();
+
+ Object details = saveMany.getValue();
+
+ // check that the list is not null and if it is a BeanCollection
+ // check that is has been populated (don't trigger lazy loading)
+ // For a Map this is a collection of Map.Entry objects and not beans
+ Collection> collection = BeanCollectionUtil.getActualEntries(details);
+
+ if (collection == null) {
+ // nothing to do here
+ return;
+ }
+
+ BeanDescriptor> targetDescriptor = prop.getTargetDescriptor();
+ if (saveMany.isInsertedParent()) {
+ // performance optimisation for large collections
+ targetDescriptor.preAllocateIds(collection.size());
+ }
+
+ ArrayList detailIds = null;
+ if (deleteMissingChildren) {
+ // collect the Id's (to exclude from deleteManyDetails)
+ detailIds = new ArrayList();
+ }
+
+ // increase depth for batching order
+ SpiTransaction t = saveMany.getTransaction();
+ t.depth(+1);
+
+ // if a map, then we get the key value and
+ // set it to the appropriate property on the
+ // detail bean before we save it
+ boolean isMap = ManyType.JAVA_MAP.equals(prop.getManyType());
+ EntityBean parentBean = saveMany.getParentBean();
+ Object mapKeyValue = null;
+
+ boolean saveSkippable = prop.isSaveRecurseSkippable();
+ boolean skipSavingThisBean;
+
+ for (Object detailBean : collection) {
+ if (isMap) {
+ // its a map so need the key and value
+ Map.Entry, ?> entry = (Map.Entry, ?>) detailBean;
+ mapKeyValue = entry.getKey();
+ detailBean = entry.getValue();
+ }
+
+ if (detailBean instanceof EntityBean) {
+ EntityBean detail = (EntityBean)detailBean;
+ EntityBeanIntercept ebi = detail._ebean_getIntercept();
+ if (prop.isManyToMany()) {
+ skipSavingThisBean = targetDescriptor.isReference(ebi);
+ } else {
+ if (targetDescriptor.isReference(ebi)) {
+ // we can skip this one
+ skipSavingThisBean = true;
+
+ } else if (ebi.isNewOrDirty()) {
+ skipSavingThisBean = false;
+ // set the parent bean to detailBean
+ prop.setJoinValuesToChild(parentBean, detail, mapKeyValue);
+
+ } else {
+ // unmodified so skip depending on prop.isSaveRecurseSkippable();
+ skipSavingThisBean = saveSkippable;
+ }
+ }
+
+ if (!skipSavingThisBean) {
+ saveRecurse(detail, t, parentBean, insertMode);
+ }
+ if (detailIds != null) {
+ // remember the Id (other details not in the collection) will be removed
+ Object id = targetDescriptor.getId(detail);
+ if (!DmlUtil.isNullOrZero(id)) {
+ detailIds.add(id);
+ }
+ }
+ }
+ }
+
+ if (detailIds != null) {
+ // deleteMissingChildren is true so deleting children that were not just processed
+ deleteManyDetails(t, prop.getBeanDescriptor(), parentBean, prop, detailIds);
+ }
+
+ t.depth(-1);
+ }
+
+ public int deleteManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t) {
+
+ BeanDescriptor> descriptor = beanDescriptorManager.getBeanDescriptor(ownerBean.getClass());
+ BeanPropertyAssocMany> prop = (BeanPropertyAssocMany>) descriptor.getBeanProperty(propertyName);
+ return deleteAssocManyIntersection(ownerBean, prop, t);
+ }
+
+ public void saveManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t) {
+
+ BeanDescriptor> descriptor = beanDescriptorManager.getBeanDescriptor(ownerBean.getClass());
+ BeanPropertyAssocMany> prop = (BeanPropertyAssocMany>) descriptor.getBeanProperty(propertyName);
+
+ saveAssocManyIntersection(new SaveManyPropRequest(prop, ownerBean, (SpiTransaction) t), false);
+ }
+
+ public void saveAssociation(EntityBean parentBean, String propertyName, Transaction t) {
+
+ BeanDescriptor> descriptor = beanDescriptorManager.getBeanDescriptor(parentBean.getClass());
+ SpiTransaction trans = (SpiTransaction) t;
+
+ BeanProperty prop = descriptor.getBeanProperty(propertyName);
+ if (prop == null) {
+ String msg = "Could not find property [" + propertyName + "] on bean " + parentBean.getClass();
+ throw new PersistenceException(msg);
+ }
+
+ if (prop instanceof BeanPropertyAssocMany>) {
+ BeanPropertyAssocMany> manyProp = (BeanPropertyAssocMany>) prop;
+ saveMany(new SaveManyPropRequest(manyProp, parentBean, (SpiTransaction) t), true);
+
+ } else if (prop instanceof BeanPropertyAssocOne>) {
+ BeanPropertyAssocOne> oneProp = (BeanPropertyAssocOne>) prop;
+ EntityBean assocBean = oneProp.getValueAsEntityBean(parentBean);
+
+ int depth = oneProp.isOneToOneExported() ? 1 : -1;
+ int revertDepth = -1 * depth;
+
+ trans.depth(depth);
+ saveRecurse(assocBean, t, parentBean, true);
+ trans.depth(revertDepth);
+
+ } else {
+ String msg = "Expecting [" + prop.getFullBeanName() + "] to be a OneToMany, OneToOne, ManyToOne or ManyToMany property?";
+ throw new PersistenceException(msg);
+ }
+
+ }
+
+ /**
+ * Save the additions and removals from a ManyToMany collection as inserts
+ * and deletes from the intersection table.
+ *
+ * This is done via MapBeans.
+ *
+ */
+ private void saveAssocManyIntersection(SaveManyPropRequest saveManyPropRequest, boolean deleteMissingChildren) {
+
+ BeanPropertyAssocMany> prop = saveManyPropRequest.getMany();
+ Object value = prop.getValue(saveManyPropRequest.getParentBean());
+ if (value == null) {
+ return;
+ }
+
+ SpiTransaction t = saveManyPropRequest.getTransaction();
+ boolean vanillaCollection = !(value instanceof BeanCollection>);
+
+ if (vanillaCollection || deleteMissingChildren) {
+ // delete all intersection rows and then treat all
+ // beans in the collection as additions
+ deleteAssocManyIntersection(saveManyPropRequest.getParentBean(), prop, t);
+ }
+
+ Collection> deletions = null;
+ Collection> additions;
+
+ if (saveManyPropRequest.isInsertedParent() || vanillaCollection || deleteMissingChildren) {
+ // treat everything in the list/set/map as an intersection addition
+ if (value instanceof Map, ?>) {
+ additions = ((Map, ?>) value).values();
+ } else if (value instanceof Collection>) {
+ additions = (Collection>) value;
+ } else {
+ String msg = "Unhandled ManyToMany type " + value.getClass().getName() + " for " + prop.getFullBeanName();
+ throw new PersistenceException(msg);
+ }
+ if (!vanillaCollection) {
+ ((BeanCollection>) value).modifyReset();
+ }
+ } else {
+ // BeanCollection so get the additions/deletions
+ BeanCollection> manyValue = (BeanCollection>) value;
+ additions = manyValue.getModifyAdditions();
+ deletions = manyValue.getModifyRemovals();
+ // reset so the changes are only processed once
+ manyValue.modifyReset();
+ }
+
+ t.depth(+1);
+
+ if (additions != null && !additions.isEmpty()) {
+ for (Object other : additions) {
+ EntityBean otherBean = (EntityBean)other;
+ // the object from the 'other' side of the ManyToMany
+ if (deletions != null && deletions.remove(otherBean)) {
+ String m = "Inserting and Deleting same object? " + otherBean;
+ if (t.isLogSummary()) {
+ t.logSummary(m);
+ }
+ logger.warn(m);
+
+ } else {
+ if (!prop.hasImportedId(otherBean)) {
+ String msg = "ManyToMany bean " + otherBean + " does not have an Id value.";
+ throw new PersistenceException(msg);
+
+ } else {
+ // build a intersection row for 'insert'
+ IntersectionRow intRow = prop.buildManyToManyMapBean(saveManyPropRequest.getParentBean(), otherBean);
+ SqlUpdate sqlInsert = intRow.createInsert(server);
+ executeSqlUpdate(sqlInsert, t);
+ }
+ }
+ }
+ }
+ if (deletions != null && !deletions.isEmpty()) {
+ for (Object other : deletions) {
+ EntityBean otherDelete = (EntityBean)other;
+ // the object from the 'other' side of the ManyToMany
+ // build a intersection row for 'delete'
+ IntersectionRow intRow = prop.buildManyToManyMapBean(saveManyPropRequest.getParentBean(), otherDelete);
+ SqlUpdate sqlDelete = intRow.createDelete(server);
+ executeSqlUpdate(sqlDelete, t);
+ }
+ }
+
+ // decrease the depth back to what it was
+ t.depth(-1);
+ }
+
+ private int deleteAssocManyIntersection(EntityBean bean, BeanPropertyAssocMany> many, Transaction t) {
+
+ // delete all intersection rows for this bean
+ IntersectionRow intRow = many.buildManyToManyDeleteChildren(bean);
+ SqlUpdate sqlDelete = intRow.createDeleteChildren(server);
+
+ return executeSqlUpdate(sqlDelete, t);
+ }
+
+ /**
+ * Delete beans in any associated many.
+ *
+ * This is called prior to deleting the parent bean.
+ *
+ */
+ private void deleteAssocMany(PersistRequestBean> request) {
+
+ SpiTransaction t = request.getTransaction();
+ t.depth(-1);
+
+ BeanDescriptor> desc = request.getBeanDescriptor();
+ EntityBean parentBean = request.getEntityBean();
+
+ BeanPropertyAssocOne>[] expOnes = desc.propertiesOneExportedDelete();
+ if (expOnes.length > 0) {
+
+ DeleteUnloadedForeignKeys unloaded = null;
+ for (int i = 0; i < expOnes.length; i++) {
+ BeanPropertyAssocOne> prop = expOnes[i];
+ if (request.isLoadedProperty(prop)) {
+ Object detailBean = prop.getValue(parentBean);
+ if (detailBean != null) {
+ deleteRecurse(detailBean, t);
+ }
+ } else {
+ if (unloaded == null) {
+ unloaded = new DeleteUnloadedForeignKeys(server, request);
+ }
+ unloaded.add(prop);
+ }
+ }
+ if (unloaded != null) {
+ unloaded.queryForeignKeys();
+ unloaded.deleteCascade();
+ }
+ }
+
+ // Many's with delete cascade
+ BeanPropertyAssocMany>[] manys = desc.propertiesManyDelete();
+ for (int i = 0; i < manys.length; i++) {
+ if (manys[i].isManyToMany()) {
+ // delete associated rows from intersection table
+ deleteAssocManyIntersection(parentBean, manys[i], t);
+
+ } else {
+
+ if (ModifyListenMode.REMOVALS.equals(manys[i].getModifyListenMode())) {
+ // PrivateOwned ...
+ Object details = manys[i].getValue(parentBean);
+ if (details instanceof BeanCollection>) {
+ Set> modifyRemovals = ((BeanCollection>) details).getModifyRemovals();
+ if (modifyRemovals != null && !modifyRemovals.isEmpty()) {
+
+ // delete the orphans that have been removed from the collection
+ for (Object detail : modifyRemovals) {
+ EntityBean detailBean = (EntityBean)detail;
+ if (manys[i].hasId(detailBean)) {
+ deleteRecurse(detailBean, t);
+ }
+ }
+ }
+ }
+ }
+
+ deleteManyDetails(t, desc, parentBean, manys[i], null);
+ }
+ }
+
+ // restore the depth
+ t.depth(+1);
+ }
+
+ /**
+ * Delete the 'many' detail beans for a given parent bean.
+ *
+ * For stateless updates this deletes details beans that are no longer in
+ * the many - the excludeDetailIds holds the detail beans that are in the
+ * collection (and should not be deleted).
+ *
+ */
+ private void deleteManyDetails(SpiTransaction t, BeanDescriptor> desc, EntityBean parentBean,
+ BeanPropertyAssocMany> many, ArrayList excludeDetailIds) {
+
+ if (many.getCascadeInfo().isDelete()) {
+ // cascade delete the beans in the collection
+ BeanDescriptor> targetDesc = many.getTargetDescriptor();
+ if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isBeanCaching()) {
+ // Just delete all the children with one statement
+ IntersectionRow intRow = many.buildManyDeleteChildren(parentBean, excludeDetailIds);
+ SqlUpdate sqlDelete = intRow.createDelete(server);
+ executeSqlUpdate(sqlDelete, t);
+
+ } else {
+ // Delete recurse using the Id values of the children
+ Object parentId = desc.getId(parentBean);
+ List idsByParentId = many.findIdsByParentId(parentId, null, t, excludeDetailIds);
+ if (!idsByParentId.isEmpty()) {
+ deleteChildrenById(t, targetDesc, idsByParentId);
+ }
+ }
+ }
+ }
+
+ /**
+ * Cascade delete child entities by Id.
+ *
+ * Will use delete by object if the child entity has manyToMany relationships.
+ */
+ private void deleteChildrenById(SpiTransaction t, BeanDescriptor> targetDesc, List childIds) {
+
+ if (targetDesc.propertiesManyToMany().length > 0) {
+ // convert into a list of reference objects and perform delete by object
+ List refList = new ArrayList(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, boolean insertMode) {
+
+ BeanDescriptor> desc = request.getBeanDescriptor();
+
+ // imported ones with save cascade
+ BeanPropertyAssocOne>[] ones = desc.propertiesOneImportedSave();
+
+ for (int i = 0; i < ones.length; i++) {
+ BeanPropertyAssocOne> prop = ones[i];
+
+ // check for partial objects
+ if (request.isLoadedProperty(prop)) {
+ EntityBean detailBean = prop.getValueAsEntityBean(request.getEntityBean());
+ if (detailBean != null
+ && !prop.isSaveRecurseSkippable(detailBean)
+ && !prop.isReference(detailBean)
+ && !request.isParent(detailBean)) {
+ SpiTransaction t = request.getTransaction();
+ t.depth(-1);
+ saveRecurse(detailBean, t, null, insertMode);
+ t.depth(+1);
+ }
+ }
+ }
+ }
+
+ /**
+ * Support for loading any Imported Associated One properties that are not
+ * loaded but required for Delete cascade.
+ */
+ private DeleteUnloadedForeignKeys getDeleteUnloadedForeignKeys(PersistRequestBean> request) {
+
+ DeleteUnloadedForeignKeys fkeys = null;
+
+ BeanPropertyAssocOne>[] ones = request.getBeanDescriptor().propertiesOneImportedDelete();
+ for (int i = 0; i < ones.length; i++) {
+ if (!request.isLoadedProperty(ones[i])) {
+ // we have cascade Delete on a partially populated bean and
+ // this property was not loaded (so we are going to have to fetch it)
+ if (fkeys == null) {
+ fkeys = new DeleteUnloadedForeignKeys(server, request);
+ }
+ fkeys.add(ones[i]);
+ }
+ }
+
+ return fkeys;
+ }
+
+ /**
+ * Delete any associated one beans.
+ */
+ private void deleteAssocOne(PersistRequestBean> request) {
+
+ BeanDescriptor> desc = request.getBeanDescriptor();
+ BeanPropertyAssocOne>[] ones = desc.propertiesOneImportedDelete();
+
+ for (int i = 0; i < ones.length; i++) {
+ BeanPropertyAssocOne> prop = ones[i];
+ if (request.isLoadedProperty(prop)) {
+ Object detailBean = prop.getValue(request.getEntityBean());
+ if (detailBean != null) {
+ EntityBean detail = (EntityBean)detailBean;
+ if (prop.hasId(detail)) {
+ deleteRecurse(detail, request.getTransaction());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Set Id Generated value for insert.
+ */
+ private void setIdGenValue(PersistRequestBean> request) {
+
+ BeanDescriptor> desc = request.getBeanDescriptor();
+ if (!desc.isUseIdGenerator()) {
+ return;
+ }
+
+ BeanProperty idProp = desc.getIdProperty();
+ if (idProp == null || idProp.isEmbedded()) {
+ // not supporting IdGeneration for concatenated or Embedded
+ return;
+ }
+
+ EntityBean bean = request.getEntityBean();
+ Object uid = idProp.getValue(bean);
+
+ if (DmlUtil.isNullOrZero(uid)) {
+
+ // generate the nextId and set it to the property
+ Object nextId = desc.nextId(request.getTransaction());
+
+ // cast the data type if required and set it
+ desc.convertSetId(nextId, bean);
+ }
+ }
+
+ /**
+ * Create the Persist Request Object that wraps all the objects used to
+ * perform an insert, update or delete.
+ */
+ private PersistRequestBean createRequest(T bean, Transaction t, Object parentBean, PersistRequest.Type type) {
+ BeanManager mgr = getBeanManager(bean);
+ if (mgr == null) {
+ throw new PersistenceException(errNotRegistered(bean.getClass()));
+ }
+ return createRequest(bean, t, parentBean, mgr, type, false);
+ }
+
+ /**
+ * Create an Insert or Update PersistRequestBean when cascading.
+ *
+ * This call determines the PersistRequest.Type based on bean state and the insert flag (root persist type).
+ */
+ private PersistRequestBean createRequest(T bean, Transaction t, Object parentBean, boolean insertMode) {
+ BeanManager mgr = getBeanManager(bean);
+ if (mgr == null) {
+ throw new PersistenceException(errNotRegistered(bean.getClass()));
+ }
+ BeanDescriptor 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(T bean, Transaction t, Object parentBean, BeanManager> mgr, PersistRequest.Type type, boolean saveRecurse) {
+
+ 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.
+ *
+ * Note that this checks to see if the bean is a MapBean with a tableName.
+ * If so it will return the table based BeanDescriptor.
+ *
+ * This is only used by CallableSql and UpdateSql requests and does not support
+ * getGeneratedKeys.
+ *
+ */
+public class PstmtFactory {
+
+
+ private final PstmtBatch pstmtBatch;
+
+ public PstmtFactory(PstmtBatch pstmtBatch) {
+ this.pstmtBatch = pstmtBatch;
+ }
+
+ /**
+ * Get a callable statement without any batching.
+ */
+ public CallableStatement getCstmt(SpiTransaction t, String sql) throws SQLException {
+ Connection conn = t.getInternalConnection();
+ return conn.prepareCall(sql);
+ }
+
+ /**
+ * Get a prepared statement without any batching.
+ */
+ public PreparedStatement getPstmt(SpiTransaction t, String sql) throws SQLException {
+ Connection conn = t.getInternalConnection();
+ return conn.prepareStatement(sql);
+ }
+
+ /**
+ * Return a prepared statement taking into account batch requirements.
+ */
+ public PreparedStatement getPstmt(SpiTransaction t, boolean logSql, String sql, BatchPostExecute batchExe)
+ throws SQLException {
+
+ BatchedPstmtHolder batch = t.getBatchControl().getPstmtHolder();
+ PreparedStatement stmt = batch.getStmt(sql, batchExe);
+
+ if (stmt != null) {
+ return stmt;
+ }
+
+ if (logSql){
+ t.logSql(sql);
+ }
+
+ Connection conn = t.getInternalConnection();
+ stmt = conn.prepareStatement(sql);
+
+ if (pstmtBatch != null){
+ pstmtBatch.setBatchSize(stmt, t.getBatchControl().getBatchSize());
+ }
+
+ BatchedPstmt bs = new BatchedPstmt(stmt, false, sql, pstmtBatch, false);
+ batch.addStmt(bs, batchExe);
+ return stmt;
+ }
+
+ /**
+ * Return a callable statement taking into account batch requirements.
+ */
+ public CallableStatement getCstmt(SpiTransaction t, boolean logSql, String sql, BatchPostExecute batchExe)
+ throws SQLException {
+
+ BatchedPstmtHolder batch = t.getBatchControl().getPstmtHolder();
+ CallableStatement stmt = (CallableStatement) batch.getStmt(sql, batchExe);
+
+ if (stmt != null) {
+ return stmt;
+ }
+
+ if (logSql){
+ t.logSql(sql);
+ }
+
+ Connection conn = t.getInternalConnection();
+ stmt = conn.prepareCall(sql);
+
+ BatchedPstmt bs = new BatchedPstmt(stmt, false, sql, pstmtBatch, false);
+ batch.addStmt(bs, batchExe);
+ return stmt;
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteHandler.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteHandler.java
index 09d676d4f..0e2c814ac 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteHandler.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteHandler.java
@@ -1,56 +1,56 @@
-package com.avaje.ebeaninternal.server.persist.dml;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-
-import javax.persistence.OptimisticLockException;
-
-import com.avaje.ebeaninternal.api.DerivedRelationshipData;
-import com.avaje.ebeaninternal.api.SpiTransaction;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.type.DataBind;
-
-/**
- * Delete bean handler.
- */
-public class DeleteHandler extends DmlHandler {
-
- private final DeleteMeta meta;
-
- public DeleteHandler(PersistRequestBean> persist, DeleteMeta meta) {
- super(persist, meta.isEmptyStringAsNull());
- this.meta = meta;
- }
-
- /**
- * Generate and bind the delete statement.
- */
- public void bind() throws SQLException {
-
- sql = meta.getSql(persistRequest);
- SpiTransaction t = persistRequest.getTransaction();
-
- PreparedStatement pstmt;
- if (persistRequest.isBatched()) {
- pstmt = getPstmt(t, sql, persistRequest, false);
- } else {
- pstmt = getPstmt(t, sql, false);
- }
- dataBind = new DataBind(pstmt);
- meta.bind(persistRequest, this);
- logSql(sql);
- }
-
- /**
- * Execute the delete non-batch.
- */
- public void execute() throws SQLException, OptimisticLockException {
- int rowCount = dataBind.executeUpdate();
- checkRowCount(rowCount);
- }
-
- public void registerDerivedRelationship(DerivedRelationshipData assocBean) {
- throw new RuntimeException("Never called on delete");
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dml;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import javax.persistence.OptimisticLockException;
+
+import com.avaje.ebeaninternal.api.DerivedRelationshipData;
+import com.avaje.ebeaninternal.api.SpiTransaction;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.type.DataBind;
+
+/**
+ * Delete bean handler.
+ */
+public class DeleteHandler extends DmlHandler {
+
+ private final DeleteMeta meta;
+
+ public DeleteHandler(PersistRequestBean> persist, DeleteMeta meta) {
+ super(persist, meta.isEmptyStringAsNull());
+ this.meta = meta;
+ }
+
+ /**
+ * Generate and bind the delete statement.
+ */
+ public void bind() throws SQLException {
+
+ sql = meta.getSql(persistRequest);
+ SpiTransaction t = persistRequest.getTransaction();
+
+ PreparedStatement pstmt;
+ if (persistRequest.isBatched()) {
+ pstmt = getPstmt(t, sql, persistRequest, false);
+ } else {
+ pstmt = getPstmt(t, sql, false);
+ }
+ dataBind = new DataBind(pstmt);
+ meta.bind(persistRequest, this);
+ logSql(sql);
+ }
+
+ /**
+ * Execute the delete non-batch.
+ */
+ public void execute() throws SQLException, OptimisticLockException {
+ int rowCount = dataBind.executeUpdate();
+ checkRowCount(rowCount);
+ }
+
+ public void registerDerivedRelationship(DerivedRelationshipData assocBean) {
+ throw new RuntimeException("Never called on delete");
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteMeta.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteMeta.java
index 8bbf28b9a..f3855e6d2 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteMeta.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteMeta.java
@@ -1,112 +1,112 @@
-package com.avaje.ebeaninternal.server.persist.dml;
-
-import java.sql.SQLException;
-
-import com.avaje.ebean.annotation.ConcurrencyMode;
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
-import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId;
-
-/**
- * Meta data for delete handler. The meta data is for a particular bean type. It
- * is considered immutable and is thread safe.
- */
-public final class DeleteMeta {
-
- private final String sqlVersion;
-
- private final String sqlNone;
-
- private final BindableId id;
-
- private final Bindable version;
-
- private final String tableName;
-
- private final boolean emptyStringAsNull;
-
- public DeleteMeta(boolean emptyStringAsNull, BeanDescriptor> desc, BindableId id, Bindable version) {
- this.emptyStringAsNull = emptyStringAsNull;
- this.tableName = desc.getBaseTable();
- this.id = id;
- this.version = version;
- this.sqlNone = genSql(ConcurrencyMode.NONE);
- this.sqlVersion = genSql(ConcurrencyMode.VERSION);
- }
-
- public boolean isEmptyStringAsNull() {
- return emptyStringAsNull;
- }
-
- /**
- * Return the table name.
- */
- public String getTableName() {
- return tableName;
- }
-
- /**
- * Bind the request based on the concurrency mode.
- */
- public void bind(PersistRequestBean> persist, DmlHandler bind) throws SQLException {
-
- EntityBean bean = persist.getEntityBean();
-
- id.dmlBind(bind, bean);
-
- switch (persist.getConcurrencyMode()) {
- case VERSION:
- version.dmlBind(bind, bean);
- break;
-
- default:
- break;
- }
- }
-
- /**
- * get or generate the sql based on the concurrency mode.
- */
- public String getSql(PersistRequestBean> request) throws SQLException {
-
- if (id.isEmpty()) {
- throw new IllegalStateException("Can not deleteById on " + request.getFullName() + " as no @Id property");
- }
-
- switch (request.determineConcurrencyMode()) {
- case NONE:
- return sqlNone;
-
- case VERSION:
- return sqlVersion;
-
- default:
- throw new RuntimeException("Invalid mode " + request.determineConcurrencyMode());
- }
- }
-
- private String genSql(ConcurrencyMode conMode) {
-
- // delete ... where bcol=? and bc1=? and bc2 is null and ...
-
- GenerateDmlRequest request = new GenerateDmlRequest(emptyStringAsNull);
-
- request.append("delete from ").append(tableName);
- request.append(" where ");
-
- request.setWhereIdMode();
- id.dmlAppend(request);
-
- if (ConcurrencyMode.VERSION.equals(conMode)) {
- if (version == null) {
- return null;
- }
- version.dmlAppend(request);
- }
-
- return request.toString();
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dml;
+
+import java.sql.SQLException;
+
+import com.avaje.ebean.annotation.ConcurrencyMode;
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
+import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId;
+
+/**
+ * Meta data for delete handler. The meta data is for a particular bean type. It
+ * is considered immutable and is thread safe.
+ */
+public final class DeleteMeta {
+
+ private final String sqlVersion;
+
+ private final String sqlNone;
+
+ private final BindableId id;
+
+ private final Bindable version;
+
+ private final String tableName;
+
+ private final boolean emptyStringAsNull;
+
+ public DeleteMeta(boolean emptyStringAsNull, BeanDescriptor> desc, BindableId id, Bindable version) {
+ this.emptyStringAsNull = emptyStringAsNull;
+ this.tableName = desc.getBaseTable();
+ this.id = id;
+ this.version = version;
+ this.sqlNone = genSql(ConcurrencyMode.NONE);
+ this.sqlVersion = genSql(ConcurrencyMode.VERSION);
+ }
+
+ public boolean isEmptyStringAsNull() {
+ return emptyStringAsNull;
+ }
+
+ /**
+ * Return the table name.
+ */
+ public String getTableName() {
+ return tableName;
+ }
+
+ /**
+ * Bind the request based on the concurrency mode.
+ */
+ public void bind(PersistRequestBean> persist, DmlHandler bind) throws SQLException {
+
+ EntityBean bean = persist.getEntityBean();
+
+ id.dmlBind(bind, bean);
+
+ switch (persist.getConcurrencyMode()) {
+ case VERSION:
+ version.dmlBind(bind, bean);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /**
+ * get or generate the sql based on the concurrency mode.
+ */
+ public String getSql(PersistRequestBean> request) throws SQLException {
+
+ if (id.isEmpty()) {
+ throw new IllegalStateException("Can not deleteById on " + request.getFullName() + " as no @Id property");
+ }
+
+ switch (request.determineConcurrencyMode()) {
+ case NONE:
+ return sqlNone;
+
+ case VERSION:
+ return sqlVersion;
+
+ default:
+ throw new RuntimeException("Invalid mode " + request.determineConcurrencyMode());
+ }
+ }
+
+ private String genSql(ConcurrencyMode conMode) {
+
+ // delete ... where bcol=? and bc1=? and bc2 is null and ...
+
+ GenerateDmlRequest request = new GenerateDmlRequest(emptyStringAsNull);
+
+ request.append("delete from ").append(tableName);
+ request.append(" where ");
+
+ request.setWhereIdMode();
+ id.dmlAppend(request);
+
+ if (ConcurrencyMode.VERSION.equals(conMode)) {
+ if (version == null) {
+ return null;
+ }
+ version.dmlAppend(request);
+ }
+
+ return request.toString();
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersister.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersister.java
index 3e148e129..26d25fa4a 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersister.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersister.java
@@ -1,103 +1,103 @@
-package com.avaje.ebeaninternal.server.persist.dml;
-
-import java.sql.SQLException;
-
-import javax.persistence.PersistenceException;
-
-import com.avaje.ebeaninternal.api.SpiTransaction;
-import com.avaje.ebeaninternal.server.core.PersistRequest;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.lib.util.StringHelper;
-import com.avaje.ebeaninternal.server.persist.BeanPersister;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Bean persister that uses the Handler and Meta objects.
- *
- * The design of this is based on the immutable Meta objects. They hold a
- * information in the form of lists of Bindable objects. This effectively
- * flattens the structure of the bean with embedded and associated objects into
- * a flat list of Bindable objects.
- *
+ * The design of this is based on the immutable Meta objects. They hold a
+ * information in the form of lists of Bindable objects. This effectively
+ * flattens the structure of the bean with embedded and associated objects into
+ * a flat list of Bindable objects.
+ *
+ */
+public final class DmlBeanPersister implements BeanPersister {
+
+ private static final Logger logger = LoggerFactory.getLogger(DmlBeanPersister.class);
+
+ private final UpdateMeta updateMeta;
+
+ private final InsertMeta insertMeta;
+
+ private final DeleteMeta deleteMeta;
+
+
+ public DmlBeanPersister(UpdateMeta updateMeta, InsertMeta insertMeta, DeleteMeta deleteMeta) {
+
+ this.updateMeta = updateMeta;
+ this.insertMeta = insertMeta;
+ this.deleteMeta = deleteMeta;
+ }
+
+ /**
+ * execute the bean delete request.
+ */
+ public void delete(PersistRequestBean> request) {
+
+ DeleteHandler delete = new DeleteHandler(request, deleteMeta);
+ execute(request, delete);
+ }
+
+ /**
+ * execute the bean insert request.
+ */
+ public void insert(PersistRequestBean> request) {
+
+ InsertHandler insert = new InsertHandler(request, insertMeta);
+ execute(request, insert);
+ }
+
+ /**
+ * execute the bean update request.
+ */
+ public void update(PersistRequestBean> request) {
+
+ UpdateHandler update = new UpdateHandler(request, updateMeta);
+ execute(request, update);
+ }
+
+ /**
+ * execute request taking batching into account.
+ */
+ private void execute(PersistRequestBean> request, PersistHandler handler) {
+
+ boolean batched = request.isBatched();
+ try {
+ handler.bind();
+ if (batched) {
+ handler.addBatch();
+ } else {
+ handler.execute();
+ }
+
+ } catch (SQLException e) {
+ // log the error to the transaction log
+ String errMsg = StringHelper.replaceStringMulti(e.getMessage(), new String[]{"\r","\n"}, "\\n ");
+ String msg = "ERROR executing DML bindLog["+handler.getBindLog()+"] error["+errMsg+"]";
+ if (request.getTransaction().isLogSummary()) {
+ request.getTransaction().logSummary(msg);
+ }
+ throw new PersistenceException(msg, e);
+
+ } finally {
+ if (!batched && handler != null) {
+ try {
+ handler.close();
+ } catch (SQLException e) {
+ logger.error(null, e);
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersisterFactory.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersisterFactory.java
index 0e36acc70..5bfcf5957 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersisterFactory.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersisterFactory.java
@@ -1,33 +1,33 @@
-package com.avaje.ebeaninternal.server.persist.dml;
-
-import com.avaje.ebean.config.dbplatform.DatabasePlatform;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.persist.BeanPersister;
-import com.avaje.ebeaninternal.server.persist.BeanPersisterFactory;
-
-/**
- * Factory for creating a DmlBeanPersister for a bean type.
- */
-public class DmlBeanPersisterFactory implements BeanPersisterFactory {
-
- private final MetaFactory metaFactory;
-
- public DmlBeanPersisterFactory(DatabasePlatform dbPlatform) {
- this.metaFactory = new MetaFactory(dbPlatform);
- }
-
-
- /**
- * Create a DmlBeanPersister for the given bean type.
- */
- public BeanPersister create(BeanDescriptor> desc) {
-
- UpdateMeta updMeta = metaFactory.createUpdate(desc);
- DeleteMeta delMeta = metaFactory.createDelete(desc);
- InsertMeta insMeta = metaFactory.createInsert(desc);
-
- return new DmlBeanPersister(updMeta, insMeta, delMeta);
-
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dml;
+
+import com.avaje.ebean.config.dbplatform.DatabasePlatform;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.persist.BeanPersister;
+import com.avaje.ebeaninternal.server.persist.BeanPersisterFactory;
+
+/**
+ * Factory for creating a DmlBeanPersister for a bean type.
+ */
+public class DmlBeanPersisterFactory implements BeanPersisterFactory {
+
+ private final MetaFactory metaFactory;
+
+ public DmlBeanPersisterFactory(DatabasePlatform dbPlatform) {
+ this.metaFactory = new MetaFactory(dbPlatform);
+ }
+
+
+ /**
+ * Create a DmlBeanPersister for the given bean type.
+ */
+ public BeanPersister create(BeanDescriptor> desc) {
+
+ UpdateMeta updMeta = metaFactory.createUpdate(desc);
+ DeleteMeta delMeta = metaFactory.createDelete(desc);
+ InsertMeta insMeta = metaFactory.createInsert(desc);
+
+ return new DmlBeanPersister(updMeta, insMeta, delMeta);
+
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlHandler.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlHandler.java
index 016b3bf8f..7668103f3 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlHandler.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlHandler.java
@@ -1,307 +1,307 @@
-package com.avaje.ebeaninternal.server.persist.dml;
-
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.ArrayList;
-
-import javax.persistence.OptimisticLockException;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.avaje.ebean.bean.EntityBean;
-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;
-import com.avaje.ebeaninternal.server.transaction.TransactionManager;
-import com.avaje.ebeaninternal.server.type.DataBind;
-
-/**
- * Base class for Handler implementations.
- */
-public abstract class DmlHandler implements PersistHandler, BindableRequest {
-
- private static final Logger logger = LoggerFactory.getLogger(DmlHandler.class);
-
- /**
- * The originating request.
- */
- protected final PersistRequestBean> persistRequest;
-
- protected final StringBuilder bindLog;
-
- protected final SpiTransaction transaction;
-
- protected final boolean emptyStringToNull;
-
- protected final boolean logLevelSql;
-
- /**
- * The PreparedStatement used for the dml.
- */
- protected DataBind dataBind;
-
- protected String sql;
-
- protected ArrayList updateGenValues;
-
- protected DmlHandler(PersistRequestBean> persistRequest, boolean emptyStringToNull) {
- this.persistRequest = persistRequest;
- this.emptyStringToNull = emptyStringToNull;
- this.transaction = persistRequest.getTransaction();
- this.logLevelSql = transaction.isLogSql();
- if (logLevelSql) {
- this.bindLog = new StringBuilder(50);
- } else {
- this.bindLog = null;
- }
- }
-
- public PersistRequestBean> getPersistRequest() {
- return persistRequest;
- }
-
- /**
- * Get the sql and bind the statement.
- */
- public abstract void bind() throws SQLException;
-
- /**
- * Execute now for non-batch execution.
- */
- public abstract void execute() throws SQLException;
-
- /**
- * Check the rowCount.
- */
- protected void checkRowCount(int rowCount) throws SQLException, OptimisticLockException {
- try {
- persistRequest.checkRowCount(rowCount);
- persistRequest.postExecute();
- } catch (OptimisticLockException e) {
- // add the SQL and bind values to error message
- String m = e.getMessage() + " sql[" + sql + "] bind[" + bindLog + "]";
- persistRequest.getTransaction().logSummary("OptimisticLockException:" + m);
- throw new OptimisticLockException(m, null, e.getEntity());
- }
- }
-
- /**
- * Add this for batch execution.
- */
- public void addBatch() throws SQLException {
- PstmtBatch pstmtBatch = persistRequest.getPstmtBatch();
- if (pstmtBatch != null) {
- pstmtBatch.addBatch(dataBind.getPstmt());
- } else {
- dataBind.getPstmt().addBatch();
- }
- }
-
- /**
- * Close the underlying statement.
- */
- public void close() {
- try {
- if (dataBind != null) {
- dataBind.close();
- }
- } catch (SQLException ex) {
- logger.error(null, ex);
- }
- }
-
- /**
- * Return the bind log.
- */
- public String getBindLog() {
- return bindLog == null ? "" : bindLog.toString();
- }
-
- /**
- * Set the Id value that was bound. This value is used for logging summary
- * level information.
- */
- public void setIdValue(Object idValue) {
- persistRequest.setBoundId(idValue);
- }
-
- /**
- * Log the sql to the transaction log.
- */
- protected void logSql(String sql) {
- if (logLevelSql) {
- if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
- sql = Str.add(sql, "; --bind(", bindLog.toString(), ")");
- }
- transaction.logSql(sql);
- }
- }
-
- /**
- * Bind a raw value. Used to bind the discriminator column.
- */
- public Object bind(String propName, Object value, int sqlType) throws SQLException {
- if (logLevelSql) {
- if (value == null) {
- bindLog.append("null");
- } else {
- String sval = value.toString();
- if (sval.length() > 50) {
- bindLog.append(sval.substring(0, 47)).append("...");
- } else {
- bindLog.append(sval);
- }
- }
- bindLog.append(",");
- }
- dataBind.setObject(value, sqlType);
- return value;
- }
-
- public Object bindNoLog(Object value, int sqlType, String logPlaceHolder) throws SQLException {
- if (logLevelSql) {
- bindLog.append(logPlaceHolder).append(" ");
- }
- dataBind.setObject(value, sqlType);
- return value;
- }
-
- /**
- * Bind the value to the preparedStatement.
- */
- public Object bind(Object value, BeanProperty prop) throws SQLException {
- return bindInternal(logLevelSql, value, prop);
- }
-
- /**
- * Bind the value to the preparedStatement without logging.
- */
- public Object bindNoLog(Object value, BeanProperty prop) throws SQLException {
- return bindInternal(false, value, prop);
- }
-
- private Object bindInternal(boolean log, Object value, BeanProperty prop) throws SQLException {
-
- if (log) {
- if (prop.isLob()) {
- bindLog.append("[LOB]");
- } else {
- String sv = String.valueOf(value);
- if (sv.length() > 50) {
- sv = sv.substring(0, 47) + "...";
- }
- bindLog.append(sv);
- }
- bindLog.append(",");
- }
- // do the actual binding to PreparedStatement
- prop.bind(dataBind, value);
- return value;
- }
-
- /**
- * Register a generated value on a update. This can not be set to the bean
- * until after the where clause has been bound for concurrency checking.
- *
- * GeneratedProperty values are likely going to be used for optimistic
- * concurrency checking. This includes 'counter' and 'update timestamp'
- * generation.
- *
- */
- public void registerUpdateGenValue(BeanProperty prop, EntityBean bean, Object value) {
- if (updateGenValues == null) {
- updateGenValues = new ArrayList();
- }
- updateGenValues.add(new UpdateGenValue(prop, bean, value));
- }
-
- /**
- * Set any update generated values to the bean. Must be called after where
- * clause has been bound.
- */
- public void setUpdateGenValues() {
- if (updateGenValues != null) {
- for (int i = 0; i < updateGenValues.size(); i++) {
- UpdateGenValue updGenVal = updateGenValues.get(i);
- updGenVal.setValue();
- }
- }
- }
-
- /**
- * Check with useGeneratedKeys to get appropriate PreparedStatement.
- */
- protected PreparedStatement getPstmt(SpiTransaction t, String sql, boolean genKeys) throws SQLException {
-
- Connection conn = t.getInternalConnection();
- if (genKeys) {
- // the Id generated is always the first column
- // Required to stop Oracle10 giving us Oracle rowId??
- // Other jdbc drivers seem fine without this hint.
- int[] columns = { 1 };
- return conn.prepareStatement(sql, columns);
-
- } else {
- return conn.prepareStatement(sql);
- }
- }
-
- /**
- * Return a prepared statement taking into account batch requirements.
- */
- protected PreparedStatement getPstmt(SpiTransaction t, String sql, PersistRequestBean> request,
- boolean genKeys) throws SQLException {
-
- BatchedPstmtHolder batch = t.getBatchControl().getPstmtHolder();
- PreparedStatement stmt = batch.getStmt(sql, request);
-
- if (stmt != null) {
- return stmt;
- }
-
- stmt = getPstmt(t, sql, genKeys);
-
- PstmtBatch pstmtBatch = request.getPstmtBatch();
- if (pstmtBatch != null) {
- pstmtBatch.setBatchSize(stmt, t.getBatchControl().getBatchSize());
- }
-
- BatchedPstmt bs = new BatchedPstmt(stmt, genKeys, sql, request.getPstmtBatch(), true);
- batch.addStmt(bs, request);
- return stmt;
- }
-
- /**
- * Hold the values from GeneratedValue that need to be set to the bean
- * property after the where clause has been built.
- */
- private static final class UpdateGenValue {
-
- private final BeanProperty property;
-
- private final EntityBean bean;
-
- private final Object value;
-
- private UpdateGenValue(BeanProperty property, EntityBean bean, Object value) {
- this.property = property;
- this.bean = bean;
- this.value = value;
- }
-
- /**
- * Set the value to the bean property.
- */
- private void setValue() {
- // support PropertyChangeSupport
- property.setValueIntercept(bean, value);
- }
- }
-}
+package com.avaje.ebeaninternal.server.persist.dml;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+
+import javax.persistence.OptimisticLockException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.avaje.ebean.bean.EntityBean;
+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;
+import com.avaje.ebeaninternal.server.transaction.TransactionManager;
+import com.avaje.ebeaninternal.server.type.DataBind;
+
+/**
+ * Base class for Handler implementations.
+ */
+public abstract class DmlHandler implements PersistHandler, BindableRequest {
+
+ private static final Logger logger = LoggerFactory.getLogger(DmlHandler.class);
+
+ /**
+ * The originating request.
+ */
+ protected final PersistRequestBean> persistRequest;
+
+ protected final StringBuilder bindLog;
+
+ protected final SpiTransaction transaction;
+
+ protected final boolean emptyStringToNull;
+
+ protected final boolean logLevelSql;
+
+ /**
+ * The PreparedStatement used for the dml.
+ */
+ protected DataBind dataBind;
+
+ protected String sql;
+
+ protected ArrayList updateGenValues;
+
+ protected DmlHandler(PersistRequestBean> persistRequest, boolean emptyStringToNull) {
+ this.persistRequest = persistRequest;
+ this.emptyStringToNull = emptyStringToNull;
+ this.transaction = persistRequest.getTransaction();
+ this.logLevelSql = transaction.isLogSql();
+ if (logLevelSql) {
+ this.bindLog = new StringBuilder(50);
+ } else {
+ this.bindLog = null;
+ }
+ }
+
+ public PersistRequestBean> getPersistRequest() {
+ return persistRequest;
+ }
+
+ /**
+ * Get the sql and bind the statement.
+ */
+ public abstract void bind() throws SQLException;
+
+ /**
+ * Execute now for non-batch execution.
+ */
+ public abstract void execute() throws SQLException;
+
+ /**
+ * Check the rowCount.
+ */
+ protected void checkRowCount(int rowCount) throws SQLException, OptimisticLockException {
+ try {
+ persistRequest.checkRowCount(rowCount);
+ persistRequest.postExecute();
+ } catch (OptimisticLockException e) {
+ // add the SQL and bind values to error message
+ String m = e.getMessage() + " sql[" + sql + "] bind[" + bindLog + "]";
+ persistRequest.getTransaction().logSummary("OptimisticLockException:" + m);
+ throw new OptimisticLockException(m, null, e.getEntity());
+ }
+ }
+
+ /**
+ * Add this for batch execution.
+ */
+ public void addBatch() throws SQLException {
+ PstmtBatch pstmtBatch = persistRequest.getPstmtBatch();
+ if (pstmtBatch != null) {
+ pstmtBatch.addBatch(dataBind.getPstmt());
+ } else {
+ dataBind.getPstmt().addBatch();
+ }
+ }
+
+ /**
+ * Close the underlying statement.
+ */
+ public void close() {
+ try {
+ if (dataBind != null) {
+ dataBind.close();
+ }
+ } catch (SQLException ex) {
+ logger.error(null, ex);
+ }
+ }
+
+ /**
+ * Return the bind log.
+ */
+ public String getBindLog() {
+ return bindLog == null ? "" : bindLog.toString();
+ }
+
+ /**
+ * Set the Id value that was bound. This value is used for logging summary
+ * level information.
+ */
+ public void setIdValue(Object idValue) {
+ persistRequest.setBoundId(idValue);
+ }
+
+ /**
+ * Log the sql to the transaction log.
+ */
+ protected void logSql(String sql) {
+ if (logLevelSql) {
+ if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
+ sql = Str.add(sql, "; --bind(", bindLog.toString(), ")");
+ }
+ transaction.logSql(sql);
+ }
+ }
+
+ /**
+ * Bind a raw value. Used to bind the discriminator column.
+ */
+ public Object bind(String propName, Object value, int sqlType) throws SQLException {
+ if (logLevelSql) {
+ if (value == null) {
+ bindLog.append("null");
+ } else {
+ String sval = value.toString();
+ if (sval.length() > 50) {
+ bindLog.append(sval.substring(0, 47)).append("...");
+ } else {
+ bindLog.append(sval);
+ }
+ }
+ bindLog.append(",");
+ }
+ dataBind.setObject(value, sqlType);
+ return value;
+ }
+
+ public Object bindNoLog(Object value, int sqlType, String logPlaceHolder) throws SQLException {
+ if (logLevelSql) {
+ bindLog.append(logPlaceHolder).append(" ");
+ }
+ dataBind.setObject(value, sqlType);
+ return value;
+ }
+
+ /**
+ * Bind the value to the preparedStatement.
+ */
+ public Object bind(Object value, BeanProperty prop) throws SQLException {
+ return bindInternal(logLevelSql, value, prop);
+ }
+
+ /**
+ * Bind the value to the preparedStatement without logging.
+ */
+ public Object bindNoLog(Object value, BeanProperty prop) throws SQLException {
+ return bindInternal(false, value, prop);
+ }
+
+ private Object bindInternal(boolean log, Object value, BeanProperty prop) throws SQLException {
+
+ if (log) {
+ if (prop.isLob()) {
+ bindLog.append("[LOB]");
+ } else {
+ String sv = String.valueOf(value);
+ if (sv.length() > 50) {
+ sv = sv.substring(0, 47) + "...";
+ }
+ bindLog.append(sv);
+ }
+ bindLog.append(",");
+ }
+ // do the actual binding to PreparedStatement
+ prop.bind(dataBind, value);
+ return value;
+ }
+
+ /**
+ * Register a generated value on a update. This can not be set to the bean
+ * until after the where clause has been bound for concurrency checking.
+ *
+ * GeneratedProperty values are likely going to be used for optimistic
+ * concurrency checking. This includes 'counter' and 'update timestamp'
+ * generation.
+ *
+ */
+ public void registerUpdateGenValue(BeanProperty prop, EntityBean bean, Object value) {
+ if (updateGenValues == null) {
+ updateGenValues = new ArrayList();
+ }
+ updateGenValues.add(new UpdateGenValue(prop, bean, value));
+ }
+
+ /**
+ * Set any update generated values to the bean. Must be called after where
+ * clause has been bound.
+ */
+ public void setUpdateGenValues() {
+ if (updateGenValues != null) {
+ for (int i = 0; i < updateGenValues.size(); i++) {
+ UpdateGenValue updGenVal = updateGenValues.get(i);
+ updGenVal.setValue();
+ }
+ }
+ }
+
+ /**
+ * Check with useGeneratedKeys to get appropriate PreparedStatement.
+ */
+ protected PreparedStatement getPstmt(SpiTransaction t, String sql, boolean genKeys) throws SQLException {
+
+ Connection conn = t.getInternalConnection();
+ if (genKeys) {
+ // the Id generated is always the first column
+ // Required to stop Oracle10 giving us Oracle rowId??
+ // Other jdbc drivers seem fine without this hint.
+ int[] columns = { 1 };
+ return conn.prepareStatement(sql, columns);
+
+ } else {
+ return conn.prepareStatement(sql);
+ }
+ }
+
+ /**
+ * Return a prepared statement taking into account batch requirements.
+ */
+ protected PreparedStatement getPstmt(SpiTransaction t, String sql, PersistRequestBean> request,
+ boolean genKeys) throws SQLException {
+
+ BatchedPstmtHolder batch = t.getBatchControl().getPstmtHolder();
+ PreparedStatement stmt = batch.getStmt(sql, request);
+
+ if (stmt != null) {
+ return stmt;
+ }
+
+ stmt = getPstmt(t, sql, genKeys);
+
+ PstmtBatch pstmtBatch = request.getPstmtBatch();
+ if (pstmtBatch != null) {
+ pstmtBatch.setBatchSize(stmt, t.getBatchControl().getBatchSize());
+ }
+
+ BatchedPstmt bs = new BatchedPstmt(stmt, genKeys, sql, request.getPstmtBatch(), true);
+ batch.addStmt(bs, request);
+ return stmt;
+ }
+
+ /**
+ * Hold the values from GeneratedValue that need to be set to the bean
+ * property after the where clause has been built.
+ */
+ private static final class UpdateGenValue {
+
+ private final BeanProperty property;
+
+ private final EntityBean bean;
+
+ private final Object value;
+
+ private UpdateGenValue(BeanProperty property, EntityBean bean, Object value) {
+ this.property = property;
+ this.bean = bean;
+ this.value = value;
+ }
+
+ /**
+ * Set the value to the bean property.
+ */
+ private void setValue() {
+ // support PropertyChangeSupport
+ property.setValueIntercept(bean, value);
+ }
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertHandler.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertHandler.java
index 5aeccace0..0a1783640 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertHandler.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertHandler.java
@@ -1,234 +1,234 @@
-package com.avaje.ebeaninternal.server.persist.dml;
-
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.List;
-
-import javax.persistence.OptimisticLockException;
-import javax.persistence.PersistenceException;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.api.DerivedRelationshipData;
-import com.avaje.ebeaninternal.api.SpiEbeanServer;
-import com.avaje.ebeaninternal.api.SpiTransaction;
-import com.avaje.ebeaninternal.server.core.Message;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-import com.avaje.ebeaninternal.server.persist.DmlUtil;
-import com.avaje.ebeaninternal.server.type.DataBind;
-
-/**
- * Insert bean handler.
- */
-public class InsertHandler extends DmlHandler {
- private static final Logger logger = LoggerFactory.getLogger(InsertHandler.class);
-
- /**
- * The associated InsertMeta data.
- */
- private final InsertMeta meta;
-
- /**
- * Set to true when the key is concatenated.
- */
- private final boolean concatinatedKey;
-
- /**
- * Flag set when using getGeneratedKeys.
- */
- private boolean useGeneratedKeys;
-
- /**
- * A SQL Select used to fetch back the Id where generatedKeys is not
- * supported.
- */
- private String selectLastInsertedId;
-
- /**
- * Create to handle the insert execution.
- */
- public InsertHandler(PersistRequestBean> persist, InsertMeta meta) {
- super(persist, meta.isEmptyStringToNull());
- this.meta = meta;
- this.concatinatedKey = meta.isConcatinatedKey();
- }
-
- /**
- * Generate and bind the insert statement.
- */
- public void bind() throws SQLException {
-
- BeanDescriptor> desc = persistRequest.getBeanDescriptor();
- EntityBean bean = persistRequest.getEntityBean();
-
- Object idValue = desc.getId(bean);
-
- boolean withId = !DmlUtil.isNullOrZero(idValue);
-
- // check to see if we are going to use generated keys
- if (!withId) {
- if (concatinatedKey) {
- // expecting a concatenated key that can
- // be built from supplied AssocOne beans
- withId = meta.deriveConcatenatedId(persistRequest);
-
- } else if (meta.supportsGetGeneratedKeys()) {
- // Identity with getGeneratedKeys
- useGeneratedKeys = true;
- } else {
- // use a query to get the last inserted id
- selectLastInsertedId = meta.getSelectLastInsertedId();
- }
- }
-
- SpiTransaction t = persistRequest.getTransaction();
-
- // get the appropriate sql
- sql = meta.getSql(withId);
-
- PreparedStatement pstmt;
- if (persistRequest.isBatched()) {
- pstmt = getPstmt(t, sql, persistRequest, useGeneratedKeys);
- } else {
- pstmt = getPstmt(t, sql, useGeneratedKeys);
- }
- dataBind = new DataBind(pstmt);
-
- // bind the bean property values
- meta.bind(this, bean, withId);
-
- logSql(sql);
- }
-
- /**
- * Check with useGeneratedKeys to get appropriate PreparedStatement.
- */
- @Override
- protected PreparedStatement getPstmt(SpiTransaction t, String sql, boolean useGeneratedKeys) throws SQLException {
- Connection conn = t.getInternalConnection();
- if (useGeneratedKeys) {
- return conn.prepareStatement(sql, meta.getIdentityDbColumns());
-
- } else {
- return conn.prepareStatement(sql);
- }
- }
-
- /**
- * Execute the insert in a normal non batch fashion. Additionally using
- * getGeneratedKeys if required.
- */
- public void execute() throws SQLException, OptimisticLockException {
- int rc = dataBind.executeUpdate();
- if (useGeneratedKeys) {
- // get the auto-increment value back and set into the bean
- getGeneratedKeys();
-
- } else if (selectLastInsertedId != null) {
- // fetch back the Id using a query
- fetchGeneratedKeyUsingSelect();
- }
-
- checkRowCount(rc);
- executeDerivedRelationships();
- persistRequest.postInsert();
- }
-
- protected void executeDerivedRelationships() {
- List derivedRelationships = persistRequest.getDerivedRelationships();
- if (derivedRelationships != null) {
-
- SpiEbeanServer ebeanServer = (SpiEbeanServer)persistRequest.getEbeanServer();
-
- for (int i = 0; i < derivedRelationships.size(); i++) {
- DerivedRelationshipData derivedRelationshipData = derivedRelationships.get(i);
-
- BeanDescriptor> beanDescriptor = ebeanServer.getBeanDescriptor(derivedRelationshipData.getBean().getClass());
-
- BeanProperty prop = beanDescriptor.getBeanProperty(derivedRelationshipData.getLogicalName());
- EntityBean entityBean = (EntityBean)derivedRelationshipData.getBean();
- entityBean._ebean_getIntercept().markPropertyAsChanged(prop.getPropertyIndex());
-
- ebeanServer.update(entityBean, transaction);
- }
- }
- }
-
- /**
- * For non batch insert with generated keys.
- */
- private void getGeneratedKeys() throws SQLException {
-
- ResultSet rset = dataBind.getPstmt().getGeneratedKeys();
- try {
- if (rset.next()) {
- Object idValue = rset.getObject(1);
- if (idValue != null) {
- persistRequest.setGeneratedKey(idValue);
- }
-
- } else {
- throw new PersistenceException(Message.msg("persist.autoinc.norows"));
- }
- } finally {
- try {
- rset.close();
- } catch (SQLException ex) {
- String msg = "Error closing rset for returning generatedKeys?";
- logger.warn(msg, ex);
- }
- }
- }
-
- /**
- * For non batch insert with DBs that do not support getGeneratedKeys. Use a
- * SQL select to fetch back the Id value.
- */
- private void fetchGeneratedKeyUsingSelect() throws SQLException {
-
- Connection conn = transaction.getConnection();
-
- PreparedStatement stmt = null;
- ResultSet rset = null;
- try {
- stmt = conn.prepareStatement(selectLastInsertedId);
- rset = stmt.executeQuery();
- if (rset.next()) {
- Object idValue = rset.getObject(1);
- if (idValue != null) {
- persistRequest.setGeneratedKey(idValue);
- }
- } else {
- throw new PersistenceException(Message.msg("persist.autoinc.norows"));
- }
- } finally {
- try {
- if (rset != null) {
- rset.close();
- }
- } catch (SQLException ex) {
- String msg = "Error closing rset for fetchGeneratedKeyUsingSelect?";
- logger.warn(msg, ex);
- }
- try {
- if (stmt != null) {
- stmt.close();
- }
- } catch (SQLException ex) {
- String msg = "Error closing stmt for fetchGeneratedKeyUsingSelect?";
- logger.warn(msg, ex);
- }
- }
- }
-
- public void registerDerivedRelationship(DerivedRelationshipData derivedRelationship) {
- persistRequest.getTransaction().registerDerivedRelationship(derivedRelationship);
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dml;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+import javax.persistence.OptimisticLockException;
+import javax.persistence.PersistenceException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.api.DerivedRelationshipData;
+import com.avaje.ebeaninternal.api.SpiEbeanServer;
+import com.avaje.ebeaninternal.api.SpiTransaction;
+import com.avaje.ebeaninternal.server.core.Message;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+import com.avaje.ebeaninternal.server.persist.DmlUtil;
+import com.avaje.ebeaninternal.server.type.DataBind;
+
+/**
+ * Insert bean handler.
+ */
+public class InsertHandler extends DmlHandler {
+ private static final Logger logger = LoggerFactory.getLogger(InsertHandler.class);
+
+ /**
+ * The associated InsertMeta data.
+ */
+ private final InsertMeta meta;
+
+ /**
+ * Set to true when the key is concatenated.
+ */
+ private final boolean concatinatedKey;
+
+ /**
+ * Flag set when using getGeneratedKeys.
+ */
+ private boolean useGeneratedKeys;
+
+ /**
+ * A SQL Select used to fetch back the Id where generatedKeys is not
+ * supported.
+ */
+ private String selectLastInsertedId;
+
+ /**
+ * Create to handle the insert execution.
+ */
+ public InsertHandler(PersistRequestBean> persist, InsertMeta meta) {
+ super(persist, meta.isEmptyStringToNull());
+ this.meta = meta;
+ this.concatinatedKey = meta.isConcatinatedKey();
+ }
+
+ /**
+ * Generate and bind the insert statement.
+ */
+ public void bind() throws SQLException {
+
+ BeanDescriptor> desc = persistRequest.getBeanDescriptor();
+ EntityBean bean = persistRequest.getEntityBean();
+
+ Object idValue = desc.getId(bean);
+
+ boolean withId = !DmlUtil.isNullOrZero(idValue);
+
+ // check to see if we are going to use generated keys
+ if (!withId) {
+ if (concatinatedKey) {
+ // expecting a concatenated key that can
+ // be built from supplied AssocOne beans
+ withId = meta.deriveConcatenatedId(persistRequest);
+
+ } else if (meta.supportsGetGeneratedKeys()) {
+ // Identity with getGeneratedKeys
+ useGeneratedKeys = true;
+ } else {
+ // use a query to get the last inserted id
+ selectLastInsertedId = meta.getSelectLastInsertedId();
+ }
+ }
+
+ SpiTransaction t = persistRequest.getTransaction();
+
+ // get the appropriate sql
+ sql = meta.getSql(withId);
+
+ PreparedStatement pstmt;
+ if (persistRequest.isBatched()) {
+ pstmt = getPstmt(t, sql, persistRequest, useGeneratedKeys);
+ } else {
+ pstmt = getPstmt(t, sql, useGeneratedKeys);
+ }
+ dataBind = new DataBind(pstmt);
+
+ // bind the bean property values
+ meta.bind(this, bean, withId);
+
+ logSql(sql);
+ }
+
+ /**
+ * Check with useGeneratedKeys to get appropriate PreparedStatement.
+ */
+ @Override
+ protected PreparedStatement getPstmt(SpiTransaction t, String sql, boolean useGeneratedKeys) throws SQLException {
+ Connection conn = t.getInternalConnection();
+ if (useGeneratedKeys) {
+ return conn.prepareStatement(sql, meta.getIdentityDbColumns());
+
+ } else {
+ return conn.prepareStatement(sql);
+ }
+ }
+
+ /**
+ * Execute the insert in a normal non batch fashion. Additionally using
+ * getGeneratedKeys if required.
+ */
+ public void execute() throws SQLException, OptimisticLockException {
+ int rc = dataBind.executeUpdate();
+ if (useGeneratedKeys) {
+ // get the auto-increment value back and set into the bean
+ getGeneratedKeys();
+
+ } else if (selectLastInsertedId != null) {
+ // fetch back the Id using a query
+ fetchGeneratedKeyUsingSelect();
+ }
+
+ checkRowCount(rc);
+ executeDerivedRelationships();
+ persistRequest.postInsert();
+ }
+
+ protected void executeDerivedRelationships() {
+ List derivedRelationships = persistRequest.getDerivedRelationships();
+ if (derivedRelationships != null) {
+
+ SpiEbeanServer ebeanServer = (SpiEbeanServer)persistRequest.getEbeanServer();
+
+ for (int i = 0; i < derivedRelationships.size(); i++) {
+ DerivedRelationshipData derivedRelationshipData = derivedRelationships.get(i);
+
+ BeanDescriptor> beanDescriptor = ebeanServer.getBeanDescriptor(derivedRelationshipData.getBean().getClass());
+
+ BeanProperty prop = beanDescriptor.getBeanProperty(derivedRelationshipData.getLogicalName());
+ EntityBean entityBean = (EntityBean)derivedRelationshipData.getBean();
+ entityBean._ebean_getIntercept().markPropertyAsChanged(prop.getPropertyIndex());
+
+ ebeanServer.update(entityBean, transaction);
+ }
+ }
+ }
+
+ /**
+ * For non batch insert with generated keys.
+ */
+ private void getGeneratedKeys() throws SQLException {
+
+ ResultSet rset = dataBind.getPstmt().getGeneratedKeys();
+ try {
+ if (rset.next()) {
+ Object idValue = rset.getObject(1);
+ if (idValue != null) {
+ persistRequest.setGeneratedKey(idValue);
+ }
+
+ } else {
+ throw new PersistenceException(Message.msg("persist.autoinc.norows"));
+ }
+ } finally {
+ try {
+ rset.close();
+ } catch (SQLException ex) {
+ String msg = "Error closing rset for returning generatedKeys?";
+ logger.warn(msg, ex);
+ }
+ }
+ }
+
+ /**
+ * For non batch insert with DBs that do not support getGeneratedKeys. Use a
+ * SQL select to fetch back the Id value.
+ */
+ private void fetchGeneratedKeyUsingSelect() throws SQLException {
+
+ Connection conn = transaction.getConnection();
+
+ PreparedStatement stmt = null;
+ ResultSet rset = null;
+ try {
+ stmt = conn.prepareStatement(selectLastInsertedId);
+ rset = stmt.executeQuery();
+ if (rset.next()) {
+ Object idValue = rset.getObject(1);
+ if (idValue != null) {
+ persistRequest.setGeneratedKey(idValue);
+ }
+ } else {
+ throw new PersistenceException(Message.msg("persist.autoinc.norows"));
+ }
+ } finally {
+ try {
+ if (rset != null) {
+ rset.close();
+ }
+ } catch (SQLException ex) {
+ String msg = "Error closing rset for fetchGeneratedKeyUsingSelect?";
+ logger.warn(msg, ex);
+ }
+ try {
+ if (stmt != null) {
+ stmt.close();
+ }
+ } catch (SQLException ex) {
+ String msg = "Error closing stmt for fetchGeneratedKeyUsingSelect?";
+ logger.warn(msg, ex);
+ }
+ }
+ }
+
+ public void registerDerivedRelationship(DerivedRelationshipData derivedRelationship) {
+ persistRequest.getTransaction().registerDerivedRelationship(derivedRelationship);
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertMeta.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertMeta.java
index b202bce00..c6c7cbe17 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertMeta.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertMeta.java
@@ -1,195 +1,195 @@
-package com.avaje.ebeaninternal.server.persist.dml;
-
-import java.sql.SQLException;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebean.config.dbplatform.DatabasePlatform;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.deploy.InheritInfo;
-import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
-import com.avaje.ebeaninternal.server.persist.dmlbind.BindableDiscriminator;
-import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId;
-
-/**
- * Meta data for insert handler. The meta data is for a particular bean type. It
- * is considered immutable and is thread safe.
- */
-public final class InsertMeta {
-
- private final String sqlNullId;
-
- private final String sqlWithId;
-
- private final BindableId id;
-
- private final Bindable discriminator;
-
- private final Bindable all;
-
- private final boolean supportsGetGeneratedKeys;
-
- private final boolean concatinatedKey;
-
- private final String tableName;
-
- /**
- * Used for DB that do not support getGeneratedKeys.
- */
- private final String selectLastInsertedId;
-
- private final Bindable shadowFKey;
-
- private final String[] identityDbColumns;
-
- private final boolean emptyStringToNull;
-
- public InsertMeta(DatabasePlatform dbPlatform, BeanDescriptor> desc, Bindable shadowFKey, BindableId id, Bindable all) {
-
- this.emptyStringToNull = dbPlatform.isTreatEmptyStringsAsNull();
- this.tableName = desc.getBaseTable();
- this.discriminator = getDiscriminator(desc);
- this.id = id;
- this.all = all;
- this.shadowFKey = shadowFKey;
-
- this.sqlWithId = genSql(false);
-
- // only available for single Id property
- if (id.isConcatenated()) {
- // concatenated key
- this.concatinatedKey = true;
- this.identityDbColumns = null;
- this.sqlNullId = null;
- this.supportsGetGeneratedKeys = false;
- this.selectLastInsertedId = null;
-
- } else {
- // insert sql for db identity or sequence insert
- this.concatinatedKey = false;
- if (id.getIdentityColumn() == null) {
- this.identityDbColumns = new String[]{};
- this.supportsGetGeneratedKeys = false;
- this.selectLastInsertedId = null;
- } else {
- this.identityDbColumns = new String[]{id.getIdentityColumn()};
- this.supportsGetGeneratedKeys = dbPlatform.getDbIdentity().isSupportsGetGeneratedKeys();
- this.selectLastInsertedId = desc.getSelectLastInsertedId();
- }
- this.sqlNullId = genSql(true);
- }
- }
-
- private static Bindable getDiscriminator(BeanDescriptor> desc){
- InheritInfo inheritInfo = desc.getInheritInfo();
- if (inheritInfo != null){
- return new BindableDiscriminator(inheritInfo);
- } else {
- return null;
- }
- }
-
- /**
- * Return true if empty strings should be treated as null.
- */
- public boolean isEmptyStringToNull() {
- return emptyStringToNull;
- }
-
- /**
- * Return true if this is a concatenated key.
- */
- public boolean isConcatinatedKey() {
- return concatinatedKey;
- }
-
- public String[] getIdentityDbColumns() {
- return identityDbColumns;
- }
-
- /**
- * Returns sql that is used to fetch back the last inserted id. This will
- * return null if it should not be used.
- *
- * This is only for DB's that do not support getGeneratedKeys. For MS
- * SQLServer 2000 this could return "SELECT (at)(at)IDENTITY as id".
- *
- */
- public String getSelectLastInsertedId() {
- return selectLastInsertedId;
- }
-
- /**
- * Return true if getGeneratedKeys is supported by the underlying jdbc
- * driver and database.
- */
- public boolean supportsGetGeneratedKeys() {
- return supportsGetGeneratedKeys;
- }
-
- /**
- * Return true if the Id can be derived from other property values.
- */
- public boolean deriveConcatenatedId(PersistRequestBean> persist) {
- return id.deriveConcatenatedId(persist);
- }
-
- /**
- * Bind the request based on whether the id value(s) are null.
- */
- public void bind(DmlHandler request, EntityBean bean, boolean withId) throws SQLException {
-
- if (withId) {
- id.dmlBind(request, bean);
- }
- if (shadowFKey != null){
- shadowFKey.dmlBind(request, bean);
- }
- if (discriminator != null){
- discriminator.dmlBind(request, bean);
- }
- all.dmlBind(request, bean);
- }
-
- /**
- * get the sql based whether the id value(s) are null.
- */
- public String getSql(boolean withId) {
-
- if (withId) {
- return sqlWithId;
- } else {
- return sqlNullId;
- }
- }
-
- private String genSql(boolean nullId) {
-
- GenerateDmlRequest request = new GenerateDmlRequest(emptyStringToNull, null, true);
- request.setInsertSetMode();
-
- request.append("insert into ").append(tableName);
- request.append(" (");
-
- if (!nullId) {
- id.dmlAppend(request);
- }
-
- if (shadowFKey != null){
- shadowFKey.dmlAppend(request);
- }
-
- if (discriminator != null){
- discriminator.dmlAppend(request);
- }
-
- all.dmlAppend(request);
-
- request.append(") values (");
- request.append(request.getInsertBindBuffer());
- request.append(")");
-
- return request.toString();
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dml;
+
+import java.sql.SQLException;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebean.config.dbplatform.DatabasePlatform;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.InheritInfo;
+import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
+import com.avaje.ebeaninternal.server.persist.dmlbind.BindableDiscriminator;
+import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId;
+
+/**
+ * Meta data for insert handler. The meta data is for a particular bean type. It
+ * is considered immutable and is thread safe.
+ */
+public final class InsertMeta {
+
+ private final String sqlNullId;
+
+ private final String sqlWithId;
+
+ private final BindableId id;
+
+ private final Bindable discriminator;
+
+ private final Bindable all;
+
+ private final boolean supportsGetGeneratedKeys;
+
+ private final boolean concatinatedKey;
+
+ private final String tableName;
+
+ /**
+ * Used for DB that do not support getGeneratedKeys.
+ */
+ private final String selectLastInsertedId;
+
+ private final Bindable shadowFKey;
+
+ private final String[] identityDbColumns;
+
+ private final boolean emptyStringToNull;
+
+ public InsertMeta(DatabasePlatform dbPlatform, BeanDescriptor> desc, Bindable shadowFKey, BindableId id, Bindable all) {
+
+ this.emptyStringToNull = dbPlatform.isTreatEmptyStringsAsNull();
+ this.tableName = desc.getBaseTable();
+ this.discriminator = getDiscriminator(desc);
+ this.id = id;
+ this.all = all;
+ this.shadowFKey = shadowFKey;
+
+ this.sqlWithId = genSql(false);
+
+ // only available for single Id property
+ if (id.isConcatenated()) {
+ // concatenated key
+ this.concatinatedKey = true;
+ this.identityDbColumns = null;
+ this.sqlNullId = null;
+ this.supportsGetGeneratedKeys = false;
+ this.selectLastInsertedId = null;
+
+ } else {
+ // insert sql for db identity or sequence insert
+ this.concatinatedKey = false;
+ if (id.getIdentityColumn() == null) {
+ this.identityDbColumns = new String[]{};
+ this.supportsGetGeneratedKeys = false;
+ this.selectLastInsertedId = null;
+ } else {
+ this.identityDbColumns = new String[]{id.getIdentityColumn()};
+ this.supportsGetGeneratedKeys = dbPlatform.getDbIdentity().isSupportsGetGeneratedKeys();
+ this.selectLastInsertedId = desc.getSelectLastInsertedId();
+ }
+ this.sqlNullId = genSql(true);
+ }
+ }
+
+ private static Bindable getDiscriminator(BeanDescriptor> desc){
+ InheritInfo inheritInfo = desc.getInheritInfo();
+ if (inheritInfo != null){
+ return new BindableDiscriminator(inheritInfo);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Return true if empty strings should be treated as null.
+ */
+ public boolean isEmptyStringToNull() {
+ return emptyStringToNull;
+ }
+
+ /**
+ * Return true if this is a concatenated key.
+ */
+ public boolean isConcatinatedKey() {
+ return concatinatedKey;
+ }
+
+ public String[] getIdentityDbColumns() {
+ return identityDbColumns;
+ }
+
+ /**
+ * Returns sql that is used to fetch back the last inserted id. This will
+ * return null if it should not be used.
+ *
+ * This is only for DB's that do not support getGeneratedKeys. For MS
+ * SQLServer 2000 this could return "SELECT (at)(at)IDENTITY as id".
+ *
+ */
+ public String getSelectLastInsertedId() {
+ return selectLastInsertedId;
+ }
+
+ /**
+ * Return true if getGeneratedKeys is supported by the underlying jdbc
+ * driver and database.
+ */
+ public boolean supportsGetGeneratedKeys() {
+ return supportsGetGeneratedKeys;
+ }
+
+ /**
+ * Return true if the Id can be derived from other property values.
+ */
+ public boolean deriveConcatenatedId(PersistRequestBean> persist) {
+ return id.deriveConcatenatedId(persist);
+ }
+
+ /**
+ * Bind the request based on whether the id value(s) are null.
+ */
+ public void bind(DmlHandler request, EntityBean bean, boolean withId) throws SQLException {
+
+ if (withId) {
+ id.dmlBind(request, bean);
+ }
+ if (shadowFKey != null){
+ shadowFKey.dmlBind(request, bean);
+ }
+ if (discriminator != null){
+ discriminator.dmlBind(request, bean);
+ }
+ all.dmlBind(request, bean);
+ }
+
+ /**
+ * get the sql based whether the id value(s) are null.
+ */
+ public String getSql(boolean withId) {
+
+ if (withId) {
+ return sqlWithId;
+ } else {
+ return sqlNullId;
+ }
+ }
+
+ private String genSql(boolean nullId) {
+
+ GenerateDmlRequest request = new GenerateDmlRequest(emptyStringToNull, null, true);
+ request.setInsertSetMode();
+
+ request.append("insert into ").append(tableName);
+ request.append(" (");
+
+ if (!nullId) {
+ id.dmlAppend(request);
+ }
+
+ if (shadowFKey != null){
+ shadowFKey.dmlAppend(request);
+ }
+
+ if (discriminator != null){
+ discriminator.dmlAppend(request);
+ }
+
+ all.dmlAppend(request);
+
+ request.append(") values (");
+ request.append(request.getInsertBindBuffer());
+ request.append(")");
+
+ return request.toString();
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/MetaFactory.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/MetaFactory.java
index 5d232221b..76edf43cb 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/MetaFactory.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/MetaFactory.java
@@ -1,112 +1,112 @@
-package com.avaje.ebeaninternal.server.persist.dml;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import com.avaje.ebean.config.dbplatform.DatabasePlatform;
-import com.avaje.ebean.config.dbplatform.DbEncrypt;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
-import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
-import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId;
-import com.avaje.ebeaninternal.server.persist.dmlbind.BindableList;
-import com.avaje.ebeaninternal.server.persist.dmlbind.BindableUnidirectional;
-import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryAssocOnes;
-import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryBaseProperties;
-import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryEmbedded;
-import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryId;
-import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryVersion;
-
-/**
- * Factory for creating InsertMeta UpdateMeta and DeleteMeta.
- */
-public class MetaFactory {
-
- private final FactoryBaseProperties baseFact;
- private final FactoryEmbedded embeddedFact;
- private final FactoryVersion versionFact = new FactoryVersion();
- private final FactoryAssocOnes assocOneFact = new FactoryAssocOnes();
-
- private final FactoryId idFact = new FactoryId();
-
- /**
- * Include Lobs in the base statement. Generally true. Oracle9 used to require
- * a separate statement for Clobs and Blobs.
- */
- private static final boolean includeLobs = true;
-
- private final DatabasePlatform dbPlatform;
-
- private final boolean emptyStringAsNull;
-
- public MetaFactory(DatabasePlatform dbPlatform) {
- this.dbPlatform = dbPlatform;
- this.emptyStringAsNull = dbPlatform.isTreatEmptyStringsAsNull();
-
- // to bind encryption data before or after the encryption key
- DbEncrypt dbEncrypt = dbPlatform.getDbEncrypt();
- boolean bindEncryptDataFirst = dbEncrypt == null ? true : dbEncrypt.isBindEncryptDataFirst();
-
- this.baseFact = new FactoryBaseProperties(bindEncryptDataFirst);
- this.embeddedFact = new FactoryEmbedded(bindEncryptDataFirst);
- }
-
- /**
- * Create the UpdateMeta for the given bean type.
- */
- public UpdateMeta createUpdate(BeanDescriptor> desc) {
-
- List setList = new ArrayList();
-
- baseFact.create(setList, desc, DmlMode.UPDATE, includeLobs);
- embeddedFact.create(setList, desc, DmlMode.UPDATE, includeLobs);
- assocOneFact.create(setList, desc, DmlMode.UPDATE);
-
- BindableId id = idFact.createId(desc);
-
- Bindable ver = versionFact.create(desc);
-
- BindableList setBindable = new BindableList(setList);
-
- return new UpdateMeta(emptyStringAsNull, desc, setBindable, id, ver);
- }
-
- /**
- * Create the DeleteMeta for the given bean type.
- */
- public DeleteMeta createDelete(BeanDescriptor> desc) {
-
- BindableId id = idFact.createId(desc);
-
- Bindable ver = versionFact.create(desc);
-
- return new DeleteMeta(emptyStringAsNull, desc, id, ver);
- }
-
- /**
- * Create the InsertMeta for the given bean type.
- */
- public InsertMeta createInsert(BeanDescriptor> desc) {
-
- BindableId id = idFact.createId(desc);
-
- List allList = new ArrayList();
-
- baseFact.create(allList, desc, DmlMode.INSERT, includeLobs);
- embeddedFact.create(allList, desc, DmlMode.INSERT, includeLobs);
- assocOneFact.create(allList, desc, DmlMode.INSERT);
-
- Bindable allBindable = new BindableList(allList);
-
- BeanPropertyAssocOne> unidirectional = desc.getUnidirectional();
-
- Bindable shadowFkey;
- if (unidirectional == null) {
- shadowFkey = null;
- } else {
- shadowFkey = new BindableUnidirectional(desc, unidirectional);
- }
-
- return new InsertMeta(dbPlatform, desc, shadowFkey, id, allBindable);
- }
-}
+package com.avaje.ebeaninternal.server.persist.dml;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.avaje.ebean.config.dbplatform.DatabasePlatform;
+import com.avaje.ebean.config.dbplatform.DbEncrypt;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
+import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
+import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId;
+import com.avaje.ebeaninternal.server.persist.dmlbind.BindableList;
+import com.avaje.ebeaninternal.server.persist.dmlbind.BindableUnidirectional;
+import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryAssocOnes;
+import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryBaseProperties;
+import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryEmbedded;
+import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryId;
+import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryVersion;
+
+/**
+ * Factory for creating InsertMeta UpdateMeta and DeleteMeta.
+ */
+public class MetaFactory {
+
+ private final FactoryBaseProperties baseFact;
+ private final FactoryEmbedded embeddedFact;
+ private final FactoryVersion versionFact = new FactoryVersion();
+ private final FactoryAssocOnes assocOneFact = new FactoryAssocOnes();
+
+ private final FactoryId idFact = new FactoryId();
+
+ /**
+ * Include Lobs in the base statement. Generally true. Oracle9 used to require
+ * a separate statement for Clobs and Blobs.
+ */
+ private static final boolean includeLobs = true;
+
+ private final DatabasePlatform dbPlatform;
+
+ private final boolean emptyStringAsNull;
+
+ public MetaFactory(DatabasePlatform dbPlatform) {
+ this.dbPlatform = dbPlatform;
+ this.emptyStringAsNull = dbPlatform.isTreatEmptyStringsAsNull();
+
+ // to bind encryption data before or after the encryption key
+ DbEncrypt dbEncrypt = dbPlatform.getDbEncrypt();
+ boolean bindEncryptDataFirst = dbEncrypt == null ? true : dbEncrypt.isBindEncryptDataFirst();
+
+ this.baseFact = new FactoryBaseProperties(bindEncryptDataFirst);
+ this.embeddedFact = new FactoryEmbedded(bindEncryptDataFirst);
+ }
+
+ /**
+ * Create the UpdateMeta for the given bean type.
+ */
+ public UpdateMeta createUpdate(BeanDescriptor> desc) {
+
+ List setList = new ArrayList();
+
+ baseFact.create(setList, desc, DmlMode.UPDATE, includeLobs);
+ embeddedFact.create(setList, desc, DmlMode.UPDATE, includeLobs);
+ assocOneFact.create(setList, desc, DmlMode.UPDATE);
+
+ BindableId id = idFact.createId(desc);
+
+ Bindable ver = versionFact.create(desc);
+
+ BindableList setBindable = new BindableList(setList);
+
+ return new UpdateMeta(emptyStringAsNull, desc, setBindable, id, ver);
+ }
+
+ /**
+ * Create the DeleteMeta for the given bean type.
+ */
+ public DeleteMeta createDelete(BeanDescriptor> desc) {
+
+ BindableId id = idFact.createId(desc);
+
+ Bindable ver = versionFact.create(desc);
+
+ return new DeleteMeta(emptyStringAsNull, desc, id, ver);
+ }
+
+ /**
+ * Create the InsertMeta for the given bean type.
+ */
+ public InsertMeta createInsert(BeanDescriptor> desc) {
+
+ BindableId id = idFact.createId(desc);
+
+ List allList = new ArrayList();
+
+ baseFact.create(allList, desc, DmlMode.INSERT, includeLobs);
+ embeddedFact.create(allList, desc, DmlMode.INSERT, includeLobs);
+ assocOneFact.create(allList, desc, DmlMode.INSERT);
+
+ Bindable allBindable = new BindableList(allList);
+
+ BeanPropertyAssocOne> unidirectional = desc.getUnidirectional();
+
+ Bindable shadowFkey;
+ if (unidirectional == null) {
+ shadowFkey = null;
+ } else {
+ shadowFkey = new BindableUnidirectional(desc, unidirectional);
+ }
+
+ return new InsertMeta(dbPlatform, desc, shadowFkey, id, allBindable);
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/PersistHandler.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/PersistHandler.java
index f431f4b71..731953250 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/PersistHandler.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/PersistHandler.java
@@ -1,34 +1,34 @@
-package com.avaje.ebeaninternal.server.persist.dml;
-
-import java.sql.SQLException;
-
-/**
- * Implementation API for insert update and delete handlers.
- */
-public interface PersistHandler {
-
- /**
- * Return the bind log.
- */
- public String getBindLog();
-
- /**
- * Get the sql and bind the statement.
- */
- public void bind() throws SQLException;
-
- /**
- * Add this for batch execution.
- */
- public void addBatch() throws SQLException;
-
- /**
- * Execute now for non-batch execution.
- */
- public void execute() throws SQLException;
-
- /**
- * Close resources including underlying preparedStatement.
- */
- public void close() throws SQLException;
-}
+package com.avaje.ebeaninternal.server.persist.dml;
+
+import java.sql.SQLException;
+
+/**
+ * Implementation API for insert update and delete handlers.
+ */
+public interface PersistHandler {
+
+ /**
+ * Return the bind log.
+ */
+ public String getBindLog();
+
+ /**
+ * Get the sql and bind the statement.
+ */
+ public void bind() throws SQLException;
+
+ /**
+ * Add this for batch execution.
+ */
+ public void addBatch() throws SQLException;
+
+ /**
+ * Execute now for non-batch execution.
+ */
+ public void execute() throws SQLException;
+
+ /**
+ * Close resources including underlying preparedStatement.
+ */
+ public void close() throws SQLException;
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateHandler.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateHandler.java
index 948180c02..ed60eee0e 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateHandler.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateHandler.java
@@ -1,81 +1,81 @@
-package com.avaje.ebeaninternal.server.persist.dml;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-
-import javax.persistence.OptimisticLockException;
-
-import com.avaje.ebeaninternal.api.DerivedRelationshipData;
-import com.avaje.ebeaninternal.api.SpiTransaction;
-import com.avaje.ebeaninternal.api.SpiUpdatePlan;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.type.DataBind;
-
-/**
- * Update bean handler.
- */
-public class UpdateHandler extends DmlHandler {
-
- private final UpdateMeta meta;
-
- private boolean emptySetClause;
-
- public UpdateHandler(PersistRequestBean> persist, UpdateMeta meta) {
- super(persist, meta.isEmptyStringAsNull());
- this.meta = meta;
- }
-
- /**
- * Generate and bind the update statement.
- */
- public void bind() throws SQLException {
-
- SpiUpdatePlan updatePlan = meta.getUpdatePlan(persistRequest);
-
- if (updatePlan.isEmptySetClause()) {
- emptySetClause = true;
- return;
- }
-
- sql = updatePlan.getSql();
-
- SpiTransaction t = persistRequest.getTransaction();
-
- PreparedStatement pstmt;
- if (persistRequest.isBatched()) {
- pstmt = getPstmt(t, sql, persistRequest, false);
- } else {
- pstmt = getPstmt(t, sql, false);
- }
- dataBind = new DataBind(pstmt);
-
- meta.bind(persistRequest, this, updatePlan);
-
- setUpdateGenValues();
-
- logSql(sql);
- }
-
- @Override
- public void addBatch() throws SQLException {
- if (!emptySetClause){
- super.addBatch();
- }
- }
-
- /**
- * Execute the update in non-batch.
- */
- @Override
- public void execute() throws SQLException, OptimisticLockException {
- if (!emptySetClause){
- int rowCount = dataBind.executeUpdate();
- checkRowCount(rowCount);
- }
- }
-
- public void registerDerivedRelationship(DerivedRelationshipData derivedRelationship) {
- persistRequest.getTransaction().registerDerivedRelationship(derivedRelationship);
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dml;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import javax.persistence.OptimisticLockException;
+
+import com.avaje.ebeaninternal.api.DerivedRelationshipData;
+import com.avaje.ebeaninternal.api.SpiTransaction;
+import com.avaje.ebeaninternal.api.SpiUpdatePlan;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.type.DataBind;
+
+/**
+ * Update bean handler.
+ */
+public class UpdateHandler extends DmlHandler {
+
+ private final UpdateMeta meta;
+
+ private boolean emptySetClause;
+
+ public UpdateHandler(PersistRequestBean> persist, UpdateMeta meta) {
+ super(persist, meta.isEmptyStringAsNull());
+ this.meta = meta;
+ }
+
+ /**
+ * Generate and bind the update statement.
+ */
+ public void bind() throws SQLException {
+
+ SpiUpdatePlan updatePlan = meta.getUpdatePlan(persistRequest);
+
+ if (updatePlan.isEmptySetClause()) {
+ emptySetClause = true;
+ return;
+ }
+
+ sql = updatePlan.getSql();
+
+ SpiTransaction t = persistRequest.getTransaction();
+
+ PreparedStatement pstmt;
+ if (persistRequest.isBatched()) {
+ pstmt = getPstmt(t, sql, persistRequest, false);
+ } else {
+ pstmt = getPstmt(t, sql, false);
+ }
+ dataBind = new DataBind(pstmt);
+
+ meta.bind(persistRequest, this, updatePlan);
+
+ setUpdateGenValues();
+
+ logSql(sql);
+ }
+
+ @Override
+ public void addBatch() throws SQLException {
+ if (!emptySetClause){
+ super.addBatch();
+ }
+ }
+
+ /**
+ * Execute the update in non-batch.
+ */
+ @Override
+ public void execute() throws SQLException, OptimisticLockException {
+ if (!emptySetClause){
+ int rowCount = dataBind.executeUpdate();
+ checkRowCount(rowCount);
+ }
+ }
+
+ public void registerDerivedRelationship(DerivedRelationshipData derivedRelationship) {
+ persistRequest.getTransaction().registerDerivedRelationship(derivedRelationship);
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java
index 4a3a303c1..afadc9090 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java
@@ -1,191 +1,191 @@
-package com.avaje.ebeaninternal.server.persist.dml;
-
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.avaje.ebean.annotation.ConcurrencyMode;
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebean.bean.EntityBeanIntercept;
-import com.avaje.ebeaninternal.api.SpiUpdatePlan;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
-import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId;
-import com.avaje.ebeaninternal.server.persist.dmlbind.BindableList;
-
-/**
- * Meta data for update handler. The meta data is for a particular bean type. It
- * is considered immutable and is thread safe.
- */
-public final class UpdateMeta {
-
- private final String sqlVersion;
-
- private final String sqlNone;
-
- private final BindableList set;
- private final BindableId id;
- private final Bindable version;
-
- private final String tableName;
-
- private final UpdatePlan modeNoneUpdatePlan;
- private final UpdatePlan modeVersionUpdatePlan;
-
- private final boolean emptyStringAsNull;
-
- public UpdateMeta(boolean emptyStringAsNull, BeanDescriptor> desc, BindableList set, BindableId id, Bindable version) {
- this.emptyStringAsNull = emptyStringAsNull;
- this.tableName = desc.getBaseTable();
- this.set = set;
- this.id = id;
- this.version = version;
-
- this.sqlNone = genSql(ConcurrencyMode.NONE, null, set);
- this.sqlVersion = genSql(ConcurrencyMode.VERSION, null, set);
-
- this.modeNoneUpdatePlan = new UpdatePlan(ConcurrencyMode.NONE, sqlNone, set);
- this.modeVersionUpdatePlan = new UpdatePlan(ConcurrencyMode.VERSION, sqlVersion, set);
- }
-
- /**
- * Return true if empty strings should be treated as null.
- */
- public boolean isEmptyStringAsNull() {
- return emptyStringAsNull;
- }
-
- /**
- * Return the base table name.
- */
- public String getTableName() {
- return tableName;
- }
-
- /**
- * Bind the request based on the concurrency mode.
- */
- public void bind(PersistRequestBean> persist, DmlHandler bind, SpiUpdatePlan updatePlan) throws SQLException {
-
- EntityBean bean = persist.getEntityBean();
-
- updatePlan.bindSet(bind, bean);
-
- id.dmlBind(bind, bean);
-
- switch (persist.getConcurrencyMode()) {
- case VERSION:
- version.dmlBind(bind, bean);
- break;
-
- default:
- break;
- }
- }
-
- /**
- * get or generate the sql based on the concurrency mode.
- */
- public SpiUpdatePlan getUpdatePlan(PersistRequestBean> request) {
-
- ConcurrencyMode mode = request.determineConcurrencyMode();
- if (request.isDynamicUpdateSql()) {
- return getDynamicUpdatePlan(mode, request);
- }
-
- // 'full bean' update...
- switch (mode) {
- case NONE:
- return modeNoneUpdatePlan;
-
- case VERSION:
- return modeVersionUpdatePlan;
-
- default:
- throw new RuntimeException("Invalid mode " + mode);
- }
- }
-
- private SpiUpdatePlan getDynamicUpdatePlan(ConcurrencyMode mode, PersistRequestBean> persistRequest) {
-
-
- // we can use a cached UpdatePlan for the changed properties
-
- EntityBeanIntercept ebi = persistRequest.getEntityBeanIntercept();
- int hash = ebi.getDirtyPropertyHash();
-
- BeanDescriptor> beanDescriptor = persistRequest.getBeanDescriptor();
-
- BeanProperty versionProperty = beanDescriptor.getVersionProperty();
- if (versionProperty != null) {
- if (ebi.isLoadedProperty(versionProperty.getPropertyIndex())) {
- hash = hash * 31 + 7;
- }
- }
-
- Integer key = Integer.valueOf(hash);
-
- SpiUpdatePlan updatePlan = beanDescriptor.getUpdatePlan(key);
- if (updatePlan != null) {
- return updatePlan;
- }
-
- // build a new UpdatePlan and cache it
-
- // build a bindableList that only contains the changed properties
- List list = new ArrayList();
- set.addToUpdate(persistRequest, list);
- BindableList bindableList = new BindableList(list);
-
- // build the SQL for this update statement
- String sql = genSql(mode, persistRequest, bindableList);
-
- updatePlan = new UpdatePlan(key, mode, sql, bindableList);
-
- // add the UpdatePlan to the cache
- beanDescriptor.putUpdatePlan(key, updatePlan);
-
- return updatePlan;
- }
-
- private String genSql(ConcurrencyMode conMode, PersistRequestBean> persistRequest, BindableList bindableList) {
-
- // update set col0=?, col1=?, col2=? where bcol=? and bc1=? and bc2=?
-
- GenerateDmlRequest request;
- if (persistRequest == null) {
- // For generation of None and Version DML/SQL
- request = new GenerateDmlRequest(emptyStringAsNull);
- } else {
- request = persistRequest.createGenerateDmlRequest(emptyStringAsNull);
- }
-
- request.append("update ").append(tableName).append(" set ");
-
- request.setUpdateSetMode();
- bindableList.dmlAppend(request);
-
- if (request.getBindColumnCount() == 0) {
- // update properties must have been updatable=false
- // with the result that nothing is in the set clause
- return null;
- }
-
- request.append(" where ");
-
- request.setWhereIdMode();
- id.dmlAppend(request);
-
- if (ConcurrencyMode.VERSION.equals(conMode)) {
- if (version == null) {
- return null;
- }
- version.dmlAppend(request);
- }
-
- return request.toString();
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dml;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.avaje.ebean.annotation.ConcurrencyMode;
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebean.bean.EntityBeanIntercept;
+import com.avaje.ebeaninternal.api.SpiUpdatePlan;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
+import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId;
+import com.avaje.ebeaninternal.server.persist.dmlbind.BindableList;
+
+/**
+ * Meta data for update handler. The meta data is for a particular bean type. It
+ * is considered immutable and is thread safe.
+ */
+public final class UpdateMeta {
+
+ private final String sqlVersion;
+
+ private final String sqlNone;
+
+ private final BindableList set;
+ private final BindableId id;
+ private final Bindable version;
+
+ private final String tableName;
+
+ private final UpdatePlan modeNoneUpdatePlan;
+ private final UpdatePlan modeVersionUpdatePlan;
+
+ private final boolean emptyStringAsNull;
+
+ public UpdateMeta(boolean emptyStringAsNull, BeanDescriptor> desc, BindableList set, BindableId id, Bindable version) {
+ this.emptyStringAsNull = emptyStringAsNull;
+ this.tableName = desc.getBaseTable();
+ this.set = set;
+ this.id = id;
+ this.version = version;
+
+ this.sqlNone = genSql(ConcurrencyMode.NONE, null, set);
+ this.sqlVersion = genSql(ConcurrencyMode.VERSION, null, set);
+
+ this.modeNoneUpdatePlan = new UpdatePlan(ConcurrencyMode.NONE, sqlNone, set);
+ this.modeVersionUpdatePlan = new UpdatePlan(ConcurrencyMode.VERSION, sqlVersion, set);
+ }
+
+ /**
+ * Return true if empty strings should be treated as null.
+ */
+ public boolean isEmptyStringAsNull() {
+ return emptyStringAsNull;
+ }
+
+ /**
+ * Return the base table name.
+ */
+ public String getTableName() {
+ return tableName;
+ }
+
+ /**
+ * Bind the request based on the concurrency mode.
+ */
+ public void bind(PersistRequestBean> persist, DmlHandler bind, SpiUpdatePlan updatePlan) throws SQLException {
+
+ EntityBean bean = persist.getEntityBean();
+
+ updatePlan.bindSet(bind, bean);
+
+ id.dmlBind(bind, bean);
+
+ switch (persist.getConcurrencyMode()) {
+ case VERSION:
+ version.dmlBind(bind, bean);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /**
+ * get or generate the sql based on the concurrency mode.
+ */
+ public SpiUpdatePlan getUpdatePlan(PersistRequestBean> request) {
+
+ ConcurrencyMode mode = request.determineConcurrencyMode();
+ if (request.isDynamicUpdateSql()) {
+ return getDynamicUpdatePlan(mode, request);
+ }
+
+ // 'full bean' update...
+ switch (mode) {
+ case NONE:
+ return modeNoneUpdatePlan;
+
+ case VERSION:
+ return modeVersionUpdatePlan;
+
+ default:
+ throw new RuntimeException("Invalid mode " + mode);
+ }
+ }
+
+ private SpiUpdatePlan getDynamicUpdatePlan(ConcurrencyMode mode, PersistRequestBean> persistRequest) {
+
+
+ // we can use a cached UpdatePlan for the changed properties
+
+ EntityBeanIntercept ebi = persistRequest.getEntityBeanIntercept();
+ int hash = ebi.getDirtyPropertyHash();
+
+ BeanDescriptor> beanDescriptor = persistRequest.getBeanDescriptor();
+
+ BeanProperty versionProperty = beanDescriptor.getVersionProperty();
+ if (versionProperty != null) {
+ if (ebi.isLoadedProperty(versionProperty.getPropertyIndex())) {
+ hash = hash * 31 + 7;
+ }
+ }
+
+ Integer key = Integer.valueOf(hash);
+
+ SpiUpdatePlan updatePlan = beanDescriptor.getUpdatePlan(key);
+ if (updatePlan != null) {
+ return updatePlan;
+ }
+
+ // build a new UpdatePlan and cache it
+
+ // build a bindableList that only contains the changed properties
+ List list = new ArrayList();
+ set.addToUpdate(persistRequest, list);
+ BindableList bindableList = new BindableList(list);
+
+ // build the SQL for this update statement
+ String sql = genSql(mode, persistRequest, bindableList);
+
+ updatePlan = new UpdatePlan(key, mode, sql, bindableList);
+
+ // add the UpdatePlan to the cache
+ beanDescriptor.putUpdatePlan(key, updatePlan);
+
+ return updatePlan;
+ }
+
+ private String genSql(ConcurrencyMode conMode, PersistRequestBean> persistRequest, BindableList bindableList) {
+
+ // update set col0=?, col1=?, col2=? where bcol=? and bc1=? and bc2=?
+
+ GenerateDmlRequest request;
+ if (persistRequest == null) {
+ // For generation of None and Version DML/SQL
+ request = new GenerateDmlRequest(emptyStringAsNull);
+ } else {
+ request = persistRequest.createGenerateDmlRequest(emptyStringAsNull);
+ }
+
+ request.append("update ").append(tableName).append(" set ");
+
+ request.setUpdateSetMode();
+ bindableList.dmlAppend(request);
+
+ if (request.getBindColumnCount() == 0) {
+ // update properties must have been updatable=false
+ // with the result that nothing is in the set clause
+ return null;
+ }
+
+ request.append(" where ");
+
+ request.setWhereIdMode();
+ id.dmlAppend(request);
+
+ if (ConcurrencyMode.VERSION.equals(conMode)) {
+ if (version == null) {
+ return null;
+ }
+ version.dmlAppend(request);
+ }
+
+ return request.toString();
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdatePlan.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdatePlan.java
index 16602ae83..093764da7 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdatePlan.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdatePlan.java
@@ -1,128 +1,128 @@
-package com.avaje.ebeaninternal.server.persist.dml;
-
-import java.sql.SQLException;
-
-import com.avaje.ebean.annotation.ConcurrencyMode;
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.api.SpiUpdatePlan;
-import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
-
-/**
- * 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.
- */
- public static final UpdatePlan EMPTY_SET_CLAUSE = new UpdatePlan();
-
- private final Integer key;
-
- private final ConcurrencyMode mode;
-
- private final String sql;
-
- private final Bindable set;
-
- private final long timeCreated;
-
- private final boolean emptySetClause;
-
- private Long timeLastUsed;
-
- /**
- * Create a non cached UpdatePlan.
- */
- public UpdatePlan(ConcurrencyMode mode, String sql, Bindable set) {
-
- this(null, mode, sql, set);
- }
-
- /**
- * Create a UpdatePlan with a given key.
- */
- public UpdatePlan(Integer key, 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();
- }
-
- /**
- * 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;
- }
-
- /**
- * Run the prepared statement binding for the 'update set' properties.
- */
- public void bindSet(DmlHandler bind, EntityBean bean) throws SQLException {
-
- set.dmlBind(bind, bean);
-
- // not strictly 'thread safe' but object assignment is atomic
- Long touched = Long.valueOf(System.currentTimeMillis());
- this.timeLastUsed = touched;
- }
-
- /**
- * Return the time this plan was created.
- */
- public long getTimeCreated() {
- return timeCreated;
- }
-
- /**
- * Return the time this plan was last used.
- */
- public Long getTimeLastUsed() {
-
- // not thread safe but atomic
- return timeLastUsed;
- }
-
- /**
- * 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;
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dml;
+
+import java.sql.SQLException;
+
+import com.avaje.ebean.annotation.ConcurrencyMode;
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.api.SpiUpdatePlan;
+import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
+
+/**
+ * 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.
+ */
+ public static final UpdatePlan EMPTY_SET_CLAUSE = new UpdatePlan();
+
+ private final Integer key;
+
+ private final ConcurrencyMode mode;
+
+ private final String sql;
+
+ private final Bindable set;
+
+ private final long timeCreated;
+
+ private final boolean emptySetClause;
+
+ private Long timeLastUsed;
+
+ /**
+ * Create a non cached UpdatePlan.
+ */
+ public UpdatePlan(ConcurrencyMode mode, String sql, Bindable set) {
+
+ this(null, mode, sql, set);
+ }
+
+ /**
+ * Create a UpdatePlan with a given key.
+ */
+ public UpdatePlan(Integer key, 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();
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Run the prepared statement binding for the 'update set' properties.
+ */
+ public void bindSet(DmlHandler bind, EntityBean bean) throws SQLException {
+
+ set.dmlBind(bind, bean);
+
+ // not strictly 'thread safe' but object assignment is atomic
+ Long touched = Long.valueOf(System.currentTimeMillis());
+ this.timeLastUsed = touched;
+ }
+
+ /**
+ * Return the time this plan was created.
+ */
+ public long getTimeCreated() {
+ return timeCreated;
+ }
+
+ /**
+ * Return the time this plan was last used.
+ */
+ public Long getTimeLastUsed() {
+
+ // not thread safe but atomic
+ return timeLastUsed;
+ }
+
+ /**
+ * 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;
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/Bindable.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/Bindable.java
index 861d69253..a0823a0c9 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/Bindable.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/Bindable.java
@@ -1,42 +1,42 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-import java.util.List;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
-
-/**
- * Item held by Meta objects used to generate and bind bean insert update and
- * delete statements.
- *
- * An implementation is expected to be immutable and thread safe.
- *
- *
- * The design is to take a bean structure with embedded and associated objects
- * etc and flatten that into lists of Bindable objects. These are put into
- * InsertMeta UpdateMeta and DeleteMeta objects to support the generation of DML
- * and binding of statements in a fast and painless manor.
- *
- */
-public interface Bindable {
-
- /**
- * For Updates including only changed properties add the Bindable to the
- * list if it should be included in the 'update set'.
- */
- public void addToUpdate(PersistRequestBean> request, List list);
-
- /**
- * append sql to the buffer with prefix and suffix options.
- */
- public void dmlAppend(GenerateDmlRequest request);
-
- /**
- * Bind given the request and bean. The bean could be the oldValues bean
- * when binding a update or delete where clause with ALL concurrency mode.
- */
- public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException;
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
+
+/**
+ * Item held by Meta objects used to generate and bind bean insert update and
+ * delete statements.
+ *
+ * An implementation is expected to be immutable and thread safe.
+ *
+ *
+ * The design is to take a bean structure with embedded and associated objects
+ * etc and flatten that into lists of Bindable objects. These are put into
+ * InsertMeta UpdateMeta and DeleteMeta objects to support the generation of DML
+ * and binding of statements in a fast and painless manor.
+ *
+ */
+public interface Bindable {
+
+ /**
+ * For Updates including only changed properties add the Bindable to the
+ * list if it should be included in the 'update set'.
+ */
+ public void addToUpdate(PersistRequestBean> request, List list);
+
+ /**
+ * append sql to the buffer with prefix and suffix options.
+ */
+ public void dmlAppend(GenerateDmlRequest request);
+
+ /**
+ * Bind given the request and bean. The bean could be the oldValues bean
+ * when binding a update or delete where clause with ALL concurrency mode.
+ */
+ public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException;
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableAssocOne.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableAssocOne.java
index b7c639aeb..4b0c6aa59 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableAssocOne.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableAssocOne.java
@@ -1,56 +1,56 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-import java.util.List;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.api.DerivedRelationshipData;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
-import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
-import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
-
-/**
- * Bindable for an ManyToOne or OneToOne associated bean.
- */
-public class BindableAssocOne implements Bindable {
-
- private final BeanPropertyAssocOne> assocOne;
-
- private final ImportedId importedId;
-
- public BindableAssocOne(BeanPropertyAssocOne> assocOne) {
- this.assocOne = assocOne;
- this.importedId = assocOne.getImportedId();
- }
-
- public String toString() {
- return "BindableAssocOne " + assocOne;
- }
-
- public void addToUpdate(PersistRequestBean> request, List list) {
- if (request.isAddToUpdate(assocOne)) {
- list.add(this);
- }
- }
-
- public void dmlAppend(GenerateDmlRequest request) {
- importedId.dmlAppend(request);
- }
-
- public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
-
- EntityBean assocBean = (EntityBean)assocOne.getValue(bean);
- Object boundValue = importedId.bind(request, assocBean);
- if (boundValue == null && assocBean != null) {
- // this is the scenario for a derived foreign key
- // which will require an additional update
- // register for post insert of assocBean
- // update of bean set ... importedId.getLogicalName();
- // value of assocBean.getId
- DerivedRelationshipData d = new DerivedRelationshipData(assocBean, assocOne.getName(), bean);
- request.registerDerivedRelationship(d);
- }
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.api.DerivedRelationshipData;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
+import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
+import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
+
+/**
+ * Bindable for an ManyToOne or OneToOne associated bean.
+ */
+public class BindableAssocOne implements Bindable {
+
+ private final BeanPropertyAssocOne> assocOne;
+
+ private final ImportedId importedId;
+
+ public BindableAssocOne(BeanPropertyAssocOne> assocOne) {
+ this.assocOne = assocOne;
+ this.importedId = assocOne.getImportedId();
+ }
+
+ public String toString() {
+ return "BindableAssocOne " + assocOne;
+ }
+
+ public void addToUpdate(PersistRequestBean> request, List list) {
+ if (request.isAddToUpdate(assocOne)) {
+ list.add(this);
+ }
+ }
+
+ public void dmlAppend(GenerateDmlRequest request) {
+ importedId.dmlAppend(request);
+ }
+
+ public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
+
+ EntityBean assocBean = (EntityBean)assocOne.getValue(bean);
+ Object boundValue = importedId.bind(request, assocBean);
+ if (boundValue == null && assocBean != null) {
+ // this is the scenario for a derived foreign key
+ // which will require an additional update
+ // register for post insert of assocBean
+ // update of bean set ... importedId.getLogicalName();
+ // value of assocBean.getId
+ DerivedRelationshipData d = new DerivedRelationshipData(assocBean, assocOne.getName(), bean);
+ request.registerDerivedRelationship(d);
+ }
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableCompound.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableCompound.java
index a70604b29..8f631c50d 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableCompound.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableCompound.java
@@ -1,54 +1,54 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-import java.util.Arrays;
-import java.util.List;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
-import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
-
-/**
- * Bindable for a Immutable Compound value object.
- */
-public class BindableCompound implements Bindable {
-
- private final BindableProperty[] items;
-
- private final BeanPropertyCompound compound;
-
- public BindableCompound(BeanPropertyCompound embProp, List list) {
- this.compound = embProp;
- this.items = list.toArray(new BindableProperty[list.size()]);
- }
-
- public String toString() {
- return "BindableCompound " + compound + " items:" + Arrays.toString(items);
- }
-
- public void dmlAppend(GenerateDmlRequest request) {
-
- for (int i = 0; i < items.length; i++) {
- items[i].dmlAppend(request);
- }
- }
-
- public void addToUpdate(PersistRequestBean> request, List list) {
- if (request.isAddToUpdate(compound)) {
- list.add(this);
- }
- }
-
- public void dmlBind(BindableRequest bindRequest, EntityBean bean) throws SQLException {
-
- // get the compound type value
- Object valueObject = compound.getValue(bean);
-
- // bind each of the underlying scalar values for this compound type
- for (int i = 0; i < items.length; i++) {
- items[i].dmlBindObject(bindRequest, valueObject);
- }
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.List;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
+import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
+
+/**
+ * Bindable for a Immutable Compound value object.
+ */
+public class BindableCompound implements Bindable {
+
+ private final BindableProperty[] items;
+
+ private final BeanPropertyCompound compound;
+
+ public BindableCompound(BeanPropertyCompound embProp, List list) {
+ this.compound = embProp;
+ this.items = list.toArray(new BindableProperty[list.size()]);
+ }
+
+ public String toString() {
+ return "BindableCompound " + compound + " items:" + Arrays.toString(items);
+ }
+
+ public void dmlAppend(GenerateDmlRequest request) {
+
+ for (int i = 0; i < items.length; i++) {
+ items[i].dmlAppend(request);
+ }
+ }
+
+ public void addToUpdate(PersistRequestBean> request, List list) {
+ if (request.isAddToUpdate(compound)) {
+ list.add(this);
+ }
+ }
+
+ public void dmlBind(BindableRequest bindRequest, EntityBean bean) throws SQLException {
+
+ // get the compound type value
+ Object valueObject = compound.getValue(bean);
+
+ // bind each of the underlying scalar values for this compound type
+ for (int i = 0; i < items.length; i++) {
+ items[i].dmlBindObject(bindRequest, valueObject);
+ }
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableDiscriminator.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableDiscriminator.java
index 6d7574e50..dd9a7ad6b 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableDiscriminator.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableDiscriminator.java
@@ -1,45 +1,45 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-import java.util.List;
-
-import javax.persistence.PersistenceException;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.InheritInfo;
-import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
-
-/**
- * Bindable for inserting a discriminator value.
- */
-public class BindableDiscriminator implements Bindable {
-
- private final String columnName;
- private final Object discValue;
- private final int sqlType;
-
- public BindableDiscriminator(InheritInfo inheritInfo) {
- this.columnName = inheritInfo.getDiscriminatorColumn();
- this.discValue = inheritInfo.getDiscriminatorValue();
- this.sqlType = inheritInfo.getDiscriminatorType();
- }
-
- public String toString() {
- return columnName + " = " + discValue;
- }
-
- public void addToUpdate(PersistRequestBean> request, List list) {
- throw new PersistenceException("Never called (only for inserts)");
- }
-
- public void dmlAppend(GenerateDmlRequest request) {
- request.appendColumn(columnName);
- }
-
- public void dmlBind(BindableRequest bindRequest, EntityBean bean) throws SQLException {
-
- bindRequest.bind(columnName, discValue, sqlType);
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import javax.persistence.PersistenceException;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.InheritInfo;
+import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
+
+/**
+ * Bindable for inserting a discriminator value.
+ */
+public class BindableDiscriminator implements Bindable {
+
+ private final String columnName;
+ private final Object discValue;
+ private final int sqlType;
+
+ public BindableDiscriminator(InheritInfo inheritInfo) {
+ this.columnName = inheritInfo.getDiscriminatorColumn();
+ this.discValue = inheritInfo.getDiscriminatorValue();
+ this.sqlType = inheritInfo.getDiscriminatorType();
+ }
+
+ public String toString() {
+ return columnName + " = " + discValue;
+ }
+
+ public void addToUpdate(PersistRequestBean> request, List list) {
+ throw new PersistenceException("Never called (only for inserts)");
+ }
+
+ public void dmlAppend(GenerateDmlRequest request) {
+ request.appendColumn(columnName);
+ }
+
+ public void dmlBind(BindableRequest bindRequest, EntityBean bean) throws SQLException {
+
+ bindRequest.bind(columnName, discValue, sqlType);
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEmbedded.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEmbedded.java
index e4b0697ae..bec5aab8c 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEmbedded.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEmbedded.java
@@ -1,60 +1,60 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-import java.util.Arrays;
-import java.util.List;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
-import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
-
-/**
- * Bindable for a Embedded bean.
- */
-public class BindableEmbedded implements Bindable {
-
- private final Bindable[] items;
-
- private final BeanPropertyAssocOne> embProp;
-
- public BindableEmbedded(BeanPropertyAssocOne> embProp, List bindList) {
- this.embProp = embProp;
- this.items = bindList.toArray(new Bindable[bindList.size()]); //this.props = propList.toArray(new BeanProperty[propList.size()]);
- }
-
- public String toString() {
- return "BindableEmbedded " + embProp + " items:" + Arrays.toString(items);
- }
-
- public void dmlAppend(GenerateDmlRequest request) {
-
- for (int i = 0; i < items.length; i++) {
- items[i].dmlAppend(request);
- }
- }
-
- public void addToUpdate(PersistRequestBean> request, List list) {
- if (request.isAddToUpdate(embProp)) {
- list.add(this);
- }
- }
-
- public void dmlBind(BindableRequest bindRequest, EntityBean bean) throws SQLException {
-
- // get the embedded bean
- EntityBean embBean = (EntityBean)embProp.getValue(bean);
- if (embBean == null) {
- for (int i = 0; i < items.length; i++) {
- items[i].dmlBind(bindRequest, null);
- }
- } else {
- //EntityBeanIntercept ebi = embBean._ebean_getIntercept();
- for (int i = 0; i < items.length; i++) {
- //if (ebi.isLoadedProperty(props[i].getPropertyIndex())) {
- items[i].dmlBind(bindRequest, embBean);
- //}
- }
- }
- }
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.List;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
+import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
+
+/**
+ * Bindable for a Embedded bean.
+ */
+public class BindableEmbedded implements Bindable {
+
+ private final Bindable[] items;
+
+ private final BeanPropertyAssocOne> embProp;
+
+ public BindableEmbedded(BeanPropertyAssocOne> embProp, List bindList) {
+ this.embProp = embProp;
+ this.items = bindList.toArray(new Bindable[bindList.size()]); //this.props = propList.toArray(new BeanProperty[propList.size()]);
+ }
+
+ public String toString() {
+ return "BindableEmbedded " + embProp + " items:" + Arrays.toString(items);
+ }
+
+ public void dmlAppend(GenerateDmlRequest request) {
+
+ for (int i = 0; i < items.length; i++) {
+ items[i].dmlAppend(request);
+ }
+ }
+
+ public void addToUpdate(PersistRequestBean> request, List list) {
+ if (request.isAddToUpdate(embProp)) {
+ list.add(this);
+ }
+ }
+
+ public void dmlBind(BindableRequest bindRequest, EntityBean bean) throws SQLException {
+
+ // get the embedded bean
+ EntityBean embBean = (EntityBean)embProp.getValue(bean);
+ if (embBean == null) {
+ for (int i = 0; i < items.length; i++) {
+ items[i].dmlBind(bindRequest, null);
+ }
+ } else {
+ //EntityBeanIntercept ebi = embBean._ebean_getIntercept();
+ for (int i = 0; i < items.length; i++) {
+ //if (ebi.isLoadedProperty(props[i].getPropertyIndex())) {
+ items[i].dmlBind(bindRequest, embBean);
+ //}
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEncryptedProperty.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEncryptedProperty.java
index f32212d7f..44a091299 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEncryptedProperty.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEncryptedProperty.java
@@ -1,69 +1,69 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-import java.sql.Types;
-import java.util.List;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
-
-/**
- * Bindable for a DB encrypted BeanProperty.
- */
-public class BindableEncryptedProperty implements Bindable {
-
- private final BeanProperty prop;
-
- private final boolean bindEncryptDataFirst;
-
- public BindableEncryptedProperty(BeanProperty prop, boolean bindEncryptDataFirst) {
- this.prop = prop;
- this.bindEncryptDataFirst = bindEncryptDataFirst;
- }
-
- public String toString() {
- return prop.toString();
- }
-
- public void addToUpdate(PersistRequestBean> request, List list) {
- if (request.isAddToUpdate(prop)) {
- list.add(this);
- }
- }
-
- public void dmlAppend(GenerateDmlRequest request) {
-
- // columnName = AES_ENCRYPT(?,?)
- request.appendColumn(prop.getDbColumn(), prop.getDbBind());
- }
-
-
- /**
- * Bind a value in a Insert SET clause.
- */
- public void dmlBind(BindableRequest request, EntityBean bean)
- throws SQLException {
-
- Object value = null;
- if (bean != null) {
- value = prop.getValue(bean);
- }
-
- // get Encrypt key
- String encryptKeyValue = prop.getEncryptKey().getStringValue();
-
- if (!bindEncryptDataFirst){
- // H2 encrypt function ... different parameter order
- request.bindNoLog(encryptKeyValue, Types.VARCHAR, prop.getName() + "=****");
- }
- request.bindNoLog(value, prop);
-
- if (bindEncryptDataFirst){
- // MySql, Postgres, Oracle
- request.bindNoLog(encryptKeyValue, Types.VARCHAR, prop.getName() + "=****");
- }
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.List;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
+
+/**
+ * Bindable for a DB encrypted BeanProperty.
+ */
+public class BindableEncryptedProperty implements Bindable {
+
+ private final BeanProperty prop;
+
+ private final boolean bindEncryptDataFirst;
+
+ public BindableEncryptedProperty(BeanProperty prop, boolean bindEncryptDataFirst) {
+ this.prop = prop;
+ this.bindEncryptDataFirst = bindEncryptDataFirst;
+ }
+
+ public String toString() {
+ return prop.toString();
+ }
+
+ public void addToUpdate(PersistRequestBean> request, List list) {
+ if (request.isAddToUpdate(prop)) {
+ list.add(this);
+ }
+ }
+
+ public void dmlAppend(GenerateDmlRequest request) {
+
+ // columnName = AES_ENCRYPT(?,?)
+ request.appendColumn(prop.getDbColumn(), prop.getDbBind());
+ }
+
+
+ /**
+ * Bind a value in a Insert SET clause.
+ */
+ public void dmlBind(BindableRequest request, EntityBean bean)
+ throws SQLException {
+
+ Object value = null;
+ if (bean != null) {
+ value = prop.getValue(bean);
+ }
+
+ // get Encrypt key
+ String encryptKeyValue = prop.getEncryptKey().getStringValue();
+
+ if (!bindEncryptDataFirst){
+ // H2 encrypt function ... different parameter order
+ request.bindNoLog(encryptKeyValue, Types.VARCHAR, prop.getName() + "=****");
+ }
+ request.bindNoLog(value, prop);
+
+ if (bindEncryptDataFirst){
+ // MySql, Postgres, Oracle
+ request.bindNoLog(encryptKeyValue, Types.VARCHAR, prop.getName() + "=****");
+ }
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableId.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableId.java
index d5f5243b9..4ae2966db 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableId.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableId.java
@@ -1,42 +1,42 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-
-/**
- * Adds support for id creation for concatenated ids on intersection tables.
- *
- * Specifically if the concatenated id object is null on insert this can be
- * built from the matching ManyToOne associated beans. For example RoleUserId
- * embeddedId object could be built from the associated Role and User beans.
- *
- *
- * This is only attempted if the id is null when it gets to the insert.
- *
- */
-public interface BindableId extends Bindable {
-
- /**
- * Return true if there is no Id properties at all.
- */
- public boolean isEmpty();
-
- /**
- * Return true if this is a concatenated key.
- */
- public boolean isConcatenated();
-
- /**
- * Return the DB Column to use with genGeneratedKeys.
- */
- public String getIdentityColumn();
-
- /**
- * Create the concatenated id for inserts with PFK relationships.
- *
- * Really only where there are ManyToOne assoc beans that make up the
- * primary key and the values can be got from those.
- *
- */
- public boolean deriveConcatenatedId(PersistRequestBean> persist);
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+
+/**
+ * Adds support for id creation for concatenated ids on intersection tables.
+ *
+ * Specifically if the concatenated id object is null on insert this can be
+ * built from the matching ManyToOne associated beans. For example RoleUserId
+ * embeddedId object could be built from the associated Role and User beans.
+ *
+ *
+ * This is only attempted if the id is null when it gets to the insert.
+ *
+ */
+public interface BindableId extends Bindable {
+
+ /**
+ * Return true if there is no Id properties at all.
+ */
+ public boolean isEmpty();
+
+ /**
+ * Return true if this is a concatenated key.
+ */
+ public boolean isConcatenated();
+
+ /**
+ * Return the DB Column to use with genGeneratedKeys.
+ */
+ public String getIdentityColumn();
+
+ /**
+ * Create the concatenated id for inserts with PFK relationships.
+ *
+ * Really only where there are ManyToOne assoc beans that make up the
+ * primary key and the values can be got from those.
+ *
+ */
+ public boolean deriveConcatenatedId(PersistRequestBean> persist);
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdEmbedded.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdEmbedded.java
index 17db1a309..be1ce40d4 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdEmbedded.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdEmbedded.java
@@ -1,101 +1,101 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-import java.util.Arrays;
-import java.util.List;
-
-import javax.persistence.PersistenceException;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-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.persist.dml.GenerateDmlRequest;
-
-/**
- * Bindable for a EmbeddedId.
- */
-public final class BindableIdEmbedded implements BindableId {
-
- private final BeanPropertyAssocOne> embId;
-
- private final BeanProperty[] props;
-
- private final MatchedImportedProperty[] matches;
-
- public BindableIdEmbedded(BeanPropertyAssocOne> embId, BeanDescriptor> desc) {
- this.embId = embId;
- this.props = embId.getProperties();
- matches = MatchedImportedProperty.build(props, desc);
- }
-
- public boolean isEmpty() {
- return false;
- }
-
- public boolean isConcatenated() {
- return true;
- }
-
- public String getIdentityColumn() {
- // return null for concatenated keys
- return null;
- }
-
- @Override
- public String toString() {
- return embId + " props:" + Arrays.toString(props);
- }
-
- /**
- * Does nothing for BindableId.
- */
- public void addToUpdate(PersistRequestBean> request, List list) {
- // do nothing (id not changing)
- }
-
- public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
-
- EntityBean idValue = (EntityBean)embId.getValue(bean);
-
- for (int i = 0; i < props.length; i++) {
-
- Object value = props[i].getValue(idValue);
- request.bind(value, props[i]);
- }
-
- request.setIdValue(idValue);
- }
-
- public void dmlAppend(GenerateDmlRequest request) {
- for (int i = 0; i < props.length; i++) {
- request.appendColumn(props[i].getDbColumn());
- }
- }
-
- public boolean deriveConcatenatedId(PersistRequestBean> persist) {
-
- if (matches == null) {
- String m = "Matches for the concatinated key columns where not found?"
- + " I expect that the concatinated key was null, and this bean does"
- + " not have ManyToOne assoc beans matching the primary key columns?";
- throw new PersistenceException(m);
- }
-
- EntityBean bean = persist.getEntityBean();
-
- // create the new id
- EntityBean newId = (EntityBean)embId.createEmbeddedId();
-
- // populate it from the assoc one id values...
- for (int i = 0; i < matches.length; i++) {
- matches[i].populate(bean, newId);
- }
-
- // support PropertyChangeSupport
- embId.setValueIntercept(bean, newId);
- return true;
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.persistence.PersistenceException;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+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.persist.dml.GenerateDmlRequest;
+
+/**
+ * Bindable for a EmbeddedId.
+ */
+public final class BindableIdEmbedded implements BindableId {
+
+ private final BeanPropertyAssocOne> embId;
+
+ private final BeanProperty[] props;
+
+ private final MatchedImportedProperty[] matches;
+
+ public BindableIdEmbedded(BeanPropertyAssocOne> embId, BeanDescriptor> desc) {
+ this.embId = embId;
+ this.props = embId.getProperties();
+ matches = MatchedImportedProperty.build(props, desc);
+ }
+
+ public boolean isEmpty() {
+ return false;
+ }
+
+ public boolean isConcatenated() {
+ return true;
+ }
+
+ public String getIdentityColumn() {
+ // return null for concatenated keys
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return embId + " props:" + Arrays.toString(props);
+ }
+
+ /**
+ * Does nothing for BindableId.
+ */
+ public void addToUpdate(PersistRequestBean> request, List list) {
+ // do nothing (id not changing)
+ }
+
+ public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
+
+ EntityBean idValue = (EntityBean)embId.getValue(bean);
+
+ for (int i = 0; i < props.length; i++) {
+
+ Object value = props[i].getValue(idValue);
+ request.bind(value, props[i]);
+ }
+
+ request.setIdValue(idValue);
+ }
+
+ public void dmlAppend(GenerateDmlRequest request) {
+ for (int i = 0; i < props.length; i++) {
+ request.appendColumn(props[i].getDbColumn());
+ }
+ }
+
+ public boolean deriveConcatenatedId(PersistRequestBean> persist) {
+
+ if (matches == null) {
+ String m = "Matches for the concatinated key columns where not found?"
+ + " I expect that the concatinated key was null, and this bean does"
+ + " not have ManyToOne assoc beans matching the primary key columns?";
+ throw new PersistenceException(m);
+ }
+
+ EntityBean bean = persist.getEntityBean();
+
+ // create the new id
+ EntityBean newId = (EntityBean)embId.createEmbeddedId();
+
+ // populate it from the assoc one id values...
+ for (int i = 0; i < matches.length; i++) {
+ matches[i].populate(bean, newId);
+ }
+
+ // support PropertyChangeSupport
+ embId.setValueIntercept(bean, newId);
+ return true;
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdMap.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdMap.java
index b824020b6..dc33dd9b6 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdMap.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdMap.java
@@ -1,95 +1,95 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-
-import javax.persistence.PersistenceException;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
-
-/**
- * Bindable for a concatenated id that is not embedded.
- */
-public final class BindableIdMap implements BindableId {
-
- private final BeanProperty[] uids;
-
- private final MatchedImportedProperty[] matches;
-
- public BindableIdMap(BeanProperty[] uids, BeanDescriptor> desc) {
- this.uids = uids;
- matches = MatchedImportedProperty.build(uids, desc);
- }
-
- public boolean isEmpty() {
- return false;
- }
-
- public boolean isConcatenated() {
- return true;
- }
-
- public String getIdentityColumn() {
- // return null for concatenated keys
- return null;
- }
-
- @Override
- public String toString() {
- return Arrays.toString(uids);
- }
-
- /**
- * Does nothing for BindableId.
- */
- public void addToUpdate(PersistRequestBean> request, List list) {
- // do nothing (id not changing)
- }
-
- public void dmlAppend(GenerateDmlRequest request) {
- for (int i = 0; i < uids.length; i++) {
- request.appendColumn(uids[i].getDbColumn());
- }
- }
-
- public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
-
- LinkedHashMap mapId = new LinkedHashMap();
- for (int i = 0; i < uids.length; i++) {
- Object value = uids[i].getValue(bean);
-
- request.bind(value, uids[i]);
-
- // putting logicalType into map rather than
- // the dbType (which may have been converted).
- mapId.put(uids[i].getName(), value);
- }
- request.setIdValue(mapId);
- }
-
- public boolean deriveConcatenatedId(PersistRequestBean> persist) {
-
- if (matches == null) {
- String m = "Matches for the concatinated key columns where not found?"
- + " I expect that the concatinated key was null, and this bean does"
- + " not have ManyToOne assoc beans matching the primary key columns?";
- throw new PersistenceException(m);
- }
-
- EntityBean bean = persist.getEntityBean();
-
- // populate it from the assoc one id values...
- for (int i = 0; i < matches.length; i++) {
- matches[i].populate(bean, bean);
- }
-
- return true;
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import javax.persistence.PersistenceException;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
+
+/**
+ * Bindable for a concatenated id that is not embedded.
+ */
+public final class BindableIdMap implements BindableId {
+
+ private final BeanProperty[] uids;
+
+ private final MatchedImportedProperty[] matches;
+
+ public BindableIdMap(BeanProperty[] uids, BeanDescriptor> desc) {
+ this.uids = uids;
+ matches = MatchedImportedProperty.build(uids, desc);
+ }
+
+ public boolean isEmpty() {
+ return false;
+ }
+
+ public boolean isConcatenated() {
+ return true;
+ }
+
+ public String getIdentityColumn() {
+ // return null for concatenated keys
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.toString(uids);
+ }
+
+ /**
+ * Does nothing for BindableId.
+ */
+ public void addToUpdate(PersistRequestBean> request, List list) {
+ // do nothing (id not changing)
+ }
+
+ public void dmlAppend(GenerateDmlRequest request) {
+ for (int i = 0; i < uids.length; i++) {
+ request.appendColumn(uids[i].getDbColumn());
+ }
+ }
+
+ public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
+
+ LinkedHashMap mapId = new LinkedHashMap();
+ for (int i = 0; i < uids.length; i++) {
+ Object value = uids[i].getValue(bean);
+
+ request.bind(value, uids[i]);
+
+ // putting logicalType into map rather than
+ // the dbType (which may have been converted).
+ mapId.put(uids[i].getName(), value);
+ }
+ request.setIdValue(mapId);
+ }
+
+ public boolean deriveConcatenatedId(PersistRequestBean> persist) {
+
+ if (matches == null) {
+ String m = "Matches for the concatinated key columns where not found?"
+ + " I expect that the concatinated key was null, and this bean does"
+ + " not have ManyToOne assoc beans matching the primary key columns?";
+ throw new PersistenceException(m);
+ }
+
+ EntityBean bean = persist.getEntityBean();
+
+ // populate it from the assoc one id values...
+ for (int i = 0; i < matches.length; i++) {
+ matches[i].populate(bean, bean);
+ }
+
+ return true;
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdScalar.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdScalar.java
index 5da2cd89b..e17946d92 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdScalar.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdScalar.java
@@ -1,70 +1,70 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-import java.util.List;
-
-import javax.persistence.PersistenceException;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
-
-/**
- * Bindable for a single scalar id property.
- */
-public final class BindableIdScalar implements BindableId {
-
- private final BeanProperty uidProp;
-
- public BindableIdScalar(BeanProperty uidProp) {
- this.uidProp = uidProp;
- }
-
- public boolean isEmpty() {
- return false;
- }
-
- public boolean isConcatenated() {
- return false;
- }
-
- public String getIdentityColumn() {
- return uidProp.getDbColumn();
- }
-
- @Override
- public String toString() {
- return uidProp.toString();
- }
-
- /**
- * Does nothing for BindableId.
- */
- public void addToUpdate(PersistRequestBean> request, List list) {
- // do nothing (id not changing)
- }
-
- /**
- * Should not be called as this is really only for concatenated keys.
- */
- public boolean deriveConcatenatedId(PersistRequestBean> persist) {
- throw new PersistenceException("Should not be called? only for concatinated keys");
- }
-
- public void dmlAppend(GenerateDmlRequest request) {
-
- request.appendColumn(uidProp.getDbColumn());
- }
-
- public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
-
- Object value = uidProp.getValue(bean);
-
- request.bind(value, uidProp);
-
- // used for summary logging
- request.setIdValue(value);
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import javax.persistence.PersistenceException;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
+
+/**
+ * Bindable for a single scalar id property.
+ */
+public final class BindableIdScalar implements BindableId {
+
+ private final BeanProperty uidProp;
+
+ public BindableIdScalar(BeanProperty uidProp) {
+ this.uidProp = uidProp;
+ }
+
+ public boolean isEmpty() {
+ return false;
+ }
+
+ public boolean isConcatenated() {
+ return false;
+ }
+
+ public String getIdentityColumn() {
+ return uidProp.getDbColumn();
+ }
+
+ @Override
+ public String toString() {
+ return uidProp.toString();
+ }
+
+ /**
+ * Does nothing for BindableId.
+ */
+ public void addToUpdate(PersistRequestBean> request, List list) {
+ // do nothing (id not changing)
+ }
+
+ /**
+ * Should not be called as this is really only for concatenated keys.
+ */
+ public boolean deriveConcatenatedId(PersistRequestBean> persist) {
+ throw new PersistenceException("Should not be called? only for concatinated keys");
+ }
+
+ public void dmlAppend(GenerateDmlRequest request) {
+
+ request.appendColumn(uidProp.getDbColumn());
+ }
+
+ public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
+
+ Object value = uidProp.getValue(bean);
+
+ request.bind(value, uidProp);
+
+ // used for summary logging
+ request.setIdValue(value);
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableList.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableList.java
index 641f0367b..e2cbf2be8 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableList.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableList.java
@@ -1,48 +1,48 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-import java.util.List;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
-
-/**
- * List of Bindable items.
- */
-public class BindableList implements Bindable {
-
- private final Bindable[] items;
-
- public BindableList(List list) {
- items = list.toArray(new Bindable[list.size()]);
- }
-
- public void addAll(List list) {
- for (int i = 0; i < items.length; i++) {
- list.add(items[i]);
- }
- }
-
- public void addToUpdate(PersistRequestBean> request, List list) {
- for (int i = 0; i < items.length; i++) {
- items[i].addToUpdate(request, list);
- }
- }
-
- public void dmlAppend(GenerateDmlRequest request) {
-
- for (int i = 0; i < items.length; i++) {
- items[i].dmlAppend(request);
- }
- }
-
- public void dmlBind(BindableRequest bindRequest, EntityBean bean)
- throws SQLException {
-
- for (int i = 0; i < items.length; i++) {
- items[i].dmlBind(bindRequest, bean);
- }
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
+
+/**
+ * List of Bindable items.
+ */
+public class BindableList implements Bindable {
+
+ private final Bindable[] items;
+
+ public BindableList(List list) {
+ items = list.toArray(new Bindable[list.size()]);
+ }
+
+ public void addAll(List list) {
+ for (int i = 0; i < items.length; i++) {
+ list.add(items[i]);
+ }
+ }
+
+ public void addToUpdate(PersistRequestBean> request, List list) {
+ for (int i = 0; i < items.length; i++) {
+ items[i].addToUpdate(request, list);
+ }
+ }
+
+ public void dmlAppend(GenerateDmlRequest request) {
+
+ for (int i = 0; i < items.length; i++) {
+ items[i].dmlAppend(request);
+ }
+ }
+
+ public void dmlBind(BindableRequest bindRequest, EntityBean bean)
+ throws SQLException {
+
+ for (int i = 0; i < items.length; i++) {
+ items[i].dmlBind(bindRequest, bean);
+ }
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableProperty.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableProperty.java
index 98f12be16..44ac14a17 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableProperty.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableProperty.java
@@ -1,59 +1,59 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-import java.util.List;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
-
-/**
- * Bindable for a single BeanProperty.
- */
-public class BindableProperty implements Bindable {
-
- protected final BeanProperty prop;
-
- public BindableProperty(BeanProperty prop) {
- this.prop = prop;
- }
-
- public String toString() {
- return prop.toString();
- }
-
- public void addToUpdate(PersistRequestBean> request, List list) {
- if (request.isAddToUpdate(prop)) {
- list.add(this);
- }
- }
-
- public void dmlAppend(GenerateDmlRequest request) {
- request.appendColumn(prop.getDbColumn());
- }
-
- /**
- * Normal binding of a property value from the bean.
- */
- public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
-
- Object value = null;
- if (bean != null) {
- value = prop.getValue(bean);
- }
- request.bind(value, prop);
- }
-
- /**
- * For compound types bind one of the underlying scalar values for a compound type.
- */
- public void dmlBindObject(BindableRequest request, Object bean) throws SQLException {
-
- Object value = null;
- if (bean != null) {
- value = prop.getValueObject(bean);
- }
- request.bind(value, prop);
- }
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
+
+/**
+ * Bindable for a single BeanProperty.
+ */
+public class BindableProperty implements Bindable {
+
+ protected final BeanProperty prop;
+
+ public BindableProperty(BeanProperty prop) {
+ this.prop = prop;
+ }
+
+ public String toString() {
+ return prop.toString();
+ }
+
+ public void addToUpdate(PersistRequestBean> request, List list) {
+ if (request.isAddToUpdate(prop)) {
+ list.add(this);
+ }
+ }
+
+ public void dmlAppend(GenerateDmlRequest request) {
+ request.appendColumn(prop.getDbColumn());
+ }
+
+ /**
+ * Normal binding of a property value from the bean.
+ */
+ public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
+
+ Object value = null;
+ if (bean != null) {
+ value = prop.getValue(bean);
+ }
+ request.bind(value, prop);
+ }
+
+ /**
+ * For compound types bind one of the underlying scalar values for a compound type.
+ */
+ public void dmlBindObject(BindableRequest request, Object bean) throws SQLException {
+
+ Object value = null;
+ if (bean != null) {
+ value = prop.getValueObject(bean);
+ }
+ request.bind(value, prop);
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyInsertGenerated.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyInsertGenerated.java
index 86c3da57c..9a4f88340 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyInsertGenerated.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyInsertGenerated.java
@@ -1,46 +1,46 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
-import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
-
-/**
- * Bindable for insert on a property with a GeneratedProperty.
- *
- * This is typically a 'insert timestamp', 'update timestamp' or 'counter'.
- *
- */
-public class BindablePropertyInsertGenerated extends BindableProperty {
-
- private final GeneratedProperty gen;
-
- public BindablePropertyInsertGenerated(BeanProperty prop, GeneratedProperty gen) {
- super(prop);
- this.gen = gen;
- }
-
- public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
-
- Object value = gen.getInsertValue(prop, bean);
-
- // generated value should be the correct type
- if (bean != null){
- // support PropertyChangeSupport
- //prop.setValueIntercept(bean, value);
- prop.setValue(bean, value);
- }
- request.bind(value, prop);
- }
-
- /**
- * Always bind on Insert SET.
- */
- @Override
- public void dmlAppend(GenerateDmlRequest request){
- request.appendColumn(prop.getDbColumn());
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
+import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
+
+/**
+ * Bindable for insert on a property with a GeneratedProperty.
+ *
+ * This is typically a 'insert timestamp', 'update timestamp' or 'counter'.
+ *
+ */
+public class BindablePropertyInsertGenerated extends BindableProperty {
+
+ private final GeneratedProperty gen;
+
+ public BindablePropertyInsertGenerated(BeanProperty prop, GeneratedProperty gen) {
+ super(prop);
+ this.gen = gen;
+ }
+
+ public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
+
+ Object value = gen.getInsertValue(prop, bean);
+
+ // generated value should be the correct type
+ if (bean != null){
+ // support PropertyChangeSupport
+ //prop.setValueIntercept(bean, value);
+ prop.setValue(bean, value);
+ }
+ request.bind(value, prop);
+ }
+
+ /**
+ * Always bind on Insert SET.
+ */
+ @Override
+ public void dmlAppend(GenerateDmlRequest request){
+ request.appendColumn(prop.getDbColumn());
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyUpdateGenerated.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyUpdateGenerated.java
index 080bfdc67..15b9afd81 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyUpdateGenerated.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyUpdateGenerated.java
@@ -1,64 +1,64 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-import java.util.List;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
-import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
-
-/**
- * Bindable for update on a property with a GeneratedProperty.
- *
- * This is typically a 'update timestamp' or 'counter'.
- *
- */
-public class BindablePropertyUpdateGenerated extends BindableProperty {
-
- private final GeneratedProperty gen;
-
- public BindablePropertyUpdateGenerated(BeanProperty prop, GeneratedProperty gen) {
- super(prop);
- this.gen = gen;
- }
-
- /**
- * Add BindablePropertyUpdateGenerated if the property is loaded.
- */
- public void addToUpdate(PersistRequestBean> request, List list) {
- if (gen.includeInAllUpdates()) {
- list.add(this);
- } else if (request.isLoadedProperty(prop)) {
- list.add(this);
- }
- }
-
- public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
-
- Object value = gen.getUpdateValue(prop, bean);
-
- // generated value should be the correct type
- request.bind(value, prop);
-
- // only register the update value if it was included
- // in the bean in the first place
- if (request.getPersistRequest().isLoadedProperty(prop)) {
- //if (request.isIncluded(prop)) {
- // need to set the generated value to the bean later
- // after the where clause has been generated
- request.registerUpdateGenValue(prop, bean, value);
- }
- }
-
- /**
- * Always bind on Insert SET.
- */
- @Override
- public void dmlAppend(GenerateDmlRequest request){
- request.appendColumn(prop.getDbColumn());
- }
-
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
+import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
+
+/**
+ * Bindable for update on a property with a GeneratedProperty.
+ *
+ * This is typically a 'update timestamp' or 'counter'.
+ *
+ */
+public class BindablePropertyUpdateGenerated extends BindableProperty {
+
+ private final GeneratedProperty gen;
+
+ public BindablePropertyUpdateGenerated(BeanProperty prop, GeneratedProperty gen) {
+ super(prop);
+ this.gen = gen;
+ }
+
+ /**
+ * Add BindablePropertyUpdateGenerated if the property is loaded.
+ */
+ public void addToUpdate(PersistRequestBean> request, List list) {
+ if (gen.includeInAllUpdates()) {
+ list.add(this);
+ } else if (request.isLoadedProperty(prop)) {
+ list.add(this);
+ }
+ }
+
+ public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
+
+ Object value = gen.getUpdateValue(prop, bean);
+
+ // generated value should be the correct type
+ request.bind(value, prop);
+
+ // only register the update value if it was included
+ // in the bean in the first place
+ if (request.getPersistRequest().isLoadedProperty(prop)) {
+ //if (request.isIncluded(prop)) {
+ // need to set the generated value to the bean later
+ // after the where clause has been generated
+ request.registerUpdateGenValue(prop, bean, value);
+ }
+ }
+
+ /**
+ * Always bind on Insert SET.
+ */
+ @Override
+ public void dmlAppend(GenerateDmlRequest request){
+ request.appendColumn(prop.getDbColumn());
+ }
+
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableRequest.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableRequest.java
index bb9b4e0d1..0eb4b20c0 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableRequest.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableRequest.java
@@ -1,60 +1,60 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.api.DerivedRelationshipData;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-
-/**
- * Request object passed to bindables.
- */
-public interface BindableRequest {
-
- /**
- * Set the id for use with summary level logging.
- */
- public void setIdValue(Object idValue);
-
- /**
- * Bind the value to a PreparedStatement.
- *
- * Takes into account logicalType to dbType conversion if required.
- *
- *
- * Returns the value that was bound (and was potentially converted from
- * logicalType to dbType.
- *
- */
- public Object bind(Object value, BeanProperty prop) throws SQLException;
-
- /**
- * Bind a raw value. Used to bind the discriminator column.
- */
- public Object bind(String propName, Object value, int sqlType) throws SQLException;
-
- /**
- * Bind a raw value with a placeHolder to put into the transaction log.
- */
- public Object bindNoLog(Object value, int sqlType, String logPlaceHolder) throws SQLException;
-
- /**
- * Bind the value to the preparedStatement without logging.
- */
- public Object bindNoLog(Object value, BeanProperty prop) throws SQLException;
-
-
- /**
- * Register the value from a update GeneratedValue. This can only be set to
- * the bean property after the where clause has bean built.
- */
- public void registerUpdateGenValue(BeanProperty prop, EntityBean bean, Object value);
-
- /**
- * Return the original PersistRequest.
- */
- public PersistRequestBean> getPersistRequest();
-
- public void registerDerivedRelationship(DerivedRelationshipData assocBean);
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.api.DerivedRelationshipData;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+
+/**
+ * Request object passed to bindables.
+ */
+public interface BindableRequest {
+
+ /**
+ * Set the id for use with summary level logging.
+ */
+ public void setIdValue(Object idValue);
+
+ /**
+ * Bind the value to a PreparedStatement.
+ *
+ * Takes into account logicalType to dbType conversion if required.
+ *
+ *
+ * Returns the value that was bound (and was potentially converted from
+ * logicalType to dbType.
+ *
+ */
+ public Object bind(Object value, BeanProperty prop) throws SQLException;
+
+ /**
+ * Bind a raw value. Used to bind the discriminator column.
+ */
+ public Object bind(String propName, Object value, int sqlType) throws SQLException;
+
+ /**
+ * Bind a raw value with a placeHolder to put into the transaction log.
+ */
+ public Object bindNoLog(Object value, int sqlType, String logPlaceHolder) throws SQLException;
+
+ /**
+ * Bind the value to the preparedStatement without logging.
+ */
+ public Object bindNoLog(Object value, BeanProperty prop) throws SQLException;
+
+
+ /**
+ * Register the value from a update GeneratedValue. This can only be set to
+ * the bean property after the where clause has bean built.
+ */
+ public void registerUpdateGenValue(BeanProperty prop, EntityBean bean, Object value);
+
+ /**
+ * Return the original PersistRequest.
+ */
+ public PersistRequestBean> getPersistRequest();
+
+ public void registerDerivedRelationship(DerivedRelationshipData assocBean);
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableUnidirectional.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableUnidirectional.java
index faa2f36ec..f27bb195c 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableUnidirectional.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableUnidirectional.java
@@ -1,68 +1,68 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.sql.SQLException;
-import java.util.List;
-
-import javax.persistence.PersistenceException;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
-import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
-import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
-
-/**
- * Bindable for a unidirectional relationship.
- *
- * This inserts the foreign key value that is retrieved from the id of the
- * parentBean.
- *
- */
-public class BindableUnidirectional implements Bindable {
-
- private final BeanPropertyAssocOne> unidirectional;
-
- private final ImportedId importedId;
-
- private final BeanDescriptor> desc;
-
- public BindableUnidirectional(BeanDescriptor> desc, BeanPropertyAssocOne> unidirectional) {
- this.desc = desc;
- this.unidirectional = unidirectional;
- this.importedId = unidirectional.getImportedId();
-
- }
-
- public String toString() {
- return "BindableShadowFKey " + unidirectional;
- }
-
- public void addToUpdate(PersistRequestBean> request, List list) {
- throw new PersistenceException("Never called (for insert only)");
- }
-
- public void dmlAppend(GenerateDmlRequest request) {
- // always included (in insert)
- importedId.dmlAppend(request);
- }
-
-
- public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
-
- PersistRequestBean> persistRequest = request.getPersistRequest();
- Object parentBean = persistRequest.getParentBean();
-
- if (parentBean == null) {
- Class> localType = desc.getBeanType();
- Class> targetType = unidirectional.getTargetType();
-
- String msg = "Error inserting bean [" + localType + "] with unidirectional relationship. ";
- msg += "For inserts you must use cascade save on the master bean [" + targetType + "].";
- throw new PersistenceException(msg);
- }
-
- importedId.bind(request, (EntityBean)parentBean);
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import javax.persistence.PersistenceException;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
+import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
+import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
+
+/**
+ * Bindable for a unidirectional relationship.
+ *
+ * This inserts the foreign key value that is retrieved from the id of the
+ * parentBean.
+ *
+ */
+public class BindableUnidirectional implements Bindable {
+
+ private final BeanPropertyAssocOne> unidirectional;
+
+ private final ImportedId importedId;
+
+ private final BeanDescriptor> desc;
+
+ public BindableUnidirectional(BeanDescriptor> desc, BeanPropertyAssocOne> unidirectional) {
+ this.desc = desc;
+ this.unidirectional = unidirectional;
+ this.importedId = unidirectional.getImportedId();
+
+ }
+
+ public String toString() {
+ return "BindableShadowFKey " + unidirectional;
+ }
+
+ public void addToUpdate(PersistRequestBean> request, List list) {
+ throw new PersistenceException("Never called (for insert only)");
+ }
+
+ public void dmlAppend(GenerateDmlRequest request) {
+ // always included (in insert)
+ importedId.dmlAppend(request);
+ }
+
+
+ public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
+
+ PersistRequestBean> persistRequest = request.getPersistRequest();
+ Object parentBean = persistRequest.getParentBean();
+
+ if (parentBean == null) {
+ Class> localType = desc.getBeanType();
+ Class> targetType = unidirectional.getTargetType();
+
+ String msg = "Error inserting bean [" + localType + "] with unidirectional relationship. ";
+ msg += "For inserts you must use cascade save on the master bean [" + targetType + "].";
+ throw new PersistenceException(msg);
+ }
+
+ importedId.bind(request, (EntityBean)parentBean);
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryAssocOnes.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryAssocOnes.java
index e9054c334..e40175931 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryAssocOnes.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryAssocOnes.java
@@ -1,47 +1,47 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.util.List;
-
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
-import com.avaje.ebeaninternal.server.persist.dml.DmlMode;
-
-/**
- * A factory that builds Bindable for BeanPropertyAssocOne properties.
- */
-public class FactoryAssocOnes {
-
- public FactoryAssocOnes() {
- }
-
- /**
- * Add foreign key columns from associated one beans.
- */
- public List create(List list, BeanDescriptor> desc, DmlMode mode) {
-
- BeanPropertyAssocOne>[] ones = desc.propertiesOneImported();
-
- for (int i = 0; i < ones.length; i++) {
- if (ones[i].isImportedPrimaryKey()){
- // excluded as already part of the primary key
-
- } else {
- switch (mode) {
- case INSERT:
- if (!ones[i].isInsertable()) {
- continue;
- }
- break;
- case UPDATE:
- if (!ones[i].isUpdateable()) {
- continue;
- }
- break;
- }
- list.add(new BindableAssocOne(ones[i]));
- }
- }
-
- return list;
- }
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.util.List;
+
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
+import com.avaje.ebeaninternal.server.persist.dml.DmlMode;
+
+/**
+ * A factory that builds Bindable for BeanPropertyAssocOne properties.
+ */
+public class FactoryAssocOnes {
+
+ public FactoryAssocOnes() {
+ }
+
+ /**
+ * Add foreign key columns from associated one beans.
+ */
+ public List create(List list, BeanDescriptor> desc, DmlMode mode) {
+
+ BeanPropertyAssocOne>[] ones = desc.propertiesOneImported();
+
+ for (int i = 0; i < ones.length; i++) {
+ if (ones[i].isImportedPrimaryKey()){
+ // excluded as already part of the primary key
+
+ } else {
+ switch (mode) {
+ case INSERT:
+ if (!ones[i].isInsertable()) {
+ continue;
+ }
+ break;
+ case UPDATE:
+ if (!ones[i].isUpdateable()) {
+ continue;
+ }
+ break;
+ }
+ list.add(new BindableAssocOne(ones[i]));
+ }
+ }
+
+ return list;
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryBaseProperties.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryBaseProperties.java
index 69de1e8f1..efd36a064 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryBaseProperties.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryBaseProperties.java
@@ -1,67 +1,67 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
-import com.avaje.ebeaninternal.server.persist.dml.DmlMode;
-
-/**
- * Add base properties to the BindableList for a bean type.
- *
- * This excludes unique embedded and associated properties.
- *
- */
-public class FactoryBaseProperties {
-
- private final FactoryProperty factoryProperty;
-
-
- public FactoryBaseProperties(boolean bindEncryptDataFirst) {
- factoryProperty = new FactoryProperty(bindEncryptDataFirst);
- }
-
- /**
- * Add Bindable for the base properties to the list.
- */
- public void create(List list, BeanDescriptor> desc, DmlMode mode, boolean withLobs) {
-
- add(desc.propertiesBaseScalar(), list, desc, mode, withLobs);
-
- BeanPropertyCompound[] compoundProps = desc.propertiesBaseCompound();
- for (int i = 0; i < compoundProps.length; i++) {
- BeanProperty[] props = compoundProps[i].getScalarProperties();
-
- List newList = new ArrayList(props.length);
- addCompound(props, newList, desc, mode, withLobs);
-
- BindableCompound compoundBindable = new BindableCompound(compoundProps[i], newList);
-
- list.add(compoundBindable);
- }
- }
-
- private void add(BeanProperty[] props, List list, BeanDescriptor> desc, DmlMode mode, boolean withLobs) {
-
- for (int i = 0; i < props.length; i++) {
- Bindable item = factoryProperty.create(props[i], mode, withLobs);
- if (item != null) {
- list.add(item);
- }
- }
- }
-
- private void addCompound(BeanProperty[] props, List list, BeanDescriptor> desc, DmlMode mode, boolean withLobs) {
-
- for (int i = 0; i < props.length; i++) {
- BindableProperty item = (BindableProperty) factoryProperty.create(props[i], mode, withLobs);
- if (item != null) {
- list.add(item);
- }
- }
-
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
+import com.avaje.ebeaninternal.server.persist.dml.DmlMode;
+
+/**
+ * Add base properties to the BindableList for a bean type.
+ *
+ * This excludes unique embedded and associated properties.
+ *
+ */
+public class FactoryBaseProperties {
+
+ private final FactoryProperty factoryProperty;
+
+
+ public FactoryBaseProperties(boolean bindEncryptDataFirst) {
+ factoryProperty = new FactoryProperty(bindEncryptDataFirst);
+ }
+
+ /**
+ * Add Bindable for the base properties to the list.
+ */
+ public void create(List list, BeanDescriptor> desc, DmlMode mode, boolean withLobs) {
+
+ add(desc.propertiesBaseScalar(), list, desc, mode, withLobs);
+
+ BeanPropertyCompound[] compoundProps = desc.propertiesBaseCompound();
+ for (int i = 0; i < compoundProps.length; i++) {
+ BeanProperty[] props = compoundProps[i].getScalarProperties();
+
+ List newList = new ArrayList(props.length);
+ addCompound(props, newList, desc, mode, withLobs);
+
+ BindableCompound compoundBindable = new BindableCompound(compoundProps[i], newList);
+
+ list.add(compoundBindable);
+ }
+ }
+
+ private void add(BeanProperty[] props, List list, BeanDescriptor> desc, DmlMode mode, boolean withLobs) {
+
+ for (int i = 0; i < props.length; i++) {
+ Bindable item = factoryProperty.create(props[i], mode, withLobs);
+ if (item != null) {
+ list.add(item);
+ }
+ }
+ }
+
+ private void addCompound(BeanProperty[] props, List list, BeanDescriptor> desc, DmlMode mode, boolean withLobs) {
+
+ for (int i = 0; i < props.length; i++) {
+ BindableProperty item = (BindableProperty) factoryProperty.create(props[i], mode, withLobs);
+ if (item != null) {
+ list.add(item);
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryEmbedded.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryEmbedded.java
index 0480f62c5..f4981e30f 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryEmbedded.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryEmbedded.java
@@ -1,47 +1,47 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import java.util.ArrayList;
-import java.util.List;
-
-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.persist.dml.DmlMode;
-
-/**
- * A factory that builds Bindable for embedded bean properties.
- */
-public class FactoryEmbedded {
-
- private final FactoryProperty factoryProperty;
-
- public FactoryEmbedded(boolean bindEncryptDataFirst) {
- factoryProperty = new FactoryProperty(bindEncryptDataFirst);
- }
-
- /**
- * Add bindable for the embedded properties to the list.
- */
- public void create(List list, BeanDescriptor> desc, DmlMode mode, boolean withLobs) {
-
- BeanPropertyAssocOne>[] embedded = desc.propertiesEmbedded();
-
- for (int j = 0; j < embedded.length; j++) {
-
- BeanProperty[] props = embedded[j].getProperties();
-
- List bindList = new ArrayList(props.length);
-
- for (int i = 0; i < props.length; i++) {
- Bindable item = factoryProperty.create(props[i], mode, withLobs);
- if (item != null){
- bindList.add(item);
- }
- }
-
- list.add(new BindableEmbedded(embedded[j], bindList));
- }
- }
-
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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.persist.dml.DmlMode;
+
+/**
+ * A factory that builds Bindable for embedded bean properties.
+ */
+public class FactoryEmbedded {
+
+ private final FactoryProperty factoryProperty;
+
+ public FactoryEmbedded(boolean bindEncryptDataFirst) {
+ factoryProperty = new FactoryProperty(bindEncryptDataFirst);
+ }
+
+ /**
+ * Add bindable for the embedded properties to the list.
+ */
+ public void create(List list, BeanDescriptor> desc, DmlMode mode, boolean withLobs) {
+
+ BeanPropertyAssocOne>[] embedded = desc.propertiesEmbedded();
+
+ for (int j = 0; j < embedded.length; j++) {
+
+ BeanProperty[] props = embedded[j].getProperties();
+
+ List bindList = new ArrayList(props.length);
+
+ for (int i = 0; i < props.length; i++) {
+ Bindable item = factoryProperty.create(props[i], mode, withLobs);
+ if (item != null){
+ bindList.add(item);
+ }
+ }
+
+ list.add(new BindableEmbedded(embedded[j], bindList));
+ }
+ }
+
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryId.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryId.java
index 9b85bde19..fb703db2b 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryId.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryId.java
@@ -1,33 +1,33 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
-
-/**
- * Create a Bindable for the ids of a bean type.
- */
-public class FactoryId {
-
- public FactoryId() {
- }
-
- /**
- * Add uniqueId properties.
- */
- public BindableId createId(BeanDescriptor> desc) {
-
- BeanProperty id = desc.getIdProperty();
- if (id == null) {
- return new BindableIdEmpty();
-
- }
- if (!id.isEmbedded()) {
- return new BindableIdScalar(id);
-
- } else {
- BeanPropertyAssocOne> embId = (BeanPropertyAssocOne>) id;
- return new BindableIdEmbedded(embId, desc);
- }
- }
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
+
+/**
+ * Create a Bindable for the ids of a bean type.
+ */
+public class FactoryId {
+
+ public FactoryId() {
+ }
+
+ /**
+ * Add uniqueId properties.
+ */
+ public BindableId createId(BeanDescriptor> desc) {
+
+ BeanProperty id = desc.getIdProperty();
+ if (id == null) {
+ return new BindableIdEmpty();
+
+ }
+ if (!id.isEmbedded()) {
+ return new BindableIdScalar(id);
+
+ } else {
+ BeanPropertyAssocOne> embId = (BeanPropertyAssocOne>) id;
+ return new BindableIdEmbedded(embId, desc);
+ }
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryProperty.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryProperty.java
index 0e7a1705e..1582a9d70 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryProperty.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryProperty.java
@@ -1,65 +1,65 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
-import com.avaje.ebeaninternal.server.persist.dml.DmlMode;
-
-/**
- * Creates the appropriate Bindable for a BeanProperty.
- *
- * Lob properties can be excluded and it creates BindablePropertyInsertGenerated
- * and BindablePropertyUpdateGenerated as required.
- *
- */
-public class FactoryProperty {
-
- private final boolean bindEncryptDataFirst;
-
- public FactoryProperty(boolean bindEncryptDataFirst) {
- this.bindEncryptDataFirst = bindEncryptDataFirst;
- }
-
- /**
- * Create a Bindable for the property given the mode and withLobs flag.
- */
- public Bindable create(BeanProperty prop, DmlMode mode, boolean withLobs) {
-
- if (DmlMode.INSERT.equals(mode) && !prop.isDbInsertable()){
- return null;
- }
- if (DmlMode.UPDATE.equals(mode) && !prop.isDbUpdatable()){
- return null;
- }
-
- if (prop.isLob()) {
- if (!withLobs) {
- // Lob exclusion
- return null;
- } else {
- return prop.isDbEncrypted() ? new BindableEncryptedProperty(prop, bindEncryptDataFirst) : new BindableProperty(prop);
- }
- }
-
- GeneratedProperty gen = prop.getGeneratedProperty();
- if (gen != null) {
- if (DmlMode.INSERT.equals(mode)) {
- if (gen.includeInInsert()) {
- return new BindablePropertyInsertGenerated(prop, gen);
- } else {
- return null;
- }
-
- }
- if (DmlMode.UPDATE.equals(mode)) {
- if (gen.includeInUpdate()) {
- return new BindablePropertyUpdateGenerated(prop, gen);
- } else {
- // An 'Insert Timestamp' is never updated
- return null;
- }
- }
- }
-
- return prop.isDbEncrypted() ? new BindableEncryptedProperty(prop, bindEncryptDataFirst) : new BindableProperty(prop);
- }
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
+import com.avaje.ebeaninternal.server.persist.dml.DmlMode;
+
+/**
+ * Creates the appropriate Bindable for a BeanProperty.
+ *
+ * Lob properties can be excluded and it creates BindablePropertyInsertGenerated
+ * and BindablePropertyUpdateGenerated as required.
+ *
+ */
+public class FactoryProperty {
+
+ private final boolean bindEncryptDataFirst;
+
+ public FactoryProperty(boolean bindEncryptDataFirst) {
+ this.bindEncryptDataFirst = bindEncryptDataFirst;
+ }
+
+ /**
+ * Create a Bindable for the property given the mode and withLobs flag.
+ */
+ public Bindable create(BeanProperty prop, DmlMode mode, boolean withLobs) {
+
+ if (DmlMode.INSERT.equals(mode) && !prop.isDbInsertable()){
+ return null;
+ }
+ if (DmlMode.UPDATE.equals(mode) && !prop.isDbUpdatable()){
+ return null;
+ }
+
+ if (prop.isLob()) {
+ if (!withLobs) {
+ // Lob exclusion
+ return null;
+ } else {
+ return prop.isDbEncrypted() ? new BindableEncryptedProperty(prop, bindEncryptDataFirst) : new BindableProperty(prop);
+ }
+ }
+
+ GeneratedProperty gen = prop.getGeneratedProperty();
+ if (gen != null) {
+ if (DmlMode.INSERT.equals(mode)) {
+ if (gen.includeInInsert()) {
+ return new BindablePropertyInsertGenerated(prop, gen);
+ } else {
+ return null;
+ }
+
+ }
+ if (DmlMode.UPDATE.equals(mode)) {
+ if (gen.includeInUpdate()) {
+ return new BindablePropertyUpdateGenerated(prop, gen);
+ } else {
+ // An 'Insert Timestamp' is never updated
+ return null;
+ }
+ }
+ }
+
+ return prop.isDbEncrypted() ? new BindableEncryptedProperty(prop, bindEncryptDataFirst) : new BindableProperty(prop);
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryVersion.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryVersion.java
index 2ace73efa..767a0cbd7 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryVersion.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryVersion.java
@@ -1,27 +1,27 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-
-/**
- * Creates a Bindable to support version concurrency where clauses.
- */
-public class FactoryVersion {
-
-
- public FactoryVersion() {
- }
-
- /**
- * Create a Bindable for the version property(s) for a bean type.
- */
- public Bindable create(BeanDescriptor> desc) {
-
- BeanProperty versionProperty = desc.getVersionProperty();
- if (versionProperty == null) {
- return null;
- }
-
- return new BindableProperty(versionProperty);
- }
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+
+/**
+ * Creates a Bindable to support version concurrency where clauses.
+ */
+public class FactoryVersion {
+
+
+ public FactoryVersion() {
+ }
+
+ /**
+ * Create a Bindable for the version property(s) for a bean type.
+ */
+ public Bindable create(BeanDescriptor> desc) {
+
+ BeanProperty versionProperty = desc.getVersionProperty();
+ if (versionProperty == null) {
+ return null;
+ }
+
+ return new BindableProperty(versionProperty);
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/MatchedImportedProperty.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/MatchedImportedProperty.java
index 2b329710b..2211e9d3d 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/MatchedImportedProperty.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/MatchedImportedProperty.java
@@ -1,86 +1,86 @@
-package com.avaje.ebeaninternal.server.persist.dmlbind;
-
-import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.deploy.BeanProperty;
-import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
-
-/**
- * Matches local embedded id properties to 'matching' properties from a
- * ManyToOne associated bean that is a 'imported primary key'.
- *
- * This object is designed to help BindableIdEmbedded and BindableIdMap to
- * create a concatenated id from the id values from ManyToOne associated beans.
- * This can be done when those ManyToOne associated beans make up the primary
- * key. This typically means the BindableIdEmbedded is for a intersection table
- * of a Many to Many relationship.
- *
- */
-class MatchedImportedProperty {
-
- private final BeanPropertyAssocOne> assocOne;
-
- private final BeanProperty foreignProp;
-
- private final BeanProperty localProp;
-
- protected MatchedImportedProperty(BeanPropertyAssocOne> assocOne, BeanProperty foreignProp,
- BeanProperty localProp) {
- this.assocOne = assocOne;
- this.foreignProp = foreignProp;
- this.localProp = localProp;
- }
-
- protected void populate(EntityBean sourceBean, EntityBean destBean) {
- Object assocBean = assocOne.getValue(sourceBean);
- if (assocBean == null) {
- String msg = "The assoc bean for " + assocOne + " is null?";
- throw new NullPointerException(msg);
- }
-
- Object value = foreignProp.getValue((EntityBean)assocBean);
- localProp.setValue(destBean, value);
- }
-
- /**
- * Create the array of matchedImportedProperty based on the properties and descriptor.
- */
- protected static MatchedImportedProperty[] build(BeanProperty[] props, BeanDescriptor> desc) {
-
- MatchedImportedProperty[] matches = new MatchedImportedProperty[props.length];
-
- for (int i = 0; i < props.length; i++) {
- // find matching assoc one property for dbColumn
- matches[i] = MatchedImportedProperty.findMatch(props[i], desc);
- if (matches[i] == null) {
- // ok, the assoc ones are not on the bean?
- return null;
- }
- }
- return matches;
- }
-
- private static MatchedImportedProperty findMatch(BeanProperty prop, BeanDescriptor> desc) {
-
- // find matching against the local database column
- String dbColumn = prop.getDbColumn();
-
- BeanPropertyAssocOne>[] assocOnes = desc.propertiesOne();
- for (int i = 0; i < assocOnes.length; i++) {
- if (assocOnes[i].isImportedPrimaryKey()) {
-
- // search using the ImportedId from the assoc one
- BeanProperty foreignMatch = assocOnes[i].getImportedId().findMatchImport(dbColumn);
-
- if (foreignMatch != null) {
- return new MatchedImportedProperty(assocOnes[i], foreignMatch, prop);
- }
- }
- }
-
- // there was no matching assoc one property.
- // example UserRole bean missing assoc one to User?
- return null;
- }
-
-}
+package com.avaje.ebeaninternal.server.persist.dmlbind;
+
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
+
+/**
+ * Matches local embedded id properties to 'matching' properties from a
+ * ManyToOne associated bean that is a 'imported primary key'.
+ *
+ * This object is designed to help BindableIdEmbedded and BindableIdMap to
+ * create a concatenated id from the id values from ManyToOne associated beans.
+ * This can be done when those ManyToOne associated beans make up the primary
+ * key. This typically means the BindableIdEmbedded is for a intersection table
+ * of a Many to Many relationship.
+ *
+ */
+class MatchedImportedProperty {
+
+ private final BeanPropertyAssocOne> assocOne;
+
+ private final BeanProperty foreignProp;
+
+ private final BeanProperty localProp;
+
+ protected MatchedImportedProperty(BeanPropertyAssocOne> assocOne, BeanProperty foreignProp,
+ BeanProperty localProp) {
+ this.assocOne = assocOne;
+ this.foreignProp = foreignProp;
+ this.localProp = localProp;
+ }
+
+ protected void populate(EntityBean sourceBean, EntityBean destBean) {
+ Object assocBean = assocOne.getValue(sourceBean);
+ if (assocBean == null) {
+ String msg = "The assoc bean for " + assocOne + " is null?";
+ throw new NullPointerException(msg);
+ }
+
+ Object value = foreignProp.getValue((EntityBean)assocBean);
+ localProp.setValue(destBean, value);
+ }
+
+ /**
+ * Create the array of matchedImportedProperty based on the properties and descriptor.
+ */
+ protected static MatchedImportedProperty[] build(BeanProperty[] props, BeanDescriptor> desc) {
+
+ MatchedImportedProperty[] matches = new MatchedImportedProperty[props.length];
+
+ for (int i = 0; i < props.length; i++) {
+ // find matching assoc one property for dbColumn
+ matches[i] = MatchedImportedProperty.findMatch(props[i], desc);
+ if (matches[i] == null) {
+ // ok, the assoc ones are not on the bean?
+ return null;
+ }
+ }
+ return matches;
+ }
+
+ private static MatchedImportedProperty findMatch(BeanProperty prop, BeanDescriptor> desc) {
+
+ // find matching against the local database column
+ String dbColumn = prop.getDbColumn();
+
+ BeanPropertyAssocOne>[] assocOnes = desc.propertiesOne();
+ for (int i = 0; i < assocOnes.length; i++) {
+ if (assocOnes[i].isImportedPrimaryKey()) {
+
+ // search using the ImportedId from the assoc one
+ BeanProperty foreignMatch = assocOnes[i].getImportedId().findMatchImport(dbColumn);
+
+ if (foreignMatch != null) {
+ return new MatchedImportedProperty(assocOnes[i], foreignMatch, prop);
+ }
+ }
+ }
+
+ // there was no matching assoc one property.
+ // example UserRole bean missing assoc one to User?
+ return null;
+ }
+
+}