0% found this document useful (0 votes)
93 views23 pages

Unit 3

The document discusses Java Database Connectivity (JDBC) and Spring Data JPA. JDBC is an API that allows Java applications to connect to databases. Spring Data JPA provides an abstraction layer over JPA to simplify database access from Spring applications. The document also describes the architecture and configuration of JPA and Spring Data JPA.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
93 views23 pages

Unit 3

The document discusses Java Database Connectivity (JDBC) and Spring Data JPA. JDBC is an API that allows Java applications to connect to databases. Spring Data JPA provides an abstraction layer over JPA to simplify database access from Spring applications. The document also describes the architecture and configuration of JPA and Spring Data JPA.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 23

UNIT-3

Spring Data JPA with Boot


What is Java Database Connectivity?

Java Database Connectivity (or JDBC for short) is a Java API designed to connect and manage relational
databases in applications programmed with Java.

The JDBC API uses JDBC drivers to interact with a database. There are different types of JDBC drivers:

 JDBC-ODBC bridge driver: Use JDBC-ODBC bridge driver for testing JDBC applications against
an ODBC (Open database connectivity) data source.
 Native driver: Native driver requires a native database API.
 Network driver protocol: Network driver protocol is common; it’s created for a particular vendor’s
database.

Using JDBC API


When using JDBC to connect and manage a database, JDBC works in the following steps:

 Register the JDBC driver class


 Create a connection to the database
 Create a statement object to perform an SQL query
 Execute the statement object and return a query ResultSet
 Process the ResultSet
 Close ResultSet and statement
 Close the connection

Advantages of JDBC API


1. No installation: JDBC comes with every JDK, and you don’t need any additional libraries installed to
use it.
2. Database Agnostic: JDBC can read any database as long as the connection is with the proper drivers.
3. Connection pooling: JDBC connection pooling saves time when opening and closing database
connections for every user.
4. Easy database access: JDBC low-level database access makes complex SQL queries efficient.

Limitations of JDBC API


 With JDBC, each type of database requires a specific driver.
 To prevent SQL injection attacks, JDBC does not allow more than one value to a placeholder.
 The significant programming overhead makes use of JDBC in large-scale projects hard.
 Java Developers using JDBC must know how to write good SQL queries.

Spring Data JPA:

Spring Data JPA API provides JpaTemplate class to integrate spring application with JPA.

JPA (Java Persistent API) is the sun specification for persisting objects in the enterprise
application. It is currently used as the replacement for complex entity beans.
The implementation of JPA specification is provided by many vendors such as:

o Hibernate
o Toplink
o iBatis
o OpenJPA etc.

Advantage of Spring JpaTemplate

You don't need to write the before and after code for persisting, updating, deleting or searching
object such as creating Persistence instance, creating EntityManagerFactory instance, creating
EntityTransaction instance, creating EntityManager instance, commiting EntityTransaction
instance and closing EntityManager.

So, it saves a lot of code.

Spring Boot JPA

What is JPA?

Spring Boot JPA is a Java specification for managing relational data in Java applications. It
allows us to access and persist data between Java object/ class and relational database. JPA
follows Object-Relation Mapping (ORM). It is a set of interfaces. It also provides a
runtime EntityManager API for processing queries and transactions on the objects against the
database. It uses a platform-independent object-oriented query language JPQL (Java Persistent
Query Language).

In the context of persistence, it covers three areas:

o The Java Persistence API


o Object-Relational metadata
o The API itself, defined in the persistence package

JPA is not a framework. It defines a concept that can be implemented by any framework.

Why should we use JPA?

JPA is simpler, cleaner, and less labor-intensive than JDBC, SQL, and hand-written mapping.
JPA is suitable for non-performance oriented complex applications. The main advantage of JPA
over JDBC is that, in JPA, data is represented by objects and classes while in JDBC data is
represented by tables and records. It uses POJO to represent persistent data that simplifies
database programming. There are some other advantages of JPA:
o JPA avoids writing DDL in a database-specific dialect of SQL. Instead of this, it allows
mapping in XML or using Java annotations.
o JPA allows us to avoid writing DML in the database-specific dialect of SQL.
o JPA allows us to save and load Java objects and graphs without any DML language at
all.
o When we need to perform queries JPQL, it allows us to express the queries in terms of
Java entities rather than the (native) SQL table and columns.

