Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

Partner – Microsoft – NPI EA (cat = Baeldung)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Microsoft – NPI EA (cat= Spring Boot)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, you can get started over on the documentation page.

And, you can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Orkes – NPI EA (cat=Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag=Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – All Access – NPI EA (cat= Spring)
announcement - icon

All Access is finally out, with all of my Spring courses. Learn JUnit is out as well, and Learn Maven is coming fast. And, of course, quite a bit more affordable. Finally.

>> GET THE COURSE
Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – LambdaTest – NPI EA (cat=Testing)
announcement - icon

End-to-end testing is a very useful method to make sure that your application works as intended. This highlights issues in the overall functionality of the software, that the unit and integration test stages may miss.

Playwright is an easy-to-use, but powerful tool that automates end-to-end testing, and supports all modern browsers and platforms.

When coupled with LambdaTest (an AI-powered cloud-based test execution platform) it can be further scaled to run the Playwright scripts in parallel across 3000+ browser and device combinations:

>> Automated End-to-End Testing With Playwright

Course – Spring Sale 2025 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our Spring Sale. All Courses are 25% off until 26th May, 2025:

>> EXPLORE ACCESS NOW

Course – Spring Sale 2025 – NPI (cat=Baeldung)
announcement - icon

Yes, we're now running our Spring Sale. All Courses are 25% off until 26th May, 2025:

>> EXPLORE ACCESS NOW

1. Overview

In this tutorial, we’ll explore how to use Java Records with JPA. We’ll start by exploring why records can’t be used at entities.

Then, we’ll see how to use records with JPA. We’ll also look at how to use records with Spring Data JPA in a Spring Boot Application.

2. Records vs. Entities

Records are immutable and are used to store data. They contain fields, all-args constructor, getters, toString, and equals/hashCode methods. Since they are immutable, they don’t have setters. Because of their concise syntax, they are often used as data transfer objects (DTOs) in Java applications.

Entities are classes that are mapped to a database table. They are used to represent an entry in a database. Their fields are mapped to columns in the database table.

2.1. Records Can’t Be Entities

Entities are handled by the JPA provider. JPA providers are responsible for creating the database tables, mapping the entities to the tables, and persisting the entities to the database. In popular JPA providers like Hibernate, entities are created and managed using proxies.

Proxies are classes that are generated at runtime and extend the entity class. These proxies rely on the entity class to have a no-args constructor and setters. Since records don’t have these, they can’t be used as entities.

2.2. Other Ways to Use Records With JPA

Due to the ease and safety of using records within Java applications, it may be beneficial to use them with JPA in some other ways.

In JPA, we can use records in the following ways:

  • Convert the results of a query to a record
  • Use records as DTOs to transfer data between layers
  • Convert entities to records.
  • Use records as @IdClass for composite primary keys

3. Project Setup

We’ll use Spring Boot to create a simple application that uses JPA and Spring Data JPA. Then we’ll look at a few ways to use records while interacting with the database.

3.1. Dependencies

Let’s start by adding the Spring Data JPA dependency to our project:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>3.0.4</version>
</dependency>

In addition to Spring Data JPA, we’ll also need to configure a database. We can use any SQL database. For example, we can use the in-memory H2 database.

3.2. Entity and Record

Let’s create an entity that we’ll use to interact with the database. We’ll create a Book entity that will be mapped to a book table in the database:

@Entity
@Table(name = "book")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String author;
    private String isbn;
    
    // constructors, getters, setters
}

Let’s also create a Record that corresponds to the Book entity:

public record BookRecord(Long id, String title, String author, String isbn) {}

Next, we’ll look at a few ways to use the record instead of the entity in our application.

4. Using Records With JPA

The JPA API provides a few ways to interact with the database in which it is possible to use records. Let’s look at a few of them.

4.1. Criteria Builder

Let’s start by looking at how to use records with CriteriaBuilder. We’ll make a query that returns all the books in the database:

public class QueryService {
    @PersistenceContext
    private EntityManager entityManager;
    
    public List<BookRecord> findAllBooks() {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<BookRecord> query = cb.createQuery(BookRecord.class);
        Root<Book> root = query.from(Book.class);
        query.select(cb.construct(BookRecord.class, root.get("id"), root.get("title"), 
          root.get("author"), root.get("isbn")));
        return entityManager.createQuery(query).getResultList();
    }
}

In the above code, we use CriteriaBuilder to create a CriteriaQuery that returns a BookRecord.

Let’s look at some of the steps in the above code:

  • We create a CriteriaQuery using the CriteriaBuilder.createQuery() method. We pass the class of the record that we want to return as the parameter
  • We then create a Root using the CriteriaQuery.from() method. We pass the entity class as the parameter. This is how we specify the table that we want to query
  • Then, we use the CriteriaQuery.select() method to specify a select clause. We use the CriteriaBuilder.construct() method to convert the query results to a record. We pass the class of the record and the fields of the entity that we want to pass to the record constructor as the parameters
  • Finally, we use the EntityManager.createQuery() method to create a TypedQuery from CriteriaQuery. We then use the TypedQuery.getResultList() method to get the results of the query

