Custom repository implementation for Neo4j doesn't work

邮差的信 提交于 2020-01-03 05:52:06

问题


This is similar to what is discussed at Unable to use two Neo4j Instances with Spring boot/Spring data neo4j but I don't have two databases. I have downloaded the spring-data neo4j sample java application from the git repo and want to execute a dynamic query instead of executing a static query via repository interface.

I am facing an issue of null transaction manager.

Here's my interface :

public interface SearchRepositoryCustom {

    Iterable<Movie> searchByCriteria();
}

Here's my custom repo impl:

@Repository
@Transactional
public class SearchRepositoryImpl implements SearchRepositoryCustom {

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public Iterable<Movie> searchByCriteria() {

        String query = "MATCH (m:Movie)<-[r:ACTED_IN]-(a:Person) RETURN m,r,a LIMIT 10";
        return sessionFactory.openSession().query(Movie.class, query, Collections.emptyMap());
    }

}

Here's my configuration :

@Configuration
@EnableTransactionManagement
@EnableNeo4jRepositories(basePackages = "movies.spring.data.neo4j.repositories")
public class Neo4jPersistenceConfig {

    @Bean
    @ConfigurationProperties("spring.data.neo4j")
    public Neo4jProperties neo4jProperties() {
        return new Neo4jProperties();
    }

    @Bean
    public org.neo4j.ogm.config.Configuration userConfiguration() {
        return neo4jProperties().createConfiguration();
    }

    @Bean
    public SessionFactory getSessionFactory() {
        return new SessionFactory(userConfiguration(), "movies.spring.data.neo4j.domain");
    }

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

Since I have only one TransactionManager and One SessionFactory (as I have only one Neo4j instance) I don't need to name the beans separately.

I am seeing the following exception :

org.neo4j.ogm.exception.core.TransactionManagerException: Transaction is not current for this thread
    at org.neo4j.ogm.session.transaction.DefaultTransactionManager.rollback(DefaultTransactionManager.java:86) ~[neo4j-ogm-core-3.1.0.jar:3.1.0]
    at org.neo4j.ogm.transaction.AbstractTransaction.rollback(AbstractTransaction.java:65) ~[neo4j-ogm-api-3.1.0.jar:3.1.0]
    at org.neo4j.ogm.drivers.bolt.transaction.BoltTransaction.rollback(BoltTransaction.java:61) ~[neo4j-ogm-bolt-driver-3.1.0.jar:3.1.0]
    at org.neo4j.ogm.transaction.AbstractTransaction.close(AbstractTransaction.java:144) ~[neo4j-ogm-api-3.1.0.jar:3.1.0]
    at org.springframework.data.neo4j.transaction.Neo4jTransactionManager.doCleanupAfterCompletion(Neo4jTransactionManager.java:379) ~[spring-data-neo4j-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager.java:1007) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:793) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:714) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:532) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:304) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689) ~[spring-aop-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at movies.spring.data.neo4j.repositories.SearchRepositoryImpl$$EnhancerBySpringCGLIB$$d2631bcd.searchByCriteria(<generated>) ~[classes/:na]
    at movies.spring.data.neo4j.controller.MovieController.advGlobal(MovieController.java:54) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_171]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_171]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_171]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_171]

Even if I actually go ahead and declare the name of the beans and mark the method transactional by specifying the name of the transactionManager, I still get the same error consistently.

Java version : 1.8

neo4j version : 3.4.6

What am I missing?


回答1:


Gerrit is right. I'd like to add the two options we have here. We provide an injectable Session that is bound to the current thread and is integrated with Springs transactions. Just auto wire that instead of the SessionFactory and you're good to go with your solution. Please note that I'm using constructor injection as recommended throughout all Spring projects:

@Repository
@Transactional
class SearchRepositoryImpl implements SearchRepositoryCustom {
    private final Session session;

    public SearchRepositoryImpl(Session session) {
        this.session = session;
    }

    @Override
    public Iterable<ThingEntity> searchByCriteria() {
        String query = "MATCH (t:ThingEntity)  RETURN t LIMIT 10";
        return session.query(ThingEntity.class, query, Map.of());
    }
}

I have used another domain to create a concise example project, but the idea stays the same.

For a simple use case like that I fully agree with Gerrit and would use the @Query annotation on a declarative Spring Data Neo4j repository like this:

interface ThingRepository extends Neo4jRepository<ThingEntity, Long> {
    @Query("MATCH (t:ThingEntity)  RETURN t LIMIT 10")
    public Iterable<ThingEntity> searchByCriteria();
}

The usage is the same, as demonstrated here:

@Component
class ExampleUsage implements CommandLineRunner {
    private final ThingRepository thingRepository;

    private final SearchRepositoryCustom searchRepositoryCustom;

    public ExampleUsage(ThingRepository thingRepository,  SearchRepositoryCustom searchRepositoryCustom) {
        this.thingRepository = thingRepository;
        this.searchRepositoryCustom = searchRepositoryCustom;
    }

    @Override
    public void run(String... args) {
        this.thingRepository.save(new ThingEntity(1));
        this.thingRepository.save(new ThingEntity(2));

        var things = this.searchRepositoryCustom.searchByCriteria();
        things.forEach(System.out::println);

        things = this.thingRepository.searchByCriteria();
        things.forEach(System.out::println);
    }
}

You'll find the complete application as a gist: Use Spring Data Neo4js injectable OGM Session. I have used Java 10 instead of 8 as we approaching EOL for Java 8, but that doesn't change the repository implementations. Apart from that, tested with Spring Boot 2.0.4, Spring Data Kay and OGM 3.1.0.

Edit: In regard to the comment: The injectable session is a proxy. The field itself is final, but the proxy opens sessions as needed and then delegates to it.




回答2:


You are mixing up Neo4j-OGM‘s SessionFactory/Session and the @Transactional support of Spring (Data Neo4j). The latter will create a new transaction of which the OGM code is not aware and tries to create a fresh transaction.

If you use Spring Data Neo4j you can also define the query within your entity repository with a @Query annotated method.

The other solution would be to remove the @Transactional annotation in your service layer and create it manually if you plan to execute multiple operations (Not needed for one because OGM will create a transaction implicit if it does not exist).



来源:https://stackoverflow.com/questions/52051625/custom-repository-implementation-for-neo4j-doesnt-work

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!