포스트

JPA 와 엔티티 데이터

JPA 와 엔티티 데이터

명시되지 않은 @Access

JPA 가 엔티티 데이터에 접근하는 방식을 지정하기 위해서는 @Access 를 사용할 수 있습니다. Access 어노테이션에는 두 가지 옵션이 있는데, 필드 접근을 위한 FIELD 와 프로퍼티 접근을 위한 PROPERTY 가 있습니다.
@Access 를 명시적으로 사용하지 않는 경우엔, ID 어노테이션을 기준으로 접근 방식을 결정합니다.

1
2
3
4
5
6
7
8
@Entity
public class Member {

  @Id
  private Long id;
}

private 프로퍼티를 JPA 가 접근할 수 있는 이유

아이디가 필드에 있기 때문에 필드 접근 방식을 사용한 것과 동일합니다. 즉, JPA 는 member.id 로 필드 접근을 한다는 의미입니다. 그렇다면 여기서 의문이 생기는데, JPA private 필드인 id 에 어떻게 필드 접근을 할까요?
예상하셨다시피, reflection 을 사용해서 JPA 는 프리이빗 필드에 접근한다고 합니다.

1
2
3
4
5
6
7
8
9
Member member=new Member();
  // 리플렉션을 사용하여 id 필드에 접근
  Field idField=Member.class.getDeclaredField("id");
  idField.setAccessible(true); // private 필드에 접근할 수 있도록 설정
  // id 값 설정
  idField.set(member,123L);
  System.out.println("Member id: "+member.getId());

이러한 형태로 private 으로 생성된 필드에 대해서도 값을 할당할 수 있을 것입니다. 이와 관련해서 stackoverflow 의 글들을 살펴봤습니다.

JPA 에서 getter setter 가 없이도 private field 에 접근하는 방법
답변을 보면 이런 내용이 나옵니다.

The term directly refers to an access strategy which allows the manipulation of an object’s field (value) without the need to use getter/setter methods. In Java and for most OR-mappers (at least the ones I know of) this is achieved via Introspection - using the Java Reflection API. This way, classes’ fields can be inspected for and manipulated to hold/represent data values from the ( relational) database entries (i.e., their respective columns).

JPA 명세 상, 직접 접근 방식은 객체의 필드에 대한 조작을 통해서 달성된다고 합니다. 흔히들 (java reflection api 를 사용하는) introspection 이라고 부르는 방법입니다.
intr ospection 은 runtime 에서 클래스의 정보나 메소드, 필드를 검출하기 위해서 사용하는 방법입니다. 자세한 내용은 하단의 문서를 살펴보시면 좋을 것 같습니다.

introspection java tutorial

@Access(AccessType.PROPERTY)

이 설정을 통해서 프로퍼티 접근 방식을 사용할 수 있습니다. 프로퍼티 접근 방식과 기존의 필드 접근 방식을 모두 사용하는 것도 가능합니다.

둘 모두를 적용한 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Entity
public class Member {

  @Id
  private Long id;

  @Transient
  private String firstName;

  @Transient
  private String lastName;

  @Access(AccessType.PROPERTY)
  public String getFullName() {
    return firstName + lastName;
  }
}

db 의 컬럼에 있는 fullName 에는 firstName + lastName 이 저장될 것입니다. 위의 예시에서는 fullName 이라는 필드를 별도로 클래스 프로퍼티로 생성하지 않았지만, 추가를 해줘야 합니다. 없는 경우엔 이런 에러가 발생합니다.

1
2
3
For property-based access both setter and getter should be present

1
2
3
4
@Column(name = "real_name")
private String fullName;

만약에 이런 경우는 어떻게 될까요?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Entity
public class Member {

  @Id
  private Long id;

  private String firstName;

  @Access(AccessType.PROPERTY)
  public String getFirstName() {
    return "Mr. " + firstName;
  }
}

Desktop View
firstName 의 필드가 Mr. sb 로 들어간 것을 보실 수 있습니다.

1
2
3
4
5
6
7
8
9
10
Member member=new Member();
  member.setName("kim");
  member.setFirstName("sb");

  Long saveId=memberService.join(member);

  Member foundMember=memberRepository.findById(saveId).get();
  assertEquals("Mr. Mr. sb",member.getFirstName());

이렇게 테스트를 해볼 수 있는데, getFirstName 의 경우엔, 실제 firstName 의 Mr. 이 붙은 결과를 반환해주기에 위의 코드와 같은 결과가 나옵니다.
결론적으로, 우리는 특정 필드에 대해서 조합형으로 사용하기 위해서는 필드를 잘 설계해서 중복되지 않게 결과가 저장되도록 해야 함을 알 수 있습니다. 이런 주의 사항은 stackoverflow 에서 확인할 수 있었습니다.
어떻게 AccessType.FILED 와 AccessType.PROPERTY 를 사용할지

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.