Hibernate와 동시성 제어에 대한 가장 중요한 점은 이해하기가 매우 쉽다는 점이다. Hibernate는 어떤 추가적인 잠금 행위 없이 JDBC 커넥션들과 JTA 리소스들을 직접 사용한다. 우리는 당신의 데이터베이스 관리 시스템의 JDBC, ANSI, 그리고 트랜잭션 격리 명세에 약간의 시간을 할애할 것을 매우 권장한다.
Hibernate는 메모리 내에서 객체들을 잠그지 않는다. 당신의 어플리케이션은 격리 레벨에 의해 정의된 대로 행위를 기대할 수 있다. 또한 transaction-영역의 캐시인 Session 덕분에, Hibernate는 (스칼라 값들을 반환하는 질의들을 보고하지 않는) 식별자와 엔티티 질의들에 의한 룩업을 위해 반복 가능한 읽기를 제공한다.
자동적인 optimistic 동시성 제어를 위한 버전화에 덧붙여, Hibernate는 또한 SELECT FOR UPDATE 구문을 사용하여 행들에 대한 pessimistic 잠금을 위한 하나의 (마이너)API를 제공한다. optimistic 동시성 제어와 이 API는 이 장의 뒷부분에서 논의된다.
우리는 Configuration, SessionFactory, Session, 알갱이를 가진 Hibernate에서의 동시성 제어 뿐만 아니라 데이터베이스 트랜잭션과 장기간에 걸친 (컴퓨터와의)대화들에 대한 논의를 시작한다.
SessionFactory는 모든 어플리케이션 쓰레드들에 의해 공유되도록 고안된 생성에 비용이 드는, 쓰레드안전(threadsafe) 객체이다. 그것은 대개 어플리케이션 시작 시에 Configuration 인스턴스로부터 한번 생성된다.
Session은 하나의 요청, 하나의 대화 , 하나의 작업 단위를 위해 한번만 사용되고 나서 폐기될 예정인, 비용이 들지 않는, 쓰레드 안전하지 않은 객체이다. Session은 커넥션이 필요하지 않으면 하나의 JDBC Connection(또는 Datasource)를 얻지 않을 것이므로, 사용될 때까지 리소스들을 소비하지 않는다.
이 그림을 완성하기 위해 당신은 또한 데이터베이스 트랜재션들에 대해 생각해야 한다. 데이터베이스 트랜잭션은 데이터베이스에서 잠금 다툼을 줄이기 위해 가능한 짧아야 한다. 긴 데이터베이스 트랜잭션들은 당신의 어플리케이션이 고도의 동시성 로드로의 가용성을 높이는 것을 방해할 것이다. 그러므로 사용자가 생각하는 시간 동안 단위 작업이 완료될 때까지 데이터베이스 트랜잭션을 열려진채로 소유하는 것은 대개 결코 좋은 설계는 아니다.
하나의 작업 단위의 영역은 무엇인가? 하나의 Hibernate Session은 몇몇 데이터베이스 트랜잭션들에 걸칠 수 있는가 또는 이것은 영역들의 one-to-one 관계인가? 당신은 언제 Session을 열고 닫는가 그리고 당신은 데이터베이스 트랜잭션 경계들을 어떻게 한정하는가?
첫번째로, session-per-operation anti-패턴을 사용하지 말라. 즉, 단일 쓰레드 내에서 모든 간단한 데이터베이스 호출에 대해 Session을 열고 닫지 말라! 물론 같은 것이 데이터베이스 트랜잭션들에 대해서도 참이다. 어플리케이션 내의 데이터베이스 호출들은 계획된 순서를사용하여 행해지며, 그것들은 원자 작업 단위 속으로 그룹지워진다. (이것은 또한 모든 하나의 SQL 문장 뒤의 auto-commit(자동-커밋)이 어플리케이션 내에서 무용지물임을 의미하고, 이 모드가 SQL 콘솔 작업을 돕도록 고안되었음을 노트하라. Hibernate는 의미하고, 이 모드는 Hibernate는 즉시 자동-커밋 모드를 사용 불가능하게 하거나, 어플리케이션 서버가 그렇게 행하고, 즉시 자동-커밋시키는 것을 사용불가능하게 하거나 ,그렇게 행하는 것을 기대한다.) 데이터베이스 트랜잭션들은 결코 옵션이 아니며, 하나의 데이터베이스와의 모든 통신은 당신이 데이터를 읽든 쓰단간에 상관없이 하나의 트랜잭션 내에서 발생해야 한다. 설명하였듯이, 데이터 읽기를 위한 auto-commit 특징을 피해야 할 것이며, 많은 작은 트랜잭션들은 하나의 명료하게 정의된 작업 단위보다 더 좋게 수행될 것 같지 않다. 후자가 또한 훨씬 더 유지가능하고 확장적이다.
다중 사용자 클라이언트/서버 어플리케이션에서 가장 공통된 패턴은 session-per-request이다. 이 모형에서, 클라이언트로부터의 요청은 (Hibernate 영속 계층이 실행되는) 서버로 전송되고, 새로운 Hibernate Session이 열려지고, 모든 데이터베이스 오퍼레이션들이 이 작업 단위 내에서 실행된다. 일단 그 작업이 완료되었다면(그리고 클라이언트에 대한 응답이 준비되었다면), 그 세션은 flush 되고 닫혀진다. 당신은 또한 당신이 Session을 열고 닫을 때 그것을 시작하고 커밋시켜서 클라이언트 요청에 서비스하는데 한 개의 데이터베이스 트랜잭션을 사용하게 될 것이다. 둘 사이의 관계는 일대일 대응이고 이 모형은 많은 어플리케이션들에서 완전하게 적합하다.
난제는 구현에 놓여있다: Hibernate는 이 패턴을 단순화 시키기 위해 "현재 세션"에 관한 미리 빌드된 관리를 제공한다. 당신이 행해야할 모든 것은 서버 요청이 처리되어야 할 때 트랜잭션을 시작하고, 그 응답이 클라이언트로 전송되기 전에 트랜잭션을 끝내는 것이다. 당신은 당신이 좋아하는 임의의 방법으로 이것을 행할 수 있으며, 공통된 해결책들은 서비스 메소드들 상의 첨단, 또는 하나의 프락시/인터셉션 컨테이너를 가진 APO 인터셉터인, ServletFilter이다. 하나의 EJB 컨테이너는 CMT의 경우에 선언적으로 EJB session beans 상에 트랜잭션 경계구분과 같은 동시 대조 측면들을 구현하는 표준 방법이다. 만일 당신이 프로그램 상의 트랜잭션 경계구분을 사용하고자 결정한다면, 사용의 용이성과 코드 이식성을 위해 이 장의 뒷 부분에서 보여진 Hibernate Transaction을 선호하라.
당신의 어플리케이션 코드는 어디서든 필요할 때 종종 sessionFactory.getCurrentSession()을 간단히 호출함으로써 요청을 처리할 "현재 세션"에 접근할 수 있다. 당신은 현재 데이터베이스 트랜잭션으로 영역화된 하나의 Session을 항상 얻게 될 것이다. 이것은 resource-local 환경 또는 JTA 환경에 대해 구성되어야 하며, 2.5절. “컨텍스트 상의 세션들”을 보라.
때때로 "뷰가 렌더링될 때까지" 하나의 Session 과 데이터베이스 트랜잭션의 영역을 확장시키는 것이 편리하다. 이것은 요청이 처리된 후에 하나의 별도의 렌더링 단계를 활용하는 서블릿 어플리케이션들에서 특히 유용하다. 뷰 렌더링이 완료될 때까지 데이터베이스 트랜잭션을 확장하는 것은 당신이 당신 자신의 인터셉터를 구현하는 경우에 행하기가 쉽다. 하지만 만일 당신이 컨테이너에 의해 관리되는 트랜잭션들을 가진 EJB들에 의존할 경우에, 하나의 EJB 메소드가 반환될 때 임의의 뷰 렌더링이 시작될 수 있기 전에 하나의 트랜잭션이 완료되기 때문에, 행하는 것이 쉽지 않다. 이 Open Session in View 패턴을 둘러싼 팁들과 예제들은 Hibernate 웹 사이트와 포럼을 보라.
session-per-request 패턴은 당신이 작업 단위들을 설계하는데 사용할 수 있는 유일한 유용한 개념이 아니다. 많은 비지니스 프로세스들은 데이터베이스 접근들을 중재하는 사용자 사이의 전체 일련의 상호작용들을 필요로 한다. 웹과 엔터프라이즈 어플리케이션에서 사용자 상호작용에 걸치는 것은 데이터베이스 트랜잭션에 허용되지 않는다. 다음 예제를 검토하자:
대화상자의 첫 번째 화면이 열리고, 사용자에게 보여진 데이터는 특정 Session과 데이터베이스 트랜잭션 속에 로드되었다. 사용자가 객체들을 변경시키는 것이 자유롭다.
사용자는 5분 후에 "저장"을 클릭하고 그의 변경들이 영속화 되기를 기대한다; 그는 또한 그가 이 정보를 편집하는 유일한 개인이고 변경 충돌이 발생하지 않기를 기대한다.
우리는 사용자의 관점에서, 이것을 작업 단위, 장기간 실행되는 대화 (또는 어플리케이션 트랜잭션)이라고 명명한다. 당신이 당신의 어플리케이션에서 이것을 어떻게 구현할 수 있는 많은 방법들이 존재한다.
첫 번째 naive 구현은 동시성 변경을 방지하고, 격리와 atomicity(원자 단위성)을 보장하기 위해 데이터베이스에 의해 소유된 잠금으로 사용자가 생각하는동안 Session과 데이터베이스 트랜잭션을 유지할 수도 있다. 이것은 물론 anti-패턴이다. 왜냐하면 잠금 다툼은 어플리케이션이 동시 사용자들의 가용 숫자를 높이는 것을 허용하지 않을 것이기 때문이다.
명료하게, 우리는 대화(어플리케이션 트랜잭션)를 구현하는데 몇몇 데이터베이스 트랜잭션들을 사용해야 한다. 이 경우에, 비지니스 프로세스들의 격리를 유지하는 것은 어플리케이션 티어의 부분적인 책임이 된다. 단일 대화는 대개 여러 개의 데이터베이스 트랜잭션들에 걸친다. 그것은 이들 데이터베이스 트랜잭션들 중 오직 한 개(마지막 트랜잭션)가 업데이트된 데이터를 저장하고, 모든 다른 트랜잭션들이 단순히 데이터를 읽는 (예를 들면, 몇몇 요청/응답 주기에 걸치는 마법사 스타일의 대화 상자에서) 경우에만 원자단위가 될 것이다. 특히 당신이 Hibernate의 특징들을 사용할 경우에 , 이것은 들리는 것보다 구현하기가 더 쉽다:
자동적인 버전화 - Hibernate는 당신을 위해 자동적인 optimistic 동시성 제어를 행할 수 있고, 그것은 사용자가 생각하는 시간 동안 동시적인 변경이 발생했는지를 자동적으로 검출할 수 있다. 대개 우리는 오직 대화의 끝에서 체크한다.
Detached 객체들 - 만일 당신이 이미 논의된 session-per-request 패턴을 사용하고자 결정하는 경우, 모든 로드된 인스턴스들은 사용자가 생각하는 시간 동안 detached 상태에 있을 것이다. Hibernate는 그 객체들을 재첨부시키고 변경들을 영속화 시키는 것을 허용해주며, 그 패턴은 session-per-request-with-detached-objects(detached-객체들을 가진 요청 당 세션)으로 명명된다. 자동적인 버전화는 동시성 변경들을 격리시키는데 사용된다.
확장된 (또는 Long) Session - Hibernate Session은 데이터베이스 트랜잭션이 커밋된 후에 기본 JDBC 커넥션이 연결 해제될 수도 있고, 새로운 클라이언트 요청이 발생할 때 다시 연결될 수 있다. 이 패턴은 session-per-conversation(대화 당 세션)으로 알려져 있고 재첨부를 불필요하게 만든다. 자동적인 버전화는 동시성 변경들을 격리시키는데 사용되고 Session은 자동적으로 flush되는 것이 허용되지 않지만 명시적으로 flush되는 것은 허용된다.
session-per-request-with-detached-objects과 session-per-conversation 양자는 장점들과 단점들을 갖는데, 우리는 이 장의 뒷 부분에서 optimistic 동시성 제어 단락에서 그것들을 논의한다.
어플리케이션은 두 개의 다른 Session들 내에 있는 동일한 영속 상태에 동시에 접근할 수도 있다. 하지만 영속 클래스의 인스턴스는 두 개의 Session 인스턴스들 사이에 결코 공유되지 않는다. 그러므로 identity에 대한 두 개의 다른 개념들이 존재한다:
foo.getId().equals( bar.getId() )
foo==bar
그때 (예를 들어 Session 영역에서) 특정 Session에 첨부된 객체들의 경우 두 개의 개념들은 동등한 것이고, 데이터베이스 identity에 대한 JVM identity가 Hibernate에 의해 보장된다. 하지만, 어플리케이션이 두 개의 다른 세션들에서 "동일한" (영속 identity) 비지니스 객체에 동시에 접근하는 동안, 두 개의 인스턴스들은 실제로 "다르다"(JVM identity). 충돌들은 flush/커밋 시에 (자동적인 버전화)를 사용하여, optimistic 접근법을 사용하여 해결된다.
이 접근법은 Hibernate와 데이터베이스가 동시성에 대해 걱정하지 않도록 해준다; 그것은 또한 최상의 scalability를 제공한다. 왜냐하면 단일 쓰레드-작업 단위 내에서 identity 보장은 단지 비용이 드는 잠금이나 다른 동기화 수단들을 필요로 하지 않기 때문이다. 어플리케이션은 그것이 Session 당 단일 쓰레드를 강제하는 한, 어떤 비지니스 객체에 대해 결코 동기화 시킬 필요가 없다. 하나의 Session 내에서 어플리케이션은 객체들을 비교하는데 ==를 안전하게 사용할 수가 있다.
하지만, 하나의 Session 외부에서 ==를 사용하는 어플리케이션은 예기치 않은 결과들을 보게 될 수도 있다. 이것은 어떤 예기치 않은 장소들에서, 예를 들어 당신이 두 개의 detached 인스턴스들을 동일한 Set 내에 집어넣을 경우에 발생할 수도 있다. 둘 다 동일한 데이터베이스 identity를 가질 수 있지만 (예를 들어 그것들은 동일한 행을 표현한다), JVM identity는 정의 상 detached 상태에 있는 인스턴스들을 보장하지 않는다. 개발자는 영속 클래스들내에 equals() 메소드와 hashCode() 메소드를 오버라이드 시켜야 하고 객체 equality에 대한 그 자신의 개념을 구현해야 한다. 하나의 경고가 존재한다: equality를 구현하는데 데이터베이스 identifier를 결코 사용하지 말고, 하나의 비지니스 키, 유일한, 대개 불변인 속성들의 조합을 사용하라. 데이터베이스 식별자는 만일 transient 객체가 영속화되는 경우에 변경될 것이다. 만일 transient 인스턴스가(대개 detached 인스턴스들과 함께) Set 내에 보관되는 경우에, hashcode 변경은 Set의 계약을 파기시킨다. 비지니스 키들에 대한 속성들은 데이터베이스 프라이머리 키들 만큼 안정적이어서는 안되며, 당신은 오직 객체들이 동일한 Set 내에 있는 한에서 안정성을 보장해야만 한다. 이 쟁점에 관한 논의에 대한 더 많은 것을 Hibernate 웹 사이트를 보라. 또한 이것이 Hibernate 쟁점이 아니며, 단지 자바 객체 identity와 equality가 구현되어야 하는 방법임을 노트하라.
안티-패턴들 session-per-user-session 또는 session-per-application을 결코 사용하지 말라(물론 이 규칙에 대한 드문 예외상황들이 존재한다). 다음 쟁점들 중 몇몇이 또한 권장되는 패턴들로 나타날 수 있음을 노트하고, 당신이 설계 결정을 내리기 전에 내포된 의미들을 확실히 이해하라:
Session은 쓰레드-안전하지 않다. HTTP 요청들, 세션 빈즈, 또는 Swing worker들처럼 동시에 작업하는 것으로 가정되는 것들은 하나의 Session 인스턴스가 공유될 경우에 경쟁 조건들을 발생시킬 것이다. 만일 당신이 당신의 HttpSession 내에 Hibernate Session을 유지시키는 경우(나중에 논의됨), 당신은 당신의 Http 세션에 대한 접근을 동기화 시키는 것을 고려해야 한다. 그 밖의 경우, 충분히 빠르게 reload를 클릭하는 사용자는 두 개의 동시적으로 실행되는 쓰레드들 내에서 동일한 Session을 사용할 수도 있다.
Hibernate에 의해 던져진 예외상황은 당신이 당신의 데이터베이스 트랜잭션을 롤백 시키고 즉시 Session을 닫아야 함을 의미한다(나중에 상세히 논의됨). 만일 당신의 Session이 어플리케이션에 바인드 되어 있는 경우, 당신은 어플리케이션을 중지시켜야 한다. 데이터베이스 트랜잭션 롤백은 당신의 비지니스 객체들을 그것들이 트랜잭션의 시작 시에 머물렀던 상태로 되돌리지는 않는다. 이것은 데이터베이스 상태와 비지니스 객체들이 동기화를 벗어남을 의미한다. 대개 이것은 문제가 아니다. 왜냐하면 예외상황들은 회복가능한 것이 아니고 당신이 어떻게든 롤백 후에 시작해야 하기 때문이다.
Session은 (Hibernate에 의해 dirty 상태로 관찰되었거나 체크된) 영속 상태에 있는 모든 객체를 캐시 시킨다. 이것은 당신이 오랜 시간 동안 Session을 열어둔 채로 유지하거나 단순하게 너무 많은 데이터를 로드시킬 경우에, 당신이 OutOfMemoryException을 얻기 전까지, 그것이 끝없이 성장한다는 점을 의미한다. 이것에 대한 하나의 해결책은 Session 캐시를 관리하기 위해 clear()와 evict()를 호출하는 것이지만, 당신이 대용량 데이터 오퍼레이션들을 필요로 하는 경우에 당신은 대개 내장 프로시저를 고려해야 할 것이다. 몇몇 해결책들이 13장. Batch 처리에 보여져 있다. 사용자 세션 동안에 Session을 열려진 채로 유지하는 것은 또한 실효성이 떨어진 데이터에 대한 높은 확률을 의미한다.
데이터베이스 (또는 시스템) 트랜잭션 경계들은 항상 필수적이다. 데이터베이스와의 통신은 데이터베이스 트랜잭션의 외부에서 발생할 수 없다(이것은 자동-커밋 모드로 사용되는 많은 개발자들에게는 혼동스러워 보인다). 항상 심지어 읽기 전용 오퍼레이션들에 대해서도 명료한 트랜잭션 경계들을 사용하라. 당신의 격리 레벨과 데이터베이스 가용성들에 따라, 이것은 필요하지 않을 수 있지만, 만일 당신이 항상 트랜잭션들을 명시적으로 경계 설정할 경우에는 하강하는 결점들이 존재하지 않는다. 확실히, 하나의 데이터베이스 트랜잭션은 심지어 데이터 읽기조차도 많은 작은 트랜잭션들의 경우보다는 더 좋게 수행될 것이다.
Hibernate 어플리케이션은 관리되지 않는 환경(예를 들면. 스탠드얼론, 간단히 웹 어플리케이션들 또는 Swing 어플리케이션들)과 관리되는 J2EE 환경에서 실행될 수 있다. 관리되지 않는 환경에서, Hibernate는 대개 그것 자신의 데이터베이스 커넥션 풀에 대한 책임이 있다. 어플리케이션 개발자는 트랜잭션 경계들을 손수 설정해야 한다. 달리 말해, 개발자 스스로 데이터베이스 트랜잭션들을 시작하고, 커밋시키거나 롤백시켜야 한다. 관리되는 환경은 대개 예를 들어 EJB 세션 빈즈의 배치 디스크립터 속에 선언적으로 정의된 트랜잭션 어셈블리를 가진, 컨테이너에 의해-관리되는 트랜잭션들(CMT)을 제공한다. 그때 프로그램 상의 트랜잭션 경계 설정은 더 이상 필요하지 않다.
However, it is often desirable to keep your persistence layer portable between non-managed resource-local environments, and systems that can rely on JTA but use BMT instead of CMT. In both cases you'd use programmatic transaction demaracation. 하지만, CMT 대신 BMT를 사용하는 JTA에 의존할 수 있는 시스템들, 그리고 관리되지 않는 resource-local 환경들 사이에서 당신의 영속 계층에 이식성을 유지시키는 것이 자주 희망된다. 두 경우들에서 당신은 프로그램 상의 트랜잭션 경계설정을 사용할 것이다. Hibernate는 당신의 배치 환경의 고유한 트랜잭션 시스템 속으로 변환되는 Transaction이라 명명되는 wrapper API 를 제공한다. 이 API는 실제로 옵션이지만 우리는 당신이 CMT session bean 속에 있지 않는 한 그것의 사용을 강력하게 권장한다.
대개 Session 종료는 네 개의 구분되는 단계들을 수반한다:
세션을 flush 시킨다
트랜잭션을 커밋 시킨다
세션을 닫는다
예외상황들을 처리한다
세션을 flush 시키는 것은 앞서 논의되었고, 우리는 이제 관리되는 환경과 관리되지 않는 환경 양자에서 트랜잭션 경계 설정과 예외상황을 더 자세히 살펴볼 것이다.
만일 Hibernate 영속 계층이 관리되지 않는(non-managed) 환경에서 실행될 경우, 데이터베이스 커넥션들은 대개 Hibernate가 필요로할 때 커넥션들을 획득하는 간단한 (예를 들면 DataSource가 아닌) 커넥션 풀(pool)들로부터 처리된다. session/transaction 처리 관용구는 다음과 같다:
// Non-managed environment idiom Session sess = factory.openSession(); Transaction tx = null; try { tx = sess.beginTransaction(); // do some work ... tx.commit(); } catch (RuntimeException e) { if (tx != null) tx.rollback(); throw e; // or display error message } finally { sess.close(); }
당신은 Session을 명시적으로 flush() 하지 말아야 한다 - commit()에 대한 호출은 (그 세션에 대한 10.10절. “Session을 flush 시키기”에 따라)자동적으로 동기화를 트리거시킨다. close()에 대한 호출은 세션의 끝을 마크한다. close()의 주된 구현은 JDBC 커넥션이 그 세션에 의해 포기될 것이라는 점이다. 이 Java 코드는 관리되지 않는 환경과 JTA 환경 양자에서 이식성이 있고 실행된다.
보다 더 유연한 해결책은 앞서 설명했듯이 Hibernate의 미리 빌드되어 있는 "현재 세션" 컨텍스트 관리이다:
// Non-managed environment idiom with getCurrentSession() try { factory.getCurrentSession().beginTransaction(); // do some work ... factory.getCurrentSession().getTransaction().commit(); } catch (RuntimeException e) { factory.getCurrentSession().getTransaction().rollback(); throw e; // or display error message }
당신은 통상의 어플리케이션에서 비지니스 코드 속에 이 관용구를 결코 보지 않을 것이다; 치명적인(시스템) 예외상황들은 항상 "상단"에서 잡혀야 한다. 달리 말해, (영속 계층에서) Hibernate 호출들을 실행시키는 코드와 RuntimeException을 처리하(고 대개 오직 제거하고 빠져나갈 수 있는) 코드는 다른 계층들 속에 있다. Hibernate에 의한 현재 컨텍스트 관리는 이 설계를 현격하게 단순화시켜서, 당신이 필요로 하는 모든 것은 SessionFactory에 대한 접근이다.예외상황 처리는 이 장의 뒷부분에서 논의된다.
당신은 (디폴트인) org.hibernate.transaction.JDBCTransactionFactory를 선택해야 하고, 두번째 예제의 경우 당신의 hibernate.current_session_context_class를 선택해야 함을 노트하라.
만일 당신의 영속 계층이 어플리케이션 서버에서(예를 들어, EJB 세션 빈즈 이면에서) 실행될 경우, Hibernate에 의해 획득된 모든 데이터소스 커넥션은 자동적으로 전역 JTA 트랜잭션의 부분일 것이다. 당신은 또한 스탠드얼론 JTA 구현을 설치할 수 있고 EJB 없이 그것을 사용할 수 있다. Hibernate는 JTA 통합을 위한 두 개의 방도들을 제공한다.
만일 당신이 bean-managed transactions(BMT)를 사용할 경우 Hibernate는 당신이 Transaction API를 사용할 경우에 BMT 트랜잭션을 시작하고 종료하도록 어플리케이션 서버에게 알려줄 것이다. 따라서 트랜잭션 관리 코드는 non-managed 환경과 동일하다.
// BMT idiom Session sess = factory.openSession(); Transaction tx = null; try { tx = sess.beginTransaction(); // do some work ... tx.commit(); } catch (RuntimeException e) { if (tx != null) tx.rollback(); throw e; // or display error message } finally { sess.close(); }
만일 당신이 트랜잭션에 묶인 Session, 즉 쉬운 컨텍스트 보급을 위한 getCurrentSession() 기능을 사용하고자 원할 경우, 당신은 JTA UserTransaction API를 직접 사용해야 할 것이다:
// BMT idiom with getCurrentSession() try { UserTransaction tx = (UserTransaction)new InitialContext() .lookup("java:comp/UserTransaction"); tx.begin(); // Do some work on Session bound to transaction factory.getCurrentSession().load(...); factory.getCurrentSession().persist(...); tx.commit(); } catch (RuntimeException e) { tx.rollback(); throw e; // or display error message }
CMT의 경우, 트랜잭션 관할[경계 설정]은 프로그램 상이 아닌, session bean 배치 디스크립터들 속에서 행해진다. 그러므로 코드는 다음으로 감소된다:
// CMT idiom Session sess = factory.getCurrentSession(); // do some work ...
왜냐하면 하나의 세션 빈 메소드에 의해 던져진 처리되지 않은 RuntimeException이 글로벌 트랜잭션을 rollback으로 설정하도록 컨테이너에게 알려주기 때문에, CMT/EJB에서조차 롤백은 자동적으로 발생된다. 이것은 당신이 BMT 이든 CMT이든 모두에서 Hibernate Transaction API를 사용할 필요가 없으며, 당신은 그 트랜잭션에 묶인 "현재" Session의 자동적인 보급(propagation)을 얻게 됨을 의미한다.
당신이 Hibernate의 트랜잭션 팩토리를 구성할 때, 당신이 JTA를 직접 사용할 경우(BMT) 당신은 org.hibernate.transaction.JTATransactionFactory를 선택해야하고, CMT session bean에서는 org.hibernate.transaction.CMTTransactionFactory를 선택해야 함을 노트하라. 또한 org.hibernate.transaction.manager_lookup_class를 설정하는 것을 염두에 두라. 게다가 반드시 당신의 hibernate.current_session_context_class이 설정되지 않도록 하거나(역호환성), 또는 "jta"로 설정되도록 하라.
getCurrentSession() 오퍼레이션들은 JTA 환경에서 한 가지 단점을 갖고 있다. 디폴트로 사용되는, after_statement 커넥션 해제 모드 사용에 대한 하나의 보류 통보가 존재한다. JTA 명세서의 어리석은 제한으로 인해, Hibernate가 scroll() 또는 iterate()에 의해 반환되는 임의의 닫혀지지 않은 ScrollableResults 또는 Iterator 인스턴스들을 자동적으로 제거하는 것이 불가능하다. 당신은 finally 블록 내에서 명시적으로 ScrollableResults.close() 또는 Hibernate.close(Iterator)를 호출하여 기본 데이터베이스 커서를 해제시켜야 한다.(물론 대부분의 어플리케이션들은 JTA 또는 CMT 코드에서 scroll()이나 iterate()를 사용하는 것을 쉽게 피할 수 있다.)
만일Session이 (어떤 SQLException을 포함하는) 예외상황을 던질 경우, 당신은 데이터베이스 트랜잭션을 즉시 롤백시키고, Session.close()를 호출하고 Session 인스턴스를 폐기시켜야한다. Session의 어떤 메소드들은 그 세션을 일관된 상태로 남겨두지 않을 것이다. Hibernate에 의해 던져진 예외상황은 복구가능한 것으로 취급될 수 없다. 그 Session이 finally 블록 내에서 close()를 호출하여 닫혀지도록 확실히 하라.
Hibernate 영속 계층에서 발생할 수 있는 대부분의 오류들을 포장하는, HibernateException은 체크되지 않은 예외상황이다(그것은 Hibernate의 이전 버전에는 없었다). 우리의 의견으로, 우리는 낮은 계층에서 복구불가능한 예외상황을 붙잡도록 어플리케이션 개발자에게 강제하지 않을 것이다. 대부분의 시스템들에서, 체크되지 않은 치명적인 예외상황들은 (예를 들어, 더 높은 계층에서) 메소드 호출 스택의 첫 번째 프레임들 중 하나 속에서 처리되고, 한 개의 오류 메시지가 어플리케이션 사용자에게 표시된다(또는 어떤 다른 적절한 액션이 취해진다). Hibernate는 또한 HibernateException이 아닌, 다른 체크되지 않은 예외상황들을 던질 수도 있음을 노트하라. 다시 이것들은 복구가능하지 않고 적절한 액션이 취해져야 한다.
Hibernate는 데이터베이스와 상호작용하는 동안에 던져진 SQLException들을 하나의 JDBCException 속에 포장한다. 사실, Hibernate는 그 예외상황을 JDBCException의 보다 의미있는 서브클래스로 변환하려고 시도할 것이다. 기본 SQLException은 JDBCException.getCause()를 통해 항상 이용 가능하다. Hibernate는SessionFactory에 첨부된 SQLExceptionConverter를 사용하여 SQLException을 적당한 하나의 JDBCException 서브클래스로 변환시킨다. 디폴트로 SQLExceptionConverter는 구성된 dialect에 의해 정의된다; 하지만 맞춤 구현 속에 플러그인 시키는 것이 또한 가능하다(상세한 것은 SQLExceptionConverterFactory 클래스에 관한 javadocs를 보라). 표준 JDBCException 서브타입은 다음과 같다:
JDBCConnectionException - 기본 JDBC 통신에 대한 오류를 나타낸다.
SQLGrammarException - 생겨난 SQL에 대한 문법 또는 구문 문제점을 나타낸다.
ConstraintViolationException - 무결성 제약 위반에 관한 어떤 형식을 나타낸다.
LockAcquisitionException - 요청된 오퍼레이션을 실행하는데 필수적인 잠금 레벨을 획득하는 오류를 나타낸다.
GenericJDBCException - 다른 카테고리들 중 어떤 것으로 분류되지 않았던 일반적인 예외상황.
관리되지 않는 코드에 대해서는 결코 제공되지 않는 EJB와 같이 관리되는 환경에 의해 제공되는 한 가지 극히 중요한 특징은 트랜잭션 타임아웃이다. 트랜잭션 타임아웃은 사용자에게 응답을 반환하지 않는 동안에 무례한 행동을 행하는 트랜잭션이 자원들을 무한정 묶어둘 수 없음을 보증해준다. 관리되는 (JTA) 환경 외부에서, Hibernate는 이 기능을 온전하게 제공할 수 없다. 하지만 Hibernate는 데이터베이스 레벨 데드락들과 거대한 결과 셋들을 가진 질의들이 정의된 타임아웃에 의해 제약되는 것을 보장함으로써 최소한 데이터 접근 오퍼레이션들을 제어할 수 있다. 관리되는 환경에서, Hibernate는 트랜잭션 타임아웃을 JTA에게 위임시킬 수 있다. 이 기능은 Hibernate Transaction 객체에 의해 추상화 된다.
Session sess = factory.openSession(); try { //set transaction timeout to 3 seconds sess.getTransaction().setTimeout(3); sess.getTransaction().begin(); // do some work ... sess.getTransaction().commit() } catch (RuntimeException e) { sess.getTransaction().rollback(); throw e; // or display error message } finally { sess.close(); }
setTimeout()은 CMT bean 내에서 호출도리 수 없음을 노트하라. 여기서 트랜잭션 타임아웃들은 선언적으로 정의되어야 한다.
고도의 동시성과 고도의 가용성을 일치시키는 유일한 접근법은 버전화를 가진 optimistic동시성 제어이다. 버전 체킹은 업데이트 충돌을 검출하기 위해(그리고 업데이트 손실을 방지하기 위해) 버전 번호들 또는 timestamp들을 사용한다. Hibernate는 optimistic 동시성을 사용하는 어플리케이션 코드 작성에 세 가지 가능한 접근법들을 제공한다. 우리가 보여주는 쓰임새들은 장시간의 대화의 상황 속에 있지만, 버전 체킹 또한 단일 데이터베이스 트랜잭션들에서 업데이트 손실을 방지하는 이점을 갖고 있다.
하나의 구현에서 Hibernate로부터 많은 도움이 없이, 데이터베이스에 대한 각각의 상호작용은 새로운 Session 내에서 일어나고, 개발자는 영속 인스턴스들을 처리하기 전에 데이터베이스로부터 모든 영속 인스턴스들을 다시 로드시킬 책임이 있다. 이 접근법은 대화 트랜잭션을 확실히 격리시키기 위해 그것 자신의 버전 체킹을 수행하도록 어플리케이션에게 강제시킨다.
// foo is an instance loaded by a previous Session session = factory.openSession(); Transaction t = session.beginTransaction(); int oldVersion = foo.getVersion(); session.load( foo, foo.getKey() ); // load the current state if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException(); foo.setProperty("bar"); t.commit(); session.close();
version 프로퍼티는 <version>을 사용하여 매핑되고, Hibernate는 만일 엔티티가 dirty일 경우 flush 동안에 그것을 자동적으로 증가시킬 것이다.
물론, 당신이 낮은 데이터 동시성 환경에서 작업하고 있고 버전 체킹을 필요로 하지 않을 경우에, 당신은 이 접근법을 사용할 수 도 있고 단지 버전 체크를 생략할 수도 있다. 그 경우에, 마지막의 커밋 성공은 당신의 장시간의 대화에 대한 디폴트 방도가 될 것이다. 이것이 어플리케이션의 사용자들을 혼동시킬 수 있음을 염두에 두라. 왜냐하면 사용자들은 오류 메시지들 또는 충돌 변경들을 병합시킬 기회 없이 업데이트들 손실을 겪을 수도 있기 때문이다.
명료하게 수작업 버전 체킹은 매우 사소한 환경들에서도 공포적이고 대부분의 어플리케이션들에 대해 실제적이지 않다. 흔히 단일 인스턴스 뿐만 아니라 변경된 객체들의 전체 그래프들이 체크되어야 한다. Hibernate는 설계 패러다임으로서 하나의 확장된 Session 또는 detached 인스턴스들에 대해 자동적인 버전 체킹을 제공한다.
하나의 Session 인스턴스와 그것의 영속 인스턴스들은 전체 어플리케이션 트랜잭션에 사용된다. Hibernate는 flush 할 때 인스턴스 버전들을 체크하고 만일 동시성 변경이 검출될 경우에 예외상황을 던진다. 이 예외상황을 잡아내고 처리하는 것을 개발자의 몫이다(공통된 옵션들은 변경들을 병합시키거나 또는 쓸모가 없지 않은 데이터로 비지니스 프로세스를 다시 시작하는 기회를 사용자에게 주는 것이다). 하나의 Session 인스턴스와 그것의 영속 인스턴스들은 session-per-conversation로 알려진 전체 대화에 사용된다. Hibernate는 flush 시점에서 인스턴스 버전들을 체크하며, 만일 동시적인 변경이 검출될 경우에 하나의 예외상황을 던진다. 이 예외상황을 포착하고 처리하는 것은 개발자의 몫이다(공통된 옵션들은 사용자가 변경들을 병합하거나 손실되지 않은 데이터를 가지고 비지니스 대화를 재시작하는 기회이다).
Session은 사용자 상호작용을 기다릴 때 어떤 기본 JDBC 커넥션으로부터 연결해제된다. 이 접근법은 데이터베이스 접근의 관점에서 보면 가장 효율적이다. 어플리케이션은 버전 체킹 또는 detached 인스턴스들을 재첨부하는 것에 그 자체 관계할 필요가 없거나 그것은 모든 데이터베이스 트랜잭션에서 인스턴스들을 다시 로드시킬 필요가 없다.
// foo is an instance loaded earlier by the old session Transaction t = session.beginTransaction(); // Obtain a new JDBC connection, start transaction foo.setProperty("bar"); session.flush(); // Only for last transaction in conversation t.commit(); // Also return JDBC connection session.close(); // Only for last transaction in conversation
foo 객체는 그것이 로드되었던 Session이 어느 것인지를 여전히 알고 있다. 이전 세션 상에서 하나의 새로운 데이터베이스 트랜잭션을 시작하는 것은 하나의 새로운 커넥션을 획득하고 그 세션을 소비한다. 데이터베이스 트랜잭션을 커밋(확약)시키는 것은 그 JDBC 커넥션으로부터 하나의 세션을 연결해제시키고 그 커넥션을 풀(pool)로 반환시킬 것이다. 재연결 후에, 당신이 업데이트하고 있지 않은 데이터에 대한 버전 체크를 강제시키기 위해서, 당신은 또 다른 트랜잭션에 의해 업데이트되었을 수도 있는 임의의 객체들에 대해 LockMode.READ로서 Session.lock()을 호출할 수도 있다. 당신은 당신이 업데이트 중인 임의의 데이터를 잠금할 필요가 없다. 대개 당신은 마지막 데이터베이스 트랜잭션 주기만이 이 대화 내에서 행해진 모든 변경들을 실제로 영속화시키는 것이 허용되도록 하기 위해, 하나의 확장된 Session에 대해 FlushMode.NEVER를 설정할 것이다. 그러므로 오직 이 마지막 데이터베이스 트랜잭션 만이 flush() 오퍼레이션을 포함할 것이고, 또한 대화를 종료시키기 위해 세션을 close()할 것이다.
만일 사용자가 생각하는시간 동안 Session이 저장되기에 너무 큰 경우 이 패턴은 문제성이 있다. 예를 들어 HttpSession은 가능한 작은 것으로 유지되어야 한다. 또한 Session은 (필수의) 첫 번째 레벨 캐시이고 모든 로드된 객체들을 포함하기 때문에, 우리는 아마 적은 요청/응답 주기들에 대해서만 이 방도를 사용할 수 있다. 당신은 하나의 대화에 대해서만 하나의 Session을 사용해야 한다. 왜냐하면 그것은 또한 곧 실없는 데이터가 될 것이기 때문이다.
(초기의 Hibernate 버전들은 Session에 대한 명시적인 연결해제와 재연결을 필요로 했음을 노트하라. 트랜잭션을 시작하고 끝내는 것이 동일한 효과를 가지므로, 이들 방법들은 진부하게 되었다.)
또한 당신은 영속 계층에 대해 연결해제된 Session을 닫혀진채로 유지해야함을 노트하라. 달 리말해, 하나의 3-tier 환경에서 Session을 소유하는데 EJB stateful session bean을 사용하고, HttpSession 내에 그것을 저장하기 위해 그것을 웹 계층에 전송하지 말라 (또는 그것을 별도의 티어에 직렬화 시키지도 말라).
확장된 세션 패턴, 또는 session-per-conversation은 자동적인 현재 세션 컨텍스트 관리에 대해 구현하기가 더 어렵다. 당신은 이를 위해 당신 자신의 CurrentSessionContext 구현을 공급할 필요가 있으며, 예제들은 Hibernate Wiki를 보라.
영속 저장소에 대한 각각의 상호작용은 새로운 Session에서 일어난다. 하지만 동일한 영속 인스턴스들은 데이터베이스와의 각각의 상호작용에 재사용된다. 어플리케이션은 원래 로드되었던 detached 인스턴스들의 상태를 또 다른 Session 내에서 처리하고 나서 Session.update(), Session.saveOrUpdate(), Session.merge()를 사용하여 그것들을 다시 첨부시킨다.
// foo is an instance loaded by a previous Session foo.setProperty("bar"); session = factory.openSession(); Transaction t = session.beginTransaction(); session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already t.commit(); session.close();
다시, Hibernate는 flush 동안에 인스턴스 버전들을 체크할 것이고 업데이트 충돌이 발생할 경우에 예외상황을 던질 것이다.
당신은 또한 update()대신에 lock()을 호출할 수도 있고 만일 그 객체가 변경되지 않았음을 당신이 확신하는 경우에 (버전 체킹을 수행하고 모든 캐시들을 무시하는) LockMode.READ를 사용할 수 있다.
당신은 optimistic-lock 매핑 속성을 false로 설정함으로써 특정 프로퍼티들과 콜렉션들에 대한 Hibernate의 자동적인 버전 증가를 불가능하도록 할 수도 있다. 그때 Hibernate는 그 프로퍼티가 dirty 일 경우에 더 이상 버전을 증가시키지 않을 것이다.
리거시 데이터베이스 스키마들은 자주 static이고 변경될 수 없다. 또는 다른 어플리케이션들은 또한 동일한 데이터베이스에 접근하고 버전 번호들 또는 심지어 timestamp들을 처리하는 방법을 모를 수도 있다. 두 경우들에서, 버전화는 테이블 내의 특정 컬럼에 의지할 수 없다. version 또는 timestamp 프로퍼티 매핑 없이 행 내의 모든 필드들에 대한 상태를 비교하여 버전 체크를 강제시키기 위해서, <class> 매핑 속에 optimistic-lock="all"을 표시하라. 만일 Hibernate가 이전 상태와 새로운 상태를 비교할 수 있을 경우에, 예를 들면 당신이 하나의 긴 Session을 사용하고 session-per-request-with-detached-objects을 사용하지 않을 경우 이것은 개념적으로만 동작함을 노트하라.
때때로 행해졌던 변경들이 중첩되지 않는 한 동시적인 변경이 허용될 수 있다. 만일 <class>를 매핑할 때 당신이 optimistic-lock="dirty"를 설정하면, Hibernate는 flush 동안에 dirty 필드들을 비교만 할 것이다.
두 경우들에서, 전용 version/timestamp 컬럼의 경우 또는 full/dirty 필드 비교의 경우, Hibernate는 법전 체크를 실행하고 정보를 업데이트하는데 엔티티 당 (적절한 WHERE 절을 가진) 한 개의UPDATE 문장을 사용한다. 만일 당신이 연관된 엔티티들에 대한 재첨부를 케스케이드 하는데 transitive 영속을 사용할 경우, Hibernate는 불필요하게 업데이트들을 실행할 수도 있다. 이것은 대개 문제가 아니지만, 심지어 변경들이 detached 인스턴스들에 대해 행해지지 않았을 때에도 데이터베이스 내에서 on update 트리거들이 실행될 수도 있다. 그 행을 업데이트하기 전에 변경들이 실제로 일어났음을 확인하기 위해 인스턴스를 SELECT하는 것을 Hibernate에게 강제시키는, <class> 매핑 속에 select-before-update="true"를 설정함으로써 당신은 이 특징을 맞춤화 시킬 수 있다.
사용자들은 잠금 방도에 대해 걱정하는데 많은 시간을 할애하하려고 생각하지 않는다. 대개 JDBC 커넥션들에 대한 격리 레벨을 지정하는 것으로 충분하고 그런 다음 단순히 데이터베이스로 하여금 모든 작업을 행하도록 한다. 하지만 진일보한 사용자들은 때때로 배타적인 pessimistic 잠금들을 얻거나 또는 새로운 트랜잭션의 시작 시에 잠금들을 다시 얻고자 원할 수도 있다.
Hibernate는 결코 메모리 내에 있는 객체들이 아닌, 데이터베이스의 잠금 메커니즘을 항상 사용할 것이다!
LockMode 클래스는 Hibernate에 의해 획득될 수 있는 다른 잠금 레벨들을 정의한다. 잠금은 다음 메커니즘들에 의해 얻어진다:
LockMode.WRITE는 Hibernate가 한 행을 업데이트 하거나 insert 할 때 자동적으로 획득된다.
LockMode.UPGRADE는 SELECT ... FOR UPDATE 구문을 지원하는 데이터베이스 상에서 SELECT ... FOR UPDATE를 사용하여 명시적인 사용자 요청 상에서 얻어질 수 있다.
LockMode.UPGRADE_NOWAIT는 오라클에서 SELECT ... FOR UPDATE NOWAIT를 사용하여 명시적인 사용자 요청 상에서 얻어질 수도 있다.
LockMode.READ는 Hibernate가 반복 가능한 읽기(Repeatable Read) 또는 Serialization 격리 레벨에서 데이터를 읽어들일 때 자동적으로 얻어질 수도 있다. 명시적인 사용자 요청에 의해 다시 얻어질 수도 있다.
LockMode.NONE은 잠금이 없음을 나타낸다. 모든 객체들은 Transaction의 끝에서 이 잠금 모드로 전환된다. update() 또는 saveOrUpdate()에 대한 호출을 통해 세션과 연관된 객체들이 또한 이 잠금 모드로 시작된다.
"명시적인 사용자 요청"은 다음 방법들 중 하나로 표현된다:
LockMode를 지정한 Session.load()에 대한 호출.
Session.lock()에 대한 호출.
Query.setLockMode()에 대한 호출.
만일 Session.load()가 UPGRADE 또는 UPGRADE_NOWAIT 모드로 호출되고 ,요청된 객체가 아직 이 세션에 의해 로드되지 않았다면, 그 객체는 SELECT ... FOR UPDATE를 사용하여 로드된다. 만일 요청된 것이 아닌 다소 제한적인 잠금으로 이미 로드되어 있는 객체에 대해 load()가 호출될 경우, Hibernate는 그 객체에 대해 lock()을 호출한다.
만일 지정된 잠금 모드가 READ, UPGRADE 또는 UPGRADE_NOWAIT 일 경우에 Session.lock()은 버전 번호 체크를 수행한다. (UPGRADE 또는 UPGRADE_NOWAIT 인 경우에, SELECT ... FOR UPDATE가 사용된다.)
만일 데이터베이스가 요청된 잠금 모드를 지원하지 않을 경우, (예외상황을 던지는 대신에) Hibernate는 적절한 대체 모드를 사용할 것이다. 이것은 어플리케이션이 이식 가능할 것임을 확실히 해준다.
JDBC 커넥션 관리에 관한 Hibernate의 리거시(2.x) 특징은 그것이 처음으로 필요로 했을 때 하나의 Session이 하나의 커넥션을 획득할 것이고, 그런 다음 그 커넥션은 그 세션이 닫혀질때까지 보관된다는 것이었다. Hibernate 3.x는 세션에게 그것의 JDBC 커넥션들을 처리하는 방법을 알려주기 위해 연결 해제 모드들에 관한 개념을 도입했다. 다음 논의는 구성된 ConnectionProvider를 통해 제공되는 커넥션들에 대해서만 적절하다는 점을 노트하라; 사용자가 제공하는 커넥션들은 org.hibernate.ConnectionReleaseMode의 열거된 값들에 의해 식별된다:
ON_CLOSE - 는 본질적으로 위에 설명된 리거시 특징이다. Hibernate 세션은 그것이 어떤 JDBC 접근을 수행하고 세션이 닫혀질 때까지 그 커넥션을 보관할 필요가 있을 때 하나의 커넥션을 획득한다.
AFTER_TRANSACTION - 은 하나의 org.hibernate.Transaction이 완료된 후에 연결들을 해제하라고 말한다.
AFTER_STATEMENT (또한 적극적인 해제라고 언급됨) - 는 각각의 모든 문장 실행 후에 커넥션들을 해제하라고 말한다. 이 적극적인 해제는 그 문장이 주어진 세션과 연관된 리소스들을 열려진채로 남겨둘 경우에는 건너뛰게(skip) 된다; 현재 이것이 일어나는 유일한 상황은 org.hibernate.ScrollableResults의 사용을 통해서이다.
사용할 해제 모드를 지정하기 위해 구성 파라미터 hibernate.connection.release_mode가 사용된다. 가능한 값들은 다음과 같다:
auto (디폴트) - 이 선택은 org.hibernate.transaction.TransactionFactory.getDefaultReleaseMode() 메소드에 의해 반환된 해제 모드로 위임시킨다. JTATransactionFactory인 경우, 이것은 ConnectionReleaseMode.AFTER_STATEMENT를 반환한다; JDBCTransactionFactory인 경우, 이것은 ConnectionReleaseMode.AFTER_TRANSACTION을 반환한다. 이 설정의 값이 사용자 코드 내의 버그들 그리고/또는 유효하지 않은 가정들을 가리키는 경향이 있음으로 인해 이 디폴트 특징을 실패로 변경하는 것은 거의 좋은 생각이 아니다.
on_close - 는 ConnectionReleaseMode.ON_CLOSE를 사용하라고 말한다. 이 설정은 역호환성을 위해 남겨졌지만, 그것의 사용은 매우 권장되지 않는다.
after_transaction - 은 ConnectionReleaseMode.AFTER_TRANSACTION을 사용하라고 말한다. 이 설정은 JTA 환경들에서 사용되지 않을 것이다. 또한 ConnectionReleaseMode.AFTER_TRANSACTION인 경우에 만일 세션이 auto-commit 모드에 있도록 고려될 경우, 커넥션들은 마치 해제 모드가 AFTER_STATEMENT인 것처럼 해제될 것임을 또한 노트하라.
after_statement - 는 ConnectionReleaseMode.AFTER_STATEMENT를 사용하라고 말한다. 추가적으로 구성된 ConnectionProvider는 그것이 이 설정 (supportsAggressiveRelease())을 지원하는지 여부를 알기 위해 참고된다. 만일 지원하지 않을 경우, 해제 모드는 ConnectionReleaseMode.AFTER_TRANSACTION으로 재설정된다. 이 설정은 우리가 ConnectionProvider.getConnection()을 호출할 때마다 우리가 동일한 기본 JDBC 커넥션을 다시 필요로 할 수 있는 환경들에서 또는 우리가 동일한 커넥션을 얻는 것에 상관없는 auto-commit 환경에서 오직 안전하다.