본문 바로가기
개발 이론/JPA

[JPA]@Formula 이게 무엇이고..?

by dal_been 2023. 11. 12.
728x90

다른분의 코드를 보다가 @Formula라는 어노테이션을 보았다. 그분 코드를 보니 약간 좋아요수를 불러오는 것같던데 근데 엥?? 엔티티에 @Formula가 있길래 이게 뭐야...? 라는 느낌에 찾아보았다

 

 

@Formula

 

하이버네이트 사이트에 들어가서 보면 이렇게 적혀져 있다

 

@Formula
 allows mapping any database computed value as a virtual read-only column.

The @Formula annotation takes a native SQL clause which may affect database portability.
@Formula is a Hibernate-specific mapping construct and not covered by Jakarta Persistence. Applications interested in portability should avoid its use.

한마디로 JPA상에는 존재하지만 DB에는 생성되지 않는 칼럼인 가상 칼럼이고 읽기전용 칼럼이다.
이 어노테이션은 데이터의 이식성에 영향을 주는 native sql문을 사용한다. (즉 jpql이 아니라 native sql이기에 rdms 종료 변경시 이식성이 안좋다는 의미인것같다)

 

 

예시

 

예시 1)

@Entity(name = "Account")
public static class Account {

	@Id
	private Long id;

	private Double credit;

	private Double rate;

	@Formula(value = "credit * rate")
	private Double interest;

	//Getters and setters omitted for brevity

}

와같이 가상 칼럼을 만들어 사용가능하다. 그래서 만약 조회를 하게 된다면

 

SELECT
    a.id as id1_0_0_,
    a.credit as credit2_0_0_,
    a.rate as rate3_0_0_,
    a.credit * a.rate as formula0_0_
FROM
    Account a
WHERE
    a.id = 1

 

이렇게 나올 것이다

 

 

 

에시 2) 컬렉션 수 반환하기

 

예를들어 간접참조이거나 지연로딩일때 관련 엔티티에 대해서 사이즈가 필요할때가 있다. 뭐 좋아요 수라든지, 부서수라든지

지연로딩같은경우 실제 사용 시점에서 데이터를 조회한다.

그래서 만약 카운트만을 조회하기 위해 연관엔티티를 사용한다면 데이터가 많아지면 많아질수록 sql실행속도가 늘려질뿐만 아니라 데이터를 담는 컬렉션도 많은 메모리를 사용하기 때문에 점점 성능이 덜어진다. 더 큰 문제는 카운트를 조회시 해당 엔티티의 수만큼 반복한 다는 것이다

 

이때 @Formula를 사용하기 한다

 

@Formula("(select count(*) from employee e where e.dept_id = id)")
  private int employees;

  

와 같이 부서에 대한 직원수를 구하기 위해 @Formula를 사용한다

 

이렇게 조회하게 되면

Hibernate:
    select
        department0_.id as id1_0_,
        department0_.name as name2_0_,
        (select
            count(*)
        from
            employee e
        where
            e.dept_id = department0_.id) as formula0_)
    from
        Department department0_

이렇게  Formula가 기본적으로 fetchType.Eager로 가져오게 된다

 

물론 Formula도 지연로딩이 가능하다. 적용할 속성이나 getterdp @Basic(fetch=FetchType.Lazy) 를 적용하면 된다

다만 Bytecode Enhancement를 추가로 설정해줘야 동작한다

 

 Bytecode Enhancement 이 아이에 대해서는 다음에 설명하도록 하겠다...