При обновлении и сохранении связанной сущности в разных транзакциях сервер перестает работать - PullRequest
0 голосов
/ 02 ноября 2018

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

Я создаю таблицу в PostgreSQL db:

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

Если я запускаю сервер, я получаю исключение: o rg.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). Я не знаю, правильно ли это)

...