package com.avaje.ebeaninternal.server.query; import com.avaje.ebean.bean.BeanCollection; import com.avaje.ebean.bean.EntityBean; 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.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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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; /** * Executes the select row count query. */ public class CQueryFetchIds { private static final Logger logger = LoggerFactory.getLogger(CQueryFetchIds.class); /** * 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; private RsetDataReader dataReader; /** * The statement used to create the resultSet. */ private PreparedStatement pstmt; private String bindLog; private int executionTimeMicros; private int rowCount; private final int maxRows; /** * Create the Sql select based on the request. */ public CQueryFetchIds(OrmQueryRequest request, CQueryPredicates predicates, String sql) { this.request = request; this.query = request.getQuery(); this.sql = sql; this.maxRows = query.getMaxRows(); query.setGeneratedSql(sql); this.desc = request.getBeanDescriptor(); this.predicates = predicates; } /** * Return a summary description of this query. */ public String getSummary() { //noinspection StringBufferReplaceableByString StringBuilder sb = new StringBuilder(80); 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; } /** * Execute the query returning the row count. */ public BeanIdList findIds() throws SQLException { long 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 idList = query.getIdList(); if (idList == null) { // running in foreground thread (not FutureIds query) idList = Collections.synchronizedList(new ArrayList()); 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; } } if (hitMaxRows) { result.setHasMore(hasMoreRows); } long exeNano = System.nanoTime() - startNano; executionTimeMicros = (int) exeNano / 1000; return result; } finally { close(); } } /** * Close the resources. *

* The jdbc resultSet and statement need to be closed. Its important that * this method is called. *

*/ private void close() { try { if (dataReader != null) { dataReader.close(); dataReader = null; } } catch (SQLException e) { logger.error(null, e); } try { if (pstmt != null) { pstmt.close(); pstmt = null; } } catch (SQLException e) { logger.error(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 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 pathMap) { // no-op } public void setLazyLoadedChildBean(EntityBean loadedBean, Object lazyLoadParentId) { // no-op } } }