mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Change license to Apache2 and reformat
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+259
-278
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+99
-118
@@ -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;
|
||||
Reference in New Issue
Block a user