简体   繁体   中英

Hibernate @OneToMany does not save foreign key

I am having an issue saving a one to many relationship. It seems that it is saving the elements of the list on the table, but it does not save the foreign key in the proper table.

@Entity
@Table(name = "tbl_applications")
public class ApplicationEntity
{
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "intAPI", referencedColumnName = "intCode")
    private ApiResourceEntity objApi;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "strApplication")
    List<ApplicationRoleEntity> colApplicationRoles = new ArrayList<ApplicationRoleEntity>();
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "strApplication")
    List<URLEntity> colUrls = new ArrayList<URLEntity>();
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "strApplication")
    List<KeyCredentialEntity> colKeyCredentials = new ArrayList<KeyCredentialEntity>();
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "strApplication")
    List<PasswordCredentialEntity> colPasswordCredentials = new ArrayList<PasswordCredentialEntity>();
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "strApplication")
    List<RequiredResourceAccessEntity> colRequiredResourceAccess = new ArrayList<RequiredResourceAccessEntity>();
}
@Entity
@Table(name = "tbl_apiresources")
public class ApiResourceEntity
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "intCode")
    private Integer intCode;
    private Integer intRequestedAccessTokenVersion;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "intCode")
    private List<PermissionScopeEntity> colOauth2PermissionsScope = new ArrayList<PermissionScopeEntity>();
}
@Entity
@Table(name = "tbl_permissionsscope")
public class PermissionScopeEntity
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "intCode")
    private Integer intCode;
    private String strAdminConsentDescription;
    private String strAdminConsentDisplayName;
    private String strID;
    private Boolean bolIsEnabled;
    private String strOrigin;
    private String strType;
    private String strUserConsentDescription;
    private String strUserConsentDisplayName;
    private String strValue;
}

an example of what it saved on the database:

tbl_applications
intAPI = 1
tbl_apiresources
intCode = 1
intRequestAccessTokenVersion = 2
intOauth2PermissionScopes = NULL
tbl_permissionsscope
intCode = 1
strAdminConsentDescription = xxxxxx
strAdminConsentDisplayName = xxxxxx
strID = xxxxxx
bolIsEnabled = xxxxxx
strOrigin = xxxxxx
strType = xxxxxx
strUserConsentDescription = xxxxxx
strUserConsentDisplayName = xxxxxx
strValue = xxxxxx

it saves the elements of the list (Permissions Scope), however the foreign key is not save on the table tbl_apiresources. Maybe because the identifier is an Identity Field (Self Generated)

All the @OneToMany relationships of the ApplicationEntity Object are saved properly. @OneToMany can not point to @ManyToOne as I get a circular reference exception

When you specify only a @OneToMany relationship, ie, unidirectional from Parent to Child, hibernate will implement the mapping with a join table. When you save a parent/child relation the keys are inserted into the join table:

@Entity
public class Parent {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    @OneToMany
    Set<Child> children;
}
@Entity
public class Child {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
}

Results in the join table parent_child

Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table child (id bigint not null, primary key (id))
Hibernate: create table parent (id bigint not null, primary key (id))
Hibernate: create table parent_children (parent_id bigint not null, children_id bigint not null, primary key (parent_id, children_id))
Hibernate: alter table parent_children add constraint UK_fh9rqlaf2416b31ec7n92nrfh unique (children_id)
Hibernate: alter table parent_children add constraint FK2li53iimvay1c1bjvc1hed3gl foreign key (children_id) references child
Hibernate: alter table parent_children add constraint FKdnxvj4hlnv40nix37bpjsvecn foreign key (parent_id) references parent
Hibernate: call next value for hibernate_sequence
Hibernate: insert into child (id) values (?)
Hibernate: call next value for hibernate_sequence
Hibernate: insert into parent (id) values (?)
Hibernate: insert into parent_children (parent_id, children_id) values (?, ?)

If you want the FK to be in the child table, and you do, then define a unidirectional mapping from the child to the parent.

@Entity
public class Parent {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
}
@Entity
public class Child {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;   
    @ManyToOne
    Parent parent;
}

Which does not create a join table

Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table child (id bigint not null, parent_id bigint, primary key (id))
Hibernate: create table parent (id bigint not null, primary key (id))
Hibernate: alter table child add constraint FK7dag1cncltpyhoc2mbwka356h foreign key (parent_id) references parent
Hibernate: call next value for hibernate_sequence
Hibernate: insert into parent (id) values (?)
Hibernate: call next value for hibernate_sequence
Hibernate: insert into child (parent_id, id) values (?, ?)

If you want a bidirectional mapping than add both but understand that you should do persistence like the second example and use the parent Set<Child> children for queries only for performance reasons.

@Entity
public class Parent {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    @OneToMany
    Set<Child> children;
}
@Entity
public class Child {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    @ManyToOne
    Parent parent;
}

After inserting, then query with left outer join fetch or better by using an EntityGraph .

"from Parent p left outer join fetch p.children where p.id = :id"

Kindly find below the modifications that I made to the code

@Entity
@Table(name = "tbl_applications")
public class ApplicationEntity
{
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "intAPI", referencedColumnName = "intCode")
    private ApiResourceEntity objApi;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "strApplication")
    List<ApplicationRoleEntity> colApplicationRoles = new ArrayList<ApplicationRoleEntity>();
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "strApplication")
    List<URLEntity> colUrls = new ArrayList<URLEntity>();
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "strApplication")
    List<KeyCredentialEntity> colKeyCredentials = new ArrayList<KeyCredentialEntity>();
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "strApplication")
    List<PasswordCredentialEntity> colPasswordCredentials = new ArrayList<PasswordCredentialEntity>();
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "strApplication")
    List<RequiredResourceAccessEntity> colRequiredResourceAccess = new ArrayList<RequiredResourceAccessEntity>();
}
@Entity
@Table(name = "tbl_apiresources")
public class ApiResourceEntity
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "intCode")
    private Integer intCode;
    private Integer intRequestedAccessTokenVersion;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "intCode")
    private List<PermissionScopeEntity> colOauth2PermissionsScope = new ArrayList<PermissionScopeEntity>();
}
@Entity
@Table(name = "tbl_permissionsscope")
public class PermissionScopeEntity
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "intCode")
    private Integer intCode;
    private String strAdminConsentDescription;
    private String strAdminConsentDisplayName;
    private String strID;
    private Boolean bolIsEnabled;
    private String strOrigin;
    private String strType;
    private String strUserConsentDescription;
    private String strUserConsentDisplayName;
    private String strValue;
    @ManyToOne
    private ApiResourceEntity objApi;
}

If I only let the annotation @OneToMany on the ApiResourceEntity I got the exception mentioned above

java.lang.IllegalStateException: org.hibernate.TransientObjectException: object circular reference on transient instance - circular reference on transient instance: azure.entities.PermissionScopeEntity

When I add the cascade = CascadeType.ALL I got the following error:

org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Invalid column name 'objApi_intCode'.

with and without the mappedBy = "intCode"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM