由一对多关系引起的hibernate堆栈溢出
Stack overflow on hibernate caused by one to many relationship
我已经创建了一个一对多的关系。虽然当我通过 id 从多到一侧查询对象时一切看起来都很好,但连接的列会抛出一个 Whosebug 异常,我无法弄清楚为什么会发生。大致上我已经关注了this tutorial.
玩家实体
@Entity
@Data
public class Player {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "player_id", updatable = false, unique = true, nullable = false)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "num")
private int num;
@Column(name = "position")
private String position;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
}
团队实体
@Entity
@Data
public class Team {
@Id
@Column(name="team_id", updatable = false, unique = true)
private String id;
@Column(name = "name")
private String name;
@OneToMany(mappedBy = "team")
private List<Player> players;
}
我的播放器库
@Repository
public interface PlayerRepository extends CrudRepository<Player, Long> {
Optional<Player> findById(Long id);
}
import.sql
中的数据库条目
insert into Team (team_id,name) values('Barcelona','Barcelona');
insert into Player (name,num,position,team_id) values('Andreas Inniesta', 8, 'Midfielder', 'Barcelona');
insert into Player (name,num,position,team_id) values('Lionel Messi', 10, 'Forward', 'Barcelona');
玩家 table 在 h2-console
这是例外情况
java.lang.WhosebugError: null
at java.lang.StringBuilder.append(StringBuilder.java:136) ~[na:1.8.0_121]
at mypackage.entity.Player.toString(Player.java:8) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_121]
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:510) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Team.toString(Team.java:9) ~[classes!/:1.0-SNAPSHOT]
at sun.reflect.GeneratedMethodAccessor43.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:84) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at mypackage.entity.Team_$$_jvst2b7_1.toString(Team_$$_jvst2b7_1.java) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Player.toString(Player.java:8) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_121]
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:510) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Team.toString(Team.java:9) ~[classes!/:1.0-SNAPSHOT]
at sun.reflect.GeneratedMethodAccessor43.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:84) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at mypackage.entity.Team_$$_jvst2b7_1.toString(Team_$$_jvst2b7_1.java) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Player.toString(Player.java:8) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_121]
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:510) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Team.toString(Team.java:9) ~[classes!/:1.0-SNAPSHOT]
at sun.reflect.GeneratedMethodAccessor43.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:84) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at mypackage.entity.Team_$$_jvst2b7_1.toString(Team_$$_jvst2b7_1.java) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Player.toString(Player.java:8) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_121]
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:510) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Team.toString(Team.java:9) ~[classes!/:1.0-SNAPSHOT]
at sun.reflect.GeneratedMethodAccessor43.invoke(Unknown Source) ~[na:na]
异常的根源
at mypackage.entity.Team.toString(Team.java:9) ~[classes!/:1.0-SNAPSHOT]
at sun.reflect.GeneratedMethodAccessor43.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:84) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at mypackage.entity.Team_$$_jvst2b7_1.toString(Team_$$_jvst2b7_1.java) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Player.toString(Player.java:8) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_121]
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:510) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Team.toString(Team.java:9) ~[classes!/:1.0-SNAPSHOT]
at sun.reflect.GeneratedMethodAccessor43.invoke(Unknown Source) ~[na:na]
进一步调试错误表明,在连接列上某种类型的字符串内部转换失败。
发生这种情况是因为当你想查询团队时,比如团队 'A',它还包含团队 'A' 中的球员列表,这些列表本身包含他们所在的团队(team 'A'),它还带有一个 Players 列表......你明白了,它在这种关系中无限循环,没有尽头。为避免 Whosebug 错误,如果使用 Jackson,则需要使用 @JsonIgnore 注释 @OneToMany 的一侧,如果使用普通 jsonb,则需要使用 @JsonbTransient 注释。
@OneToMany(mappedBy = "team")
@JsonIgnore
private List<Player> players;
或
@OneToMany(mappedBy = "team")
@JsonbTransient
private List<Player> players;
这样,在查询球队时,球员名单将被忽略,您将避免错误。
据我所知,您正在使用 Lombok
。
您认为您没有创建 toString
方法,但 Lombok
不这么认为。 @Data
注释导致生成 toString
方法。
来自 Lombok
文档:
@Data
is a convenient shortcut annotation that bundles the features of
@ToString
, @EqualsAndHashCode
, @Getter
/ @Setter
and
@RequiredArgsConstructor
together: In other words, @Data
generates all
the boilerplate that is normally associated with simple POJOs (Plain
Old Java Objects) and beans: getters for all fields, setters for all
non-final fields, and appropriate toString, equals and hashCode
implementations that involve the fields of the class, and a
constructor that initializes all final fields, as well as all
non-final fields with no initializer that have been marked with
@NonNull
, in order to ensure the field is never null.
因此 Lombok
为您创建了 toString
导致无限递归的方法。
您可以通过以下方式修复它:
@Data
@ToString(exclude = "players")
public class Team {
// ...
}
或者:
@Data
public class Team {
@OneToMany(mappedBy = "team")
@ToString.Exclude
private List<Player> players;
}
值得一提的是,如果您仍然需要在 Team
的 toString
表示中包含 players
,请尝试反汇编 @Data
注释并仅添加部分你真的需要。然后定义你自己的toString
方法。
即使我也遇到过类似的问题。我在尝试保持一对多关系时遇到 Whosebug 错误。
解决方案是。
@Data
@EqualsAndHashCode(exclude = "players") // players is @OneToMany
class Team {
@OneToMany(mappedBy = "team")
private List<Player> players;
}
这是导致问题的原因,因为它曾经使用所有实体创建哈希码。这导致递归。
我已经创建了一个一对多的关系。虽然当我通过 id 从多到一侧查询对象时一切看起来都很好,但连接的列会抛出一个 Whosebug 异常,我无法弄清楚为什么会发生。大致上我已经关注了this tutorial.
玩家实体
@Entity
@Data
public class Player {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "player_id", updatable = false, unique = true, nullable = false)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "num")
private int num;
@Column(name = "position")
private String position;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
}
团队实体
@Entity
@Data
public class Team {
@Id
@Column(name="team_id", updatable = false, unique = true)
private String id;
@Column(name = "name")
private String name;
@OneToMany(mappedBy = "team")
private List<Player> players;
}
我的播放器库
@Repository
public interface PlayerRepository extends CrudRepository<Player, Long> {
Optional<Player> findById(Long id);
}
import.sql
中的数据库条目insert into Team (team_id,name) values('Barcelona','Barcelona');
insert into Player (name,num,position,team_id) values('Andreas Inniesta', 8, 'Midfielder', 'Barcelona');
insert into Player (name,num,position,team_id) values('Lionel Messi', 10, 'Forward', 'Barcelona');
玩家 table 在 h2-console
这是例外情况
java.lang.WhosebugError: null
at java.lang.StringBuilder.append(StringBuilder.java:136) ~[na:1.8.0_121]
at mypackage.entity.Player.toString(Player.java:8) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_121]
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:510) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Team.toString(Team.java:9) ~[classes!/:1.0-SNAPSHOT]
at sun.reflect.GeneratedMethodAccessor43.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:84) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at mypackage.entity.Team_$$_jvst2b7_1.toString(Team_$$_jvst2b7_1.java) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Player.toString(Player.java:8) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_121]
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:510) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Team.toString(Team.java:9) ~[classes!/:1.0-SNAPSHOT]
at sun.reflect.GeneratedMethodAccessor43.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:84) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at mypackage.entity.Team_$$_jvst2b7_1.toString(Team_$$_jvst2b7_1.java) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Player.toString(Player.java:8) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_121]
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:510) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Team.toString(Team.java:9) ~[classes!/:1.0-SNAPSHOT]
at sun.reflect.GeneratedMethodAccessor43.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:84) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at mypackage.entity.Team_$$_jvst2b7_1.toString(Team_$$_jvst2b7_1.java) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Player.toString(Player.java:8) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_121]
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:510) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Team.toString(Team.java:9) ~[classes!/:1.0-SNAPSHOT]
at sun.reflect.GeneratedMethodAccessor43.invoke(Unknown Source) ~[na:na]
异常的根源
at mypackage.entity.Team.toString(Team.java:9) ~[classes!/:1.0-SNAPSHOT]
at sun.reflect.GeneratedMethodAccessor43.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:84) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at mypackage.entity.Team_$$_jvst2b7_1.toString(Team_$$_jvst2b7_1.java) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Player.toString(Player.java:8) ~[classes!/:1.0-SNAPSHOT]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_121]
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:510) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_121]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_121]
at mypackage.entity.Team.toString(Team.java:9) ~[classes!/:1.0-SNAPSHOT]
at sun.reflect.GeneratedMethodAccessor43.invoke(Unknown Source) ~[na:na]
进一步调试错误表明,在连接列上某种类型的字符串内部转换失败。
发生这种情况是因为当你想查询团队时,比如团队 'A',它还包含团队 'A' 中的球员列表,这些列表本身包含他们所在的团队(team 'A'),它还带有一个 Players 列表......你明白了,它在这种关系中无限循环,没有尽头。为避免 Whosebug 错误,如果使用 Jackson,则需要使用 @JsonIgnore 注释 @OneToMany 的一侧,如果使用普通 jsonb,则需要使用 @JsonbTransient 注释。
@OneToMany(mappedBy = "team")
@JsonIgnore
private List<Player> players;
或
@OneToMany(mappedBy = "team")
@JsonbTransient
private List<Player> players;
这样,在查询球队时,球员名单将被忽略,您将避免错误。
据我所知,您正在使用 Lombok
。
您认为您没有创建 toString
方法,但 Lombok
不这么认为。 @Data
注释导致生成 toString
方法。
来自 Lombok
文档:
@Data
is a convenient shortcut annotation that bundles the features of@ToString
,@EqualsAndHashCode
,@Getter
/@Setter
and@RequiredArgsConstructor
together: In other words,@Data
generates all the boilerplate that is normally associated with simple POJOs (Plain Old Java Objects) and beans: getters for all fields, setters for all non-final fields, and appropriate toString, equals and hashCode implementations that involve the fields of the class, and a constructor that initializes all final fields, as well as all non-final fields with no initializer that have been marked with@NonNull
, in order to ensure the field is never null.
因此 Lombok
为您创建了 toString
导致无限递归的方法。
您可以通过以下方式修复它:
@Data
@ToString(exclude = "players")
public class Team {
// ...
}
或者:
@Data
public class Team {
@OneToMany(mappedBy = "team")
@ToString.Exclude
private List<Player> players;
}
值得一提的是,如果您仍然需要在 Team
的 toString
表示中包含 players
,请尝试反汇编 @Data
注释并仅添加部分你真的需要。然后定义你自己的toString
方法。
即使我也遇到过类似的问题。我在尝试保持一对多关系时遇到 Whosebug 错误。 解决方案是。
@Data
@EqualsAndHashCode(exclude = "players") // players is @OneToMany
class Team {
@OneToMany(mappedBy = "team")
private List<Player> players;
}
这是导致问题的原因,因为它曾经使用所有实体创建哈希码。这导致递归。