Question

I'm using the simple object/graph mapping in Spring Data Neo4j 2.0, where I perform persistence operations using the Spring Data repository framework. I'm working with the repositories rather than working with the Neo4jTemplate. I inject the repositories into my Spring Web MVC controllers, and the controllers call the repos directly. (No intermediate service layer--my operations are generally CRUDs and finder queries.)

When I do read operations, there are no issues. But when I do write operations, I get "NotInTransactionException". My understanding is that read ops in Neo4j don't require transactions, but write ops do.

What's the best way to get transactions into the picture here, assuming I want to stick with the simple OGM? I'm wanting to use @Transactional, but putting that on the various repository interfaces doesn't work. If I introduce an intermediate service tier in between the controllers and the repositories and then annotate the service beans with @Transactional, then it works, but I'm wondering whether there's a simpler way to do it. Without Spring Data, I'd typically have access to the DAO (repository) implementations, so I'd be able to annotate the concrete DAOs with @Transactional if I wanted to avoid a pass-through service tier. With Spring Data the repos are dynamically generated so that doesn't appear to be an option.

Was it helpful?

Solution

First, note that having transactional DAOs is not generally a good practice. But if you don't have a service layer, then let it be on the DAOs.

Then, you can enable declarative transactions. Here's how I did it:

First, define an annotation called @GraphTransactional:

@Retention(RetentionPolicy.RUNTIME)
@Transactional("neo4jTransactionManager")
public @interface GraphTransactional {

}

Update: spring-data-neo4j have added such an annotation, so you can reuse it instead of creating a new one: @Neo4jTransactional

Then, in applicationContext.xml, have the following (where neo4jdb is your EmbeddedGraphDatabase):

<bean id="neo4jTransactionManagerService"
    class="org.neo4j.kernel.impl.transaction.SpringTransactionManager">
    <constructor-arg ref="neo4jdb" />
</bean>
<bean id="neo4jUserTransactionService" class="org.neo4j.kernel.impl.transaction.UserTransactionImpl">
    <constructor-arg ref="neo4jdb" />
</bean>

<bean id="neo4jTransactionManager"
    class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="transactionManager" ref="neo4jTransactionManagerService" />
    <property name="userTransaction" ref="neo4jUserTransactionService" />
</bean>

<tx:annotation-driven transaction-manager="neo4jTransactionManager" />

Have in mind that if you use another transaction manager as well, you'd have to specify order="2" for this annotation-driven definition, and also have in mind that you won't have two-phase commit if you have one method that is declared to be both sql and neo4j transactional.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top