Shacklz
Shacklz

Reputation: 3

@Transactional: Not committing at end of transaction; using Spring Boot and neo4j

I wanted to make use of @Transactional, expecting that annotated methods get a separate transaction which will be committed at the end of the method.

However, if I check the DB, there was nothing committed:

@Transactional
public boolean borrowLibraryItem(Long libraryUserId, Long uniqueLibraryItemNumber) {
    boolean success = false;
    LibraryUser borrower = libraryUserRepository.findByLibraryUserId(libraryUserId);
    LibraryItem borrowItem = libraryItemRepository.findByUniqueLibraryItemNumber(uniqueLibraryItemNumber);
    success = borrower != null && borrowItem != null;
    if (success) {
        BorrowedByRel borrowedByRel = new BorrowedByRel(borrower, borrowItem);
        borrowedByRel.setBorrowDate(LocalDateTime.now());
        borrowItem.setBorrowedByRel(borrowedByRel);
        // libraryItemRepository.save(borrowItem);
    }
    return success;
}

The code commits the changes perfectly fine when using the repository.save-method, but not without.

Configuration is done via spring boot - as far as I understood, things should work out of the box this way (this might be the part where I got something wrong):

@SpringBootApplication
@EnableNeo4jRepositories(basePackages = "yalms.libraryapi.repositories")
@EntityScan("yalms.libraryapi.entities")
@EnableTransactionManagement
public class YalmsLibraryApplication {
    public static void main(String[] args) {
        SpringApplication.run(YalmsLibraryApplication.class, args);
    }
}

Something regarding transactions seems to be happening, as the following logging.level.org.springframework.transaction.interceptor=TRACE shows:

Getting transaction for borrowLibraryItem()..
Don't need to create transaction for findByLibraryUserId, not transactional..
Request: MATCH (n:`LibraryUser`)..
Don't need to create transaction for findByUniqueLibraryItemNumber, not transactional..
Request: MATCH (n:`LibraryItem`)..
Completing transaction for borrowLibraryItem().

I would expect though that the changes (the addition of a relationship) would be committed. Am I misunderstanding something here or do I have something not configured right? Any help would be very much appreciated, thanks in advance!

UPDATE: I've added a Neo4jTransactionManager-Bean as suggested in the comments, unfortunately it didn't help to solve my issue:

    @Bean
    public SessionFactory sessionFactory() {
        org.neo4j.ogm.config.Configuration configuration = new org.neo4j.ogm.config.Configuration.Builder()
                .uri(databaseUrl)
                .credentials(userName, password)
                .build();
        return new SessionFactory(configuration,"yalms.libraryapi");
    }

    @Bean
    public Neo4jTransactionManager transactionManager() {
        return new Neo4jTransactionManager(sessionFactory());
    }

I've enabled trace-output regarding everything coming from spring.data.*, and it's quite surprising to me as it seems that everything seems to be working fine:

TRACE 19634 --- [nio-8080-exec-2] .s.t.s.TransactionSynchronizationManager : Initializing transaction synchronization
TRACE 19634 --- [nio-8080-exec-2] o.s.t.i.TransactionInterceptor           : Getting transaction for [yalms.libraryapi.services.BorrowService.borrowLibraryItem]
TRACE 19634 --- [nio-8080-exec-2] o.s.t.i.TransactionInterceptor           : Don't need to create transaction for [org.springframework.data.neo4j.repository.support.SimpleNeo4jRepository.findByLibraryUserId]: This method isn't transactional.
DEBUG 19634 --- [nio-8080-exec-2] .s.d.n.r.q.d.DerivedGraphRepositoryQuery : Executing query for method findByLibraryUserId
TRACE 19634 --- [nio-8080-exec-2] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.data.neo4j.transaction.SessionHolder@4adaed6] for key [org.neo4j.ogm.session.SessionFactory@5f4fecd0] bound to thread [http-nio-8080-exec-2]
 INFO 19634 --- [nio-8080-exec-2] o.n.o.drivers.bolt.request.BoltRequest   : Request: MATCH (n:`LibraryUser`) WHERE n.`libraryUserId` = { `libraryUserId_0` } WITH n RETURN n,[ [ (n)<-[r_b1:`BORROWED_BY`]-(l1:`LibraryItem`) | [ r_b1, l1 ] ] ], ID(n) with params {libraryUserId_0=0}
