EntityValuedPathInterpretation fix TableGroup resolution for implicit join e.g or

This commit is contained in:
Andrea Boriero 2020-08-24 15:41:12 +01:00 committed by Andrea Boriero
parent 0bd2c32f76
commit fc6f515407
14 changed files with 668 additions and 489 deletions

View File

@ -68,7 +68,10 @@ public interface EntityValuedModelPart extends FetchableContainer {
Consumer<JdbcMapping> 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 )
); }
}

View File

@ -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,

View File

@ -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<SqmCteTableColumn> sqmCteColumns = sqmCteTable.getColumns();
final List<CteColumn> 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<JdbcParameter> 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

View File

@ -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<T> extends AbstractSqmPathInterpreta
public static <T> EntityValuedPathInterpretation<T> from(
SqmEntityValuedSimplePath<T> 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 <T> SqlTuple resolveSqlExpression(
SqmEntityValuedSimplePath<T> sqmPath,
SqmToSqlAstConverter sqlAstCreationState,
TableGroup tableGroup,
EntityValuedModelPart mapping) {
final List<ColumnReference> 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 <T> TableReference getTableReference(
SqmEntityValuedSimplePath<T> 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;
}
}

View File

@ -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<T> extends AbstractSqmPathInte
super(sqmPath, mapping, tableGroup);
this.sqlExpression = sqlExpression;
}
@Override
public void accept(SqlAstWalker sqlTreeWalker) {
sqlExpression.accept( sqlTreeWalker );
throw new NotYetImplementedFor6Exception( getClass() );
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.collection.map;
package org.hibernate.orm.test.collection.map;
/**

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.collection.map;
package org.hibernate.orm.test.collection.map;
import javax.persistence.Embeddable;

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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<String, String> aCollection = new HashMap<String, String>();
@CollectionTable(name = "AnEntity_aCollection", joinColumns = { @JoinColumn(name = "AnEntity_id") })
private Map<String, String> aCollection = new HashMap<>();
}
}

View File

@ -10,7 +10,7 @@
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.test.collection.map">
<hibernate-mapping package="org.hibernate.orm.test.collection.map">
<class name="Parent">
<id name="name" column="NAME" type="string" />

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.collection.map;
package org.hibernate.orm.test.collection.map;
import java.util.HashMap;
import java.util.Map;

View File

@ -1,4 +1,4 @@
package org.hibernate.test.collection.map;
package org.hibernate.orm.test.collection.map;
import javax.persistence.*;

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.collection.map;
package org.hibernate.orm.test.collection.map;
import java.util.HashMap;
import java.util.Map;

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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<User> 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<String, UserData> userDatas = new HashMap<>();
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL)
@MapKeyColumn(name = "addressType", insertable = false, updatable = false)
private Map<String, Address> addresses = new HashMap<>();
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@MapKeyColumn(name = "detailType", insertable = false, updatable = false)
@JoinColumn
private Map<String, Detail> 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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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<String, UserData> userDatas = new HashMap<String, UserData>();
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL)
@MapKeyColumn(name = "addressType", insertable = false, updatable = false)
private Map<String, Address> addresses = new HashMap<String, Address>();
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@MapKeyColumn(name = "detailType", insertable = false, updatable = false)
@JoinColumn
private Map<String, Detail> details = new HashMap<String, Detail>();
}
@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;
}
}