Search code examples
springspring-bootneo4jspring-data-neo4j-4neo4j-ogm

Spring Data Neo4j InvalidDataAccessApiUsageException: Transaction is not current for this thread


I have a Spring boot neo4j application and want to store users in the Neo4j database. I followed the instructions found here. The neo4j configuration is this:

@Configuration
@EnableWebMvc
@ComponentScan({"eu.bm"})
@EnableNeo4jRepositories("eu.bm.repository")
@EnableTransactionManagement
public class bmConfiguration extends WebMvcConfigurerAdapter {

@Bean
public OpenSessionInViewInterceptor openSessionInViewInterceptor() {
    OpenSessionInViewInterceptor openSessionInViewInterceptor =
            new OpenSessionInViewInterceptor();
    openSessionInViewInterceptor.setSessionFactory(sessionFactory());
    return openSessionInViewInterceptor;
}

public void addInterceptors(InterceptorRegistry registry) {
    registry.addWebRequestInterceptor(openSessionInViewInterceptor());
}

@Bean
public static SessionFactory sessionFactory() {
    return new SessionFactory("eu.bm.domain");
}

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

The user repository is this:

@Repository
public interface UserRepository extends GraphRepository<User>{

@Query("MATCH (user:User) where user.name={0} return user ")
User findUser(String username);

@Query("MATCH (user:User) where user.name={0} delete user ")
User deleteUser(String username);

@Query("match (user:User) delete user")
User deleteAllUsers();

}

I also have this User management service setup:

@Component
public interface UserManagementService {

List<User> listAll();

User save(User user);

User findUser(String username);
}

which is implemented here:

@Service
@Transactional
public class UserManagementServiceImpl implements UserManagementService {

private UserRepository userRepository;

@Autowired
public UserManagementServiceImpl(UserRepository userRepository) {this.userRepository = userRepository;}

@Override
public List<User> listAll() {
    List<User> users = new ArrayList<>();
    userRepository.findAll().forEach(users::add);
    return users;
}

@Override
public User save(User user) {
    userRepository.save(user);
    return user;
}

@Override
public User findUser(String username) {
    return userRepository.findUser(username);
}

I then perform a simple write-read test like this:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class DatabaseConnectionTest {

// User 1
private static final String UNAME1 = "Paul";
private static final String EMAIL1 = "[email protected]";
private static final String PASSWORD1 = "p@ss";
private static final String USERNAME1 = "paul";

@Autowired
private UserRepository userRepository;

@Before
public void setUp() throws Exception {

    // Clear database before adding new user
    userRepository.deleteAllUsers();

    // Creating user
    User user = new User(UNAME1, USERNAME1, PASSWORD1, EMAIL1);
    userRepository.save(user);
}

@Test
public void testPersistence() {

    Assert.assertEquals(UNAME1, userRepository.findUser(UNAME1).getName());
    Assert.assertEquals(USERNAME1, userRepository.findUser(UNAME1).getUsername());

}

The result of the above is this error:

2017-08-16 14:59:38.990 INFO 6496 --- [ main] e.b.repository.DatabaseConnectionTest : Started DatabaseConnectionTest in 7.131 seconds (JVM running for 8.331) 2017-08-16 14:59:39.872 INFO 6496 --- [ main] o.n.o.drivers.http.request.HttpRequest : Thread: 1, url: http://localhost:7474/db/data/transaction/39, request: {"statements":[{"statement":"UNWIND {rows} as row CREATE (n:User) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, row.type as type","parameters":{"rows":[{"nodeRef":-1821370276,"type":"node","props":{"password":"p@ss","name":"Paul","email":"[email protected]","username":"paul"}}]},"resultDataContents":["row"],"includeStats":false}]} 2017-08-16 14:59:40.358 ERROR 6496 --- [ main] o.s.d.n.t.Neo4jTransactionManager : Commit exception overridden by rollback exception

org.springframework.dao.InvalidDataAccessApiUsageException: Transaction is not current for this thread; nested exception is org.neo4j.ogm.exception.TransactionManagerException: Transaction is not current for this thread

  • Why does userRepository.save(user) creates the record in the database but throws the exception?
  • What does "Transaction is not current for this thread" excactly mean?
  • Why does userRepository.save(user) ultimately failes but userRepository.deleteAllUsers() works?

edit: My dependencies.gradle includes the following:

compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-neo4j', version: '1.5.6.RELEASE'
//compile group: 'org.springframework.data', name: 'spring-data-neo4j', version: '4.2.6.RELEASE'
compile group: 'org.neo4j', name: 'neo4j-ogm-core', version: '2.1.3'
//compile group: 'org.neo4j', name: 'neo4j-ogm-bolt-driver', version: '2.1.3'
runtime group: 'org.neo4j', name: 'neo4j-ogm-http-driver', version: '2.1.3'
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile('org.springframework.boot:spring-boot-starter-web')
compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: '1.5.4.RELEASE'
testCompile('org.springframework.boot:spring-boot-starter-test')

Solution

  • Your SessionFactory bean should not be declared as static:

    @Bean
    public static SessionFactory sessionFactory() {
        return new SessionFactory("eu.bm.domain");
    }
    

    should be

    @Bean
    public SessionFactory sessionFactory() {
        return new SessionFactory("eu.bm.domain");
    }
    

    Background

    "Transaction is not current for this thread" means that something is trying to commit a transaction which is different than current tx in a thread local context.

    You are using default transaction management - it creates a transaction around repository calls.

    When the SessionFactory bean is defined as static the Session somehow doesn't see the transaction in thread local (the one started by the default tx management) and creates new one, tries to commit it and the exception appears.