Home > Blockchain >  Spring JPA / HIbernate - Foreign key is never set
Spring JPA / HIbernate - Foreign key is never set

Time:08-30

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)

CodePudding user response:

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.

  • Related