This will create a select query to get all the books in the database. It’ll then convert each result to a BookRecord using the construct() method and return a list of records instead of a list of entities when we call the getResultList() method.

In this way, we can use the entity class to create a query but use records for the rest of the application.

4.2. Typed Query

Similar to the CriteriaBuilder, we can use a typed query to return a record instead of an entity. Let’s add a method in our QueryService to get a single book as records using a typed query:

public BookRecord findBookByTitle(String title) {
    TypedQuery<BookRecord> query = entityManager
      .createQuery("SELECT " +
        "new com.baeldung.recordswithjpa.records.BookRecord(b.id, b.title, b.author, b.isbn) " +
        "FROM Book b WHERE b.title = :title", BookRecord.class);
    query.setParameter("title", title);
    return query.getSingleResult();
}

TypedQuery allows us to convert the results of a query into any type as long as the type has a constructor that takes the same number of parameters as the results of the query.

In the above code, we use the EntityManager.createQuery() method to create a TypedQuery. We pass the query string and the class of the record as the parameters. Then, we use the TypedQuery.setParameter() method to set the parameters of the query. Finally, we use the TypedQuery.getSingleResult() method to get the result of the query, which will be a BookRecord object.

4.3. Native Query

We can also use native queries to get the results of a query as records. However, a native query doesn’t allow us to convert the results into any type. Instead, we need to use a mapping to convert the results into a record. First, let’s define a mapping in our entity:

@SqlResultSetMapping(
  name = "BookRecordMapping",
  classes = @ConstructorResult(
    targetClass = BookRecord.class,
    columns = {
      @ColumnResult(name = "id", type = Long.class),
      @ColumnResult(name = "title", type = String.class),
      @ColumnResult(name = "author", type = String.class),
      @ColumnResult(name = "isbn", type = String.class)
    }
  )
)
@Entity
@Table(name = "book")
public class Book {
    // ...
}

The mapping will work in the following way:

  • The name attribute of the @SqlResultSetMapping annotation specifies the name of the mapping.
  • The @ConstructorResult annotation specifies that we want to use the constructor of the record to convert the results.
  • The targetClass attribute of the @ConstructorResult annotation specifies the class of the record.
  • The @ColumnResult annotation specifies the column name and the type of the column. These column values will be passed to the constructor of the record.

We can then use this mapping in our native query to get the results as records:

public List<BookRecord> findAllBooksUsingMapping() {
    Query query = entityManager.createNativeQuery("SELECT * FROM book", "BookRecordMapping");
    return query.getResultList();
}

This will create a native query that returns all the books in the database. It’ll convert the results into a BookRecord using the mapping and return a list of records instead of a list of entities when we call the getResultList() method.

5. Using Records With Spring Data JPA

Spring Data JPA provides a few improvements to the JPA API. It enables us to use records with Spring Data JPA repositories in a few ways. Let’s look at how we can use records with Spring Data JPA repositories.

5.1. Automatic Mapping From Entity to Record

Spring Data Repositories allow us to use records as the return type of the methods in the repository. This will automatically map the entity to the record. This is only possible if the record has exactly the same fields as the entity. Let’s look at an example:

public interface BookRepository extends JpaRepository<Book, Long> {
    List<BookRecord> findBookByAuthor(String author);
}

Since the BookRecord has the same fields as the Book entity, Spring Data JPA will automatically map the entity to the record and return a list of records instead of a list of entities when we call the findBookByAuthor() method.

5.2. Using Records With @Query

Similar to TypedQuery, we can use records with the @Query annotation in Spring Data JPA repositories. Let’s look at an example:

public interface BookRepository extends JpaRepository<Book, Long> {
    @Query("SELECT new com.baeldung.jpa.records.BookRecord(b.id, b.title, b.author, b.isbn) FROM Book b WHERE b.id = :id")
    BookRecord findBookById(@Param("id") Long id);
}

Spring Data JPA will automatically convert the results of the query into a BookRecord and return a single record instead of an entity when we call the findBookById() method.

5.3. Custom Repository Implementation

In case automatic mapping is not an option, we can also define a custom repository implementation that allows us to define our own mapping. Let’s start by creating a CustomBookRecord class that will be used as the return type of the methods in the repository:

public record CustomBookRecord(Long id, String title) {}

Please note that the CustomBookRecord class doesn’t have the same fields as the Book entity. It only has the id and the title fields.

Then, we can create a custom repository implementation that will use the CustomBookRecord class:

public interface CustomBookRepository {
    List<CustomBookRecord> findAllBooks();
}

In the implementation of the repository, we can define the methods that will be used to map the results of the queries into the CustomBookRecord class:

@Repository
public class CustomBookRepositoryImpl implements CustomBookRepository {
    private final JdbcTemplate jdbcTemplate;

    public CustomBookRepositoryImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public List<CustomBookRecord> findAllBooks() {
        return jdbcTemplate.query("SELECT id, title FROM book", (rs, rowNum) ->
          new CustomBookRecord(rs.getLong("id"), rs.getString("title")));
    }
}

