From b3e0d363bde0f610b26e4a705755ee9e259947aa Mon Sep 17 00:00:00 2001 From: Vlad Mihalcea Date: Wed, 27 Jun 2018 11:45:02 +0300 Subject: [PATCH] HHH-12738 - Session/EntityManager is closed in ForeignGenerator (JTA setup) Add test case proving the issue does not replicate for either RESOURCE_LOCAL or JTA transactions --- .../foreign/ForeignGeneratorJtaTest.java | 50 +++ .../ForeignGeneratorResourceLocalTest.java | 310 ++++++++++++++++++ 2 files changed, 360 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/idgen/foreign/ForeignGeneratorJtaTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/idgen/foreign/ForeignGeneratorResourceLocalTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/idgen/foreign/ForeignGeneratorJtaTest.java b/hibernate-core/src/test/java/org/hibernate/test/idgen/foreign/ForeignGeneratorJtaTest.java new file mode 100644 index 0000000000..ba9d9f539d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/idgen/foreign/ForeignGeneratorJtaTest.java @@ -0,0 +1,50 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.idgen.foreign; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +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.MapsId; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.hibernate.cfg.Environment; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.jta.TestingJtaBootstrap; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; + +/** + * @author Vlad Mihalcea + */ +@TestForIssue(jiraKey = "HHH-12738") +public class ForeignGeneratorJtaTest extends ForeignGeneratorResourceLocalTest { + + @Override + protected void addConfigOptions(Map options) { + TestingJtaBootstrap.prepare( options ); + options.put( Environment.TRANSACTION_COORDINATOR_STRATEGY, "jta" ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/idgen/foreign/ForeignGeneratorResourceLocalTest.java b/hibernate-core/src/test/java/org/hibernate/test/idgen/foreign/ForeignGeneratorResourceLocalTest.java new file mode 100644 index 0000000000..929ba9347d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/idgen/foreign/ForeignGeneratorResourceLocalTest.java @@ -0,0 +1,310 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.idgen.foreign; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +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.MapsId; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; + +/** + * @author Vlad Mihalcea + */ +@TestForIssue( jiraKey = "HHH-12738" ) +public class ForeignGeneratorResourceLocalTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Contract.class, + Customer.class, + CustomerContractRelation.class + }; + } + + @Test + public void addRelationImplicitFlush() throws Exception { + + Long contractId = doInJPA( this::entityManagerFactory, entityManager -> { + Contract contract = new Contract(); + + entityManager.persist( contract ); + return contract.getId(); + } ); + + Long customerId = doInJPA( this::entityManagerFactory, entityManager -> { + Customer customer = new Customer(); + + entityManager.persist( customer ); + return customer.getId(); + } ); + + doInJPA( this::entityManagerFactory, entityManager -> { + + Customer customer = entityManager.createQuery( + "SELECT c " + + "FROM Customer c " + + " LEFT JOIN FETCH c.contractRelations " + + " WHERE c.id = :customerId", Customer.class ) + .setParameter( "customerId", customerId ) + .getSingleResult(); + + CustomerContractRelation relation = new CustomerContractRelation(); + relation.setContractId( contractId ); + customer.addContractRelation( relation ); + } ); + } + + @Test + public void addRelationExplicitFlush() throws Exception { + Long contractId = doInJPA( this::entityManagerFactory, entityManager -> { + Contract contract = new Contract(); + + entityManager.persist( contract ); + return contract.getId(); + } ); + + Long customerId = doInJPA( this::entityManagerFactory, entityManager -> { + Customer customer = new Customer(); + + entityManager.persist( customer ); + return customer.getId(); + } ); + + doInJPA( this::entityManagerFactory, entityManager -> { + + Customer customer = entityManager.createQuery( + "SELECT c " + + "FROM Customer c " + + " LEFT JOIN FETCH c.contractRelations " + + " WHERE c.id = :customerId", Customer.class ) + .setParameter( "customerId", customerId ) + .getSingleResult(); + + CustomerContractRelation relation = new CustomerContractRelation(); + relation.setContractId( contractId ); + customer.addContractRelation( relation ); + + entityManager.flush(); + } ); + } + + @Test + public void addRelationImplicitFlushOneTx() throws Exception { + + doInJPA( this::entityManagerFactory, entityManager -> { + Contract contract = new Contract(); + + entityManager.persist( contract ); + + Customer customer = new Customer(); + + entityManager.persist( customer ); + + customer = entityManager.createQuery( + "SELECT c " + + "FROM Customer c " + + " LEFT JOIN FETCH c.contractRelations " + + " WHERE c.id = :customerId", Customer.class ) + .setParameter( "customerId", customer.getId() ) + .getSingleResult(); + + CustomerContractRelation relation = new CustomerContractRelation(); + relation.setContractId( customer.getId() ); + customer.addContractRelation( relation ); + } ); + } + + @Entity(name = "Contract") + @Table(name = "CONTRACT") + public static class Contract { + @Id + @GeneratedValue + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + } + + @Entity(name = "Customer") + @Table(name = "CUSTOMER") + public static class Customer { + @Id + @GeneratedValue + private Long id; + + @OneToMany(mappedBy = "customer", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE }, + orphanRemoval = true, targetEntity = CustomerContractRelation.class) + private final Set contractRelations = new HashSet<>(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Set getContractRelations() { + return contractRelations; + } + + public boolean addContractRelation(CustomerContractRelation relation) { + if ( relation != null ) { + relation.setCustomer( this ); + return contractRelations.add( relation ); + } + return false; + } + } + + @Embeddable + public static class CustomerContractId implements Serializable { + private static final long serialVersionUID = 1115591676841551563L; + + @Column(name = "CUSTOMERID", nullable = false) + private Long customerId; + + @Column(name = "CONTRACTID", nullable = false) + private Long contractId; + + public Long getCustomerId() { + return customerId; + } + + public void setCustomerId(Long customerId) { + this.customerId = customerId; + } + + public Long getContractId() { + return contractId; + } + + public void setContractId(Long contractId) { + this.contractId = contractId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ( ( contractId == null ) ? 0 : contractId.hashCode() ); + result = prime * result + ( ( customerId == null ) ? 0 : customerId.hashCode() ); + return result; + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) { + return true; + } + if ( obj == null ) { + return false; + } + if ( getClass() != obj.getClass() ) { + return false; + } + CustomerContractId other = (CustomerContractId) obj; + if ( contractId == null ) { + if ( other.contractId != null ) { + return false; + } + } + else if ( !contractId.equals( other.contractId ) ) { + return false; + } + if ( customerId == null ) { + if ( other.customerId != null ) { + return false; + } + } + else if ( !customerId.equals( other.customerId ) ) { + return false; + } + return true; + } + } + + @Entity(name = "CustomerContractRelation") + @Table(name = "CUSTOMER_CONTRACT_RELATION") + public static class CustomerContractRelation { + @EmbeddedId + private final CustomerContractId id = new CustomerContractId(); + + @Temporal(TemporalType.TIMESTAMP) + @Column(nullable = true, name = "SIGNEDONDATE") + private Date signedOn; + + @MapsId(value = "customerId") + @JoinColumn(name = "CUSTOMERID", nullable = false) + @ManyToOne(fetch = FetchType.LAZY) + private Customer customer; + + public CustomerContractId getId() { + return id; + } + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } + + public Date getSignedOn() { + return signedOn; + } + + public void setSignedOn(Date signedOn) { + this.signedOn = signedOn; + } + + public Long getCustomerId() { + return id.getCustomerId(); + } + + public void setCustomerId(Long customerId) { + id.setCustomerId( customerId ); + } + + public Long getContractId() { + return id.getContractId(); + } + + public void setContractId(Long contractId) { + id.setContractId( contractId ); + } + } +}