HHH-10602 - Retrieve cached value with enable_lazy_load_no_trans throws an exception
This commit is contained in:
parent
cf27164532
commit
0385b1436a
|
@ -56,6 +56,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
|
||||||
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AbstractPersistentCollection.class );
|
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AbstractPersistentCollection.class );
|
||||||
|
|
||||||
private transient SessionImplementor session;
|
private transient SessionImplementor session;
|
||||||
|
private boolean isTempSession = false;
|
||||||
private boolean initialized;
|
private boolean initialized;
|
||||||
private transient List<DelayedOperation> operationQueue;
|
private transient List<DelayedOperation> operationQueue;
|
||||||
private transient boolean directlyAccessible;
|
private transient boolean directlyAccessible;
|
||||||
|
@ -190,14 +191,11 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
|
private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
|
||||||
SessionImplementor originalSession = null;
|
SessionImplementor tempSession = null;
|
||||||
boolean isTempSession = false;
|
|
||||||
boolean isJTA = false;
|
|
||||||
|
|
||||||
if ( session == null ) {
|
if ( session == null ) {
|
||||||
if ( allowLoadOutsideTransaction ) {
|
if ( allowLoadOutsideTransaction ) {
|
||||||
session = openTemporarySessionForLoading();
|
tempSession = openTemporarySessionForLoading();
|
||||||
isTempSession = true;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throwLazyInitializationException( "could not initialize proxy - no Session" );
|
throwLazyInitializationException( "could not initialize proxy - no Session" );
|
||||||
|
@ -205,9 +203,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
|
||||||
}
|
}
|
||||||
else if ( !session.isOpen() ) {
|
else if ( !session.isOpen() ) {
|
||||||
if ( allowLoadOutsideTransaction ) {
|
if ( allowLoadOutsideTransaction ) {
|
||||||
originalSession = session;
|
tempSession = openTemporarySessionForLoading();
|
||||||
session = openTemporarySessionForLoading();
|
|
||||||
isTempSession = true;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" );
|
throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" );
|
||||||
|
@ -215,16 +211,23 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
|
||||||
}
|
}
|
||||||
else if ( !session.isConnected() ) {
|
else if ( !session.isConnected() ) {
|
||||||
if ( allowLoadOutsideTransaction ) {
|
if ( allowLoadOutsideTransaction ) {
|
||||||
originalSession = session;
|
tempSession = openTemporarySessionForLoading();
|
||||||
session = openTemporarySessionForLoading();
|
|
||||||
isTempSession = true;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
|
throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isTempSession ) {
|
|
||||||
|
SessionImplementor originalSession = null;
|
||||||
|
boolean isJTA = false;
|
||||||
|
|
||||||
|
if ( tempSession != null ) {
|
||||||
|
isTempSession = true;
|
||||||
|
originalSession = session;
|
||||||
|
session = tempSession;
|
||||||
|
|
||||||
|
|
||||||
isJTA = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
|
isJTA = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
|
||||||
|
|
||||||
if ( !isJTA ) {
|
if ( !isJTA ) {
|
||||||
|
@ -246,18 +249,20 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
|
||||||
return lazyInitializationWork.doWork();
|
return lazyInitializationWork.doWork();
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if ( isTempSession ) {
|
if ( tempSession != null ) {
|
||||||
// make sure the just opened temp session gets closed!
|
// make sure the just opened temp session gets closed!
|
||||||
|
isTempSession = false;
|
||||||
|
session = originalSession;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ( !isJTA ) {
|
if ( !isJTA ) {
|
||||||
( (Session) session ).getTransaction().commit();
|
( (Session) tempSession ).getTransaction().commit();
|
||||||
}
|
}
|
||||||
( (Session) session ).close();
|
( (Session) tempSession ).close();
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
LOG.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
|
LOG.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
|
||||||
}
|
}
|
||||||
session = originalSession;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -601,7 +606,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
|
||||||
public final boolean unsetSession(SessionImplementor currentSession) {
|
public final boolean unsetSession(SessionImplementor currentSession) {
|
||||||
prepareForPossibleLoadingOutsideTransaction();
|
prepareForPossibleLoadingOutsideTransaction();
|
||||||
if ( currentSession == this.session ) {
|
if ( currentSession == this.session ) {
|
||||||
this.session = null;
|
if ( !isTempSession ) {
|
||||||
|
this.session = null;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -731,25 +731,30 @@ public abstract class CollectionType extends AbstractType implements Association
|
||||||
collection = persistenceContext.useUnownedCollection( new CollectionKey(persister, key, entityMode) );
|
collection = persistenceContext.useUnownedCollection( new CollectionKey(persister, key, entityMode) );
|
||||||
|
|
||||||
if ( collection == null ) {
|
if ( collection == null ) {
|
||||||
// create a new collection wrapper, to be initialized later
|
|
||||||
collection = instantiate( session, persister, key );
|
collection = persistenceContext.getCollection( new CollectionKey(persister, key, entityMode) );
|
||||||
|
|
||||||
collection.setOwner(owner);
|
if ( collection == null ) {
|
||||||
|
// create a new collection wrapper, to be initialized later
|
||||||
persistenceContext.addUninitializedCollection( persister, collection, key );
|
collection = instantiate( session, persister, key );
|
||||||
|
|
||||||
// some collections are not lazy:
|
collection.setOwner( owner );
|
||||||
if ( initializeImmediately() ) {
|
|
||||||
session.initializeCollection( collection, false );
|
persistenceContext.addUninitializedCollection( persister, collection, key );
|
||||||
|
|
||||||
|
// some collections are not lazy:
|
||||||
|
if ( initializeImmediately() ) {
|
||||||
|
session.initializeCollection( collection, false );
|
||||||
|
}
|
||||||
|
else if ( !persister.isLazy() ) {
|
||||||
|
persistenceContext.addNonLazyCollection( collection );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hasHolder() ) {
|
||||||
|
session.getPersistenceContext().addCollectionHolder( collection );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if ( !persister.isLazy() ) {
|
|
||||||
persistenceContext.addNonLazyCollection( collection );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( hasHolder() ) {
|
|
||||||
session.getPersistenceContext().addCollectionHolder( collection );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( LOG.isTraceEnabled() ) {
|
if ( LOG.isTraceEnabled() ) {
|
||||||
|
|
189
hibernate-core/src/test/java/org/hibernate/test/ondemandload/cache/CacheLazyLoadNoTransTest.java
vendored
Normal file
189
hibernate-core/src/test/java/org/hibernate/test/ondemandload/cache/CacheLazyLoadNoTransTest.java
vendored
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
* 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.ondemandload.cache;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.persistence.Cacheable;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToMany;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.annotations.Cache;
|
||||||
|
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||||
|
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
import org.hibernate.cfg.Environment;
|
||||||
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
|
|
||||||
|
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Janario Oliveira
|
||||||
|
*/
|
||||||
|
public class CacheLazyLoadNoTransTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
protected void addSettings(Map settings) {
|
||||||
|
super.addSettings( settings );
|
||||||
|
|
||||||
|
settings.put( AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS, "true" );
|
||||||
|
settings.put( Environment.USE_SECOND_LEVEL_CACHE, "true" );
|
||||||
|
settings.put( Environment.USE_QUERY_CACHE, "true" );
|
||||||
|
settings.put( Environment.CACHE_PROVIDER_CONFIG, "true" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOneToMany() {
|
||||||
|
Customer customer = new Customer();
|
||||||
|
Item item1 = new Item( customer );
|
||||||
|
Item item2 = new Item( customer );
|
||||||
|
customer.boughtItems.add( item1 );
|
||||||
|
customer.boughtItems.add( item2 );
|
||||||
|
persist( customer );
|
||||||
|
|
||||||
|
//init cache
|
||||||
|
assertFalse( isCached( customer.id, Customer.class, "boughtItems" ) );
|
||||||
|
customer = find( Customer.class, customer.id );
|
||||||
|
assertEquals( 2, customer.boughtItems.size() );
|
||||||
|
|
||||||
|
//read from cache
|
||||||
|
assertTrue( isCached( customer.id, Customer.class, "boughtItems" ) );
|
||||||
|
customer = find( Customer.class, customer.id );
|
||||||
|
assertEquals( 2, customer.boughtItems.size() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testManyToMany() {
|
||||||
|
Application application = new Application();
|
||||||
|
persist( application );
|
||||||
|
Customer customer = new Customer();
|
||||||
|
customer.applications.add( application );
|
||||||
|
application.customers.add( customer );
|
||||||
|
persist( customer );
|
||||||
|
|
||||||
|
//init cache
|
||||||
|
assertFalse( isCached( customer.id, Customer.class, "applications" ) );
|
||||||
|
assertFalse( isCached( application.id, Application.class, "customers" ) );
|
||||||
|
|
||||||
|
customer = find( Customer.class, customer.id );
|
||||||
|
assertEquals( 1, customer.applications.size() );
|
||||||
|
application = find( Application.class, application.id );
|
||||||
|
assertEquals( 1, application.customers.size() );
|
||||||
|
|
||||||
|
assertTrue( isCached( customer.id, Customer.class, "applications" ) );
|
||||||
|
assertTrue( isCached( application.id, Application.class, "customers" ) );
|
||||||
|
|
||||||
|
//read from cache
|
||||||
|
customer = find( Customer.class, customer.id );
|
||||||
|
assertEquals( 1, customer.applications.size() );
|
||||||
|
application = find( Application.class, application.id );
|
||||||
|
assertEquals( 1, application.customers.size() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persist(Object entity) {
|
||||||
|
Session session = openSession();
|
||||||
|
session.beginTransaction();
|
||||||
|
session.persist( entity );
|
||||||
|
session.getTransaction().commit();
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private <E> E find(Class<E> entityClass, int id) {
|
||||||
|
Session session;
|
||||||
|
session = openSession();
|
||||||
|
E customer = session.get( entityClass, id );
|
||||||
|
session.close();
|
||||||
|
return customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCached(Serializable id, Class<?> entityClass, String attr) {
|
||||||
|
Session session = openSession();
|
||||||
|
CollectionPersister persister = sessionFactory().getCollectionPersister( entityClass.getName() + "." + attr );
|
||||||
|
CollectionRegionAccessStrategy cache = persister.getCacheAccessStrategy();
|
||||||
|
Object key = cache.generateCacheKey( id, persister, sessionFactory(), session.getTenantIdentifier() );
|
||||||
|
Object cachedValue = cache.get(
|
||||||
|
( (SessionImplementor) session ),
|
||||||
|
key,
|
||||||
|
( (SessionImplementor) session ).getTimestamp()
|
||||||
|
);
|
||||||
|
session.close();
|
||||||
|
return cachedValue != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class[] getAnnotatedClasses() {
|
||||||
|
return new Class[] {Application.class, Customer.class, Item.class};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "application")
|
||||||
|
@Cacheable
|
||||||
|
public static class Application {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@ManyToMany(mappedBy = "applications")
|
||||||
|
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
||||||
|
private List<Customer> customers = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "customer")
|
||||||
|
@Cacheable
|
||||||
|
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
||||||
|
public static class Customer {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@ManyToMany
|
||||||
|
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
||||||
|
private List<Application> applications = new ArrayList<>();
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
|
||||||
|
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
||||||
|
private List<Item> boughtItems = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "item")
|
||||||
|
@Cacheable
|
||||||
|
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
||||||
|
public static class Item {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Integer id;
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "customer_id")
|
||||||
|
private Customer customer;
|
||||||
|
|
||||||
|
protected Item() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Item(Customer customer) {
|
||||||
|
this.customer = customer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue