首页 > 解决方案 > 使用 SessionFactory 时事务管理在 neo4j-omg 中不起作用

问题描述

我编写了一个小型 Spring Boot 应用程序,用于spring-boot-starter-data-neo4j连接到 Neo4j 实例。

我想通过 Cypher 查询执行一些更新,但是在Session使用SessionFactory. 事务管理通过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 和 Jill 都不会保存到 Neo4j。按预期工作。

另一个服务通过在 Neo4j 上执行 Cypher 查询来做同样的事情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());
    }
}

但是,事务管理失败,因为 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 不处理这个问题?

标签: javaspring-bootspring-transactionsspring-data-neo4j

解决方案


刚看了文档。根据文档,当您以编程方式调用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 方法来做到这一点。

下面编写的代码有效,因为即使您正在创建一个全新的 Session,事务也是由您管理的。在这里,您正在创建一个自我管理的事务,而不是 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();
    }
}

推荐阅读