关于JPA @Access注解的使用验证及测试实现求助
关于JPA @Access注解工作机制的测试验证问题
我对JPA的@Access注解工作机制存在疑惑。已知默认访问类型由@Id的位置决定:@Id标注在字段上时,默认访问类型是AccessType.FIELD;标注在Getter方法上时,默认是AccessType.PROPERTY。
我写了如下Student实体类:
@Entity public class Student { @Id @GeneratedValue private Long ID; private String firstName; @Access(value= AccessType.PROPERTY) private String lastName; public String getFirstName() { System.out.println("~~~~~~~~getFirstName~~~~~~~~~"); return firstName; } public void setFirstName(String firstName) { this.firstName = "FirstName: " + firstName; System.out.println("~~~~~~~~setFirstName~~~~~~~~~"); } public String getlastName() { System.out.println("~~~~~~~~getlastName~~~~~~~~~"); return lastName; } public void setLastName(String lastName) { this.lastName = "LastName: "+ lastName; System.out.println("~~~~~~~~setLastname~~~~~~~~~"); } }
我在Setter方法里加了前缀("FirstName: "和"LastName: ")来判断方法是否被调用。因为@Id标注在字段上,默认访问类型是AccessType.FIELD,但我给lastName字段指定了AccessType.PROPERTY。
我理解这种情况下,JPA保存到数据库时:
- firstName字段不会带前缀(因为不会调用setFirstName)
- lastName字段会带前缀(因为会调用setLastName)
但我不知道怎么写测试来验证这个逻辑,以下是未完成的测试代码:
class StudentTest { private EntityManagerFactory entityManagerFactory; private EntityManager entityManager; @BeforeEach public void init() { entityManagerFactory = Persistence.createEntityManagerFactory( "in.memory.test"); entityManager = entityManagerFactory.createEntityManager(); entityManager.getTransaction().begin(); } @AfterEach public void destroy() { entityManager.getTransaction().commit(); entityManager.close(); entityManagerFactory.close(); } @Test public void studentFirstName(){ } @Test public void studentLastName(){ } }
请指导如何实现测试验证上述逻辑。
测试实现方案
核心思路
要验证访问类型的差异,需要:
- 保存实体后,清空EntityManager的缓存(避免直接从内存取对象),再重新查询实体
- 对比查询结果中字段的值是否符合预期(是否带前缀)
- 也可以结合日志输出判断Getter/Setter是否被调用,但数据库值的对比是更可靠的验证方式
完整测试代码实现
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import static org.junit.jupiter.api.Assertions.*; class StudentTest { private EntityManagerFactory entityManagerFactory; private EntityManager entityManager; @BeforeEach public void init() { entityManagerFactory = Persistence.createEntityManagerFactory("in.memory.test"); entityManager = entityManagerFactory.createEntityManager(); entityManager.getTransaction().begin(); } @AfterEach public void destroy() { if (entityManager.getTransaction().isActive()) { entityManager.getTransaction().commit(); } entityManager.close(); entityManagerFactory.close(); } @Test public void testFirstNameUsesFieldAccess() { // 创建实体并设置字段值 Student student = new Student(); student.setFirstName("Alice"); student.setLastName("Smith"); // 保存实体 entityManager.persist(student); Long studentId = student.getID(); // 清空EntityManager缓存,强制从数据库查询 entityManager.flush(); entityManager.clear(); // 重新查询实体 Student retrievedStudent = entityManager.find(Student.class, studentId); // 验证:firstName没有前缀(因为FIELD访问直接操作字段,setFirstName的前缀不会被持久化) assertEquals("Alice", retrievedStudent.getFirstName()); } @Test public void testLastNameUsesPropertyAccess() { Student student = new Student(); student.setFirstName("Bob"); student.setLastName("Johnson"); entityManager.persist(student); Long studentId = student.getID(); entityManager.flush(); entityManager.clear(); Student retrievedStudent = entityManager.find(Student.class, studentId); // 验证:lastName带有前缀(因为PROPERTY访问会调用setLastName,前缀被持久化到数据库) assertEquals("LastName: Johnson", retrievedStudent.getlastName()); } }
关键说明
flush()和clear()的作用:调用flush()会把EntityManager中的持久化操作同步到数据库;clear()会清空EntityManager的一级缓存,这样后续的find()操作会从数据库重新加载数据,而不是直接返回内存中的对象,确保验证的是数据库实际存储的值。- 断言逻辑:
- firstName用FIELD访问,JPA直接操作字段
firstName,所以数据库中存储的是原始值("Alice"),不会带上setFirstName里的前缀。 - lastName用PROPERTY访问,JPA会调用
setLastName(),所以数据库中存储的是带前缀的值("LastName: Johnson")。
- firstName用FIELD访问,JPA直接操作字段
- 日志辅助验证:运行测试时,你会看到
~~~~~~~~setLastName~~~~~~~~~的输出,但不会看到~~~~~~~~setFirstName~~~~~~~~~的输出(除非主动调用getFirstName),这也能侧面验证访问类型的差异。
内容的提问来源于stack exchange,提问作者Romillion
相关产品推荐
相关产品推荐

