【问题标题】:Why does EclipseLink support the @CascadeOnDelete with @ElementCollection?为什么 EclipseLink 支持 @CascadeOnDelete 和 @ElementCollection?
【发布时间】:2016-05-24 15:45:28
【问题描述】:

我有以下包含 @Lob 的可嵌入类:

@Embeddable
public class EntityState {

    private Integer version;
    @Lob
    @XmlJavaTypeAdapter(CharArrayAdapter.class)
    private char[] xmlState;
    ...
}

我还有以下包含上述可嵌入类的可嵌入类:

@Embeddable
public class EntityEvent {

    @NotNull
    private String note;

    private EntityState entityState;

    ...
}

最后,我有许多实体类,它们包含一个名为 history 的属性,它是 EntityEvents 的列表。下面是一个例子:

@Entity
public class Company {

    @NotNull
    @ElementCollection
    private List<EntityEvent> history;
    
    ...
}

当我在 GlassFish 4.1 中部署我的应用程序时,EclipseLink 会在我的 Derby 10.11.1.1 数据库中创建以下表:

  • 公司
  • COMPANY_HISTORY

当我创建一个新公司时,我的应用程序会创建一个 EntityEvent 并将 EntityEvent 添加到公司历史记录中。

当我修改公司时,我的应用程序会执行以下操作:

  • 创建一个 EntityState 对象并将 xmlState 属性设置为未修改实体的 XML 表示形式。
  • 创建一个包含上述 EntityState 的 EntityEvent 对象。
  • 将 EntityEvent 添加到公司历史记录中。

问题是,当我尝试删除具有多个 EntityEvents 历史记录的实体时,我收到以下错误:

异常 [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException 内部异常: java.sql.SQLSyntaxErrorException: 'CLOB (UCS_BASIC)' 和不支持“CLOB (UCS_BASIC)”。类型必须具有可比性。字符串类型也必须有匹配的排序规则。如果排序规则不匹配,一个可能的解决方案是将操作数强制转换为默认排序规则(例如 SELECT tablename FROM sys.systables WHERE CAST(tablename AS VARCHAR(128)) = 'T1')

错误代码:20000 调用:DELETE FROM Company_HISTORY WHERE ((((((((((CHANGES = ?) AND (CLIENTTYPE = ?)) AND (CREATED = ?)) AND (IPADDRESS = ?)) AND (注意 = ?)) AND (TYPE = ?)) AND (VERSION = ?)) AND (XMLSTATE = ?)) AND (CREATER_ID = ?)) AND (Company_ID = ?)) bind => [10 个参数绑定]

我在以下链接中找到了对该问题的一些参考:

我尝试了上面引用的 stackoverflow 文章中描述的 @OrderColumn 技术,但这在 EclipseLink 中不起作用。

对我有用的解决方案是将 EclipseLink 非标准 @CascadeOnDelete 注释添加到我的实体中,如下所示:

@Entity
public class Company {

    @NotNull
    @ElementCollection
    @CascadeOnDelete
    private List<EntityEvent> history;
    
    ...
}

执行此更改并重建我的数据库后,我的 COMPANY_HISTORY 表有一个新定义:

  • 没有@CascadeOnDelete
    • ALTER TABLE COMPANY_HISTORY 添加约束 CMPNYHISTORYCMPNYD 外键 (COMPANY_ID) 引用公司 (ID);
  • 使用@CascadeOnDelete
    • ALTER TABLE COMPANY_HISTORY 添加约束 CMPNYHISTORYCMPNYD 外键 (COMPANY_ID) 引用公司 (ID) 删除级联

我的问题的解决方案让我感到惊讶,因为它似乎是重复的。我的理解是,当实体被删除时,JPA 应该删除与实体关联的所有可嵌入对象。 EclipseLink 具有以下链接中记录的非标准注释这一事实使我认为 EclipseLink 有一个错误,而不是修复该错误,而是创建了一个新的 @CascadeOnDelete 注释,以便该错误将被数据库级联删除功能所掩盖。

所以我的问题是为什么。为什么 EclipseLink 支持 @CascadeOnDelete 和 @ElementCollection?

【问题讨论】:

    标签: jpa eclipselink cascade


    【解决方案1】:

    CascadeOnDelete 只是一个功能,它指定您在表中指定了“On Delete Cascade”选项,因此 JPA 不需要发出 SQL 来删除相应的引用。此 SQL 可以应用于任何引用,这就是 CascadeOnDelete 处理元素集合映射和任何其他引用映射的原因。

    您的问题与数据库中的 lob 比较限制有关,并且由于没有 ID 字段来唯一标识元素集合行,因此此限制会干扰 EclipseLink 尝试确保仅删除所需行的方式。如果您愿意在表中添加订单列,为什么不直接将 EntityEvent 设为实体?或者,您可以按照here 的描述自定义 EclipseLink,以便它使用外键和 orderBy 字段或字段的任何组合作为主键来唯一标识行,而不是包括 lob 字段。

    【讨论】:

    • @CascadeOnDelete 似乎是我的问题的完美解决方案,因为它提供了在删除 Company 实体时从 COMPANY_HISTORY 表中删除所有链接嵌入的环境,并减少了执行此操作所需的 SQL任务。但是,我很困惑为什么这是必要的。删除实体时,必须删除可嵌入对象。为什么在删除公司实体时 EclipseLink 不简单地发送“从 COMPANY_HISTORY 中删除,其中 company_id = ”命令?为什么要比较所有领域?感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2012-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-24
    • 2012-01-14
    • 1970-01-01
    相关资源
    最近更新 更多