시리즈1에 이어 두번째 어노테이션 정리 시간이다.
오르미 부트캠프 스프링 프로젝트가 끝났다. 프로젝트같은 프로젝트를 처음 해봐서 그런지 몸 피곤하고 머리도 아프고 그래도 프로젝트 기간동안 많은 점을 배운 것 같다. 😂
특히 잘하는 사람은 엄청 많다는 생각이 들었다. 자만하지 말고 열심히 공부해야한다.
오늘의 공부할 내용은 아래와 같다 :)
저번 포스트보다 어노테이션의 개수가 많다.. 공부하면 할 수록 새로 공부할게 너무 많아지는 것 같다.
이번 포스트의 특징은 모두 Entity 클래스에서 사용하는 어노테이션이라는 점이다 !
아예 동작을 모르는 어노테이션, 이름만 보고도 어떤 기능을 할 지 예상이되는 어노테이션 등등 다양하지만 정확하게 알고 넘어가고 싶어 정리해본다 !
실제로 이번 프로젝트를 하며 엄청 햇갈렸던 개념이다.
이름 그대로 다른테이블과 다대일 관계에 있을때 해당 키를 정해주는 역할로 이해하고 사용했었다.
나는 질문 게시판을 만드는 프로젝트를 했다. 질문과 답변은 1:N 관계에 있다. 한 질문에 대해 여러 답변이 달릴 수 있기 때문이다.
ManyToOne이기때문에, 다
에 해당하는 부분에 해당 어노테이션을 사용한다!
내 프로젝트에서는 한 개의 question_id 에 여러 answer이 올 수 있으니 ManyToOne을 적어준다. 일대다 관계를 나타내는 OneToMany 어노테이션도 존재한다.
일대다, 다대일 관계를 걸때, 연관관계의 주인을 설정할 수 있다. 이는 아래에서 설명할 어노테이션인 @JoinColumn을 사용하기 때문에 아래에서 설명하도록 하겠다.
만약 본인이 일대다, 다대일 관계 매핑을 했다면, 연관관계의 주인에 대해서도 잘 생각해 볼 필요가 있다.
연관관계 주인
연관관계의 주인을 정해주게 되면, 주인이 아닌 경우 읽기만 가능하고 값을 변경할 수 없게 해 불변성을 유지할 수 있기 때문이다. 이를 통해 외래키의 키값을 지킬 수 있다.
방금 위에서 배운 ManyToOne 어노테이션을 보자
이 어노테이션은 아까도 말했지만 일대다에서 다에 해당하는 entity 필드에 적어준다.
내 코드에서는 다 에 해당하는 question은 여러 answer를 가질 수 있기에 다대일을 적어줬다.
보통은 외래키를 가지고 있는 entity가 주인이 되고, 이 외래키는 일대다 관계에서 보통 다 쪽이 가지고 있다.
명시적으로 주인이 누구다!
를 정해줄 수 있는데 이때, 사용하는 어노테이션이 @JoinColumn 어노테이션이다.
반대로 OneToMany 어노테이션은 일대다 중 일에 해당하는 관계에서 적어주고 이때는 mappedBy 속성을 이용해서 주인을 적어준다.
이때는 qustion이 mappedBy 뒤에 오게 될 것이다.
앞에서 말한 어노테이션들의 경우 JPA를 잘 활용하기 위해 사용하는 어노테이션이므로, 굳이 사용해주는게 좋다. but, JPA를 사용하지 않을 경우 사용하는 어노테이션의 구문이 다를 수 있으니 주의 하도록 하자
다음 두 개의 어노테이션은 이름만 봐도 대충 어떤 역할을 할 지 알 수 있다.
내가 이번 프로젝트 중 사용했던 Answer entity의 필드 모습이다.
필드의 이름을 보면 더욱 어떤 역할을 할지 예상되지 않는가 ?
대충 만들어진 시간과 수정된 시간을 가져오는 군 ~
정도로 예상 할 수 있을 듯 하다.
Spring Data JPA에서 사용되는 어노테이션으로, 엔티티 클래스의 생성일을 나타내는 필드에 적용하는 어노테이션이다.
엔티티 생성 시 자동으로 시간 정보가 설정된다. 쉽게 말하면 데이터가 내 데이터 "표" 에 들어온 순간! 시간정보를 설정해준다.
해당 시간관련 정보는 런타임중에 유지되어야 하기 때문에 Retention 어노테이션으로 런타임이 설정되어 있는 것을 확인할 수 있다.
이 어노테이션 또한 Spring Data JPA에서 사용되는 어노테이션으로, 엔티티의 변화와 관련되어 있는 역할을 수행한다.
엔티티가 수정될때마다 해당 시간을 업데이트하고 그 중 마지막 수정시간을 시간 정보로 설정해준다.
위의 생성, 삭제 시 시간을 설정하는 어노테이션을 사용하기 위해서는 @EntityListeners의 적절한 설정이 필요하다.
해당 어노테이션은 JPA에서 엔티티 라이프 사이클 이벤트를 수신하는 리스너를 지정하는데에 사용된다.
쉽게 말해 내가 정보를 담는 데이터 테이블의 생명주기와 관련된 동작이 일어날때 그 동작을 수신하는 리스너를 지정하는 것이다 !
이를 통해 생성, 수정같은 엔티티의 변경 이벤트를 감지하고 활용하는데에 사용된다.
보통 AuditingEntityListner가 앞서말한 수신 리스너의 역할을 하며, 리스너 이름에서 알 수 있듯이 Auditing 기능을 수행한다.
말 그대로 감사의 역할을 한다.
쉽게 말해 잘 듣고 있는 역할을 한다.
(tmi지만, 실제로 Audit은 Audio에서 파생된 말이다.)
클래스 상단에 선언해서 생성, 수정을 읽어오는 역할을 하게 설정한다.
Answer 엔티티의 시작부분이다.
@Id와 @GeneratedValue 태그를 사용해서 테이블의 기본키를 설정한다.
여기서 기본키란, 해당 테이블의 필드 중 각각의 데이터들이 유일하게 갖는 구분될 수 있는 필드 값을 말한다. 불변의 성격을 가지고 있어야하며 null 값을 가질 수 없고 중복될 수 없다. ex) 주민등록번호
이 어노테이션은 기본키와 관련된 어노테이션이다.
기본키를 자동으로 생성할때, @Id와 @GeneratedValue를 같이 사용해 기본키를 생성할 수 있다.
기본키 생성전략
이라고 불리는 부분이며, 여러 속성이 존재한다.
@GeneratedValue(strategy = GenerationType.xxx)
- IDENTITY
- SEQUENCE
- TABLE
xxx 자리에 해당 속성 중 한가지가 들어간다.
내가 이번에 사용한 생성전략이다. DB에 기본키 생성에 대한 책임을 위임하는 방식으로 id 값을 내가 따로 할당하지 않고 1부터 차례로 수가 증가하며(1,2,3,4,...) 기본키를 생성해준다.
@Entity
public class ExampleEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
SequenceGenerator 어노테이션을 함께 사용하는 방법이다.
DB가 기본키를 생성해준다는 특징은 IDENTITY 생성 전략과 동일하고 다른 점으로는 초기값, 시퀀스 이름 등을 지정해줄 수 있다는 특징이 있다.
우리가 흔히 기본키를 생성할때, 다른 테이블과의 관계를 생각해보게되는데 아래와 같은 조건일때 사용하기 적합한 형태이다.
table_one
과table_two
,table_three
가 있다고 가정하자table_one
과table_two
는 id 가 중복되도 되지만table_three
와는 중복되면 안될때table_one
,table_two
를 한 개의 sequence에 넣고 관리하고 (sequenceName으로 구분한다.)table_three
는 다른 sequence에서 관리하면 된다.
@Entity
public class ExampleEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "example_seq")
@SequenceGenerator(name = "example_seq", sequenceName = "example_sequence", allocationSize = 1)
private Long id;
}
sequence와 비슷하지만, DB종속성을 벗어나 기본키 값을 생성하는 형태로 DB간 이식성을 보장한다.
하지만 동시성이 낮기때문에 여러 스레드가 동작하는 환경에서 성능저하를 일으킬 수 있다.
@Entity
public class ExampleEntity {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "example_table_gen")
@TableGenerator(name = "example_table_gen", table = "id_generator", pkColumnName = "gen_name", valueColumnName = "gen_value", allocationSize = 1)
private Long id;
}
프로젝트를 진행하며 엔티티에 관련된 처음보는 어노테이션들이 많아 많이 해매게 되었다.
그럼에도 이번 프로젝트 과정 중 새롭게 배운 점이나 보기만 했던 내용들을 직접 적용해보면서 다음 프로젝트때는 조금 더 개선된 개발을 해보고 싶다는 생각이 든다...😂