From 98caa8e44ba906b6a0ce3745b4495dc1237f8519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A5le=20W=2E=20Pedersen?= Date: Wed, 18 Jul 2012 21:00:01 +0200 Subject: [PATCH] HHH-7457 enabled hibernate to reattach a collection that hasnt been loaded to the session. note: this can cause data to be loaded outside a transaction. use with caution. (cherry picked from commit a1c9ed775686e0ace4e3e38c85581000ce647ccc) --- .../org/hibernate/cfg/AvailableSettings.java | 2 + .../AbstractPersistentCollection.java | 175 ++++++++++--- .../proxy/AbstractLazyInitializer.java | 61 ++++- .../b/specjmapid/ondemand/CustomerDemand.java | 245 ++++++++++++++++++ .../ondemand/CustomerInventoryDemand.java | 149 +++++++++++ .../ondemand/CustomerInventoryDemandPK.java | 66 +++++ .../e1/b/specjmapid/ondemand/ItemDemand.java | 143 ++++++++++ .../specjmapid/ondemand/LazyLoadingTest.java | 113 ++++++++ 8 files changed, 911 insertions(+), 43 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerDemand.java create 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/CustomerInventoryDemandPK.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/ItemDemand.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/LazyLoadingTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java index 6d70bc4949..4a55bb0c36 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java @@ -592,4 +592,6 @@ public interface AvailableSettings { public static final String MULTI_TENANT_IDENTIFIER_RESOLVER = "hibernate.tenant_identifier_resolver"; public static final String FORCE_DISCRIMINATOR_IN_SELECTS_BY_DEFAULT = "hibernate.discriminator.force_in_select"; + + public static final String ENABLE_LAZY_LOAD_NO_TRANS = "hibernate.enable_lazy_load_no_trans"; } 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 cce4eaf948..cd9c37a259 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 @@ -24,24 +24,17 @@ package org.hibernate.collection.internal; import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; +import java.util.*; import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.LazyInitializationException; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.internal.ForeignKeys; -import org.hibernate.engine.spi.CollectionEntry; -import org.hibernate.engine.spi.EntityEntry; -import org.hibernate.engine.spi.SessionImplementor; -import org.hibernate.engine.spi.Status; -import org.hibernate.engine.spi.TypedValue; +import org.hibernate.engine.spi.*; +import org.hibernate.internal.SessionFactoryRegistry; import org.hibernate.internal.util.MarkerObject; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.EmptyIterator; @@ -51,6 +44,8 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.pretty.MessageHelper; import org.hibernate.type.Type; +import javax.naming.NamingException; + /** * Base class implementing {@link org.hibernate.collection.spi.PersistentCollection} * @@ -73,6 +68,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers private boolean dirty; private Serializable storedSnapshot; + private String sessionFactoryUuid; + private boolean specjLazyLoad = false; + public final String getRole() { return role; } @@ -123,54 +121,82 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers return true; } else { - throwLazyInitializationExceptionIfNotConnected(); - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); - CollectionPersister persister = entry.getLoadedPersister(); - if ( persister.isExtraLazy() ) { - if ( hasQueuedOperations() ) { - session.flush(); + try { + if(specjLazyLoad) { + specialSpecjInitialization(false); } - cachedSize = persister.getSize( entry.getLoadedKey(), session ); - return true; + else + throwLazyInitializationExceptionIfNotConnected(); + CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); + CollectionPersister persister = entry.getLoadedPersister(); + if ( persister.isExtraLazy() ) { + if ( hasQueuedOperations() ) { + session.flush(); + } + cachedSize = persister.getSize( entry.getLoadedKey(), session ); + return true; + } + } + finally { + if(specjLazyLoad) + session = null; } } } read(); return false; } - + protected Boolean readIndexExistence(Object index) { if (!initialized) { - throwLazyInitializationExceptionIfNotConnected(); - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); - CollectionPersister persister = entry.getLoadedPersister(); - if ( persister.isExtraLazy() ) { - if ( hasQueuedOperations() ) { - session.flush(); + try { + if(specjLazyLoad) { + specialSpecjInitialization(false); } - return persister.indexExists( entry.getLoadedKey(), index, session ); + 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 ); + } + } + finally { + if(specjLazyLoad) + session = null; } } read(); return null; - } - + protected Boolean readElementExistence(Object element) { if (!initialized) { - throwLazyInitializationExceptionIfNotConnected(); - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); - CollectionPersister persister = entry.getLoadedPersister(); - if ( persister.isExtraLazy() ) { - if ( hasQueuedOperations() ) { - session.flush(); + try { + if(specjLazyLoad) { + specialSpecjInitialization(false); } - return persister.elementExists( entry.getLoadedKey(), element, session ); + else + throwLazyInitializationExceptionIfNotConnected(); + CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); + CollectionPersister persister = entry.getLoadedPersister(); + if ( persister.isExtraLazy() ) { + if ( hasQueuedOperations() ) { + session.flush(); + } + return persister.elementExists( entry.getLoadedKey(), element, session ); + } + } + finally { + if(specjLazyLoad) + session = null; } } read(); return null; - } protected static final Object UNKNOWN = new MarkerObject("UNKNOWN"); @@ -184,7 +210,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers if ( hasQueuedOperations() ) { session.flush(); } - return persister.getElementByIndex( entry.getLoadedKey(), index, session, owner ); + return persister.getElementByIndex(entry.getLoadedKey(), index, session, owner); } } read(); @@ -376,11 +402,25 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers if (initializing) { throw new LazyInitializationException("illegal access to loading collection"); } - throwLazyInitializationExceptionIfNotConnected(); - session.initializeCollection(this, writing); + else if ( specjLazyLoad ) { + specialSpecjInitialization(writing); + } + else if ( session==null ) { + throw new LazyInitializationException("could not initialize proxy - no Session"); + } + else if ( !session.isOpen() ) { + throw new LazyInitializationException("could not initialize proxy - the owning Session was closed"); + } + else if ( !session.isConnected() ) { + throw new LazyInitializationException("could not initialize proxy - the owning Session is disconnected"); + } + else { + throwLazyInitializationExceptionIfNotConnected(); + session.initializeCollection(this, writing); + } } } - + private void throwLazyInitializationExceptionIfNotConnected() { if ( !isConnectedToSession() ) { throwLazyInitializationException("no session or session was closed"); @@ -389,6 +429,40 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers throwLazyInitializationException("session is disconnected"); } } + + 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( @@ -420,6 +494,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers * @return true if this was currently associated with the given session */ public final boolean unsetSession(SessionImplementor currentSession) { + prepareForPossibleSpecialSpecjInitialization(); if (currentSession==this.session) { this.session=null; return true; @@ -429,6 +504,22 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } } + protected void prepareForPossibleSpecialSpecjInitialization() { + if ( session != null ) { + specjLazyLoad = Boolean.parseBoolean( session.getFactory().getProperties().getProperty( AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS)); + + if ( specjLazyLoad && sessionFactoryUuid == null) { + try { + sessionFactoryUuid = (String) session.getFactory().getReference().get("uuid").getContent(); + } + catch (NamingException e) { + //not much we can do if this fails... + } + } + } + } + + /** * Associate the collection with the given session. * @return false if the collection was already associated with the session 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 908305dfe1..e6e825a568 100755 --- a/hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java @@ -23,14 +23,18 @@ */ package org.hibernate.proxy; +import javax.naming.NamingException; import java.io.Serializable; import org.hibernate.HibernateException; import org.hibernate.LazyInitializationException; import org.hibernate.SessionException; import org.hibernate.TransientObjectException; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.engine.spi.EntityKey; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.internal.SessionFactoryRegistry; import org.hibernate.persister.entity.EntityPersister; /** @@ -50,6 +54,8 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { private transient SessionImplementor session; private Boolean readOnlyBeforeAttachedToSession; + private String sessionFactoryUuid; + private boolean specjLazyLoad = false; /** * For serialization from the non-pojo initializers (HHH-3309) */ @@ -137,6 +143,7 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { @Override public final void unsetSession() { + prepareForPossibleSpecialSpecjInitialization(); session = null; readOnly = false; readOnlyBeforeAttachedToSession = null; @@ -145,7 +152,10 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { @Override public final void initialize() throws HibernateException { if (!initialized) { - if ( session==null ) { + if( specjLazyLoad ) { + specialSpecjInitialization(); + } + else if ( session==null ) { throw new LazyInitializationException("could not initialize proxy - no Session"); } else if ( !session.isOpen() ) { @@ -165,6 +175,55 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { } } + protected void specialSpecjInitialization() { + if ( session == null ) { + //we have a detached collection thats set to null, reattach + if ( sessionFactoryUuid == null ) { + throw new LazyInitializationException("could not initialize proxy - no Session"); + } + try { + SessionFactoryImplementor sf = (SessionFactoryImplementor) + SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid ); + session = (SessionImplementor) sf.openSession(); + + target = session.immediateLoad( entityName, id ); + initialized = true; + checkTargetState(); + } + catch( Exception e ) { + e.printStackTrace(); + throw new LazyInitializationException(e.getMessage()); + } + finally { + session = null; + } + } + else if(session.isOpen() && session.isConnected()) { + target = session.immediateLoad( entityName, id ); + initialized = true; + checkTargetState(); + } + else { + throw new LazyInitializationException("could not initialize proxy - Session was closed or disced"); + } + } + + protected void prepareForPossibleSpecialSpecjInitialization() { + if ( session != null ) { + specjLazyLoad = + Boolean.parseBoolean( session.getFactory().getProperties().getProperty( AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS)); + + if ( specjLazyLoad && sessionFactoryUuid == null) { + try { + sessionFactoryUuid = (String) session.getFactory().getReference().get("uuid").getContent(); + } + catch (NamingException e) { + //not much we can do if this fails... + } + } + } + } + private void checkTargetState() { if ( !unwrap ) { if ( target == null ) { 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 new file mode 100644 index 0000000000..373d877cbe --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerDemand.java @@ -0,0 +1,245 @@ +/* + * 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 new file mode 100644 index 0000000000..d4f8f6e52f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerInventoryDemand.java @@ -0,0 +1,149 @@ +/* + * 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/CustomerInventoryDemandPK.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerInventoryDemandPK.java new file mode 100644 index 0000000000..f2e2fa5a6e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/CustomerInventoryDemandPK.java @@ -0,0 +1,66 @@ +/* + * 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; + + +public class CustomerInventoryDemandPK implements Serializable { + + private Integer id; + private int custId; + + public CustomerInventoryDemandPK() { + } + + public CustomerInventoryDemandPK(Integer id, int custId) { + 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() { + return id; + } + + public int getCustId() { + return custId; + } + + +} 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 new file mode 100644 index 0000000000..3bb57d70f5 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/ItemDemand.java @@ -0,0 +1,143 @@ +/* + * 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 new file mode 100644 index 0000000000..9533d50220 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/ondemand/LazyLoadingTest.java @@ -0,0 +1,113 @@ + /* + * JBoss, Home of Professional Open Source + * Copyright 2012, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.hibernate.test.annotations.derivedidentities.e1.b.specjmapid.ondemand; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.math.BigDecimal; +import java.util.List; + +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.Test; + + public class LazyLoadingTest extends BaseCoreFunctionalTestCase{ + + public LazyLoadingTest() { + System.setProperty( "hibernate.enable_specj_proprietary_syntax", "true" ); + } + + @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" ); + + + + 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); + s.clear(); + + // 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 + + s.close(); + } + + @Override + protected void configure(Configuration cfg) { + super.configure( cfg ); + cfg.setProperty( Environment.ENABLE_LAZY_LOAD_NO_TRANS, "true"); + + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + CustomerDemand.class, + CustomerInventoryDemand.class, + CustomerInventoryDemandPK.class, + ItemDemand.class + + }; + } + }