Enterprise JavaJava

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: @SpringBootApplication marks 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-sql to print out the SQL statements.
  • Line 17: disable the Spring Open-Session-In-View (OSIV).
  • Line 21, 22: set the logging level to TRACE for 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 JpaRepository works 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.

JPA OperationsEvent
find, queryLoadEvent
mergeMergeEvent
detach, clearEvicEvent
dirty checkingFlushEntityEvent

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: @OneToMany defaults to FetchType.LAZY. Will use detach() properly to avoid the LazyInitializationException in 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: @Repository is 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: @Repository is 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: @Transactional is needed.
  • Line 45: call the getOrders() trigger the Hibernate to lazy load the related UserOrder entities for the User object.

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 throw org.hibernate.LazyInitializationException at line 39 if not commented out.
  • Line 44, 47: call the getOrders() is fine as getUserAfterLazyLoad() already loaded the related UserOrder entities for the User object.

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 LazyInitializationException when accessing lazy load orders outside 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: @Transactional is needed.
  • Line 25, 26: calling merge() without detach() 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. After detach(), merge() will return a new entity object.
  • Line 49, 52: clear() detaches all managed entities. After clear(), 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() and detach() 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 after detach().
  • Line 36, 37, 38: the entity will NOT be tracked as detach() is called after triggering lazy loading. The setEmail() 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 readOnly hint is true.
  • 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.

detach attach entities
Figure 1. Unit 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.LazyInitializationException as Hibernate default @OneToMany related entities with FetchType.LAZY – only load when it’s explicitly accessed.

Here is the general guideline on what to use based on the need:

NeedUse
DB insert/update/delete CRD operationJPA Annotations
Attach/Detach DetectionHibernate Event Listeners
DebuggingTRACE
Cross-cutting logicInterceptor
AuditingSpring AOP

8. Download

This was an example of a Gradle project that included detach attach entities in Spring Data JPA.

Download
You can download the full source code of this example here: Detach & Attach Entities in Spring JPA Example

Mary Zheng

Mary graduated from the Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She worked as a lead Software Engineer where she led and worked with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button