This is an automated email from the ASF dual-hosted git repository.

doebele pushed a commit to branch version3
in repository https://gitbox.apache.org/repos/asf/empire-db.git


The following commit(s) were added to refs/heads/version3 by this push:
     new 1bd35d1  version 3 record transaction test
1bd35d1 is described below

commit 1bd35d1944e3a233211de5030fe8b10029ca7d39
Author: Rainer Döbele <[email protected]>
AuthorDate: Wed Jan 19 14:29:36 2022 +0100

    version 3 record transaction test
---
 .../org/apache/empire/samples/db/SampleApp.java    | 119 ++++++++++++++++++++
 .../main/java/org/apache/empire/db/DBContext.java  |  22 ++++
 .../main/java/org/apache/empire/db/DBRecord.java   | 122 +++++++++++++++++++++
 .../main/java/org/apache/empire/db/DBRecordV3.java |  72 ++++++++++++
 .../org/apache/empire/db/DBRollbackHandler.java    |  12 ++
 .../apache/empire/db/context/DBContextBase.java    | 108 ++++++++++++++++++
 .../apache/empire/db/context/DBContextStatic.java  |  33 ++++++
 7 files changed, 488 insertions(+)

diff --git 
a/empire-db-examples/empire-db-example-basic/src/main/java/org/apache/empire/samples/db/SampleApp.java
 
b/empire-db-examples/empire-db-example-basic/src/main/java/org/apache/empire/samples/db/SampleApp.java
index 34769f3..31ede21 100644
--- 
a/empire-db-examples/empire-db-example-basic/src/main/java/org/apache/empire/samples/db/SampleApp.java
+++ 
b/empire-db-examples/empire-db-example-basic/src/main/java/org/apache/empire/samples/db/SampleApp.java
@@ -26,10 +26,13 @@ import org.apache.empire.commons.StringUtils;
 import org.apache.empire.data.bean.BeanResult;
 import org.apache.empire.db.DBColumnExpr;
 import org.apache.empire.db.DBCommand;
+import org.apache.empire.db.DBContext;
 import org.apache.empire.db.DBDatabaseDriver;
 import org.apache.empire.db.DBReader;
 import org.apache.empire.db.DBRecord;
+import org.apache.empire.db.DBRecordV3;
 import org.apache.empire.db.DBSQLScript;
+import org.apache.empire.db.context.DBContextStatic;
 import org.apache.empire.db.derby.DBDatabaseDriverDerby;
 import org.apache.empire.db.h2.DBDatabaseDriverH2;
 import org.apache.empire.db.hsql.DBDatabaseDriverHSql;
