【问题标题】:Hibernate @OneToOne with Shared Primary Key(bidirectional). Dependent entity not persisted in DB.使用共享主键(双向)休眠@OneToOne。依赖实体未保留在数据库中。
【发布时间】:2017-01-13 08:08:16
【问题描述】:

我有两个实体 PointOfInterest(从这里称为 POI)及其地址。我想用两者之间的共享主键定义一对一的双向映射,POI 作为所有者实体。我正在使用 postgreSQL DB,POI 表的 POIId 为 PK,由 DB 中定义的序列生成器生成。地址表有 POIId 列,它是地址表的 PK,也是 POI 表的 POIId 列的 FK,以及它自己的其他列。

**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;

在使用 session.saveOrUpdate(poi) 保存 POI 对象之前。我实例化并设置 POI 对象的所有其他属性。然后,从我的逻辑中的单独方法获取地址对象并执行类似的操作。

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();

当我看到生成的查询时,我看到如下内容:

   Hibernate: 
   select
        nextval ('poi_seq_poiid')

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

因此,显然 hibernate 并没有在地址表中进行插入语句。我在互联网上到处搜索并比较了我的映射,它们似乎是正确的。但是,地址表中没有插入任何行。当我调试流程时,我发现地址对象正在被实例化和填充。请帮我弄清楚这是为什么。我能想到的唯一原因是我使用了序列生成器,并且在互联网上的所有示例和代码 sn-ps 中,每个人都使用了 hibernate 的自动生成策略,那么,是因为这个吗?我不能使用自动生成密钥,我只能使用我的数据库序列。如果这些注释不起作用,请提出一些替代方案。

我正在使用 Spring 和 Hibernate 以及 Spring 框架版本 4.0.5.RELEASE 以及从 org.springframework.orm.hibernate4.LocalSessionFactoryBean 获得的会话工厂和从 org.springframework.orm.hibernate4.HibernateTransactionManager 获得的事务管理器。

【问题讨论】:

    标签: java spring hibernate one-to-one bidirectional


    【解决方案1】:

    您也不想在 Address 上生成 PK(即使使用临时生成器)。

    由于这两个实体共享 PK,因此生成可能只发生一次。然后映射必须负责用相同的值填充POI_IDADDRESS_ID

    奇怪的是,@PrimaryKeyJoinColumn 在 Hibernate 5.2.2 上不再工作,但这里有一个等效的映射:

    @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;
    
        ...
    }
    

    使用 Hibernate DDL 生成,这是生成的 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)
    

    【讨论】:

    • 您好,感谢您的回答,但问题仍未解决。只有 PointOfItnerest 实体被填充。您能否详细说明一下,为什么不需要临时生成器?
    • 你说得对,我在 Hibernate 5.2.2 上对其进行了测试,但它不再工作了。有关负责 ID 引用的备用映射,请参阅更新。请注意,我只测试了 DDL 生成,不知道地址 id 是否在运行时自动填充(但应该)。
    • 感谢您的编辑 :) 我确实做到了。刚刚将 cascadeType 更改为 cascadeType.ALL 并且突然插入地址行。
    【解决方案2】:

    经过数小时的代码试验,发现这种一对一的双向映射似乎只有在指定 cascade=cascadeType.ALL 时才有效。我不希望这样,因为在保存 POI 时,地址字段由基于 POI 地理的内部 API 调用填充。因此,当有人尝试更新 POI 时,他们永远不会知道它的地址,因此它将为空。一旦发生更新,这会将特定 POI 的地址表行设置为空。但是,现在会解决它。但是,仍然不确定为什么必须为此类映射提供 cascadeType.ALL。这当然是合乎逻辑的,但没有解释为什么 cascadeType.PERSIST 不起作用。

    此外,我使用的映射(例如 @GenericGenerator)是特定于休眠的,因此此代码以特定于休眠的方式而不是 JPA 进行映射。此外,@GenericGenerator 只是一种告诉 hibernate 它应该用已按其序列为 POI 生成的 Id 填充 Address 的 Id 的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-14
      • 2014-03-27
      • 2018-02-11
      • 1970-01-01
      • 2014-02-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多