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 – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

1. Introduction

In this article, we’re going to look at how to use the @Find annotation in Hibernate. We’ll see what it is, what it’s used for, and how to use it.

2. Setting up Hibernate

Before we can use the @Find annotation, we need to set up Hibernate and our database.

2.1. Dependencies

To use @Find in Hibernate, we need to use version 6.3 or newer. The newest release at the time of writing is 7.4.6.Final:

<dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>7.4.6.Final</version>
</dependency>

We also need the Jakarta Data Core API. The newest release at the time of writing is 1.0.2:

<dependency>
    <groupId>jakarta.data</groupId>
    <artifactId>jakarta.data-api</artifactId>
    <version>1.0.2</version>
</dependency>

This gives us everything we need to write our repository code.

2.2. Hibernate Processor

In addition to the dependencies for writing our repositories, we need to configure the Hibernate Processor. This will generate concrete classes for our repository interfaces at compile time.

When using Hibernate 7, this is configured by adding an annotation processor to the maven-compiler-plugin:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.hibernate.orm</groupId>
                        <artifactId>hibernate-processor</artifactId>
                        <version>7.4.6.Final</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

This needs to match the version of the hibernate-core dependency we specified earlier.

Once the plugin is added, the Maven build will automatically generate additional classes for us, including Hibernate Metamodel classes for our entities, and concrete classes that implement our repository interfaces, as we’ll see later.

2.3. Database

For this article, we need to set up a database. Our tables will look as follows:

CREATE TABLE authors (
  author_id   BIGINT PRIMARY KEY,
  name        TEXT NOT NULL
);

CREATE TABLE books (
  book_id     BIGINT PRIMARY KEY,
  title       TEXT NOT NULL,
  author_id   BIGINT NOT NULL,
  FOREIGN KEY (author_id) REFERENCES authors(author_id)
);

This gives us two tables – books and authors – such that there’s a foreign key between them.

We then need some data in our database:

INSERT INTO authors (author_id, name) VALUES
  (1, 'George Orwell'),
  (2, 'Haruki Murakami'),
  (3, 'Agatha Christie'),
  (4, 'Ursula K. Le Guin');

INSERT INTO books (book_id, title, author_id) VALUES
  (101, '1984', 1),
  (102, 'Animal Farm', 1),
  (103, 'Norwegian Wood', 2),
  (104, 'Kafka on the Shore', 2),
  (105, 'Murder on the Orient Express', 3),
  (106, 'And Then There Were None', 3),
  (107, 'The Left Hand of Darkness', 4);

2.4. JPA Entity

Finally, since we’ll be working with Hibernate, we also need Hibernate entities to represent our data.

First, our Author entity:

@Entity
@Table(name = "authors")
public class Author {
    @Id
    @Column(name = "author_id")
    private Long authorId;

    private String name;

    public Long getAuthorId() {
        return authorId;
    }

    public String getName() {
        return name;
    }
}

And then the Book entity:

@Entity
@Table(name = "books")
public class Book {
    @Id
    @Column(name = "book_id")
    private Long bookId;

    private String title;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "author_id", nullable = false)
    private Author author;

    public Long getBookId() {
        return bookId;
    }

    public String getTitle() {
        return title;
    }

    public Author getAuthor() {
        return author;
    }
}

This references our Author entity so we can follow those links in our queries.

3. Repository Interfaces

Once everything is set up, we’re ready to start writing our repositories. These are written as Java interfaces and annotated with the @jakarta.data.repository.Repository annotation:

@Repository
public interface BookRepository {
}

On its own, this is enough for the Hibernate Processor to discover and generate a concrete implementation – in this case, called BookRepository_.

// Generated code
public class BookRepository_ implements BookRepository {
    protected StatelessSession session;

    public BookRepository_(StatelessSession session) {
        this.session = session;
    }

    public StatelessSession session() {
        return session;
    }
}

This generates an implementation constructed with a StatelessSession instance. We can then create instances of this as needed when we want to query our data.

3.1. Injecting an EntityManager

We often don’t want to create repository instances on demand. Instead, we’d like to create them once at the start of our application and then pass them around. For example, we might want to create them in our Spring context.

Fortunately, Hibernate supports this too. All we need to do is add a special method to our interface that returns an EntityManager:

@Repository
public interface AuthorRepository {
    EntityManager entityManager();
}

If we do this, Hibernate will construct an alternative form of our repository that is constructed using our EntityManager instead:

// Generated code
public class AuthorRepository_ implements AuthorRepository {
    protected EntityManager entityManager;

