계산 된 속성을 JPA 및 Hibernate로 매핑하는 방법


104

내 Java bean에는 childCount 속성이 있습니다. 이 속성은 데이터베이스 열에 매핑되지 않습니다 . 대신, 내 Java bean과 그 자식의 조인에서 작동 하는 COUNT()함수 를 사용하여 데이터베이스에서 계산 해야합니다 . 이 속성을 요청시 / "게으르게"계산할 수 있다면 더 좋겠지 만 필수는 아닙니다.

최악의 시나리오에서는 HQL 또는 Criteria API를 사용하여이 빈의 속성을 설정할 수 있지만 선호하지 않습니다.

Hibernate @Formula주석이 도움이 될 수 있지만 문서를 거의 찾을 수 없습니다.

어떤 도움이라도 대단히 감사합니다. 감사.

답변:


162

JPA는 파생 속성에 대한 지원을 제공하지 않으므로 공급자 별 확장을 사용해야합니다. 당신이 언급했듯이, @FormulaHibernate를 사용할 때 이것에 완벽합니다. SQL 조각을 사용할 수 있습니다.

@Formula("PRICE*1.155")
private float finalPrice;

또는 다른 테이블에 대한 복잡한 쿼리 :

@Formula("(select min(o.creation_date) from Orders o where o.customer_id = id)")
private Date firstOrderDate;

현재 엔티티 id는 어디에 있습니까 id?

다음 블로그 게시물은 읽을 가치가 있습니다 : Hibernate Derived Properties-Performance and Portability .

자세한 내용이 없으면 더 정확한 답변을 드릴 수 없지만 위 링크가 도움이 될 것입니다.

또한보십시오:


파스칼 감사합니다. 다음이 작동하지 않는 이유를 알고 있습니까? @Formula (value = "(select count ( ) from ic inner join c where ic.category_id = c.id and c.id = id)") public Integer getCountInternal () {return countInternal; } 쿼리는 정상이며 로그에서 실행되고 있습니다. 매핑은 괜찮은 것 같습니다 : main org.hibernate.cfg.Ejb3Column-binding formula (select count ( ) blah blah blah But countInternal is set to its initial value (-1) :( I have try using field annotations instead of getter annotations, but 여전히 작동하지 않습니다.
Francois

고마워, 정말 내 작업에 많은 도움이되었습니다!
Mythul 2013

13
@NeilMcGuigan : @Formula주석은 HQL이 아닌 SQL을 사용합니다.
user1438038

7
쿼리를 괄호로 묶는 것이 얼마나 중요한지 방금 배웠습니다. 이 쿼리는 물론 호스트 개체가로드 될 때 하위 쿼리가되므로 항상 SQL 예외가 발생합니다. MySQL은 괄호가없는 인라인 선택을 좋아하지 않았습니다.
sorrymissjackson


53

이 도움말에 설명 된대로 세 가지 옵션이 있습니다.

  • @Transient방법을 사용하여 속성을 계산하거나
  • @PostLoad엔티티 리스너 를 사용할 수도 있습니다.
  • 또는 Hibernate 특정 @Formula주석을 사용할 수 있습니다.

최대 절전 모드를 사용할 수 있지만 @Formula을 JPA와 함께, 당신은 사용할 수 있습니다 @PostLoad의 채우는 콜백을 과도 약간의 계산 결과와 속성을 :

@Column(name = "price")
private Double price;

@Column(name = "tax_percentage")
private Double taxes;

@Transient
private Double priceWithTaxes;

@PostLoad
private void onLoad() {
    this.priceWithTaxes = price * taxes;
}

더 복잡한 쿼리의 경우이 기사@Formula 에서 설명한대로 Hibernate를 사용할 수 있습니다 .

@Formula(
    "round(" +
    "   (interestRate::numeric / 100) * " +
    "   cents * " +
    "   date_part('month', age(now(), createdOn)" +
    ") " +
    "/ 12) " +
    "/ 100::numeric")
private double interestDollars;

7
그러나 이것은 더 복잡한 쿼리를 허용하지 않습니다
휴 버트 Grzeskowiak에게

2
대답을 업데이트 했으므로 이제 더 복잡한 쿼리에 대한 솔루션도 있습니다.
Vlad Mihalcea

1

JPA 위에서 작동하고 일류 DTO 지원을 제공하는 Blaze-Persistence Entity Views 를 살펴보십시오 . 엔티티 뷰 내의 속성에 무엇이든 프로젝션 할 수 있으며 가능한 경우 기존 조인 노드를 다시 사용하여 연관시킬 수도 있습니다.

다음은 매핑의 예입니다.

@EntityView(Order.class)
interface OrderSummary {
  Integer getId();
  @Mapping("SUM(orderPositions.price * orderPositions.amount * orderPositions.tax)")
  BigDecimal getOrderAmount();
  @Mapping("COUNT(orderPositions)")
  Long getItemCount();
}

이것을 가져 오면 다음과 유사한 JPQL / HQL 쿼리가 생성됩니다.

SELECT
  o.id,
  SUM(p.price * p.amount * p.tax),
  COUNT(p.id)
FROM
  Order o
LEFT JOIN
  o.orderPositions p
GROUP BY
  o.id

다음은 사용자에게 흥미로울 수있는 사용자 지정 하위 쿼리 공급자에 대한 블로그 게시물입니다. https://blazebit.com/blog/2017/entity-view-mapping-subqueries.html


0

나는 내 코드에서 이것을 시도했다.

 @Formula(value = "(select DATEDIFF(expiry_date,issue_date)  from document_storage)")
    private String  myColumn;

콧수염에 열을 표시하기 전에도 실행하고 내 뷰를 표시 할 때 발생하는 오류는 다음과 같습니다.

java.lang.NullPointerException
    at java.base/java.lang.String$CaseInsensitiveComparator.compare(String.java:1241)
    at java.base/java.lang.String$CaseInsensitiveComparator.compare(String.java:1235)
    at java.base/java.util.TreeMap.getEntryUsingComparator(TreeMap.java:374)
    at java.base/java.util.TreeMap.getEntry(TreeMap.java:343)
    at java.base/java.util.TreeMap.get(TreeMap.java:277)
    at com.mysql.cj.result.DefaultColumnDefinition.findColumn(DefaultColumnDefinition.java:182)

내가 묻는 질문은 mySQL과 함께 JPA / Hibernate를 사용하여 계산 된 열의 값을 얻는 방법이 있습니까?

내가 뭘 잘못하고 있죠 ?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.