При обновлении и сохранении связанной сущности в разных транзакциях сервер перестает работать

У меня длинный вопрос, так как вначале я написал полный список того, что я тестировал, поэтому, пожалуйста, либо закончите чтение, либо перейдите к самому вопросу.

Я создаю таблицу в базе данных PostgreSQL:

create table if not exists users
(
    id bigserial not null
        constraint users_pkey
            primary key,
    name varchar(50),
    is_active boolean not null,
    old_user bigint
        constraint users_users_id_fk
            references users
);

alter table users owner to postgres;

create unique index if not exists users_id_uindex on users (id);

create unique index if not exists users_is_active_idx
    on users (is_active) where (is_active = true);

Может быть только один активный пользователь.

Я создаю приложение для загрузки Spring. Ниже описание конструкции (очень простое) и под ним сам вопрос.

1) Сущность:

@Data
@NoArgsConstructor
@Entity
@Table(name = "users")
public class User {

    public User(String name, Boolean isActive, User oldUser) {
        this.name = name;
        this.isActive = isActive;
        this.oldUser = oldUser;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @Column(name = "is_active")
    private Boolean isActive;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "old_user")
    private User oldUser;
}

2) Репозиторий:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

3) Сервис:

@Service
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public User getOne(Long id) {
        return userRepository.getOne(id);
    }

    @Override
    public User save(User user) {
        return userRepository.save(user);
    }

    @Override
    public User saveAndFlush(User user) {
        return userRepository.saveAndFlush(user);
    }
}

Вставляю одного активного пользователя: INSERT INTO "public"."users" ("id", "name", "is_active", "old_user") VALUES (DEFAULT, 'FirstUser', true, NULL)

И теперь я хочу вставить второго пользователя (активного), но из приложения:

@Slf4j
@Component
public class TestBean implements CommandLineRunner {

    private final UserService userService;

    public TestBean(UserService userService) {
        this.userService = userService;
    }

    @Override
    @Transactional
    public void run(String... args) throws Exception {
        log.info("Start...");

        User first = userService.getOne(1L);
        User secondUser = new User("SecondUser", true, first);
        userService.saveAndFlush(secondUser);
    }
}

Если я запускаю сервер, я получаю исключение: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "users_is_active_idx"

Чтобы исправить это, я должен сначала деактивировать старого пользователя, а затем активировать нового. Но это просто не сработает, поэтому, прежде чем я активирую нового пользователя, я должен сбросить его в контекст:

    User first = userService.getOne(1L);
    first.setIsActive(false);
    userService.saveAndFlush(first);
    User secondUser = new User("SecondUser", true, first);
    userService.saveAndFlush(secondUser);

И это решение отлично работает. Но теперь я добавляю в сервис новый метод:

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public User saveInRequiredNew(User user) {
    return userRepository.saveAndFlush(user);
}

И попробуйте спасти пользователя этим методом:

    User first = userService.getOne(1L);
    first.setIsActive(false);
    userService.saveAndFlush(first);
    User secondUser = new User("SecondUser", true, first);
    User user = userService.saveInRequiredNew(secondUser);
    log.info(String.valueOf(user.getId()));

В этом случае, когда я вызываю метод saveInRequiredNew, мое приложение не генерирует исключения и не дает результата. он просто перестает работать. В реальном приложении моя логика построена так, что я должен сохранить объект в транзакции с помощью Propagation.REQUIRES_NEW.

Мой вопрос - почему это происходит и какое будет решение? Можно обновить обе сущности (старых и новых пользователей за одну транзакцию (Propagation.REQUIRES_NEW). Я не знаю, правильно ли это)

Что вы имеете в виду под словом «перестает работать»?

Simon Martinelli 02.11.2018 10:54

@Simon Martinelli просто перестает принимать запросы, не отображает ошибок, не отображает ответы, не выходит из debbag. как будто в рабочем состоянии но ни на что не реагирует

ip696 02.11.2018 11:25

И вы уверены, что у вас нет активной точки останова? Что, если запустить без отладки?

Simon Martinelli 02.11.2018 12:40

да, если я нажму F9 и у меня будет точка останова на следующей строке, я не войду в эту строку

ip696 02.11.2018 15:49

а поток все еще работает?

Simon Martinelli 02.11.2018 16:52

похоже, что основной поток работает

ip696 02.11.2018 20:44
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
118
0

Другие вопросы по теме