diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml
index e84115b70a..c4893df759 100644
--- a/spring-data-jpa/pom.xml
+++ b/spring-data-jpa/pom.xml
@@ -21,11 +21,21 @@
org.hibernate
hibernate-ehcache
+
+ org.hibernate
+ hibernate-envers
+
com.h2database
h2
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
com.google.guava
guava
diff --git a/spring-data-jpa/src/main/java/com/baeldung/persistence/dao/IBarCrudRepository.java b/spring-data-jpa/src/main/java/com/baeldung/persistence/dao/IBarCrudRepository.java
new file mode 100644
index 0000000000..e7941e7093
--- /dev/null
+++ b/spring-data-jpa/src/main/java/com/baeldung/persistence/dao/IBarCrudRepository.java
@@ -0,0 +1,10 @@
+package com.baeldung.persistence.dao;
+
+import com.baeldung.persistence.model.Bar;
+import org.springframework.data.repository.CrudRepository;
+
+import java.io.Serializable;
+
+public interface IBarCrudRepository extends CrudRepository {
+ //
+}
diff --git a/spring-data-jpa/src/main/java/com/baeldung/persistence/dao/common/IOperations.java b/spring-data-jpa/src/main/java/com/baeldung/persistence/dao/common/IOperations.java
new file mode 100644
index 0000000000..4ef99221ab
--- /dev/null
+++ b/spring-data-jpa/src/main/java/com/baeldung/persistence/dao/common/IOperations.java
@@ -0,0 +1,20 @@
+package com.baeldung.persistence.dao.common;
+
+import java.io.Serializable;
+import java.util.List;
+
+public interface IOperations {
+
+ T findOne(final long id);
+
+ List findAll();
+
+ void create(final T entity);
+
+ T update(final T entity);
+
+ void delete(final T entity);
+
+ void deleteById(final long entityId);
+
+}
diff --git a/spring-data-jpa/src/main/java/com/baeldung/persistence/model/Bar.java b/spring-data-jpa/src/main/java/com/baeldung/persistence/model/Bar.java
new file mode 100644
index 0000000000..daa590e43c
--- /dev/null
+++ b/spring-data-jpa/src/main/java/com/baeldung/persistence/model/Bar.java
@@ -0,0 +1,229 @@
+package com.baeldung.persistence.model;
+
+import com.google.common.collect.Sets;
+import org.baeldung.persistence.model.Foo;
+import org.hibernate.annotations.OrderBy;
+import org.hibernate.envers.Audited;
+import org.jboss.logging.Logger;
+import org.springframework.data.annotation.CreatedBy;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedBy;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Set;
+
+@Entity
+@NamedQuery(name = "Bar.findAll", query = "SELECT b FROM Bar b")
+@Audited
+@EntityListeners(AuditingEntityListener.class)
+public class Bar implements Serializable {
+
+ private static Logger logger = Logger.getLogger(Bar.class);
+
+ public enum OPERATION {
+ INSERT, UPDATE, DELETE;
+ private String value;
+
+ OPERATION() {
+ value = toString();
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public static OPERATION parse(final String value) {
+ OPERATION operation = null;
+ for (final OPERATION op : OPERATION.values()) {
+ if (op.getValue().equals(value)) {
+ operation = op;
+ break;
+ }
+ }
+ return operation;
+ }
+ };
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ @Column(name = "id")
+ private int id;
+
+ @Column(name = "name")
+ private String name;
+
+ @OneToMany(mappedBy = "bar", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
+ @OrderBy(clause = "NAME DESC")
+ // @NotAudited
+ private Set fooSet = Sets.newHashSet();
+
+ @Column(name = "operation")
+ private String operation;
+
+ @Column(name = "timestamp")
+ private long timestamp;
+
+ @Column(name = "created_date", updatable = false, nullable = false)
+ @CreatedDate
+ private long createdDate;
+
+ @Column(name = "modified_date")
+ @LastModifiedDate
+ private long modifiedDate;
+
+ @Column(name = "created_by")
+ @CreatedBy
+ private String createdBy;
+
+ @Column(name = "modified_by")
+ @LastModifiedBy
+ private String modifiedBy;
+
+ public Bar() {
+ super();
+ }
+
+ public Bar(final String name) {
+ super();
+
+ this.name = name;
+ }
+
+ // API
+
+ public Set getFooSet() {
+ return fooSet;
+ }
+
+ public void setFooSet(final Set fooSet) {
+ this.fooSet = fooSet;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(final int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ public OPERATION getOperation() {
+ return OPERATION.parse(operation);
+ }
+
+ public void setOperation(final OPERATION operation) {
+ this.operation = operation.getValue();
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(final long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public long getCreatedDate() {
+ return createdDate;
+ }
+
+ public void setCreatedDate(final long createdDate) {
+ this.createdDate = createdDate;
+ }
+
+ public long getModifiedDate() {
+ return modifiedDate;
+ }
+
+ public void setModifiedDate(final long modifiedDate) {
+ this.modifiedDate = modifiedDate;
+ }
+
+ public String getCreatedBy() {
+ return createdBy;
+ }
+
+ public void setCreatedBy(final String createdBy) {
+ this.createdBy = createdBy;
+ }
+
+ public String getModifiedBy() {
+ return modifiedBy;
+ }
+
+ public void setModifiedBy(final String modifiedBy) {
+ this.modifiedBy = modifiedBy;
+ }
+
+ public void setOperation(final String operation) {
+ this.operation = operation;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ final Bar other = (Bar) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("Bar [name=").append(name).append("]");
+ return builder.toString();
+ }
+
+ @PrePersist
+ public void onPrePersist() {
+ logger.info("@PrePersist");
+ audit(OPERATION.INSERT);
+ }
+
+ @PreUpdate
+ public void onPreUpdate() {
+ logger.info("@PreUpdate");
+ audit(OPERATION.UPDATE);
+ }
+
+ @PreRemove
+ public void onPreRemove() {
+ logger.info("@PreRemove");
+ audit(OPERATION.DELETE);
+ }
+
+ private void audit(final OPERATION operation) {
+ setOperation(operation);
+ setTimestamp((new Date()).getTime());
+ }
+
+}
diff --git a/spring-data-jpa/src/main/java/com/baeldung/persistence/model/Foo.java b/spring-data-jpa/src/main/java/com/baeldung/persistence/model/Foo.java
new file mode 100644
index 0000000000..2e6c80de22
--- /dev/null
+++ b/spring-data-jpa/src/main/java/com/baeldung/persistence/model/Foo.java
@@ -0,0 +1,94 @@
+package com.baeldung.persistence.model;
+
+import org.hibernate.envers.Audited;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+@NamedNativeQueries({ @NamedNativeQuery(name = "callGetAllFoos", query = "CALL GetAllFoos()", resultClass = Foo.class), @NamedNativeQuery(name = "callGetFoosByName", query = "CALL GetFoosByName(:fooName)", resultClass = Foo.class) })
+@Entity
+@Audited
+// @Proxy(lazy = false)
+public class Foo implements Serializable {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ @Column(name = "id")
+ private long id;
+
+ @Column(name = "name")
+ private String name;
+
+ @ManyToOne(targetEntity = Bar.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
+ @JoinColumn(name = "BAR_ID")
+ private Bar bar = new Bar();
+
+ public Foo() {
+ super();
+ }
+
+ public Foo(final String name) {
+ super();
+ this.name = name;
+ }
+
+ //
+
+ public Bar getBar() {
+ return bar;
+ }
+
+ public void setBar(final Bar bar) {
+ this.bar = bar;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(final long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ //
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ final Foo other = (Foo) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("Foo [name=").append(name).append("]");
+ return builder.toString();
+ }
+}
diff --git a/spring-data-jpa/src/main/java/com/baeldung/persistence/service/IBarService.java b/spring-data-jpa/src/main/java/com/baeldung/persistence/service/IBarService.java
new file mode 100644
index 0000000000..21185b5990
--- /dev/null
+++ b/spring-data-jpa/src/main/java/com/baeldung/persistence/service/IBarService.java
@@ -0,0 +1,8 @@
+package com.baeldung.persistence.service;
+
+import com.baeldung.persistence.dao.common.IOperations;
+import com.baeldung.persistence.model.Bar;
+
+public interface IBarService extends IOperations {
+ //
+}
diff --git a/spring-data-jpa/src/main/java/com/baeldung/persistence/service/common/AbstractSpringDataJpaService.java b/spring-data-jpa/src/main/java/com/baeldung/persistence/service/common/AbstractSpringDataJpaService.java
new file mode 100644
index 0000000000..b5c3120ff5
--- /dev/null
+++ b/spring-data-jpa/src/main/java/com/baeldung/persistence/service/common/AbstractSpringDataJpaService.java
@@ -0,0 +1,45 @@
+package com.baeldung.persistence.service.common;
+
+import com.baeldung.persistence.dao.common.IOperations;
+import com.google.common.collect.Lists;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Transactional(value = "jpaTransactionManager")
+public abstract class AbstractSpringDataJpaService implements IOperations {
+
+ @Override
+ public T findOne(final long id) {
+ return getDao().findOne(Long.valueOf(id));
+ }
+
+ @Override
+ public List findAll() {
+ return Lists.newArrayList(getDao().findAll());
+ }
+
+ @Override
+ public void create(final T entity) {
+ getDao().save(entity);
+ }
+
+ @Override
+ public T update(final T entity) {
+ return getDao().save(entity);
+ }
+
+ @Override
+ public void delete(final T entity) {
+ getDao().delete(entity);
+ }
+
+ @Override
+ public void deleteById(final long entityId) {
+ getDao().delete(Long.valueOf(entityId));
+ }
+
+ protected abstract CrudRepository getDao();
+}
diff --git a/spring-data-jpa/src/main/java/com/baeldung/persistence/service/impl/BarSpringDataJpaService.java b/spring-data-jpa/src/main/java/com/baeldung/persistence/service/impl/BarSpringDataJpaService.java
new file mode 100644
index 0000000000..c893473e04
--- /dev/null
+++ b/spring-data-jpa/src/main/java/com/baeldung/persistence/service/impl/BarSpringDataJpaService.java
@@ -0,0 +1,26 @@
+package com.baeldung.persistence.service.impl;
+
+import com.baeldung.persistence.dao.IBarCrudRepository;
+import com.baeldung.persistence.model.Bar;
+import com.baeldung.persistence.service.IBarService;
+import com.baeldung.persistence.service.common.AbstractSpringDataJpaService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.repository.CrudRepository;
+
+import java.io.Serializable;
+
+public class BarSpringDataJpaService extends AbstractSpringDataJpaService implements IBarService {
+
+ @Autowired
+ private IBarCrudRepository dao;
+
+ public BarSpringDataJpaService() {
+ super();
+ }
+
+ @Override
+ protected CrudRepository getDao() {
+ return dao;
+ }
+
+}
diff --git a/spring-data-jpa/src/main/java/com/baeldung/spring/PersistenceConfig.java b/spring-data-jpa/src/main/java/com/baeldung/spring/PersistenceConfig.java
new file mode 100644
index 0000000000..75583b3eb9
--- /dev/null
+++ b/spring-data-jpa/src/main/java/com/baeldung/spring/PersistenceConfig.java
@@ -0,0 +1,169 @@
+package com.baeldung.spring;
+
+import com.baeldung.persistence.dao.IBarAuditableDao;
+import com.baeldung.persistence.dao.IBarDao;
+import com.baeldung.persistence.dao.IFooAuditableDao;
+import com.baeldung.persistence.dao.IFooDao;
+import com.baeldung.persistence.dao.impl.*;
+import com.baeldung.persistence.service.IBarAuditableService;
+import com.baeldung.persistence.service.IBarService;
+import com.baeldung.persistence.service.IFooAuditableService;
+import com.baeldung.persistence.service.IFooService;
+import com.baeldung.persistence.service.impl.*;
+import com.google.common.base.Preconditions;
+import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;
+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.EnableJpaAuditing;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.orm.hibernate4.HibernateTransactionManager;
+import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
+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.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.sql.DataSource;
+import java.util.Properties;
+
+@Configuration
+@EnableTransactionManagement
+@EnableJpaRepositories(basePackages = { "com.baeldung.persistence" }, transactionManagerRef = "jpaTransactionManager")
+@EnableJpaAuditing
+@PropertySource({ "classpath:persistence-mysql.properties" })
+@ComponentScan({ "com.baeldung.persistence" })
+public class PersistenceConfig {
+
+ @Autowired
+ private Environment env;
+
+ public PersistenceConfig() {
+ super();
+ }
+
+ @Bean
+ public LocalSessionFactoryBean sessionFactory() {
+ final LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
+ sessionFactory.setDataSource(restDataSource());
+ sessionFactory.setPackagesToScan(new String[] { "com.baeldung.persistence.model" });
+ sessionFactory.setHibernateProperties(hibernateProperties());
+
+ return sessionFactory;
+ }
+
+ @Bean
+ public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
+ final LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
+ emf.setDataSource(restDataSource());
+ emf.setPackagesToScan(new String[] { "com.baeldung.persistence.model" });
+
+ final JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
+ emf.setJpaVendorAdapter(vendorAdapter);
+ emf.setJpaProperties(hibernateProperties());
+
+ return emf;
+ }
+
+ @Bean
+ public DataSource restDataSource() {
+ final BasicDataSource dataSource = new BasicDataSource();
+ dataSource.setDriverClassName(Preconditions.checkNotNull(env.getProperty("jdbc.driverClassName")));
+ dataSource.setUrl(Preconditions.checkNotNull(env.getProperty("jdbc.url")));
+ dataSource.setUsername(Preconditions.checkNotNull(env.getProperty("jdbc.user")));
+ dataSource.setPassword(Preconditions.checkNotNull(env.getProperty("jdbc.pass")));
+
+ return dataSource;
+ }
+
+ @Bean
+ public PlatformTransactionManager hibernateTransactionManager() {
+ final HibernateTransactionManager transactionManager = new HibernateTransactionManager();
+ transactionManager.setSessionFactory(sessionFactory().getObject());
+ return transactionManager;
+ }
+
+ @Bean
+ public PlatformTransactionManager jpaTransactionManager() {
+ final JpaTransactionManager transactionManager = new JpaTransactionManager();
+ transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
+ return transactionManager;
+ }
+
+ @Bean
+ public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
+ return new PersistenceExceptionTranslationPostProcessor();
+ }
+
+ @Bean
+ public IBarService barJpaService() {
+ return new BarJpaService();
+ }
+
+ @Bean
+ public IBarService barSpringDataJpaService() {
+ return new BarSpringDataJpaService();
+ }
+
+ @Bean
+ public IFooService fooHibernateService() {
+ return new FooService();
+ }
+
+ @Bean
+ public IBarAuditableService barHibernateAuditableService() {
+ return new BarAuditableService();
+ }
+
+ @Bean
+ public IFooAuditableService fooHibernateAuditableService() {
+ return new FooAuditableService();
+ }
+
+ @Bean
+ public IBarDao barJpaDao() {
+ return new BarJpaDao();
+ }
+
+ @Bean
+ public IBarDao barHibernateDao() {
+ return new BarDao();
+ }
+
+ @Bean
+ public IBarAuditableDao barHibernateAuditableDao() {
+ return new BarAuditableDao();
+ }
+
+ @Bean
+ public IFooDao fooHibernateDao() {
+ return new FooDao();
+ }
+
+ @Bean
+ public IFooAuditableDao fooHibernateAuditableDao() {
+ return new FooAuditableDao();
+ }
+
+ private final Properties hibernateProperties() {
+ 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.show_sql", "true");
+ // hibernateProperties.setProperty("hibernate.format_sql", "true");
+ // hibernateProperties.setProperty("hibernate.globally_quoted_identifiers", "true");
+
+ // Envers properties
+ hibernateProperties.setProperty("org.hibernate.envers.audit_table_suffix", env.getProperty("envers.audit_table_suffix"));
+
+ return hibernateProperties;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-data-jpa/src/test/java/com/baeldung/persistence/audit/SpringDataJPABarAuditIntegrationTest.java b/spring-data-jpa/src/test/java/com/baeldung/persistence/audit/SpringDataJPABarAuditIntegrationTest.java
new file mode 100644
index 0000000000..4105773e3b
--- /dev/null
+++ b/spring-data-jpa/src/test/java/com/baeldung/persistence/audit/SpringDataJPABarAuditIntegrationTest.java
@@ -0,0 +1,72 @@
+package com.baeldung.persistence.audit;
+
+import com.baeldung.persistence.model.Bar;
+import com.baeldung.persistence.service.IBarService;
+import com.baeldung.spring.config.PersistenceTestConfig;
+import org.junit.*;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.support.AnnotationConfigContextLoader;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = { PersistenceTestConfig.class }, loader = AnnotationConfigContextLoader.class)
+public class SpringDataJPABarAuditIntegrationTest {
+
+ private static Logger logger = LoggerFactory.getLogger(SpringDataJPABarAuditIntegrationTest.class);
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ logger.info("setUpBeforeClass()");
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ logger.info("tearDownAfterClass()");
+ }
+
+ @Autowired
+ @Qualifier("barSpringDataJpaService")
+ private IBarService barService;
+
+ @Autowired
+ private EntityManagerFactory entityManagerFactory;
+
+ private EntityManager em;
+
+ @Before
+ public void setUp() throws Exception {
+ logger.info("setUp()");
+ em = entityManagerFactory.createEntityManager();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ logger.info("tearDown()");
+ em.close();
+ }
+
+ @Test
+ @WithMockUser(username = "tutorialuser")
+ public final void whenBarsModified_thenBarsAudited() {
+ Bar bar = new Bar("BAR1");
+ barService.create(bar);
+ assertEquals(bar.getCreatedDate(), bar.getModifiedDate());
+ assertEquals("tutorialuser", bar.getCreatedBy(), bar.getModifiedBy());
+ bar.setName("BAR2");
+ bar = barService.update(bar);
+ assertTrue(bar.getCreatedDate() < bar.getModifiedDate());
+ assertEquals("tutorialuser", bar.getCreatedBy(), bar.getModifiedBy());
+ }
+}
diff --git a/spring-data-jpa/src/test/java/com/baeldung/spring/config/PersistenceTestConfig.java b/spring-data-jpa/src/test/java/com/baeldung/spring/config/PersistenceTestConfig.java
new file mode 100644
index 0000000000..4e3ecd67f5
--- /dev/null
+++ b/spring-data-jpa/src/test/java/com/baeldung/spring/config/PersistenceTestConfig.java
@@ -0,0 +1,169 @@
+package com.baeldung.spring.config;
+
+import com.baeldung.persistence.dao.IBarAuditableDao;
+import com.baeldung.persistence.dao.IBarDao;
+import com.baeldung.persistence.dao.IFooAuditableDao;
+import com.baeldung.persistence.dao.IFooDao;
+import com.baeldung.persistence.dao.impl.*;
+import com.baeldung.persistence.service.IBarAuditableService;
+import com.baeldung.persistence.service.IBarService;
+import com.baeldung.persistence.service.IFooAuditableService;
+import com.baeldung.persistence.service.IFooService;
+import com.baeldung.persistence.service.impl.*;
+import com.google.common.base.Preconditions;
+import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;
+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.EnableJpaAuditing;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.orm.hibernate4.HibernateTransactionManager;
+import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
+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.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.sql.DataSource;
+import java.util.Properties;
+
+@Configuration
+@EnableTransactionManagement
+@EnableJpaRepositories(basePackages = { "com.baeldung.persistence" }, transactionManagerRef = "jpaTransactionManager")
+@EnableJpaAuditing
+@PropertySource({ "classpath:persistence-h2.properties" })
+@ComponentScan({ "com.baeldung.persistence" })
+public class PersistenceTestConfig {
+
+ @Autowired
+ private Environment env;
+
+ public PersistenceTestConfig() {
+ super();
+ }
+
+ @Bean
+ public LocalSessionFactoryBean sessionFactory() {
+ final LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
+ sessionFactory.setDataSource(restDataSource());
+ sessionFactory.setPackagesToScan(new String[] { "com.baeldung.persistence.model" });
+ sessionFactory.setHibernateProperties(hibernateProperties());
+
+ return sessionFactory;
+ }
+
+ @Bean
+ public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
+ final LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
+ emf.setDataSource(restDataSource());
+ emf.setPackagesToScan(new String[] { "com.baeldung.persistence.model" });
+
+ final JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
+ emf.setJpaVendorAdapter(vendorAdapter);
+ emf.setJpaProperties(hibernateProperties());
+
+ return emf;
+ }
+
+ @Bean
+ public DataSource restDataSource() {
+ final BasicDataSource dataSource = new BasicDataSource();
+ dataSource.setDriverClassName(Preconditions.checkNotNull(env.getProperty("jdbc.driverClassName")));
+ dataSource.setUrl(Preconditions.checkNotNull(env.getProperty("jdbc.url")));
+ dataSource.setUsername(Preconditions.checkNotNull(env.getProperty("jdbc.user")));
+ dataSource.setPassword(Preconditions.checkNotNull(env.getProperty("jdbc.pass")));
+
+ return dataSource;
+ }
+
+ @Bean
+ public PlatformTransactionManager hibernateTransactionManager() {
+ final HibernateTransactionManager transactionManager = new HibernateTransactionManager();
+ transactionManager.setSessionFactory(sessionFactory().getObject());
+ return transactionManager;
+ }
+
+ @Bean
+ public PlatformTransactionManager jpaTransactionManager() {
+ final JpaTransactionManager transactionManager = new JpaTransactionManager();
+ transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
+ return transactionManager;
+ }
+
+ @Bean
+ public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
+ return new PersistenceExceptionTranslationPostProcessor();
+ }
+
+ @Bean
+ public IBarService barJpaService() {
+ return new BarJpaService();
+ }
+
+ @Bean
+ public IBarService barSpringDataJpaService() {
+ return new BarSpringDataJpaService();
+ }
+
+ @Bean
+ public IFooService fooHibernateService() {
+ return new FooService();
+ }
+
+ @Bean
+ public IBarAuditableService barHibernateAuditableService() {
+ return new BarAuditableService();
+ }
+
+ @Bean
+ public IFooAuditableService fooHibernateAuditableService() {
+ return new FooAuditableService();
+ }
+
+ @Bean
+ public IBarDao barJpaDao() {
+ return new BarJpaDao();
+ }
+
+ @Bean
+ public IBarDao barHibernateDao() {
+ return new BarDao();
+ }
+
+ @Bean
+ public IBarAuditableDao barHibernateAuditableDao() {
+ return new BarAuditableDao();
+ }
+
+ @Bean
+ public IFooDao fooHibernateDao() {
+ return new FooDao();
+ }
+
+ @Bean
+ public IFooAuditableDao fooHibernateAuditableDao() {
+ return new FooAuditableDao();
+ }
+
+ private final Properties hibernateProperties() {
+ 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.show_sql", "true");
+ // hibernateProperties.setProperty("hibernate.format_sql", "true");
+ // hibernateProperties.setProperty("hibernate.globally_quoted_identifiers", "true");
+
+ // Envers properties
+ hibernateProperties.setProperty("org.hibernate.envers.audit_table_suffix", env.getProperty("envers.audit_table_suffix"));
+
+ return hibernateProperties;
+ }
+
+}
\ No newline at end of file