001: /*
002: * ============================================================================
003: * GNU Lesser General Public License
004: * ============================================================================
005: *
006: * JasperReports - Free Java report-generating library.
007: * Copyright (C) 2001-2005 JasperSoft Corporation http://www.jaspersoft.com
008: *
009: * This library is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU Lesser General Public
011: * License as published by the Free Software Foundation; either
012: * version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * Lesser General Public License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
022: *
023: * JasperSoft Corporation
024: * 303 Second Street, Suite 450 North
025: * San Francisco, CA 94107
026: * http://www.jaspersoft.com
027: */
028: package net.sf.jasperreports.engine.query;
029:
030: import java.util.HashSet;
031: import java.util.Iterator;
032: import java.util.List;
033: import java.util.Map;
034: import java.util.Set;
035:
036: import javax.persistence.EntityManager;
037: import javax.persistence.Query;
038:
039: import net.sf.jasperreports.engine.JRDataSource;
040: import net.sf.jasperreports.engine.JRDataset;
041: import net.sf.jasperreports.engine.JRException;
042: import net.sf.jasperreports.engine.JRParameter;
043: import net.sf.jasperreports.engine.JRPropertiesMap;
044: import net.sf.jasperreports.engine.JRRuntimeException;
045: import net.sf.jasperreports.engine.JRValueParameter;
046: import net.sf.jasperreports.engine.data.JRJpaDataSource;
047: import net.sf.jasperreports.engine.util.JRProperties;
048: import net.sf.jasperreports.engine.util.JRStringUtil;
049:
050: import org.apache.commons.logging.Log;
051: import org.apache.commons.logging.LogFactory;
052:
053: /**
054: * EJBQL query executer that uses the Java Persistence API.
055: * <p/>
056: * To use EJBQL in queries, an <code>javax.persistence.EntityManager</code> is needed.
057: * When running or filling reports the <code>em</code> need to be supplied with the named parameter {@link net.sf.jasperreports.engine.query.JRJpaQueryExecuterFactory#PARAMETER_JPA_ENTITY_MANAGER}.
058: * <p/>
059: * Example:
060: * <code>
061: * <pre>
062: * Map parameters = new HashMap();
063: * EntityManager em = emf.createEntityManager();
064: * parameters.put(JRJpaQueryExecuterFactory.PARAMETER_JPA_ENTITY_MANAGER, em);
065: * JasperRunManager.runReportToPdfFile(fileName, parameters);
066: * </pre>
067: * </code>
068: * <p/>
069: * When dealing with large result sets, pagination can be used by setting the {@link net.sf.jasperreports.engine.query.JRJpaQueryExecuterFactory#PROPERTY_JPA_QUERY_PAGE_SIZE} property in the report template.
070: * <p/>
071: * Example:
072: * <code>
073: * <pre>
074: * <property name="net.sf.jasperreports.ejbql.query.page.size" value="100"/>
075: * </pre>
076: * </code>
077: * <p/>
078: * Implementation-specific query hints can be set either using report properties in the report template,
079: * or by supplying the named parameter {@link net.sf.jasperreports.engine.query.JRJpaQueryExecuterFactory#PARAMETER_JPA_QUERY_HINTS_MAP}
080: * containing a <code>java.util.Map</code> with named/value query hints.
081: * <p/>
082: * Example using report property:
083: * <code>
084: * <pre>
085: * <property name="net.sf.jasperreports.ejbql.query.hint.fetchSize" value="100"/>
086: * </pre>
087: * </code>
088: * The name of the query hint need to be prefixed with <code>net.sf.jasperreports.ejbql.query.hint.</code> ({@link net.sf.jasperreports.engine.query.JRJpaQueryExecuterFactory#PROPERTY_JPA_QUERY_HINT_PREFIX}).
089: * Above example will set a query hint with the name <code>fetchSize</code> and the <code>String</code> value <code>100</code>.
090: * <p/>
091: * Example using map:
092: * <code>
093: * <pre>
094: * Map hints = new HashMap();
095: * hints.put("fetchSize", new Integer(100));
096: * hints.put("anyName", anyObject());
097: * Map parameters = new HashMap();
098: * EntityManager em = emf.createEntityManager();
099: * parameters.put(JRJpaQueryExecuterFactory.PARAMETER_JPA_ENTITY_MANAGER, em);
100: * parameters.put(JRJpaQueryExecuterFactory.PARAMETER_JPA_QUERY_HINTS_MAP, hints);
101: * JasperRunManager.runReportToPdfFile(fileName, parameters);
102: * </pre>
103: * </code>
104: * Note that when using report properties only <code>String</code> values can be set as query hint.
105: * When using a query hints map, any <code>Object</code> can be set as value.
106: *
107: * @author Marcel Overdijk (marceloverdijk@hotmail.com)
108: * @version $Id: JRJpaQueryExecuter.java 1472 2006-11-09 17:16:52Z lucianc $
109: * @see net.sf.jasperreports.engine.query.JRJpaQueryExecuterFactory
110: */
111: public class JRJpaQueryExecuter extends JRAbstractQueryExecuter {
112:
113: private static final Log log = LogFactory
114: .getLog(JRJpaQueryExecuter.class);
115:
116: private final Integer reportMaxCount;
117:
118: private EntityManager em;
119: private Query query;
120:
121: public JRJpaQueryExecuter(JRDataset dataset, Map parameters) {
122: super (dataset, parameters);
123:
124: em = (EntityManager) getParameterValue(JRJpaQueryExecuterFactory.PARAMETER_JPA_ENTITY_MANAGER);
125: reportMaxCount = (Integer) getParameterValue(JRParameter.REPORT_MAX_COUNT);
126:
127: if (em == null) {
128: log
129: .warn("The supplied javax.persistence.EntityManager object is null.");
130: }
131:
132: parseQuery();
133: }
134:
135: public JRDataSource createDatasource() throws JRException {
136: JRDataSource datasource = null;
137: String queryString = getQueryString();
138:
139: if (em != null && queryString != null
140: && queryString.trim().length() > 0) {
141: createQuery(queryString);
142:
143: datasource = createResultDatasource();
144: }
145:
146: return datasource;
147: }
148:
149: /**
150: * Creates the EJBQL query object.
151: *
152: * @param queryString the query string
153: */
154: protected synchronized void createQuery(String queryString) {
155:
156: if (log.isDebugEnabled()) {
157: log.debug("EJBQL query: " + queryString);
158: }
159:
160: query = em.createQuery(queryString);
161:
162: // Set parameters.
163: List parameterNames = getCollectedParameterNames();
164: if (!parameterNames.isEmpty()) {
165: // Use set to prevent the parameter to be set multiple times.
166: Set namesSet = new HashSet();
167: for (Iterator iter = parameterNames.iterator(); iter
168: .hasNext();) {
169: String parameterName = (String) iter.next();
170: if (namesSet.add(parameterName)) {
171: JRValueParameter parameter = getValueParameter(parameterName);
172: String ejbParamName = getEjbqlParameterName(parameterName);
173: Object paramValue = parameter.getValue();
174:
175: if (log.isDebugEnabled()) {
176: log.debug("Parameter " + ejbParamName + ": "
177: + paramValue);
178: }
179:
180: query.setParameter(ejbParamName, paramValue);
181: }
182: }
183: }
184:
185: // Set query hints.
186: // First, set query hints supplied by the JPA_QUERY_HINTS_MAP parameter.
187: Map queryHintsMap = (Map) getParameterValue(JRJpaQueryExecuterFactory.PARAMETER_JPA_QUERY_HINTS_MAP);
188: if (queryHintsMap != null) {
189: for (Iterator i = queryHintsMap.entrySet().iterator(); i
190: .hasNext();) {
191: Map.Entry pairs = (Map.Entry) i.next();
192: log.debug("EJBQL query hint [" + pairs.getKey()
193: + "] set.");
194: query
195: .setHint((String) pairs.getKey(), pairs
196: .getValue());
197: }
198: }
199: // Second, set query hints supplied by report properties which start with JREjbPersistenceQueryExecuterFactory.PROPERTY_JPA_PERSISTENCE_QUERY_HINT_PREFIX
200: // Example: net.sf.jasperreports.ejbql.query.hint.fetchSize
201: // This property will result in a query hint set with the name: fetchSize
202: JRPropertiesMap datasetProperties = dataset.getPropertiesMap();
203: String[] propertyNames = datasetProperties.getPropertyNames();
204: for (int i = 0; i < propertyNames.length; i++) {
205: String propertyName = propertyNames[i];
206: if (propertyName
207: .startsWith(JRJpaQueryExecuterFactory.PROPERTY_JPA_QUERY_HINT_PREFIX)) {
208: String queryHint = propertyName
209: .replaceFirst(
210: JRJpaQueryExecuterFactory.PROPERTY_JPA_QUERY_HINT_PREFIX,
211: "");
212: if (queryHint != null && queryHint.length() > 0) {
213: String property = datasetProperties
214: .getProperty(propertyName);
215: log.debug("EJBQL query hint [" + queryHint
216: + "] set to: " + property);
217: query.setHint(queryHint, property);
218: }
219: }
220: }
221: }
222:
223: /**
224: * Creates a data source out of the query result.
225: *
226: * @return the data source
227: */
228: protected JRDataSource createResultDatasource() {
229: JRDataSource resDatasource;
230:
231: JRPropertiesMap datasetProperties = dataset.getPropertiesMap();
232:
233: try {
234: int pageSize = JRProperties
235: .getIntegerProperty(
236: datasetProperties,
237: JRJpaQueryExecuterFactory.PROPERTY_JPA_QUERY_PAGE_SIZE,
238: 0);
239:
240: resDatasource = new JRJpaDataSource(this , pageSize);
241: } catch (NumberFormatException e) {
242: throw new JRRuntimeException(
243: "The "
244: + JRJpaQueryExecuterFactory.PROPERTY_JPA_QUERY_PAGE_SIZE
245: + " property must be numerical.");
246: }
247:
248: return resDatasource;
249: }
250:
251: public synchronized void close() {
252: query = null;
253: }
254:
255: public synchronized boolean cancelQuery() throws JRException {
256: return false;
257: }
258:
259: protected String getParameterReplacement(String parameterName) {
260: return ':' + getEjbqlParameterName(parameterName);
261: }
262:
263: protected String getEjbqlParameterName(String parameterName) {
264: return JRStringUtil.getLiteral(parameterName);
265: }
266:
267: /**
268: * Runs the query by calling <code>javax.persistence.Query.getResultList</code>.
269: * <p/>
270: * All the result rows are returned.
271: *
272: * @return the result of the query as a list
273: */
274: public List getResultList() {
275: if (reportMaxCount != null) {
276: query.setMaxResults(reportMaxCount.intValue());
277: }
278:
279: return query.getResultList();
280: }
281:
282: /**
283: * Returns a page of the query results by calling <code>javax.persistence.Query.getResultList</code>.
284: *
285: * @param firstIndex the index of the first row to return
286: * @param resultCount the number of rows to return
287: * @return result row list
288: */
289: public List getResultList(int firstIndex, int resultCount) {
290: if (reportMaxCount != null
291: && firstIndex + resultCount > reportMaxCount.intValue()) {
292: resultCount = reportMaxCount.intValue() - firstIndex;
293: }
294:
295: query.setFirstResult(firstIndex);
296: query.setMaxResults(resultCount);
297:
298: return query.getResultList();
299: }
300: }
|