Object-relational Mapping Using JPA Hibernate and Spring Data JPA. Persistence with Spring Data JPA

Object-relational Mapping Using JPA Hibernate and Spring Data JPA. Persistence with Spring Data JPA

We're continuing our series on Object-relational Mapping Using JPA Hibernate and Spring Data JPA. Happy reading.

Persistence with Spring Data JPA

In this section, we’ll write a Spring Data JPA application, which saves an item in the database and then retrieves it. We add the Spring dependencies on the side of the Apache Maven configuration.

Listing 9 The Maven dependencies on Spring

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>

The spring-data-jpa module provides the repository support for JPA and includes transitive dependencies on other modules we’ll need, such as spring-core and spring-context.

We also need the spring-test dependency, to run the tests with the help of the Spring extension.

The standard configuration file for Spring Data JPA is a Java class to create and setup the beans needed by Spring Data. The configuration can be done using either an XML file or Java code and we chose this second alternative. We create the following configuration file for the persistence application:

Listing 10 The SpringDataConfiguration class

@EnableJpaRepositories

                ("cscs23.orm.repositories")     #1
public class SpringDataConfiguration {
    @Bean
    public DataSource dataSource() {             #2
      DriverManagerDataSource dataSource =

         new DriverManagerDataSource();          #2
      dataSource.setDriverClassName

            ("com.mysql.cj.jdbc.Driver");       #3
      dataSource.setUrl

           ("jdbc:mysql://localhost:3306/CSCS"); #4
      dataSource.setUsername("root");            #5
      dataSource.setPassword("");               #6
      return dataSource;                        #2
    }

    @Bean
    public JpaTransactionManager transactionManager

           (EntityManagerFactory emf) {          
      return new JpaTransactionManager(emf);   #7
    }

    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {                           
      HibernateJpaVendorAdapter jpaVendorAdapter =

         new HibernateJpaVendorAdapter();        #8
      jpaVendorAdapter.

        setDatabase(Database.MYSQL);             #9
      return jpaVendorAdapter;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean

     entityManagerFactory() {
      LocalContainerEntityManagerFactoryBean                            

    localContainerEntityManagerFactoryBean =                         
       new

    LocalContainerEntityManagerFactoryBean();#10
       localContainerEntityManagerFactoryBean.

       setDataSource(dataSource());          #11
       localContainerEntityManagerFactoryBean.                           

       setJpaVendorAdapter(

        jpaVendorAdapter()); #12
        localContainerEntityManagerFactoryBean.                           

        setPackagesToScan(

          "cscs23.orm"); #13
       return

      localContainerEntityManagerFactoryBean;                    
    }
}


  • The @EnableJpaRepositories annotation will scan the package of the annotated configuration class for Spring Data repositories #1.
  • We create a data source bean #2 and we indicate the JDBC properties - driver #3, URL of the database #4, the username #5, and password #6 to access it.
  • We create a transaction manager bean based on an entity manager factory #7. Every interaction with the database should occur within transaction boundaries and Spring Data needs a transaction manager bean.
  • We create a JPA vendor adapter bean that is needed by JPA to interact with Hibernate #8. We configure this vendor adapter to access a MySQL database #9.
  • We create an object belonging to the class LocalContainerEntityManagerFactoryBean– this is a factory bean that produces an EntityManagerFactory following the JPA standard container bootstrap contract #10.
  • We set the data source #11, the vendor adapter #12, and the packages to scan for entity classes #13. As the Item entity is located in cscs23.orm, we set this package to be scanned.
  • Spring Data JPA provides support for JPA-based data access layers reducing the boilerplate code and creating implementations for the repository interfaces. We need only to define our own repository interface, to extend one of the Spring
  • Data interfaces.

Object-relational Mapping Using JPA Hibernate and Spring Data JPA Persistence with Spring Data JPA..jpg


Listing 11 The ItemRepository interface

public interface ItemRepository extends CrudRepository<Item, Long> {

}


The ItemRepository interface extends CrudRepository
<Item, Long>. This means that it is a repository of Item entities, having a Long identifier. Remember, the Item class has an id field annotated as @Id of type Long. We can directly call methods as save, findAll or findById, inherited from CrudRepository and we can use them without additional information, to execute operations against a database.

For additional operations, one may use the JpaRepository interface that in turn extends the CrudRepository interface. This one comes with more additional methods, including here methods that allow pagination and sorting or batch operations.

Spring Data JPA uses the proxy pattern to provide a mechanism to interact with the database. A proxy is a substitute that will have the purpose to replace another object. Spring Data JPA will create a proxy class implementing the ItemRepository interface and will create its methods (figure 1). This proxy class will define all the methods inherited from CrudRepository, including here save, findAll or findById. The programmer will no longer waste time writing the logic of these methods – their implementation will come from the side of Spring Data JPA. This acts in the spirit of frameworks and of Spring in particular – invert the control, the working flow is already included, provide hook points that the programmer should fill out by focusing on the business logic.

Let’s save an Item to the database using Spring Data JPA.

Listing 12 The ItemSpringDataJPATest class


@ExtendWith(SpringExtension.class)               #1
@ContextConfiguration(classes = {

SpringDataConfiguration.class})                #2
public class ItemSpringDataJPATest {

@Autowired                                                                
private ItemRepository itemRepository;         #3

@Test
void saveRetrieveItem() {
    Item item = new Item();                      #4
    item.setInfo(

      "Item from Spring Data JPA");              #4
    itemRepository.save(item);                  #5
}
}

The Spring Data JPA Proxy class implements the ItemRepository interface.png


Fig. 1 The Spring Data JPA Proxy class implements the ItemRepository interface

  • We extend the test using SpringExtension #1. This extension is used to integrate the Spring test context with the JUnit 5 Jupiter test.
  • The Spring test context is configured using the beans defined in the previously presented SpringDataConfiguration class #2.
  • An ItemRepository bean is injected by Spring through auto-wiring #3. This is possible as the cscs23.orm.repositories package where ItemRepository is located was used as the argument of the @EnableJpaRepositories annotation in listing 10.
  • If we would like to check and call itemRepository.getClass(), we’ll see that it is something like com.sun.proxy.$Proxy41 – a proxy generated by Spring Data JPA, as explained in figure 1.
  • Create a new instance of the Item class, and set its info property #4.
  • Persist the item object #5. The save method is inherited from the CrudRepository interface and its body will be generated by Spring Data JPA when the proxy class is created. It will simply save an Item entity to the database.
  • The advantages of working with JPA or Hibernate native are still here: no SQL code or JDBC needed; no CRUD operations inside the Java code, but only classes, objects, and methods; the portability to each SQL dialect is addressed by the ORM.
  • Additionally, the Spring Data JPA test is considerably shorter than the ones using JPA or Hibernate native. This is because the boilerplate code has been removed – no more explicit object creation or explicit control of the transactions. The repository object is injected and it provides the generated methods of the proxy class. The burden is heavier now on the configuration side – but this should be done only once per application.
Interested in learning how to program with Java or in upgrading your Java programming skills? Check out our trainings

Catalin Tudose
Java and Web Technologies Expert
Still have questions?
Connect with us