Change license to Apache2 and reformat

This commit is contained in:
rbygrave
2012-09-15 00:00:08 +12:00
parent 96ce4c0ddf
commit 7aae897def
560 changed files with 73292 additions and 85907 deletions
@@ -1,87 +1,68 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.api.SpiTransaction;
/**
* Continue the fetch using a Background thread. The client knows when this has
* finished by checking to see if beanList.finishedFetch() is true.
*/
public class BackgroundFetch implements Callable<Integer> {
private static final Logger logger = Logger.getLogger(BackgroundFetch.class.getName());
private final CQuery<?> cquery;
private final SpiTransaction transaction;
/**
* Create the BackgroundFetch.
*/
public BackgroundFetch(CQuery<?> cquery) {
this.cquery = cquery;
this.transaction = cquery.getTransaction();
}
/**
* Continue the fetch.
*/
public Integer call() {
try {
BeanCollection<?> bc = cquery.continueFetchingInBackground();
return bc.size();
} catch (Exception e) {
logger.log(Level.SEVERE, null, e);
return Integer.valueOf(0);
} finally {
try {
cquery.close();
} catch (Exception e) {
logger.log(Level.SEVERE, null, e);
}
try {
// we must have our own transaction for background fetching
// and this performs the rollback... returning the
// connection back into the connection pool.
transaction.rollback();
} catch (Exception e) {
logger.log(Level.SEVERE, null, e);
}
}
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("BackgroundFetch ").append(cquery);
return sb.toString();
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.api.SpiTransaction;
/**
* Continue the fetch using a Background thread. The client knows when this has
* finished by checking to see if beanList.finishedFetch() is true.
*/
public class BackgroundFetch implements Callable<Integer> {
private static final Logger logger = Logger.getLogger(BackgroundFetch.class.getName());
private final CQuery<?> cquery;
private final SpiTransaction transaction;
/**
* Create the BackgroundFetch.
*/
public BackgroundFetch(CQuery<?> cquery) {
this.cquery = cquery;
this.transaction = cquery.getTransaction();
}
/**
* Continue the fetch.
*/
public Integer call() {
try {
BeanCollection<?> bc = cquery.continueFetchingInBackground();
return bc.size();
} catch (Exception e) {
logger.log(Level.SEVERE, null, e);
return Integer.valueOf(0);
} finally {
try {
cquery.close();
} catch (Exception e) {
logger.log(Level.SEVERE, null, e);
}
try {
// we must have our own transaction for background fetching
// and this performs the rollback... returning the
// connection back into the connection pool.
transaction.rollback();
} catch (Exception e) {
logger.log(Level.SEVERE, null, e);
}
}
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("BackgroundFetch ").append(cquery);
return sb.toString();
}
}
@@ -1,128 +1,109 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
/**
* Continue the fetch using a Background thread. The client knows when this has
* finished by checking to see if beanList.finishedFetch() is true.
*/
public class BackgroundIdFetch implements Callable<Integer> {
private static final Logger logger = Logger.getLogger(BackgroundIdFetch.class.getName());
private final ResultSet rset;
private final PreparedStatement pstmt;
private final SpiTransaction transaction;
private final DbReadContext ctx;
private final BeanDescriptor<?> beanDescriptor;
private final BeanIdList idList;
/**
* Create the BackgroundFetch.
*/
public BackgroundIdFetch(SpiTransaction transaction,
ResultSet rset, PreparedStatement pstmt,
DbReadContext ctx, BeanDescriptor<?> beanDescriptor,
BeanIdList idList) {
this.ctx = ctx;
this.transaction = transaction;
this.rset = rset;
this.pstmt = pstmt;
this.beanDescriptor = beanDescriptor;
this.idList = idList;
}
/**
* Continue the fetch.
*/
public Integer call() {
try {
int startSize = idList.getIdList().size();
int rowsRead = 0;
while (rset.next()){
Object idValue = beanDescriptor.getIdBinder().read(ctx);
idList.add(idValue);
ctx.getDataReader().resetColumnPosition();
rowsRead++;
}
if (logger.isLoggable(Level.INFO)){
logger.info("BG FetchIds read:"+rowsRead+" total:"+(startSize+rowsRead));
}
return rowsRead;
} catch (Exception e) {
logger.log(Level.SEVERE, null, e);
return 0;
} finally {
try {
close();
} catch (Exception e) {
logger.log(Level.SEVERE, null, e);
}
try {
// we must have our own transaction for background fetching
// and this performs the rollback... returning the
// connection back into the connection pool.
transaction.rollback();
} catch (Exception e) {
logger.log(Level.SEVERE, null, e);
}
}
}
private void close() {
try {
if (rset != null) {
rset.close();
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
}
}
package com.avaje.ebeaninternal.server.query;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
/**
* Continue the fetch using a Background thread. The client knows when this has
* finished by checking to see if beanList.finishedFetch() is true.
*/
public class BackgroundIdFetch implements Callable<Integer> {
private static final Logger logger = Logger.getLogger(BackgroundIdFetch.class.getName());
private final ResultSet rset;
private final PreparedStatement pstmt;
private final SpiTransaction transaction;
private final DbReadContext ctx;
private final BeanDescriptor<?> beanDescriptor;
private final BeanIdList idList;
/**
* Create the BackgroundFetch.
*/
public BackgroundIdFetch(SpiTransaction transaction,
ResultSet rset, PreparedStatement pstmt,
DbReadContext ctx, BeanDescriptor<?> beanDescriptor,
BeanIdList idList) {
this.ctx = ctx;
this.transaction = transaction;
this.rset = rset;
this.pstmt = pstmt;
this.beanDescriptor = beanDescriptor;
this.idList = idList;
}
/**
* Continue the fetch.
*/
public Integer call() {
try {
int startSize = idList.getIdList().size();
int rowsRead = 0;
while (rset.next()){
Object idValue = beanDescriptor.getIdBinder().read(ctx);
idList.add(idValue);
ctx.getDataReader().resetColumnPosition();
rowsRead++;
}
if (logger.isLoggable(Level.INFO)){
logger.info("BG FetchIds read:"+rowsRead+" total:"+(startSize+rowsRead));
}
return rowsRead;
} catch (Exception e) {
logger.log(Level.SEVERE, null, e);
return 0;
} finally {
try {
close();
} catch (Exception e) {
logger.log(Level.SEVERE, null, e);
}
try {
// we must have our own transaction for background fetching
// and this performs the rollback... returning the
// connection back into the connection pool.
transaction.rollback();
} catch (Exception e) {
logger.log(Level.SEVERE, null, e);
}
}
}
private void close() {
try {
if (rset != null) {
rset.close();
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
}
}
@@ -1,66 +1,47 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* A base object for query Future objects.
*
* @author rbygrave
*
* @param <T> the entity bean type
*/
public abstract class BaseFuture<T> implements Future<T> {
private final FutureTask<T> futureTask;
public BaseFuture(FutureTask<T> futureTask) {
this.futureTask = futureTask;
}
public boolean cancel(boolean mayInterruptIfRunning) {
return futureTask.cancel(mayInterruptIfRunning);
}
public T get() throws InterruptedException, ExecutionException {
return futureTask.get();
}
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return futureTask.get(timeout, unit);
}
public boolean isCancelled() {
return futureTask.isCancelled();
}
public boolean isDone() {
return futureTask.isDone();
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* A base object for query Future objects.
*
* @author rbygrave
*
* @param <T> the entity bean type
*/
public abstract class BaseFuture<T> implements Future<T> {
private final FutureTask<T> futureTask;
public BaseFuture(FutureTask<T> futureTask) {
this.futureTask = futureTask;
}
public boolean cancel(boolean mayInterruptIfRunning) {
return futureTask.cancel(mayInterruptIfRunning);
}
public T get() throws InterruptedException, ExecutionException {
return futureTask.get();
}
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return futureTask.get(timeout, unit);
}
public boolean isCancelled() {
return futureTask.isCancelled();
}
public boolean isDone() {
return futureTask.isDone();
}
}
@@ -1,220 +1,201 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.Collection;
import java.util.Map;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.core.RelationalQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.util.BeanCollectionFactory;
import com.avaje.ebeaninternal.server.util.BeanCollectionParams;
/**
* Wraps a BeanCollection with helper methods to add beans.
* <p>
* Helps adding the bean to the underlying set list or map.
* </p>
*/
public final class BeanCollectionWrapper {
/**
* Flag set if this builds a Map rather than a Collection.
*/
private final boolean isMap;
/**
* The type.
*/
private final SpiQuery.Type queryType;
/**
* A property name used as key for a Map.
*/
private final String mapKey;
/**
* The actual BeanCollection.
*/
private final BeanCollection<?> beanCollection;
/**
* Collection type of BeanCollection.
*/
private final Collection<Object> collection;
/**
* Map type of BeanCollection.
*/
private final Map<Object,Object> map;
/**
* The associated BeanDescriptor.
*/
private final BeanDescriptor<?> desc;
/**
* The number of rows added.
*/
private int rowCount;
public BeanCollectionWrapper(RelationalQueryRequest request) {
this.desc = null;
this.queryType = request.getQueryType();
this.mapKey = request.getQuery().getMapKey();
this.isMap = SpiQuery.Type.MAP.equals(queryType);
this.beanCollection = createBeanCollection(queryType);
this.collection = getCollection(isMap);
this.map = getMap(isMap);
}
/**
* Create based on a Find.
*/
public BeanCollectionWrapper(OrmQueryRequest<?> request) {
this.desc = request.getBeanDescriptor();
this.queryType = request.getQueryType();
this.mapKey = request.getQuery().getMapKey();
this.isMap = SpiQuery.Type.MAP.equals(queryType);
this.beanCollection = createBeanCollection(queryType);
this.collection = getCollection(isMap);
this.map = getMap(isMap);
}
/**
* Create based on a ManyType and mapKey. Note the mapKey is only used if
* the manyType is a Map.
* <p>
* modifyListening is set to true if this is a collection used to hold
* ManyToMany associated objects.
* </p>
*/
public BeanCollectionWrapper(BeanPropertyAssocMany<?> manyProp) {
this.queryType = manyProp.getManyType().getQueryType();
this.mapKey = manyProp.getMapKey();
this.desc = manyProp.getTargetDescriptor();
this.isMap = SpiQuery.Type.MAP.equals(queryType);
this.beanCollection = createBeanCollection(queryType);
this.collection = getCollection(isMap);
this.map = getMap(isMap);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Map<Object,Object> getMap(boolean isMap) {
return isMap ? (Map)beanCollection : null;
}
@SuppressWarnings("unchecked")
private Collection<Object> getCollection(boolean isMap) {
return isMap ? null : (Collection<Object>)beanCollection ;
}
/**
* Return the underlying BeanCollection.
*/
public BeanCollection<?> getBeanCollection() {
return beanCollection;
}
/**
* Create a BeanCollection of the correct type.
*/
private BeanCollection<?> createBeanCollection(SpiQuery.Type manyType) {
BeanCollectionParams p = new BeanCollectionParams(manyType);
return BeanCollectionFactory.create(p);
}
/**
* Return true if this wraps a Map rather than a set or list.
*/
public boolean isMap() {
return isMap;
}
/**
* Return the number of rows added to this wrapper.
*/
public int size() {
return rowCount;
}
/**
* Add the bean to the collection held in this wrapper.
*/
public void add(Object bean) {
add(bean, beanCollection);
}
/**
* Add the bean to the collection passed.
*
* @param bean
* the bean to add
* @param collection
* the collection or map to add the bean to
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public void add(Object bean, Object collection) {
if (bean == null) {
return;
}
rowCount++;
if (isMap) {
Object keyValue = null;
if (mapKey != null) {
// use the value for the property
keyValue = desc.getValue(bean, mapKey);
} else {
// use the uniqueId for this
keyValue = desc.getId(bean);
}
Map mapColl = (Map) collection;
mapColl.put(keyValue, bean);
} else {
((Collection) collection).add(bean);
}
}
/**
* Specifically add to a Collection.
*/
public void addToCollection(Object bean) {
collection.add(bean);
}
/**
* Specifically add to this as a Map with a known key.
*/
public void addToMap(Object bean, Object key) {
map.put(key, bean);
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.Collection;
import java.util.Map;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.core.RelationalQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.util.BeanCollectionFactory;
import com.avaje.ebeaninternal.server.util.BeanCollectionParams;
/**
* Wraps a BeanCollection with helper methods to add beans.
* <p>
* Helps adding the bean to the underlying set list or map.
* </p>
*/
public final class BeanCollectionWrapper {
/**
* Flag set if this builds a Map rather than a Collection.
*/
private final boolean isMap;
/**
* The type.
*/
private final SpiQuery.Type queryType;
/**
* A property name used as key for a Map.
*/
private final String mapKey;
/**
* The actual BeanCollection.
*/
private final BeanCollection<?> beanCollection;
/**
* Collection type of BeanCollection.
*/
private final Collection<Object> collection;
/**
* Map type of BeanCollection.
*/
private final Map<Object,Object> map;
/**
* The associated BeanDescriptor.
*/
private final BeanDescriptor<?> desc;
/**
* The number of rows added.
*/
private int rowCount;
public BeanCollectionWrapper(RelationalQueryRequest request) {
this.desc = null;
this.queryType = request.getQueryType();
this.mapKey = request.getQuery().getMapKey();
this.isMap = SpiQuery.Type.MAP.equals(queryType);
this.beanCollection = createBeanCollection(queryType);
this.collection = getCollection(isMap);
this.map = getMap(isMap);
}
/**
* Create based on a Find.
*/
public BeanCollectionWrapper(OrmQueryRequest<?> request) {
this.desc = request.getBeanDescriptor();
this.queryType = request.getQueryType();
this.mapKey = request.getQuery().getMapKey();
this.isMap = SpiQuery.Type.MAP.equals(queryType);
this.beanCollection = createBeanCollection(queryType);
this.collection = getCollection(isMap);
this.map = getMap(isMap);
}
/**
* Create based on a ManyType and mapKey. Note the mapKey is only used if
* the manyType is a Map.
* <p>
* modifyListening is set to true if this is a collection used to hold
* ManyToMany associated objects.
* </p>
*/
public BeanCollectionWrapper(BeanPropertyAssocMany<?> manyProp) {
this.queryType = manyProp.getManyType().getQueryType();
this.mapKey = manyProp.getMapKey();
this.desc = manyProp.getTargetDescriptor();
this.isMap = SpiQuery.Type.MAP.equals(queryType);
this.beanCollection = createBeanCollection(queryType);
this.collection = getCollection(isMap);
this.map = getMap(isMap);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Map<Object,Object> getMap(boolean isMap) {
return isMap ? (Map)beanCollection : null;
}
@SuppressWarnings("unchecked")
private Collection<Object> getCollection(boolean isMap) {
return isMap ? null : (Collection<Object>)beanCollection ;
}
/**
* Return the underlying BeanCollection.
*/
public BeanCollection<?> getBeanCollection() {
return beanCollection;
}
/**
* Create a BeanCollection of the correct type.
*/
private BeanCollection<?> createBeanCollection(SpiQuery.Type manyType) {
BeanCollectionParams p = new BeanCollectionParams(manyType);
return BeanCollectionFactory.create(p);
}
/**
* Return true if this wraps a Map rather than a set or list.
*/
public boolean isMap() {
return isMap;
}
/**
* Return the number of rows added to this wrapper.
*/
public int size() {
return rowCount;
}
/**
* Add the bean to the collection held in this wrapper.
*/
public void add(Object bean) {
add(bean, beanCollection);
}
/**
* Add the bean to the collection passed.
*
* @param bean
* the bean to add
* @param collection
* the collection or map to add the bean to
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public void add(Object bean, Object collection) {
if (bean == null) {
return;
}
rowCount++;
if (isMap) {
Object keyValue = null;
if (mapKey != null) {
// use the value for the property
keyValue = desc.getValue(bean, mapKey);
} else {
// use the uniqueId for this
keyValue = desc.getId(bean);
}
Map mapColl = (Map) collection;
mapColl.put(keyValue, bean);
} else {
((Collection) collection).add(bean);
}
}
/**
* Specifically add to a Collection.
*/
public void addToCollection(Object bean) {
collection.add(bean);
}
/**
* Specifically add to this as a Map with a known key.
*/
public void addToMap(Object bean, Object key) {
map.put(key, bean);
}
}
File diff suppressed because it is too large Load Diff
@@ -1,417 +1,398 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.Iterator;
import java.util.Set;
import javax.persistence.PersistenceException;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.RawSql.ColumnMapping;
import com.avaje.ebean.RawSql.ColumnMapping.Column;
import com.avaje.ebean.RawSqlBuilder;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.SqlLimitRequest;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebean.config.dbplatform.SqlLimiter;
import com.avaje.ebean.text.PathProperties;
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.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest;
/**
* Generates the SQL SELECT statements taking into account the physical
* deployment properties.
*/
public class CQueryBuilder implements Constants {
private final String tableAliasPlaceHolder;
private final String columnAliasPrefix;
private final SqlLimiter sqlLimiter;
private final RawSqlSelectClauseBuilder sqlSelectBuilder;
private final CQueryBuilderRawSql rawSqlHandler;
private final Binder binder;
private final BackgroundExecutor backgroundExecutor;
private final boolean selectCountWithAlias;
private DatabasePlatform dbPlatform;
/**
* Create the SqlGenSelect.
*/
public CQueryBuilder(BackgroundExecutor backgroundExecutor, DatabasePlatform dbPlatform, Binder binder) {
this.backgroundExecutor = backgroundExecutor;
this.binder = binder;
this.tableAliasPlaceHolder = GlobalProperties.get("ebean.tableAliasPlaceHolder", "${ta}");
this.columnAliasPrefix = GlobalProperties.get("ebean.columnAliasPrefix", "c");
this.sqlSelectBuilder = new RawSqlSelectClauseBuilder(dbPlatform, binder);
this.sqlLimiter = dbPlatform.getSqlLimiter();
this.rawSqlHandler = new CQueryBuilderRawSql(sqlLimiter, dbPlatform);
this.selectCountWithAlias = dbPlatform.isSelectCountWithAlias();
this.dbPlatform = dbPlatform;
}
/**
* split the order by claus on the field delimiter and prefix each field with
* the relation name
*/
public static String prefixOrderByFields(String name, String orderBy) {
StringBuilder sb = new StringBuilder();
for (String token : orderBy.split(",")) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(name);
sb.append(".");
sb.append(token.trim());
}
return sb.toString();
}
/**
* Build the row count query.
*/
public <T> CQueryFetchIds buildFetchIdsQuery(OrmQueryRequest<T> request) {
SpiQuery<T> query = request.getQuery();
query.setSelectId();
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
if (queryPlan != null) {
// skip building the SqlTree and Sql string
predicates.prepare(false);
String sql = queryPlan.getSql();
return new CQueryFetchIds(request, predicates, sql, backgroundExecutor);
}
// use RawSql or generated Sql
predicates.prepare(true);
SqlTree sqlTree = createSqlTree(request, predicates);
SqlLimitResponse s = buildSql(null, request, predicates, sqlTree);
String sql = s.getSql();
// cache the query plan
queryPlan = new CQueryPlan(sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql());
request.putQueryPlan(queryPlan);
return new CQueryFetchIds(request, predicates, sql, backgroundExecutor);
}
/**
* Build the row count query.
*/
public <T> CQueryRowCount buildRowCountQuery(OrmQueryRequest<T> request) {
SpiQuery<T> query = request.getQuery();
// always set the order by to null for row count query
query.setOrder(null);
boolean hasMany = !query.getManyWhereJoins().isEmpty();
query.setSelectId();
String sqlSelect = "select count(*)";
if (hasMany) {
// need to count distinct id's ...
query.setDistinct(true);
sqlSelect = null;
}
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
if (queryPlan != null) {
// skip building the SqlTree and Sql string
predicates.prepare(false);
String sql = queryPlan.getSql();
return new CQueryRowCount(request, predicates, sql);
}
predicates.prepare(true);
SqlTree sqlTree = createSqlTree(request, predicates);
SqlLimitResponse s = buildSql(sqlSelect, request, predicates, sqlTree);
String sql = s.getSql();
if (hasMany) {
sql = "select count(*) from ( " + sql + ")";
if (selectCountWithAlias) {
sql += " as c";
}
}
// cache the query plan
queryPlan = new CQueryPlan(sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql());
request.putQueryPlan(queryPlan);
return new CQueryRowCount(request, predicates, sql);
}
/**
* Return the SQL Select statement as a String. Converts logical property
* names to physical deployment column names.
*/
public <T> CQuery<T> buildQuery(OrmQueryRequest<T> request) {
if (request.isSqlSelect()) {
return sqlSelectBuilder.build(request);
}
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
if (queryPlan != null) {
// Reuse the query plan so skip generating SqlTree and SQL.
// We do prepare and bind the new parameters
predicates.prepare(false);
return new CQuery<T>(request, predicates, queryPlan);
}
// RawSql or Generated Sql query
// Prepare the where, having and order by clauses.
// This also parses them from logical property names to
// database columns and determines 'includes'.
// We need to check these 'includes' for extra joins
// that are not included via select
predicates.prepare(true);
// Build the tree structure that represents the query.
SqlTree sqlTree = createSqlTree(request, predicates);
SqlLimitResponse res = buildSql(null, request, predicates, sqlTree);
boolean rawSql = request.isRawSql();
if (rawSql) {
queryPlan = new CQueryPlanRawSql(request, res, sqlTree, predicates.getLogWhereSql());
} else {
queryPlan = new CQueryPlan(request, res, sqlTree, rawSql, predicates.getLogWhereSql(), null);
}
// cache the query plan because we can reuse it and also
// gather query performance statistics based on it.
request.putQueryPlan(queryPlan);
return new CQuery<T>(request, predicates, queryPlan);
}
/**
* Build the SqlTree.
* <p>
* The SqlTree is immutable after construction and so is safe to use by
* concurrent threads.
* </p>
* <p>
* The predicates is used to add additional joins that come from the where or
* order by clauses that are not already included for the select clause.
* </p>
*/
private SqlTree createSqlTree(OrmQueryRequest<?> request, CQueryPredicates predicates) {
if (request.isRawSql()) {
return createRawSqlSqlTree(request, predicates);
}
return new SqlTreeBuilder(tableAliasPlaceHolder, columnAliasPrefix, request, predicates).build();
}
private SqlTree createRawSqlSqlTree(OrmQueryRequest<?> request, CQueryPredicates predicates) {
BeanDescriptor<?> descriptor = request.getBeanDescriptor();
ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping();
PathProperties pathProps = new PathProperties();
// convert list of columns into (tree like) PathProperties
Iterator<Column> it = columnMapping.getColumns();
while (it.hasNext()) {
RawSql.ColumnMapping.Column column = it.next();
String propertyName = column.getPropertyName();
if (!RawSqlBuilder.IGNORE_COLUMN.equals(propertyName)) {
ElPropertyValue el = descriptor.getElGetValue(propertyName);
if (el == null) {
String msg = "Property [" + propertyName + "] not found on " + descriptor.getFullName();
throw new PersistenceException(msg);
}
BeanProperty beanProperty = el.getBeanProperty();
if (beanProperty.isId()) {
// For @Id properties we chop off the last part of the path
propertyName = SplitName.parent(propertyName);
} else if (beanProperty instanceof BeanPropertyAssocOne<?>) {
String msg = "Column [" + column.getDbColumn() + "] mapped to complex Property[" + propertyName + "]";
msg += ". It should be mapped to a simple property (proably the Id property). ";
throw new PersistenceException(msg);
}
if (propertyName != null) {
String[] pathProp = SplitName.split(propertyName);
pathProps.addToPath(pathProp[0], pathProp[1]);
}
}
}
OrmQueryDetail detail = new OrmQueryDetail();
// transfer PathProperties into OrmQueryDetail
Iterator<String> pathIt = pathProps.getPaths().iterator();
while (pathIt.hasNext()) {
String path = pathIt.next();
Set<String> props = pathProps.get(path);
detail.getChunk(path, true).setDefaultProperties(null, props);
}
// build SqlTree based on OrmQueryDetail of the RawSql
return new SqlTreeBuilder(request, predicates, detail).build();
}
private SqlLimitResponse buildSql(String selectClause, OrmQueryRequest<?> request, CQueryPredicates predicates, SqlTree select) {
SpiQuery<?> query = request.getQuery();
RawSql rawSql = query.getRawSql();
if (rawSql != null) {
return rawSqlHandler.buildSql(request, predicates, rawSql.getSql());
}
BeanPropertyAssocMany<?> manyProp = select.getManyProperty();
boolean useSqlLimiter = false;
StringBuilder sb = new StringBuilder(500);
if (selectClause != null) {
sb.append(selectClause);
} else {
useSqlLimiter = (query.hasMaxRowsOrFirstRow() && manyProp == null);
if (!useSqlLimiter) {
sb.append("select ");
if (query.isDistinct()) {
sb.append("distinct ");
}
}
sb.append(select.getSelectSql());
}
sb.append(" ").append(NEW_LINE);
sb.append("from ");
// build the from clause potentially with joins
// required only for the predicates
sb.append(select.getFromSql());
String inheritanceWhere = select.getInheritanceWhereSql();
boolean hasWhere = false;
if (inheritanceWhere.length() > 0) {
sb.append(" ").append(NEW_LINE).append("where");
sb.append(inheritanceWhere);
hasWhere = true;
}
if (request.isFindById() || query.getId() != null) {
if (hasWhere) {
sb.append(" and ");
} else {
sb.append(NEW_LINE).append("where ");
}
BeanDescriptor<?> desc = request.getBeanDescriptor();
String idSql = desc.getIdBinderIdSql();
if (idSql.isEmpty()) {
throw new IllegalStateException("Executing FindById query on entity bean " + desc.getName()
+ " that doesn't have an @Id property??");
}
sb.append(idSql).append(" ");
hasWhere = true;
}
String dbWhere = predicates.getDbWhere();
if (!isEmpty(dbWhere)) {
if (!hasWhere) {
hasWhere = true;
sb.append(" ").append(NEW_LINE).append("where ");
} else {
sb.append("and ");
}
sb.append(dbWhere);
}
String dbFilterMany = predicates.getDbFilterMany();
if (!isEmpty(dbFilterMany)) {
if (!hasWhere) {
sb.append(" ").append(NEW_LINE).append("where ");
} else {
sb.append("and ");
}
sb.append(dbFilterMany);
}
String dbOrderBy = predicates.getDbOrderBy();
if (dbOrderBy != null) {
sb.append(" ").append(NEW_LINE);
sb.append("order by ").append(dbOrderBy);
}
if (useSqlLimiter) {
// use LIMIT/OFFSET, ROW_NUMBER() or rownum type SQL query limitation
SqlLimitRequest r = new OrmQueryLimitRequest(sb.toString(), dbOrderBy, query, dbPlatform);
return sqlLimiter.limit(r);
} else {
return new SqlLimitResponse(dbPlatform.completeSql(sb.toString(), query), false);
}
}
private boolean isEmpty(String s) {
return s == null || s.length() == 0;
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.Iterator;
import java.util.Set;
import javax.persistence.PersistenceException;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.RawSql.ColumnMapping;
import com.avaje.ebean.RawSql.ColumnMapping.Column;
import com.avaje.ebean.RawSqlBuilder;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.SqlLimitRequest;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebean.config.dbplatform.SqlLimiter;
import com.avaje.ebean.text.PathProperties;
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.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest;
/**
* Generates the SQL SELECT statements taking into account the physical
* deployment properties.
*/
public class CQueryBuilder implements Constants {
private final String tableAliasPlaceHolder;
private final String columnAliasPrefix;
private final SqlLimiter sqlLimiter;
private final RawSqlSelectClauseBuilder sqlSelectBuilder;
private final CQueryBuilderRawSql rawSqlHandler;
private final Binder binder;
private final BackgroundExecutor backgroundExecutor;
private final boolean selectCountWithAlias;
private DatabasePlatform dbPlatform;
/**
* Create the SqlGenSelect.
*/
public CQueryBuilder(BackgroundExecutor backgroundExecutor, DatabasePlatform dbPlatform, Binder binder) {
this.backgroundExecutor = backgroundExecutor;
this.binder = binder;
this.tableAliasPlaceHolder = GlobalProperties.get("ebean.tableAliasPlaceHolder", "${ta}");
this.columnAliasPrefix = GlobalProperties.get("ebean.columnAliasPrefix", "c");
this.sqlSelectBuilder = new RawSqlSelectClauseBuilder(dbPlatform, binder);
this.sqlLimiter = dbPlatform.getSqlLimiter();
this.rawSqlHandler = new CQueryBuilderRawSql(sqlLimiter, dbPlatform);
this.selectCountWithAlias = dbPlatform.isSelectCountWithAlias();
this.dbPlatform = dbPlatform;
}
/**
* split the order by claus on the field delimiter and prefix each field with
* the relation name
*/
public static String prefixOrderByFields(String name, String orderBy) {
StringBuilder sb = new StringBuilder();
for (String token : orderBy.split(",")) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(name);
sb.append(".");
sb.append(token.trim());
}
return sb.toString();
}
/**
* Build the row count query.
*/
public <T> CQueryFetchIds buildFetchIdsQuery(OrmQueryRequest<T> request) {
SpiQuery<T> query = request.getQuery();
query.setSelectId();
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
if (queryPlan != null) {
// skip building the SqlTree and Sql string
predicates.prepare(false);
String sql = queryPlan.getSql();
return new CQueryFetchIds(request, predicates, sql, backgroundExecutor);
}
// use RawSql or generated Sql
predicates.prepare(true);
SqlTree sqlTree = createSqlTree(request, predicates);
SqlLimitResponse s = buildSql(null, request, predicates, sqlTree);
String sql = s.getSql();
// cache the query plan
queryPlan = new CQueryPlan(sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql());
request.putQueryPlan(queryPlan);
return new CQueryFetchIds(request, predicates, sql, backgroundExecutor);
}
/**
* Build the row count query.
*/
public <T> CQueryRowCount buildRowCountQuery(OrmQueryRequest<T> request) {
SpiQuery<T> query = request.getQuery();
// always set the order by to null for row count query
query.setOrder(null);
boolean hasMany = !query.getManyWhereJoins().isEmpty();
query.setSelectId();
String sqlSelect = "select count(*)";
if (hasMany) {
// need to count distinct id's ...
query.setDistinct(true);
sqlSelect = null;
}
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
if (queryPlan != null) {
// skip building the SqlTree and Sql string
predicates.prepare(false);
String sql = queryPlan.getSql();
return new CQueryRowCount(request, predicates, sql);
}
predicates.prepare(true);
SqlTree sqlTree = createSqlTree(request, predicates);
SqlLimitResponse s = buildSql(sqlSelect, request, predicates, sqlTree);
String sql = s.getSql();
if (hasMany) {
sql = "select count(*) from ( " + sql + ")";
if (selectCountWithAlias) {
sql += " as c";
}
}
// cache the query plan
queryPlan = new CQueryPlan(sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql());
request.putQueryPlan(queryPlan);
return new CQueryRowCount(request, predicates, sql);
}
/**
* Return the SQL Select statement as a String. Converts logical property
* names to physical deployment column names.
*/
public <T> CQuery<T> buildQuery(OrmQueryRequest<T> request) {
if (request.isSqlSelect()) {
return sqlSelectBuilder.build(request);
}
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
if (queryPlan != null) {
// Reuse the query plan so skip generating SqlTree and SQL.
// We do prepare and bind the new parameters
predicates.prepare(false);
return new CQuery<T>(request, predicates, queryPlan);
}
// RawSql or Generated Sql query
// Prepare the where, having and order by clauses.
// This also parses them from logical property names to
// database columns and determines 'includes'.
// We need to check these 'includes' for extra joins
// that are not included via select
predicates.prepare(true);
// Build the tree structure that represents the query.
SqlTree sqlTree = createSqlTree(request, predicates);
SqlLimitResponse res = buildSql(null, request, predicates, sqlTree);
boolean rawSql = request.isRawSql();
if (rawSql) {
queryPlan = new CQueryPlanRawSql(request, res, sqlTree, predicates.getLogWhereSql());
} else {
queryPlan = new CQueryPlan(request, res, sqlTree, rawSql, predicates.getLogWhereSql(), null);
}
// cache the query plan because we can reuse it and also
// gather query performance statistics based on it.
request.putQueryPlan(queryPlan);
return new CQuery<T>(request, predicates, queryPlan);
}
/**
* Build the SqlTree.
* <p>
* The SqlTree is immutable after construction and so is safe to use by
* concurrent threads.
* </p>
* <p>
* The predicates is used to add additional joins that come from the where or
* order by clauses that are not already included for the select clause.
* </p>
*/
private SqlTree createSqlTree(OrmQueryRequest<?> request, CQueryPredicates predicates) {
if (request.isRawSql()) {
return createRawSqlSqlTree(request, predicates);
}
return new SqlTreeBuilder(tableAliasPlaceHolder, columnAliasPrefix, request, predicates).build();
}
private SqlTree createRawSqlSqlTree(OrmQueryRequest<?> request, CQueryPredicates predicates) {
BeanDescriptor<?> descriptor = request.getBeanDescriptor();
ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping();
PathProperties pathProps = new PathProperties();
// convert list of columns into (tree like) PathProperties
Iterator<Column> it = columnMapping.getColumns();
while (it.hasNext()) {
RawSql.ColumnMapping.Column column = it.next();
String propertyName = column.getPropertyName();
if (!RawSqlBuilder.IGNORE_COLUMN.equals(propertyName)) {
ElPropertyValue el = descriptor.getElGetValue(propertyName);
if (el == null) {
String msg = "Property [" + propertyName + "] not found on " + descriptor.getFullName();
throw new PersistenceException(msg);
}
BeanProperty beanProperty = el.getBeanProperty();
if (beanProperty.isId()) {
// For @Id properties we chop off the last part of the path
propertyName = SplitName.parent(propertyName);
} else if (beanProperty instanceof BeanPropertyAssocOne<?>) {
String msg = "Column [" + column.getDbColumn() + "] mapped to complex Property[" + propertyName + "]";
msg += ". It should be mapped to a simple property (proably the Id property). ";
throw new PersistenceException(msg);
}
if (propertyName != null) {
String[] pathProp = SplitName.split(propertyName);
pathProps.addToPath(pathProp[0], pathProp[1]);
}
}
}
OrmQueryDetail detail = new OrmQueryDetail();
// transfer PathProperties into OrmQueryDetail
Iterator<String> pathIt = pathProps.getPaths().iterator();
while (pathIt.hasNext()) {
String path = pathIt.next();
Set<String> props = pathProps.get(path);
detail.getChunk(path, true).setDefaultProperties(null, props);
}
// build SqlTree based on OrmQueryDetail of the RawSql
return new SqlTreeBuilder(request, predicates, detail).build();
}
private SqlLimitResponse buildSql(String selectClause, OrmQueryRequest<?> request, CQueryPredicates predicates, SqlTree select) {
SpiQuery<?> query = request.getQuery();
RawSql rawSql = query.getRawSql();
if (rawSql != null) {
return rawSqlHandler.buildSql(request, predicates, rawSql.getSql());
}
BeanPropertyAssocMany<?> manyProp = select.getManyProperty();
boolean useSqlLimiter = false;
StringBuilder sb = new StringBuilder(500);
if (selectClause != null) {
sb.append(selectClause);
} else {
useSqlLimiter = (query.hasMaxRowsOrFirstRow() && manyProp == null);
if (!useSqlLimiter) {
sb.append("select ");
if (query.isDistinct()) {
sb.append("distinct ");
}
}
sb.append(select.getSelectSql());
}
sb.append(" ").append(NEW_LINE);
sb.append("from ");
// build the from clause potentially with joins
// required only for the predicates
sb.append(select.getFromSql());
String inheritanceWhere = select.getInheritanceWhereSql();
boolean hasWhere = false;
if (inheritanceWhere.length() > 0) {
sb.append(" ").append(NEW_LINE).append("where");
sb.append(inheritanceWhere);
hasWhere = true;
}
if (request.isFindById() || query.getId() != null) {
if (hasWhere) {
sb.append(" and ");
} else {
sb.append(NEW_LINE).append("where ");
}
BeanDescriptor<?> desc = request.getBeanDescriptor();
String idSql = desc.getIdBinderIdSql();
if (idSql.isEmpty()) {
throw new IllegalStateException("Executing FindById query on entity bean " + desc.getName()
+ " that doesn't have an @Id property??");
}
sb.append(idSql).append(" ");
hasWhere = true;
}
String dbWhere = predicates.getDbWhere();
if (!isEmpty(dbWhere)) {
if (!hasWhere) {
hasWhere = true;
sb.append(" ").append(NEW_LINE).append("where ");
} else {
sb.append("and ");
}
sb.append(dbWhere);
}
String dbFilterMany = predicates.getDbFilterMany();
if (!isEmpty(dbFilterMany)) {
if (!hasWhere) {
sb.append(" ").append(NEW_LINE).append("where ");
} else {
sb.append("and ");
}
sb.append(dbFilterMany);
}
String dbOrderBy = predicates.getDbOrderBy();
if (dbOrderBy != null) {
sb.append(" ").append(NEW_LINE);
sb.append("order by ").append(dbOrderBy);
}
if (useSqlLimiter) {
// use LIMIT/OFFSET, ROW_NUMBER() or rownum type SQL query limitation
SqlLimitRequest r = new OrmQueryLimitRequest(sb.toString(), dbOrderBy, query, dbPlatform);
return sqlLimiter.limit(r);
} else {
return new SqlLimitResponse(dbPlatform.completeSql(sb.toString(), query), false);
}
}
private boolean isEmpty(String s) {
return s == null || s.length() == 0;
}
}
@@ -1,167 +1,148 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebean.config.dbplatform.SqlLimiter;
import com.avaje.ebeaninternal.api.BindParams;
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.OrmQueryLimitRequest;
import com.avaje.ebeaninternal.server.util.BindParamsParser;
public class CQueryBuilderRawSql implements Constants {
private final SqlLimiter sqlLimiter;
private final DatabasePlatform dbPlatform;
CQueryBuilderRawSql(SqlLimiter sqlLimiter, DatabasePlatform dbPlatform) {
this.sqlLimiter = sqlLimiter;
this.dbPlatform = dbPlatform;
}
/**
* Build the full SQL Select statement for the request.
*/
public SqlLimitResponse buildSql(OrmQueryRequest<?> request, CQueryPredicates predicates, RawSql.Sql rsql) {
if (!rsql.isParsed()){
String sql = rsql.getUnparsedSql();
BindParams bindParams = request.getQuery().getBindParams();
if (bindParams != null && bindParams.requiresNamedParamsPrepare()){
// convert named parameters into positioned parameters
sql = BindParamsParser.parse(bindParams, sql);
}
return new SqlLimitResponse(sql, false);
}
String orderBy = getOrderBy(predicates, rsql);
// build the actual sql String
String sql = buildMainQuery(orderBy, request, predicates, rsql);
SpiQuery<?> query = request.getQuery();
if (query.hasMaxRowsOrFirstRow() && sqlLimiter != null) {
// wrap with a limit offset or ROW_NUMBER() etc
return sqlLimiter.limit(new OrmQueryLimitRequest(sql, orderBy, query, dbPlatform));
} else {
// add back select keyword (it was removed to support sqlQueryLimiter)
String prefix = "select "+ (rsql.isDistinct() ? "distinct " : "");
sql = prefix + sql;
return new SqlLimitResponse(sql, false);
}
}
private String buildMainQuery(String orderBy, OrmQueryRequest<?> request, CQueryPredicates predicates, RawSql.Sql sql) {
StringBuilder sb = new StringBuilder();
sb.append(sql.getPreFrom());
sb.append(" ");
sb.append(NEW_LINE);
String s = sql.getPreWhere();
BindParams bindParams = request.getQuery().getBindParams();
if (bindParams != null && bindParams.requiresNamedParamsPrepare()){
// convert named parameters into positioned parameters
// Named Parameters only allowed prior to dynamic where
// clause (so not allowed in having etc - use unparsed)
s = BindParamsParser.parse(bindParams, s);
}
sb.append(s);
sb.append(" ");
String dynamicWhere = null;
if (request.getQuery().getId() != null) {
// need to convert this as well. This avoids the
// assumption that id has its proper dbColumn assigned
// which may change if using multiple raw sql statements
// against the same bean.
BeanDescriptor<?> descriptor = request.getBeanDescriptor();
//FIXME: I think this is broken... needs to be logical
// and then parsed for RawSqlSelect...
dynamicWhere = descriptor.getIdBinderIdSql();
}
String dbWhere = predicates.getDbWhere();
if (!isEmpty(dbWhere)) {
if (dynamicWhere == null) {
dynamicWhere = dbWhere;
} else {
dynamicWhere += " and " + dbWhere;
}
}
if (!isEmpty(dynamicWhere)) {
sb.append(NEW_LINE);
if (sql.isAndWhereExpr()) {
sb.append("and ");
} else {
sb.append("where ");
}
sb.append(dynamicWhere);
sb.append(" ");
}
String preHaving = sql.getPreHaving();
if (!isEmpty(preHaving)) {
sb.append(NEW_LINE);
sb.append(preHaving);
sb.append(" ");
}
String dbHaving = predicates.getDbHaving();
if (!isEmpty(dbHaving)) {
sb.append(" ");
sb.append(NEW_LINE);
if (sql.isAndHavingExpr()) {
sb.append("and ");
} else {
sb.append("having ");
}
sb.append(dbHaving);
sb.append(" ");
}
if (!isEmpty(orderBy)) {
sb.append(NEW_LINE);
sb.append(" order by ").append(orderBy);
}
return sb.toString().trim();
}
private boolean isEmpty(String s) {
return s == null || s.length() == 0;
}
private String getOrderBy(CQueryPredicates predicates, RawSql.Sql sql) {
String orderBy = predicates.getDbOrderBy();
if (orderBy != null) {
return orderBy;
} else {
return sql.getOrderBy();
}
}
}
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebean.config.dbplatform.SqlLimiter;
import com.avaje.ebeaninternal.api.BindParams;
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.OrmQueryLimitRequest;
import com.avaje.ebeaninternal.server.util.BindParamsParser;
public class CQueryBuilderRawSql implements Constants {
private final SqlLimiter sqlLimiter;
private final DatabasePlatform dbPlatform;
CQueryBuilderRawSql(SqlLimiter sqlLimiter, DatabasePlatform dbPlatform) {
this.sqlLimiter = sqlLimiter;
this.dbPlatform = dbPlatform;
}
/**
* Build the full SQL Select statement for the request.
*/
public SqlLimitResponse buildSql(OrmQueryRequest<?> request, CQueryPredicates predicates, RawSql.Sql rsql) {
if (!rsql.isParsed()){
String sql = rsql.getUnparsedSql();
BindParams bindParams = request.getQuery().getBindParams();
if (bindParams != null && bindParams.requiresNamedParamsPrepare()){
// convert named parameters into positioned parameters
sql = BindParamsParser.parse(bindParams, sql);
}
return new SqlLimitResponse(sql, false);
}
String orderBy = getOrderBy(predicates, rsql);
// build the actual sql String
String sql = buildMainQuery(orderBy, request, predicates, rsql);
SpiQuery<?> query = request.getQuery();
if (query.hasMaxRowsOrFirstRow() && sqlLimiter != null) {
// wrap with a limit offset or ROW_NUMBER() etc
return sqlLimiter.limit(new OrmQueryLimitRequest(sql, orderBy, query, dbPlatform));
} else {
// add back select keyword (it was removed to support sqlQueryLimiter)
String prefix = "select "+ (rsql.isDistinct() ? "distinct " : "");
sql = prefix + sql;
return new SqlLimitResponse(sql, false);
}
}
private String buildMainQuery(String orderBy, OrmQueryRequest<?> request, CQueryPredicates predicates, RawSql.Sql sql) {
StringBuilder sb = new StringBuilder();
sb.append(sql.getPreFrom());
sb.append(" ");
sb.append(NEW_LINE);
String s = sql.getPreWhere();
BindParams bindParams = request.getQuery().getBindParams();
if (bindParams != null && bindParams.requiresNamedParamsPrepare()){
// convert named parameters into positioned parameters
// Named Parameters only allowed prior to dynamic where
// clause (so not allowed in having etc - use unparsed)
s = BindParamsParser.parse(bindParams, s);
}
sb.append(s);
sb.append(" ");
String dynamicWhere = null;
if (request.getQuery().getId() != null) {
// need to convert this as well. This avoids the
// assumption that id has its proper dbColumn assigned
// which may change if using multiple raw sql statements
// against the same bean.
BeanDescriptor<?> descriptor = request.getBeanDescriptor();
//FIXME: I think this is broken... needs to be logical
// and then parsed for RawSqlSelect...
dynamicWhere = descriptor.getIdBinderIdSql();
}
String dbWhere = predicates.getDbWhere();
if (!isEmpty(dbWhere)) {
if (dynamicWhere == null) {
dynamicWhere = dbWhere;
} else {
dynamicWhere += " and " + dbWhere;
}
}
if (!isEmpty(dynamicWhere)) {
sb.append(NEW_LINE);
if (sql.isAndWhereExpr()) {
sb.append("and ");
} else {
sb.append("where ");
}
sb.append(dynamicWhere);
sb.append(" ");
}
String preHaving = sql.getPreHaving();
if (!isEmpty(preHaving)) {
sb.append(NEW_LINE);
sb.append(preHaving);
sb.append(" ");
}
String dbHaving = predicates.getDbHaving();
if (!isEmpty(dbHaving)) {
sb.append(" ");
sb.append(NEW_LINE);
if (sql.isAndHavingExpr()) {
sb.append("and ");
} else {
sb.append("having ");
}
sb.append(dbHaving);
sb.append(" ");
}
if (!isEmpty(orderBy)) {
sb.append(NEW_LINE);
sb.append(" order by ").append(orderBy);
}
return sb.toString().trim();
}
private boolean isEmpty(String s) {
return s == null || s.length() == 0;
}
private String getOrderBy(CQueryPredicates predicates, RawSql.Sql sql) {
String orderBy = predicates.getDbOrderBy();
if (orderBy != null) {
return orderBy;
} else {
return sql.getOrderBy();
}
}
}
@@ -1,417 +1,398 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.concurrent.FutureTask;
import java.util.logging.Logger;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.jmx.MAdminLogging;
import com.avaje.ebeaninternal.server.persist.Binder;
/**
* Handles the Object Relational fetching.
*/
public class CQueryEngine {
private static final Logger logger = Logger.getLogger(CQueryEngine.class.getName());
private final CQueryBuilder queryBuilder;
private final MAdminLogging logControl;
private final BackgroundExecutor backgroundExecutor;
private final int defaultSecondaryQueryBatchSize = 100;
public CQueryEngine(DatabasePlatform dbPlatform, MAdminLogging logControl, Binder binder, BackgroundExecutor backgroundExecutor) {
this.logControl = logControl;
this.backgroundExecutor = backgroundExecutor;
this.queryBuilder = new CQueryBuilder(backgroundExecutor, dbPlatform, binder);
}
public <T> CQuery<T> buildQuery(OrmQueryRequest<T> request) {
return queryBuilder.buildQuery(request);
}
/**
* Build and execute the find Id's query.
*/
public <T> BeanIdList findIds(OrmQueryRequest<T> request) {
CQueryFetchIds rcQuery = queryBuilder.buildFetchIdsQuery(request);
try {
String sql = rcQuery.getGeneratedSql();
sql = sql.replace(Constants.NEW_LINE, ' ');
if (logControl.isDebugGeneratedSql()) {
System.out.println(sql);
}
request.logSql(sql);
BeanIdList list = rcQuery.findIds();
if (request.isLogSummary()) {
request.getTransaction().logInternal(rcQuery.getSummary());
}
if (!list.isFetchingInBackground() && request.getQuery().isFutureFetch()) {
// end the transaction for futureFindIds (it had it's own one)
logger.fine("Future findIds completed!");
request.getTransaction().end();
}
return list;
} catch (SQLException e) {
throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql());
}
}
/**
* Build and execute the row count query.
*/
public <T> int findRowCount(OrmQueryRequest<T> request) {
CQueryRowCount rcQuery = queryBuilder.buildRowCountQuery(request);
try {
String sql = rcQuery.getGeneratedSql();
sql = sql.replace(Constants.NEW_LINE, ' ');
if (logControl.isDebugGeneratedSql()) {
System.out.println(sql);
}
request.logSql(sql);
int rowCount = rcQuery.findRowCount();
if (request.isLogSummary()) {
request.getTransaction().logInternal(rcQuery.getSummary());
}
if (request.getQuery().isFutureFetch()) {
logger.fine("Future findRowCount completed!");
request.getTransaction().end();
}
return rowCount;
} catch (SQLException e) {
throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql());
}
}
/**
* Read many beans using an iterator (except you need to close() the iterator
* when you have finished).
*/
public <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request) {
CQuery<T> cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
try {
if (logControl.isDebugGeneratedSql()) {
logSqlToConsole(cquery);
}
if (request.isLogSql()) {
logSql(cquery);
}
if (!cquery.prepareBindExecuteQuery()) {
// query has been cancelled already
logger.finest("Future fetch already cancelled");
return null;
}
int iterateBufferSize = request.getSecondaryQueriesMinBatchSize(defaultSecondaryQueryBatchSize);
QueryIterator<T> readIterate = cquery.readIterate(iterateBufferSize, request);
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
return readIterate;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
}
}
/**
* Find a list/map/set of beans.
*/
public <T> BeanCollection<T> findMany(OrmQueryRequest<T> request) {
// flag indicating whether we need to close the resources...
boolean useBackgroundToContinueFetch = false;
CQuery<T> cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
try {
if (logControl.isDebugGeneratedSql()) {
logSqlToConsole(cquery);
}
if (request.isLogSql()) {
logSql(cquery);
}
if (!cquery.prepareBindExecuteQuery()) {
// query has been cancelled already
logger.finest("Future fetch already cancelled");
return null;
}
BeanCollection<T> beanCollection = cquery.readCollection();
BeanCollectionTouched collectionTouched = request.getQuery().getBeanCollectionTouched();
if (collectionTouched != null) {
// register a listener that wants to be notified when the
// bean collection is first used
beanCollection.setBeanCollectionTouched(collectionTouched);
}
if (cquery.useBackgroundToContinueFetch()) {
// stop the request from putting connection back into pool
// before background fetching is finished.
request.setBackgroundFetching();
useBackgroundToContinueFetch = true;
BackgroundFetch fetch = new BackgroundFetch(cquery);
FutureTask<Integer> future = new FutureTask<Integer>(fetch);
beanCollection.setBackgroundFetch(future);
backgroundExecutor.execute(future);
}
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
request.executeSecondaryQueries(defaultSecondaryQueryBatchSize);
return beanCollection;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);// request, e,
// cquery.getBindLog(),
// cquery.getGeneratedSql());
} finally {
if (useBackgroundToContinueFetch) {
// left closing resources to BackgroundFetch...
} else {
if (cquery != null) {
cquery.close();
}
if (request.getQuery().isFutureFetch()) {
// end the transaction for futureFindIds
// as it had it's own transaction
logger.fine("Future fetch completed!");
request.getTransaction().end();
}
}
}
}
/**
* Find and return a single bean using its unique id.
*/
public <T> T find(OrmQueryRequest<T> request) {
T bean = null;
CQuery<T> cquery = queryBuilder.buildQuery(request);
try {
if (logControl.isDebugGeneratedSql()) {
logSqlToConsole(cquery);
}
if (request.isLogSql()) {
logSql(cquery);
}
cquery.prepareBindExecuteQuery();
if (cquery.readBean()) {
bean = cquery.getLoadedBean();
}
if (request.isLogSummary()) {
logFindBeanSummary(cquery);
}
request.executeSecondaryQueries(defaultSecondaryQueryBatchSize);
return bean;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
} finally {
cquery.close();
}
}
/**
* Log the generated SQL to the console.
*/
private void logSqlToConsole(CQuery<?> cquery) {
SpiQuery<?> query = cquery.getQueryRequest().getQuery();
String loadMode = query.getLoadMode();
String loadDesc = query.getLoadDescription();
String sql = cquery.getGeneratedSql();
String summary = cquery.getSummary();
StringBuilder sb = new StringBuilder(1000);
sb.append("<sql ");
if (query.isAutofetchTuned()) {
sb.append("tuned='true' ");
}
if (loadMode != null) {
sb.append("mode='").append(loadMode).append("' ");
}
sb.append("summary='").append(summary);
if (loadDesc != null) {
sb.append("' load='").append(loadDesc);
}
sb.append("' >");
sb.append(Constants.NEW_LINE);
sb.append(sql);
sb.append(Constants.NEW_LINE).append("</sql>");
System.out.println(sb.toString());
}
/**
* Log the generated SQL to the transaction log.
*/
private void logSql(CQuery<?> query) {
String sql = query.getGeneratedSql();
sql = sql.replace(Constants.NEW_LINE, ' ');
query.getTransaction().logInternal(sql);
}
/**
* Log the FindById summary to the transaction log.
*/
private void logFindBeanSummary(CQuery<?> q) {
SpiQuery<?> query = q.getQueryRequest().getQuery();
String loadMode = query.getLoadMode();
String loadDesc = query.getLoadDescription();
String lazyLoadProp = query.getLazyLoadProperty();
ObjectGraphNode node = query.getParentNode();
String originKey;
if (node == null || node.getOriginQueryPoint() == null) {
originKey = null;
} else {
originKey = node.getOriginQueryPoint().getKey();
}
StringBuilder msg = new StringBuilder(200);
msg.append("FindBean ");
if (loadMode != null) {
msg.append("mode[").append(loadMode).append("] ");
}
msg.append("type[").append(q.getBeanName()).append("] ");
if (query.isAutofetchTuned()) {
msg.append("tuned[true] ");
}
if (originKey != null) {
msg.append("origin[").append(originKey).append("] ");
}
if (lazyLoadProp != null) {
msg.append("lazyLoadProp[").append(lazyLoadProp).append("] ");
}
if (loadDesc != null) {
msg.append("load[").append(loadDesc).append("] ");
}
msg.append("exeMicros[").append(q.getQueryExecutionTimeMicros());
msg.append("] rows[").append(q.getLoadedRowDetail());
msg.append("] bind[").append(q.getBindLog()).append("]");
q.getTransaction().logInternal(msg.toString());
}
/**
* Log the FindMany to the transaction log.
*/
private void logFindManySummary(CQuery<?> q) {
SpiQuery<?> query = q.getQueryRequest().getQuery();
String loadMode = query.getLoadMode();
String loadDesc = query.getLoadDescription();
String lazyLoadProp = query.getLazyLoadProperty();
ObjectGraphNode node = query.getParentNode();
String originKey;
if (node == null || node.getOriginQueryPoint() == null) {
originKey = null;
} else {
originKey = node.getOriginQueryPoint().getKey();
}
StringBuilder msg = new StringBuilder(200);
msg.append("FindMany ");
if (loadMode != null) {
msg.append("mode[").append(loadMode).append("] ");
}
msg.append("type[").append(q.getBeanName()).append("] ");
if (query.isAutofetchTuned()) {
msg.append("tuned[true] ");
}
if (originKey != null) {
msg.append("origin[").append(originKey).append("] ");
}
if (lazyLoadProp != null) {
msg.append("lazyLoadProp[").append(lazyLoadProp).append("] ");
}
if (loadDesc != null) {
msg.append("load[").append(loadDesc).append("] ");
}
msg.append("exeMicros[").append(q.getQueryExecutionTimeMicros());
msg.append("] rows[").append(q.getLoadedRowDetail());
msg.append("] name[").append(q.getName());
msg.append("] predicates[").append(q.getLogWhereSql());
msg.append("] bind[").append(q.getBindLog()).append("]");
q.getTransaction().logInternal(msg.toString());
}
}
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.concurrent.FutureTask;
import java.util.logging.Logger;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.jmx.MAdminLogging;
import com.avaje.ebeaninternal.server.persist.Binder;
/**
* Handles the Object Relational fetching.
*/
public class CQueryEngine {
private static final Logger logger = Logger.getLogger(CQueryEngine.class.getName());
private final CQueryBuilder queryBuilder;
private final MAdminLogging logControl;
private final BackgroundExecutor backgroundExecutor;
private final int defaultSecondaryQueryBatchSize = 100;
public CQueryEngine(DatabasePlatform dbPlatform, MAdminLogging logControl, Binder binder, BackgroundExecutor backgroundExecutor) {
this.logControl = logControl;
this.backgroundExecutor = backgroundExecutor;
this.queryBuilder = new CQueryBuilder(backgroundExecutor, dbPlatform, binder);
}
public <T> CQuery<T> buildQuery(OrmQueryRequest<T> request) {
return queryBuilder.buildQuery(request);
}
/**
* Build and execute the find Id's query.
*/
public <T> BeanIdList findIds(OrmQueryRequest<T> request) {
CQueryFetchIds rcQuery = queryBuilder.buildFetchIdsQuery(request);
try {
String sql = rcQuery.getGeneratedSql();
sql = sql.replace(Constants.NEW_LINE, ' ');
if (logControl.isDebugGeneratedSql()) {
System.out.println(sql);
}
request.logSql(sql);
BeanIdList list = rcQuery.findIds();
if (request.isLogSummary()) {
request.getTransaction().logInternal(rcQuery.getSummary());
}
if (!list.isFetchingInBackground() && request.getQuery().isFutureFetch()) {
// end the transaction for futureFindIds (it had it's own one)
logger.fine("Future findIds completed!");
request.getTransaction().end();
}
return list;
} catch (SQLException e) {
throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql());
}
}
/**
* Build and execute the row count query.
*/
public <T> int findRowCount(OrmQueryRequest<T> request) {
CQueryRowCount rcQuery = queryBuilder.buildRowCountQuery(request);
try {
String sql = rcQuery.getGeneratedSql();
sql = sql.replace(Constants.NEW_LINE, ' ');
if (logControl.isDebugGeneratedSql()) {
System.out.println(sql);
}
request.logSql(sql);
int rowCount = rcQuery.findRowCount();
if (request.isLogSummary()) {
request.getTransaction().logInternal(rcQuery.getSummary());
}
if (request.getQuery().isFutureFetch()) {
logger.fine("Future findRowCount completed!");
request.getTransaction().end();
}
return rowCount;
} catch (SQLException e) {
throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql());
}
}
/**
* Read many beans using an iterator (except you need to close() the iterator
* when you have finished).
*/
public <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request) {
CQuery<T> cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
try {
if (logControl.isDebugGeneratedSql()) {
logSqlToConsole(cquery);
}
if (request.isLogSql()) {
logSql(cquery);
}
if (!cquery.prepareBindExecuteQuery()) {
// query has been cancelled already
logger.finest("Future fetch already cancelled");
return null;
}
int iterateBufferSize = request.getSecondaryQueriesMinBatchSize(defaultSecondaryQueryBatchSize);
QueryIterator<T> readIterate = cquery.readIterate(iterateBufferSize, request);
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
return readIterate;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
}
}
/**
* Find a list/map/set of beans.
*/
public <T> BeanCollection<T> findMany(OrmQueryRequest<T> request) {
// flag indicating whether we need to close the resources...
boolean useBackgroundToContinueFetch = false;
CQuery<T> cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
try {
if (logControl.isDebugGeneratedSql()) {
logSqlToConsole(cquery);
}
if (request.isLogSql()) {
logSql(cquery);
}
if (!cquery.prepareBindExecuteQuery()) {
// query has been cancelled already
logger.finest("Future fetch already cancelled");
return null;
}
BeanCollection<T> beanCollection = cquery.readCollection();
BeanCollectionTouched collectionTouched = request.getQuery().getBeanCollectionTouched();
if (collectionTouched != null) {
// register a listener that wants to be notified when the
// bean collection is first used
beanCollection.setBeanCollectionTouched(collectionTouched);
}
if (cquery.useBackgroundToContinueFetch()) {
// stop the request from putting connection back into pool
// before background fetching is finished.
request.setBackgroundFetching();
useBackgroundToContinueFetch = true;
BackgroundFetch fetch = new BackgroundFetch(cquery);
FutureTask<Integer> future = new FutureTask<Integer>(fetch);
beanCollection.setBackgroundFetch(future);
backgroundExecutor.execute(future);
}
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
request.executeSecondaryQueries(defaultSecondaryQueryBatchSize);
return beanCollection;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);// request, e,
// cquery.getBindLog(),
// cquery.getGeneratedSql());
} finally {
if (useBackgroundToContinueFetch) {
// left closing resources to BackgroundFetch...
} else {
if (cquery != null) {
cquery.close();
}
if (request.getQuery().isFutureFetch()) {
// end the transaction for futureFindIds
// as it had it's own transaction
logger.fine("Future fetch completed!");
request.getTransaction().end();
}
}
}
}
/**
* Find and return a single bean using its unique id.
*/
public <T> T find(OrmQueryRequest<T> request) {
T bean = null;
CQuery<T> cquery = queryBuilder.buildQuery(request);
try {
if (logControl.isDebugGeneratedSql()) {
logSqlToConsole(cquery);
}
if (request.isLogSql()) {
logSql(cquery);
}
cquery.prepareBindExecuteQuery();
if (cquery.readBean()) {
bean = cquery.getLoadedBean();
}
if (request.isLogSummary()) {
logFindBeanSummary(cquery);
}
request.executeSecondaryQueries(defaultSecondaryQueryBatchSize);
return bean;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
} finally {
cquery.close();
}
}
/**
* Log the generated SQL to the console.
*/
private void logSqlToConsole(CQuery<?> cquery) {
SpiQuery<?> query = cquery.getQueryRequest().getQuery();
String loadMode = query.getLoadMode();
String loadDesc = query.getLoadDescription();
String sql = cquery.getGeneratedSql();
String summary = cquery.getSummary();
StringBuilder sb = new StringBuilder(1000);
sb.append("<sql ");
if (query.isAutofetchTuned()) {
sb.append("tuned='true' ");
}
if (loadMode != null) {
sb.append("mode='").append(loadMode).append("' ");
}
sb.append("summary='").append(summary);
if (loadDesc != null) {
sb.append("' load='").append(loadDesc);
}
sb.append("' >");
sb.append(Constants.NEW_LINE);
sb.append(sql);
sb.append(Constants.NEW_LINE).append("</sql>");
System.out.println(sb.toString());
}
/**
* Log the generated SQL to the transaction log.
*/
private void logSql(CQuery<?> query) {
String sql = query.getGeneratedSql();
sql = sql.replace(Constants.NEW_LINE, ' ');
query.getTransaction().logInternal(sql);
}
/**
* Log the FindById summary to the transaction log.
*/
private void logFindBeanSummary(CQuery<?> q) {
SpiQuery<?> query = q.getQueryRequest().getQuery();
String loadMode = query.getLoadMode();
String loadDesc = query.getLoadDescription();
String lazyLoadProp = query.getLazyLoadProperty();
ObjectGraphNode node = query.getParentNode();
String originKey;
if (node == null || node.getOriginQueryPoint() == null) {
originKey = null;
} else {
originKey = node.getOriginQueryPoint().getKey();
}
StringBuilder msg = new StringBuilder(200);
msg.append("FindBean ");
if (loadMode != null) {
msg.append("mode[").append(loadMode).append("] ");
}
msg.append("type[").append(q.getBeanName()).append("] ");
if (query.isAutofetchTuned()) {
msg.append("tuned[true] ");
}
if (originKey != null) {
msg.append("origin[").append(originKey).append("] ");
}
if (lazyLoadProp != null) {
msg.append("lazyLoadProp[").append(lazyLoadProp).append("] ");
}
if (loadDesc != null) {
msg.append("load[").append(loadDesc).append("] ");
}
msg.append("exeMicros[").append(q.getQueryExecutionTimeMicros());
msg.append("] rows[").append(q.getLoadedRowDetail());
msg.append("] bind[").append(q.getBindLog()).append("]");
q.getTransaction().logInternal(msg.toString());
}
/**
* Log the FindMany to the transaction log.
*/
private void logFindManySummary(CQuery<?> q) {
SpiQuery<?> query = q.getQueryRequest().getQuery();
String loadMode = query.getLoadMode();
String loadDesc = query.getLoadDescription();
String lazyLoadProp = query.getLazyLoadProperty();
ObjectGraphNode node = query.getParentNode();
String originKey;
if (node == null || node.getOriginQueryPoint() == null) {
originKey = null;
} else {
originKey = node.getOriginQueryPoint().getKey();
}
StringBuilder msg = new StringBuilder(200);
msg.append("FindMany ");
if (loadMode != null) {
msg.append("mode[").append(loadMode).append("] ");
}
msg.append("type[").append(q.getBeanName()).append("] ");
if (query.isAutofetchTuned()) {
msg.append("tuned[true] ");
}
if (originKey != null) {
msg.append("origin[").append(originKey).append("] ");
}
if (lazyLoadProp != null) {
msg.append("lazyLoadProp[").append(lazyLoadProp).append("] ");
}
if (loadDesc != null) {
msg.append("load[").append(loadDesc).append("] ");
}
msg.append("exeMicros[").append(q.getQueryExecutionTimeMicros());
msg.append("] rows[").append(q.getLoadedRowDetail());
msg.append("] name[").append(q.getName());
msg.append("] predicates[").append(q.getLogWhereSql());
msg.append("] bind[").append(q.getBindLog()).append("]");
q.getTransaction().logInternal(msg.toString());
}
}
@@ -1,335 +1,316 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.FutureTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.DataReader;
import com.avaje.ebeaninternal.server.type.RsetDataReader;
/**
* Executes the select row count query.
*/
public class CQueryFetchIds {
private static final Logger logger = Logger.getLogger(CQueryFetchIds.class.getName());
/**
* The overall find request wrapper object.
*/
private final OrmQueryRequest<?> request;
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
private final BackgroundExecutor backgroundExecutor;
/**
* Where clause predicates.
*/
private final CQueryPredicates predicates;
/**
* The final sql that is generated.
*/
private final String sql;
private RsetDataReader dataReader;
/**
* The statement used to create the resultSet.
*/
private PreparedStatement pstmt;
private String bindLog;
private long startNano;
private int executionTimeMicros;
private int rowCount;
private final int maxRows;
private final int bgFetchAfter;
/**
* Create the Sql select based on the request.
*/
public CQueryFetchIds(OrmQueryRequest<?> request, CQueryPredicates predicates,
String sql, BackgroundExecutor backgroundExecutor) {
this.backgroundExecutor = backgroundExecutor;
this.request = request;
this.query = request.getQuery();
this.sql = sql;
this.maxRows = query.getMaxRows();
this.bgFetchAfter = query.getBackgroundFetchAfter();
query.setGeneratedSql(sql);
this.desc = request.getBeanDescriptor();
this.predicates = predicates;
}
/**
* Return a summary description of this query.
*/
public String getSummary() {
StringBuilder sb = new StringBuilder();
sb.append("FindIds exeMicros[").append(executionTimeMicros)
.append("] rows[").append(rowCount)
.append("] type[").append(desc.getName())
.append("] predicates[").append(predicates.getLogWhereSql())
.append("] bind[").append(bindLog).append("]");
return sb.toString();
}
/**
* Return the bind log.
*/
public String getBindLog() {
return bindLog;
}
/**
* Return the generated sql.
*/
public String getGeneratedSql() {
return sql;
}
public SpiOrmQueryRequest<?> getQueryRequest() {
return request;
}
/**
* Execute the query returning the row count.
*/
public BeanIdList findIds() throws SQLException {
boolean useBackgroundToContinueFetch = false;
startNano = System.nanoTime();
try {
// get the list that we are going to put the id's into.
// This was already set so that it is available to be
// read by other threads (it is a synchronised list)
List<Object> idList = query.getIdList();
if (idList == null){
// running in foreground thread (not FutureIds query)
idList = Collections.synchronizedList(new ArrayList<Object>());
query.setIdList(idList);
}
BeanIdList result = new BeanIdList(idList);
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
pstmt = conn.prepareStatement(sql);
if (query.getBufferFetchSizeHint() > 0){
pstmt.setFetchSize(query.getBufferFetchSizeHint());
}
if (query.getTimeout() > 0){
pstmt.setQueryTimeout(query.getTimeout());
}
bindLog = predicates.bind(new DataBind(pstmt));
ResultSet rset = pstmt.executeQuery();
dataReader = new RsetDataReader(rset);
boolean hitMaxRows = false;
boolean hasMoreRows = false;
rowCount = 0;
DbReadContext ctx = new DbContext();
while (rset.next()){
Object idValue = desc.getIdBinder().read(ctx);
idList.add(idValue);
// reset back to 0
dataReader.resetColumnPosition();
rowCount++;
if (maxRows > 0 && rowCount == maxRows) {
hitMaxRows = true;
hasMoreRows = rset.next();
break;
} else if (bgFetchAfter > 0 && rowCount >= bgFetchAfter) {
useBackgroundToContinueFetch = true;
break;
}
}
if (hitMaxRows){
result.setHasMore(hasMoreRows);
}
if (useBackgroundToContinueFetch){
// tell the request not to end the transaction
// as we leave that up to the BackgroundIdFetch
request.setBackgroundFetching();
// submit background future task
BackgroundIdFetch bgFetch = new BackgroundIdFetch(t, rset, pstmt, ctx, desc, result);
FutureTask<Integer> future = new FutureTask<Integer>(bgFetch);
backgroundExecutor.execute(future);
// set on result so we can use the futureTask to wait
result.setBackgroundFetch(future);
}
long exeNano = System.nanoTime() - startNano;
executionTimeMicros = (int)exeNano/1000;
return result;
} finally {
if (useBackgroundToContinueFetch) {
// left closing resources to BackgroundFetch...
} else {
close();
}
}
}
/**
* Close the resources.
* <p>
* The jdbc resultSet and statement need to be closed. Its important that
* this method is called.
* </p>
*/
private void close() {
try {
if (dataReader != null) {
dataReader.close();
dataReader = null;
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
try {
if (pstmt != null) {
pstmt.close();
pstmt = null;
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
}
class DbContext implements DbReadContext {
public void propagateState(Object e) {
throw new RuntimeException("Not Called");
}
public Mode getQueryMode() {
return Mode.NORMAL;
}
public DataReader getDataReader() {
return dataReader;
}
public boolean isVanillaMode() {
return false;
}
public Boolean isReadOnly() {
return Boolean.FALSE;
}
public boolean isRawSql() {
return false;
}
public void register(String path, EntityBeanIntercept ebi){
}
public void register(String path, BeanCollection<?> bc){
}
public BeanPropertyAssocMany<?> getManyProperty() {
// always null
return null;
}
public PersistenceContext getPersistenceContext() {
// always null
return null;
}
public boolean isAutoFetchProfiling() {
return false;
}
public void profileBean(EntityBeanIntercept ebi, String prefix) {
// no-op
}
public void setCurrentPrefix(String currentPrefix,Map<String, String> pathMap) {
// no-op
}
public void setLoadedBean(Object loadedBean, Object id) {
// no-op
}
public void setLoadedManyBean(Object loadedBean) {
// no-op
}
}
}
package com.avaje.ebeaninternal.server.query;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.FutureTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.DataReader;
import com.avaje.ebeaninternal.server.type.RsetDataReader;
/**
* Executes the select row count query.
*/
public class CQueryFetchIds {
private static final Logger logger = Logger.getLogger(CQueryFetchIds.class.getName());
/**
* The overall find request wrapper object.
*/
private final OrmQueryRequest<?> request;
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
private final BackgroundExecutor backgroundExecutor;
/**
* Where clause predicates.
*/
private final CQueryPredicates predicates;
/**
* The final sql that is generated.
*/
private final String sql;
private RsetDataReader dataReader;
/**
* The statement used to create the resultSet.
*/
private PreparedStatement pstmt;
private String bindLog;
private long startNano;
private int executionTimeMicros;
private int rowCount;
private final int maxRows;
private final int bgFetchAfter;
/**
* Create the Sql select based on the request.
*/
public CQueryFetchIds(OrmQueryRequest<?> request, CQueryPredicates predicates,
String sql, BackgroundExecutor backgroundExecutor) {
this.backgroundExecutor = backgroundExecutor;
this.request = request;
this.query = request.getQuery();
this.sql = sql;
this.maxRows = query.getMaxRows();
this.bgFetchAfter = query.getBackgroundFetchAfter();
query.setGeneratedSql(sql);
this.desc = request.getBeanDescriptor();
this.predicates = predicates;
}
/**
* Return a summary description of this query.
*/
public String getSummary() {
StringBuilder sb = new StringBuilder();
sb.append("FindIds exeMicros[").append(executionTimeMicros)
.append("] rows[").append(rowCount)
.append("] type[").append(desc.getName())
.append("] predicates[").append(predicates.getLogWhereSql())
.append("] bind[").append(bindLog).append("]");
return sb.toString();
}
/**
* Return the bind log.
*/
public String getBindLog() {
return bindLog;
}
/**
* Return the generated sql.
*/
public String getGeneratedSql() {
return sql;
}
public SpiOrmQueryRequest<?> getQueryRequest() {
return request;
}
/**
* Execute the query returning the row count.
*/
public BeanIdList findIds() throws SQLException {
boolean useBackgroundToContinueFetch = false;
startNano = System.nanoTime();
try {
// get the list that we are going to put the id's into.
// This was already set so that it is available to be
// read by other threads (it is a synchronised list)
List<Object> idList = query.getIdList();
if (idList == null){
// running in foreground thread (not FutureIds query)
idList = Collections.synchronizedList(new ArrayList<Object>());
query.setIdList(idList);
}
BeanIdList result = new BeanIdList(idList);
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
pstmt = conn.prepareStatement(sql);
if (query.getBufferFetchSizeHint() > 0){
pstmt.setFetchSize(query.getBufferFetchSizeHint());
}
if (query.getTimeout() > 0){
pstmt.setQueryTimeout(query.getTimeout());
}
bindLog = predicates.bind(new DataBind(pstmt));
ResultSet rset = pstmt.executeQuery();
dataReader = new RsetDataReader(rset);
boolean hitMaxRows = false;
boolean hasMoreRows = false;
rowCount = 0;
DbReadContext ctx = new DbContext();
while (rset.next()){
Object idValue = desc.getIdBinder().read(ctx);
idList.add(idValue);
// reset back to 0
dataReader.resetColumnPosition();
rowCount++;
if (maxRows > 0 && rowCount == maxRows) {
hitMaxRows = true;
hasMoreRows = rset.next();
break;
} else if (bgFetchAfter > 0 && rowCount >= bgFetchAfter) {
useBackgroundToContinueFetch = true;
break;
}
}
if (hitMaxRows){
result.setHasMore(hasMoreRows);
}
if (useBackgroundToContinueFetch){
// tell the request not to end the transaction
// as we leave that up to the BackgroundIdFetch
request.setBackgroundFetching();
// submit background future task
BackgroundIdFetch bgFetch = new BackgroundIdFetch(t, rset, pstmt, ctx, desc, result);
FutureTask<Integer> future = new FutureTask<Integer>(bgFetch);
backgroundExecutor.execute(future);
// set on result so we can use the futureTask to wait
result.setBackgroundFetch(future);
}
long exeNano = System.nanoTime() - startNano;
executionTimeMicros = (int)exeNano/1000;
return result;
} finally {
if (useBackgroundToContinueFetch) {
// left closing resources to BackgroundFetch...
} else {
close();
}
}
}
/**
* Close the resources.
* <p>
* The jdbc resultSet and statement need to be closed. Its important that
* this method is called.
* </p>
*/
private void close() {
try {
if (dataReader != null) {
dataReader.close();
dataReader = null;
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
try {
if (pstmt != null) {
pstmt.close();
pstmt = null;
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
}
class DbContext implements DbReadContext {
public void propagateState(Object e) {
throw new RuntimeException("Not Called");
}
public Mode getQueryMode() {
return Mode.NORMAL;
}
public DataReader getDataReader() {
return dataReader;
}
public boolean isVanillaMode() {
return false;
}
public Boolean isReadOnly() {
return Boolean.FALSE;
}
public boolean isRawSql() {
return false;
}
public void register(String path, EntityBeanIntercept ebi){
}
public void register(String path, BeanCollection<?> bc){
}
public BeanPropertyAssocMany<?> getManyProperty() {
// always null
return null;
}
public PersistenceContext getPersistenceContext() {
// always null
return null;
}
public boolean isAutoFetchProfiling() {
return false;
}
public void profileBean(EntityBeanIntercept ebi, String prefix) {
// no-op
}
public void setCurrentPrefix(String currentPrefix,Map<String, String> pathMap) {
// no-op
}
public void setLoadedBean(Object loadedBean, Object id) {
// no-op
}
public void setLoadedManyBean(Object loadedBean) {
// no-op
}
}
}
@@ -1,68 +1,46 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
*
*/
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import javax.persistence.PersistenceException;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* QueryIterator that does not require a buffer for secondary queries.
*
* @author rbygrave
*/
class CQueryIteratorSimple<T> implements QueryIterator<T> {
private final CQuery<T> cquery;
private final OrmQueryRequest<T> request;
CQueryIteratorSimple(CQuery<T> cquery, OrmQueryRequest<T> request){
this.cquery = cquery;
this.request = request;
}
public boolean hasNext() {
try {
return cquery.hasNextBean(true);
} catch (SQLException e){
throw cquery.createPersistenceException(e);
}
}
public T next() {
return cquery.getLoadedBean();
}
public void close() {
cquery.updateExecutionStatistics();
cquery.close();
request.endTransIfRequired();
}
public void remove() {
throw new PersistenceException("Remove not allowed");
}
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import javax.persistence.PersistenceException;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* QueryIterator that does not require a buffer for secondary queries.
*
* @author rbygrave
*/
class CQueryIteratorSimple<T> implements QueryIterator<T> {
private final CQuery<T> cquery;
private final OrmQueryRequest<T> request;
CQueryIteratorSimple(CQuery<T> cquery, OrmQueryRequest<T> request){
this.cquery = cquery;
this.request = request;
}
public boolean hasNext() {
try {
return cquery.hasNextBean(true);
} catch (SQLException e){
throw cquery.createPersistenceException(e);
}
}
public T next() {
return cquery.getLoadedBean();
}
public void close() {
cquery.updateExecutionStatistics();
cquery.close();
request.endTransIfRequired();
}
public void remove() {
throw new PersistenceException("Remove not allowed");
}
}
@@ -1,89 +1,67 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
*
*/
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.persistence.PersistenceException;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* A QueryIterator that uses a buffer to execute secondary queries periodically.
*
* @author rbygrave
*/
class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
private final CQuery<T> cquery;
private final int bufferSize;
private final OrmQueryRequest<T> request;
private final ArrayList<T> buffer;
private boolean moreToLoad = true;
CQueryIteratorWithBuffer(CQuery<T> cquery, OrmQueryRequest<T> request, int bufferSize) {
this.cquery = cquery;
this.request = request;
this.bufferSize = bufferSize;
this.buffer = new ArrayList<T>(bufferSize);
}
public boolean hasNext() {
try {
if (buffer.isEmpty() && moreToLoad) {
// load buffer
int i = -1;
while (moreToLoad && ++i < bufferSize) {
if (cquery.hasNextBean(true)) {
buffer.add(cquery.getLoadedBean());
} else {
moreToLoad = false;
}
}
// execute secondary queries
request.executeSecondaryQueries(bufferSize);
}
return !buffer.isEmpty();
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
}
}
public T next() {
return buffer.remove(0);
}
public void close() {
cquery.updateExecutionStatistics();
cquery.close();
request.endTransIfRequired();
}
public void remove() {
throw new PersistenceException("Remove not allowed");
}
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.persistence.PersistenceException;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* A QueryIterator that uses a buffer to execute secondary queries periodically.
*
* @author rbygrave
*/
class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
private final CQuery<T> cquery;
private final int bufferSize;
private final OrmQueryRequest<T> request;
private final ArrayList<T> buffer;
private boolean moreToLoad = true;
CQueryIteratorWithBuffer(CQuery<T> cquery, OrmQueryRequest<T> request, int bufferSize) {
this.cquery = cquery;
this.request = request;
this.bufferSize = bufferSize;
this.buffer = new ArrayList<T>(bufferSize);
}
public boolean hasNext() {
try {
if (buffer.isEmpty() && moreToLoad) {
// load buffer
int i = -1;
while (moreToLoad && ++i < bufferSize) {
if (cquery.hasNextBean(true)) {
buffer.add(cquery.getLoadedBean());
} else {
moreToLoad = false;
}
}
// execute secondary queries
request.executeSecondaryQueries(bufferSize);
}
return !buffer.isEmpty();
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
}
}
public T next() {
return buffer.remove(0);
}
public void close() {
cquery.updateExecutionStatistics();
cquery.close();
request.endTransIfRequired();
}
public void remove() {
throw new PersistenceException("Remove not allowed");
}
}
@@ -1,96 +1,77 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.OrderBy.Property;
import com.avaje.ebeaninternal.api.SpiQuery;
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.id.IdBinder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
/**
* Creates the order by expression clause.
*/
public class CQueryOrderBy {
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
/**
* Create the logical order by clause.
*/
public static String parse(BeanDescriptor<?> desc, SpiQuery<?> query) {
return new CQueryOrderBy(desc, query).parseInternal();
}
private CQueryOrderBy(BeanDescriptor<?> desc, SpiQuery<?> query) {
this.desc = desc;
this.query = query;
}
private String parseInternal() {
OrderBy<?> orderBy = query.getOrderBy();
if (orderBy == null){
return null;
}
StringBuilder sb = new StringBuilder();
List<Property> properties = orderBy.getProperties();
if (properties.isEmpty()){
// order by clause removed by filterMany()
return null;
}
for (int i = 0; i < properties.size(); i++) {
if (i > 0){
sb.append(", ");
}
Property p = properties.get(i);
String expression = parseProperty(p);
sb.append(expression);
}
return sb.toString();
}
private String parseProperty(Property p) {
String propName = p.getProperty();
ElPropertyValue el = desc.getElGetValue(propName);
if (el == null){
return p.toStringFormat();
}
BeanProperty beanProperty = el.getBeanProperty();
if (beanProperty instanceof BeanPropertyAssoc<?>){
BeanPropertyAssoc<?> ap = (BeanPropertyAssoc<?>)beanProperty;
IdBinder idBinder = ap.getTargetDescriptor().getIdBinder();
return idBinder.getOrderBy(el.getElName(), p.isAscending());
}
return p.toStringFormat();
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.OrderBy.Property;
import com.avaje.ebeaninternal.api.SpiQuery;
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.id.IdBinder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
/**
* Creates the order by expression clause.
*/
public class CQueryOrderBy {
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
/**
* Create the logical order by clause.
*/
public static String parse(BeanDescriptor<?> desc, SpiQuery<?> query) {
return new CQueryOrderBy(desc, query).parseInternal();
}
private CQueryOrderBy(BeanDescriptor<?> desc, SpiQuery<?> query) {
this.desc = desc;
this.query = query;
}
private String parseInternal() {
OrderBy<?> orderBy = query.getOrderBy();
if (orderBy == null){
return null;
}
StringBuilder sb = new StringBuilder();
List<Property> properties = orderBy.getProperties();
if (properties.isEmpty()){
// order by clause removed by filterMany()
return null;
}
for (int i = 0; i < properties.size(); i++) {
if (i > 0){
sb.append(", ");
}
Property p = properties.get(i);
String expression = parseProperty(p);
sb.append(expression);
}
return sb.toString();
}
private String parseProperty(Property p) {
String propName = p.getProperty();
ElPropertyValue el = desc.getElGetValue(propName);
if (el == null){
return p.toStringFormat();
}
BeanProperty beanProperty = el.getBeanProperty();
if (beanProperty instanceof BeanPropertyAssoc<?>){
BeanPropertyAssoc<?> ap = (BeanPropertyAssoc<?>)beanProperty;
IdBinder idBinder = ap.getTargetDescriptor().getIdBinder();
return idBinder.getOrderBy(el.getElName(), p.isAscending());
}
return p.toStringFormat();
}
}
@@ -1,63 +1,44 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.sql.ResultSet;
import java.util.List;
import com.avaje.ebean.RawSql.ColumnMapping;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.type.DataReader;
import com.avaje.ebeaninternal.server.type.RsetDataReaderIndexed;
public class CQueryPlanRawSql extends CQueryPlan {
private final int[] rsetIndexPositions;
public CQueryPlanRawSql(OrmQueryRequest<?> request, SqlLimitResponse sqlRes, SqlTree sqlTree, String logWhereSql) {
super(request, sqlRes, sqlTree, true, logWhereSql, null);
this.rsetIndexPositions = createIndexPositions(request, sqlTree);
}
public DataReader createDataReader(ResultSet rset){
return new RsetDataReaderIndexed(rset, rsetIndexPositions, isRowNumberIncluded());
}
private int[] createIndexPositions(OrmQueryRequest<?> request, SqlTree sqlTree) {
List<String> chain = sqlTree.buildSelectExpressionChain();
ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping();
int[] indexPositions = new int[chain.size()];
for (int i = 0; i < chain.size(); i++) {
String expr = chain.get(i);
int indexPos = 1 + columnMapping.getIndexPosition(expr);
indexPositions[i] = indexPos;
}
return indexPositions;
}
}
package com.avaje.ebeaninternal.server.query;
import java.sql.ResultSet;
import java.util.List;
import com.avaje.ebean.RawSql.ColumnMapping;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.type.DataReader;
import com.avaje.ebeaninternal.server.type.RsetDataReaderIndexed;
public class CQueryPlanRawSql extends CQueryPlan {
private final int[] rsetIndexPositions;
public CQueryPlanRawSql(OrmQueryRequest<?> request, SqlLimitResponse sqlRes, SqlTree sqlTree, String logWhereSql) {
super(request, sqlRes, sqlTree, true, logWhereSql, null);
this.rsetIndexPositions = createIndexPositions(request, sqlTree);
}
public DataReader createDataReader(ResultSet rset){
return new RsetDataReaderIndexed(rset, rsetIndexPositions, isRowNumberIncluded());
}
private int[] createIndexPositions(OrmQueryRequest<?> request, SqlTree sqlTree) {
List<String> chain = sqlTree.buildSelectExpressionChain();
ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping();
int[] indexPositions = new int[chain.size()];
for (int i = 0; i < chain.size(); i++) {
String expr = chain.get(i);
int indexPos = 1 + columnMapping.getIndexPosition(expr);
indexPositions[i] = indexPos;
}
return indexPositions;
}
}
@@ -1,192 +1,173 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.PersistenceException;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.type.DataBind;
/**
* Executes the select row count query.
*/
public class CQueryRowCount {
private static final Logger logger = Logger.getLogger(CQueryRowCount.class.getName());
/**
* The overall find request wrapper object.
*/
private final OrmQueryRequest<?> request;
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
/**
* Where clause predicates.
*/
private final CQueryPredicates predicates;
/**
* The final sql that is generated.
*/
private final String sql;
/**
* The resultSet that is read and converted to objects.
*/
private ResultSet rset;
/**
* The statement used to create the resultSet.
*/
private PreparedStatement pstmt;
private String bindLog;
private long startNano;
private int executionTimeMicros;
private int rowCount;
/**
* Create the Sql select based on the request.
*/
public CQueryRowCount(OrmQueryRequest<?> request, CQueryPredicates predicates, String sql) {
this.request = request;
this.query = request.getQuery();
this.sql = sql;
query.setGeneratedSql(sql);
this.desc = request.getBeanDescriptor();
this.predicates = predicates;
}
/**
* Return a summary description of this query.
*/
public String getSummary() {
StringBuilder sb = new StringBuilder();
sb.append("FindRowCount exeMicros[").append(executionTimeMicros)
.append("] rows[").append(rowCount)
.append("] type[").append(desc.getFullName())
.append("] predicates[").append(predicates.getLogWhereSql())
.append("] bind[").append(bindLog).append("]");
return sb.toString();
}
/**
* Return the bind log.
*/
public String getBindLog() {
return bindLog;
}
/**
* Return the generated sql.
*/
public String getGeneratedSql() {
return sql;
}
public SpiOrmQueryRequest<?> getQueryRequest() {
return request;
}
/**
* Execute the query returning the row count.
*/
public int findRowCount() throws SQLException {
startNano = System.nanoTime();
try {
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
pstmt = conn.prepareStatement(sql);
if (query.getTimeout() > 0){
pstmt.setQueryTimeout(query.getTimeout());
}
bindLog = predicates.bind(new DataBind(pstmt));
rset = pstmt.executeQuery();
if (!rset.next()){
throw new PersistenceException("Expecting 1 row but got none?");
}
rowCount = rset.getInt(1);
long exeNano = System.nanoTime() - startNano;
executionTimeMicros = (int)exeNano/1000;
return rowCount;
} finally {
close();
}
}
/**
* Close the resources.
* <p>
* The jdbc resultSet and statement need to be closed. Its important that
* this method is called.
* </p>
*/
private void close() {
try {
if (rset != null) {
rset.close();
rset = null;
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
try {
if (pstmt != null) {
pstmt.close();
pstmt = null;
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
}
}
package com.avaje.ebeaninternal.server.query;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.PersistenceException;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.type.DataBind;
/**
* Executes the select row count query.
*/
public class CQueryRowCount {
private static final Logger logger = Logger.getLogger(CQueryRowCount.class.getName());
/**
* The overall find request wrapper object.
*/
private final OrmQueryRequest<?> request;
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
/**
* Where clause predicates.
*/
private final CQueryPredicates predicates;
/**
* The final sql that is generated.
*/
private final String sql;
/**
* The resultSet that is read and converted to objects.
*/
private ResultSet rset;
/**
* The statement used to create the resultSet.
*/
private PreparedStatement pstmt;
private String bindLog;
private long startNano;
private int executionTimeMicros;
private int rowCount;
/**
* Create the Sql select based on the request.
*/
public CQueryRowCount(OrmQueryRequest<?> request, CQueryPredicates predicates, String sql) {
this.request = request;
this.query = request.getQuery();
this.sql = sql;
query.setGeneratedSql(sql);
this.desc = request.getBeanDescriptor();
this.predicates = predicates;
}
/**
* Return a summary description of this query.
*/
public String getSummary() {
StringBuilder sb = new StringBuilder();
sb.append("FindRowCount exeMicros[").append(executionTimeMicros)
.append("] rows[").append(rowCount)
.append("] type[").append(desc.getFullName())
.append("] predicates[").append(predicates.getLogWhereSql())
.append("] bind[").append(bindLog).append("]");
return sb.toString();
}
/**
* Return the bind log.
*/
public String getBindLog() {
return bindLog;
}
/**
* Return the generated sql.
*/
public String getGeneratedSql() {
return sql;
}
public SpiOrmQueryRequest<?> getQueryRequest() {
return request;
}
/**
* Execute the query returning the row count.
*/
public int findRowCount() throws SQLException {
startNano = System.nanoTime();
try {
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
pstmt = conn.prepareStatement(sql);
if (query.getTimeout() > 0){
pstmt.setQueryTimeout(query.getTimeout());
}
bindLog = predicates.bind(new DataBind(pstmt));
rset = pstmt.executeQuery();
if (!rset.next()){
throw new PersistenceException("Expecting 1 row but got none?");
}
rowCount = rset.getInt(1);
long exeNano = System.nanoTime() - startNano;
executionTimeMicros = (int)exeNano/1000;
return rowCount;
} finally {
close();
}
}
/**
* Close the resources.
* <p>
* The jdbc resultSet and statement need to be closed. Its important that
* this method is called.
* </p>
*/
private void close() {
try {
if (rset != null) {
rset.close();
rset = null;
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
try {
if (pstmt != null) {
pstmt.close();
pstmt = null;
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
}
}
@@ -1,47 +1,28 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
/**
* Base object for making query execution into Callable's.
*
* @author rbygrave
*
* @param <T> the entity bean type
*/
public abstract class CallableQuery<T> {
protected final Query<T> query;
protected final SpiEbeanServer server;
protected final Transaction t;
public CallableQuery(SpiEbeanServer server, Query<T> query, Transaction t) {
this.server = server;
this.query = query;
this.t = t;
}
}
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
/**
* Base object for making query execution into Callable's.
*
* @author rbygrave
*
* @param <T> the entity bean type
*/
public abstract class CallableQuery<T> {
protected final Query<T> query;
protected final SpiEbeanServer server;
protected final Transaction t;
public CallableQuery(SpiEbeanServer server, Query<T> query, Transaction t) {
this.server = server;
this.query = query;
this.t = t;
}
}
@@ -1,51 +1,32 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Callable;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
/**
* Represent the fetch Id's query as a Callable.
*
* @param <T> the entity bean type
*/
public class CallableQueryIds<T> extends CallableQuery<T> implements Callable<List<Object>> {
public CallableQueryIds(SpiEbeanServer server, Query<T> query, Transaction t) {
super(server, query, t);
}
/**
* Execute the find Id's query returning the list of Id's.
*/
public List<Object> call() throws Exception {
// we have already made a copy of the query
// this way the same query instance is available to the
// QueryFutureIds (as so has access to the List before it is done)
return server.findIdsWithCopy(query, t);
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Callable;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
/**
* Represent the fetch Id's query as a Callable.
*
* @param <T> the entity bean type
*/
public class CallableQueryIds<T> extends CallableQuery<T> implements Callable<List<Object>> {
public CallableQueryIds(SpiEbeanServer server, Query<T> query, Transaction t) {
super(server, query, t);
}
/**
* Execute the find Id's query returning the list of Id's.
*/
public List<Object> call() throws Exception {
// we have already made a copy of the query
// this way the same query instance is available to the
// QueryFutureIds (as so has access to the List before it is done)
return server.findIdsWithCopy(query, t);
}
}
@@ -1,50 +1,31 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Callable;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
/**
* Represent the findList query as a Callable.
*
* @param <T> the entity bean type
*/
public class CallableQueryList<T> extends CallableQuery<T> implements Callable<List<T>> {
public CallableQueryList(SpiEbeanServer server, Query<T> query, Transaction t) {
super(server, query, t);
}
/**
* Execute the query returning the resulting List.
*/
public List<T> call() throws Exception {
return server.findList(query, t);
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Callable;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
/**
* Represent the findList query as a Callable.
*
* @param <T> the entity bean type
*/
public class CallableQueryList<T> extends CallableQuery<T> implements Callable<List<T>> {
public CallableQueryList(SpiEbeanServer server, Query<T> query, Transaction t) {
super(server, query, t);
}
/**
* Execute the query returning the resulting List.
*/
public List<T> call() throws Exception {
return server.findList(query, t);
}
}
@@ -1,49 +1,30 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.concurrent.Callable;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
/**
* Represent the findRowCount query as a Callable.
*
* @param <T> the entity bean type
*/
public class CallableQueryRowCount<T> extends CallableQuery<T> implements Callable<Integer> {
public CallableQueryRowCount(SpiEbeanServer server, Query<T> query, Transaction t) {
super(server, query, t);
}
/**
* Execute the query returning the row count.
*/
public Integer call() throws Exception {
return server.findRowCountWithCopy(query, t);
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.concurrent.Callable;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
/**
* Represent the findRowCount query as a Callable.
*
* @param <T> the entity bean type
*/
public class CallableQueryRowCount<T> extends CallableQuery<T> implements Callable<Integer> {
public CallableQueryRowCount(SpiEbeanServer server, Query<T> query, Transaction t) {
super(server, query, t);
}
/**
* Execute the query returning the row count.
*/
public Integer call() throws Exception {
return server.findRowCountWithCopy(query, t);
}
}
@@ -1,56 +1,37 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Callable;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.SqlQuery;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.Transaction;
/**
* Represent the SQL query findList as a Callable.
*/
public class CallableSqlQueryList implements Callable<List<SqlRow>> {
private final SqlQuery query;
private final EbeanServer server;
private final Transaction t;
public CallableSqlQueryList(EbeanServer server, SqlQuery query, Transaction t) {
this.server = server;
this.query = query;
this.t = t;
}
/**
* Execute the query returning the resulting list.
*/
public List<SqlRow> call() throws Exception {
return server.findList(query, t);
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Callable;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.SqlQuery;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.Transaction;
/**
* Represent the SQL query findList as a Callable.
*/
public class CallableSqlQueryList implements Callable<List<SqlRow>> {
private final SqlQuery query;
private final EbeanServer server;
private final Transaction t;
public CallableSqlQueryList(EbeanServer server, SqlQuery query, Transaction t) {
this.server = server;
this.query = query;
this.t = t;
}
/**
* Execute the query returning the resulting list.
*/
public List<SqlRow> call() throws Exception {
return server.findList(query, t);
}
}
@@ -1,38 +1,19 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
/**
* Defines a cancelable query.
* <p>
* Typically holds a representation of the PreparedStatement to perform the
* actual cancel.
* </p>
*/
public interface CancelableQuery {
/**
* Cancel the query.
* <p>
* For JDBC this translates to calling cancel on the PreparedStatement.
* </p>
*/
public void cancel();
}
package com.avaje.ebeaninternal.server.query;
/**
* Defines a cancelable query.
* <p>
* Typically holds a representation of the PreparedStatement to perform the
* actual cancel.
* </p>
*/
public interface CancelableQuery {
/**
* Cancel the query.
* <p>
* For JDBC this translates to calling cancel on the PreparedStatement.
* </p>
*/
public void cancel();
}
@@ -1,59 +1,40 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
/**
* Constants used in find processing.
*/
public interface Constants {
/**
* the new line character used.
* <p>
* Note that this is removed for logging sql to the transaction log.
* </p>
*/
public static final char NEW_LINE = '\n';
/**
* The carriage return character.
*/
public static final char CARRIAGE_RETURN = '\r';
/**
* literal used for SQL LIMIT in MySql and Postgres.
*/
public static final String LIMIT = "limit";
/**
* Literal used for SQL LIMIT OFFSET clause in MySql and Postgres.
*/
public static final String OFFSET = "offset";
/**
* ROW_NUMBER() OVER (ORDER BY
*/
public static final String ROW_NUMBER_OVER = "row_number() over (order by ";
/**
* ) as rn,
*/
public static final String ROW_NUMBER_AS = ") as rn, ";
}
package com.avaje.ebeaninternal.server.query;
/**
* Constants used in find processing.
*/
public interface Constants {
/**
* the new line character used.
* <p>
* Note that this is removed for logging sql to the transaction log.
* </p>
*/
public static final char NEW_LINE = '\n';
/**
* The carriage return character.
*/
public static final char CARRIAGE_RETURN = '\r';
/**
* literal used for SQL LIMIT in MySql and Postgres.
*/
public static final String LIMIT = "limit";
/**
* Literal used for SQL LIMIT OFFSET clause in MySql and Postgres.
*/
public static final String OFFSET = "offset";
/**
* ROW_NUMBER() OVER (ORDER BY
*/
public static final String ROW_NUMBER_OVER = "row_number() over (order by ";
/**
* ) as rn,
*/
public static final String ROW_NUMBER_AS = ") as rn, ";
}
@@ -1,148 +1,129 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.Collection;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.OrmQueryEngine;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
/**
* Main Finder implementation.
*/
public class DefaultOrmQueryEngine implements OrmQueryEngine {
/**
* Find using predicates
*/
private final CQueryEngine queryEngine;
/**
* Create the Finder.
*/
public DefaultOrmQueryEngine(BeanDescriptorManager descMgr, CQueryEngine queryEngine) {
this.queryEngine = queryEngine;
}
public <T> int findRowCount(OrmQueryRequest<T> request){
return queryEngine.findRowCount(request);
}
public <T> BeanIdList findIds(OrmQueryRequest<T> request){
return queryEngine.findIds(request);
}
public <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request) {
// LIMITATION: You can not use QueryIterator to load bean cache
SpiTransaction t = request.getTransaction();
// before we perform a query, we need to flush any
// previous persist requests that are queued/batched.
// The query may read data affected by those requests.
t.flushBatch();
return queryEngine.findIterate(request);
}
public <T> BeanCollection<T> findMany(OrmQueryRequest<T> request) {
SpiQuery<T> query = request.getQuery();
BeanCollection<T> result = null;
SpiTransaction t = request.getTransaction();
// before we perform a query, we need to flush any
// previous persist requests that are queued/batched.
// The query may read data affected by those requests.
t.flushBatch();
BeanFinder<T> finder = request.getBeanFinder();
if (finder != null) {
// this bean type has its own specific finder
result = finder.findMany(request);
} else {
result = queryEngine.findMany(request);
}
if (query.isLoadBeanCache()){
// load the individual beans into the bean cache
BeanDescriptor<T> descriptor = request.getBeanDescriptor();
Collection<T> c = result.getActualDetails();
for (T bean : c) {
descriptor.cachePutBeanData(bean);
}
}
if (!result.isEmpty() && query.isUseQueryCache()){
// load the query result into the query cache
request.putToQueryCache(result);
}
return result;
}
/**
* Find a single bean using its unique id.
*/
public <T> T findId(OrmQueryRequest<T> request) {
T result = null;
SpiTransaction t = request.getTransaction();
if (t.isBatchFlushOnQuery()){
// before we perform a query, we need to flush any
// previous persist requests that are queued/batched.
// The query may read data affected by those requests.
t.flushBatch();
}
BeanFinder<T> finder = request.getBeanFinder();
if (finder != null) {
result = finder.find(request);
} else {
result = queryEngine.find(request);
}
if (result != null && request.isUseBeanCache()){
request.getBeanDescriptor().cachePutBeanData(result);
}
return result;
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.Collection;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.OrmQueryEngine;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
/**
* Main Finder implementation.
*/
public class DefaultOrmQueryEngine implements OrmQueryEngine {
/**
* Find using predicates
*/
private final CQueryEngine queryEngine;
/**
* Create the Finder.
*/
public DefaultOrmQueryEngine(BeanDescriptorManager descMgr, CQueryEngine queryEngine) {
this.queryEngine = queryEngine;
}
public <T> int findRowCount(OrmQueryRequest<T> request){
return queryEngine.findRowCount(request);
}
public <T> BeanIdList findIds(OrmQueryRequest<T> request){
return queryEngine.findIds(request);
}
public <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request) {
// LIMITATION: You can not use QueryIterator to load bean cache
SpiTransaction t = request.getTransaction();
// before we perform a query, we need to flush any
// previous persist requests that are queued/batched.
// The query may read data affected by those requests.
t.flushBatch();
return queryEngine.findIterate(request);
}
public <T> BeanCollection<T> findMany(OrmQueryRequest<T> request) {
SpiQuery<T> query = request.getQuery();
BeanCollection<T> result = null;
SpiTransaction t = request.getTransaction();
// before we perform a query, we need to flush any
// previous persist requests that are queued/batched.
// The query may read data affected by those requests.
t.flushBatch();
BeanFinder<T> finder = request.getBeanFinder();
if (finder != null) {
// this bean type has its own specific finder
result = finder.findMany(request);
} else {
result = queryEngine.findMany(request);
}
if (query.isLoadBeanCache()){
// load the individual beans into the bean cache
BeanDescriptor<T> descriptor = request.getBeanDescriptor();
Collection<T> c = result.getActualDetails();
for (T bean : c) {
descriptor.cachePutBeanData(bean);
}
}
if (!result.isEmpty() && query.isUseQueryCache()){
// load the query result into the query cache
request.putToQueryCache(result);
}
return result;
}
/**
* Find a single bean using its unique id.
*/
public <T> T findId(OrmQueryRequest<T> request) {
T result = null;
SpiTransaction t = request.getTransaction();
if (t.isBatchFlushOnQuery()){
// before we perform a query, we need to flush any
// previous persist requests that are queued/batched.
// The query may read data affected by those requests.
t.flushBatch();
}
BeanFinder<T> finder = request.getBeanFinder();
if (finder != null) {
result = finder.find(request);
} else {
result = queryEngine.find(request);
}
if (result != null && request.isUseBeanCache()){
request.getBeanDescriptor().cachePutBeanData(result);
}
return result;
}
}
@@ -1,278 +1,259 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.PersistenceException;
import com.avaje.ebean.SqlQueryListener;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.BindParams;
import com.avaje.ebeaninternal.api.SpiSqlQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.RelationalQueryEngine;
import com.avaje.ebeaninternal.server.core.RelationalQueryRequest;
import com.avaje.ebeaninternal.server.jmx.MAdminLogging;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.util.BindParamsParser;
/**
* Perform native sql fetches.
*/
public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
private static final Logger logger = Logger.getLogger(DefaultRelationalQueryEngine.class.getName());
private final int defaultMaxRows;
private final Binder binder;
private final String dbTrueValue;
public DefaultRelationalQueryEngine(MAdminLogging logControl, Binder binder, String dbTrueValue) {
this.binder = binder;
this.defaultMaxRows = GlobalProperties.getInt("nativesql.defaultmaxrows",100000);
this.dbTrueValue = dbTrueValue == null ? "true" : dbTrueValue;
}
public Object findMany(RelationalQueryRequest request) {
SpiSqlQuery query = request.getQuery();
long startTime = System.currentTimeMillis();
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
ResultSet rset = null;
PreparedStatement pstmt = null;
// flag indicating whether we need to close the resources...
boolean useBackgroundToContinueFetch = false;
String sql = query.getQuery();
BindParams bindParams = query.getBindParams();
if (!bindParams.isEmpty()) {
// convert any named parameters if required
sql = BindParamsParser.parse(bindParams, sql);
}
try {
String bindLog = "";
String[] propNames = null;
synchronized (query) {
if (query.isCancelled()){
logger.finest("Query already cancelled");
return null;
}
// synchronise for query.cancel() support
pstmt = conn.prepareStatement(sql);
if (query.getTimeout() > 0){
pstmt.setQueryTimeout(query.getTimeout());
}
if (query.getBufferFetchSizeHint() > 0){
pstmt.setFetchSize(query.getBufferFetchSizeHint());
}
if (!bindParams.isEmpty()) {
bindLog = binder.bind(bindParams, new DataBind(pstmt));
}
if (request.isLogSql()) {
String sOut = sql.replace(Constants.NEW_LINE, ' ');
sOut = sOut.replace(Constants.CARRIAGE_RETURN, ' ');
t.logInternal(sOut);
}
rset = pstmt.executeQuery();
propNames = getPropertyNames(rset);
}
// calculate the initialCapacity of the Map to reduce
// rehashing for queries with 12+ columns
float initCap = (propNames.length) / 0.7f;
int estimateCapacity = (int) initCap + 1;
// determine the maxRows limit
int maxRows = defaultMaxRows;
if (query.getMaxRows() >= 1) {
maxRows = query.getMaxRows();
}
boolean hasHitMaxRows = false;
int loadRowCount = 0;
SqlQueryListener listener = query.getListener();
BeanCollectionWrapper wrapper = new BeanCollectionWrapper(request);
boolean isMap = wrapper.isMap();
String mapKey = query.getMapKey();
SqlRow bean = null;
while (rset.next()) {
synchronized (query) {
// synchronise for query.cancel() support
if (!query.isCancelled()){
bean = readRow(request, rset, propNames, estimateCapacity);
}
}
if (bean != null){
// bean can be null if query cancelled
if (listener != null) {
listener.process(bean);
} else {
if (isMap) {
Object keyValue = bean.get(mapKey);
wrapper.addToMap(bean, keyValue);
} else {
wrapper.addToCollection(bean);
}
}
loadRowCount++;
if (loadRowCount == maxRows) {
// break, as we have hit the max rows to fetch...
hasHitMaxRows = true;
break;
}
}
}
BeanCollection<?> beanColl = wrapper.getBeanCollection();
if (hasHitMaxRows) {
if (rset.next()) {
// there are more rows available after the maxRows limit
beanColl.setHasMoreRows(true);
}
}
if (!useBackgroundToContinueFetch) {
beanColl.setFinishedFetch(true);
}
if (request.isLogSummary()) {
long exeTime = System.currentTimeMillis() - startTime;
String msg = "SqlQuery rows[" + loadRowCount + "] time[" + exeTime + "] bind["
+ bindLog + "] finished[" + beanColl.isFinishedFetch() + "]";
t.logInternal(msg);
}
if (query.isCancelled()){
logger.fine("Query was cancelled during execution rows:"+loadRowCount);
}
return beanColl;
} catch (Exception e) {
String m = Message.msg("fetch.error", e.getMessage(), sql);
throw new PersistenceException(m, e);
} finally {
if (!useBackgroundToContinueFetch) {
try {
if (rset != null) {
rset.close();
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
}
}
}
/**
* Build the list of property names.
*/
protected String[] getPropertyNames(ResultSet rset) throws SQLException {
ArrayList<String> propNames = new ArrayList<String>();
ResultSetMetaData rsmd = rset.getMetaData();
int columnsPlusOne = rsmd.getColumnCount()+1;
for (int i = 1; i < columnsPlusOne; i++) {
String columnName = rsmd.getColumnLabel(i);
// will convert columnName to lower case
propNames.add(columnName);
}
return (String[]) propNames.toArray(new String[propNames.size()]);
}
/**
* Read the row from the ResultSet and return as a MapBean.
*/
protected SqlRow readRow(RelationalQueryRequest request, ResultSet rset,
String[] propNames, int initialCapacity) throws SQLException {
// by default a map will rehash on the 12th entry
// it will be pretty common to have 12 or more entries so
// to reduce rehashing I am trying to estimate a good
// initial capacity for the MapBean to use.
SqlRow bean = new DefaultSqlRow(initialCapacity, 0.75f, dbTrueValue);
int index = 0;
for (int i = 0; i < propNames.length; i++) {
index++;
Object value = rset.getObject(index);
bean.set(propNames[i], value);
}
return bean;
}
}
package com.avaje.ebeaninternal.server.query;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.PersistenceException;
import com.avaje.ebean.SqlQueryListener;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.BindParams;
import com.avaje.ebeaninternal.api.SpiSqlQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.RelationalQueryEngine;
import com.avaje.ebeaninternal.server.core.RelationalQueryRequest;
import com.avaje.ebeaninternal.server.jmx.MAdminLogging;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.util.BindParamsParser;
/**
* Perform native sql fetches.
*/
public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
private static final Logger logger = Logger.getLogger(DefaultRelationalQueryEngine.class.getName());
private final int defaultMaxRows;
private final Binder binder;
private final String dbTrueValue;
public DefaultRelationalQueryEngine(MAdminLogging logControl, Binder binder, String dbTrueValue) {
this.binder = binder;
this.defaultMaxRows = GlobalProperties.getInt("nativesql.defaultmaxrows",100000);
this.dbTrueValue = dbTrueValue == null ? "true" : dbTrueValue;
}
public Object findMany(RelationalQueryRequest request) {
SpiSqlQuery query = request.getQuery();
long startTime = System.currentTimeMillis();
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
ResultSet rset = null;
PreparedStatement pstmt = null;
// flag indicating whether we need to close the resources...
boolean useBackgroundToContinueFetch = false;
String sql = query.getQuery();
BindParams bindParams = query.getBindParams();
if (!bindParams.isEmpty()) {
// convert any named parameters if required
sql = BindParamsParser.parse(bindParams, sql);
}
try {
String bindLog = "";
String[] propNames = null;
synchronized (query) {
if (query.isCancelled()){
logger.finest("Query already cancelled");
return null;
}
// synchronise for query.cancel() support
pstmt = conn.prepareStatement(sql);
if (query.getTimeout() > 0){
pstmt.setQueryTimeout(query.getTimeout());
}
if (query.getBufferFetchSizeHint() > 0){
pstmt.setFetchSize(query.getBufferFetchSizeHint());
}
if (!bindParams.isEmpty()) {
bindLog = binder.bind(bindParams, new DataBind(pstmt));
}
if (request.isLogSql()) {
String sOut = sql.replace(Constants.NEW_LINE, ' ');
sOut = sOut.replace(Constants.CARRIAGE_RETURN, ' ');
t.logInternal(sOut);
}
rset = pstmt.executeQuery();
propNames = getPropertyNames(rset);
}
// calculate the initialCapacity of the Map to reduce
// rehashing for queries with 12+ columns
float initCap = (propNames.length) / 0.7f;
int estimateCapacity = (int) initCap + 1;
// determine the maxRows limit
int maxRows = defaultMaxRows;
if (query.getMaxRows() >= 1) {
maxRows = query.getMaxRows();
}
boolean hasHitMaxRows = false;
int loadRowCount = 0;
SqlQueryListener listener = query.getListener();
BeanCollectionWrapper wrapper = new BeanCollectionWrapper(request);
boolean isMap = wrapper.isMap();
String mapKey = query.getMapKey();
SqlRow bean = null;
while (rset.next()) {
synchronized (query) {
// synchronise for query.cancel() support
if (!query.isCancelled()){
bean = readRow(request, rset, propNames, estimateCapacity);
}
}
if (bean != null){
// bean can be null if query cancelled
if (listener != null) {
listener.process(bean);
} else {
if (isMap) {
Object keyValue = bean.get(mapKey);
wrapper.addToMap(bean, keyValue);
} else {
wrapper.addToCollection(bean);
}
}
loadRowCount++;
if (loadRowCount == maxRows) {
// break, as we have hit the max rows to fetch...
hasHitMaxRows = true;
break;
}
}
}
BeanCollection<?> beanColl = wrapper.getBeanCollection();
if (hasHitMaxRows) {
if (rset.next()) {
// there are more rows available after the maxRows limit
beanColl.setHasMoreRows(true);
}
}
if (!useBackgroundToContinueFetch) {
beanColl.setFinishedFetch(true);
}
if (request.isLogSummary()) {
long exeTime = System.currentTimeMillis() - startTime;
String msg = "SqlQuery rows[" + loadRowCount + "] time[" + exeTime + "] bind["
+ bindLog + "] finished[" + beanColl.isFinishedFetch() + "]";
t.logInternal(msg);
}
if (query.isCancelled()){
logger.fine("Query was cancelled during execution rows:"+loadRowCount);
}
return beanColl;
} catch (Exception e) {
String m = Message.msg("fetch.error", e.getMessage(), sql);
throw new PersistenceException(m, e);
} finally {
if (!useBackgroundToContinueFetch) {
try {
if (rset != null) {
rset.close();
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
}
}
}
/**
* Build the list of property names.
*/
protected String[] getPropertyNames(ResultSet rset) throws SQLException {
ArrayList<String> propNames = new ArrayList<String>();
ResultSetMetaData rsmd = rset.getMetaData();
int columnsPlusOne = rsmd.getColumnCount()+1;
for (int i = 1; i < columnsPlusOne; i++) {
String columnName = rsmd.getColumnLabel(i);
// will convert columnName to lower case
propNames.add(columnName);
}
return (String[]) propNames.toArray(new String[propNames.size()]);
}
/**
* Read the row from the ResultSet and return as a MapBean.
*/
protected SqlRow readRow(RelationalQueryRequest request, ResultSet rset,
String[] propNames, int initialCapacity) throws SQLException {
// by default a map will rehash on the 12th entry
// it will be pretty common to have 12 or more entries so
// to reduce rehashing I am trying to estimate a good
// initial capacity for the MapBean to use.
SqlRow bean = new DefaultSqlRow(initialCapacity, 0.75f, dbTrueValue);
int index = 0;
for (int i = 0; i < propNames.length; i++) {
index++;
Object value = rset.getObject(index);
bean.set(propNames[i], value);
}
return bean;
}
}
@@ -1,226 +1,207 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.avaje.ebean.SqlQuery;
import com.avaje.ebean.SqlRow;
import com.avaje.ebeaninternal.server.core.BasicTypeConverter;
/**
* Used to return raw SQL query results.
* <p>
* Refer to {@link SqlQuery} for examples.
* </p>
* <p>
* There are convenience methods such as getInteger(), getBigDecimal() etc. The
* reason for these methods is that the values put into this map often come
* straight from the JDBC resultSet. Depending on the JDBC driver it may put a
* different type into a given property. For example an Integer, BigDecimal,
* Double could all be put into a property depending on the JDBC driver used.
* These convenience methods automatically convert the value as required
* returning the type you expect.
* </p>
*/
public class DefaultSqlRow implements SqlRow {
static final long serialVersionUID = -3120927797041336242L;
private final String dbTrueValue;
/**
* The underlying map of property data.
*/
Map<String, Object> map;
/**
* Create with a specific Map implementation.
* <p>
* The default Map implementation is LinkedHashMap.
* </p>
*/
public DefaultSqlRow(Map<String, Object> map, String dbTrueValue) {
this.map = map;
this.dbTrueValue = dbTrueValue;
}
/**
* Create a new MapBean based on a LinkedHashMap with default
* initialCapacity (of 16).
*/
public DefaultSqlRow(String dbTrueValue) {
this.map = new LinkedHashMap<String, Object>();
this.dbTrueValue = dbTrueValue;
}
/**
* Create with an initialCapacity and loadFactor.
* <p>
* The defaults of these are 16 and 0.75.
* </p>
* <p>
* Note that the Map will rehash the contents when the number of keys in
* this map reaches its threshold (initialCapacity * loadFactor).
* </p>
*/
public DefaultSqlRow(int initialCapacity, float loadFactor, String dbTrueValue) {
this.map = new LinkedHashMap<String, Object>(initialCapacity, loadFactor);
this.dbTrueValue = dbTrueValue;
}
public Iterator<String> keys() {
return map.keySet().iterator();
}
public Object remove(Object name) {
name = ((String) name).toLowerCase();
return map.remove(name);
}
public Object get(Object name) {
name = ((String) name).toLowerCase();
return map.get(name);
}
public Object put(String name, Object value) {
return setInternal(name, value);
}
public Object set(String name, Object value) {
return setInternal(name, value);
}
private Object setInternal(String name, Object newValue) {
// MapBean properties are always lowercase
name = name.toLowerCase();
// valueList = null;
return map.put(name, newValue);
}
public UUID getUUID(String name) {
Object val = get(name);
return BasicTypeConverter.toUUID(val);
}
public Boolean getBoolean(String name) {
Object val = get(name);
return BasicTypeConverter.toBoolean(val, dbTrueValue);
}
public Integer getInteger(String name) {
Object val = get(name);
return BasicTypeConverter.toInteger(val);
}
public BigDecimal getBigDecimal(String name) {
Object val = get(name);
return BasicTypeConverter.toBigDecimal(val);
}
public Long getLong(String name) {
Object val = get(name);
return BasicTypeConverter.toLong(val);
}
public Double getDouble(String name) {
Object val = get(name);
return BasicTypeConverter.toDouble(val);
}
public Float getFloat(String name) {
Object val = get(name);
return BasicTypeConverter.toFloat(val);
}
public String getString(String name) {
Object val = get(name);
return BasicTypeConverter.toString(val);
}
public java.util.Date getUtilDate(String name) {
Object val = get(name);
return BasicTypeConverter.toUtilDate(val);
}
public Date getDate(String name) {
Object val = get(name);
return BasicTypeConverter.toDate(val);
}
public Timestamp getTimestamp(String name) {
Object val = get(name);
return BasicTypeConverter.toTimestamp(val);
}
public String toString() {
return map.toString();
}
// ------------------------------------
// Normal map methods...
public void clear() {
map.clear();
}
public boolean containsKey(Object key) {
key = ((String) key).toLowerCase();
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public Set<Map.Entry<String, Object>> entrySet() {
return map.entrySet();
}
public boolean isEmpty() {
return map.isEmpty();
}
public Set<String> keySet() {
return map.keySet();
}
public void putAll(Map<? extends String, ? extends Object> t) {
map.putAll(t);
}
public int size() {
return map.size();
}
public Collection<Object> values() {
return map.values();
}
}
package com.avaje.ebeaninternal.server.query;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.avaje.ebean.SqlQuery;
import com.avaje.ebean.SqlRow;
import com.avaje.ebeaninternal.server.core.BasicTypeConverter;
/**
* Used to return raw SQL query results.
* <p>
* Refer to {@link SqlQuery} for examples.
* </p>
* <p>
* There are convenience methods such as getInteger(), getBigDecimal() etc. The
* reason for these methods is that the values put into this map often come
* straight from the JDBC resultSet. Depending on the JDBC driver it may put a
* different type into a given property. For example an Integer, BigDecimal,
* Double could all be put into a property depending on the JDBC driver used.
* These convenience methods automatically convert the value as required
* returning the type you expect.
* </p>
*/
public class DefaultSqlRow implements SqlRow {
static final long serialVersionUID = -3120927797041336242L;
private final String dbTrueValue;
/**
* The underlying map of property data.
*/
Map<String, Object> map;
/**
* Create with a specific Map implementation.
* <p>
* The default Map implementation is LinkedHashMap.
* </p>
*/
public DefaultSqlRow(Map<String, Object> map, String dbTrueValue) {
this.map = map;
this.dbTrueValue = dbTrueValue;
}
/**
* Create a new MapBean based on a LinkedHashMap with default
* initialCapacity (of 16).
*/
public DefaultSqlRow(String dbTrueValue) {
this.map = new LinkedHashMap<String, Object>();
this.dbTrueValue = dbTrueValue;
}
/**
* Create with an initialCapacity and loadFactor.
* <p>
* The defaults of these are 16 and 0.75.
* </p>
* <p>
* Note that the Map will rehash the contents when the number of keys in
* this map reaches its threshold (initialCapacity * loadFactor).
* </p>
*/
public DefaultSqlRow(int initialCapacity, float loadFactor, String dbTrueValue) {
this.map = new LinkedHashMap<String, Object>(initialCapacity, loadFactor);
this.dbTrueValue = dbTrueValue;
}
public Iterator<String> keys() {
return map.keySet().iterator();
}
public Object remove(Object name) {
name = ((String) name).toLowerCase();
return map.remove(name);
}
public Object get(Object name) {
name = ((String) name).toLowerCase();
return map.get(name);
}
public Object put(String name, Object value) {
return setInternal(name, value);
}
public Object set(String name, Object value) {
return setInternal(name, value);
}
private Object setInternal(String name, Object newValue) {
// MapBean properties are always lowercase
name = name.toLowerCase();
// valueList = null;
return map.put(name, newValue);
}
public UUID getUUID(String name) {
Object val = get(name);
return BasicTypeConverter.toUUID(val);
}
public Boolean getBoolean(String name) {
Object val = get(name);
return BasicTypeConverter.toBoolean(val, dbTrueValue);
}
public Integer getInteger(String name) {
Object val = get(name);
return BasicTypeConverter.toInteger(val);
}
public BigDecimal getBigDecimal(String name) {
Object val = get(name);
return BasicTypeConverter.toBigDecimal(val);
}
public Long getLong(String name) {
Object val = get(name);
return BasicTypeConverter.toLong(val);
}
public Double getDouble(String name) {
Object val = get(name);
return BasicTypeConverter.toDouble(val);
}
public Float getFloat(String name) {
Object val = get(name);
return BasicTypeConverter.toFloat(val);
}
public String getString(String name) {
Object val = get(name);
return BasicTypeConverter.toString(val);
}
public java.util.Date getUtilDate(String name) {
Object val = get(name);
return BasicTypeConverter.toUtilDate(val);
}
public Date getDate(String name) {
Object val = get(name);
return BasicTypeConverter.toDate(val);
}
public Timestamp getTimestamp(String name) {
Object val = get(name);
return BasicTypeConverter.toTimestamp(val);
}
public String toString() {
return map.toString();
}
// ------------------------------------
// Normal map methods...
public void clear() {
map.clear();
}
public boolean containsKey(Object key) {
key = ((String) key).toLowerCase();
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public Set<Map.Entry<String, Object>> entrySet() {
return map.entrySet();
}
public boolean isEmpty() {
return map.isEmpty();
}
public Set<String> keySet() {
return map.keySet();
}
public void putAll(Map<? extends String, ? extends Object> t) {
map.putAll(t);
}
public int size() {
return map.size();
}
public Collection<Object> values() {
return map.values();
}
}
@@ -1,244 +1,225 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import com.avaje.ebean.Page;
public class LimitOffsetList<T> implements List<T> {
private final LimitOffsetPagingQuery<T> owner;
private List<T> localCopy;
public LimitOffsetList(LimitOffsetPagingQuery<T> owner) {
this.owner = owner;
}
private void ensureLocalCopy() {
if (localCopy == null){
localCopy = new ArrayList<T>();
int pgIndex = 0;
while(true){
Page<T> page = owner.getPage(pgIndex++);
List<T> list = page.getList();
localCopy.addAll(list);
if (!page.hasNext()){
break;
}
}
}
}
private boolean hasNext(int position){
return owner.hasNext(position);
}
public void clear() {
localCopy = new ArrayList<T>();
}
public T get(int index) {
if (localCopy != null){
return localCopy.get(index);
} else {
return owner.get(index);
}
}
public boolean isEmpty() {
if (localCopy != null){
return localCopy.isEmpty();
} else {
return owner.getTotalRowCount() == 0;
}
}
public int size() {
if (localCopy != null){
return localCopy.size();
} else {
return owner.getTotalRowCount();
}
}
public Iterator<T> iterator() {
if (localCopy != null){
return localCopy.iterator();
} else {
return new ListItr(this, 0);
}
}
public ListIterator<T> listIterator() {
if (localCopy != null){
return localCopy.listIterator();
} else {
return new ListItr(this, 0);
}
}
public ListIterator<T> listIterator(int index) {
if (localCopy != null){
return localCopy.listIterator(index);
} else {
return new ListItr(this, index);
}
}
public List<T> subList(int fromIndex, int toIndex) {
if (localCopy != null){
return localCopy.subList(fromIndex, toIndex);
} else {
//FIXME: subList not implemented ...
throw new RuntimeException("Not implemented at this point");
}
}
public int lastIndexOf(Object o) {
ensureLocalCopy();
return localCopy.lastIndexOf(o);
}
public void add(int index, T element) {
ensureLocalCopy();
localCopy.add(index, element);
}
public boolean add(T o) {
ensureLocalCopy();
return localCopy.add(o);
}
public boolean addAll(Collection<? extends T> c) {
ensureLocalCopy();
return localCopy.addAll(c);
}
public boolean addAll(int index, Collection<? extends T> c) {
ensureLocalCopy();
return localCopy.addAll(index, c);
}
public boolean contains(Object o) {
ensureLocalCopy();
return localCopy.contains(o);
}
public boolean containsAll(Collection<?> c) {
ensureLocalCopy();
return localCopy.containsAll(c);
}
public int indexOf(Object o) {
ensureLocalCopy();
return localCopy.indexOf(o);
}
public T remove(int index) {
ensureLocalCopy();
return localCopy.remove(index);
}
public boolean remove(Object o) {
ensureLocalCopy();
return localCopy.remove(o);
}
public boolean removeAll(Collection<?> c) {
ensureLocalCopy();
return localCopy.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
ensureLocalCopy();
return localCopy.retainAll(c);
}
public T set(int index, T element) {
ensureLocalCopy();
return localCopy.set(index, element);
}
public Object[] toArray() {
ensureLocalCopy();
return localCopy.toArray();
}
public <K> K[] toArray(K[] a) {
ensureLocalCopy();
return localCopy.toArray(a);
}
private class ListItr implements ListIterator<T> {
private LimitOffsetList<T> ownerList;
private int position;
ListItr(LimitOffsetList<T> ownerList, int position) {
this.ownerList = ownerList;
this.position = position;
}
public void add(T o) {
ownerList.add(position++, o);
}
public boolean hasNext() {
return ownerList.hasNext(position);
}
public boolean hasPrevious() {
return position > 0;
}
public T next() {
return ownerList.get(position++);
}
public int nextIndex() {
return position;
}
public T previous() {
return get(--position);
}
public int previousIndex() {
return position - 1;
}
public void remove() {
throw new RuntimeException("Not supported yet");
}
public void set(T o) {
throw new RuntimeException("Not supported yet");
}
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import com.avaje.ebean.Page;
public class LimitOffsetList<T> implements List<T> {
private final LimitOffsetPagingQuery<T> owner;
private List<T> localCopy;
public LimitOffsetList(LimitOffsetPagingQuery<T> owner) {
this.owner = owner;
}
private void ensureLocalCopy() {
if (localCopy == null){
localCopy = new ArrayList<T>();
int pgIndex = 0;
while(true){
Page<T> page = owner.getPage(pgIndex++);
List<T> list = page.getList();
localCopy.addAll(list);
if (!page.hasNext()){
break;
}
}
}
}
private boolean hasNext(int position){
return owner.hasNext(position);
}
public void clear() {
localCopy = new ArrayList<T>();
}
public T get(int index) {
if (localCopy != null){
return localCopy.get(index);
} else {
return owner.get(index);
}
}
public boolean isEmpty() {
if (localCopy != null){
return localCopy.isEmpty();
} else {
return owner.getTotalRowCount() == 0;
}
}
public int size() {
if (localCopy != null){
return localCopy.size();
} else {
return owner.getTotalRowCount();
}
}
public Iterator<T> iterator() {
if (localCopy != null){
return localCopy.iterator();
} else {
return new ListItr(this, 0);
}
}
public ListIterator<T> listIterator() {
if (localCopy != null){
return localCopy.listIterator();
} else {
return new ListItr(this, 0);
}
}
public ListIterator<T> listIterator(int index) {
if (localCopy != null){
return localCopy.listIterator(index);
} else {
return new ListItr(this, index);
}
}
public List<T> subList(int fromIndex, int toIndex) {
if (localCopy != null){
return localCopy.subList(fromIndex, toIndex);
} else {
//FIXME: subList not implemented ...
throw new RuntimeException("Not implemented at this point");
}
}
public int lastIndexOf(Object o) {
ensureLocalCopy();
return localCopy.lastIndexOf(o);
}
public void add(int index, T element) {
ensureLocalCopy();
localCopy.add(index, element);
}
public boolean add(T o) {
ensureLocalCopy();
return localCopy.add(o);
}
public boolean addAll(Collection<? extends T> c) {
ensureLocalCopy();
return localCopy.addAll(c);
}
public boolean addAll(int index, Collection<? extends T> c) {
ensureLocalCopy();
return localCopy.addAll(index, c);
}
public boolean contains(Object o) {
ensureLocalCopy();
return localCopy.contains(o);
}
public boolean containsAll(Collection<?> c) {
ensureLocalCopy();
return localCopy.containsAll(c);
}
public int indexOf(Object o) {
ensureLocalCopy();
return localCopy.indexOf(o);
}
public T remove(int index) {
ensureLocalCopy();
return localCopy.remove(index);
}
public boolean remove(Object o) {
ensureLocalCopy();
return localCopy.remove(o);
}
public boolean removeAll(Collection<?> c) {
ensureLocalCopy();
return localCopy.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
ensureLocalCopy();
return localCopy.retainAll(c);
}
public T set(int index, T element) {
ensureLocalCopy();
return localCopy.set(index, element);
}
public Object[] toArray() {
ensureLocalCopy();
return localCopy.toArray();
}
public <K> K[] toArray(K[] a) {
ensureLocalCopy();
return localCopy.toArray(a);
}
private class ListItr implements ListIterator<T> {
private LimitOffsetList<T> ownerList;
private int position;
ListItr(LimitOffsetList<T> ownerList, int position) {
this.ownerList = ownerList;
this.position = position;
}
public void add(T o) {
ownerList.add(position++, o);
}
public boolean hasNext() {
return ownerList.hasNext(position);
}
public boolean hasPrevious() {
return position > 0;
}
public T next() {
return ownerList.get(position++);
}
public int nextIndex() {
return position;
}
public T previous() {
return get(--position);
}
public int previousIndex() {
return position - 1;
}
public void remove() {
throw new RuntimeException("Not supported yet");
}
public void set(T o) {
throw new RuntimeException("Not supported yet");
}
}
}
@@ -1,127 +1,108 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import javax.persistence.PersistenceException;
import com.avaje.ebean.FutureList;
import com.avaje.ebean.Page;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Page implementation based on limit offset types of queries.
*
* @author rbygrave
*
* @param <T>
* the entity bean type
*/
public class LimitOffsetPage<T> implements Page<T>, BeanCollectionTouched {
private final int pageIndex;
private final LimitOffsetPagingQuery<T> owner;
private FutureList<T> futureList;
public LimitOffsetPage(int pageIndex, LimitOffsetPagingQuery<T> owner) {
this.pageIndex = pageIndex;
this.owner = owner;
}
public FutureList<T> getFutureList() {
if (futureList == null) {
SpiQuery<T> originalQuery = owner.getSpiQuery();
SpiQuery<T> copy = originalQuery.copy();
copy.setPersistenceContext(originalQuery.getPersistenceContext());
int pageSize = owner.getPageSize();
copy.setFirstRow(pageIndex * pageSize);
copy.setMaxRows(pageSize);
copy.setBeanCollectionTouched(this);
futureList = owner.getServer().findFutureList(copy, null);
}
return futureList;
}
/**
* Perform fetch ahead when the list is first accessed.
*/
public void notifyTouched(BeanCollection<?> c) {
if (c.hasMoreRows()) {
owner.fetchAheadIfRequired(pageIndex);
}
}
public List<T> getList() {
try {
return getFutureList().get();
} catch (Exception e) {
throw new PersistenceException(e);
}
}
@SuppressWarnings("unchecked")
public boolean hasNext() {
return ((BeanCollection<T>) getList()).hasMoreRows();
}
public boolean hasPrev() {
return pageIndex > 0;
}
public Page<T> next() {
return owner.getPage(pageIndex + 1);
}
public Page<T> prev() {
return owner.getPage(pageIndex - 1);
}
public int getPageIndex() {
return pageIndex;
}
public int getTotalPageCount() {
return owner.getTotalPageCount();
}
public int getTotalRowCount() {
return owner.getTotalRowCount();
}
public String getDisplayXtoYofZ(String to, String of) {
int first = pageIndex * owner.getPageSize() + 1;
int last = first + getList().size() - 1;
int total = getTotalRowCount();
return first+to+last+of+total;
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import javax.persistence.PersistenceException;
import com.avaje.ebean.FutureList;
import com.avaje.ebean.Page;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Page implementation based on limit offset types of queries.
*
* @author rbygrave
*
* @param <T>
* the entity bean type
*/
public class LimitOffsetPage<T> implements Page<T>, BeanCollectionTouched {
private final int pageIndex;
private final LimitOffsetPagingQuery<T> owner;
private FutureList<T> futureList;
public LimitOffsetPage(int pageIndex, LimitOffsetPagingQuery<T> owner) {
this.pageIndex = pageIndex;
this.owner = owner;
}
public FutureList<T> getFutureList() {
if (futureList == null) {
SpiQuery<T> originalQuery = owner.getSpiQuery();
SpiQuery<T> copy = originalQuery.copy();
copy.setPersistenceContext(originalQuery.getPersistenceContext());
int pageSize = owner.getPageSize();
copy.setFirstRow(pageIndex * pageSize);
copy.setMaxRows(pageSize);
copy.setBeanCollectionTouched(this);
futureList = owner.getServer().findFutureList(copy, null);
}
return futureList;
}
/**
* Perform fetch ahead when the list is first accessed.
*/
public void notifyTouched(BeanCollection<?> c) {
if (c.hasMoreRows()) {
owner.fetchAheadIfRequired(pageIndex);
}
}
public List<T> getList() {
try {
return getFutureList().get();
} catch (Exception e) {
throw new PersistenceException(e);
}
}
@SuppressWarnings("unchecked")
public boolean hasNext() {
return ((BeanCollection<T>) getList()).hasMoreRows();
}
public boolean hasPrev() {
return pageIndex > 0;
}
public Page<T> next() {
return owner.getPage(pageIndex + 1);
}
public Page<T> prev() {
return owner.getPage(pageIndex - 1);
}
public int getPageIndex() {
return pageIndex;
}
public int getTotalPageCount() {
return owner.getTotalPageCount();
}
public int getTotalRowCount() {
return owner.getTotalRowCount();
}
public String getDisplayXtoYofZ(String to, String of) {
int first = pageIndex * owner.getPageSize() + 1;
int last = first + getList().size() - 1;
int total = getTotalRowCount();
return first+to+last+of+total;
}
}
@@ -1,156 +1,137 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import javax.persistence.PersistenceException;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.Page;
import com.avaje.ebean.PagingList;
import com.avaje.ebeaninternal.api.Monitor;
import com.avaje.ebeaninternal.api.SpiQuery;
public class LimitOffsetPagingQuery<T> implements PagingList<T> {
private transient EbeanServer server;
private final SpiQuery<T> query;
private final List<LimitOffsetPage<T>> pages = new ArrayList<LimitOffsetPage<T>>();
private final Monitor monitor = new Monitor();
private final int pageSize;
private boolean fetchAhead = true;
private Future<Integer> futureRowCount;
public LimitOffsetPagingQuery(EbeanServer server, SpiQuery<T> query, int pageSize) {
this.query = query;
this.pageSize = pageSize;
this.server = server;
}
public EbeanServer getServer() {
return server;
}
public void setServer(EbeanServer server) {
this.server = server;
}
public SpiQuery<T> getSpiQuery() {
return query;
}
public PagingList<T> setFetchAhead(boolean fetchAhead) {
this.fetchAhead = fetchAhead;
return this;
}
public List<T> getAsList() {
return new LimitOffsetList<T>(this);
}
public Future<Integer> getFutureRowCount() {
synchronized (monitor) {
if (futureRowCount == null){
futureRowCount = server.findFutureRowCount(query, null);
}
return futureRowCount;
}
}
private LimitOffsetPage<T> internalGetPage(int i){
synchronized (monitor) {
int ps = pages.size();
if (ps <= i){
for (int j = ps; j <= i; j++) {
LimitOffsetPage<T> p = new LimitOffsetPage<T>(j, this);
pages.add(p);
}
}
return pages.get(i);
}
}
protected void fetchAheadIfRequired(int pageIndex){
synchronized (monitor) {
// Already checked in LimitOffsetPage that there is another page
if (fetchAhead){
// fetchAhead is turned on so get the next page and trigger query
LimitOffsetPage<T> nextPage = internalGetPage(pageIndex + 1);
nextPage.getFutureList();
}
}
}
public void refresh() {
synchronized (monitor) {
futureRowCount = null;
pages.clear();
}
}
public Page<T> getPage(int i) {
return internalGetPage(i);
}
protected boolean hasNext(int position){
return position < getTotalRowCount();
}
protected T get(int rowIndex){
int pg = rowIndex / pageSize;
int offset = rowIndex % pageSize;
Page<T> page = getPage(pg);
return page.getList().get(offset);
}
public int getTotalPageCount() {
int rowCount = getTotalRowCount();
if (rowCount == 0){
return 0;
} else {
return ((rowCount-1) / pageSize) + 1;
}
}
public int getPageSize() {
return pageSize;
}
public int getTotalRowCount() {
try {
return getFutureRowCount().get();
} catch (Exception e) {
throw new PersistenceException(e);
}
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import javax.persistence.PersistenceException;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.Page;
import com.avaje.ebean.PagingList;
import com.avaje.ebeaninternal.api.Monitor;
import com.avaje.ebeaninternal.api.SpiQuery;
public class LimitOffsetPagingQuery<T> implements PagingList<T> {
private transient EbeanServer server;
private final SpiQuery<T> query;
private final List<LimitOffsetPage<T>> pages = new ArrayList<LimitOffsetPage<T>>();
private final Monitor monitor = new Monitor();
private final int pageSize;
private boolean fetchAhead = true;
private Future<Integer> futureRowCount;
public LimitOffsetPagingQuery(EbeanServer server, SpiQuery<T> query, int pageSize) {
this.query = query;
this.pageSize = pageSize;
this.server = server;
}
public EbeanServer getServer() {
return server;
}
public void setServer(EbeanServer server) {
this.server = server;
}
public SpiQuery<T> getSpiQuery() {
return query;
}
public PagingList<T> setFetchAhead(boolean fetchAhead) {
this.fetchAhead = fetchAhead;
return this;
}
public List<T> getAsList() {
return new LimitOffsetList<T>(this);
}
public Future<Integer> getFutureRowCount() {
synchronized (monitor) {
if (futureRowCount == null){
futureRowCount = server.findFutureRowCount(query, null);
}
return futureRowCount;
}
}
private LimitOffsetPage<T> internalGetPage(int i){
synchronized (monitor) {
int ps = pages.size();
if (ps <= i){
for (int j = ps; j <= i; j++) {
LimitOffsetPage<T> p = new LimitOffsetPage<T>(j, this);
pages.add(p);
}
}
return pages.get(i);
}
}
protected void fetchAheadIfRequired(int pageIndex){
synchronized (monitor) {
// Already checked in LimitOffsetPage that there is another page
if (fetchAhead){
// fetchAhead is turned on so get the next page and trigger query
LimitOffsetPage<T> nextPage = internalGetPage(pageIndex + 1);
nextPage.getFutureList();
}
}
}
public void refresh() {
synchronized (monitor) {
futureRowCount = null;
pages.clear();
}
}
public Page<T> getPage(int i) {
return internalGetPage(i);
}
protected boolean hasNext(int position){
return position < getTotalRowCount();
}
protected T get(int rowIndex){
int pg = rowIndex / pageSize;
int offset = rowIndex % pageSize;
Page<T> page = getPage(pg);
return page.getList().get(offset);
}
public int getTotalPageCount() {
int rowCount = getTotalRowCount();
if (rowCount == 0){
return 0;
} else {
return ((rowCount-1) / pageSize) + 1;
}
}
public int getPageSize() {
return pageSize;
}
public int getTotalRowCount() {
try {
return getFutureRowCount().get();
} catch (Exception e) {
throw new PersistenceException(e);
}
}
}
@@ -1,54 +1,35 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.FutureTask;
import com.avaje.ebean.FutureIds;
import com.avaje.ebean.Query;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Default implementation of FutureIds.
*/
public class QueryFutureIds<T> extends BaseFuture<List<Object>> implements FutureIds<T> {
private final SpiQuery<T> query;
public QueryFutureIds(SpiQuery<T> query, FutureTask<List<Object>> futureTask) {
super(futureTask);
this.query = query;
}
public Query<T> getQuery() {
return query;
}
public List<Object> getPartialIds() {
return query.getIdList();
}
public boolean cancel(boolean mayInterruptIfRunning) {
query.cancel();
return super.cancel(mayInterruptIfRunning);
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.FutureTask;
import com.avaje.ebean.FutureIds;
import com.avaje.ebean.Query;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Default implementation of FutureIds.
*/
public class QueryFutureIds<T> extends BaseFuture<List<Object>> implements FutureIds<T> {
private final SpiQuery<T> query;
public QueryFutureIds(SpiQuery<T> query, FutureTask<List<Object>> futureTask) {
super(futureTask);
this.query = query;
}
public Query<T> getQuery() {
return query;
}
public List<Object> getPartialIds() {
return query.getIdList();
}
public boolean cancel(boolean mayInterruptIfRunning) {
query.cancel();
return super.cancel(mayInterruptIfRunning);
}
}
@@ -1,51 +1,32 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.FutureTask;
import com.avaje.ebean.FutureList;
import com.avaje.ebean.Query;
/**
* Default implementation for FutureList.
*/
public class QueryFutureList<T> extends BaseFuture<List<T>> implements FutureList<T> {
private final Query<T> query;
public QueryFutureList(Query<T> query, FutureTask<List<T>> futureTask) {
super(futureTask);
this.query = query;
}
public Query<T> getQuery() {
return query;
}
public boolean cancel(boolean mayInterruptIfRunning) {
query.cancel();
return super.cancel(mayInterruptIfRunning);
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.FutureTask;
import com.avaje.ebean.FutureList;
import com.avaje.ebean.Query;
/**
* Default implementation for FutureList.
*/
public class QueryFutureList<T> extends BaseFuture<List<T>> implements FutureList<T> {
private final Query<T> query;
public QueryFutureList(Query<T> query, FutureTask<List<T>> futureTask) {
super(futureTask);
this.query = query;
}
public Query<T> getQuery() {
return query;
}
public boolean cancel(boolean mayInterruptIfRunning) {
query.cancel();
return super.cancel(mayInterruptIfRunning);
}
}
@@ -1,49 +1,30 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.concurrent.FutureTask;
import com.avaje.ebean.FutureRowCount;
import com.avaje.ebean.Query;
/**
* Future implementation for the row count query.
*/
public class QueryFutureRowCount<T> extends BaseFuture<Integer> implements FutureRowCount<T> {
private final Query<T> query;
public QueryFutureRowCount(Query<T> query, FutureTask<Integer> futureTask) {
super(futureTask);
this.query = query;
}
public Query<T> getQuery() {
return query;
}
public boolean cancel(boolean mayInterruptIfRunning) {
query.cancel();
return super.cancel(mayInterruptIfRunning);
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.concurrent.FutureTask;
import com.avaje.ebean.FutureRowCount;
import com.avaje.ebean.Query;
/**
* Future implementation for the row count query.
*/
public class QueryFutureRowCount<T> extends BaseFuture<Integer> implements FutureRowCount<T> {
private final Query<T> query;
public QueryFutureRowCount(Query<T> query, FutureTask<Integer> futureTask) {
super(futureTask);
this.query = query;
}
public Query<T> getQuery() {
return query;
}
public boolean cancel(boolean mayInterruptIfRunning) {
query.cancel();
return super.cancel(mayInterruptIfRunning);
}
}
@@ -1,118 +1,99 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebean.config.dbplatform.SqlLimiter;
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.DRawSqlSelect;
import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
import com.avaje.ebeaninternal.server.deploy.DeployParser;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest;
import javax.persistence.PersistenceException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Factory for SqlSelectClause based on raw sql.
* <p>
* Its job is to execute the sql, read the meta data to determine the columns to
* bean property mapping.
* </p>
*/
public class RawSqlSelectClauseBuilder {
private static final Logger logger = Logger.getLogger(RawSqlSelectClauseBuilder.class.getName());
private final Binder binder;
private final SqlLimiter dbQueryLimiter;
private final DatabasePlatform dbPlatform;
public RawSqlSelectClauseBuilder(DatabasePlatform dbPlatform, Binder binder) {
this.binder = binder;
this.dbQueryLimiter = dbPlatform.getSqlLimiter();
this.dbPlatform = dbPlatform;
}
/**
* Build based on the includes and using the BeanJoinTree.
*/
public <T> CQuery<T> build(OrmQueryRequest<T> request) throws PersistenceException {
SpiQuery<T> query = request.getQuery();
BeanDescriptor<T> desc = request.getBeanDescriptor();
DeployNamedQuery namedQuery = desc.getNamedQuery(query.getName());
DRawSqlSelect sqlSelect = namedQuery.getSqlSelect();
// create a parser for this specific SqlSelect... has to be really
// as each SqlSelect could have different table alias etc
DeployParser parser = sqlSelect.createDeployPropertyParser();
CQueryPredicates predicates = new CQueryPredicates(binder, request);
// prepare and convert logical property names to dbColumns etc
predicates.prepareRawSql(parser);
SqlTreeAlias alias = new SqlTreeAlias(sqlSelect.getTableAlias());
predicates.parseTableAlias(alias);
String sql = null;
try {
boolean includeRowNumColumn = false;
String orderBy = sqlSelect.getOrderBy(predicates);
// build the actual sql String
sql = sqlSelect.buildSql(orderBy, predicates, request);
if (query.hasMaxRowsOrFirstRow() && dbQueryLimiter != null) {
// wrap with a limit offset or ROW_NUMBER() etc
SqlLimitResponse limitSql = dbQueryLimiter.limit(new OrmQueryLimitRequest(sql, orderBy, query, dbPlatform));
includeRowNumColumn = limitSql.isIncludesRowNumberColumn();
sql = limitSql.getSql();
} else {
// add back select keyword
// ... was removed to support dbQueryLimiter
sql = "select " + sql;
}
SqlTree sqlTree = sqlSelect.getSqlTree();
CQueryPlan queryPlan = new CQueryPlan(sql, sqlTree, true, includeRowNumColumn, "");
CQuery<T> compiledQuery = new CQuery<T>(request, predicates, queryPlan);
return compiledQuery;
} catch (Exception e) {
String msg = "Error with " + desc.getFullName() + " query:\r" + sql;
logger.log(Level.SEVERE, msg);
throw new PersistenceException(e);
}
}
}
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebean.config.dbplatform.SqlLimiter;
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.DRawSqlSelect;
import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
import com.avaje.ebeaninternal.server.deploy.DeployParser;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest;
import javax.persistence.PersistenceException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Factory for SqlSelectClause based on raw sql.
* <p>
* Its job is to execute the sql, read the meta data to determine the columns to
* bean property mapping.
* </p>
*/
public class RawSqlSelectClauseBuilder {
private static final Logger logger = Logger.getLogger(RawSqlSelectClauseBuilder.class.getName());
private final Binder binder;
private final SqlLimiter dbQueryLimiter;
private final DatabasePlatform dbPlatform;
public RawSqlSelectClauseBuilder(DatabasePlatform dbPlatform, Binder binder) {
this.binder = binder;
this.dbQueryLimiter = dbPlatform.getSqlLimiter();
this.dbPlatform = dbPlatform;
}
/**
* Build based on the includes and using the BeanJoinTree.
*/
public <T> CQuery<T> build(OrmQueryRequest<T> request) throws PersistenceException {
SpiQuery<T> query = request.getQuery();
BeanDescriptor<T> desc = request.getBeanDescriptor();
DeployNamedQuery namedQuery = desc.getNamedQuery(query.getName());
DRawSqlSelect sqlSelect = namedQuery.getSqlSelect();
// create a parser for this specific SqlSelect... has to be really
// as each SqlSelect could have different table alias etc
DeployParser parser = sqlSelect.createDeployPropertyParser();
CQueryPredicates predicates = new CQueryPredicates(binder, request);
// prepare and convert logical property names to dbColumns etc
predicates.prepareRawSql(parser);
SqlTreeAlias alias = new SqlTreeAlias(sqlSelect.getTableAlias());
predicates.parseTableAlias(alias);
String sql = null;
try {
boolean includeRowNumColumn = false;
String orderBy = sqlSelect.getOrderBy(predicates);
// build the actual sql String
sql = sqlSelect.buildSql(orderBy, predicates, request);
if (query.hasMaxRowsOrFirstRow() && dbQueryLimiter != null) {
// wrap with a limit offset or ROW_NUMBER() etc
SqlLimitResponse limitSql = dbQueryLimiter.limit(new OrmQueryLimitRequest(sql, orderBy, query, dbPlatform));
includeRowNumColumn = limitSql.isIncludesRowNumberColumn();
sql = limitSql.getSql();
} else {
// add back select keyword
// ... was removed to support dbQueryLimiter
sql = "select " + sql;
}
SqlTree sqlTree = sqlSelect.getSqlTree();
CQueryPlan queryPlan = new CQueryPlan(sql, sqlTree, true, includeRowNumColumn, "");
CQuery<T> compiledQuery = new CQuery<T>(request, predicates, queryPlan);
return compiledQuery;
} catch (Exception e) {
String msg = "Error with " + desc.getFullName() + " query:\r" + sql;
logger.log(Level.SEVERE, msg);
throw new PersistenceException(e);
}
}
}
@@ -1,142 +1,123 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.Set;
import javax.persistence.PersistenceException;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
/**
* Controls the loading of property data into a bean.
* <p>
* Takes into account the differences of lazy loading and
* partial objects.
* </p>
*/
public class SqlBeanLoad {
private final DbReadContext ctx;
private final Object bean;
private final Class<?> type;
private final Object originalOldValues;
private final boolean isLazyLoad;
// set of properties to exclude from the refresh because it is
// not a refresh but rather a lazyLoading event.
private final Set<String> excludes;
private final boolean setOriginalOldValues;
private final boolean rawSql;
public SqlBeanLoad(DbReadContext ctx, Class<?> type, Object bean, Mode queryMode) {
this.ctx = ctx;
this.rawSql = ctx.isRawSql();
this.type = type;
this.isLazyLoad = queryMode.equals(Mode.LAZYLOAD_BEAN);
this.bean = bean;
if (bean instanceof EntityBean) {
EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept();
this.excludes = isLazyLoad ? ebi.getLoadedProps() : null;
if (excludes != null) {
// lazy loading a "Partial Object"... which already
// contains some properties and perhaps some oldValues
// and these will need to be maintained...
originalOldValues = ebi.getOldValues();
} else {
originalOldValues = null;
}
this.setOriginalOldValues = originalOldValues != null;
} else {
this.excludes = null;
this.originalOldValues = null;
this.setOriginalOldValues = false;
}
}
/**
* Return true if this is a lazy loading.
*/
public boolean isLazyLoad() {
return isLazyLoad;
}
/**
* Increment the resultSet index 1.
*/
public void loadIgnore(int increment) {
ctx.getDataReader().incrementPos(increment);
}
public Object load(BeanProperty prop) throws SQLException {
if (!rawSql && prop.isTransient()){
return null;
}
if ((bean == null)
|| (excludes != null && excludes.contains(prop.getName()))
|| (type != null && !prop.isAssignableFrom(type))){
// ignore this property
// ... null: bean already in persistence context
// ... excludes: partial bean that is lazy loading
// ... type: inheritance and not assignable to this instance
prop.loadIgnore(ctx);
return null;
}
try {
Object dbVal = prop.read(ctx);
if (isLazyLoad){
prop.setValue(bean, dbVal);
} else {
prop.setValueIntercept(bean, dbVal);
}
if (setOriginalOldValues){
// maintain original oldValues for partially loaded bean
prop.setValue(originalOldValues, dbVal);
}
return dbVal;
} catch (Exception e) {
String msg = "Error loading on " + prop.getFullBeanName();
throw new PersistenceException(msg, e);
}
}
public void loadAssocMany(BeanPropertyAssocMany<?> prop) {
// do nothing, as a lazy loading BeanCollection 'reference'
// is created and registered with the loading context
// in SqlTreeNodeBean.createListProxies()
}
}
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.Set;
import javax.persistence.PersistenceException;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
/**
* Controls the loading of property data into a bean.
* <p>
* Takes into account the differences of lazy loading and
* partial objects.
* </p>
*/
public class SqlBeanLoad {
private final DbReadContext ctx;
private final Object bean;
private final Class<?> type;
private final Object originalOldValues;
private final boolean isLazyLoad;
// set of properties to exclude from the refresh because it is
// not a refresh but rather a lazyLoading event.
private final Set<String> excludes;
private final boolean setOriginalOldValues;
private final boolean rawSql;
public SqlBeanLoad(DbReadContext ctx, Class<?> type, Object bean, Mode queryMode) {
this.ctx = ctx;
this.rawSql = ctx.isRawSql();
this.type = type;
this.isLazyLoad = queryMode.equals(Mode.LAZYLOAD_BEAN);
this.bean = bean;
if (bean instanceof EntityBean) {
EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept();
this.excludes = isLazyLoad ? ebi.getLoadedProps() : null;
if (excludes != null) {
// lazy loading a "Partial Object"... which already
// contains some properties and perhaps some oldValues
// and these will need to be maintained...
originalOldValues = ebi.getOldValues();
} else {
originalOldValues = null;
}
this.setOriginalOldValues = originalOldValues != null;
} else {
this.excludes = null;
this.originalOldValues = null;
this.setOriginalOldValues = false;
}
}
/**
* Return true if this is a lazy loading.
*/
public boolean isLazyLoad() {
return isLazyLoad;
}
/**
* Increment the resultSet index 1.
*/
public void loadIgnore(int increment) {
ctx.getDataReader().incrementPos(increment);
}
public Object load(BeanProperty prop) throws SQLException {
if (!rawSql && prop.isTransient()){
return null;
}
if ((bean == null)
|| (excludes != null && excludes.contains(prop.getName()))
|| (type != null && !prop.isAssignableFrom(type))){
// ignore this property
// ... null: bean already in persistence context
// ... excludes: partial bean that is lazy loading
// ... type: inheritance and not assignable to this instance
prop.loadIgnore(ctx);
return null;
}
try {
Object dbVal = prop.read(ctx);
if (isLazyLoad){
prop.setValue(bean, dbVal);
} else {
prop.setValueIntercept(bean, dbVal);
}
if (setOriginalOldValues){
// maintain original oldValues for partially loaded bean
prop.setValue(originalOldValues, dbVal);
}
return dbVal;
} catch (Exception e) {
String msg = "Error loading on " + prop.getFullBeanName();
throw new PersistenceException(msg, e);
}
}
public void loadAssocMany(BeanPropertyAssocMany<?> prop) {
// do nothing, as a lazy loading BeanCollection 'reference'
// is created and registered with the loading context
// in SqlTreeNodeBean.createListProxies()
}
}
@@ -1,191 +1,172 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
/**
* Represents the SELECT clause part of the SQL query.
*/
public class SqlTree {
private SqlTreeNode rootNode;
/**
* Property if resultSet contains master and detail rows.
*/
private BeanPropertyAssocMany<?> manyProperty;
private String manyPropertyName;
private ElPropertyValue manyPropEl;
private Set<String> includes;
/**
* Summary of the select being generated.
*/
private String summary;
private String selectSql;
private String fromSql;
/**
* Encrypted Properties require additional binding.
*/
private BeanProperty[] encryptedProps;
/**
* Where clause for inheritance.
*/
private String inheritanceWhereSql;
/**
* Create the SqlSelectClause.
*/
public SqlTree() {
}
public List<String> buildSelectExpressionChain() {
ArrayList<String> list = new ArrayList<String>();
rootNode.buildSelectExpressionChain(list);
return list;
}
/**
* Return the includes. Associated beans lists etc.
*/
public Set<String> getIncludes() {
return includes;
}
/**
* Set the association includes (Ones and Many's).
*/
public void setIncludes(Set<String> includes) {
this.includes = includes;
}
/**
* Set the manyProperty used for this query.
*/
public void setManyProperty(BeanPropertyAssocMany<?> manyProperty, String manyPropertyName, ElPropertyValue manyPropEl) {
this.manyProperty = manyProperty;
this.manyPropertyName = manyPropertyName;
this.manyPropEl = manyPropEl;
}
/**
* Return the String for the actual SQL.
*/
public String getSelectSql() {
return selectSql;
}
/**
* Set the select sql clause.
*/
public void setSelectSql(String selectSql) {
this.selectSql = selectSql;
}
public String getFromSql() {
return fromSql;
}
public void setFromSql(String fromSql) {
this.fromSql = fromSql;
}
/**
* Return the where clause for inheritance.
*/
public String getInheritanceWhereSql() {
return inheritanceWhereSql;
}
/**
* Set where clause(s) for inheritance.
*/
public void setInheritanceWhereSql(String whereSql) {
this.inheritanceWhereSql = whereSql;
}
/**
* Set the summary description of the query.
*/
public void setSummary(String summary) {
this.summary = summary;
}
/**
* Return a summary of the select clause.
*/
public String getSummary() {
return summary;
}
public SqlTreeNode getRootNode() {
return rootNode;
}
public void setRootNode(SqlTreeNode rootNode) {
this.rootNode = rootNode;
}
/**
* Return the property that is associated with the many. There can only be
* one per SqlSelect. This can be null.
*/
public BeanPropertyAssocMany<?> getManyProperty() {
return manyProperty;
}
public String getManyPropertyName() {
return manyPropertyName;
}
public ElPropertyValue getManyPropertyEl() {
return manyPropEl;
}
/**
* Return true if this query includes a Many association.
*/
public boolean isManyIncluded() {
return (manyProperty != null);
}
public BeanProperty[] getEncryptedProps() {
return encryptedProps;
}
public void setEncryptedProps(BeanProperty[] encryptedProps) {
this.encryptedProps = encryptedProps;
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
/**
* Represents the SELECT clause part of the SQL query.
*/
public class SqlTree {
private SqlTreeNode rootNode;
/**
* Property if resultSet contains master and detail rows.
*/
private BeanPropertyAssocMany<?> manyProperty;
private String manyPropertyName;
private ElPropertyValue manyPropEl;
private Set<String> includes;
/**
* Summary of the select being generated.
*/
private String summary;
private String selectSql;
private String fromSql;
/**
* Encrypted Properties require additional binding.
*/
private BeanProperty[] encryptedProps;
/**
* Where clause for inheritance.
*/
private String inheritanceWhereSql;
/**
* Create the SqlSelectClause.
*/
public SqlTree() {
}
public List<String> buildSelectExpressionChain() {
ArrayList<String> list = new ArrayList<String>();
rootNode.buildSelectExpressionChain(list);
return list;
}
/**
* Return the includes. Associated beans lists etc.
*/
public Set<String> getIncludes() {
return includes;
}
/**
* Set the association includes (Ones and Many's).
*/
public void setIncludes(Set<String> includes) {
this.includes = includes;
}
/**
* Set the manyProperty used for this query.
*/
public void setManyProperty(BeanPropertyAssocMany<?> manyProperty, String manyPropertyName, ElPropertyValue manyPropEl) {
this.manyProperty = manyProperty;
this.manyPropertyName = manyPropertyName;
this.manyPropEl = manyPropEl;
}
/**
* Return the String for the actual SQL.
*/
public String getSelectSql() {
return selectSql;
}
/**
* Set the select sql clause.
*/
public void setSelectSql(String selectSql) {
this.selectSql = selectSql;
}
public String getFromSql() {
return fromSql;
}
public void setFromSql(String fromSql) {
this.fromSql = fromSql;
}
/**
* Return the where clause for inheritance.
*/
public String getInheritanceWhereSql() {
return inheritanceWhereSql;
}
/**
* Set where clause(s) for inheritance.
*/
public void setInheritanceWhereSql(String whereSql) {
this.inheritanceWhereSql = whereSql;
}
/**
* Set the summary description of the query.
*/
public void setSummary(String summary) {
this.summary = summary;
}
/**
* Return a summary of the select clause.
*/
public String getSummary() {
return summary;
}
public SqlTreeNode getRootNode() {
return rootNode;
}
public void setRootNode(SqlTreeNode rootNode) {
this.rootNode = rootNode;
}
/**
* Return the property that is associated with the many. There can only be
* one per SqlSelect. This can be null.
*/
public BeanPropertyAssocMany<?> getManyProperty() {
return manyProperty;
}
public String getManyPropertyName() {
return manyPropertyName;
}
public ElPropertyValue getManyPropertyEl() {
return manyPropEl;
}
/**
* Return true if this query includes a Many association.
*/
public boolean isManyIncluded() {
return (manyProperty != null);
}
public BeanProperty[] getEncryptedProps() {
return encryptedProps;
}
public void setEncryptedProps(BeanProperty[] encryptedProps) {
this.encryptedProps = encryptedProps;
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,112 +1,93 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
/**
* Join to Many (or child of a many) to support where clause predicates on many properties.
*
* @author rbygrave
*/
public class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
private final String parentPrefix;
private final String prefix;
private final BeanPropertyAssoc<?> nodeBeanProp;
private final SqlTreeNode[] children;
public SqlTreeNodeManyWhereJoin(String prefix, BeanPropertyAssoc<?> prop) {
this.nodeBeanProp = prop;
this.prefix = prefix;
String[] split = SplitName.split(prefix);
this.parentPrefix = split[0];
List<SqlTreeNode> childrenList = new ArrayList<SqlTreeNode>(0);
this.children = childrenList.toArray(new SqlTreeNode[childrenList.size()]);
}
/**
* Append to the FROM clause for this node.
*/
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
appendFromBaseTable(ctx, forceOuterJoin);
for (int i = 0; i < children.length; i++) {
children[i].appendFrom(ctx, forceOuterJoin);
}
}
/**
* Join to base table for this node. This includes a join to the
* intersection table if this is a ManyToMany node.
*/
public void appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) {
String alias = ctx.getTableAliasManyWhere(prefix);
String parentAlias = ctx.getTableAliasManyWhere(parentPrefix);
if (nodeBeanProp instanceof BeanPropertyAssocOne<?>){
nodeBeanProp.addInnerJoin(parentAlias, alias, ctx);
} else {
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>)nodeBeanProp;
if (!manyProp.isManyToMany()) {
manyProp.addInnerJoin(parentAlias, alias, ctx);
} else {
String alias2 = alias + "z_";
TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin();
manyToManyJoin.addInnerJoin(parentAlias, alias2, ctx);
manyProp.addInnerJoin(alias2, alias, ctx);
}
}
}
public void buildSelectExpressionChain(List<String> selectChain) {
// nothing to add
}
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
// nothing to do here
}
public void appendWhere(DbSqlContext ctx) {
// nothing to do here
}
public void load(DbReadContext ctx, Object parentBean) throws SQLException {
// nothing to do here
}
}
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
/**
* Join to Many (or child of a many) to support where clause predicates on many properties.
*
* @author rbygrave
*/
public class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
private final String parentPrefix;
private final String prefix;
private final BeanPropertyAssoc<?> nodeBeanProp;
private final SqlTreeNode[] children;
public SqlTreeNodeManyWhereJoin(String prefix, BeanPropertyAssoc<?> prop) {
this.nodeBeanProp = prop;
this.prefix = prefix;
String[] split = SplitName.split(prefix);
this.parentPrefix = split[0];
List<SqlTreeNode> childrenList = new ArrayList<SqlTreeNode>(0);
this.children = childrenList.toArray(new SqlTreeNode[childrenList.size()]);
}
/**
* Append to the FROM clause for this node.
*/
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
appendFromBaseTable(ctx, forceOuterJoin);
for (int i = 0; i < children.length; i++) {
children[i].appendFrom(ctx, forceOuterJoin);
}
}
/**
* Join to base table for this node. This includes a join to the
* intersection table if this is a ManyToMany node.
*/
public void appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) {
String alias = ctx.getTableAliasManyWhere(prefix);
String parentAlias = ctx.getTableAliasManyWhere(parentPrefix);
if (nodeBeanProp instanceof BeanPropertyAssocOne<?>){
nodeBeanProp.addInnerJoin(parentAlias, alias, ctx);
} else {
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>)nodeBeanProp;
if (!manyProp.isManyToMany()) {
manyProp.addInnerJoin(parentAlias, alias, ctx);
} else {
String alias2 = alias + "z_";
TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin();
manyToManyJoin.addInnerJoin(parentAlias, alias2, ctx);
manyProp.addInnerJoin(alias2, alias, ctx);
}
}
}
public void buildSelectExpressionChain(List<String> selectChain) {
// nothing to add
}
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
// nothing to do here
}
public void appendWhere(DbSqlContext ctx) {
// nothing to do here
}
public void load(DbReadContext ctx, Object parentBean) throws SQLException {
// nothing to do here
}
}
@@ -1,107 +1,104 @@
/**
*
*/
package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
/**
* The select properties for a node in the SqlTree.
*/
public class SqlTreeProperties {
/**
* The included Properties that will be used by EntityBeanIntercept
* to determine lazy loading on partial objects.
*/
Set<String> includedProps;
/**
* True if this node of the tree should have read only entity beans.
*/
boolean readOnly;
/**
* set to false if the id field is not included.
*/
boolean includeId = true;
TableJoin[] tableJoins = new TableJoin[0];
/**
* The bean properties in order.
*/
List<BeanProperty> propsList = new ArrayList<BeanProperty>();
/**
* Maintain a list of property names to detect embedded bean additions.
*/
LinkedHashSet<String> propNames = new LinkedHashSet<String>();
public SqlTreeProperties() {
}
public boolean containsProperty(String propName){
return propNames.contains(propName);
}
public void add(BeanProperty[] props) {
for (BeanProperty beanProperty : props) {
propsList.add(beanProperty);
}
}
public void add(BeanProperty prop) {
propsList.add(prop);
propNames.add(prop.getName());
}
public BeanProperty[] getProps() {
return propsList.toArray(new BeanProperty[propsList.size()]);
}
public boolean isIncludeId() {
return includeId;
}
public void setIncludeId(boolean includeId) {
this.includeId = includeId;
}
public boolean isPartialObject() {
return includedProps != null;
}
public Set<String> getIncludedProperties() {
return includedProps;
}
public void setIncludedProperties(Set<String> includedProps) {
this.includedProps = includedProps;
}
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
public TableJoin[] getTableJoins() {
return tableJoins;
}
public void setTableJoins(TableJoin[] tableJoins) {
this.tableJoins = tableJoins;
}
package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
/**
* The select properties for a node in the SqlTree.
*/
public class SqlTreeProperties {
/**
* The included Properties that will be used by EntityBeanIntercept
* to determine lazy loading on partial objects.
*/
Set<String> includedProps;
/**
* True if this node of the tree should have read only entity beans.
*/
boolean readOnly;
/**
* set to false if the id field is not included.
*/
boolean includeId = true;
TableJoin[] tableJoins = new TableJoin[0];
/**
* The bean properties in order.
*/
List<BeanProperty> propsList = new ArrayList<BeanProperty>();
/**
* Maintain a list of property names to detect embedded bean additions.
*/
LinkedHashSet<String> propNames = new LinkedHashSet<String>();
public SqlTreeProperties() {
}
public boolean containsProperty(String propName){
return propNames.contains(propName);
}
public void add(BeanProperty[] props) {
for (BeanProperty beanProperty : props) {
propsList.add(beanProperty);
}
}
public void add(BeanProperty prop) {
propsList.add(prop);
propNames.add(prop.getName());
}
public BeanProperty[] getProps() {
return propsList.toArray(new BeanProperty[propsList.size()]);
}
public boolean isIncludeId() {
return includeId;
}
public void setIncludeId(boolean includeId) {
this.includeId = includeId;
}
public boolean isPartialObject() {
return includedProps != null;
}
public Set<String> getIncludedProperties() {
return includedProps;
}
public void setIncludedProperties(Set<String> includedProps) {
this.includedProps = includedProps;
}
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
public TableJoin[] getTableJoins() {
return tableJoins;
}
public void setTableJoins(TableJoin[] tableJoins) {
this.tableJoins = tableJoins;
}
}
@@ -1,4 +1 @@
/**
* Controls execution of the Orm and Raw Sql queries.
*/
package com.avaje.ebeaninternal.server.query;