Issue
I have an TimelineEntity
entity, that uses HoTimelineType
enum with custom integer value. That custom integer value is stored in the database. Implemented via Using @PostLoad and @PrePersist Annotations
Sprint JPA Repository is used to save and get entities.
Here is the issue:
@Entity
@Table(name = TABLE_NAME)
@IdClass(TimelineKey.class)
public class TimelineEntity {
public interface Persistence {
String TABLE_NAME = "timelines";
}
@Id
@Column(name = "node_id")
private Long nodeId;
@Id
@Column(name = "timeline_id")
private Long timelineId;
@Column(name = "ho_timeline_type")
private Integer hoTimelineTypeValue;
@Transient
private HoTimelineType hoTimelineType;
public Long getNodeId() {
return nodeId;
}
public void setNodeId(Long nodeId) {
this.nodeId = nodeId;
}
public Long getTimelineId() {
return timelineId;
}
public void setTimelineId(Long timelineId) {
this.timelineId = timelineId;
}
public HoTimelineType getHoTimelineType() {
return hoTimelineType;
}
public void setHoTimelineType(HoTimelineType hoTimelineType) {
this.hoTimelineType = hoTimelineType;
}
public Integer getHoTimelineTypeValue() {
return hoTimelineTypeValue;
}
public void setHoTimelineTypeValue(Integer hoTimelineTypeValue) {
this.hoTimelineTypeValue = hoTimelineTypeValue;
}
@PostLoad
private void postLoad() {
this.hoTimelineType = HoTimelineType.of(hoTimelineTypeValue);
}
@PrePersist
private void prePersist() {
this.hoTimelineTypeValue = hoTimelineType.getValue();
}
}
@Eager
public interface TimelineEntityRepository extends JpaRepository<TimelineEntity, TimelineKey> {
List<TimelineEntity> findByNodeId(Long nodeId);
}
@Autowired
private TimelineEntityRepository timelineEntityRepository;
...
TimelineEntity newTE = new TimelineEntity();
newTE.setNodeId(10L);
newTE.setTimelineId(22L);
newTE.setHoTimelineType(HoTimelineType.TYPE_1);
newTE = timelineEntityRepository.save(newTE);
When the newTE
entity is saved, prePersist
is invoked, and inside this method, the hoTimelineType
is null and I get NPE. nodeId
and timelineId
are not nulls. If I stay with a debugger on the last line, outside of prePersist
, I see that hoTimelineType
has the value, I set before.
When I load entities, inserted with test data, everything works fine and both hoTimelineType
and hoTimelineTypeValue
have not nullable values.
I skipped the code of TimelineKey
and HoTimelineType
to simplify the example. Can add it, if needed.
What could reset hoTimelineType
? What do I miss?
Solution
It seems there is no way to control the saving behaviour of spring jpa repository proxy.
Possible solutions for issue:
- Via
javax.persistence.Converter
. It is pretty clear, the structe of an entity is simple. Can confirm it works fine with Spring Jpa Repository generation. - Explicitely set
hoTimelineTypeValue
before you save an entity. Error-prone solution. Everytime you save an entity you must think about the difference between thehoTimelineTypeValue
andhoTimelineType
. - You could enrich setters and getters of the entity class, to explicitely control the consistency between the fields. It makes implementation of entity classes not so obvious. You get more compicated solution for nothing. As a result error-prone solution. Do not recommend it as well.
Cause of disadvantages of #2 and #3 I do not provide examples. It makes no sense.
Example of the solution #1 can be found here: Using JPA 2.1 @Converter Annotation
Answered By - Alexandr
Answer Checked By - Willingham (JavaFixing Volunteer)