java - 为什么当多对一是惰性的时,Hibernate 会出现 StackOverflowError?
问题描述
我有下表架构,其中模拟有很多搜索,任何搜索都有很多属性。
由于我想同时保存一个 Simulation 实体及其搜索及其属性,因此我像这样映射我的实体:
模拟.java
@Data
@EqualsAndHashCode(of = "id")
@ToString(exclude = "searches")
@Entity
@Table(name = "SIMULATION")
public class Simulation implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "simulation_generator")
@SequenceGenerator(name = "simulation_generator", sequenceName = "SIMULATION_SEQ", allocationSize = 1)
private Long id;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "SIMULATION_ID")
private Set<SimulationSearch> searches = new HashSet<>(0);
// other fields
}
模拟搜索.java
@Data
@EqualsAndHashCode(of = "id")
@ToString(exclude = "properties")
@Entity
@Table(name = "SIM_SEARCH")
public class SimulationSearch implements Serializable {
@EmbeddedId
private SimulationSearchId id;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumns({
@JoinColumn(name = "SIMULATION_ID", referencedColumnName = "SIMULATION_ID"),
@JoinColumn(name = "POSITION", referencedColumnName = "POSITION")
})
private Set<SimulationSearchProperty> properties = new HashSet<>(0);
// other fields...
@Data
public static class SimulationSearchId implements Serializable {
@ManyToOne
@JoinColumn(name = "SIMULATION_ID", insertable = false, updatable = false)
private Simulation simulation;
private int position;
}
}
SimulationSearchProperties.java
@Data
@EqualsAndHashCode(of = "id")
@Entity
@Table(name = "SIM_SEARCH_PROPERTY")
public class SimulationSearchProperty implements Serializable {
@EmbeddedId
private SimulationSearchPropertyId id;
private String value;
@Data
public static class SimulationSearchPropertyId implements Serializable {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns({
@JoinColumn(name = "SIMULATION_ID", referencedColumnName = "SIMULATION_ID", insertable = false, updatable = false),
@JoinColumn(name = "POSITION", referencedColumnName = "POSITION", insertable = false, updatable = false)
})
private SimulationSearch search;
private String label;
}
}
发生的事情是 Hibernate 一直打印以下查询,直到它发生 StackOverflowError。
select simulation0_.*, searches1_.*, properties5_.*
from simulation simulation0_
left outer join sim_search searches1_ on simulation0_.id = searches1_.simulation_id
left outer join sim_search_property properties5_ on searches1_.position = properties5_.position and searches1_.simulation_id = properties5_.simulation_id
where simulation0_.id = ?
虽然和之间的映射与Simulation
和映射SimulationSearch
非常相似SimulationSearch
,但当我将注释设置为惰性获取SimulationSearchProperty
时开始发生此错误,即使我也设置为惰性也不会停止。ManyToOne
SimulationSearch#properties
SimulationSearchPropertyId#search
我错过了什么?
更新
我正在使用 Hibernate 4.2.6.Final
部分堆栈跟踪日志:
java.lang.StackOverflowError
at org.hibernate.engine.spi.QueryParameters.<init>(QueryParameters.java:148)
at org.hibernate.engine.spi.QueryParameters.<init>(QueryParameters.java:104)
at org.hibernate.engine.spi.QueryParameters.<init>(QueryParameters.java:81)
at org.hibernate.loader.Loader.loadEntity(Loader.java:2114)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:82)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:72)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3927)
at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:460)
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:429)
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:206)
at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:262)
at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:150)
at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1092)
at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1019)
at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:672)
at org.hibernate.type.EntityType.resolve(EntityType.java:490)
at org.hibernate.type.ComponentType.resolve(ComponentType.java:667)
at org.hibernate.type.ComponentType.nullSafeGet(ComponentType.java:349)
at org.hibernate.type.ManyToOneType.hydrate(ManyToOneType.java:190)
at org.hibernate.type.ComponentType.hydrate(ComponentType.java:642)
at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:775)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:708)
at org.hibernate.loader.Loader.processResultSet(Loader.java:943)
at org.hibernate.loader.Loader.doQuery(Loader.java:911)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:342)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:312)
at org.hibernate.loader.Loader.loadEntity(Loader.java:2121)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:82)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:72)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3927)
at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:460)
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:429)
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:206)
at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:262)
at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:150)
at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1092)
at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1019)
at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:672)
at org.hibernate.type.EntityType.resolve(EntityType.java:490)
at org.hibernate.type.ComponentType.resolve(ComponentType.java:667)
at org.hibernate.type.ComponentType.nullSafeGet(ComponentType.java:349)
at org.hibernate.type.ManyToOneType.hydrate(ManyToOneType.java:190)
at org.hibernate.type.ComponentType.hydrate(ComponentType.java:642)
at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:775)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:708)
at org.hibernate.loader.Loader.processResultSet(Loader.java:943)
at org.hibernate.loader.Loader.doQuery(Loader.java:911)
...
我刚刚更新了一些实体映射,删除了mappedBy
注释属性并添加了@JoinColumn
注释。现在持久性工作正常,但是当我尝试加载单个模拟时 StackOverflowError 仍然存在。
我还清理了 Hibernate 生成的 sql,删除了无趣的信息。
解决方案
您忘记将您的关系注释为双向的。
对于你的第一堂课,变化应该是
@Data
@EqualsAndHashCode(of = "id")
@ToString(exclude = "searches")
@Entity
@Table(name = "SIMULATION")
public class Simulation implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "simulation_generator")
@SequenceGenerator(name = "simulation_generator", sequenceName = "SIMULATION_SEQ", allocationSize = 1)
private Long id;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "SimulationSearchId")
private Set<SimulationSearch> searches = new HashSet<>(0);
// other fields
}
如果没有该属性mappedBy
,JPA 提供程序会假定您有 2 个单向关系,这会导致您的数据之间存在无限循环引用。
参考
JSR 338:Java TM 持久性 API,版本 2.1
11.1.40 一对多注解
第 4 段,第 2 句
如果关系是双向的,则该
mappedBy
元素必须用于指定作为关系所有者的实体的关系字段或属性。
推荐阅读
- docker - Docker Desktop 无法切换到 Windows 容器
- java - 带有自定义 HTTP 错误响应的自定义 ConstraintValidator 消息
- javascript - 如何使用 Node.js 随机化 JavaScript 对象中的单个值
- javascript - 如何在 DataTable().row.add() 之后继续自定义 dataTable?
- python - 在字典中自动增加相同的值
- flutter - Flutter中的组件卸载(相当于React componentWillUnmount) - Flutter web
- c++ - 在访问私有元素时重载 << 运算符(朋友)
- java - 我在我的 firebase recyclerview 适配器的 logcat “E/RecyclerView:未连接适配器;跳过布局”中收到以下错误
- java - Java 运行时没有正确格式化 python args
- discord.js - discord.js 无论他们如何编写命令,我如何让机器人工作,比如 -piNG