/* * JasperReports - Free Java Reporting Library. * Copyright (C) 2001 - 2009 Jaspersoft Corporation. All rights reserved. * http://www.jaspersoft.com * * Unless you have purchased a commercial license agreement from Jaspersoft, * the following license terms apply: * * This program is part of JasperReports. * * JasperReports 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 3 of the License, or * (at your option) any later version. * * JasperReports 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 JasperReports. If not, see <http://www.gnu.org/licenses/>. */ package net.sf.jasperreports.engine.query; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.Query; import net.sf.jasperreports.engine.JRDataSource; import net.sf.jasperreports.engine.JRDataset; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRParameter; import net.sf.jasperreports.engine.JRRuntimeException; import net.sf.jasperreports.engine.JRValueParameter; import net.sf.jasperreports.engine.data.JRJpaDataSource; import net.sf.jasperreports.engine.util.JRProperties; import net.sf.jasperreports.engine.util.JRStringUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * EJBQL query executer that uses the Java Persistence API. * <p/> * To use EJBQL in queries, an <code>javax.persistence.EntityManager</code> is needed. * 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}. * <p/> * Example: * <code> * <pre> * Map parameters = new HashMap(); * EntityManager em = emf.createEntityManager(); * parameters.put(JRJpaQueryExecuterFactory.PARAMETER_JPA_ENTITY_MANAGER, em); * JasperRunManager.runReportToPdfFile(fileName, parameters); * </pre> * </code> * <p/> * 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. * <p/> * Example: * <code> * <pre> * <property name="net.sf.jasperreports.ejbql.query.page.size" value="100"/> * </pre> * </code> * <p/> * Implementation-specific query hints can be set either using report properties in the report template, * or by supplying the named parameter {@link net.sf.jasperreports.engine.query.JRJpaQueryExecuterFactory#PARAMETER_JPA_QUERY_HINTS_MAP} * containing a <code>java.util.Map</code> with named/value query hints. * <p/> * Example using report property: * <code> * <pre> * <property name="net.sf.jasperreports.ejbql.query.hint.fetchSize" value="100"/> * </pre> * </code> * 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}). * Above example will set a query hint with the name <code>fetchSize</code> and the <code>String</code> value <code>100</code>. * <p/> * Example using map: * <code> * <pre> * Map hints = new HashMap(); * hints.put("fetchSize", Integer.valueOf(100)); * hints.put("anyName", anyObject()); * Map parameters = new HashMap(); * EntityManager em = emf.createEntityManager(); * parameters.put(JRJpaQueryExecuterFactory.PARAMETER_JPA_ENTITY_MANAGER, em); * parameters.put(JRJpaQueryExecuterFactory.PARAMETER_JPA_QUERY_HINTS_MAP, hints); * JasperRunManager.runReportToPdfFile(fileName, parameters); * </pre> * </code> * Note that when using report properties only <code>String</code> values can be set as query hint. * When using a query hints map, any <code>Object</code> can be set as value. * * @author Marcel Overdijk (marceloverdijk@hotmail.com) * @version $Id: JRJpaQueryExecuter.java 3689 2010-04-06 11:33:27Z shertage $ * @see net.sf.jasperreports.engine.query.JRJpaQueryExecuterFactory */ public class JRJpaQueryExecuter extends JRAbstractQueryExecuter { private static final Log log = LogFactory.getLog(JRJpaQueryExecuter.class); private final Integer reportMaxCount; private EntityManager em; private Query query; public JRJpaQueryExecuter(JRDataset dataset, Map parameters) { super(dataset, parameters); em = (EntityManager)getParameterValue(JRJpaQueryExecuterFactory.PARAMETER_JPA_ENTITY_MANAGER); reportMaxCount = (Integer)getParameterValue(JRParameter.REPORT_MAX_COUNT); if (em == null) { log.warn("The supplied javax.persistence.EntityManager object is null."); } parseQuery(); } public JRDataSource createDatasource() throws JRException { JRDataSource datasource = null; String queryString = getQueryString(); if (em != null && queryString != null && queryString.trim().length() > 0) { createQuery(queryString); datasource = createResultDatasource(); } return datasource; } /** * Creates the EJBQL query object. * * @param queryString the query string */ protected synchronized void createQuery(String queryString) { if (log.isDebugEnabled()) { log.debug("EJBQL query: " + queryString); } query = em.createQuery(queryString); // Set parameters. List parameterNames = getCollectedParameterNames(); if (!parameterNames.isEmpty()) { // Use set to prevent the parameter to be set multiple times. Set namesSet = new HashSet(); for (Iterator iter = parameterNames.iterator(); iter.hasNext();) { String parameterName = (String)iter.next(); if (namesSet.add(parameterName)) { JRValueParameter parameter = getValueParameter(parameterName); String ejbParamName = getEjbqlParameterName(parameterName); Object paramValue = parameter.getValue(); if (log.isDebugEnabled()) { log.debug("Parameter " + ejbParamName + ": " + paramValue); } query.setParameter(ejbParamName, paramValue); } } } // Set query hints. // First, set query hints supplied by the JPA_QUERY_HINTS_MAP parameter. Map queryHintsMap = (Map)getParameterValue(JRJpaQueryExecuterFactory.PARAMETER_JPA_QUERY_HINTS_MAP); if (queryHintsMap != null) { for (Iterator i = queryHintsMap.entrySet().iterator(); i.hasNext(); ) { Map.Entry pairs = (Map.Entry)i.next(); log.debug("EJBQL query hint [" + pairs.getKey() + "] set."); query.setHint((String)pairs.getKey(), pairs.getValue()); } } // Second, set query hints supplied by report properties which start with JREjbPersistenceQueryExecuterFactory.PROPERTY_JPA_PERSISTENCE_QUERY_HINT_PREFIX // Example: net.sf.jasperreports.ejbql.query.hint.fetchSize // This property will result in a query hint set with the name: fetchSize List properties = JRProperties.getProperties(dataset, JRJpaQueryExecuterFactory.PROPERTY_JPA_QUERY_HINT_PREFIX); for (Iterator it = properties.iterator(); it.hasNext();) { JRProperties.PropertySuffix property = (JRProperties.PropertySuffix) it.next(); String queryHint = property.getSuffix(); if (queryHint.length() > 0) { String value = property.getValue(); if (log.isDebugEnabled()) { log.debug("EJBQL query hint [" + queryHint + "] set to: " + value); } query.setHint(queryHint, property); } } } /** * Creates a data source out of the query result. * * @return the data source */ protected JRDataSource createResultDatasource() { JRDataSource resDatasource; try { int pageSize = JRProperties.getIntegerProperty(dataset, JRJpaQueryExecuterFactory.PROPERTY_JPA_QUERY_PAGE_SIZE, 0); resDatasource = new JRJpaDataSource(this, pageSize); } catch (NumberFormatException e) { throw new JRRuntimeException("The " + JRJpaQueryExecuterFactory.PROPERTY_JPA_QUERY_PAGE_SIZE + " property must be numerical.",e); } return resDatasource; } public synchronized void close() { query = null; } public synchronized boolean cancelQuery() throws JRException { return false; } protected String getParameterReplacement(String parameterName) { return ':' + getEjbqlParameterName(parameterName); } protected String getEjbqlParameterName(String parameterName) { return JRStringUtil.getJavaIdentifier(parameterName); } /** * Runs the query by calling <code>javax.persistence.Query.getResultList</code>. * <p/> * All the result rows are returned. * * @return the result of the query as a list */ public List getResultList() { if (reportMaxCount != null) { query.setMaxResults(reportMaxCount.intValue()); } return query.getResultList(); } /** * Returns a page of the query results by calling <code>javax.persistence.Query.getResultList</code>. * * @param firstIndex the index of the first row to return * @param resultCount the number of rows to return * @return result row list */ public List getResultList(int firstIndex, int resultCount) { if (reportMaxCount != null && firstIndex + resultCount > reportMaxCount.intValue()) { resultCount = reportMaxCount.intValue() - firstIndex; } query.setFirstResult(firstIndex); query.setMaxResults(resultCount); return query.getResultList(); } }