为什么 EclipseLink 支持 @CascadeOnDelete 和 @ElementCollection?
Why does EclipseLink support the @CascadeOnDelete with @ElementCollection?
我有以下可嵌入的 class,其中包含一个 @Lob:
@Embeddable
public class EntityState {
private Integer version;
@Lob
@XmlJavaTypeAdapter(CharArrayAdapter.class)
private char[] xmlState;
...
}
我还有以下可嵌入的 class,其中包含上述可嵌入的:
@Embeddable
public class EntityEvent {
@NotNull
private String note;
private EntityState entityState;
...
}
最后,我有许多实体 classes,它们包含一个名为 属性 的 history,它是一个 EntityEvents 列表。下面是一个例子:
@Entity
public class Company {
@NotNull
@ElementCollection
private List<EntityEvent> history;
...
}
当我在 GlassFish 4.1 中部署我的应用程序时,EclipseLink 在我的 Derby 10.11.1.1 数据库中创建了以下 table:
- 公司
- COMPANY_HISTORY
当我创建新公司时,我的应用程序会创建一个 EntityEvent 并将该 EntityEvent 添加到公司历史记录中。
当我修改公司时,我的应用程序执行以下操作:
- 创建一个 EntityState 对象并将 xmlState 属性 设置为未修改实体的 XML 表示。
- 创建一个包含上述 EntityState 的 EntityEvent 对象。
- 将 EntityEvent 添加到公司历史记录中。
问题是,当我尝试删除具有多个 EntityEvents 历史记录的实体时,我收到以下错误:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: java.sql.SQLSyntaxErrorException: Comparisons between 'CLOB (UCS_BASIC)' and 'CLOB (UCS_BASIC)' are not supported. Types must be comparable. String types must also have matching collation. If collation does not match, a possible solution is to cast operands to force them to the default collation (e.g. SELECT tablename FROM sys.systables WHERE CAST(tablename AS VARCHAR(128)) = 'T1')
Error Code: 20000 Call: DELETE FROM Company_HISTORY WHERE ((((((((((CHANGES = ?) AND (CLIENTTYPE = ?)) AND (CREATED = ?)) AND (IPADDRESS = ?)) AND (NOTE = ?)) AND (TYPE = ?)) AND (VERSION = ?)) AND (XMLSTATE = ?)) AND (CREATER_ID = ?)) AND (Company_ID = ?)) bind => [10 parameters bound]
我在以下 link 中找到了一些关于该问题的参考资料:
- Hibernate - @ElementCollection - Strange delete/insert behavior
- http://eclipse.1072660.n5.nabble.com/Customizing-delete-calls-before-updating-a-ElementCollection-td7312.html
我尝试了上面引用的 Whosebug 文章中描述的 @OrderColumn 技术,但这在 EclipseLink 中不起作用。
对我有用的解决方案是将 EclipseLink 非标准 @CascadeOnDelete 注释添加到我的实体中,如下所示:
@Entity
public class Company {
@NotNull
@ElementCollection
@CascadeOnDelete
private List<EntityEvent> history;
...
}
执行此更改并重建我的数据库后,我的 COMPANY_HISTORY table 有了新定义:
- 没有@CascadeOnDelete
- 更改 TABLE COMPANY_HISTORY 添加约束 CMPNYHISTORYCMPNYD 外键 (COMPANY_ID) 参考公司 (ID);
- 使用@CascadeOnDelete
- 更改TABLECOMPANY_HISTORY添加约束CMPNYHISTORYCMPNYD外键(COMPANY_ID)引用公司(ID)删除级联;
我的问题的解决方案让我感到惊讶,因为它似乎是重复的。我的理解是 JPA 应该在删除实体时删除与该实体关联的所有可嵌入对象。事实上,EclipseLink 有这个非标准注释,如以下 link 中所述,这让我认为 EclipseLink 有一个错误,而不是修复错误,而是创建了一个新的 @CascadeOnDelete 注释,这样错误就会被数据库级联覆盖删除功能。
所以我的问题是为什么。为什么 EclipseLink 支持 @CascadeOnDelete 和 @ElementCollection?
CascadeOnDelete 只是一个功能,指定您在 table 中指定了 "On Delete Cascade" 选项,这样 JPA 就不需要发出 SQL 来删除相应的引用. SQL 可以应用于任何引用,这就是为什么 CascadeOnDelete 适用于元素集合映射和任何其他引用映射的原因。
您的问题与数据库中的 lob 比较限制有关,并且由于没有 ID 字段来唯一标识元素集合行,因此此限制会干扰 EclipseLink 尝试确保仅删除所需元素的方式行。如果您愿意向 table 添加一个订单列,为什么不直接将 EntityEvent 设为一个实体呢?或者您可以按照 here 所述自定义 EclipseLink,以便它使用外键和一个 orderBy 字段或任何字段组合作为主键来唯一标识行,而不是包括 lob 字段。
我有以下可嵌入的 class,其中包含一个 @Lob:
@Embeddable
public class EntityState {
private Integer version;
@Lob
@XmlJavaTypeAdapter(CharArrayAdapter.class)
private char[] xmlState;
...
}
我还有以下可嵌入的 class,其中包含上述可嵌入的:
@Embeddable
public class EntityEvent {
@NotNull
private String note;
private EntityState entityState;
...
}
最后,我有许多实体 classes,它们包含一个名为 属性 的 history,它是一个 EntityEvents 列表。下面是一个例子:
@Entity
public class Company {
@NotNull
@ElementCollection
private List<EntityEvent> history;
...
}
当我在 GlassFish 4.1 中部署我的应用程序时,EclipseLink 在我的 Derby 10.11.1.1 数据库中创建了以下 table:
- 公司
- COMPANY_HISTORY
当我创建新公司时,我的应用程序会创建一个 EntityEvent 并将该 EntityEvent 添加到公司历史记录中。
当我修改公司时,我的应用程序执行以下操作:
- 创建一个 EntityState 对象并将 xmlState 属性 设置为未修改实体的 XML 表示。
- 创建一个包含上述 EntityState 的 EntityEvent 对象。
- 将 EntityEvent 添加到公司历史记录中。
问题是,当我尝试删除具有多个 EntityEvents 历史记录的实体时,我收到以下错误:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: java.sql.SQLSyntaxErrorException: Comparisons between 'CLOB (UCS_BASIC)' and 'CLOB (UCS_BASIC)' are not supported. Types must be comparable. String types must also have matching collation. If collation does not match, a possible solution is to cast operands to force them to the default collation (e.g. SELECT tablename FROM sys.systables WHERE CAST(tablename AS VARCHAR(128)) = 'T1')
Error Code: 20000 Call: DELETE FROM Company_HISTORY WHERE ((((((((((CHANGES = ?) AND (CLIENTTYPE = ?)) AND (CREATED = ?)) AND (IPADDRESS = ?)) AND (NOTE = ?)) AND (TYPE = ?)) AND (VERSION = ?)) AND (XMLSTATE = ?)) AND (CREATER_ID = ?)) AND (Company_ID = ?)) bind => [10 parameters bound]
我在以下 link 中找到了一些关于该问题的参考资料:
- Hibernate - @ElementCollection - Strange delete/insert behavior
- http://eclipse.1072660.n5.nabble.com/Customizing-delete-calls-before-updating-a-ElementCollection-td7312.html
我尝试了上面引用的 Whosebug 文章中描述的 @OrderColumn 技术,但这在 EclipseLink 中不起作用。
对我有用的解决方案是将 EclipseLink 非标准 @CascadeOnDelete 注释添加到我的实体中,如下所示:
@Entity
public class Company {
@NotNull
@ElementCollection
@CascadeOnDelete
private List<EntityEvent> history;
...
}
执行此更改并重建我的数据库后,我的 COMPANY_HISTORY table 有了新定义:
- 没有@CascadeOnDelete
- 更改 TABLE COMPANY_HISTORY 添加约束 CMPNYHISTORYCMPNYD 外键 (COMPANY_ID) 参考公司 (ID);
- 使用@CascadeOnDelete
- 更改TABLECOMPANY_HISTORY添加约束CMPNYHISTORYCMPNYD外键(COMPANY_ID)引用公司(ID)删除级联;
我的问题的解决方案让我感到惊讶,因为它似乎是重复的。我的理解是 JPA 应该在删除实体时删除与该实体关联的所有可嵌入对象。事实上,EclipseLink 有这个非标准注释,如以下 link 中所述,这让我认为 EclipseLink 有一个错误,而不是修复错误,而是创建了一个新的 @CascadeOnDelete 注释,这样错误就会被数据库级联覆盖删除功能。
所以我的问题是为什么。为什么 EclipseLink 支持 @CascadeOnDelete 和 @ElementCollection?
CascadeOnDelete 只是一个功能,指定您在 table 中指定了 "On Delete Cascade" 选项,这样 JPA 就不需要发出 SQL 来删除相应的引用. SQL 可以应用于任何引用,这就是为什么 CascadeOnDelete 适用于元素集合映射和任何其他引用映射的原因。
您的问题与数据库中的 lob 比较限制有关,并且由于没有 ID 字段来唯一标识元素集合行,因此此限制会干扰 EclipseLink 尝试确保仅删除所需元素的方式行。如果您愿意向 table 添加一个订单列,为什么不直接将 EntityEvent 设为一个实体呢?或者您可以按照 here 所述自定义 EclipseLink,以便它使用外键和一个 orderBy 字段或任何字段组合作为主键来唯一标识行,而不是包括 lob 字段。