From 41be577ffb87e373f62ff1c009b483c1ceac1eda Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 30 Apr 2024 12:40:23 -0500 Subject: [PATCH] HHH-18039 - EntityListeners defined in XML should replace those from annotations, not add to --- .../xml/internal/ManagedTypeProcessor.java | 6 +- .../xml/internal/XmlAnnotationHelper.java | 38 +++-- .../listeneroverrides/CallbackTarget.java | 83 +++++++++ .../callback/listeneroverrides/LineItem.java | 67 ++++++++ .../listeneroverrides/LineItemSuper.java | 34 ++++ .../callback/listeneroverrides/ListenerA.java | 56 ++++++ .../callback/listeneroverrides/ListenerB.java | 56 ++++++ .../listeneroverrides/ListenerBase.java | 57 +++++++ .../callback/listeneroverrides/ListenerC.java | 56 ++++++ .../callback/listeneroverrides/Order.java | 72 ++++++++ .../listeneroverrides/OverridesTests.java | 160 ++++++++++++++++++ .../callback/listeneroverrides/Product.java | 56 ++++++ .../mappings/callbacks/listener_overrides.xml | 45 +++++ 13 files changed, 764 insertions(+), 22 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/CallbackTarget.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/LineItem.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/LineItemSuper.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerA.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerB.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerBase.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerC.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/Order.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/OverridesTests.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/Product.java create mode 100644 hibernate-core/src/test/resources/mappings/callbacks/listener_overrides.xml diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/ManagedTypeProcessor.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/ManagedTypeProcessor.java index a607fdf85f..52d504f991 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/ManagedTypeProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/ManagedTypeProcessor.java @@ -854,11 +854,7 @@ public class ManagedTypeProcessor { xmlDocumentContext ); - if ( jaxbEntity.getEntityListenerContainer() != null ) { - jaxbEntity.getEntityListenerContainer().getEntityListeners().forEach( ( jaxbEntityListener -> { - XmlAnnotationHelper.applyEntityListener( jaxbEntityListener, classDetails, xmlDocumentContext ); - } ) ); - } + XmlAnnotationHelper.applyEntityListeners( jaxbEntity.getEntityListenerContainer(), classDetails, xmlDocumentContext ); } public static void processCompleteEmbeddable( diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java index d0a3f255ea..1501f1814a 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java @@ -50,6 +50,7 @@ import org.hibernate.boot.jaxb.mapping.spi.JaxbDiscriminatorColumnImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbDiscriminatorFormulaImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbElementCollectionImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbEntity; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityListenerContainerImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityListenerImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbGeneratedValueImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbHbmFilterImpl; @@ -1178,31 +1179,34 @@ public class XmlAnnotationHelper { idClassAnn.setAttributeValue( "value", idClassImpl ); } - static void applyEntityListener( - JaxbEntityListenerImpl jaxbEntityListener, + public static void applyEntityListeners( + JaxbEntityListenerContainerImpl entityListenerContainer, MutableClassDetails classDetails, XmlDocumentContext xmlDocumentContext) { - final MutableAnnotationUsage listenersUsage = classDetails.applyAnnotationUsage( + if ( entityListenerContainer == null || entityListenerContainer.getEntityListeners().isEmpty() ) { + return; + } + + final MutableAnnotationUsage listenersUsage = classDetails.replaceAnnotationUsage( JpaAnnotations.ENTITY_LISTENERS, xmlDocumentContext.getModelBuildingContext() ); + final List values = arrayList( entityListenerContainer.getEntityListeners().size() ); + listenersUsage.setAttributeValue( "value", values ); - final MutableClassDetails entityListenerClass = xmlDocumentContext.resolveJavaType( jaxbEntityListener.getClazz() ); - applyLifecycleCallbacks( - jaxbEntityListener, - JpaEventListenerStyle.LISTENER, - entityListenerClass, - xmlDocumentContext - ); - final List values = listenersUsage.getAttributeValue( "value" ); - if ( values != null ) { + entityListenerContainer.getEntityListeners().forEach( (jaxbEntityListener) -> { + final MutableClassDetails entityListenerClass = xmlDocumentContext.resolveJavaType( jaxbEntityListener.getClazz() ); + applyLifecycleCallbacks( + jaxbEntityListener, + JpaEventListenerStyle.LISTENER, + entityListenerClass, + xmlDocumentContext + ); values.add( entityListenerClass ); - } - else { - listenersUsage.setAttributeValue( "value", new ArrayList<>( List.of( entityListenerClass ) ) ); - } + } ); + } - + static void applyLifecycleCallbacks( JaxbLifecycleCallbackContainer lifecycleCallbackContainer, JpaEventListenerStyle callbackType, diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/CallbackTarget.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/CallbackTarget.java new file mode 100644 index 0000000000..ebdfe67d7e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/CallbackTarget.java @@ -0,0 +1,83 @@ +/* + * 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.jpa.compliance.callback.listeneroverrides; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Steve Ebersole + */ +public class CallbackTarget { + private List prePersistCallbacks = new ArrayList<>(); + private List postPersistCallbacks = new ArrayList<>(); + + private List preUpdateCallbacks = new ArrayList<>(); + private List postUpdateCallbacks = new ArrayList<>(); + + private List preRemoveCallbacks = new ArrayList<>(); + private List postRemoveCallbacks = new ArrayList<>(); + + private List postLoadCallbacks = new ArrayList<>(); + + public void prePersistCalled(String listenerName) { + prePersistCallbacks.add( listenerName ); + } + + public void postPersistCalled(String listenerName) { + postPersistCallbacks.add( listenerName ); + } + + public void preUpdateCalled(String listenerName) { + preUpdateCallbacks.add( listenerName ); + } + + public void postUpdateCalled(String listenerName) { + postUpdateCallbacks.add( listenerName ); + } + + public void preRemoveCalled(String listenerName) { + preRemoveCallbacks.add( listenerName ); + } + + public void postRemoveCalled(String listenerName) { + postRemoveCallbacks.add( listenerName ); + } + + public void postLoadCalled(String listenerName) { + postLoadCallbacks.add( listenerName ); + } + + + public List getPrePersistCallbacks() { + return prePersistCallbacks; + } + + public List getPostPersistCallbacks() { + return postPersistCallbacks; + } + + public List getPreUpdateCallbacks() { + return preUpdateCallbacks; + } + + public List getPostUpdateCallbacks() { + return postUpdateCallbacks; + } + + public List getPreRemoveCallbacks() { + return preRemoveCallbacks; + } + + public List getPostRemoveCallbacks() { + return postRemoveCallbacks; + } + + public List getPostLoadCallbacks() { + return postLoadCallbacks; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/LineItem.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/LineItem.java new file mode 100644 index 0000000000..49ecb85146 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/LineItem.java @@ -0,0 +1,67 @@ +/* + * 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.jpa.compliance.callback.listeneroverrides; + +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.ExcludeDefaultListeners; +import jakarta.persistence.ExcludeSuperclassListeners; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; + +/** + * @author Steve Ebersole + */ +@Entity +@EntityListeners({ListenerC.class, ListenerB.class}) +@ExcludeDefaultListeners() +@ExcludeSuperclassListeners +public class LineItem extends LineItemSuper { + @Id + private Integer id; + @ManyToOne + @JoinColumn(name = "order_fk") + private Order order; + @ManyToOne + @JoinColumn(name = "product_fk") + private Product product; + + public LineItem() { + } + + public LineItem(Integer id, Order order, Product product, int quantity) { + super( quantity ); + this.id = id; + this.order = order; + this.product = product; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Order getOrder() { + return order; + } + + public void setOrder(Order order) { + this.order = order; + } + + public Product getProduct() { + return product; + } + + public void setProduct(Product product) { + this.product = product; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/LineItemSuper.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/LineItemSuper.java new file mode 100644 index 0000000000..ced2e55685 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/LineItemSuper.java @@ -0,0 +1,34 @@ +/* + * 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.jpa.compliance.callback.listeneroverrides; + +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; + +/** + * @author Steve Ebersole + */ +@MappedSuperclass +@EntityListeners({ListenerA.class, ListenerB.class}) +public class LineItemSuper extends CallbackTarget { + private int quantity; + + public LineItemSuper() { + } + + public LineItemSuper(int quantity) { + this.quantity = quantity; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerA.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerA.java new file mode 100644 index 0000000000..5f97219959 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerA.java @@ -0,0 +1,56 @@ +/* + * 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.jpa.compliance.callback.listeneroverrides; + +import jakarta.persistence.PostLoad; +import jakarta.persistence.PostPersist; +import jakarta.persistence.PostRemove; +import jakarta.persistence.PostUpdate; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreRemove; +import jakarta.persistence.PreUpdate; + +/** + * @author Steve Ebersole + */ +public class ListenerA { + @PrePersist + protected void prePersist(CallbackTarget target) { + target.prePersistCalled( "ListenerA" ); + } + + @PostPersist + protected void postPersist(CallbackTarget target) { + target.postPersistCalled( "ListenerA" ); + } + + @PreRemove + protected void preRemove(CallbackTarget target) { + target.preRemoveCalled( "ListenerA" ); + } + + @PostRemove + protected void postRemove(CallbackTarget target) { + target.postRemoveCalled( "ListenerA" ); + } + + @PreUpdate + protected void preUpdate(CallbackTarget target) { + target.preUpdateCalled( "ListenerA" ); + } + + @PostUpdate + protected void postUpdate(CallbackTarget target) { + target.postUpdateCalled( "ListenerA" ); + } + + @PostLoad + protected void postLoad(CallbackTarget target) { + target.postLoadCalled( "ListenerA" ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerB.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerB.java new file mode 100644 index 0000000000..d4715f3688 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerB.java @@ -0,0 +1,56 @@ +/* + * 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.jpa.compliance.callback.listeneroverrides; + +import jakarta.persistence.PostLoad; +import jakarta.persistence.PostPersist; +import jakarta.persistence.PostRemove; +import jakarta.persistence.PostUpdate; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreRemove; +import jakarta.persistence.PreUpdate; + +/** + * @author Steve Ebersole + */ +public class ListenerB { + @PrePersist + protected void prePersist(CallbackTarget target) { + target.prePersistCalled( "ListenerB" ); + } + + @PostPersist + protected void postPersist(CallbackTarget target) { + target.postPersistCalled( "ListenerB" ); + } + + @PreRemove + protected void preRemove(CallbackTarget target) { + target.preRemoveCalled( "ListenerB" ); + } + + @PostRemove + protected void postRemove(CallbackTarget target) { + target.postRemoveCalled( "ListenerB" ); + } + + @PreUpdate + protected void preUpdate(CallbackTarget target) { + target.preUpdateCalled( "ListenerB" ); + } + + @PostUpdate + protected void postUpdate(CallbackTarget target) { + target.postUpdateCalled( "ListenerB" ); + } + + @PostLoad + protected void postLoad(CallbackTarget target) { + target.postLoadCalled( "ListenerB" ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerBase.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerBase.java new file mode 100644 index 0000000000..a20a12f567 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerBase.java @@ -0,0 +1,57 @@ +/* + * 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.jpa.compliance.callback.listeneroverrides; + +import jakarta.persistence.PostLoad; +import jakarta.persistence.PostPersist; +import jakarta.persistence.PostRemove; +import jakarta.persistence.PostUpdate; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreRemove; +import jakarta.persistence.PreUpdate; + +/** + * @author Steve Ebersole + */ +public abstract class ListenerBase { + protected abstract String getListenerName(); + + @PrePersist + protected void prePersist(CallbackTarget target) { + target.prePersistCalled( getListenerName() ); + } + + @PostPersist + protected void postPersist(CallbackTarget target) { + target.postPersistCalled( getListenerName() ); + } + + @PreRemove + protected void preRemove(CallbackTarget target) { + target.preRemoveCalled( getListenerName() ); + } + + @PostRemove + protected void postRemove(CallbackTarget target) { + target.postRemoveCalled( getListenerName() ); + } + + @PreUpdate + protected void preUpdate(CallbackTarget target) { + target.preUpdateCalled( getListenerName() ); + } + + @PostUpdate + protected void postUpdate(CallbackTarget target) { + target.postUpdateCalled( getListenerName() ); + } + + @PostLoad + protected void postLoad(CallbackTarget target) { + target.postLoadCalled( getListenerName() ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerC.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerC.java new file mode 100644 index 0000000000..ebd85c4d26 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/ListenerC.java @@ -0,0 +1,56 @@ +/* + * 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.jpa.compliance.callback.listeneroverrides; + +import jakarta.persistence.PostLoad; +import jakarta.persistence.PostPersist; +import jakarta.persistence.PostRemove; +import jakarta.persistence.PostUpdate; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreRemove; +import jakarta.persistence.PreUpdate; + +/** + * @author Steve Ebersole + */ +public class ListenerC { + + @PrePersist + protected void prePersist(CallbackTarget target) { + target.prePersistCalled( "ListenerC" ); + } + + @PostPersist + protected void postPersist(CallbackTarget target) { + target.postPersistCalled( "ListenerC" ); + } + + @PreRemove + protected void preRemove(CallbackTarget target) { + target.preRemoveCalled( "ListenerC" ); + } + + @PostRemove + protected void postRemove(CallbackTarget target) { + target.postRemoveCalled( "ListenerC" ); + } + + @PreUpdate + protected void preUpdate(CallbackTarget target) { + target.preUpdateCalled( "ListenerC" ); + } + + @PostUpdate + protected void postUpdate(CallbackTarget target) { + target.postUpdateCalled( "ListenerC" ); + } + + @PostLoad + protected void postLoad(CallbackTarget target) { + target.postLoadCalled( "ListenerC" ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/Order.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/Order.java new file mode 100644 index 0000000000..19e5c78c18 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/Order.java @@ -0,0 +1,72 @@ +/* + * 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.jpa.compliance.callback.listeneroverrides; + +import java.util.ArrayList; +import java.util.Collection; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +/** + * @author Steve Ebersole + */ +@Entity +@Table(name = "orders") +@EntityListeners({ListenerC.class, ListenerB.class}) +public class Order extends CallbackTarget { + @Id + private Integer id; + @Column(name = "total_price") + private double totalPrice; + @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) + private Collection lineItems = new java.util.ArrayList<>(); + + public Order() { + } + + public Order(Integer id, double totalPrice) { + this.id = id; + this.totalPrice = totalPrice; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public double getTotalPrice() { + return totalPrice; + } + + public void setTotalPrice(double totalPrice) { + this.totalPrice = totalPrice; + } + + public Collection getLineItems() { + return lineItems; + } + + public void setLineItems(Collection lineItems) { + this.lineItems = lineItems; + } + + public void addLineItem(LineItem lineItem) { + if ( lineItems == null ) { + lineItems = new ArrayList<>(); + } + lineItems.add( lineItem ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/OverridesTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/OverridesTests.java new file mode 100644 index 0000000000..bd05b35a02 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/OverridesTests.java @@ -0,0 +1,160 @@ +/* + * 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.jpa.compliance.callback.listeneroverrides; + +import java.util.List; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +@SuppressWarnings("JUnitMalformedDeclaration") +@DomainModel( + annotatedClasses = {Product.class,Order.class, LineItemSuper.class, LineItem.class}, + xmlMappings = "mappings/callbacks/listener_overrides.xml" +) +@SessionFactory +public class OverridesTests { + public static final String[] LISTENER_ABC = {"ListenerA", "ListenerB", "ListenerC"}; + public static final String[] LISTENER_BC = {"ListenerB", "ListenerC"}; + + @Test + void testSimplePersist(SessionFactoryScope scope) { + final Product product = new Product( 1, "987654321", 123 ); + scope.inTransaction( (session) -> { + session.persist( product ); + assertThat( product.getPrePersistCallbacks() ).containsExactly( LISTENER_ABC ); + } ); + assertThat( product.getPostPersistCallbacks() ).containsExactly( LISTENER_ABC ); + } + + @Test + void testCascadingPersist(SessionFactoryScope scope) { + final Product product = new Product( 1, "987654321", 123 ); + final Order order = new Order( 1, 246 ); + final LineItem lineItem = new LineItem( 1, order, product, 2 ); + order.addLineItem( lineItem ); + + scope.inTransaction( (session) -> { + session.persist( product ); + session.persist( order ); + + assertThat( product.getPrePersistCallbacks() ).containsExactly( LISTENER_ABC ); + assertThat( order.getPrePersistCallbacks() ).containsExactly( LISTENER_ABC ); + assertThat( lineItem.getPrePersistCallbacks() ).containsExactly( LISTENER_BC ); + } ); + + assertThat( product.getPostPersistCallbacks() ).containsExactly( LISTENER_ABC ); + assertThat( order.getPostPersistCallbacks() ).containsExactly( LISTENER_ABC ); + assertThat( lineItem.getPostPersistCallbacks() ).containsExactly( LISTENER_BC ); + } + + @Test + void testSimpleRemove(SessionFactoryScope scope) { + final Product product = new Product( 1, "987654321", 123 ); + scope.inTransaction( (session) -> { + session.persist( product ); + } ); + scope.inTransaction( (session) -> { + session.remove( product ); + assertThat( product.getPreRemoveCallbacks() ).containsExactly( LISTENER_ABC ); + } ); + assertThat( product.getPostRemoveCallbacks() ).containsExactly( LISTENER_ABC ); + } + + @Test + void testCascadingRemove(SessionFactoryScope scope) { + final Product product = new Product( 1, "987654321", 123 ); + final Order order = new Order( 1, 246 ); + final LineItem lineItem = new LineItem( 1, order, product, 2 ); + order.addLineItem( lineItem ); + + scope.inTransaction( (session) -> { + session.persist( product ); + session.persist( order ); + } ); + + scope.inTransaction( (session) -> { + session.remove( order ); + assertThat( order.getPreRemoveCallbacks() ).containsExactly( LISTENER_ABC ); + assertThat( lineItem.getPreRemoveCallbacks() ).containsExactly( LISTENER_BC ); + } ); + + assertThat( order.getPostRemoveCallbacks() ).containsExactly( LISTENER_ABC ); + assertThat( lineItem.getPostRemoveCallbacks() ).containsExactly( LISTENER_BC ); + } + + @Test + void testSimpleUpdate(SessionFactoryScope scope) { + scope.inTransaction( (session) -> { + final Product product = new Product( 1, "987654321", 123 ); + session.persist( product ); + } ); + + + final Product updated = scope.fromTransaction( (session) -> { + final Product product = session.find( Product.class, 1 ); + + assertThat( product.getPreUpdateCallbacks() ).isEmpty(); + assertThat( product.getPostUpdateCallbacks() ).isEmpty(); + + product.setCost( 789 ); + + return product; + } ); + + assertThat( updated.getPreUpdateCallbacks() ).containsExactly( LISTENER_ABC ); + assertThat( updated.getPostUpdateCallbacks() ).containsExactly( LISTENER_ABC ); + } + + @Test + void testLoading(SessionFactoryScope scope) { + scope.inTransaction( (session) -> { + final Product product = new Product( 1, "987654321", 123 ); + final Order order = new Order( 1, 246 ); + final LineItem lineItem = new LineItem( 1, order, product, 2 ); + order.addLineItem( lineItem ); + + session.persist( product ); + session.persist( order ); + } ); + + scope.inTransaction( (session) -> { + final Product product = session.find( Product.class, 1 ); + assertThat( product.getPostLoadCallbacks() ).containsExactly( LISTENER_ABC ); + } ); + + scope.inTransaction( (session) -> { + final List products = session.createSelectionQuery( "from Product", Product.class ).list(); + products.forEach( (product) -> { + assertThat( product.getPostLoadCallbacks() ).containsExactly( LISTENER_ABC ); + } ); + } ); + + scope.inTransaction( (session) -> { + final LineItem lineItem = session.find( LineItem.class, 1 ); + assertThat( lineItem.getPostLoadCallbacks() ).containsExactly( LISTENER_BC ); + assertThat( lineItem.getOrder().getPostLoadCallbacks() ).containsExactly( LISTENER_ABC ); + } ); + } + + @AfterEach + void dropData(SessionFactoryScope scope) { + scope.inTransaction( (session) -> { + session.createMutationQuery( "delete LineItem" ).executeUpdate(); + session.createMutationQuery( "delete Order" ).executeUpdate(); + session.createMutationQuery( "delete Product" ).executeUpdate(); + } ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/Product.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/Product.java new file mode 100644 index 0000000000..2e825a34d4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listeneroverrides/Product.java @@ -0,0 +1,56 @@ +/* + * 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.jpa.compliance.callback.listeneroverrides; + +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.Id; + +/** + * @author Steve Ebersole + */ +@Entity +@EntityListeners({ListenerC.class, ListenerB.class}) +public class Product extends CallbackTarget { + @Id + private Integer id; + private String partNumber; + private double cost; + + public Product() { + } + + public Product(Integer id, String partNumber, double cost) { + this.id = id; + this.partNumber = partNumber; + this.cost = cost; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getPartNumber() { + return partNumber; + } + + public void setPartNumber(String partNumber) { + this.partNumber = partNumber; + } + + public double getCost() { + return cost; + } + + public void setCost(double cost) { + this.cost = cost; + } +} diff --git a/hibernate-core/src/test/resources/mappings/callbacks/listener_overrides.xml b/hibernate-core/src/test/resources/mappings/callbacks/listener_overrides.xml new file mode 100644 index 0000000000..586455b913 --- /dev/null +++ b/hibernate-core/src/test/resources/mappings/callbacks/listener_overrides.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + org.hibernate.orm.test.jpa.compliance.callback.listeneroverrides + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file