You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

咨询@SpringBootTest与@DataJpaTest下assertSame的行为差异原因

同一ID的JPA实体实例为何在不同测试注解下断言结果不同?

问题场景

测试中出现三种不同表现:

  • 使用@DataJpaTest时,assertSame(emp, semp)结果为true
  • 使用@SpringBootTest时,该断言结果为false
  • 给@SpringBootTest的测试方法加上@Transactional后,断言又变回true

涉及代码如下:

实体类

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Data;

@Entity
@Data
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long eno;
}

Repository接口

import org.springframework.data.jpa.repository.JpaRepository;
import com.doding.board.domain.Employee;

public interface EmployeeRepository extends JpaRepository<Employee, Long> {}

测试类

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.doding.board.domain.Employee;
import com.doding.board.repo.EmployeeRepository;

@SpringBootTest
//@DataJpaTest
public class IdentityTest {
    @Autowired
    EmployeeRepository repo;

    @Test
    //@Transactional
    void identityTest() {
      Employee emp = new Employee();
      repo.save(emp);
      Employee semp = repo.findById(emp.getEno()).get();
      assertEquals(emp, semp); // 始终为true
      assertSame(emp, semp); // 结果随注解变化
    }
}

原因解析

核心在于**JPA的一级缓存(EntityManager缓存)**和不同测试注解下的事务/EntityManager生命周期:

  • @DataJpaTest的默认行为:
    这个注解会自动为测试方法添加@Transactional,整个测试过程共享同一个EntityManager。调用repo.save(emp)后,实体实例会存入一级缓存;后续findById直接从缓存取,不会创建新实例,所以assertSame返回true。测试结束后事务会自动回滚,避免污染测试数据库。

  • @SpringBootTest无事务的情况:
    @SpringBootTest默认不给测试方法绑定事务。repo.save(emp)执行时,Spring Data JPA会临时开启一个EntityManager完成保存,之后立即关闭,此时emp变成游离态(脱离EntityManager管理)。调用repo.findById时,会新建一个EntityManager从数据库查询,生成全新的实体实例返回,两个实例内存地址不同,所以assertSame为false。但@Data生成的equals方法基于实体属性(这里是eno)比较,所以assertEquals始终成立。

  • @SpringBootTest加@Transactional的情况:
    加上事务后,整个测试方法处于同一个事务上下文,EntityManager会存活到测试结束。save和findById共用同一个EntityManager,findById直接从一级缓存获取之前的实例,所以assertSame返回true。

内容的提问来源于stack exchange,提问作者andy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 21:47:20