spring-data-neo4j icon indicating copy to clipboard operation
spring-data-neo4j copied to clipboard

transactionTemplate is null error when use spring data neo4j with spring data jpa

Open abccbaandy opened this issue 1 year ago • 7 comments

As title, it's work well with spring data neo4j alone, but fail with spring data jpa.

java.lang.NullPointerException: Cannot invoke "org.springframework.transaction.support.TransactionTemplate.execute(org.springframework.transaction.support.TransactionCallback)" because "this.this$0.transactionTemplate" is null
	at org.springframework.data.neo4j.core.Neo4jTemplate$DefaultExecutableQuery.getResults(Neo4jTemplate.java:1262) ~[spring-data-neo4j-7.3.2.jar:7.3.2]
	at org.springframework.data.neo4j.repository.query.Neo4jQueryExecution$DefaultQueryExecution.execute(Neo4jQueryExecution.java:51) ~[spring-data-neo4j-7.3.2.jar:7.3.2]
	at org.springframework.data.neo4j.repository.query.AbstractNeo4jQuery.execute(AbstractNeo4jQuery.java:93) ~[spring-data-neo4j-7.3.2.jar:7.3.2]
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:170) ~[spring-data-commons-3.3.2.jar:3.3.2]
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:158) ~[spring-data-commons-3.3.2.jar:3.3.2]
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:164) ~[spring-data-commons-3.3.2.jar:3.3.2]
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143) ~[spring-data-commons-3.3.2.jar:3.3.2]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.11.jar:6.1.11]
	at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:70) ~[spring-data-commons-3.3.2.jar:3.3.2]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.11.jar:6.1.11]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:379) ~[spring-tx-6.1.11.jar:6.1.11]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.1.11.jar:6.1.11]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.11.jar:6.1.11]
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:138) ~[spring-tx-6.1.11.jar:6.1.11]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.11.jar:6.1.11]
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-6.1.11.jar:6.1.11]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.11.jar:6.1.11]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223) ~[spring-aop-6.1.11.jar:6.1.11]
	at jdk.proxy4/jdk.proxy4.$Proxy136.queryCategory(Unknown Source) ~[na:na]

abccbaandy avatar Jul 31 '24 09:07 abccbaandy

Got same error on Spring boot 3.3.1 and 3.3.2

I think this maybe cause of this https://github.com/spring-projects/spring-boot/blob/8ea6d3c92ead011af1e8980272b2e1d8502bab4e/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java#L113 Neo4jTransactionManager transactionManager not created.

I got some error related to https://github.com/spring-projects/spring-boot/issues/40895 and I think it's same problem like this https://github.com/spring-projects/spring-boot/issues/41275

My workaround is define a beam Neo4jTransactionManager transactionManager copy from Neo4jDataAutoConfiguration

yangyaofei avatar Aug 01 '24 07:08 yangyaofei

Update, I found that declare a transactionManager will lead jpa transaction error when call saveAndFlush.