JPA Features:

There are following features of JPA:

o It is a powerful repository and custom object-mapping abstraction.


o It supports for cross-store persistence. It means an entity can be partially stored in
MySQL and Neo4j (Graph Database Management System).
o It dynamically generates queries from queries methods name.
o The domain base classes provide basic properties.
o It supports transparent auditing.
o Possibility to integrate custom repository code.
o It is easy to integrate with Spring Framework with the custom namespace.

JPA Architecture:
JPA is a source to store business entities as relational entities. It shows how to define a POJO as
an entity and how to manage entities with relation.

The following figure describes the class-level architecture of JPA that describes the core classes
and interfaces of JPA that is defined in the javax persistence package. The JPA architecture
contains the following units:

o Persistence: It is a class that contains static methods to obtain an EntityManagerFactory


instance.
o EntityManagerFactory: It is a factory class of EntityManager. It creates and manages
multiple instances of EntityManager.
o EntityManager: It is an interface. It controls the persistence operations on objects. It
works for the Query instance.
o Entity: The entities are the persistence objects stores as a record in the database.
o Persistence Unit: It defines a set of all entity classes. In an application, EntityManager
instances manage it. The set of entity classes represents the data contained within a single
data store.
o EntityTransaction: It has a one-to-one relationship with the EntityManager class. For
each EntityManager, operations are maintained by EntityTransaction class.
o Query: It is an interface that is implemented by each JPA vendor to obtain relation
objects that meet the criteria.

Spring data JPA Configuration:

We can configure Spring Data JPA through either:

1) “Annotation-based Configuration” (Java configuration)


2) “Spring Namespace” (XML configuration)

1) Annotation-based Configuration:

The Spring Data JPA repositories support can be activated through both JavaConfig as well as a
custom XML namespace, as shown in the following example:

Example 1. Spring Data JPA repositories using JavaConfig

@Configuration
@EnableJpaRepositories
@EnableTransactionManagement
class ApplicationConfig {

@Bean
public DataSource dataSource() {

EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();


return builder.setType(EmbeddedDatabaseType.HSQL).build();
}

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();


vendorAdapter.setGenerateDdl(true);

LocalContainerEntityManagerFactoryBean factory = new


LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("com.acme.domain");
factory.setDataSource(dataSource());
return factory;
}

@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory
entityManagerFactory)
{
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}

You must create LocalContainerEntityManagerFactoryBean and not EntityManagerFactory


directly, since the former also participates in exception translation mechanisms in addition to
creating EntityManagerFactory.

The preceding configuration class sets up an embedded HSQL database by using the
EmbeddedDatabaseBuilder API of spring-jdbc. Spring Data then sets up an
EntityManagerFactory and uses Hibernate as the sample persistence provider. The last
infrastructure component declared here is the JpaTransactionManager. Finally, the example
activates Spring Data JPA repositories by using the @EnableJpaRepositories annotation, which
essentially carries the same attributes as the XML namespace. If no base package is configured,
it uses the one in which the configuration class resides.

2) Spring Namespace

The JPA module of Spring Data contains a custom namespace that allows defining repository
beans. It also contains certain features and element attributes that are special to JPA. Generally,
the JPA repositories can be set up by using the repositories element, as shown in the following
example:

Example 2. Setting up JPA repositories by using the namespace

<?xml version="1.0" encoding="UTF-8"?>


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa
https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<jpa:repositories base-package="com.acme.repositories" />
</beans>

Which is better, JavaConfig or XML? XML is how Spring was configured long ago. In today’s
era of fast-growing Java, record types, annotations, and more, new projects typically use as
much pure Java as possible. While there is no immediate plan to remove XML support, some of
the newest features may not be available through XML.

