Reputation: 1064
I'm just starting with Graph databases and figured the best to learn with was neo4j. I've got an embedded instance of neo4j running within the application (JSF Based). The use case of the application is mainly for me to learn the concepts and gotchas of using a graph db and also to help in determining the best way (i.e. embedded or stand alone). But my test application is a very basic contact app with the ability to define relationships (i.e. KNOWS, WORKS_WITH, WORKS_FOR, etc) and eventually be able to make recommendations (i.e. "you may also know") based on those relationships.
So far I've got basic c.r.u.d operations and can add/remove relationships. My next step will be to actually start traversing the graph (I think). But the issue I'm currently running into is once my session with the server expires, I can't re-establish a new session without getting the IllegalStateException error complaining that the store is currently in use. I think that it's because I never call shutdown on the neo4j instance. So this leads me to think that the way I'm handling transactions is all wrong so I'm hoping someone can correct my understanding (or mis-understanding as the case may be) of how I should be going about this.
Here are a few methods from my helper class which is being used to handle operations between the web client and neo4j:
public class NeoNodeUtils {
private GraphDatabaseService graphDb;
public NeoNodeUtils() {
setup();
}
public void setup() {
GraphDatabaseFactory neoFactory = new GraphDatabaseFactory();
setGraphDb(neoFactory.newEmbeddedDatabase("Morpheus"));
registerShutdownHook();
}
public GraphDatabaseService getGraphDb() {
return graphDb;
}
public void setGraphDb(GraphDatabaseService graphDb) {
this.graphDb = graphDb;
}
public Node getNode(Long id) {
Transaction tx = getGraphDb().beginTx();
Node node = null;
try {
node = getGraphDb().getNodeById(id);
tx.success();
} catch (Exception e) {
tx.failure();
e.printStackTrace();
} finally {
tx.finish();
return node;
}
}
public Node createNode() {
Transaction tx = getGraphDb().beginTx();
Node node = null;
try {
node = graphDb.createNode();
System.out.println("new nodeId = " + node.getId());
tx.success();
} catch (Exception e) {
tx.failure();
e.printStackTrace();
} finally {
tx.finish();
return node;
}
}
public Node addNodeProperty(Node node, String propertyName, Object propertyValue) {
Transaction tx = getGraphDb().beginTx();
try {
node.setProperty(propertyName, propertyValue);
tx.success();
} catch (Exception e) {
tx.failure();
e.printStackTrace();
} finally {
tx.finish();
return node;
}
}
public void shutDown() {
graphDb.shutdown();
}
public void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
graphDb.shutdown();
}
});
}
}
Every helper method is pretty much structured the same exact way as getNode(Long id) and createNode(), of course some have a little more logic but nothing very complex as I just want to learn with this.
So, finally to my question(s):
* Is this the proper way to go about handling these transactions? Or, should I implement some type of transaction manager?
* Also, should I be calling shutdown after every transaction?
* Maybe this should be handled at the client session level instead of the application level?
This class stays in server memory until the server (http/jvm) is restarted (if you're familiar with jsf, applicationScope) so it seems a little overkill to be calling shutdown after every transaction.
EDIT: I'm actually torn on which answer below to mark as accepted because they both answer my question to some extent or another. So, if you're wondering why an answer wasn't accepted, that's why.
Upvotes: 3
Views: 3950
Reputation: 2037
Since Java 7 autoclose and Neo4J 2.0 the way to handle transactions is:
try (Transaction tx = graphDb.beginTx())
{
...
tx.success();
}
So an explicit finish
isn't needed anymore.
Upvotes: 3
Reputation: 3054
Don't see a GraphDatabaseService as an SQLConnection or similar, it's a long lived instance and starting stopping it frequently removes a lot of benefits gained from a long lived such instance and imposes a lot of big unecessary overhead.
You don't need to call tx.failure() like that. A simple:
public Node createNode() { Transaction tx = getGraphDb().beginTx(); try { Node node = graphDb.createNode(); System.out.println("new nodeId = " + node.getId()); tx.success(); return node; } finally { tx.finish(); } }
Is fine since a transaction is only considered successful if success() is called and will otherwise be rolled back. Also read operations doesn't need to be performed in a transaction at all, so:
public Node getNode(Long id) { return getGraphDb().getNodeById(id); }
Is enough.
Upvotes: 5
Reputation: 19373
Your transaction handling seems fine. And no, you should not shutdown after every transaction. Just get a handle to the neo4j db via a singleton- from what you've mentioned above, it looks like this class is re-instantiated and that's the problem- it's trying to open another connection to neo4j and that's not allowed.
Upvotes: 1