@@ -81,6 +84,8 @@ public class SampleApp
                        // STEP 2: Choose a driver
                        System.out.println("*** Step 2: getDatabaseProvider() 
***");
                        DBDatabaseDriver driver = 
getDatabaseDriver(config.getDatabaseProvider(), conn);
+                       
+                       DBContext context = new DBContextStatic(driver, conn); 
 
             // STEP 3: Open Database (and create if not existing)
             System.out.println("*** Step 3: openDatabase() ***");
@@ -122,6 +127,13 @@ public class SampleApp
                        int idPers2 = insertEmployee(conn, "Fred", "Bloggs", 
Gender.M, idDevDep);
                        int idPers3 = insertEmployee(conn, "Emma", "White",  
Gender.F, idSalDep);
 
+            // commit
+            db.commit(conn);
+                       
+            int idEmp = testTransactionCreate(context, idDevDep);
+            testTransactionUpdate(context, idEmp);
+            testTransactionDelete(context, idEmp);
+
                        // STEP 7: Update Records (by setting the phone Number)
                        System.out.println("*** Step 7: updateEmployee() ***");
                        updateEmployee(conn, idPers1, "+49-7531-457160");
@@ -326,6 +338,113 @@ public class SampleApp
        }
 
        /**
+        * @param context
+        * @param idDep
+        */
+       private static int testTransactionCreate(DBContext context, int idDep)
+    {
+        SampleDB.Employees T = db.EMPLOYEES;
+        DBRecordV3 rec = new DBRecordV3(context, T);
+        
+        rec.create();
+        rec.setValue(T.FIRSTNAME, "Foo");
+        rec.setValue(T.LASTNAME, "Manchoo");
+        rec.setValue(T.GENDER, Gender.M);
+        rec.setValue(T.DEPARTMENT_ID, idDep);
+        rec.update();
+        log.info("Timestamp {}", rec.getString(T.UPDATE_TIMESTAMP));
+        
+        rec.setValue(T.FIRSTNAME, "Foo 2");
+        rec.setValue(T.LASTNAME, "Manchu");
+        rec.setValue(T.PHONE_NUMBER, "0815/4711");
+        rec.update();
+        log.info("Timestamp {}", rec.getString(T.UPDATE_TIMESTAMP));
+        
+        context.rollback();
+        
+        rec.setValue(T.FIRSTNAME, "Dr. Foo");
+        rec.update();
+        log.info("Timestamp {}", rec.getString(T.UPDATE_TIMESTAMP));
+
+        rec.delete();
+        
+        context.rollback();
+
+        // insert final
+        rec.update();
+        log.info("Timestamp {}", rec.getString(T.UPDATE_TIMESTAMP));
+        
+        log.info("testTransactionCreate performed OK");
+        context.commit();
+        
+        return rec.getInt(T.EMPLOYEE_ID);
+    }
+    /**
+     * @param context
+     * @param idDep
+     */
+    private static void testTransactionUpdate(DBContext context, int idEmp)
+    {
+        SampleDB.Employees T = db.EMPLOYEES;
+        DBRecordV3 rec = new DBRecordV3(context, T);
+        
+        rec.read(idEmp);
+        rec.setValue(T.PHONE_NUMBER, null);
+        rec.setValue(T.SALARY, "100.000");
+        rec.update();
+
+        log.info("Timestamp {}", rec.getString(T.UPDATE_TIMESTAMP));
+        
+        context.rollback();
+        
+        rec.setValue(T.PHONE_NUMBER, "07531-45716-0");
+        rec.update();
+
+        log.info("Timestamp {}", rec.getString(T.UPDATE_TIMESTAMP));
+        
+        context.rollback();
+
+        rec.update();
+
+        log.info("Timestamp {}", rec.getString(T.UPDATE_TIMESTAMP));
+        log.info("testTransactionUpdate performed OK");
+        context.commit();
+        
+    }
+    /**
+     * @param context
+     * @param idDep
+     */
+    private static void testTransactionDelete(DBContext context, int idEmp)
+    {
+        SampleDB.Employees T = db.EMPLOYEES;
+        DBRecordV3 rec = new DBRecordV3(context, T);
+        
+        rec.read(idEmp);
+        /*
+        log.info("Timestamp {}", rec.getString(T.UPDATE_TIMESTAMP));
+        rec.setValue(T.SALARY, "100.001");
+        rec.update();
+        log.info("Timestamp {}", rec.getString(T.UPDATE_TIMESTAMP));
+        */
+        rec.delete();
+        
+        context.rollback();
+
+        /*
+        DBCommand cmd = db.createCommand();
+        cmd.select(T.UPDATE_TIMESTAMP);
+        cmd.where (T.EMPLOYEE_ID.is(idEmp));
+        log.info("Timestamp {}", db.querySingleString(cmd, 
context.getConnection()));
+        */
+        
+        rec.update();
+        
+        log.info("Transaction performed OK");
+        
+    }
+
+       /**
         * <PRE>
         * Performs an SQL-Query and prints the result to System.out
         * 
diff --git a/empire-db/src/main/java/org/apache/empire/db/DBContext.java 
b/empire-db/src/main/java/org/apache/empire/db/DBContext.java
new file mode 100644
index 0000000..51e7017
--- /dev/null
+++ b/empire-db/src/main/java/org/apache/empire/db/DBContext.java
@@ -0,0 +1,22 @@
+/*
+ * ESTEAM Software GmbH, 19.01.2022
+ */
+package org.apache.empire.db;
+
+import java.sql.Connection;
+
+public interface DBContext
+{
+    DBDatabaseDriver getDriver();
+    
+    Connection getConnection();
+    
+    void commit();
+
+    void rollback();
+
+    void removeRollbackHandler(DBObject object);
+    
+    void addRollbackHandler(DBRollbackHandler handler);
+    
+}
diff --git a/empire-db/src/main/java/org/apache/empire/db/DBRecord.java 
b/empire-db/src/main/java/org/apache/empire/db/DBRecord.java
index d3ac89b..091db47 100644
--- a/empire-db/src/main/java/org/apache/empire/db/DBRecord.java
+++ b/empire-db/src/main/java/org/apache/empire/db/DBRecord.java
@@ -54,6 +54,128 @@ import org.w3c.dom.Element;
 public class DBRecord extends DBRecordData implements Record, Cloneable
 {
     private final static long serialVersionUID = 1L;
+    
+    /**
+     * DBRecordRollbackHandler
+     * @author doebele
+     */
+    public static class DBRecordRollbackHandler implements DBRollbackHandler
+    {
+        // Logger
+        private static final Logger log = 
LoggerFactory.getLogger(DBRecordRollbackHandler.class);
+        
+        public final DBRecord   record;
+        
+        private final State     state;  /* the original state */
+        private Object[]        fields;
+        private boolean[]       modified;
+        private Object          rowsetData;
+        
+        public DBRecordRollbackHandler(DBRecord record)
+        {
+            this.record = record;
+            // check
+            if (record.state==State.Invalid)
+                throw new ObjectNotValidException(record);
+            // save state
+            this.state = record.state;            
+            this.modified = copy(record.modified);
+            this.fields   = copy(record.fields);
+            this.rowsetData = record.rowsetData;
+        }
+
+        @Override
+        public DBObject getObject()
+        {
+            return record;
+        }
+
+        @Override
+        public void combine(DBRollbackHandler successor)
+        {
+            if (record!=successor.getObject())
+                throw new InvalidArgumentException("successor", successor);
+            // combine now
+            DBRecordRollbackHandler s = (DBRecordRollbackHandler)successor;
+            log.info("combining rollback state for record {}/{}", 
record.getRowSet().getName(), StringUtils.arrayToString(record.getKeyValues(), 
"|"));
+            if (s.modified==null)
+            {
+                return; // not modified!
+            }
+            // copy
+            for (int i=0; i<fields.length; i++)
+            {
+                if (fields[i]!= s.fields[i])
+                    fields[i] = s.fields[i]; 
+                // not modified
+                if (modified==null)
+                    continue;
+                if (modified[i]==false && s.modified[i])
+                    modified[i] = s.modified[i]; 
+            }
+            // check modified
+            if (modified==null && s.modified!=null)
+                modified = copy(s.modified);
+            /*
+            if (this.fields==s.fields)
+            {
+                // combine modified
+                if (this.modified==null)
+                    this.modified =s.modified;  // for delete case only!
+                else if (s.modified!=null)
+                    combineModified(s.modified);
+            }
+            else
+            {   // warn
+                log.warn("record fields have changed {}/{}", 
record.getRowSet().getName(), StringUtils.arrayToString(record.getKeyValues(), 
"|"));
+                this.fields = s.fields;
+                this.modified = s.modified;
+            }
+            */
+        }
+
+        @Override
+        public void rollback()
+        {
+            // rollback
+            record.state = this.state;
+            record.fields = this.fields;
+            record.modified = this.modified;
+            record.rowsetData = rowsetData;
+            // done
+            log.info("Rollback for record {}/{} performed", 
record.getRowSet().getName(), StringUtils.arrayToString(record.getKeyValues(), 
"|"));
+        }
+
+        @Override
+        public void discard()
+        {
+            /* nothing */
+        }
+        
+        private boolean[] copy(boolean[] other)
+        {
+            if (other==null)
+                return null;
+            boolean[] copy = new boolean[other.length];
+            for (int i=0; i<copy.length; i++)
+            {
+                copy[i] = other[i]; 
+            }
+            return copy;
+        }
+        
+        private Object[] copy(Object[] other)
+        {
+            if (other==null)
+                return null;
+            Object[] copy = new Object[other.length];
+            for (int i=0; i<copy.length; i++)
+            {
+                copy[i] = other[i]; 
+            }
+            return copy;
+        }
+    }
   
     /* Record state enum */
     public enum State
diff --git a/empire-db/src/main/java/org/apache/empire/db/DBRecordV3.java 
b/empire-db/src/main/java/org/apache/empire/db/DBRecordV3.java
new file mode 100644
index 0000000..ba13ca0
--- /dev/null
+++ b/empire-db/src/main/java/org/apache/empire/db/DBRecordV3.java
@@ -0,0 +1,72 @@
+/*
+ * ESTEAM Software GmbH, 19.01.2022
+ */
+package org.apache.empire.db;
+
+import org.apache.empire.exceptions.ObjectNotValidException;
+
+public class DBRecordV3 extends DBRecord
+{
+    
+    private static final long serialVersionUID = 1L;
+    
+    private final DBContext context;
+    
+    public DBRecordV3(DBContext context, DBRowSet rowset)
+    {
+        super(rowset);
+        this.context = context;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T extends DBContext> T  getContext()
+    {
+        return ((T)context);
+    }
+    
+    @SuppressWarnings("unchecked")
+    public <T extends DBRowSet> T getTable()
+    {
+        return (T)super.getRowSet();
+    }
+    
+    public void create()
+    {
+        super.create(getRowSet());
+        // remove rollback
+        context.removeRollbackHandler(this);
+    }
+    
+    public void read(Object... key)
+    {
+        super.read(getRowSet(), key, context.getConnection());
+        // remove rollback
+        context.removeRollbackHandler(this);
+    }
+    
+    public void update()
+    {
+        if (!isValid())
+            throw new ObjectNotValidException(this);
+        if (!isModified())
+            return; /* Not modified. Nothing to do! */
+        // allow rollback
+        context.addRollbackHandler(createRollbackHandler());
+        // update
+        super.update(context.getConnection());
+    }
+    
+    public void delete()
+    {
+        // allow rollback
+        context.addRollbackHandler(createRollbackHandler());
+        // delete
+        super.delete(context.getConnection());
+    }
+    
+    protected DBRollbackHandler createRollbackHandler()
+    {
+        return new DBRecordRollbackHandler(this);
+    }
+    
+}
diff --git 
a/empire-db/src/main/java/org/apache/empire/db/DBRollbackHandler.java 
b/empire-db/src/main/java/org/apache/empire/db/DBRollbackHandler.java
new file mode 100644
index 0000000..6e79a19
--- /dev/null
+++ b/empire-db/src/main/java/org/apache/empire/db/DBRollbackHandler.java
@@ -0,0 +1,12 @@
+/*
+ * ESTEAM Software GmbH, 19.01.2022
+ */
+package org.apache.empire.db;
+
+public interface DBRollbackHandler
+{
+    DBObject getObject();
+    void combine(DBRollbackHandler successor);
+    void rollback();
+    void discard();
+}
diff --git 
a/empire-db/src/main/java/org/apache/empire/db/context/DBContextBase.java 
b/empire-db/src/main/java/org/apache/empire/db/context/DBContextBase.java
new file mode 100644
index 0000000..5f4c6af
--- /dev/null
+++ b/empire-db/src/main/java/org/apache/empire/db/context/DBContextBase.java
@@ -0,0 +1,108 @@
+/*
+ * ESTEAM Software GmbH, 19.01.2022
+ */
+package org.apache.empire.db.context;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.empire.db.DBContext;
+import org.apache.empire.db.DBObject;
+import org.apache.empire.db.DBRollbackHandler;
+import org.apache.empire.db.exceptions.EmpireSQLException;
+import org.apache.empire.exceptions.InvalidArgumentException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class DBContextBase implements DBContext
+{
+    // Logger
+    private static final Logger log = 
LoggerFactory.getLogger(DBContextBase.class);
+    
+    private Map<DBObject, DBRollbackHandler> rollbackHandler;
+    
+    @Override
+    public synchronized void commit()
+    {
+        try
+        {   // Check argument
+            Connection conn = getConnection();
+            if (conn==null)
+                throw new InvalidArgumentException("conn", conn);
+            // Commit
+            if (conn.getAutoCommit()==false)
+                conn.commit();
+            // discard rollbacks
+            if (rollbackHandler!=null)
+                for (DBRollbackHandler handler : rollbackHandler.values())
+                    handler.discard();
+            // Done
+            return;
+        } catch (SQLException sqle) { 
+            // Commit failed!
+            throw new EmpireSQLException(getDriver(), sqle);
+        } finally {
+            rollbackHandler=null;
+        }
+    }
+
+    /**
+     * Discards all changes made since the previous commit/rollback
+     * and releases any database locks currently held by this
+     * Connection.
+     * <P>
+     * @param conn a valid database connection
+     */
+    @Override
+    public synchronized void rollback()
+    {
+        try
+        {   // Check argument
+            Connection conn = getConnection();
+            if (conn==null)
+                throw new InvalidArgumentException("conn", conn);
+            // rollback
+            log.info("Database rollback issued!");
+            conn.rollback();
+            // rollback
+            if (rollbackHandler!=null)
+                for (DBRollbackHandler handler : rollbackHandler.values())
+                    handler.rollback();
+            // Done
+            return;
+        } catch (SQLException sqle) { 
+            // Commit failed!
+            throw new EmpireSQLException(getDriver(), sqle);
+        } finally {
+            rollbackHandler=null;
+        }
+    }
+    
+    @Override
+    public synchronized void addRollbackHandler(DBRollbackHandler handler)
+    {
+        if (rollbackHandler==null)
+            rollbackHandler = new LinkedHashMap<DBObject, DBRollbackHandler>();
+        // check
+        DBObject object = handler.getObject();
+        if (rollbackHandler.containsKey(object))
+            rollbackHandler.get(object).combine(handler);
+        else
+            rollbackHandler.put(object, handler);
+    }
+    
+    @Override
+    public synchronized void removeRollbackHandler(DBObject object)
+    {
+        if (rollbackHandler!=null && rollbackHandler.containsKey(object))
+            log.info("test");
+        
+        if (object==null)
+            rollbackHandler=null;   // remove all
+        else if (rollbackHandler!=null && rollbackHandler.remove(object)!=null)
+            log.info("Rollback handler for object {} was removed", 
object.getClass().getSimpleName());
+    }
+
+}
diff --git 
a/empire-db/src/main/java/org/apache/empire/db/context/DBContextStatic.java 
b/empire-db/src/main/java/org/apache/empire/db/context/DBContextStatic.java
new file mode 100644
index 0000000..2102af4
--- /dev/null
+++ b/empire-db/src/main/java/org/apache/empire/db/context/DBContextStatic.java
@@ -0,0 +1,33 @@
+/*
+ * ESTEAM Software GmbH, 19.01.2022
+ */
+package org.apache.empire.db.context;
+
+import java.sql.Connection;
+
+import org.apache.empire.db.DBDatabaseDriver;
+
+public class DBContextStatic extends DBContextBase
+{
+    private final DBDatabaseDriver driver;
+    private final Connection conn;
+    
+    public DBContextStatic(DBDatabaseDriver driver, Connection conn)
+    {
+        this.driver = driver;
+        this.conn = conn;
+    }
+
+    @Override
+    public DBDatabaseDriver getDriver()
+    {
+        return driver;
+    }
+
+    @Override
+    public Connection getConnection()
+    {
+        return conn;
+    }
+
+}

Reply via email to