Using the repositories element it activates persistence exception translation for all beans
annotated with @Repository, to let exceptions being thrown by the JPA persistence providers be
converted into Spring’s DataAccessException hierarchy.
Custom Namespace Attributes

Beyond the default attributes of the repositories element, the JPA namespace offers additional
attributes to let you gain more detailed control over the setup of the repositories:

Custom JPA-specific attributes of the repositories element:

1) entity-manager-factory-ref: Explicitly wire the EntityManagerFactory to be used with the


repositories being detected by the repositories element. Usually used if multiple
EntityManagerFactory beans are used within the application. If not configured, Spring Data
automatically looks up the EntityManagerFactory bean with the name entityManagerFactory in
the ApplicationContext.

2) transaction-manager-ref: Explicitly wire the PlatformTransactionManager to be used with


the repositories being detected by the repositories element. Usually only necessary if multiple
transaction managers or EntityManagerFactory beans have been configured. Default to a single
defined PlatformTransactionManager inside the current ApplicationContext.

Spring Data JPA requires a PlatformTransactionManager bean named transactionManager to be


present if no explicit transaction-manager-ref is defined.

Bootstrap Mode

By default, Spring Data JPA repositories are default Spring beans. They are singleton scoped
and eagerly initialized. During startup, they already interact with the JPA EntityManager for
verification and metadata analysis purposes. Spring Framework supports the initialization of the
JPA EntityManagerFactory in a background thread because that process usually takes up a
significant amount of startup time in a Spring application. To make use of that background
initialization effectively, we need to make sure that JPA repositories are initialized as late as
possible.

As of Spring Data JPA 2.1 you can now configure a BootstrapMode (either via the
@EnableJpaRepositories annotation or the XML namespace) that takes the following values:

DEFAULT (default) — Repositories are instantiated eagerly unless explicitly annotated with
@Lazy. The lazification only has effect if no client bean needs an instance of the repository as
that will require the initialization of the repository bean.

LAZY — Implicitly declares all repository beans lazy and also causes lazy initialization proxies
to be created to be injected into client beans. That means, that repositories will not get
instantiated if the client bean is simply storing the instance in a field and not making use of the
repository during initialization. Repository instances will be initialized and verified upon first
interaction with the repository.

DEFERRED — Fundamentally the same mode of operation as LAZY, but triggering repository
initialization in response to an ContextRefreshedEvent so that repositories are verified before the
application has completely started.

Pagination and Sorting using Spring Data JPA:

Pagination is often helpful when we have a large dataset and we want to present it to the user in
smaller chunks.
Also, we often need to sort that data by some criteria while paging.

Initial Setup

First, let’s say we have a Product entity as our domain class:


