Neo4j 的自定义存储库实现不起作用
Custom repository implementation for Neo4j doesn't work
这与 中讨论的内容类似,但我没有两个数据库。我已经从 git 存储库下载了 spring-data neo4j 示例 java 应用程序,并希望执行动态查询而不是通过存储库接口执行静态查询。
我面临空事务管理器的问题。
这是我的界面:
public interface SearchRepositoryCustom {
Iterable<Movie> searchByCriteria();
}
这是我的自定义回购实现:
@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());
}
}
这是我的配置:
@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());
}
}
因为我只有一个 TransactionManager 和一个 SessionFactory(因为我只有一个 Neo4j 实例)所以我不需要单独命名 bean。
我看到以下异常:
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]
即使我真的继续声明 bean 的名称并通过指定 transactionManager 的名称将方法标记为事务性的,我仍然会始终遇到相同的错误。
Java 版本:1.8
neo4j 版本:3.4.6
我错过了什么?
您混淆了 Neo4j-OGM 的 SessionFactory
/Session
和 Spring (Data Neo4j) 的 @Transactional
支持。后者将创建 OGM 代码不知道的新交易并尝试创建新交易。
如果您使用 Spring Data Neo4j,您还可以使用 @Query
注释方法在您的实体存储库中定义查询。
另一种解决方案是删除服务层中的 @Transactional
注释,如果您计划执行多个操作则手动创建它(一个不需要,因为 OGM 将创建一个隐含的事务,如果它不存在)。
是对的。我想添加我们这里的两个选项。我们提供了一个绑定到当前线程并与 Springs 事务集成的可注入 Session
。只需自动连线而不是 SessionFactory
即可使用您的解决方案。请注意,我在所有 Spring 项目中都按照推荐使用了构造函数注入:
@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());
}
}
我已经使用另一个域创建了一个简洁的示例项目,但想法保持不变。
对于像这样的简单用例,我完全同意 Gerrit,并且会在声明性 Spring Data Neo4j 存储库上使用 @Query
注释,如下所示:
interface ThingRepository extends Neo4jRepository<ThingEntity, Long> {
@Query("MATCH (t:ThingEntity) RETURN t LIMIT 10")
public Iterable<ThingEntity> searchByCriteria();
}
用法相同,如下所示:
@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);
}
}
您将找到作为要点的完整应用程序:Use Spring Data Neo4js injectable OGM Session。当我们接近 Java 8 的 EOL 时,我使用了 Java 10 而不是 8,但这并没有改变存储库的实现。除此之外,使用 Spring Boot 2.0.4、Spring Data Kay 和 OGM 3.1.0 进行测试。
编辑:关于评论:可注入会话是一个代理。该字段本身是最终的,但代理会根据需要打开会话,然后委托给它。
这与
我面临空事务管理器的问题。
这是我的界面:
public interface SearchRepositoryCustom {
Iterable<Movie> searchByCriteria();
}
这是我的自定义回购实现:
@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());
}
}
这是我的配置:
@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());
}
}
因为我只有一个 TransactionManager 和一个 SessionFactory(因为我只有一个 Neo4j 实例)所以我不需要单独命名 bean。
我看到以下异常:
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]
即使我真的继续声明 bean 的名称并通过指定 transactionManager 的名称将方法标记为事务性的,我仍然会始终遇到相同的错误。
Java 版本:1.8
neo4j 版本:3.4.6
我错过了什么?
您混淆了 Neo4j-OGM 的 SessionFactory
/Session
和 Spring (Data Neo4j) 的 @Transactional
支持。后者将创建 OGM 代码不知道的新交易并尝试创建新交易。
如果您使用 Spring Data Neo4j,您还可以使用 @Query
注释方法在您的实体存储库中定义查询。
另一种解决方案是删除服务层中的 @Transactional
注释,如果您计划执行多个操作则手动创建它(一个不需要,因为 OGM 将创建一个隐含的事务,如果它不存在)。
Session
。只需自动连线而不是 SessionFactory
即可使用您的解决方案。请注意,我在所有 Spring 项目中都按照推荐使用了构造函数注入:
@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());
}
}
我已经使用另一个域创建了一个简洁的示例项目,但想法保持不变。
对于像这样的简单用例,我完全同意 Gerrit,并且会在声明性 Spring Data Neo4j 存储库上使用 @Query
注释,如下所示:
interface ThingRepository extends Neo4jRepository<ThingEntity, Long> {
@Query("MATCH (t:ThingEntity) RETURN t LIMIT 10")
public Iterable<ThingEntity> searchByCriteria();
}
用法相同,如下所示:
@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);
}
}
您将找到作为要点的完整应用程序:Use Spring Data Neo4js injectable OGM Session。当我们接近 Java 8 的 EOL 时,我使用了 Java 10 而不是 8,但这并没有改变存储库的实现。除此之外,使用 Spring Boot 2.0.4、Spring Data Kay 和 OGM 3.1.0 进行测试。
编辑:关于评论:可注入会话是一个代理。该字段本身是最终的,但代理会根据需要打开会话,然后委托给它。