diff --git a/spring-jpa/pom.xml b/spring-jpa/pom.xml
index 25dd960435..2c189bde4b 100644
--- a/spring-jpa/pom.xml
+++ b/spring-jpa/pom.xml
@@ -29,6 +29,11 @@
hibernate-entitymanager
${hibernate.version}
+
+ org.hibernate
+ hibernate-ehcache
+ ${hibernate.version}
+
xml-apis
xml-apis
@@ -50,6 +55,11 @@
spring-data-jpa
${spring-data-jpa.version}
+
+ com.h2database
+ h2
+ ${h2.version}
+
@@ -186,6 +196,7 @@
4.3.11.Final
5.1.38
1.8.2.RELEASE
+
1.4.192
1.7.13
diff --git a/spring-jpa/src/main/java/org/baeldung/config/PersistenceJPAConfig.java b/spring-jpa/src/main/java/org/baeldung/config/PersistenceJPAConfig.java
index c9358190e7..010eb5b8a1 100644
--- a/spring-jpa/src/main/java/org/baeldung/config/PersistenceJPAConfig.java
+++ b/spring-jpa/src/main/java/org/baeldung/config/PersistenceJPAConfig.java
@@ -78,6 +78,8 @@ public class PersistenceJPAConfig {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
+ hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", env.getProperty("hibernate.cache.use_second_level_cache"));
+ hibernateProperties.setProperty("hibernate.cache.use_query_cache", env.getProperty("hibernate.cache.use_query_cache"));
// hibernateProperties.setProperty("hibernate.globally_quoted_identifiers", "true");
return hibernateProperties;
}
diff --git a/spring-jpa/src/main/java/org/baeldung/config/PersistenceJPAConfigL2Cache.java b/spring-jpa/src/main/java/org/baeldung/config/PersistenceJPAConfigL2Cache.java
new file mode 100644
index 0000000000..3ca0dbf5e4
--- /dev/null
+++ b/spring-jpa/src/main/java/org/baeldung/config/PersistenceJPAConfigL2Cache.java
@@ -0,0 +1,84 @@
+package org.baeldung.config;
+
+import com.google.common.base.Preconditions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.env.Environment;
+import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.jdbc.datasource.DriverManagerDataSource;
+import org.springframework.orm.jpa.JpaTransactionManager;
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.persistence.EntityManagerFactory;
+import javax.sql.DataSource;
+import java.util.Properties;
+
+@Configuration
+@EnableTransactionManagement
+@PropertySource({ "classpath:persistence-h2.properties" })
+@ComponentScan({ "org.baeldung.persistence" })
+@EnableJpaRepositories(basePackages = "org.baeldung.persistence.dao")
+public class PersistenceJPAConfigL2Cache {
+
+ @Autowired
+ private Environment env;
+
+ public PersistenceJPAConfigL2Cache() {
+ super();
+ }
+
+ // beans
+
+ @Bean
+ public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
+ final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
+ em.setDataSource(dataSource());
+ em.setPackagesToScan(new String[] { "org.baeldung.persistence.model" });
+
+ final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
+ em.setJpaVendorAdapter(vendorAdapter);
+ em.setJpaProperties(additionalProperties());
+
+ return em;
+ }
+
+ @Bean
+ public DataSource dataSource() {
+ final DriverManagerDataSource dataSource = new DriverManagerDataSource();
+ dataSource.setDriverClassName(Preconditions.checkNotNull(env.getProperty("jdbc.driverClassName")));
+ dataSource.setUrl(Preconditions.checkNotNull(env.getProperty("jdbc.url")));
+ dataSource.setUsername(Preconditions.checkNotNull(env.getProperty("jdbc.user")));
+
+ return dataSource;
+ }
+
+ @Bean
+ public PlatformTransactionManager transactionManager(final EntityManagerFactory emf) {
+ final JpaTransactionManager transactionManager = new JpaTransactionManager();
+ transactionManager.setEntityManagerFactory(emf);
+ return transactionManager;
+ }
+
+ @Bean
+ public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
+ return new PersistenceExceptionTranslationPostProcessor();
+ }
+
+ final Properties additionalProperties() {
+ final Properties hibernateProperties = new Properties();
+ hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
+ hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
+ hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", env.getProperty("hibernate.cache.use_second_level_cache"));
+ hibernateProperties.setProperty("hibernate.cache.use_query_cache", env.getProperty("hibernate.cache.use_query_cache"));
+ hibernateProperties.setProperty("hibernate.cache.region.factory_class", env.getProperty("hibernate.cache.region.factory_class"));
+ return hibernateProperties;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-jpa/src/main/java/org/baeldung/persistence/model/Foo.java b/spring-jpa/src/main/java/org/baeldung/persistence/model/Foo.java
index 585cefb159..209ab081de 100644
--- a/spring-jpa/src/main/java/org/baeldung/persistence/model/Foo.java
+++ b/spring-jpa/src/main/java/org/baeldung/persistence/model/Foo.java
@@ -1,17 +1,13 @@
package org.baeldung.persistence.model;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+
+import javax.persistence.*;
import java.io.Serializable;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.JoinColumn;
-import javax.persistence.ManyToOne;
-
@Entity
+@Cacheable
+@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Foo implements Serializable {
private static final long serialVersionUID = 1L;
diff --git a/spring-jpa/src/main/resources/jpaConfig.xml b/spring-jpa/src/main/resources/jpaConfig.xml
index 1f0b8d899f..5afc0af94d 100644
--- a/spring-jpa/src/main/resources/jpaConfig.xml
+++ b/spring-jpa/src/main/resources/jpaConfig.xml
@@ -21,6 +21,8 @@
${hibernate.hbm2ddl.auto}
${hibernate.dialect}
+ ${hibernate.cache.use_second_level_cache}
+ ${hibernate.cache.use_query_cache}
diff --git a/spring-jpa/src/main/resources/persistence-h2.properties b/spring-jpa/src/main/resources/persistence-h2.properties
new file mode 100644
index 0000000000..d195af5ec9
--- /dev/null
+++ b/spring-jpa/src/main/resources/persistence-h2.properties
@@ -0,0 +1,13 @@
+# jdbc.X
+jdbc.driverClassName=org.h2.Driver
+jdbc.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
+jdbc.user=sa
+# jdbc.pass=
+
+# hibernate.X
+hibernate.dialect=org.hibernate.dialect.H2Dialect
+hibernate.show_sql=false
+hibernate.hbm2ddl.auto=create-drop
+hibernate.cache.use_second_level_cache=true
+hibernate.cache.use_query_cache=true
+hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
\ No newline at end of file
diff --git a/spring-jpa/src/main/resources/persistence-multiple-db.properties b/spring-jpa/src/main/resources/persistence-multiple-db.properties
index d59956ba03..1a0d99c704 100644
--- a/spring-jpa/src/main/resources/persistence-multiple-db.properties
+++ b/spring-jpa/src/main/resources/persistence-multiple-db.properties
@@ -9,3 +9,5 @@ jdbc.pass=tutorialmy5ql
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.show_sql=false
hibernate.hbm2ddl.auto=create-drop
+hibernate.cache.use_second_level_cache=false
+hibernate.cache.use_query_cache=false
\ No newline at end of file
diff --git a/spring-jpa/src/main/resources/persistence-mysql.properties b/spring-jpa/src/main/resources/persistence-mysql.properties
index c4de4ceb80..12b4c93d80 100644
--- a/spring-jpa/src/main/resources/persistence-mysql.properties
+++ b/spring-jpa/src/main/resources/persistence-mysql.properties
@@ -8,3 +8,5 @@ jdbc.pass=tutorialmy5ql
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.show_sql=false
hibernate.hbm2ddl.auto=create-drop
+hibernate.cache.use_second_level_cache=false
+hibernate.cache.use_query_cache=false
\ No newline at end of file
diff --git a/spring-jpa/src/test/java/META-INF/persistence.xml b/spring-jpa/src/test/java/META-INF/persistence.xml
index e528491795..ba0d2377d1 100644
--- a/spring-jpa/src/test/java/META-INF/persistence.xml
+++ b/spring-jpa/src/test/java/META-INF/persistence.xml
@@ -10,6 +10,8 @@
+
+
diff --git a/spring-jpa/src/test/java/org/baeldung/persistence/service/SecondLevelCacheIntegrationTest.java b/spring-jpa/src/test/java/org/baeldung/persistence/service/SecondLevelCacheIntegrationTest.java
new file mode 100644
index 0000000000..f97f53b82c
--- /dev/null
+++ b/spring-jpa/src/test/java/org/baeldung/persistence/service/SecondLevelCacheIntegrationTest.java
@@ -0,0 +1,84 @@
+package org.baeldung.persistence.service;
+
+import net.sf.ehcache.CacheManager;
+import org.baeldung.config.PersistenceJPAConfigL2Cache;
+import org.baeldung.persistence.model.Bar;
+import org.baeldung.persistence.model.Foo;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.support.AnnotationConfigContextLoader;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.support.TransactionTemplate;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+
+import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.junit.Assert.assertThat;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = { PersistenceJPAConfigL2Cache.class }, loader = AnnotationConfigContextLoader.class)
+public class SecondLevelCacheIntegrationTest {
+
+ @PersistenceContext
+ private EntityManager entityManager;
+ @Autowired
+ private FooService fooService;
+ @Autowired
+ private PlatformTransactionManager platformTransactionManager;
+
+ @Before
+ public final void before() {
+ entityManager.getEntityManagerFactory().getCache().evictAll();
+ }
+
+ @Test
+ public final void givenEntityIsLoaded_thenItIsCached() {
+ final Foo foo = new Foo(randomAlphabetic(6));
+ fooService.create(foo);
+ fooService.findOne(foo.getId());
+ final int size = CacheManager.ALL_CACHE_MANAGERS.get(0)
+ .getCache("org.baeldung.persistence.model.Foo").getSize();
+ assertThat(size, greaterThan(0));
+ }
+
+ @Test
+ public final void givenBarIsUpdatedInNativeQuery_thenFoosAreNotEvicted() {
+ final Foo foo = new Foo(randomAlphabetic(6));
+ fooService.create(foo);
+ fooService.findOne(foo.getId());
+
+ new TransactionTemplate(platformTransactionManager).execute(status -> {
+ final Bar bar = new Bar(randomAlphabetic(6));
+ entityManager.persist(bar);
+ final Query nativeQuery = entityManager.createNativeQuery("update BAR set NAME = :updatedName where ID = :id");
+ nativeQuery.setParameter("updatedName", "newName");
+ nativeQuery.setParameter("id", bar.getId());
+ nativeQuery.unwrap(org.hibernate.SQLQuery.class).addSynchronizedEntityClass(Bar.class);
+ return nativeQuery.executeUpdate();
+ });
+
+ final int size = CacheManager.ALL_CACHE_MANAGERS.get(0)
+ .getCache("org.baeldung.persistence.model.Foo").getSize();
+ assertThat(size, greaterThan(0));
+ }
+
+ @Test
+ public final void givenCacheableQueryIsExecuted_thenItIsCached() {
+ new TransactionTemplate(platformTransactionManager).execute(status -> {
+ return entityManager.createQuery("select f from Foo f")
+ .setHint("org.hibernate.cacheable", true)
+ .getResultList();
+ });
+
+ final int size = CacheManager.ALL_CACHE_MANAGERS.get(0)
+ .getCache("org.hibernate.cache.internal.StandardQueryCache").getSize();
+ assertThat(size, greaterThan(0));
+ }
+}