Fix PersistenceUnitUtilImpl#getIdentifier() throws NPE for NonAggregateIdentifier
This commit is contained in:
parent
eae9a39b14
commit
411355852a
|
@ -79,7 +79,7 @@ public class LazyAttributeLoadingInterceptor extends AbstractLazyLoadInterceptor
|
|||
final EntityPersister persister = session.getFactory().getMetamodel().entityPersister( getEntityName() );
|
||||
|
||||
if ( isTemporarySession ) {
|
||||
final Object id = persister.getIdentifier( target, null );
|
||||
final Object id = persister.getIdentifier( target, session );
|
||||
|
||||
// Add an entry for this entity in the PC of the temp Session
|
||||
// NOTE : a few arguments that would be nice to pass along here...
|
||||
|
|
|
@ -51,7 +51,7 @@ public final class EntityPrinter {
|
|||
result.put(
|
||||
entityPersister.getIdentifierPropertyName(),
|
||||
entityPersister.getIdentifierType().toLoggableString(
|
||||
entityPersister.getIdentifier( entity ),
|
||||
entityPersister.getIdentifier( entity, factory ),
|
||||
factory
|
||||
)
|
||||
);
|
||||
|
|
|
@ -103,7 +103,7 @@ public class PersistenceUnitUtilImpl implements PersistenceUnitUtil, Serializabl
|
|||
catch (MappingException ex) {
|
||||
throw new IllegalArgumentException( entityClass.getName() + " is not an entity", ex );
|
||||
}
|
||||
return persister.getIdentifier( entity, null );
|
||||
return persister.getIdentifier( entity, persister.getFactory() );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.metamodel.mapping;
|
||||
|
||||
import org.hibernate.engine.spi.IdentifierValue;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
||||
/**
|
||||
|
@ -30,6 +31,10 @@ public interface EntityIdentifierMapping extends ValueMapping, ModelPart {
|
|||
IdentifierValue getUnsavedStrategy();
|
||||
|
||||
Object getIdentifier(Object entity, SharedSessionContractImplementor session);
|
||||
|
||||
Object getIdentifier(Object entity, SessionFactoryImplementor sessionFactory);
|
||||
|
||||
void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session);
|
||||
|
||||
Object instantiate();
|
||||
}
|
||||
|
|
|
@ -125,6 +125,14 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
|
|||
return propertyAccess.getGetter().get( entity );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getIdentifier(Object entity, SessionFactoryImplementor sessionFactory) {
|
||||
if ( entity instanceof HibernateProxy ) {
|
||||
return ( (HibernateProxy) entity ).getHibernateLazyInitializer().getIdentifier();
|
||||
}
|
||||
return propertyAccess.getGetter().get( entity );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) {
|
||||
propertyAccess.getSetter().set( entity, id, session.getFactory() );
|
||||
|
|
|
@ -79,6 +79,15 @@ public class EmbeddedIdentifierMappingImpl
|
|||
|
||||
@Override
|
||||
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
|
||||
return getIdentifier( entity );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getIdentifier(Object entity, SessionFactoryImplementor sessionFactory) {
|
||||
return EmbeddedIdentifierMappingImpl.this.getIdentifier( entity );
|
||||
}
|
||||
|
||||
private Object getIdentifier(Object entity) {
|
||||
if ( entity instanceof HibernateProxy ) {
|
||||
return ( (HibernateProxy) entity ).getHibernateLazyInitializer().getIdentifier();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.io.Serializable;
|
|||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.EntityNameResolver;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
@ -24,12 +25,15 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
|
|||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||
import org.hibernate.metamodel.spi.MetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.type.ComponentType;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* A "non-aggregated" composite identifier.
|
||||
|
@ -42,8 +46,8 @@ import org.hibernate.type.ComponentType;
|
|||
public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentifierMapping {
|
||||
|
||||
private final List<SingularAttributeMapping> idAttributeMappings;
|
||||
private final ComponentType bootIdComponentType;
|
||||
private final ComponentType bootCidComponentType;
|
||||
private final ComponentType mappedIdComponentType;
|
||||
private final ComponentType virtualComponentType;
|
||||
|
||||
private final boolean[] isBootIdPropertyManyToOne;
|
||||
|
||||
|
@ -71,8 +75,8 @@ public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentif
|
|||
for ( int i = 0; i < propertySpan; i++ ) {
|
||||
isBootIdPropertyManyToOne[i] = bootIdClassDescriptor.getProperty( i ).getValue() instanceof ManyToOne;
|
||||
}
|
||||
bootIdComponentType = (ComponentType) bootIdClassDescriptor.getType();
|
||||
bootCidComponentType = (ComponentType) bootCidDescriptor.getType();
|
||||
mappedIdComponentType = (ComponentType) bootIdClassDescriptor.getType();
|
||||
virtualComponentType = (ComponentType) bootCidDescriptor.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -88,14 +92,75 @@ public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentif
|
|||
@Override
|
||||
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
|
||||
if ( hasContainingClass() ) {
|
||||
final Serializable disassemble = bootCidComponentType.disassemble( entity, session, null );
|
||||
return bootIdComponentType.assemble( disassemble, session, null );
|
||||
final Serializable disassemble = virtualComponentType.disassemble( entity, session, null );
|
||||
return mappedIdComponentType.assemble( disassemble, session, null );
|
||||
}
|
||||
else {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getIdentifier(Object entity, SessionFactoryImplementor sessionFactory){
|
||||
if ( hasContainingClass() ) {
|
||||
final Object id = mappedIdComponentType.instantiate();
|
||||
final Object[] propertyValues = virtualComponentType.getPropertyValues( entity );
|
||||
final Type[] subTypes = virtualComponentType.getSubtypes();
|
||||
final Type[] copierSubTypes = mappedIdComponentType.getSubtypes();
|
||||
final int length = subTypes.length;
|
||||
for ( int i = 0; i < length; i++ ) {
|
||||
//JPA 2 @MapsId + @IdClass points to the pk of the entity
|
||||
if ( subTypes[i].isAssociationType() && !copierSubTypes[i].isAssociationType() ) {
|
||||
propertyValues[i] = determineEntityId( propertyValues[i], sessionFactory );
|
||||
}
|
||||
}
|
||||
mappedIdComponentType.setPropertyValues( id, propertyValues );
|
||||
return id;
|
||||
}
|
||||
else {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
private static Object determineEntityId(Object entity, SessionFactoryImplementor sessionFactory) {
|
||||
if ( entity == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( HibernateProxy.class.isInstance( entity ) ) {
|
||||
// entity is a proxy, so we know it is not transient; just return ID from proxy
|
||||
return ( (HibernateProxy) entity ).getHibernateLazyInitializer().getInternalIdentifier();
|
||||
}
|
||||
|
||||
final EntityPersister persister = resolveEntityPersister(
|
||||
entity,
|
||||
sessionFactory
|
||||
);
|
||||
|
||||
return persister.getIdentifier( entity, sessionFactory );
|
||||
}
|
||||
|
||||
private static EntityPersister resolveEntityPersister(Object entity, SessionFactoryImplementor sessionFactory) {
|
||||
assert sessionFactory != null;
|
||||
|
||||
String entityName = null;
|
||||
final MetamodelImplementor metamodel = sessionFactory.getMetamodel();
|
||||
for ( EntityNameResolver entityNameResolver : metamodel.getEntityNameResolvers() ) {
|
||||
entityName = entityNameResolver.resolveEntityName( entity );
|
||||
if ( entityName != null ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( entityName == null ) {
|
||||
// old fall-back
|
||||
entityName = entity.getClass().getName();
|
||||
}
|
||||
|
||||
return metamodel.findEntityDescriptor( entityName );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Object disassemble(Object value, SharedSessionContractImplementor session) {
|
||||
if ( !getEmbeddableTypeDescriptor().getMappedJavaTypeDescriptor()
|
||||
|
@ -121,7 +186,7 @@ public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentif
|
|||
}
|
||||
else {
|
||||
final SessionFactoryImplementor factory = session.getFactory();
|
||||
final Object[] propertyValues = bootIdComponentType.getPropertyValues( id, session );
|
||||
final Object[] propertyValues = mappedIdComponentType.getPropertyValues( id, session );
|
||||
forEachAttribute(
|
||||
(position, attribute) -> {
|
||||
Object propertyValue = propertyValues[position];
|
||||
|
@ -214,6 +279,6 @@ public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentif
|
|||
|
||||
@Override
|
||||
public boolean hasContainingClass() {
|
||||
return bootIdComponentType != bootCidComponentType;
|
||||
return mappedIdComponentType != virtualComponentType;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5029,7 +5029,7 @@ public abstract class AbstractEntityPersister
|
|||
|
||||
@Override
|
||||
public Object getIdentifier(Object object) {
|
||||
return getIdentifier( object, null );
|
||||
return getIdentifier( object, getFactory() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -5037,6 +5037,11 @@ public abstract class AbstractEntityPersister
|
|||
return identifierMapping.getIdentifier( entity, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getIdentifier(Object entity, SessionFactoryImplementor sessionFactory){
|
||||
return identifierMapping.getIdentifier( entity, sessionFactory );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) {
|
||||
identifierMapping.setIdentifier( entity, id, session );
|
||||
|
|
|
@ -821,6 +821,8 @@ public interface EntityPersister
|
|||
*/
|
||||
Object getIdentifier(Object entity, SharedSessionContractImplementor session);
|
||||
|
||||
Object getIdentifier(Object entity, SessionFactoryImplementor sessionFactoryImplementor);
|
||||
|
||||
/**
|
||||
* Inject the identifier value into the given entity.
|
||||
*/
|
||||
|
|
|
@ -3395,8 +3395,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
associationKeyPart = identifierMapping;
|
||||
associationKey = identifierMapping.getIdentifier(
|
||||
literal.getLiteralValue(),
|
||||
null
|
||||
);
|
||||
getCreationContext().getSessionFactory());
|
||||
}
|
||||
if ( associationKeyPart instanceof BasicValuedMapping ) {
|
||||
return new QueryLiteral<>(
|
||||
|
|
|
@ -156,7 +156,7 @@ public class AnyType extends AbstractType implements CompositeType, AssociationT
|
|||
final EntityPersister concretePersister = guessEntityPersister( entity );
|
||||
return concretePersister == null
|
||||
? null
|
||||
: concretePersister.getIdentifier( entity, null );
|
||||
: concretePersister.getIdentifier( entity,concretePersister.getFactory() );
|
||||
}
|
||||
|
||||
private EntityPersister guessEntityPersister(Object object) {
|
||||
|
|
|
@ -324,7 +324,7 @@ public abstract class EntityType extends AbstractType implements AssociationType
|
|||
else {
|
||||
final Class mappedClass = persister.getMappedClass();
|
||||
if ( mappedClass.isAssignableFrom( x.getClass() ) ) {
|
||||
id = persister.getIdentifier( x );
|
||||
id = persister.getIdentifier( x, factory );
|
||||
}
|
||||
else {
|
||||
id = x;
|
||||
|
@ -353,7 +353,7 @@ public abstract class EntityType extends AbstractType implements AssociationType
|
|||
}
|
||||
else {
|
||||
if ( mappedClass.isAssignableFrom( x.getClass() ) ) {
|
||||
xid = persister.getIdentifier( x );
|
||||
xid = persister.getIdentifier( x, factory );
|
||||
}
|
||||
else {
|
||||
//JPA 2 case where @IdClass contains the id and not the associated entity
|
||||
|
@ -368,7 +368,7 @@ public abstract class EntityType extends AbstractType implements AssociationType
|
|||
}
|
||||
else {
|
||||
if ( mappedClass.isAssignableFrom( y.getClass() ) ) {
|
||||
yid = persister.getIdentifier( y );
|
||||
yid = persister.getIdentifier( y, factory );
|
||||
}
|
||||
else {
|
||||
//JPA 2 case where @IdClass contains the id and not the associated entity
|
||||
|
@ -504,7 +504,7 @@ public abstract class EntityType extends AbstractType implements AssociationType
|
|||
id = proxy.getHibernateLazyInitializer().getInternalIdentifier();
|
||||
}
|
||||
else {
|
||||
id = persister.getIdentifier( value );
|
||||
id = persister.getIdentifier( value, factory );
|
||||
}
|
||||
|
||||
result.append( '#' )
|
||||
|
|
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
* 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.annotations.derivedidentities.bidirectional;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.hibernate.jpa.internal.PersistenceUnitUtilImpl;
|
||||
|
||||
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.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.IdClass;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@DomainModel(
|
||||
annotatedClasses = {
|
||||
CompositeIdDerivedIdWithIdClassTest.ShoppingCart.class,
|
||||
CompositeIdDerivedIdWithIdClassTest.LineItem.class
|
||||
}
|
||||
)
|
||||
@SessionFactory
|
||||
public class CompositeIdDerivedIdWithIdClassTest {
|
||||
|
||||
@AfterEach
|
||||
public void cleanup(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from LineItem" ).executeUpdate();
|
||||
session.createQuery( "delete from Cart" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11328")
|
||||
public void testMergeTransientIdManyToOne(SessionFactoryScope scope) {
|
||||
ShoppingCart transientCart = new ShoppingCart( "cart1" );
|
||||
transientCart.addLineItem( new LineItem( 0, "description2", transientCart ) );
|
||||
|
||||
// assertion for HHH-11274 - checking for exception
|
||||
final Object identifier = new PersistenceUnitUtilImpl( scope.getSessionFactory() ).getIdentifier( transientCart.getLineItems()
|
||||
.get( 0 ) );
|
||||
|
||||
// merge ID with transient many-to-one
|
||||
scope.inTransaction(
|
||||
session ->
|
||||
session.merge( transientCart )
|
||||
);
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
ShoppingCart updatedCart = session.get( ShoppingCart.class, "cart1" );
|
||||
// assertion for HHH-11274 - checking for exception
|
||||
new PersistenceUnitUtilImpl( scope.getSessionFactory() )
|
||||
.getIdentifier( transientCart.getLineItems().get( 0 ) );
|
||||
|
||||
assertEquals( 1, updatedCart.getLineItems().size() );
|
||||
assertEquals( "description2", updatedCart.getLineItems().get( 0 ).getDescription() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-10623")
|
||||
public void testMergeDetachedIdManyToOne(SessionFactoryScope scope) {
|
||||
ShoppingCart cart = new ShoppingCart( "cart1" );
|
||||
|
||||
scope.inTransaction(
|
||||
session ->
|
||||
session.persist( cart )
|
||||
);
|
||||
|
||||
// cart is detached now
|
||||
LineItem lineItem = new LineItem( 0, "description2", cart );
|
||||
cart.addLineItem( lineItem );
|
||||
|
||||
// merge lineItem with an ID with detached many-to-one
|
||||
scope.inTransaction(
|
||||
session ->
|
||||
session.merge( lineItem )
|
||||
);
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
ShoppingCart updatedCart = session.get( ShoppingCart.class, "cart1" );
|
||||
assertEquals( 1, updatedCart.getLineItems().size() );
|
||||
assertEquals( "description2", updatedCart.getLineItems().get( 0 ).getDescription() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-12007")
|
||||
public void testBindTransientEntityWithTransientKeyManyToOne(SessionFactoryScope scope) {
|
||||
|
||||
ShoppingCart cart = new ShoppingCart( "cart" );
|
||||
LineItem item = new LineItem( 0, "desc", cart );
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
String cartId = session.createQuery(
|
||||
"select c.id from Cart c left join c.lineItems i where i = :item",
|
||||
String.class
|
||||
).setParameter( "item", item ).uniqueResult();
|
||||
|
||||
assertNull( cartId );
|
||||
|
||||
assertFalse( session.contains( item ) );
|
||||
assertFalse( session.contains( cart ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-12007")
|
||||
public void testBindTransientEntityWithPersistentKeyManyToOne(SessionFactoryScope scope) {
|
||||
ShoppingCart cart = new ShoppingCart( "cart" );
|
||||
LineItem item = new LineItem( 0, "desc", cart );
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.persist( cart );
|
||||
String cartId = session.createQuery(
|
||||
"select c.id from Cart c left join c.lineItems i where i = :item",
|
||||
String.class
|
||||
).setParameter( "item", item ).uniqueResult();
|
||||
|
||||
assertNull( cartId );
|
||||
|
||||
assertFalse( session.contains( item ) );
|
||||
assertTrue( session.contains( cart ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-12007")
|
||||
public void testBindTransientEntityWithDetachedKeyManyToOne(SessionFactoryScope scope) {
|
||||
|
||||
ShoppingCart cart = new ShoppingCart( "cart" );
|
||||
LineItem item = new LineItem( 0, "desc", cart );
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
String cartId = session.createQuery(
|
||||
"select c.id from Cart c left join c.lineItems i where i = :item",
|
||||
String.class
|
||||
).setParameter( "item", item ).uniqueResult();
|
||||
|
||||
assertNull( cartId );
|
||||
|
||||
assertFalse( session.contains( item ) );
|
||||
assertFalse( session.contains( cart ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-12007")
|
||||
public void testBindTransientEntityWithCopiedKeyManyToOne(SessionFactoryScope scope) {
|
||||
|
||||
ShoppingCart cart = new ShoppingCart( "cart" );
|
||||
LineItem item = new LineItem( 0, "desc", new ShoppingCart( "cart" ) );
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
String cartId = session.createQuery(
|
||||
"select c.id from Cart c left join c.lineItems i where i = :item",
|
||||
String.class
|
||||
).setParameter( "item", item ).uniqueResult();
|
||||
|
||||
assertNull( cartId );
|
||||
|
||||
assertFalse( session.contains( item ) );
|
||||
assertFalse( session.contains( cart ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "Cart")
|
||||
public static class ShoppingCart implements Serializable {
|
||||
@Id
|
||||
@Column(name = "id", nullable = false)
|
||||
private String id;
|
||||
|
||||
@OneToMany(mappedBy = "cart", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private List<LineItem> lineItems = new ArrayList<>();
|
||||
|
||||
protected ShoppingCart() {
|
||||
}
|
||||
|
||||
public ShoppingCart(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List<LineItem> getLineItems() {
|
||||
return lineItems;
|
||||
}
|
||||
|
||||
public void addLineItem(LineItem lineItem) {
|
||||
lineItems.add( lineItem );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
ShoppingCart that = (ShoppingCart) o;
|
||||
return Objects.equals( id, that.id );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( id );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "LineItem")
|
||||
@IdClass(LineItem.Pk.class)
|
||||
public static class LineItem implements Serializable {
|
||||
|
||||
@Id
|
||||
@Column(name = "item_seq_number", nullable = false)
|
||||
private Integer sequenceNumber;
|
||||
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
@Id
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "cart_id")
|
||||
private ShoppingCart cart;
|
||||
|
||||
protected LineItem() {
|
||||
}
|
||||
|
||||
public LineItem(Integer sequenceNumber, String description, ShoppingCart cart) {
|
||||
this.sequenceNumber = sequenceNumber;
|
||||
this.description = description;
|
||||
this.cart = cart;
|
||||
}
|
||||
|
||||
public Integer getSequenceNumber() {
|
||||
return sequenceNumber;
|
||||
}
|
||||
|
||||
public ShoppingCart getCart() {
|
||||
return cart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( !( o instanceof LineItem ) ) {
|
||||
return false;
|
||||
}
|
||||
LineItem lineItem = (LineItem) o;
|
||||
return Objects.equals( getSequenceNumber(), lineItem.getSequenceNumber() ) &&
|
||||
Objects.equals( getCart(), lineItem.getCart() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( getSequenceNumber(), getCart() );
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public static class Pk implements Serializable {
|
||||
public Integer sequenceNumber;
|
||||
public String cart;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( !( o instanceof Pk ) ) {
|
||||
return false;
|
||||
}
|
||||
Pk pk = (Pk) o;
|
||||
return Objects.equals( sequenceNumber, pk.sequenceNumber ) &&
|
||||
Objects.equals( cart, pk.cart );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( sequenceNumber, cart );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -570,6 +570,11 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getIdentifier(Object entity, SessionFactoryImplementor sessionFactoryImplementor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) {
|
||||
}
|
||||
|
|
|
@ -580,6 +580,11 @@ public class PersisterClassProviderTest {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getIdentifier(Object entity, SessionFactoryImplementor sessionFactoryImplementor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) {
|
||||
}
|
||||
|
|
|
@ -212,6 +212,11 @@ public class CustomPersister implements EntityPersister {
|
|||
return ( (Custom) entity ).id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getIdentifier(Object entity, SessionFactoryImplementor sessionFactoryImplementor) {
|
||||
return ( (Custom) entity ).id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) {
|
||||
( (Custom) entity ).id = (String) id;
|
||||
|
|
|
@ -1,329 +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.annotations.derivedidentities.bidirectional;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.IdClass;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.jpa.internal.PersistenceUnitUtilImpl;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CompositeIdDerivedIdWithIdClassTest extends BaseCoreFunctionalTestCase {
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
ShoppingCart.class,
|
||||
LineItem.class
|
||||
};
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
Session s = openSession();
|
||||
s.getTransaction().begin();
|
||||
s.createQuery( "delete from LineItem" ).executeUpdate();
|
||||
s.createQuery( "delete from Cart" ).executeUpdate();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11328")
|
||||
public void testMergeTransientIdManyToOne() throws Exception {
|
||||
ShoppingCart transientCart = new ShoppingCart( "cart1" );
|
||||
transientCart.addLineItem( new LineItem( 0, "description2", transientCart ) );
|
||||
|
||||
// assertion for HHH-11274 - checking for exception
|
||||
final Object identifier = new PersistenceUnitUtilImpl( sessionFactory() ).getIdentifier( transientCart.getLineItems().get( 0 ) );
|
||||
|
||||
// merge ID with transient many-to-one
|
||||
Session s = openSession();
|
||||
s.getTransaction().begin();
|
||||
s.merge( transientCart );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
s = openSession();
|
||||
s.getTransaction().begin();
|
||||
ShoppingCart updatedCart = s.get( ShoppingCart.class, "cart1" );
|
||||
// assertion for HHH-11274 - checking for exception
|
||||
new PersistenceUnitUtilImpl( sessionFactory() ).getIdentifier( transientCart.getLineItems().get( 0 ) );
|
||||
assertEquals( 1, updatedCart.getLineItems().size() );
|
||||
assertEquals( "description2", updatedCart.getLineItems().get( 0 ).getDescription() );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-10623")
|
||||
public void testMergeDetachedIdManyToOne() throws Exception {
|
||||
ShoppingCart cart = new ShoppingCart("cart1");
|
||||
|
||||
Session s = openSession();
|
||||
s.getTransaction().begin();
|
||||
s.persist( cart );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
// cart is detached now
|
||||
LineItem lineItem = new LineItem( 0, "description2", cart );
|
||||
cart.addLineItem( lineItem );
|
||||
|
||||
// merge lineItem with an ID with detached many-to-one
|
||||
s = openSession();
|
||||
s.getTransaction().begin();
|
||||
s.merge( lineItem );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
s = openSession();
|
||||
s.getTransaction().begin();
|
||||
ShoppingCart updatedCart = s.get( ShoppingCart.class, "cart1" );
|
||||
assertEquals( 1, updatedCart.getLineItems().size() );
|
||||
assertEquals("description2", updatedCart.getLineItems().get( 0 ).getDescription());
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-12007")
|
||||
public void testBindTransientEntityWithTransientKeyManyToOne() {
|
||||
|
||||
ShoppingCart cart = new ShoppingCart( "cart" );
|
||||
LineItem item = new LineItem( 0, "desc", cart );
|
||||
|
||||
Session s = openSession();
|
||||
s.getTransaction().begin();
|
||||
|
||||
String cartId = s.createQuery(
|
||||
"select c.id from Cart c left join c.lineItems i where i = :item",
|
||||
String.class
|
||||
).setParameter( "item", item ).uniqueResult();
|
||||
|
||||
assertNull( cartId );
|
||||
|
||||
assertFalse( s.contains( item ) );
|
||||
assertFalse( s.contains( cart ) );
|
||||
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-12007")
|
||||
public void testBindTransientEntityWithPersistentKeyManyToOne() {
|
||||
ShoppingCart cart = new ShoppingCart( "cart" );
|
||||
LineItem item = new LineItem( 0, "desc", cart );
|
||||
|
||||
Session s = openSession();
|
||||
s.getTransaction().begin();
|
||||
|
||||
session.persist( cart );
|
||||
String cartId = s.createQuery(
|
||||
"select c.id from Cart c left join c.lineItems i where i = :item",
|
||||
String.class
|
||||
).setParameter( "item", item ).uniqueResult();
|
||||
|
||||
assertNull( cartId );
|
||||
|
||||
assertFalse( s.contains( item ) );
|
||||
assertTrue( s.contains( cart ) );
|
||||
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-12007")
|
||||
public void testBindTransientEntityWithDetachedKeyManyToOne() {
|
||||
Session s = openSession();
|
||||
s.getTransaction().begin();
|
||||
|
||||
ShoppingCart cart = new ShoppingCart( "cart" );
|
||||
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
LineItem item = new LineItem( 0, "desc", cart );
|
||||
|
||||
s = openSession();
|
||||
s.getTransaction().begin();
|
||||
|
||||
String cartId = s.createQuery(
|
||||
"select c.id from Cart c left join c.lineItems i where i = :item",
|
||||
String.class
|
||||
).setParameter( "item", item ).uniqueResult();
|
||||
|
||||
assertNull( cartId );
|
||||
|
||||
assertFalse( s.contains( item ) );
|
||||
assertFalse( s.contains( cart ) );
|
||||
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-12007")
|
||||
public void testBindTransientEntityWithCopiedKeyManyToOne() {
|
||||
Session s = openSession();
|
||||
s.getTransaction().begin();
|
||||
|
||||
ShoppingCart cart = new ShoppingCart( "cart" );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
LineItem item = new LineItem( 0, "desc", new ShoppingCart( "cart" ) );
|
||||
|
||||
s = openSession();
|
||||
s.getTransaction().begin();
|
||||
|
||||
String cartId = s.createQuery(
|
||||
"select c.id from Cart c left join c.lineItems i where i = :item",
|
||||
String.class
|
||||
).setParameter( "item", item ).uniqueResult();
|
||||
|
||||
assertNull( cartId );
|
||||
|
||||
assertFalse( s.contains( item ) );
|
||||
assertFalse( s.contains( cart ) );
|
||||
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Entity(name = "Cart")
|
||||
public static class ShoppingCart implements Serializable{
|
||||
@Id
|
||||
@Column(name = "id", nullable = false)
|
||||
private String id;
|
||||
|
||||
@OneToMany(mappedBy = "cart", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private List<LineItem> lineItems = new ArrayList<>();
|
||||
|
||||
protected ShoppingCart() {
|
||||
}
|
||||
|
||||
public ShoppingCart(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List<LineItem> getLineItems() {
|
||||
return lineItems;
|
||||
}
|
||||
|
||||
public void addLineItem(LineItem lineItem) {
|
||||
lineItems.add(lineItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ShoppingCart that = (ShoppingCart) o;
|
||||
return Objects.equals( id, that.id );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "LineItem")
|
||||
@IdClass(LineItem.Pk.class)
|
||||
public static class LineItem implements Serializable {
|
||||
|
||||
@Id
|
||||
@Column(name = "item_seq_number", nullable = false)
|
||||
private Integer sequenceNumber;
|
||||
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
@Id
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "cart_id")
|
||||
private ShoppingCart cart;
|
||||
|
||||
protected LineItem() {
|
||||
}
|
||||
|
||||
public LineItem(Integer sequenceNumber, String description, ShoppingCart cart) {
|
||||
this.sequenceNumber = sequenceNumber;
|
||||
this.description = description;
|
||||
this.cart = cart;
|
||||
}
|
||||
|
||||
public Integer getSequenceNumber() {
|
||||
return sequenceNumber;
|
||||
}
|
||||
|
||||
public ShoppingCart getCart() {
|
||||
return cart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof LineItem)) return false;
|
||||
LineItem lineItem = (LineItem) o;
|
||||
return Objects.equals(getSequenceNumber(), lineItem.getSequenceNumber()) &&
|
||||
Objects.equals(getCart(), lineItem.getCart());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( getSequenceNumber(), getCart() );
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public static class Pk implements Serializable {
|
||||
public Integer sequenceNumber;
|
||||
public String cart;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Pk)) return false;
|
||||
Pk pk = (Pk) o;
|
||||
return Objects.equals(sequenceNumber, pk.sequenceNumber) &&
|
||||
Objects.equals(cart, pk.cart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(sequenceNumber, cart);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue