代码之家  ›  专栏  ›  技术社区  ›  Ville Myrskyneva

JPA 2,理解CascadeType。ALL和GenerationType。自动(EclipseLink 2.5)

  •  1
  • Ville Myrskyneva  · 技术社区  · 9 年前

    我很难理解当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 调用:插入“地址”(“ADDRESS_ID”、“STREET_ADDRESS”、“COUNTRY”、“ZIP_CODE”、“CITY”和“ADDRESS_TYPE_ID”)值(?、?、 绑定=>[2,测试道路1,国家,99999,测试城市,ABCDEF-123456]

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

    笔记:
    -地址作为组织的一部分(CascadeType.ALL)而不是单独保存。
    -在测试中,我将所有现有组织加载到执行持久化操作的同一EntityManager中=>该组织急切地访问其地址,因此它们应该在em缓存中可用。它在单元测试中抱怨的重复address_id似乎是一个孤立的实体(也许这实际上是错误的原因?)。
    -我可以在使用Derby的单元测试中得到这个错误,但使用OracleDB的测试服务器在日志中也有这些错误。
    -我还尝试添加“查找所有”查询,将所有地址实体加载到执行组织持久化操作的同一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将从该值正确地继续。测试完成后,表将被清空,而表中仍有数据=>下次会失败。 -使用GenerationType时。AUTO,序列表得到一个默认序列,但在测试后它被清除(与自定义表相同)

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

    1 回复  |  直到 9 年前
        1
  •  0
  •   Ville Myrskyneva    9 年前

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

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

    它似乎有效,但我不知道为什么它的行为与使用“AUTO”有本质区别? 重启服务器时默认序列为空是正常的吗?