티스토리 뷰
Oracle DB를 사용할 때 PK를 SEQUENCE 전략을 사용한다면 JPA에서 주의해야 할 점이 있다. 각각의 테이블에 사용할 SEQUENCE를 지정해주지 않으면 모든 테이블이 PK를 공유하는 문제가 발생한다.
시퀀스를 공유하는 테이블
Member 엔티티
PK를 보면 SEQUENCE 전략을 사용하고 있다.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@Column(name = "name")
private String username;
private Integer age;
@Enumerated(EnumType.STRING)
private RoleType roleType;
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
@Lob
private String description;
public Member() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
Team 엔티티
여기 또한 PK를 SEQUENCE 전략으로 사용하고 있다.
@Entity
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "TEAM_Id")
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Entity 저장
이제 EntityManager를 통해 DB에 값을 저장해보자.
Member와 Team은 SEQUENCE 전략을 사용하기 때문에 Id 값을 지정할 필요가 없다.
@SpringBootApplication
public class JpashopApplication {
public static void main(String[] args) {
SpringApplication.run(JpashopApplication.class, args);
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
Member member = new Member();
member.setUsername("hope2");
em.persist(member);
Team team = new Team();
team.setName("teamA");
em.persist(team);
transaction.commit();
}catch(Exception ex) {
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
}
결과
Member와 Team 둘다 SEQUENCE 전략을 사용하기 때문에 DB에서 자동으로 PK 값을 설정한다. 그러나 Team을 조회해 보면 ID값으로 2가 저장되었다. 1이 저장되길 기대했으나 Member의 ID에만 1이 저장되었다.
테이블마다 시퀀스를 생성하기
위에서 Member와 Team 테이블이 Sequence 값을 공유하는 문제가 있었다. Member와 Team 각 테이블마다 전용 Sequence를 만들기 위해서는 다음과 같이 할 수 있다.
@SequenceGenerator(
name = "시퀀스 이름",
sequenceName = "데이터베이스 시퀀스 이름"
)
@GeneratedValue(generator = "시퀀스 이름")
Member 엔티티
@Entity
@SequenceGenerator(
name = "MEMBER_SEQUENCE_GENERATOR",
sequenceName = "MEMBER_SEQUENCE"
)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQUENCE_GENERATOR")
private Long id;
@Column(name = "name")
private String username;
private Integer age;
@Enumerated(EnumType.STRING)
private RoleType roleType;
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
@Lob
private String description;
public Member() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
Team 엔티티
@Entity
@SequenceGenerator(
name = "TEAM_SEQUENCE_GENERATOR",
sequenceName = "TEAM_SEQUENCE"
)
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TEAM_SEQUENCE_GENERATOR")
@Column(name = "TEAM_Id")
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
결과
각 테이블의 PK값이 1부터 시작함을 알 수 있다.
결론
테이블 별로 Sequence 생성기를 만들어주지 않으면 값을 공유하는 문제가 발생한다. 이는 두 테이블이 hibernate에서 제공하는 기본 시퀀스 생성기를 같이 사용하기 때문이다.
따라서 각 엔티티에 @SequenceGenerator 애너테이션을 사용하며 테이블마다 시퀀스 생성기를 만들어주면 위의 문제가 해결된다.