Search code examples
spring-boothibernatespring-data-jpaone-to-manymany-to-one

Spring JPA / HIbernate - Foreign key is never set


I have 2 Oracle tables in a 1:M relationship:

EVENT(ID, NAME, DATE)

EVENT_LOG(ID, EVENT_ID, COMPLETED)
  • EVENT has PK=ID

  • EVENT_LOG has PK=ID and FK=EVENT_ID on EVENT.ID

I want to use Spring JPA to persist this data to database, so I build my entities with @OneToMany and @ManyToOne annotations like below (some details omitted for clarity):

@Entity
@Table(name = "EVENT")
public class EventEntity implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @Column(name = "ID")
    private Long id;

    @Column(name = "NAME")
    private String name;

    @Column(name = "DATE"")
    private OffsetDateTime date;

    @OneToMany(mappedBy = "eventEntityDetails", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private List<EventLogEntity> eventLogs;
    
    ...
}


@Entity
@Table(name = "EVENT_LOG")
public class EventLogEntity implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @Column(name = "ID")
    private Long id;

    @Column(name = "EVENT_ID")
    private long eventId;

    @Column(name = "COMPLETED")
    private int completed;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "E_ID", referencedColumnName = "ID")
    private EventEntity eventEntityDetails;

    // ...
}

In order to persist my EVENT and EVENT_LOG, I have this method. It receives EntityDto object which I map to a new instance of Entity and then I create new EventLogEntity, set its COMPLETED value and call repository to save my event.

At this stage, I cannot set the EventLog.EVENT_ID as at this stage EVENT.ID has not been generated, so my expectation is that hibernate takes care of that for me. However that turns to be wrong and I think I am missing something either in this method or in my @OneToMany and @ManyToOne annotations above in my entities:

private EventEntity saveEvent(EventDto eventDto) {

    EventEntity eventEntity = new EventEntity();

    ModelMapper modelMapper = new ModelMapper();
    modelMapper.map(eventDto, eventEntity);

    List<EventLogEntity> eventLogs = new ArrayList<EventLogEntity>();

    EventLogEntity eventLogEntity = new EventLogEntity();
    eventLogEntity.setCompleted(1);
    eventLogs.add(eventLogEntity);

    eventEntity.setEventtLogs(eventLogs);

    EventEntity storedEvent = eventRepository.saveAndFlush(eventEntity);

    return storedEvent;
}

PROBLEM

The above call to safeAndFlush() will save my Event and EventLog to their tables, however EVENT_LOG.EVENT_ID (which is the FK to EVENT.ID) is always set 0 instead of being set to the value in EVENT.ID.

I would expect it to be set to the ID of EVENT like in example below:

EVENT(1, 'open_door', 20220210T11:55:10)

EVENT_LOG(1, 1, 1)

, but instead, I get:

EVENT(1, 'open_door', 20220210T11:55:10)

EVENT_LOG(1, 0, 1)

Solution

  • I think you have to assign the EventEntity object to the EventLogEntity before flushing:

    EventLogEntity eventLogEntity = new EventLogEntity();
    eventLogEntity.setCompleted(1);
    eventLogEntity.setEventEntityDetails(eventEntity);
    eventLogs.add(eventLogEntity);
    

    See if that helps.

    Update: I think what you want is the following:

    @Entity
    @Table(name = "EVENT")
    public class EventEntity implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        @Column(name = "ID")
        private Long id;
    
        @OneToMany(mappedBy = "event", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
        private List<EventLogEntity> eventLogs;
        
        ...
    }
    
    
    @Entity
    @Table(name = "EVENT_LOG")
    public class EventLogEntity implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        @Column(name = "ID")
        private Long id;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "EVENT_ID")
        private EventEntity event;
    
    }
    

    Notice a few things here:

    • I removed the referencedColumnName info because by default the primary key of the referencing table is used, which is fine in our case
    • I removed the explicit EVENT_ID column which you declared as part of the class. This column will be created automatically as part of attaching the @ManyToOne annotation on the event field.
    • I renamed the eventEntityDetails field simply to event

    I am pretty sure the solution will work! Please let me know.