事务管理在使用SessionFactory时在neo4j-omg中不起作用。

huangapple go评论88阅读模式
英文:

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();
    }
}

huangapple
  • 本文由 发表于 2020年9月10日 17:23:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/63826673.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定