Issue
I can not understand the reason for the error when saving the entity to the database. I want to clarify that sometimes entities are saved.
I have two entities:
@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;
}
The RuleAttribute must contain RuleCollection.
At first during processing of the JSON file, "RuleCollection" objects are saved:
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()));
After that, the "RuleAttribute" are filled and saved:
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);
Then an error may appear:
could not initialize proxy - no Session; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session
If I do an attribute save inside the stream the error changes:
.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
I can't figure out what could be the problem because "RuleCollection" already saved.
I tried changing the class "RuleAttribute" as mentioned in a post with a similar problem on StackOverflow:
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "rule_collection_id", nullable = false)
private RuleCollection ruleCollection;
The error occurs on the same data. At one point, I can save the data without error, delete it from the database, and get this error when I save it again.
UPD:
RuleCollectionRepository and RuleAttributeRepository extends org.springframework.data.repository.CrudRepository
Solution
Saving and processing was in the method with @Transactional
attribute.
Apparently, the parallel stream in this case violates the Hibernate session: Java .parallelStream() with spring annotated methods
After searching for similar problems, I found several solutions:
Set parameter propagation in attribute @Transactional to Propagation.REQUIRES_NEW. However, this option is unreliable, as it can lead to data loss or similar problems due to the fact that the main session does not know about the state of child sessions.
Don't using parallel stream.
If it is impossible to refuse parallel streams, you need to remove the
@Transactional
attribute and first set the necessary data and then perform sequential saving in a separate method marked with the@Transactional
attribute. For example: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); }
Answered By - Shadow4571
Answer Checked By - David Marino (JavaFixing Volunteer)