@Access(AccessType.FIELD), @Access(AccessType.PROPERTY) 란?
JPA가 엔티티 데이터에 접근하는 방식을 지정할 수 있는 어노테이션
@Access(AccessType.FIELD) - 필드 접근
@Access(AccessType.PROPERTY) - 프로퍼티 접근
먼저 필드 접근 방식부터 알아보자
@Access(AccessType.FIELD) - 필드 접근 방식
... 생략
@Entity
@Access(AccessType.FIELD)// 생략해도 동일함
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Transient
private String firstName;
@Transient
private String lastName;
private String fullName;
... 생략
}
필드 접근 방식 정리
- @Access를 설정하지 않으면 @Id의 위치를 기준으로 접근 방식이 설정된다.
- @Id가 필드에 있으므로 @Access(AccessType.FIELD) 로 설정한 것과 동일해서 생략해도 된다.
참고로 위 예제의 @Transient 에 대해 설명하면
해당 필드는 데이터베이스에 저장하지 않고 조회하지도 않는다.
임시로 어떤 값을 보관하고 싶을때 사용한다. (테이블 DDL에도 포함안됨)
필드 접근 방식에 대해서는 딱히 헷갈릴 부분도 어려운 부분도 없는 것 같으니 바로 프로퍼티 접근 방법으로 넘어가보자
@Access(AccessType.PROPERTY) - 프로퍼티 접근 방식
@Entity
@Access(AccessType.PROPERTY) // 생략해도됨
public class Member {
private Long id;
@Id // 프로퍼티 메서드에 선언
public Long getId() {
return id;
}
private String firstName;
private String lastName;
public void setId(Long id) {
this.id = id;
}
@Transient
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@Transient
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
프로퍼티 접근 방식 정리
- 필드 접근과 동일하게 @Id가 프로퍼티(getter)에 있으므로 @Access(AccessType.PROPERTY) 로 설정한 것과 같아 생략가능
- 직접 테스트 해본 결과 프로퍼티 접근 방식을 사용하려면 getter / setter를 구현해야 하고 매핑 어노테이션인 @Transient 도 getter 메서드에 선언하지 않으면 제대로 동작하지 않았다.
- 위의 필드 접근 방식과 비교하면 가독성이 많이 떨어지고 getter / setter 가 비즈니스 로직에서 호출되지 말아야 하는 엔티티라면 문제가 생길 수도 있을 것 같고 딱 봐도 별로인것 같다.
그렇다면 @Access(AccessType.PROPERTY) 는 왜 있는거고 어떤식으로 활용해 볼 수 있을까??
@Access(AccessType.FIELD) & @Access(AccessType.PROPERTY) - 함께 사용
아래와 같이 필드 접근 방식과 프로퍼티 접근 방식을 함께 사용할 수도 있다.
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Transient
private String firstName;
@Transient
private String lastName;
@Setter
private String fullName;
@Access(AccessType.PROPERTY)
public String getFullName() {
return firstName + " " + lastName;
}
public void changeFirstName(String firstName) {
this.firstName = firstName;
}
public void changeLastName(String lastName) {
this.lastName = lastName;
}
}
엔티티를 보면 @Id가 필드에 선언되어 있으므로 JPA가 기본 접근 방식으로 필드 접근을 사용할 것이다. 그리고 특정 필드만 프로퍼티 접근 방식을 사용하도록 선언한다. setter 또한 프로퍼티 접근 방식을 사용하는 필드만 열어둔다.
과연 회원 엔티티의 firstName과 lastName의 값만 저장하면 fullName 컬럼에 getFullName(); 을 호출한 결과가 저장될까??
그리고 firstName과 lastName은 setter를 사용하는 대신 메서드에 이름을 부여해서 변경 기능을 만들어줬다.
잘 동작하는지 테스트를 해보자
@DisplayName("AccessType.PROPERTY테스트")
@Rollback(value = false)
@Test
void 엑세스타입프로퍼티() {
// given
Member member = new Member();
member.changeFirstName("hello");
member.changeLastName("world");
// when
em.persist(member);
Member newMember = em.find(Member.class, member.getId());
// then
System.out.println("회원 아이디 = " + newMember.getId());
System.out.println("회원 이름 = " + newMember.getFullName());
}
실행결과
create table member (
id bigint not null auto_increment,
full_name varchar(255),
primary key (id)
)
insert
into
member
(full_name)
values
(?)
회원 아이디 = 1
회원 이름 = hello world
실행결과를 확인해보면 @Transient(데이터베이스 제외), 필드, 프로퍼티 접근 모두 잘 동작하는것을 확인할 수 있었다.
참고로 설명드리면 @Rollback(value = false)은 DB에 저장된 결과를 확인하고 싶어서 롤백 하지 않는다고 선언한 것, 스프링부트 테스트는 기본적으로 메서드가 실행된 후 결과를 롤백한다.
데이터베이스 결과도 확인할 수 있다.
참고 - 자바 ORM 표준 JPA 프로그래밍 (김영한)