Мой вопрос длинный, так как вначале я написал полный список того, что я тестировал, поэтому, пожалуйста, либо закончите чтение, либо перейдите к концу самого вопроса.
Я создаю таблицу в 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
). Я не знаю, правильно ли это)