In the above code, we use the JdbcTemplate.query() method to execute the query and map the results into a CustomBookRecord using a lambda expression which is an implementation of the RowMapper interface.

6. Using Records as @Embeddables

With the recent updates, Hibernate now supports mapping Java records as embeddable. We can use Java records to represent a group of related properties we want to embed within an entity class:

@Embeddable
public record Author (
    String firstName,
    String lastName
) {}

In this example, the Author is a Java record marked as @Embeddable. It has two fields: firstName and lastName. We can use this record in an entity class to represent an author. To use this Record inside our Entity, we should mark it with @Embedded annotation:

@Entity
@Table(name = "embeadable_author_book")
public class EmbeddableBook {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    @Embedded
    private Author author;
    private String isbn;
    //...
}

Hibernate 6 before the 6.2 version requires some additional work to use Records. The Author field would need additional annotation @EmbeddableInstantiator(AuthorInstallator.class), and we should provide the implementation for the EmbeddableInstantiator interface:

public class AuthorInstallator implements EmbeddableInstantiator {

    public boolean isInstance(Object object, SessionFactoryImplementor sessionFactory) {
        return object instanceof Author;
    }

    public boolean isSameClass(Object object, SessionFactoryImplementor sessionFactory) {
        return object.getClass().equals(Author.class);
    }
    
    @Override
    public Object instantiate(final ValueAccess valueAccess, 
      final SessionFactoryImplementor sessionFactoryImplementor) {
        final String firstName = valueAccess.getValue(0, String.class);
        final String secondName = valueAccess.getValue(1, String.class);
        return new Author(firstName, secondName);
    }
}

Furthermore, Hibernate also supports using structured SQL types to persist to a struct. The Author record can be marked with @Struct annotation that allows mapping the record to a structured SQL type. This can be useful when we want to map the record to a complex type in the database corresponding to a struct.

7. Using Records as @IdClass

In JPA, an entity may have a composite primary key. Normally, we use an @IdClass annotation to define a class that will be used as the primary key of the entity. Since the latest Hibernate updates, we can also use records as the @IdClass of an entity.

7.1. Defining an @IdClass Using a Record

Let’s look at an example:

public record BookId(Long id, Long isbn) {
}

In the above code, we define a BookId record that has two fields: id and isbn.

7.2. Using the @IdClass in an Entity

Next, let’s use the BookId record as the @IdClass of the CompositeBook entity:

@Entity
@Table(name = "book")
@IdClass(BookId.class)
public class CompositeBook {
    @Id
    private Long id;
    @Id
    private Long isbn;
    private String title;
    private String author;
    
    // constructors, getters, setters
}

In the above code, we use the @IdClass annotation to link the BookId record to the CompositeBook entity. The id and isbn fields are marked with @Id and can be set using a BookId object.

7.3. Using the Record in a Repository

We can use the BookId record as the primary key in the repository as well:

@Repository
public interface CompositeBookRepository extends JpaRepository<CompositeBook, BookId> {
}

In the above code, we use the BookId record as the primary key of the CompositeBook entity in the repository.

7.4. Using Record to Fetch Data

Since we added the BookId record to the repository, we can use it to fetch or update data.  Let’s write a test to save a CompositeBook object to the database and then fetch it using the BookId record:

@SpringBootTest
public class CompositeBookRepositoryIntegrationTest {
    @Autowired
    private CompositeBookRepository compositeBookRepository;

    @Test
    public void givenCompositeBook_whenSave_thenSaveToDatabase() {
        CompositeBook compositeBook = new CompositeBook(new BookId(1L, 1234567890L),
          "Book Title", "Author Name");
        compositeBookRepository.save(compositeBook);

        CompositeBook savedBook = compositeBookRepository
          .findById(new BookId(1L, 1234567890L))
          .orElse(null);
        assertNotNull(savedBook);
        assertEquals("Book Title", savedBook.getTitle());
        assertEquals("Author Name", savedBook.getAuthor());
    }
}

As we can see, we can use the BookId record in the findById() method of the repository to get the CompositeBook object from the database.

8. Conclusion

In this article, we’ve looked at how we can use records with JPA and Spring Data JPA. We’ve seen how we can use records with the JPA API using CriteriaBuilderTypedQuery, and native queries. We’ve also seen how we can use records with Spring Data JPA repositories using automatic mapping, custom queries, custom repository implementations, and as an @IdClass.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

Partner – Microsoft – NPI EA (cat = Spring Boot)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Orkes – NPI EA (cat = Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag = Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Course – Spring Sale 2025 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our Spring Sale. All Courses are 25% off until 26th May, 2025:

>> EXPLORE ACCESS NOW

Course – Spring Sale 2025 – NPI (All)
announcement - icon

Yes, we're now running our Spring Sale. All Courses are 25% off until 26th May, 2025:

>> EXPLORE ACCESS NOW

Course – LSD – NPI (cat=JPA)
announcement - icon

Get started with Spring Data JPA through the reference Learn Spring Data JPA:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)
1 Comment
Oldest
Newest
Inline Feedbacks
View all comments