From 694a694d5d4826b55fdd0aac622bf6ff78bf7fed Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 16 Oct 2009 00:50:22 +0000 Subject: [PATCH] HHH-4203 - Implement JPA 2.0 criteria apis (compiling) git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@17772 1b8cb986-b30d-0410-93ca-fae66ebed9b2 --- .../ejb/AbstractEntityManagerImpl.java | 56 +++++------------ .../java/org/hibernate/ejb/QueryImpl.java | 12 ++-- .../ejb/criteria/CriteriaQueryCompiler.java | 61 +++++++++++++++++++ .../ejb/criteria/CriteriaQueryImpl.java | 40 ++++++++++++ 4 files changed, 122 insertions(+), 47 deletions(-) create mode 100644 entitymanager/src/main/java/org/hibernate/ejb/criteria/CriteriaQueryCompiler.java diff --git a/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java b/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java index d04e898234..02fa0c989c 100755 --- a/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java @@ -42,7 +42,7 @@ import javax.persistence.Query; import javax.persistence.TransactionRequiredException; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.QueryBuilder; +import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.metamodel.Metamodel; import javax.persistence.spi.PersistenceUnitTransactionType; import javax.transaction.Status; @@ -72,6 +72,7 @@ import org.hibernate.UnresolvableObjectException; import org.hibernate.cfg.Environment; import org.hibernate.ejb.transaction.JoinableCMTTransaction; import org.hibernate.ejb.util.ConfigurationHelper; +import org.hibernate.ejb.criteria.CriteriaQueryCompiler; import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.SessionImplementor; import org.hibernate.proxy.HibernateProxy; @@ -148,38 +149,13 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage } } - public TypedQuery createQuery(CriteriaQuery criteriaQuery) { - // TODO-STEVE : here is the interpretation/compilation portion. - // One option is to build on top of the existing - // org.hibernate.loader.custom.CustomQuery infastructure - // (which is how native sql queries are implemented e.g.). - // If so, then here we could interpret the criteria into - // a CustomQuery instance which is passed into the - // Query instance returned here. We would then call into - // the various SessionImplementor methods for execution - // such as #listCustomQuery and #scrollCustomQuery. - // - // The drawback to this (^^) approach is that CustomQuery + - // SessionImplementor combo does not support #executeUpdate - // processing... - throw new UnsupportedOperationException( "Not yet implemented!" ); - } + private CriteriaQueryCompiler criteriaQueryCompiler; - public TypedQuery createQuery(CriteriaQuery criteriaQuery, Class resultClass) { - // TODO-STEVE : here is the interpretation/compilation portion. - // One option is to build on top of the existing - // org.hibernate.loader.custom.CustomQuery infastructure - // (which is how native sql queries are implemented e.g.). - // If so, then here we could interpret the criteria into - // a CustomQuery instance which is passed into the - // Query instance returned here. We would then call into - // the various SessionImplementor methods for execution - // such as #listCustomQuery and #scrollCustomQuery. - // - // The drawback to this (^^) approach is that CustomQuery + - // SessionImplementor combo does not support #executeUpdate - // processing... - throw new UnsupportedOperationException( "Not yet implemented!" ); + public TypedQuery createQuery(CriteriaQuery criteriaQuery) { + if ( criteriaQueryCompiler == null ) { + criteriaQueryCompiler = new CriteriaQueryCompiler( this ); + } + return criteriaQueryCompiler.compile( criteriaQuery ); } public Query createNamedQuery(String name) { @@ -481,8 +457,8 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage /** * {@inheritDoc} */ - public QueryBuilder getQueryBuilder() { - return getEntityManagerFactory().getQueryBuilder(); + public CriteriaBuilder getCriteriaBuilder() { + return getEntityManagerFactory().getCriteriaBuilder(); } /** @@ -672,13 +648,13 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage } catch ( SystemException se ) { log.error( "could not determine transaction status", se ); - //throwPersistenceException will mark the transaction as rollbacked - throwPersistenceException( - new PersistenceException( - "could not determine transaction status in beforeCompletion()", - se - ) + PersistenceException pe = new PersistenceException( + "could not determine transaction status in beforeCompletion()", + se ); + // handlePersistenceException will mark the transaction as rollbacked + handlePersistenceException( pe ); + throw pe; } catch ( HibernateException he ) { throwPersistenceException( he ); diff --git a/entitymanager/src/main/java/org/hibernate/ejb/QueryImpl.java b/entitymanager/src/main/java/org/hibernate/ejb/QueryImpl.java index 82dfaee354..81c8ce0a02 100755 --- a/entitymanager/src/main/java/org/hibernate/ejb/QueryImpl.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/QueryImpl.java @@ -21,8 +21,6 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ - -//$Id$ package org.hibernate.ejb; import java.util.Calendar; @@ -150,7 +148,7 @@ public class QueryImpl extends org.hibernate.ejb.AbstractQueryImpl impleme return position; } - public Class getJavaType() { + public Class getParameterType() { return javaType; } } @@ -521,10 +519,10 @@ public class QueryImpl extends org.hibernate.ejb.AbstractQueryImpl impleme @SuppressWarnings({ "unchecked" }) public Parameter getParameter(String name, Class type) { Parameter param = getParameter( name ); - if ( param.getJavaType() != null ) { + if ( param.getParameterType() != null ) { // we were able to determine the expected type during analysis, so validate it here throw new IllegalArgumentException( - "Parameter type [" + param.getJavaType().getName() + + "Parameter type [" + param.getParameterType().getName() + "] is not assignment compatible with requested type [" + type.getName() + "]" ); @@ -538,10 +536,10 @@ public class QueryImpl extends org.hibernate.ejb.AbstractQueryImpl impleme @SuppressWarnings({ "unchecked" }) public Parameter getParameter(int position, Class type) { Parameter param = getParameter( position ); - if ( param.getJavaType() != null ) { + if ( param.getParameterType() != null ) { // we were able to determine the expected type during analysis, so validate it here throw new IllegalArgumentException( - "Parameter type [" + param.getJavaType().getName() + + "Parameter type [" + param.getParameterType().getName() + "] is not assignment compatible with requested type [" + type.getName() + "]" ); diff --git a/entitymanager/src/main/java/org/hibernate/ejb/criteria/CriteriaQueryCompiler.java b/entitymanager/src/main/java/org/hibernate/ejb/criteria/CriteriaQueryCompiler.java new file mode 100644 index 0000000000..09d11a11e7 --- /dev/null +++ b/entitymanager/src/main/java/org/hibernate/ejb/criteria/CriteriaQueryCompiler.java @@ -0,0 +1,61 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by + * third-party contributors as indicated by either @author tags or express + * copyright attribution statements applied by the authors. All + * third-party contributions are distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program 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 this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.ejb.criteria; + +import java.util.Set; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.ParameterExpression; + +import org.hibernate.ejb.HibernateEntityManagerImplementor; + +/** + * Compiles a JPA criteria query into an executable {@link TypedQuery}. Its single contract is the {@link #compile} + * method. + *

+ * NOTE : This is a temporay implementation which simply translates the criteria query into a JPAQL query string. A + * better, long-term solution is being implemented as part of refactoring the JPAQL/HQL translator. + * + * @author Steve Ebersole + */ +public class CriteriaQueryCompiler { + private final HibernateEntityManagerImplementor entityManager; + + public CriteriaQueryCompiler(HibernateEntityManagerImplementor entityManager) { + this.entityManager = entityManager; + } + + public TypedQuery compile(CriteriaQuery criteriaQuery) { + CriteriaQueryImpl criteriaQueryImpl = ( CriteriaQueryImpl ) criteriaQuery; + + criteriaQueryImpl.validate(); + Set> explicitParameters = criteriaQueryImpl.getParameters(); + // todo : implicit parameter handling (handling literal as param, etc). + String jpaqlEquivalent = criteriaQueryImpl.render(); + + + + return null; + } +} diff --git a/entitymanager/src/main/java/org/hibernate/ejb/criteria/CriteriaQueryImpl.java b/entitymanager/src/main/java/org/hibernate/ejb/criteria/CriteriaQueryImpl.java index a1bc1d9b69..5c5eb21050 100644 --- a/entitymanager/src/main/java/org/hibernate/ejb/criteria/CriteriaQueryImpl.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/criteria/CriteriaQueryImpl.java @@ -293,4 +293,44 @@ public class CriteriaQueryImpl extends AbstractNode implements CriteriaQuery< return queryStructure.subquery( subqueryType ); } + public void validate() { + // getRoots() is explicitly supposed to return empty if none defined, no need to check for null + if ( getRoots().isEmpty() ) { + throw new IllegalStateException( "No criteria query roots were specified" ); + } + + // if there is not an explicit selection, there is an *implicit* selection of the root entity provided only + // a single query root was defined. + if ( getSelection() == null && !hasImplicitSelection() ) { + throw new IllegalStateException( "No explicit selection and an implicit one cold not be determined" ); + } + } + + /** + * If no explicit selection was defined, we have a condition called an implicit selection if the query specified + * a single {@link Root} and the java type of that {@link Root root's} model is the same as this criteria's + * {@link #getResultType() result type}. + * + * @return True if there is an explicit selection; false otherwise. + */ + private boolean hasImplicitSelection() { + if ( getRoots().size() != 1 ) { + return false; + } + + Root root = getRoots().iterator().next(); + if ( root.getModel().getJavaType() != returnType ) { + return false; + } + + // if we get here, the query defined no selection but defined a single root of the same type as the + // criteria query return, so we use that as the implicit selection + // + // todo : should we put an implicit marker in the selection to this fact to make later processing easier? + return true; + } + + public String render() { + return null; + } }