概要
Spring Data JPA において、Cascase.ALL のリレーションがあるエンティティのインサート時に、Many 側からの INSERT が発生したため、外部制約違反が発生してしまう現象が発生しました。
下記の例ですと、order から insert が実行されるので、親である user に insert される order の userNo が存在していません。
そのため、外部キー制約にて例外が発生しました。
User.java
@Entity
@Getter
@NoArgsConstructor
public class User {
@Id
private String userNo;
@OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Order> orders;
}
Order.java
@Entity
@NoArgsConstructor
public class Order {
@Getter
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Getter
private String userNo;
@ManyToOne
@JoinColumn(name = "user_no", updatable = false, insertable = false)
private User user;
}
例外が発生する SQL
insert into order (id, user_no ...) values (1, '001' ...);
原因
JpaRepository にて該当の User オブジェクトを取得しているため、EntityManager の管理下にあるためでした。
EntityManager#detach を実行することで、user から insert が実行され、正常に order を登録できました。
UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
@EntityGraph(value = "user", attributePaths = "orders", type = EntityGraph.EntityGraphType.LOAD)
Optional<User> findOne(String userNo);
}
UserService.java
@Service
@RequiredArgsConstructor
@Slf4j
@Transactional
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final EntityManager em;
@Override
public User getUser(String userNo) {
Optional<User> optional = userRepository.findOne(userNo);
User result = optional.get();
em.detach(result);
return result;
}
}
補足
今回の要件では、登録されている User オブジェクトの洗い替えを実行したいために、登録対象の User オブジェクトを削除するため、
上記のメソッドを実行しています。