No effective change - change newline char

This commit is contained in:
rbygrave
2015-05-09 01:09:57 +12:00
parent 4143c0de26
commit 4eaa6adf08
58 changed files with 7127 additions and 7127 deletions
@@ -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);
}
}
}
}
}
@@ -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;
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
@@ -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);
//}
}
}
}
}
@@ -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);
}
@@ -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;
}
}
@@ -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);
}
}
}
@@ -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);
}
}
@@ -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());
}
}
@@ -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);
}
@@ -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);
}
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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;
}
}