Detach & Attach Entities in Spring JPA Example
1. Introduction
Spring Data JPA simplifies persistence by abstracting away Hibernate-specific boilerplate code. The org.springframework.data.jpa.repository.JpaRepository interface provides CRUD operations for managed entities in the Persistence Context. The JPA jakarta.persistence.EntityManager exposes detach() and merge() operations to detach or reattach entities. In this example, I will show how to detach attach entities in a Spring Data JPA project.
2. Setup
In this step, I will create a Spring Boot Java17 project via Spring Initializer with PostgreSQL Driver, Lombok, and Spring Data JPA libraries.
2.1 Build Gradle File
No change to the generated build.gradle file.
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '4.0.2'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'org.jcg.zheng.demo'
version = '0.0.1-SNAPSHOT'
description = 'Demo project for Spring Boot'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-webmvc'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa-test'
testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
2.2 Spring Boot Application
No change to the generated JpaDetachAttachDemoApplication.java.
JpaDetachAttachDemoApplication.java
package org.jcg.zheng.demo.jpa_detach_attach_demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class JpaDetachAttachDemoApplication {
public static void main(String[] args) {
SpringApplication.run(JpaDetachAttachDemoApplication.class, args);
}
}
- Line 6:
@SpringBootApplicationmarks it as a Spring Boot application.
2.3 Application Setting
Update the generated application.yaml to enable the Spring data JPA logging.
application.yaml
spring:
application:
name: jpa-detach-attach-demo
datasource:
url: jdbc:postgresql://localhost:5432/postgres?currentSchema=marydb
username: postgres
password: zheng22
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
open-in-view: false
logging:
level:
org.springframework.transaction: Trace
org.hibernate.orm.jdbc.bind: Trace
- Line 13, 16: enable the
show-sqlto print out the SQL statements. - Line 17: disable the Spring Open-Session-In-View (OSIV).
- Line 21, 22: set the logging level to
TRACEfor both Spring transaction and parameter binding.
3. Java Entities
Spring Data JPA defines the following four entity states:
- Transient: the entity is not associated with persistence context
- Managed: the entity is attached to persistence context, changes are tracked. Spring
JpaRepositoryworks with managed entities inside a transaction. - Detached: the entity is previously managed, but no longer tracked.
- Removed: the entity is scheduled for deletion.
In this step, I will use the following database row transition annotations to track the entity’s state as outlined in step 3.1.
- @jakarta.persistence.PrePersist – about to persist an entity in the database.
- @jakarta.persistence.PostPersist – after persisting an entity in the database.
- @jakarta.persistence.PreUpdate – about to update an entity in the database.
- @jakarta.persistence.PostUpdate – after updating an entity in the database.
- @jakarta.persistence.PreRemove – about to detach an entity in the database.
- @jakarta.persistence.PostRemove – after detaching an entity in the database.
- @jakarta.persistence.PostLoad – after being loaded entities from the database.
| JPA Operations | Event |
| find, query | LoadEvent |
| merge | MergeEvent |
| detach, clear | EvicEvent |
| dirty checking | FlushEntityEvent |
3.1 Entity Life Cycle Event Listener
In this step, I will create a EntityLifeCycleEventTracker.java class that prints out messages for JPA Entity Life Cycle Events.
EntityLifeCycleEventTracker.java
package org.jcg.zheng.demo.jpa_detach_attach_demo.entity;
import jakarta.persistence.PostLoad;
import jakarta.persistence.PostPersist;
import jakarta.persistence.PostRemove;
import jakarta.persistence.PostUpdate;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreRemove;
import jakarta.persistence.PreUpdate;
/**
* JPA Life Cycle Annotations
*/
public class EntityLifeCycleEventTracker {
@PostLoad
public void postLoad(Object entity) {
System.out.println("on_postLoad: " + entity);
}
@PostPersist
public void postPersist(Object entity) {
System.out.println("on_PostPersist: " + entity);
}
@PostRemove
public void postRemove(Object entity) {
System.out.println("on_PostRemove: " + entity);
}
@PostUpdate
public void postUpdate(Object entity) {
System.out.println("on_PostUpdate: " + entity);
}
@PrePersist
public void prePersist(Object entity) {
System.out.println(" on_PrePersist: " + entity);
}
@PreRemove
public void preRemove(Object entity) {
System.out.println("on_preRemove: " + entity);
}
@PreUpdate
public void preUpdate(Object entity) {
System.out.println("on_PreUpdate: " + entity);
}
}
- Line 18, 23, 28, 33, 38, 43, 48: print out the messages and will be used when executing the unit tests.
3.2 User Entity
In this step, I will create a User.java class that annotates with @Entity and maps to “MZ_USER_TABLE“.
User.java
package org.jcg.zheng.demo.jpa_detach_attach_demo.entity;
import java.util.ArrayList;
import java.util.List;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.ToString;
@Data
@Entity
@EntityListeners(EntityLifeCycleEventTracker.class)
@Table(name = "MZ_USER_TABLE")
public class User {
private String email;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String status;
@OneToMany(mappedBy="user")
@ToString.Exclude
private List<UserOrder> orders = new ArrayList<>();
}
- Line 18:
@EntityListeners(EntityLifeCycleEventTracker.class)is used to print out the Entity life cycle events. - Line 29:
@OneToManydefaults toFetchType.LAZY. Will usedetach()properly to avoid theLazyInitializationExceptionin step 5.1 and 6.1.
3.3 User Repository
In this step, I will create a UserRepository.java interface that annotates with @Repository and extends from JpaRepository.
Note: JpaRepository automatically manages entity state and relies on transaction boundaries. However, it does not expose detach(), merge(), etc methods.
UserRepository.java
package org.jcg.zheng.demo.jpa_detach_attach_demo.repo;
import org.jcg.zheng.demo.jpa_detach_attach_demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}
- Line 7:
@Repositoryis used.
3.4 User Order Entity
In this step, I will create a UserOrder.java class that annotates with @Entity and maps to “MZ_USER_ORDER“.
UserOrder.java
package org.jcg.zheng.demo.jpa_detach_attach_demo.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.Data;
@Entity
@Table(name="MZ_USER_ORDER")
@EntityListeners(EntityLifeCycleEventTracker.class)
@Data
public class UserOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@ManyToOne
@JoinColumn(name="user_id")
private User user;
private String name;
private double price;
}
- Line 15:
@EntityListeners(DemoEntityListener.class)is used to print out the Entity life cycle events.
3.5 User Order Repository
In this step, I will create a UserOrderRepository.java interface that annotates with @Repository and extends from JpaRepository.
UserOrderRepository.java
package org.jcg.zheng.demo.jpa_detach_attach_demo.repo;
import org.jcg.zheng.demo.jpa_detach_attach_demo.entity.UserOrder;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserOrderRepository extends JpaRepository<UserOrder, Integer> {
}
- Line 7:
@Repositoryis used.
4. User Service
4.1 UserService
In this step, I will create a UserService.java class that wraps with UserRepository and UserOrderRepository to create, update, activate, get, and delete a User object.
UserService.java
package org.jcg.zheng.demo.jpa_detach_attach_demo.service;
import java.util.Optional;
import org.jcg.zheng.demo.jpa_detach_attach_demo.entity.User;
import org.jcg.zheng.demo.jpa_detach_attach_demo.entity.UserOrder;
import org.jcg.zheng.demo.jpa_detach_attach_demo.repo.UserOrderRepository;
import org.jcg.zheng.demo.jpa_detach_attach_demo.repo.UserRepository;
import org.springframework.stereotype.Service;
import jakarta.transaction.Transactional;
@Service
@Transactional
public class UserService {
private final UserRepository userRepo;
private final UserOrderRepository userOrderRepo;
public UserService(final UserRepository userRepo, final UserOrderRepository userOrderRepo) {
super();
this.userRepo = userRepo;
this.userOrderRepo = userOrderRepo;
}
public User activeateUser(final User user) {
user.setStatus("Active");
return userRepo.save(user);
}
public User createUser(final String name, final String email) {
User user = new User();
user.setEmail(email);
user.setName(name);
return userRepo.save(user);
}
public void deleteUser(final Integer userId) {
userRepo.deleteById(userId);
}
public User getUserAfterLazyLoad(final Integer userId) {
Optional found = userRepo.findById(userId);
if (found.isPresent()) {
found.get().getOrders().size(); // trigger lazy load orders
return found.get();
}
return null;
}
public User addOrder(final User user, double price, final String orderName) {
UserOrder order = new UserOrder();
order.setName(orderName);
order.setPrice(price);
order.setUser(user);
userOrderRepo.save(order);
user.getOrders().add(order);
return userRepo.save(user);
}
}
- Line 14:
@Transactionalis needed. - Line 45: call the
getOrders()trigger the Hibernate to lazy load the relatedUserOrderentities for theUserobject.
4.2 UserServiceTest
In this step, I will create a UserServiceTest.java class that test these methods.
UserDataServiceTest.java
package org.jcg.zheng.demo.jpa_detach_attach_demo.service;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.jcg.zheng.demo.jpa_detach_attach_demo.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class UseraServiceTest {
@Autowired
private UserService testClass;
@Test
void test_crudUser() {
User newUser = testClass.createUser("Zheng", "zheng11111@gmail.com");
assertNotNull(newUser.getId());
User activatedUser = testClass.activeateUser(newUser);
assertEquals("Active", activatedUser.getStatus());
testClass.deleteUser(newUser.getId());
}
@Test
void test_addUserOrder() {
User newUser = testClass.createUser("Zheng", "zheng11111@gmail.com");
assertNotNull(newUser.getId());
User updatedUser = testClass.addOrder(newUser, 10.10, "order1");
updatedUser = testClass.addOrder(newUser, 20.20, "order2");
//getOrders() will get org.hibernate.LazyInitializationException: Cannot lazily initialize collection of role 'org.jcg.zheng.demo.jpa_detach_attach_demo.entity.User.orders' with key '1' (no session)
//updatedUser.getOrders().size();
}
@Test
void test_getUser() {
User user = testClass.getUserAfterLazyLoad(1);
//this is ok
user.getOrders().size();
}
}
- Line 36, 39: call the
getOrders()will throworg.hibernate.LazyInitializationExceptionat line 39 if not commented out. - Line 44, 47: call the
getOrders()is fine asgetUserAfterLazyLoad()already loaded the relatedUserOrderentities for theUserobject.
4.3 UserServiceTest Output
In this step, I will execute the UserServiceTest and capture the output.
UserDataServiceTest Output
10:32:57.013 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UseraServiceTest]: UseraServiceTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
10:32:57.330 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @SpringBootConfiguration org.jcg.zheng.demo.jpa_detach_attach_demo.JpaDetachAttachDemoApplication for test class org.jcg.zheng.demo.jpa_detach_attach_demo.service.UseraServiceTest
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v4.0.2)
2026-01-31T10:32:58.134-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] o.j.z.d.j.service.UseraServiceTest : Starting UseraServiceTest using Java 21.0.8 with PID 1952 (started by zzhen in C:\MaryZheng\workspace\jpa-detach-attach-demo)
2026-01-31T10:32:58.137-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] o.j.z.d.j.service.UseraServiceTest : No active profile set, falling back to 1 default profile: "default"
2026-01-31T10:32:59.105-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2026-01-31T10:32:59.191-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 71 ms. Found 2 JPA repository interfaces.
2026-01-31T10:32:59.760-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jpa : HHH008540: Processing PersistenceUnitInfo [name: default]
2026-01-31T10:32:59.852-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.core : HHH000001: Hibernate ORM core version 7.2.1.Final
2026-01-31T10:33:00.646-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
2026-01-31T10:33:00.710-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2026-01-31T10:33:01.024-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@32d1d6c5
2026-01-31T10:33:01.026-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2026-01-31T10:33:01.134-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.connections.pooling : HHH10001005: Database info:
Database JDBC URL [jdbc:postgresql://localhost:5432/postgres?currentSchema=marydb]
Database driver: PostgreSQL JDBC Driver
Database dialect: PostgreSQLDialect
Database version: 18.0
Default catalog/schema: postgres/marydb
Autocommit mode: undefined/unknown
Isolation level: READ_COMMITTED [default READ_COMMITTED]
JDBC fetch size: none
Pool: DataSourceConnectionProvider
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-01-31T10:33:02.513-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.core : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2026-01-31T10:33:02.601-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2026-01-31T10:33:02.681-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.mergeAfterClearUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:33:02.744-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.deattachAtachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:33:02.744-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.reattachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:33:02.744-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.getDetachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:33:02.745-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.attachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:33:02.882-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] o.s.d.j.r.query.QueryEnhancerFactories : Hibernate is in classpath; If applicable, HQL parser will be used.
2026-01-31T10:33:03.057-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.activeateUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:33:03.061-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.getUserAfterLazyLoad' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:33:03.061-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.addOrder' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:33:03.061-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.deleteUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:33:03.062-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.createUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:33:03.076-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.readOnlyUserviaHint' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:33:03.079-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.readOnlyUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
2026-01-31T10:33:03.079-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.detachMergeUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:33:03.080-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.detachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:33:03.826-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ main] o.j.z.d.j.service.UseraServiceTest : Started UseraServiceTest in 6.22 seconds (process running for 32.082)
Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. Please add Mockito as an agent to your build as described in Mockito's documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#0.3
WARNING: A Java agent has been loaded dynamically (C:\Users\zzhen\.gradle\caches\modules-2\files-2.1\net.bytebuddy\byte-buddy-agent\1.17.8\f09415827a71be7ed621c7bd02550678f28bc81c\byte-buddy-agent-1.17.8.jar)
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2026-01-31T10:33:04.384-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.getUserAfterLazyLoad]
2026-01-31T10:33:04.388-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findById]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:33:04.413-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
2026-01-31T10:33:04.480-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findById]
Hibernate:
select
o1_0.user_id,
o1_0.id,
o1_0.name,
o1_0.price
from
mz_user_order o1_0
where
o1_0.user_id=?
2026-01-31T10:33:04.490-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: UserOrder(id=1, user=User(email=test111@test.com, id=1, name=todo, status=null), name=order1, price=10.1)
on_postLoad: UserOrder(id=2, user=User(email=test111@test.com, id=1, name=todo, status=null), name=order2, price=20.2)
2026-01-31T10:33:04.502-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.getUserAfterLazyLoad]
2026-01-31T10:33:04.534-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.createUser]
2026-01-31T10:33:04.535-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
on_PrePersist: User(email=zheng11111@gmail.com, id=null, name=Zheng, status=null)
Hibernate:
insert
into
mz_user_table
(email, name, status)
values
(?, ?, ?)
2026-01-31T10:33:04.556-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:VARCHAR) <- [zheng11111@gmail.com]
2026-01-31T10:33:04.556-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (2:VARCHAR) <- [Zheng]
2026-01-31T10:33:04.557-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (3:VARCHAR) <- [null]
on_PostPersist: User(email=zheng11111@gmail.com, id=11, name=Zheng, status=null)
2026-01-31T10:33:04.567-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2026-01-31T10:33:04.567-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.createUser]
2026-01-31T10:33:04.579-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.addOrder]
2026-01-31T10:33:04.585-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
on_PrePersist: UserOrder(id=null, user=User(email=zheng11111@gmail.com, id=11, name=Zheng, status=null), name=order1, price=10.1)
Hibernate:
insert
into
mz_user_order
(name, price, user_id)
values
(?, ?, ?)
2026-01-31T10:33:04.587-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:VARCHAR) <- [order1]
2026-01-31T10:33:04.588-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (2:DOUBLE) <- [10.1]
2026-01-31T10:33:04.588-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (3:INTEGER) <- [11]
on_PostPersist: UserOrder(id=11, user=User(email=zheng11111@gmail.com, id=11, name=Zheng, status=null), name=order1, price=10.1)
2026-01-31T10:33:04.592-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2026-01-31T10:33:04.592-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:33:04.597-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [11]
on_postLoad: User(email=zheng11111@gmail.com, id=11, name=Zheng, status=null)
2026-01-31T10:33:04.603-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2026-01-31T10:33:04.606-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.addOrder]
2026-01-31T10:33:04.614-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.addOrder]
2026-01-31T10:33:04.614-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
on_PrePersist: UserOrder(id=null, user=User(email=zheng11111@gmail.com, id=11, name=Zheng, status=null), name=order2, price=20.2)
Hibernate:
insert
into
mz_user_order
(name, price, user_id)
values
(?, ?, ?)
2026-01-31T10:33:04.614-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:VARCHAR) <- [order2]
2026-01-31T10:33:04.615-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (2:DOUBLE) <- [20.2]
2026-01-31T10:33:04.615-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (3:INTEGER) <- [11]
on_PostPersist: UserOrder(id=12, user=User(email=zheng11111@gmail.com, id=11, name=Zheng, status=null), name=order2, price=20.2)
2026-01-31T10:33:04.616-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2026-01-31T10:33:04.616-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:33:04.616-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [11]
on_postLoad: User(email=zheng11111@gmail.com, id=11, name=Zheng, status=null)
Hibernate:
select
uo1_0.id,
uo1_0.name,
uo1_0.price,
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_order uo1_0
left join
mz_user_table u1_0
on u1_0.id=uo1_0.user_id
where
uo1_0.id=?
2026-01-31T10:33:04.617-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [11]
on_postLoad: UserOrder(id=11, user=User(email=zheng11111@gmail.com, id=11, name=Zheng, status=null), name=order1, price=10.1)
2026-01-31T10:33:04.619-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2026-01-31T10:33:04.619-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.addOrder]
2026-01-31T10:33:04.626-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.createUser]
2026-01-31T10:33:04.627-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
on_PrePersist: User(email=zheng11111@gmail.com, id=null, name=Zheng, status=null)
Hibernate:
insert
into
mz_user_table
(email, name, status)
values
(?, ?, ?)
2026-01-31T10:33:04.629-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:VARCHAR) <- [zheng11111@gmail.com]
2026-01-31T10:33:04.629-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (2:VARCHAR) <- [Zheng]
2026-01-31T10:33:04.629-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (3:VARCHAR) <- [null]
on_PostPersist: User(email=zheng11111@gmail.com, id=12, name=Zheng, status=null)
2026-01-31T10:33:04.630-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2026-01-31T10:33:04.630-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.createUser]
2026-01-31T10:33:04.631-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.activeateUser]
2026-01-31T10:33:04.632-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:33:04.632-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [12]
on_postLoad: User(email=zheng11111@gmail.com, id=12, name=Zheng, status=null)
2026-01-31T10:33:04.633-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2026-01-31T10:33:04.633-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.activeateUser]
on_PreUpdate: User(email=zheng11111@gmail.com, id=12, name=Zheng, status=Active)
Hibernate:
update
mz_user_table
set
email=?,
name=?,
status=?
where
id=?
2026-01-31T10:33:04.641-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:VARCHAR) <- [zheng11111@gmail.com]
2026-01-31T10:33:04.641-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (2:VARCHAR) <- [Zheng]
2026-01-31T10:33:04.641-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (3:VARCHAR) <- [Active]
2026-01-31T10:33:04.641-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (4:INTEGER) <- [12]
on_PostUpdate: User(email=zheng11111@gmail.com, id=12, name=Zheng, status=Active)
2026-01-31T10:33:04.655-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.deleteUser]
2026-01-31T10:33:04.656-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.deleteById]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:33:04.657-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [12]
on_postLoad: User(email=zheng11111@gmail.com, id=12, name=Zheng, status=Active)
on_preRemove: User(email=zheng11111@gmail.com, id=12, name=Zheng, status=Active)
2026-01-31T10:33:04.662-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.deleteById]
2026-01-31T10:33:04.662-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.deleteUser]
Hibernate:
delete
from
mz_user_table
where
id=?
2026-01-31T10:33:04.664-06:00 TRACE 1952 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [12]
on_PostRemove: User(email=zheng11111@gmail.com, id=12, name=Zheng, status=Active)
2026-01-31T10:33:04.690-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2026-01-31T10:33:04.693-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2026-01-31T10:33:04.698-06:00 INFO 1952 --- [jpa-detach-attach-demo] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
- All the highlighted messages print the entity life cycle events.
Uncommented the line 39 and run the test_addUserOrder and capture the output.
UserDataServiceTest test_addUserOrder Output
org.hibernate.LazyInitializationException: Cannot lazily initialize collection of role 'org.jcg.zheng.demo.jpa_detach_attach_demo.entity.User.orders' with key '13' (no session) at org.hibernate.collection.spi.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:649) at org.hibernate.collection.spi.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:234) at org.hibernate.collection.spi.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:161) at org.hibernate.collection.spi.PersistentBag.size(PersistentBag.java:403) at org.jcg.zheng.demo.jpa_detach_attach_demo.service.UseraServiceTest.test_addUserOrder(UseraServiceTest.java:39)
- Line 1: caught the
LazyInitializationExceptionwhen accessing lazy loadordersoutside of transactions.
5. Demo Attach Detach Service
Spring Data JPA allows injection of EntityManager using the @PersistenceContext annotation. The EntityManager is transaction-scoped and integrated with Spring’s @Transactional.
5.1 DemoAttachDetachService
In this step, I will create a DemoAttachDetachService.java class that uses EntityManager to find, detach, clear, and merge a User object.
DemoAttachDetachService.java
package org.jcg.zheng.demo.jpa_detach_attach_demo.service;
import org.jcg.zheng.demo.jpa_detach_attach_demo.entity.User;
import org.springframework.stereotype.Service;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
@Service
@Transactional
public class DemoAttachDetachService {
@PersistenceContext
private final EntityManager em;
public DemoAttachDetachService(final EntityManager em) {
super();
this.em = em;
}
public User attachUser(final Integer userId, final String name) {
User u = em.find(User.class, userId);
u.setName(name);
User managed = em.merge(u);
System.out.println("find object is same as merged object, u == managed:" + (u == managed));
return managed;
}
public User deattachAtachUser(final Integer userId, final String name) {
User u = em.find(User.class, userId);
em.detach(u);
u.setName(name);
User managed = em.merge(u);
System.out.println("detached object is different from merged object, u == managed:" + (u == managed));
return managed;
}
public User getDetachUser(final Integer userid) {
User found = em.find(User.class, userid);
em.detach(found);
return found;
}
public User mergeAfterClearUser(final Integer userid) {
User u = em.find(User.class, userid);
em.clear();
User managed = em.merge(u);
System.out.println("cleared object is different from merged object, u == managed" + (u == managed));
return managed;
}
}
- Line 11:
@Transactionalis needed. - Line 25, 26: calling
merge()withoutdetach()will not change the entity objects. - Line 32, 34, 35, 42:
detach()method removes the entity from the persistence context, so JPA stops tracking changes. Afterdetach(),merge()will return a new entity object. - Line 49, 52:
clear()detaches all managed entities. Afterclear(),merge()will return a new entity object.
5.2 TestDemoAttachDetachService
In this step, I will create a TestDemoAttachDetachService.java class that test the methods.
DemoAttachDetachServiceTest.java
package org.jcg.zheng.demo.jpa_detach_attach_demo.service;
import static org.junit.jupiter.api.Assertions.*;
import org.jcg.zheng.demo.jpa_detach_attach_demo.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DemoAttachDetachServiceTest {
@Autowired
private DemoAttachDetachService testClass;
@Test
void test_attachUser() {
User user = testClass.attachUser(1, "newName");
assertEquals("newName", user.getName());
}
@Test
void test_deattachAtachUser() {
User user = testClass.deattachAtachUser(1, "todo");
assertEquals("todo", user.getName());
}
@Test
void test_getDetachUser() {
User found = testClass.getDetachUser(1);
assertEquals(1, found.getId().intValue());
}
@Test
void test_mergeAfterClearUser() {
User firstUser = testClass.getDetachUser(1);
User user = testClass.mergeAfterClearUser(1);
assertEquals(firstUser.getName(), user.getName());
}
}
5.3 TestDemoAttachDetachService Output
In this step, I will run the test in the TestDemoAttachDetachService.java class and capture the output.
DemoAttachDetachServiceTest Output
10:47:31.392 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class [org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachServiceTest]: DemoAttachDetachServiceTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
10:47:31.538 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @SpringBootConfiguration org.jcg.zheng.demo.jpa_detach_attach_demo.JpaDetachAttachDemoApplication for test class org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachServiceTest
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v4.0.2)
2026-01-31T10:47:31.868-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] o.j.z.d.j.s.DemoAttachDetachServiceTest : Starting DemoAttachDetachServiceTest using Java 21.0.8 with PID 20636 (started by zzhen in C:\MaryZheng\workspace\jpa-detach-attach-demo)
2026-01-31T10:47:31.869-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] o.j.z.d.j.s.DemoAttachDetachServiceTest : No active profile set, falling back to 1 default profile: "default"
2026-01-31T10:47:32.367-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2026-01-31T10:47:32.411-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 36 ms. Found 2 JPA repository interfaces.
2026-01-31T10:47:32.693-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jpa : HHH008540: Processing PersistenceUnitInfo [name: default]
2026-01-31T10:47:32.749-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.core : HHH000001: Hibernate ORM core version 7.2.1.Final
2026-01-31T10:47:33.234-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
2026-01-31T10:47:33.252-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2026-01-31T10:47:33.388-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@59a2bed1
2026-01-31T10:47:33.389-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2026-01-31T10:47:33.456-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.connections.pooling : HHH10001005: Database info:
Database JDBC URL [jdbc:postgresql://localhost:5432/postgres?currentSchema=marydb]
Database driver: PostgreSQL JDBC Driver
Database dialect: PostgreSQLDialect
Database version: 18.0
Default catalog/schema: postgres/marydb
Autocommit mode: undefined/unknown
Isolation level: READ_COMMITTED [default READ_COMMITTED]
JDBC fetch size: none
Pool: DataSourceConnectionProvider
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-01-31T10:47:34.112-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.core : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2026-01-31T10:47:34.164-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2026-01-31T10:47:34.203-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.attachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:47:34.255-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.deattachAtachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:47:34.255-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.getDetachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:47:34.255-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.mergeAfterClearUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:47:34.255-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.reattachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:47:34.330-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] o.s.d.j.r.query.QueryEnhancerFactories : Hibernate is in classpath; If applicable, HQL parser will be used.
2026-01-31T10:47:34.438-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.addOrder' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:47:34.439-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.deleteUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:47:34.439-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.createUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:47:34.440-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.getUserAfterLazyLoad' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:47:34.440-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.activeateUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:47:34.447-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.detachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:47:34.448-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.readOnlyUserviaHint' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:47:34.448-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.detachMergeUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:47:34.450-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.readOnlyUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
2026-01-31T10:47:34.831-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ main] o.j.z.d.j.s.DemoAttachDetachServiceTest : Started DemoAttachDetachServiceTest in 3.193 seconds (process running for 4.1)
Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. Please add Mockito as an agent to your build as described in Mockito's documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#0.3
WARNING: A Java agent has been loaded dynamically (C:\Users\zzhen\.gradle\caches\modules-2\files-2.1\net.bytebuddy\byte-buddy-agent\1.17.8\f09415827a71be7ed621c7bd02550678f28bc81c\byte-buddy-agent-1.17.8.jar)
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2026-01-31T10:47:35.125-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.attachUser]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:47:35.137-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
find object is same as merged object, u == managed:true
2026-01-31T10:47:35.157-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.attachUser]
on_PreUpdate: User(email=test111@test.com, id=1, name=newName, status=null)
Hibernate:
update
mz_user_table
set
email=?,
name=?,
status=?
where
id=?
2026-01-31T10:47:35.173-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:VARCHAR) <- [test111@test.com]
2026-01-31T10:47:35.173-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (2:VARCHAR) <- [newName]
2026-01-31T10:47:35.173-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (3:VARCHAR) <- [null]
2026-01-31T10:47:35.173-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (4:INTEGER) <- [1]
on_PostUpdate: User(email=test111@test.com, id=1, name=newName, status=null)
2026-01-31T10:47:35.193-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.deattachAtachUser]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:47:35.193-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=newName, status=null)
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:47:35.197-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=newName, status=null)
detached object is different from merged object, u == managed:false
2026-01-31T10:47:35.199-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.deattachAtachUser]
on_PreUpdate: User(email=test111@test.com, id=1, name=todo, status=null)
Hibernate:
update
mz_user_table
set
email=?,
name=?,
status=?
where
id=?
2026-01-31T10:47:35.199-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:VARCHAR) <- [test111@test.com]
2026-01-31T10:47:35.200-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (2:VARCHAR) <- [todo]
2026-01-31T10:47:35.200-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (3:VARCHAR) <- [null]
2026-01-31T10:47:35.200-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (4:INTEGER) <- [1]
on_PostUpdate: User(email=test111@test.com, id=1, name=todo, status=null)
2026-01-31T10:47:35.269-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.getDetachUser]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:47:35.269-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
2026-01-31T10:47:35.270-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.getDetachUser]
2026-01-31T10:47:35.271-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.mergeAfterClearUser]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:47:35.271-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:47:35.273-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
cleared object is different from merged object, u == managedfalse
2026-01-31T10:47:35.274-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.mergeAfterClearUser]
2026-01-31T10:47:35.278-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.getDetachUser]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:47:35.278-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
2026-01-31T10:47:35.279-06:00 TRACE 20636 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.getDetachUser]
2026-01-31T10:47:35.287-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2026-01-31T10:47:35.294-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2026-01-31T10:47:35.300-06:00 INFO 20636 --- [jpa-detach-attach-demo] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
- Line 72, 114: print out the entity comparation after
merge()anddetach()methods.
6. User Registration Service
6.1 UserRegistrationService
In this step, I will create a UserRegistrationService.java class that demonstrates properly way to use the detach() and merge() methods.
UserRegistrationService.java
package org.jcg.zheng.demo.jpa_detach_attach_demo.service;
import org.jcg.zheng.demo.jpa_detach_attach_demo.entity.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
@Service
public class UserRegistrationService {
@PersistenceContext
private final EntityManager em;
private final UserService userDataService;
public UserRegistrationService(final UserService userDataService, final EntityManager em) {
super();
this.em = em;
this.userDataService = userDataService;
}
@Transactional
public User detachMergeUser(final Integer userId, final String email) {
User found = em.find(User.class, userId);
em.detach(found);
found.setEmail(email); // the DB value will be updated as the found is merged later;
em.merge(found);
return found;
}
@Transactional
public User detachUser(final Integer userId, final String email) {
User found = em.find(User.class, userId);
found.getOrders().size(); //trigger lazy load
em.detach(found);
found.setEmail(email); // the DB value is not updated as the found is detached;
return found;
}
public User getUser(final Integer userId) {
return userDataService.getUserAfterLazyLoad(userId);
}
@Transactional(readOnly = true)
public User readOnlyUser(final Integer userId, final String email) {
User found = em.find(User.class, userId);
found.setEmail(email); // the DB value is not updated as the found is read-only;
return found;
}
@Transactional
public User readOnlyUserviaHint(final Integer userId, final String email) {
User found = em.createQuery("select u from User u where u.id = :id", User.class)
.setParameter("id", userId)
.setHint("org.hibernate.readOnly", true).getSingleResult();
found.setEmail(email); // the DB value is not updated as the found is read-only;
return found;
}
public User registerUserWith2Steps(final String name, final String email) {
User newUser = userDataService.createUser(name, email);
em.detach(newUser);
return userDataService.activeateUser(newUser);
}
}
- Line 27, 28, 29: the entity will be persisted to database as
merge()is called afterdetach(). - Line 36, 37, 38: the entity will NOT be tracked as
detach()is called after triggering lazy loading. ThesetEmail()in line 38 is a common pitfall in Hibernate. - Line 46, 49: the entity will NOT be persisted to the database due to
readOnly=true. - Line 57, 59: the entity will NOT be persisted to the database due to
readOnlyhint istrue. - Line 65: calling
detach()between two transactions to reduce the dirty-checking.
6.2 TestUserRegistrationService
In this step, I will create a TestUserRegistrationService.java to test the methods.
UserRegistrationServiceTest.java
package org.jcg.zheng.demo.jpa_detach_attach_demo.service;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.jcg.zheng.demo.jpa_detach_attach_demo.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class UserRegistrationServiceTest {
@Autowired
private UserRegistrationService testClass;
@Test
void test_registerUser() {
User newUser = testClass.registerUserWith2Steps("Mary", "mary1@gmail.com");
assertNotNull(newUser.getId());
assertEquals("Active", newUser.getStatus());
}
@Test
void test_readOnlyUserviaHint() {
String newEmail = "test11@test.com";
User updatedUser = testClass.readOnlyUserviaHint(1, newEmail);
assertEquals(newEmail, updatedUser.getEmail());
User dbUser = testClass.getUser(1);
assertNotEquals(newEmail, dbUser.getEmail());
}
@Test
void test_readOnlyUser() {
String newEmail = "test11@test.com";
User updatedUser = testClass.readOnlyUser(1, newEmail);
assertEquals(newEmail, updatedUser.getEmail());
User dbUser = testClass.getUser(1);
assertNotEquals(newEmail, dbUser.getEmail());
}
@Test
void test_detachUser() {
String newEmail = "test11@test.com";
User updatedUser = testClass.detachUser(1, newEmail);
assertEquals(newEmail, updatedUser.getEmail());
User dbUser = testClass.getUser(1);
assertNotEquals(newEmail, dbUser.getEmail());
assertTrue(updatedUser.getOrders().size() > 0);
}
@Test
void test_detachMergeUser() {
String newEmail = "test111@test.com";
User updatedUser = testClass.detachMergeUser(1, newEmail);
assertEquals(newEmail, updatedUser.getEmail());
User dbUser = testClass.getUser(1);
assertEquals(newEmail, dbUser.getEmail());
}
}
- Line 33, 43, 53: it shows the changes made after
detach()no longer tracked. This is the common Hibernate pitfall when not coding properly.
6.3 TestUserRegistrationService Output
In this step, I will run Junit test for TestUserRegistrationService.java and capture the output.
UserRegistrationServiceTest Test Output
10:49:37.025 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationServiceTest]: UserRegistrationServiceTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
10:49:37.118 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @SpringBootConfiguration org.jcg.zheng.demo.jpa_detach_attach_demo.JpaDetachAttachDemoApplication for test class org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationServiceTest
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v4.0.2)
2026-01-31T10:49:37.459-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] o.j.z.d.j.s.UserRegistrationServiceTest : Starting UserRegistrationServiceTest using Java 21.0.8 with PID 1640 (started by zzhen in C:\MaryZheng\workspace\jpa-detach-attach-demo)
2026-01-31T10:49:37.460-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] o.j.z.d.j.s.UserRegistrationServiceTest : No active profile set, falling back to 1 default profile: "default"
2026-01-31T10:49:37.975-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2026-01-31T10:49:38.017-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 33 ms. Found 2 JPA repository interfaces.
2026-01-31T10:49:38.337-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jpa : HHH008540: Processing PersistenceUnitInfo [name: default]
2026-01-31T10:49:38.379-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.core : HHH000001: Hibernate ORM core version 7.2.1.Final
2026-01-31T10:49:38.840-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
2026-01-31T10:49:38.860-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2026-01-31T10:49:39.002-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@1939a394
2026-01-31T10:49:39.003-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2026-01-31T10:49:39.063-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.connections.pooling : HHH10001005: Database info:
Database JDBC URL [jdbc:postgresql://localhost:5432/postgres?currentSchema=marydb]
Database driver: PostgreSQL JDBC Driver
Database dialect: PostgreSQLDialect
Database version: 18.0
Default catalog/schema: postgres/marydb
Autocommit mode: undefined/unknown
Isolation level: READ_COMMITTED [default READ_COMMITTED]
JDBC fetch size: none
Pool: DataSourceConnectionProvider
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-01-31T10:49:39.790-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.core : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2026-01-31T10:49:39.843-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2026-01-31T10:49:39.869-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.attachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:49:39.919-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.getDetachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:49:39.920-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.reattachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:49:39.920-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.deattachAtachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:49:39.920-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.DemoAttachDetachService.mergeAfterClearUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:49:39.998-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] o.s.d.j.r.query.QueryEnhancerFactories : Hibernate is in classpath; If applicable, HQL parser will be used.
2026-01-31T10:49:40.089-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.activeateUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:49:40.090-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.getUserAfterLazyLoad' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:49:40.091-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.createUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:49:40.091-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.deleteUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:49:40.091-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.addOrder' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:49:40.099-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.readOnlyUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
2026-01-31T10:49:40.099-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.detachMergeUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:49:40.099-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.detachUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:49:40.099-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.readOnlyUserviaHint' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2026-01-31T10:49:40.510-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ main] o.j.z.d.j.s.UserRegistrationServiceTest : Started UserRegistrationServiceTest in 3.284 seconds (process running for 4.159)
Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. Please add Mockito as an agent to your build as described in Mockito's documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#0.3
WARNING: A Java agent has been loaded dynamically (C:\Users\zzhen\.gradle\caches\modules-2\files-2.1\net.bytebuddy\byte-buddy-agent\1.17.8\f09415827a71be7ed621c7bd02550678f28bc81c\byte-buddy-agent-1.17.8.jar)
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2026-01-31T10:49:40.828-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.detachMergeUser]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:49:40.840-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:49:40.862-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
2026-01-31T10:49:40.864-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.detachMergeUser]
2026-01-31T10:49:40.876-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.getUserAfterLazyLoad]
2026-01-31T10:49:40.878-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findById]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:49:40.881-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
2026-01-31T10:49:40.884-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findById]
Hibernate:
select
o1_0.user_id,
o1_0.id,
o1_0.name,
o1_0.price
from
mz_user_order o1_0
where
o1_0.user_id=?
2026-01-31T10:49:40.886-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: UserOrder(id=1, user=User(email=test111@test.com, id=1, name=todo, status=null), name=order1, price=10.1)
on_postLoad: UserOrder(id=2, user=User(email=test111@test.com, id=1, name=todo, status=null), name=order2, price=20.2)
2026-01-31T10:49:40.894-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.getUserAfterLazyLoad]
2026-01-31T10:49:40.904-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.readOnlyUser]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:49:40.905-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
2026-01-31T10:49:40.905-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.readOnlyUser]
2026-01-31T10:49:40.906-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.getUserAfterLazyLoad]
2026-01-31T10:49:40.906-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findById]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:49:40.906-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
2026-01-31T10:49:40.907-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findById]
Hibernate:
select
o1_0.user_id,
o1_0.id,
o1_0.name,
o1_0.price
from
mz_user_order o1_0
where
o1_0.user_id=?
2026-01-31T10:49:40.907-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: UserOrder(id=1, user=User(email=test111@test.com, id=1, name=todo, status=null), name=order1, price=10.1)
on_postLoad: UserOrder(id=2, user=User(email=test111@test.com, id=1, name=todo, status=null), name=order2, price=20.2)
2026-01-31T10:49:40.909-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.getUserAfterLazyLoad]
2026-01-31T10:49:40.913-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.createUser]
2026-01-31T10:49:40.913-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
on_PrePersist: User(email=mary1@gmail.com, id=null, name=Mary, status=null)
Hibernate:
insert
into
mz_user_table
(email, name, status)
values
(?, ?, ?)
2026-01-31T10:49:40.924-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:VARCHAR) <- [mary1@gmail.com]
2026-01-31T10:49:40.924-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (2:VARCHAR) <- [Mary]
2026-01-31T10:49:40.924-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (3:VARCHAR) <- [null]
on_PostPersist: User(email=mary1@gmail.com, id=14, name=Mary, status=null)
2026-01-31T10:49:40.932-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2026-01-31T10:49:40.932-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.createUser]
2026-01-31T10:49:41.003-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.activeateUser]
2026-01-31T10:49:41.003-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:49:41.004-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [14]
on_postLoad: User(email=mary1@gmail.com, id=14, name=Mary, status=null)
2026-01-31T10:49:41.005-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2026-01-31T10:49:41.005-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.activeateUser]
on_PreUpdate: User(email=mary1@gmail.com, id=14, name=Mary, status=Active)
Hibernate:
update
mz_user_table
set
email=?,
name=?,
status=?
where
id=?
2026-01-31T10:49:41.009-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:VARCHAR) <- [mary1@gmail.com]
2026-01-31T10:49:41.009-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (2:VARCHAR) <- [Mary]
2026-01-31T10:49:41.009-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (3:VARCHAR) <- [Active]
2026-01-31T10:49:41.009-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (4:INTEGER) <- [14]
on_PostUpdate: User(email=mary1@gmail.com, id=14, name=Mary, status=Active)
2026-01-31T10:49:41.105-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.detachUser]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:49:41.106-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
Hibernate:
select
o1_0.user_id,
o1_0.id,
o1_0.name,
o1_0.price
from
mz_user_order o1_0
where
o1_0.user_id=?
2026-01-31T10:49:41.107-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: UserOrder(id=1, user=User(email=test111@test.com, id=1, name=todo, status=null), name=order1, price=10.1)
on_postLoad: UserOrder(id=2, user=User(email=test111@test.com, id=1, name=todo, status=null), name=order2, price=20.2)
2026-01-31T10:49:41.109-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.detachUser]
2026-01-31T10:49:41.110-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.getUserAfterLazyLoad]
2026-01-31T10:49:41.110-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findById]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:49:41.111-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
2026-01-31T10:49:41.111-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findById]
Hibernate:
select
o1_0.user_id,
o1_0.id,
o1_0.name,
o1_0.price
from
mz_user_order o1_0
where
o1_0.user_id=?
2026-01-31T10:49:41.112-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: UserOrder(id=1, user=User(email=test111@test.com, id=1, name=todo, status=null), name=order1, price=10.1)
on_postLoad: UserOrder(id=2, user=User(email=test111@test.com, id=1, name=todo, status=null), name=order2, price=20.2)
2026-01-31T10:49:41.113-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.getUserAfterLazyLoad]
2026-01-31T10:49:41.117-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.readOnlyUserviaHint]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:49:41.376-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
2026-01-31T10:49:41.377-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserRegistrationService.readOnlyUserviaHint]
2026-01-31T10:49:41.378-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.getUserAfterLazyLoad]
2026-01-31T10:49:41.378-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findById]
Hibernate:
select
u1_0.id,
u1_0.email,
u1_0.name,
u1_0.status
from
mz_user_table u1_0
where
u1_0.id=?
2026-01-31T10:49:41.378-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: User(email=test111@test.com, id=1, name=todo, status=null)
2026-01-31T10:49:41.379-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findById]
Hibernate:
select
o1_0.user_id,
o1_0.id,
o1_0.name,
o1_0.price
from
mz_user_order o1_0
where
o1_0.user_id=?
2026-01-31T10:49:41.380-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [1]
on_postLoad: UserOrder(id=1, user=User(email=test111@test.com, id=1, name=todo, status=null), name=order1, price=10.1)
on_postLoad: UserOrder(id=2, user=User(email=test111@test.com, id=1, name=todo, status=null), name=order2, price=20.2)
2026-01-31T10:49:41.381-06:00 TRACE 1640 --- [jpa-detach-attach-demo] [ main] o.s.t.i.TransactionInterceptor : Completing transaction for [org.jcg.zheng.demo.jpa_detach_attach_demo.service.UserService.getUserAfterLazyLoad]
2026-01-31T10:49:41.389-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2026-01-31T10:49:41.390-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2026-01-31T10:49:41.396-06:00 INFO 1640 --- [jpa-detach-attach-demo] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
Figure 1 shows the Junit test results.
7. Conclusion
Spring Data JPA intentionally hides persistence context details for simplicity. Detach attach entities are advanced operations for the following benefits:
- optimize performance by reducing unnecessary flushes.
- avoid automatic update from Hibernate dirty checking.
- support a multi-transaction workflow.
- temporarily treat entities as read-only.
- avoid
org.hibernate.LazyInitializationExceptionas Hibernate default@OneToManyrelated entities withFetchType.LAZY– only load when it’s explicitly accessed.
Here is the general guideline on what to use based on the need:
| Need | Use |
| DB insert/update/delete CRD operation | JPA Annotations |
| Attach/Detach Detection | Hibernate Event Listeners |
| Debugging | TRACE |
| Cross-cutting logic | Interceptor |
| Auditing | Spring AOP |
8. Download
This was an example of a Gradle project that included detach attach entities in Spring Data JPA.
You can download the full source code of this example here: Detach & Attach Entities in Spring JPA Example





