英文:
Transaction management not working in neo4j-omg when using SessionFactory
问题
我编写了一个小的Spring Boot应用程序,使用spring-boot-starter-data-neo4j
连接到Neo4j实例。
我想通过Cypher查询执行一些更新操作,但是在使用SessionFactory
打开Session
时无法使事务管理起作用。使用带有OGM注释的实体通过Neo4jRepository
进行事务管理工作正常。
为了测试,我编写了两个简单的带有@Transactional
注释的服务:
使用带有OGM注释的类和Neo4jRepository
:
@Service
@Transactional
public class OgmServiceImpl implements OgmService {
private final PersonRepository personRepository;
public OgmServiceImpl(PersonRepository personRepository) {
this.personRepository = personRepository;
}
@Override
public void storeData() {
personRepository.save(new Person("Jack"));
if (true) {
throw new IllegalStateException();
}
personRepository.save(new Person("Jill"));
}
}
执行storeData()
方法时,既不会将Jack保存到Neo4j,也不会将Jill保存。按预期工作。
另一个服务通过在Neo4j Session
上执行Cypher查询执行相同的操作:
@Service
@Transactional
public class CypherServiceImpl implements CypherService {
private final SessionFactory sessionFactory;
public CypherServiceImpl(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public void storeData() {
Session session = sessionFactory.openSession();
session.query("CREATE (p:Person { name: 'Jack' })", Map.of());
if (true) {
throw new IllegalStateException();
}
session.query("CREATE (p:Person { name: 'Jill'})", Map.of());
}
}
然而,事务管理失败,因为Jack被存储在Neo4j中。
对我来说,这种行为相当令人意外。我可以通过显式地开始事务来使事务管理起作用,但这不是我想要的方式:
public void storeData() {
Session session = sessionFactory.openSession();
try (Transaction tx = session.beginTransaction()) {
session.query("CREATE (p:Person { name: 'Jack' })", Map.of());
if (true) {
throw new IllegalStateException();
}
session.query("CREATE (p:Person { name: 'Jill'})", Map.of());
tx.commit();
}
}
我需要自己配置SessionFactory
才能使其正常工作吗?为什么Spring Boot没有处理这个呢?
英文:
I wrote a small Spring Boot application that uses spring-boot-starter-data-neo4j
to connect to a Neo4j instance.
I'd like to execute some updates through Cypher queries, but can't get transaction management to work when opening a Session
using the SessionFactory
. Transaction management works fine using OGM annotated entities through a Neo4jRepository
.
For testing purposes, I wrote two simple services annotated with @Transactional
:
Using OGM annotated class and a Neo4jRepository
:
@Service
@Transactional
public class OgmServiceImpl implements OgmService {
private final PersonRepository personRepository;
public OgmServiceImpl(PersonRepository personRepository) {
this.personRepository = personRepository;
}
@Override
public void storeData() {
personRepository.save(new Person("Jack"));
if (true) {
throw new IllegalStateException();
}
personRepository.save(new Person("Jill"));
}
}
When executing the storeData() method, neither Jack nor Jill is saved to Neo4j. Works as expected.
The other service does the same by executing Cypher queries on the Neo4j Session
:
@Service
@Transactional
public class CypherServiceImpl implements CypherService {
private final SessionFactory sessionFactory;
public CypherServiceImpl(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public void storeData() {
Session session = sessionFactory.openSession();
session.query("CREATE (p:Person { name: 'Jack' })", Map.of());
if (true) {
throw new IllegalStateException();
}
session.query("CREATE (p:Person { name: 'Jill'})", Map.of());
}
}
However, transaction management fails, as Jack is stored in Neo4j.
To me, this behavior is quite unexpected. I can get transaction management to work by explicitly starting a transaction, but that is not the way I like it to be:
public void storeData() {
Session session = sessionFactory.openSession();
try (Transaction tx = session.beginTransaction()) {
session.query("CREATE (p:Person { name: 'Jack' })", Map.of());
if (true) {
throw new IllegalStateException();
}
session.query("CREATE (p:Person { name: 'Jill'})", Map.of());
tx.commit();
}
}
Do I need to configure the SessionFactory
myself to get this to work? Why doesn't Spring Boot take care of this?
答案1
得分: 1
只看了一下文档。根据文档,在编程时调用 SessionFactory#openSession
会创建一个全新的会话,该会话不一定位于使用 @Transactional
注解指定的事务范围内。
为了进一步确保这一点,您可以尝试使用以下代码检查断言错误:
@Override
public void storeData() {
Session session = sessionFactory.openSession();
assert session.getTransaction() != null;
session.query("CREATE (p:Person { name: 'Jack' })", Map.of());
session.query("CREATE (p:Person { name: 'Jill'})", Map.of());
}
这应该会导致断言错误。与提供获取当前会话方式的 Hibernate 不同,我在 neo4j-ogm 中找不到类似的方法。
下面的代码之所以有效,是因为尽管您创建了一个全新的会话,但事务是由您管理的。在这里,您创建了一个自管理的事务,而不是 Spring 容器管理的事务,尽管这通常不是一个好主意。
public void storeData() {
Session session = sessionFactory.openSession();
try (Transaction tx = session.beginTransaction()) {
session.query("CREATE (p:Person { name: 'Jack' })", Map.of());
if (true) {
throw new IllegalStateException();
}
session.query("CREATE (p:Person { name: 'Jill'})", Map.of());
tx.commit();
}
}
英文:
Just looked at the docs. According to the docs when you programatically call SessionFactory#openSession
a completely new session is created which isn't necessarily in the transaction scope specified with the @Transactional
annotation.
To further ensure this you can try checking for an assertion error with this code:
@Override
public void storeData() {
Session session = sessionFactory.openSession();
assert session.getTransaction() != null;
session.query("CREATE (p:Person { name: 'Jack' })", Map.of());
session.query("CREATE (p:Person { name: 'Jill'})", Map.of());
}
This should result in an assertion error. Unlike hibernate which provides a way of getting the current session I couldn't find neo4J-ogm way to do it though.
The code written below works because even though you are creating a completely new Session is created the transaction is managed by you. Here you are creating a self managed transaction, not the Spring container managed transaction, although it is generally not a good idea.
public void storeData() {
Session session = sessionFactory.openSession();
try (Transaction tx = session.beginTransaction()) {
session.query("CREATE (p:Person { name: 'Jack' })", Map.of());
if (true) {
throw new IllegalStateException();
}
session.query("CREATE (p:Person { name: 'Jill'})", Map.of());
tx.commit();
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论