[hibernate-commits] Hibernate SVN: r18006 - in core/trunk: core/src/main/java/org/hibernate/hql/ast/tree and 2 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed Nov 18 16:02:19 EST 2009


Author: steve.ebersole at jboss.com
Date: 2009-11-18 16:02:19 -0500 (Wed, 18 Nov 2009)
New Revision: 18006

Added:
   core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/ComponentJoin.java
   core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/ql/
   core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/ql/ComponentJoinsTest.java
Modified:
   core/trunk/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElement.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElementFactory.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElementType.java
Log:
HHH-4584 - Query Language needs to support joins on embedded values


Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java	2009-11-18 16:56:13 UTC (rev 18005)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java	2009-11-18 21:02:19 UTC (rev 18006)
@@ -69,6 +69,7 @@
 import org.hibernate.hql.ast.tree.UpdateStatement;
 import org.hibernate.hql.ast.tree.OperatorNode;
 import org.hibernate.hql.ast.tree.ParameterContainer;
+import org.hibernate.hql.ast.tree.FromElementFactory;
 import org.hibernate.hql.ast.util.ASTPrinter;
 import org.hibernate.hql.ast.util.ASTUtil;
 import org.hibernate.hql.ast.util.AliasGenerator;
@@ -92,6 +93,7 @@
 import org.hibernate.type.Type;
 import org.hibernate.type.VersionType;
 import org.hibernate.type.DbTimestampType;
+import org.hibernate.type.ComponentType;
 import org.hibernate.usertype.UserVersionType;
 import org.hibernate.util.ArrayHelper;
 import org.hibernate.util.StringHelper;
@@ -362,14 +364,29 @@
 		// Generate an explicit join for the root dot node.   The implied joins will be collected and passed up
 		// to the root dot node.
 		dot.resolve( true, false, alias == null ? null : alias.getText() );
-		FromElement fromElement = dot.getImpliedJoin();
-		fromElement.setAllPropertyFetch(propertyFetch!=null);
 
