JpaRepository
SpringBoot
findOne
Spring Data JPA
database-query

How should I use JpaRepository.findOne with SpringBoot?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Spring Data JPA 2.0+, findOne() was replaced by findById(), which returns Optional<T> instead of the entity directly. If you are using a recent version of Spring Boot (2.x or 3.x), use findById(id) and handle the Optional. The old findOne(ID id) method no longer exists on CrudRepository. If you need to find by a non-ID field, use findOne(Example<T>) from QueryByExampleExecutor or define a custom query method.

The Change: findOne to findById

java
1// Spring Data JPA 1.x (old — no longer available)
2User user = userRepository.findOne(1L);  // Returns null if not found
3
4// Spring Data JPA 2.0+ (current)
5Optional<User> user = userRepository.findById(1L);  // Returns Optional

The change was made to eliminate null-pointer bugs — Optional forces you to handle the "not found" case explicitly.

java
1@Service
2public class UserService {
3
4    @Autowired
5    private UserRepository userRepository;
6
7    public User getUser(Long id) {
8        return userRepository.findById(id)
9            .orElseThrow(() -> new EntityNotFoundException("User not found: " + id));
10    }
11
12    public User getUserOrNull(Long id) {
13        return userRepository.findById(id).orElse(null);
14    }
15
16    public User getUserWithDefault(Long id) {
17        return userRepository.findById(id)
18            .orElse(new User("default", "[email protected]"));
19    }
20}

Repository Interface

java
public interface UserRepository extends JpaRepository<User, Long> {
    // findById is inherited from CrudRepository — no need to declare it
}

Handling Optional Results

java
1Optional<User> optional = userRepository.findById(1L);
2
3// Check if present
4if (optional.isPresent()) {
5    User user = optional.get();
6    System.out.println(user.getName());
7}
8
9// orElseThrow — throw if not found
10User user = optional.orElseThrow(
11    () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found"));
12
13// orElse — provide default value
14User user = optional.orElse(new User());
15
16// orElseGet — lazy default (computed only if empty)
17User user = optional.orElseGet(() -> createDefaultUser());
18
19// ifPresent — run action only if present
20optional.ifPresent(user -> System.out.println(user.getName()));
21
22// map — transform the value
23String name = optional.map(User::getName).orElse("Unknown");

findOne with Example (Query by Example)

JpaRepository extends QueryByExampleExecutor, which has findOne(Example<T>):

java
1// Find one user matching an example
2User probe = new User();
3probe.setEmail("[email protected]");
4
5Example<User> example = Example.of(probe);
6Optional<User> user = userRepository.findOne(example);
7
8// With custom matching
9ExampleMatcher matcher = ExampleMatcher.matching()
10    .withIgnoreCase()
11    .withIgnoreNullValues()
12    .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);
13
14Example<User> example = Example.of(probe, matcher);
15Optional<User> user = userRepository.findOne(example);

This version of findOne takes an Example parameter, not an ID. It finds a single entity matching the provided probe values.

Custom Query Methods

For finding by non-ID fields, define custom repository methods:

java
1public interface UserRepository extends JpaRepository<User, Long> {
2
3    // Derived query — Spring generates SQL from method name
4    Optional<User> findByEmail(String email);
5
6    Optional<User> findByUsernameAndActive(String username, boolean active);
7
8    // JPQL query
9    @Query("SELECT u FROM User u WHERE u.email = :email")
10    Optional<User> findOneByEmail(@Param("email") String email);
11
12    // Native SQL query
13    @Query(value = "SELECT * FROM users WHERE email = ?1", nativeQuery = true)
14    Optional<User> findByEmailNative(String email);
15}
java
// Usage
Optional<User> user = userRepository.findByEmail("[email protected]");
user.ifPresent(u -> System.out.println(u.getName()));

getReferenceById (Lazy Loading)

If you only need a reference (proxy) without hitting the database:

java
1// Returns a proxy — does NOT query the database immediately
2User userRef = userRepository.getReferenceById(1L);
3
4// Database is queried only when you access a property
5String name = userRef.getName();  // SQL query executes here
6
7// Throws EntityNotFoundException if the entity doesn't exist
8// (only when a property is accessed, not when getReferenceById is called)

Use getReferenceById when you need to set a foreign key without loading the full entity:

java
Order order = new Order();
order.setCustomer(userRepository.getReferenceById(customerId));  // No SELECT query
orderRepository.save(order);  // INSERT with customer_id

Controller Example

java
1@RestController
2@RequestMapping("/api/users")
3public class UserController {
4
5    @Autowired
6    private UserRepository userRepository;
7
8    @GetMapping("/{id}")
9    public ResponseEntity<User> getUser(@PathVariable Long id) {
10        return userRepository.findById(id)
11            .map(ResponseEntity::ok)
12            .orElse(ResponseEntity.notFound().build());
13    }
14
15    @GetMapping("/by-email")
16    public ResponseEntity<User> getUserByEmail(@RequestParam String email) {
17        return userRepository.findByEmail(email)
18            .map(ResponseEntity::ok)
19            .orElse(ResponseEntity.notFound().build());
20    }
21}

Migration from findOne to findById

java
1// Before (Spring Data JPA 1.x)
2User user = repo.findOne(id);
3if (user == null) {
4    throw new NotFoundException("User not found");
5}
6
7// After (Spring Data JPA 2.0+)
8User user = repo.findById(id)
9    .orElseThrow(() -> new NotFoundException("User not found"));

Common Pitfalls

  • Calling findOne(id) on JpaRepository: The findOne(ID) method no longer exists in Spring Data JPA 2.0+. It was replaced by findById(ID) which returns Optional<T>. If your code calls findOne(1L), it will not compile.
  • Calling .get() without checking isPresent(): Optional.get() throws NoSuchElementException if the Optional is empty. Always use orElseThrow(), orElse(), or ifPresent() instead of raw .get().
  • Confusing findOne(Example) with findOne(ID): findOne(Example<T>) from QueryByExampleExecutor still exists and takes a probe object, not an ID. It throws IncorrectResultSizeDataAccessException if more than one result matches.
  • Using orElse() with a method call: orElse(createUser()) always calls createUser(), even when the Optional has a value. Use orElseGet(() -> createUser()) for lazy evaluation.
  • Not using getReferenceById for associations: Loading a full entity with findById just to set a foreign key wastes a database query. Use getReferenceById to get a proxy reference without querying.

Summary

  • Use findById(id) instead of the removed findOne(id) — it returns Optional<T>
  • Handle the Optional with orElseThrow(), orElse(), map(), or ifPresent()
  • Use findOne(Example<T>) to query by non-ID fields with Query by Example
  • Define custom repository methods (findByEmail) for typed, compile-safe queries
  • Use getReferenceById(id) when you only need a proxy reference, not the full entity

Course illustration
Course illustration

All Rights Reserved.