IDEMPIERE-5683 - Improve Record ID and Record UU by Constraint Type (#1810)
* IDEMPIERE-5683 - Improve Record ID and Record UU by Constraint Type * IDEMPIERE-5683 - replace direct sql with model classes - rename deleteSetNull to setRecordIdNull - replace direct sql with model classes in deleteCascade, setRecordIdNull - remove unnecessary change from AD_Ref_List * IDEMPIERE-5683 - cache tables arrays in PO_Record * IDEMPIERE-5683 - fix Dynamic Validation * IDEMPIERE-5683 - change CleanOrphanCascade - IsCleanChangeLog process parameter removed - replace direct sql with java model - delete just on Model Cascade constraint - new Set Null rule implemented * IDEMPIERE-5683 - fix when Record_ID is mandatory
This commit is contained in:
parent
afcbf6e70f
commit
3bf447b009
|
@ -0,0 +1,33 @@
|
||||||
|
-- IDEMPIERE-5683
|
||||||
|
SELECT register_migration_script('202304280940_IDEMPIERE-5683.sql') FROM dual;
|
||||||
|
|
||||||
|
SET SQLBLANKLINES ON
|
||||||
|
SET DEFINE OFF
|
||||||
|
|
||||||
|
-- Apr 28, 2023, 9:41:24 AM CEST
|
||||||
|
UPDATE AD_Field SET DisplayLogic='@AD_Reference_ID@=19 | @AD_Reference_ID@=30 | @AD_Reference_ID@=18 | @AD_Reference_ID@=21 | @AD_Reference_ID@=25 | @AD_Reference_ID@=31 | @AD_Reference_ID@=35 | @AD_Reference_ID@=33 | @AD_Reference_ID@=32 | @AD_Reference_ID@=53370 | @AD_Reference_ID@=200233 | @AD_Reference_ID@=200234 | @AD_Reference_ID@=200235 | @AD_Reference_ID@=200202 | @AD_Reference_ID@=200240',Updated=TO_TIMESTAMP('2023-04-28 09:41:24','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202519
|
||||||
|
;
|
||||||
|
|
||||||
|
-- update Constraint Type based on java arrays from PO_Record.java
|
||||||
|
UPDATE AD_Column
|
||||||
|
SET FKConstraintType =
|
||||||
|
CASE
|
||||||
|
WHEN AD_Table_ID IN(254,754,389,200000,200215,200347) THEN 'C' /* AD_Attachment, AD_Archive, AD_Note, AD_RecentItem, AD_PostIt, AD_LabelAssignment - Cascade */
|
||||||
|
WHEN AD_Table_ID IN(417,876) THEN 'N' /* R_Request, CM_Chat - No Action */
|
||||||
|
ELSE 'D'
|
||||||
|
END
|
||||||
|
WHERE AD_Reference_ID IN(200202,200240) /* Record ID, Record UU */
|
||||||
|
;
|
||||||
|
|
||||||
|
-- May 4, 2023, 5:28:36 PM CEST
|
||||||
|
UPDATE AD_Val_Rule SET Code='(
|
||||||
|
(AD_Ref_List.Value!=''M'' OR @AD_Reference_ID@ IN (18,19,30,200233,200234,200235,200202,200240))
|
||||||
|
AND
|
||||||
|
((AD_Ref_List.Value=''C'' AND @AD_Reference_ID@ NOT IN (200202,200240)) OR (AD_Ref_List.Value!=''C''))
|
||||||
|
)',Updated=TO_TIMESTAMP('2023-05-04 17:28:36','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Val_Rule_ID=200064
|
||||||
|
;
|
||||||
|
|
||||||
|
-- May 5, 2023, 8:15:59 AM CEST
|
||||||
|
DELETE FROM AD_Process_Para WHERE AD_Process_Para_UU='459a7f88-ec79-47cf-9c7b-7429ac565f55'
|
||||||
|
;
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
-- IDEMPIERE-5683
|
||||||
|
SELECT register_migration_script('202304280940_IDEMPIERE-5683.sql') FROM dual;
|
||||||
|
|
||||||
|
-- Apr 28, 2023, 9:41:24 AM CEST
|
||||||
|
UPDATE AD_Field SET DisplayLogic='@AD_Reference_ID@=19 | @AD_Reference_ID@=30 | @AD_Reference_ID@=18 | @AD_Reference_ID@=21 | @AD_Reference_ID@=25 | @AD_Reference_ID@=31 | @AD_Reference_ID@=35 | @AD_Reference_ID@=33 | @AD_Reference_ID@=32 | @AD_Reference_ID@=53370 | @AD_Reference_ID@=200233 | @AD_Reference_ID@=200234 | @AD_Reference_ID@=200235 | @AD_Reference_ID@=200202 | @AD_Reference_ID@=200240',Updated=TO_TIMESTAMP('2023-04-28 09:41:24','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202519
|
||||||
|
;
|
||||||
|
|
||||||
|
-- update Constraint Type based on java arrays from PO_Record.java
|
||||||
|
UPDATE AD_Column
|
||||||
|
SET FKConstraintType =
|
||||||
|
CASE
|
||||||
|
WHEN AD_Table_ID IN(254,754,389,200000,200215,200347) THEN 'C' /* AD_Attachment, AD_Archive, AD_Note, AD_RecentItem, AD_PostIt, AD_LabelAssignment - Cascade */
|
||||||
|
WHEN AD_Table_ID IN(417,876) THEN 'N' /* R_Request, CM_Chat - No Action */
|
||||||
|
ELSE 'D'
|
||||||
|
END
|
||||||
|
WHERE AD_Reference_ID IN(200202,200240) /* Record ID, Record UU */
|
||||||
|
;
|
||||||
|
|
||||||
|
-- May 4, 2023, 5:28:36 PM CEST
|
||||||
|
UPDATE AD_Val_Rule SET Code='(
|
||||||
|
(AD_Ref_List.Value!=''M'' OR @AD_Reference_ID@ IN (18,19,30,200233,200234,200235,200202,200240))
|
||||||
|
AND
|
||||||
|
((AD_Ref_List.Value=''C'' AND @AD_Reference_ID@ NOT IN (200202,200240)) OR (AD_Ref_List.Value!=''C''))
|
||||||
|
)',Updated=TO_TIMESTAMP('2023-05-04 17:28:36','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Val_Rule_ID=200064
|
||||||
|
;
|
||||||
|
|
||||||
|
-- May 5, 2023, 8:15:59 AM CEST
|
||||||
|
DELETE FROM AD_Process_Para WHERE AD_Process_Para_UU='459a7f88-ec79-47cf-9c7b-7429ac565f55'
|
||||||
|
;
|
||||||
|
|
|
@ -26,16 +26,15 @@ package org.idempiere.process;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import org.compiere.model.MArchive;
|
import org.compiere.model.MColumn;
|
||||||
import org.compiere.model.MAttachment;
|
|
||||||
import org.compiere.model.MProcessPara;
|
|
||||||
import org.compiere.model.MTable;
|
import org.compiere.model.MTable;
|
||||||
import org.compiere.model.MTree_Base;
|
import org.compiere.model.MTree_Base;
|
||||||
|
import org.compiere.model.PO;
|
||||||
import org.compiere.model.Query;
|
import org.compiere.model.Query;
|
||||||
import org.compiere.model.X_AD_Package_UUID_Map;
|
import org.compiere.model.X_AD_Package_UUID_Map;
|
||||||
import org.compiere.process.ProcessInfoParameter;
|
|
||||||
import org.compiere.process.SvrProcess;
|
import org.compiere.process.SvrProcess;
|
||||||
import org.compiere.util.DB;
|
import org.compiere.util.DB;
|
||||||
import org.compiere.util.Msg;
|
import org.compiere.util.Msg;
|
||||||
|
@ -49,22 +48,11 @@ import org.compiere.util.ValueNamePair;
|
||||||
public class CleanOrphanCascade extends SvrProcess
|
public class CleanOrphanCascade extends SvrProcess
|
||||||
{
|
{
|
||||||
|
|
||||||
private boolean p_IsCleanChangeLog;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare - e.g., get Parameters.
|
* Prepare - e.g., get Parameters.
|
||||||
*/
|
*/
|
||||||
protected void prepare()
|
protected void prepare()
|
||||||
{
|
{
|
||||||
for (ProcessInfoParameter para : getParameter())
|
|
||||||
{
|
|
||||||
String name = para.getParameterName();
|
|
||||||
if ("IsCleanChangeLog".equals(name)) {
|
|
||||||
p_IsCleanChangeLog = para.getParameterAsBoolean();
|
|
||||||
} else {
|
|
||||||
MProcessPara.validateUnknownParameter(getProcessInfo().getAD_Process_ID(), para);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // prepare
|
} // prepare
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,9 +108,6 @@ public class CleanOrphanCascade extends SvrProcess
|
||||||
+ " FROM AD_Column ck "
|
+ " FROM AD_Column ck "
|
||||||
+ " WHERE ck.IsActive='Y' AND ck.AD_Table_ID = AD_Table.AD_Table_ID "
|
+ " WHERE ck.IsActive='Y' AND ck.AD_Table_ID = AD_Table.AD_Table_ID "
|
||||||
+ " AND ck.ColumnName = AD_Table.TableName || '_ID')";
|
+ " AND ck.ColumnName = AD_Table.TableName || '_ID')";
|
||||||
if (! p_IsCleanChangeLog) {
|
|
||||||
whereTables += " AND TableName != 'AD_ChangeLog'";
|
|
||||||
}
|
|
||||||
|
|
||||||
List<MTable> tables = new Query(getCtx(), "AD_Table", whereTables, get_TrxName())
|
List<MTable> tables = new Query(getCtx(), "AD_Table", whereTables, get_TrxName())
|
||||||
.setOnlyActiveRecords(true)
|
.setOnlyActiveRecords(true)
|
||||||
|
@ -134,16 +119,21 @@ public class CleanOrphanCascade extends SvrProcess
|
||||||
boolean isUUIDMap = X_AD_Package_UUID_Map.Table_Name.equals(tableName);
|
boolean isUUIDMap = X_AD_Package_UUID_Map.Table_Name.equals(tableName);
|
||||||
|
|
||||||
StringBuilder sqlRef = new StringBuilder();
|
StringBuilder sqlRef = new StringBuilder();
|
||||||
sqlRef.append("SELECT DISTINCT t.AD_Table_ID, ");
|
sqlRef.append("SELECT DISTINCT t.AD_Table_ID, ")
|
||||||
sqlRef.append(" t.TableName ");
|
.append(" t.TableName, ")
|
||||||
sqlRef.append("FROM ").append(tableName).append(" r ");
|
.append(" c.FKConstraintType, ")
|
||||||
sqlRef.append(" JOIN AD_Table t ON ( r.AD_Table_ID = t.AD_Table_ID ) ");
|
.append(" c.IsMandatory ")
|
||||||
sqlRef.append("ORDER BY t.Tablename");
|
.append("FROM ").append(tableName).append(" r ")
|
||||||
|
.append(" JOIN AD_Table t ON ( r.AD_Table_ID = t.AD_Table_ID ) ")
|
||||||
|
.append(" JOIN AD_Column c ON (t.AD_Table_ID = c.AD_Table_ID AND c.ColumnName = 'Record_ID') ")
|
||||||
|
.append("ORDER BY t.Tablename");
|
||||||
List<List<Object>> rowTables = DB.getSQLArrayObjectsEx(get_TrxName(), sqlRef.toString());
|
List<List<Object>> rowTables = DB.getSQLArrayObjectsEx(get_TrxName(), sqlRef.toString());
|
||||||
if (rowTables != null) {
|
if (rowTables != null) {
|
||||||
for (List<Object> row : rowTables) {
|
for (List<Object> row : rowTables) {
|
||||||
int refTableID = ((BigDecimal) row.get(0)).intValue();
|
int refTableID = ((BigDecimal) row.get(0)).intValue();
|
||||||
String refTableName = row.get(1).toString();
|
String refTableName = row.get(1).toString();
|
||||||
|
String constraintType = Objects.toString(row.get(2), "");
|
||||||
|
Boolean isMandatory = row.get(3).toString().equalsIgnoreCase("Y");
|
||||||
|
|
||||||
MTable refTable = MTable.get(getCtx(), refTableID);
|
MTable refTable = MTable.get(getCtx(), refTableID);
|
||||||
StringBuilder whereClause = new StringBuilder();
|
StringBuilder whereClause = new StringBuilder();
|
||||||
|
@ -168,24 +158,19 @@ public class CleanOrphanCascade extends SvrProcess
|
||||||
}
|
}
|
||||||
|
|
||||||
int noDel = 0;
|
int noDel = 0;
|
||||||
if (MAttachment.Table_Name.equals(tableName)) {
|
List<PO> poList = new Query(getCtx(), tableName, whereClause.toString(), get_TrxName()).list();
|
||||||
// special case for attachment because of store
|
for (PO po : poList) {
|
||||||
List<MAttachment> attachments = new Query(getCtx(), tableName, whereClause.toString(), get_TrxName()).list();
|
if(MColumn.FKCONSTRAINTTYPE_ModelCascade.equals(constraintType)) {
|
||||||
for (MAttachment attachment : attachments) {
|
po.deleteEx(true, get_TrxName());
|
||||||
attachment.deleteEx(true, get_TrxName());
|
|
||||||
noDel++;
|
|
||||||
}
|
}
|
||||||
} else if (MArchive.Table_Name.equals(tableName)) {
|
else if(MColumn.FKCONSTRAINTTYPE_SetNull.equals(constraintType)) {
|
||||||
// special case for archive because of store
|
if(isMandatory)
|
||||||
List<MArchive> archives = new Query(getCtx(), tableName, whereClause.toString(), get_TrxName()).list();
|
po.set_ValueOfColumn("Record_ID", 0);
|
||||||
for (MArchive archive : archives) {
|
else
|
||||||
archive.deleteEx(true, get_TrxName());
|
po.set_ValueOfColumn("Record_ID", null);
|
||||||
noDel++;
|
po.saveEx(get_TrxName());
|
||||||
}
|
}
|
||||||
} else {
|
noDel++;
|
||||||
StringBuilder sqlDelete = new StringBuilder();
|
|
||||||
sqlDelete.append("DELETE FROM ").append(tableName).append(" WHERE ").append(whereClause);
|
|
||||||
noDel = DB.executeUpdateEx(sqlDelete.toString(), get_TrxName());
|
|
||||||
}
|
}
|
||||||
if (noDel > 0) {
|
if (noDel > 0) {
|
||||||
addLog(Msg.parseTranslation(getCtx(), noDel + " " + tableName + " " + "@Deleted@ -> " + refTableName));
|
addLog(Msg.parseTranslation(getCtx(), noDel + " " + tableName + " " + "@Deleted@ -> " + refTableName));
|
||||||
|
@ -199,14 +184,16 @@ public class CleanOrphanCascade extends SvrProcess
|
||||||
} // doIt
|
} // doIt
|
||||||
|
|
||||||
private void delTree(String treeTable, String foreignTable, String columnName, int treeId) {
|
private void delTree(String treeTable, String foreignTable, String columnName, int treeId) {
|
||||||
StringBuilder sqlDelete = new StringBuilder()
|
String whereClause = columnName + ">0 AND " + columnName + " NOT IN (SELECT " + foreignTable + "_ID FROM " + foreignTable + ")";
|
||||||
.append("DELETE FROM ").append(treeTable)
|
if(treeId > 0)
|
||||||
.append(" WHERE ").append(columnName).append(">0 AND ")
|
whereClause += " AND AD_Tree_ID=" + treeId;
|
||||||
.append(columnName).append(" NOT IN (SELECT ").append(foreignTable).append("_ID FROM ").append(foreignTable).append(")");
|
List<PO> poList = new Query(getCtx(), treeTable, whereClause, get_TrxName()).list();
|
||||||
if (treeId > 0) {
|
|
||||||
sqlDelete.append(" AND AD_Tree_ID=").append(treeId);
|
int noDel = 0;
|
||||||
|
for(PO po : poList) {
|
||||||
|
po.deleteEx(true, get_TrxName());
|
||||||
|
noDel++;
|
||||||
}
|
}
|
||||||
int noDel = DB.executeUpdateEx(sqlDelete.toString(), get_TrxName());
|
|
||||||
if (noDel > 0) {
|
if (noDel > 0) {
|
||||||
addLog(Msg.parseTranslation(getCtx(), noDel + " " + treeTable + " " + "@Deleted@ -> " + foreignTable
|
addLog(Msg.parseTranslation(getCtx(), noDel + " " + treeTable + " " + "@Deleted@ -> " + foreignTable
|
||||||
+ (treeId > 0 ? " Tree=" + treeId: "" )));
|
+ (treeId > 0 ? " Tree=" + treeId: "" )));
|
||||||
|
|
|
@ -3973,6 +3973,9 @@ public abstract class PO
|
||||||
PO_Record.deleteModelCascade(p_info.getTableName(), Record_ID, localTrxName);
|
PO_Record.deleteModelCascade(p_info.getTableName(), Record_ID, localTrxName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set referencing Record_ID Null AD_Table_ID/Record_ID
|
||||||
|
PO_Record.setRecordIdNull(AD_Table_ID, Record_ID, localTrxName);
|
||||||
|
|
||||||
// The Delete Statement
|
// The Delete Statement
|
||||||
String where = isLogSQLScript() ? get_WhereClause(true, get_ValueAsString(getUUIDColumnName())) : get_WhereClause(true);
|
String where = isLogSQLScript() ? get_WhereClause(true, get_ValueAsString(getUUIDColumnName())) : get_WhereClause(true);
|
||||||
List<Object> optimisticLockingParams = new ArrayList<Object>();
|
List<Object> optimisticLockingParams = new ArrayList<Object>();
|
||||||
|
|
|
@ -18,13 +18,16 @@ package org.compiere.model;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import org.compiere.util.CCache;
|
||||||
import org.compiere.util.CLogger;
|
import org.compiere.util.CLogger;
|
||||||
import org.compiere.util.DB;
|
import org.compiere.util.DB;
|
||||||
import org.compiere.util.DisplayType;
|
import org.compiere.util.DisplayType;
|
||||||
import org.compiere.util.Env;
|
import org.compiere.util.Env;
|
||||||
|
import org.compiere.util.KeyNamePair;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maintain AD_Table_ID/Record_ID constraint
|
* Maintain AD_Table_ID/Record_ID constraint
|
||||||
|
@ -34,6 +37,9 @@ import org.compiere.util.Env;
|
||||||
*/
|
*/
|
||||||
public class PO_Record
|
public class PO_Record
|
||||||
{
|
{
|
||||||
|
/* Cache for arrays of KeyNamePair<AD_Table_ID, TableName> for types of deletion: Cascade, Set Null, No Action */
|
||||||
|
private static final CCache<String, KeyNamePair[]> s_po_record_tables_cache = new CCache<>(null, "PORecordTables", 3, 120, false, 3);
|
||||||
|
|
||||||
/** Parent Tables */
|
/** Parent Tables */
|
||||||
private static int[] s_parents = new int[]{
|
private static int[] s_parents = new int[]{
|
||||||
X_C_Order.Table_ID
|
X_C_Order.Table_ID
|
||||||
|
@ -48,40 +54,6 @@ public class PO_Record
|
||||||
X_C_OrderLine.Table_Name
|
X_C_OrderLine.Table_Name
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Cascade Table ID */
|
|
||||||
private static int[] s_cascades = new int[]{
|
|
||||||
X_AD_Attachment.Table_ID,
|
|
||||||
X_AD_Archive.Table_ID,
|
|
||||||
X_AD_Note.Table_ID,
|
|
||||||
X_AD_RecentItem.Table_ID,
|
|
||||||
X_AD_PostIt.Table_ID,
|
|
||||||
X_AD_LabelAssignment.Table_ID
|
|
||||||
};
|
|
||||||
/** Cascade Table Names */
|
|
||||||
private static String[] s_cascadeNames = new String[]{
|
|
||||||
X_AD_Attachment.Table_Name,
|
|
||||||
X_AD_Archive.Table_Name,
|
|
||||||
X_AD_Note.Table_Name,
|
|
||||||
X_AD_RecentItem.Table_Name,
|
|
||||||
X_AD_PostIt.Table_Name,
|
|
||||||
X_AD_LabelAssignment.Table_Name
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Restrict Table ID */
|
|
||||||
private static int[] s_restricts = new int[]{
|
|
||||||
X_R_Request.Table_ID,
|
|
||||||
X_CM_Chat.Table_ID
|
|
||||||
// X_Fact_Acct.Table_ID
|
|
||||||
};
|
|
||||||
/** Restrict Table Names */
|
|
||||||
private static String[] s_restrictNames = new String[]{
|
|
||||||
X_R_Request.Table_Name,
|
|
||||||
X_CM_Chat.Table_Name
|
|
||||||
// X_Fact_Acct.Table_Name
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Logger */
|
/** Logger */
|
||||||
private static CLogger log = CLogger.getCLogger (PO_Record.class);
|
private static CLogger log = CLogger.getCLogger (PO_Record.class);
|
||||||
|
|
||||||
|
@ -94,60 +66,52 @@ public class PO_Record
|
||||||
*/
|
*/
|
||||||
static boolean deleteCascade (int AD_Table_ID, int Record_ID, String trxName)
|
static boolean deleteCascade (int AD_Table_ID, int Record_ID, String trxName)
|
||||||
{
|
{
|
||||||
|
KeyNamePair[] cascades = getTablesWithRecordColumnFromCache(MColumn.FKCONSTRAINTTYPE_Cascade);
|
||||||
// Table Loop
|
// Table Loop
|
||||||
for (int i = 0; i < s_cascades.length; i++)
|
for (KeyNamePair table : cascades)
|
||||||
{
|
{
|
||||||
// DELETE FROM table WHERE AD_Table_ID=#1 AND Record_ID=#2
|
// DELETE FROM table WHERE AD_Table_ID=#1 AND Record_ID=#2
|
||||||
if (s_cascades[i] != AD_Table_ID)
|
if (table.getKey() != AD_Table_ID)
|
||||||
{
|
{
|
||||||
Object[] params = new Object[]{Integer.valueOf(AD_Table_ID), Integer.valueOf(Record_ID)};
|
List<PO> poList = new Query(Env.getCtx(), table.getName(), "AD_Table_ID=? AND Record_ID=?", trxName)
|
||||||
if (s_cascadeNames[i].equals(X_AD_Attachment.Table_Name) || s_cascadeNames[i].equals(X_AD_Archive.Table_Name))
|
.setParameters(AD_Table_ID, Record_ID)
|
||||||
{
|
.list();
|
||||||
Query query = new Query(Env.getCtx(), s_cascadeNames[i], "AD_Table_ID=? AND Record_ID=?", trxName);
|
|
||||||
List<PO> list = query.setParameters(params).list();
|
int count = 0;
|
||||||
for(PO po : list)
|
for(PO po : poList)
|
||||||
{
|
{
|
||||||
po.deleteEx(true);
|
po.deleteEx(true);
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
}
|
if (count > 0)
|
||||||
else
|
if (log.isLoggable(Level.CONFIG)) log.config(table.getName() + " (" + AD_Table_ID + "/" + Record_ID + ") #" + count);
|
||||||
{
|
|
||||||
StringBuilder sql = new StringBuilder ("DELETE FROM ")
|
|
||||||
.append(s_cascadeNames[i])
|
|
||||||
.append(" WHERE AD_Table_ID=? AND Record_ID=?");
|
|
||||||
int no = DB.executeUpdate(sql.toString(), params, false, trxName);
|
|
||||||
if (no > 0) {
|
|
||||||
if (log.isLoggable(Level.CONFIG)) log.config(s_cascadeNames[i] + " (" + AD_Table_ID + "/" + Record_ID + ") #" + no);
|
|
||||||
} else if (no < 0) {
|
|
||||||
log.severe(s_cascadeNames[i] + " (" + AD_Table_ID + "/" + Record_ID + ") #" + no);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Parent Loop
|
// Parent Loop
|
||||||
for (int j = 0; j < s_parents.length; j++)
|
for (int i = 0; i < s_parents.length; i++)
|
||||||
{
|
{
|
||||||
if (s_parents[j] == AD_Table_ID)
|
if (s_parents[i] == AD_Table_ID)
|
||||||
{
|
{
|
||||||
int AD_Table_IDchild = s_parentChilds[j];
|
int AD_Table_IDchild = s_parentChilds[i];
|
||||||
Object[] params = new Object[]{Integer.valueOf(AD_Table_IDchild), Integer.valueOf(Record_ID)};
|
for (KeyNamePair table : cascades)
|
||||||
for (int i = 0; i < s_cascades.length; i++)
|
|
||||||
{
|
{
|
||||||
StringBuilder sql = new StringBuilder ("DELETE FROM ")
|
String whereClause = " AD_Table_ID=? AND Record_ID IN (SELECT "
|
||||||
.append(s_cascadeNames[i])
|
+ s_parentChildNames[i] + "_ID FROM "
|
||||||
.append(" WHERE AD_Table_ID=? AND Record_ID IN (SELECT ")
|
+ s_parentChildNames[i] + " WHERE "
|
||||||
.append(s_parentChildNames[j]).append("_ID FROM ")
|
+ s_parentNames[i] + "_ID=?) ";
|
||||||
.append(s_parentChildNames[j]).append(" WHERE ")
|
List<PO> poList = new Query(Env.getCtx(), table.getName(), whereClause, trxName)
|
||||||
.append(s_parentNames[j]).append("_ID=?)");
|
.setParameters(AD_Table_IDchild, Record_ID)
|
||||||
int no = DB.executeUpdate(sql.toString(), params, false, trxName);
|
.list();
|
||||||
if (no > 0) {
|
|
||||||
if (log.isLoggable(Level.CONFIG)) log.config(s_cascadeNames[i] + " " + s_parentNames[j]
|
int count = 0;
|
||||||
+ " (" + AD_Table_ID + "/" + Record_ID + ") #" + no);
|
for(PO po : poList)
|
||||||
} else if (no < 0) {
|
{
|
||||||
log.severe(s_cascadeNames[i] + " " + s_parentNames[j]
|
po.deleteEx(true);
|
||||||
+ " (" + AD_Table_ID + "/" + Record_ID + ") #" + no);
|
count++;
|
||||||
return false;
|
}
|
||||||
|
if(count > 0) {
|
||||||
|
if (log.isLoggable(Level.CONFIG)) log.config(table.getName() + " " + s_parentNames[i]
|
||||||
|
+ " (" + AD_Table_ID + "/" + Record_ID + ") #" + count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,6 +155,38 @@ public class PO_Record
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a referencing Record ID or Record UU exists to the deleted record, set it to NULL
|
||||||
|
* @param AD_Table_ID
|
||||||
|
* @param Record_ID
|
||||||
|
* @param trxName
|
||||||
|
*/
|
||||||
|
public static void setRecordIdNull(int AD_Table_ID, int Record_ID, String trxName){
|
||||||
|
KeyNamePair[] tables = getTablesWithRecordColumnFromCache(MColumn.FKCONSTRAINTTYPE_SetNull);
|
||||||
|
// Table loop
|
||||||
|
for (KeyNamePair table : tables) {
|
||||||
|
if(table.getKey() == AD_Table_ID)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
List<PO> poList = new Query(Env.getCtx(), table.getName(), " AD_Table_ID = ? AND Record_ID = ? ", trxName)
|
||||||
|
.setParameters(AD_Table_ID, Record_ID)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for(PO po : poList) {
|
||||||
|
if(po.isColumnMandatory(po.get_ColumnIndex("Record_ID")))
|
||||||
|
po.set_Value("Record_ID", 0);
|
||||||
|
else
|
||||||
|
po.set_Value("Record_ID", null);
|
||||||
|
po.saveEx(trxName);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
if (log.isLoggable(Level.CONFIG)) log.config(table.getName() + " (" + AD_Table_ID + "/" + Record_ID + ") #" + count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An entry Exists for restrict table/record combination
|
* An entry Exists for restrict table/record combination
|
||||||
* @param AD_Table_ID table
|
* @param AD_Table_ID table
|
||||||
|
@ -200,16 +196,17 @@ public class PO_Record
|
||||||
*/
|
*/
|
||||||
static String exists (int AD_Table_ID, int Record_ID, String trxName)
|
static String exists (int AD_Table_ID, int Record_ID, String trxName)
|
||||||
{
|
{
|
||||||
|
KeyNamePair[] restricts = getTablesWithRecordColumnFromCache(MColumn.FKCONSTRAINTTYPE_NoAction);
|
||||||
// Table Loop only
|
// Table Loop only
|
||||||
for (int i = 0; i < s_restricts.length; i++)
|
for (int i = 0; i < restricts.length; i++)
|
||||||
{
|
{
|
||||||
// SELECT COUNT(*) FROM table WHERE AD_Table_ID=#1 AND Record_ID=#2
|
// SELECT COUNT(*) FROM table WHERE AD_Table_ID=#1 AND Record_ID=#2
|
||||||
StringBuilder sql = new StringBuilder ("SELECT COUNT(*) FROM ")
|
StringBuilder sql = new StringBuilder ("SELECT COUNT(*) FROM ")
|
||||||
.append(s_restrictNames[i])
|
.append(restricts[i].getName())
|
||||||
.append(" WHERE AD_Table_ID=? AND Record_ID=?");
|
.append(" WHERE AD_Table_ID=? AND Record_ID=?");
|
||||||
int no = DB.getSQLValue(trxName, sql.toString(), AD_Table_ID, Record_ID);
|
int no = DB.getSQLValue(trxName, sql.toString(), AD_Table_ID, Record_ID);
|
||||||
if (no > 0)
|
if (no > 0)
|
||||||
return s_restrictNames[i];
|
return restricts[i].getName();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} // exists
|
} // exists
|
||||||
|
@ -261,19 +258,77 @@ public class PO_Record
|
||||||
*/
|
*/
|
||||||
static private void validate (int AD_Table_ID, String TableName)
|
static private void validate (int AD_Table_ID, String TableName)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < s_cascades.length; i++)
|
KeyNamePair[] cascades = getTablesWithRecordColumnFromCache(MColumn.FKCONSTRAINTTYPE_Cascade);
|
||||||
|
for (int i = 0; i < cascades.length; i++)
|
||||||
{
|
{
|
||||||
StringBuilder sql = new StringBuilder ("DELETE FROM ")
|
StringBuilder sql = new StringBuilder ("DELETE FROM ")
|
||||||
.append(s_cascadeNames[i])
|
.append(cascades[i].getName())
|
||||||
.append(" WHERE AD_Table_ID=").append(AD_Table_ID)
|
.append(" WHERE AD_Table_ID=").append(AD_Table_ID)
|
||||||
.append(" AND Record_ID NOT IN (SELECT ")
|
.append(" AND Record_ID NOT IN (SELECT ")
|
||||||
.append(TableName).append("_ID FROM ").append(TableName).append(")");
|
.append(TableName).append("_ID FROM ").append(TableName).append(")");
|
||||||
int no = DB.executeUpdate(sql.toString(), null);
|
int no = DB.executeUpdate(sql.toString(), null);
|
||||||
if (no > 0)
|
if (no > 0)
|
||||||
if (log.isLoggable(Level.CONFIG)) log.config(s_cascadeNames[i] + " (" + AD_Table_ID + "/" + TableName
|
if (log.isLoggable(Level.CONFIG)) log.config(cascades[i].getName() + " (" + AD_Table_ID + "/" + TableName
|
||||||
+ ") Invalid #" + no);
|
+ ") Invalid #" + no);
|
||||||
}
|
}
|
||||||
} // validate
|
} // validate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get array of tables which has a Record_ID or Record_UU column with the defined Constraint Type from cache
|
||||||
|
* @param constraintType - FKConstraintType of AD_Column
|
||||||
|
* @return array of KeyNamePair<AD_Table_ID, TableName>
|
||||||
|
*/
|
||||||
|
static private KeyNamePair[] getTablesWithRecordColumnFromCache(String constraintType) {
|
||||||
|
KeyNamePair[] tables = s_po_record_tables_cache.get(constraintType);
|
||||||
|
if(tables != null)
|
||||||
|
return tables;
|
||||||
|
tables = getTablesWithRecordColumn(constraintType);
|
||||||
|
s_po_record_tables_cache.put(constraintType, tables);
|
||||||
|
return tables;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get array of tables which has a Record_ID or Record_UU column with the defined Constraint Type
|
||||||
|
* @param constraintType - FKConstraintType of AD_Column
|
||||||
|
* @return array of KeyNamePair<AD_Table_ID, TableName>
|
||||||
|
*/
|
||||||
|
static private KeyNamePair[] getTablesWithRecordColumn(String constraintType) {
|
||||||
|
ArrayList<KeyNamePair> tables = new ArrayList<KeyNamePair>();
|
||||||
|
|
||||||
|
String sql = " SELECT t.AD_Table_ID, TableName "
|
||||||
|
+ " FROM AD_Column c "
|
||||||
|
+ " JOIN AD_Table t ON (c.AD_Table_ID = t.AD_Table_ID) "
|
||||||
|
+ " WHERE c.ColumnName IN (?,?) AND c.FKConstraintType = ? ";
|
||||||
|
PreparedStatement pstmt = null;
|
||||||
|
ResultSet rs = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pstmt = DB.prepareStatement (sql, null);
|
||||||
|
int idx = 1;
|
||||||
|
pstmt.setString(idx++, "Record_ID");
|
||||||
|
pstmt.setString(idx++, "Record_UU");
|
||||||
|
pstmt.setString(idx++, constraintType);
|
||||||
|
rs = pstmt.executeQuery ();
|
||||||
|
while (rs.next ())
|
||||||
|
{
|
||||||
|
tables.add(new KeyNamePair(rs.getInt(1), rs.getString(2)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.log (Level.SEVERE, sql, e);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
DB.close(rs, pstmt);
|
||||||
|
rs = null; pstmt = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyNamePair[] tablesArray = new KeyNamePair[tables.size()];
|
||||||
|
for(int i=0; i<tables.size(); i++) {
|
||||||
|
tablesArray[i] = tables.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tablesArray;
|
||||||
|
}
|
||||||
|
|
||||||
} // PO_Record
|
} // PO_Record
|
||||||
|
|
Loading…
Reference in New Issue