@Entity
public class Product {

@Id
private long id;
private String name;
private double price;

// constructors, getters and setters

Each of our Product instances has a unique identifier: id, its name and its price associated with
it.

Creating a Repository

To access our Products, we’ll need a ProductRepository:


public interface ProductRepository extends PagingAndSortingRepository<Product,
Integer> {

List<Product> findAllByPrice(double price, Pageable pageable);


}Copy
By having it extend PagingAndSortingRepository, we get findAll(Pageable
pageable) and findAll(Sort sort) methods for paging and sorting.
Conversely, we could have chosen to extend JpaRepository instead, as it
extends PagingAndSortingRepository too.
Once we extend PagingAndSortingRepository, we can add our own methods that
take Pageable and Sort as parameters, like we did here with findAllByPrice.
Let’s take a look at how to paginate our Products using our new method.
3.Pagination

Once we have our repository extending from PagingAndSortingRepository, we just need to:

1. Create or obtain a PageRequest object, which is an implementation of


the Pageable interface
2. Pass the PageRequest object as an argument to the repository method we intend to use

We can create a PageRequest object by passing in the requested page number and the page size.
Here the page count starts at zero:
Pageable firstPageWithTwoElements = PageRequest.of(0, 2);

Pageable secondPageWithFiveElements = PageRequest.of(1, 5);Copy


In Spring MVC, we can also choose to obtain the Pageable instance in our controller
using Spring Data Web Support.
Once we have our PageRequest object, we can pass it in while invoking our repository’s
method:
Page<Product> allProducts = productRepository.findAll(firstPageWithTwoElements);

List<Product> allTenDollarProducts =
productRepository.findAllByPrice(10, secondPageWithFiveElements);Copy
The findAll(Pageable pageable) method by default returns a Page<T> object.
However, we can choose to return either a Page<T>, a Slice<T>, or a List<T> from any of
our custom methods returning paginated data.
A Page<T> instance, in addition to having the list of Products, also knows about the total
number of available pages. It triggers an additional count query to achieve it. To avoid such
an overhead cost, we can instead return a Slice<T> or a List<T>.
A Slice only knows whether the next slice is available or not.

4. Pagination and Sorting

Similarly, to just have our query results sorted, we can simply pass an instance of Sort to the
method:
Page<Product> allProductsSortedByName = productRepository.findAll(Sort.by("name"));Copy
However, what if we want to both sort and page our data?
We can do that by passing the sorting details into our PageRequest object itself:
Pageable sortedByName =
PageRequest.of(0, 3, Sort.by("name"));

Pageable sortedByPriceDesc =
PageRequest.of(0, 3, Sort.by("price").descending());

Pageable sortedByPriceDescNameAsc =
PageRequest.of(0, 5, Sort.by("price").descending().and(Sort.by("name")));Copy
Based on our sorting requirements, we can specify the sort fields and the sort direction while
creating our PageRequest instance.
As usual, we can then pass this Pageable type instance to the repository’s method.

Spring Data JPA: Query Methods


One way to find information from the database is to use query methods. However, before we can
create custom database queries with query methods, we have to find the answers to the following
questions:

 What are query methods?


