1

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

    @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

我的问题是表上的 UPDATE 语句TECAttachment更新了具有给定 Id 的所有记录,而不仅仅是那些与 Entity 相关的记录TECPRHTABidParticipationItem。我想我必须重写 sql 语句DeleteObjectQuery,或者DataModifyQuery但不知道如何。

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

预先感谢!

4

1 回答 1

1

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

  • 我的附件集合是一个单向的自定义@OneToMany 关系。
  • DescriptorCustomizer我在尝试使用集合定制器(通过注释与我的集合关联并实现接口的类)来实现我的目标的正确路径。
  • 我的定制器不仅需要自定义选择“子”记录的方式,还需要自定义删除父记录时的操作。
  • 这样做的方法是覆盖默认removeAllTargetsQuery属性,通过该方法提供一个新的自定义查询mapping.setCustomRemoveAllTargetsQuery(DataModifyQuery)
  • 最困难的部分是了解底层 eclipselink 实现如何将参数(which、order、type 等)发送到 custom 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);
    }
}
于 2021-08-17T01:02:25.660 回答