Hibernate - Random occurrence of an error: Not-null property references a transient value - transient instance must be saved before current operation
Hibernate - Random occurrence of an error: Not-null property references a transient value - transient instance must be saved before current operation
我无法理解将实体保存到数据库时出错的原因。我想澄清一下,有时实体会被保存。
我有两个实体:
@Data
@Entity
@Table(...)
public class RuleCollection {
// Fields
}
@Data
@Entity
@Table(...)
public class RuleAttribute {
// Fields
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "rule_collection_id", nullable = false)
private RuleCollection ruleCollection;
}
RuleAttribute 必须包含 RuleCollection。
首先在 JSON 文件的处理过程中,“RuleCollection”对象被保存:
Map<UUID, RuleCollection> ruleCollectionsMap =
json.getRuleCollections().stream().map(ruleCollectionDto -> {
RuleCollection ruleCollection = new RuleCollection();
// Set ruleCollection fields
return ruleCollectionRepository.save(ruleCollection);
}
).collect(Collectors.toMap(RuleCollection::getId, Function.identity()));
之后,填写并保存“RuleAttribute”:
List<RuleAttribute> attributes = json.getAttributes().stream()
.filter(attr -> ruleCollectionsMap.get(attr.getCollectionId()) != null)
.parallel()
.map(attr -> {
RuleAttribute ruleAttribute = new RuleAttribute();
// Set ruleAttribute fields
ruleAttribute.setRuleCollection(ruleCollectionsMap.get(attr.getCollectionId()));
return ruleAttribute;
}
).collect(Collectors.toList());
ruleAttributeRepository.save(attributes);
然后可能出现错误:
could not initialize proxy - no Session; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session
如果我在流中进行属性保存,错误会更改:
.map(attr -> {
...
return ruleAttributeRepository.save(ruleAttribute);
}
).collect(Collectors.toList());
org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation :
RuleAttribute.ruleCollection -> RuleCollection;
nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation :
RuleAttribute.ruleCollection -> RuleCollection
我不知道可能是什么问题,因为“RuleCollection”已经保存。
我尝试更改 class“RuleAttribute”,如 post 中所述,在 Whosebug 上出现类似问题:
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "rule_collection_id", nullable = false)
private RuleCollection ruleCollection;
同样的数据出现错误。有一次我保存数据没有报错,从数据库中删除,再次保存时出现这个错误。
更新:
RuleCollectionRepository 和 RuleAttributeRepository 扩展 org.springframework.data.repository.CrudRepository
保存和处理在具有@Transactional
属性的方法中。
显然,这种情况下的并行流违反了 Hibernate 会话:
Java .parallelStream() with spring annotated methods
搜索类似问题后,找到了几种解决方案:
将属性@Transactional 中的参数传播设置为Propagation.REQUIRES_NEW。 但是,此选项不可靠,因为主会话不知道子会话的状态,可能会导致数据丢失或类似问题。
不要使用并行流。
如果无法拒绝并行流,则需要去掉@Transactional
属性,先设置必要的数据,然后在单独的方法中进行顺序保存,标有@Transactional
属性。
例如:
public void saveData(Json json) {
Map<UUID, RuleCollection> ruleCollectionsMap = // Set data
List<RuleAttribute> attributes = // Set data
saveInDB(ruleCollectionsMap.values(), attributes);
}
@Transactional
private void saveInDB(Collection<RuleCollection> ruleCollections, Collection<RuleAttribute> attributes) {
ruleCollectionRepository.save(ruleCollections);
ruleAttributeRepository.save(attributes);
}
我无法理解将实体保存到数据库时出错的原因。我想澄清一下,有时实体会被保存。
我有两个实体:
@Data
@Entity
@Table(...)
public class RuleCollection {
// Fields
}
@Data
@Entity
@Table(...)
public class RuleAttribute {
// Fields
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "rule_collection_id", nullable = false)
private RuleCollection ruleCollection;
}
RuleAttribute 必须包含 RuleCollection。
首先在 JSON 文件的处理过程中,“RuleCollection”对象被保存:
Map<UUID, RuleCollection> ruleCollectionsMap =
json.getRuleCollections().stream().map(ruleCollectionDto -> {
RuleCollection ruleCollection = new RuleCollection();
// Set ruleCollection fields
return ruleCollectionRepository.save(ruleCollection);
}
).collect(Collectors.toMap(RuleCollection::getId, Function.identity()));
之后,填写并保存“RuleAttribute”:
List<RuleAttribute> attributes = json.getAttributes().stream()
.filter(attr -> ruleCollectionsMap.get(attr.getCollectionId()) != null)
.parallel()
.map(attr -> {
RuleAttribute ruleAttribute = new RuleAttribute();
// Set ruleAttribute fields
ruleAttribute.setRuleCollection(ruleCollectionsMap.get(attr.getCollectionId()));
return ruleAttribute;
}
).collect(Collectors.toList());
ruleAttributeRepository.save(attributes);
然后可能出现错误:
could not initialize proxy - no Session; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session
如果我在流中进行属性保存,错误会更改:
.map(attr -> {
...
return ruleAttributeRepository.save(ruleAttribute);
}
).collect(Collectors.toList());
org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : RuleAttribute.ruleCollection -> RuleCollection; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : RuleAttribute.ruleCollection -> RuleCollection
我不知道可能是什么问题,因为“RuleCollection”已经保存。
我尝试更改 class“RuleAttribute”,如 post 中所述,在 Whosebug 上出现类似问题:
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "rule_collection_id", nullable = false)
private RuleCollection ruleCollection;
同样的数据出现错误。有一次我保存数据没有报错,从数据库中删除,再次保存时出现这个错误。
更新:
RuleCollectionRepository 和 RuleAttributeRepository 扩展 org.springframework.data.repository.CrudRepository
保存和处理在具有@Transactional
属性的方法中。
显然,这种情况下的并行流违反了 Hibernate 会话: Java .parallelStream() with spring annotated methods
搜索类似问题后,找到了几种解决方案:
将属性@Transactional 中的参数传播设置为Propagation.REQUIRES_NEW。 但是,此选项不可靠,因为主会话不知道子会话的状态,可能会导致数据丢失或类似问题。
不要使用并行流。
如果无法拒绝并行流,则需要去掉
@Transactional
属性,先设置必要的数据,然后在单独的方法中进行顺序保存,标有@Transactional
属性。 例如:public void saveData(Json json) { Map<UUID, RuleCollection> ruleCollectionsMap = // Set data List<RuleAttribute> attributes = // Set data saveInDB(ruleCollectionsMap.values(), attributes); } @Transactional private void saveInDB(Collection<RuleCollection> ruleCollections, Collection<RuleAttribute> attributes) { ruleCollectionRepository.save(ruleCollections); ruleAttributeRepository.save(attributes); }