 What kind of return values can we use?
 How can we pass parameters to our query methods?
This blog post answers to all of these questions. Let’s start by finding out the answer to the first
question.

Introduction to Query Methods

Query methods are methods that find information from the database and are declared on the
repository interface. For example, if we want to create a database query that finds
the Todo object that has a specific id, we can create the query method by adding
the findById() method to the TodoRepository interface. After we have done this, our repository
interface looks as follows:
1 import org.springframework.data.repository.Repository;
2
3 interface TodoRepository extends Repository<Todo, Long> {
4
5 //This is a query method.
6 Todo findById(Long id);
7 }

Returning Values from Query Methods

A query method can return only one result or more than one result. Also, we can create a query
method that is invoked asynchronously. This section addresses each of these situations and
describes what kind of return values we can use in each situation.

First, if we are writing a query that should return only one result, we can return the following
types:
 Basic type. Our query method will return the found basic type or null.
 Entity. Our query method will return an entity object or null.
 Guava / Java 8 Optional<T>. Our query method will return an Optional that contains the
found object or an empty Optional.
Here are some examples of query methods that return only one result:

1 import java.util.Optional;
2 import org.springframework.data.jpa.repository.Query;
3 import org.springframework.data.repository.Repository;
4 import org.springframework.data.repository.query.Param;
5
6 interface TodoRepository extends Repository<Todo, Long> {
7
8 @Query("SELECT t.title FROM Todo t where t.id = :id")
9 String findTitleById(@Param("id") Long id);
10
11 @Query("SELECT t.title FROM Todo t where t.id = :id")
12 Optional<String> findTitleById(@Param("id") Long id);
13
14 Todo findById(Long id);
15
16 Optional<Todo> findById(Long id);
17 }

Second, if we are writing a query method that should return more than one result, we can return
the following types:
 List<T>. Our query method will return a list that contains the query results or an empty list.
 Stream<T>. Our query method will return a Stream that can be used to access the query
results or an empty Stream.
Here are some examples of query methods that return more than one result:

1 import java.util.stream.Stream;
2 import org.springframework.data.repository.Repository;
3
4 interface TodoRepository extends Repository<Todo, Long> {
5
6 List<Todo> findByTitle(String title);
7
8 Stream<Todo> findByTitle(String title);
9 }

Third, if we want that our query method is executed asynchronously, we have to annotate it with
the @Async annotation and return a Future<T> object. Here are some examples of query
methods that are executed asynchronously:
1 import java.util.concurrent.Future;
2 import java.util.stream.Stream;
3 import org.springframework.data.jpa.repository.Query;
4 import org.springframework.data.repository.Repository;
5 import org.springframework.data.repository.query.Param;
6 import org.springframework.scheduling.annotation.Async;
7
8 interface TodoRepository extends Repository<Todo, Long> {
9
10 @Async
11 @Query("SELECT t.title FROM Todo t where t.id = :id")
12 Future<String> findTitleById(@Param("id") Long id);
13
14 @Async
15 @Query("SELECT t.title FROM Todo t where t.id = :id")
16 Future<Optional<String>> findTitleById(@Param("id") Long id);
17
18 @Async
Future<Todo> findById(Long id);

19 @Async
20 Future<Optional<Todo>> findById(Long id);
21
22 @Async
23 Future<List<Todo>> findByTitle(String title);
24
25 @Async
Future<Stream<Todo>> findByTitle(String title);
}

Passing Method Parameters to Query Methods

We can pass parameters to our database queries by passing method parameters to our query
methods. Spring Data JPA supports both position based parameter binding and named
parameters. Both of these options are described in the following.

The position based parameter binding means that the order of our method parameters decides
which placeholders are replaced with them. In other words, the first placeholder is replaced with
the first method parameter, the second placeholder is replaced with the second method
parameter, and so on.

Here are some query methods that use the position based parameter binding:

1 import java.util.Optional
2 import org.springframework.data.jpa.repository.Query;
3 import org.springframework.data.repository.Repository;
4
5
6 interface TodoRepository extends Repository<Todo, Long> {
7
8 public Optional<Todo> findByTitleAndDescription(String title, String description);
9
10 @Query("SELECT t FROM Todo t where t.title = ?1 AND t.description = ?2")
11 public Optional<Todo> findByTitleAndDescription(String title, String description);
12
13 @Query(value = "SELECT * FROM todos t where t.title = ?0 AND t.description = ?1",
14 nativeQuery=true
15 )
16 public Optional<Todo> findByTitleAndDescription(String title, String description);
17 }

Using position based parameter binding is a bit error prone because we cannot change the order
of the method parameters or the order of the placeholders without breaking our database query.
We can solve this problem by using named parameters.
We can use named parameters by replacing the numeric placeholders found from our database
queries with concrete parameter names, and annotating our method parameters with
the @Param annotation.
The @Param annotation configures the name of the named parameter that is replaced with the
value of the method parameter.

Here are some query methods that use named parameters:

1 import java.util.Optional
2 import org.springframework.data.jpa.repository.Query;
3 import org.springframework.data.repository.Repository;
4 import org.springframework.data.repository.query.Param;
5
6
7 interface TodoRepository extends Repository<Todo, Long> {
8
9 @Query("SELECT t FROM Todo t where t.title = :title AND t.description = :description")
10 public Optional<Todo> findByTitleAndDescription(@Param("title") String title,
11 @Param("description") String description);
12
13 @Query(
14 value = "SELECT * FROM todos t where t.title = :title AND t.description = :description",
15 nativeQuery=true
16 )
17 public Optional<Todo> findByTitleAndDescription(@Param("title") String title,
18 @Param("description") String description);
19 }

Spring Boot JPA - Named Queries

Some time case arises, where we need a custom query to fulfil one test case. We can
use @NamedQuery annotation to specify a named query within an entity class and
then declare that method in repository. Following is an example.

We've added custom methods in Repository in JPA Custom Methods chapter. Now
let's add another method using @NamedQuery and test it.

Entity - Entity.java
Following is the default code of Employee. It represents a Employee table with id,
name, age and email columns.

package com.tutorialspoint.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@Table
@NamedQuery(name = "Employee.findByEmail",
query = "select e from Employee e where e.email = ?1")
public class Employee {
@Id
@Column
private int id;

@Column
private String name;

@Column
private int age;

@Column
private String email;

public int getId() {


return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

Repository - EmployeeRepository.java
Add a method to find an employee by its name and age.

package com.tutorialspoint.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.tutorialspoint.entity.Employee;

@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
public List<Employee> findByName(String name);
public List<Employee> findByAge(int age);
public Employee findByEmail(String email);
}

Now Spring JPA will create the implementation of above methods automatically
using the query provided in named query. Let's test the methods added by adding their
test cases in test file. Last two methods of below file tests the named query method
added.

Following is the complete code of EmployeeRepositoryTest.

package com.tutorialspoint.repository;

import static org.junit.jupiter.api.Assertions.assertEquals;


import java.util.ArrayList;
import java.util.List;
import javax.transaction.Transactional;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.tutorialspoint.entity.Employee;
import com.tutorialspoint.sprintbooth2.SprintBootH2Application;

@ExtendWith(SpringExtension.class)
@Transactional
@SpringBootTest(classes = SprintBootH2Application.class)
public class EmployeeRepositoryTest {
@Autowired
private EmployeeRepository employeeRepository;

@Test
public void testFindById() {
Employee employee = getEmployee();
employeeRepository.save(employee);
Employee result = employeeRepository.findById(employee.getId()).get();
assertEquals(employee.getId(), result.getId());
}
@Test
public void testFindAll() {
Employee employee = getEmployee();
employeeRepository.save(employee);
List<Employee> result = new ArrayList<>();
employeeRepository.findAll().forEach(e -> result.add(e));
assertEquals(result.size(), 1);
}
@Test
public void testSave() {
Employee employee = getEmployee();
employeeRepository.save(employee);
Employee found = employeeRepository.findById(employee.getId()).get();
assertEquals(employee.getId(), found.getId());
}
@Test
public void testDeleteById() {
Employee employee = getEmployee();
employeeRepository.save(employee);
employeeRepository.deleteById(employee.getId());
List<Employee> result = new ArrayList<>();
employeeRepository.findAll().forEach(e -> result.add(e));
assertEquals(result.size(), 0);
}
private Employee getEmployee() {
Employee employee = new Employee();
employee.setId(1);
employee.setName("Mahesh");
employee.setAge(30);
employee.setEmail("[email protected]");
return employee;
}
@Test
public void testFindByName() {
Employee employee = getEmployee();
employeeRepository.save(employee);
List<Employee> result = new ArrayList<>();
employeeRepository.findByName(employee.getName()).forEach(e ->
result.add(e));
assertEquals(result.size(), 1);
}
@Test
public void testFindByAge() {
Employee employee = getEmployee();
employeeRepository.save(employee);
List<Employee> result = new ArrayList<>();
employeeRepository.findByAge(employee.getAge()).forEach(e -> result.add(e));
assertEquals(result.size(), 1);
}
@Test
public void testFindByEmail() {
Employee employee = getEmployee();
employeeRepository.save(employee);
Employee result = employeeRepository.findByEmail(employee.getEmail());
assertNotNull(result);
}
}

Run the test cases


Right Click on the file in eclipse and select Run a JUnit Test and verify the result.

Transactions with Spring and JPA

1. Overview

This tutorial will discuss the right way to configure Spring Transactions, how to use
the @Transactional annotation and common pitfalls.
For a more in-depth discussion on the core persistence configuration, check out the Spring with
JPA tutorial.
Basically, there are two distinct ways to configure Transactions, annotations and AOP, each with
its own advantages. We’re going to discuss the more common annotation config here.

2. Configure Transactions

Spring 3.1 introduces the @EnableTransactionManagement annotation that we can use in


a @Configuration class to enable transactional support:
@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
//...
}

@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
}Copy
However, if we’re using a Spring Boot project and have a spring-data-* or spring-tx
dependencies on the classpath, then transaction management will be enabled by default.

