diff --git a/base/src/org/compiere/model/PO.java b/base/src/org/compiere/model/PO.java index 35b94c9229..5afbfdf604 100644 --- a/base/src/org/compiere/model/PO.java +++ b/base/src/org/compiere/model/PO.java @@ -2104,6 +2104,14 @@ public abstract class PO } else { + if (savepoint != null) + { + try { + trx.releaseSavepoint(savepoint); + } catch (SQLException e) { + e.printStackTrace(); + } + } savepoint = null; trx = null; } diff --git a/base/src/org/compiere/util/Trx.java b/base/src/org/compiere/util/Trx.java index 5d86f9c0e8..a1ce74c12f 100644 --- a/base/src/org/compiere/util/Trx.java +++ b/base/src/org/compiere/util/Trx.java @@ -45,6 +45,9 @@ import org.adempiere.exceptions.AdempiereException; * - use UUID for safer transaction name generation * @author Teo Sarca, http://www.arhipac.ro *
  • FR [ 2080217 ] Implement TrxRunnable + * @author Teo Sarca, teo.sarca@gmail.com + *
  • BF [ 2849122 ] PO.AfterSave is not rollback on error - add releaseSavepoint method + * https://sourceforge.net/tracker/index.php?func=detail&aid=2849122&group_id=176962&atid=879332# */ public class Trx implements VetoableChangeListener { @@ -388,6 +391,25 @@ public class Trx implements VetoableChangeListener } } + /** + * Release Savepoint + * @param savepoint + * @throws SQLException + * @see {@link Connection#releaseSavepoint(Savepoint)} + */ + public void releaseSavepoint(Savepoint savepoint) throws SQLException + { + if (m_connection == null) + { + getConnection(); + } + if(m_connection != null) + { + m_connection.releaseSavepoint(savepoint); + } + + } + /** * String Representation * @return info diff --git a/extend/src/test/functional/POTest.java b/extend/src/test/functional/POTest.java index da57809df3..d25f6252e6 100644 --- a/extend/src/test/functional/POTest.java +++ b/extend/src/test/functional/POTest.java @@ -15,6 +15,63 @@ import test.AdempiereTestCase; */ public class POTest extends AdempiereTestCase { + public static class MyTestPO extends MTest + { + private static final long serialVersionUID = -6861171283806782985L; + protected boolean failOnSave = false; + + + private MyTestPO m_parent = null; + private MyTestPO m_dependentRecord = null; + + public static String getName(int Test_ID, String trxName) + { + String sql = "SELECT "+COLUMNNAME_Name+" FROM "+Table_Name + +" WHERE "+COLUMNNAME_Test_ID+"=?"; + return DB.getSQLValueStringEx(trxName, sql, Test_ID); + } + + public static boolean exists(int Test_ID, String trxName) + { + final String sql = "SELECT "+COLUMNNAME_Test_ID+" FROM "+Table_Name + +" WHERE "+COLUMNNAME_Test_ID+"=?"; + int id = DB.getSQLValueEx(trxName, sql, Test_ID); + return id > 0 && id == Test_ID; + } + + public MyTestPO(Properties ctx, boolean failOnSave, String trxName) + { + super(ctx, "Test_"+System.currentTimeMillis(), 10); + this.set_TrxName(trxName); + this.setDescription(""+getClass()); + this.failOnSave = failOnSave; + } + public MyTestPO(Properties ctx, int id, String trxName) + { + super(ctx, id, trxName); + } + @Override + protected boolean afterSave(boolean newRecord, boolean success) + { + if (m_parent == null) + { + m_dependentRecord = new MyTestPO(getCtx(), false, get_TrxName()); + m_dependentRecord.m_parent = this; + m_dependentRecord.setName("D_"+this.getName()); + m_dependentRecord.saveEx(); + } + + if (this.failOnSave) + throw new RuntimeException("Never save this object [trxName="+get_TrxName()+", success="+success+"]"); + return true; + } + + public int getDependent_ID() + { + return (m_dependentRecord != null ? m_dependentRecord.get_ID() : -1); + } + }; + /** * Tests the following methods: *