HHH-12771 - Caused by: java.lang.UnsupportedOperationException: Cache provider [org.hibernate.cache.ehcache.internal.EhcacheRegionFactory@3271ec2a] does not support `transactional` access

This commit is contained in:
Vlad Mihalcea 2018-07-11 13:07:36 +03:00
parent c424583d8e
commit cf3622f64c
11 changed files with 435 additions and 7 deletions

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.cache.spi;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.jboss.logging.BasicLogger;
@ -90,4 +91,12 @@ public interface SecondLevelCacheLogger extends BasicLogger {
)
void usingLegacyCacheName(String currentName, String legacyName);
@LogMessage(level = WARN)
@Message(
value = "Cache [%1$s] uses the [%2$s] access type, but [%3$s] does not support it natively." +
" Make sure your cache implementation supports JTA transactions.",
id = NAMESPACE + 8
)
void nonStandardSupportForAccessType(String key, String accessType, String regionName);
}

View File

@ -4,7 +4,7 @@
* 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.testing.cache;
package org.hibernate.cache.spi.support;
import org.hibernate.cache.cfg.spi.CollectionDataCachingConfig;
import org.hibernate.cache.spi.CacheKeysFactory;

View File

@ -4,7 +4,7 @@
* 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.testing.cache;
package org.hibernate.cache.spi.support;
import org.hibernate.cache.cfg.spi.CollectionDataCachingConfig;
import org.hibernate.cache.cfg.spi.DomainDataRegionBuildingContext;
@ -15,7 +15,6 @@ import org.hibernate.cache.spi.CacheKeysFactory;
import org.hibernate.cache.spi.access.CollectionDataAccess;
import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.cache.spi.access.NaturalIdDataAccess;
import org.hibernate.cache.spi.support.DomainDataRegionTemplate;
/**
* @author Steve Ebersole
@ -24,13 +23,14 @@ public class DomainDataRegionImpl extends DomainDataRegionTemplate {
@SuppressWarnings("WeakerAccess")
public DomainDataRegionImpl(
DomainDataRegionConfig regionConfig,
CachingRegionFactory regionFactory,
RegionFactoryTemplate regionFactory,
DomainDataStorageAccess domainDataStorageAccess,
CacheKeysFactory defaultKeysFactory,
DomainDataRegionBuildingContext buildingContext) {
super(
regionConfig,
regionFactory,
new MapStorageAccessImpl(),
domainDataStorageAccess,
defaultKeysFactory,
buildingContext
);

View File

@ -4,7 +4,7 @@
* 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.testing.cache;
package org.hibernate.cache.spi.support;
import org.hibernate.cache.cfg.spi.EntityDataCachingConfig;
import org.hibernate.cache.spi.CacheKeysFactory;

View File

@ -4,7 +4,7 @@
* 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.testing.cache;
package org.hibernate.cache.spi.support;
import org.hibernate.cache.cfg.spi.NaturalIdDataCachingConfig;
import org.hibernate.cache.spi.CacheKeysFactory;

View File

@ -25,11 +25,13 @@ import org.hibernate.cache.ehcache.ConfigSettings;
import org.hibernate.cache.ehcache.MissingCacheStrategy;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.spi.CacheKeysFactory;
import org.hibernate.cache.spi.DomainDataRegion;
import org.hibernate.cache.spi.SecondLevelCacheLogger;
import org.hibernate.cache.spi.support.DomainDataStorageAccess;
import org.hibernate.cache.spi.support.RegionFactoryTemplate;
import org.hibernate.cache.spi.support.RegionNameQualifier;
import org.hibernate.cache.spi.support.StorageAccess;
import org.hibernate.cache.spi.support.DomainDataRegionImpl;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import static org.hibernate.cache.ehcache.ConfigSettings.EHCACHE_CONFIGURATION_RESOURCE_NAME;
@ -65,6 +67,18 @@ public class EhcacheRegionFactory extends RegionFactoryTemplate {
return cacheKeysFactory;
}
@Override
public DomainDataRegion buildDomainDataRegion(
DomainDataRegionConfig regionConfig, DomainDataRegionBuildingContext buildingContext) {
return new DomainDataRegionImpl(
regionConfig,
this,
createDomainDataStorageAccess( regionConfig, buildingContext ),
cacheKeysFactory,
buildingContext
);
}
@Override
protected DomainDataStorageAccess createDomainDataStorageAccess(
DomainDataRegionConfig regionConfig,

View File

@ -0,0 +1,162 @@
/*
* 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.cache.ehcache.test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.hibernate.testing.jta.TestingJtaBootstrap;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
public class EhcacheTransactionalCacheConcurrencyStrategyTest
extends BaseNonConfigCoreFunctionalTestCase {
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
sqlStatementInterceptor = new SQLStatementInterceptor( sfb );
}
@Override
protected void addSettings(Map settings) {
settings.put( Environment.ENABLE_LAZY_LOAD_NO_TRANS, "true" );
TestingJtaBootstrap.prepare( settings );
settings.put( Environment.TRANSACTION_COORDINATOR_STRATEGY, "jta" );
settings.put( Environment.CACHE_REGION_FACTORY, "ehcache" );
}
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Parent.class,
Child.class
};
}
@Entity(name = "Parent")
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public static class Parent {
@Id
@GeneratedValue
private Long id;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "parent")
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
private List<Child> children = new ArrayList<Child>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<Child> getChildren() {
return children;
}
public void setChildren(List<Child> children) {
this.children = children;
}
Child addChild() {
final Child c = new Child();
c.setParent( this );
this.children.add( c );
return c;
}
}
@Entity(name = "Child")
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public static class Child {
@Id
@GeneratedValue
private Long id;
@ManyToOne(
fetch = FetchType.LAZY
)
private Parent parent;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
@Test
public void testTransactional() {
Parent parent = new Parent();
doInHibernate( this::sessionFactory, session -> {
for ( int i = 0; i < 2; i++ ) {
parent.addChild();
session.persist( parent );
}
} );
doInHibernate( this::sessionFactory, session -> {
sqlStatementInterceptor.getSqlQueries().clear();
Parent _parent = session.find( Parent.class, parent.getId() );
assertEquals( 0, sqlStatementInterceptor.getSqlQueries().size() );
assertEquals( 2, _parent.getChildren().size() );
} );
doInHibernate( this::sessionFactory, session -> {
sqlStatementInterceptor.getSqlQueries().clear();
Parent _parent = session.find( Parent.class, parent.getId() );
assertEquals( 2, _parent.getChildren().size() );
assertEquals( 0, sqlStatementInterceptor.getSqlQueries().size() );
} );
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.cache.jcache.internal;
import org.hibernate.cache.cfg.spi.CollectionDataCachingConfig;
import org.hibernate.cache.cfg.spi.DomainDataRegionBuildingContext;
import org.hibernate.cache.cfg.spi.DomainDataRegionConfig;
import org.hibernate.cache.cfg.spi.EntityDataCachingConfig;
import org.hibernate.cache.cfg.spi.NaturalIdDataCachingConfig;
import org.hibernate.cache.spi.CacheKeysFactory;
import org.hibernate.cache.spi.SecondLevelCacheLogger;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.CollectionDataAccess;
import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.cache.spi.access.NaturalIdDataAccess;
import org.hibernate.cache.spi.support.DomainDataRegionImpl;
import org.hibernate.cache.spi.support.DomainDataStorageAccess;
import org.hibernate.cache.spi.support.RegionFactoryTemplate;
/**
* @author Vlad Mihalcea
*/
public class JCacheDomainDataRegionImpl extends DomainDataRegionImpl {
public JCacheDomainDataRegionImpl(
DomainDataRegionConfig regionConfig,
RegionFactoryTemplate regionFactory,
DomainDataStorageAccess domainDataStorageAccess,
CacheKeysFactory defaultKeysFactory,
DomainDataRegionBuildingContext buildingContext) {
super( regionConfig, regionFactory, domainDataStorageAccess, defaultKeysFactory, buildingContext );
}
@Override
protected EntityDataAccess generateTransactionalEntityDataAccess(EntityDataCachingConfig entityAccessConfig) {
SecondLevelCacheLogger.INSTANCE.nonStandardSupportForAccessType(
getName(),
AccessType.TRANSACTIONAL.getExternalName(),
getRegionFactory().getClass().getSimpleName()
);
return super.generateTransactionalEntityDataAccess( entityAccessConfig );
}
@Override
protected NaturalIdDataAccess generateTransactionalNaturalIdDataAccess(NaturalIdDataCachingConfig accessConfig) {
SecondLevelCacheLogger.INSTANCE.nonStandardSupportForAccessType(
getName(),
AccessType.TRANSACTIONAL.getExternalName(),
getRegionFactory().getClass().getSimpleName()
);
return super.generateTransactionalNaturalIdDataAccess( accessConfig );
}
@Override
protected CollectionDataAccess generateTransactionalCollectionDataAccess(CollectionDataCachingConfig accessConfig) {
SecondLevelCacheLogger.INSTANCE.nonStandardSupportForAccessType(
getName(),
AccessType.TRANSACTIONAL.getExternalName(),
getRegionFactory().getClass().getSimpleName()
);
return super.generateTransactionalCollectionDataAccess( accessConfig );
}
}

