* Implement mechanism to register parent nodes with changed child nodes with the transactor.
When finishing the transaction, the transactor will call setLastSubnodeChange() on the parent nodes. This is necessary because the lastSubnodeChange flag should only be set after child node changes have been committed to the database. (Fixes bug 285 http://helma.org/bugs/show_bug.cgi?id=285 ) * Changed the way select statements are built: Use tablename.* rather than * to prevent columns from additionalTables are fetched unnecessarily. * Code cleanup everywhere, mostly in Transactor.java, Relation.java and NodeManager.java
This commit is contained in:
parent
6f8c9b9837
commit
36e0b31790
1 changed files with 98 additions and 51 deletions
|
@ -18,7 +18,6 @@ package helma.objectmodel.db;
|
||||||
|
|
||||||
import helma.framework.TimeoutException;
|
import helma.framework.TimeoutException;
|
||||||
import helma.objectmodel.*;
|
import helma.objectmodel.*;
|
||||||
import helma.util.Timer;
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -28,14 +27,18 @@ import java.util.*;
|
||||||
* changes in the database when a transaction is commited.
|
* changes in the database when a transaction is commited.
|
||||||
*/
|
*/
|
||||||
public class Transactor extends Thread {
|
public class Transactor extends Thread {
|
||||||
|
|
||||||
|
// The associated node manager
|
||||||
NodeManager nmgr;
|
NodeManager nmgr;
|
||||||
|
|
||||||
// List of nodes to be updated
|
// List of nodes to be updated
|
||||||
private HashMap nodes;
|
private HashMap dirtyNodes;
|
||||||
private ArrayList nodesArray;
|
|
||||||
|
|
||||||
// List of visited clean nodes
|
// List of visited clean nodes
|
||||||
private HashMap cleannodes;
|
private HashMap cleanNodes;
|
||||||
|
|
||||||
|
// List of nodes whose child index has been modified
|
||||||
|
private HashSet parentNodes;
|
||||||
|
|
||||||
// Is a transaction in progress?
|
// Is a transaction in progress?
|
||||||
private volatile boolean active;
|
private volatile boolean active;
|
||||||
|
@ -46,7 +49,6 @@ public class Transactor extends Thread {
|
||||||
|
|
||||||
// Transactions for SQL data sources
|
// Transactions for SQL data sources
|
||||||
protected HashMap sqlCon;
|
protected HashMap sqlCon;
|
||||||
public Timer timer;
|
|
||||||
|
|
||||||
// when did the current transaction start?
|
// when did the current transaction start?
|
||||||
private long tstart;
|
private long tstart;
|
||||||
|
@ -64,13 +66,14 @@ public class Transactor extends Thread {
|
||||||
public Transactor(Runnable runnable, ThreadGroup group, NodeManager nmgr) {
|
public Transactor(Runnable runnable, ThreadGroup group, NodeManager nmgr) {
|
||||||
super(group, runnable, group.getName());
|
super(group, runnable, group.getName());
|
||||||
this.nmgr = nmgr;
|
this.nmgr = nmgr;
|
||||||
nodes = new HashMap();
|
|
||||||
nodesArray = new ArrayList();
|
dirtyNodes = new HashMap();
|
||||||
cleannodes = new HashMap();
|
cleanNodes = new HashMap();
|
||||||
|
parentNodes = new HashSet();
|
||||||
|
|
||||||
sqlCon = new HashMap();
|
sqlCon = new HashMap();
|
||||||
active = false;
|
active = false;
|
||||||
killed = false;
|
killed = false;
|
||||||
timer = new Timer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,9 +85,8 @@ public class Transactor extends Thread {
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
Key key = node.getKey();
|
Key key = node.getKey();
|
||||||
|
|
||||||
if (!nodes.containsKey(key)) {
|
if (!dirtyNodes.containsKey(key)) {
|
||||||
nodes.put(key, node);
|
dirtyNodes.put(key, node);
|
||||||
nodesArray.add(node);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,8 +100,7 @@ public class Transactor extends Thread {
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
Key key = node.getKey();
|
Key key = node.getKey();
|
||||||
|
|
||||||
nodes.remove(key);
|
dirtyNodes.remove(key);
|
||||||
nodesArray.remove(node);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,8 +113,8 @@ public class Transactor extends Thread {
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
Key key = node.getKey();
|
Key key = node.getKey();
|
||||||
|
|
||||||
if (!cleannodes.containsKey(key)) {
|
if (!cleanNodes.containsKey(key)) {
|
||||||
cleannodes.put(key, node);
|
cleanNodes.put(key, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,8 +127,8 @@ public class Transactor extends Thread {
|
||||||
*/
|
*/
|
||||||
public void visitCleanNode(Key key, Node node) {
|
public void visitCleanNode(Key key, Node node) {
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
if (!cleannodes.containsKey(key)) {
|
if (!cleanNodes.containsKey(key)) {
|
||||||
cleannodes.put(key, node);
|
cleanNodes.put(key, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,9 +141,20 @@ public class Transactor extends Thread {
|
||||||
* @return ...
|
* @return ...
|
||||||
*/
|
*/
|
||||||
public Node getVisitedNode(Object key) {
|
public Node getVisitedNode(Object key) {
|
||||||
return (key == null) ? null : (Node) cleannodes.get(key);
|
return (key == null) ? null : (Node) cleanNodes.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param key ...
|
||||||
|
* @param node ...
|
||||||
|
*/
|
||||||
|
public void visitParentNode(Node node) {
|
||||||
|
parentNodes.add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
@ -181,7 +193,7 @@ public class Transactor extends Thread {
|
||||||
* @throws Exception ...
|
* @throws Exception ...
|
||||||
* @throws DatabaseException ...
|
* @throws DatabaseException ...
|
||||||
*/
|
*/
|
||||||
public synchronized void begin(String tnm) throws Exception {
|
public synchronized void begin(String name) throws Exception {
|
||||||
if (killed) {
|
if (killed) {
|
||||||
throw new DatabaseException("Transaction started on killed thread");
|
throw new DatabaseException("Transaction started on killed thread");
|
||||||
}
|
}
|
||||||
|
@ -190,13 +202,13 @@ public class Transactor extends Thread {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes.clear();
|
dirtyNodes.clear();
|
||||||
nodesArray.clear();
|
cleanNodes.clear();
|
||||||
cleannodes.clear();
|
parentNodes.clear();
|
||||||
txn = nmgr.db.beginTransaction();
|
txn = nmgr.db.beginTransaction();
|
||||||
active = true;
|
active = true;
|
||||||
tstart = System.currentTimeMillis();
|
tstart = System.currentTimeMillis();
|
||||||
tname = tnm;
|
tname = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -211,15 +223,19 @@ public class Transactor extends Thread {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ins = 0;
|
int inserted = 0;
|
||||||
int upd = 0;
|
int updated = 0;
|
||||||
int dlt = 0;
|
int deleted = 0;
|
||||||
int l = nodesArray.size();
|
|
||||||
|
|
||||||
|
Object[] dirty = dirtyNodes.values().toArray();
|
||||||
|
|
||||||
|
// the replicator to send update notifications to, if defined
|
||||||
Replicator replicator = nmgr.getReplicator();
|
Replicator replicator = nmgr.getReplicator();
|
||||||
|
// the set to collect DbMappings to be marked as changed
|
||||||
|
HashSet dirtyDbMappings = new HashSet();
|
||||||
|
|
||||||
for (int i = 0; i < l; i++) {
|
for (int i = 0; i < dirty.length; i++) {
|
||||||
Node node = (Node) nodesArray.get(i);
|
Node node = (Node) dirty[i];
|
||||||
|
|
||||||
// update nodes in db
|
// update nodes in db
|
||||||
int nstate = node.getState();
|
int nstate = node.getState();
|
||||||
|
@ -227,47 +243,67 @@ public class Transactor extends Thread {
|
||||||
if (nstate == Node.NEW) {
|
if (nstate == Node.NEW) {
|
||||||
nmgr.registerNode(node); // register node with nodemanager cache
|
nmgr.registerNode(node); // register node with nodemanager cache
|
||||||
nmgr.insertNode(nmgr.db, txn, node);
|
nmgr.insertNode(nmgr.db, txn, node);
|
||||||
|
dirtyDbMappings.add(node.getDbMapping());
|
||||||
node.setState(Node.CLEAN);
|
node.setState(Node.CLEAN);
|
||||||
|
|
||||||
if (replicator != null) {
|
if (replicator != null) {
|
||||||
replicator.addNewNode(node);
|
replicator.addNewNode(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
ins++;
|
inserted++;
|
||||||
nmgr.app.logEvent("inserted: Node " + node.getPrototype() + "/" +
|
nmgr.app.logEvent("inserted: Node " + node.getPrototype() + "/" +
|
||||||
node.getID());
|
node.getID());
|
||||||
} else if (nstate == Node.MODIFIED) {
|
} else if (nstate == Node.MODIFIED) {
|
||||||
nmgr.updateNode(nmgr.db, txn, node);
|
// only mark DbMapping as dirty if updateNode returns true
|
||||||
|
if (nmgr.updateNode(nmgr.db, txn, node)) {
|
||||||
|
dirtyDbMappings.add(node.getDbMapping());
|
||||||
|
}
|
||||||
node.setState(Node.CLEAN);
|
node.setState(Node.CLEAN);
|
||||||
|
|
||||||
if (replicator != null) {
|
if (replicator != null) {
|
||||||
replicator.addModifiedNode(node);
|
replicator.addModifiedNode(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
upd++;
|
updated++;
|
||||||
nmgr.app.logEvent("updated: Node " + node.getPrototype() + "/" +
|
nmgr.app.logEvent("updated: Node " + node.getPrototype() + "/" +
|
||||||
node.getID());
|
node.getID());
|
||||||
} else if (nstate == Node.DELETED) {
|
} else if (nstate == Node.DELETED) {
|
||||||
// nmgr.app.logEvent ("deleted: "+node.getFullName ()+" ("+node.getName ()+")");
|
|
||||||
nmgr.deleteNode(nmgr.db, txn, node);
|
nmgr.deleteNode(nmgr.db, txn, node);
|
||||||
|
dirtyDbMappings.add(node.getDbMapping());
|
||||||
nmgr.evictNode(node);
|
nmgr.evictNode(node);
|
||||||
|
|
||||||
if (replicator != null) {
|
if (replicator != null) {
|
||||||
replicator.addDeletedNode(node);
|
replicator.addDeletedNode(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
dlt++;
|
deleted++;
|
||||||
} else {
|
|
||||||
// nmgr.app.logEvent ("noop: "+node.getFullName ());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
node.clearWriteLock();
|
node.clearWriteLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes.clear();
|
long now = System.currentTimeMillis();
|
||||||
nodesArray.clear();
|
|
||||||
cleannodes.clear();
|
|
||||||
|
|
||||||
|
// set last data change times in db-mappings
|
||||||
|
for (Iterator i = dirtyDbMappings.iterator(); i.hasNext(); ) {
|
||||||
|
DbMapping dbm = (DbMapping) i.next();
|
||||||
|
if (dbm != null) {
|
||||||
|
dbm.setLastDataChange(now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set last subnode change times in parent nodes
|
||||||
|
for (Iterator i = parentNodes.iterator(); i.hasNext(); ) {
|
||||||
|
Node node = (Node) i.next();
|
||||||
|
node.setLastSubnodeChange(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear the node collections
|
||||||
|
dirtyNodes.clear();
|
||||||
|
cleanNodes.clear();
|
||||||
|
parentNodes.clear();
|
||||||
|
|
||||||
|
// save the id-generator for the embedded db, if necessary
|
||||||
if (nmgr.idgen.dirty) {
|
if (nmgr.idgen.dirty) {
|
||||||
nmgr.db.saveIDGenerator(txn, nmgr.idgen);
|
nmgr.db.saveIDGenerator(txn, nmgr.idgen);
|
||||||
nmgr.idgen.dirty = false;
|
nmgr.idgen.dirty = false;
|
||||||
|
@ -279,9 +315,10 @@ public class Transactor extends Thread {
|
||||||
txn = null;
|
txn = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
nmgr.app.logAccess(tname + " " + l + " marked, " + ins + " inserted, " + upd +
|
nmgr.app.logAccess(tname + " " + dirty.length + " marked, " + inserted +
|
||||||
" updated, " + dlt + " deleted in " +
|
" inserted, " + updated +
|
||||||
(System.currentTimeMillis() - tstart) + " millis");
|
" updated, " + deleted + " deleted in " +
|
||||||
|
(now - tstart) + " millis");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -290,20 +327,30 @@ public class Transactor extends Thread {
|
||||||
* @throws Exception ...
|
* @throws Exception ...
|
||||||
*/
|
*/
|
||||||
public synchronized void abort() throws Exception {
|
public synchronized void abort() throws Exception {
|
||||||
int l = nodesArray.size();
|
Object[] dirty = dirtyNodes.values().toArray();
|
||||||
|
|
||||||
for (int i = 0; i < l; i++) {
|
// evict dirty nodes from cache
|
||||||
Node node = (Node) nodesArray.get(i);
|
for (int i = 0; i < dirty.length; i++) {
|
||||||
|
Node node = (Node) dirty[i];
|
||||||
|
|
||||||
// Declare node as invalid, so it won't be used by other threads that want to
|
// Declare node as invalid, so it won't be used by other threads
|
||||||
// write on it and remove it from cache
|
// that want to write on it and remove it from cache
|
||||||
nmgr.evictNode(node);
|
nmgr.evictNode(node);
|
||||||
node.clearWriteLock();
|
node.clearWriteLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes.clear();
|
long now = System.currentTimeMillis();
|
||||||
nodesArray.clear();
|
|
||||||
cleannodes.clear();
|
// set last subnode change times in parent nodes
|
||||||
|
for (Iterator i = parentNodes.iterator(); i.hasNext(); ) {
|
||||||
|
Node node = (Node) i.next();
|
||||||
|
node.setLastSubnodeChange(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear the node collections
|
||||||
|
dirtyNodes.clear();
|
||||||
|
cleanNodes.clear();
|
||||||
|
parentNodes.clear();
|
||||||
|
|
||||||
// close any JDBC connections associated with this transactor thread
|
// close any JDBC connections associated with this transactor thread
|
||||||
closeConnections();
|
closeConnections();
|
||||||
|
|
Loading…
Add table
Reference in a new issue