引用关系维护

August 03, 2025 / Administrator / 7阅读 / 0评论/ 分类: Java

引用关系维护

在业务中,经常出现以下这种情形:

A--->B--->C

A引用B,B引用C。

A可能有增删改查接口,B也有增删改查接口。


此时,如果删除B,会导致A业务在展示时报错。另外,如果在删除B时,不删除C,就会导致C是一个孤立的脏数据,永远存在于数据库中,但是又没有用。


为了解决上面的2个问题,这里提供了一个表,讲述的是,引用者和资源之间的关系。

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[busi_refer_resource]') AND type in (N'U')) DROP TABLE [dbo].[busi_refer_resource];
CREATE TABLE [dbo].[busi_refer_resource](
    id VARCHAR(50) NOT NULL,
    referer_table_name VARCHAR(200) NOT NULL,
    referer_column_name VARCHAR(200) NOT NULL,
    referer_busi_name VARCHAR(200) NOT NULL,
    resource_table_name VARCHAR(200) NOT NULL,
    resource_column_name VARCHAR(200) NOT NULL,
    resource_busi_name VARCHAR(200) NOT NULL,
    before_delete_res INT NOT NULL,
    after_delete_res INT NOT NULL,
    error_scene_type INT,
    PRIMARY KEY (id)
);

EXEC sp_addextendedproperty 'MS_Description', '业务中引用资源', 'SCHEMA', dbo, 'table', busi_refer_resource, null, null;
EXEC sp_addextendedproperty 'MS_Description', '主键', 'SCHEMA', dbo, 'table', busi_refer_resource, 'column', id;
EXEC sp_addextendedproperty 'MS_Description', '引用者的表名', 'SCHEMA', dbo, 'table', busi_refer_resource, 'column', referer_table_name;
EXEC sp_addextendedproperty 'MS_Description', '引用者的字段名', 'SCHEMA', dbo, 'table', busi_refer_resource, 'column', referer_column_name;
EXEC sp_addextendedproperty 'MS_Description', '引用者的业务名称', 'SCHEMA', dbo, 'table', busi_refer_resource, 'column', referer_busi_name;
EXEC sp_addextendedproperty 'MS_Description', '资源的表名', 'SCHEMA', dbo, 'table', busi_refer_resource, 'column', resource_table_name;
EXEC sp_addextendedproperty 'MS_Description', '资源的字段名', 'SCHEMA', dbo, 'table', busi_refer_resource, 'column', resource_column_name;
EXEC sp_addextendedproperty 'MS_Description', '资源的业务名称', 'SCHEMA', dbo, 'table', busi_refer_resource, 'column', resource_busi_name;
EXEC sp_addextendedproperty 'MS_Description', '在删除资源之前的操作;1:允许删除;2:阻止删除', 'SCHEMA', dbo, 'table', busi_refer_resource, 'column', before_delete_res;
EXEC sp_addextendedproperty 'MS_Description', '在删除资源之后的操作;1:不做任何操作;2:引用者也被删除', 'SCHEMA', dbo, 'table', busi_refer_resource, 'column', after_delete_res;
EXEC sp_addextendedproperty 'MS_Description', '检测时,对应的异常场景类型', 'SCHEMA', dbo, 'table', busi_refer_resource, 'column', error_scene_type;

删除资源之前,判断是否该资源被引用

下面是关键代码:


public class TableColumnRecord {
    private String tableName;

    private String columnName;

    private String preventReason;

    private Object columnValue;

}


// ------------------------------------

@Component
public class ResourceRoleExer {

    @PersistenceContext
    private EntityManager entityManager;

    public boolean checkBeforeDelete(TableColumnRecord tableColumnRecord) {
        String tableName = tableColumnRecord.getTableName();
        String columnName = tableColumnRecord.getColumnName();

       String sql = "SELECT * FROM busi_refer_resource WHERE resource_table_name = :tablename and resource_column_name = :columnname and before_delete_res=2";
       List<BusiReferResourcePO> resultList = entityManager.createNativeQuery(sql, BusiReferResourcePO.class)
                .setParameter("tablename", tableName).setParameter("columnname", columnName).getResultList();
        if (CollectionUtils.isEmpty(resultList)) {
            return true;
        } else {
            for (BusiReferResourcePO busiReferResourcePO : resultList) {
                String dataSql = String.format("SELECT * FROM %s WHERE %s = :columnValue", busiReferResourcePO.getRefererTableName(), busiReferResourcePO.getRefererColumnName());
                List<Object> dataResultList = entityManager.createNativeQuery(dataSql).setParameter("columnValue", tableColumnRecord.getColumnValue()).getResultList();
                if (CollectionUtils.isNotEmpty(dataResultList)) {
                    tableColumnRecord.setPreventReason("准备删除的"+busiReferResourcePO.getResourceBusiName()+",已被"+busiReferResourcePO.getRefererBusiName()+"引用了,无法直接删除");
                    return false;
                }
            }
            return true;
        }
    }


下面,再展示一个,批量检查的关键代码


public class BatchTableColumnRecord {
    private String tableName;

    private String columnName;

    private String preventReason;

