Hibernate @OneToOne with Shared Primary Key(bidirectional). Dependent entity not persisted in DB.

百般思念 提交于 2019-12-10 23:15:45

问题


I have two entities PointOfInterest (referred as POI from here on) and its Address. I want to define a one to one bidirectional mapping with shared primary key between the two, with POI as owner entity. I am using postgreSQL DB, POI table has POIId as PK, generated by a sequence generator defined in DB. Address table has column POIId which is the PK of Address table and also a FK to POI table's POIId column, along with other columns of its own.

**PointOfInterest.java**

@Entity
@Table(name = "\"POI\"")
public class PointOfInterest  implements Serializable {
    private static final long serialVersionUID = -5406785879200149642L;

    @Id
    @Column(name="\"POIId\"", nullable=false)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="poi_seq_poiid_generator")
    @SequenceGenerator(name = "poi_seq_poiid_generator", sequenceName = "poi_seq_poiid", allocationSize=1)
    private Long poiId;

    @OneToOne(mappedBy = "poi",cascade = CascadeType.PERSIST)
    private Address address;

    //Other fields

-----------------------------------------------------------------
    **Address.java**
    @Entity
    @Table(name="\"Address\"")
    public class Address implements Serializable{

        private static final long serialVersionUID = -7146133528411371107L;

        @Id
        @GeneratedValue(generator="sharedPrimaryKeyGenerator")
        @GenericGenerator(name="sharedPrimaryKeyGenerator",strategy="foreign",parameters =  @Parameter(name="property", value="poi"))
        @Column(name="\"POIId\"")
        private Long poiId;

       @OneToOne
       @PrimaryKeyJoinColumn
       private PointOfInterest poi;

Before saving the POI object using session.saveOrUpdate(poi). I instantiate and set all other properties of POI object. Then, obtain Address object from a separate method in my logic and do something like.

PointOfInterest poi = methodCallToGetPOI();
Address address = methodCallToGetAddress();

poi.setAddress(address);
address.setPOI(poi);

//Send POI object to DB layer to an appropriate method which does:

session.saveOrUpdate(poi);
session.flush();

When I see the query generated, I see something like this:

   Hibernate: 
   select
        nextval ('poi_seq_poiid')

 Hibernate: 
     insert 
     into
         "POI"
         ("CreatedBy", "CreatedTime", "LocationGeographic", "ModifiedBy", "ModifiedTime", "Name", "POICode", "Radius", "POIType", "POIId") 
     values
         (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

So, clearly hibernate is not making an insert statement in Address Table. I have searched everywhere on the internet and compared my mappings, they seem to be correct. However, no row is being inserted in the address table. When I debug the flow, I found that the address object is being instantiated and populated. Please help me figure out why is this. The only reason I can think of this that I am using a sequence generator and in all the examples and code snippets on the internet everybody has used hibernate's auto generation strategy, so, is it because of this? I cannot use auto gen key, I have to use my DB sequence only. If these annotations won't work, kindly suggest some alternative.

I am using Spring and Hibernate with spring framework version 4.0.5.RELEASE and a session factory obtained from org.springframework.orm.hibernate4.LocalSessionFactoryBean and transaction manager obtained from org.springframework.orm.hibernate4.HibernateTransactionManager.


回答1:


you don't want to generate the PK on Address too (even with an ad-hoc generator).

Since these two entities share the PK, the generation may happen only once. Then the mapping have to be responsible to populate both POI_ID and ADDRESS_ID with the same value.

Strange thing, @PrimaryKeyJoinColumn does not work anymore on Hibernate 5.2.2, but here is an equivalent mapping:

@Entity
@Table(name = "POI")
public class PointOfInterest implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "poi_seq_poiid_generator")
    @SequenceGenerator(name = "poi_seq_poiid_generator", sequenceName = "poi_seq_poiid", allocationSize = 1)
    @Column(name = "POI_ID")
    private Long id;

    @OneToOne(mappedBy = "poi", cascade = CascadeType.ALL, orphanRemoval = true)
    private Address address;

    @Column
    private String name;

    ...
}

@Entity
@Table(name = "Address")
public class Address implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Id
    @OneToOne
    @JoinColumn(name = "ADDRESS_ID")
    private PointOfInterest poi;

    @Column
    private String name;

    ...
}

Using Hibernate DDL generation, this is the resulting SQL:

create table Address (name varchar(255), ADDRESS_ID bigint not null, primary key (ADDRESS_ID)) ENGINE=InnoDB
create table POI (POI_ID bigint not null, name varchar(255), primary key (POI_ID)) ENGINE=InnoDB
alter table Address add constraint FK_Address_ADDRESS_ID foreign key (ADDRESS_ID) references POI (POI_ID)



回答2:


After hours of experimenting with the code, found that this one to one bidirectional mapping seems to work only when cascade=cascadeType.ALL is specified. I did not want that because, while saving POI, address field is filled from internal API calls which are based on the geographics of POI. SO, when someone tries to update POI, they will never know it's address and so it will be null. As soon as the update happens this will set the Address table row to null for that particular POI. But, will do a work around for it now. However, still not sure why it is mandatory to give cascadeType.ALL for such a mapping. It sure is logical but does not explain why cascadeType.PERSIST won't work.

Also, the mappings I used such as @GenericGenerator are hibernate specific and so this code does the mapping in a hibernate specific way rather than JPA. Also, @GenericGenerator is just a means to tell hibernate that it should fill the Id of Address with Id that has been generated for POI by its sequence.



来源:https://stackoverflow.com/questions/41629976/hibernate-onetoone-with-shared-primary-keybidirectional-dependent-entity-not

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!