Let's get started with a Microservice Architecture with Spring Cloud:
Detach and Attach Entity in Spring JpaRepository
Last updated: January 28, 2026
1. Introduction
Spring JPA simplifies querying in the persistence layer, eliminating the need to write any Hibernate-specific boilerplate code.
However, we require certain behavior that the JpaRepository classes do not include.
Such a scenario involves detaching and reattaching an entity to the persistent context. The purpose of detaching the entity can vary, such as to avoid automatic updates, to support a multi-transaction workflow, to improve performance, or to avoid the LazyInitializationException.
In this tutorial, we’ll learn how to detach and attach an entity in the Spring application.
2. Example Application
Let’s imagine we need to build an application that creates new users.
2.1. Define the Data Model
We’ll define the User entity with a few fields:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Column(unique = true)
private String email;
private boolean activated;
}
In the code above, we’ve included an activated field to store the active user.
2.2. Define the JpaRepository
Let’s implement the UserRepository interface by extending the JpaRepository class:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {}
Next, we’ll implement a few operations on the User entity.
2.3. Implement the DataService
To create and activate the user, we’ll implement two methods in the service class.
First, we’ll implement the createUser method in the UserDataService class:
@Service
public class UserDataService {
private final UserRepository userRepository;
@Transactional
public User createUser(String name, String email) {
User user = new User();
user.setName(name);
user.setEmail(email);
user.setActivated(false);
User savedUser = userRepository.save(user);
return savedUser;
}
}
In the code above, we create a new user in the inactive state.
Next, we’ll implement the activateUser method in the same UserDataService class:
@Transactional
public User activateUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found for Id" + id));
user.setActivated(true);
userRepository.save(user);
return user;
}
In the code above, we save the activated user only if one exists. Otherwise, we throw the RuntimeException exception.
2.4. Define an API Client
We’ll define the UserApiClient interface with the verify method:
public interface UserApiClient {
boolean verify(String email);
}
To demonstrate this, we don’t need to implement the interface above.
Next, we’ll integrate all the classes defined above to create the user.
2.5. Implement the User Registration
To orchestrate user registration, we’ll implement a separate class to handle creation, verification, and activation. After the successful external verification, the user will be active.
We’ll implement the handleRegistration method in the UserRegistrationService class:
public User handleRegistration(String name, String email) {
User user = userDataService.createUser(name, email);
if (userApiClient.verify(email)) {
user = userDataService.activateUser(user.getId());
}
return user;
}
As we’re using the User entity across services outside the transaction, it’s good practice to detach the entity from the persistent context to avoid automatic changes caused by Hibernate’s dirty-checking mechanism. Additionally, it’ll improve the performance of such a long-running process.
3. Detach and Attach the Entity
We can detach and reattach the entity using JPA’s support.
3.1. Detach the Entity
To detach an entity, we’ll use the EntityManager‘s detach method.
We’ll integrate the detach method in the existing UserRepository interface, as it’s a specific use case.
First, we’ll define a detach method in the DetachableRepository interface:
public interface DetachableRepository<T> {
void detach(T t);
}
Next, let’s implement the above method in the DetachableRepositoryImpl class:
public class DetachableRepositoryImpl<T> implements DetachableRepository<T> {
@PersistenceContext
private EntityManager entityManager;
@Override
public void detach(T entity) {
if(entity != null) {
entityManager.detach(entity);
}
}
}
In the code above, the EntityManager‘s detach method removes the entity from the persistent context. In essence, the detached entity will no longer be managed and persisted.
Alternatively, to detach all entities, we can either use the EntityManager‘s clear method or disable the Open-Session in View to implicitly detach entities after the transaction closes.
Then, we’ll extend the UserRepository interface with the DetachableRepository interface:
@Repository
public interface UserRepository extends JpaRepository<User, Long>, DetachableRepository<User> {}
Finally, we’ll include the detach method in the createUser method after the user is created:
userRepository.detach(savedUser);
Now, entity changes will not be automatically persisted, regardless of whether they are executed within or outside the transaction boundary.
We can invoke the EntityManager‘s detach method directly in the UserDataService‘s createUser method. However, it’ll reduce the flexibility and introduce coupling between the service and persistence layer.
3.2. Attach the Entity
As we’ve detached the entity, we’ll need to re-attach it to persist any changes.
To attach an entity, there are a few approaches. We can re-query and update the entity in a transaction, or explicitly save the same entity, or use the EntityManager‘s merge method. We’ll implement it by re-querying the same entity and automatically persisting the required changes in a transactional boundary.
We’ll keep the activateUser method with the transactional annotation and remove the redundant save method:
@Transactional
public User activateUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found for Id" + id));
user.setActivated(true);
return user;
}
We should note that the transactional annotation will attach and save the updated entity without explicitly requiring us to call the UserRepository‘s save method.
We can visualize the overall flow of the internal services:
Next, we’ll implement the integration tests for the services.
4. Test the Application
We’ll implement the tests for both the UserRepository and UserRegistration using the Spring Boot test support.
4.1. Testing the Repository
We’ll test a scenario where the user is saved, detached, and then updated:
@Test
void givenValidUserIsDetached_whenUserSaveIsCalled_AndUpdated_thenUserIsNotUpdated() {
User user = new User();
user.setName("test1");
user.setEmail("[email protected]");
user.setActivated(true);
userRepository.save(user);
userRepository.detach(user);
user.setName("test1_updated");
entityManager.flush();
Optional<User> savedUser = userRepository.findById(user.getId());
assertNotNull(savedUser);
assertTrue(savedUser.isPresent());
assertEquals("test1", savedUser.get().getName());
assertEquals("[email protected]", savedUser.get().getEmail());
assertTrue(savedUser.get().isActivated());
}
From the above test, we confirm that the updated change is not flushed by the EntityManager, as its being detached from the persistence context.
4.2. Testing the Service
We’ll test the UserRegistration class for the detach behavior.
First, we’ll implement a test where a valid user is registered:
@Test
void givenValidUser_whenUserIsRegistrationIsCalled_thenSaveActiveUser() {
Mockito.when(userApiClient.verify(any())).thenReturn(true);
User user = userRegistrationService.handleRegistration("test1", "[email protected]");
Optional<User> savedUser = userRepository.findById(user.getId());
assertNotNull(savedUser);
assertTrue(savedUser.isPresent());
assertEquals("test1", savedUser.get().getName());
assertEquals("[email protected]", savedUser.get().getEmail());
assertTrue(savedUser.get().isActivated());
}
We confirm that all the assertions pass, including that the user is activated after detaching.
Now, let’s test another scenario where the user’s email is invalid:
@Test
void givenInValidUser_whenUserIsRegistrationIsCalled_thenSaveInActiveUser() {
Mockito.when(userApiClient.verify(any())).thenReturn(false);
User user = userRegistrationService.handleRegistration("test2", "[email protected]");
Optional<User> savedUser = userRepository.findById(user.getId());
assertNotNull(savedUser);
assertTrue(savedUser.isPresent());
assertEquals("test2", savedUser.get().getName());
assertEquals("[email protected]", savedUser.get().getEmail());
assertFalse(savedUser.get().isActivated());
}
Based on the test above, we confirm that the invalid user is inactive.
Note that detaching the entity should not introduce side effects.
5. Conclusion
In this article, we’ve learned why and how to detach and attach an entity in the Spring JPA application. We’ve implemented the code using both EntityManager‘s detach method and transaction support. We’ve also tested the application across the JPA repository and service layers.
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.

