TRACE 19634 --- [nio-8080-exec-2] o.s.t.i.TransactionInterceptor           : Don't need to create transaction for [org.springframework.data.neo4j.repository.support.SimpleNeo4jRepository.findByUniqueLibraryItemNumber]: This method isn't transactional.
DEBUG 19634 --- [nio-8080-exec-2] .s.d.n.r.q.d.DerivedGraphRepositoryQuery : Executing query for method findByUniqueLibraryItemNumber
TRACE 19634 --- [nio-8080-exec-2] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.data.neo4j.transaction.SessionHolder@4adaed6] for key [org.neo4j.ogm.session.SessionFactory@5f4fecd0] bound to thread [http-nio-8080-exec-2]
 INFO 19634 --- [nio-8080-exec-2] o.n.o.drivers.bolt.request.BoltRequest   : Request: MATCH (n:`LibraryItem`) WHERE n.`uniqueLibraryItemNumber` = { `uniqueLibraryItemNumber_0` } WITH n RETURN n,[ [ (n)-[r_b1:`BORROWED_BY`]->(l1:`LibraryUser`) | [ r_b1, l1 ] ] ], ID(n) with params {uniqueLibraryItemNumber_0=2}
TRACE 19634 --- [nio-8080-exec-2] o.s.t.i.TransactionInterceptor           : Completing transaction for [yalms.libraryapi.services.BorrowService.borrowLibraryItem]
TRACE 19634 --- [nio-8080-exec-2] o.s.d.n.t.Neo4jTransactionManager        : Triggering beforeCommit synchronization
TRACE 19634 --- [nio-8080-exec-2] o.s.d.n.t.Neo4jTransactionManager        : Triggering beforeCompletion synchronization
DEBUG 19634 --- [nio-8080-exec-2] o.s.d.n.t.Neo4jTransactionManager        : Initiating transaction commit
DEBUG 19634 --- [nio-8080-exec-2] o.s.d.n.t.Neo4jTransactionManager        : Committing Neo4j OGM transaction [org.neo4j.ogm.drivers.bolt.transaction.BoltTransaction@5a5172dc] on Session [org.neo4j.ogm.session.Neo4jSession@7dc575ae]
TRACE 19634 --- [nio-8080-exec-2] o.s.d.n.t.Neo4jTransactionManager        : Triggering afterCommit synchronization
TRACE 19634 --- [nio-8080-exec-2] .s.t.s.TransactionSynchronizationManager : Clearing transaction synchronization
TRACE 19634 --- [nio-8080-exec-2] o.s.d.n.t.Neo4jTransactionManager        : Triggering afterCompletion synchronization
DEBUG 19634 --- [nio-8080-exec-2] o.s.d.n.t.Neo4jTransactionManager        : Not closing pre-bound Neo4j Session after transaction
TRACE 19634 --- [nio-8080-exec-2] .s.t.s.TransactionSynchronizationManager : Removed value [org.springframework.data.neo4j.transaction.SessionHolder@4adaed6] for key [org.neo4j.ogm.session.SessionFactory@5f4fecd0] from thread [http-nio-8080-exec-2]
DEBUG 19634 --- [nio-8080-exec-2] o.s.d.n.w.s.OpenSessionInViewInterceptor : Closed Neo4j OGM Session in OpenSessionInViewInterceptor

But the update still does not end up in DB - makes kinda sense as there is no query that creates the new relation, but I do not understand why not: The retrieved entities seem to be attached to the transaction, the entities get modified within the transaction, so the changes should be committed once the transaction completes. Or did I misunderstand something here fundamentally?

Upvotes: 0

Views: 1969

Answers (1)

meistermeier
meistermeier

Reputation: 8262

Neo4j-OGM (the object graph mapper behind Spring Data Neo4j) needs an explicit save call. This is currently done by the explicit Spring Data Neo4j save call. There is no auto-commit when using Spring's transactional boundary.

Basically you have no error in your application and the explicit save call needs to be un-commented.

Some notes on the comments: You do not need to define a TransactionManager on your own within a Spring Boot applications. The spring-boot-starter-neo4j takes care of initialising Configuration, SessionFactory and an appropriate TransactionManager.

Upvotes: 3

Related Questions