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
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.
Using findById (Recommended)
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
public interface UserRepository extends JpaRepository<User, Long> {
// findById is inherited from CrudRepository — no need to declare it
}
Handling Optional Results
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>):
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:
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}
// 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:
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:
Order order = new Order();
order.setCustomer(userRepository.getReferenceById(customerId)); // No SELECT query
orderRepository.save(order); // INSERT with customer_id
Controller Example
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
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