2005-07-07 13:55:10 +00:00

226 lines
12 KiB
XML

<chapter id="batch">
<title>Batch 처리</title>
<para>
Hibernate를 사용하여 데이터베이스 내에서 100 000 개의 행들을 삽입시키는 본래의 접근법은 다음과 같다:
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
}
tx.commit();
session.close();]]></programlisting>
<para>
이것은 50 000번째 행 가까운 곳에서 <literal>OutOfMemoryException</literal>으로 떨어질 것이다. 그것은 Hibernate가
session-level 캐시 속에 모든 새로이 삽입된 <literal>Customer</literal> 인스턴스들을 캐시시키기 때문이다.
</para>
<para>
이 장에서 우리는 이 문제를 피하는 방법을 당신에게 보여줄 것이다. 하지만 먼저 당신이 배치 처리를 행하는 중이라면, 당신이 적당한 퍼포먼스를
성취하려고 할 경우에 당신이 JDBC 배치 사용을 가능하게 하는 것은 절대적으로 필요하다. JDBC 배치 사이즈를 적당한 숫자(10-50)로 설정하라:
</para>
<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
<para>
당신은 또한 second-level 캐시를 가진 상호작용이 완전하게 불가능한 프로세스 내에서 이런 종류의 작업을 행하고 싶어할 수도 있다:
You also might like to do this kind of work in a process where interaction with
the second-level cache is completely disabled:
</para>
<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
<sect1 id="batch-inserts">
<title>Batch inserts</title>
<para>
새로운 객체들을 영속화 시킬 때, 당신은 first-level 캐시의 사이즈를 제어하기 위해 세션을 정기적으로 <literal>flush()</literal>
시키고 나서 <literal>clear()</literal> 시켜야 한다.
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
if ( i % 20 == 0 ) { //20, same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();]]></programlisting>
</sect1>
<sect1 id="batch-update" >
<title>Batch updates</title>
<para>
데이터 검색과 업데이트의 경우 동일한 개념들이 적용된다. 게다가 당신은 많은 데이터 행들을 반환하는 질의들에 대해 서버-측 커서들의 장점을
취하는데 <literal>scroll()</literal>을 사용할 필요가 있다.
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
ScrollableResults customers = session.getNamedQuery("GetCustomers")
.setCacheMode(CacheMode.IGNORE)
.scroll(ScrollMode.FORWARD_ONLY);
int count=0;
while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.updateStuff(...);
if ( ++count % 20 == 0 ) {
//flush a batch of updates and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();]]></programlisting>
</sect1>
<sect1 id="batch-direct" revision="1">
<title>DML-스타일 연산들</title>
<para>
이미 논의했듯이, 자동적이고 투명한 객체/관계형 매핑은 객체 상태에 대한 관리에 관계된다. 이것은 객체 상태가 메모리 내에서 이용 가능함을
의미하므로, (SQL <literal>Data Manipulation Language</literal>(DML) 문장들 : <literal>INSERT</literal>,
<literal>UPDATE</literal>, <literal>DELETE</literal>를 사용하여) 데이터베이스에서 직접 데이터를
업데이트하거나 삭제하는 것은 메모리 내 상태에 영향을 주지 않을 것이다. 하지만 Hibernate는 Hibernate Query Language
(<xref linkend="queryhql">HQL</xref>)를 통해 실행되는 대량 SQL-스타일의 DML 문장 실행을 위한 메소드들을 제공한다.
</para>
<para>
<literal>UPDATE</literal><literal>DELETE</literal> 문장들의 유사-구문은 다음과 같다:
<literal>( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?</literal>. 노트할 몇 가지 :
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
from-절에서, FROM 키워드는 옵션이다
</para>
</listitem>
<listitem>
<para>
from-절 내에 한 개의 명명된 엔티티가 오직 존재할 수 있다; 그것은 선택적으로 alias될 수 있다. 만일 엔티티
이름이 alias되면, 그때 임의의 프로퍼티 참조들은 그 alias를 사용하여 수식되어야 한다; 만일 엔티티 이름이
alias되지 않을 경우, 임의의 프로퍼티 참조들에 대해 수식되는 것은 규칙에 어긋난다.
</para>
</listitem>
<listitem>
<para>
join들은 (함축적이든 명시적이든) 대량 HQL 질의 속에 지정될 수 없다. 서브-질의들이 where-절에 사용될 수 있다;
서브질의들 그 자신들은 조인들을 포함할 수 있다.
</para>
</listitem>
<listitem>
<para>
where-절 또한 옵션이다.
</para>
</listitem>
</itemizedlist>
<para>
하나의 예제로서, 한 개의 HQL <literal>UPDATE</literal>를 실행하기 위해, <literal>Query.executeUpdate()</literal>
메소드(이 메소드는 JDBC의 <literal>PreparedStatement.executeUpdate()</literal>와 유사하게 명명된다)를 사용하라:
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
// or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();]]></programlisting>
<para>
한 개의 HQL <literal>DELETE</literal>를 실행하기 위해, 동일한 <literal>Query.executeUpdate()</literal> 메소드를
사용하라:
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlDelete = "delete Customer c where c.name = :oldName";
// or String hqlDelete = "delete Customer where name = :oldName";
int deletedEntities = s.createQuery( hqlDelete )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();]]></programlisting>
<para>
<literal>Query.executeUpdate()</literal> 메소드에 의해 반환되는 <literal>int</literal> 값은 그 오퍼레이션에 의해
영향받은 엔티티들의 개수를 나타낸다. 이것이 데이터베이스 내에서 영향받은 행들의 개수와 상관이 있는지 없는지 여부를 살펴보자. HQL
대량 오퍼레이션은 예를 들어 joined-subclass의 경우에 실행 중인 여러 개의 실제 SQL 문장들로 귀결될 수 있다. 반환되는 숫자는
그 문장에 의해 영향받은 실제 엔티티들의 개수를 나타낸다. joined-subclass 예제로 되돌아가면, 서브 클래스들 중 하나에 대한 삭제는
단지 그 서브클래스가 매핑되어 있는 테이블에 대한 삭제 뿐만 아니라 또한 "루트" 테이블과 상속 계층에서 더 내려온 잠정적으로
조인된-서브클래스 테이블들에 대한 삭제들로 귀결될 수 있다.
</para>
<para>
장래의 배포본들에서 전달될 대량 HQL 오퍼레이션들에 대한 몇 가지 제한들이 현재 존재함을 노트하라; 상세한 것은 JIRA 로드맵을 참조하라.
<literal>INSERT</literal> 문장들을 위한 유사-구문은 다음과 같다:
<literal>INSERT INTO EntityName (properties_list)? select_statement</literal>. 노트할 몇 가지:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
오직 INSERT INTO ... SELECT ... 형식 만일 지원된다; INSERT INTO ... VALUES ... 형식은 지원되지 않는다.
</para>
<para>
properties_list는 옵션이다. 그것은 SQL <literal>INSERT</literal> 문장에서 <literal>column speficiation</literal>
유사하다. 만일 생략될 경우, 모든 "적당한"(다음을 보라) 프로퍼티들이 자동적으로 포함된다.
</para>
</listitem>
<listitem>
<para>
매핑된 상속에 참여하는 모든 엔티티들에 대해, 그 주어진 클래스-레벨에 직접 정의된 프로퍼티들 만이 properties_list에
사용될 수 있다. 슈퍼클래스 프로퍼티들은 허용되지 않는다; 그리고 서브클래스 프로퍼티들은 의미가 없다. 달리 말해,
<literal>INSERT</literal> 문장들은 본래 다형적이지 않다.
</para>
<para>
select_statement는 반환 타입들이 insert에 의해 기대되는 타입들과 일치해야 한다는 단서 하에 임의의 유효한 HQL select 질의일 수
있다. 현재 이것은 체크를 데이터베이스로 이관시키는 것을 허용하기 보다는 질의 컴파일 동안에 체크된다. 하지만 이것은
<emphasis>equal</emphasis>과는 대조적으로 <emphasis>등가인(equivalent)</emphasis> Hibernate <literal>Type</literal>
사이에서 문제점들을 일으킬 수도 있음을 노트하라. 비록 데이터베이스가 구별짓지 않을 수 있거나 변환을 처리할 수 있을 지라도, 이것은
<literal>org.hibernate.type.DateType</literal>로서 정의된 프로퍼티와 <literal>org.hibernate.type.TimestampType</literal>으로
정의된 프로퍼티 사이에 불일치 쟁점들을 일으킨다.
</para>
</listitem>
</itemizedlist>
<para>
예제 HQL <literal>INSERT</literal> 문장 실행:
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = s.createQuery( hqlInsert )
.executeUpdate();
tx.commit();
session.close();]]></programlisting>
</sect1>
</chapter>