3. Configure Transactions With XML

For versions before 3.1, or if Java is not an option, here is the XML configuration
using annotation-driven and namespace support:
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEmf" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />Copy

4. The @Transactional Annotation

With transactions configured, we can now annotate a bean with @Transactional either at the
class or method level:
@Service
@Transactional
public class FooService {
//...
}Copy
The annotation supports further configuration as well:

 the Propagation Type of the transaction


 the Isolation Level of the transaction
 a Timeout for the operation wrapped by the transaction
 a readOnly flag – a hint for the persistence provider that the transaction should be read
only
 the Rollback rules for the transaction

Note that by default, rollback happens for runtime, unchecked exceptions only. The checked
exception does not trigger a rollback of the transaction. We can, of course, configure this
behavior with the rollbackFor and noRollbackFor annotation parameters.
Declarative Transaction Management

Most Spring Framework users choose declarative transaction management. This option has the least impact o
with the ideals of a non-invasive lightweight container.

The Spring Framework’s declarative transaction management is made possible with Spring
aspect-oriented programming (AOP). However, as the transactional aspects code comes with the
Spring Framework distribution and may be used in a boilerplate fashion, AOP concepts do not
generally have to be understood to make effective use of this code.

The Spring Framework’s declarative transaction management is similar to EJB CMT, in that you
can specify transaction behavior (or lack of it) down to the individual method level. You can
make a setRollbackOnly() call within a transaction context, if necessary. The differences
between the two types of transaction management are:

 Unlike EJB CMT, which is tied to JTA, the Spring Framework’s declarative transaction
