Spring data @Transaction не выполняется по порядку

Мой сервисный код выглядит следующим образом:

import javax.transaction.Transactional;

@Service
public class UserServiceImpl implements UserService {
    @Transactional
    @Override
    public void changeAuthorities(Long id, ChangeUserAuthoritiesRequest model) throws RecordNotFoundException {
        Optional<User> userOptional = userRepository.findById(id);
        if (userOptional.isPresent()) {
            User user = userOptional.get();
            long result = userAuthoritiesRepository.removeByUser(user);
//            System.out.println(userAuthoritiesRepository.findByUser(user));
            model.getAuthorityIds().stream().forEach(authorityId -> {
                UserAuthority userAuthority = new UserAuthority();
                Authority authority = authorityRepository.findById(authorityId).get();
                userAuthority.setUser(user);
                userAuthority.setAuthority(authority);
                userAuthoritiesRepository.save(userAuthority);
            });
        } else {
            throw new RecordNotFoundException("User not found with id: " + id);
        }
    }
}

Код означает «удалить все записи с заданным идентификатором, а затем снова добавить новые» (новые могут быть такими же, как и старые). Моя проблема в том, что метод userAuthoritiesRepository.removeByUser(user) не выполняется до того, как новые записи будут сохранены userAuthoritiesRepository.save(userAuthority). Итак, приложение вызывает исключение:

Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Violation of UNIQUE KEY constraint 'user_authority_unique'. Cannot insert duplicate key in object 'dbo.user_authorities'. The duplicate key value is (1, 1).
    at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:258)
    at com.microsoft.sqlserver.jdbc.SQLServerStatement.getNextResult(SQLServerStatement.java:1535)
    at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.doExecutePreparedStatement(SQLServerPreparedStatement.java:467)
    at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement$PrepStmtExecCmd.doExecute(SQLServerPreparedStatement.java:409)
    at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:7151)
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:2478)
    at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeCommand(SQLServerStatement.java:219)
    at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeStatement(SQLServerStatement.java:199)
    at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.executeUpdate(SQLServerPreparedStatement.java:356)
    at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)
    at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:175)
    ... 177 more

person user123    schedule 03.07.2019    source источник
comment
Привет, вам нужно уточнить смысл и грамматику в этом предложении: Проблема в том, что ... не выполняется до того, как новые записи, сохраненные ..., вызвали исключение:   -  person Tristan    schedule 03.07.2019
comment
docs.spring.io/spring-data/jpa/docs/current/api/org/   -  person JB Nizet    schedule 03.07.2019
comment
@JB Nizet, поэтому каждый раз, когда я вызываю метод JPARepository, мне нужно вызывать flush() ?. Это работает, но как я неправильно понимаю весеннюю транзакцию. В некоторых других проектах я также вызываю так много методов в транзакции и не испытываю проблем. Пожалуйста, разъясните мне, спасибо!   -  person user123    schedule 03.07.2019
comment
Зачем вам каждый раз вызывать флеш? Вам нужно вызвать flush(), когда вы хотите, чтобы некоторые операторы выполнялись раньше других, и JPA не может понять это сам. Это довольно редко. Далеко не каждый раз. Это не имеет ничего общего с транзакциями.   -  person JB Nizet    schedule 03.07.2019
comment
Возможный дубликат Hibernate JPA: @OneToMany удалить старый, вставить новый без смыть   -  person Jens Schauder    schedule 04.07.2019


Ответы (1)


Нет ничего плохого в аннотации @Transaction или порядке выполнения вашего кода. Просто изменения не передаются в базу данных. Итак, вы получаете эту ошибку.

Вам нужно будет вызвать метод flush() после вызова метода removeByUser(), чтобы внести изменения в базу данных.

Чтобы понять, почему необходим

person vatsal gosar    schedule 05.07.2019