    private List<?> columnValues;
    
}

// -----------------------------------

public boolean batchCheckBeforeDelete(BatchTableColumnRecord batchTableColumnRecord) {
        String tableName = batchTableColumnRecord.getTableName();
        String columnName = batchTableColumnRecord.getColumnName();

        String sql = "SELECT * FROM busi_refer_resource WHERE resource_table_name = :tablename and resource_column_name = :columnname and before_delete_res=2";
        List<BusiReferResourcePO> resultList = entityManager.createNativeQuery(sql, BusiReferResourcePO.class)
                .setParameter("tablename", tableName).setParameter("columnname", columnName).getResultList();
        if (CollectionUtils.isEmpty(resultList)) {
            return true;
        } else {
            for (BusiReferResourcePO busiReferResourcePO : resultList) {
                String dataSql = String.format("SELECT * FROM %s WHERE %s in (:columnValues)", busiReferResourcePO.getRefererTableName(), busiReferResourcePO.getRefererColumnName());
                List<Object> dataResultList = entityManager.createNativeQuery(dataSql).setParameter("columnValues", batchTableColumnRecord.getColumnValues()).getResultList();
                if (CollectionUtils.isNotEmpty(dataResultList)) {
                    batchTableColumnRecord.setPreventReason("准备删除的"+busiReferResourcePO.getResourceBusiName()+",已被"+busiReferResourcePO.getRefererBusiName()+"引用了,无法直接删除");
                    return false;
                }
            }
            return true;
        }
    }

删除引用者后,删除引用的独占资源

A--->B--->C

A引用B,B引用C。

在删除A时,需要考虑,如果B是A的独占资源,不会被A的其他实例共享。那么在删除A实例时,需要将B也一起删掉。


实现思路就是:在删除A之前,找到A有哪些独占资源,然后将这些资源,一起删掉。

关键代码如下:


public interface DeleteReferedResourceService {

    /**
     * 根据id,获取待删除的独占资源
     * @param id
     * @return
     */
    public List<Supplier> getPendingDeleteExclusiveResourceById(String id);

    /**
     * 根据id列表,做简单删除,只有删除,没有任何逻辑
     * @param ids
     * @return
     */
    public int simpleDeleteByIds(List<String> ids);
}


// --------------------

// com.jshxhb.module.scada.biz.service.monpanel.ScadaMonitorPanelServiceImpl#getPendingDeleteExclusiveResourceById

@Override
    public List<Supplier> getPendingDeleteExclusiveResourceById(String id) {
        //
        QScadaPropertyPO qScadaPropertyPO = QScadaPropertyPO.scadaPropertyPO;
        List<ScadaPropertyPO> scadaPropertyPOList = jpaQueryFactory.selectFrom(qScadaPropertyPO)
                .where(qScadaPropertyPO.associaType.eq(ScadaPropertyAssociaTypeEnum.ASSOCIA_MONITOR_PANEL.getTypeCode()).and(qScadaPropertyPO.associaId.eq(id))).fetch();

        List<Supplier> suppliers = new ArrayList<>();
        if (CollectionUtils.isEmpty(scadaPropertyPOList)) {
            return suppliers;
        }
        List<String> propertyIds = scadaPropertyPOList.stream().map(ScadaPropertyPO::getId).collect(Collectors.toList());
        Supplier<Object> supplier = () -> scadaPropertyService.simpleDeleteByIds(propertyIds);
        suppliers.add(supplier);
        for (String propertyId : propertyIds) {
            List<Supplier> list = scadaPropertyService.getPendingDeleteExclusiveResourceById(propertyId);
            if (CollectionUtils.isNotEmpty(list)) {
                suppliers.addAll(list);
            }
        }
        return suppliers;
    }
    
// ------------------------------
// com.jshxhb.module.scada.biz.service.monpanel.ScadaMonitorPanelServiceImpl#deleteMonPanelById

@Override
    public int deleteMonPanelById(String id, String branchId) {
        ScadaMonitorPanelBO scadaMonitorPanelBO = new ScadaMonitorPanelBO();
        scadaMonitorPanelBO.setId(id);
        scadaMonitorPanelBO.setBranchId(branchId);
        List<ScadaMonitorPanelBO> scadaMonitorPanelBOList = select(scadaMonitorPanelBO);
        boolean existed = CollectionUtils.isNotEmpty(scadaMonitorPanelBOList) ? true : false;
        if (!existed) {
            throw new ReqParamInvalidException("该点位面板未找到");
        }
        int affectedRows = 0;
        // 这一步关键,在删除前,先获取到待删除的独占资源
        List<Supplier> list = getPendingDeleteExclusiveResourceById(id);

        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setName("deleteScadaMonitorPanelTx");
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus txStatus = txManager.getTransaction(def);
        try {
            // 下面这一步,是将自身删除掉
            simpleDeleteByIds(List.of(id));
            // 下面这一步,是将独占的资源删除掉
            for (Supplier supplier : list) {
                supplier.get();
            }

            affectedRows += 1;
            // 删除 ScadaProperty
//            scadaPropertyService.deleteAllAssociaProperty(scadaMonitorPanelBO.getId(), scadaMonitorPanelBO.getBranchId());
            txManager.commit(txStatus);
        } catch (Exception e) {
            LOGGER.error("deleteScadaMonitorPanel occur exception:{}", ExceptionUtils.getStackTrace(e));
            txManager.rollback(txStatus);
        }
        if (existed) {
            String companyId = loginUserProvider.getLoginUser().getCompanyId();
            finishRefreshByGraphId(scadaMonitorPanelBOList.get(0).getGraphId(), companyId);
        }
        return affectedRows;
    }

文章作者:Administrator

文章链接:http://localhost:8090//archives/%E5%BC%95%E7%94%A8%E5%85%B3%E7%B3%BB%E7%BB%B4%E6%8A%A4

版权声明:本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0 许可协议,转载请注明出处!


评论