【问题标题】:Cascade delete problem on **custom** unidirectional @OneToMany relationship (JPA eclipselink)**custom** 单向@OneToMany 关系上的级联删除问题(JPA eclipselink)
【发布时间】:2021-10-18 19:08:52
【问题描述】:

我在删除具有单向 @OneToMany custom 关系的实体时遇到问题。 这里是“基础”实体的关系(仅相关列):

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "Id", updatable = false)
    protected Integer id;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "ObjectId", referencedColumnName = "Id")
    protected Collection<Attachment> attachmentsCollection;

这里是“子”实体的相关列:

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "Id", updatable = false)
    protected Integer id;


    @NotNull
    @Basic(optional = false)
    @Size(min = 1, max = 64)
    @Column(name = "ObjectTable", updatable = false)
    protected String objectTable;


    @Basic(optional = false)
    @Column(name = "ObjectId", updatable = false)
    protected Integer objectId;


    @NotNull
    @Basic(optional = false)
    @Lob
    @Column(name = "Data")
    protected byte[] data;


    @NotNull
    @Basic(optional = false)
    @Column(name = "SizeInBytes")
    protected Long sizeInBytes;


    @NotNull
    @Basic(optional = false)
    @Size(min = 1, max = 128)
    @Column(name = "Name")
    protected String name;

这里解释了为什么这是一个自定义关系: 基础对象是所有实体的超类。除了自己的 id 之外,它还有机会通过附件集合关联任意数量的附件。由于 Id 在表(实体)之间不是唯一的,因此需要在子表(附件表)中添加一个附加列。此附加列 (ObjectTable) 标识拥有附件的实体种类。将此列添加到entity'id(ObjectId)列中,关系就完成了:

假设实体Invoice的记录99有2个附件(附件'Z'和附件'Y'):

Table Invoice
-------------
  Id    ColumnA    ColumnB      ColumnC...
  99    'xyz'      '2343'       'zyx'
  .
  .


Table Attachment
----------------
  Id    ObjectTable  ObjectId   Data       SizeInBytes      Name
  43542 'Invoice'    99         11100110   437834           'Z.pdf'
  43543 'Invoice'    99         101110     867454           'Y.pdf'

我设法用映射定制器加载了关系:

    public    static final  String              TABLENAME                   = "TECAttachment";
    public    static final  String              OBJECTIDFIELDNAME           = TABLENAME + ".ObjectId";
    public    static final  String              OBJECTTABLEFIELDNAME        = TABLENAME + ".ObjectTable";


    // Customize how records are selecting inside entity's attachments collection: include the entities table name
    @Override
    public void customize(ClassDescriptor descriptor) {
        OneToManyMapping    mapping     = (OneToManyMapping)descriptor.getMappingForAttributeName(AttachmentEntitySessionCustomizer.ATTACHMENTSCOLLECTIONNAME);

        ExpressionBuilder   eb          = new ExpressionBuilder();
        Expression          eObjectIdNotNull    = eb.getField(AttachmentEntitySessionCustomizer.OBJECTIDFIELDNAME).notNull();
        Expression          eObjectId           = eb.getField(AttachmentEntitySessionCustomizer.OBJECTIDFIELDNAME).equal(eb.getParameter(descriptor.getPrimaryKeyFields().get(0)));
        Expression          eObjectTable        = eb.getField(AttachmentEntitySessionCustomizer.OBJECTTABLEFIELDNAME).equalsIgnoreCase(descriptor.getTableName());
        mapping.setSelectionCriteria(eObjectIdNotNull.and(eObjectId.and(eObjectTable)));
    }

...但我在删除任何实体时遇到问题。由于我仍然不明白的原因,JPA 正在附件表上执行更新语句,而不考虑 ObjectTable 列。以下是我从表 PRHTABidParticipationItem 中删除记录时发生的情况:

Finest: Execute query DeleteObjectQuery(com.tec.uportal.prhta.model.bid.participation.PRHTABidParticipationItem[ id=24 ])
Finest: Execute query DataModifyQuery()
Fine: UPDATE TECAttachment SET ObjectId = ? WHERE (ObjectId = ?)
    bind => [null, 24]
Fine: DELETE FROM TECPRHTABidParticipationItem WHERE (Id = ?)
    bind => [24]
