spring-projects / spring-data-neo4j

Provide support to increase developer productivity in Java when using Neo4j. Uses familiar Spring concepts such as a template classes for core API usage and lightweight repository style data access.
http://spring.io/projects/spring-data-neo4j
Apache License 2.0
829 stars 620 forks source link

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

Open abccbaandy opened 2 months ago

abccbaandy commented 2 months ago

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]
yangyaofei commented 2 months ago

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 commented 2 months ago

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);
    }
meistermeier commented 2 months ago

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.

abccbaandy commented 2 months ago

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.

yangyaofei commented 2 months ago

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.

meistermeier commented 2 months ago

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.

abccbaandy commented 2 months ago

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)