使用共享主键(双向)休眠@OneToOne。依赖实体未保留在数据库中。
Hibernate @OneToOne with Shared Primary Key(bidirectional). Dependent entity not persisted in DB.
我有两个实体 PointOfInterest(以下简称为 POI)及其地址。我想定义一个一对一的双向映射,两者之间共享主键,POI 作为所有者实体。我正在使用 postgreSQL 数据库,POI table 将 POIId 作为 PK,由数据库中定义的序列生成器生成。地址 table 的 POIId 列是地址 table 的 PK,也是 POI table 的 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
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
因此,显然休眠没有在地址 Table 中进行插入语句。我在互联网上到处搜索并比较了我的映射,它们似乎是正确的。但是,地址 table 中没有插入任何行。当我调试流程时,我发现地址对象正在被实例化和填充。请帮我弄清楚这是为什么。我能想到这一点的唯一原因是我正在使用序列生成器,并且在互联网上的所有示例和代码片段中,每个人都使用了 hibernate 的自动生成策略,所以,是因为这个吗?我不能使用自动生成密钥,我只能使用我的数据库序列。如果这些注释不起作用,请提出一些替代方案。
我正在使用 Spring 和带 spring 框架版本 4.0 的 Hibernate。5.RELEASE 和从 org.springframework.orm 获得的会话工厂。hibernate4.LocalSessionFactoryBean 和事务管理器从 org.springframework.orm.hibernate4.HibernateTransactionManager.
获得
您也不想在地址上生成 PK(即使使用 ad-hoc 生成器)。
由于这两个实体共享 PK,生成可能只发生一次。然后映射必须负责用相同的值填充 POI_ID
和 ADDRESS_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)
经过数小时的代码试验,发现这种一对一的双向映射似乎只有在指定 cascade=cascadeType.ALL 时才有效。我不希望这样,因为在保存 POI 时,地址字段由基于 POI 地理的内部 API 调用填充。因此,当有人尝试更新 POI 时,他们永远不会知道它的地址,因此它将为空。一旦更新发生,这会将特定 POI 的地址 table 行设置为空。但是,现在将解决它。但是,仍然不确定为什么必须为这种映射提供 cascadeType.ALL。这当然是合乎逻辑的,但没有解释为什么 cascadeType.PERSIST 不起作用。
此外,我使用的映射(例如 @GenericGenerator)是特定于休眠的,因此此代码以特定于休眠的方式而不是 JPA 进行映射。另外,@GenericGenerator 只是一种告诉hibernate 它应该用为POI 按顺序生成的Id 填充Address 的Id 的方法。
我有两个实体 PointOfInterest(以下简称为 POI)及其地址。我想定义一个一对一的双向映射,两者之间共享主键,POI 作为所有者实体。我正在使用 postgreSQL 数据库,POI table 将 POIId 作为 PK,由数据库中定义的序列生成器生成。地址 table 的 POIId 列是地址 table 的 PK,也是 POI table 的 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
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
因此,显然休眠没有在地址 Table 中进行插入语句。我在互联网上到处搜索并比较了我的映射,它们似乎是正确的。但是,地址 table 中没有插入任何行。当我调试流程时,我发现地址对象正在被实例化和填充。请帮我弄清楚这是为什么。我能想到这一点的唯一原因是我正在使用序列生成器,并且在互联网上的所有示例和代码片段中,每个人都使用了 hibernate 的自动生成策略,所以,是因为这个吗?我不能使用自动生成密钥,我只能使用我的数据库序列。如果这些注释不起作用,请提出一些替代方案。
我正在使用 Spring 和带 spring 框架版本 4.0 的 Hibernate。5.RELEASE 和从 org.springframework.orm 获得的会话工厂。hibernate4.LocalSessionFactoryBean 和事务管理器从 org.springframework.orm.hibernate4.HibernateTransactionManager.
获得您也不想在地址上生成 PK(即使使用 ad-hoc 生成器)。
由于这两个实体共享 PK,生成可能只发生一次。然后映射必须负责用相同的值填充 POI_ID
和 ADDRESS_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)
经过数小时的代码试验,发现这种一对一的双向映射似乎只有在指定 cascade=cascadeType.ALL 时才有效。我不希望这样,因为在保存 POI 时,地址字段由基于 POI 地理的内部 API 调用填充。因此,当有人尝试更新 POI 时,他们永远不会知道它的地址,因此它将为空。一旦更新发生,这会将特定 POI 的地址 table 行设置为空。但是,现在将解决它。但是,仍然不确定为什么必须为这种映射提供 cascadeType.ALL。这当然是合乎逻辑的,但没有解释为什么 cascadeType.PERSIST 不起作用。
此外,我使用的映射(例如 @GenericGenerator)是特定于休眠的,因此此代码以特定于休眠的方式而不是 JPA 进行映射。另外,@GenericGenerator 只是一种告诉hibernate 它应该用为POI 按顺序生成的Id 填充Address 的Id 的方法。