mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
No effective change - change newline char
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<LoadBuffer> 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<DLoadBeanContext.LoadBuffer>();
|
||||
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<EntityBeanIntercept> list;
|
||||
private PersistenceContext persistenceContext;
|
||||
|
||||
public LoadBuffer(DLoadBeanContext context, int batchSize) {
|
||||
this.context = context;
|
||||
this.batchSize = batchSize;
|
||||
this.list = new ArrayList<EntityBeanIntercept>(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<EntityBeanIntercept> 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<EntityBeanIntercept> 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<LoadBuffer> 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<DLoadBeanContext.LoadBuffer>();
|
||||
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<EntityBeanIntercept> list;
|
||||
private PersistenceContext persistenceContext;
|
||||
|
||||
public LoadBuffer(DLoadBeanContext context, int batchSize) {
|
||||
this.context = context;
|
||||
this.batchSize = batchSize;
|
||||
this.list = new ArrayList<EntityBeanIntercept>(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<EntityBeanIntercept> 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<EntityBeanIntercept> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String, DLoadBeanContext> beanMap = new HashMap<String, DLoadBeanContext>();
|
||||
private final Map<String, DLoadManyContext> manyMap = new HashMap<String, DLoadManyContext>();
|
||||
|
||||
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<String,ObjectGraphNode> nodePathMap = new HashMap<String, ObjectGraphNode>();
|
||||
|
||||
private PersistenceContext persistenceContext;
|
||||
|
||||
private List<OrmQueryProperties> 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.
|
||||
* <p>
|
||||
* The parts of the secondary queries are removed and used
|
||||
* by LoadBeanContext/LoadManyContext to build the appropriate
|
||||
* queries.
|
||||
* </p>
|
||||
*/
|
||||
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<OrmQueryProperties> 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<String, DLoadBeanContext> beanMap = new HashMap<String, DLoadBeanContext>();
|
||||
private final Map<String, DLoadManyContext> manyMap = new HashMap<String, DLoadManyContext>();
|
||||
|
||||
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<String,ObjectGraphNode> nodePathMap = new HashMap<String, ObjectGraphNode>();
|
||||
|
||||
private PersistenceContext persistenceContext;
|
||||
|
||||
private List<OrmQueryProperties> 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.
|
||||
* <p>
|
||||
* The parts of the secondary queries are removed and used
|
||||
* by LoadBeanContext/LoadManyContext to build the appropriate
|
||||
* queries.
|
||||
* </p>
|
||||
*/
|
||||
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<OrmQueryProperties> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<LoadBuffer> 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<DLoadManyContext.LoadBuffer>();
|
||||
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<BeanCollection<?>> 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<BeanCollection<?>>(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<BeanCollection<?>> 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<LoadBuffer> 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<DLoadManyContext.LoadBuffer>();
|
||||
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<BeanCollection<?>> 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<BeanCollection<?>>(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<BeanCollection<?>> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* Persist requests include bean inserts updates deletes and UpdateSql and
|
||||
* CallableSql requests.
|
||||
* </p>
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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<String, BatchedBeanHolder> beanHoldMap = new HashMap<String, BatchedBeanHolder>();
|
||||
|
||||
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.
|
||||
* <p>
|
||||
* 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).
|
||||
* </p>
|
||||
*/
|
||||
public void setBatchFlushOnMixed(boolean flushBatchOnMixed) {
|
||||
this.batchFlushOnMixed = flushBatchOnMixed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batchSize.
|
||||
*/
|
||||
public int getBatchSize() {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the size of batch execution.
|
||||
* <p>
|
||||
* The user can set this via the Transaction.
|
||||
* </p>
|
||||
*/
|
||||
public void setBatchSize(int batchSize) {
|
||||
if (batchSize > 1) {
|
||||
this.batchSize = batchSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not to use getGeneratedKeys for this batch execution.
|
||||
* <p>
|
||||
* The user can set this via the transaction
|
||||
* </p>
|
||||
*/
|
||||
public void setGetGeneratedKeys(Boolean getGeneratedKeys) {
|
||||
if (getGeneratedKeys != null) {
|
||||
this.getGeneratedKeys = getGeneratedKeys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a Orm Update, SqlUpdate or CallableSql.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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<PersistRequest> 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.
|
||||
* <p>
|
||||
* Persist requests include bean inserts updates deletes and UpdateSql and
|
||||
* CallableSql requests.
|
||||
* </p>
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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<String, BatchedBeanHolder> beanHoldMap = new HashMap<String, BatchedBeanHolder>();
|
||||
|
||||
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.
|
||||
* <p>
|
||||
* 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).
|
||||
* </p>
|
||||
*/
|
||||
public void setBatchFlushOnMixed(boolean flushBatchOnMixed) {
|
||||
this.batchFlushOnMixed = flushBatchOnMixed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batchSize.
|
||||
*/
|
||||
public int getBatchSize() {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the size of batch execution.
|
||||
* <p>
|
||||
* The user can set this via the Transaction.
|
||||
* </p>
|
||||
*/
|
||||
public void setBatchSize(int batchSize) {
|
||||
if (batchSize > 1) {
|
||||
this.batchSize = batchSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not to use getGeneratedKeys for this batch execution.
|
||||
* <p>
|
||||
* The user can set this via the transaction
|
||||
* </p>
|
||||
*/
|
||||
public void setGetGeneratedKeys(Boolean getGeneratedKeys) {
|
||||
if (getGeneratedKeys != null) {
|
||||
this.getGeneratedKeys = getGeneratedKeys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a Orm Update, SqlUpdate or CallableSql.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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<PersistRequest> 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()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
public class BatchDepthComparator implements Comparator<BatchedBeanHolder>, 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
public class BatchDepthComparator implements Comparator<BatchedBeanHolder>, 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Handles the processing required after batch execution.
|
||||
* <p>
|
||||
* This includes concurrency checking, generated keys on inserts, transaction
|
||||
* logging, transaction event table modifcation and for beans resetting their
|
||||
* 'loaded' status.
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* This includes transaction logging, transaction event table modification
|
||||
* and for beans resetting their 'loaded' status.
|
||||
* </p>
|
||||
*/
|
||||
public void postExecute() throws SQLException;
|
||||
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Handles the processing required after batch execution.
|
||||
* <p>
|
||||
* This includes concurrency checking, generated keys on inserts, transaction
|
||||
* logging, transaction event table modifcation and for beans resetting their
|
||||
* 'loaded' status.
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* This includes transaction logging, transaction event table modification
|
||||
* and for beans resetting their 'loaded' status.
|
||||
* </p>
|
||||
*/
|
||||
public void postExecute() throws SQLException;
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* Has a depth which is used to determine the order in which it should be
|
||||
* executed. The lowest depth is executed first.
|
||||
* </p>
|
||||
*/
|
||||
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<PersistRequest> inserts;
|
||||
|
||||
/**
|
||||
* The list of bean update requests.
|
||||
*/
|
||||
private ArrayList<PersistRequest> updates;
|
||||
|
||||
/**
|
||||
* The list of bean delete requests.
|
||||
*/
|
||||
private ArrayList<PersistRequest> 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<Object,Object> persistedBeans = new IdentityHashMap<Object,Object>();
|
||||
|
||||
/**
|
||||
* Create a new entry with a given type and depth.
|
||||
*/
|
||||
public BatchedBeanHolder(BatchControl control, BeanDescriptor<?> beanDescriptor, int order) {
|
||||
this.control = control;
|
||||
this.shortDesc = beanDescriptor.getName() + ":" + order;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the depth.
|
||||
*/
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all the persist requests in this entry.
|
||||
* <p>
|
||||
* This will Batch all the similar requests into one or more BatchStatements
|
||||
* and then execute them.
|
||||
* </p>
|
||||
*/
|
||||
public void executeNow() {
|
||||
// process the requests. Creates one or more PreparedStatements
|
||||
// with binding addBatch() for each request.
|
||||
// Note updates and deletes can result in many PreparedStatements
|
||||
// if their where clauses differ via use of IS NOT NULL.
|
||||
if (inserts != null && !inserts.isEmpty()) {
|
||||
control.executeNow(inserts);
|
||||
inserts.clear();
|
||||
}
|
||||
if (updates != null && !updates.isEmpty()) {
|
||||
control.executeNow(updates);
|
||||
updates.clear();
|
||||
}
|
||||
if (deletes != null && !deletes.isEmpty()) {
|
||||
control.executeNow(deletes);
|
||||
deletes.clear();
|
||||
}
|
||||
persistedBeans.clear();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(shortDesc.length()+18);
|
||||
sb.append(shortDesc);
|
||||
if (inserts != null) {
|
||||
sb.append(" i:").append(inserts.size());
|
||||
}
|
||||
if (updates != null) {
|
||||
sb.append(" u:").append(updates.size());
|
||||
}
|
||||
if (deletes != null) {
|
||||
sb.append(" d:").append(deletes.size());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the request to the appropriate persist list.
|
||||
*/
|
||||
public int append(PersistRequestBean<?> request) {
|
||||
|
||||
Object alreadyInBatch = persistedBeans.put(request.getEntityBean(), DUMMY);
|
||||
if (alreadyInBatch != null) {
|
||||
// special case where the same bean instance has already been
|
||||
// added to the batch (doesn't really occur with non-batching
|
||||
// as the bean gets changed from dirty to loaded earlier)
|
||||
return 0;
|
||||
}
|
||||
|
||||
request.setBatched();
|
||||
|
||||
switch (request.getType()) {
|
||||
case INSERT:
|
||||
if (inserts == null) {
|
||||
inserts = new ArrayList<PersistRequest>();
|
||||
}
|
||||
inserts.add(request);
|
||||
return inserts.size();
|
||||
|
||||
case UPDATE:
|
||||
if (updates == null) {
|
||||
updates = new ArrayList<PersistRequest>();
|
||||
}
|
||||
updates.add(request);
|
||||
return updates.size();
|
||||
|
||||
case DELETE:
|
||||
if (deletes == null) {
|
||||
deletes = new ArrayList<PersistRequest>();
|
||||
}
|
||||
deletes.add(request);
|
||||
return deletes.size();
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Invalid type code " + request.getType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is empty containing no batched beans.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return persistedBeans.isEmpty();
|
||||
}
|
||||
}
|
||||
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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* Has a depth which is used to determine the order in which it should be
|
||||
* executed. The lowest depth is executed first.
|
||||
* </p>
|
||||
*/
|
||||
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<PersistRequest> inserts;
|
||||
|
||||
/**
|
||||
* The list of bean update requests.
|
||||
*/
|
||||
private ArrayList<PersistRequest> updates;
|
||||
|
||||
/**
|
||||
* The list of bean delete requests.
|
||||
*/
|
||||
private ArrayList<PersistRequest> 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<Object,Object> persistedBeans = new IdentityHashMap<Object,Object>();
|
||||
|
||||
/**
|
||||
* Create a new entry with a given type and depth.
|
||||
*/
|
||||
public BatchedBeanHolder(BatchControl control, BeanDescriptor<?> beanDescriptor, int order) {
|
||||
this.control = control;
|
||||
this.shortDesc = beanDescriptor.getName() + ":" + order;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the depth.
|
||||
*/
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all the persist requests in this entry.
|
||||
* <p>
|
||||
* This will Batch all the similar requests into one or more BatchStatements
|
||||
* and then execute them.
|
||||
* </p>
|
||||
*/
|
||||
public void executeNow() {
|
||||
// process the requests. Creates one or more PreparedStatements
|
||||
// with binding addBatch() for each request.
|
||||
// Note updates and deletes can result in many PreparedStatements
|
||||
// if their where clauses differ via use of IS NOT NULL.
|
||||
if (inserts != null && !inserts.isEmpty()) {
|
||||
control.executeNow(inserts);
|
||||
inserts.clear();
|
||||
}
|
||||
if (updates != null && !updates.isEmpty()) {
|
||||
control.executeNow(updates);
|
||||
updates.clear();
|
||||
}
|
||||
if (deletes != null && !deletes.isEmpty()) {
|
||||
control.executeNow(deletes);
|
||||
deletes.clear();
|
||||
}
|
||||
persistedBeans.clear();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(shortDesc.length()+18);
|
||||
sb.append(shortDesc);
|
||||
if (inserts != null) {
|
||||
sb.append(" i:").append(inserts.size());
|
||||
}
|
||||
if (updates != null) {
|
||||
sb.append(" u:").append(updates.size());
|
||||
}
|
||||
if (deletes != null) {
|
||||
sb.append(" d:").append(deletes.size());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the request to the appropriate persist list.
|
||||
*/
|
||||
public int append(PersistRequestBean<?> request) {
|
||||
|
||||
Object alreadyInBatch = persistedBeans.put(request.getEntityBean(), DUMMY);
|
||||
if (alreadyInBatch != null) {
|
||||
// special case where the same bean instance has already been
|
||||
// added to the batch (doesn't really occur with non-batching
|
||||
// as the bean gets changed from dirty to loaded earlier)
|
||||
return 0;
|
||||
}
|
||||
|
||||
request.setBatched();
|
||||
|
||||
switch (request.getType()) {
|
||||
case INSERT:
|
||||
if (inserts == null) {
|
||||
inserts = new ArrayList<PersistRequest>();
|
||||
}
|
||||
inserts.add(request);
|
||||
return inserts.size();
|
||||
|
||||
case UPDATE:
|
||||
if (updates == null) {
|
||||
updates = new ArrayList<PersistRequest>();
|
||||
}
|
||||
updates.add(request);
|
||||
return updates.size();
|
||||
|
||||
case DELETE:
|
||||
if (deletes == null) {
|
||||
deletes = new ArrayList<PersistRequest>();
|
||||
}
|
||||
deletes.add(request);
|
||||
return deletes.size();
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Invalid type code " + request.getType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is empty containing no batched beans.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return persistedBeans.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,157 +1,157 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.avaje.ebeaninternal.server.core.PstmtBatch;
|
||||
|
||||
/**
|
||||
* A batched statement that is held in BatchedPstmtHolder. It has a list of
|
||||
* BatchPostExecute which it will process after the statement is executed.
|
||||
* <p>
|
||||
* This can hold CallableStatements as well.
|
||||
* </p>
|
||||
*/
|
||||
public class BatchedPstmt {
|
||||
|
||||
/**
|
||||
* The underlying statement.
|
||||
*/
|
||||
private PreparedStatement pstmt;
|
||||
|
||||
/**
|
||||
* True if an insert that uses generated keys.
|
||||
*/
|
||||
private final boolean isGenKeys;
|
||||
|
||||
/**
|
||||
* The list of BatchPostExecute used to perform post processing.
|
||||
*/
|
||||
private final ArrayList<BatchPostExecute> list = new ArrayList<BatchPostExecute>();
|
||||
|
||||
private final String sql;
|
||||
|
||||
private final PstmtBatch pstmtBatch;
|
||||
|
||||
private final boolean occCheck;
|
||||
|
||||
|
||||
/**
|
||||
* Create with a given statement.
|
||||
* @param isGenKeys true if an insert that uses generatedKeys
|
||||
*/
|
||||
public BatchedPstmt(PreparedStatement pstmt, boolean isGenKeys, String sql, PstmtBatch pstmtBatch, boolean occCheck) {
|
||||
|
||||
this.pstmt = pstmt;
|
||||
this.isGenKeys = isGenKeys;
|
||||
this.sql = sql;
|
||||
this.pstmtBatch = pstmtBatch;
|
||||
this.occCheck = occCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of batched statements.
|
||||
*/
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sql
|
||||
*/
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the statement.
|
||||
*/
|
||||
public PreparedStatement getStatement() {
|
||||
return pstmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the BatchPostExecute to the list for post execute processing.
|
||||
*/
|
||||
public void add(BatchPostExecute batchExecute){
|
||||
list.add(batchExecute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the statement using executeBatch().
|
||||
* Run any post processing including getGeneratedKeys.
|
||||
*/
|
||||
public void executeBatch(boolean getGeneratedKeys) throws SQLException {
|
||||
|
||||
executeAndCheckRowCounts();
|
||||
if (isGenKeys && getGeneratedKeys){
|
||||
getGeneratedKeys();
|
||||
}
|
||||
postExecute();
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the underlying statement.
|
||||
*/
|
||||
public void close() throws SQLException {
|
||||
if (pstmt != null){
|
||||
pstmt.close();
|
||||
pstmt = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void postExecute() throws SQLException {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).postExecute();
|
||||
}
|
||||
}
|
||||
|
||||
private void executeAndCheckRowCounts() throws SQLException {
|
||||
|
||||
if (pstmtBatch != null){
|
||||
// oracle specific JDBC batch processing
|
||||
int rc = pstmtBatch.executeBatch(pstmt, list.size(), sql, occCheck);
|
||||
if (list.size() == 1){
|
||||
list.get(0).checkRowCount(rc);
|
||||
}
|
||||
// the optimistic concurrency row count check
|
||||
// has already been done by pstmtBatch so just return
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// normal JDBC batch processing
|
||||
int[] results = pstmt.executeBatch();
|
||||
|
||||
if (results.length != list.size()){
|
||||
String s = "results array error "+results.length+" "+list.size();
|
||||
throw new SQLException(s);
|
||||
}
|
||||
|
||||
// check for concurrency exceptions...
|
||||
for (int i = 0; i < results.length; i++) {
|
||||
list.get(i).checkRowCount(results[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void getGeneratedKeys() throws SQLException {
|
||||
|
||||
int index = 0;
|
||||
ResultSet rset = pstmt.getGeneratedKeys();
|
||||
try {
|
||||
while(rset.next()) {
|
||||
Object idValue = rset.getObject(1);
|
||||
list.get(index).setGeneratedKey(idValue);
|
||||
index++;
|
||||
}
|
||||
} finally {
|
||||
if (rset != null){
|
||||
rset.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.avaje.ebeaninternal.server.core.PstmtBatch;
|
||||
|
||||
/**
|
||||
* A batched statement that is held in BatchedPstmtHolder. It has a list of
|
||||
* BatchPostExecute which it will process after the statement is executed.
|
||||
* <p>
|
||||
* This can hold CallableStatements as well.
|
||||
* </p>
|
||||
*/
|
||||
public class BatchedPstmt {
|
||||
|
||||
/**
|
||||
* The underlying statement.
|
||||
*/
|
||||
private PreparedStatement pstmt;
|
||||
|
||||
/**
|
||||
* True if an insert that uses generated keys.
|
||||
*/
|
||||
private final boolean isGenKeys;
|
||||
|
||||
/**
|
||||
* The list of BatchPostExecute used to perform post processing.
|
||||
*/
|
||||
private final ArrayList<BatchPostExecute> list = new ArrayList<BatchPostExecute>();
|
||||
|
||||
private final String sql;
|
||||
|
||||
private final PstmtBatch pstmtBatch;
|
||||
|
||||
private final boolean occCheck;
|
||||
|
||||
|
||||
/**
|
||||
* Create with a given statement.
|
||||
* @param isGenKeys true if an insert that uses generatedKeys
|
||||
*/
|
||||
public BatchedPstmt(PreparedStatement pstmt, boolean isGenKeys, String sql, PstmtBatch pstmtBatch, boolean occCheck) {
|
||||
|
||||
this.pstmt = pstmt;
|
||||
this.isGenKeys = isGenKeys;
|
||||
this.sql = sql;
|
||||
this.pstmtBatch = pstmtBatch;
|
||||
this.occCheck = occCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of batched statements.
|
||||
*/
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sql
|
||||
*/
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the statement.
|
||||
*/
|
||||
public PreparedStatement getStatement() {
|
||||
return pstmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the BatchPostExecute to the list for post execute processing.
|
||||
*/
|
||||
public void add(BatchPostExecute batchExecute){
|
||||
list.add(batchExecute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the statement using executeBatch().
|
||||
* Run any post processing including getGeneratedKeys.
|
||||
*/
|
||||
public void executeBatch(boolean getGeneratedKeys) throws SQLException {
|
||||
|
||||
executeAndCheckRowCounts();
|
||||
if (isGenKeys && getGeneratedKeys){
|
||||
getGeneratedKeys();
|
||||
}
|
||||
postExecute();
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the underlying statement.
|
||||
*/
|
||||
public void close() throws SQLException {
|
||||
if (pstmt != null){
|
||||
pstmt.close();
|
||||
pstmt = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void postExecute() throws SQLException {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).postExecute();
|
||||
}
|
||||
}
|
||||
|
||||
private void executeAndCheckRowCounts() throws SQLException {
|
||||
|
||||
if (pstmtBatch != null){
|
||||
// oracle specific JDBC batch processing
|
||||
int rc = pstmtBatch.executeBatch(pstmt, list.size(), sql, occCheck);
|
||||
if (list.size() == 1){
|
||||
list.get(0).checkRowCount(rc);
|
||||
}
|
||||
// the optimistic concurrency row count check
|
||||
// has already been done by pstmtBatch so just return
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// normal JDBC batch processing
|
||||
int[] results = pstmt.executeBatch();
|
||||
|
||||
if (results.length != list.size()){
|
||||
String s = "results array error "+results.length+" "+list.size();
|
||||
throw new SQLException(s);
|
||||
}
|
||||
|
||||
// check for concurrency exceptions...
|
||||
for (int i = 0; i < results.length; i++) {
|
||||
list.get(i).checkRowCount(results[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void getGeneratedKeys() throws SQLException {
|
||||
|
||||
int index = 0;
|
||||
ResultSet rset = pstmt.getGeneratedKeys();
|
||||
try {
|
||||
while(rset.next()) {
|
||||
Object idValue = rset.getObject(1);
|
||||
list.get(index).setGeneratedKey(idValue);
|
||||
index++;
|
||||
}
|
||||
} finally {
|
||||
if (rset != null){
|
||||
rset.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,144 +1,144 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Used to hold BatchedPstmt objects for batch based execution.
|
||||
* <p>
|
||||
* The BatchControl 'front ends' the batching by queuing the persist requests
|
||||
* and ordering them according to depth and type. This object should only batch
|
||||
* statements of a single 'depth' at any given time.
|
||||
* </p>
|
||||
*/
|
||||
public class BatchedPstmtHolder {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BatchedPstmtHolder.class);
|
||||
|
||||
/**
|
||||
* A Map of the statements using a String key. This is used so that the same
|
||||
* Statement,Prepared,Callable is reused.
|
||||
*/
|
||||
private LinkedHashMap<String,BatchedPstmt> stmtMap = new LinkedHashMap<String,BatchedPstmt>();
|
||||
|
||||
/**
|
||||
* The Max size across all the BatchedPstmt.
|
||||
*/
|
||||
private int maxSize;
|
||||
|
||||
public BatchedPstmtHolder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the PreparedStatement if it has already been used in this Batch.
|
||||
* This will return null if no matching PreparedStatement is found.
|
||||
*/
|
||||
public PreparedStatement getStmt(String stmtKey, BatchPostExecute postExecute) {
|
||||
BatchedPstmt bs = stmtMap.get(stmtKey);
|
||||
if (bs == null) {
|
||||
// the PreparedStatement has need been created
|
||||
return null;
|
||||
}
|
||||
// add the post execute processing for this bean/row
|
||||
bs.add(postExecute);
|
||||
|
||||
// maintain a max batch size for any given batched stmt.
|
||||
// Used to determine when to flush.
|
||||
int bsSize = bs.size();
|
||||
if (bsSize > maxSize){
|
||||
maxSize = bsSize;
|
||||
}
|
||||
return bs.getStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new PreparedStatement wrapped in the BatchStatement object.
|
||||
*/
|
||||
public void addStmt(BatchedPstmt bs, BatchPostExecute postExecute) {
|
||||
// add the batch post execute to the statement for POST processing
|
||||
bs.add(postExecute);
|
||||
|
||||
// cache so that getStmt() can find it for additional beans/rows
|
||||
stmtMap.put(bs.getSql(), bs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the batch has no statements to execute.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return stmtMap.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all batched PreparedStatements.
|
||||
*
|
||||
* @param getGeneratedKeys
|
||||
* if true try to get generated keys for inserts
|
||||
*/
|
||||
public void flush(boolean getGeneratedKeys) throws PersistenceException {
|
||||
|
||||
SQLException firstError = null;
|
||||
String errorSql = null;
|
||||
|
||||
// flag set if something fails. Will not execute
|
||||
// but still need to close PreparedStatements.
|
||||
boolean isError = false;
|
||||
|
||||
for (BatchedPstmt bs : stmtMap.values()) {
|
||||
try {
|
||||
if (!isError) {
|
||||
bs.executeBatch(getGeneratedKeys);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
SQLException next = ex.getNextException();
|
||||
while(next != null) {
|
||||
logger.error("Next Exception during batch execution", next);
|
||||
next = next.getNextException();
|
||||
}
|
||||
|
||||
if (firstError == null) {
|
||||
firstError = ex;
|
||||
errorSql = bs.getSql();
|
||||
} else {
|
||||
logger.error(null, ex);
|
||||
}
|
||||
isError = true;
|
||||
|
||||
} finally {
|
||||
try {
|
||||
bs.close();
|
||||
} catch (SQLException ex) {
|
||||
// error closing PreparedStatement
|
||||
logger.error(null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clear the batch cache
|
||||
stmtMap.clear();
|
||||
maxSize = 0;
|
||||
|
||||
if (firstError != null) {
|
||||
String msg = "Error when batch flush on sql: "+errorSql;
|
||||
throw new PersistenceException(msg, firstError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the size of the biggest batched statement.
|
||||
* <p>
|
||||
* Used to determine when to flush the batch.
|
||||
* </p>
|
||||
*/
|
||||
public int getMaxSize() {
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Used to hold BatchedPstmt objects for batch based execution.
|
||||
* <p>
|
||||
* The BatchControl 'front ends' the batching by queuing the persist requests
|
||||
* and ordering them according to depth and type. This object should only batch
|
||||
* statements of a single 'depth' at any given time.
|
||||
* </p>
|
||||
*/
|
||||
public class BatchedPstmtHolder {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BatchedPstmtHolder.class);
|
||||
|
||||
/**
|
||||
* A Map of the statements using a String key. This is used so that the same
|
||||
* Statement,Prepared,Callable is reused.
|
||||
*/
|
||||
private LinkedHashMap<String,BatchedPstmt> stmtMap = new LinkedHashMap<String,BatchedPstmt>();
|
||||
|
||||
/**
|
||||
* The Max size across all the BatchedPstmt.
|
||||
*/
|
||||
private int maxSize;
|
||||
|
||||
public BatchedPstmtHolder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the PreparedStatement if it has already been used in this Batch.
|
||||
* This will return null if no matching PreparedStatement is found.
|
||||
*/
|
||||
public PreparedStatement getStmt(String stmtKey, BatchPostExecute postExecute) {
|
||||
BatchedPstmt bs = stmtMap.get(stmtKey);
|
||||
if (bs == null) {
|
||||
// the PreparedStatement has need been created
|
||||
return null;
|
||||
}
|
||||
// add the post execute processing for this bean/row
|
||||
bs.add(postExecute);
|
||||
|
||||
// maintain a max batch size for any given batched stmt.
|
||||
// Used to determine when to flush.
|
||||
int bsSize = bs.size();
|
||||
if (bsSize > maxSize){
|
||||
maxSize = bsSize;
|
||||
}
|
||||
return bs.getStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new PreparedStatement wrapped in the BatchStatement object.
|
||||
*/
|
||||
public void addStmt(BatchedPstmt bs, BatchPostExecute postExecute) {
|
||||
// add the batch post execute to the statement for POST processing
|
||||
bs.add(postExecute);
|
||||
|
||||
// cache so that getStmt() can find it for additional beans/rows
|
||||
stmtMap.put(bs.getSql(), bs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the batch has no statements to execute.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return stmtMap.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all batched PreparedStatements.
|
||||
*
|
||||
* @param getGeneratedKeys
|
||||
* if true try to get generated keys for inserts
|
||||
*/
|
||||
public void flush(boolean getGeneratedKeys) throws PersistenceException {
|
||||
|
||||
SQLException firstError = null;
|
||||
String errorSql = null;
|
||||
|
||||
// flag set if something fails. Will not execute
|
||||
// but still need to close PreparedStatements.
|
||||
boolean isError = false;
|
||||
|
||||
for (BatchedPstmt bs : stmtMap.values()) {
|
||||
try {
|
||||
if (!isError) {
|
||||
bs.executeBatch(getGeneratedKeys);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
SQLException next = ex.getNextException();
|
||||
while(next != null) {
|
||||
logger.error("Next Exception during batch execution", next);
|
||||
next = next.getNextException();
|
||||
}
|
||||
|
||||
if (firstError == null) {
|
||||
firstError = ex;
|
||||
errorSql = bs.getSql();
|
||||
} else {
|
||||
logger.error(null, ex);
|
||||
}
|
||||
isError = true;
|
||||
|
||||
} finally {
|
||||
try {
|
||||
bs.close();
|
||||
} catch (SQLException ex) {
|
||||
// error closing PreparedStatement
|
||||
logger.error(null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clear the batch cache
|
||||
stmtMap.clear();
|
||||
maxSize = 0;
|
||||
|
||||
if (firstError != null) {
|
||||
String msg = "Error when batch flush on sql: "+errorSql;
|
||||
throw new PersistenceException(msg, firstError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the size of the biggest batched statement.
|
||||
* <p>
|
||||
* Used to determine when to flush the batch.
|
||||
* </p>
|
||||
*/
|
||||
public int getMaxSize() {
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
|
||||
/**
|
||||
* Defines bean insert update and delete implementation.
|
||||
*/
|
||||
public interface BeanPersister {
|
||||
|
||||
/**
|
||||
* execute the insert bean request.
|
||||
*/
|
||||
public void insert(PersistRequestBean<?> request) throws PersistenceException;
|
||||
|
||||
/**
|
||||
* execute the update bean request.
|
||||
*/
|
||||
public void update(PersistRequestBean<?> request) throws PersistenceException;
|
||||
|
||||
/**
|
||||
* execute the delete bean request.
|
||||
*/
|
||||
public void delete(PersistRequestBean<?> request) throws PersistenceException;
|
||||
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
|
||||
/**
|
||||
* Defines bean insert update and delete implementation.
|
||||
*/
|
||||
public interface BeanPersister {
|
||||
|
||||
/**
|
||||
* execute the insert bean request.
|
||||
*/
|
||||
public void insert(PersistRequestBean<?> request) throws PersistenceException;
|
||||
|
||||
/**
|
||||
* execute the update bean request.
|
||||
*/
|
||||
public void update(PersistRequestBean<?> request) throws PersistenceException;
|
||||
|
||||
/**
|
||||
* execute the delete bean request.
|
||||
*/
|
||||
public void delete(PersistRequestBean<?> request) throws PersistenceException;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
/**
|
||||
* Factory for creating BeanPersister implementations.
|
||||
*/
|
||||
public interface BeanPersisterFactory {
|
||||
|
||||
/**
|
||||
* Create the BeanPersister implemenation for a given type.
|
||||
*/
|
||||
public BeanPersister create(BeanDescriptor<?> desc);
|
||||
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
/**
|
||||
* Factory for creating BeanPersister implementations.
|
||||
*/
|
||||
public interface BeanPersisterFactory {
|
||||
|
||||
/**
|
||||
* Create the BeanPersister implemenation for a given type.
|
||||
*/
|
||||
public BeanPersister create(BeanDescriptor<?> desc);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,116 +1,116 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Holds a list of bind values for binding to a PreparedStatement.
|
||||
*/
|
||||
public class BindValues {
|
||||
|
||||
int commentCount;
|
||||
|
||||
final ArrayList<Value> list = new ArrayList<Value>();
|
||||
|
||||
/**
|
||||
* Create with a Binder.
|
||||
*/
|
||||
public BindValues(){
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of bind values.
|
||||
*/
|
||||
public int size() {
|
||||
return list.size() - commentCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a bind value with its JDBC datatype.
|
||||
*
|
||||
* @param value the bind value
|
||||
* @param dbType the type as per java.sql.Types
|
||||
*/
|
||||
public void add(Object value, int dbType, String name){
|
||||
list.add(new Value(value, dbType, name));
|
||||
}
|
||||
|
||||
public void addComment(String comment){
|
||||
++commentCount;
|
||||
list.add(new Value(comment));
|
||||
}
|
||||
|
||||
/**
|
||||
* List of bind values.
|
||||
*/
|
||||
public ArrayList<Value> values() {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Value has additionally the JDBC data type.
|
||||
*/
|
||||
public static class Value {
|
||||
|
||||
private final Object value;
|
||||
|
||||
private final int dbType;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final boolean isComment;
|
||||
|
||||
/**
|
||||
* Create a comment. This is so that comments can be put into
|
||||
* the bind log.
|
||||
*/
|
||||
public Value(String comment) {
|
||||
this.name = comment;
|
||||
this.isComment = true;
|
||||
value = null;
|
||||
dbType = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the value.
|
||||
*/
|
||||
public Value(Object value, int dbType, String name) {
|
||||
this.isComment = false;
|
||||
this.value = value;
|
||||
this.dbType = dbType;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a comment for the bind log and NOT an actual bind value.
|
||||
*/
|
||||
public boolean isComment() {
|
||||
return isComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type as per java.sql.Types.
|
||||
*/
|
||||
public int getDbType() {
|
||||
return dbType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value.
|
||||
*/
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return ""+value;
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Holds a list of bind values for binding to a PreparedStatement.
|
||||
*/
|
||||
public class BindValues {
|
||||
|
||||
int commentCount;
|
||||
|
||||
final ArrayList<Value> list = new ArrayList<Value>();
|
||||
|
||||
/**
|
||||
* Create with a Binder.
|
||||
*/
|
||||
public BindValues(){
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of bind values.
|
||||
*/
|
||||
public int size() {
|
||||
return list.size() - commentCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a bind value with its JDBC datatype.
|
||||
*
|
||||
* @param value the bind value
|
||||
* @param dbType the type as per java.sql.Types
|
||||
*/
|
||||
public void add(Object value, int dbType, String name){
|
||||
list.add(new Value(value, dbType, name));
|
||||
}
|
||||
|
||||
public void addComment(String comment){
|
||||
++commentCount;
|
||||
list.add(new Value(comment));
|
||||
}
|
||||
|
||||
/**
|
||||
* List of bind values.
|
||||
*/
|
||||
public ArrayList<Value> values() {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Value has additionally the JDBC data type.
|
||||
*/
|
||||
public static class Value {
|
||||
|
||||
private final Object value;
|
||||
|
||||
private final int dbType;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final boolean isComment;
|
||||
|
||||
/**
|
||||
* Create a comment. This is so that comments can be put into
|
||||
* the bind log.
|
||||
*/
|
||||
public Value(String comment) {
|
||||
this.name = comment;
|
||||
this.isComment = true;
|
||||
value = null;
|
||||
dbType = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the value.
|
||||
*/
|
||||
public Value(Object value, int dbType, String name) {
|
||||
this.isComment = false;
|
||||
this.value = value;
|
||||
this.dbType = dbType;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a comment for the bind log and NOT an actual bind value.
|
||||
*/
|
||||
public boolean isComment() {
|
||||
return isComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type as per java.sql.Types.
|
||||
*/
|
||||
public int getDbType() {
|
||||
return dbType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value.
|
||||
*/
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return ""+value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,382 +1,382 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.server.core.Message;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.type.TypeManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Binds bean values to a PreparedStatement.
|
||||
*/
|
||||
public class Binder {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(Binder.class);
|
||||
|
||||
//private final Calendar calendar;
|
||||
|
||||
private final TypeManager typeManager;
|
||||
|
||||
/**
|
||||
* Set the PreparedStatement with which to bind variables to.
|
||||
*/
|
||||
public Binder(TypeManager typeManager) {
|
||||
|
||||
this.typeManager = typeManager;
|
||||
//this.calendar = new GregorianCalendar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the values to the Prepared Statement.
|
||||
*/
|
||||
public void bind(BindValues bindValues, DataBind dataBind, StringBuilder bindBuf)
|
||||
throws SQLException {
|
||||
|
||||
String logPrefix = "";
|
||||
|
||||
ArrayList<BindValues.Value> list = bindValues.values();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
BindValues.Value bindValue = list.get(i);
|
||||
if (bindValue.isComment()) {
|
||||
if (bindBuf != null) {
|
||||
bindBuf.append(bindValue.getName());
|
||||
if (logPrefix.equals("")) {
|
||||
logPrefix = ", ";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Object val = bindValue.getValue();
|
||||
int dt = bindValue.getDbType();
|
||||
bindObject(dataBind, val, dt);
|
||||
|
||||
if (bindBuf != null) {
|
||||
bindBuf.append(logPrefix);
|
||||
if (logPrefix.equals("")) {
|
||||
logPrefix = ", ";
|
||||
}
|
||||
bindBuf.append(bindValue.getName());
|
||||
bindBuf.append("=");
|
||||
if (isLob(dt)) {
|
||||
bindBuf.append("[LOB]");
|
||||
} else {
|
||||
bindBuf.append(String.valueOf(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the list of positionedParameters in BindParams.
|
||||
*/
|
||||
public String bind(BindParams bindParams, DataBind dataBind)
|
||||
throws SQLException {
|
||||
|
||||
StringBuilder bindLog = new StringBuilder();
|
||||
bind(bindParams, dataBind, bindLog);
|
||||
return bindLog.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the list of positionedParameters in BindParams.
|
||||
*/
|
||||
public void bind(BindParams bindParams, DataBind dataBind, StringBuilder bindLog)
|
||||
throws SQLException {
|
||||
|
||||
bind(bindParams.positionedParameters(), dataBind, bindLog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the list of parameters..
|
||||
*/
|
||||
public void bind(List<BindParams.Param> list, DataBind dataBind, StringBuilder bindLog)
|
||||
throws SQLException {
|
||||
|
||||
CallableStatement cstmt = null;
|
||||
|
||||
if (dataBind.getPstmt() instanceof CallableStatement) {
|
||||
cstmt = (CallableStatement) dataBind.getPstmt();
|
||||
}
|
||||
|
||||
// the iterator is assumed to be in the correct order
|
||||
Object value = null;
|
||||
try {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
|
||||
BindParams.Param param = list.get(i);
|
||||
|
||||
if (param.isOutParam() && cstmt != null){
|
||||
cstmt.registerOutParameter(dataBind.nextPos(), param.getType());
|
||||
if (param.isInParam()) {
|
||||
dataBind.decrementPos();
|
||||
}
|
||||
}
|
||||
if (param.isInParam()) {
|
||||
value = param.getInValue();
|
||||
if (bindLog != null) {
|
||||
if (param.isEncryptionKey()){
|
||||
bindLog.append("****");
|
||||
} else {
|
||||
bindLog.append(value);
|
||||
}
|
||||
bindLog.append(", ");
|
||||
}
|
||||
if (value == null) {
|
||||
// this doesn't work for query predicates
|
||||
bindObject(dataBind, null, param.getType());
|
||||
} else {
|
||||
bindObject(dataBind, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (SQLException ex) {
|
||||
logger.warn(Message.msg("fetch.bind.error", "" + (dataBind.currentPos() - 1), value));
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind an Object with unknown data type.
|
||||
*/
|
||||
public void bindObject(DataBind dataBind, Object value) throws SQLException {
|
||||
|
||||
if (value == null) {
|
||||
// null of unknown type
|
||||
bindObject(dataBind, null, Types.OTHER);
|
||||
|
||||
} else {
|
||||
|
||||
ScalarType<?> type = typeManager.getScalarType(value.getClass());
|
||||
if (type == null){
|
||||
// the type is not registered with the TypeManager.
|
||||
String msg = "No ScalarType registered for "+value.getClass();
|
||||
throw new PersistenceException(msg);
|
||||
|
||||
} else if (!type.isJdbcNative()) {
|
||||
// convert to a JDBC native type
|
||||
value = type.toJdbcType(value);
|
||||
}
|
||||
|
||||
int dbType = type.getJdbcType();
|
||||
bindObject(dataBind, value, dbType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* bind a single value.
|
||||
* <p>
|
||||
* Note that java.math.BigInteger is supported by converting it to a Long.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note if we get a java.util.Date or java.util.Calendar then these have
|
||||
* been anonymously passed in (UpdateSql etc). There is a global setting to
|
||||
* convert then to a java.sql.Date or java.sql.Timestamp for binding. The
|
||||
* default is that both are converted to java.sql.Timestamp.
|
||||
* </p>
|
||||
*/
|
||||
public void bindObject(DataBind dataBind, Object data, int dbType)
|
||||
throws SQLException {
|
||||
|
||||
if (data == null){
|
||||
dataBind.setNull(dbType);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (dbType) {
|
||||
case java.sql.Types.LONGVARCHAR:
|
||||
bindLongVarChar(dataBind, data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.LONGVARBINARY:
|
||||
bindLongVarBinary(dataBind, data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.CLOB:
|
||||
bindClob(dataBind, data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.BLOB:
|
||||
bindBlob(dataBind, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
bindSimpleData(dataBind, dbType, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the value to the statement according to the data type.
|
||||
*/
|
||||
private void bindSimpleData(DataBind b, int dataType, Object data)
|
||||
throws SQLException {
|
||||
|
||||
try {
|
||||
switch (dataType) {
|
||||
case java.sql.Types.BOOLEAN:
|
||||
b.setBoolean((Boolean) data);
|
||||
break;
|
||||
case java.sql.Types.BIT:
|
||||
// Types.BIT should map to Java Boolean
|
||||
b.setBoolean((Boolean) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.VARCHAR:
|
||||
b.setString((String) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.CHAR:
|
||||
b.setString(data.toString());
|
||||
break;
|
||||
|
||||
case java.sql.Types.TINYINT:
|
||||
b.setByte((Byte) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.SMALLINT:
|
||||
b.setShort((Short) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.INTEGER:
|
||||
b.setInt((Integer) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.BIGINT:
|
||||
b.setLong((Long) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.REAL:
|
||||
b.setFloat((Float) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.FLOAT:
|
||||
// DB Float in theory maps to Java Double type
|
||||
b.setDouble((Double) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.DOUBLE:
|
||||
b.setDouble((Double) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.NUMERIC:
|
||||
b.setBigDecimal((BigDecimal) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.DECIMAL:
|
||||
b.setBigDecimal((BigDecimal) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.TIME:
|
||||
//pstmt.setTime(index, (java.sql.Time) data, calendar);
|
||||
b.setTime((java.sql.Time) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.DATE:
|
||||
//pstmt.setDate(index, (java.sql.Date) data, calendar);
|
||||
b.setDate((java.sql.Date) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.TIMESTAMP:
|
||||
//pstmt.setTimestamp(index, (java.sql.Timestamp) data, calendar);
|
||||
b.setTimestamp((java.sql.Timestamp) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.BINARY:
|
||||
b.setBytes((byte[]) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.VARBINARY:
|
||||
b.setBytes((byte[]) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.OTHER:
|
||||
b.setObject(data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.JAVA_OBJECT:
|
||||
// Not too sure about this.
|
||||
b.setObject(data);
|
||||
break;
|
||||
|
||||
default:
|
||||
String msg = Message.msg("persist.bind.datatype", "" + dataType, "" + b.currentPos());
|
||||
throw new SQLException(msg);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
String dataClass = "Data is null?";
|
||||
if (data != null) {
|
||||
dataClass = data.getClass().getName();
|
||||
}
|
||||
String m = "Error with property[" + b.currentPos() + "] dt[" + dataType + "]";
|
||||
m += "data[" + data + "][" + dataClass + "]";
|
||||
throw new PersistenceException(m, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind String data to a LONGVARCHAR column.
|
||||
*/
|
||||
private void bindLongVarChar(DataBind b, Object data)
|
||||
throws SQLException {
|
||||
|
||||
String sd = (String) data;
|
||||
b.setClob(sd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind byte[] data to a LONGVARBINARY column.
|
||||
*/
|
||||
private void bindLongVarBinary(DataBind b, Object data)
|
||||
throws SQLException {
|
||||
|
||||
byte[] bytes = (byte[]) data;
|
||||
b.setBlob(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind String data to a CLOB column.
|
||||
*/
|
||||
private void bindClob(DataBind b, Object data) throws SQLException {
|
||||
|
||||
String sd = (String) data;
|
||||
b.setClob(sd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind byte[] data to a BLOB column.
|
||||
*/
|
||||
private void bindBlob(DataBind b, Object data) throws SQLException {
|
||||
|
||||
byte[] bytes = (byte[]) data;
|
||||
b.setBlob(bytes);
|
||||
}
|
||||
|
||||
private boolean isLob(int dbType) {
|
||||
switch (dbType) {
|
||||
case Types.CLOB:
|
||||
return true;
|
||||
case Types.LONGVARCHAR:
|
||||
return true;
|
||||
case Types.BLOB:
|
||||
return true;
|
||||
case Types.LONGVARBINARY:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.server.core.Message;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.type.TypeManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Binds bean values to a PreparedStatement.
|
||||
*/
|
||||
public class Binder {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(Binder.class);
|
||||
|
||||
//private final Calendar calendar;
|
||||
|
||||
private final TypeManager typeManager;
|
||||
|
||||
/**
|
||||
* Set the PreparedStatement with which to bind variables to.
|
||||
*/
|
||||
public Binder(TypeManager typeManager) {
|
||||
|
||||
this.typeManager = typeManager;
|
||||
//this.calendar = new GregorianCalendar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the values to the Prepared Statement.
|
||||
*/
|
||||
public void bind(BindValues bindValues, DataBind dataBind, StringBuilder bindBuf)
|
||||
throws SQLException {
|
||||
|
||||
String logPrefix = "";
|
||||
|
||||
ArrayList<BindValues.Value> list = bindValues.values();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
BindValues.Value bindValue = list.get(i);
|
||||
if (bindValue.isComment()) {
|
||||
if (bindBuf != null) {
|
||||
bindBuf.append(bindValue.getName());
|
||||
if (logPrefix.equals("")) {
|
||||
logPrefix = ", ";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Object val = bindValue.getValue();
|
||||
int dt = bindValue.getDbType();
|
||||
bindObject(dataBind, val, dt);
|
||||
|
||||
if (bindBuf != null) {
|
||||
bindBuf.append(logPrefix);
|
||||
if (logPrefix.equals("")) {
|
||||
logPrefix = ", ";
|
||||
}
|
||||
bindBuf.append(bindValue.getName());
|
||||
bindBuf.append("=");
|
||||
if (isLob(dt)) {
|
||||
bindBuf.append("[LOB]");
|
||||
} else {
|
||||
bindBuf.append(String.valueOf(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the list of positionedParameters in BindParams.
|
||||
*/
|
||||
public String bind(BindParams bindParams, DataBind dataBind)
|
||||
throws SQLException {
|
||||
|
||||
StringBuilder bindLog = new StringBuilder();
|
||||
bind(bindParams, dataBind, bindLog);
|
||||
return bindLog.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the list of positionedParameters in BindParams.
|
||||
*/
|
||||
public void bind(BindParams bindParams, DataBind dataBind, StringBuilder bindLog)
|
||||
throws SQLException {
|
||||
|
||||
bind(bindParams.positionedParameters(), dataBind, bindLog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the list of parameters..
|
||||
*/
|
||||
public void bind(List<BindParams.Param> list, DataBind dataBind, StringBuilder bindLog)
|
||||
throws SQLException {
|
||||
|
||||
CallableStatement cstmt = null;
|
||||
|
||||
if (dataBind.getPstmt() instanceof CallableStatement) {
|
||||
cstmt = (CallableStatement) dataBind.getPstmt();
|
||||
}
|
||||
|
||||
// the iterator is assumed to be in the correct order
|
||||
Object value = null;
|
||||
try {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
|
||||
BindParams.Param param = list.get(i);
|
||||
|
||||
if (param.isOutParam() && cstmt != null){
|
||||
cstmt.registerOutParameter(dataBind.nextPos(), param.getType());
|
||||
if (param.isInParam()) {
|
||||
dataBind.decrementPos();
|
||||
}
|
||||
}
|
||||
if (param.isInParam()) {
|
||||
value = param.getInValue();
|
||||
if (bindLog != null) {
|
||||
if (param.isEncryptionKey()){
|
||||
bindLog.append("****");
|
||||
} else {
|
||||
bindLog.append(value);
|
||||
}
|
||||
bindLog.append(", ");
|
||||
}
|
||||
if (value == null) {
|
||||
// this doesn't work for query predicates
|
||||
bindObject(dataBind, null, param.getType());
|
||||
} else {
|
||||
bindObject(dataBind, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (SQLException ex) {
|
||||
logger.warn(Message.msg("fetch.bind.error", "" + (dataBind.currentPos() - 1), value));
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind an Object with unknown data type.
|
||||
*/
|
||||
public void bindObject(DataBind dataBind, Object value) throws SQLException {
|
||||
|
||||
if (value == null) {
|
||||
// null of unknown type
|
||||
bindObject(dataBind, null, Types.OTHER);
|
||||
|
||||
} else {
|
||||
|
||||
ScalarType<?> type = typeManager.getScalarType(value.getClass());
|
||||
if (type == null){
|
||||
// the type is not registered with the TypeManager.
|
||||
String msg = "No ScalarType registered for "+value.getClass();
|
||||
throw new PersistenceException(msg);
|
||||
|
||||
} else if (!type.isJdbcNative()) {
|
||||
// convert to a JDBC native type
|
||||
value = type.toJdbcType(value);
|
||||
}
|
||||
|
||||
int dbType = type.getJdbcType();
|
||||
bindObject(dataBind, value, dbType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* bind a single value.
|
||||
* <p>
|
||||
* Note that java.math.BigInteger is supported by converting it to a Long.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note if we get a java.util.Date or java.util.Calendar then these have
|
||||
* been anonymously passed in (UpdateSql etc). There is a global setting to
|
||||
* convert then to a java.sql.Date or java.sql.Timestamp for binding. The
|
||||
* default is that both are converted to java.sql.Timestamp.
|
||||
* </p>
|
||||
*/
|
||||
public void bindObject(DataBind dataBind, Object data, int dbType)
|
||||
throws SQLException {
|
||||
|
||||
if (data == null){
|
||||
dataBind.setNull(dbType);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (dbType) {
|
||||
case java.sql.Types.LONGVARCHAR:
|
||||
bindLongVarChar(dataBind, data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.LONGVARBINARY:
|
||||
bindLongVarBinary(dataBind, data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.CLOB:
|
||||
bindClob(dataBind, data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.BLOB:
|
||||
bindBlob(dataBind, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
bindSimpleData(dataBind, dbType, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the value to the statement according to the data type.
|
||||
*/
|
||||
private void bindSimpleData(DataBind b, int dataType, Object data)
|
||||
throws SQLException {
|
||||
|
||||
try {
|
||||
switch (dataType) {
|
||||
case java.sql.Types.BOOLEAN:
|
||||
b.setBoolean((Boolean) data);
|
||||
break;
|
||||
case java.sql.Types.BIT:
|
||||
// Types.BIT should map to Java Boolean
|
||||
b.setBoolean((Boolean) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.VARCHAR:
|
||||
b.setString((String) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.CHAR:
|
||||
b.setString(data.toString());
|
||||
break;
|
||||
|
||||
case java.sql.Types.TINYINT:
|
||||
b.setByte((Byte) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.SMALLINT:
|
||||
b.setShort((Short) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.INTEGER:
|
||||
b.setInt((Integer) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.BIGINT:
|
||||
b.setLong((Long) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.REAL:
|
||||
b.setFloat((Float) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.FLOAT:
|
||||
// DB Float in theory maps to Java Double type
|
||||
b.setDouble((Double) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.DOUBLE:
|
||||
b.setDouble((Double) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.NUMERIC:
|
||||
b.setBigDecimal((BigDecimal) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.DECIMAL:
|
||||
b.setBigDecimal((BigDecimal) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.TIME:
|
||||
//pstmt.setTime(index, (java.sql.Time) data, calendar);
|
||||
b.setTime((java.sql.Time) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.DATE:
|
||||
//pstmt.setDate(index, (java.sql.Date) data, calendar);
|
||||
b.setDate((java.sql.Date) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.TIMESTAMP:
|
||||
//pstmt.setTimestamp(index, (java.sql.Timestamp) data, calendar);
|
||||
b.setTimestamp((java.sql.Timestamp) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.BINARY:
|
||||
b.setBytes((byte[]) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.VARBINARY:
|
||||
b.setBytes((byte[]) data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.OTHER:
|
||||
b.setObject(data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.JAVA_OBJECT:
|
||||
// Not too sure about this.
|
||||
b.setObject(data);
|
||||
break;
|
||||
|
||||
default:
|
||||
String msg = Message.msg("persist.bind.datatype", "" + dataType, "" + b.currentPos());
|
||||
throw new SQLException(msg);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
String dataClass = "Data is null?";
|
||||
if (data != null) {
|
||||
dataClass = data.getClass().getName();
|
||||
}
|
||||
String m = "Error with property[" + b.currentPos() + "] dt[" + dataType + "]";
|
||||
m += "data[" + data + "][" + dataClass + "]";
|
||||
throw new PersistenceException(m, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind String data to a LONGVARCHAR column.
|
||||
*/
|
||||
private void bindLongVarChar(DataBind b, Object data)
|
||||
throws SQLException {
|
||||
|
||||
String sd = (String) data;
|
||||
b.setClob(sd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind byte[] data to a LONGVARBINARY column.
|
||||
*/
|
||||
private void bindLongVarBinary(DataBind b, Object data)
|
||||
throws SQLException {
|
||||
|
||||
byte[] bytes = (byte[]) data;
|
||||
b.setBlob(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind String data to a CLOB column.
|
||||
*/
|
||||
private void bindClob(DataBind b, Object data) throws SQLException {
|
||||
|
||||
String sd = (String) data;
|
||||
b.setClob(sd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind byte[] data to a BLOB column.
|
||||
*/
|
||||
private void bindBlob(DataBind b, Object data) throws SQLException {
|
||||
|
||||
byte[] bytes = (byte[]) data;
|
||||
b.setBlob(bytes);
|
||||
}
|
||||
|
||||
private boolean isLob(int dbType) {
|
||||
switch (dbType) {
|
||||
case Types.CLOB:
|
||||
return true;
|
||||
case Types.LONGVARCHAR:
|
||||
return true;
|
||||
case Types.BLOB:
|
||||
return true;
|
||||
case Types.LONGVARBINARY:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
/**
|
||||
* Contants used in persist.
|
||||
*/
|
||||
public interface Constant {
|
||||
|
||||
/**
|
||||
* An INSERT clause.
|
||||
*/
|
||||
public static final int IN_INSERT = 1;
|
||||
|
||||
/**
|
||||
* An UPDATE SET clause.
|
||||
*/
|
||||
public static final int IN_UPDATE_SET = 2;
|
||||
|
||||
/**
|
||||
* An UPDATE WHERE clause.
|
||||
*/
|
||||
public static final int IN_UPDATE_WHERE = 3;
|
||||
|
||||
/**
|
||||
* A DELETE WHERE clause.
|
||||
*/
|
||||
public static final int IN_DELETE_WHERE = 4;
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
/**
|
||||
* Contants used in persist.
|
||||
*/
|
||||
public interface Constant {
|
||||
|
||||
/**
|
||||
* An INSERT clause.
|
||||
*/
|
||||
public static final int IN_INSERT = 1;
|
||||
|
||||
/**
|
||||
* An UPDATE SET clause.
|
||||
*/
|
||||
public static final int IN_UPDATE_SET = 2;
|
||||
|
||||
/**
|
||||
* An UPDATE WHERE clause.
|
||||
*/
|
||||
public static final int IN_UPDATE_WHERE = 3;
|
||||
|
||||
/**
|
||||
* A DELETE WHERE clause.
|
||||
*/
|
||||
public static final int IN_DELETE_WHERE = 4;
|
||||
}
|
||||
|
||||
@@ -1,113 +1,113 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.*;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanManager;
|
||||
|
||||
/**
|
||||
* Default PersistExecute implementation using DML statements.
|
||||
* <p>
|
||||
* Supports the use of PreparedStatement batching.
|
||||
* </p>
|
||||
*/
|
||||
public final class DefaultPersistExecute implements PersistExecute {
|
||||
|
||||
private final ExeCallableSql exeCallableSql;
|
||||
|
||||
private final ExeUpdateSql exeUpdateSql;
|
||||
|
||||
private final ExeOrmUpdate exeOrmUpdate;
|
||||
|
||||
/**
|
||||
* The default batch size.
|
||||
*/
|
||||
private final int defaultBatchSize;
|
||||
|
||||
/**
|
||||
* Default for whether to call getGeneratedKeys after batch insert.
|
||||
*/
|
||||
private final boolean defaultBatchGenKeys = true;
|
||||
|
||||
/**
|
||||
* Construct this DmlPersistExecute.
|
||||
*/
|
||||
public DefaultPersistExecute(Binder binder, PstmtBatch pstmtBatch, int defaultBatchSize) {
|
||||
|
||||
this.exeOrmUpdate = new ExeOrmUpdate(binder, pstmtBatch);
|
||||
this.exeUpdateSql = new ExeUpdateSql(binder, pstmtBatch);
|
||||
this.exeCallableSql = new ExeCallableSql(binder, pstmtBatch);
|
||||
this.defaultBatchSize = defaultBatchSize;
|
||||
}
|
||||
|
||||
public BatchControl createBatchControl(SpiTransaction t) {
|
||||
|
||||
// create a BatchControl and set its defaults
|
||||
return new BatchControl(t, defaultBatchSize, defaultBatchGenKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* execute the bean insert request.
|
||||
*/
|
||||
public <T> void executeInsertBean(PersistRequestBean<T> request) {
|
||||
|
||||
BeanManager<T> mgr = request.getBeanManager();
|
||||
BeanPersister persister = mgr.getBeanPersister();
|
||||
|
||||
BeanPersistController controller = request.getBeanController();
|
||||
if (controller == null || controller.preInsert(request)) {
|
||||
persister.insert(request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* execute the bean update request.
|
||||
*/
|
||||
public <T> void executeUpdateBean(PersistRequestBean<T> request) {
|
||||
|
||||
BeanManager<T> mgr = request.getBeanManager();
|
||||
BeanPersister persister = mgr.getBeanPersister();
|
||||
|
||||
BeanPersistController controller = request.getBeanController();
|
||||
if (controller == null || controller.preUpdate(request)) {
|
||||
request.postControllerPrepareUpdate();
|
||||
persister.update(request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* execute the bean delete request.
|
||||
*/
|
||||
public <T> void executeDeleteBean(PersistRequestBean<T> request) {
|
||||
|
||||
BeanManager<T> mgr = request.getBeanManager();
|
||||
BeanPersister persister = mgr.getBeanPersister();
|
||||
|
||||
BeanPersistController controller = request.getBeanController();
|
||||
if (controller == null || controller.preDelete(request)) {
|
||||
persister.delete(request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the updateSqlRequest
|
||||
*/
|
||||
public int executeOrmUpdate(PersistRequestOrmUpdate request) {
|
||||
return exeOrmUpdate.execute(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the updateSqlRequest
|
||||
*/
|
||||
public int executeSqlUpdate(PersistRequestUpdateSql request) {
|
||||
return exeUpdateSql.execute(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the CallableSqlRequest.
|
||||
*/
|
||||
public int executeSqlCallable(PersistRequestCallableSql request) {
|
||||
return exeCallableSql.execute(request);
|
||||
}
|
||||
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.*;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanManager;
|
||||
|
||||
/**
|
||||
* Default PersistExecute implementation using DML statements.
|
||||
* <p>
|
||||
* Supports the use of PreparedStatement batching.
|
||||
* </p>
|
||||
*/
|
||||
public final class DefaultPersistExecute implements PersistExecute {
|
||||
|
||||
private final ExeCallableSql exeCallableSql;
|
||||
|
||||
private final ExeUpdateSql exeUpdateSql;
|
||||
|
||||
private final ExeOrmUpdate exeOrmUpdate;
|
||||
|
||||
/**
|
||||
* The default batch size.
|
||||
*/
|
||||
private final int defaultBatchSize;
|
||||
|
||||
/**
|
||||
* Default for whether to call getGeneratedKeys after batch insert.
|
||||
*/
|
||||
private final boolean defaultBatchGenKeys = true;
|
||||
|
||||
/**
|
||||
* Construct this DmlPersistExecute.
|
||||
*/
|
||||
public DefaultPersistExecute(Binder binder, PstmtBatch pstmtBatch, int defaultBatchSize) {
|
||||
|
||||
this.exeOrmUpdate = new ExeOrmUpdate(binder, pstmtBatch);
|
||||
this.exeUpdateSql = new ExeUpdateSql(binder, pstmtBatch);
|
||||
this.exeCallableSql = new ExeCallableSql(binder, pstmtBatch);
|
||||
this.defaultBatchSize = defaultBatchSize;
|
||||
}
|
||||
|
||||
public BatchControl createBatchControl(SpiTransaction t) {
|
||||
|
||||
// create a BatchControl and set its defaults
|
||||
return new BatchControl(t, defaultBatchSize, defaultBatchGenKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* execute the bean insert request.
|
||||
*/
|
||||
public <T> void executeInsertBean(PersistRequestBean<T> request) {
|
||||
|
||||
BeanManager<T> mgr = request.getBeanManager();
|
||||
BeanPersister persister = mgr.getBeanPersister();
|
||||
|
||||
BeanPersistController controller = request.getBeanController();
|
||||
if (controller == null || controller.preInsert(request)) {
|
||||
persister.insert(request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* execute the bean update request.
|
||||
*/
|
||||
public <T> void executeUpdateBean(PersistRequestBean<T> request) {
|
||||
|
||||
BeanManager<T> mgr = request.getBeanManager();
|
||||
BeanPersister persister = mgr.getBeanPersister();
|
||||
|
||||
BeanPersistController controller = request.getBeanController();
|
||||
if (controller == null || controller.preUpdate(request)) {
|
||||
request.postControllerPrepareUpdate();
|
||||
persister.update(request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* execute the bean delete request.
|
||||
*/
|
||||
public <T> void executeDeleteBean(PersistRequestBean<T> request) {
|
||||
|
||||
BeanManager<T> mgr = request.getBeanManager();
|
||||
BeanPersister persister = mgr.getBeanPersister();
|
||||
|
||||
BeanPersistController controller = request.getBeanController();
|
||||
if (controller == null || controller.preDelete(request)) {
|
||||
persister.delete(request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the updateSqlRequest
|
||||
*/
|
||||
public int executeOrmUpdate(PersistRequestOrmUpdate request) {
|
||||
return exeOrmUpdate.execute(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the updateSqlRequest
|
||||
*/
|
||||
public int executeSqlUpdate(PersistRequestUpdateSql request) {
|
||||
return exeUpdateSql.execute(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the CallableSqlRequest.
|
||||
*/
|
||||
public int executeSqlCallable(PersistRequestCallableSql request) {
|
||||
return exeCallableSql.execute(request);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +1,23 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
|
||||
/**
|
||||
* Utility object with helper methods for DML.
|
||||
*/
|
||||
public class DmlUtil {
|
||||
|
||||
/**
|
||||
* Return true if the value is null or a Numeric 0 (for primitive int's and long's) or Option empty.
|
||||
*/
|
||||
public static boolean isNullOrZero(Object value){
|
||||
if (value == null){
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value instanceof Number){
|
||||
return ((Number)value).longValue() == 0l;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
|
||||
/**
|
||||
* Utility object with helper methods for DML.
|
||||
*/
|
||||
public class DmlUtil {
|
||||
|
||||
/**
|
||||
* Return true if the value is null or a Numeric 0 (for primitive int's and long's) or Option empty.
|
||||
*/
|
||||
public static boolean isNullOrZero(Object value){
|
||||
if (value == null){
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value instanceof Number){
|
||||
return ((Number)value).longValue() == 0l;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,111 +1,111 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.api.SpiCallableSql;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql;
|
||||
import com.avaje.ebeaninternal.server.core.PstmtBatch;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.util.BindParamsParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Handles the execution of CallableSql requests.
|
||||
*/
|
||||
public class ExeCallableSql {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ExeCallableSql.class);
|
||||
|
||||
private final Binder binder;
|
||||
|
||||
private final PstmtFactory pstmtFactory;
|
||||
|
||||
public ExeCallableSql(Binder binder, PstmtBatch pstmtBatch) {
|
||||
this.binder = binder;
|
||||
// no batch support for CallableStatement in Oracle anyway
|
||||
this.pstmtFactory = new PstmtFactory(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* execute the CallableSql requests.
|
||||
*/
|
||||
public int execute(PersistRequestCallableSql request) {
|
||||
|
||||
boolean batchThisRequest = request.isBatchThisRequest();
|
||||
|
||||
CallableStatement cstmt = null;
|
||||
try {
|
||||
cstmt = bindStmt(request, batchThisRequest);
|
||||
if (batchThisRequest) {
|
||||
cstmt.addBatch();
|
||||
// return -1 to indicate batch mode
|
||||
return -1;
|
||||
} else {
|
||||
// handles executeOverride() and also
|
||||
// reading of registered OUT parameters
|
||||
int rowCount = request.executeUpdate();
|
||||
request.postExecute();
|
||||
return rowCount;
|
||||
}
|
||||
|
||||
} catch (SQLException ex) {
|
||||
throw new PersistenceException(ex);
|
||||
|
||||
} finally {
|
||||
if (!batchThisRequest && cstmt != null) {
|
||||
try {
|
||||
cstmt.close();
|
||||
} catch (SQLException e) {
|
||||
logger.error(null, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private CallableStatement bindStmt(PersistRequestCallableSql request, boolean batchThisRequest) throws SQLException {
|
||||
|
||||
SpiCallableSql callableSql = request.getCallableSql();
|
||||
SpiTransaction t = request.getTransaction();
|
||||
|
||||
String sql = callableSql.getSql();
|
||||
|
||||
BindParams bindParams = callableSql.getBindParams();
|
||||
|
||||
// process named parameters if required
|
||||
sql = BindParamsParser.parse(bindParams, sql);
|
||||
|
||||
boolean logSql = request.isLogSql();
|
||||
|
||||
CallableStatement cstmt;
|
||||
if (batchThisRequest) {
|
||||
cstmt = pstmtFactory.getCstmt(t, logSql, sql, request);
|
||||
} else {
|
||||
if (logSql) {
|
||||
t.logSql(sql);
|
||||
}
|
||||
cstmt = pstmtFactory.getCstmt(t, sql);
|
||||
}
|
||||
|
||||
if (callableSql.getTimeout() > 0) {
|
||||
cstmt.setQueryTimeout(callableSql.getTimeout());
|
||||
}
|
||||
|
||||
String bindLog = null;
|
||||
if (!bindParams.isEmpty()) {
|
||||
bindLog = binder.bind(bindParams, new DataBind(cstmt));
|
||||
}
|
||||
|
||||
request.setBindLog(bindLog);
|
||||
|
||||
// required to read OUT params later
|
||||
request.setBound(bindParams, cstmt);
|
||||
return cstmt;
|
||||
}
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.api.SpiCallableSql;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql;
|
||||
import com.avaje.ebeaninternal.server.core.PstmtBatch;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.util.BindParamsParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Handles the execution of CallableSql requests.
|
||||
*/
|
||||
public class ExeCallableSql {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ExeCallableSql.class);
|
||||
|
||||
private final Binder binder;
|
||||
|
||||
private final PstmtFactory pstmtFactory;
|
||||
|
||||
public ExeCallableSql(Binder binder, PstmtBatch pstmtBatch) {
|
||||
this.binder = binder;
|
||||
// no batch support for CallableStatement in Oracle anyway
|
||||
this.pstmtFactory = new PstmtFactory(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* execute the CallableSql requests.
|
||||
*/
|
||||
public int execute(PersistRequestCallableSql request) {
|
||||
|
||||
boolean batchThisRequest = request.isBatchThisRequest();
|
||||
|
||||
CallableStatement cstmt = null;
|
||||
try {
|
||||
cstmt = bindStmt(request, batchThisRequest);
|
||||
if (batchThisRequest) {
|
||||
cstmt.addBatch();
|
||||
// return -1 to indicate batch mode
|
||||
return -1;
|
||||
} else {
|
||||
// handles executeOverride() and also
|
||||
// reading of registered OUT parameters
|
||||
int rowCount = request.executeUpdate();
|
||||
request.postExecute();
|
||||
return rowCount;
|
||||
}
|
||||
|
||||
} catch (SQLException ex) {
|
||||
throw new PersistenceException(ex);
|
||||
|
||||
} finally {
|
||||
if (!batchThisRequest && cstmt != null) {
|
||||
try {
|
||||
cstmt.close();
|
||||
} catch (SQLException e) {
|
||||
logger.error(null, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private CallableStatement bindStmt(PersistRequestCallableSql request, boolean batchThisRequest) throws SQLException {
|
||||
|
||||
SpiCallableSql callableSql = request.getCallableSql();
|
||||
SpiTransaction t = request.getTransaction();
|
||||
|
||||
String sql = callableSql.getSql();
|
||||
|
||||
BindParams bindParams = callableSql.getBindParams();
|
||||
|
||||
// process named parameters if required
|
||||
sql = BindParamsParser.parse(bindParams, sql);
|
||||
|
||||
boolean logSql = request.isLogSql();
|
||||
|
||||
CallableStatement cstmt;
|
||||
if (batchThisRequest) {
|
||||
cstmt = pstmtFactory.getCstmt(t, logSql, sql, request);
|
||||
} else {
|
||||
if (logSql) {
|
||||
t.logSql(sql);
|
||||
}
|
||||
cstmt = pstmtFactory.getCstmt(t, sql);
|
||||
}
|
||||
|
||||
if (callableSql.getTimeout() > 0) {
|
||||
cstmt.setQueryTimeout(callableSql.getTimeout());
|
||||
}
|
||||
|
||||
String bindLog = null;
|
||||
if (!bindParams.isEmpty()) {
|
||||
bindLog = binder.bind(bindParams, new DataBind(cstmt));
|
||||
}
|
||||
|
||||
request.setBindLog(bindLog);
|
||||
|
||||
// required to read OUT params later
|
||||
request.setBound(bindParams, cstmt);
|
||||
return cstmt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,129 +1,129 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.api.SpiUpdate;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate;
|
||||
import com.avaje.ebeaninternal.server.core.PstmtBatch;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.util.BindParamsParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Executes the UpdateSql requests.
|
||||
*/
|
||||
public class ExeOrmUpdate {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ExeOrmUpdate.class);
|
||||
|
||||
private final Binder binder;
|
||||
|
||||
private final PstmtFactory pstmtFactory;
|
||||
|
||||
/**
|
||||
* Create with a given binder.
|
||||
*/
|
||||
public ExeOrmUpdate(Binder binder, PstmtBatch pstmtBatch) {
|
||||
this.pstmtFactory = new PstmtFactory(pstmtBatch);
|
||||
this.binder = binder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the orm update request.
|
||||
*/
|
||||
public int execute(PersistRequestOrmUpdate request) {
|
||||
|
||||
boolean batchThisRequest = request.isBatchThisRequest();
|
||||
|
||||
PreparedStatement pstmt = null;
|
||||
try {
|
||||
pstmt = bindStmt(request, batchThisRequest);
|
||||
if (batchThisRequest) {
|
||||
PstmtBatch pstmtBatch = request.getPstmtBatch();
|
||||
if (pstmtBatch != null) {
|
||||
pstmtBatch.addBatch(pstmt);
|
||||
} else {
|
||||
pstmt.addBatch();
|
||||
}
|
||||
// return -1 to indicate batch mode
|
||||
return -1;
|
||||
} else {
|
||||
SpiUpdate<?> ormUpdate = request.getOrmUpdate();
|
||||
if (ormUpdate.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(ormUpdate.getTimeout());
|
||||
}
|
||||
int rowCount = pstmt.executeUpdate();
|
||||
request.checkRowCount(rowCount);
|
||||
request.postExecute();
|
||||
return rowCount;
|
||||
}
|
||||
|
||||
} catch (SQLException ex) {
|
||||
throw new PersistenceException("Error executing: " + request.getOrmUpdate().getGeneratedSql(), ex);
|
||||
|
||||
} finally {
|
||||
if (!batchThisRequest && pstmt != null) {
|
||||
try {
|
||||
pstmt.close();
|
||||
} catch (SQLException e) {
|
||||
logger.error(null, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert bean and property names to db table and columns.
|
||||
*/
|
||||
private String translate(PersistRequestOrmUpdate request, String sql) {
|
||||
|
||||
BeanDescriptor<?> descriptor = request.getBeanDescriptor();
|
||||
return descriptor.convertOrmUpdateToSql(sql);
|
||||
}
|
||||
|
||||
private PreparedStatement bindStmt(PersistRequestOrmUpdate request, boolean batchThisRequest) throws SQLException {
|
||||
|
||||
SpiUpdate<?> ormUpdate = request.getOrmUpdate();
|
||||
SpiTransaction t = request.getTransaction();
|
||||
|
||||
String sql = ormUpdate.getUpdateStatement();
|
||||
|
||||
// convert bean and property names to table and
|
||||
// column names if required
|
||||
sql = translate(request, sql);
|
||||
|
||||
BindParams bindParams = ormUpdate.getBindParams();
|
||||
|
||||
// process named parameters if required
|
||||
sql = BindParamsParser.parse(bindParams, sql);
|
||||
|
||||
ormUpdate.setGeneratedSql(sql);
|
||||
|
||||
boolean logSql = request.isLogSql();
|
||||
|
||||
PreparedStatement pstmt;
|
||||
if (batchThisRequest) {
|
||||
pstmt = pstmtFactory.getPstmt(t, logSql, sql, request);
|
||||
} else {
|
||||
if (logSql) {
|
||||
t.logSql(sql);
|
||||
}
|
||||
pstmt = pstmtFactory.getPstmt(t, sql);
|
||||
}
|
||||
|
||||
String bindLog = null;
|
||||
if (!bindParams.isEmpty()) {
|
||||
bindLog = binder.bind(bindParams, new DataBind(pstmt));
|
||||
}
|
||||
|
||||
request.setBindLog(bindLog);
|
||||
return pstmt;
|
||||
}
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.api.SpiUpdate;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate;
|
||||
import com.avaje.ebeaninternal.server.core.PstmtBatch;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.util.BindParamsParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Executes the UpdateSql requests.
|
||||
*/
|
||||
public class ExeOrmUpdate {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ExeOrmUpdate.class);
|
||||
|
||||
private final Binder binder;
|
||||
|
||||
private final PstmtFactory pstmtFactory;
|
||||
|
||||
/**
|
||||
* Create with a given binder.
|
||||
*/
|
||||
public ExeOrmUpdate(Binder binder, PstmtBatch pstmtBatch) {
|
||||
this.pstmtFactory = new PstmtFactory(pstmtBatch);
|
||||
this.binder = binder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the orm update request.
|
||||
*/
|
||||
public int execute(PersistRequestOrmUpdate request) {
|
||||
|
||||
boolean batchThisRequest = request.isBatchThisRequest();
|
||||
|
||||
PreparedStatement pstmt = null;
|
||||
try {
|
||||
pstmt = bindStmt(request, batchThisRequest);
|
||||
if (batchThisRequest) {
|
||||
PstmtBatch pstmtBatch = request.getPstmtBatch();
|
||||
if (pstmtBatch != null) {
|
||||
pstmtBatch.addBatch(pstmt);
|
||||
} else {
|
||||
pstmt.addBatch();
|
||||
}
|
||||
// return -1 to indicate batch mode
|
||||
return -1;
|
||||
} else {
|
||||
SpiUpdate<?> ormUpdate = request.getOrmUpdate();
|
||||
if (ormUpdate.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(ormUpdate.getTimeout());
|
||||
}
|
||||
int rowCount = pstmt.executeUpdate();
|
||||
request.checkRowCount(rowCount);
|
||||
request.postExecute();
|
||||
return rowCount;
|
||||
}
|
||||
|
||||
} catch (SQLException ex) {
|
||||
throw new PersistenceException("Error executing: " + request.getOrmUpdate().getGeneratedSql(), ex);
|
||||
|
||||
} finally {
|
||||
if (!batchThisRequest && pstmt != null) {
|
||||
try {
|
||||
pstmt.close();
|
||||
} catch (SQLException e) {
|
||||
logger.error(null, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert bean and property names to db table and columns.
|
||||
*/
|
||||
private String translate(PersistRequestOrmUpdate request, String sql) {
|
||||
|
||||
BeanDescriptor<?> descriptor = request.getBeanDescriptor();
|
||||
return descriptor.convertOrmUpdateToSql(sql);
|
||||
}
|
||||
|
||||
private PreparedStatement bindStmt(PersistRequestOrmUpdate request, boolean batchThisRequest) throws SQLException {
|
||||
|
||||
SpiUpdate<?> ormUpdate = request.getOrmUpdate();
|
||||
SpiTransaction t = request.getTransaction();
|
||||
|
||||
String sql = ormUpdate.getUpdateStatement();
|
||||
|
||||
// convert bean and property names to table and
|
||||
// column names if required
|
||||
sql = translate(request, sql);
|
||||
|
||||
BindParams bindParams = ormUpdate.getBindParams();
|
||||
|
||||
// process named parameters if required
|
||||
sql = BindParamsParser.parse(bindParams, sql);
|
||||
|
||||
ormUpdate.setGeneratedSql(sql);
|
||||
|
||||
boolean logSql = request.isLogSql();
|
||||
|
||||
PreparedStatement pstmt;
|
||||
if (batchThisRequest) {
|
||||
pstmt = pstmtFactory.getPstmt(t, logSql, sql, request);
|
||||
} else {
|
||||
if (logSql) {
|
||||
t.logSql(sql);
|
||||
}
|
||||
pstmt = pstmtFactory.getPstmt(t, sql);
|
||||
}
|
||||
|
||||
String bindLog = null;
|
||||
if (!bindParams.isEmpty()) {
|
||||
bindLog = binder.bind(bindParams, new DataBind(pstmt));
|
||||
}
|
||||
|
||||
request.setBindLog(bindLog);
|
||||
return pstmt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,190 +1,190 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.api.SpiSqlUpdate;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql.SqlType;
|
||||
import com.avaje.ebeaninternal.server.core.PstmtBatch;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.util.BindParamsParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Executes the UpdateSql requests.
|
||||
*/
|
||||
public class ExeUpdateSql {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ExeUpdateSql.class);
|
||||
|
||||
private final Binder binder;
|
||||
|
||||
private final PstmtFactory pstmtFactory;
|
||||
|
||||
private final PstmtBatch pstmtBatch;
|
||||
|
||||
private int defaultBatchSize = 20;
|
||||
|
||||
/**
|
||||
* Create with a given binder.
|
||||
*/
|
||||
public ExeUpdateSql(Binder binder, PstmtBatch pstmtBatch) {
|
||||
this.binder = binder;
|
||||
this.pstmtBatch = pstmtBatch;
|
||||
this.pstmtFactory = new PstmtFactory(pstmtBatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the UpdateSql request.
|
||||
*/
|
||||
public int execute(PersistRequestUpdateSql request) {
|
||||
|
||||
boolean batchThisRequest = request.isBatchThisRequest();
|
||||
|
||||
PreparedStatement pstmt = null;
|
||||
try {
|
||||
|
||||
pstmt = bindStmt(request, batchThisRequest);
|
||||
|
||||
if (batchThisRequest) {
|
||||
if (pstmtBatch != null) {
|
||||
pstmtBatch.addBatch(pstmt);
|
||||
} else {
|
||||
pstmt.addBatch();
|
||||
}
|
||||
// return -1 to indicate batch mode
|
||||
return -1;
|
||||
} else {
|
||||
int rowCount = pstmt.executeUpdate();
|
||||
request.checkRowCount(rowCount);
|
||||
request.postExecute();
|
||||
return rowCount;
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
throw new PersistenceException(ex);
|
||||
|
||||
} finally {
|
||||
if (!batchThisRequest && pstmt != null) {
|
||||
try {
|
||||
pstmt.close();
|
||||
} catch (SQLException e) {
|
||||
logger.error(null, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PreparedStatement bindStmt(PersistRequestUpdateSql request, boolean batchThisRequest) throws SQLException {
|
||||
|
||||
SpiSqlUpdate updateSql = request.getUpdateSql();
|
||||
SpiTransaction t = request.getTransaction();
|
||||
|
||||
String sql = updateSql.getSql();
|
||||
|
||||
BindParams bindParams = updateSql.getBindParams();
|
||||
|
||||
// process named parameters if required
|
||||
sql = BindParamsParser.parse(bindParams, sql);
|
||||
updateSql.setGeneratedSql(sql);
|
||||
|
||||
boolean logSql = request.isLogSql();
|
||||
|
||||
PreparedStatement pstmt;
|
||||
if (batchThisRequest) {
|
||||
pstmt = pstmtFactory.getPstmt(t, logSql, sql, request);
|
||||
if (pstmtBatch != null) {
|
||||
// oracle specific JDBC setting batch size ahead of time
|
||||
int batchSize = t.getBatchSize();
|
||||
if (batchSize < 1) {
|
||||
batchSize = defaultBatchSize;
|
||||
}
|
||||
pstmtBatch.setBatchSize(pstmt, batchSize);
|
||||
}
|
||||
} else {
|
||||
if (logSql) {
|
||||
t.logSql(sql);
|
||||
}
|
||||
pstmt = pstmtFactory.getPstmt(t, sql);
|
||||
}
|
||||
|
||||
if (updateSql.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(updateSql.getTimeout());
|
||||
}
|
||||
|
||||
String bindLog = null;
|
||||
if (!bindParams.isEmpty()) {
|
||||
bindLog = binder.bind(bindParams, new DataBind(pstmt));
|
||||
}
|
||||
|
||||
request.setBindLog(bindLog);
|
||||
|
||||
// derive the statement type (for TransactionEvent)
|
||||
parseUpdate(sql, request);
|
||||
return pstmt;
|
||||
}
|
||||
|
||||
|
||||
private void determineType(String word1, String word2, String word3, PersistRequestUpdateSql request) {
|
||||
|
||||
if (word1.equalsIgnoreCase("UPDATE")) {
|
||||
request.setType(SqlType.SQL_UPDATE, word2, "UpdateSql");
|
||||
|
||||
} else if (word1.equalsIgnoreCase("DELETE")) {
|
||||
request.setType(SqlType.SQL_DELETE, word3, "DeleteSql");
|
||||
|
||||
} else if (word1.equalsIgnoreCase("INSERT")) {
|
||||
request.setType(SqlType.SQL_INSERT, word3, "InsertSql");
|
||||
|
||||
} else {
|
||||
request.setType(SqlType.SQL_UNKNOWN, null, "UnknownSql");
|
||||
}
|
||||
}
|
||||
|
||||
private void parseUpdate(String sql, PersistRequestUpdateSql request) {
|
||||
|
||||
int start = leadingTrim(sql);
|
||||
|
||||
int[] pos = new int[3];
|
||||
int spaceCount = 0;
|
||||
|
||||
int len = sql.length();
|
||||
for (int i = start; i < len; i++) {
|
||||
char c = sql.charAt(i);
|
||||
if (Character.isWhitespace(c)) {
|
||||
pos[spaceCount] = i;
|
||||
spaceCount++;
|
||||
if (spaceCount > 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String firstWord = sql.substring(0, pos[0]);
|
||||
String secWord = sql.substring(pos[0] + 1, pos[1]);
|
||||
String thirdWord;
|
||||
if (pos[2] == 0) {
|
||||
// there is nothing after the table name
|
||||
thirdWord = sql.substring(pos[1] + 1);
|
||||
} else {
|
||||
thirdWord = sql.substring(pos[1] + 1, pos[2]);
|
||||
}
|
||||
|
||||
determineType(firstWord, secWord, thirdWord, request);
|
||||
}
|
||||
|
||||
private int leadingTrim(String s) {
|
||||
int len = s.length();
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!Character.isWhitespace(s.charAt(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.api.SpiSqlUpdate;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql.SqlType;
|
||||
import com.avaje.ebeaninternal.server.core.PstmtBatch;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.util.BindParamsParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Executes the UpdateSql requests.
|
||||
*/
|
||||
public class ExeUpdateSql {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ExeUpdateSql.class);
|
||||
|
||||
private final Binder binder;
|
||||
|
||||
private final PstmtFactory pstmtFactory;
|
||||
|
||||
private final PstmtBatch pstmtBatch;
|
||||
|
||||
private int defaultBatchSize = 20;
|
||||
|
||||
/**
|
||||
* Create with a given binder.
|
||||
*/
|
||||
public ExeUpdateSql(Binder binder, PstmtBatch pstmtBatch) {
|
||||
this.binder = binder;
|
||||
this.pstmtBatch = pstmtBatch;
|
||||
this.pstmtFactory = new PstmtFactory(pstmtBatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the UpdateSql request.
|
||||
*/
|
||||
public int execute(PersistRequestUpdateSql request) {
|
||||
|
||||
boolean batchThisRequest = request.isBatchThisRequest();
|
||||
|
||||
PreparedStatement pstmt = null;
|
||||
try {
|
||||
|
||||
pstmt = bindStmt(request, batchThisRequest);
|
||||
|
||||
if (batchThisRequest) {
|
||||
if (pstmtBatch != null) {
|
||||
pstmtBatch.addBatch(pstmt);
|
||||
} else {
|
||||
pstmt.addBatch();
|
||||
}
|
||||
// return -1 to indicate batch mode
|
||||
return -1;
|
||||
} else {
|
||||
int rowCount = pstmt.executeUpdate();
|
||||
request.checkRowCount(rowCount);
|
||||
request.postExecute();
|
||||
return rowCount;
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
throw new PersistenceException(ex);
|
||||
|
||||
} finally {
|
||||
if (!batchThisRequest && pstmt != null) {
|
||||
try {
|
||||
pstmt.close();
|
||||
} catch (SQLException e) {
|
||||
logger.error(null, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PreparedStatement bindStmt(PersistRequestUpdateSql request, boolean batchThisRequest) throws SQLException {
|
||||
|
||||
SpiSqlUpdate updateSql = request.getUpdateSql();
|
||||
SpiTransaction t = request.getTransaction();
|
||||
|
||||
String sql = updateSql.getSql();
|
||||
|
||||
BindParams bindParams = updateSql.getBindParams();
|
||||
|
||||
// process named parameters if required
|
||||
sql = BindParamsParser.parse(bindParams, sql);
|
||||
updateSql.setGeneratedSql(sql);
|
||||
|
||||
boolean logSql = request.isLogSql();
|
||||
|
||||
PreparedStatement pstmt;
|
||||
if (batchThisRequest) {
|
||||
pstmt = pstmtFactory.getPstmt(t, logSql, sql, request);
|
||||
if (pstmtBatch != null) {
|
||||
// oracle specific JDBC setting batch size ahead of time
|
||||
int batchSize = t.getBatchSize();
|
||||
if (batchSize < 1) {
|
||||
batchSize = defaultBatchSize;
|
||||
}
|
||||
pstmtBatch.setBatchSize(pstmt, batchSize);
|
||||
}
|
||||
} else {
|
||||
if (logSql) {
|
||||
t.logSql(sql);
|
||||
}
|
||||
pstmt = pstmtFactory.getPstmt(t, sql);
|
||||
}
|
||||
|
||||
if (updateSql.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(updateSql.getTimeout());
|
||||
}
|
||||
|
||||
String bindLog = null;
|
||||
if (!bindParams.isEmpty()) {
|
||||
bindLog = binder.bind(bindParams, new DataBind(pstmt));
|
||||
}
|
||||
|
||||
request.setBindLog(bindLog);
|
||||
|
||||
// derive the statement type (for TransactionEvent)
|
||||
parseUpdate(sql, request);
|
||||
return pstmt;
|
||||
}
|
||||
|
||||
|
||||
private void determineType(String word1, String word2, String word3, PersistRequestUpdateSql request) {
|
||||
|
||||
if (word1.equalsIgnoreCase("UPDATE")) {
|
||||
request.setType(SqlType.SQL_UPDATE, word2, "UpdateSql");
|
||||
|
||||
} else if (word1.equalsIgnoreCase("DELETE")) {
|
||||
request.setType(SqlType.SQL_DELETE, word3, "DeleteSql");
|
||||
|
||||
} else if (word1.equalsIgnoreCase("INSERT")) {
|
||||
request.setType(SqlType.SQL_INSERT, word3, "InsertSql");
|
||||
|
||||
} else {
|
||||
request.setType(SqlType.SQL_UNKNOWN, null, "UnknownSql");
|
||||
}
|
||||
}
|
||||
|
||||
private void parseUpdate(String sql, PersistRequestUpdateSql request) {
|
||||
|
||||
int start = leadingTrim(sql);
|
||||
|
||||
int[] pos = new int[3];
|
||||
int spaceCount = 0;
|
||||
|
||||
int len = sql.length();
|
||||
for (int i = start; i < len; i++) {
|
||||
char c = sql.charAt(i);
|
||||
if (Character.isWhitespace(c)) {
|
||||
pos[spaceCount] = i;
|
||||
spaceCount++;
|
||||
if (spaceCount > 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String firstWord = sql.substring(0, pos[0]);
|
||||
String secWord = sql.substring(pos[0] + 1, pos[1]);
|
||||
String thirdWord;
|
||||
if (pos[2] == 0) {
|
||||
// there is nothing after the table name
|
||||
thirdWord = sql.substring(pos[1] + 1);
|
||||
} else {
|
||||
thirdWord = sql.substring(pos[1] + 1, pos[2]);
|
||||
}
|
||||
|
||||
determineType(firstWord, secWord, thirdWord, request);
|
||||
}
|
||||
|
||||
private int leadingTrim(String s) {
|
||||
int len = s.length();
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!Character.isWhitespace(s.charAt(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql;
|
||||
|
||||
/**
|
||||
* The actual execution of persist requests.
|
||||
* <p>
|
||||
* A Persister 'front-ends' this object and handles the
|
||||
* batching, cascading, concurrency mode detection etc.
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
public interface PersistExecute {
|
||||
|
||||
/**
|
||||
* Create a BatchControl for the current transaction.
|
||||
*/
|
||||
public BatchControl createBatchControl(SpiTransaction t);
|
||||
|
||||
/**
|
||||
* Execute a Bean (or MapBean) insert.
|
||||
*/
|
||||
public <T> void executeInsertBean(PersistRequestBean<T> request);
|
||||
|
||||
/**
|
||||
* Execute a Bean (or MapBean) update.
|
||||
*/
|
||||
public <T> void executeUpdateBean(PersistRequestBean<T> request);
|
||||
|
||||
/**
|
||||
* Execute a Bean (or MapBean) delete.
|
||||
*/
|
||||
public <T> void executeDeleteBean(PersistRequestBean<T> request);
|
||||
|
||||
/**
|
||||
* Execute a Update.
|
||||
*/
|
||||
public int executeOrmUpdate(PersistRequestOrmUpdate request);
|
||||
|
||||
/**
|
||||
* Execute a CallableSql.
|
||||
*/
|
||||
public int executeSqlCallable(PersistRequestCallableSql request);
|
||||
|
||||
/**
|
||||
* Execute a UpdateSql.
|
||||
*/
|
||||
public int executeSqlUpdate(PersistRequestUpdateSql request);
|
||||
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql;
|
||||
|
||||
/**
|
||||
* The actual execution of persist requests.
|
||||
* <p>
|
||||
* A Persister 'front-ends' this object and handles the
|
||||
* batching, cascading, concurrency mode detection etc.
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
public interface PersistExecute {
|
||||
|
||||
/**
|
||||
* Create a BatchControl for the current transaction.
|
||||
*/
|
||||
public BatchControl createBatchControl(SpiTransaction t);
|
||||
|
||||
/**
|
||||
* Execute a Bean (or MapBean) insert.
|
||||
*/
|
||||
public <T> void executeInsertBean(PersistRequestBean<T> request);
|
||||
|
||||
/**
|
||||
* Execute a Bean (or MapBean) update.
|
||||
*/
|
||||
public <T> void executeUpdateBean(PersistRequestBean<T> request);
|
||||
|
||||
/**
|
||||
* Execute a Bean (or MapBean) delete.
|
||||
*/
|
||||
public <T> void executeDeleteBean(PersistRequestBean<T> request);
|
||||
|
||||
/**
|
||||
* Execute a Update.
|
||||
*/
|
||||
public int executeOrmUpdate(PersistRequestOrmUpdate request);
|
||||
|
||||
/**
|
||||
* Execute a CallableSql.
|
||||
*/
|
||||
public int executeSqlCallable(PersistRequestCallableSql request);
|
||||
|
||||
/**
|
||||
* Execute a UpdateSql.
|
||||
*/
|
||||
public int executeSqlUpdate(PersistRequestUpdateSql request);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,96 +1,96 @@
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.PstmtBatch;
|
||||
|
||||
/**
|
||||
* Factory for creating Statements.
|
||||
* <p>
|
||||
* This is only used by CallableSql and UpdateSql requests and does not support
|
||||
* getGeneratedKeys.
|
||||
* </p>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.PstmtBatch;
|
||||
|
||||
/**
|
||||
* Factory for creating Statements.
|
||||
* <p>
|
||||
* This is only used by CallableSql and UpdateSql requests and does not support
|
||||
* getGeneratedKeys.
|
||||
* </p>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+33
-33
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<UpdateGenValue> 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.
|
||||
* <p>
|
||||
* GeneratedProperty values are likely going to be used for optimistic
|
||||
* concurrency checking. This includes 'counter' and 'update timestamp'
|
||||
* generation.
|
||||
* </p>
|
||||
*/
|
||||
public void registerUpdateGenValue(BeanProperty prop, EntityBean bean, Object value) {
|
||||
if (updateGenValues == null) {
|
||||
updateGenValues = new ArrayList<UpdateGenValue>();
|
||||
}
|
||||
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<UpdateGenValue> 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.
|
||||
* <p>
|
||||
* GeneratedProperty values are likely going to be used for optimistic
|
||||
* concurrency checking. This includes 'counter' and 'update timestamp'
|
||||
* generation.
|
||||
* </p>
|
||||
*/
|
||||
public void registerUpdateGenValue(BeanProperty prop, EntityBean bean, Object value) {
|
||||
if (updateGenValues == null) {
|
||||
updateGenValues = new ArrayList<UpdateGenValue>();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<DerivedRelationshipData> 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<DerivedRelationshipData> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* This is only for DB's that do not support getGeneratedKeys. For MS
|
||||
* SQLServer 2000 this could return "SELECT (at)(at)IDENTITY as id".
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* This is only for DB's that do not support getGeneratedKeys. For MS
|
||||
* SQLServer 2000 this could return "SELECT (at)(at)IDENTITY as id".
|
||||
* </p>
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Bindable> setList = new ArrayList<Bindable>();
|
||||
|
||||
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<Bindable> allList = new ArrayList<Bindable>();
|
||||
|
||||
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<Bindable> setList = new ArrayList<Bindable>();
|
||||
|
||||
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<Bindable> allList = new ArrayList<Bindable>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Bindable> list = new ArrayList<Bindable>();
|
||||
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<Bindable> list = new ArrayList<Bindable>();
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* An implementation is expected to be immutable and thread safe.
|
||||
* </p>
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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<Bindable> 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.
|
||||
* <p>
|
||||
* An implementation is expected to be immutable and thread safe.
|
||||
* </p>
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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<Bindable> 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;
|
||||
|
||||
}
|
||||
|
||||
+56
-56
@@ -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<Bindable> 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<Bindable> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+54
-54
@@ -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<BindableProperty> 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<Bindable> 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<BindableProperty> 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<Bindable> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+45
-45
@@ -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<Bindable> 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<Bindable> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+60
-60
@@ -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<Bindable> 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<Bindable> 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<Bindable> 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<Bindable> 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);
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+69
-69
@@ -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<Bindable> 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<Bindable> 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() + "=****");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is only attempted if the id is null when it gets to the insert.
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* Really only where there are ManyToOne assoc beans that make up the
|
||||
* primary key and the values can be got from those.
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is only attempted if the id is null when it gets to the insert.
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* Really only where there are ManyToOne assoc beans that make up the
|
||||
* primary key and the values can be got from those.
|
||||
* </p>
|
||||
*/
|
||||
public boolean deriveConcatenatedId(PersistRequestBean<?> persist);
|
||||
|
||||
}
|
||||
|
||||
+101
-101
@@ -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<Bindable> 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<Bindable> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Bindable> 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<String, Object> mapId = new LinkedHashMap<String, Object>();
|
||||
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<Bindable> 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<String, Object> mapId = new LinkedHashMap<String, Object>();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+70
-70
@@ -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<Bindable> 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<Bindable> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Bindable> list) {
|
||||
items = list.toArray(new Bindable[list.size()]);
|
||||
}
|
||||
|
||||
public void addAll(List<Bindable> list) {
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
list.add(items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void addToUpdate(PersistRequestBean<?> request, List<Bindable> 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<Bindable> list) {
|
||||
items = list.toArray(new Bindable[list.size()]);
|
||||
}
|
||||
|
||||
public void addAll(List<Bindable> list) {
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
list.add(items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void addToUpdate(PersistRequestBean<?> request, List<Bindable> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+59
-59
@@ -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<Bindable> 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<Bindable> 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);
|
||||
}
|
||||
}
|
||||
|
||||
+46
-46
@@ -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.
|
||||
* <p>
|
||||
* This is typically a 'insert timestamp', 'update timestamp' or 'counter'.
|
||||
* </p>
|
||||
*/
|
||||
public class BindablePropertyInsertGenerated extends BindableProperty {
|
||||
|
||||
private final GeneratedProperty gen;
|
||||
|
||||
public BindablePropertyInsertGenerated(BeanProperty prop, GeneratedProperty gen) {
|
||||
super(prop);
|
||||
this.gen = gen;
|
||||
}
|
||||
|
||||
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.
|
||||
* <p>
|
||||
* This is typically a 'insert timestamp', 'update timestamp' or 'counter'.
|
||||
* </p>
|
||||
*/
|
||||
public class BindablePropertyInsertGenerated extends BindableProperty {
|
||||
|
||||
private final GeneratedProperty gen;
|
||||
|
||||
public BindablePropertyInsertGenerated(BeanProperty prop, GeneratedProperty gen) {
|
||||
super(prop);
|
||||
this.gen = gen;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+64
-64
@@ -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.
|
||||
* <p>
|
||||
* This is typically a 'update timestamp' or 'counter'.
|
||||
* </p>
|
||||
*/
|
||||
public class BindablePropertyUpdateGenerated extends BindableProperty {
|
||||
|
||||
private final GeneratedProperty gen;
|
||||
|
||||
public BindablePropertyUpdateGenerated(BeanProperty prop, GeneratedProperty gen) {
|
||||
super(prop);
|
||||
this.gen = gen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add BindablePropertyUpdateGenerated if the property is loaded.
|
||||
*/
|
||||
public void addToUpdate(PersistRequestBean<?> request, List<Bindable> 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.
|
||||
* <p>
|
||||
* This is typically a 'update timestamp' or 'counter'.
|
||||
* </p>
|
||||
*/
|
||||
public class BindablePropertyUpdateGenerated extends BindableProperty {
|
||||
|
||||
private final GeneratedProperty gen;
|
||||
|
||||
public BindablePropertyUpdateGenerated(BeanProperty prop, GeneratedProperty gen) {
|
||||
super(prop);
|
||||
this.gen = gen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add BindablePropertyUpdateGenerated if the property is loaded.
|
||||
*/
|
||||
public void addToUpdate(PersistRequestBean<?> request, List<Bindable> 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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* Takes into account logicalType to dbType conversion if required.
|
||||
* </p>
|
||||
* <p>
|
||||
* Returns the value that was bound (and was potentially converted from
|
||||
* logicalType to dbType.
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* Takes into account logicalType to dbType conversion if required.
|
||||
* </p>
|
||||
* <p>
|
||||
* Returns the value that was bound (and was potentially converted from
|
||||
* logicalType to dbType.
|
||||
* </p>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
+68
-68
@@ -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.
|
||||
* <p>
|
||||
* This inserts the foreign key value that is retrieved from the id of the
|
||||
* parentBean.
|
||||
* </p>
|
||||
*/
|
||||
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<Bindable> 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.
|
||||
* <p>
|
||||
* This inserts the foreign key value that is retrieved from the id of the
|
||||
* parentBean.
|
||||
* </p>
|
||||
*/
|
||||
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<Bindable> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+47
-47
@@ -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<Bindable> create(List<Bindable> 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<Bindable> create(List<Bindable> 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;
|
||||
}
|
||||
}
|
||||
|
||||
+67
-67
@@ -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.
|
||||
* <p>
|
||||
* This excludes unique embedded and associated properties.
|
||||
* </p>
|
||||
*/
|
||||
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<Bindable> 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<BindableProperty> newList = new ArrayList<BindableProperty>(props.length);
|
||||
addCompound(props, newList, desc, mode, withLobs);
|
||||
|
||||
BindableCompound compoundBindable = new BindableCompound(compoundProps[i], newList);
|
||||
|
||||
list.add(compoundBindable);
|
||||
}
|
||||
}
|
||||
|
||||
private void add(BeanProperty[] props, List<Bindable> 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<BindableProperty> 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.
|
||||
* <p>
|
||||
* This excludes unique embedded and associated properties.
|
||||
* </p>
|
||||
*/
|
||||
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<Bindable> 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<BindableProperty> newList = new ArrayList<BindableProperty>(props.length);
|
||||
addCompound(props, newList, desc, mode, withLobs);
|
||||
|
||||
BindableCompound compoundBindable = new BindableCompound(compoundProps[i], newList);
|
||||
|
||||
list.add(compoundBindable);
|
||||
}
|
||||
}
|
||||
|
||||
private void add(BeanProperty[] props, List<Bindable> 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<BindableProperty> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Bindable> 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<Bindable> bindList = new ArrayList<Bindable>(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<Bindable> 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<Bindable> bindList = new ArrayList<Bindable>(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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* Lob properties can be excluded and it creates BindablePropertyInsertGenerated
|
||||
* and BindablePropertyUpdateGenerated as required.
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* Lob properties can be excluded and it creates BindablePropertyInsertGenerated
|
||||
* and BindablePropertyUpdateGenerated as required.
|
||||
* </p>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+86
-86
@@ -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'.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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'.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user