JPA 2,了解CascadeType.ALL和GenerationType.AUTO(EclipseLink 2.5)

时间:2016-01-29 08:13:55

标签: eclipselink jpa-2.0

我无法理解在重新启动JVM并且数据库已包含先前会话的数据时,如何正确地保留具有子实体的实体。

我大致有以下几个实体:

@Entity
public class Organization {
    ...
    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
    @JoinColumn(name = "\"ADDRESS_ID\"", nullable = false)
    private Address address;
}

@Entity
public class Address {
    ...
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "\"ADDRESS_ID\"")
    private int addressId;
    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.MERGE, optional = false)
    @JoinColumn(name = "\"ADDRESS_TYPE_ID\"", nullable = false)
    private AddressType addressType;
}

@Entity
public class AddressType {
    ...
    // Not bi-directional, so nothing special here
}

在创建地址之前,会想到地址类型存在于数据库(CascadeType.MERGE)中。使用新地址创建新组织,并且地址具有从给定选择中设置的类型。 =>当存在干净的数据库(仅存在地址类型)时,这可以正常工作。

仍在开发中,所以我不时地关闭服务器(JVM)并重新启动应用程序。然后我想在数据库中添加一个新组织,该组织已经包含以前会话中保留的数据,然后我收到以下错误:

  

异常[EclipseLink-4002](Eclipse Persistence Services - 2.5.0.v20130507-3faac2b):org.eclipse.persistence.exceptions.DatabaseException   内部异常:java.sql.SQLIntegrityConstraintViolationException:语句已中止,因为它会在唯一或主键约束或由'ADDRESS'上定义的'SQL151120084237691'标识的唯一索引中导致重复键值。   错误代码:-20001   调用:INSERT INTO“ADDRESS”(“ADDRESS_ID”,“STREET_ADDRESS”,“COUNTRY”,“ZIP_CODE”,“CITY”,“ADDRESS_TYPE_ID”)值(?,?,?,?,?,?)       bind => [2,testroad 1,Country,99999,testcity,ABCDEF-123456]

它尝试使用数据库中已存在的相同ID。如何让它意识到id已经被使用并且它应该从最后继续?

注意:
- 地址作为组织的一部分(CascadeType.ALL)而不是单独保留 - 在测试中,我将所有现有的组织加载到执行持久操作的同一个EntityManager =>组织急切地访问其地址,因此它们应该在em-cache中可用。它在单元测试中抱怨的重复address_id似乎是一个孤儿实体(也许这就是实际错误的原因?)。
- 我可以在使用Derby的单元测试中得到此错误,但使用Oracle DB的测试服务器在日志中具有这些相同的错误 - 我还尝试添加一个'find all'查询来将所有地址实体加载到执行组织持久操作的同一EntityManager的缓存中。执行'查找全部'是在执行持久之前执行=>它仍然失败。

//更新
即使我使用TableGenerator获取id值,也会发生同样的事情。

@Entity
public class Address {
...
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "addr_gen")
    @TableGenerator(name = "addr_gen", allocationSize = 1, initialValue = 100, table = "\"ADDRESS_GEN\"")
    @Column(name = "\"ADDRESS_ID\"")
    private int osoiteId;
    ...
}

生成器表已创建,但它仍为空。然而,id从初始值'100'开始运行。

更多说明:
- 当使用自定义表并为序列插入值时,地址实体的id从该值继续正确。当测试被finsihed时,表格会被清空,而表格中仍有数据=>下次会失败。 - 使用GenerationType.AUTO时,序列表获得默认序列,但在测试后它被清除(与自定义表相同)

^我想这是在测试服务器中发生的,并且可以通过在测试后不清空数据库来复制它。然而,序列表被清空。那么问题是,如何在JVM启动后同步序列表(或防止它不自行清空)?

1 个答案:

答案 0 :(得分:0)

我不知道这是一个很好的解决方案,甚至是一般的原始主题,但我设法通过为所有自动生成的id字段分别定义序列来进行某种解决方法。

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "addrSeq")
@SequenceGenerator(name = "addrSeq", sequenceName = "addr_seq", allocationSize = 10)
@Column(name = "\"ADDRESS_ID\"")
private int addressId;

它似乎有效,但我不知道为什么这种行为与使用' AUTO'? 重新启动服务器时,默认序列是否为空是否正常?