Управление транзакциями не работает в neo4j-omg при использовании SessionFactory

Я написал небольшое приложение Spring Boot, которое использует spring-boot-starter-data-neo4j для подключения к экземпляру Neo4j.

Я хотел бы выполнить некоторые обновления с помощью запросов Cypher, но не могу заставить работать управление транзакциями при открытии файла Session с использованием файла SessionFactory. Управление транзакциями прекрасно работает с использованием аннотированных объектов 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() ни Джек, ни Джилл не сохраняются в Neo4j. Работает как положено.

Другой сервис делает то же самое, выполняя запросы Cypher на 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());
    }
}

Однако управление транзакциями не удается, так как Джек хранится в 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 не позаботится об этом?


person verhage    schedule 10.09.2020    source источник
comment
Вы пытались изменить sessionFactory.openSession() на sessionFactory.getCurrentSession() ?   -  person Shababb Karim    schedule 10.09.2020
comment
На самом деле Neo4j-ogm SessionFactory не предлагает такого метода. Хотя спасибо за предложение :)   -  person verhage    schedule 10.09.2020


Ответы (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();
    }
}
person Shababb Karim    schedule 10.09.2020
comment
Я также не смог найти способ получить текущий сеанс. Просто мне есть с чем смириться :) - person verhage; 14.09.2020