[java]JPA에 @Transient

allnight5·2023년 1월 31일
0

JPA

목록 보기
1/5

참조사이트1 알참.

1.JPA에서 @Transient 애노테이션이 존재하는 이유

@Transient는 JPA의 표준이라 할 수 있는 javax.persistence 패키지에 포함되어있으며 해당 데이터를 테이블의 컬럼과 매핑 시키지 않는다.
단순히 “컬럼을 제외한다.” 라기보단 영속 대상에서 제외시키기 위해 사용하는 어노테이션입니다.

그 이유는 @Entity는 엔티티메니저에 의해서 관리되는데 여기서 엔티티메니저는 영속성 컨텍스트에 들어가있는 영속성 대상만을 관리해주기때문에 영속상태가 아니라면 관리하지 않습니다. 그로인하여 엔티티에 들어있어고 영속상태가 아니기 때문에 엔티티매니저가 아무것도 해주지 않는다는 것으로 “테이블의 컬럼과 매핑을 하지 않는다.”고 합니다.

2. JPA는 두가지 방식을 통해 영속 상태인 엔티티 객체의 데이터에 접근 할수있습니다.

1.프로퍼티 방식 (getter/setter Method, JavaBeans Property 스타일)
2.필드 방식 (Instance Fileds)

// [1] 필드 방식
@Entity
public class Member{
    @Id
    private Long id;
    private String name;
    ...
}

// [2] 메서드 방식
@Entity
public class Member{
    private Long id;
    private String name;
    
    @Id
    public Long getId(){return this.id;}
    public void setId(Long id){this.id = id;}
    
    public String getName(){return this.name;}
    public void setName(String name){this.name = name;}
}
	@Target({ElementType.METHOD, ElementType.FIELD}

따라서 개발자가 JPA의 두 가지 접근 방식을 중 선택하여 개발할 수 있도록, 필드 레벨에 선언시킬 수 있는 모든 JPA의 애노테이션들은 기본적으로 Property(getter/setter method) 방식을 지원하기 위해 메서드 레벨을 지원하고 있습니다.

여기서 중요한 핵심은 설계 방식에 따라 JPA는 엔티티 객체의 접근 방식이 다르게 결정된다는 것입니다. 기본적으로 엔티티 매니저의 1차 캐시는 Map<@Id, @Entity> 형태로 설계되어 key에 해당하는 @Id와 value에 해당하는 엔티티 객체를 저장하여 관리하게 됩니다.

JPA의 엔티티의 접근 방식 = @Id 위치

결과적으로 JPA의 엔티티의 접근 방식은 @Id 애노테이션의 위치에 의해 결정되며, 엔티티의 모든 필드 또는 상속된 엔티티의 계층에 대해서도 일관성 있게 적용해줘야 합니다.

2.1.1. Field 방식

첫 번째로 필드 방식은 영속 대상에서 제외하고 싶은 isEvent 필드에 @Transient 애노테이션을 선언시키면 됩니다.

@Entity
public class Product{
    @Id
    private Long id;
    private String name;
    private BigDecimal price;
    @Transient
    private boolean isEvent;
    // ^-- 해당 필드 영속 제외 대상
}

2.1.2. Method 방식

이 외에도 @Transient는 메서드에도 선언시킬 수 있습니다.

@Entity
public class Product{
    private Long id;
    private String isEvent;
    
    @Id @GeneratedValue
    public Long getId(){ return this.id; }
    public void setId(Long id){ this.id = id; }
    // ^-- @GeneratedValue는 JPA의 내부적인 프로세스에 의해
    // setter 메서드를 통해 데이터를 셋팅하기 때문에 구성함

    @Transient // <-- 해당 메서드 영속 제외 대상
    public String getIsEventProduct(){ return this.isEvent; }
}

메서드 방식에서 주의할점
주의할 점은 setter 메서드가 아닌, getter 메서드에 애노테이션을 선언해줘야 합니다.

2.1.3 개발 의도와는 다르게 동작하는 JPA

@Entity
public class Member{
    @Id // 필드 방식
    private Long id;
    private String userId;
    private String password;
    private String confirmPassword;
    
    @Transient // JPA에서 인식 불가 → 동작 안함
    public String getComfirmPassword(){ return this.confirmPassword; }
    //인식시키려면 confirmPassword위에 @Transient을 추가해줘야한다.
    //Id가 메소드 형식이 아닌 필드 방식이기 때문
    //@Transient
    //private String confirmPassword; 
}
Hibernate: 
    create table member (
       id bigint not null,
       user_id varchar(255),
       password varchar(255),
       confirm_password varchar(255), <-- 하지만 영속 대상에서 제외되지 않음
       primary key (id)
    )

따라서 다음 문제가 된 엔티티의 구조에선 @Id 애노테이션의 위치가 필드에 있으므로 JPA는 필드 접근 방식을 따르게 됩니다. 이러한 이유로 confirmPassword 필드는 영속 대상에서 제외되지 않습니다.

엔티티의 접근방식에 따라 애노테이션을 선언해줘야 하며 엔티티 접근 방식을 혼합하여 사용할 수 없다고 정의하고 있습니다.

그러니 Id가 필드방식이라면 @Transient를 붙여줄 필드도 필드방식으로
Id가 메소드 방식이라면 @Transient를 붙여줄 필드도 메소드 방식으로 만들어 메소드에 붙여 줘야 합니다.

3. 정리

마지막으로 @Transient 애노테이션을 정리하면 다음과 같습니다.

1.@Transient는 영속 대상에서 제외한다.
2.JPA 컬럼 매핑 레퍼런스 애노테이션은 Filed, Property 방식을 지원하기 위해 필드와 메서드에 선언할 수 있다.
3.컬럼 매핑 레퍼런스 애노테이션을 사용할 때 JPA의 엔티티 접근 방식을 살펴보자.
@Id 애노테이션의 위치를 보자.(@Access 애노테이션으로 접근 방식을 재정의하지 않는 이상)
4.QueryDSL 또한 JPA의 엔티티 접근 방식을 기준으로 QDomain 클래스를 생성한다.
또한, @Transient 애노테이션이 선언된 필드나 메서드는 QDomain 클래스의 쿼리 타입 생성과정에서 제외된다.
5. @Transient를 사용할때에는 Id의 형식(메소드, 컬럼)에 따라서 알맞게 선언해주자.

profile
공부기록하기

0개의 댓글