management works in any environment. It can work with JTA transactions or local
transactions by using JDBC, JPA, or Hibernate by adjusting the configuration files.

 You can apply the Spring Framework declarative transaction management to any class,
not merely special classes such as EJBs.

 The Spring Framework offers declarative rollback rules, a feature with no EJB
equivalent. Both programmatic and declarative support for rollback rules is provided.

 The Spring Framework lets you customize transactional behavior by using AOP. For
example, you can insert custom behavior in the case of transaction rollback. You can also
add arbitrary advice, along with transactional advice. With EJB CMT, you cannot
influence the container’s transaction management, except with setRollbackOnly().

 The Spring Framework does not support propagation of transaction contexts across
remote calls, as high-end application servers do. If you need this feature, we recommend
that you use EJB. However, consider carefully before using such a feature, because,
normally, one does not want transactions to span remote calls.

The concept of rollback rules is important. They let you specify which exceptions (and
throwables) should cause automatic rollback. You can specify this declaratively, in
configuration, not in Java code. So, although you can still call setRollbackOnly() on
the TransactionStatus object to roll back the current transaction back, most often you can specify
a rule that MyApplicationException must always result in rollback. The significant advantage to
this option is that business objects do not depend on the transaction infrastructure. For example,
they typically do not need to import Spring transaction APIs or other Spring APIs.

Although EJB container default behavior automatically rolls back the transaction on a system
exception (usually a runtime exception), EJB CMT does not roll back the transaction
automatically on an application exception (that is, a checked exception other
than java.rmi.RemoteException). While the Spring default behavior for declarative transaction
management follows EJB convention (roll back is automatic only on unchecked exceptions), it is
often useful to customize this behavior.

Update an entity using spring-data-jpa:


To update an entity using Spring Data JPA, you will need to follow these steps:

1. Retrieve the entity object that you want to update. You can do this using
the findById() method of the JpaRepository interface.

2. Modify the fields of the entity object as needed.

3. Save the entity back to the database by calling the save() method of
the JpaRepository interface.

Here's an example:

// Retrieve the entity object

Optional<User> optionalUser = userRepository.findById(1L);

