Nikolay Shabak
Nikolay Shabak

Reputation: 606

Field not initialized when persist object using JPARepository, @OneToMany <=> @ManyToOne

I have following entities: MissionInfo:

@Entity
@Table(name = "mission_info")
public class MissionInfo implements Serializable {

    @Id
    @Column(name = "id")
    @GeneratedValue
    private Long id;

    @OneToMany(cascade={CascadeType.ALL})
    @JoinColumn(name="mission_info_id")
    private List<Mission> missions;

    public MissionInfo() {
    }
    // getters and setters
}

Mission:

@Entity
@Table(name = "mission")
public class Mission implements Serializable {

    @Id
    @Column(name = "id")
    @GeneratedValue
    private Long id;

    @Column(name = "mission_info_id")
    private Long missionInfoId;

    @ManyToOne
    @JoinColumn(name="mission_info_id", insertable=false, updatable=false)
    private MissionInfo missionInfo;

    public Mission() {
    }
    // getters and setters
}

As I understood in this scheme owner side - MissionInfo

I use following code for saving: MissionInfo info = new MissionInfo();

Mission o = new Mission();

List<Mission> list = new ArrayList<Mission>();
list.add(o);
info.setMissions(list);

MissionInfo createdMissionInfo = missionInfoRepository.save(info);

MissionInfo mi = createdMissionInfo.getMissions().get(0).getMissionInfo(); <-- NULL
Long mId = createdMissionInfo.getMissions().get(0).getMissionInfoId(); <-- NULL

Object tree saved correctly, createdMissionInfo have list with one Mission. BUT after saving single mission object (createdMissionInfo.getMissions().get(0)) have NULLs in missionInfoId and missionInfo.

It is possible to tune jpa/hibernate entities that AFTER SAVING will have actual values? Like this:

// should be a pointer to saved mission info
createdMissionInfo.getMissions().get(0).getMissionInfo(); 
// should be a id of saved mission info
createdMissionInfo.getMissions().get(0).getMissionInfoId();

Thanks!

UPDATE:

I correct entities according to kostja comment:

@Entity
@Table(name = "mission_info")
public class MissionInfo implements Serializable {

    @Id
    @Column(name = "id")
    @GeneratedValue
    private Long id;

    @OneToMany(mappedBy = "missionInfo", cascade=CascadeType.ALL)
    private List<Mission> missions;

}


@Entity
@Table(name = "mission")
public class Mission implements Serializable {

    @Id
    @Column(name = "id")
    @GeneratedValue
    private Long id;

    @ManyToOne
    @JoinColumn(name="mission_info_id")
    private MissionInfo missionInfo;

}

But when I save using this code:

MissionInfo info = new MissionInfo();
Mission o = new Mission();
List<Mission> list = new ArrayList<Mission>();
list.add(o);
info.setMissions(list);
MissionInfo createdMissionInfo = missionInfoRepository.save(info);

sql foreign key 'mission_info_id' is NULL. And my main question in not resolved:

MissionInfo mi = createdMissionInfo.getMissions().get(0).getMissionInfo(); <-- NULL

Upvotes: 0

Views: 2582

Answers (1)

kostja
kostja

Reputation: 61558

You assumptions are not entirely correct.

  • Not the MissionInfo is the owning side, but Mission. The owning side is the one storing the relationship information. In many-to-one relationships, the many side is the ownining one, since it has to store a single FK to the one side.

  • Your MissionInfo class does not participate in the same relationship as the Mission. In order to do so, remove the joinColumn annotation and add a mappedBy attribute for the missions field:

    @OneToMany(cascade=CascadeType.ALL, mappedBy="missionInfo")
    private List<Mission> missions;
    
  • You can remove the missionInfoId field from Mission. Thsi column is already present due to the joinColumn annotation on the missionInfo field.

  • If you make the joinColumn non-insertable and non-updateable, you will not be able able to add any relationship data. Remove those attributes:

    @ManyToOne
    @JoinColumn(name="mission_info_id")
    private MissionInfo missionInfo; 
    

The persisting code should work now.

EDIT: I thought that the cascade on the MissionInfo would take care of the saving. I dont know why it is not being triggered. What you can do to work around that, is to set the relationship data on the owning side, i.e. Mission. Then you don't have to rely on the cascade:

Mission o = new Mission();
o.setMissionInfo(info);
// persist Mission

Upvotes: 2

Related Questions