Finer: end unit of work flush
Finer: resume unit of work
Finer: begin unit of work commit
Finer: commit transaction

我的问题是表TECAttachment 上的UPDATE 语句更新了具有给定ID 的所有记录,而不仅仅是与实体TECPRHTABidParticipationItem 相关的记录。 我想我必须重写 sql 语句 DeleteObjectQueryDataModifyQuery 但不知道如何。

任何帮助将不胜感激。 我正在使用 eclipselink-2.7.4

提前致谢!

【问题讨论】:

    标签: jpa relationship eclipselink cascade customizer


    【解决方案1】:

    经过大量搜索、挖掘和阅读后,我设法解决了我的问题。 基本上:

    • 我的附件集合是一个单向的自定义 @OneToMany 关系。
    • 我在尝试使用集合定制器(一个通过注释与我的集合关联并实现 DescriptorCustomizer 接口的类)来实现我的目标的正确路径。
    • 我的定制器不仅需要自定义选择“子”记录的方式,还需要自定义删除父记录时的操作。
    • 这样做的方法是覆盖默认的removeAllTargetsQuery 属性,通过mapping.setCustomRemoveAllTargetsQuery(DataModifyQuery) 方法提供一个新的custom 查询。
    • 最困难的部分是了解底层 eclipselink 实现如何将参数(which、order、type 等)发送到自定义 DataModifyQuery 我必须下载 EclipseLink 的 JPA 实现的源代码并弄清楚事情是如何完成的......

    最后,感谢以下简单的DescriptorCustomizer

    package com.tec.uportal.model.customizer;
    
    import org.eclipse.persistence.config.DescriptorCustomizer;
    import org.eclipse.persistence.descriptors.ClassDescriptor;
    import org.eclipse.persistence.expressions.Expression;
    import org.eclipse.persistence.expressions.ExpressionBuilder;
    import org.eclipse.persistence.internal.helper.DatabaseField;
    import org.eclipse.persistence.mappings.OneToManyMapping;
    import org.eclipse.persistence.queries.DataModifyQuery;
    
    
    
    /**
     *
     * @author MarcB
     */
    public class AttachmenstCollectionAttributeCustomizer implements DescriptorCustomizer {
        public    static final  String              ATTACHMENTS__COLLECTION_NAME                = "attachmentsCollection";
        public    static final  String              ATTACHMENTS_TABLE_OBJECT_TABLE__FIELD_NAME  = "ObjectTable";
    
    
    
    
        // Customize attachments collection mapping
        @Override
        public void customize(ClassDescriptor descriptor) {
            // Customize how records are selected inside parent entity's attachments collection: include in the WHERE clause the column ObjectTable
            OneToManyMapping    mapping     = (OneToManyMapping)descriptor.getMappingForAttributeName(ATTACHMENTS__COLLECTION_NAME);
            ExpressionBuilder   eb          = new ExpressionBuilder();
            Expression          eObjectId           = eb.getField(mapping.getTargetForeignKeyFields().get(0).getQualifiedName()).equal(eb.getParameter(descriptor.getPrimaryKeyFields().get(0)));
            Expression          eObjectTable        = eb.getField(mapping.getTargetForeignKeyFields().get(0).getTable().getQualifiedName() + "." + ATTACHMENTS_TABLE_OBJECT_TABLE__FIELD_NAME).equalsIgnoreCase(descriptor.getTable(descriptor.getTableName()).getQualifiedName());
            mapping.setSelectionCriteria(eObjectId.and(eObjectTable));
    
    
            // Customize what must be done (delete childs) when parent entity is deleted
            DataModifyQuery     dmf                 = new DataModifyQuery("DELETE " + mapping.getTargetForeignKeyFields().get(0).getTable().getQualifiedName() + " WHERE " + mapping.getTargetForeignKeyFields().get(0).getName() + " = #" + mapping.getTargetForeignKeyFields().get(0).getName() + " AND " + ATTACHMENTS_TABLE_OBJECT_TABLE__FIELD_NAME + " = '" + descriptor.getTableName() + "'");
            mapping.setCustomRemoveAllTargetsQuery(dmf);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-10-07
      • 1970-01-01
      • 1970-01-01
      • 2014-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多