diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/AbstractMapComponentNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/AbstractMapComponentNode.java index cebf10d654..361525da11 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/AbstractMapComponentNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/AbstractMapComponentNode.java @@ -24,6 +24,7 @@ import antlr.collections.AST; * @author Steve Ebersole */ public abstract class AbstractMapComponentNode extends FromReferenceNode implements HqlSqlTokenTypes { + private FromElement mapFromElement; private String[] columns; public FromReferenceNode getMapReference() { @@ -72,6 +73,8 @@ public abstract class AbstractMapComponentNode extends FromReferenceNode impleme throw nonMap(); } + mapFromElement = sourceFromElement; + setFromElement( sourceFromElement ); setDataType( resolveType( sourceFromElement.getQueryableCollection() ) ); this.columns = resolveColumns( sourceFromElement.getQueryableCollection() ); @@ -79,6 +82,10 @@ public abstract class AbstractMapComponentNode extends FromReferenceNode impleme setFirstChild( null ); } + public FromElement getMapFromElement() { + return mapFromElement; + } + private boolean isAliasRef(FromReferenceNode mapReference) { return ALIAS_REF == mapReference.getType(); } @@ -107,4 +114,19 @@ public abstract class AbstractMapComponentNode extends FromReferenceNode impleme public void resolveIndex(AST parent) throws SemanticException { throw new UnsupportedOperationException( expressionDescription() + " expression cannot be the source for an index operation" ); } + + protected MapKeyEntityFromElement findOrAddMapKeyEntityFromElement(QueryableCollection collectionPersister) { + if ( !collectionPersister.getIndexType().isEntityType() ) { + return null; + } + + + for ( FromElement destination : getFromElement().getDestinations() ) { + if ( destination instanceof MapKeyEntityFromElement ) { + return (MapKeyEntityFromElement) destination; + } + } + + return MapKeyEntityFromElement.buildKeyJoin( getFromElement() ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java index 0aeec2f06c..1710441e22 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java @@ -7,6 +7,7 @@ package org.hibernate.hql.internal.ast.tree; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -64,7 +65,7 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa private boolean initialized; private FromElementType elementType; private boolean useWhereFragment = true; - private List destinations = new LinkedList(); + private List destinations; private boolean manyToMany; private String withClauseFragment; private String withClauseJoinAlias; @@ -218,6 +219,14 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa return elementType.renderPropertySelect( size, k, isAllPropertyFetch ); } + public String renderMapKeyPropertySelectFragment(int size, int k) { + return elementType.renderMapKeyPropertySelectFragment( size, k ); + } + + public String renderMapEntryPropertySelectFragment(int size, int k) { + return elementType.renderMapEntryPropertySelectFragment( size, k ); + } + String renderCollectionSelectFragment(int size, int k) { return elementType.renderCollectionSelectFragment( size, k ); } @@ -415,11 +424,19 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa } private void addDestination(FromElement fromElement) { + if ( destinations == null ) { + destinations = new LinkedList(); + } destinations.add( fromElement ); } - public List getDestinations() { - return destinations; + public List getDestinations() { + if ( destinations == null ) { + return Collections.emptyList(); + } + else { + return destinations; + } } public FromElement getOrigin() { diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementType.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementType.java index 48da3d4810..63ce831e96 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementType.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementType.java @@ -14,10 +14,13 @@ import java.util.Set; import org.hibernate.MappingException; import org.hibernate.QueryException; import org.hibernate.engine.internal.JoinSequence; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.hql.internal.CollectionProperties; import org.hibernate.hql.internal.CollectionSubqueryFactory; import org.hibernate.hql.internal.NameGenerator; import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes; +import org.hibernate.hql.internal.ast.HqlSqlWalker; +import org.hibernate.hql.internal.ast.util.SessionFactoryHelper; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.log.DeprecationLogger; @@ -215,6 +218,46 @@ class FromElementType { } } + public String renderMapKeyPropertySelectFragment(int size, int k) { + if ( persister == null ) { + throw new IllegalStateException( "Unexpected state in call to renderMapKeyPropertySelectFragment" ); + } + + final String fragment = ( (Queryable) persister ).propertySelectFragment( + getTableAlias(), + getSuffix( size, k ), + false + ); + return trimLeadingCommaAndSpaces( fragment ); + +// if ( queryableCollection == null +// || !Map.class.isAssignableFrom( queryableCollection.getCollectionType().getReturnedClass() ) ) { +// throw new IllegalStateException( "Illegal call to renderMapKeyPropertySelectFragment() when FromElement is not a Map" ); +// } +// +// if ( !queryableCollection.getIndexType().isEntityType() ) { +// return null; +// } +// +// final HqlSqlWalker walker = fromElement.getWalker(); +// final SessionFactoryHelper sfh = walker.getSessionFactoryHelper(); +// final SessionFactoryImplementor sf = sfh.getFactory(); +// +// final EntityType indexEntityType = (EntityType) queryableCollection.getIndexType(); +// final EntityPersister indexEntityPersister = (EntityPersister) indexEntityType.getAssociatedJoinable( sf ); +// +// final String fragment = ( (Queryable) indexEntityPersister ).propertySelectFragment( +// getTableAlias(), +// getSuffix( size, k ), +// false +// ); +// return trimLeadingCommaAndSpaces( fragment ); + } + + public String renderMapEntryPropertySelectFragment(int size, int k) { + return null; + } + String renderCollectionSelectFragment(int size, int k) { if ( queryableCollection == null ) { return ""; diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MapEntryNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MapEntryNode.java index 784a2a5d88..6f68763b20 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MapEntryNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MapEntryNode.java @@ -95,13 +95,11 @@ public class MapEntryNode extends AbstractMapComponentNode implements Aggregated AliasGenerator aliasGenerator = new LocalAliasGenerator( 0 ); appendSelectExpressions( collectionPersister.getIndexColumnNames(), selections, aliasGenerator ); Type keyType = collectionPersister.getIndexType(); - if ( keyType.isAssociationType() ) { - EntityType entityType = (EntityType) keyType; - Queryable keyEntityPersister = (Queryable) sfi().getEntityPersister( - entityType.getAssociatedEntityName( sfi() ) - ); + if ( keyType.isEntityType() ) { + MapKeyEntityFromElement mapKeyEntityFromElement = findOrAddMapKeyEntityFromElement( collectionPersister ); + Queryable keyEntityPersister = mapKeyEntityFromElement.getQueryable(); SelectFragment fragment = keyEntityPersister.propertySelectFragmentFragment( - collectionTableAlias(), + mapKeyEntityFromElement.getTableAlias(), null, false ); diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MapKeyEntityFromElement.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MapKeyEntityFromElement.java new file mode 100644 index 0000000000..76f3d20658 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MapKeyEntityFromElement.java @@ -0,0 +1,101 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.hql.internal.ast.tree; + +import org.hibernate.engine.internal.JoinSequence; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes; +import org.hibernate.hql.internal.ast.HqlSqlWalker; +import org.hibernate.hql.internal.ast.util.SessionFactoryHelper; +import org.hibernate.persister.collection.QueryableCollection; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Joinable; +import org.hibernate.sql.JoinType; +import org.hibernate.type.EntityType; +import org.hibernate.type.Type; + +/** + * @author Steve Ebersole + */ +public class MapKeyEntityFromElement extends FromElement { + private final boolean useThetaJoin; + + public MapKeyEntityFromElement(boolean useThetaJoin) { + super(); + this.useThetaJoin = useThetaJoin; + } + + @Override + public boolean isImplied() { + return useThetaJoin; + } + + @Override + public int getType() { + return useThetaJoin ? HqlSqlTokenTypes.FROM_FRAGMENT : HqlSqlTokenTypes.JOIN_FRAGMENT; + } + + public static MapKeyEntityFromElement buildKeyJoin(FromElement collectionFromElement) { + final HqlSqlWalker walker = collectionFromElement.getWalker(); + final SessionFactoryHelper sfh = walker.getSessionFactoryHelper(); + final SessionFactoryImplementor sf = sfh.getFactory(); + + final QueryableCollection collectionPersister = collectionFromElement.getQueryableCollection(); + final Type indexType = collectionPersister.getIndexType(); + if ( indexType == null ) { + throw new IllegalArgumentException( "Given collection is not indexed" ); + } + if ( !indexType.isEntityType() ) { + throw new IllegalArgumentException( "Given collection does not have an entity index" ); + } + + final EntityType indexEntityType = (EntityType) indexType; + final EntityPersister indexEntityPersister = (EntityPersister) indexEntityType.getAssociatedJoinable( sf ); + + final String rhsAlias = walker.getAliasGenerator().createName( indexEntityPersister.getEntityName() ); + final boolean useThetaJoin = collectionFromElement.getJoinSequence().isThetaStyle(); + + MapKeyEntityFromElement join = new MapKeyEntityFromElement( useThetaJoin ); + join.initialize( HqlSqlTokenTypes.JOIN_FRAGMENT, ( (Joinable) indexEntityPersister ).getTableName() ); + join.initialize( collectionFromElement.getWalker() ); + + join.initializeEntity( + collectionFromElement.getFromClause(), + indexEntityPersister.getEntityName(), + indexEntityPersister, + indexEntityType, + "", + rhsAlias + ); + +// String[] joinColumns = determineJoinColuns( collectionPersister, joinTableAlias ); + // todo : assumes columns, no formulas + String[] joinColumns = collectionPersister.getIndexColumnNames( collectionFromElement.getCollectionTableAlias() ); + + JoinSequence joinSequence = sfh.createJoinSequence( + useThetaJoin, + indexEntityType, + rhsAlias, + // todo : ever a time when INNER is appropriate? + //JoinType.LEFT_OUTER_JOIN, + // needs to be an inner join because of how JoinSequence/JoinFragment work - ugh + JoinType.INNER_JOIN, + joinColumns + ); + join.setJoinSequence( joinSequence ); + + join.setOrigin( collectionFromElement, collectionPersister.isManyToMany() ); + join.setColumns( joinColumns ); + + join.setUseFromFragment( collectionFromElement.useFromFragment() ); + join.setUseWhereFragment( collectionFromElement.useWhereFragment() ); + + walker.addQuerySpaces( indexEntityPersister.getQuerySpaces() ); + + return join; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MapKeyNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MapKeyNode.java index 061bd61a1e..8046d2a32a 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MapKeyNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MapKeyNode.java @@ -15,6 +15,8 @@ import org.hibernate.type.Type; * @author Steve Ebersole */ public class MapKeyNode extends AbstractMapComponentNode { + private MapKeyEntityFromElement mapKeyEntityFromElement; + @Override protected String expressionDescription() { return "key(*)"; @@ -22,7 +24,12 @@ public class MapKeyNode extends AbstractMapComponentNode { @Override protected String[] resolveColumns(QueryableCollection collectionPersister) { - final FromElement fromElement = getFromElement(); + this.mapKeyEntityFromElement = findOrAddMapKeyEntityFromElement( collectionPersister ); + if ( mapKeyEntityFromElement != null ) { + setFromElement( mapKeyEntityFromElement ); + } + + final FromElement fromElement = getMapFromElement(); return fromElement.toColumns( fromElement.getCollectionTableAlias(), "index", // the JPA KEY "qualifier" is the same concept as the HQL INDEX function/property @@ -34,4 +41,8 @@ public class MapKeyNode extends AbstractMapComponentNode { protected Type resolveType(QueryableCollection collectionPersister) { return collectionPersister.getIndexType(); } + + public MapKeyEntityFromElement getMapKeyEntityFromElement() { + return mapKeyEntityFromElement; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java index 3786fb25da..53bf6b8043 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java @@ -432,7 +432,7 @@ public class SelectClause extends SelectExpressionList { if ( !selectExpressions[i].isScalar() ) { FromElement fromElement = selectExpressions[i].getFromElement(); if ( fromElement != null ) { - renderNonScalarProperties( appender, fromElement, nonscalarSize, k ); + renderNonScalarProperties( appender, selectExpressions[i], fromElement, nonscalarSize, k ); k++; } } @@ -446,13 +446,24 @@ public class SelectClause extends SelectExpressionList { int j, SelectExpression expr, ASTAppender appender) { - String text = fromElement.renderIdentifierSelect( nonscalarSize, j ); if ( !fromElement.getFromClause().isSubQuery() ) { if ( !scalarSelect && !getWalker().isShallowQuery() ) { - //TODO: is this a bit ugly? +// // todo : ugh this is all fugly code +// if ( expr instanceof MapKeyNode ) { +// // don't over-write node text +// } +// else if ( expr instanceof MapEntryNode ) { +// // don't over-write node text +// } +// else { +// String text = fromElement.renderIdentifierSelect( nonscalarSize, j ); +// expr.setText( text ); +// } + String text = fromElement.renderIdentifierSelect( nonscalarSize, j ); expr.setText( text ); } else { + String text = fromElement.renderIdentifierSelect( nonscalarSize, j ); if (! alreadyRenderedIdentifiers.contains(text)) { appender.append( SqlTokenTypes.SQL_TOKEN, text, false ); alreadyRenderedIdentifiers.add(text); @@ -461,21 +472,43 @@ public class SelectClause extends SelectExpressionList { } } - private void renderNonScalarProperties(ASTAppender appender, FromElement fromElement, int nonscalarSize, int k) { - String text = fromElement.renderPropertySelect( nonscalarSize, k ); - appender.append( SqlTokenTypes.SQL_TOKEN, text, false ); - if ( fromElement.getQueryableCollection() != null && fromElement.isFetch() ) { - text = fromElement.renderCollectionSelectFragment( nonscalarSize, k ); - appender.append( SqlTokenTypes.SQL_TOKEN, text, false ); + private void renderNonScalarProperties( + ASTAppender appender, + SelectExpression selectExpression, + FromElement fromElement, + int nonscalarSize, + int k) { + final String text; + if ( selectExpression instanceof MapKeyNode ) { + final MapKeyNode mapKeyNode = (MapKeyNode) selectExpression; + if ( mapKeyNode.getMapKeyEntityFromElement() != null ) { + text = mapKeyNode.getMapKeyEntityFromElement().renderMapKeyPropertySelectFragment( nonscalarSize, k ); + } + else { + text = fromElement.renderPropertySelect( nonscalarSize, k ); + } } + else if ( selectExpression instanceof MapEntryNode ) { + text = fromElement.renderMapEntryPropertySelectFragment( nonscalarSize, k ); + } + else { + text = fromElement.renderPropertySelect( nonscalarSize, k ); + } + appender.append( SqlTokenTypes.SQL_TOKEN, text, false ); + + if ( fromElement.getQueryableCollection() != null && fromElement.isFetch() ) { + String subText1 = fromElement.renderCollectionSelectFragment( nonscalarSize, k ); + appender.append( SqlTokenTypes.SQL_TOKEN, subText1, false ); + } + // Look through the FromElement's children to find any collections of values that should be fetched... - ASTIterator iter = new ASTIterator( fromElement ); - while ( iter.hasNext() ) { - FromElement child = (FromElement) iter.next(); + ASTIterator itr = new ASTIterator( fromElement ); + while ( itr.hasNext() ) { + FromElement child = (FromElement) itr.next(); if ( child.isCollectionOfValuesOrComponents() && child.isFetch() ) { // Need a better way to define the suffixes here... - text = child.renderValueCollectionSelectFragment( nonscalarSize, nonscalarSize + k ); - appender.append( SqlTokenTypes.SQL_TOKEN, text, false ); + final String subText2 = child.renderValueCollectionSelectFragment( nonscalarSize, nonscalarSize + k ); + appender.append( SqlTokenTypes.SQL_TOKEN, subText2, false ); } } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/MapFunctionExpressionsTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/MapFunctionExpressionsTest.java index 67d616def4..211fd68ca9 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/MapFunctionExpressionsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/MapFunctionExpressionsTest.java @@ -7,30 +7,66 @@ package org.hibernate.test.hql; import java.util.HashMap; +import java.util.List; import java.util.Map; -import javax.persistence.Column; -import javax.persistence.ElementCollection; -import javax.persistence.Embeddable; import javax.persistence.Entity; -import javax.persistence.EnumType; import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.MapKeyColumn; -import javax.persistence.MapKeyEnumerated; +import javax.persistence.JoinTable; +import javax.persistence.MapKeyJoinColumn; import javax.persistence.OneToMany; import javax.persistence.Table; import org.hibernate.Session; +import org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Before; import org.junit.Test; +import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; +import static org.junit.Assert.assertEquals; + /** * Test originally written to help verify/diagnose HHH-10125 * * @author Steve Ebersole */ public class MapFunctionExpressionsTest extends BaseNonConfigCoreFunctionalTestCase { + private final ASTQueryTranslatorFactory queryTranslatorFactory = new ASTQueryTranslatorFactory(); + + @Before + public void prepareTestData() { + Session s = openSession(); + s.getTransaction().begin(); + + AddressType homeType = new AddressType( 1, "home" ); + s.persist( homeType ); + + Address address = new Address( 1, "Main St.", "Somewhere, USA" ); + s.persist( address ); + + Contact contact = new Contact( 1, "John" ); + contact.addresses.put( homeType, address ); + s.persist( contact ); + + s.getTransaction().commit(); + s.close(); + } + + @After + public void cleanUpTestData() { + Session s = openSession(); + s.getTransaction().begin(); + + s.delete( s.get( Contact.class, 1 ) ); + + s.delete( s.get( Address.class, 1 ) ); + s.delete( s.get( AddressType.class, 1 ) ); + + s.getTransaction().commit(); + s.close(); + } @Test public void testMapKeyExpressionInWhere() { @@ -39,10 +75,17 @@ public class MapFunctionExpressionsTest extends BaseNonConfigCoreFunctionalTestC Session s = openSession(); s.getTransaction().begin(); + // JPA form - s.createQuery( "from Contact c join c.addresses a where key(a) = 'HOME'" ).list(); + List contacts = s.createQuery( "select c from Contact c join c.addresses a where key(a) is not null" ).list(); + assertEquals( 1, contacts.size() ); + Contact contact = assertTyping( Contact.class, contacts.get( 0 ) ); + // Hibernate additional form - s.createQuery( "from Contact c where key(c.addresses) = 'HOME'" ).list(); + contacts = s.createQuery( "select c from Contact c where key(c.addresses) is not null" ).list(); + assertEquals( 1, contacts.size() ); + contact = assertTyping( Contact.class, contacts.get( 0 ) ); + s.getTransaction().commit(); s.close(); } @@ -54,10 +97,17 @@ public class MapFunctionExpressionsTest extends BaseNonConfigCoreFunctionalTestC Session s = openSession(); s.getTransaction().begin(); + // JPA form - s.createQuery( "select key(a) from Contact c join c.addresses a" ).list(); + List types = s.createQuery( "select key(a) from Contact c join c.addresses a" ).list(); + assertEquals( 1, types.size() ); + assertTyping( AddressType.class, types.get( 0 ) ); + // Hibernate additional form - s.createQuery( "select key(c.addresses) from Contact c" ).list(); + types = s.createQuery( "select key(c.addresses) from Contact c" ).list(); + assertEquals( 1, types.size() ); + assertTyping( AddressType.class, types.get( 0 ) ); + s.getTransaction().commit(); s.close(); } @@ -66,21 +116,55 @@ public class MapFunctionExpressionsTest extends BaseNonConfigCoreFunctionalTestC public void testMapValueExpressionInSelect() { Session s = openSession(); s.getTransaction().begin(); - s.createQuery( "select value(a) from Contact c join c.addresses a" ).list(); - s.createQuery( "select value(c.addresses) from Contact c" ).list(); + + List addresses = s.createQuery( "select value(a) from Contact c join c.addresses a" ).list(); + assertEquals( 1, addresses.size() ); + assertTyping( Address.class, addresses.get( 0 ) ); + + addresses = s.createQuery( "select value(c.addresses) from Contact c" ).list(); + assertEquals( 1, addresses.size() ); + assertTyping( Address.class, addresses.get( 0 ) ); + + s.getTransaction().commit(); + s.close(); + } + + @Test + public void testMapEntryExpressionInSelect() { + Session s = openSession(); + s.getTransaction().begin(); + + List addresses = s.createQuery( "select entry(a) from Contact c join c.addresses a" ).list(); + assertEquals( 1, addresses.size() ); + assertTyping( Map.Entry.class, addresses.get( 0 ) ); + + addresses = s.createQuery( "select entry(c.addresses) from Contact c" ).list(); + assertEquals( 1, addresses.size() ); + assertTyping( Map.Entry.class, addresses.get( 0 ) ); + s.getTransaction().commit(); s.close(); } @Override protected Class[] getAnnotatedClasses() { - return new Class[] { Address.class, Contact.class }; + return new Class[] { Address.class, AddressType.class, Contact.class }; } - public static enum AddressType { - HOME, - WORK, - BUSINESS + @Entity(name = "AddressType") + @Table(name = "address_type") + public static class AddressType { + @Id + public Integer id; + String name; + + public AddressType() { + } + + public AddressType(Integer id, String name) { + this.id = id; + this.name = name; + } } @Entity(name = "Address") @@ -91,6 +175,15 @@ public class MapFunctionExpressionsTest extends BaseNonConfigCoreFunctionalTestC public Integer id; String street; String city; + + public Address() { + } + + public Address(Integer id, String street, String city) { + this.id = id; + this.street = street; + this.city = city; + } } @Entity(name = "Contact") @@ -100,10 +193,20 @@ public class MapFunctionExpressionsTest extends BaseNonConfigCoreFunctionalTestC public Integer id; String name; @OneToMany - @JoinColumn + @JoinTable(name = "contact_address") + @MapKeyJoinColumn(name="address_type_id", referencedColumnName="id") +// @JoinColumn // @ElementCollection - @MapKeyEnumerated(EnumType.STRING) - @MapKeyColumn(name = "addr_type") +// @MapKeyEnumerated(EnumType.STRING) +// @MapKeyColumn(name = "addr_type") Map addresses = new HashMap(); + + public Contact() { + } + + public Contact(Integer id, String name) { + this.id = id; + this.name = name; + } } }