From fc6f5154076ed261e11b44e044df79704163a916 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 24 Aug 2020 15:41:12 +0100 Subject: [PATCH] EntityValuedPathInterpretation fix TableGroup resolution for implicit join e.g or --- .../mapping/EntityValuedModelPart.java | 11 +- .../mapping/internal/AbstractDomainPath.java | 13 +- .../sqm/sql/BaseSqmToSqlAstConverter.java | 24 +- .../EntityValuedPathInterpretation.java | 123 ++++- .../PluralValuedSimplePathInterpretation.java | 4 +- .../{ => orm}/test/collection/map/Child.java | 2 +- .../test/collection/map/LocalizedString.java | 2 +- .../map/MapElementNullBasicTest.java | 109 ++--- .../test/collection/map/Mappings.hbm.xml | 2 +- .../collection/map/MultilingualString.java | 2 +- .../map/MultilingualStringParent.java | 2 +- .../{ => orm}/test/collection/map/Parent.java | 2 +- .../collection/map/PersistentMapTest.java | 455 ++++++++++++++++++ .../collection/map/PersistentMapTest.java | 406 ---------------- 14 files changed, 668 insertions(+), 489 deletions(-) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/collection/map/Child.java (93%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/collection/map/LocalizedString.java (93%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/collection/map/MapElementNullBasicTest.java (55%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/collection/map/Mappings.hbm.xml (92%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/collection/map/MultilingualString.java (96%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/collection/map/MultilingualStringParent.java (94%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/collection/map/Parent.java (95%) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/PersistentMapTest.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/test/collection/map/PersistentMapTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityValuedModelPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityValuedModelPart.java index 2450a9395a..1d5c9ffce9 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityValuedModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityValuedModelPart.java @@ -68,7 +68,10 @@ public interface EntityValuedModelPart extends FetchableContainer { Consumer action, Clause clause, TypeConfiguration typeConfiguration) { - getEntityMappingType().visitJdbcTypes( action, clause, typeConfiguration ); + getEntityMappingType().getAttributeMappings().forEach( + attributeMapping -> + attributeMapping.visitJdbcTypes( action, clause, typeConfiguration ) + ); } @Override @@ -91,6 +94,8 @@ public interface EntityValuedModelPart extends FetchableContainer { Clause clause, JdbcValuesConsumer consumer, SharedSessionContractImplementor session) { - getEntityMappingType().visitJdbcValues( value, clause, consumer, session ); - } + getEntityMappingType().getAttributeMappings().forEach( + attributeMapping -> + attributeMapping.visitJdbcValues( value, clause, consumer, session ) + ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java index 6636e42a33..82c580338a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java @@ -62,7 +62,7 @@ public abstract class AbstractDomainPath implements DomainPath { SessionFactoryImplementor sessionFactory, SqlExpressionResolver sqlExprResolver) { if ( referenceModelPart instanceof BasicValuedModelPart ) { - assSortSpecification( + addSortSpecification( (BasicValuedModelPart) referenceModelPart, ast, tableGroup, @@ -92,7 +92,7 @@ public abstract class AbstractDomainPath implements DomainPath { ); } else if ( referenceModelPart instanceof EmbeddableValuedModelPart ) { - assSortSpecification( + addSortSpecification( (EmbeddableValuedModelPart) referenceModelPart, ast, tableGroup, @@ -105,11 +105,12 @@ public abstract class AbstractDomainPath implements DomainPath { ); } else { - throw new NotYetImplementedFor6Exception( "Ordering for " + getReferenceModelPart() + "not yet supported" ); + // sure it can happen + throw new NotYetImplementedFor6Exception( "Ordering for " + getReferenceModelPart() + "not supported" ); } } - private void assSortSpecification( + private void addSortSpecification( EmbeddableValuedModelPart embeddableValuedModelPart, QuerySpec ast, TableGroup tableGroup, @@ -151,7 +152,7 @@ public abstract class AbstractDomainPath implements DomainPath { else { ModelPart subPart = embeddableValuedModelPart.findSubPart( modelPartName, null ); assert subPart instanceof BasicValuedModelPart; - assSortSpecification( + addSortSpecification( (BasicValuedModelPart) subPart, ast, tableGroup, @@ -162,7 +163,7 @@ public abstract class AbstractDomainPath implements DomainPath { } } - private void assSortSpecification( + private void addSortSpecification( BasicValuedModelPart basicValuedPart, QuerySpec ast, TableGroup tableGroup, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 06c61f2a57..ef100f7866 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -28,6 +28,7 @@ import org.hibernate.internal.util.collections.StandardStack; import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; +import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.PluralAttributeMapping; @@ -380,9 +381,7 @@ public abstract class BaseSqmToSqlAstConverter final List sqmCteColumns = sqmCteTable.getColumns(); final List sqlCteColumns = new ArrayList<>( sqmCteColumns.size() ); - for ( int i = 0; i < sqmCteColumns.size(); i++ ) { - final SqmCteTableColumn sqmCteTableColumn = sqmCteColumns.get( i ); - + for ( final SqmCteTableColumn sqmCteTableColumn : sqmCteColumns ) { sqlCteColumns.add( new CteColumn( sqmCteTableColumn.getColumnName(), @@ -1148,11 +1147,20 @@ public abstract class BaseSqmToSqlAstConverter SqmParameter expression, MappingModelExpressable valueMapping, Consumer jdbcParameterConsumer) { - valueMapping.visitJdbcTypes( - jdbcMapping -> jdbcParameterConsumer.accept( new JdbcParameterImpl( jdbcMapping ) ), - getCurrentClauseStack().getCurrent(), - getCreationContext().getDomainModel().getTypeConfiguration() - ); + if ( valueMapping instanceof EntityValuedModelPart ) { + ( (EntityValuedModelPart) valueMapping ).getEntityMappingType().getIdentifierMapping().visitJdbcTypes( + jdbcMapping -> jdbcParameterConsumer.accept( new JdbcParameterImpl( jdbcMapping ) ), + getCurrentClauseStack().getCurrent(), + getCreationContext().getDomainModel().getTypeConfiguration() + ); + } + else { + valueMapping.visitJdbcTypes( + jdbcMapping -> jdbcParameterConsumer.accept( new JdbcParameterImpl( jdbcMapping ) ), + getCurrentClauseStack().getCurrent(), + getCreationContext().getDomainModel().getTypeConfiguration() + ); + } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java index 78032c07f3..25ff4d7abe 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java @@ -6,11 +6,26 @@ */ package org.hibernate.query.sqm.sql.internal; +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.LockMode; import org.hibernate.metamodel.mapping.EntityValuedModelPart; +import org.hibernate.metamodel.mapping.ModelPart; +import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath; +import org.hibernate.sql.ast.Clause; +import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.spi.SqlAstCreationState; +import org.hibernate.sql.ast.spi.SqlExpressionResolver; +import org.hibernate.sql.ast.tree.expression.ColumnReference; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableGroupJoin; +import org.hibernate.sql.ast.tree.from.TableReference; /** * @author Koen Aers @@ -19,29 +34,131 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta public static EntityValuedPathInterpretation from( SqmEntityValuedSimplePath sqmPath, - SqlAstCreationState sqlAstCreationState) { + SqmToSqlAstConverter sqlAstCreationState) { final TableGroup tableGroup = sqlAstCreationState .getFromClauseAccess() .findTableGroup( sqmPath.getLhs().getNavigablePath() ); - final EntityValuedModelPart mapping = (EntityValuedModelPart) tableGroup - .getModelPart() + final EntityValuedModelPart mapping = (EntityValuedModelPart) tableGroup.getModelPart() .findSubPart( sqmPath.getReferencedPathSource().getPathName(), null ); + + SqlTuple sqlExpression = resolveSqlExpression( + sqmPath, + sqlAstCreationState, + tableGroup, + mapping + ); return new EntityValuedPathInterpretation<>( + sqlExpression, sqmPath, tableGroup, mapping ); } + private final Expression sqlExpression; + private EntityValuedPathInterpretation( + Expression sqlExpression, SqmEntityValuedSimplePath sqmPath, TableGroup tableGroup, EntityValuedModelPart mapping) { super( sqmPath, mapping, tableGroup ); + this.sqlExpression = sqlExpression; } @Override public void accept(SqlAstWalker sqlTreeWalker) { + sqlExpression.accept( sqlTreeWalker ); + } + private static SqlTuple resolveSqlExpression( + SqmEntityValuedSimplePath sqmPath, + SqmToSqlAstConverter sqlAstCreationState, + TableGroup tableGroup, + EntityValuedModelPart mapping) { + final List columnReferences = new ArrayList<>(); + + assert mapping instanceof ToOneAttributeMapping; + + final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) mapping; + final ModelPart modelPart = getModelPart( + sqlAstCreationState, + toOneAttributeMapping + ); + + modelPart.visitColumns( + (containingTableExpression, columnExpression, isFormula, customReadExpression, customWriteExpression, jdbcMapping) -> { + final TableReference tf; + final TableReference tableReference = getTableReference( + sqmPath, + sqlAstCreationState, + tableGroup, + toOneAttributeMapping, + containingTableExpression + ); + + final Expression columnReference = sqlAstCreationState.getSqlExpressionResolver() + .resolveSqlExpression( + SqlExpressionResolver.createColumnReferenceKey( + tableReference, + columnExpression + ), + sqlAstProcessingState -> new ColumnReference( + tableReference.getIdentificationVariable(), + columnExpression, + isFormula, + customReadExpression, + customWriteExpression, + jdbcMapping, + sqlAstCreationState.getCreationContext().getSessionFactory() + ) + ); + + columnReferences.add( (ColumnReference) columnReference ); + } + ); + + SqlTuple sqlExpression = new SqlTuple( columnReferences, mapping ); + return sqlExpression; + } + + private static ModelPart getModelPart( + SqmToSqlAstConverter sqlAstCreationState, + ToOneAttributeMapping toOneAttributeMapping) { + final Clause current = sqlAstCreationState.getCurrentClauseStack() + .getCurrent(); + final ModelPart modelPart; + if ( current == Clause.SELECT ) { + modelPart = toOneAttributeMapping.getAssociatedEntityMappingType().getIdentifierMapping(); + } + else { + modelPart = toOneAttributeMapping.getForeignKeyDescriptor(); + } + return modelPart; + } + + private static TableReference getTableReference( + SqmEntityValuedSimplePath sqmPath, + SqlAstCreationState sqlAstCreationState, + TableGroup tableGroup, + ToOneAttributeMapping toOneAttributeMapping, + String containingTableExpression) { + TableReference tableReference = tableGroup.getTableReference( containingTableExpression ); + if ( tableReference == null ) { + final TableGroupJoin tableGroupJoin = toOneAttributeMapping.createTableGroupJoin( + sqmPath.getNavigablePath(), + tableGroup, + null, + toOneAttributeMapping.isNullable() ? SqlAstJoinType.INNER : SqlAstJoinType.LEFT, + LockMode.NONE, + sqlAstCreationState + ); + sqlAstCreationState.getFromClauseAccess().registerTableGroup( + sqmPath.getNavigablePath(), + tableGroupJoin.getJoinedGroup() + ); + return tableGroupJoin.getJoinedGroup().getTableReference( containingTableExpression ); + } + return tableReference; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/PluralValuedSimplePathInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/PluralValuedSimplePathInterpretation.java index a7f4cde47a..239537de59 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/PluralValuedSimplePathInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/PluralValuedSimplePathInterpretation.java @@ -6,6 +6,7 @@ */ package org.hibernate.query.sqm.sql.internal; +import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath; @@ -45,8 +46,9 @@ public class PluralValuedSimplePathInterpretation extends AbstractSqmPathInte super(sqmPath, mapping, tableGroup); this.sqlExpression = sqlExpression; } + @Override public void accept(SqlAstWalker sqlTreeWalker) { - sqlExpression.accept( sqlTreeWalker ); + throw new NotYetImplementedFor6Exception( getClass() ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/collection/map/Child.java b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/Child.java similarity index 93% rename from hibernate-core/src/test/java/org/hibernate/test/collection/map/Child.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/Child.java index 929240b425..c49e694d1d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/collection/map/Child.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/Child.java @@ -4,7 +4,7 @@ * 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.test.collection.map; +package org.hibernate.orm.test.collection.map; /** diff --git a/hibernate-core/src/test/java/org/hibernate/test/collection/map/LocalizedString.java b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/LocalizedString.java similarity index 93% rename from hibernate-core/src/test/java/org/hibernate/test/collection/map/LocalizedString.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/LocalizedString.java index 6f8d1c3f9d..6a8b102b8e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/collection/map/LocalizedString.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/LocalizedString.java @@ -4,7 +4,7 @@ * 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.test.collection.map; +package org.hibernate.orm.test.collection.map; import javax.persistence.Embeddable; diff --git a/hibernate-core/src/test/java/org/hibernate/test/collection/map/MapElementNullBasicTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/MapElementNullBasicTest.java similarity index 55% rename from hibernate-core/src/test/java/org/hibernate/test/collection/map/MapElementNullBasicTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/MapElementNullBasicTest.java index 75726b890d..438a52f741 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/collection/map/MapElementNullBasicTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/MapElementNullBasicTest.java @@ -4,7 +4,7 @@ * 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.test.collection.map; +package org.hibernate.orm.test.collection.map; import java.util.HashMap; import java.util.List; @@ -17,28 +17,26 @@ import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.Table; -import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; -import org.junit.Test; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; -import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author Gail Badner */ -public class MapElementNullBasicTest extends BaseCoreFunctionalTestCase { - - @Override - protected Class[] getAnnotatedClasses() { - return new Class[] { - AnEntity.class - }; - } +@DomainModel( + annotatedClasses = MapElementNullBasicTest.AnEntity.class +) +@SessionFactory +public class MapElementNullBasicTest { @Test - public void testPersistNullValue() { - int entityId = doInHibernate( - this::sessionFactory, session -> { + public void testPersistNullValue(SessionFactoryScope scope) { + int entityId = scope.fromTransaction( + session -> { AnEntity e = new AnEntity(); e.aCollection.put( "null", null ); session.persist( e ); @@ -46,49 +44,49 @@ public class MapElementNullBasicTest extends BaseCoreFunctionalTestCase { } ); - doInHibernate( - this::sessionFactory, session -> { + scope.inTransaction( + session -> { AnEntity e = session.get( AnEntity.class, entityId ); assertEquals( 0, e.aCollection.size() ); - assertEquals( 0, getCollectionElementRows( entityId ).size() ); + assertEquals( 0, getCollectionElementRows( entityId, scope ).size() ); session.delete( e ); } ); } @Test - public void addNullValue() { - int entityId = doInHibernate( - this::sessionFactory, session -> { + public void addNullValue(SessionFactoryScope scope) { + int entityId = scope.fromTransaction( + session -> { AnEntity e = new AnEntity(); session.persist( e ); return e.id; } ); - doInHibernate( - this::sessionFactory, session -> { + scope.inTransaction( + session -> { AnEntity e = session.get( AnEntity.class, entityId ); assertEquals( 0, e.aCollection.size() ); - assertEquals( 0, getCollectionElementRows( entityId ).size() ); + assertEquals( 0, getCollectionElementRows( entityId, scope ).size() ); e.aCollection.put( "null", null ); } ); - doInHibernate( - this::sessionFactory, session -> { + scope.inTransaction( + session -> { AnEntity e = session.get( AnEntity.class, entityId ); assertEquals( 0, e.aCollection.size() ); - assertEquals( 0, getCollectionElementRows( entityId ).size() ); + assertEquals( 0, getCollectionElementRows( entityId, scope ).size() ); session.delete( e ); } ); } @Test - public void testUpdateNonNullValueToNull() { - int entityId = doInHibernate( - this::sessionFactory, session -> { + public void testUpdateNonNullValueToNull(SessionFactoryScope scope) { + int entityId = scope.fromTransaction( + session -> { AnEntity e = new AnEntity(); e.aCollection.put( "abc", "def" ); session.persist( e ); @@ -96,29 +94,28 @@ public class MapElementNullBasicTest extends BaseCoreFunctionalTestCase { } ); - doInHibernate( - this::sessionFactory, session -> { + scope.inTransaction( + session -> { AnEntity e = session.get( AnEntity.class, entityId ); assertEquals( 1, e.aCollection.size() ); - assertEquals( 1, getCollectionElementRows( entityId ).size() ); + assertEquals( 1, getCollectionElementRows( entityId, scope ).size() ); e.aCollection.put( "abc", null ); } ); - - doInHibernate( - this::sessionFactory, session -> { + scope.inTransaction( + session -> { AnEntity e = session.get( AnEntity.class, entityId ); assertEquals( 0, e.aCollection.size() ); - assertEquals( 0, getCollectionElementRows( entityId ).size() ); + assertEquals( 0, getCollectionElementRows( entityId, scope ).size() ); session.delete( e ); } ); } @Test - public void testUpdateNonNullValueToNullToNonNull() { - int entityId = doInHibernate( - this::sessionFactory, session -> { + public void testUpdateNonNullValueToNullToNonNull(SessionFactoryScope scope) { + int entityId = scope.fromTransaction( + session -> { AnEntity e = new AnEntity(); e.aCollection.put( "abc", "def" ); session.persist( e ); @@ -126,38 +123,38 @@ public class MapElementNullBasicTest extends BaseCoreFunctionalTestCase { } ); - doInHibernate( - this::sessionFactory, session -> { + scope.inTransaction( + session -> { AnEntity e = session.get( AnEntity.class, entityId ); assertEquals( 1, e.aCollection.size() ); - assertEquals( 1, getCollectionElementRows( entityId ).size() ); + assertEquals( 1, getCollectionElementRows( entityId, scope ).size() ); e.aCollection.put( "abc", null ); } ); - doInHibernate( - this::sessionFactory, session -> { + scope.inTransaction( + session -> { AnEntity e = session.get( AnEntity.class, entityId ); assertEquals( 0, e.aCollection.size() ); - assertEquals( 0, getCollectionElementRows( entityId ).size() ); + assertEquals( 0, getCollectionElementRows( entityId, scope ).size() ); e.aCollection.put( "abc", "not null" ); } ); - doInHibernate( - this::sessionFactory, session -> { + scope.inTransaction( + session -> { AnEntity e = session.get( AnEntity.class, entityId ); assertEquals( 1, e.aCollection.size() ); - assertEquals( 1, getCollectionElementRows( entityId ).size() ); + assertEquals( 1, getCollectionElementRows( entityId, scope ).size() ); assertEquals( "not null", e.aCollection.get( "abc" ) ); session.delete( e ); } ); } - private List getCollectionElementRows(int id) { - return doInHibernate( - this::sessionFactory, session -> { + private List getCollectionElementRows(int id, SessionFactoryScope scope) { + return scope.fromTransaction( + session -> { return session.createNativeQuery( "SELECT aCollection FROM AnEntity_aCollection where AnEntity_id = " + id ).list(); @@ -166,14 +163,14 @@ public class MapElementNullBasicTest extends BaseCoreFunctionalTestCase { } @Entity - @Table(name="AnEntity") + @Table(name = "AnEntity") public static class AnEntity { @Id @GeneratedValue private int id; @ElementCollection - @CollectionTable(name = "AnEntity_aCollection", joinColumns = { @JoinColumn( name = "AnEntity_id" ) }) - private Map aCollection = new HashMap(); + @CollectionTable(name = "AnEntity_aCollection", joinColumns = { @JoinColumn(name = "AnEntity_id") }) + private Map aCollection = new HashMap<>(); } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/collection/map/Mappings.hbm.xml b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/Mappings.hbm.xml similarity index 92% rename from hibernate-core/src/test/java/org/hibernate/test/collection/map/Mappings.hbm.xml rename to hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/Mappings.hbm.xml index a0815864db..65e409f27a 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/collection/map/Mappings.hbm.xml +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/Mappings.hbm.xml @@ -10,7 +10,7 @@ "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> - + diff --git a/hibernate-core/src/test/java/org/hibernate/test/collection/map/MultilingualString.java b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/MultilingualString.java similarity index 96% rename from hibernate-core/src/test/java/org/hibernate/test/collection/map/MultilingualString.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/MultilingualString.java index 3155d6cf51..7b581c23af 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/collection/map/MultilingualString.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/MultilingualString.java @@ -4,7 +4,7 @@ * 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.test.collection.map; +package org.hibernate.orm.test.collection.map; import java.util.HashMap; import java.util.Map; diff --git a/hibernate-core/src/test/java/org/hibernate/test/collection/map/MultilingualStringParent.java b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/MultilingualStringParent.java similarity index 94% rename from hibernate-core/src/test/java/org/hibernate/test/collection/map/MultilingualStringParent.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/MultilingualStringParent.java index aba7618b7d..0f96406fdd 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/collection/map/MultilingualStringParent.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/MultilingualStringParent.java @@ -1,4 +1,4 @@ -package org.hibernate.test.collection.map; +package org.hibernate.orm.test.collection.map; import javax.persistence.*; diff --git a/hibernate-core/src/test/java/org/hibernate/test/collection/map/Parent.java b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/Parent.java similarity index 95% rename from hibernate-core/src/test/java/org/hibernate/test/collection/map/Parent.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/Parent.java index 649b5ddc3e..f7b01b79ea 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/collection/map/Parent.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/Parent.java @@ -4,7 +4,7 @@ * 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.test.collection.map; +package org.hibernate.orm.test.collection.map; import java.util.HashMap; import java.util.Map; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/PersistentMapTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/PersistentMapTest.java new file mode 100644 index 0000000000..7033a13cd0 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/PersistentMapTest.java @@ -0,0 +1,455 @@ +/* + * 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.orm.test.collection.map; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MapKeyColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +import org.hibernate.collection.internal.PersistentMap; +import org.hibernate.query.Query; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test various situations using a {@link PersistentMap}. + * + * @author Steve Ebersole + * @author Brett Meyer + * @author Gail Badner + */ +@DomainModel( + xmlMappings = "org/hibernate/orm/test/collection/map/Mappings.hbm.xml", + annotatedClasses = { + PersistentMapTest.User.class, + PersistentMapTest.UserData.class, + MultilingualString.class, + MultilingualStringParent.class, + PersistentMapTest.Address.class, + PersistentMapTest.Detail.class + } +) +@SessionFactory +public class PersistentMapTest { + + @Test + @SuppressWarnings({ "unchecked" }) + public void testWriteMethodDirtying(SessionFactoryScope scope) { + Parent parent = new Parent( "p1" ); + Child child = new Child( "c1" ); + parent.getChildren().put( child.getName(), child ); + child.setParent( parent ); + Child otherChild = new Child( "c2" ); + + scope.inTransaction( + session -> { + session.save( parent ); + session.flush(); + // at this point, the map on parent has now been replaced with a PersistentMap... + PersistentMap children = (PersistentMap) parent.getChildren(); + + Object old = children.put( child.getName(), child ); + assertTrue( old == child ); + assertFalse( children.isDirty() ); + + old = children.remove( otherChild.getName() ); + assertNull( old ); + assertFalse( children.isDirty() ); + + HashMap otherMap = new HashMap(); + otherMap.put( child.getName(), child ); + children.putAll( otherMap ); + assertFalse( children.isDirty() ); + + otherMap = new HashMap(); + otherMap.put( otherChild.getName(), otherChild ); + children.putAll( otherMap ); + assertTrue( children.isDirty() ); + + children.clearDirty(); + session.delete( child ); + children.clear(); + assertTrue( children.isDirty() ); + session.flush(); + + children.clear(); + assertFalse( children.isDirty() ); + + session.delete( parent ); + } + ); + } + + @Test + public void testPutAgainstUninitializedMap(SessionFactoryScope scope) { + // prepare map owner... + Parent p = new Parent( "p1" ); + scope.inTransaction( + session -> { + session.save( p ); + } + ); + + // Now, reload the parent and test adding children + Parent parent = scope.fromTransaction( + session -> { + Parent p1 = session.get( Parent.class, p.getName() ); + p1.addChild( "c1" ); + p1.addChild( "c2" ); + return p1; + } + ); + + assertEquals( 2, parent.getChildren().size() ); + + scope.inTransaction( + session -> session.delete( parent ) + ); + } + + @Test + public void testRemoveAgainstUninitializedMap(SessionFactoryScope scope) { + Parent parent = new Parent( "p1" ); + Child child = new Child( "c1" ); + parent.addChild( child ); + + scope.inTransaction( + session -> session.save( parent ) + ); + + // Now reload the parent and test removing the child + Child child2 = scope.fromTransaction( + session -> { + Parent p = session.get( Parent.class, parent.getName() ); + Child c2 = (Child) p.getChildren().remove( child.getName() ); + c2.setParent( null ); + assertNotNull( c2 ); + assertTrue( p.getChildren().isEmpty() ); + return c2; + } + ); + + + // Load the parent once again and make sure child is still gone + // then cleanup + scope.inTransaction( + session -> { + Parent p = session.get( Parent.class, parent.getName() ); + assertTrue( p.getChildren().isEmpty() ); + session.delete( child2 ); + session.delete( p ); + } + ); + } + + + @Test + public void testSelect(SessionFactoryScope scope) { + User user = new User(); + scope.inTransaction( + session -> { + UserData userData = new UserData(); + userData.user = user; + user.userDatas.put( "foo", userData ); + session.persist( user ); + } + ); + + scope.inTransaction( + session -> { + Query q = session.createQuery( "SELECT d.user FROM " + UserData.class.getName() + " d " ); + List list = q.list(); + + assertEquals( 1, list.size() ); + } + ); + } + + @Test + @TestForIssue(jiraKey = "HHH-5732") + public void testClearMap(SessionFactoryScope scope) { + scope.inSession( + session -> { + session.getTransaction().begin(); + try { + User user = new User(); + UserData userData = new UserData(); + userData.user = user; + user.userDatas.put( "foo", userData ); + session.persist( user ); + session.getTransaction().commit(); + session.clear(); + + session.beginTransaction(); + + + user = session.get( User.class, 1 ); + user.userDatas.clear(); + session.update( user ); + + Query q = session.createQuery( "DELETE FROM " + UserData.class.getName() + " d WHERE d.user = :user" ); + q.setParameter( "user", user ); + q.executeUpdate(); + + session.getTransaction().commit(); + + session.getTransaction().begin(); +/* +select + userdatas0_.userId as userid2_8_0_, + userdatas0_.id as id1_8_0_, + userdatas0_.name as name3_0_, + userdatas0_.id as id1_8_1_, + userdatas0_.userId as userid2_8_1_ + from + UserData userdatas0_ + where + userdatas0_.userId=1 + */ + assertEquals( 0, session.get( User.class, user.id ).userDatas.size() ); + assertEquals( 0, session.createQuery( "FROM " + UserData.class.getName() ).list().size() ); + session.createQuery( "delete " + User.class.getName() ) + .executeUpdate(); + session.getTransaction().commit(); + } + catch (Exception e) { + if ( session.getTransaction().isActive() ) { + session.getTransaction().rollback(); + } + throw e; + } + } + ); + } + + @Test + @TestForIssue(jiraKey = "HHH-5393") + public void testMapKeyColumnInEmbeddableElement(SessionFactoryScope scope) { + MultilingualString m = new MultilingualString(); + scope.inTransaction( + session -> { + LocalizedString localizedString = new LocalizedString(); + localizedString.setLanguage( "English" ); + localizedString.setText( "name" ); + m.getMap().put( localizedString.getLanguage(), localizedString ); + localizedString = new LocalizedString(); + localizedString.setLanguage( "English Pig Latin" ); + localizedString.setText( "amenay" ); + m.getMap().put( localizedString.getLanguage(), localizedString ); + session.persist( m ); + } + ); + + scope.inTransaction( + session -> { + MultilingualString multilingualString = session.get( MultilingualString.class, m.getId() ); + assertEquals( 2, multilingualString.getMap().size() ); + LocalizedString localizedString = multilingualString.getMap().get( "English" ); + assertEquals( "English", localizedString.getLanguage() ); + assertEquals( "name", localizedString.getText() ); + localizedString = multilingualString.getMap().get( "English Pig Latin" ); + assertEquals( "English Pig Latin", localizedString.getLanguage() ); + assertEquals( "amenay", localizedString.getText() ); + session.delete( multilingualString ); + } + ); + } + + @Test + @TestForIssue(jiraKey = "HQLPARSER-15") + public void testJoinFetchElementCollectionWithParentSelect(SessionFactoryScope scope) { + MultilingualStringParent parent = new MultilingualStringParent(); + scope.inTransaction( + session -> { + MultilingualString m = new MultilingualString(); + LocalizedString localizedString = new LocalizedString(); + localizedString.setLanguage( "English" ); + localizedString.setText( "name" ); + m.getMap().put( localizedString.getLanguage(), localizedString ); + localizedString = new LocalizedString(); + localizedString.setLanguage( "English Pig Latin" ); + localizedString.setText( "amenay" ); + m.getMap().put( localizedString.getLanguage(), localizedString ); + + parent.setString( m ); + + session.persist( m ); + session.persist( parent ); + } + ); + + + scope.inTransaction( + session -> { + MultilingualString m = session.createQuery( + "SELECT s FROM MultilingualStringParent parent " + + "JOIN parent.string s " + + "JOIN FETCH s.map", MultilingualString.class ) + .getSingleResult(); + assertEquals( 2, m.getMap().size() ); + LocalizedString localizedString = m.getMap().get( "English" ); + assertEquals( "English", localizedString.getLanguage() ); + assertEquals( "name", localizedString.getText() ); + localizedString = m.getMap().get( "English Pig Latin" ); + assertEquals( "English Pig Latin", localizedString.getLanguage() ); + assertEquals( "amenay", localizedString.getText() ); + session.delete( parent ); + session.delete( m ); + } + ); + } + + @Test + @TestForIssue(jiraKey = "HHH-11038") + public void testMapKeyColumnNonInsertableNonUpdatableBidirOneToMany(SessionFactoryScope scope) { + User user = new User(); + scope.inTransaction( + session -> { + Address address = new Address(); + address.addressType = "email"; + address.addressText = "jane@doe.com"; + user.addresses.put( address.addressType, address ); + address.user = user; + session.persist( user ); + } + ); + + scope.inTransaction( + session -> { + User u = session.get( User.class, user.id ); + u.addresses.clear(); + } + ); + + + scope.inTransaction( + session -> { + User u = session.get( User.class, user.id ); + session.delete( u ); + session.createQuery( "delete from " + User.class.getName() ).executeUpdate(); + + } + ); + } + + @Test + @TestForIssue(jiraKey = "HHH-11038") + public void testMapKeyColumnNonInsertableNonUpdatableUnidirOneToMany(SessionFactoryScope scope) { + User user = new User(); + scope.inTransaction( + session -> { + Detail detail = new Detail(); + detail.description = "desc"; + detail.detailType = "trivial"; + user.details.put( detail.detailType, detail ); + session.persist( user ); + } + ); + + scope.inTransaction( + session -> { + User u = session.get( User.class, user.id ); + u.details.clear(); + } + ); + + scope.inTransaction( + session -> { + User u = session.get( User.class, user.id ); + session.delete( u ); + session.createQuery( "delete from " + User.class.getName() ).executeUpdate(); + + } + ); + } + + @Entity + @Table(name = "MyUser") + static class User implements Serializable { + @Id + @GeneratedValue + private Integer id; + + @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL) + @MapKeyColumn(name = "name", nullable = true) + private Map userDatas = new HashMap<>(); + + @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL) + @MapKeyColumn(name = "addressType", insertable = false, updatable = false) + private Map addresses = new HashMap<>(); + + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @MapKeyColumn(name = "detailType", insertable = false, updatable = false) + @JoinColumn + private Map details = new HashMap<>(); + } + + @Entity + @Table(name = "UserData") + static class UserData { + @Id + @GeneratedValue + private Integer id; + + @ManyToOne + @JoinColumn(name = "userId") + private User user; + } + + @Entity + @Table(name = "Address") + static class Address { + @Id + @GeneratedValue + private Integer id; + + @ManyToOne + @JoinColumn(name = "userId") + private User user; + + @Column(nullable = false) + private String addressType; + + @Column(nullable = false) + private String addressText; + } + + @Entity + @Table(name = "Detail") + static class Detail { + @Id + @GeneratedValue + private Integer id; + + @Column(nullable = false) + private String detailType; + + private String description; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/collection/map/PersistentMapTest.java b/hibernate-core/src/test/java/org/hibernate/test/collection/map/PersistentMapTest.java deleted file mode 100644 index 780f3e58a1..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/collection/map/PersistentMapTest.java +++ /dev/null @@ -1,406 +0,0 @@ -/* - * 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.test.collection.map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.MapKeyColumn; -import javax.persistence.OneToMany; -import javax.persistence.Table; - -import org.hibernate.query.Query; -import org.hibernate.Session; -import org.hibernate.collection.internal.PersistentMap; -import org.hibernate.testing.TestForIssue; -import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; -import org.junit.Test; - -/** - * Test various situations using a {@link PersistentMap}. - * - * @author Steve Ebersole - * @author Brett Meyer - * @author Gail Badner - */ -public class PersistentMapTest extends BaseCoreFunctionalTestCase { - @Override - public String[] getMappings() { - return new String[] { "collection/map/Mappings.hbm.xml" }; - } - - @Override - protected Class[] getAnnotatedClasses() { - return new Class[] { - User.class, - UserData.class, - MultilingualString.class, - MultilingualStringParent.class, - Address.class, - Detail.class - }; - } - - @Test - @SuppressWarnings({ "unchecked" }) - public void testWriteMethodDirtying() { - Parent parent = new Parent( "p1" ); - Child child = new Child( "c1" ); - parent.getChildren().put( child.getName(), child ); - child.setParent( parent ); - Child otherChild = new Child( "c2" ); - - Session session = openSession(); - session.beginTransaction(); - session.save( parent ); - session.flush(); - // at this point, the map on parent has now been replaced with a PersistentMap... - PersistentMap children = ( PersistentMap ) parent.getChildren(); - - Object old = children.put( child.getName(), child ); - assertTrue( old == child ); - assertFalse( children.isDirty() ); - - old = children.remove( otherChild.getName() ); - assertNull( old ); - assertFalse( children.isDirty() ); - - HashMap otherMap = new HashMap(); - otherMap.put( child.getName(), child ); - children.putAll( otherMap ); - assertFalse( children.isDirty() ); - - otherMap = new HashMap(); - otherMap.put( otherChild.getName(), otherChild ); - children.putAll( otherMap ); - assertTrue( children.isDirty() ); - - children.clearDirty(); - session.delete( child ); - children.clear(); - assertTrue( children.isDirty() ); - session.flush(); - - children.clear(); - assertFalse( children.isDirty() ); - - session.delete( parent ); - session.getTransaction().commit(); - session.close(); - } - - @Test - public void testPutAgainstUninitializedMap() { - // prepare map owner... - Session session = openSession(); - session.beginTransaction(); - Parent parent = new Parent( "p1" ); - session.save( parent ); - session.getTransaction().commit(); - session.close(); - - // Now, reload the parent and test adding children - session = openSession(); - session.beginTransaction(); - parent = ( Parent ) session.get( Parent.class, parent.getName() ); - parent.addChild( "c1" ); - parent.addChild( "c2" ); - session.getTransaction().commit(); - session.close(); - - assertEquals( 2, parent.getChildren().size() ); - - session = openSession(); - session.beginTransaction(); - session.delete( parent ); - session.getTransaction().commit(); - session.close(); - } - - @Test - public void testRemoveAgainstUninitializedMap() { - Parent parent = new Parent( "p1" ); - Child child = new Child( "c1" ); - parent.addChild( child ); - - Session session = openSession(); - session.beginTransaction(); - session.save( parent ); - session.getTransaction().commit(); - session.close(); - - // Now reload the parent and test removing the child - session = openSession(); - session.beginTransaction(); - parent = ( Parent ) session.get( Parent.class, parent.getName() ); - Child child2 = ( Child ) parent.getChildren().remove( child.getName() ); - child2.setParent( null ); - assertNotNull( child2 ); - assertTrue( parent.getChildren().isEmpty() ); - session.getTransaction().commit(); - session.close(); - - // Load the parent once again and make sure child is still gone - // then cleanup - session = openSession(); - session.beginTransaction(); - parent = ( Parent ) session.get( Parent.class, parent.getName() ); - assertTrue( parent.getChildren().isEmpty() ); - session.delete( child2 ); - session.delete( parent ); - session.getTransaction().commit(); - session.close(); - } - - @Test - @TestForIssue(jiraKey = "HHH-5732") - public void testClearMap() { - Session s = openSession(); - s.beginTransaction(); - - User user = new User(); - UserData userData = new UserData(); - userData.user = user; - user.userDatas.put( "foo", userData ); - s.persist( user ); - - s.getTransaction().commit(); - s.clear(); - - s.beginTransaction(); - - user = s.get( User.class, 1 ); - user.userDatas.clear(); - s.update( user ); - Query q = s.createQuery( "DELETE FROM " + UserData.class.getName() + " d WHERE d.user = :user" ); - q.setParameter( "user", user ); - q.executeUpdate(); - - s.getTransaction().commit(); - - s.getTransaction().begin(); - - assertEquals( s.get( User.class, user.id ).userDatas.size(), 0 ); - assertEquals( s.createQuery( "FROM " + UserData.class.getName() ).list().size(), 0 ); - s.createQuery( "delete " + User.class.getName() ).executeUpdate(); - - s.getTransaction().commit(); - s.close(); - } - - @Test - @TestForIssue( jiraKey = "HHH-5393") - public void testMapKeyColumnInEmbeddableElement() { - Session s = openSession(); - s.getTransaction().begin(); - MultilingualString m = new MultilingualString(); - LocalizedString localizedString = new LocalizedString(); - localizedString.setLanguage( "English" ); - localizedString.setText( "name" ); - m.getMap().put( localizedString.getLanguage(), localizedString ); - localizedString = new LocalizedString(); - localizedString.setLanguage( "English Pig Latin" ); - localizedString.setText( "amenay" ); - m.getMap().put( localizedString.getLanguage(), localizedString ); - s.persist( m ); - s.getTransaction().commit(); - s.close(); - - s = openSession(); - s.beginTransaction(); - m = s.get( MultilingualString.class, m.getId()); - assertEquals( 2, m.getMap().size() ); - localizedString = m.getMap().get( "English" ); - assertEquals( "English", localizedString.getLanguage() ); - assertEquals( "name", localizedString.getText() ); - localizedString = m.getMap().get( "English Pig Latin" ); - assertEquals( "English Pig Latin", localizedString.getLanguage() ); - assertEquals( "amenay", localizedString.getText() ); - s.delete( m ); - s.getTransaction().commit(); - s.close(); - } - - @Test - @TestForIssue( jiraKey = "HQLPARSER-15") - public void testJoinFetchElementCollectionWithParentSelect() { - Session s = openSession(); - s.getTransaction().begin(); - - MultilingualString m = new MultilingualString(); - LocalizedString localizedString = new LocalizedString(); - localizedString.setLanguage( "English" ); - localizedString.setText( "name" ); - m.getMap().put( localizedString.getLanguage(), localizedString ); - localizedString = new LocalizedString(); - localizedString.setLanguage( "English Pig Latin" ); - localizedString.setText( "amenay" ); - m.getMap().put( localizedString.getLanguage(), localizedString ); - - MultilingualStringParent parent = new MultilingualStringParent(); - parent.setString( m ); - - s.persist( m ); - s.persist( parent ); - s.getTransaction().commit(); - s.close(); - - s = openSession(); - s.beginTransaction(); - m = s.createQuery( - "SELECT s FROM MultilingualStringParent parent " + - "JOIN parent.string s " + - "JOIN FETCH s.map", MultilingualString.class ) - .getSingleResult(); - assertEquals( 2, m.getMap().size() ); - localizedString = m.getMap().get( "English" ); - assertEquals( "English", localizedString.getLanguage() ); - assertEquals( "name", localizedString.getText() ); - localizedString = m.getMap().get( "English Pig Latin" ); - assertEquals( "English Pig Latin", localizedString.getLanguage() ); - assertEquals( "amenay", localizedString.getText() ); - s.delete( parent ); - s.delete( m ); - s.getTransaction().commit(); - s.close(); - } - - @Test - @TestForIssue( jiraKey = "HHH-11038" ) - public void testMapKeyColumnNonInsertableNonUpdatableBidirOneToMany() { - Session s = openSession(); - s.getTransaction().begin(); - User user = new User(); - Address address = new Address(); - address.addressType = "email"; - address.addressText = "jane@doe.com"; - user.addresses.put( address.addressType, address ); - address.user = user; - s.persist( user ); - s.getTransaction().commit(); - s.close(); - - s = openSession(); - s.getTransaction().begin(); - user = s.get( User.class, user.id ); - user.addresses.clear(); - s.getTransaction().commit(); - s.close(); - - s = openSession(); - s.getTransaction().begin(); - user = s.get( User.class, user.id ); - s.delete( user ); - s.createQuery( "delete from " + User.class.getName() ).executeUpdate(); - s.getTransaction().commit(); - s.close(); - } - - @Test - @TestForIssue( jiraKey = "HHH-11038" ) - public void testMapKeyColumnNonInsertableNonUpdatableUnidirOneToMany() { - Session s = openSession(); - s.getTransaction().begin(); - User user = new User(); - Detail detail = new Detail(); - detail.description = "desc"; - detail.detailType = "trivial"; - user.details.put( detail.detailType, detail ); - s.persist( user ); - s.getTransaction().commit(); - s.close(); - - s = openSession(); - s.getTransaction().begin(); - user = s.get( User.class, user.id ); - user.details.clear(); - s.getTransaction().commit(); - s.close(); - - s = openSession(); - s.getTransaction().begin(); - user = s.get( User.class, user.id ); - s.delete( user ); - s.createQuery( "delete from " + User.class.getName() ).executeUpdate(); - s.getTransaction().commit(); - s.close(); - } - - @Entity - @Table(name = "MyUser") - private static class User implements Serializable { - @Id @GeneratedValue - private Integer id; - - @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL) - @MapKeyColumn(name = "name", nullable = true) - private Map userDatas = new HashMap(); - - @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL) - @MapKeyColumn(name = "addressType", insertable = false, updatable = false) - private Map addresses = new HashMap(); - - @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) - @MapKeyColumn(name = "detailType", insertable = false, updatable = false) - @JoinColumn - private Map details = new HashMap(); - } - - @Entity - @Table(name = "UserData") - private static class UserData { - @Id @GeneratedValue - private Integer id; - - @ManyToOne - @JoinColumn(name = "userId") - private User user; - } - - @Entity - @Table(name = "Address") - private static class Address { - @Id @GeneratedValue - private Integer id; - - @ManyToOne - @JoinColumn(name = "userId") - private User user; - - @Column(nullable = false) - private String addressType; - - @Column(nullable = false) - private String addressText; - } - - @Entity - @Table(name="Detail") - private static class Detail { - @Id @GeneratedValue - private Integer id; - - @Column(nullable = false) - private String detailType; - - private String description; - } -}