From d8651c3ba78abe0cd014df8ac966fa205502bbc4 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 15 Aug 2012 10:51:47 -0500 Subject: [PATCH] HHH-7524 - Enabling AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS results in leaking DB-connections --- .../AbstractPersistentCollection.java | 277 +++++++++++------- .../proxy/AbstractLazyInitializer.java | 23 +- .../b/specjmapid/ondemand/CustomerDemand.java | 245 ---------------- .../ondemand/CustomerInventoryDemand.java | 149 ---------- .../e1/b/specjmapid/ondemand/Inventory.java | 105 +++++++ .../e1/b/specjmapid/ondemand/ItemDemand.java | 143 --------- .../specjmapid/ondemand/LazyLoadingTest.java | 167 +++++++---- ...merInventoryDemandPK.java => Product.java} | 79 +++-- .../e1/b/specjmapid/ondemand/Store.java | 94 ++++++ 9 files changed, 550 insertions(+), 732 deletions(-) delete mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerDemand.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerInventoryDemand.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/Inventory.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/ItemDemand.java rename hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/{CustomerInventoryDemandPK.java => Product.java} (56%) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/Store.java diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java index 4053a8d4cc..196284a824 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java @@ -33,9 +33,12 @@ import java.util.Iterator; import java.util.List; import java.util.ListIterator; +import org.jboss.logging.Logger; + import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.LazyInitializationException; +import org.hibernate.Session; import org.hibernate.cfg.AvailableSettings; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.internal.ForeignKeys; @@ -60,6 +63,7 @@ import org.hibernate.type.Type; * @author Gavin King */ public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection { + private static final Logger log = Logger.getLogger( AbstractPersistentCollection.class ); private transient SessionImplementor session; private boolean initialized; @@ -131,105 +135,195 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers return true; } else { - try { - if ( specjLazyLoad ) { - specialSpecjInitialization( false ); - } - else { - throwLazyInitializationExceptionIfNotConnected(); - } - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( this ); - CollectionPersister persister = entry.getLoadedPersister(); - if ( persister.isExtraLazy() ) { - if ( hasQueuedOperations() ) { - session.flush(); + boolean isExtraLazy = withTemporarySessionIfNeeded( + new LazyInitializationWork() { + @Override + public Boolean doWork() { + CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); + CollectionPersister persister = entry.getLoadedPersister(); + if ( persister.isExtraLazy() ) { + if ( hasQueuedOperations() ) { + session.flush(); + } + cachedSize = persister.getSize( entry.getLoadedKey(), session ); + return true; + } + else { + read(); + } + return false; + } } - cachedSize = persister.getSize( entry.getLoadedKey(), session ); - return true; - } - } - finally { - if ( specjLazyLoad ) { - session = null; - } + ); + if ( isExtraLazy ) { + return true; } } } - read(); return false; } - protected Boolean readIndexExistence(Object index) { - if ( !initialized ) { - try { - if ( specjLazyLoad ) { - specialSpecjInitialization( false ); - } - else { - throwLazyInitializationExceptionIfNotConnected(); - } - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( this ); - CollectionPersister persister = entry.getLoadedPersister(); - if ( persister.isExtraLazy() ) { - if ( hasQueuedOperations() ) { - session.flush(); - } - return persister.indexExists( entry.getLoadedKey(), index, session ); - } + public static interface LazyInitializationWork { + public T doWork(); + } + + private T withTemporarySessionIfNeeded(LazyInitializationWork lazyInitializationWork) { + SessionImplementor originalSession = null; + boolean isTempSession = false; + + if ( session == null ) { + if ( specjLazyLoad ) { + session = openTemporarySessionForLoading(); + isTempSession = true; } - finally { - if ( specjLazyLoad ) { - session = null; - } + else { + throw new LazyInitializationException( "could not initialize proxy - no Session" ); + } + } + else if ( !session.isOpen() ) { + if ( specjLazyLoad ) { + originalSession = session; + session = openTemporarySessionForLoading(); + isTempSession = true; + } + else { + throw new LazyInitializationException( "could not initialize proxy - the owning Session was closed" ); + } + } + else if ( !session.isConnected() ) { + if ( specjLazyLoad ) { + originalSession = session; + session = openTemporarySessionForLoading(); + isTempSession = true; + } + else { + throw new LazyInitializationException( "could not initialize proxy - the owning Session is disconnected" ); + } + } + + if ( isTempSession ) { + session.getPersistenceContext().addUninitializedDetachedCollection( + session.getFactory().getCollectionPersister( getRole() ), + this + ); + } + + try { + return lazyInitializationWork.doWork(); + } + finally { + if ( isTempSession ) { + // make sure the just opened temp session gets closed! + try { + ( (Session) session ).close(); + } + catch (Exception e) { + log.warn( "Unable to close temporary session used to load lazy collection associated to no session" ); + } + session = originalSession; + } + } + } + + private SessionImplementor openTemporarySessionForLoading() { + if ( sessionFactoryUuid == null ) { + throwLazyInitializationException( "SessionFactory UUID not known to create temporary Session for loading" ); + } + + SessionFactoryImplementor sf = (SessionFactoryImplementor) + SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid ); + return (SessionImplementor) sf.openSession(); + } + + protected Boolean readIndexExistence(final Object index) { + if ( !initialized ) { + Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded( + new LazyInitializationWork() { + @Override + public Boolean doWork() { + CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); + CollectionPersister persister = entry.getLoadedPersister(); + if ( persister.isExtraLazy() ) { + if ( hasQueuedOperations() ) { + session.flush(); + } + return persister.indexExists( entry.getLoadedKey(), index, session ); + } + else { + read(); + } + return null; + } + } + ); + if ( extraLazyExistenceCheck != null ) { + return extraLazyExistenceCheck; } } - read(); return null; } - protected Boolean readElementExistence(Object element) { + protected Boolean readElementExistence(final Object element) { if ( !initialized ) { - try { - if ( specjLazyLoad ) { - specialSpecjInitialization( false ); - } - else { - throwLazyInitializationExceptionIfNotConnected(); - } - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( this ); - CollectionPersister persister = entry.getLoadedPersister(); - if ( persister.isExtraLazy() ) { - if ( hasQueuedOperations() ) { - session.flush(); + Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded( + new LazyInitializationWork() { + @Override + public Boolean doWork() { + CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); + CollectionPersister persister = entry.getLoadedPersister(); + if ( persister.isExtraLazy() ) { + if ( hasQueuedOperations() ) { + session.flush(); + } + return persister.elementExists( entry.getLoadedKey(), element, session ); + } + else { + read(); + } + return null; + } } - return persister.elementExists( entry.getLoadedKey(), element, session ); - } - } - finally { - if ( specjLazyLoad ) { - session = null; - } + ); + if ( extraLazyExistenceCheck != null ) { + return extraLazyExistenceCheck; } } - read(); return null; } protected static final Object UNKNOWN = new MarkerObject( "UNKNOWN" ); - protected Object readElementByIndex(Object index) { + protected Object readElementByIndex(final Object index) { if ( !initialized ) { - throwLazyInitializationExceptionIfNotConnected(); - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( this ); - CollectionPersister persister = entry.getLoadedPersister(); - if ( persister.isExtraLazy() ) { - if ( hasQueuedOperations() ) { - session.flush(); + class ExtraLazyElementByIndexReader implements LazyInitializationWork { + private boolean isExtraLazy; + private Object element; + + @Override + public Object doWork() { + CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); + CollectionPersister persister = entry.getLoadedPersister(); + isExtraLazy = persister.isExtraLazy(); + if ( isExtraLazy ) { + if ( hasQueuedOperations() ) { + session.flush(); + } + element = persister.getElementByIndex( entry.getLoadedKey(), index, session, owner ); + } + else { + read(); + } + return null; } - return persister.getElementByIndex( entry.getLoadedKey(), index, session, owner ); + } + + ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader(); + //noinspection unchecked + withTemporarySessionIfNeeded( reader ); + if ( reader.isExtraLazy ) { + return reader.element; } } - read(); return UNKNOWN; } @@ -423,9 +517,6 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers if ( initializing ) { throw new LazyInitializationException( "illegal access to loading collection" ); } - else if ( specjLazyLoad ) { - specialSpecjInitialization( writing ); - } else if ( session == null ) { throw new LazyInitializationException( "could not initialize proxy - no Session" ); } @@ -451,40 +542,6 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } } - protected void specialSpecjInitialization(boolean writing) { - if ( session == null ) { - //we have a detached collection thats set to null, reattach - if ( sessionFactoryUuid == null ) { - throwLazyInitializationExceptionIfNotConnected(); - } - try { - SessionFactoryImplementor sf = (SessionFactoryImplementor) - SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid ); - session = (SessionImplementor) sf.openSession(); - CollectionPersister collectionPersister = - session.getFactory().getCollectionPersister( this.getRole() ); - session.getPersistenceContext() - .addUninitializedDetachedCollection( collectionPersister, this ); - - session.initializeCollection( this, writing ); - - //CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); - //CollectionPersister persister = entry.getLoadedPersister(); - //cachedSize = persister.getSize( entry.getLoadedKey(), session); - - //session = null; - } - catch (Exception e) { - e.printStackTrace(); - throw new LazyInitializationException( e.getMessage() ); - } - } - else { - session.initializeCollection( this, writing ); - } - } - - private void throwLazyInitializationException(String message) { throw new LazyInitializationException( "failed to lazily initialize a collection" + diff --git a/hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java b/hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java index d065a95f7c..be044a96d7 100755 --- a/hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java @@ -26,8 +26,11 @@ package org.hibernate.proxy; import javax.naming.NamingException; import java.io.Serializable; +import org.jboss.logging.Logger; + import org.hibernate.HibernateException; import org.hibernate.LazyInitializationException; +import org.hibernate.Session; import org.hibernate.SessionException; import org.hibernate.TransientObjectException; import org.hibernate.cfg.AvailableSettings; @@ -45,6 +48,8 @@ import org.hibernate.persister.entity.EntityPersister; * @author Gavin King */ public abstract class AbstractLazyInitializer implements LazyInitializer { + private static final Logger log = Logger.getLogger( AbstractLazyInitializer.class ); + private String entityName; private Serializable id; private Object target; @@ -185,9 +190,20 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { try { SessionFactoryImplementor sf = (SessionFactoryImplementor) SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid ); - session = (SessionImplementor) sf.openSession(); + SessionImplementor session = (SessionImplementor) sf.openSession(); - target = session.immediateLoad( entityName, id ); + try { + target = session.immediateLoad( entityName, id ); + } + finally { + // make sure the just opened temp session gets closed! + try { + ( (Session) session ).close(); + } + catch (Exception e) { + log.warn( "Unable to close temporary session used to load lazy proxy associated to no session" ); + } + } initialized = true; checkTargetState(); } @@ -195,9 +211,6 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { e.printStackTrace(); throw new LazyInitializationException( e.getMessage() ); } - finally { - session = null; - } } else if ( session.isOpen() && session.isConnected() ) { target = session.immediateLoad( entityName, id ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerDemand.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerDemand.java deleted file mode 100644 index 373d877cbe..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerDemand.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat, Inc. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - */ -package org.hibernate.test.annotations.derivedidentities.e1.b.specjmapid.ondemand; -import java.io.Serializable; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; -import javax.persistence.Basic; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Version; - -@SuppressWarnings("serial") -@NamedQueries({ - @NamedQuery(name = CustomerDemand.QUERY_ALL, - query = "select a from CustomerDemand a"), - @NamedQuery(name = CustomerDemand.QUERY_COUNT, - query = "select COUNT(a) from CustomerDemand a"), - @NamedQuery(name = CustomerDemand.QUERY_BY_CREDIT, - query = "SELECT c.id FROM CustomerDemand c WHERE c.creditLimit > :limit") - }) -@Entity -@Table(name = "O_CUSTOMERDEMAND") -public class CustomerDemand implements Serializable { - public static final String QUERY_ALL = "Customer.selectAll"; - public static final String QUERY_COUNT = "Customer.count"; - public static final String QUERY_BY_CREDIT = "Customer.selectByCreditLimit"; - - public static final String BAD_CREDIT = "BC"; - - @Id - @Column(name = "C_ID") - private int id; - - @Column(name = "C_FIRST") - private String firstName; - - @Column(name = "C_LAST") - private String lastName; - - @Column(name = "C_CONTACT") - private String contact; - - @Basic(fetch = FetchType.LAZY) - @Column(name = "C_CREDIT") - private String credit; - - @Column(name = "C_CREDIT_LIMIT") - private BigDecimal creditLimit; - - @Column(name = "C_SINCE") - @Temporal(TemporalType.DATE) - private Calendar since; - - @Column(name = "C_BALANCE") - private BigDecimal balance; - - @Column(name = "C_YTD_PAYMENT") - private BigDecimal ytdPayment; - - @OneToMany(targetEntity = CustomerInventoryDemand.class, - mappedBy = "customer", - cascade = CascadeType.ALL, - fetch = FetchType.LAZY) - private List customerInventories; - - @Version - @Column(name = "C_VERSION") - private int version; - - protected CustomerDemand() { - } - - public CustomerDemand(String first, String last, - String contact, String credit, BigDecimal creditLimit, - BigDecimal balance, BigDecimal YtdPayment) { - - this.firstName = first; - this.lastName = last; - this.contact = contact; - this.since = Calendar.getInstance(); - this.credit = credit; - this.creditLimit = creditLimit; - this.balance = balance; - this.ytdPayment = YtdPayment; - } - - public Integer getId() { - return id; - } - - public void setId(Integer customerId) { - this.id = customerId; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - public String getContact() { - return contact; - } - - public void setContact(String contact) { - this.contact = contact; - } - - public String getCredit() { - return credit; - } - - public void setCredit(String credit) { - this.credit = credit; - } - - public BigDecimal getCreditLimit() { - return creditLimit; - } - - public void setCreditLimit(BigDecimal creditLimit) { - this.creditLimit = creditLimit; - } - - public Calendar getSince() { - return since; - } - - public void setSince(Calendar since) { - this.since = since; - } - - public BigDecimal getBalance() { - return balance; - } - - public void setBalance(BigDecimal balance) { - this.balance = balance; - } - - public void changeBalance(BigDecimal change) { - setBalance( balance.add( change ).setScale( 2, BigDecimal.ROUND_DOWN ) ); - } - - public BigDecimal getYtdPayment() { - return ytdPayment; - } - - public void setYtdPayment(BigDecimal ytdPayment) { - this.ytdPayment = ytdPayment; - } - - public List getInventories() { - if ( customerInventories == null ) { - customerInventories = new ArrayList(); - } - return customerInventories; - } - - public CustomerInventoryDemand addInventory(ItemDemand item, int quantity, - BigDecimal totalValue) { - - CustomerInventoryDemand inventory = new CustomerInventoryDemand( - this, item, - quantity, totalValue - ); - getInventories().add( inventory ); - return inventory; - } - - public int getVersion() { - return version; - } - - public boolean hasSufficientCredit(BigDecimal amount) { - return !BAD_CREDIT.equals( getCredit() ) - && creditLimit != null - && creditLimit.compareTo( amount ) >= 0; - } - - @Override - public boolean equals(Object o) { - if ( this == o ) { - return true; - } - if ( o == null || getClass() != o.getClass() ) { - return false; - } - return id == ( ( CustomerDemand ) o ).id; - } - - @Override - public int hashCode() { - return new Integer( id ).hashCode(); - } - - @Override - public String toString() { - return this.getFirstName() + " " + this.getLastName(); - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerInventoryDemand.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerInventoryDemand.java deleted file mode 100644 index d4f8f6e52f..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerInventoryDemand.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat, Inc. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - */ -package org.hibernate.test.annotations.derivedidentities.e1.b.specjmapid.ondemand; -import java.io.Serializable; -import java.math.BigDecimal; -import java.util.Comparator; -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.IdClass; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; -import javax.persistence.TableGenerator; -import javax.persistence.Version; - -@NamedQueries({ - @NamedQuery(name = "CustomerInventoryDemand.selectAll", - query = "select a from CustomerInventoryDemand a") -}) -@SuppressWarnings("serial") -@Entity -@Table(name = "O_CUSTINVENTORY") -@IdClass(CustomerInventoryDemandPK.class) -public class CustomerInventoryDemand implements Serializable, Comparator { - - @Id - @TableGenerator(name = "inventory", - table = "U_SEQUENCES", - pkColumnName = "S_ID", - valueColumnName = "S_NEXTNUM", - pkColumnValue = "inventory", - allocationSize = 1000) - @GeneratedValue(strategy = GenerationType.TABLE, generator = "inventory") - @Column(name = "CI_ID") - private Integer id; - - @Id - @Column(name = "CI_CUSTOMERID", insertable = false, updatable = false) - private int custId; - - @ManyToOne(cascade = CascadeType.MERGE) - @JoinColumn(name = "CI_CUSTOMERID", nullable = false) - private CustomerDemand customer; - - @ManyToOne(cascade = CascadeType.MERGE) - @JoinColumn(name = "CI_ITEMID") - private ItemDemand vehicle; - - @Column(name = "CI_VALUE") - private BigDecimal totalCost; - - @Column(name = "CI_QUANTITY") - private int quantity; - - @Version - @Column(name = "CI_VERSION") - private int version; - - protected CustomerInventoryDemand() { - } - - CustomerInventoryDemand(CustomerDemand customer, ItemDemand vehicle, int quantity, BigDecimal totalValue) { - this.customer = customer; - this.vehicle = vehicle; - this.quantity = quantity; - this.totalCost = totalValue; - } - - public ItemDemand getVehicle() { - return vehicle; - } - - public BigDecimal getTotalCost() { - return totalCost; - } - - public int getQuantity() { - return quantity; - } - - public Integer getId() { - return id; - } - - public CustomerDemand getCustomer() { - return customer; - } - - public int getCustId() { - return custId; - } - - public int getVersion() { - return version; - } - - public int compare(CustomerInventoryDemand cdb1, CustomerInventoryDemand cdb2) { - return cdb1.id.compareTo( cdb2.id ); - } - - @Override - public boolean equals(Object obj) { - if ( obj == this ) { - return true; - } - if ( obj == null || !( obj instanceof CustomerInventoryDemand ) ) { - return false; - } - if ( this.id == ( ( CustomerInventoryDemand ) obj ).id ) { - return true; - } - if ( this.id != null && ( ( CustomerInventoryDemand ) obj ).id == null ) { - return false; - } - if ( this.id == null && ( ( CustomerInventoryDemand ) obj ).id != null ) { - return false; - } - - return this.id.equals( ( ( CustomerInventoryDemand ) obj ).id ); - } - -} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/Inventory.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/Inventory.java new file mode 100644 index 0000000000..9c1836db68 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/Inventory.java @@ -0,0 +1,105 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.annotations.derivedidentities.e1.b.specjmapid.ondemand; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +import java.math.BigDecimal; + +import org.hibernate.annotations.GenericGenerator; + +@Entity +@Table(name = "O_CUSTINVENTORY") +public class Inventory { + private int id = -1; + private Store store; + private Product product; + private Long quantity; + private BigDecimal storePrice; + + public Inventory() { + } + + public Inventory(Store store, Product product) { + this.store = store; + this.product = product; + } + + @Id + @GeneratedValue + @GenericGenerator( name = "increment", strategy = "increment" ) + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @ManyToOne + @JoinColumn( name = "store_id" ) + public Store getStore() { + return store; + } + + public Inventory setStore(Store store) { + this.store = store; + return this; + } + + @ManyToOne + @JoinColumn( name = "prod_id" ) + public Product getProduct() { + return product; + } + + public Inventory setProduct(Product product) { + this.product = product; + return this; + } + + public Long getQuantity() { + return quantity; + } + + public Inventory setQuantity(Long quantity) { + this.quantity = quantity; + return this; + } + + public BigDecimal getStorePrice() { + return storePrice; + } + + public Inventory setStorePrice(BigDecimal storePrice) { + this.storePrice = storePrice; + return this; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/ItemDemand.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/ItemDemand.java deleted file mode 100644 index 3bb57d70f5..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/ItemDemand.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat, Inc. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - */ -package org.hibernate.test.annotations.derivedidentities.e1.b.specjmapid.ondemand; -import java.io.Serializable; -import java.math.BigDecimal; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; -import javax.persistence.Version; - - -@NamedQueries({ - @NamedQuery(name = "Item.findByCategory", - query = "SELECT i FROM ItemDemand i WHERE i.category=:category ORDER BY i.id") -}) -@SuppressWarnings("serial") -@Entity -@Table(name = "O_ITEM") -public class ItemDemand implements Serializable { - - public static final String QUERY_BY_CATEGORY = "Item.findByCategory"; - - @Id - @Column(name = "I_ID") - private String id; - - @Column(name = "I_NAME") - private String name; - - @Column(name = "I_PRICE") - private BigDecimal price; - - @Column(name = "I_DESC") - private String description; - - @Column(name = "I_DISCOUNT") - private BigDecimal discount; - - @Column(name = "I_CATEGORY") - private int category; - - @Version - @Column(name = "I_VERSION") - int version; - - public String getId() { - return id; - } - - public void setId(String i) { - id = i; - } - - public int getCategory() { - return category; - } - - public void setCategory(int category) { - this.category = category; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public BigDecimal getDiscount() { - return discount; - } - - public void setDiscount(BigDecimal discount) { - if ( discount.doubleValue() < 0 || discount.doubleValue() > 100.0 ) { - throw new IllegalArgumentException( - this + " discount " + discount - + " is invalid. Must be between 0.0 and 100.0" - ); - } - this.discount = discount; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public BigDecimal getPrice() { - return price; - } - - public void setPrice(BigDecimal price) { - this.price = price; - } - - public int getVersion() { - return version; - } - - @Override - public boolean equals(Object other) { - if ( other == null || other.getClass() != this.getClass() ) { - return false; - } - if ( other == this ) { - return true; - } - return id.equals( ( ( ItemDemand ) other ).id ); - } - - @Override - public int hashCode() { - return id.hashCode(); - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/LazyLoadingTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/LazyLoadingTest.java index 9533d50220..e584e17bc1 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/LazyLoadingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/LazyLoadingTest.java @@ -23,91 +23,152 @@ package org.hibernate.test.annotations.derivedidentities.e1.b.specjmapid.ondemand; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.math.BigDecimal; import java.util.List; +import org.hibernate.Hibernate; import org.hibernate.Session; -import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +import org.junit.After; +import org.junit.Before; import org.junit.Test; - public class LazyLoadingTest extends BaseCoreFunctionalTestCase{ +public class LazyLoadingTest extends BaseCoreFunctionalTestCase { - public LazyLoadingTest() { - System.setProperty( "hibernate.enable_specj_proprietary_syntax", "true" ); + @Before + public void setUpData() { + Session s = openSession(); + s.beginTransaction(); + Store store = new Store( 1 ) + .setName( "Acme Super Outlet" ); + s.persist( store ); + + Product product = new Product( "007" ) + .setName( "widget" ) + .setDescription( "FooBar" ); + s.persist( product ); + + store.addInventoryProduct( product ) + .setQuantity( 10L ) + .setStorePrice( new BigDecimal( 500 ) ); + + s.getTransaction().commit(); + s.close(); + } + + @After + public void cleanUpData() { + Session s = openSession(); + s.beginTransaction(); + s.delete( s.get( Store.class, 1 ) ); + s.delete( s.get( Product.class, "007" ) ); + s.getTransaction().commit(); + s.close(); } @Test - public void testOnDemandLoading() - { - Session s; - Transaction tx; - s = openSession(); - tx = s.beginTransaction(); - // store entity in datastore - CustomerDemand cust = new CustomerDemand("John", "Doe", "123456", "1.0", new BigDecimal(1),new BigDecimal(1), new BigDecimal(5)); - cust.setCredit( "GOOD" ); - s.persist(cust); - ItemDemand item = new ItemDemand(); - item.setId("007"); - item.setName("widget"); - item.setDescription( "FooBar" ); + public void testLazyCollectionLoadingWithClearedSession() { + sessionFactory().getStatistics().clear(); + Session s = openSession(); + s.beginTransaction(); + // first load the store, making sure collection is not initialized + Store store = (Store)s.get( Store.class, 1 ); + assertNotNull( store ); + assertFalse( Hibernate.isInitialized( store.getInventories() ) ); + assertEquals( 1, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 0, sessionFactory().getStatistics().getSessionCloseCount() ); - cust.addInventory(item, 1, new BigDecimal(500)); - s.persist(item); - s.persist(cust); - s.flush(); - CustomerInventoryDemand eager = cust.getInventories().get( 0 ); - Integer lazyId = cust.getId(); - tx.commit(); - s.close(); - - s = openSession(); - - // load the lazy entity, orm configuration loaded during @Before defines loading - //tx = s.beginTransaction(); - CustomerDemand lazyCustomer = (CustomerDemand)s.get(CustomerDemand.class, lazyId); - assertNotNull(lazyCustomer); + // then clear session and try to initialize collection s.clear(); + store.getInventories().size(); + assertTrue( Hibernate.isInitialized( store.getInventories() ) ); - // access the association, outside the session that loaded the entity - List inventories = lazyCustomer.getInventories(); // on-demand load - assertNotNull(inventories); - assertTrue( inventories.contains( eager ) ); // test readElementExistence - assertEquals(1, inventories.size()); // test readSize - //assertTrue(inventories.contains(item)); - assertEquals( cust.getCredit(), lazyCustomer.getCredit() ); - CustomerInventoryDemand inv = inventories.get(0); - assertNotNull(inv); - assertEquals(inv.getQuantity(), 1); // field access - assertEquals(inv.getVehicle().getDescription(), "FooBar"); // field access + assertEquals( 2, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 1, sessionFactory().getStatistics().getSessionCloseCount() ); + s.getTransaction().commit(); s.close(); } + @Test + public void testLazyCollectionLoadingWithClosedSession() { + sessionFactory().getStatistics().clear(); + + Session s = openSession(); + s.beginTransaction(); + // first load the store, making sure collection is not initialized + Store store = (Store)s.get( Store.class, 1 ); + assertNotNull( store ); + assertFalse( Hibernate.isInitialized( store.getInventories() ) ); + + assertEquals( 1, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 0, sessionFactory().getStatistics().getSessionCloseCount() ); + + // close the session and try to initialize collection + s.getTransaction().commit(); + s.close(); + + assertEquals( 1, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 1, sessionFactory().getStatistics().getSessionCloseCount() ); + + store.getInventories().size(); + assertTrue( Hibernate.isInitialized( store.getInventories() ) ); + + assertEquals( 2, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 2, sessionFactory().getStatistics().getSessionCloseCount() ); + } + + @Test + public void testLazyEntityLoadingWithClosedSession() { + sessionFactory().getStatistics().clear(); + + Session s = openSession(); + s.beginTransaction(); + // first load the store, making sure it is not initialized + Store store = (Store) s.load( Store.class, 1 ); + assertNotNull( store ); + assertFalse( Hibernate.isInitialized( store ) ); + + assertEquals( 1, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 0, sessionFactory().getStatistics().getSessionCloseCount() ); + + // close the session and try to initialize store + s.getTransaction().commit(); + s.close(); + + assertEquals( 1, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 1, sessionFactory().getStatistics().getSessionCloseCount() ); + + store.getName(); + assertTrue( Hibernate.isInitialized( store ) ); + + assertEquals( 2, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 2, sessionFactory().getStatistics().getSessionCloseCount() ); + } + + public LazyLoadingTest() { + System.setProperty( "hibernate.enable_specj_proprietary_syntax", "true" ); + } + @Override protected void configure(Configuration cfg) { super.configure( cfg ); - cfg.setProperty( Environment.ENABLE_LAZY_LOAD_NO_TRANS, "true"); - + cfg.setProperty( Environment.ENABLE_LAZY_LOAD_NO_TRANS, "true" ); + cfg.setProperty( Environment.GENERATE_STATISTICS, "true" ); } @Override protected Class[] getAnnotatedClasses() { - return new Class[] { - CustomerDemand.class, - CustomerInventoryDemand.class, - CustomerInventoryDemandPK.class, - ItemDemand.class - - }; + return new Class[] { Store.class, Inventory.class, Product.class }; } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerInventoryDemandPK.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/Product.java similarity index 56% rename from hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerInventoryDemandPK.java rename to hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/Product.java index f2e2fa5a6e..e73af479e7 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerInventoryDemandPK.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/Product.java @@ -22,45 +22,70 @@ * Boston, MA 02110-1301 USA */ package org.hibernate.test.annotations.derivedidentities.e1.b.specjmapid.ondemand; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Version; import java.io.Serializable; +import java.math.BigDecimal; +@Entity +public class Product implements Serializable { + private String id; + private String name; + private String description; + private BigDecimal msrp; + private int version; -public class CustomerInventoryDemandPK implements Serializable { - - private Integer id; - private int custId; - - public CustomerInventoryDemandPK() { + private Product() { } - public CustomerInventoryDemandPK(Integer id, int custId) { + public Product(String id) { this.id = id; - this.custId = custId; } - public boolean equals(Object other) { - if ( other == this ) { - return true; - } - if ( other == null || getClass() != other.getClass() ) { - return false; - } - CustomerInventoryDemandPK cip = ( CustomerInventoryDemandPK ) other; - return ( custId == cip.custId && ( id == cip.id || - ( id != null && id.equals( cip.id ) ) ) ); - } - - public int hashCode() { - return ( id == null ? 0 : id.hashCode() ) ^ custId; - } - - public Integer getId() { + @Id + public String getId() { return id; } - public int getCustId() { - return custId; + private void setId(String id) { + this.id = id; } + public String getName() { + return name; + } + public Product setName(String name) { + this.name = name; + return this; + } + + public String getDescription() { + return description; + } + + public Product setDescription(String description) { + this.description = description; + return this; + } + + public BigDecimal getMsrp() { + return msrp; + } + + public Product setMsrp(BigDecimal msrp) { + this.msrp = msrp; + return this; + } + + @Version + public int getVersion() { + return version; + } + + private void setVersion(int version) { + this.version = version; + } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/Store.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/Store.java new file mode 100644 index 0000000000..f8ea0ae5ba --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/Store.java @@ -0,0 +1,94 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.annotations.derivedidentities.e1.b.specjmapid.ondemand; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Version; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +@Entity +public class Store implements Serializable { + private int id; + private String name; + private List inventories = new ArrayList(); + private int version; + + protected Store() { + } + + public Store(int id) { + this.id = id; + } + + @Id + @Column(name = "ID") + public Integer getId() { + return id; + } + + private void setId(int id) { + this.id = id; + } + + @Column(name = "NAME") + public String getName() { + return name; + } + + public Store setName(String name) { + this.name = name; + return this; + } + + @OneToMany(mappedBy = "store", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + public List getInventories() { + return inventories; + } + + public void setInventories(List inventories) { + this.inventories = inventories; + } + + public Inventory addInventoryProduct(Product product) { + final Inventory inventory = new Inventory( this, product ); + this.inventories.add( inventory ); + return inventory; + } + + @Version + public int getVersion() { + return version; + } + + private void setVersion(int version) { + this.version = version; + } +}