-		if ( with != null ) {
-			if ( fetch ) {
-				throw new SemanticException( "with-clause not allowed on fetched associations; use filters" );
+		final FromElement fromElement;
+		if ( dot.getDataType() != null && dot.getDataType().isComponentType() ) {
+			FromElementFactory factory = new FromElementFactory( 
+					getCurrentFromClause(),
+					dot.getLhs().getFromElement(),
+					dot.getPropertyPath(),
+					alias == null ? null : alias.getText(),
+					null,
+					false
+			);
+			fromElement = factory.createComponentJoin( (ComponentType) dot.getDataType() );
+		}
+		else {
+			fromElement = dot.getImpliedJoin();
+			fromElement.setAllPropertyFetch( propertyFetch != null );
+
+			if ( with != null ) {
+				if ( fetch ) {
+					throw new SemanticException( "with-clause not allowed on fetched associations; use filters" );
+				}
+				handleWithFragment( fromElement, with );
 			}
-			handleWithFragment( fromElement, with );
 		}
 
 		if ( log.isDebugEnabled() ) {

Added: core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/ComponentJoin.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/ComponentJoin.java	                        (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/ComponentJoin.java	2009-11-18 21:02:19 UTC (rev 18006)
@@ -0,0 +1,181 @@
+/*
+ * 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.hql.ast.tree;
+
+import org.hibernate.type.ComponentType;
+import org.hibernate.type.Type;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.QueryException;
+import org.hibernate.util.StringHelper;
+import org.hibernate.hql.NameGenerator;
+
+/**
+ * Models an explicit join terminating at a component value (e.g. <tt>... from Person p join p.name as n ...</tt>)
+ *
+ * @author Steve Ebersole
+ */
+public class ComponentJoin extends FromElement {
+	private final String componentPath;
+	private final ComponentType componentType;
+
+	private final String componentProperty;
+	private final String columns;
+
+	public ComponentJoin(
+			FromClause fromClause,
+			FromElement origin,
+			String alias,
+			String componentPath,
+			ComponentType componentType) {
+		super( fromClause, origin, alias );
+		this.componentPath = componentPath;
+		this.componentType = componentType;
+		this.componentProperty = StringHelper.unqualify( componentPath );
+		fromClause.addJoinByPathMap( componentPath, this );
+		initializeComponentJoin( new ComponentFromElementType( this ) );
+
+		final String[] cols = origin.getPropertyMapping( "" ).toColumns( getTableAlias(), componentProperty );
+		StringBuffer buf = new StringBuffer();
+		for ( int j = 0; j < cols.length; j++ ) {
+			final String column = cols[j];
+			if ( j > 0 ) {
+				buf.append( ", " );
+			}
+			buf.append( column );
+		}
+		this.columns = buf.toString();
+	}
+
+	public String getComponentPath() {
+		return componentPath;
+	}
+
+	public String getComponentProperty() {
+		return componentProperty;
+	}
+
+	public ComponentType getComponentType() {
+		return componentType;
+	}
+
+
+	public Type getDataType() {
+		return getComponentType();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getIdentityColumn() {
+		// used to "resolve" the IdentNode when our alias is encountered *by itself* in the query; so
+		//		here we use the component
+		// NOTE : ^^ is true *except for* when encountered by itself in the SELECT clause.  That gets
+		// 		routed through org.hibernate.hql.ast.tree.ComponentJoin.ComponentFromElementType.renderScalarIdentifierSelect()
+		//		which we also override to account for
+		return columns;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getDisplayText() {
+		return "ComponentJoin{path=" + getComponentPath() + ", type=" + componentType.getReturnedClass() + "}";
+	}
+
+	public class ComponentFromElementType extends FromElementType {
+		private final PropertyMapping propertyMapping = new ComponentPropertyMapping();
+
+		public ComponentFromElementType(FromElement fromElement) {
+			super( fromElement );
+		}
+
+		public Type getDataType() {
+			return getComponentType();
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		public QueryableCollection getQueryableCollection() {
+			return null;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		public PropertyMapping getPropertyMapping(String propertyName) {
+			return propertyMapping;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		public Type getPropertyType(String propertyName, String propertyPath) {
+			int index = getComponentType().getPropertyIndex( propertyName );
+			return getComponentType().getSubtypes()[index];
+		}
+
+		public String renderScalarIdentifierSelect(int i) {
+			String[] cols = getBasePropertyMapping().toColumns( getTableAlias(), getComponentProperty() );
+			StringBuffer buf = new StringBuffer();
+			// For property references generate <tablealias>.<columnname> as <projectionalias>
+			for ( int j = 0; j < cols.length; j++ ) {
+				final String column = cols[j];
+				if ( j > 0 ) {
+					buf.append( ", " );
+				}
+				buf.append( column ).append( " as " ).append( NameGenerator.scalarName( i, j ) );
+			}
+			return buf.toString();
+		}
+	}
+
+	protected PropertyMapping getBasePropertyMapping() {
+		return getOrigin().getPropertyMapping( "" );
+	}
+
+	private final class ComponentPropertyMapping implements PropertyMapping {
+		public Type getType() {
+			return getComponentType();
+		}
+
+		public Type toType(String propertyName) throws QueryException {
+			return getBasePropertyMapping().toType( getPropertyPath( propertyName ) );
+		}
+
+		protected String getPropertyPath(String propertyName) {
+			return getComponentPath() + '.' + propertyName;
+		}
+
+		public String[] toColumns(String alias, String propertyName) throws QueryException {
+			return getBasePropertyMapping().toColumns( alias, getPropertyPath( propertyName ) );
+		}
+
+		public String[] toColumns(String propertyName) throws QueryException, UnsupportedOperationException {
+			return getBasePropertyMapping().toColumns( getPropertyPath( propertyName ) );
+		}
+	}
+}

Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElement.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElement.java	2009-11-18 16:56:13 UTC (rev 18005)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElement.java	2009-11-18 21:02:19 UTC (rev 18006)
@@ -90,6 +90,30 @@
 	public FromElement() {
 	}
 
+	/**
+	 * Constructor form used to initialize {@link ComponentJoin}
+	 *
+	 * @param fromClause The FROM clause to which this element belongs
+	 * @param origin The origin (LHS) of this element
+	 * @param alias The alias applied to this element
+	 */
+	protected FromElement(
+			FromClause fromClause,
+			FromElement origin,
+			String alias) {
+		this.fromClause = fromClause;
+		this.origin = origin;
+		this.classAlias = alias;
+		this.tableAlias = origin.getTableAlias();
+		super.initialize( fromClause.getWalker() );
+	}
+
+	protected void initializeComponentJoin(FromElementType elementType) {
+		this.elementType = elementType;
+		fromClause.registerFromElement( this );
+		initialized = true;
+	}
+
 	public String getCollectionSuffix() {
 		return elementType.getCollectionSuffix();
 	}

Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElementFactory.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElementFactory.java	2009-11-18 16:56:13 UTC (rev 18005)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElementFactory.java	2009-11-18 21:02:19 UTC (rev 18006)
@@ -39,6 +39,7 @@
 import org.hibernate.type.CollectionType;
 import org.hibernate.type.EntityType;
 import org.hibernate.type.Type;
+import org.hibernate.type.ComponentType;
 import org.hibernate.util.StringHelper;
 
 import antlr.ASTFactory;
@@ -53,7 +54,7 @@
  *
  * @author josh
  */
-class FromElementFactory implements SqlTokenTypes {
+public class FromElementFactory implements SqlTokenTypes {
 
 	private static final Logger log = LoggerFactory.getLogger( FromElementFactory.class );
 
@@ -295,6 +296,12 @@
 		return elem;
 	}
 
+	public FromElement createComponentJoin(ComponentType type) {
+		// need to create a "place holder" from-element that can store the component/alias for this
+		// 		component join
+		return new ComponentJoin( fromClause, origin, classAlias, path, type );
+	}
+
 	FromElement createElementJoin(QueryableCollection queryableCollection) throws SemanticException {
 		FromElement elem;
 

Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElementType.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElementType.java	2009-11-18 16:56:13 UTC (rev 18005)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElementType.java	2009-11-18 21:02:19 UTC (rev 18006)
@@ -74,6 +74,10 @@
 		}
 	}
 
+	protected FromElementType(FromElement fromElement) {
+		this.fromElement = fromElement;
+	}
+
 	private String getTableAlias() {
 		return fromElement.getTableAlias();
 	}

Added: core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/ql/ComponentJoinsTest.java
===================================================================
--- core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/ql/ComponentJoinsTest.java	                        (rev 0)
+++ core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/ql/ComponentJoinsTest.java	2009-11-18 21:02:19 UTC (rev 18006)
@@ -0,0 +1,57 @@
+/*
+ * 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.test.ql;
+
+import javax.persistence.EntityManager;
+
+import org.hibernate.ejb.test.TestCase;
+import org.hibernate.ejb.criteria.components.Client;
+
+/**
+ * Tests related to specifying joins on components (embedded values).
+ *
+ * @author Steve Ebersole
+ */
+public class ComponentJoinsTest extends TestCase {
+	public Class[] getAnnotatedClasses() {
+		return new Class[] { Client.class };
+	}
+
+	public void testComponentJoins() {
+		// Just checking proper query construction and syntax checking via database query parser...
+		EntityManager em = getOrCreateEntityManager();
+		em.getTransaction().begin();
+		// use it in WHERE
+		em.createQuery( "select c from Client c join c.name as n where n.lastName like '%'" ).getResultList();
+		// use it in SELECT
+		em.createQuery( "select n.lastName from Client c join c.name as n" ).getResultList();
+		em.createQuery( "select n from Client c join c.name as n" ).getResultList();
+		// use it in ORDER BY
+		em.createQuery( "select n from Client c join c.name as n order by n.lastName" ).getResultList();
+		em.createQuery( "select n from Client c join c.name as n order by c" ).getResultList();
+		em.createQuery( "select n from Client c join c.name as n order by n" ).getResultList();
+		em.getTransaction().commit();
+		em.close();
+	}
+}



More information about the hibernate-commits mailing list