    public AuthorRepository_(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Override
    public EntityManager entityManager() {
        return entityManager;
    }
}

We can then safely construct this once and reuse it as much as needed.

4. Finding By Fields

Once we’ve got our repository, we need to be able to do something with it. We can add methods to find entities using the @Find  annotation, with special conventions for parameter names and return types.

@Repository
public interface BookRepository {
    @Find
    List<Book> getAllBooks();
}

Here we have a method called getAllBooks. The method name is unimportant. However, the fact that it returns a List<Book> means that the generated code will understand that we’re working with Book entities, and that we’re returning all of the ones that match our query.

This will equate to running the following query:

SELECT b1_0.book_id, b1_0.author_id, b1_0.title 
FROM books b1_0

We can go a step further with this by actually filtering our results:

@Find
Book getBookWithTitle(String title);

Again, the method title can be anything. However, the parameter name must exactly match a field in our Book entity. As such, Hibernate generates code that will filter by that field:

SELECT b1_0.book_id, b1_0.author_id, b1_0.title 
FROM books b1_0 
WHERE b1_0.title = ?

This time, we return only a single Book entity. As such, Hibernate knows to return a single matching entity. If there isn’t one, we’ll get an EmptyResultException thrown instead. Alternatively, if there are multiple records that match, we’ll get a NonUniqueResultException thrown.

5. Optional Results

Sometimes we want to search for a single record that may not exist. We can handle the EmptyResultException that gets thrown, but this isn’t ideal.

Hibernate supports a few ways to handle this automatically. The most obvious is to update our method to return Optional<Book> instead:

@Find
Optional<Book> getOptionalBookWithTitle(String title);

In this case, an unknown record returns Optional.empty() instead.

Alternatively, if we don’t want to deal with this, we can annotate our method with jakarta.annotation.Nullable.

@Find
@Nullable
Book getNullableBookWithTitle(String title);

This tells the generated code to return null instead of throwing an exception.

In both cases, the database queries are identical. The only difference is what the generated code does with the results after executing the query. Both cases also still throw a NonUniqueResultException if the query returns more than one result.

6. Finding By Nested Fields

So far we’ve seen how to find records based on data directly in that record. However, often we want to search based on related data too. Hibernate can do this based on how we name our method parameters. If we use a “$” symbol, this is interpreted as linking fields through related entities:

@Find
List<Book> getAllBooksByAuthorName(String author$name);

Here, our query will join from Book through the Book.author field, and then query based on Author.name:

SELECT b1_0.book_id, b1_0.author_id, b1_0.title 
FROM books b1_0 
  JOIN authors a1_0 ON a1_0.author_id = b1_0.author_id 
WHERE a1_0.name = ?

We can use this technique for as many steps as we want. However, we can only follow relationships that use @ManyToOne or @OneToOne. Relationships using @OneToMany or @ManyToMany won’t work since the generated query would need to link to multiple records.

7. Multiple Results

So far we’ve seen how to return either a single result or all matching results. However, sometimes we need more control. Hibernate can manage all of this for us.

7.1. Sorting

By default, queries that return lists of records return them in the order the database returns them. This can vary based on many factors, so we can’t rely on it for consistent ordering.

If we add the @OrderBy annotation to our method, Hibernate will sort our results by the specified fields:

@Find
@OrderBy("title")
List<Book> getAllBooks();

This equates to executing the following query:

SELECT b1_0.book_id, b1_0.author_id, b1_0.title 
FROM books b1_0 
ORDER BY b1_0.title

With the addition of the ORDER BY clause for consistent sorting.

If necessary, we can repeat the annotation to allow sorting by multiple fields:

@Find
@OrderBy("title")
@OrderBy("author$name")
List<Book> getAllBooks();

Note that we can also sort by fields on joined entities using exactly the same syntax as we saw earlier. Doing this generates a query like this:

SELECT b1_0.book_id, b1_0.author_id, b1_0.title 
FROM books b1_0 
  JOIN authors a1_0 ON a1_0.author_id = b1_0.author_id 
ORDER BY b1_0.title desc, a1_0.name

We can also specify the sort direction using the descending parameter. This defaults to false, but we can change this to indicate descending sorts:

@Find
@OrderBy(value = "title", descending = true)
List<Book> getAllBooks();

Unsurprisingly, we can mix all of this as needed, allowing for sorts on multiple fields in different directions.

We can also allow dynamic sorting by accepting a parameter of type Order<Book>.

@Find
List<Book> getAllBooks(Order<Book> sort);

This is generic over the entity that we’re working on. If we use the wrong generic type, Hibernate will throw an error at compile time. We can now call this and define the sorts at runtime:

List<Book> books = repository.getAllBooks(Order.by(
  Sort.asc("title"),
  Sort.desc("author.name")
));

Note that here we need to specify nested fields using dotted syntax instead of the “$” symbol, but otherwise it all works the same.

7.2. Pagination

In addition to sorting our results, we can also request specific pages of results. We can do this by adding a parameter of type PageRequest to our method:

@Find
@OrderBy("title")
List<Book> getBooksPage(PageRequest pageRequest);

We don’t technically need to enforce ordering on these queries, but it’s usually a good idea so results stay consistent across pages.

We can then call this specifying the desired page and page size:

List<Book> books = repository.getBooksPage(PageRequest.ofPage(1, 3, true));

Note that the page is 1-indexed, so this will return the first page of 3 records.

If we want to know more about the details of the page, we need to change our return type to Page<Book> instead of List<Book>:

@Find
@OrderBy("title")
Page<Book> getBooksPage(PageRequest pageRequest);

We now have access to not only the page contents, but also page details like the total number of elements and whether there are next and previous pages:

long total = books.totalElements();
if (books.hasNext()) {
    // Do something
}
if (books.hasPrevious()) {
    // Do something
}

Note that the third parameter to our PageRequest.ofPage() call tells Hibernate whether we want a total count of the elements across all pages. If we pass true, Hibernate executes an additional query to count matching elements. If we pass in false, Hibernate doesn’t execute this additional query, and the total number of elements won’t be available.

8. Summary

In this article, we’ve had a very brief look at using the @Find annotation in Hibernate. We’ve seen what it is, how it works, and how we can use it. Next time you need to generate repositories like this, why not give it a go?

As always, all the code from this article is available over on GitHub.

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

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

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

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)
guest
0 Comments
Oldest
Newest