IDEMPIERE-6150 NON-DB attachments are disappearing when a DB deletion fails (#2377)
* IDEMPIERE-6150 NON-DB attachments are disappearing when a DB deletion fails - delete AD_Attachment and AD_Archive after commit of deletion Trx * - add patch from Heng Sin to convert string concatenation to text block --------- Co-authored-by: hengsin <hengsin@gmail.com>
This commit is contained in:
parent
9740f2bf18
commit
69995b178b
|
@ -4078,14 +4078,14 @@ public abstract class PO
|
||||||
if (m_KeyColumns != null && m_KeyColumns.length == 1 && !getTable().isUUIDKeyTable()) {
|
if (m_KeyColumns != null && m_KeyColumns.length == 1 && !getTable().isUUIDKeyTable()) {
|
||||||
//delete cascade only for single key column record
|
//delete cascade only for single key column record
|
||||||
PO_Record.deleteModelCascade(p_info.getTableName(), Record_ID, localTrxName);
|
PO_Record.deleteModelCascade(p_info.getTableName(), Record_ID, localTrxName);
|
||||||
// Delete Cascade AD_Table_ID/Record_ID (Attachments, ..)
|
// Delete Cascade AD_Table_ID/Record_ID except Attachments/Archive (that's postponed until trx commit)
|
||||||
PO_Record.deleteRecordCascade(AD_Table_ID, Record_ID, localTrxName);
|
PO_Record.deleteRecordCascade(AD_Table_ID, Record_ID, "AD_Table.TableName NOT IN ('AD_Attachment','AD_Archive')", localTrxName);
|
||||||
// Set referencing Record_ID Null AD_Table_ID/Record_ID
|
// Set referencing Record_ID Null AD_Table_ID/Record_ID
|
||||||
PO_Record.setRecordNull(AD_Table_ID, Record_ID, localTrxName);
|
PO_Record.setRecordNull(AD_Table_ID, Record_ID, localTrxName);
|
||||||
}
|
}
|
||||||
if (Record_UU != null) {
|
if (Record_UU != null) {
|
||||||
PO_Record.deleteModelCascade(p_info.getTableName(), Record_UU, localTrxName);
|
PO_Record.deleteModelCascade(p_info.getTableName(), Record_UU, localTrxName);
|
||||||
PO_Record.deleteRecordCascade(AD_Table_ID, Record_UU, localTrxName);
|
PO_Record.deleteRecordCascade(AD_Table_ID, Record_UU, "AD_Table.TableName NOT IN ('AD_Attachment','AD_Archive')", localTrxName);
|
||||||
PO_Record.setRecordNull(AD_Table_ID, Record_UU, localTrxName);
|
PO_Record.setRecordNull(AD_Table_ID, Record_UU, localTrxName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4226,9 +4226,10 @@ public abstract class PO
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (CacheMgt.get().hasCache(p_info.getTableName())) {
|
Trx trxdel = Trx.get(get_TrxName(), false);
|
||||||
Trx trxdel = Trx.get(get_TrxName(), false);
|
if (trxdel != null) {
|
||||||
if (trxdel != null) {
|
// Schedule the reset cache for after committed the delete
|
||||||
|
if (CacheMgt.get().hasCache(p_info.getTableName())) {
|
||||||
trxdel.addTrxEventListener(new TrxEventListener() {
|
trxdel.addTrxEventListener(new TrxEventListener() {
|
||||||
@Override
|
@Override
|
||||||
public void afterRollback(Trx trxdel, boolean success) {
|
public void afterRollback(Trx trxdel, boolean success) {
|
||||||
|
@ -4245,6 +4246,28 @@ public abstract class PO
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// trigger the deletion of attachments and archives for after committed the delete
|
||||||
|
trxdel.addTrxEventListener(new TrxEventListener() {
|
||||||
|
@Override
|
||||||
|
public void afterRollback(Trx trxdel, boolean success) {
|
||||||
|
trxdel.removeTrxEventListener(this);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void afterCommit(Trx trxdel, boolean success) {
|
||||||
|
if (success) {
|
||||||
|
if (m_KeyColumns != null && m_KeyColumns.length == 1 && !getTable().isUUIDKeyTable())
|
||||||
|
// Delete Cascade AD_Table_ID/Record_ID on Attachments/Archive
|
||||||
|
// after commit because operations on external storage providers don't have rollback
|
||||||
|
PO_Record.deleteRecordCascade(AD_Table_ID, Record_ID, "AD_Table.TableName IN ('AD_Attachment','AD_Archive')", null);
|
||||||
|
if (Record_UU != null)
|
||||||
|
PO_Record.deleteRecordCascade(AD_Table_ID, Record_UU, "AD_Table.TableName IN ('AD_Attachment','AD_Archive')", null);
|
||||||
|
}
|
||||||
|
trxdel.removeTrxEventListener(this);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void afterClose(Trx trxdel) {
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (localTrx != null)
|
if (localTrx != null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.compiere.util.DisplayType;
|
||||||
import org.compiere.util.Env;
|
import org.compiere.util.Env;
|
||||||
import org.compiere.util.KeyNamePair;
|
import org.compiere.util.KeyNamePair;
|
||||||
import org.compiere.util.Msg;
|
import org.compiere.util.Msg;
|
||||||
|
import org.compiere.util.Util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maintain AD_Table_ID/Record_ID constraint
|
* Maintain AD_Table_ID/Record_ID constraint
|
||||||
|
@ -47,10 +48,11 @@ public class PO_Record
|
||||||
* Delete Cascade including (selected)parent relationships
|
* Delete Cascade including (selected)parent relationships
|
||||||
* @param AD_Table_ID table
|
* @param AD_Table_ID table
|
||||||
* @param Record_IDorUU record ID (int) or UUID (String)
|
* @param Record_IDorUU record ID (int) or UUID (String)
|
||||||
|
* @param whereTables filter for the Tables
|
||||||
* @param trxName transaction
|
* @param trxName transaction
|
||||||
* @return false if could not be deleted
|
* @return false if could not be deleted
|
||||||
*/
|
*/
|
||||||
protected static boolean deleteRecordCascade (int AD_Table_ID, Serializable Record_IDorUU, String trxName)
|
protected static boolean deleteRecordCascade (int AD_Table_ID, Serializable Record_IDorUU, String whereTables, String trxName)
|
||||||
{
|
{
|
||||||
int refId;
|
int refId;
|
||||||
String columnName;
|
String columnName;
|
||||||
|
@ -63,7 +65,7 @@ public class PO_Record
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException(Record_IDorUU.getClass().getName() + " not supported for ID/UUID");
|
throw new IllegalArgumentException(Record_IDorUU.getClass().getName() + " not supported for ID/UUID");
|
||||||
}
|
}
|
||||||
KeyNamePair[] cascades = getTablesWithConstraintType(refId, MColumn.FKCONSTRAINTTYPE_ModelCascade, trxName);
|
KeyNamePair[] cascades = getTablesWithConstraintType(refId, MColumn.FKCONSTRAINTTYPE_ModelCascade, whereTables, trxName);
|
||||||
// Table Loop
|
// Table Loop
|
||||||
StringBuilder whereClause = new StringBuilder("AD_Table_ID=? AND ").append(columnName).append("=?");
|
StringBuilder whereClause = new StringBuilder("AD_Table_ID=? AND ").append(columnName).append("=?");
|
||||||
for (KeyNamePair table : cascades)
|
for (KeyNamePair table : cascades)
|
||||||
|
@ -146,21 +148,21 @@ public class PO_Record
|
||||||
KeyNamePair[] tables = s_po_record_tables_cache.get(key.toString());
|
KeyNamePair[] tables = s_po_record_tables_cache.get(key.toString());
|
||||||
if (tables != null)
|
if (tables != null)
|
||||||
return tables;
|
return tables;
|
||||||
final String sql = ""
|
final String sql = """
|
||||||
+ "SELECT t.AD_Table_ID, "
|
SELECT t.AD_Table_ID,
|
||||||
+ " c.ColumnName "
|
c.ColumnName
|
||||||
+ "FROM AD_Column c "
|
FROM AD_Column c
|
||||||
+ " JOIN AD_Table t ON c.AD_Table_ID = t.AD_Table_ID "
|
JOIN AD_Table t ON c.AD_Table_ID = t.AD_Table_ID
|
||||||
+ " LEFT JOIN AD_Ref_Table r ON c.AD_Reference_Value_ID = r.AD_Reference_ID "
|
LEFT JOIN AD_Ref_Table r ON c.AD_Reference_Value_ID = r.AD_Reference_ID
|
||||||
+ " LEFT JOIN AD_Table tr ON r.AD_Table_ID = tr.AD_Table_ID "
|
LEFT JOIN AD_Table tr ON r.AD_Table_ID = tr.AD_Table_ID
|
||||||
+ "WHERE t.IsView = 'N' "
|
WHERE t.IsView = 'N'
|
||||||
+ " AND t.IsActive = 'Y' "
|
AND t.IsActive = 'Y'
|
||||||
+ " AND c.IsActive = 'Y' "
|
AND c.IsActive = 'Y'
|
||||||
+ " AND ( ( c.AD_Reference_ID = ? "
|
AND ( ( c.AD_Reference_ID = ?
|
||||||
+ " AND c.ColumnName = ? || '_ID' ) "
|
AND c.ColumnName = ? || '_ID' )
|
||||||
+ " OR ( c.AD_Reference_ID IN (? , ?) "
|
OR ( c.AD_Reference_ID IN (? , ?)
|
||||||
+ " AND ( tr.TableName = ? OR ( tr.TableName IS NULL AND c.ColumnName = ? || '_ID' ) ) ) ) "
|
AND ( tr.TableName = ? OR ( tr.TableName IS NULL AND c.ColumnName = ? || '_ID' ) ) ) )
|
||||||
+ " AND c.FKConstraintType = ?";
|
AND c.FKConstraintType = ?""";
|
||||||
List<List<Object>> dependents = DB.getSQLArrayObjectsEx(trxName, sql,
|
List<List<Object>> dependents = DB.getSQLArrayObjectsEx(trxName, sql,
|
||||||
refTableDirId, tableName, refTableId, refTableSearchId, tableName, tableName, MColumn.FKCONSTRAINTTYPE_ModelCascade);
|
refTableDirId, tableName, refTableId, refTableSearchId, tableName, tableName, MColumn.FKCONSTRAINTTYPE_ModelCascade);
|
||||||
if (dependents != null) {
|
if (dependents != null) {
|
||||||
|
@ -275,6 +277,18 @@ public class PO_Record
|
||||||
* @return array of KeyNamePair<AD_Table_ID, TableName>
|
* @return array of KeyNamePair<AD_Table_ID, TableName>
|
||||||
*/
|
*/
|
||||||
private static KeyNamePair[] getTablesWithConstraintType(int refId, String constraintType, String trxName) {
|
private static KeyNamePair[] getTablesWithConstraintType(int refId, String constraintType, String trxName) {
|
||||||
|
return getTablesWithConstraintType(refId, constraintType, null, trxName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get array of tables which has a refId column with the defined Constraint Type
|
||||||
|
* @param refId AD_Reference_ID - Record_ID or Record_UU
|
||||||
|
* @param constraintType - FKConstraintType of AD_Column
|
||||||
|
* @param whereTables - optional filter
|
||||||
|
* @param trxName
|
||||||
|
* @return array of KeyNamePair<AD_Table_ID, TableName>
|
||||||
|
*/
|
||||||
|
private static KeyNamePair[] getTablesWithConstraintType(int refId, String constraintType, String whereTables, String trxName) {
|
||||||
String columnName;
|
String columnName;
|
||||||
if (refId == DisplayType.RecordID) {
|
if (refId == DisplayType.RecordID) {
|
||||||
columnName = "Record_ID";
|
columnName = "Record_ID";
|
||||||
|
@ -284,11 +298,14 @@ public class PO_Record
|
||||||
log.warning(refId + " not supported for ID/UUID");
|
log.warning(refId + " not supported for ID/UUID");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
StringBuilder key = new StringBuilder(constraintType).append("|").append(refId);
|
StringBuilder key = new StringBuilder(constraintType).append("|").append(refId).append("|").append(whereTables);
|
||||||
KeyNamePair[] tables = s_po_record_tables_cache.get(key.toString());
|
KeyNamePair[] tables = s_po_record_tables_cache.get(key.toString());
|
||||||
if (tables != null)
|
if (tables != null)
|
||||||
return tables;
|
return tables;
|
||||||
List<MTable> listTables = new Query(Env.getCtx(), MTable.Table_Name, "c.AD_Reference_ID=? AND c.FKConstraintType=? AND AD_Table.IsView='N' AND c.ColumnName=?", trxName)
|
String whereClause = "c.AD_Reference_ID=? AND c.FKConstraintType=? AND AD_Table.IsView='N' AND c.ColumnName=?";
|
||||||
|
if (! Util.isEmpty(whereTables))
|
||||||
|
whereClause = whereClause + " AND (" + whereTables + ")";
|
||||||
|
List<MTable> listTables = new Query(Env.getCtx(), MTable.Table_Name, whereClause, trxName)
|
||||||
.addJoinClause("JOIN AD_Column c ON (c.AD_Table_ID=AD_Table.AD_Table_ID)")
|
.addJoinClause("JOIN AD_Column c ON (c.AD_Table_ID=AD_Table.AD_Table_ID)")
|
||||||
.setOnlyActiveRecords(true)
|
.setOnlyActiveRecords(true)
|
||||||
.setParameters(refId, constraintType, columnName)
|
.setParameters(refId, constraintType, columnName)
|
||||||
|
|
Loading…
Reference in New Issue