문서의 선택한 두 판 사이의 차이를 보여줍니다.
양쪽 이전 판 이전 판 다음 판 | 이전 판 다음 판 양쪽 다음 판 | ||
java:hibernate:gotchas [2015/03/04 15:39] kwon37xi [One-To-One 과 Many-To-One 관계를 조심하라] |
java:hibernate:gotchas [2022/05/27 16:40] kwon37xi [to-many 관계에 대해서 fetch join은 안하는게 낫다] |
||
---|---|---|---|
줄 1: | 줄 1: | ||
- | ====== Hibernate/ | + | ====== Hibernate/ |
하이버네이트/ | 하이버네이트/ | ||
줄 5: | 줄 5: | ||
hashCode와 equals를 항상 구현해야 한다. | hashCode와 equals를 항상 구현해야 한다. | ||
- | 이때 DB 프라이머리키(ID)가 아닌 **항상 변치 않고 해당 객체를 대표하는 의미를 가질 수 있는 비즈니스 키(business key)를 hashCode와 equals의 대상으로 삼아야 | + | 이때 DB Primary Key(ID)가 아닌 **항상 변치 않고 해당 객체를 대표하는 의미를 가질 수 있는 비즈니스 키(business key)를 hashCode와 equals의 대상으로 삼도록 노력한다.** |
이유는 Set등에 신규 객체를 넣을 경우 신규 객체는 아직 프라이머리키가 지정되지 않은 상태이기 때문에 ID가 모두 '' | 이유는 Set등에 신규 객체를 넣을 경우 신규 객체는 아직 프라이머리키가 지정되지 않은 상태이기 때문에 ID가 모두 '' | ||
+ | |||
+ | 하지만 실제로 해보면 인공키를 hashCode의 기준으로 삼을 수 밖에 없는 경우도 많다. Set/Map 과 함께 사용할 때 주의를 기울여야한다. | ||
따라서 ID를 equals/ | 따라서 ID를 equals/ | ||
+ | ===== to-many 관계에 대해서 fetch join은 안하는게 낫다 ===== | ||
+ | * to-many 관계에 대해서 fetch join 을 하면 부모측이 자식측(many)의 갯수만큼 중복으로 결과 갯수가 늘어난다. | ||
+ | * 이를 해결하려면 '' | ||
+ | * to-many 관계에 대해서 '' | ||
+ | * 때문에 하이버네이트는 SQL에 limit을 걸리 않고 limit 없이 전체 데이터를 조회한 뒤에 메모리에서 parent 데이터를 원하는 limit 갯수만큼 끊어서 반환한다. | ||
+ | * 이는 엄청난 성능저하로 이어진다. | ||
+ | * 결과적으로 특별한 이유가 없다면 **to-many 관계에 대해서는 fetch join을 하지 말고 항상 '' | ||
+ | * [[java: | ||
+ | * '' | ||
===== One-To-One 과 Many-To-One 관계를 조심하라 ===== | ===== One-To-One 과 Many-To-One 관계를 조심하라 ===== | ||
One-To-One과 Many-To-One 관계에서 One 측은 '' | One-To-One과 Many-To-One 관계에서 One 측은 '' | ||
줄 23: | 줄 34: | ||
===== OpenSessionInView 패턴 사용시 HTML 주석을 사용치 말라 ===== | ===== OpenSessionInView 패턴 사용시 HTML 주석을 사용치 말라 ===== | ||
+ | 무엇보다 가급적 **OpenSessionInView 패턴을 사용하지 말라**. 처음엔 편하지만 나중에는 불명확한 쿼리 실행으로 튜닝이 오히려 어렵고 복잡해진다. | ||
+ | |||
toString 메소드에서와 같은 현상이 OpenSessionInView 패턴 사용시 뷰에서 발생할 수 있다. | toString 메소드에서와 같은 현상이 OpenSessionInView 패턴 사용시 뷰에서 발생할 수 있다. | ||
더이상 필요없는 부분 특히 하이버네이트 도메인 객체를 호출하는 부분을 주석처리할 때 **HTML 주석을 사용하지 말라.** HTML주석은 서버사이드는 그대로 동작한다. 따라서 레이지 로딩으로 지정한 값을 HTML 주석 부분에서 호출하면 그대로 값이 로딩되어 출력된다. | 더이상 필요없는 부분 특히 하이버네이트 도메인 객체를 호출하는 부분을 주석처리할 때 **HTML 주석을 사용하지 말라.** HTML주석은 서버사이드는 그대로 동작한다. 따라서 레이지 로딩으로 지정한 값을 HTML 주석 부분에서 호출하면 그대로 값이 로딩되어 출력된다. | ||
줄 28: | 줄 41: | ||
항상 **템플릿 엔진의 주석(JSP의 경우 <%-- --%>)을 사용하라.** | 항상 **템플릿 엔진의 주석(JSP의 경우 <%-- --%>)을 사용하라.** | ||
- | ===== 쿼리 로그를 남겨라 ===== | + | ===== 개발 환경에서는 |
<code properties> | <code properties> | ||
hibernate.show_sql=true | hibernate.show_sql=true | ||
줄 36: | 줄 49: | ||
[[java: | [[java: | ||
+ | 단, 실 운영환경에서는 로그를 남기는 순간 매우 느려지므로 로그 레벨을 높여야 한다. | ||
===== 쿼리에 진짜로 ":" | ===== 쿼리에 진짜로 ":" | ||
Native SQL에 '':'' | Native SQL에 '':'' | ||
줄 61: | 줄 75: | ||
* 아니면 '' | * 아니면 '' | ||
+ | ===== JPQL Positional Parameter Bug ===== | ||
+ | * JPQL 방식의 Positional Parameter(''? | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | |||
+ | ===== @Where 에서 true, false 등에 대해 잘못된 반응 ===== | ||
+ | * '' | ||
+ | select ... from address address0_ | ||
+ | where ( address0_.deleted=address0_.FALSE) -- 이 부분 | ||
+ | and address0_.contact_id=? | ||
+ | </ | ||
+ | * [[http:// | ||
+ | * '' | ||
+ | * 조건을 '' | ||
+ | * 혹은 사용중인 Dialect 에 '' | ||
+ | public class ImprovedHSQLDialect extends HSQLDialect { | ||
+ | |||
+ | public ImprovedHSQLDialect() { | ||
+ | super(); | ||
+ | registerKeyword(" | ||
+ | registerKeyword(" | ||
+ | // registerKeyword(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | * [[https:// | ||
+ | |||
+ | ===== SQL / JPQL / HQL Keyword " | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * Entity 의 package name 에 '' | ||
+ | * **패키지 이름에서 '' | ||
+ | * [[https:// | ||
+ | |||
+ | ===== null id in entry ===== | ||
+ | > HHH000099: an assertion failure occurred (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: | ||
+ | |||
+ | * [[https:// | ||
+ | * 핵심은 **" | ||
+ | * 하나의 트랜잭션에서 | ||
+ | * 쓰기 작업시 에러가 발생해서 '' | ||
+ | * 혹은, entity 삭제가 아니 직접 쿼리로 엔티티를 삭제하거나 한 상황에서 | ||
+ | * '' | ||
+ | * 나중에 flush 가 일어날 때 잘못된 Entity 혹은 DB상에 존재하지도 않지만 기존에 존재했던 것처럼 표시된 Entity 때문에 오류가 발생하게 된다. | ||
+ | * 쓰기 작업과 그 후속 읽기 작업 트랜잭션을 분리해서 '' | ||
+ | * 혹은 직접 쿼리를 통한 삭제 작업의 경우에는 '' | ||
===== 참조 ===== | ===== 참조 ===== | ||
* [[http:// | * [[http:// | ||
* 기타 직접 겪은 것들을 정리한다. | * 기타 직접 겪은 것들을 정리한다. |