Close

Spring ORM - Spring-driven JPA declarative transaction

[Updated: Jan 2, 2018, Created: Jan 1, 2018]

This examples shows how to use declarative transactions in JPA based Spring application. We will use Spring's JpaTransactionManager which is an implementation of PlatformTransactionManager (check out JDBC based Spring Declarative Transaction example ).

Example

A Generic Dao Interface

public interface Dao<T> {
  void save(T t);

  T load(long id);

  void delete(long id);

  void update(T t);

  List<T> loadAll();
}

A JPA Entity

@Entity
public class Person {
  @Id
  @GeneratedValue
  private long id;
  private String firstName;
  private String lastName;
  private String address;
    .............
}

The DAO implementation

package com.logicbig.example;

import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;

@Repository
public class PersonDaoJpaImpl implements Dao<Person> {

  @PersistenceContext
  private EntityManager em;

  @Override
  public void save(Person person) {
      em.persist(person);
  }

  @Override
  public Person load(long id) {
      Person person = em.find(Person.class, id);
      return person;
  }

  @Override
  public void delete(long id) {
      Person employee = em.find(Person.class, id);
      em.remove(employee);
  }

  @Override
  public void update(Person person) {
      em.merge(person);
  }

  @Override
  public List<Person> loadAll() {
      List<Person> persons = em.createQuery("Select t from Person t")
                               .getResultList();
      return persons;
  }
}

Note that, we used JPA's @PersistenceContext annotation on EntityManager. That way we don't have to create a new EntityManager through EntityManagerFactory each time (as compared to the last example). In above code, we are requesting a transactional EntityManager (also called "shared EntityManager" because it is a shared, thread-safe proxy for the actual transactional EntityManager) to be injected instead of the factory.

The Service Layer

package com.logicbig.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.List;

@Service
public class PersonService {
  @Autowired
  private Dao<Person> dao;

  @Transactional
  public void savePerson(Person person) {
      try {
          dao.save(person);
      } catch (DataAccessException e) {
          e.printStackTrace();
      }
  }

  public List<Person> getAllPersons() {
      try {
          return dao.loadAll();
      } catch (DataAccessException e) {
          e.printStackTrace();
      }
      return null;
  }

  public Person getPersonById(long id) {
      try {
          return dao.load(id);
      } catch (DataAccessException e) {
          e.printStackTrace();
      }
      return null;
  }

  @Transactional
  public void deletePerson(long id) {
      try {
          dao.delete(id);
      } catch (DataAccessException e) {
          e.printStackTrace();
      }
  }

  @Transactional
  public void updatePerson(Person person) {
      try {
          dao.update(person);
      } catch (DataAccessException e) {
          e.printStackTrace();
      }
  }
}

Note that we used javax.transaction.Transactional. Instead of that, we could have used org.springframework.transaction.annotation.Transactional without any other changes.

The Example Client

@Component
public class ExampleClientBean {

  @Autowired
  PersonService personService;

  public void run() {
      System.out.println("'Persisting persons'");
      Person person = Person.create("Dana", "Whitley", "464 Gorsuch Drive");
      personService.savePerson(person);

      person = Person.create("Robin", "Cash", "64 Zella Park");
      personService.savePerson(person);

      System.out.println("'loading all persons'");
      List<Person> allPersons = personService.getAllPersons();
      System.out.println("Persons loaded: " + allPersons);

      person = personService.getPersonById(2);
      System.out.println("Person loaded by id 2 : " + person);

      System.out.println("'updating person address with id 2'");
      person.setAddress("111 Yellow Hill");
      personService.updatePerson(person);

      System.out.println("'Deleting person by id 1'");
      personService.deletePerson(1);

      System.out.println("'loading all persons'");
      allPersons = personService.getAllPersons();
      System.out.println("Persons loaded: " + allPersons);
  }
}

persistence.xml

src/main/resources/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             version="2.1"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

    <persistence-unit name="example-unit" transaction-type="RESOURCE_LOCAL">
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="javax.persistence.schema-generation.database.action" value="create"/>
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"/>
        </properties>
    </persistence-unit>

</persistence>

Java Config and main class

package com.logicbig.example;

import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;

@Configuration
@ComponentScan
@EnableTransactionManagement
public class AppConfig {

  @Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
      LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
      factory.setPersistenceProviderClass(HibernatePersistenceProvider.class);
      factory.setPersistenceUnitName("example-unit");
      return factory;
  }

  @Bean
  public PlatformTransactionManager transactionManager() {
      JpaTransactionManager transactionManager = new JpaTransactionManager();
      transactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject());
      return transactionManager;
  }

  public static void main(String[] args) {
      AnnotationConfigApplicationContext context =
              new AnnotationConfigApplicationContext(AppConfig.class);
      context.getBean(ExampleClientBean.class).run();
      EntityManagerFactory emf = context.getBean(EntityManagerFactory.class);
      emf.close();
  }
}

Output

'Persisting persons'
'loading all persons'
Persons loaded: [Person{id=1, firstName='Dana', lastName='Whitley', address='464 Gorsuch Drive'}, Person{id=2, firstName='Robin', lastName='Cash', address='64 Zella Park'}]
Person loaded by id 2 : Person{id=2, firstName='Robin', lastName='Cash', address='64 Zella Park'}
'updating person address with id 2'
'Deleting person by id 1'
'loading all persons'
Persons loaded: [Person{id=2, firstName='Robin', lastName='Cash', address='111 Yellow Hill'}]

Example Project

Dependencies and Technologies Used:

  • spring-context 5.0.2.RELEASE: Spring Context.
  • spring-orm 5.0.2.RELEASE: Spring Object/Relational Mapping.
  • hibernate-core 5.2.12.Final: The core O/RM functionality as provided by Hibernate.
    Implements javax.persistence:javax.persistence-api version 2.1
  • h2 1.4.196: H2 Database Engine.
  • JDK 1.8
  • Maven 3.3.9

Spring JPA Transactions Example Select All Download
  • spring-jpa-transaction-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • PersonService.java
          • resources
            • META-INF

    See Also