HHH-11916 - Unnecessary initialization of lazy collection on PERSIST cascade

This commit is contained in:
Viefhaus 2017-08-08 09:57:33 +02:00 committed by Vlad Mihalcea
parent 65c6964c96
commit 4235d75610
2 changed files with 242 additions and 1 deletions

View File

@ -304,7 +304,7 @@ public class CascadingActions {
CollectionType collectionType,
Object collection) {
// persists don't cascade to uninitialized collections
return getAllElementsIterator( session, collectionType, collection );
return getLoadedElementsIterator( session, collectionType, collection );
}
@Override

View File

@ -0,0 +1,241 @@
/*
* 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.mapping.cascade;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.LazyInitializationException;
import org.hibernate.Session;
import org.hibernate.stat.SessionStatistics;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Assert;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.fail;
/**
* Testing relationships between components: example invoice -> invoice line
*
* @author Jan-Oliver Lustig, Sebastian Viefhaus
*/
public class PersistOnLazyCollectionTest extends BaseCoreFunctionalTestCase{
static String RECEIPT_A = "Receipt A";
static String INVOICE_A = "Invoice A";
static String INVOICELINE_A = "InvoiceLine A";
static String INVOICELINE_B = "InvoiceLine B";
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
Invoice.class,
InvoiceLine.class,
Receipt.class
};
}
public Invoice createInvoiceWithTwoInvoiceLines(Session session) {
InvoiceLine lineA = new InvoiceLine(INVOICELINE_A);
InvoiceLine lineB = new InvoiceLine(INVOICELINE_B);
Invoice invoice = new Invoice(INVOICE_A);
invoice.addInvoiceLine(lineA);
invoice.addInvoiceLine(lineB);
session.persist(invoice);
return invoice;
}
@Test
@TestForIssue( jiraKey = "HHH-11916" )
public void testPersistOnAlreadyPersistentEntityWithUninitializedLazyCollection() {
final Invoice _invoice = doInHibernate( this::sessionFactory, this::createInvoiceWithTwoInvoiceLines );
Invoice invoiceAfter = doInHibernate( this::sessionFactory, session -> {
SessionStatistics stats = session.getStatistics();
// load invoice, invoiceLines should not be loaded
Invoice invoice = session.get(Invoice.class, _invoice.getId());
Assert.assertEquals(
"Invoice lines should not be initialized while loading the invoice, " +
"because of the lazy association.", 1, stats.getEntityCount());
invoice.setName(invoice.getName() + " !");
return invoice;
} );
try {
invoiceAfter.getInvoiceLines().size();
fail("Should throw LazyInitializationException");
}
catch (LazyInitializationException expected) {
}
}
@Test
@TestForIssue( jiraKey = "HHH-11916" )
public void testPersistOnNewEntityRelatedToAlreadyPersistentEntityWithUninitializedLazyCollection() {
final Invoice _invoice = doInHibernate( this::sessionFactory, this::createInvoiceWithTwoInvoiceLines );
Invoice invoiceAfter = doInHibernate( this::sessionFactory, session -> {
SessionStatistics stats = session.getStatistics();
// load invoice, invoiceLines should not be loaded
Invoice invoice = session.get(Invoice.class, _invoice.getId());
Assert.assertEquals(
"Invoice lines should not be initialized while loading the invoice, " +
"because of the lazy association.", 1, stats.getEntityCount());
Receipt receipt = new Receipt(RECEIPT_A);
receipt.setInvoice(invoice);
session.persist(receipt);
return invoice;
} );
try {
invoiceAfter.getInvoiceLines().size();
fail("Should throw LazyInitializationException");
}
catch (LazyInitializationException expected) {
}
}
@Entity
@Table(name = "OTM_Invoice")
public static class Invoice {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(length = 50, nullable = false)
private String name;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "INVOICE_ID", nullable = false)
private final Set<InvoiceLine> invoiceLines = new HashSet<>();
public Invoice() {
super();
}
public Invoice(String name) {
super();
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<InvoiceLine> getInvoiceLines() {
return invoiceLines;
}
public void addInvoiceLine(InvoiceLine invoiceLine) {
invoiceLines.add(invoiceLine);
}
}
@Entity
@Table(name = "OTM_InvoiceLine")
public static class InvoiceLine {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(length = 50, nullable = false)
private String name;
public InvoiceLine() {
super();
}
public InvoiceLine(String name) {
super();
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
@Entity
@Table(name = "OTM_Receipt")
public static class Receipt {
@OneToOne(cascade = { CascadeType.PERSIST})
private Invoice invoice;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(length = 50, nullable = false)
private String name;
public Receipt() {
super();
}
public Receipt(String name) {
super();
this.name = name;
}
public void setInvoice(Invoice invoice) {
this.invoice = invoice;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
}