I am trying to integrate ehcache into spring mvc and hibernate application but its not working below is the code. I have followed the link - how to use ehcache in spring mvc with hibernate but still i am facing the problem. This problem is coming when i am starting the sertver. I am using spring 5.0.5
springmvc.xml
<beans:bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="packagesToScan">
<beans:array>
<beans:value>com.kalavakuri.springmvcandorm</beans:value>
</beans:array>
</beans:property>
<beans:property name="hibernateProperties">
<beans:props>
<beans:prop key="hibernate.dialect">${hibernate.dialect}</beans:prop>
<beans:prop key="hibernate.show_sql">${hibernate.show_sql}</beans:prop>
<beans:prop key="hibernate.cache.use_second_level_cache">true</beans:prop>
<beans:prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</beans:prop>
<beans:prop key="hibernate.cache.provider_configuration_file_resource_path">ehcache.xml</beans:prop>
<beans:prop key="hibernate.cache.use_query_cache">true</beans:prop>
<!-- <beans:prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</beans:prop> -->
</beans:props>
</beans:property>
</beans:bean>
ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<diskStore path="java.io.tmpdir" />
<!--defaultCache eternal="false" maxElementsInMemory="1000" maxElementsOnDisk="10000"
overflowToDisk="true" diskPersistent="true" timeToLiveSeconds="300" statistics="true"
copyOnWrite="true" / -->
<cache name="student" maxElementsInMemory="100000"
eternal="true" overflowToDisk="false" memoryStoreEvictionPolicy="LFU"
statistics="true" timeToLiveSeconds="3600" />
</ehcache>
I am getting below error:
Caused by: org.hibernate.cache.CacheException: On-the-fly creation of JCache Cache objects is not supported [org.hibernate.cache.spi.TimestampsRegion]
at org.hibernate.cache.ehcache.internal.EhcacheRegionFactory.createCache(EhcacheRegionFactory.java:106)
at org.hibernate.cache.ehcache.internal.EhcacheRegionFactory.getOrCreateCache(EhcacheRegionFactory.java:100)
at org.hibernate.cache.ehcache.internal.EhcacheRegionFactory.createTimestampsRegionStorageAccess(EhcacheRegionFactory.java:86)
at org.hibernate.cache.spi.support.RegionFactoryTemplate.buildTimestampsRegion(RegionFactoryTemplate.java:70)
at org.hibernate.cache.internal.EnabledCaching.<init>(EnabledCaching.java:80)
at org.hibernate.engine.spi.CacheInitiator.initiateService(CacheInitiator.java:33)
at org.hibernate.engine.spi.CacheInitiator.initiateService(CacheInitiator.java:24)
at org.hibernate.service.spi.SessionFactoryServiceInitiator.initiateService(SessionFactoryServiceInitiator.java:30)
at org.hibernate.service.internal.SessionFactoryServiceRegistryImpl.initiateService(SessionFactoryServiceRegistryImpl.java:68)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263)
... 99 more
According to the hibernate documentation you must create other 2 cache regions
org.hibernate.cache.spi.UpdateTimestampsCache: thios should be as great as possible (ideally never expiring)
org.hibernate.cache.internal.StandardQueryCache
Hibernate can't crate them on the fly (above all the first one)
Actually by using ehcahe 3 and hibernate 5 I use this configuration:
#Configuration
#EnableCaching
public class CacheConfiguration extends CachingConfigurerSupport
{
#Autowired
private Environment env;
#Bean("cacheManager")
#Override
public org.springframework.cache.CacheManager cacheManager()
{
return new JCacheCacheManager(createCacheManager());
}
private CacheManager createCacheManager()
{
long dimensioneCache = new Long(env.getProperty("arca.context.cache.size"));
long ttlMillisecondi = new Long(env.getProperty("arca.context.cache.ttl"));
org.ehcache.config.CacheConfiguration<Object, Object> cacheConfiguration = CacheConfigurationBuilder.
newCacheConfigurationBuilder(Object.class, Object.class,
ResourcePoolsBuilder.heap(dimensioneCache)
).withExpiry(Expirations.timeToLiveExpiration(new org.ehcache.expiry.Duration(ttlMillisecondi, TimeUnit.MILLISECONDS))).build();
Map<String, org.ehcache.config.CacheConfiguration<?, ?>> caches = createCacheConfigurations(cacheConfiguration);
//Creo la cache di hibernate org.hibernate.cache.spi.UpdateTimestampsCache.
//Dalla documentazione di hibernate https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#caching
ResourcePoolsBuilder rpb = ResourcePoolsBuilder.heap(dimensioneCache*1000000);
org.ehcache.config.CacheConfiguration<Object, Object> eternalCacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, rpb).withExpiry(Expirations.noExpiration()).build();
caches.put("org.hibernate.cache.spi.UpdateTimestampsCache", eternalCacheConfiguration);
EhcacheCachingProvider provider = getCachingProvider();
DefaultConfiguration configuration = new DefaultConfiguration(caches, provider.getDefaultClassLoader());
CacheManager result = provider.getCacheManager(provider.getDefaultURI(), configuration);
return result;
}
private Map<String, org.ehcache.config.CacheConfiguration<?, ?>> createCacheConfigurations(org.ehcache.config.CacheConfiguration<Object, Object> cacheConfiguration)
{
Map<String, org.ehcache.config.CacheConfiguration<?, ?>> caches = new HashMap<>();
// I'm searcing for all objects with #Entity annotation in order to create cache regions
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(Entity.class));
for (BeanDefinition bd : scanner.findCandidateComponents("it.test.models"))
{
String className = bd.getBeanClassName();
caches.put(className, cacheConfiguration);
}
//org.hibernate.cache.internal.StandardQueryCache creation
caches.put("org.hibernate.cache.internal.StandardQueryCache", cacheConfiguration);
return caches;
}
private EhcacheCachingProvider getCachingProvider()
{
return (EhcacheCachingProvider) Caching.getCachingProvider();
}
}
In my hibernate configuration I use this cache facory it.olegna.tests.hibernate.cache.config.JCacheRegionFactory its code is the following:
public class JCacheRegionFactory extends org.hibernate.cache.jcache.JCacheRegionFactory
{
private static final long serialVersionUID = 1021281213463444167L;
#Override
protected Cache<Object, Object> createCache(String regionName, Properties properties, CacheDataDescription metadata)
{
throw new IllegalArgumentException("Unknown hibernate cache: " + regionName);
}
}
I had the same issue. By adding #EnableCaching to the spring boot application #SpringBootApplication problem got resolved. Please try.
Related
I am having an issue with inserts not working. Reads (Selects) work okay. However, an insert is not being executed at end of my #Transactional method. Even though I do see the Transaction commit/close being called on the Entity Manager. I have tried different configurations and I still cannot get records (inserts) to work at the end of a transaction. And I don't see any errors being generated. I enabled the hibernate.transaction DEBUG and I don't see any hibernate transaction messages.
I am running Spring boot (.1.5.3) as a WAR executable with Tomcat. I am using persistence.xml (which is required by hibernate5-ddl-maven-plugin< to generate the SQL DDL during the build) on Spring Boot.
While debugging, I saw the JpaTransactionManager creating new transaction (Creating new transaction with name... Opened new EntityManager ,,,Exposing JPA transaction as JDBC transaction), joining other #Transactional methods (Found thread-bound EntityManager... Participating in existing transaction) and committing TX (Initiating transaction commit... Committing JPA transaction on EntityManager), and closing the JPA EM (Closing JPA EntityManager). This all happens according to the #Transactional specs. However, the record is not inserted into to the database. And I don't see any hibernate transaction messages.
Below is some of my configuration.
persistence.xml:
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<!--
This file is needed to generate the DDL.
-->
<persistence-unit
name="EzListaPersistence"
transaction-type="RESOURCE_LOCAL">
<description>
The set of entity types that can be managed by a
given entity manager is defined by a persistence unit. A
persistence unit defines the set of all classes that are
related or grouped by the application, and which must be
collocated in their mapping to a single data store.
</description>
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!--
List of fully qualified Entity Classes
-->
<class>com.ezlista.domain.account.Account</class>
...... NOT DISPLAYED ....
<class>com.ezlista.domain.useraccount.Notification</class>
<properties>
<!-- Hibernate Connection Settings -->
<property
name="hibernate.connection.provider_class"
value="org.hibernate.hikaricp.internal.HikariCPConnectionProvider" />
<property
name="hibernate.hikari.dataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" />
<property
name="hibernate.hikari.dataSource.url"
value="jdbc:mysql://localhost:3306/EZLISTA?verifyServerCertificate=false&useSSL=false" />
<property
name="hibernate.hikari.dataSource.user"
value="rubens" />
<property
name="hibernate.hikari.dataSource.password"
value="***MASKED***" />
<property
name="hibernate.hikari.dataSource.cachePrepStmts"
value="true" />
<property
name="hibernate.hikari.dataSource.prepStmtCacheSize"
value="250" />
<property
name="hibernate.hikari.dataSource.prepStmtCacheSqlLimit"
value="2048" />
<property
name="hibernate.hikari.minimumIdle"
value="5" />
<property
name="hibernate.hikari.maximumPoolSize"
value="10" />
<property
name="hibernate.hikari.idleTimeout"
value="30000" />
<!-- SQL Settings -->
<property name="hibernate.dialect"
value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<property name="hibernate.show_sql"
value="false" />
<property name="hibernate.format_sql"
value="true" />
<property name="hibernate.use_sql_comments"
value="false" />
<property name="hibernate.ddl-auto"
value="none" />
<!-- Hibernate Cache Settings -->
<property name="hibernate.javax.cache.provider"
value="org.ehcache.jsr107.EhcacheCachingProvider" />
<property name="hibernate.cache.region.factory_class"
value="org.hibernate.cache.jcache.JCacheRegionFactory" />
<property name="hibernate.cache.use_second_level_cache"
value="true" />
<property name="hibernate.cache.use_query_cache"
value="false" />
<property name="hibernate.cache.use_structured_entries"
value="true" />
<property name="hibernate.cache.use_minimal_puts"
value="true" />
</properties>
</persistence-unit>
Java Configuration:
#EntityScan("com.ezlista.domain")
#EnableTransactionManagement
/**
* Spring Bootstrap Repository Configuration.
*
* #author Rubens Gomes
*/
#Configuration
public class RepositoryConfiguration
{
private final static Logger logger = LoggerFactory.getLogger(RepositoryConfiguration.class);
private final static String PERSISTENCE_UNIT = "EzListaPersistence";
#Inject
private Environment env;
public RepositoryConfiguration()
{
super();
logger.debug("Constructed");
}
// DataSource
#Bean(name = "dataSource")
public DataSource dataSource()
{
logger.info("Registering HikariDataSource bean.");
HikariDataSource ds = new HikariDataSource();
ds.setDataSourceClassName(env.getRequiredProperty("hikari.dataSourceClassName"));
ds.setMinimumIdle(env.getRequiredProperty("hikari.minimumIdle", Integer.class));
ds.setMaximumPoolSize(env.getRequiredProperty("hikari.maximumPoolSize", Integer.class));
ds.setIdleTimeout(env.getRequiredProperty("hikari.idleTimeout", Integer.class));
ds.setPoolName(env.getRequiredProperty("hikari.poolName"));
ds.addDataSourceProperty("user", env.getRequiredProperty("hikari.dataSource.user"));
ds.addDataSourceProperty("password", env.getRequiredProperty("hikari.dataSource.password"));
ds.addDataSourceProperty("databaseName", env.getRequiredProperty("hikari.dataSource.databaseName"));
ds.addDataSourceProperty("serverName", env.getRequiredProperty("hikari.dataSource.serverName"));
ds.addDataSourceProperty("cachePrepStmts", env.getRequiredProperty("hikari.dataSource.cachePrepStmts", Boolean.class));
ds.addDataSourceProperty("prepStmtCacheSize", env.getRequiredProperty("hikari.dataSource.prepStmtCacheSize", Integer.class));
ds.addDataSourceProperty("prepStmtCacheSqlLimit", env.getRequiredProperty("hikari.dataSource.prepStmtCacheSqlLimit", Integer.class));
return ds;
}
// EntityManagerFactory
#Bean(name = "entityManagerFactory")
public EntityManagerFactory entityManagerFactory()
{
logger.info("Registering EntityManagerFactory bean.");
JpaVendorAdapter hibernateJpavendorAdapter = new HibernateJpaVendorAdapter();
JpaDialect hibernateJpaDialect = new HibernateJpaDialect();
LocalContainerEntityManagerFactoryBean emfBean =
new LocalContainerEntityManagerFactoryBean();
emfBean.setJpaVendorAdapter(hibernateJpavendorAdapter);
emfBean.setJpaDialect(hibernateJpaDialect);
emfBean.setPersistenceUnitName(PERSISTENCE_UNIT);
emfBean.setDataSource(dataSource());
emfBean.afterPropertiesSet();
return emfBean.getObject();
}
// TransactionManager
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager()
{
logger.info("Registering JpaTransactionManager bean.");
JpaTransactionManager txManager = new JpaTransactionManager();
EntityManagerFactory emf = entityManagerFactory();
txManager.setEntityManagerFactory(emf);
txManager.setDataSource(dataSource());
return txManager;
}
}
Bingo!!!!
I annotated my EntityManager with #PersistenceContext(unitName = PERSISTENCE_UNIT_NAME). After that it works.
Here is the code that fixed the above problem. Notice the #PersistenceContext annotation below (that's what fixed the issue). Before I had #Inject annotation on the em.
#PersistenceContext(unitName = PERSISTENCE_UNIT_NAME)
protected EntityManager em;
Consider the following two test. The findOne() has no side effect, the delete() has a side effect on the underlying h2 database. My problem is the #Transactional does not rollback the changes for the delete() method.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:app-context.xml")
public class AccountProcessorTest extends BaseRouteTest {
private static final String ACCOUNTS_ENDPOINT = "seda:bar";
#Test
#Transactional
public void findOne() {
final Map<String, Object> headers = new HashMap<String, Object>();
headers.put("id", 1);
headers.put(Exchange.HTTP_METHOD, "GET");
final String response = template.requestBodyAndHeaders(ACCOUNTS_ENDPOINT, null, headers, String.class);
assertEquals("Checking account",JsonPath.read(response, "name"));
}
#Test
#Transactional
public void delete() {
final Map<String, Object> headers = new HashMap<String, Object>();
headers.put("id", 1);
headers.put(Exchange.HTTP_METHOD, "DELETE");
final String response = template.requestBodyAndHeaders(ACCOUNTS_ENDPOINT, null, headers, String.class);
assertEquals(200, JsonPath.read(response, "code"));
}
}
The BaseRouteTest is just a utility where I get a reference to the Camel ProducerTemplate
public class BaseRouteTest implements InitializingBean {
#Autowired
private ApplicationContext applicationContext;
protected ProducerTemplate template;
#Override
public void afterPropertiesSet() throws Exception {
template = getCamelContext().createProducerTemplate();
}
private CamelContext getCamelContext() {
return applicationContext.getBean("foo", CamelContext.class);
}
}
I have marked the route as transacted using the transacted tag.
<!-- camel-context -->
<camel:camelContext id="foo">
<camel:route>
<camel:from uri="seda:bar"/>
<camel:transacted />
<camel:process ref="accountProcessor"/>
</camel:route>
</camel:camelContext>
My spring configuration file:
<context:component-scan base-package="com.backbase.progfun"/>
<!-- embedded datasource -->
<jdbc:embedded-database id="dataSource" type="H2">
<jdbc:script location="classpath:data/schema.ddl"/>
<jdbc:script location="classpath:data/insert.sql"/>
</jdbc:embedded-database>
<!-- spring jdbc template -->
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource" />
</bean>
<!-- transaction management start -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- transaction management end -->
<!-- camel-context -->
<camel:camelContext id="foo">
<camel:route>
<camel:from uri="seda:bar"/>
<camel:transacted />
<camel:process ref="accountProcessor"/>
</camel:route>
</camel:camelContext>
You can try it out quickly if you clone my github repo found here:
https://github.com/altfatterz/camel-transaction
If you run the AccountProcessorTest it the findOne() test case fails because the side effect of delete() test case is not rolled back.
Any suggestion would be greatly appreciated.
Thank you.
Transactions aren't carried across SEDA queues.
Therefore the transaction started by your test is a different transaction from the transaction in your route. So the changes made by your route won't be rolled back when the transaction started by your test is rolled back.
Does anyone know how to set EntityManagerFactory through constructor by #PersistenceUnit annotation. I could this through xml configuration. But i dont know the corresponding annotation configuration.
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml" />
<property name="persistenceUnitName" value="myUnit"></property>
<bean id="handler" class="com.handler.LocalHTHandler">
<constructor-arg ref="entityManagerFactory"></constructor-arg>
And it is working fine. Can we do it through annotation for my handler bean. I heard about #persistanceunit , but it can not be placed in constructor to achieve it. Is that correct?
As #Dherik said you can do this via constructor. Multi data source example below (tested on SpringBoot 2.0.4):
SomeRepository.java
#Repository
public class SomeRepository {
private final EntityManager entityManager;
public TestService(#Qualifier("someEntityManagerFactory") EntityManager entityManager) {
this.entityManager = entityManager;
}
}
SomeDatabaseConfig.java
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.HibernateJpaDialect;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "someEntityManagerFactory")
#EnableConfigurationProperties(JpaProperties.class)
class SomeDatabaseConfig {
private static final String PERSISTENCE_UNIT = "some";
private static final String[] PACKAGES_TO_SCAN = {"package.where.you.store.your.entities"};
#Bean(name = "someDataSourceProps")
#ConfigurationProperties("some.datasource")
DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
#Bean(name = "someDataSource")
DataSource dataSource(#Qualifier("someDataSourceProps") DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
#Bean(name = "someEntityManagerFactory")
LocalContainerEntityManagerFactoryBean entityManagerFactory(
JpaProperties jpaProperties,
#Qualifier("someDataSource") DataSource dataSource,
#Qualifier("someJpaVendorAdapter") JpaVendorAdapter jpaVendorAdapter) {
return createEntityManagerFactory(jpaProperties, dataSource, jpaVendorAdapter, PERSISTENCE_UNIT, PACKAGES_TO_SCAN);
}
#Bean("someJpaVendorAdapter")
JpaVendorAdapter jpaVendorAdapter(JpaProperties jpaProperties) {
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(jpaProperties.isShowSql());
return vendorAdapter;
}
#Bean(name = "someTransactionManager")
PlatformTransactionManager transactionManager(#Qualifier("someEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
static LocalContainerEntityManagerFactoryBean createEntityManagerFactory(JpaProperties jpaProperties, DataSource dataSource, JpaVendorAdapter jpaVendorAdapter, String persistenceUnit, String[] packagesToScan) {
final LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
factoryBean.setJpaDialect(new HibernateJpaDialect());
factoryBean.setJpaPropertyMap(getVendorProperties(jpaProperties));
factoryBean.setPersistenceUnitName(persistenceUnit);
factoryBean.setPackagesToScan(packagesToScan);
return factoryBean;
}
private static Map<String, Object> getVendorProperties(JpaProperties jpaProperties) {
return jpaProperties.getHibernateProperties(new HibernateSettings());
}
}
Application.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
#SpringBootApplication(
exclude = {
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class
}
)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Not possible with Spring using JSR 330 annotations. Currently it's not possible to inject EntityManagers into constructors as #PersistenceContext is defined to not be allowed on parameters. See JPA_SPEC-72 and Allow injecting EntityManagers through constructor injection (and at non-#PersistenceContext injection points in general) [SPR-10443].
Currently, the Spring team says that is possible inject EntityManager by constructor, as they already use on Spring JPA Repository.
Just:
private final EntityManager em;
public YourRepository(EntityManager em) {
this.em = em;
}
I tested. Works :). No more field injection.
If you would not like to use Spring Data in your project for some reason (e.g. you're just making a legacy project a bit better), you can create the following FactoryBean to make EntityManager injectable via constructor injection:
/**
* Makes the {#link EntityManager} injectable via <i>#Autowired</i>,
* so it can be injected with constructor injection too.
* (<i>#PersistenceContext</i> cannot be used for constructor injection.)
*/
public static class EntityManagerInjectionFactory extends AbstractFactoryBean<EntityManager> {
#PersistenceContext
private EntityManager entityManager;
#Override
public Class<?> getObjectType() {
return EntityManager.class;
}
#Override
protected EntityManager createInstance() {
return entityManager;
}
}
Please note, that because we use the #PersistenceContext annotation internally, the returned EntityManager will be a proper thread-safe proxy, as it would have been injected directly at the place of usage with field injection.
In our code, we have a base dao object which uses the injected entity manager through PersistenceContext:
public abstract class BasicJpaDao<T> implements IBasicDao<T> {
#PersistenceContext(type = PersistenceContextType.TRANSACTION, unitName = "default")
protected EntityManager entityManager;
// Default constructor for Spring
public BasicJpaDao() {}
//Use this constructor to set the entity manager yourself
public BasicJpaDao(EntityManager entityManager) {
this.entityManager = entityManager;
}
...
}
The persistence unit is defined in the Application Context file like so:
<!-- JPA -->
<!-- Creates an EntityManagerFactory for use with the Hibernate JPA provider -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSourceGlobal" />
<property name="packagesToScan" value="me.comocomo.nutrino.domain.jpa" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="jpaPropertyMap">
<map merge="true">
<entry key="hibernate.format_sql" value="${hibernate.format_sql}" />
</map>
</property>
</bean>
<!-- jpaVendorAdapter (works in conjunction with the persistence.xml) -->
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="${jpa.database}" />
<property name="showSql" value="${jpa.showSql}" />
<property name="databasePlatform" value="${jpa.dialect}" />
<property name="generateDdl" value="${jpa.generateDdl}" />
</bean>
<!-- In order to enable EntityManager injection -->
<bean id="persistenceAnnotation"
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<!-- Transactions -->
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSourceGlobal" />
</bean>
Similarly, you can create an EntityManagerFactoryFactory class which gets the EntityManagerFactory using Injection, and in turn passes the factory to your bean:
#Component
public class EntityManagerFactoryFactory {
#Inject
private EntityManagerFactory factory;
}
I need to get a specific EhCache instance by name and I'd prefer to autowire if possible. Given the following automatically configured controller, how can I autowire in the cache instance I'm looking for?
#Controller
public class MyUniqueService {
...
}
<beans ...>
<ctx:component-scan base-package="my.controllers"/>
<mvc:annotation-driven />
</beans>
How do I configure EhCache in my application context? I don't see any log messages from EhCache about it loading the ehcache.xml file in my /WEB-INF/ directory. How do I make it load it?
How can I integrate EhCache with my Spring application to have it load the ehcache.xml file from my /WEB-INF/ directory and autowire a cache by a given name into my MyUniqueService controller?
First you need to create a Ehcache CacheManager singleton in you app context like this:
<bean id="myEhCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:my-ehcache.xml"/>
</bean>
Here configLocation is set to load from classpath or use value="/WEB-INF/my-ehcache.xml".
In your controller simply inject the CacheManager instance:
#Controller
public class MyUniqueService {
#Resource(name="myEhCacheManager")
private CacheManager cacheManager;
...
}
Alternatively, if you'd like to go the "entirely autowired" route, do:
<bean class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager">
<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="/WEB-INF/ehcache.xml"/>
</bean>
</property>
</bean>
Setup your class like so:
#Controller
public class MyUniqueService {
#Autowired
private org.springframework.cache.CacheManager cacheManager;
public org.springframework.cache.Cache getUniqueObjectCache() {
return cacheManager.getCache("uniqueObjectCache");
}
}
uniqueObjectCache corresponds to this cache instance in your ehcache.xml cache definition:
<cache name="uniqueObjectCache"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"
transactionalMode="off"/>
There isn't a way to inject an actual cache instance, but as shown above, you can inject a cache manager and use it to get the cache you're interested in.
Assuming you have cacheManager defined:
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:/ehcache.xml"/>
</bean>
You can get/inject specific cache like this:
#Value("#{cacheManager.getCache('myCacheName')}")
private Cache myCache;
See also examples how to use Spring EL inside the #Value() http://www.mkyong.com/spring3/spring-el-method-invocation-example/ if you are interested.
You can also use autowire if the context can find a bean with the correct class. Here is how I configured my xml
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>WEB-INF/ehcache.xml</value>
</property>
</bean>
<bean id="cache" class="net.sf.ehcache.Cache" factory-bean="cacheManager" factory-method="getCache">
<constructor-arg value="CacheNameHere" />
</bean>
And my java class
#Autowired
private net.sf.ehcache.Cache cache;
This setup works for me.
Indeed! Or if you want to use a java config class:
#Inject
private ResourceLoader resourceLoader;
#Bean
public CacheManager cacheManager() {
EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager();
try {
ehCacheCacheManager.setCacheManager(ehcacheCacheManager().getObject());
} catch (Exception e) {
throw new IllegalStateException("Failed to create an EhCacheManagerFactoryBean", e);
}
return ehCacheCacheManager;
}
#Bean
public FactoryBean<net.sf.ehcache.CacheManager> ehcacheCacheManager() {
EhCacheManagerFactoryBean bean = new EhCacheManagerFactoryBean();
bean.setConfigLocation(resourceLoader.getResource("classpath:ehcache.xml"));
return bean;
}
I am using Spring 3.1.1 with Freemarker.
After I have successded to use the new concept of java based configuraion of the new spring 3.1.
Now I try to use Freemarker with it.
However,I got that exception:
javax.servlet.ServletException: Could not resolve view with name
'/welcome' in servlet with name 'appServlet'
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1139)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:927)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:827)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
Here is the WebConfig:
package com.springway.config;
#Configuration
#ComponentScan("com.springway")
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Inject
private Environment environment;
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJacksonHttpMessageConverter());
}
#Bean
public ViewResolver viewResolver() {
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setViewClass(FreeMarkerView.class);
return viewResolver;
}
#Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath(
"/WEB-INF/freemarker.xml");
return configurer;
}
freemarker.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- freemarker config -->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
<property name="freemarkerSettings">
<props>
<prop key="number_format">0.######</prop>
</props>
</property>
<property name="freemarkerVariables">
<map>
<entry key="xml_escape" value-ref="fmXmlEscape"/>
</map>
</property>
</bean>
<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/>
<!--
View resolvers can also be configured with ResourceBundles or XML files. If you need
different view resolving based on Locale, you have to use the resource bundle resolver.
-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true"/>
<property name="prefix" value=""/>
<property name="suffix" value=".ftl"/>
</bean>
</beans>
The controller :
#RequestMapping(value="/", method=RequestMethod.GET)
public ModelAndView home(Principal user) {
return new ModelAndView("/welcome.ftl");
}
I am struggling the same problem as yours. And I tried to turn on logging level "DEBUG" for "org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer". I found following message:
java.io.FileNotFoundException: ServletContext resource [/WEB-INF/freemarker/] cannot be resolved to URL because it does not exist
at org.springframework.web.context.support.ServletContextResource.getURL(ServletContextResource.java:154) ~[spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.springframework.web.context.support.ServletContextResource.getFile(ServletContextResource.java:169) ~[spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.springframework.ui.freemarker.FreeMarkerConfigurationFactory.getTemplateLoaderForPath(FreeMarkerConfigurationFactory.java:351) [spring-context-support-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.springframework.ui.freemarker.FreeMarkerConfigurationFactory.createConfiguration(FreeMarkerConfigurationFactory.java:304) [spring-context-support-3.1.1.RELEASE.jar:3.1.1.RELEASE]
.....
And a wired log from Jetty:
WARN org.eclipse.jetty.util.log - /freemarker/
I don't know what going on! So that I replaced the value of "templateLoaderPath" with another name, like "/WEB-INF/tmpl-freemarker/", then everything is workable!!
Next, I tried to use ServerContext.getResource() to test "/WEB-INF/freemarker/html.ftl". Here is the result:
Resource under /WEB-INF/freemarker/: null
My code of testing:
#Inject
private ServletContext servletContext;
try {
logger.info("Resource under /WEB-INF/freemarker/: {}", servletContext.getResource("/WEB-INF/freemarker/html.ftl"));
} catch(Exception e) {
logger.info("Exception while getResource", e);
}
It seems ServletContext can't find out the resource under "/WEB-INF/freemarker/" correctly either. But I'm not sure the object of ServletContext of Spring is as same as one on normal environment of Java Servlet.
I'm using FreeMarker 2.3.19, Springframework 3.1.1.RELEASE, and jetty-maven-plugin 7.4.5.v20110725 of Maven.
#Configuration
#EnableWebMvc
#ImportResource("classpath:/security-integration.xml")
#ComponentScan("com.springway")
public class WebConfiguration extends WebMvcConfigurerAdapter {
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Bean
public FreeMarkerViewResolver viewResolver() {
FreeMarkerViewResolver freeMarkerViewResolver = new FreeMarkerViewResolver();
freeMarkerViewResolver.setCache(true);
freeMarkerViewResolver.setPrefix("");
freeMarkerViewResolver.setSuffix(".ftl");
return freeMarkerViewResolver;
}
#Bean
public FreeMarkerConfigurer freemarkerConfig() {
FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
freeMarkerConfigurer.setTemplateLoaderPath("/WEB-INF/freemarker/");
Properties settings = new Properties();
settings.setProperty("number_format", "0.######");
freeMarkerConfigurer.setFreemarkerSettings(settings);
Map variables = new java.util.HashMap<String, Object>();
variables.put("xml_escape", new XmlEscape());
freeMarkerConfigurer.setFreemarkerVariables(variables);
return freeMarkerConfigurer;
}
}
You might have done wrong import
import org.springframework.web.portlet.ModelAndView;
instead of do this
import org.springframework.web.servlet.ModelAndView;