spring-data-jpa use transactionManager in JpaBaseConfiguration, and neo4j`s transactionManager will disable it.

Now I make a neo4jTemplate and new a transactionManager in it, it finally solve this.

    @Bean({"neo4jTemplate"})
    @ConditionalOnMissingBean({Neo4jOperations.class})
    public Neo4jTemplate neo4jTemplate(
            Neo4jClient neo4jClient,
            Neo4jMappingContext neo4jMappingContext,
            Driver driver, DatabaseSelectionProvider databaseNameProvider, ObjectProvider<TransactionManagerCustomizers> optionalCustomizers
    ) {
        Neo4jTransactionManager transactionManager = new Neo4jTransactionManager(driver, databaseNameProvider);
        optionalCustomizers.ifAvailable((customizer) -> {
            customizer.customize(transactionManager);
        });
        return new Neo4jTemplate(neo4jClient, neo4jMappingContext, transactionManager);
    }

yangyaofei avatar Aug 01 '24 10:08 yangyaofei

Having two spring data modules in place requires two transaction managers. Your solution is the way to go. As you have already found out, there was some trouble around the transaction managers after we required it also for the Neo4jTemplate to be in place. Sorry for the (needed) inconvenience. If there is still something causing problems, I am happy to help. Otherwise feel free to close this issue.

meistermeier avatar Aug 02 '24 05:08 meistermeier

Having two spring data modules in place requires two transaction managers. Your solution is the way to go. As you have already found out, there was some trouble around the transaction managers after we required it also for the Neo4jTemplate to be in place. Sorry for the (needed) inconvenience. If there is still something causing problems, I am happy to help. Otherwise feel free to close this issue.

I think spring data neo4j should fix this issue. To do the autoconfig is the main feature of starter, isn't it? Since I am not "customize" the transaction config, why do I need add those code?

I want to use jpa and neo4j, then I add neo4j starter and jpa starter. Then everything should just work.

abccbaandy avatar Aug 02 '24 05:08 abccbaandy

hi @meistermeier please see the https://github.com/spring-projects/spring-boot/issues/40953, I think this should be fixed in spring boot project.

And I don't know this project should give the autoconfig thing to spring-boot or spring-boot project do it separate. Whether or not, we can make this better.

yangyaofei avatar Aug 02 '24 05:08 yangyaofei

I think spring data neo4j should fix this issue. To do the autoconfig is the main feature of starter, isn't it? Since I am not "customize" the transaction config, why do I need add those code?

I understand your ideas and I would be happy to find a solution with the Spring Boot team to make it work. But at the moment, the Neo4jDataAutoConfiguration will only create a *Neo4j*TransactionManager if there is no other TransactionManager already instantiated: https://github.com/spring-projects/spring-boot/blob/9e3e067a4cbd93bdaf1ed201214e1cbd9b65080e/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java#L112

This means if the JPA tx manager was created first, there won't be a second one unless it gets defined manually.

Let's keep this open to have a reminder for me to get this sorted out in a clean way. There are some other things around this topic that needs some more testing before I can purpose a solution for this situation.

meistermeier avatar Aug 02 '24 06:08 meistermeier

I think spring data neo4j should fix this issue. To do the autoconfig is the main feature of starter, isn't it? Since I am not "customize" the transaction config, why do I need add those code?

I understand your ideas and I would be happy to find a solution with the Spring Boot team to make it work. But at the moment, the Neo4jDataAutoConfiguration will only create a *Neo4j*TransactionManager if there is no other TransactionManager already instantiated: https://github.com/spring-projects/spring-boot/blob/9e3e067a4cbd93bdaf1ed201214e1cbd9b65080e/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java#L112

This means if the JPA tx manager was created first, there won't be a second one unless it gets defined manually.

Let's keep this open to have a reminder for me to get this sorted out in a clean way. There are some other things around this topic that needs some more testing before I can purpose a solution for this situation.

Thanks for provide the root cause.

Any reason to check TransactionManager?

	@ConditionalOnMissingBean(TransactionManager.class)

Why not just check Neo4jTransactionManager? I think it will be fine with jpa.

	@ConditionalOnMissingBean(Neo4jTransactionManager.class)

abccbaandy avatar Aug 02 '24 06:08 abccbaandy

Adding one more solution here. In this case all neo4j repositories under the provided "basepackages" will use the transaction manager bean with name neo4jTransactionManager defined in the class.

This solution will allow future addition of other datasources including neo4j.

@Configuration
@EnableNeo4jRepositories(basePackages = {"com.yourpath.repository.neo4j"}, transactionManagerRef = "neo4jTransactionManager")
public class Neo4jConfig extends Neo4jDataAutoConfiguration {

    @Bean("neo4jTransactionManager")
    public Neo4jTransactionManager transactionManager(Driver driver, DatabaseSelectionProvider databaseNameProvider,
                                                      ObjectProvider<TransactionManagerCustomizers> optionalCustomizers) {
        Neo4jTransactionManager transactionManager = new Neo4jTransactionManager(driver, databaseNameProvider);
        optionalCustomizers.ifAvailable((customizer) -> customizer.customize((TransactionManager) transactionManager));
        return transactionManager;
    }
}

For good or for bad most libraries try to define their transaction manager as the default instead of creating a specific one. I personally think that is fine but some specific DEBUG log statements are needed if a required bean could not be created.

sergiopnvds avatar Nov 26 '24 22:11 sergiopnvds

Why need multiple transactionManagers? use like code below?

@Transactional(transactionManager = "neo4jTransactionManager", rollbackFor = Exception.class) @Transactional(transactionManager = "jpaTransactionManager", rollbackFor = Exception.class) public void method1() { //insert into neo4j ... //update mysql ... }

SidneyLann avatar May 21 '25 11:05 SidneyLann

This is not SDN problem. Distributed transactions might be solved like described here, but we are not going to dig into this from our side. SDN itself can of course be run with multiple Neo4jTransactionManagers with multiple different Spring Data contexts as shown here: https://github.com/spring-projects/spring-data-neo4j/blob/main/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/MultipleContextsIT.java

michael-simons avatar May 23 '25 06:05 michael-simons