[jboss-cvs] jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/listener ...
Christian Bauer
christian at hibernate.org
Tue Dec 18 23:29:21 EST 2007
User: cbauer
Date: 07/12/18 23:29:21
Added: examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/listener
InsertNestedSetOperation.java
NestedSetPostInsertEventListener.java
NestedSetOperation.java
NestedSetPostDeleteEventListener.java package.html
DeleteNestedSetOperation.java
Log:
Major rewrite of the most of the application
Revision Changes Path
1.1 date: 2007/12/19 04:29:21; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/listener/InsertNestedSetOperation.java
Index: InsertNestedSetOperation.java
===================================================================
/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.seam.wiki.core.nestedset.listener;
import org.hibernate.StatelessSession;
import org.hibernate.Query;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.seam.wiki.core.nestedset.NestedSetNode;
import java.util.Collection;
/**
* Moves the values of all nodes on the right side of an inserted node.
*
* @author Christian Bauer
*/
class InsertNestedSetOperation extends NestedSetOperation {
private static final Log log = LogFactory.getLog(InsertNestedSetOperation.class);
long spaceNeeded = 2l;
long parentThread;
long newLeft;
long newRight;
public InsertNestedSetOperation(NestedSetNode node) {
super(node);
}
protected void beforeExecution() {
log.trace("nested set insert of entity: " + nodeEntityName);
if (node.getParentNodeInfo() == null) {
log.trace("node has no parent, starting new thread");
// Root node of a tree, new thread
parentThread = node.getId();
newLeft = 1l;
newRight = 2l;
} else {
log.trace("node has a parent, appending to existing thread");
// Child node of a parent
parentThread = node.getParentNodeInfo().getNsThread();
newLeft = node.getParentNodeInfo().getNsRight();
newRight = newLeft + spaceNeeded -1;
}
log.trace("calculated the thread: " + parentThread + " left: " + newLeft + " right: " + newRight);
}
protected void executeOnDatabase(StatelessSession ss) {
log.trace("executing nested set insert on database");
Query updateLeft =
ss.createQuery("update " + nodeEntityName + " n set " +
" n.nodeInfo.nsLeft = n.nodeInfo.nsLeft + :spaceNeeded " +
" where n.nodeInfo.nsThread = :thread and n.nodeInfo.nsLeft > :right");
updateLeft.setParameter("spaceNeeded", spaceNeeded);
updateLeft.setParameter("thread", parentThread);
updateLeft.setParameter("right", newLeft);
int updateLeftCount = updateLeft.executeUpdate();
log.trace("updated left values of nested set nodes: " + updateLeftCount);
Query updateRight =
ss.createQuery("update " + nodeEntityName + " n set " +
" n.nodeInfo.nsRight = n.nodeInfo.nsRight + :spaceNeeded " +
" where n.nodeInfo.nsThread = :thread and n.nodeInfo.nsRight >= :right");
updateRight.setParameter("spaceNeeded", spaceNeeded);
updateRight.setParameter("thread", parentThread);
updateRight.setParameter("right", newLeft);
int updateRightCount = updateRight.executeUpdate();
log.trace("updated right values of nested set nodes: " + updateRightCount);
log.trace("updating the newly inserted row with thread, left, and right values");
/*
TODO: http://opensource.atlassian.com/projects/hibernate/browse/HHH-1657
Query updateNode =
ss.createQuery("update " + nodeEntityName + " n set " +
" n.nodeInfo.nsLeft = :left, n.nodeInfo.nsRight = :right, n.nodeInfo.nsThread = :thread " +
" where n.id = :id");
*/
Query updateNode = ss.getNamedQuery("updateNestedSet."+nodeEntityName);
updateNode.setParameter("thread", parentThread);
updateNode.setParameter("left", newLeft);
updateNode.setParameter("right", newRight );
updateNode.setParameter("id", node.getId());
updateNode.executeUpdate();
}
protected void executeInMemory(Collection<NestedSetNode> nodesInPersistenceContext) {
log.trace("updating in memory nodes (flat) in the persistence context: " + nodesInPersistenceContext.size());
for (NestedSetNode n : nodesInPersistenceContext) {
if (n.getNodeInfo().getNsThread().equals(parentThread) && n.getNodeInfo().getNsLeft() > newLeft) {
n.getNodeInfo().setNsLeft(n.getNodeInfo().getNsLeft() + spaceNeeded);
}
if (n.getNodeInfo().getNsThread().equals(parentThread) && n.getNodeInfo().getNsRight() >= newLeft) {
n.getNodeInfo().setNsRight(n.getNodeInfo().getNsRight() + spaceNeeded);
}
}
}
protected void afterExecution() {
// Set the values of the "read-only" properties
node.getNodeInfo().setNsThread(parentThread);
node.getNodeInfo().setNsLeft(newLeft);
node.getNodeInfo().setNsRight(newRight);
}
}
1.1 date: 2007/12/19 04:29:21; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/listener/NestedSetPostInsertEventListener.java
Index: NestedSetPostInsertEventListener.java
===================================================================
/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.seam.wiki.core.nestedset.listener;
import org.hibernate.ejb.event.EJB3PostInsertEventListener;
import org.hibernate.event.PostInsertEvent;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.seam.wiki.core.nestedset.NestedSetNode;
/**
* Executes the nested set tree traversal after a node was inserted.
*
* @author Christian Bauer
*/
public class NestedSetPostInsertEventListener extends EJB3PostInsertEventListener {
private static final Log log = LogFactory.getLog(NestedSetPostInsertEventListener.class);
public void onPostInsert(PostInsertEvent event) {
super.onPostInsert(event);
if ( NestedSetNode.class.isAssignableFrom(event.getEntity().getClass())) {
log.debug("executing nested set insert operation, recalculating the tree");
new InsertNestedSetOperation( (NestedSetNode)event.getEntity() ).execute(event.getSession());
}
}
}
1.1 date: 2007/12/19 04:29:21; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/listener/NestedSetOperation.java
Index: NestedSetOperation.java
===================================================================
/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.seam.wiki.core.nestedset.listener;
import org.hibernate.Session;
import org.hibernate.StatelessSession;
import org.hibernate.event.EventSource;
import org.hibernate.impl.SessionFactoryImpl;
import org.hibernate.util.LazyIterator;
import org.jboss.seam.wiki.core.nestedset.NestedSetNode;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
/**
* The contract of a nested set operation sequence as executed in a Hibernate event listener.
* <p>
* Guarantees that first the database tree nodes are updated, then the in-memory nodes
* currently managed by the persistence context.
* </p>
* To access the database, an operation uses a <tt>StatelessSession</tt> of Hibernate, and
* it obtains a JDBC connection using the Hibernate connection provider. If run in an
* application server with JTS/JTA, the <tt>getConnection()</tt> method
* returns the same connection handle that is already used inside the current transaction.
* This means we run on the same connection and transaction as the rest of the Hibernate flush event
* that executes the <tt>NestedSetOperation</tt>. However, if you run this outside of a managed
* environment, a new JDBC connection might be obtained from the JDBC connection pool.
* In that case, you should enable auto-commit mode in your Hibernate configuration. Or,
* if you want the database tree updates to be atomic and isolated (a good idea), you can
* override the <tt>beforeExecution()</tt> and <tt>afterExecution()</tt> methods and begin
* and commit a database transaction manually. Note that this still would be outside the
* initial connection and transaction, and therefore not be atomic with the overall tree
* manipulation. This can be improved as soon as Hibernate implements a new contract
* for the deprecated <tt>Session#connection()</tt> method.
* </p>
*
* TODO: We should lock the tables!
*
* @author Christian Bauer
*/
public class NestedSetOperation {
private static final Log log = LogFactory.getLog(NestedSetOperation.class);
protected NestedSetNode node;
protected String nodeEntityName;
public NestedSetOperation(NestedSetNode node) {
this.node = node;
this.nodeEntityName = node.getClass().getName();
}
// The main sequence of the operation, override to implement your operation
protected void beforeExecution() {}
protected void executeOnDatabase(StatelessSession session) {}
protected void executeInMemory(Collection<NestedSetNode> inMemoryState) {}
protected void afterExecution() {}
// The procedure that executes the sequence of the operation
public void execute(EventSource session) {
StatelessSession ss = null;
Connection jdbcConnection = null;
try {
jdbcConnection = getConnection(session);
log.trace("opening new session on existing connection");
ss = session.getSessionFactory().openStatelessSession(jdbcConnection);
beforeExecution();
executeOnDatabase(ss);
// Find all NestedSetNode instances in the persistence context
Collection<NestedSetNode> nodesInPersistenceContext = new HashSet<NestedSetNode>();
Iterator contextIterator = new LazyIterator( session.getPersistenceContext().getEntitiesByKey() );
while (contextIterator.hasNext()) {
Object o = contextIterator.next();
if (NestedSetNode.class.isAssignableFrom(o.getClass())) nodesInPersistenceContext.add((NestedSetNode)o);
}
executeInMemory(nodesInPersistenceContext);
afterExecution();
} catch (Exception ex) {
log.error(ex);
throw new RuntimeException(ex);
} finally {
if (ss != null) {
try {
log.trace("closing session and connection");
jdbcConnection.close();
ss.close();
} catch(SQLException ex) {
log.error("could not close connection or session", ex);
}
}
}
}
protected Connection getConnection(Session session) throws Exception {
// We do not use session.connection() because it conflicts with Hibernates aggressive collection release
return ((SessionFactoryImpl)session.getSessionFactory()).getConnectionProvider().getConnection();
}
}
1.1 date: 2007/12/19 04:29:21; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/listener/NestedSetPostDeleteEventListener.java
Index: NestedSetPostDeleteEventListener.java
===================================================================
/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.seam.wiki.core.nestedset.listener;
import org.hibernate.ejb.event.EJB3PostDeleteEventListener;
import org.hibernate.event.PostDeleteEvent;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.seam.wiki.core.nestedset.NestedSetNode;
/**
* Executes the nested set tree traversal after a node was deleted.
*
* @author Christian Bauer
*/
public class NestedSetPostDeleteEventListener extends EJB3PostDeleteEventListener {
private static final Log log = LogFactory.getLog(NestedSetPostDeleteEventListener.class);
public void onPostDelete(PostDeleteEvent event) {
super.onPostDelete(event);
if ( NestedSetNode.class.isAssignableFrom(event.getEntity().getClass())) {
log.debug("executing nested set delete operation, recalculating the tree");
new DeleteNestedSetOperation( (NestedSetNode)event.getEntity() ).execute(event.getSession());
}
}
}
1.1 date: 2007/12/19 04:29:21; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/listener/package.html
Index: package.html
===================================================================
<html>
<head></head>
<body>
<p>
Hibernate event listeners for transparent in-database nested set tree updates.
</p>
</body>
</html>
1.1 date: 2007/12/19 04:29:21; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/listener/DeleteNestedSetOperation.java
Index: DeleteNestedSetOperation.java
===================================================================
/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.seam.wiki.core.nestedset.listener;
import org.hibernate.Query;
import org.hibernate.StatelessSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.seam.wiki.core.nestedset.NestedSetNode;
import java.util.Collection;
/**
* Moves the values of all nodes on the right side of a deleted node.
*
* @author Christian Bauer
*/
class DeleteNestedSetOperation extends NestedSetOperation {
private static final Log log = LogFactory.getLog(DeleteNestedSetOperation.class);
long databaseMoveOffset;
public DeleteNestedSetOperation(NestedSetNode node) {
super(node);
}
protected void beforeExecution() {
databaseMoveOffset = node.getNodeInfo().getNsRight() - node.getNodeInfo().getNsLeft() + 1;
log.trace("calculated database offset: " + databaseMoveOffset);
}
protected void executeOnDatabase(StatelessSession ss) {
Query updateLeft =
ss.createQuery("update " + nodeEntityName + " n set " +
" n.nodeInfo.nsLeft = n.nodeInfo.nsLeft - :offset " +
" where n.nodeInfo.nsThread = :thread and n.nodeInfo.nsLeft > :right");
updateLeft.setParameter("offset", databaseMoveOffset);
updateLeft.setParameter("thread", node.getNodeInfo().getNsThread());
updateLeft.setParameter("right", node.getNodeInfo().getNsRight());
int updateLeftCount = updateLeft.executeUpdate();
log.trace("updated left values of nested set nodes: " + updateLeftCount);
Query updateRight =
ss.createQuery("update " + nodeEntityName + " n set " +
" n.nodeInfo.nsRight = n.nodeInfo.nsRight - :offset " +
" where n.nodeInfo.nsThread = :thread and n.nodeInfo.nsRight > :right");
updateRight.setParameter("offset", databaseMoveOffset);
updateRight.setParameter("thread", node.getNodeInfo().getNsThread());
updateRight.setParameter("right", node.getNodeInfo().getNsRight());
int updateRightCount = updateRight.executeUpdate();
log.trace("updated right values of nested set nodes: " + updateRightCount);
}
protected void executeInMemory(Collection<NestedSetNode> nodesInPersistenceContext) {
log.trace("updating in memory nodes (flat) in the persistence context: " + nodesInPersistenceContext.size());
for (NestedSetNode n: nodesInPersistenceContext) {
if (n.getNodeInfo().getNsThread().equals(node.getNodeInfo().getNsThread())
&& n.getNodeInfo().getNsRight() > node.getNodeInfo().getNsRight()) {
n.getNodeInfo().setNsRight(n.getNodeInfo().getNsRight() - databaseMoveOffset);
}
if (n.getNodeInfo().getNsThread().equals(node.getNodeInfo().getNsThread())
&& n.getNodeInfo().getNsLeft() > node.getNodeInfo().getNsRight()) {
n.getNodeInfo().setNsLeft(n.getNodeInfo().getNsLeft() - databaseMoveOffset);
}
}
}
}
More information about the jboss-cvs-commits
mailing list