if (optionalUser.isPresent()) {

// Modify the fields of the entity object

User user = optionalUser.get();

user.setUsername("newUsername");

user.setPassword("newPassword");

// Save the entity

userRepository.save(user);

Note that the save() method will perform an update if the entity already exists in the
database, and an insert if it does not.

custom Spring Data


Repository
Last modified: Nov 15, 2022

Follow @vlad_mihalcea
Imagine having a tool that can automatically detect JPA and Hibernate performance
issues. Wouldn’t that be just awesome?
Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring
Framework, Jakarta EE, Java EE, Quarkus, or Play Framework.
So, enjoy spending your time on the things you love rather than fixing performance
issues in your production system on a Saturday night!

Introduction
In this article, I’m going to show you the best way to write a custom Spring Data Repository.
While the default JpaRepository methods, as well as the query methods, are very
convenient in many situations, there might be times when you need custom Repository
methods that can take advantage of any JPA provider-specific functionality.

When to use a custom Spring Data


Repository
Let’s assume we want to fetch a one-to-many DTO projection, as I explained in this
article.
Our JPA query looks like this:
List<PostDTO> postDTOs = entityManager.createQuery("""

select p.id as p_id,

p.title as p_title,

pc.id as pc_id,

pc.review as pc_review

from PostComment pc

join pc.post p

order by pc.id

""")

.unwrap(org.hibernate.query.Query.class)

.setResultTransformer(new PostDTOResultTransformer())

.getResultList();

Notice that we are unwrapping the JPA Query to a


Hibernate org.hibernate.query.Query in order to provide a custom ResultTransformer that
can build a hierarchical parent-child DTO aggregate from the default table-
based Object[] projection.
We can’t just use a regular Spring Data Repository query method or a @Query annotation
because we also have to pass our own Hibernate-specific ResultTransformer.
Therefore, we need to write a custom Repository that can provide us access to the
underlying JPA EntityManager so that we can write our query using the Hibernate-
specific API.
How to write a custom Spring Data
Repository
First, we need to define an interface that provides the method signatures of our custom
Repository methods.
public interface CustomPostRepository {

List<PostDTO> findPostDTOWithComments();

Second, we need to provide an implementation of the CustomPostRepository interface:


public class CustomPostRepositoryImpl implements CustomPostRepository {

@PersistenceContext

private EntityManager entityManager;

@Override

public List<PostDTO> findPostDTOWithComments() {

return entityManager.createNativeQuery("""

SELECT p.id AS p_id,

p.title AS p_title,

pc.id AS pc_id,

pc.review AS pc_review

FROM post p

JOIN post_comment pc ON p.id = pc.post_id

ORDER BY pc.id

""")

.unwrap(org.hibernate.query.Query.class)

.setResultTransformer(new PostDTOResultTransformer())

.getResultList();

Third, we need to make the default Spring Data JPA PostRepository extend
our CustomPostRepository interface:
@Repository
public interface PostRepository

extends JpaRepository<Post, Long>, CustomPostRepository {

A picture is worth 100 words, so here’s a diagram that shows you how the custom
Spring Data Repository is associated to the standard JpaRepository one:

Testing time
Assuming we have two Post entities, the first one having two PostComment child entities,
and the second Post having a single PostComment child:
entityManager.persist(

new Post()

.setId(1L)

.setTitle("High-Performance Java Persistence")

.addComment(

new PostComment()

.setId(1L)

.setReview("Best book on JPA and Hibernate!")

.addComment(

new PostComment()

.setId(2L)

.setReview("A must-read for every Java developer!")

);

entityManager.persist(

new Post()

.setId(2L)

.setTitle("Hypersistence Optimizer")

.addComment(

new PostComment()
.setId(3L)

.setReview("It's like pair programming with Vlad!")

);

When calling the findPostDTOWithComments method, we are going to get the


expected PostDTO hierarchical projection:
List<PostDTO> postDTOs = forumService.findPostDTOWithComments();

assertEquals(2, postDTOs.size());

assertEquals(2, postDTOs.get(0).getComments().size());

assertEquals(1, postDTOs.get(1).getComments().size());

PostDTO post1DTO = postDTOs.get(0);

assertEquals(1L, post1DTO.getId().longValue());

assertEquals(2, post1DTO.getComments().size());

assertEquals(1L, post1DTO.getComments().get(0).getId().longValue());

assertEquals(2L, post1DTO.getComments().get(1).getId().longValue());

PostDTO post2DTO = postDTOs.get(1);

assertEquals(2L, post2DTO.getId().longValue());

assertEquals(1, post2DTO.getComments().size());

assertEquals(3L, post2DTO.getComments().get(0).getId().longValue());

You might also like