View File

@ -25,11 +25,13 @@ import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.jcache.ConfigSettings;
import org.hibernate.cache.jcache.MissingCacheStrategy;
import org.hibernate.cache.spi.CacheKeysFactory;
import org.hibernate.cache.spi.DomainDataRegion;
import org.hibernate.cache.spi.SecondLevelCacheLogger;
import org.hibernate.cache.spi.support.DomainDataStorageAccess;
import org.hibernate.cache.spi.support.RegionFactoryTemplate;
import org.hibernate.cache.spi.support.RegionNameQualifier;
import org.hibernate.cache.spi.support.StorageAccess;
import org.hibernate.cache.spi.support.DomainDataRegionImpl;
import org.hibernate.engine.spi.SessionFactoryImplementor;
/**
@ -60,6 +62,18 @@ public class JCacheRegionFactory extends RegionFactoryTemplate {
return cacheKeysFactory;
}
@Override
public DomainDataRegion buildDomainDataRegion(
DomainDataRegionConfig regionConfig, DomainDataRegionBuildingContext buildingContext) {
return new JCacheDomainDataRegionImpl(
regionConfig,
this,
createDomainDataStorageAccess( regionConfig, buildingContext ),
cacheKeysFactory,
buildingContext
);
}
@Override
protected DomainDataStorageAccess createDomainDataStorageAccess(
DomainDataRegionConfig regionConfig,

View File

@ -0,0 +1,160 @@
/*
* 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.cache.jcache;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.hibernate.testing.jta.TestingJtaBootstrap;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
public class JCacheTransactionalCacheConcurrencyStrategyTest
extends BaseNonConfigCoreFunctionalTestCase {
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
sqlStatementInterceptor = new SQLStatementInterceptor( sfb );
}
@Override
protected void addSettings(Map settings) {
settings.put( Environment.ENABLE_LAZY_LOAD_NO_TRANS, "true" );
TestingJtaBootstrap.prepare( settings );
settings.put( Environment.TRANSACTION_COORDINATOR_STRATEGY, "jta" );
settings.put( Environment.CACHE_REGION_FACTORY, "jcache" );
}
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Parent.class,
Child.class
};
}
@Entity(name = "Parent")
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public static class Parent {
@Id
@GeneratedValue
private Long id;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "parent")
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
private List<Child> children = new ArrayList<Child>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<Child> getChildren() {
return children;
}
public void setChildren(List<Child> children) {
this.children = children;
}
Child addChild() {
final Child c = new Child();
c.setParent( this );
this.children.add( c );
return c;
}
}
@Entity(name = "Child")
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public static class Child {
@Id
@GeneratedValue
private Long id;
@ManyToOne(
fetch = FetchType.LAZY
)
private Parent parent;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
@Test
public void testTransactional() {
Parent parent = new Parent();
doInHibernate( this::sessionFactory, session -> {
for ( int i = 0; i < 2; i++ ) {
parent.addChild();
session.persist( parent );
}
} );
doInHibernate( this::sessionFactory, session -> {
sqlStatementInterceptor.getSqlQueries().clear();
Parent _parent = session.find( Parent.class, parent.getId() );
assertEquals( 0, sqlStatementInterceptor.getSqlQueries().size() );
assertEquals( 2, _parent.getChildren().size() );
} );
doInHibernate( this::sessionFactory, session -> {
sqlStatementInterceptor.getSqlQueries().clear();
Parent _parent = session.find( Parent.class, parent.getId() );
assertEquals( 2, _parent.getChildren().size() );
assertEquals( 0, sqlStatementInterceptor.getSqlQueries().size() );
} );
}
}

View File

@ -17,6 +17,7 @@ import org.hibernate.cache.spi.CacheKeysFactory;
import org.hibernate.cache.spi.DomainDataRegion;
import org.hibernate.cache.spi.support.RegionFactoryTemplate;
import org.hibernate.cache.spi.support.StorageAccess;
import org.hibernate.cache.spi.support.DomainDataRegionImpl;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.jboss.logging.Logger;
@ -58,6 +59,7 @@ public class CachingRegionFactory extends RegionFactoryTemplate {
return new DomainDataRegionImpl(
regionConfig,
this,
new MapStorageAccessImpl(),
cacheKeysFactory,
buildingContext
);