Hibernate中@OneToOne关联find查询返回递归实体问题
我有Movie和MovieCast两个实体类,配置了@OneToOne双向关联关系,数据可正常保存至PostgreSQL数据库,但调用EntityManager的find方法查询Movie时,实体本身出现递归引用表现:Movie对象包含MovieCast,该MovieCast又包含Movie对象,形成循环嵌套。注意:问题并非出在JSON序列化,而是find方法返回的实体在toString展示时出现递归(实际JPA返回的是同一个实体引用,并非无限创建新对象)
实体类代码
Movie类
@Data @Entity @NoArgsConstructor public class Movie { @Id @Column(nullable = false, unique = true) private String movieName; @Column(nullable = false) private LocalDate releaseDate; @Column(nullable = false) private Integer releaseYear; @Enumerated(EnumType.STRING) private GENRE type; @OneToOne(cascade = {CascadeType.ALL}) @JoinColumn(name="cast_id",referencedColumnName = "castId") private MovieCast movieCasts; }
MovieCast类
@Entity @Data public class MovieCast { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long castId; @OneToOne(mappedBy = "movieCasts") private Movie movie; private String director; private String producer; private String leadActor; private String leadActress; private String [] otherActors; }
Repository类
@Repository public class MovieEntityRepo { @PersistenceContext private EntityManager em; public Movie findByName(String name){ return em.find(Movie.class,name); } }
数据库存储片段
-- Movies表数据 movie_name,creation_date,release_date,release_year,type,updation_date,cast_id The Shawshank Redemption5,2023-06-29 20:12:07.379,1994-01-01,1997,DRAMA,2023-06-29 20:12:07.379,1 -- Movie_cast表数据 cast_id,director,lead_actor,lead_actress,other_actors,producer 1,director1,leader1,leader2,58 bytes,producer1
解决方案
原因分析
核心问题是你使用了Lombok的@Data注解,它会自动生成包含所有字段的toString()方法。双向关联场景下,Movie的toString()会调用MovieCast的toString(),而MovieCast的toString()又会回调Movie的toString(),从而产生递归输出。JPA实际返回的实体是同一个内存引用,并非无限创建新对象,只是toString的递归展示造成了“循环引用”的错觉。
解决办法
排除toString中的关联字段:在双向关联的任意一端字段上添加
@ToString.Exclude,避免toString时递归调用。例如修改MovieCast类:@Entity @Data public class MovieCast { // ...其他字段 @ToString.Exclude @OneToOne(mappedBy = "movieCasts") private Movie movie; }或者修改
Movie类的关联字段:@ToString.Exclude @OneToOne(cascade = {CascadeType.ALL}) @JoinColumn(name="cast_id",referencedColumnName = "castId") private MovieCast movieCasts;设置懒加载关联:将
@OneToOne的加载方式改为FetchType.LAZY,默认查询Movie时不会立即加载MovieCast,只有主动调用关联字段的getter时才会加载,同时未加载的懒加载对象会显示代理类信息,避免toString递归:@OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY) @JoinColumn(name="cast_id",referencedColumnName = "castId") private MovieCast movieCasts;注意:懒加载必须在持久化上下文(即事务)范围内使用,否则会抛出
LazyInitializationException。改为单向关联:如果业务上不需要从
MovieCast反向关联Movie,直接删除MovieCast中的movie字段,彻底消除循环关联的可能。
内容的提问来源于stack exchange,提问作者satyesht

