*** empty log message ***

git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@7183 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
JongDae Kim 2005-06-18 12:39:09 +00:00
parent 838c40dd5f
commit 7672eaa82b
50 changed files with 19423 additions and 0 deletions

View File

@ -28,6 +28,7 @@
<antcall target="lang.all"><param name="lang" value="en"/></antcall>
<antcall target="lang.all"><param name="lang" value="zh-cn"/></antcall>
<antcall target="lang.all"><param name="lang" value="es"/></antcall>
<antcall target="lang.all"><param name="lang" value="ko"/></antcall>
</target>
@ -37,6 +38,7 @@
<!-- TRANSLATOR: Duplicate this line for your language -->
<antcall target="lang.revdiff"><param name="lang" value="zh-cn"/></antcall>
<antcall target="lang.revdiff"><param name="lang" value="es"/></antcall>
<antcall target="lang.revdiff"><param name="lang" value="ko"/></antcall>
</target>

BIN
reference/ko/fop/Gaeul.ttf Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
reference/ko/fop/gulim.ttc Normal file

Binary file not shown.

View File

@ -0,0 +1,113 @@
<!--<!DOCTYPE configuration SYSTEM "config.dtd">-->
<!--
this file contains templates which allow an user easy
configuration of Fop. Actually normally you don't need this configuration
file, but if you need to change configuration, you should
always use this file and *not* config.xml.
Usage: java org.apache.fop.apps.Fop -c userconfig.xml -fo fo-file -pdf pdf-file
-->
<configuration>
<!--
baseDir: normally the base directory is the directory where the fo file is
located. if you want to specify your own, uncomment this entry.
This value can also be a URL. Actually, the value is converted to
a URL.
-->
<!--
<entry>
<key>baseDir</key>
<value></value>
</entry>
-->
<!--
fontBaseDir: Similar to baseDir, except that this value is used for fonts. If
it isn't specified, the value from baseDir is used.
-->
<!--
<entry>
<key>fontBaseDir</key>
<value></value>
</entry>
-->
<!--
************************************************************************
HYPHENATION
************************************************************************
-->
<!--
hyphenation directory
if you want to specify your own directory with hyphenation pattern
then uncomment the next entry and add the directory name
-->
<!--
<entry>
<key>hyphenation-dir</key>
<value>/java/xml-fop/hyph</value>
</entry>
-->
<!--
************************************************************************
Add fonts here
************************************************************************
-->
<fonts>
<!-- example -->
<!--
<font metrics-file="arial.xml" kerning="yes" embed-file="arial.ttf">
<font-triplet name="Arial" style="normal" weight="normal"/>
<font-triplet name="ArialMT" style="normal" weight="normal"/>
</font>
<font metrics-file="arialb.xml" kerning="yes" embed-file="arialb.ttf">
<font-triplet name="Arial" style="normal" weight="bold"/>
<font-triplet name="ArialMT" style="normal" weight="bold"/>
</font>
<font metrics-file="ariali.xml" kerning="yes" embed-file="ariali.ttf">
<font-triplet name="Arial" style="italic" weight="normal"/>
<font-triplet name="ArialMT" style="italic" weight="normal"/>
</font>
<font metrics-file="arialbi.xml" kerning="yes" embed-file="arialbi.ttf">
<font-triplet name="Arial" style="italic" weight="bold"/>
<font-triplet name="ArialMT" style="italic" weight="bold"/>
</font>
-->
<!-- Example Japanese fonts
<font metrics-file="msgothic.xml" embed-file="D:\winnt\font\msgothic.ttc" kerning="yes">
<font-triplet name="Gothic" style="normal" weight="normal"/>
<font-triplet name="Gothic" style="normal" weight="bold"/>
<font-triplet name="Gothic" style="italic" weight="normal"/>
<font-triplet name="Gothic" style="italic" weight="bold"/>
</font>
<font metrics-file="msmincho.xml" embed-file="Cyberbit.ttf" kerning="yes">
<font-triplet name="Mincho" style="normal" weight="normal"/>
<font-triplet name="Mincho" style="normal" weight="bold"/>
<font-triplet name="Mincho" style="italic" weight="normal"/>
<font-triplet name="Mincho" style="italic" weight="bold"/>
</font>
-->
<font metrics-file="Gulim.xml" kerning="yes" embed-file="gulim.ttc">
<font-triplet name="Gulim" style="normal" weight="normal"/>
<font-triplet name="Gulim" style="normal" weight="bold"/>
<font-triplet name="Gulim" style="italic" weight="normal"/>
<font-triplet name="Gulim" style="italic" weight="bold"/>
</font>
<font metrics-file="Gaeul.xml" kerning="yes" embed-file="Gaeul.ttf">
<font-triplet name="가을체" style="normal" weight="normal"/>
<font-triplet name="가을체" style="normal" weight="bold"/>
<font-triplet name="가을체" style="italic" weight="normal"/>
<font-triplet name="가을체" style="italic" weight="bold"/>
</font>
</fonts>
</configuration>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@ -0,0 +1,429 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
[
<!ATTLIST svg
xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
]>
<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="354.331"
height="336.614"
id="svg1">
<defs
id="defs3">
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
id="linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop128" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop129" />
</linearGradient>
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
id="linearGradient130"
xlink:href="#linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad" />
<radialGradient
cx="0.5"
cy="0.5"
fx="0.5"
fy="0.5"
r="0.5"
id="radialGradient131"
xlink:href="#linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad" />
</defs>
<g
transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
style="font-size:12;"
id="g659">
<rect
width="212.257"
height="57.2441"
x="17.9576"
y="100.132"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect137" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
transform="matrix(0.743454,0,0,0.482981,6.46949,52.2178)"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect132" />
</g>
<rect
width="325.86"
height="63.6537"
x="17.4083"
y="15.194"
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect136" />
<rect
width="325.86"
height="63.6537"
x="13.6713"
y="12.4966"
style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect126" />
<g
transform="matrix(1.14345,0,0,0.729078,-1.67818,105.325)"
style="font-size:12;"
id="g164">
<rect
width="285.502"
height="77.2688"
x="16.6979"
y="222.966"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect138" />
<rect
width="285.502"
height="77.2688"
x="14.7335"
y="221.002"
transform="translate(-1.30962,-1.30992)"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect133" />
</g>
<text
x="170.824753"
y="58.402939"
transform="scale(0.823795,0.823795)"
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text183">
<tspan
x="170.824997"
y="58.402901"
id="tspan360">
Application</tspan>
</text>
<text
x="178.076340"
y="364.281433"
transform="scale(0.823795,0.823795)"
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text197">
<tspan
x="178.076004"
y="364.281006"
id="tspan421">
Database</tspan>
</text>
<text
x="68.605331"
y="138.524582"
transform="scale(0.823795,0.823795)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text216">
<tspan
x="68.605301"
y="138.524994"
id="tspan384">
SessionFactory</tspan>
</text>
<rect
width="67.0014"
height="101.35"
x="196.927"
y="89.2389"
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect387" />
<rect
width="67.0014"
height="101.35"
x="194.633"
y="86.4389"
style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect388" />
<text
x="249.108841"
y="173.885559"
transform="scale(0.823795,0.823795)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text389">
<tspan
x="249.108994"
y="173.886002"
id="tspan392">
Session</tspan>
</text>
<rect
width="73.0355"
height="101.35"
x="270.995"
y="90.0018"
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect395" />
<rect
width="73.0355"
height="101.35"
x="267.869"
y="87.2018"
style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect396" />
<text
x="328.593658"
y="174.715622"
transform="scale(0.823795,0.823795)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text397">
<tspan
x="328.593994"
y="174.716003"
id="tspan563">
Transaction</tspan>
</text>
<g
transform="matrix(0.29544,0,0,0.397877,9.70533,103.96)"
style="font-size:12;"
id="g565">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect566" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect567" />
</g>
<text
x="25.592752"
y="204.497803"
transform="scale(0.823795,0.823795)"
style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text568">
<tspan
x="25.592800"
y="204.498001"
id="tspan662">
TransactionFactory</tspan>
</text>
<g
transform="matrix(0.298082,0,0,0.397877,99.6898,103.96)"
style="font-size:12;"
id="g573">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect574" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect575" />
</g>
<text
x="134.030670"
y="205.532791"
transform="scale(0.823795,0.823795)"
style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text576">
<tspan
x="134.031006"
y="205.533005"
id="tspan664">
ConnectionProvider</tspan>
</text>
<g
transform="matrix(1.14345,0,0,0.729078,-1.67818,38.9539)"
style="font-size:12;"
id="g587">
<rect
width="285.502"
height="77.2688"
x="16.6979"
y="222.966"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect588" />
<rect
width="285.502"
height="77.2688"
x="14.7335"
y="221.002"
transform="translate(-1.30962,-1.30992)"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect589" />
</g>
<rect
width="90.951"
height="44.4829"
x="25.6196"
y="206.028"
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect594" />
<rect
width="90.951"
height="44.4829"
x="24.4229"
y="204.135"
style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
id="rect595" />
<text
x="85.575645"
y="282.300354"
transform="scale(0.823795,0.823795)"
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
id="text596">
<tspan
x="85.575600"
y="282.299988"
id="tspan607">
JNDI</tspan>
</text>
<rect
width="90.951"
height="44.4829"
x="236.937"
y="206.791"
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect610" />
<rect
width="90.951"
height="44.4829"
x="235.741"
y="204.898"
style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
id="rect611" />
<text
x="342.093201"
y="283.226410"
transform="scale(0.823795,0.823795)"
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
id="text612">
<tspan
x="342.092987"
y="283.226013"
id="tspan621">
JTA</tspan>
</text>
<rect
width="90.951"
height="44.4829"
x="130.134"
y="206.791"
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect616" />
<rect
width="90.951"
height="44.4829"
x="128.937"
y="204.898"
style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
id="rect617" />
<text
x="212.445343"
y="283.226410"
transform="scale(0.823795,0.823795)"
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
id="text618">
<tspan
x="212.445007"
y="283.226013"
id="tspan623">
JDBC</tspan>
</text>
<g
transform="matrix(0.823795,0,0,0.823795,0.120302,6.19341)"
style="font-size:12;"
id="g637">
<g
transform="matrix(0.499515,0,0,0.415467,-0.237339,5.61339)"
id="g167">
<rect
width="199.065"
height="61.5532"
x="61.8805"
y="68.4288"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect134" />
<rect
width="199.065"
height="61.5532"
x="59.2613"
y="65.8095"
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
id="rect135" />
</g>
<text
x="33.749969"
y="50.589706"
style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text188">
<tspan
x="33.750000"
y="50.589699"
id="tspan635">
Transient Objects</tspan>
</text>
</g>
<g
transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
style="font-size:12;"
id="g644">
<g
transform="matrix(0.297486,0,0,0.516482,230.251,36.9178)"
id="g364">
<rect
width="199.065"
height="61.5532"
x="61.8805"
y="68.4288"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect365" />
<rect
width="199.065"
height="61.5532"
x="59.2613"
y="65.8095"
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
id="rect366" />
</g>
<text
x="277.123230"
y="85.155571"
style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
id="text367">
<tspan
x="277.122986"
y="85.155602"
id="tspan631">
Persistent</tspan>
<tspan
x="277.122986"
y="96.155602"
id="tspan633">
Objects</tspan>
</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -0,0 +1,334 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
[
<!ATTLIST svg
xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
]>
<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="318.898"
height="248.031"
id="svg1">
<defs
id="defs3">
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
id="linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop128" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop129" />
</linearGradient>
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
id="linearGradient130"
xlink:href="#linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad" />
<radialGradient
cx="0.5"
cy="0.5"
fx="0.5"
fy="0.5"
r="0.5"
id="radialGradient131"
xlink:href="#linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad" />
</defs>
<rect
width="291.837"
height="57.0074"
x="17.3169"
y="18.646"
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect136" />
<rect
width="291.837"
height="57.0074"
x="13.9703"
y="16.2302"
style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect126" />
<g
transform="matrix(0.326107,0,0,0.765831,9.59261,8.98517)"
style="font-size:12;"
id="g161">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect137" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect132" />
</g>
<g
transform="matrix(1.02406,0,0,0.652953,0.223384,39.9254)"
style="font-size:12;"
id="g164">
<rect
width="285.502"
height="77.2688"
x="16.6979"
y="222.966"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect138" />
<rect
width="285.502"
height="77.2688"
x="14.7335"
y="221.002"
transform="translate(-1.30962,-1.30992)"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect133" />
</g>
<g
transform="matrix(0.449834,0,0,0.338463,-3.15909,9.73319)"
style="font-size:12;"
id="g167">
<rect
width="199.065"
height="61.5532"
x="61.8805"
y="68.4288"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect134" />
<rect
width="199.065"
height="61.5532"
x="59.2613"
y="65.8095"
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
id="rect135" />
</g>
<text
x="302.277679"
y="65.943230"
transform="scale(0.73778,0.73778)"
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text183">
<tspan
x="302.277954"
y="65.943184"
id="tspan360">
Application</tspan>
</text>
<text
x="36.235924"
y="63.796055"
transform="scale(0.73778,0.73778)"
style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text188">
<tspan
x="36.235950"
y="63.796051"
id="tspan427">
Transient Objects</tspan>
</text>
<text
x="180.416245"
y="290.543701"
transform="scale(0.73778,0.73778)"
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text197">
<tspan
x="180.415939"
y="290.543549"
id="tspan421">
Database</tspan>
</text>
<text
x="25.037701"
y="179.154755"
transform="scale(0.73778,0.73778)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text216">
<tspan
x="25.037655"
y="179.154648"
id="tspan384">
SessionFactory</tspan>
</text>
<g
transform="matrix(0.252763,0,0,0.765831,109.104,8.98517)"
style="font-size:12;"
id="g386">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect387" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect388" />
</g>
<g
transform="matrix(0.297394,0,0,0.572692,101.502,21.6359)"
style="font-size:12;"
id="g364">
<rect
width="199.065"
height="61.5532"
x="61.8805"
y="68.4288"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect365" />
<rect
width="199.065"
height="61.5532"
x="59.2613"
y="65.8095"
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
id="rect366" />
</g>
<text
x="202.746506"
y="102.992203"
transform="scale(0.73778,0.73778)"
style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
id="text367">
<tspan
x="202.746948"
y="102.992249"
id="tspan423">
Persistent</tspan>
<tspan
x="202.746948"
y="116.992355"
id="tspan425">
Objects</tspan>
</text>
<text
x="174.458496"
y="180.080795"
transform="scale(0.73778,0.73778)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text389">
<tspan
x="174.458618"
y="180.080338"
id="tspan392">
Session</tspan>
</text>
<g
transform="matrix(0.127369,0,0,0.765831,188.675,8.98517)"
style="font-size:12;"
id="g394">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect395" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect396" />
</g>
<text
x="260.413269"
y="179.154739"
transform="scale(0.73778,0.73778)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text397">
<tspan
x="260.412964"
y="179.154343"
id="tspan400">
JDBC</tspan>
</text>
<g
transform="matrix(0.127369,0,0,0.765831,229.156,8.98517)"
style="font-size:12;"
id="g405">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect406" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect407" />
</g>
<text
x="320.606903"
y="179.154739"
transform="scale(0.73778,0.73778)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text408">
<tspan
x="320.606964"
y="179.154343"
id="tspan417">
JNDI</tspan>
</text>
<g
transform="matrix(0.127369,0,0,0.765831,269.281,8.98517)"
style="font-size:12;"
id="g411">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect412" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect413" />
</g>
<text
x="377.096313"
y="179.154739"
transform="scale(0.73778,0.73778)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text414">
<tspan
x="377.096008"
y="179.154999"
id="tspan145">
JTA</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@ -0,0 +1,250 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
[
<!ATTLIST svg
xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
]>
<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="248.031"
height="248.031"
id="svg1">
<defs
id="defs3">
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
id="linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop128" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop129" />
</linearGradient>
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
id="linearGradient130"
xlink:href="#linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad" />
<radialGradient
cx="0.5"
cy="0.5"
fx="0.5"
fy="0.5"
r="0.5"
id="radialGradient131"
xlink:href="#linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad" />
</defs>
<g
transform="matrix(0.771934,0,0,0.771934,4.36019,-3.02123)"
style="font-size:12;"
id="g158">
<rect
width="285.502"
height="77.2688"
x="16.6979"
y="17.3527"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect136" />
<rect
width="285.502"
height="77.2688"
x="14.7335"
y="15.3883"
transform="translate(-1.30962,-1.30992)"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect126" />
</g>
<g
transform="matrix(0.771934,0,0,0.771934,4.36019,3.04452)"
style="font-size:12;"
id="g161">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect137" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect132" />
</g>
<g
transform="matrix(0.771934,0,0,0.771934,4.36019,8.0993)"
style="font-size:12;"
id="g164">
<rect
width="285.502"
height="77.2688"
x="16.6979"
y="222.966"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect138" />
<rect
width="285.502"
height="77.2688"
x="14.7335"
y="221.002"
transform="translate(-1.30962,-1.30992)"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect133" />
</g>
<g
transform="matrix(0.771934,0,0,0.543505,2.59104,21.1103)"
style="font-size:12;"
id="g167">
<rect
width="199.065"
height="61.5532"
x="61.8805"
y="68.4288"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect134" />
<rect
width="199.065"
height="61.5532"
x="59.2613"
y="65.8095"
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
id="rect135" />
</g>
<text
x="105.392174"
y="56.568123"
transform="scale(0.771934,0.771934)"
style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text183">
<tspan
x="105.392273"
y="56.568146"
id="tspan186">
Application</tspan>
</text>
<text
x="81.820183"
y="103.149330"
transform="scale(0.771934,0.771934)"
style="font-size:20;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text188">
<tspan
x="81.820213"
y="103.149727"
id="tspan206">
Persistent Objects</tspan>
</text>
<text
x="111.548180"
y="278.927887"
transform="scale(0.771934,0.771934)"
style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text197">
<tspan
x="111.547874"
y="278.927551"
id="tspan200">
Database</tspan>
</text>
<text
x="94.436180"
y="153.805740"
transform="scale(0.771934,0.771934)"
style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text216">
<tspan
x="94.436180"
y="153.805740"
id="tspan221">
HIBERNATE</tspan>
</text>
<g
transform="matrix(0.771934,0,0,0.771934,2.59083,1.02261)"
style="font-size:12;"
id="g254">
<g
transform="translate(4.58374,2.61928)"
id="g176">
<g
transform="matrix(0.571429,0,0,0.67347,-10.6174,117.093)"
id="g170">
<rect
width="199.065"
height="61.5532"
x="61.8805"
y="68.4288"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect171" />
<rect
width="199.065"
height="61.5532"
x="59.2613"
y="65.8095"
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
id="rect172" />
</g>
<g
transform="matrix(0.571429,0,0,0.67347,138.682,117.093)"
id="g173">
<rect
width="199.065"
height="61.5532"
x="61.8805"
y="68.4288"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect174" />
<rect
width="199.065"
height="61.5532"
x="59.2613"
y="65.8095"
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
id="rect175" />
</g>
</g>
<text
x="47.259438"
y="182.367538"
style="font-weight:bold;stroke-width:1pt;font-family:Courier;"
id="text191">
<tspan
x="47.259399"
y="182.367996"
id="tspan212">
hibernate.</tspan>
<tspan
x="47.259399"
y="194.367996"
id="tspan214">
properties</tspan>
</text>
<text
x="198.523010"
y="188.260941"
style="font-weight:normal;stroke-width:1pt;font-family:helvetica;"
id="text194">
<tspan
id="tspan195">
XML Mapping</tspan>
</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

191
reference/ko/master.xml Normal file
View File

@ -0,0 +1,191 @@
<?xml version='1.0' encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
"../support/docbook-dtd/docbookx.dtd"
[
<!ENTITY quickstart SYSTEM "modules/quickstart.xml">
<!ENTITY tutorial SYSTEM "modules/tutorial.xml">
<!ENTITY architecture SYSTEM "modules/architecture.xml">
<!ENTITY configuration SYSTEM "modules/configuration.xml">
<!ENTITY persistent-classes SYSTEM "modules/persistent_classes.xml">
<!ENTITY basic-mapping SYSTEM "modules/basic_mapping.xml">
<!ENTITY collection-mapping SYSTEM "modules/collection_mapping.xml">
<!ENTITY association-mapping SYSTEM "modules/association_mapping.xml">
<!ENTITY component-mapping SYSTEM "modules/component_mapping.xml">
<!ENTITY inheritance-mapping SYSTEM "modules/inheritance_mapping.xml">
<!ENTITY session-api SYSTEM "modules/session_api.xml">
<!ENTITY transactions SYSTEM "modules/transactions.xml">
<!ENTITY events SYSTEM "modules/events.xml">
<!ENTITY batch SYSTEM "modules/batch.xml">
<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
<!ENTITY filters SYSTEM "modules/filters.xml">
<!ENTITY xml SYSTEM "modules/xml.xml">
<!ENTITY performance SYSTEM "modules/performance.xml">
<!ENTITY toolset-guide SYSTEM "modules/toolset_guide.xml">
<!ENTITY example-parentchild SYSTEM "modules/example_parentchild.xml">
<!ENTITY example-weblog SYSTEM "modules/example_weblog.xml">
<!ENTITY example-mappings SYSTEM "modules/example_mappings.xml">
<!ENTITY best-practices SYSTEM "modules/best_practices.xml">
]>
<book lang="ko">
<bookinfo lang="ko">
<title>HIBERNATE - 개성있는 자바를 위한 관계 영속</title>
<subtitle>하이버네이트 참조 문서</subtitle>
<releaseinfo lang="ko">3.0.5</releaseinfo>
</bookinfo>
<toc lang="ko" />
<preface id="preface" revision="2">
<title>머리말</title>
<para>
객체 지향 소프트웨어와 관계형 데이터베이스로 작업하는 것은 오늘날의 엔터프라이즈 환경들에서 성가시고 시간이
소비될 수 있다. Hibernate는 자바 환경들을 위한 객체/관계형 매핑 도구이다.
object/relational mapping(ORM) 용어는 객체 모형으로부터 SQL-기반의 스키마를 가진
관계형 데이터 모형으로의 데이터 표상을 매핑하는 기술을 언급한다.
</para>
<para>
Hibernate는 자바 클래스들로부터 데이터베이스로의 매핑(그리고 자바 데이터 타입들로부터 SQL 데이터
타입들로의 매핑)을 처리할 뿐만 아니라, 또한 데이터 질의와 검색 편의들을 제공하며, SQL과 JDBC로
수작업 데이터 핸들링에 소요되는 개발 시간을 현저하게 단축시켜줄 수 있다.
</para>
<para>
Hibernate의 목적은 공통된 데이터 영속화 관련 프로그래밍 작업들의 95%를 덜어주는 것이다.
Hibernate는 데이터베이스에서 비지니스 로직을 구현하는데 내장 프로시저들을 전용으로 사용하는 데이터
중심적 어플리케이션에 대한 최상의 솔루션이 아닐 수도 있지만, 그것은 자바 기반 미들-티어에서 객체 지향
도메인 모형들과 비지니스 로직에 가장 유용하다. 하지만 Hibernate는 벤더 지정적인 SQL 코드를
제거하거나 캡슐화 시키는 당신을 확실히 도와줄 수 있고 테이블 형식의 표현으로부터 객체들의 그래프로 결과
셋을 변환하는 공통된 태스크를 도와줄 것이다.
</para>
<para>
만일 당신이 Hibernate와 Object/Relational 매핑 또는 심지어 자바에 초심자라면, 다음
단계들을 따르기 바란다:
</para>
<orderedlist>
<listitem>
<para>
Tomcat을 사용하는 30분짜리 빠른시작,
<xref linkend="quickstart" />
을 읽어라.
</para>
</listitem>
<listitem>
<para>
더 많은 단계적인 사용 설명서들을 가진 더 긴 튜토리얼은
<xref linkend="tutorial" />
을 읽어라.
</para>
</listitem>
<listitem>
<para>
Hibernate가 사용될 수 있는 환경을 이해려면
<xref linkend="architecture" />
를 읽어라.
</para>
</listitem>
<listitem>
<para>
Hibernate 배포본 내의
<literal>eg/</literal>
디렉토리를 살펴 보라. 이 디렉토리는 간단한 스탠드얼론 어플리케이션을 포함하고 있다. 당신의
JDBC 드라이버를
<literal>lib/</literal>
디렉토리에 복사하고 당신의 데이터베이스에 맞는 정확한 값을 지정하여
<literal>etc/hibernate.properties</literal>
를 편집하라. 배보본 디렉토리에서 명령 라인 프롬프트에서 (Ant를 사용하여)
<literal>ant eg</literal>
를 타이핑 하거나 , Windows 하에서
<literal>build eg</literal>
를 타이프 하라.
</para>
</listitem>
<listitem>
<para>
당신의 주된 정보의 소스로서 이 참조 문서를 사용하라. 만일 어플리케이션 설계에 대해 더 많은
도움을 필요로 하거나 당신이 단계적인 튜토리얼을 선호한다면
<emphasis>Hibernate in Action</emphasis>
(http://www.manning.com/bauer)을 읽는 것을 고려하라.또한
http://caveatemptor.hibernate.org에 방문하여 Hibernate in
Action용 예제 어플리케이션을 다운로드 하라.
</para>
</listitem>
<listitem>
<para>FAQ들은 Hibernate 웹 사이트 상에 답변되어 있다.</para>
</listitem>
<listitem>
<para>
제 3의 데모들, 예제들, 그리고 튜토리얼들은 Hibernate 웹 사이트 상에 링크되어
있다.
</para>
</listitem>
<listitem>
<para>
Hibernate 웹사이트 상의 공동체 영역은 설계 패턴과 다양한 통합 솔루션들(Tomcat,
JBoss AS, Struts, EJB 등.)에 관한 좋은 리소스이다.
</para>
</listitem>
</orderedlist>
<para>
질문이 있다면, Hibernate 상에 링크되어 있는 사용자 포럼을 사용하라. 우리는 또한 버그 보고와 특징
요청들을 위한 JIRA 쟁점 추적 시스템을 제공한다. 당신이 Hibernate의 개발에 관심이 있다면,
개발자 메일링 리스트에 참여하라. 만일 당신이 이 문서를 당신의 언어로 번역하는 것에 관심이 있다면, 개발자
메일링 리스트에 접촉하라.
</para>
<para>
Hibernate를 위한 상용 개발 지원, 제품 지원, 그리고 교육은 JBoss Inc를 통해 이용 가능하다
(http://www.hibernate.org/SupportTraining/를 보라). Hibernate는
JBoss Professional Open Source product 프로젝트이고 제품들에 대한 JBoss
Enterprise Middleware System (JEMS) suite의 중대한 컴포넌트이다.
</para>
</preface>
&quickstart;
&tutorial;
&architecture;
&configuration;
&persistent-classes;
&basic-mapping;
&collection-mapping;
&association-mapping;
&component-mapping;
&inheritance-mapping;
&session-api;
&transactions;
&events;
&batch;
&query-hql;
&query-criteria;
&query-sql;
&filters;
&xml;
&performance;
&toolset-guide;
&example-parentchild;
&example-weblog;
&example-mappings;
&best-practices;
</book>

View File

@ -0,0 +1,240 @@
<chapter id="architecture">
<title>아키텍처</title>
<sect1 id="architecture-overview" revision="1">
<title>개요</title>
<para>
Hibernate 아키텍처에 대한 (매우) 높은 수준의 개요::
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/overview.svg" format="SVG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/overview.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
이 다이어그램은 어플리케이션에 영속화 서비스들(과 영속 객체들)을 제공하기 위해 데이터베이스와
컨피그레이션을 사용하는 Hibernate를 보여준다.
</para>
<para>
우리는 런타임 아키텍처에 대한 보다 상세한 뷰를 보여주고 싶다. 불행하게도, Hibernate는 유연하며
몇 가지 접근법들을 제공한다. 우리는 두 가지 극단을 보여줄 것이다. "경량급" 아키텍처는 그것 자신의
JDBC 커넥션들을 제공하고 그것 자신의 트랜잭션들을 관리하는 어플리케이션을 갖는다. 이 접근법은
Hibernate의 API의 최소 부분집합을 사용한다:
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/lite.svg" format="SVG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/lite.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
"전체 정수" 아키텍처는 기본 JDBC/JTA로부터 어플리케이션을 추상화 시키고 Hibernate로 하여금
상세한 것을 처리하게 한다.
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/full_cream.svg" format="SVG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/full_cream.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
다음은 다이어그램들 내에 있는 객체들에 대한 몇가지 정의들이다:
<variablelist spacing="compact">
<varlistentry>
<term>SessionFactory (<literal>org.hibernate.SessionFactory</literal>)</term>
<listitem>
<para>
단일 데이터베이스에 대한 컴파일된 매핑들의 threadsafe (불변의) 캐시. Session과 ConnectionProvider의
클라이언트를 위한 팩토리. 프로세스 레벨 또는 클러스터 레벨에서 트랜잭션들 사이에 재사용 가능한 데이터의 선택적인
(second-level) 캐시를 보관할 수도 있다.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Session (<literal>org.hibernate.Session</literal>)</term>
<listitem>
<para>
어플리케이션과 영속 저장소 사이의 대화를 표현하는 단일 쓰레드이고, 수명이 짧은 객체. JDBC 커넥션을 포장한다.
<literal>Transaction</literal> 용 팩토리. 객체 그래프를 네비게이트 하거나 식별자로 객체들을 룩업할 때
사용되는 영속 객체들에 대한 필수적인(첫 번째 레벨의) 캐시를 보관한다.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Persistent objects and collections</term>
<listitem>
<para>
persistent 상태와 비지니스 기능을 포함하는 수명이 짧고, 단일 쓰레드인 객체들. 이것들은 통상의 JavaBeans/POJO들일
수 있고, 오직 그것들에 대한 오직 특별한 것은 그것들이 현재 (정확하게 한 개의) <literal>Session</literal>과 연관되어
있다는 점이다. <literal>Session</literal>이 닫히자마자, 그것들은 분리될(detached 상태가 될) 것이고 어플리케이션
레이어에서 사용하는 것이 자유로와진다(예를 들면. 프리젠테이션으로의 데이터 전송 객체들로서 직접적으로 그리고 프리젠테이션으로부터
데이터 전송 객체들로서 직접으로).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Transient and detached objects and collections</term>
<listitem>
<para>
<literal>Session</literal>과 현재 연관되어 있지 않은 영속 클래스들의 인스턴스들. 그것들은 어플리케이션에 의해 초기화
되었고 (아직) 영속화 되지 않았거나 그것들은 닫혀진<literal>Session</literal>에 의해 초기화 되었을 수도 있다.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Transaction (<literal>org.hibernate.Transaction</literal>)</term>
<listitem>
<para>
(옵션) 작업의 원자 단위를 지정하기 위해 어플리케이션에 의해 사용되는 단일 쓰레드이고, 수명이 짧은 객체. 기본 JDBC, JTA
또는 CORBA 트랜잭션으로부터 어플리케이션을 추상화 시킨다. 몇몇 경우들에서 하나의 <literal>Session</literal>
여러 개의 <literal>Transaction</literal>들에 걸칠 수 있다. 하지만 기본 API 또는 <literal>Transaction</literal>
사용하는 트랜잭션 경계 설정은 결코 옵션이 아니다!
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>ConnectionProvider (<literal>org.hibernate.connection.ConnectionProvider</literal>)</term>
<listitem>
<para>
(옵션) JDBC 커넥션들에 대한 팩토리(그리고 그것들의 pool). 기본 <literal>Datasource</literal> 또는
<literal>DriverManager</literal>로부터 어플리케이션을 추상화 시킨다. 어플리케이션에 노출되지는 않지만
개발자에 의해 확장/구현 된다.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>TransactionFactory (<literal>org.hibernate.TransactionFactory</literal>)</term>
<listitem>
<para>
(옵션) <literal>Transaction</literal> 인스턴스들에 대한 팩토리. 어플리케이션에 노출되지는 않지만 개발자에 의해
확장/구현 된다.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>Extension Interfaces</emphasis></term>
<listitem>
<para>
Hibernate는 당신의 영속 계층의 특성을 맞춤화 시키기 위해 당신이 구현할 수 있는 선택적인 확장 인터페이스들을 제공한다.
상세한 것은 API 문서를 보라.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
주어진 "경량급" 아키텍처의 경우, 어플리케이션은 JTA 또는 JDBC와 직접 대화하기 위해서 <literal>Transaction</literal>/<literal>TransactionFactory</literal>
그리고/또는 <literal>ConnectionProvider</literal> API들을 무시한다.
</para>
</sect1>
<sect1 id="architecture-states" revision="1">
<title>인스턴스 상태들</title>
<para>
영속 클래스들의 인스턴스는 세개의 상태들 중 하나 일 수 있다. 그것들(상태들)은 영속 컨텍스트(<emphasis>persistence context</emphasis>)에
대해 정의된다. Hibernate <literal>Session</literal> 객체는 영속 컨텍스트이다:
</para>
<variablelist spacing="compact">
<varlistentry>
<term>transient</term>
<listitem>
<para>
인스턴스는 임의의 컨텍스트와 연관되어 있지 않고, 결코 연관된 적이 없었다. 그것은 영속 식별자(프라이머리 키 값)을 갖지 않는다.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>persistent</term>
<listitem>
<para>
인스턴스는 현재 영속 컨텍스트와 연관되어 있다. 그것은 영속 식별자(프라이머리 키 값) 그리고 아마 데이터베이스 내에 있는 대응하는
행을 갖는다. 특별한 영속 컨텍스트의 경우, Hibernate는 영속 identity가 Java identity(객체의 메모리 내 위치)와 같다는 점을
<emphasis>보증한다</emphasis>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>detached</term>
<listitem>
<para>
인스턴스는 영속 컨텍스트와 한번 연관되었지만, 그 컨텍스트가 닫혔거나, 그 인스턴스가 또 다른 프로세스로 직렬화 되었다. 그것은 영속
identity 그리고, 아마 데이터베이스 내에 대응하는 행을 갖는다. detached 인스턴스들의 경우, Hibernate는 영속 identity과
Java identity 사이의 관계를 보증하지 않는다.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect1>
<sect1 id="architecture-jmx" revision="1">
<title>JMX 통합</title>
<para>
JMX는 자바 컴포넌트 관리를 위한 J2EE 표준이다. Hibernate는 JMX 표준 서비스를 통해 관리될 수도 있다. 우리는 배포본 내에 MBean 구현,
<literal>org.hibernate.jmx.HibernateService</literal>를 제공한다.
</para>
<para>
JBoss 어플리케이션 서버 상에 Hibernae를 JMX 서비스로서 배치하는 방법에 대한 예제는 JBoss 사용자 가이드를 보길 바란다. JBoss
어플리케이션 서버 상에서, 만일 당신이 JMX를 사용하여 배치할 경우 당신은 또한 다음 이점들을 얻는다:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>Session 관리:</emphasis> Hibernate <literal>Session</literal>의 생명주기가 JTA 트랜잭션의
영역 내에 자동적으로 바인드 될 수 있다. 이것은 당신이 더이상 <literal>Session</literal>을 수작업으로 열고 닫지 않아도
됨을 의미하고, 이것은 JBoss EJB 인터셉터의 업무가 된다. 당신은 또한 더 이상 당신의 코드 어느 곳에서든 트랜잭션 경계설정에
대해 걱정하지 않아도 된다(당신이 물론 이식성 있는 영속 계층을 작성하고자 원하지 않는한, 이를 위해 옵션 Hibernate <literal>Transaction</literal>
API를 사용하라). 당신은 <literal>Session</literal>에 접근하기 위해 <literal>HibernateContext</literal>를 호출한다.
</para>
</listitem>
<listitem>
<para>
<emphasis>HAR 배치:</emphasis> 대개 당신은 JBoss 서비스 배치 디스크립터를 사용하여 Hibernate JMX 서비스를
(EAR 과/또는 SAR 파일로) 배치하고, 그것은 Hibernate <literal>SessionFactory</literal>의 통상적인 구성
옵션들 모두를 지원한다. 하지만 당신은 여전히 모든 당신의 매핑 파일들을 배치 디스크립터 속에 명명해야 한다. 만일 당신이
옵션 HAR 배치를 사용하고자 결정하는 경우, JBoss는 당신의 HAR 파일 내에 있는 모든 매핑 파일들을 자동적으로 검출해낼 것이다.
</para>
</listitem>
</itemizedlist>
<para>
이들 옵션들에 대한 추가 정보는 JBoss 어플리케이션 서버 사용자 가이드를 참조하라.
</para>
<para>
JMX 서비스로서 이용 가능한 또다른 특징은 런타임 Hibernate 통계이다.
<xref linkend="configuration-optional-statistics"/>를 보라.
</para>
</sect1>
<sect1 id="architecture-jca" revision="1">
<title>JCA 지원</title>
<para>
Hibernate는 JCA 커넥터로서 구성될 수도 있다. 상세한 것은 웹 사이트를 보길 바란다. Hibernate JCA 지원은 여전히 실험적으로
검토 중에 있음을 노트하길 바란다.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,523 @@
<chapter id="associations">
<title>연관 매핑들</title>
<sect1 id="assoc-intro" revision="1">
<title>개요</title>
<para>
연관 매핑들은 올바른 것을 얻기가 종종 가장 어려운 것이다. 이 절에서 우리는 단방향 매핑들에서 시작하고, 그런 다음 양방향 경우들을
검토함으로써, 하나씩 표준적인 경우들을 상세히 논의할 것이다. 우리는 모든 예제들에서 <literal>Person</literal>
<literal>Address</literal>를 사용할 것이다.
</para>
<para>
우리는 연관들을 중재하는 join 테이블로 매핑시킬 것인지 여부에 따라, 그리고 multiplicity(다중성)에 따라 연관들을 분류할 것이다.
</para>
<para>
null 허용 가능한 foreign 키들은 전통적인 데이터 모델링에서 좋은 실례로 간주되지 않아서, 모든 우리의 예제들은 not null
foreign 키들을 사용한다. 이것은 Hibernate에서 필수가 아니고, 당신이 null 허용 가능 컨스트레인트들을 드롭시킬 경우 매핑들은
모두 동작할 것이다.
</para>
</sect1>
<sect1 id="assoc-unidirectional" revision="1">
<title>단방향 연관들</title>
<sect2 id="assoc-unidirectional-m21">
<title>many to one</title>
<para>
<emphasis>단방향 many-to-one 연관</emphasis>은 가장 공통적인 종류의 단방향 연관이다.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key,
addressId bigint not null )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-121">
<title>one to one</title>
<para>
<emphasis>foreign 키에 대한 단방향 one-to-one 연관은 대개 아주 동일하다.</emphasis> 유일한 차이점은
컬럼 유일(unique) 컨스트레인트이다.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key,
addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
<para>
<emphasis>하나의 프라이머리 키에 대한 단방향 one-to-one 연관</emphasis>은 대개 특별한 id 생성기를 사용한다.
(이 예제에서 연관의 방향이 역전되었음을 주목하라.)
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<one-to-one name="person" constrained="true"/>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table Address ( personId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-12m">
<title>one to many</title>
<para>
<emphasis>하나의 foreign 키에 대한 단방향 one-to-many 연관</emphasis>은 매우 색다른 경우이고, 실제로 권장되지 않는다.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses">
<key column="personId"
not-null="true"/>
<one-to-many class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table Address ( addressId bigint not null primary key,
personId bigint not null )
]]></programlisting>
<para>
우리는 이런 종류의 연관에 대해 하나의 join 테이블을 사용하는 것이 더 좋다고 생각한다.
</para>
</sect2>
</sect1>
<sect1 id="assoc-unidirectional-join" revision="1">
<title>join 테이블들에 대한 단방향 연관들</title>
<sect2 id="assoc-unidirectional-join-12m">
<title>one to many</title>
<para>
<emphasis>하나의 join 테이블에 대한 단방향 one-to-many 연관</emphasis>이 보다 더 선호된다. <literal>unique="true"</literal>
지정함으로써 우리는 many-to-many에서 one-to-many로 아중성(multiplicity)를 변경시켰음을 주목하라.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId not null,
addressId bigint not null primary key )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-join-m21">
<title>many to one</title>
<para>
<emphasis>하나의 join 테이블에 대한 단방향 many-to-one 연관</emphasis>은 그 연관이 선택적일 때 매우 공통적이다.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId" unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key,
addressId bigint not null )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-join-121">
<title>one to one</title>
<para>
<emphasis>하나의 join 테이블에 대한 단방향 one-to-one 연관</emphasis>은 극히 통상적이지 않지만 가능하다.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId"
unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key,
addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-join-m2m">
<title>many to many</title>
<para>
마지막으로, 우리는 <emphasis>단방향 many-to-many 연관</emphasis>을 갖는다.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null,
addressId bigint not null,
primary key (personId, addressId) )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
</sect1>
<sect1 id="assoc-bidirectional" revision="1">
<title>양방향 연관들</title>
<sect2 id="assoc-bidirectional-m21">
<title>one to many / many to one</title>
<para>
<emphasis>양방향 many-to-one 연관</emphasis>은 가장 공통된 종류의 연관이다.(이것은 표준 부모/자식 관계이다. )
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true">
<key column="addressId"/>
<one-to-many class="Person"/>
</set>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key,
addressId bigint not null )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-bidirectional-121">
<title>one to one</title>
<para>
<emphasis>foreign에 대한 양방향 one-to-one 연관</emphasis>은 꽤 공통적이다.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<one-to-one name="person"
property-ref="address"/>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key,
addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
<para>
<emphasis>하나의 프라이머리 키에 대한 양방향 one-to-one 연관</emphasis>은 특별한 id 생성기를 사용한다.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<one-to-one name="address"/>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<one-to-one name="person"
constrained="true"/>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table Address ( personId bigint not null primary key )
]]></programlisting>
</sect2>
</sect1>
<sect1 id="assoc-bidirectional-join" revision="1">
<title>join 테이블들에 대한 양방향 연관들</title>
<sect2 id="assoc-bidirectional-join-12m">
<title>one to many / many to one</title>
<para>
<emphasis>하나의 join 테이블에 대한 양방향 one-to-many 연관</emphasis>. <literal>inverse="true"</literal>
연관의 어느 쪽 끝이든 콜렉션 측으로 또는 join 측으로 갈 수 있다.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses"
table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
inverse="true"
optional="true">
<key column="addressId"/>
<many-to-one name="person"
column="personId"
not-null="true"/>
</join>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null,
addressId bigint not null primary key )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-bidirectional-join-121">
<title>one to one</title>
<para>
<emphasis>하나의 join 테이블에 대한 양방향 one-to-one 연관</emphasis>은 극히 통상적이지 않지만, 가능하다.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId"
unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true"
inverse="true">
<key column="addressId"
unique="true"/>
<many-to-one name="address"
column="personId"
not-null="true"
unique="true"/>
</join>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key,
addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-bidirectional-join-m2m">
<title>many to many</title>
<para>
마지막으로, 우리는 하나의 <emphasis>양방향 many-to-many 연관</emphasis>을 갖는다.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true">
<key column="addressId"/>
<many-to-many column="personId"
class="Person"/>
</set>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null,
addressId bigint not null,
primary key (personId, addressId) )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,178 @@
<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">
<title>대량 update/delete</title>
<para>
이미 논의했듯이, 자동적이고 투명한 객체/관계형 매핑은 객체 상태에 대한 관리에 관계된다. 이것은 객체 상태가 메모리 내에서 이용 가능함을
의미하므로, (SQL <literal>UPDATE</literal><literal>DELETE</literal>를 사용하여) 데이터베이스 내에서 직접 데이터를
업데이트하거나 삭제하는 것은 메모리 내 상태에 영향을 주지 않을 것이다. 하지만 Hibernate는 Hibernate Query Language를 통해
수행되는 대량 SQL-스타일의 <literal>UPDATE</literal><literal>DELETE</literal> 문장 실행을 위한 방법들을 제공한다.
(<xref linkend="queryhql">HQL</xref>).
</para>
<para>
<literal>UPDATE</literal><literal>DELETE</literal> 문장들의 유사-구문은 다음과 같다:
<literal>( UPDATE | DELETE ) FROM? ClassName (WHERE WHERE_CONDITIONS)?</literal>. 노트할 몇 가지 :
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
from-절에서, FROM 키워드는 옵션이다
</para>
</listitem>
<listitem>
<para>
from-절 내에 명명된 한 개의 클래스가 오직 존재할 수 있고, alias를 가질 수 <emphasis>없다</emphasis>.
</para>
</listitem>
<listitem>
<para>
join들은 (함축적이든 명시적이든) 대량 HQL 질의 속에 지정될 수 없다. 서브-질의들이 where-절 속에서 사용될 수 있다.
</para>
</listitem>
<listitem>
<para>
where-절 또한 옵션이다.
</para>
</listitem>
</itemizedlist>
<para>
하나의 예제로서, 한 개의 HQL <literal>UPDATE</literal>를 실행하기 위해, <literal>Query.executeUpdate()</literal>
메소드를 사용하라:
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
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> 메소드를
사용하라(그 메소드는 JDBC의 <literal>PreparedStatement.executeUpdate()</literal>에 친숙한 사람들을 위해 명명되어 있다):
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
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 로드맵을 참조하라.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,204 @@
<chapter id="best-practices" revision="3">
<title>최상의 실전 경험들</title>
<variablelist spacing="compact">
<varlistentry>
<term>fine-grained 클래스들을 작성하고 <literal>&lt;component&gt;</literal>를 사용하여 그것들을 매핑하라. </term>
<listitem>
<para>
<literal>street</literal>, <literal>suburb</literal>, <literal>state</literal>, <literal>postcode</literal>
캡슐화 시키는데 <literal>Address</literal> 클래스를 사용하라. 이것은 코드 재사용성을 촉진시키고 리팩토링을 단순화 시킨다.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>영속 클래스들에 대해 식별자 프로퍼티들을 선언하라.</term>
<listitem>
<para>
Hibernate는 식별자 프로퍼티들을 옵션으로 만든다. 왜 우리가 그것들을 사용해야 하는가에 대한 모든 종류의 이유들이 존재한다.
우리는 식별자들이 '합성(synthetic)'이 되는(비지니스 의미 없이 생성되는) 것을 권장한다.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>고유 키들을 식별하라.</term>
<listitem>
<para>
모든 엔티티들에 대해 고유 키들을 식별하고, <literal>&lt;natural-id&gt;</literal>를 사용하여 그것들을 매핑하라.
고유 키를 구성하는 프로퍼티들을 비교하기 위해 <literal>equals()</literal><literal>hashCode()</literal>
구현하라.
</para>
</listitem>
</varlistentry>
<varlistentry><term>각각의 클래스 매핑을 그것 자신의 파일 내에 위치지워라.</term>
<listitem>
<para>
하나의 한덩어리 매핑 문서를 사용하지 말라. <literal>com/eg/Foo.hbm.xml</literal> 파일 속에
<literal>com.eg.Foo</literal>를 매핑하라. 이것은 팀 환경에서 특히 좋은 의미를 준다.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>매핑들을 리소스들로서 로드시켜라.</term>
<listitem>
<para>
그것들이 매핑하는 클래스들에 따라서 매핑들을 배치하라
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>질의 문자열들을 객관화 시키는 것을 고려하라.</term>
<listitem>
<para>
당신의 질의들이 ANSI 표준이 아닌 SQL 함수들을 호출하는 경우는 좋은 실전연습이다. 질의 문자열들을 매핑 파일들에다가
외부화 시키는 것은 어플리케이션을 보다 이식성 있도록 만들어줄 것이다.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>바인드 변수들을 사용하라.</term>
<listitem>
<para>
JDBC에서처럼, 상수 아닌 값들을 "?"로 대체시켜라. 질의 속에 상수 아닌 값을 바인드 시키는데 문자열 처리를 결코
사용하지 말라! 더 좋게는 질의들 속에 명명된 파라미터들을 사용하는 것을 고려하라.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>당신 자신의 JDBC 커넥션들을 관리하지 말라.</term>
<listitem>
<para>
Hibernate는 어플리케이션으로 하여금 JDBC 커넥션들을 관리하도록 한다. 이 접근법은 마지막 수단으로서 고려되어야 한다.
만일 당신이 미리 만들어진 커넥션 프로바이더들을 사용할 수 없을 경우, <literal>org.hibernate.connection.ConnectionProvider</literal>
대한 당신 자신의 구현을 제공하는 것을 고려하라.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>맞춤형 타입 사용을 고려하라.</term>
<listitem>
<para>
당신이 자바 타입을 갖고 있고, 어떤 라이브러리로부터 말하고, 그것이 영속화 될 필요가 있지만 그것을 컴포넌트로서 매핑시키는데
필요한 accessor들을 제공할 필요가 없다고 가정하자. 당신은 <literal>org.hibernate.UserType</literal>을 구현하는
것을 고려해야 할 것이다. 이 접근법은 Hibernate 타입으로/으로부터 변환들을 구현하는 것으로부터 어플리케이션 코드를 자유롭게
해준다.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>병목 지점들에서 수작업으로 코딩된 JDBC를 사용하라.</term>
<listitem>
<para>
시스템의 퍼포먼스가 중대한 영역들에서, 몇몇 종류의 오퍼레이션들은 직접적인 JDBC에서 이득을 본다. 그러나 당신이 어떤 것이 병목인지를
<emphasis>알기</emphasis> 전까지 기다리길 바란다. 그리고 직접적인 JDBC가 반드시 더 빠르다고 가정하지 말라. 만일 당신이 직접적인
JDBC를 사용할 필요가 있을 경우, Hibernate <literal>Session</literal>을 열고 그 SQL 커넥션을 사용할 가치가 있다. 그 방법으로
당신은 동일한 트랜잭션 방도와 기본 커넥션 프로바이더를 여전히 사용할 수 있다
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>Session</literal> flushing을 이해하라.</term>
<listitem>
<para>
시간이 지남에 따라 Session은 그것의 영속 상태를 데이터베이스와 동기화 시킨다. 만일 이 과정이 너무 자주 발생할 경우 퍼포먼스가
영향을 받을 것이다. 당신은 때때로 자동적인 flushing을 사용 불가능하게 만들거나 특정 트랜잭션 내에서 질의들의 순서와 다른
오퍼레이션들의 순서를 변경시켜서 불필요한 flushing을 최소화 시킬 수 있다.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>3-tier 아키텍처에서, <literal>saveOrUpdate()</literal> 사용을 고려하라.</term>
<listitem>
<para>
servlet / session 빈 아키텍처를 사용할 때, 당신은 sesson bean 내에 로드된 영속 객체들을 서블릿/JSP 계층으로/으로부터
전달할/받을 수 있다. 각각의 요청을 서비스하는데 새로운 세션을 사용하라. 객체들을 데이터베이스와 동기화 시키기 위해서
<literal>Session.merge()</literal> 또는 <literal>Session.saveOrUpdate()</literal>를 사용하라.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>2-tier 아키텍처에서, 수명이 긴 영속 컨텍스트들을 사용하는 것을 고려하라.</term>
<listitem>
<para>
데이터베이스 트랜잭션들은 최상의 가용성을 위해 가능한 한 짧아야 한다. 하지만 장기간 실행되는 <emphasis>어플리케이션 트랜잭션들</emphasis>,
사용자의 뷰 관점에서 한 개의 단위 작업을 구현하는 것이 가끔 필수적이다. 하나의 어플리케이션 트랜잭션은 몇 개의 클라이언트 요청들과
응답 주기들에 걸칠 수도 있다. 어플리케이션 트랜잭션들을 구현하는데 detached 객체들을 사용하는 것이 공통적이다. 2-티어 아키텍처에서
매우 적절한 대안은 어플리케이션 트랜잭션의 전체 생명주기 동안에 한 개의 열려진 영속 접속 (세션)을 유지하는 것이고 각각의 요청의 끝에서
JDBC 커넥션을 간단하게 연결해제하고 차후의 요청의 시작 시에 다시 연결하는 것이다. 한 개 이상의 어플리케이션 트랜잭션을 가로질러서
하나의 단일 세션을 결코 공유하지 말라. 공유할 경우에 당신은 실효성이 없는 데이터로 작업하게 될 것이다.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>예외상황들을 복구가능한 것으로서 다루지 말라.</term>
<listitem>
<para>
이것은 "최상의" 실전이 아닌 보다 필수적인 실전이다. 예외상황이 발생할 때, <literal>Transaction</literal>을 롤백시키고
<literal>Session</literal>을 닫아라. 만일 당신이 그렇게 하지 않을 경우, Hibernate는 메모리 내 상태가 영속 상태를 정확하게
표현하는 것을 보증할 수 없다. 이 특별한 경우처럼, 만일 주어진 식별자를 가진 인스턴스가 데이터베이스 상에 존재하는지 여부를 결정하는데
<literal>Session.load()</literal>를 사용하지 말라; 대신에 <literal>Session.get()</literal> 또는 하나의 질의를
사용하라.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>연관들에 대한 lazy 페칭을 선호하라.</term>
<listitem>
<para>
eager 페칭을 관대하게 사용하라. second-level 캐시 내에 완전하게 보관되지 않을 것 같은 클래스들에 대한 대붑분의 연관들에
대해 프락시들과 lazy 콜렉션들을 사용하라. 캐시된 클래스들에 대한 연관들의 경우, 이곳은 캐시 성공의 매우 높은 확률이 존재하는
곳이며, <literal>lazy="false"</literal>를 사용하여 eager 페칭을 명시적으로 사용 불가능하게 하라. 한의 join 페칭이
특정 쓰임새에 대해 적절할 때, 하나의 <literal>left join fetch</literal>를 가진 질의를 사용하라.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
페치되지 않은 데이터 문제점들을 피하기 위해 <emphasis>뷰 내에 열려진 세션(open session in view)</emphasis> 패턴 또는
하나의 정리된 <emphasis>어셈블리 단계(assembly phase)</emphasis>를 사용하라.
</term>
<listitem>
<para>
Hibernate는 개발자들이 <emphasis>Data Transfer Objects</emphasis> (DTO)를 지루하게 작성하는 것으로부터
자유롭게 해준다. 전통적인 EJB 아키텍처에서, DTO는 이중 용도로 기능한다: 첫 번째로 그것들은 엔티티 빈즈가 직렬화 가능하지 않는
문제점에 대해 착수한다; 두 번째로 그것들은 뷰에 의해 사용되는 모든 데이터가 프리젠테이션 티어로 컨트롤을 반환하기 전에 DTO들 속으로
페치되고 마샬링되는 어셈블리 단계를 암묵적으로 정의한다. Hibernate는 첫 번째 용도를 제거시킨다. 하지만 당신이 뷰 렌더링 프로세스를
가로질러 열려져 있는 영속 컨텍스트(세션)을 보관할 준비가 되어 있지 않는 한, 당신은 여전히 어셈블리 단계를 필요로 할 것이다(detached
객체들에서 이용가능한 데이터가 무엇인지에 대해 프리젠테이션 티어와 엄격하게 계약을 갖도록 당신의 비지니스 메소드들을 고려하라)
이것은 Hibernate의 한계점이 아니다! 그것은 안전한 트랜잭션 데이터 접근의 필수 조건이다.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Hibernate에서 당신의 비지니스 로직을 추상화 시키는 것을 고려하라.</term>
<listitem>
<para>
(Hibernate) 데이터 액세스 코드를 인터페이스 이면에 은폐시켜라. <emphasis>DAO</emphasis>
<emphasis>Thread Local Session</emphasis> 패턴들을 결합시켜라. 당신은 심지어 <literal>UserType</literal>
통해 Hibernate에 연관된, 수작업으로 코딩된 JDBC로서 몇몇 클래스들을 영속화 시킬 수도 있다. (이 충고는 "충분히 큰"
어플리케이션들에 대한 것이다; 그것은 5개의 테이블들을 가진 어플리케이션에 대해서는 적절하지 않다!)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>신종의 연관 매핑을 사용하지 말라.</term>
<listitem>
<para>
실제의 many-to-many 연관들에 대한 좋은 쓰임새들은 드물다. 대부분의 시간 동안 당신은 "연결 테이블" 내에 저장된 추가적인 정보를 필요로 한다.
이 경우에, 매개하는 연결 클래스에 대해 두 개의 one-to-many 연관들을 사용하는 것이 훨씬 더 좋다. 사실 우리는 대부분의 연관들이 one-to-many와
many-to-one이라고 생각하며, 당신은 다른 연관 스타일을 사용할 때 주의해야 하고 그것이 진정 필수적인지를 당신 스스로 질문하라.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>양방향 연관들을 선호하라.</term>
<listitem>
<para>
단방향 연관들은 질의하기가 더 어렵다. 많은 어플리케이션에서, 거의 모든 연관들은 질의들 내에서 양 방향으로 네비게이트 가능해야 한다.
</para>
</listitem>
</varlistentry>
</variablelist>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,374 @@
<chapter id="components">
<title>Component 매핑</title>
<para>
<emphasis>component</emphasis>의 개념은 Hibernate에서 다른 용도로 몇몇 다른 컨텍스트들 내에서 재사용된다.
</para>
<sect1 id="components-dependentobjects">
<title>종속 객체들</title>
<para>
하나의 컴포넌트는 엔티티 참조가 아닌, value 타입으로서 영속화 되는 하나의 포함된 객체이다. "컴포넌트" 용어는
(아키텍처 수준의 컴포넌트들이 아닌) composition(구성,합성)에 대한 객체-지향적인 개념을 언급한다.
예를 들어 당신은 다음과 같이 개인을 모형화 시킬 수도 있다:
</para>
<programlisting><![CDATA[public class Person {
private java.util.Date birthday;
private Name name;
private String key;
public String getKey() {
return key;
}
private void setKey(String key) {
this.key=key;
}
public java.util.Date getBirthday() {
return birthday;
}
public void setBirthday(java.util.Date birthday) {
this.birthday = birthday;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
......
......
}]]></programlisting>
<programlisting><![CDATA[public class Name {
char initial;
String first;
String last;
public String getFirst() {
return first;
}
void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
void setLast(String last) {
this.last = last;
}
public char getInitial() {
return initial;
}
void setInitial(char initial) {
this.initial = initial;
}
}]]></programlisting>
<para>
이제 <literal>Name</literal><literal>Person</literal>의 컴포넌트로서 영속화 될 수도 있다. <literal>Name</literal>
그것의 영속 프로퍼티들에 대한 getter 메소드와 setter 메소드를 정의하지만, 어떤 인터페이스들이나 식별자 프로퍼티들을 선언하는 것을
필요로 하지 않음을 주목하라.
</para>
<para>
우리의 Hibernate 매핑은 다음과 같을 것이다:
</para>
<programlisting><![CDATA[<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid.hex"/>
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name"> <!-- class attribute optional -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
</class>]]></programlisting>
<para>
person 테이블은
<literal>pid</literal>,
<literal>birthday</literal>,
<literal>initial</literal>,
<literal>first</literal>,
<literal>last</literal> 컬럼들을 가질 것이다.
</para>
<para>
모든 값(value) 타입들처럼, 컴포넌트들은 공유된 참조들을 지원하지 않는다. 달리 말해, 두 명의 개인들은 동일한 이름을 가질 수
있지만, 두 개의 person 객체들은 오직 값 만이 "동일한" 두 개의 독립적인 name 객체들을 포함할 것이다. 컴포넌트의 null 값 의미는
<emphasis>특별한 용도를 위한 것이다</emphasis>. 포함된 객체를 다시 로드시킬 때, Hibernate는 모든 컴포넌트 컬럼들이
null일 경우에 전체 컴포넌트가 null이라고 가정할 것이다. 이것은 대부분의 용도에 맞을 것이다.
</para>
<para>
컴포넌트의 프로퍼티들은 임의의 Hibernate 타입일 수 있다(콜렉션들, many-to-one 연관들, 다른 컴포넌트들, 기타).
내포된 컴포넌트들은 신종의 사용례로 간주되지 <emphasis>않을</emphasis> 것이다. Hibernate는 매우 잘 정제된 객체 모형을
지원하도록 고안되어있다.
</para>
<para>
<literal>&lt;component&gt;</literal> 요소는 컴포넌트 클래스의 프로퍼티를 포함되는 엔티티에 대한 역 참조로서 매핑시키는
<literal>&lt;parent&gt;</literal> 서브요소를 허용한다.
</para>
<programlisting><![CDATA[<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid.hex"/>
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name" unique="true">
<parent name="namedPerson"/> <!-- reference back to the Person -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
</class>]]></programlisting>
</sect1>
<sect1 id="components-incollections" revision="1">
<title>종속 객체들을 가진 콜렉션들</title>
<para>
컴포넌트들을 가진 콜렉션들이 지원된다(예를 들면 <literal>Name</literal> 타입을 가진 배열).
<literal>&lt;element&gt;</literal> 태그를 <literal>&lt;composite-element&gt;</literal> 태그로
대체시켜서 당신의 컴포넌트 콜렉션을 선언하라.
</para>
<programlisting><![CDATA[<set name="someNames" table="some_names" lazy="true">
<key column="id"/>
<composite-element class="eg.Name"> <!-- class attribute required -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</composite-element>
</set>]]></programlisting>
<para>
노트: 만일 당신이 composite 요소를 가진 하나의 <literal>Set</literal>를 정의할 경우, <literal>equals()</literal>
<literal>hashCode()</literal>를 정확하게 구현하는 것이 매우 중요하다.
</para>
<para>
Composite 요소들은 컴포넌트들을 포함하지만 콜렉션들을 포함하지 않는다. 만일 당신의 composite 요소 자체가 컴포넌트들을 포함할
경우, <literal>&lt;nested-composite-element&gt;</literal> 태그를 사용하라. 이것은 꽤 신종의 경우-그것들 자체가
컴포넌트들을 갖고 있는 컴포넌트들의 콜렉션-이다. 이 단계에서 당신은 one-to-many 연관이 더 적절한지를 당신 스스로에게 질문하게
될 것이다. 하나의 엔티티로서 composite 요소를 다시 모델링하려고 시도하라 - 그러나 자바 모형들이 동일할지라도,
관계형 모형과 영속화 의미들은 여전히 약간 다르다.
</para>
<para>
당신이 하나의 <literal>&lt;set&gt;</literal>을 사용 중이라면, 하나의 composite 요소 매핑은 null 가능한 프로퍼티들을
지원하지 않음을 노트하길 바란다. Hibernate는 객체들을 삭제할 때 하나의 레코드를 식별하는데 각각의 컬럼들 값을 사용해야 하며
(composite 요소 테이블 내에 별도의 프라이머리 키 컬럼이 존재하지 않는다), 그것은 null 값들에 대해서는 불가능하다. 당신은
하나의 composite-요소 내에 not-null 프로퍼티들 만을 사용해야 하거나 하나의 <literal>&lt;list&gt;</literal>,
<literal>&lt;map&gt;</literal>, <literal>&lt;bag&gt;</literal> 또는 <literal>&lt;idbag&gt;</literal>
선택해야 한다.
</para>
<para>
composite 요소에 대한 하나의 특별한 경우는 내포된 <literal>&lt;many-to-one&gt;</literal> 요소를 가진 composite 요소이다.
이같은 매핑은 many-to-many 연관 테이블의 특별한 컬럼들을 composite 요소 클래스로 매핑시키는 것을 당신에게 허용해준다. 다음은
<literal>Order</literal>로부터 <literal>Item</literal>으로의 many-to-many 연관이다. 여기서
<literal>purchaseDate</literal>, <literal>price</literal>, 그리고 <literal>quantity</literal>는 연관의
프로퍼티들이다:
</para>
<programlisting><![CDATA[<class name="eg.Order" .... >
....
<set name="purchasedItems" table="purchase_items" lazy="true">
<key column="order_id">
<composite-element class="eg.Purchase">
<property name="purchaseDate"/>
<property name="price"/>
<property name="quantity"/>
<many-to-one name="item" class="eg.Item"/>
<!-- class attribute is optional -->
</composite-element>
</set>
</class>]]></programlisting>
<para>
물론, 양방향 연관 네비게이션의 경우, 다른 측 상에 있는 purchase에 대한 참조가 존재할 수 없다. 컴포넌트들이 값(value) 타입들이고
공유된 참조들을 허용하지 않음을 기억하라. 하나의 <literal>Purchase</literal><literal>Order</literal>를 가진 set
내에 있을 수 있지만, 그것은 동시에 <literal>Item</literal>에 의해 참조될 수 없다.
</para>
<para>심지어 세겹의(또는 네 겹의, 기타) 연관들이 가능하다:</para>
<programlisting><![CDATA[<class name="eg.Order" .... >
....
<set name="purchasedItems" table="purchase_items" lazy="true">
<key column="order_id">
<composite-element class="eg.OrderLine">
<many-to-one name="purchaseDetails class="eg.Purchase"/>
<many-to-one name="item" class="eg.Item"/>
</composite-element>
</set>
</class>]]></programlisting>
<para>
composite 요소들은 다른 엔티티들에 대한 연관들과 동일한 구문을 사용하여 질의들 내에 나타날 수도 있다.
</para>
</sect1>
<sect1 id="components-asmapindex">
<title>Map 인덱스들로서 컴포넌트들</title>
<para>
<literal>&lt;composite-map-key&gt;</literal> 요소는 당신에게 하나의 컴포넌트 클래스를 하나의 <literal>Map</literal>
키로서 매핑시키도록 한다. 당신은 컴포넌트 클래스 상에서 <literal>hashCode()</literal><literal>equals()</literal>
정확하게 오버라이드 시키도록 하라.
</para>
</sect1>
<sect1 id="components-compositeid" revision="1">
<title>composite 식별자들로서 컴포넌트들</title>
<para>
당신은 하나의 컴포넌트를 하나의 엔티티 클래스에 대한 하나의 식별자로서 사용할 수도 있다. 당신의 컴포넌트 클래스는 어떤 사양들을
충족시켜야 한다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
그것은 <literal>java.io.Serializable</literal>을 구현해야 한다.
</para>
</listitem>
<listitem>
<para>
그것은 composite 키 등가(equality)에 대한 데이터베이스 개념과 일치되게, <literal>equals()</literal>
<literal>hashCode()</literal>를 다시 구현해야 한다.
</para>
</listitem>
</itemizedlist>
<para>
<emphasis>노트: Hibernate3에서, 두 번째 사양은 Hibernate의 절대적으로 엄격한 사양이 아니다.
그러나 아무튼 그것을 행하라.</emphasis>
</para>
<para>
당신은 compsite 키들을 생성시키는데 <literal>IdentifierGenerator</literal>를 사용할 수 없다. 대신에 어플리케이션은
그것 자신의 식별자들을 할당해야 한다.
</para>
<para>
통상의 <literal>&lt;id&gt;</literal> 선언 위치에 (내포된 <literal>&lt;key-property&gt;</literal> 요소들을 가진)
<literal>&lt;composite-id&gt;</literal> 태그를 사용하라. 예를 들어, <literal>OrderLine</literal> 클래스는
<literal>Order</literal>의 (composite) 프라이머리 키에 의존하는 프라이머리 키를 갖는다.
</para>
<programlisting><![CDATA[<class name="OrderLine">
<composite-id name="id" class="OrderLineId">
<key-property name="lineId"/>
<key-property name="orderId"/>
<key-property name="customerId"/>
</composite-id>
<property name="name"/>
<many-to-one name="order" class="Order"
insert="false" update="false">
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>
....
</class>]]></programlisting>
<para>
이제 <literal>OrderLine</literal> 테이블을 참조하는 임의의 foreign 키들이 또한 compsite이다. 당신은 다른 클래스들에 대한
당신의 매핑들 속에 이것을 선언해야 한다. <literal>OrderLine</literal>에 대한 하나의 연관은 다음과 같이 매핑될 것이다:
</para>
<programlisting><![CDATA[<many-to-one name="orderLine" class="OrderLine">
<!-- the "class" attribute is optional, as usual -->
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>]]></programlisting>
<para>
(<literal>&lt;column&gt;</literal> 태그가 모든 곳에서 <literal>column</literal> 속성에 대한 대안임을 노트하라.)
</para>
<para>
<literal>OrderLine</literal>에 대한 <literal>many-to-many</literal> 연관은 또한 composite foreign 키를 사용한다:
</para>
<programlisting><![CDATA[<set name="undeliveredOrderLines">
<key column name="warehouseId"/>
<many-to-many class="OrderLine">
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-many>
</set>]]></programlisting>
<para>
<literal>Order</literal>에서 <literal>OrderLine</literal>들의 콜렉션이 사용될 것이다:
</para>
<programlisting><![CDATA[<set name="orderLines" inverse="true">
<key>
<column name="orderId"/>
<column name="customerId"/>
</key>
<one-to-many class="OrderLine"/>
</set>]]></programlisting>
<para>
(통상적으로 <literal>&lt;one-to-many&gt;</literal> 요소는 컬럼들을 선언하지 않는다.)
</para>
<para>
만일 <literal>OrderLine</literal> 자체가 하나의 콜렉션을 소유할 경우, 그것은 또한 하나의 composite foreign 키를 갖는다.
</para>
<programlisting><![CDATA[<class name="OrderLine">
....
....
<list name="deliveryAttempts">
<key> <!-- a collection inherits the composite key type -->
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</key>
<list-index column="attemptId" base="1"/>
<composite-element class="DeliveryAttempt">
...
</composite-element>
</set>
</class>]]></programlisting>
</sect1>
<sect1 id="components-dynamic" revision="1">
<title>동적인 컴포넌트들</title>
<para>
당신은 <literal>Map</literal> 타입의 프로퍼티를 매핑시킬 수도 있다:
</para>
<programlisting><![CDATA[<dynamic-component name="userAttributes">
<property name="foo" column="FOO"/>
<property name="bar" column="BAR"/>
<many-to-one name="baz" class="Baz" column="BAZ_ID"/>
</dynamic-component>]]></programlisting>
<para>
<literal>&lt;dynamic-component&gt;</literal> 매핑의 의미는 <literal>&lt;component&gt;</literal>와 동일하다.
이런 종류의 매핑의 장점은 배치 시에 단지 매핑 문서를 편집함으로써 그 bean의 실제 프로퍼티들을 결정하는 가용성이다. 매핑 문서에
대한 런타임 처리는 또한 DOM 파서를 사용하여 가능하다. 더 좋게는 당신이 <literal>Configuration</literal> 객체를 통해
Hibernate의 구성-시 메타모형에 접근할 수 있다(그리고 변경시킬 수 있다)
</para>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,219 @@
<chapter id="events">
<title>인터셉터들과 이벤트들</title>
<para>
어플리케이션이 Hibernate 내부에서 발생하는 어떤 이벤트들에 대해 반응하는 것에 흔히 유용하다. 이것은 어떤 종류의 일반적인 기능,
그리고 Hibernate의 확장 기능의 구현을 허용해준다.
</para>
<sect1 id="objectstate-interceptors" revision="1">
<title>인터셉터들</title>
<para>
<literal>Interceptor</literal> 인터페이스는 영속 객체가 저장되고, 업데이트되고, 삭제되거나 로드되기 전에 영속 객체의
프로퍼티들을 조사하고/하거나 처리하는 것을 어플리케이션에 허용해줌으로써 세션으로부터 어플리케이션으로의 콜백들을 제공한다.
이것에 대한 한 가지 가능한 사용은 감사 정보를 추적하는 것이다. 예를 들어, 다음 <literal>Interceptor</literal>
<literal>Auditable</literal>이 생성될 때 <literal>createTimestamp</literal>를 자동적으로 설정하고
<literal>Auditable</literal>이 업데이트될 때 <literal>lastUpdateTimestamp</literal> 프로퍼티를 업데이트 한다.
</para>
<programlisting><![CDATA[package org.hibernate.test;
import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.Interceptor;
import org.hibernate.type.Type;
public class AuditInterceptor implements Interceptor, Serializable {
private int updates;
private int creates;
public void onDelete(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
// do nothing
}
public boolean onFlushDirty(Object entity,
Serializable id,
Object[] currentState,
Object[] previousState,
String[] propertyNames,
Type[] types) {
if ( entity instanceof Auditable ) {
updates++;
for ( int i=0; i < propertyNames.length; i++ ) {
if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
currentState[i] = new Date();
return true;
}
}
}
return false;
}
public boolean onLoad(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
return false;
}
public boolean onSave(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if ( entity instanceof Auditable ) {
creates++;
for ( int i=0; i<propertyNames.length; i++ ) {
if ( "createTimestamp".equals( propertyNames[i] ) ) {
state[i] = new Date();
return true;
}
}
}
return false;
}
public void postFlush(Iterator entities) {
System.out.println("Creations: " + creates + ", Updates: " + updates);
}
public void preFlush(Iterator entities) {
updates=0;
creates=0;
}
...
}]]></programlisting>
<para>
세션이 생성될 때 인터셉터가 지정될 것이다.
</para>
<programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
<para>
당신은 또한 <literal>Configuration</literal>을 사용하여 인터셉터를 전역 레벨 상에 설정할 수도 있다:
</para>
<programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
</sect1>
<sect1 id="objectstate-events" revision="2">
<title>이벤트 시스템</title>
<para>
만일 당신이 당신의 영속 계층에서 특별한 이벤트들에 대해 반응해야 한다면, 당신은 또한 Hibernate3 <emphasis>이벤트</emphasis>
아키텍처를 사용할 수도 있다. 이벤트 시스템은 부가물로 사용될 수 있거나 인터셉터들에 대한 대체물로 사용될 수 있다.
</para>
<para>
본질적으로 <literal>Session</literal> 인터페이스의 모든 메소드들은 이벤트와 서로 관련되어 있다. 당신은 <literal>LoadEvent</literal>,
<literal>FlushEvent</literal>, 등을 갖는다 (정의된 이벤트 타입들의 전체 리스트에 대해서는 XML 구성 파일 DTD 또는
<literal>org.hibernate.event</literal> 패키지를 참조하라). 하나의 요청이 이들 메소드들 중 하나에 의해 만들어질 때,
Hibernate <literal>Session</literal>은 적절한 이벤트를 생성시키고 그것을 그 타입에 대한 구성된 이벤트 리스너에게 전달한다.
박싱없이, 이들 리스너들은 그들 메소드들이 항상 귀결되었던 동일한 프로세싱을 구현한다. 하지만 당신이 리스너 인터페이스들 중 하나의 맞춤을
구현하는 것이 자유롭고(예를 들어 <literal>LoadEvent</literal><literal>LoadEventListener</literal> 인터페이스의
등록된 구현에 의해 처리된다), 그 경우에 그들 구현은 <literal>Session</literal>에 대해 행해진 임의의 <literal>load()</literal>
요청들을 처리할 책임이 있을 것이다.
</para>
<para>
리스너들은 효율적이게끔 싱글톤(singleton)들로 간주되어야 할 것이다; 이것은 그것들이 요청들 사이에서 공유되고, 따라서 임의의 상태를
인스턴스 변수들로서 저장하지 말아야 함을 의미한다.
</para>
<para>
맞춤형 리스너는 그것이 편의적인 기저 클래스들(또는 리스너들이 이 용도로 final이 아닌 것으로 선언되므로 Hibernate
out-of-the-box에 의해 사용된 디폴트 이벤트 리스너들) 중 하나를 처리하고자 그리고/또는 확장하고자 원하는 이벤트들에 대해
적절한 인터페이스를 구현해야 한다. 맞춤형 리스너들은 <literal>Configuration</literal> 객체를 통해 프로그램 상으로
등록될 수 있거나, Hibernate 구성 XML 속에 지정될 수 있다 (properties 파일을 통한 선언적인 구성은 지원되지 않는다).
다음은 맞춤형 load 이벤트 리스너에 대한 예제이다:
</para>
<programlisting><![CDATA[public class MyLoadListener extends DefaultLoadEventListener {
// this is the single method defined by the LoadEventListener interface
public Object onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
throws HibernateException {
if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
throw MySecurityException("Unauthorized access");
}
return super.onLoad(event, loadType);
}
}]]></programlisting>
<para>
당신은 또한 디폴트 리스너 대신에 해당 리스너를 사용하도록 Hibernate에게 알려주는 구성 엔트리를 필요로 한다:
</para>
<programlisting><![CDATA[<hibernate-configuration>
<session-factory>
...
<listener type="load" class="MyLoadListener"/>
</session-factory>
</hibernate-configuration>]]></programlisting>
<para>
대신에 당신은 그것을 프로그래밍 방식으로 등록할 수도 있다:
</para>
<programlisting><![CDATA[Configuration cfg = new Configuration();
cfg.getSessionEventListenerConfig().setLoadEventListener( new MyLoadListener() );]]></programlisting>
<para>
선언적으로 등록된 리스너들은 인스턴스들을 공유할 수 없다. 만일 동일한 클래스 이름이 여러 <literal>&lt;listener/&gt;</literal>
요소들에서 사용될 경우, 각각의 참조는 그 클래스에 대한 별도의 인스턴스로 귀결될 것이다. 만일 당신이 리스너 타입들 사이에서 리스너 인스턴스들을
공유할 가용성을 필요로 할 경우 당신은 프로그래밍 방식의 등록 접근법을 사용해야 한다.
</para>
<para>
컨피그레이션 동안에 왜 인터페이스를 구현하고 특정 타입을 지정하는가? 물론 리스너 구현은 여러 개의 이벤트 리스너 인터페이스들을
구현할 수 있다. 등록 동안에 추가적으로 타입을 정의하는 것은 컨피그레이션 동안에 맞춤형 리스너들의 사용 여부를 전환시키는 것을
더 쉽게 해준다.
</para>
</sect1>
<sect1 id="objectstate-decl-security">
<title>Hibernate 선언적인 보안</title>
<para>
대개 Hibernate 어플리케이션들에서 선언적인 보안은 session facade 계층 내에서 관리된다. 이제, Hibernate3는 어떤 액션들이
JACC를 통해 퍼미션을 주어지고, JAAS를 통해 인가되는 것을 허용해준다. 이것은 모든 아키텍처의 상단에 빌드된 옵션 기능이다.
</para>
<para>
먼저, 당신은 JAAS authorization 사용을 이용 가능하도록 하기 위해 적절한 이벤트 리스터들을 구성해야 한다.
</para>
<programlisting><![CDATA[<listener type="pre-delete" class="org.hibernate.secure.JACCPreDeleteEventListener"/>
<listener type="pre-update" class="org.hibernate.secure.JACCPreUpdateEventListener"/>
<listener type="pre-insert" class="org.hibernate.secure.JACCPreInsertEventListener"/>
<listener type="pre-load" class="org.hibernate.secure.JACCPreLoadEventListener"/>]]></programlisting>
<para>
다음으로, 여전히 <literal>hibernate.cfg.xml</literal> 내에서 퍼미션들을 role들에 바인드 시킨다 :
</para>
<programlisting><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
<para>
역할(role) 이름들은 당신의 JACC 프로바이더에 의해 인지된 역할(role)들이다.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,652 @@
<chapter id="example-mappings">
<title>예제: 여러 가지 매핑들</title>
<para>
이 장은 몇몇 보다 복잡한 연관 매핑들을 보여준다.
</para>
<sect1 id="example-mappings-emp">
<title>Employer/Employee</title>
<para>
<literal>Employer</literal><literal>Employee</literal> 사이의 관계에 대한 다음 모형은 그 연관를 표현하는 데
실제 엔티티 클래스(<literal>Employment</literal>)를 사용한다. 동일한 두 부분들에 대해 하나 이상의 채용 주기가 존재할 수 있기
때문에 이것이 행해진다. 컴포넌트들이 화폐 값들과 종업원 이름들을 모형화 시키는데 사용된다.
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/EmployerEmployee.gif" format="GIF" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/EmployerEmployee.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
다음은 가능한 매핑 문서이다:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Employer" table="employers">
<id name="id">
<generator class="sequence">
<param name="sequence">employer_id_seq</param>
</generator>
</id>
<property name="name"/>
</class>
<class name="Employment" table="employment_periods">
<id name="id">
<generator class="sequence">
<param name="sequence">employment_id_seq</param>
</generator>
</id>
<property name="startDate" column="start_date"/>
<property name="endDate" column="end_date"/>
<component name="hourlyRate" class="MonetaryAmount">
<property name="amount">
<column name="hourly_rate" sql-type="NUMERIC(12, 2)"/>
</property>
<property name="currency" length="12"/>
</component>
<many-to-one name="employer" column="employer_id" not-null="true"/>
<many-to-one name="employee" column="employee_id" not-null="true"/>
</class>
<class name="Employee" table="employees">
<id name="id">
<generator class="sequence">
<param name="sequence">employee_id_seq</param>
</generator>
</id>
<property name="taxfileNumber"/>
<component name="name" class="Name">
<property name="firstName"/>
<property name="initial"/>
<property name="lastName"/>
</component>
</class>
</hibernate-mapping>]]></programlisting>
<para>
그리고 다음은 <literal>SchemaExport</literal>에 의해 생성된 테이블 스키마이다.
</para>
<programlisting><![CDATA[create table employers (
id BIGINT not null,
name VARCHAR(255),
primary key (id)
)
create table employment_periods (
id BIGINT not null,
hourly_rate NUMERIC(12, 2),
currency VARCHAR(12),
employee_id BIGINT not null,
employer_id BIGINT not null,
end_date TIMESTAMP,
start_date TIMESTAMP,
primary key (id)
)
create table employees (
id BIGINT not null,
firstName VARCHAR(255),
initial CHAR(1),
lastName VARCHAR(255),
taxfileNumber VARCHAR(255),
primary key (id)
)
alter table employment_periods
add constraint employment_periodsFK0 foreign key (employer_id) references employers
alter table employment_periods
add constraint employment_periodsFK1 foreign key (employee_id) references employees
create sequence employee_id_seq
create sequence employment_id_seq
create sequence employer_id_seq]]></programlisting>
</sect1>
<sect1 id="example-mappings-authorwork">
<title>Author/Work</title>
<para>
<literal>Work</literal>, <literal>Author</literal> 그리고 <literal>Person</literal> 사이의 관계들에 대한
다음 모형을 검토하자. 우리는 <literal>Work</literal><literal>Author</literal> 사이의 관계를 many-to-many
연관으로 표현한다. 우리는 <literal>Author</literal><literal>Person</literal> 사이의 관계를 one-to-one
연관으로 표현하고자 선택한다. 또 다른 가능성은 <literal>Author</literal><literal>Person</literal>을 확장하도록
하는 것일 것이다.
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/AuthorWork.gif" format="GIF" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/AuthorWork.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
다음 매핑 문서는 이들 관계들을 정확하게 표현한다:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Work" table="works" discriminator-value="W">
<id name="id" column="id">
<generator class="native"/>
</id>
<discriminator column="type" type="character"/>
<property name="title"/>
<set name="authors" table="author_work">
<key column name="work_id"/>
<many-to-many class="Author" column name="author_id"/>
</set>
<subclass name="Book" discriminator-value="B">
<property name="text"/>
</subclass>
<subclass name="Song" discriminator-value="S">
<property name="tempo"/>
<property name="genre"/>
</subclass>
</class>
<class name="Author" table="authors">
<id name="id" column="id">
<!-- The Author must have the same identifier as the Person -->
<generator class="assigned"/>
</id>
<property name="alias"/>
<one-to-one name="person" constrained="true"/>
<set name="works" table="author_work" inverse="true">
<key column="author_id"/>
<many-to-many class="Work" column="work_id"/>
</set>
</class>
<class name="Person" table="persons">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
이 매핑에는 네 개의 테이블들이 존재한다. <literal>works</literal>, <literal>authors</literal>
<literal>persons</literal>은 각각 작업 데이터, 저자 데이터, 개인 데이터를 보관한다. <literal>author_work</literal>
저자들을 작업들에 연결시키는 연관 테이블이다. 다음은 <literal>SchemaExport</literal>에 의해 생성된 테이블
스키마이다.
</para>
<programlisting><![CDATA[create table works (
id BIGINT not null generated by default as identity,
tempo FLOAT,
genre VARCHAR(255),
text INTEGER,
title VARCHAR(255),
type CHAR(1) not null,
primary key (id)
)
create table author_work (
author_id BIGINT not null,
work_id BIGINT not null,
primary key (work_id, author_id)
)
create table authors (
id BIGINT not null generated by default as identity,
alias VARCHAR(255),
primary key (id)
)
create table persons (
id BIGINT not null generated by default as identity,
name VARCHAR(255),
primary key (id)
)
alter table authors
add constraint authorsFK0 foreign key (id) references persons
alter table author_work
add constraint author_workFK0 foreign key (author_id) references authors
alter table author_work
add constraint author_workFK1 foreign key (work_id) references works]]></programlisting>
</sect1>
<sect1 id="example-mappings-customerorderproduct">
<title>Customer/Order/Product</title>
<para>
이제 <literal>Customer</literal>, <literal>Order</literal><literal>LineItem</literal>
그리고 <literal>Product</literal> 사이의 관계들에 관한 모형을 검토하자. <literal>Customer</literal>
<literal>Order</literal> 사이의 one-to-many 연관이 존재하지만, 우리는 어떻게
<literal>Order</literal> / <literal>LineItem</literal> / <literal>Product</literal>
표현할 것인가? 나는 <literal>Order</literal><literal>Product</literal> 사이의 many-to-many
연관를 나타내는 하나의 연관 클래스로서 <literal>LineItem</literal>을 매핑하기로 선택했다. Hibernate에서
이것은 composite 요소로 명명된다.
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/CustomerOrderProduct.gif" format="GIF" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/CustomerOrderProduct.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
매핑 문서:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Customer" table="customers">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="orders" inverse="true">
<key column="customer_id"/>
<one-to-many class="Order"/>
</set>
</class>
<class name="Order" table="orders">
<id name="id">
<generator class="native"/>
</id>
<property name="date"/>
<many-to-one name="customer" column="customer_id"/>
<list name="lineItems" table="line_items">
<key column="order_id"/>
<list-index column="line_number"/>
<composite-element class="LineItem">
<property name="quantity"/>
<many-to-one name="product" column="product_id"/>
</composite-element>
</list>
</class>
<class name="Product" table="products">
<id name="id">
<generator class="native"/>
</id>
<property name="serialNumber"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
<literal>customers</literal>, <literal>orders</literal>, <literal>line_items</literal> 그리고
<literal>products</literal>는 각각 고객 데이터, 주문 데이터, 주문 라인 아이템 데이터, 그리고 제품 데이터를
보관한다. <literal>line_items</literal>는 또한 주문들을 제품들과 연결시키는 연관 테이블로서 동작한다.
</para>
<programlisting><![CDATA[create table customers (
id BIGINT not null generated by default as identity,
name VARCHAR(255),
primary key (id)
)
create table orders (
id BIGINT not null generated by default as identity,
customer_id BIGINT,
date TIMESTAMP,
primary key (id)
)
create table line_items (
line_number INTEGER not null,
order_id BIGINT not null,
product_id BIGINT,
quantity INTEGER,
primary key (order_id, line_number)
)
create table products (
id BIGINT not null generated by default as identity,
serialNumber VARCHAR(255),
primary key (id)
)
alter table orders
add constraint ordersFK0 foreign key (customer_id) references customers
alter table line_items
add constraint line_itemsFK0 foreign key (product_id) references products
alter table line_items
add constraint line_itemsFK1 foreign key (order_id) references orders]]></programlisting>
</sect1>
<sect1 id="misc">
<title>기타 예제 매핑들</title>
<para>
이들 예제들은 모두 Hiberante test suite로부터 취했다. 당신은 거기서 많은
다른 유용한 예제 매핑들을 발견할 것이다. Hibernate 배포본의 <literal>test</literal>
폴더를 살펴보라.
</para>
<para>TODO: 이 내용을 둘러싼 말들을 집어넣을 것.</para>
<sect2 id="example-mappings-typed-onetone">
<title>"형식화된(Typed)" one-to-one 연관</title>
<programlisting><![CDATA[<class name="Person">
<id name="name"/>
<one-to-one name="address"
cascade="all">
<formula>name</formula>
<formula>'HOME'</formula>
</one-to-one>
<one-to-one name="mailingAddress"
cascade="all">
<formula>name</formula>
<formula>'MAILING'</formula>
</one-to-one>
</class>
<class name="Address" batch-size="2"
check="addressType in ('MAILING', 'HOME', 'BUSINESS')">
<composite-id>
<key-many-to-one name="person"
column="personName"/>
<key-property name="type"
column="addressType"/>
</composite-id>
<property name="street" type="text"/>
<property name="state"/>
<property name="zip"/>
</class>]]></programlisting>
</sect2>
<sect2 id="example-mappings-composite-key">
<title>Composite 키 예제</title>
<programlisting><![CDATA[<class name="Customer">
<id name="customerId"
length="10">
<generator class="assigned"/>
</id>
<property name="name" not-null="true" length="100"/>
<property name="address" not-null="true" length="200"/>
<list name="orders"
inverse="true"
cascade="save-update">
<key column="customerId"/>
<index column="orderNumber"/>
<one-to-many class="Order"/>
</list>
</class>
<class name="Order" table="CustomerOrder" lazy="true">
<synchronize table="LineItem"/>
<synchronize table="Product"/>
<composite-id name="id"
class="Order$Id">
<key-property name="customerId" length="10"/>
<key-property name="orderNumber"/>
</composite-id>
<property name="orderDate"
type="calendar_date"
not-null="true"/>
<property name="total">
<formula>
( select sum(li.quantity*p.price)
from LineItem li, Product p
where li.productId = p.productId
and li.customerId = customerId
and li.orderNumber = orderNumber )
</formula>
</property>
<many-to-one name="customer"
column="customerId"
insert="false"
update="false"
not-null="true"/>
<bag name="lineItems"
fetch="join"
inverse="true"
cascade="save-update">
<key>
<column name="customerId"/>
<column name="orderNumber"/>
</key>
<one-to-many class="LineItem"/>
</bag>
</class>
<class name="LineItem">
<composite-id name="id"
class="LineItem$Id">
<key-property name="customerId" length="10"/>
<key-property name="orderNumber"/>
<key-property name="productId" length="10"/>
</composite-id>
<property name="quantity"/>
<many-to-one name="order"
insert="false"
update="false"
not-null="true">
<column name="customerId"/>
<column name="orderNumber"/>
</many-to-one>
<many-to-one name="product"
insert="false"
update="false"
not-null="true"
column="productId"/>
</class>
<class name="Product">
<synchronize table="LineItem"/>
<id name="productId"
length="10">
<generator class="assigned"/>
</id>
<property name="description"
not-null="true"
length="200"/>
<property name="price" length="3"/>
<property name="numberAvailable"/>
<property name="numberOrdered">
<formula>
( select sum(li.quantity)
from LineItem li
where li.productId = productId )
</formula>
</property>
</class>]]></programlisting>
</sect2>
<sect2 id="example-mappings-composite-key-manytomany">
<title>공유된 합성 키 속성을 가진 Many-to-many</title>
<programlisting><![CDATA[<class name="User" table="`User`">
<composite-id>
<key-property name="name"/>
<key-property name="org"/>
</composite-id>
<set name="groups" table="UserGroup">
<key>
<column name="userName"/>
<column name="org"/>
</key>
<many-to-many class="Group">
<column name="groupName"/>
<formula>org</formula>
</many-to-many>
</set>
</class>
<class name="Group" table="`Group`">
<composite-id>
<key-property name="name"/>
<key-property name="org"/>
</composite-id>
<property name="description"/>
<set name="users" table="UserGroup" inverse="true">
<key>
<column name="groupName"/>
<column name="org"/>
</key>
<many-to-many class="User">
<column name="userName"/>
<formula>org</formula>
</many-to-many>
</set>
</class>
]]></programlisting>
</sect2>
<sect2 id="example-mappings-content-discrimination">
<title>내용 기반 판별</title>
<programlisting><![CDATA[<class name="Person"
discriminator-value="P">
<id name="id"
column="person_id"
unsaved-value="0">
<generator class="native"/>
</id>
<discriminator
type="character">
<formula>
case
when title is not null then 'E'
when salesperson is not null then 'C'
else 'P'
end
</formula>
</discriminator>
<property name="name"
not-null="true"
length="80"/>
<property name="sex"
not-null="true"
update="false"/>
<component name="address">
<property name="address"/>
<property name="zip"/>
<property name="country"/>
</component>
<subclass name="Employee"
discriminator-value="E">
<property name="title"
length="20"/>
<property name="salary"/>
<many-to-one name="manager"/>
</subclass>
<subclass name="Customer"
discriminator-value="C">
<property name="comments"/>
<many-to-one name="salesperson"/>
</subclass>
</class>]]></programlisting>
</sect2>
<sect2 id="example-mappings-association-alternatekeys" >
<title>대체 키들에 대한 연관들</title>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="hilo"/>
</id>
<property name="name" length="100"/>
<one-to-one name="address"
property-ref="person"
cascade="all"
fetch="join"/>
<set name="accounts"
inverse="true">
<key column="userId"
property-ref="userId"/>
<one-to-many class="Account"/>
</set>
<property name="userId" length="8"/>
</class>
<class name="Address">
<id name="id">
<generator class="hilo"/>
</id>
<property name="address" length="300"/>
<property name="zip" length="5"/>
<property name="country" length="25"/>
<many-to-one name="person" unique="true" not-null="true"/>
</class>
<class name="Account">
<id name="accountId" length="32">
<generator class="uuid.hex"/>
</id>
<many-to-one name="user"
column="userId"
property-ref="userId"/>
<property name="type" not-null="true"/>
</class>]]></programlisting>
</sect2>
</sect1>
</chapter>

View File

@ -0,0 +1,343 @@
<chapter id="example-parentchild">
<title>예제: 부모/자식</title>
<para>
새로운 사용자들이 Hibernate로 행하고자 시도하는 바로 첫 번째 것들 중 하나는 부모/자식 타입의 관계를 모형화 시키는 것이다. 이것에 대한
두 가지 다른 접근법들이 존재한다. 여러가지 이유들로 인해 특히 새로운 사용자들에게 가장 편한 접근법은 <literal>Parent</literal>로부터
<literal>Child</literal>로의 <literal>&lt;one-to-many&gt;</literal> 연관을 가진 엔티티 클래스들로서 <literal>Parent</literal>
<literal>Child</literal> 양자를 모형화 시키는 것이다. (다른 접근법은 <literal>Child</literal>
<literal>&lt;composite-element&gt;</literal>로 선언하는 것이다.) 이제, (Hibernate에서) one to many 연관에 대한 디폴트
의미는 composite 요소 매핑의 의미보다 부모/자식 관계의 통상적인 의미에 훨씬 덜 가깝다는 것이 판명된다. 우리는 부모/자식 관계를 효율적이고
강력하게 모형화 시키기 위해 <emphasis>케스케이드들을 가진 양방향 one to many 연관</emphasis>을 사용하는 방법을 설명할 것이다.
그것은 전혀 어렵지 않다!
</para>
<sect1 id="example-parentchild-collections">
<title>콜렉션들에 관한 노트</title>
<para>
Hibernate 콜렉션들은 그것들의 소유하고 있는 엔티티의 논리적 부분으로 간주된다; 결코 포함된 엔티티들의 부분이 아니다. 이것은
중대한 구분점이다! 그것은 다음은 다음 결과들을 갖는다:
</para>
<itemizedlist>
<listitem>
<para>
콜렉션으로부터 객체를 제거하고/콜렉션에 객체를 추가 시킬 때, 콜렉션 소유자의 버전 번호가 증가된다.
</para>
</listitem>
<listitem>
<para>
만일 콜렉션으로부터 제거되었던 객체가 하나의 값 타입의 인스턴스(예를 들어 composite 요소)이면, 그 객체는 영속상태를 끝내고
그것의 상태가 데이터베이스로부터 완전히 제거될 것이다. 마찬가지로 하나의 값 타입의 인스턴스를 콜렉션에 추가시키는 것은 그것의
상태가 즉시 영속화 되도록 강제시킬 것이다.
</para>
</listitem>
<listitem>
<para>
반면에, 만일 엔티티가 콜렉션으로부터 제거될 경우(one-to-many 또는 many-to-many 연관), 그것은 디폴트로 삭제되지 않을
것이다. 이 특징은 완전하게 일관적이다 - 다른 엔티티의 내부 상태에 대한 변경은 연관된 엔티티를 사라지도록 강제하지 않을 것이다!
마찬가지로 콜렉션에 엔티티를 추가시키는 것은 디폴트로 그 엔티티가 영속화 되도록 강제시키지 않는다.
</para>
</listitem>
</itemizedlist>
<para>
대신에 콜렉션으로의 엔티티 추가가 두 엔티티들 사이에 단지 하나의 링크를 생성시키는 반면에, 그것을 제거하는 것은 링크를 제거한다는 점이
디폴트 특징이다. 이것은 모든 종류의 경우들에 대해 매우 적절하다. 그것이 전혀 적절하지 않은 곳은 부모/자식 관계인 경우이고, 여기서
자식의 생애는 부모의 생명주기에 묶여져 있다.
</para>
</sect1>
<sect1 id="example-parentchild-bidir">
<title>양방향 one-to-many</title>
<para>
<literal>Parent</literal>로부터 <literal>Child</literal>로의 간단한 <literal>&lt;one-to-many&gt;</literal>
연관관계로 시작한다고 가정하자.
</para>
<programlisting><![CDATA[<set name="children">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
우리가 다음 코드를 실행시켰다면
</para>
<programlisting><![CDATA[Parent p = .....;
Child c = new Child();
p.getChildren().add(c);
session.save(c);
session.flush();]]></programlisting>
<para>
Hibernate는 두 개의 SQL 문장들을 실행할 것이다:
</para>
<itemizedlist>
<listitem>
<para><literal>c</literal>에 대한 레코드를 생성시키는 <literal>INSERT</literal></para>
</listitem>
<listitem>
<para><literal>p</literal>로부터 <literal>c</literal>로의 링크를 생성시키는 <literal>UPDATE</literal></para>
</listitem>
</itemizedlist>
<para>
이것은 비효율적일 뿐만 아니라, 또한 <literal>parent_id</literal> 컬럼 상의 임의의 <literal>NOT NULL</literal>
컨스트레인트에 위배된다. 우리는 콜렉션 매핑에서 <literal>not-null="true"</literal>를 지정함으로써 null 허용 가능
컨스트레인트 위반을 정정할 수 있다:
</para>
<programlisting><![CDATA[<set name="children">
<key column="parent_id" not-null="true"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
하지만 이것은 권장되는 해결책이 아니다.
</para>
<para>
이 행위의 기본 원인은 <literal>p</literal>로부터 <literal>c</literal>로의 링크(foreign key <literal>parent_id</literal>)가
<literal>Child</literal> 객체의 상태의 부분으로 간주되지 않고 그러므로 <literal>INSERT</literal>로 생성되지 않는다는
점이다. 따라서 해결책은 <literal>Child</literal> 매핑의 링크 부분을 만드는 것이다.
</para>
<programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
<para>
(우리는 또한 <literal>parent</literal> 프로퍼티를 <literal>Child</literal> 클래스에 추가시킬 필요가 있다.)
</para>
<para>
이제 <literal>Child</literal> 엔티티가 링크의 상태를 관리한다는 점을 노트하고, 우리는 링크를 업데이트 시키지 말도록 콜렉션에게
통보한다. 우리는 <literal>inverse</literal> 속성을 사용한다.
</para>
<programlisting><![CDATA[<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
다음 코드는 새로운 <literal>Child</literal>를 추가시키는데 사용될 것이다
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
c.setParent(p);
p.getChildren().add(c);
session.save(c);
session.flush();]]></programlisting>
<para>
그리고 이제, 유일하게 한 개의 SQL <literal>INSERT</literal>가 실행될 것이다!
</para>
<para>
약간 거칠게, 우리는 <literal>Parent</literal><literal>addChild()</literal> 메소드를 생성시킬 수 있다.
</para>
<programlisting><![CDATA[public void addChild(Child c) {
c.setParent(this);
children.add(c);
}]]></programlisting>
<para>
이제, <literal>Child</literal>를 추가하는 코드는 다음과 같다
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.save(c);
session.flush();]]></programlisting>
</sect1>
<sect1 id="example-parentchild-cascades">
<title>케스케이딩 생명주기</title>
<para>
<literal>save()</literal>에 대한 명시적인 호출은 여전히 성가시다. 우리는 케스케이딩을 사용하여 이것을 얘기할 것이다.
</para>
<programlisting><![CDATA[<set name="children" inverse="true" cascade="all">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
다음은 위의 코드를 단순화 시킨다
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.flush();]]></programlisting>
<para>
유사하게, 우리는 <literal>Parent</literal>를 저장하거나 삭제할 때 자식들에 대해 반복하는 것을 필요로 하지 않는다. 다음은
데이터베이스로부터 <literal>p</literal>와 모든 그것의 자식들을 제거시킨다.
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
session.delete(p);
session.flush();]]></programlisting>
<para>
하지만, 다음 코드
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
c.setParent(null);
session.flush();]]></programlisting>
<para>
는 데이터베이스로부터 <literal>c</literal>를 제거하지 않을 것이다; 그것은 오직 <literal>p</literal>에 대한 링크만을 제거할
것이다(그리고 이 경우에 <literal>NOT NULL</literal> 컨스트레인트 위반을 일으킬 것이다 ). 당신은 명시적으로
<literal>Child</literal><literal>delete()</literal> 시킬 필요가 있다.
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
session.delete(c);
session.flush();]]></programlisting>
<para>
이제 우리의 경우에 <literal>Child</literal>는 그것의 부모 없이는 진정으로 존재할 수 없다. 따라서 만일 우리가 콜렉션으로부터
하나의 <literal>Child</literal>를 제거할 경우, 우리는 그것이 정말로 삭제되기를 원한다. 이를 위해 우리는
<literal>cascade="all-delete-orphan"</literal>을 사용해야 한다.
</para>
<programlisting><![CDATA[<set name="children" inverse="true" cascade="all-delete-orphan">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
노트: 비록 콜렉션 매핑이 <literal>inverse="true"</literal>를 지정할 지라도, 케스케이드들은 여전히 콜렉션 요소들을
반복함으로써 처리된다. 따라서 객체가 케스케이드에 의해 저장되고, 삭제되거나 업데이트 되는 것을 당신이 필요로 할 경우, 당신은
그것을 그 콜렉션에 추가해야 한다. 단순히 <literal>setParent()</literal>를 호출하는 것으로는 충분하지 않다.
</para>
</sect1>
<sect1 id="example-parentchild-update">
<title>케스케이드들과 <literal>unsaved-value</literal></title>
<para>
우리가 하나의 <literal>Session</literal> 속에 <literal>Parent</literal>를 로드시켰고 UI 액션에서 어떤 변경들을 행했고,
<literal>update()</literal>를 호출하여 새로운 세션에서 이들 변경들을 영속화 시키는 것을 원한다고 가정하자. <literal>Parent</literal>
자식들을 가진 콜렉션을 포함할 것이고, 케스케이딩 업데이트가 사용 가능하기 때문에, Hibernate는 어느 자식들이 새로이 초기화 되는지
그리고 어느 것이 데이터베이스에서 현재 행들을 표현하는지를 알 필요가 있다. <literal>Parent</literal><literal>Child</literal>
모두 <literal>Long</literal> 타입의 식별자 프로퍼티들을 생성시켰다고 가정하자. Hibernate는 어느 자식들이 새로운 것인지를
결정하는데 식별자와 version/timestamp 프로퍼티 값을 사용할 것이다.(<xref linkend="objectstate-saveorupdate"/>을 보라.)
<emphasis>Hibernate3에서는<literal>unsaved-value</literal>를 더이상 명시적으로 지정할 필요가 없다.</emphasis>
</para>
<para>
다음 코드는 <literal>parent</literal><literal>child</literal>를 업데이트하고 <literal>newChild</literal>
삽입시킬 것이다.
</para>
<programlisting><![CDATA[//parent and child were both loaded in a previous session
parent.addChild(child);
Child newChild = new Child();
parent.addChild(newChild);
session.update(parent);
session.flush();]]></programlisting>
<para>
물론 그것은 생성되는 식별자의 경우에는 모두 매우 좋지만, 할당되는 식별자들과 composite 식별자들에 대해서는 어떠한가? 이것은 보다
어렵다. 왜냐하면 Hibernate는 (사용자에 의해 할당된 식별자를 가진) 새로이 초기화 된 객체와 이전 세션에서 로드되었던 객체 사이를
구별짓는데 식별자 프로퍼티를 사용할 수 없기 때문이다. 이 경우에, Hibernate는 timestamp 프로퍼티 또는 version 프로퍼티를
사용하거나 실제로 second-level 캐시를 질의하거나 가장 나쁜 경우에는 행이 존재하는지를 알기 위해 데이터베이스를 질의할 것이다.
</para>
<!-- undocumenting
<para>
There is one further possibility. The <literal>Interceptor</literal> method named
<literal>isUnsaved()</literal> lets the application implement its own strategy for distinguishing
newly instantiated objects. For example, you could define a base class for your persistent classes.
</para>
<programlisting><![CDATA[public class Persistent {
private boolean _saved = false;
public void onSave() {
_saved=true;
}
public void onLoad() {
_saved=true;
}
......
public boolean isSaved() {
return _saved;
}
}]]></programlisting>
<para>
(The <literal>saved</literal> property is non-persistent.)
Now implement <literal>isUnsaved()</literal>, along with <literal>onLoad()</literal>
and <literal>onSave()</literal> as follows.
</para>
<programlisting><![CDATA[public Boolean isUnsaved(Object entity) {
if (entity instanceof Persistent) {
return new Boolean( !( (Persistent) entity ).isSaved() );
}
else {
return null;
}
}
public boolean onLoad(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if (entity instanceof Persistent) ( (Persistent) entity ).onLoad();
return false;
}
public boolean onSave(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if (entity instanceof Persistent) ( (Persistent) entity ).onSave();
return false;
}]]></programlisting>
<para>
Don't worry; in Hibernate3 you don't need to write any of this kind of code if you don't want to.
</para>
-->
</sect1>
<sect1 id="example-parentchild-conclusion">
<title>결론</title>
<para>
여기에 숙지할 것이 약간 있고 그것은 처음에는 혼동스러운 것처럼 보일 수 있다. 하지만 실제로 그것은 모두 매우 좋게 동작한다. 대부분의
Hibernate 어플리케이션들은 많은 장소들에서 부모/자식 패턴을 사용한다.
</para>
<para>
우리는 첫 번째 단락에서 대안을 언급했다. 위의 쟁점들 중 어느 것도 정확하게 부모/자식 관계의 의미를 가진,
<literal>&lt;composite-element&gt;</literal> 매핑들의 경우에는 존재하지 않는다. 불행히도, composite 요소 클래스들에
대한 두 개의 커다란 제약들이 존재한다: composite 요소들은 콜렉션들을 소유하지 않고, 그것들은 유일한 부모가 아닌 다른 어떤
엔티티의 자식일 수는 없다.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,428 @@
<chapter id="example-weblog">
<title>예제: Weblog 어플리케이션</title>
<sect1 id="example-weblog-classes">
<title>영속 클래스들</title>
<para>
영속 클래스들은 웹로그, 그리고 웹 로그 내에 게시된 항목을 표현한다.그것들은
표준 부모/자식 관계로 모형화 될 것이지만, 우리는 set 대신에 순서지워진 bag를 사용할 것이다.
</para>
<programlisting><![CDATA[package eg;
import java.util.List;
public class Blog {
private Long _id;
private String _name;
private List _items;
public Long getId() {
return _id;
}
public List getItems() {
return _items;
}
public String getName() {
return _name;
}
public void setId(Long long1) {
_id = long1;
}
public void setItems(List list) {
_items = list;
}
public void setName(String string) {
_name = string;
}
}]]></programlisting>
<programlisting><![CDATA[package eg;
import java.text.DateFormat;
import java.util.Calendar;
public class BlogItem {
private Long _id;
private Calendar _datetime;
private String _text;
private String _title;
private Blog _blog;
public Blog getBlog() {
return _blog;
}
public Calendar getDatetime() {
return _datetime;
}
public Long getId() {
return _id;
}
public String getText() {
return _text;
}
public String getTitle() {
return _title;
}
public void setBlog(Blog blog) {
_blog = blog;
}
public void setDatetime(Calendar calendar) {
_datetime = calendar;
}
public void setId(Long long1) {
_id = long1;
}
public void setText(String string) {
_text = string;
}
public void setTitle(String string) {
_title = string;
}
}]]></programlisting>
</sect1>
<sect1 id="example-weblog-mappings">
<title>Hibernate 매핑들</title>
<para>
XML 매핑들은 이제 매우 간단해질 것이다.
</para>
<programlisting><![CDATA[<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class
name="Blog"
table="BLOGS">
<id
name="id"
column="BLOG_ID">
<generator class="native"/>
</id>
<property
name="name"
column="NAME"
not-null="true"
unique="true"/>
<bag
name="items"
inverse="true"
order-by="DATE_TIME"
cascade="all">
<key column="BLOG_ID"/>
<one-to-many class="BlogItem"/>
</bag>
</class>
</hibernate-mapping>]]></programlisting>
<programlisting><![CDATA[<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class
name="BlogItem"
table="BLOG_ITEMS"
dynamic-update="true">
<id
name="id"
column="BLOG_ITEM_ID">
<generator class="native"/>
</id>
<property
name="title"
column="TITLE"
not-null="true"/>
<property
name="text"
column="TEXT"
not-null="true"/>
<property
name="datetime"
column="DATE_TIME"
not-null="true"/>
<many-to-one
name="blog"
column="BLOG_ID"
not-null="true"/>
</class>
</hibernate-mapping>]]></programlisting>
</sect1>
<sect1 id="example-weblog-code">
<title>Hibernate 코드</title>
<para>
다음 클래스는 우리가 Hibernate를 사용하여 이들 클래스들로 행할 수 있는 몇몇 종류의 것들을 설명한다.
</para>
<programlisting><![CDATA[package eg;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
public class BlogMain {
private SessionFactory _sessions;
public void configure() throws HibernateException {
_sessions = new Configuration()
.addClass(Blog.class)
.addClass(BlogItem.class)
.buildSessionFactory();
}
public void exportTables() throws HibernateException {
Configuration cfg = new Configuration()
.addClass(Blog.class)
.addClass(BlogItem.class);
new SchemaExport(cfg).create(true, true);
}
public Blog createBlog(String name) throws HibernateException {
Blog blog = new Blog();
blog.setName(name);
blog.setItems( new ArrayList() );
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.persist(blog);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return blog;
}
public BlogItem createBlogItem(Blog blog, String title, String text)
throws HibernateException {
BlogItem item = new BlogItem();
item.setTitle(title);
item.setText(text);
item.setBlog(blog);
item.setDatetime( Calendar.getInstance() );
blog.getItems().add(item);
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.update(blog);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return item;
}
public BlogItem createBlogItem(Long blogid, String title, String text)
throws HibernateException {
BlogItem item = new BlogItem();
item.setTitle(title);
item.setText(text);
item.setDatetime( Calendar.getInstance() );
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Blog blog = (Blog) session.load(Blog.class, blogid);
item.setBlog(blog);
blog.getItems().add(item);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return item;
}
public void updateBlogItem(BlogItem item, String text)
throws HibernateException {
item.setText(text);
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.update(item);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
}
public void updateBlogItem(Long itemid, String text)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
item.setText(text);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
}
public List listAllBlogNamesAndItemCounts(int max)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
List result = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"select blog.id, blog.name, count(blogItem) " +
"from Blog as blog " +
"left outer join blog.items as blogItem " +
"group by blog.name, blog.id " +
"order by max(blogItem.datetime)"
);
q.setMaxResults(max);
result = q.list();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return result;
}
public Blog getBlogAndAllItems(Long blogid)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
Blog blog = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"from Blog as blog " +
"left outer join fetch blog.items " +
"where blog.id = :blogid"
);
q.setParameter("blogid", blogid);
blog = (Blog) q.uniqueResult();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return blog;
}
public List listBlogsAndRecentItems() throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
List result = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"from Blog as blog " +
"inner join blog.items as blogItem " +
"where blogItem.datetime > :minDate"
);
Calendar cal = Calendar.getInstance();
cal.roll(Calendar.MONTH, false);
q.setCalendar("minDate", cal);
result = q.list();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return result;
}
}]]></programlisting>
</sect1>
</chapter>

View File

@ -0,0 +1,123 @@
<chapter id="filters">
<title>데이터 필터링하기</title>
<para>
Hibernate3은 혁신적인 "가시성(visibility)" 규칙들로서 데이터를 처리하는 새로운 접근법을 제공한다. <emphasis>Hibernate
필터</emphasis>는 특정 Hibernate 세션에 대해 이용 가능하게 되거나 이용 불가능하게 될 수도 있는 전역, 명명된 파라미터화 된 필터이다.
</para>
<sect1 id="objectstate-filters">
<title>Hibernate 필터들</title>
<para>
Hibernate3은 필터 기준(criteria)을 미리 정의하고 클래스 레벨과 콜렉션 레벨 양자에서 그들 필터들을 첨부할 능력을 추가시킨다.
필터 기준(criteria)은 클래스 요소와 다양한 콜렉션 요소들에 대해 이용 가능한 기존의 "where" 속성과 매우 유사한 하나의 제한 절을
정의하는 능력이다. 이것들을 제외하면 필터 조건들은 파라미터화 될 수 있다. 그때 어플리케이션은 주어진 필터들이 이용 가능한지 여부
그리고 그들 파라미터 값들이 무엇이어야 하는지를 실행 시에 결정할 수 있다. 필터들은 데이터베이스 뷰들 처럼 사용될 수 있지만,
어플리케이션 내부에 파라미터화 된다.
</para>
<para>
필터들을 사용하기 위해서, 그것들은 먼저 정의되고 나서 적절한 매핑 요소들에 첨가되어야 한다. 필터를 정의하기 위해,
<literal>&lt;hibernate-mapping/&gt;</literal> 요소 내부에 <literal>&lt;filter-def/&gt;</literal> 요소를
사용하라:
</para>
<programlisting><![CDATA[<filter-def name="myFilter">
<filter-param name="myFilterParam" type="string"/>
</filter-def>]]></programlisting>
<para>
그때 이 필터는 클래스에 첨가될 수 있다:
</para>
<programlisting><![CDATA[<class name="myClass" ...>
...
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</class>]]></programlisting>
<para>
또는 콜렉션에 첨가될 수 있다:
</para>
<programlisting><![CDATA[<set ...>
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</set>]]></programlisting>
<para>
또는 동시에 양자에(또는 각각의 여러번) 첨가될 수 있다.
</para>
<para>
<literal>Session</literal> 상의 메소드들은 다음과 같다: <literal>enableFilter(String filterName)</literal>,
<literal>getEnabledFilter(String filterName)</literal>, <literal>disableFilter(String filterName)</literal>.
디폴트로, 필터들은 주어진 세션에 대해 이용 가능하지 <emphasis>않다</emphasis>; 그것들은 <literal>Session.enabledFilter()</literal>
메소드의 사용을 통해 명시적으로 이용 가능하게 되어야 한다. <literal>Session.enabledFilter()</literal>
<literal>Filter</literal> 인터페이스의 인스턴스를 반환한다. 위에 정의된 간단한 필터를 사용하면, 이것은 다음과 같을 것이다:
</para>
<programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
<para>
org.hibernate.Filter 인터페이스 상의 메소드들은 Hibernate에 매우 공통된 method-chaining을 허용한다는 점을 노트하라.
</para>
<para>
유효한 기록 날짜 패턴을 가진 시간 데이터를 사용하는 전체 예제 :
</para>
<programlisting><![CDATA[<filter-def name="effectiveDate">
<filter-param name="asOfDate" type="date"/>
</filter-def>
<class name="Employee" ...>
...
<many-to-one name="department" column="dept_id" class="Department"/>
<property name="effectiveStartDate" type="date" column="eff_start_dt"/>
<property name="effectiveEndDate" type="date" column="eff_end_dt"/>
...
<!--
Note that this assumes non-terminal records have an eff_end_dt set to
a max db date for simplicity-sake
-->
<filter name="effectiveDate"
condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
</class>
<class name="Department" ...>
...
<set name="employees" lazy="true">
<key column="dept_id"/>
<one-to-many class="Employee"/>
<filter name="effectiveDate"
condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
</set>
</class>]]></programlisting>
<para>
그때 당신이 현재 유효한 레코드들을 항상 얻는 것을 확실히 하기 위해, employee 데이터를 검색하기 전에 세션 상에 필터를
간단하게 이용 가능하게 하라:
</para>
<programlisting><![CDATA[Session session = ...;
session.enabledFilter("effectiveDate").setParameter("asOfDate", new Date());
List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
.setLong("targetSalary", new Long(1000000))
.list();
]]></programlisting>
<para>
위의 HQL 에서, 심지어 비록 우리가 결과들에 대한 봉급 컨스트레인트를 명시적으로 언급만 했을지라도, 이용 가능한 필터 때문에
그 질의는 봉급이 백만달러 이상인 현재 채용중인 직원들만을 반환할 것이다.
</para>
<para>
노트: 만일 당신이 outer 조인에 대해 필터들을 사용할 계획이라면 (HQL이든 로드 페칭이든) 조건 표현식의 방향을 주의하라.
이것을 left outer join으로 설정하는 것이 가장 안전하다; 일반적으로 오퍼레이터 뒤에 있는 컬럼 이름(들)이 뒤따르는 첫번째에
파라미터를 위치지워라.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,436 @@
<chapter id="inheritance">
<title>상속 매핑</title>
<sect1 id="inheritance-strategies" revision="2">
<title>세 가지 방도들</title>
<para>
Hibernate는 세 가지 기본적인 상속 매핑 방도들을 지원한다:
</para>
<itemizedlist>
<listitem>
<para>
table per class hierarchy
</para>
</listitem>
<listitem>
<para>
table per subclass
</para>
</listitem>
<listitem>
<para>
table per concrete class
</para>
</listitem>
</itemizedlist>
<para>
게다가 Hibernate는 네 번째의 약간 다른 종류의 다형성을 지원한다:
</para>
<itemizedlist>
<listitem>
<para>
implicit polymorphism(함축적인 다형성)
</para>
</listitem>
</itemizedlist>
<para>
동일한 상속 계층구조의 다른 가지들에 대해 다른 매핑 방도들을 사용하는 것이 가능하고, 그런 다음 전체 계층 구조를 가로질러
다형성을 성취하는데 함축적인 다형성을 사용하라. 하지만 Hibernate는 동일한 루트 <literal>&lt;class&gt;</literal> 요소
하에서 <literal>&lt;subclass&gt;</literal> 그리고 <literal>&lt;joined-subclass&gt;</literal> 그리고
<literal>&lt;union-subclass&gt;</literal> 매핑들을 혼합하는 것을 지원하지 않는다. 동일한 <literal>&lt;class&gt;</literal>
요소 하에서 <literal>&lt;subclass&gt;</literal> 요소와 <literal>&lt;join&gt;</literal> 요소를 결합시킴으로써
table per hierarchy 방도와 table per subclass 방도를 함께 혼합시키는 것이 가능하다(아래를 보라).
</para>
<sect2 id="inheritance-tableperclass" >
<title>Table per class hierarchy</title>
<para>
우리가 <literal>CreditCardPayment</literal>, <literal>CashPayment</literal>, <literal>ChequePayment</literal>
구현자들을 가진 하나의 인터페이스 <literal>Payment</literal>를 갖고 있다고 가정하자. table per hierarchy 매핑은
다음과 같을 것이다:
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<property name="creditCardType" column="CCTYPE"/>
...
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
...
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
...
</subclass>
</class>]]></programlisting>
<para>
정확히 하나의 테이블이 필요하다. 이 매핑 방도에는 다음의 하나의 큰 제약이 존재한다: <literal>CCTYPE</literal>과 같이,
서브 클래스들에 의해 선언된 컬럼들은 <literal>NOT NULL</literal> 컨스트레인트들을 가질 수 없다.
</para>
</sect2>
<sect2 id="inheritance-tablepersubclass">
<title>Table per subclass</title>
<para>
table per subclass 매핑은 다음과 같을 것이다:
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="creditCardType" column="CCTYPE"/>
...
</joined-subclass>
<joined-subclass name="CashPayment" table="CASH_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
</class>]]></programlisting>
<para>
네 개의 테이블들이 필요하다. 세 개의 서브클래스 테이블들은 슈퍼클래스 테이블에 대한 프라이머리 키 연관들을 갖는다
(따라서 그 관계형 모형은 실제로 one-to-one 연관이다).
</para>
</sect2>
<sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
<title>discriminator를 사용하는, table per subclass</title>
<para>
table-per-subclass에 대한 Hibernate의 구현은 discriminator(판별자) 컬럼을 필요로 하지 않음을 노트하라.
다른 객체/관계형 매핑기들은 슈퍼클래스 테이블 속에 하나의 타입 판별자 컬럼을 필요로 하는 table-per-subclass에 대한 다른 구현을
사용한다. Hibernate에 의해 채택된 접근법은 구현하기가 훨씬 더 어렵지만 관계형 관점에서는 아마 틀림없이 보다 더 정확하다.
만일 당신이 table per subclass 방도에 대해 하나의 판별자 컬럼을 사용하고 싶다면, 당신은 다음과 같이
<literal>&lt;subclass&gt;</literal><literal>&lt;join&gt;</literal>의 사용을 결합시킬 수도 있다:
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<join table="CREDIT_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="creditCardType" column="CCTYPE"/>
...
</join>
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
<join table="CASH_PAYMENT">
<key column="PAYMENT_ID"/>
...
</join>
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
<join table="CHEQUE_PAYMENT" fetch="select">
<key column="PAYMENT_ID"/>
...
</join>
</subclass>
</class>]]></programlisting>
<para>
선택적인 <literal>fetch="select"</literal> 선언은 슈퍼클래스를 질의할 때 outer join을 사용하여
<literal>ChequePayment</literal> 서브클래스 데이터를 페치시키지 않도록 Hibernate에게 알려준다.
</para>
</sect2>
<sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
<title>table per class hierarchy와 table per subclass를 혼합하기</title>
<para>
당신은 이 접근법을 사용하여 table per hierarchy 방도와 table per subclass 방도를 혼합시킬 수 있다:
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<join table="CREDIT_PAYMENT">
<property name="creditCardType" column="CCTYPE"/>
...
</join>
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
...
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
...
</subclass>
</class>]]></programlisting>
<para>
이들 매핑 방도들 중 어떤 것에 대해, 루트 <literal>Payment</literal> 클래스에 대한 하나의 다형성 연관은
<literal>&lt;many-to-one&gt;</literal>을 사용하여 매핑된다.
</para>
<programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
</sect2>
<sect2 id="inheritance-tableperconcrete" revision="1">
<title>Table per concrete class</title>
<para>
우리가 table per concrete class 방도 매핑에 대해 취할 수 있는 두 가지 방법들이 존재한다. 첫 번째는
<literal>&lt;union-subclass&gt;</literal>를 사용하는 것이다.
</para>
<programlisting><![CDATA[<class name="Payment">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="sequence"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<property name="creditCardType" column="CCTYPE"/>
...
</union-subclass>
<union-subclass name="CashPayment" table="CASH_PAYMENT">
...
</union-subclass>
<union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
...
</union-subclass>
</class>]]></programlisting>
<para>
세 개의 테이블들이 수반된다. 각각의 테이블은 상속된 프로퍼티들을 포함하여, 그 클래스의 모든 프로퍼티들에 대한 컬럼들을 정의한다.
</para>
<para>
이 접근법의 제약은 만일 하나의 프로퍼티가 슈퍼클래스 상으로 매핑될 경우, 그 컬럼 이름이 모든 서브클래스 테이블들 상에서 같아야 한다는
점이다.(장래의 Hibernate 배포본에서 우리는 이 제약을 풀 수도 있다.) identity 생성기 방도는 union 서브클래스 상속에서 허용되지
않으며, 진정 프라이머리 키 시드는 하나의 계층구조의 모든 unioned 서브클래스들을 가로질러 공유되어야 한다.
</para>
</sect2>
<sect2 id="inheritance-tableperconcreate-polymorphism">
<title>함축적인 다형성을 사용하는, table per concrete class</title>
<para>
대안적인 접근법은 함축적인 다형성을 사용하는 것이다:
</para>
<programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
<id name="id" type="long" column="CREDIT_PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="CREDIT_AMOUNT"/>
...
</class>
<class name="CashPayment" table="CASH_PAYMENT">
<id name="id" type="long" column="CASH_PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="CASH_AMOUNT"/>
...
</class>
<class name="ChequePayment" table="CHEQUE_PAYMENT">
<id name="id" type="long" column="CHEQUE_PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="CHEQUE_AMOUNT"/>
...
</class>]]></programlisting>
<para>
어느 곳에서도 우리가 명시적으로 <literal>Payment</literal> 인터페이스를 언급하지 않음을 주목하라.
또한 <literal>Payment</literal>의 프로퍼티들이 서브클래스들 각각에서 매핑된다는 점을 주목하라.
만일 당신이 중복을 피하고자 원한다면, XML 엔티티들을 사용하는 것을 고려하라(예를 들어 매핑에서
<literal>DOCTYPE</literal> 선언과 <literal>&amp;allproperties;</literal>에서
<literal>[ &lt;!ENTITY allproperties SYSTEM "allproperties.xml"&gt; ]</literal>).
</para>
<para>
이 접근법의 단점은 다형성 질의들을 수행할 때 Hibernate가 생성된 SQl <literal>UNION</literal>들을 생성시키는
않는다는 점이다.
</para>
<para>
이 매핑 방도의 경우, <literal>Payment</literal>에 대한 하나의 다형성 연관은 대개 <literal>&lt;any&gt;</literal>
사용하여 매핑된다.
</para>
<programlisting><![CDATA[<any name="payment" meta-type="string" id-type="long">
<meta-value value="CREDIT" class="CreditCardPayment"/>
<meta-value value="CASH" class="CashPayment"/>
<meta-value value="CHEQUE" class="ChequePayment"/>
<column name="PAYMENT_CLASS"/>
<column name="PAYMENT_ID"/>
</any>]]></programlisting>
</sect2>
<sect2 id="inheritace-mixingpolymorphism">
<title>함축적인 다형성을 다른 상속 매핑들과 혼합하기</title>
<para>
이 매핑에 대해 주목할 하나 이상의 것이 존재한다. 서브클래스들이 그것들 자신의<literal>&lt;class&gt;</literal> 요소 내에
각각 매핑되므로(그리고 <literal>Payment</literal>가 단지 인터페이스이므로), 서브클래스들 각각은 쉽게 또 다른 상속 계층구조의
부분일 수 있다! (그리고 당신은 <literal>Payment</literal> 인터페이스에 대해 여전히 다형성 질의들을 사용할 수 있다.)
</para>
<programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
<id name="id" type="long" column="CREDIT_PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="CREDIT_CARD" type="string"/>
<property name="amount" column="CREDIT_AMOUNT"/>
...
<subclass name="MasterCardPayment" discriminator-value="MDC"/>
<subclass name="VisaPayment" discriminator-value="VISA"/>
</class>
<class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">
<id name="id" type="long" column="TXN_ID">
<generator class="native"/>
</id>
...
<joined-subclass name="CashPayment" table="CASH_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="amount" column="CASH_AMOUNT"/>
...
</joined-subclass>
<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="amount" column="CHEQUE_AMOUNT"/>
...
</joined-subclass>
</class>]]></programlisting>
<para>
다시 한번, 우리는 <literal>Payment</literal>를 명시적으로 언급하지 않는다. 만일 우리가 <literal>Payment</literal>
인터페이스에 대해 하나의 질의를 실행할 경우-예를 들어, from Payment-, Hibernate는 <literal>CreditCardPayment</literal>
(와 그것의 서브클래스들, 왜냐하면 그것들 또한 <literal>Payment</literal>를 구현하므로), <literal>CashPayment</literal>
그리고 <literal>ChequePayment</literal> 인스턴스들을 자동적으로 반환할 것이지만
<literal>NonelectronicTransaction</literal>의 인스턴스들을 반환하지 않는다.
</para>
</sect2>
</sect1>
<sect1 id="inheritance-limitations">
<title>제약들</title>
<para>
table per concrete-class 매핑 방도에 대한 "함축적인 다형성" 접근법에는 어떤 제약들이 존재한다.
<literal>&lt;union-subclass&gt;</literal> 매핑들에 대해서는 다소 덜 제한적인 제약들이 존재한다:
</para>
<para>
다음 표는 Hibernate에서 table per concrete-class 매핑들에 대한 제약들, 그리고 함축적인 다형성에 대한 제약들을 보여준다.
</para>
<table frame="topbot">
<title>상속 매핑들의 특징들</title>
<tgroup cols='8' align='left' colsep='1' rowsep='1'>
<colspec colname='c1' colwidth="1*"/>
<colspec colname='c2' colwidth="1*"/>
<colspec colname='c3' colwidth="1*"/>
<colspec colname='c4' colwidth="1*"/>
<colspec colname='c5' colwidth="1*"/>
<colspec colname='c6' colwidth="1*"/>
<colspec colname='c7' colwidth="1*"/>
<colspec colname='c8' colwidth="1*"/>
<thead>
<row>
<entry>상속 방도</entry>
<entry>다형성 다대일</entry>
<entry>다형성 일대일</entry>
<entry>다형성 일대다</entry>
<entry>다형성 다대다</entry>
<entry>다형성 <literal>load()/get()</literal></entry>
<entry>다형성 질의들</entry>
<entry>다형성 조인들</entry>
<entry>Outer 조인 페칭</entry>
</row>
</thead>
<tbody>
<row>
<entry>table per class-hierarchy</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal></entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>지원됨</emphasis></entry>
</row>
<row>
<entry>table per subclass</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal></entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>지원됨</emphasis></entry>
</row>
<row>
<entry>table per concrete-class (union-subclass)</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal> (for <literal>inverse="true"</literal> only)</entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>지원됨</emphasis></entry>
</row>
<row>
<entry>table per concrete class (implicit polymorphism)</entry>
<entry><literal>&lt;any&gt;</literal></entry>
<entry><emphasis>지원되지 않음</emphasis></entry>
<entry><emphasis>지원되지 않음</emphasis></entry>
<entry><literal>&lt;many-to-any&gt;</literal></entry>
<entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><emphasis>지원되지 않음</emphasis></entry>
<entry><emphasis>지원되지 않음</emphasis></entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,440 @@
<chapter id="persistent-classes" revision="2">
<title>영속 클래스들</title>
<para>
영속 클래스들은 비지니스 문제의 엔티티들(예를 들어 E-Commerce 어플리케이션에서 고객이나 주문)을 구현하는
어플리케이션 내의 클래스들이다. 영속 클래스들의 인스턴스들은 영속 상태에 있는 것으로 전혀 간주되지 않는다 -
대신에 하나의 인스턴스는 transient 또는 detached 상태일 수 있다.
</para>
<para>
Hibernate는 이들 클래스들이 Plain Old Java Object (POJO) 프로그래밍 모형으로서 알려진, 몇몇 간단한
규칙들을 따를 경우에 가장 잘 동작한다. 하지만 이들 규칙들 중 어떤 것도 어려운 사양들이 아니다. 진정 Hibernate3는
당신의 영속 객체들의 특징에 대해 매우 적은 것을 가정한다. 당신은 다른 방법들로 도메인 모형을 표현할 수 있다 :
예를 들어 <literal>Map</literal> 인스턴스의 트리들을 사용하기.
</para>
<sect1 id="persistent-classes-pojo">
<title>간단한 POJO 예제</title>
<para>
대부분의 자바 어플리케이션들은 고양이과들을 표현하는 영속 클래스를 필요로 한다.
</para>
<programlisting><![CDATA[package eg;
import java.util.Set;
import java.util.Date;
public class Cat {
private Long id; // identifier
private Date birthdate;
private Color color;
private char sex;
private float weight;
private int litterId;
private Cat mother;
private Set kittens = new HashSet();
private void setId(Long id) {
this.id=id;
}
public Long getId() {
return id;
}
void setBirthdate(Date date) {
birthdate = date;
}
public Date getBirthdate() {
return birthdate;
}
void setWeight(float weight) {
this.weight = weight;
}
public float getWeight() {
return weight;
}
public Color getColor() {
return color;
}
void setColor(Color color) {
this.color = color;
}
void setSex(char sex) {
this.sex=sex;
}
public char getSex() {
return sex;
}
void setLitterId(int id) {
this.litterId = id;
}
public int getLitterId() {
return litterId;
}
void setMother(Cat mother) {
this.mother = mother;
}
public Cat getMother() {
return mother;
}
void setKittens(Set kittens) {
this.kittens = kittens;
}
public Set getKittens() {
return kittens;
}
// addKitten not needed by Hibernate
public void addKitten(Cat kitten) {
kitten.setMother(this);
kitten.setLitterId( kittens.size() );
kittens.add(kitten);
}
}]]></programlisting>
<para>
준수할 네 개의 주요 규칙들이 다음에 있다:
</para>
<sect2 id="persistent-classes-pojo-constructor" revision="1">
<title>아규먼트 없는 생성자를 구현하라 </title>
<para>
<literal>Cat</literal>은 아규먼트 없는 생성자를 갖는다. 모든 영속 클래스들은 Hibernate는
<literal>Constructor.newInstance()</literal>를 사용하여 그것들을 초기화 시킬 수 있도록 디폴트 생성자
(public이 아닐 수 있다)를 가져야 한다. 우리는 Hibernate 내에서 런타임 프락시 생성을 위한 최소한의
<emphasis>패키지</emphasis> 가시성(visibility)를 가진 디폴트 생성자를 가질 것을 강력하게 권장한다.
</para>
</sect2>
<sect2 id="persistent-classes-pojo-identifier" revision="2">
<title>identifier 프로퍼티를 제공하라(옵션)</title>
<para>
<literal>Cat</literal><literal>id</literal>로 명명된 하나의 프로퍼티를 갖는다. 이 프로퍼티는
데이터베이스 테이블의 프라이머리 키 컬럼으로 매핑된다. 이 프로퍼티는 어떤 것으로 명명될 수도 있고, 그것의 타입은
임의의 원시 타입, 원시 "wrapper" 타입, <literal>java.lang.String</literal> 또는 <literal>java.util.Date</literal>
수 있다. (만일 당신의 리거시 데이터베이스 테이블이 composite 키들을 갖고 있다면, 당신은 이들 타입들을 가진
사용자 정의 클래스를 사용할 수도 있다 - 나중에 composite 식별자들에 대한 절을 보라)
</para>
<para>
identifier 프로퍼티는 엄격하게 옵션이다. 당신은 그것을 생략할 수도 있고, Hibernate로 하여금 내부적으로
객체 식별자들을 추적하도록 할 수 있다. 하지만 우리는 이것을 권장하지 않는다.
</para>
<para>
사실, 어떤 기능은 identifier 프로퍼티를 선언하는 클래스들에 대해서만 이용 가능하다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
detached 객체들에 대한 Transitive reattachment(cascade update 또는 cascade merge)
- <xref linkend="objectstate-transitive"/>
</para>
</listitem>
<listitem>
<para>
<literal>Session.saveOrUpdate()</literal>
</para>
</listitem>
<listitem>
<para>
<literal>Session.merge()</literal>
</para>
<para>
를 보라
</para>
</listitem>
</itemizedlist>
<para>
우리는 당신이 영속 클래스들에 대해 일관되게 명명된 identifier 프로퍼티들을 선언할 것을 권장한다. 게다가 우리는
당신이 nullable 타입(예를 들어 non-primitive)을 사용할 것을 권장한다.
</para>
</sect2>
<sect2 id="persistent-classes-pojo-final">
<title>final이 아닌 클래스들을 선호하라(옵션)</title>
<para>
Hibernate의 중심 특징인, 프락시(<emphasis>proxies</emphasis>)들은 final이 아닌 영속 클래스들 또는 모두
public 메소드들로 선언된 인터페이스의 구현인 영속 클래스들에 의존한다.
</para>
<para>
당신은 Hibernate로 인터페이스를 구현하지 않은 <literal>final</literal> 클래스들을 영속화 시킬 수 있지만
당신은 lazy 연관 페칭(lazy association fetching)에 대해 프락시들을 사용할 수 없을 것이다 -그것은 퍼포먼스
튜닝을 위한 당신의 옵션들을 제한시킬 것이다.
</para>
<para>
당신은 또한 non-final 클래스들 상에 <literal>public final</literal> 메소드들을 선언하는 것을 피해야 한다.
만일 당신이 <literal>public final</literal> 메소드를 가진 클래스를 사용하고자 원할 경우, 당신은
<literal>lazy="false"</literal>를 설정함으로써 명시적으로 프락싱을 사용 불가능하도록 해야 한다.
</para>
</sect2>
<sect2 id="persistent-classes-pojo-accessors" revision="2">
<title>영속 필드들을 위한 accessor들과 mutator들을 선언하라(옵션)</title>
<para>
<literal>Cat</literal>은 그것의 모든 영속 필드들에 대해 accessor 메소드들을 선언한다. 많은 다른 ORM 도구들은
인스턴스 변수들을 직접 영속화 시킨다. 우리는 관계형 스키마와 클래스의 내부적인 데이터 구조들 사이에 간접적인 수단을
제공하는 것이 더 좋다고 믿고 있다. 디폴트로 Hibernate는 자바빈즈 스타일 프로퍼티들을 영속화 시키고, <literal>getFoo</literal>,
<literal>isFoo</literal><literal>setFoo</literal> 형식의 메소드 이름들을 인지한다. 당신은 진정으로
특정 프로퍼티에 대한 직접적인 필드 접근으로 전환할 수도 있다.
</para>
<para>
프로퍼티들은 public으로 선언될 필요가 <emphasis>없다</emphasis> - Hibernate는 디폴트로
<literal>protected</literal> get/set 쌍 또는 <literal>private</literal> get/set
쌍을 가진 프로퍼티를 영속화 시킬 수 있다.
</para>
</sect2>
</sect1>
<sect1 id="persistent-classes-inheritance">
<title>상속 구현하기</title>
<para>
서브클래스는 또한 첫 번째 규칙들과 두 번째 규칙들을 주시해야 한다. 그것은 슈퍼클래스 <literal>Cat</literal>으로부터
그것의 identifier 프로퍼티를 상속받는다.
</para>
<programlisting><![CDATA[package eg;
public class DomesticCat extends Cat {
private String name;
public String getName() {
return name;
}
protected void setName(String name) {
this.name=name;
}
}]]></programlisting>
</sect1>
<sect1 id="persistent-classes-equalshashcode" revision="1">
<title><literal>equals()</literal><literal>hashCode()</literal> 구현하기</title>
<para>
만일 당신이
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
하나의 <literal>Set</literal> 속에 영속 클래스들의 인스턴스들을 집어넣고자 의도하고
(many-valued 연관들에 대해 권장되는 방법)
<emphasis>그리고</emphasis>
</para>
</listitem>
<listitem>
<para>
detached 인스턴스들의 reattachment(재첨부)를 사용하고자 의도하는
</para>
</listitem>
</itemizedlist>
<para>
경우에 당신은 <literal>equals()</literal><literal>hashCode()</literal> 메소드들을 오버라이드 시켜야 한다.
</para>
<para>
Hibernate는 특정 session 범위 내에서만 persistent identity(데이터베이스 행)과 Java identity의 같음을 보장한다.
따라서 우리가 다른 세션들에서 검색된 인스턴스들을 혼합시키자마자, 우리가 <literal>Set</literal>들에 대해 유의미하게
만들고자 원할 경우, 우리는 <literal>equals()</literal><literal>hashCode()</literal>를 구현해야 한다.
</para>
<para>
가장 명백한 방법은 두 객체들의 identifier 값을 비교함으로써 <literal>equals()</literal>/<literal>hashCode()</literal>
구현하는 것이다. 만일 그 값이 동일하다면, 둘다 동일한 데이터베이스 행이어야 하고, 그러므로 그것들은 같다(둘다 하나의
<literal>Set</literal>에 추가되는 경우에, 우리는 <literal>Set</literal> 속에서 하나의 요소만을 갖게 될 것이다).
불행하게도, 우리는 생성되는 식별자들을 갖는 그 접근법을 사용할 수 없다! Hibernate는 오직 식별자 값들을 영속화 되는 객체들에
할당할 것이고, 새로이 생성된 인스턴스는 임의의 identifier 값을 갖지 않을 것이다! 만일 인스턴스가 저장되지 않고 현재 하나의
<literal>Set</literal> 속에 있을 경우에, 그것을 저장하는것은 하나의 식별자 값을 그 객체에게 할당할 것이다. 만일
<literal>equals()</literal><literal>hashCode()</literal>가 그 식별자 값에 기초할 경우, hash 코드는
<literal>Set</literal>의 계약을 파기하여 변경될 것이다. 이 문제에 대한 전체 논의에 대해서는 Hibernate 웹 사이트를 보라.
이것은 Hibernate 쟁점이 아닌, 객체 identity와 equality에 관한 통상의 자바 의미론임을 노트하라.
</para>
<para>
우리는 <emphasis>Business key equality</emphasis>를 사용하여 <literal>equals()</literal><literal>hashCode()</literal>
구현할 것 권장한다. Business key equality는 <literal>equals()</literal> 메소드가 비지니스 키, 즉 실세계에서 우리의 인스턴스를
식별하게 될 키(<emphasis>natural</emphasis> 후보 키)를 형성하는 프로퍼티들만을 비교한다는 점을 의미한다 :
</para>
<programlisting><![CDATA[public class Cat {
...
public boolean equals(Object other) {
if (this == other) return true;
if ( !(other instanceof Cat) ) return false;
final Cat cat = (Cat) other;
if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
if ( !cat.getMother().equals( getMother() ) ) return false;
return true;
}
public int hashCode() {
int result;
result = getMother().hashCode();
result = 29 * result + getLitterId();
return result;
}
}]]></programlisting>
<para>
하나의 비지니스 키는 데이터베이스 프라이머리 키 후보 만큼 견고하지 않아야 한다(<xref linkend="transactions-basics-identity"/>를 보라).
대개 변경할 수 없는 프로퍼티 또는 유일한(unique) 프로퍼티는 대개 비지니스 키에 대한 좋은 후보들이다.
</para>
</sect1>
<sect1 id="persistent-classes-dynamicmodels">
<title>동적인 모형들</title>
<para>
<emphasis>다음 특징들은 현재 실험적으로 고려되고 있으며 장래에는 변경될 수 있음을 노트하라.</emphasis>
</para>
<para>
영속 엔티티들은 반드시 실행시에 POJO 클래스들로 또는 자바빈즈 객체들로 표현되어야할 필요는 없다. Hibernate는
또한 (실행 시에 <literal>Map</literal>들을 가진 <literal>Map</literal>들을 사용하여) 동적인 모형들을 지원하고
DOM4J 트리들로서 엔티티들에 대한 표현을 지원한다. 이 접근법으로, 당신은 영속 클래스들을 작성하지 않고, 오직 매핑 파일들
만을 작성한다.
</para>
<para>
디폴트로, Hibernate는 통산의 POJO 모드로 동작한다. 당신은 <literal>default_entity_mode</literal> 구성 옵션을
사용하여 특별한 <literal>SessionFactory</literal>에 대해 디폴트 엔티티 표현 모드를 설정할 수 있다
(<xref linkend="configuration-optional-properties"/>을 보라).
</para>
<para>
다음 예제들은<literal>Map</literal>들을 사용하는 표현을 설명한다. 먼저 매핑 파일에서, <literal>entity-name</literal>
클래스 이름 대신에(또는 클래스 이름에 덧붙여) 선언되어야 한다:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class entity-name="Customer">
<id name="id"
type="long"
column="ID">
<generator class="sequence"/>
</id>
<property name="name"
column="NAME"
type="string"/>
<property name="address"
column="ADDRESS"
type="string"/>
<many-to-one name="organization"
column="ORGANIZATION_ID"
class="Organization"/>
<bag name="orders"
inverse="true"
lazy="false"
cascade="all">
<key column="CUSTOMER_ID"/>
<one-to-many class="Order"/>
</bag>
</class>
</hibernate-mapping>]]></programlisting>
<para>
심지어 비록 연관들이 대상(target) 클래스 이름들을 사용하여 선언될지라도, 연관들의 대상(target) 타입은 또한 POJO가 아닌
동적인 엔티티일 수 있음을 노트하라.
</para>
<para>
<literal>SessionFactory</literal>에 대한 디폴트 엔티티 모드를 <literal>dynamic-map</literal>으로 설정한 후에,
우리는 <literal>Map</literal>들을 가진 <literal>Map</literal>들에 대해 실행 시에 작업할 수 있다:
</para>
<programlisting><![CDATA[Session s = openSession();
Transaction tx = s.beginTransaction();
Session s = openSession();
// Create a customer
Map david = new HashMap();
david.put("name", "David");
// Create an organization
Map foobar = new HashMap();
foobar.put("name", "Foobar Inc.");
// Link both
david.put("organization", foobar);
// Save both
s.save("Customer", david);
s.save("Organization", foobar);
tx.commit();
s.close();]]></programlisting>
<para>
dynamic 매핑의 장점들은 엔티티 클래스 구현에 대한 필요 없이도 프로토타이핑을 위한 빠른 전환 시간이다. 하지만 당신은
컴파일 시 타입 체킹을 잃고 실행 시에 많은 예외상황들을 다루게 될 것이다. Hibernate 매핑 덕분에,
나중에 고유한 도메인 모형 구현을 상단에 추가하는 것이 허용되어서, 데이터베이스 스키마가 쉽게 정규화 되고 소리가 울려 퍼질 수 있다.
</para>
<para>
엔티티 표현 모드들은 또한 하나의 단위 <literal>Session</literal> 기준에 대해 설정될 수 있다:
</para>
<programlisting><![CDATA[Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
// Create a customer
Map david = new HashMap();
david.put("name", "David");
dynamicSession.save("Customer", david);
...
dynamicSession.flush();
dynamicSession.close()
...
// Continue on pojoSession
]]></programlisting>
<para>
<literal>EntityMode</literal>를 사용하는 <literal>getSession()</literal>에 대한 호출은
<literal>SessionFactory</literal>가 아닌, <literal>Session</literal> API에 대한 것임을 노트하길 바란다.
그 방법으로, 새로운 <literal>Session</literal>은 기본 JDBC 커넥션, 트랜잭션, 그리고 다른 컨텍스트 정보를 공유한다.
이것은 당신이 두 번째 <literal>Session</literal> 상에서 <literal>flush()</literal><literal>close()</literal>
호출하지 말아야 하고, 또한 트랜잭션 및 커넥션 핸들링을 주된 작업 단위에게 맡긴다는 점을 의미한다.
</para>
<para>
XML 표현 가용성들에 대한 추가 정보는 <xref linkend="xml"/>에서 찾을 수 있다.
</para>
</sect1>
<para>
TODO: property 패키지와 proxy 패키지 내에 user-extension 프레임웍을 문서화 할 것.
</para>
</chapter>

View File

@ -0,0 +1,419 @@
<chapter id="querycriteria">
<title>Criteria 질의들</title>
<para>
Hibernate는 직관적인, 확장 가능한 criteria query API를 특징 짓는다.
</para>
<sect1 id="querycriteria-creating">
<title><literal>Criteria</literal> 인스턴스 생성하기</title>
<para>
<literal>org.hibernate.Criteria</literal>인터페이스는 특정 영속 클래스에 대한 질의를 표현한다.
<literal>Session</literal><literal>Criteria</literal> 인스턴스들에 대한 팩토리이다.
</para>
<programlisting><![CDATA[Criteria crit = sess.createCriteria(Cat.class);
crit.setMaxResults(50);
List cats = crit.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-narrowing">
<title>결과 셋 제한하기</title>
<para>
개별적인 질의 기준은 <literal>org.hibernate.criterion.Criterion</literal> 인터페이스의 인스턴스이다.
<literal>org.hibernate.criterion.Restrictions</literal> 클래스는 어떤 미리 만들어진 <literal>Criterion</literal>
타입들을 얻는 팩토리 메소드들을 정의한다.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.between("weight", minWeight, maxWeight) )
.list();]]></programlisting>
<para>
제한들은 논리적으로 그룹지워질 수도 있다.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.or(
Restrictions.eq( "age", new Integer(0) ),
Restrictions.isNull("age")
) )
.list();]]></programlisting>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
.add( Restrictions.disjunction()
.add( Restrictions.isNull("age") )
.add( Restrictions.eq("age", new Integer(0) ) )
.add( Restrictions.eq("age", new Integer(1) ) )
.add( Restrictions.eq("age", new Integer(2) ) )
) )
.list();]]></programlisting>
<para>
미리 만들어진 criterion 타입들(<literal>Restrictions</literal> 서브클래스들)의 영역이 꽤 존재하지만, 특히 유용한 것은
당신으로 하여금 SQL을 직접 지정하도록 해준다.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.sql("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
.list();]]></programlisting>
<para>
질의된 엔티티의 행 alias에 의해 대체된 <literal>{alias}</literal> placeholder.
</para>
<para>
criterion을 얻는 대안적인 접근법은 <literal>Property</literal> 인스턴스로부터 그것을 얻는 것이다. 당신은
<literal>Property.forName()</literal>을 호출하여 <literal>Property</literal>를 생성시킬 수 있다.
</para>
<programlisting><![CDATA[
Property age = Property.forName("age");
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.disjunction()
.add( age.isNull() )
.add( age.eq( new Integer(0) ) )
.add( age.eq( new Integer(1) ) )
.add( age.eq( new Integer(2) ) )
) )
.add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-ordering">
<title>결과들을 순서지우기(ordering)</title>
<para>
당신은 <literal>org.hibernate.criterion.Order</literal>를 사용하여 결과들을 순서(ordering)지울 수 있다.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%")
.addOrder( Order.asc("name") )
.addOrder( Order.desc("age") )
.setMaxResults(50)
.list();]]></programlisting>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Property.forName("name").like("F%") )
.addOrder( Property.forName("name").asc() )
.addOrder( Property.forName("age").desc() )
.setMaxResults(50)
.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-associations">
<title>연관들</title>
<para>
당신은 <literal>createCriteria()</literal>를 사용하여 연관들을 네비게이트함으로써 관계된 엔티티들에 대한 컨스트레인트들을
쉽게 지정할 수 있다.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%")
.createCriteria("kittens")
.add( Restrictions.like("name", "F%")
.list();]]></programlisting>
<para>
두 번째 <literal>createCriteria()</literal><literal>Criteria</literal>의 새로운 인스턴스를 반환하며, 그것은
<literal>kittens</literal> 콜렉션의 요소들을 참조한다는 점을 노트하라.
</para>
<para>
다음 대체 형식은 어떤 환경들에서 유용하다.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.createAlias("kittens", "kt")
.createAlias("mate", "mt")
.add( Restrictions.eqProperty("kt.name", "mt.name") )
.list();]]></programlisting>
<para>
(<literal>createAlias()</literal><literal>Criteria</literal>의 새로운 인스턴스를 생성시키지 않는다.)
</para>
<para>
앞의 두 개의 질의들에 의해 반환된 <literal>Cat</literal> 인스턴스들에 의해 보관된 kittens 콜렉션들은 criteria에 의해
사전-필터링되지 <emphasis>않는다</emphasis>는 점을 노트하라! 만일 당신이 criteria(기준)과 일치하는 고양이 새끼들을
단지 검색하고자 원할 경우, 당신은 <literal>returnMaps()</literal>를 사용해야 한다.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.createCriteria("kittens", "kt")
.add( Restrictions.eq("name", "F%") )
.returnMaps()
.list();
Iterator iter = cats.iterator();
while ( iter.hasNext() ) {
Map map = (Map) iter.next();
Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
Cat kitten = (Cat) map.get("kt");
}]]></programlisting>
</sect1>
<sect1 id="querycriteria-dynamicfetching" revision="1">
<title>동적인 연관 페칭</title>
<para>
당신은 <literal>setFetchMode()</literal>를 사용하여 실행 시에 연관 페칭 의미를 지정할 수 있다.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.setFetchMode("mate", FetchMode.EAGER)
.setFetchMode("kittens", FetchMode.EAGER)
.list();]]></programlisting>
<para>
이 질의는 outer 조인으로 <literal>mate</literal><literal>kittens</literal> 모두를 페치할 것이다. 추가 정보는
<xref linkend="performance-fetching"/>을 보라.
</para>
</sect1>
<sect1 id="querycriteria-examples">
<title>예제 질의들</title>
<para>
<literal>org.hibernate.criterion.Example</literal> 클래스는 주어진 인스턴스로부터 질의 기준(criterion)을 구조화
시키는 것을 당신에게 허용해준다.
</para>
<programlisting><![CDATA[Cat cat = new Cat();
cat.setSex('F');
cat.setColor(Color.BLACK);
List results = session.createCriteria(Cat.class)
.add( Example.create(cat) )
.list();]]></programlisting>
<para>
버전 프로퍼티들, 식별자들, 연관관계들이 무시된다. 디폴트로 null 값 프로퍼티들이 제외된다.
</para>
<para>
당신은 <literal>Example</literal>이 적용되는 방법을 조정할 수 있다.
</para>
<programlisting><![CDATA[Example example = Example.create(cat)
.excludeZeroes() //exclude zero valued properties
.excludeProperty("color") //exclude the property named "color"
.ignoreCase() //perform case insensitive string comparisons
.enableLike(); //use like for string comparisons
List results = session.createCriteria(Cat.class)
.add(example)
.list();]]></programlisting>
<para>
당신은 연관된 객체들에 대한 criteria(기준)을 위치지우는데 examples를 사용할 수 있다.
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.add( Example.create(cat) )
.createCriteria("mate")
.add( Example.create( cat.getMate() ) )
.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-projection">
<title>Projections, aggregation 그리고 grouping</title>
<para>
<literal>org.hibernate.criterion.Projections</literal> 클래스는 <literal>Projection</literal> 인스턴스들에
대한 팩토리이다. 우리는 <literal>setProjection()</literal>을 호출하여 하나의 질의에 projection(투사,투영)을 적용시킨다.
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.rowCount() )
.add( Restrictions.eq("color", Color.BLACK) )
.list();]]></programlisting>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount() )
.add( Projections.avg("weight") )
.add( Projections.max("weight") )
.add( Projections.groupProperty("color") )
)
.list();]]></programlisting>
<para>
criteria 질의 내에서는 명시적인 "group by"가 필수적이지 않다. 어떤 projection 타입들은
<emphasis>grouping projections</emphasis>들이게끔 정의되고, 그것은 또한 SQL <literal>group by</literal> 절 속에
나타난다.
</para>
<para>
alias는 선택적으로 projection에 할당될 수 있어서, 투사된(projected) 값은 제한(restriction)들 또는 ordering들 내에서
참조될 수 있다. 다음은 이것을 행하는 두 개의 다른 방법들이다:
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
.addOrder( Order.asc("colr") )
.list();]]></programlisting>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.groupProperty("color").as("colr") )
.addOrder( Order.asc("colr") )
.list();]]></programlisting>
<para>
<literal>alias()</literal> 메소드와 <literal>as()</literal> 메소드는 또 다른 alias 된
<literal>Projection</literal>의 인스턴스 내에 하나의 projection 인스턴스를 간단하게 포장한다. 지름길로서,
당신이 projection을 projection 리스트에 추가할 때 당신은 alias를 할당할 수 있다:
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount(), "catCountByColor" )
.add( Projections.avg("weight"), "avgWeight" )
.add( Projections.max("weight"), "maxWeight" )
.add( Projections.groupProperty("color"), "color" )
)
.addOrder( Order.desc("catCountByColor") )
.addOrder( Order.desc("avgWeight") )
.list();]]></programlisting>
<programlisting><![CDATA[List results = session.createCriteria(Domestic.class, "cat")
.createAlias("kittens", "kit")
.setProjection( Projections.projectionList()
.add( Projections.property("cat.name"), "catName" )
.add( Projections.property("kit.name"), "kitName" )
)
.addOrder( Order.asc("catName") )
.addOrder( Order.asc("kitName") )
.list();]]></programlisting>
<para>
당신은 또한 projection들을 표현하는데 <literal>Property.forName()</literal>을 사용할 수 있다:
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Property.forName("name") )
.add( Property.forName("color").eq(Color.BLACK) )
.list();]]></programlisting>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount().as("catCountByColor") )
.add( Property.forName("weight").avg().as("avgWeight") )
.add( Property.forName("weight").max().as("maxWeight") )
.add( Property.forName("color").group().as("color" )
)
.addOrder( Order.desc("catCountByColor") )
.addOrder( Order.desc("avgWeight") )
.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-detachedqueries">
<title>Detached 질의들과 서브질의들</title>
<para>
<literal>DetachedCriteria</literal> 클래스는 당신에게 세션 영역의 외부에서 질의를 생성시키도록 하고, 그런 다음 나중에
어떤 임의의 <literal>Session</literal>을 사용하여 그것을 실행하도록 한다.
</para>
<programlisting><![CDATA[DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
.add( Property.forName("sex").eq('F') );
Session session = ....;
Transaction txn = session.beginTransaction();
List results = query.getExecutableCriteria(session).setMaxResults(100).list();
txn.commit();
session.close();]]></programlisting>
<para>
<literal>DetachedCriteria</literal>는 또한 서브질의를 표현하는데 사용된다. 서브질의들을 포함하는 Criterion 인스턴스들은
<literal>Subqueries</literal> 또는 <literal>Property</literal>를 통해 얻어질 수 있다.
</para>
<programlisting><![CDATA[DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
.setProjection( Property.forName("weight").avg() );
session.createCriteria(Cat.class)
.add( Property.forName("weight).gt(avgWeight) )
.list();]]></programlisting>
<programlisting><![CDATA[DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
.setProjection( Property.forName("weight") );
session.createCriteria(Cat.class)
.add( Subqueries.geAll("weight", weights) )
.list();]]></programlisting>
<para>
심지어 상관관계 지워진 서브질의들이 가능하다:
</para>
<programlisting><![CDATA[DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
.setProjection( Property.forName("weight").avg() )
.add( Property.forName("cat2.sex").eqProperty("cat.sex") );
session.createCriteria(Cat.class, "cat")
.add( Property.forName("weight).gt(avgWeightForSex) )
.list();]]></programlisting>
</sect1>
<!--TODO: ResultSetTransformer + aliasing. AliasToBeanTransformer는 -JDO2에서 setResultClass와 유사한- 임의적인
사용자 객체들을 반환하는 것을 허용한다. ResultTransformer에 대한 일반적인 사용이 또한 설명될 수 있다. -->
<sect1 id="query-criteria-naturalid">
<title>natural 식별자에 의한 질의들</title>
<para>
대부분의 질의들에서, criteria 질의들을 포함하여, 질의 캐시는 매우 효율적이지 않다. 왜냐하면 질의 캐시 비유효성이
너무 자주 발생하기 때문이다. 하지만, 우리가 캐시 비유효성 알고리즘을 최적화 시킬 수 있는 한 가지 특별한 종류의 질의가
존재한다: 상수 natural 키에 의한 룩업. 몇몇 어플리케이션들에서, 이런 종류의 질의가 자주 발생한다. criteria API는
이 쓰임새를 위한 특별한 설비를 제공한다.
</para>
<para>
첫번째로 당신은 <literal>&lt;natural-id&gt;</literal>를 사용하여 당신의 엔티티에 대한 natural 키를 매핑
시켜야 하고, second-level 캐시 사용을 가능하게 해야 한다.
</para>
<programlisting><![CDATA[<class name="User">
<cache usage="read-write"/>
<id name="id">
<generator class="increment"/>
</id>
<natural-id>
<property name="name"/>
<property name="org"/>
</natural-id>
<property name="password"/>
</class>]]></programlisting>
<para>
이 기능은 <emphasis>가변성 있는</emphasis> natural 키들을 가진 엔티티들의 용도로 고안되어 있지 않음을 노트하라.
</para>
<para>
다음으로 , Hibernate 질의 캐시를 사용 가능하도록 하라.
</para>
<para>
이제 <literal>Restrictions.naturalId()</literal>는 캐시 알고리즘을 보다 효율적으로 사용할 수 있도록 우리에게 허용해준다.
</para>
<programlisting><![CDATA[session.createCriteria(User.class)
.add( Restrictions.naturalId()
.set("name", "gavin")
.set("org", "hb")
).setCacheable(true)
.uniqueResult();]]></programlisting>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,457 @@
<chapter id="querysql" revision="2">
<title>Native SQL</title>
<para>
당신은 또한 당신의 데이터베이스의 native SQL dialect로 질의들을 표현할 수도 있다. 당신이 오라클의 질의 힌트들 또는
<literal>CONNECT</literal> 키워드와 같은 데이터베이스 지정적인 특징들을 활용하고자 원할 경우에 이것이 유용하다.
그것은 또한 직접적인 SQL/JDBC 기반의 어플리케이션으로부터 Hibernate로의 보다 명료한 이전 경로를 제공한다.
</para>
<para>
Hibernate3은 또한 모든 create, update, delete, load 오퍼레이션들에 대해 (내장 프로시저들을 포함하여) 손으로 작성된 SQL을
지정하는 것을 당신에게 허용해준다.
</para>
<sect1 id="querysql-creating">
<title>native SQL <literal>Query</literal> 생성시키기</title>
<para>
SQL 질의들은 <literal>SQLQuery</literal> 인터페이스를 통해 제어되고, 그것은
<literal>Session.createSQLQuery()</literal>를 호출하여 얻어진다.
</para>
<programlisting><![CDATA[List cats = sess.createSQLQuery("select {cat.*} from cats cat")
.addEntity("cat", Cat.class)
.setMaxResults(50)
.list();]]></programlisting>
<para>
이 질의는 다음을 지정했다:
</para>
<itemizedlist>
<listitem>
<para>
Hibernate가 컬럼 alias들을 도입하기 위한, placeholder를 가진, SQL 질의 문자열
</para>
</listitem>
<listitem>
<para>
그 질의에 의해 반환된 엔티티와 그것의 SQL 테이블 alias
</para>
</listitem>
</itemizedlist>
<para>
<literal>addEntity()</literal> 메소드는 SQL 테이블 alias들을 엔티티 클래스들과 연관지우고, 질의 결과 셋의 형태를 결정한다.
</para>
<para>
<literal>addJoin()</literal> 메소드는 다른 엔티티들과 콜렉션들에 대한 연관들을 로드시키는데 사용될 수 있다.
</para>
<programlisting><![CDATA[List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten
where kitten.mother = cat.id"
)
.addEntity("cat", Cat.class)
.addJoin("kitten", "cat.kittens")
.list();]]></programlisting>
<para>
native SQL 질의는 간단한 스칼라 값을 반환하거나 스칼라들과 엔티티들의 조합을 반환할 수도 있다.
</para>
<programlisting><![CDATA[Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight
from cats cat")
.addScalar("maxWeight", Hibernate.DOUBLE);
.uniqueResult();]]></programlisting>
</sect1>
<sect1 id="querysql-aliasreferences">
<title>Alias 참조와 프로퍼티 참조</title>
<para>
위에서 사용된 <literal>{cat.*}</literal> 표기는 "모든 프로퍼티"에 대한 약어이다. 다른 방법으로, 당신은 명시적으로 컬럼들을
리스트할 수 있지만, 심지어 이 경우 조차도 우리는 Hibernate로 하여금 각각의 프로퍼티에 대해 SQL 컬럼 alias들을 끼워넣도록 해야 한다.
컬럼 alias에 대한 placeholder는 테이블 alias 수식어가 붙은 프로퍼티 이름이다. 다음 예제에서, 우리는 매핑 메타데이터에 선언된
것에 대해 다른 테이블 (<literal>cat_log</literal>)로부터 <literal>Cat</literal>들을 검색한다. 우리는 심지어 우리가
좋아할 경우 where 절 속에 프로퍼티 alias들을 사용할 수도 있음을 주목하라.
</para>
<para>
The <literal>{}</literal>-syntax is <emphasis>not</emphasis> required for named queries.
See <xref linkend="querysql-namedqueries"/>
</para>
<programlisting><![CDATA[String sql = "select cat.originalId as {cat.id}, " +
"cat.mateid as {cat.mate}, cat.sex as {cat.sex}, " +
"cat.weight*10 as {cat.weight}, cat.name as {cat.name} " +
"from cat_log cat where {cat.mate} = :catId"
List loggedCats = sess.createSQLQuery(sql)
.addEntity("cat", Cat.class)
.setLong("catId", catId)
.list();]]></programlisting>
<para>
<emphasis>노트:</emphasis> 만일 당신이 각각의 프로퍼티를 명시적으로 리스트할 경우, 당신은 그 클래스와
<emphasis>그것의 서브클래스들</emphasis>의 모든 프로퍼티들을 포함해야 한다!
</para>
</sect1>
<sect1 id="querysql-namedqueries" revision="2">
<title>명명된 SQL 질의들</title>
<para>
명명된 SQL 질의들은 HQL 질의와 동일한 방법으로 매핑 문서 속에 정의될 수 있고 정확하게 호출될 수도 있다. 이 경우에, 우리는
<literal>addEntity()</literal> 호출을 필요로 하지 <emphasis>않는다</emphasis>.
</para>
<programlisting><![CDATA[<sql-query name="persons">
<return alias="person" class="eg.Person"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex}
FROM PERSON person
WHERE person.NAME LIKE :namePattern
</sql-query>]]></programlisting>
<programlisting><![CDATA[List people = sess.getNamedQuery("persons")
.setString("namePattern", namePattern)
.setMaxResults(50)
.list();]]></programlisting>
<para>
The <literal>&lt;return-join&gt;</literal> and <literal>&lt;load-collection&gt;</literal>
elements are used to join associations and define queries which initialize collections,
respectively.
</para>
<programlisting><![CDATA[<sql-query name="personsWith">
<return alias="person" class="eg.Person"/>
<return-join alias="address" property="person.mailingAddress"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
adddress.STREET AS {address.street},
adddress.CITY AS {address.city},
adddress.STATE AS {address.state},
adddress.ZIP AS {address.zip}
FROM PERSON person
JOIN ADDRESS adddress
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
</sql-query>]]></programlisting>
<para>
명명된 SQL 질의는 스칼라 값을 반환할수도 있다. 당신은 <literal>&lt;return-scalar&gt;</literal> 요소를 사용하여
컬럼 alias와 Hibernate 타입을 지정해야 한다:
</para>
<programlisting><![CDATA[<sql-query name="mySqlQuery">
<return-scalar column="name" type="string"/>
<return-scalar column="age" type="long"/>
SELECT p.NAME AS name,
p.AGE AS age,
FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
</sql-query>]]></programlisting>
<sect2 id="propertyresults">
<title>명시적으로 column/alias 이름들을 지정하는데 return-property 사용하기</title>
<para>
Hibernate로 하여금 그것 자신의 alias들을 끼워넣도록 하기 위해 <literal>{}</literal>-구문을 사용하는 것 대신에,
<literal>&lt;return-property&gt;</literal>로서 당신은 사용할 컬럼 alias들이 무엇인지를 Hibernate에게 명시적으로
알려줄 수 있다.
</para>
<programlisting><![CDATA[<sql-query name="mySqlQuery">
<return alias="person" class="eg.Person">
<return-property name="name" column="myName"/>
<return-property name="age" column="myAge"/>
<return-property name="sex" column="mySex"/>
</return>
SELECT person.NAME AS myName,
person.AGE AS myAge,
person.SEX AS mySex,
FROM PERSON person WHERE person.NAME LIKE :name
</sql-query>
]]></programlisting>
<para>
<literal>&lt;return-property&gt;</literal>는 또한 다중 컬럼들에 대해 동작한다. 이것은 다중-컬럼 프로퍼티들에
대한 fine grained 제어를 허용할 수 없는 <literal>{}</literal>-구문을 가진 제약을 해결해준다.
</para>
<programlisting><![CDATA[<sql-query name="organizationCurrentEmployments">
<return alias="emp" class="Employment">
<return-property name="salary">
<return-column name="VALUE"/>
<return-column name="CURRENCY"/>
</return-property>
<return-property name="endDate" column="myEndDate"/>
</return>
SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
FROM EMPLOYMENT
WHERE EMPLOYER = :id AND ENDDATE IS NULL
ORDER BY STARTDATE ASC
</sql-query>]]></programlisting>
<para>
이 예제에서 우리는 끼워넣기(injection)를 위해 <literal>{}</literal>-구문과 함께 <literal>&lt;return-property&gt;</literal>
사용했음을 주목하라. 사용자들이 컬럼과 프로퍼티들을 참조하고자 원하는 방법을 선택하는 것을 사용자들에게 허용해줌으로써.
</para>
<para>
만일 당신의 매핑이 한 개의 판별자(discriminator )를 가질 경우 당신은 판별자 컬럼을 지정하는데
<literal>&lt;return-discriminator&gt;</literal>를 사용해야 한다.
</para>
</sect2>
<sect2 id="sp_query">
<title>질의를 위한 내장 프로시저 사용하기</title>
<para>
Hibernate 3은 내장 프로시저들을 통한 질의들에 대한 지원을 도입한다. 내장 프로시저들은 Hibernate와 동작하는 것이
가능하도록 첫 번째 출력-파라미터로서 한 개의 결과셋을 반환해야 한다. Oracle9 이상의 버전에서 그런 내장 프로시저에
대한 예제는 다음과 같다:
</para>
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments
RETURN SYS_REFCURSOR
AS
st_cursor SYS_REFCURSOR;
BEGIN
OPEN st_cursor FOR
SELECT EMPLOYEE, EMPLOYER,
STARTDATE, ENDDATE,
REGIONCODE, EID, VALUE, CURRENCY
FROM EMPLOYMENT;
RETURN st_cursor;
END;]]></programlisting>
<para>
Hibernate에서 이 질의를 사용하기 위해 당신은 하나의 명명된 질의(a named query)를 통해 그것을 매핑할 필요가 있다.
</para>
<programlisting><![CDATA[<sql-query name="selectAllEmployees_SP" callable="true">
<return alias="emp" class="Employment">
<return-property name="employee" column="EMPLOYEE"/>
<return-property name="employer" column="EMPLOYER"/>
<return-property name="startDate" column="STARTDATE"/>
<return-property name="endDate" column="ENDDATE"/>
<return-property name="regionCode" column="REGIONCODE"/>
<return-property name="id" column="EID"/>
<return-property name="salary">
<return-column name="VALUE"/>
<return-column name="CURRENCY"/>
</return-property>
</return>
{ ? = call selectAllEmployments() }
</sql-query>]]></programlisting>
<para>
내장 프로시저들은 현재 스칼라들과 엔티티들 만을 반환함을 주목하라. <literal>&lt;return-join&gt;</literal>
<literal>&lt;load-collection&gt;</literal>은 지원되지 않는다.
</para>
<sect3 id="querysql-limits-storedprocedures">
<title>내장 프로시저들을 사용하는 규칙들/제약들</title>
<para>
Hibernate에서 내장 프로시저들을 사용하기 위해서 프로시저들은 다음 몇몇 규칙들을 따라야 한다. 만일 그것들이 그들 규칙들을
따르지 않을 경우 그것들은 Hibernate와 함께 사용 불가능하다. 만일 당신이 여전히 이들 프로시저들을 사용하고자 원할 경우,
당신은 <literal>session.connection()</literal>을 통해 그것들을 실행시켜야 한다. 데이터베이스 벤더들이 다른 내장
프로시저 의미론/구문을 갖고 있기 때문에, 규칙들은 각각의 데이터베이스에 따라 차이가 난다.
</para>
<para>
내장 프로시저 질의들은 <literal>setFirstResult()/setMaxResults()</literal>로서 쪽매김 될 수 없다.
</para>
<para>
Oracle의 경우 다음 규칙들이 적용된다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
프로시저는 한 개의 결과 셋을 반환해야 한다. 이것은 Oracle 9 또는 10에서 한 개의 <literal>SYS_REFCURSOR</literal>
반환함으로써 행해진다. Oracle에서 당신은 한 개의 <literal>REF CURSOR</literal> 타입을 정의할 필요가 있다.
</para>
</listitem>
<listitem>
<para>
권장되는 형식은 <literal>{ ? = call procName(&lt;parameters&gt;) }</literal> 또는
<literal>{ ? = call procName }</literal>이다.(이것은 Hibernate 규칙이라기 보다는 Oracle 규칙이다.)
</para>
</listitem>
</itemizedlist>
<para>
Sybase 또는 MS SQL server의 경우 다음 규칙들이 적용된다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
프로시저는 한 개의 결과 셋을 반환해야 한다. 이들 서버들이 여러 개의 결과셋들과 업데이트 카운트들을 반환 할수 있다/할 것이이므로,
Hibernate는 결과들을 반복 순환할 것이고 그것의 반환 값으로서 하나의 결과 셋인 첫 번째 결과를 취할 것이다. 그 밖의 모든 것은
폐기될 것이다.
</para>
</listitem>
<listitem>
<para>
만일 당신이 당신의 프로시저 내에 <literal>SET NOCOUNT ON</literal>을 이용 가능하게 할 수 있다면 그것은 아마
보다 효율적이게 될 것이지만 이것은 필요 조건이 아니다.
</para>
</listitem>
</itemizedlist>
</sect3>
</sect2>
</sect1>
<sect1 id="querysql-cud">
<title>create, update 그리고 delete를 위한 맞춤형 SQL</title>
<para>
Hibernate3는 create, update, delete 오퍼레이션들을 위한 맞춤형 문장들을 사용할 수 있다. Hibernate에서 클래스와 콜렉션
영속자들은 구성 시에 생성된 문자열들의 집합(insertsql, deletesql, updatesql 등)을 이미 포함하고 있다.
<literal>&lt;sql-insert&gt;</literal>, <literal>&lt;sql-delete&gt;</literal>,
<literal>&lt;sql-update&gt;</literal> 매핑 태그들은 이들 문자열들을 오버라이드 시킨다:
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class>]]></programlisting>
<para>
SQL이 당신의 데이터베이스 내에서 직접 실행되어서, 당신이 좋아하는 임의의 dialect를 사용하는 것이 자유롭다. 만일 당신이 데이터베이스
지정적인 SQL을 사용할 경우 이것은 물론 당신의 매핑의 이식성을 감소시킬 것이다.
</para>
<para>
만일 <literal>callable</literal> 속성이 설정되면 내장 프로시저들이 지원된다:
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
<sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
<sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
</class>]]></programlisting>
<para>
위치 파라미터들은 Hibernate가 그것들을 기대하는 것과 같은 순서가 되어야 하므로, 위치 파라미터들의 순서는 현재 절대적으로 중요하다.
</para>
<para>
당신은 <literal>org.hiberate.persister.entity</literal> 레벨로 디버그 로깅을 사용 가능하게 함으로써 예상된 순서를 볼 수
있다. 이 레벨을 이용 가능하게 하면 Hibernate는 엔티티들을 생성시키고, 업데이트하고, 삭제하는데 사용되는 정적인 SQL을 출력할 것이다.
(예상되는 결과를 보려면, Hibernate 생성된 정적인 sql을 오버라이드 시키게 매핑 파일들 속에 당신의 맞춤형 SQL을 포함시키지 않도록 염두에
두라.)
</para>
<para>
Hibernate가 문장의 성공을 위해 몇몇 실행 시 체크들을 행하므로, 내장 프로시저들은 대부분의 경우들(읽기:다른 경우들 보다 그것을 더 잘
행한다)에서 insert되고/업데이트되고/삭제된 행들의 개수를 반환하는데 필요하다. Hibernate는 항상 CUD 오퍼레이션들에 대한 숫자
출력 파라미터로서 첫 번째 문장 파라미터를 등록시킨다:
</para>
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
RETURN NUMBER IS
BEGIN
update PERSON
set
NAME = uname,
where
ID = uid;
return SQL%ROWCOUNT;
END updatePerson;]]></programlisting>
</sect1>
<sect1 id="querysql-load">
<title>로딩을 위한 맞춤형 SQL</title>
<para>
당신은 또한 엔티티 로딩을 위한 당신 자신의 SQL (또는 HQL)을 선언할 수도 있다:
</para>
<programlisting><![CDATA[<sql-query name="person">
<return alias="pers" class="Person" lock-mode="upgrade"/>
SELECT NAME AS {pers.name}, ID AS {pers.id}
FROM PERSON
WHERE ID=?
FOR UPDATE
</sql-query>]]></programlisting>
<para>
이것은 앞서 논의했듯이 단지 명명된 질의 선언이다. 당신은 class 매핑 속에 이 명명된 질의를 참조할 수 있다:
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<loader query-ref="person"/>
</class>]]></programlisting>
<para>
이것은 심지어 내장 프로시저들에 동작한다.
</para>
<para>
당신은 콜렉션 로딩을 위한 한 개의 질의를 정의할 수도 있다:
</para>
<programlisting><![CDATA[<set name="employments" inverse="true">
<key/>
<one-to-many class="Employment"/>
<loader query-ref="employments"/>
</set>]]></programlisting>
<programlisting><![CDATA[<sql-query name="employments">
<load-collection alias="emp" role="Person.employments"/>
SELECT {emp.*}
FROM EMPLOYMENT emp
WHERE EMPLOYER = :id
ORDER BY STARTDATE ASC, EMPLOYEE ASC
</sql-query>]]></programlisting>
<para>
당신은 심지어 조인 페칭에 의해 하나의 콜렉션을 로드시키는 하나의 엔티티를 정의할 수 있다:
</para>
<programlisting><![CDATA[<sql-query name="person">
<return alias="pers" class="Person"/>
<return-join alias="emp" property="pers.employments"/>
SELECT NAME AS {pers.*}, {emp.*}
FROM PERSON pers
LEFT OUTER JOIN EMPLOYMENT emp
ON pers.ID = emp.PERSON_ID
WHERE ID=?
</sql-query>]]></programlisting>
</sect1>
</chapter>

View File

@ -0,0 +1,572 @@
<chapter id="quickstart">
<title>Tomcat으로 빠른 시작</title>
<sect1 id="quickstart-intro" revision="2">
<title>Hibernate 시작하기</title>
<para>
이 튜토리얼은 웹 기반의 어플리케이션 용 Apache Tomcat 서블릿 컨테이너에 대한 Hibernate 3.0 셋업을
설명한다(우리는 버전 4.1을 사용했다. 5.0에 대한 차이점들은 적을 것이다). Hibernate는 모든 주요 J2EE
어플리케이션 서버들의 관리되는 환경에서 잘 동작하고, 또는 심지어 스탠드얼론 어플리케이션들에서도 잘 동작
한다. 이 튜토리얼에 사용되는 데이터베이스 시스템은 PostgreSQL 7.4이고, 다른 데이터베이스에 대한 지원은
단지 Hibernate SQL dialect 구성과 커넥션 프로퍼티들을 변경시키는 것에만 관계된다.
</para>
<para>
먼저 우리는 모든 필수적인 라이브러리들을 Tomcat 설치 장소에 복사해야 한다. 우리는 이 튜토리얼을 위해 별도의
웹 컨텍스트(<literal>webapps/quickstart</literal>)를 사용하며, 따라서 우리는 전역 라이브러리 검색 경로
(<literal>TOMCAT/common/lib</literal>)와 <literal>webapps/quickstart/WEB-INF/lib</literal>
(JAR 파일들의 경우)와 <literal>webapps/quickstart/WEB-INF/classes</literal> 내 에 있는 컨텍스트
레벨에서 클래스로더 양자를 고려해야 한다. 우리는 두 개의 클래스로더 레벨들을 전역 classpath와 컨텍스트
classpath로서 언급한다.
</para>
<para>
이제 라이브러리들을 두 개의 classpath들에 복사하라:
</para>
<orderedlist>
<listitem>
<para>
데이터베이스 용 JDBC 드라이버를 전역 classpath로 복사하라. 이것은 Tomcat에 번들로구성된 DBCP 커넥션
풀 소프트웨어에 필요하다. Hibernate는 데이터베이스 상에서 SQL을 실행시키는데 JDBC 커넥션들을 사용하므로,
당신은 풀링된 JDBC 커넥션들을 제공해야 하거나, 직접 지원되는 풀들(C3P0, Proxool) 중 하나를 사용하기
위해 Hibernate를 구성해야 한다. 이 튜토리얼을 위해, (PostgreSQL 7.4와 JDK 1.4용) <literal>pg74jdbc3.jar</literal>
라이브러리를 전역 classloaders 경로로 복사하라. 만일 당신이 다른 데이터베이스를 사용하고자 원할 경우,
간단하게 그것의 적절한 JDBC 드라이버를 복사하라.
</para>
</listitem>
<listitem>
<para>
그 밖의 어떤 것을 Tomcat 내의 전역 클래스로더 경로에 복사하지 말라. 또는 당신은 Log4j, commons-logging
그리고 다른 것들을 포함하는 여러 가지 도구들에 관련된 문제점들을 얻게 될 것이다. 각각의 웹 어플리케이션에 대해
컨텍스트 classpath를 사용하라. 즉 라이브러리들을 <literal>WEB-INF/lib</literal>에 복사하고, 당신 자신의 클래스들과
구성 파일들/프로퍼티 파일들을 <literal>WEB-INF/classes</literal>에 복사하라. 두 디렉토리들 양자는 디폴트로 컨텍스트
classpath 내에 있다.
</para>
</listitem>
<listitem>
<para>
Hibernate는 JAR 라이브러리로서 패키지화 되어 있다. <literal>hibernate3.jar</literal> 파일은 어플리케이션의 다른 클래스들과
함께 컨텍스트 classpath 속에 복사되어야 한다. Hibernate는 실행 시에 어떤 제 3의 라이브러리들을 필요로하고,
이것들은 <literal>lib/</literal> 디렉토리 내의 Hibernate 배포본에 번들화되어 있다; <xref linkend="3rdpartylibs"/>
보라. 필요한 제3의 라이브러리들을 컨텍스트 classpath로 복사하라.
</para>
</listitem>
</orderedlist>
<table frame="topbot" id="3rdpartylibs">
<title>
Hibernate 제3의 라이브러리
</title>
<tgroup cols="2" rowsep="1" colsep="1">
<colspec colname="c1" colwidth="1*"/>
<colspec colname="c2" colwidth="2*"/>
<thead>
<row>
<entry align="center">
라이브러리
</entry>
<entry align="center">
설명
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
antlr (필수)
</entry>
<entry>
Hibernate는 질의 파서들을 산출하는데 ANTLR을 사용하고, 이 라이브러리는 또한
실행 시에 필요하다.
</entry>
</row>
<row>
<entry>
dom4j (필수)
</entry>
<entry>
Hibernate는 XML 구성과 XML 매핑 메타데이터 파일들을 파싱하는데 dom4j를 사용
한다.
</entry>
</row>
<row>
<entry>
CGLIB, asm (필수)
</entry>
<entry>
Hibernate는 (Java reflection과 결합하여) 런타임 시에 클래스들을 고양시키는데
코드 생성 라이브러리를 사용한다.
</entry>
</row>
<row>
<entry>
Commons Collections, Commons Logging (필수)
</entry>
<entry>
Hibernate는 Apache Jakarta Commons 프로젝트로부터 다양한 유틸리티 라이브러리
들을 사용한다.
</entry>
</row>
<row>
<entry>
EHCache (필수)
</entry>
<entry>
Hibernate는 second-level 캐시를 위한 다양한 캐시 프로바이더들을 사용할 수 있다.
만일 구성에서 변하지 않을 경우 EHCache가 디폴트 캐시 프로바이더이다.
</entry>
</row>
<row>
<entry>
Log4j (옵션)
</entry>
<entry>
Hibernate는 기본 로깅 메커니즘으로서 Log4j를 사용할 수 있는, Commons Logging
API를 사용한다. 만일 Log4j 라이브러리가 컨텍스트 라이브러리 디렉토리 속에서 이용
가능하다면, Commons Logging은 Log4j와 컨텍스트 classpath 내에 있는 <literal>log4j.properties</literal>
구성을 사용할 것이다. Log4j에 대한 예제 properties 파일은 Hibernate 배포본에
번들화 되어 있다. 따라서 당신이 이면에서 무엇이 진행되는 지을 보고자 원할 경우에 log4j.jar와
(<literal>src/</literal>에 있는) 구성 파일을 당신의 컨텍스트 classpath 속으로 복사하라.
</entry>
</row>
<row>
<entry>
필수 여부?
</entry>
<entry>
Hibernate 배포본 내에 있는 <literal>lib/README.txt</literal> 파일을 살펴보라.
이것은 Hibernate에 배포된 제 3의 라이브러리들의 최신 목록이다. 당신은 그곳에 열거된
모든 필수 라이브러리들과 옵션 라이브러리들을 찾게 될 것이다(여기서 "빌드 시 필요함"은
당신의 어플리케이션이 아니라 Hibernate에 대한 의미임을 노트하라).
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
우리는 이제 Tomcat과 Hibernate 양자에서 데이터베이스 커넥션 풀링과 공유를 설정한다. 이것은 Tomcat이
(그것의 미리 빌드되어 있는 DBCP 풀링 특징을 사용하여) 풀링된 JDBC 커넥션들을 제공할 것이고, Hibernate가
JNDI를 통해 이들 커넥션들을 요청한다는 것을 의미한다. 달리 당신은 Hibernate로 하여금 커넥션 풀을 관리하도록
할 수 있다. Tomcat은 그것의 커넥션 풀을 JNDI에 바인드 시킨다; 우리는 리소스 선언을 Tomcat 메인 구성 파일인
<literal>TOMCAT/conf/server.xml</literal>에 추가한다:
</para>
<programlisting><![CDATA[<Context path="/quickstart" docBase="quickstart">
<Resource name="jdbc/quickstart" scope="Shareable" type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/quickstart">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<!-- DBCP database connection settings -->
<parameter>
<name>url</name>
<value>jdbc:postgresql://localhost/quickstart</value>
</parameter>
<parameter>
<name>driverClassName</name><value>org.postgresql.Driver</value>
</parameter>
<parameter>
<name>username</name>
<value>quickstart</value>
</parameter>
<parameter>
<name>password</name>
<value>secret</value>
</parameter>
<!-- DBCP connection pooling options -->
<parameter>
<name>maxWait</name>
<value>3000</value>
</parameter>
<parameter>
<name>maxIdle</name>
<value>100</value>
</parameter>
<parameter>
<name>maxActive</name>
<value>10</value>
</parameter>
</ResourceParams>
</Context>]]></programlisting>
<para>
우리가 이 예제에서 구성하는 컨텍스트는 <literal>quickstart</literal>로 명명되고, 그것의 베이스는
<literal>TOMCAT/webapp/quickstart</literal> 디렉토리이다. 임의의 서블릿들에 접근하기 위해,
(물론 당신의 <literal>web.xml</literal> 속에 매핑된 서블릿의 이름을 추가하여) 당신의 브라우저에서
<literal>http://localhost:8080/quickstart</literal> 경로를 호출하라. 당신은 또한 계속 진행하고
이제 공백의 <literal>process()</literal> 메소드를 가진 간단한 서블릿을 생성시킬 수 있다.
</para>
<para>
Tomcat은 이제 <literal>java:comp/env/jdbc/quickstart</literal>로 JNDI을 통해 커넥션들을 제공한다.
만일 당신이 실행 중인 커넥션 풀을 얻는 것에 문제가 있다면 Tomcat 문서를 참조하라. 당신이 JDBC 드라이버 예외상황
메시지를 얻을 경우, 먼저 Hibernate 없이 JDBC 커넥션 풀을 셋업하라. Tomcat &amp; JDBC 튜토리얼들은
그 웹 서이트에서 이용 가능하다.
</para>
<para>
당신의 다음 단계는 Hibernate를 구성하는 것이다. Hibernate는 그것이 JDBC 커넥션들을 얻는 방법을 알고 있어야 한다.
우리는 Hibernate의 XML 기반 구성을 사용한다. properties 파일을 사용하는 다른 접근법은 거의 동일하지만 XML
구문이 허용하는 몇몇 특징들을 누락하고 있다. XML 구성 파일은 <literal>hibernate.cfg.xml</literal>로서
컨텍스트 classpath (<literal>WEB-INF/classes</literal>) 내에 위치해 있다:
</para>
<programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.datasource">java:comp/env/jdbc/quickstart</property>
<property name="show_sql">false</property>
<property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<!-- Mapping files -->
<mapping resource="Cat.hbm.xml"/>
</session-factory>
</hibernate-configuration>]]></programlisting>
<para>
우리는 SQL 명령들에 대한 로깅을 사용하지 않고 Hibernate에게 사용되는 데이터베이스 SQL direct가 무엇인지
그리고 (Tomcat 바인드된 풀의 JNDI 주소를 선언하여) JDBC 커넥션들을 얻는 곳을 알려준다. dialect는
필수적인 설정이고, 데이터베이스들은 SQL "표준"에 대한 그것들의 해석을 달리한다. Hibernate는 차이점들을
처리하고 모든 주요 상용 데이터베이스들 및 오픈 소스 데이터베이스들 용도의 direct들을 번들로 포함하고 있다.
</para>
<para>
<literal>SessionFactory</literal>는 단일 데이터저장소에 관한 개념이고, 여러 데이터베이스들은 여러
개의 XML 구성 파일들을 생성시키고 당신의 어플리케이션 속에서 여러 개의 <literal>Configuration</literal>
<literal>SessionFactory</literal> 객체들을 생성시켜서 사용될 수 있다.
</para>
<para>
<literal>hibernate.cfg.xml</literal>의 마지막 요소는 영속 클래스 <literal>Cat</literal>에 대한
Hibernate XML 매핑 파일의 이름으로써 <literal>Cat.hbm.xml</literal>을 선언한다. 이 파일은 데이터베이스
테이블(또는 테이블들)로 POJO 클래스 <literal>Cat</literal> 을 매핑시키는 메타데이터를 포함한다. 우리는
곧 그 파일로 되돌아 갈 것이다. 먼저 POJO 클래스를 작성하고 그런 다음 그것을 위한 매핑 메타데이터를 선언하자.
</para>
</sect1>
<sect1 id="quickstart-persistentclass" revision="1">
<title>첫 번째 영속 클래스</title>
<para>
Hibernate는 영속 클래스들에 대한 Plain Old Java Objects (POJOs, 종종 Plain Ordinary Java Objects로
명명된다) 프로그래밍 모형으로 가장 잘 동작한다. POJO는 공용으로 가시적인 인터페이스로부터 내부적인 표상을 은폐시켜,
getter와 setter 메소드들을 통해 접근가능한 클래스들의 프로퍼티들을 가진 자바빈과 꽤 유사하다(필요하다면 Hibernate는
또한 필드들에 직접 접근할 수 있다):
</para>
<programlisting><![CDATA[package org.hibernate.examples.quickstart;
public class Cat {
private String id;
private String name;
private char sex;
private float weight;
public Cat() {
}
public String getId() {
return id;
}
private void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public float getWeight() {
return weight;
}
public void setWeight(float weight) {
this.weight = weight;
}
}]]></programlisting>
<para>
Hibernate는 그것의 프로퍼티 타입들의 사용에 제약되지 않고, 자바 콜렉션 프레임웍에서의 클래스들을 포함하여, 모든
자바 JDK 타입들과 (<literal>String</literal>, <literal>char</literal><literal>Date</literal>
같은) 원시타입들이 매핑될 수 있다. 당신은 그것을 값들로서, 값들을 가진 콜렉션들로서, 또는 다른 엔티티들에 대한
연관들로서 매핑시킬 수 있다. <literal>id</literal>는 그 클래스의 데이터베이스 식별자(프라이머리 키)를 표현하는
특별한 프로퍼티이고, 그것은 <literal>Cat</literal>과 같은 엔티티들에 대해 매우 권장된다. Hibernate는 내부적으로만
식별자들을 사용할 수 있지만, 우리는 우리의 어플리케이션 아키텍처에서 어떤 유연성을 상실하게 될 것이다.
</para>
<para>
특정 인터페이스는 영속 클래스들에 대해 구현되지 말아야 하거나 특정 루트 영속 클래스로부터 서브 클래스로 만들지 말아야 한다.
Hibernate는 또한 바이트 코드 처리와 같은, 어떤 빌드 시 처리를 필요로 하지 않고, 그것은 오직 자바 reflection과
(CGLIB를 통한) 런타임 클래스 고양에만 의존한다. 따라서 Hibernate에 대한 POJO 클래스의 어떤 의존성 없이도, 우리는
그것을 데이터베이스 테이블로 매핑할 수 있다.
</para>
</sect1>
<sect1 id="quickstart-mapping" revision="1">
<title>cat 매핑하기</title>
<para>
<literal>Cat.hbm.xml</literal> 매핑파일은 객체/관계형 매핑에 필요한 메타데이터를 포함한다. 메타데이터는 영속
클래스들의 선언과 데이터베이스 테이블들에 대한 (컬럼들과 다른 엔티티들에 대한 foreign 키 관계들에 대한) 프로퍼티들의
매핑을 포함한다.
</para>
<programlisting><![CDATA[<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="org.hibernate.examples.quickstart.Cat" table="CAT">
<!-- A 32 hex character is our surrogate key. It's automatically
generated by Hibernate with the UUID pattern. -->
<id name="id" type="string" unsaved-value="null" >
<column name="CAT_ID" sql-type="char(32)" not-null="true"/>
<generator class="uuid.hex"/>
</id>
<!-- A cat has to have a name, but it shouldn' be too long. -->
<property name="name">
<column name="NAME" length="16" not-null="true"/>
</property>
<property name="sex"/>
<property name="weight"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
모든 영속 클래스는 식별자 속성을 가져야 한다(실제로 value-타입의 클래스들에 의존하지 않는, 엔티티들을 표현하는 유일한
클래스들은 엔티티을 가진 컴포넌트들로 매핑된다). 이 프로퍼티는 영속 객체들을 구별짓는데 사용된다: 만일
<literal>catA.getId().equals(catB.getId())</literal>가 true일 경우, 두 개의 cat들은 같고, 이 개념은
<emphasis>database identity</emphasis>로 명명된다. Hibernate는 (데이터베이스 시퀀스, hi/lo 식별자 테이블들,
그리고 어플리케이션 할당 식별자들에 대한 native 생성기들을 포함하는) 다른 시나리오들에 대해 여러 가지 식별자 생성기들을
번들로 갖고 있다. 우리는 UUID 생성기(데이터이스에 의해 생성된 정수 대용 키들이 선호될 것이므로, 테스트용으로만 권장됨)를
사용하고 또한 Hibernate 생성된 식별자 값을 위한 <literal>CAT</literal> 테이블의 <literal>CAT_ID</literal>
컬럼을 (테이블의 프라이머리 키로서) 지정한다.
</para>
<para>
<literal>Cat</literal>의 모든 다른 프로퍼티들은 동일한 테이블로 매핑된다. <literal>name</literal> 프로퍼티의 경우에,
우리는 그것을 명시적인 데이터베이스 컬럼 선언으로 매핑시켰다. 데이터베이스 스키마가 Hibernate의 <emphasis>SchemaExport</emphasis>
도구에 의해 매핑 선언으로부터 (SQL DDL 문장들로) 자동적으로 생성될 때 이것이 특별히 유용하다. 모든 다른 프로퍼티들은
Hibernate의 디폴트 설정들을 사용하여 매핑되고, 디폴트 설정들은 당신이 가장 많은 시간을 필요로 하는 것이다. 데이터베이스
내의 테이블 <literal>CAT</literal>은 다음과 같다:
</para>
<programlisting><![CDATA[ Column | Type | Modifiers
--------+-----------------------+-----------
cat_id | character(32) | not null
name | character varying(16) | not null
sex | character(1) |
weight | real |
Indexes: cat_pkey primary key btree (cat_id)]]></programlisting>
<para>
당신은 이제 수작업으로 당신의 데이터베이스 내에 이 테이블을 생성시킬 것이고, 만일 당신이 <literal>hbm2ddl</literal>
도구로 이 단계를 자동화 시키고자 원할 경우 <xref linkend="toolsetguide"/>를 읽어라. 이 도구는 테이블 정의, 맞춤형
컬럼 타입 컨스트레인트들, 유일 컨스트레인트들과 인덱스들을 포함하는, 전체 SQL DDL을 생성시킬 수 있다.
</para>
</sect1>
<sect1 id="quickstart-playingwithcats" revision="2">
<title>cat들에 작업하기</title>
<para>
우리는 이제 Hibernate의 <literal>Session</literal>을 시작할 준비가 되어 있다. 그것은 <emphasis>persistence manager</emphasis>
(영속 관리자)이고, 우리는 데이터베이스로 <literal>Cat</literal>들을 저장하고 데이터베이스로부터
<literal>Cat</literal>들을 검색하는데 그것을 사용한다. 그러나 먼저 우리는 <literal>SessionFactory</literal>로부터
<literal>Session</literal>(Hibernate의 작업 단위)를 얻어야 한다:
</para>
<programlisting><![CDATA[SessionFactory sessionFactory =
new Configuration().configure().buildSessionFactory();]]></programlisting>
<para>
<literal>configure()</literal> 호출은 <literal>hibernate.cfg.xml</literal> 구성 파일을 로드시키고
<literal>Configuration</literal> 인스턴스를 초기화 시킨다. 당신이 SessionFactory(불변적임)를 빌드하기
<emphasis>이전에</emphasis> 당신은 <literal>Configuration</literal>에 접근함으로써 다른 프로퍼티들을
설정할 수 있다(그리고 심지어 매핑 메타데이터를 변경시킬 수 있다). 우리는 어디서 <literal>SessionFactory</literal>
생성시키고 우리의 어플리케이션 속에서 어떻게 그것에 접근할 수 있나?
</para>
<para>
<literal>SessionFactory</literal>는 대개 오직 한번만, 예를 들어 대개 <emphasis>load-on-startup</emphasis>
서블릿으로 시작 시에 빌드된다. 이것은 또한 당신이 당신의 서블릿들 내에 있는 인스턴스 변수 속에 그것을 유지하지 않을 것이지만
어떤 다른 위치에 유지시킬 것임을 의미한다. 더구나 우리는 어떤 종류의 <emphasis>Singleton</emphasis>을 필요로 하며,
따라서 우리는 어플리케이션 코드로 쉽게 <literal>SessionFactory</literal>에 액세스 할 수 있다. 다음에 보여진 접근법은
두 문제 모두를 해결한다: 시작 구성과 <literal>SessionFactory</literal>에 대한 쉬운 접근.
</para>
<para>
우리는 <literal>HibernateUtil</literal> helper 클래스를 구현한다:
</para>
<programlisting><![CDATA[import org.hibernate.*;
import org.hibernate.cfg.*;
public class HibernateUtil {
private static Log log = LogFactory.getLog(HibernateUtil.class);
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
log.error("Initial SessionFactory creation failed.", ex);
throw new ExceptionInInitializerError(ex);
}
}
public static final ThreadLocal session = new ThreadLocal();
public static Session currentSession() {
Session s = (Session) session.get();
// Open a new Session, if this Thread has none yet
if (s == null) {
s = sessionFactory.openSession();
session.set(s);
}
return s;
}
public static void closeSession() {
Session s = (Session) session.get();
if (s != null)
s.close();
session.set(null);
}
}]]></programlisting>
<para>
이 클래스는 static 초기자를 가진 <literal>SessionFactory</literal>를 처리할 뿐만 아니라 또한 현재의 쓰레드를
위한 <literal>Session</literal>을 소유하는 <literal>ThreadLocal</literal> 변수를 갖는다. 이 helper를
사용하려고 시도하기 전에 thread-local 변수에 대한 자바 개념을 이해해야 한다. 보다 복잡하고 강력한 <literal>HibernateUtil</literal>
클래스는 http://caveatemptor.hibernate.org/의 <literal>CaveatEmptor</literal>에서 찾을 수 있다.
</para>
<para>
<literal>SessionFactory</literal>는 threadsafe이고, 많은 쓰레드들이 동시에 그것에 접근할 수 있고
<literal>Session</literal>들을 요청할 수 있다. 하나의 <literal>Session</literal>은 데이터베이스에 대해
한 개의 단위 작업을 나타내는 non-threadsafe 객체이다. <literal>Session</literal>들은 <literal>SessionFactory</literal>
로부터 열려지고 모든 작업이 완료될 때 닫혀진다. 당신의 서블릿의 <literal>process()</literal> 메소드 내에 있는
예제는 다음과 같을 수 있다(예외상황 처리 없이):
</para>
<programlisting><![CDATA[Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
Cat princess = new Cat();
princess.setName("Princess");
princess.setSex('F');
princess.setWeight(7.4f);
session.save(princess);
tx.commit();
HibernateUtil.closeSession();]]></programlisting>
<para>
하나의 <literal>Session</literal> 내에서 모든 데이터베이스 오퍼레이션은 데이터베이스 오퍼레이션들(심지어 읽기 전용
오퍼레이션들 조차도)을 격리시키는 하나의 트랜잭션 내부에서 발생한다. 우리는 기본 트랜잭션 방도(우리의 경우, JDBC
트랜잭션들)로부터 추상화시키는데 Hibernates <literal>Transaction</literal> API 를 사용한다. 이것은 우리의
코드가 임의의 변경들 없이도 (JTA를 사용하는) 컨테이너-관리되는 트랜잭션들에 배치되는 것을 허용해준다.
</para>
<para>
당신이 원하는 만큼 당신이 <literal>HibernateUtil.currentSession();</literal>을 호출할 수 있고, 당신은
이 쓰레드의 현재 <literal>Session</literal>을 항상 얻을 것임을 노트하라. 당신은 서블릿 코드 내에서든 또는 서블릿 필터
내에서든 HTTP response가 전송되기 전에, 당신의 단위 작업이 완료된 후에 <literal>Session</literal>이 확실히
닫혀지도록 해야 한다. 두 번째 옵션의 좋은 측면은 쉬운 lazy 초기화이다: 뷰가 렌더링 될 때 <literal>Session</literal>
여전히 열려져 있어서, Hibernate는 당신이 현재 객체 그래프를 네비게이트 하는 동안 초기화 되지 않은 객체들을 로드시킬 수 있다.
</para>
<para>
Hibernate는 데이터베이스로부터 객체들을 검색하는데 사용될 수 있는 다양한 메소드들을 갖고 있다. 가장 유연한 방법은 Hibernate
Query Language (HQL)을 사용하는 것이다. Hibernate Query Language (HQL)은 배우기가 쉽고 SQL에 대한 강력한 객체 지향
확장이다:
</para>
<programlisting><![CDATA[Transaction tx = session.beginTransaction();
Query query = session.createQuery("select c from Cat as c where c.sex = :sex");
query.setCharacter("sex", 'F');
for (Iterator it = query.iterate(); it.hasNext();) {
Cat cat = (Cat) it.next();
out.println("Female Cat: " + cat.getName() );
}
tx.commit();]]></programlisting>
<para>
Hibernate는 또한 type-safe 질의들을 공식화 시키는데 사용될 수 있는 객체-지향 <emphasis>query by criteria</emphasis>
API을 제공한다. 물론 Hibernate는 데이터베이스와의 모든 SQL 통신을 위해 <literal>PreparedStatement</literal>들과
파라미터 바인딩을 사용한다. 당신은 또한 Hibernate 직접적인 SQL 질의 특징을 사용할 수도 있거나 드문 경우에
<literal>Session</literal>으로부터 plain JDBC 커넥션을 얻을 수도 있다.
</para>
</sect1>
<sect1 id="quickstart-summary" revision="1">
<title>마지막으로</title>
<para>
우리는 이 작은 튜토리얼 내에서 단지 Hibernate의 표면을 훑기만 했다. 우리는 우리의 예제들 속에 어떤 서블릿 지정적 코드를
포함하지 않음을 노트하라. 당신이 적합한지를 알려고 할 때 당신은 당신 자신의 서블릿을 생성시켜야 하고 Hibernate 코드를
삽입해야 한다.
</para>
<para>
데이터 접근 계층으로서 Hibernate는 당신의 어플리케이션에 강하게 통합됨을 염두에 두라. 대개 모든 다른 레이어들은 영속
메커니즘에 의존했다. 당신은 이 설계의 함축을 확실히 이해하도록 하라.
</para>
<para>
보다 복잡한 어플리케이션 예제는 http://caveatemptor.hibernate.org/ 를 보고 http://caveatemptor.hibernate.org/에
있는 다른 튜토리얼들을 살펴보라.
</para>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,444 @@
<chapter id="toolsetguide" revision="2">
<title>도구셋 안내</title>
<para>
Hibernate에 대한 라운드트립 엔지니어링은 Eclipse 플러그인 세트, 명령라인 도구들, 뿐만 아니라 Ant 태스크들을 사용하여 가능하다.
</para>
<para>
<emphasis>Hibernate 도구들</emphasis>은 현재 기존 데이터베이스들에 대한 리버스 엔지니어링을 위해 Ant 태스크들 뿐만 아니라
Eclipse IDE용 플러그인들을 포함하고 있다:
</para>
<itemizedlist>
<listitem><para>
<emphasis>매핑 편집기:</emphasis> 자동 완성 기능과 구문 강조를 지원하는 Hibernate XML 매핑 파일들에 대한 편집기. 그것은 또한
통상의 XML 편집기 보다 훨씬 더 융통성 있게 만들어서 클래스 이름들과 프로퍼티/필드 이름들에 대한 의미론적 자동 완성 기능을 지원한다.
</para></listitem>
<listitem><para>
<emphasis>콘솔:</emphasis> 콘솔은 Eclipse에서 새로운 뷰이다. 당신의 콘솔 구성들에 대한 tree overview에 덧붙여, 당신은 또한
당신의 영속 클래스들과 그것들의 관계들에 대한 상호작용 뷰를 얻는다. 콘솔은 당신의 데이터베이스에 대해 HQL 질의들을 실행하고 그 결과를
Eclipse 내에서 직접 브라우징 하도록 당신에게 허용해준다.
</para></listitem>
<listitem><para>
<emphasis>개발 마법사들:</emphasis> 몇몇 마법사들이 Hibernate Eclipse 도구들에 제공된다; 당신은 Hibernate 구성
(cfg.xml) 파일들을 빠르게 생성시키는데 마법사를 사용하거나, 심지어 당신은 기존 데이터베이스 스키마를 POJO 소스 파일들과
Hibernate 매핑 파일들로 완전하게 리버스 엔지니어링할 수도 있다. 리버스 엔지니어링 마법사는 맞춤 가능한 템플릿들을 제공한다.
</para></listitem>
<listitem><para>
<emphasis>Ant 태스크들:</emphasis>
</para></listitem>
</itemizedlist>
<para>
추가 정보는 <emphasis>Hibernate Tools</emphasis> 패키지와 그것의 문서를 참조하길 바란다.
</para>
<para>
하지만 Hibernate 메인 패키지는 통합 도구에 번들화 되어 있다(그것은 심지어 플라이 상에서 Hibernate "내에서" 사용될 수 있다):
<emphasis>SchemaExport</emphasis> 별칭은 <literal>hbm2ddl</literal>.
</para>
<sect1 id="toolsetguide-s1" revision="2">
<title>자동적인 스키마 생성</title>
<para>
DDL은 Hibernate 유틸리티에 의해 당신의 매핑 파일들로부터 생성될 수 있다. 생성된 스키마는 엔티티 테이블과 콜렉션 테이블에 대한
참조 무결성 컨스트레인트들(프라이머리 키와 foreign 키들)을 포함한다. 테이블들과 시퀀스들은 또한 페칭된 식별자 생성기들에 대해
생성된다.
</para>
<para>
DDL이 매우 벤더에 특정하므로, 이 도구를 사용할 때 당신은 <literal>hibernate.dialect</literal> 프로퍼티를 통해 한 개의 SQL
<literal>Dialect</literal>를 지정<emphasis>해야 한다</emphasis>.
</para>
<para>
먼저 생성된 스키마를 개선시키기 위해 당신의 매핑 파일들을 맞춤화 시켜라.
</para>
<sect2 id="toolsetguide-s1-2" revision="1">
<title>스키마 맞춤화 시키기</title>
<para>
많은 Hibernate 매핑 요소들은 <literal>length</literal>로 명명된 옵션 속성을 정의한다. 당신은 이 속성으로 컬럼의 길이를
설정할 수 있다.(또는 numeric/decimal 데이터 타입들, 배정도에 대해 ).
</para>
<para>
몇몇 태그들은 또한 (테이블 컬럼들에 대한 <literal>NOT NULL</literal> 컨스트레인트를 생성시키는) <literal>not-null</literal>
속성과 (테이블 컬럼들에 대한 <literal>UNIQUE</literal> 컨스트레인트를 생성시키는) <literal>unique</literal> 속성을 수용한다.
</para>
<para>
몇몇 태그들은 그 컬럼에 대한 인덱스의 이름을 지정하는 <literal>index</literal> 속성을 허용한다. <literal>unique-key</literal>
속성은 하나의 단위 키 컨스트레인트로 컬럼들을 그룹지우는데 사용될 수 있다. 현재 <literal>unique-key</literal> 속성의
지정된 값은 컨스트레인트를 명명하는데 사용되지 <emphasis>않고</emphasis>, 오직 매핑 파일 내에서 컬럼들을 그룹 지우는데
사용된다.
</para>
<para>
예제들:
</para>
<programlisting><![CDATA[<property name="foo" type="string" length="64" not-null="true"/>
<many-to-one name="bar" foreign-key="fk_foo_bar" not-null="true"/>
<element column="serial_number" type="long" not-null="true" unique="true"/>]]></programlisting>
<para>
다른 방법으로, 이들 요소들은 또한 자식 <literal>&lt;column&gt;</literal> 요소를 수용한다. 이것은 다중 컬럼 타입들에
특히 유용하다:
</para>
<programlisting><![CDATA[<property name="foo" type="string">
<column name="foo" length="64" not-null="true" sql-type="text"/>
</property>]]></programlisting>
<programlisting><![CDATA[<property name="bar" type="my.customtypes.MultiColumnType"/>
<column name="fee" not-null="true" index="bar_idx"/>
<column name="fi" not-null="true" index="bar_idx"/>
<column name="fo" not-null="true" index="bar_idx"/>
</property>]]></programlisting>
<para>
<literal>sql-type</literal> 속성은 SQL 데이터타입에 대한 Hibernate 타입의 디폴트 매핑을 오버라이드 시키는 것을
사용자에게 허용해준다.
</para>
<para>
<literal>check</literal> 속성은 check 컨스트레인트를 지정하는 것을 당신에게 허용해준다.
</para>
<programlisting><![CDATA[<property name="foo" type="integer">
<column name="foo" check="foo > 10"/>
</property>]]></programlisting>
<programlisting><![CDATA[<class name="Foo" table="foos" check="bar < 100.0">
...
<property name="bar" type="float"/>
</class>]]></programlisting>
<table frame="topbot" id="schemattributes-summary" revision="2">
<title>요약</title>
<tgroup cols="3">
<colspec colwidth="1*"/>
<colspec colwidth="1*"/>
<colspec colwidth="2.5*"/>
<thead>
<row>
<entry>속성</entry>
<entry>값들</entry>
<entry>해석</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>length</literal></entry>
<entry>number</entry>
<entry>컬럼 길이/decimal 배정도</entry>
</row>
<row>
<entry><literal>not-null</literal></entry>
<entry><literal>true|false</literal></entry>
<entry>컬럼이 null이 아니어야 함을 지정한다</entry>
</row>
<row>
<entry><literal>unique</literal></entry>
<entry><literal>true|false</literal></entry>
<entry>컬럼이 하나의 유일 컨스트레인트를 가져야함을 지정한다</entry>
</row>
<row>
<entry><literal>index</literal></entry>
<entry><literal>index_name</literal></entry>
<entry>(다중-컬럼) 인덱스의 이름을 지정한다 </entry>
</row>
<row>
<entry><literal>unique-key</literal></entry>
<entry><literal>unique_key_name</literal></entry>
<entry>다중-컬럼 유일 컨스트레인트의 이름을 지정한다</entry>
</row>
<row>
<entry><literal>foreign-key</literal></entry>
<entry><literal>foreign_key_name</literal></entry>
<entry>
하나의 연관에 대해 생성된 foreign key 컨스트레인트의 이름을 지정하고,
&lt;one-to-one>, &lt;many-to-one>, &lt;key>, and &lt;many-to-many>
매핑 요소들 상에 그것을 사용한다. <literal>inverse="true"</literal> 측들은
<literal>SchemaExport</literal>에 의해 고려되지 않을 것임을 노트하라.
</entry>
</row>
<row>
<entry><literal>sql-type</literal></entry>
<entry><literal>column_type</literal></entry>
<entry>
디폴트 컬럼 타입을 오버라이드 시킨다
(<literal>&lt;column&gt;</literal> 요소의 속성에만)
</entry>
</row>
<row>
<entry><literal>check</literal></entry>
<entry>SQL expression</entry>
<entry>
컬럼 또는 테이블에 대한 SQL check 컨스트레인트를 생성시킨다
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
<literal>&lt;comment&gt;</literal> 요소는 생성된 스키마에 대한 주석들을 지정하는 것을 당신에게 허용해준다.
</para>
<programlisting><![CDATA[<class name="Customer" table="CurCust">
<comment>Current customers only</comment>
...
</class>]]></programlisting>
<programlisting><![CDATA[<property name="balance">
<column name="bal">
<comment>Balance in USD</comment>
</column>
</property>]]></programlisting>
<para>
이것은 (지원되는 경우) 생성된 DDL에서 <literal>comment on table</literal> 또는
<literal>comment on column</literal> 문장으로 귀결된다.
</para>
</sect2>
<sect2 id="toolsetguide-s1-3">
<title>도구 실행하기</title>
<para>
<literal>SchemaExport</literal> 도구는 DDL 스크립트를 표준 출력으로 기록 하고/하거나 DDL 문장들을 실행시킨다.
</para>
<para>
<literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
<literal>org.hibernate.tool.hbm2ddl.SchemaExport</literal> <emphasis>options mapping_files</emphasis>
</para>
<table frame="topbot">
<title><literal>SchemaExport</literal> 명령 라인 옵션들</title>
<tgroup cols="2">
<colspec colwidth="1.5*"/>
<colspec colwidth="2*"/>
<thead>
<row>
<entry>옵션</entry>
<entry>설명</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>--quiet</literal></entry>
<entry>스크립트를 표준출력으로 출력하지 않는다</entry>
</row>
<row>
<entry><literal>--drop</literal></entry>
<entry>오직 테이블들을 드롭시킨다</entry>
</row>
<row>
<entry><literal>--text</literal></entry>
<entry>데이터베이스로 내보내기 하지 않는다</entry>
</row>
<row>
<entry><literal>--output=my_schema.ddl</literal></entry>
<entry>ddl 스크립트를 파일로 출력한다</entry>
</row>
<row>
<entry><literal>--config=hibernate.cfg.xml</literal></entry>
<entry>XML 파일로부터 Hibernate 구성을 읽어들인다</entry>
</row>
<row>
<entry><literal>--properties=hibernate.properties</literal></entry>
<entry>파일로부터 데이터베이스 프로퍼티들을 읽어들인다</entry>
</row>
<row>
<entry><literal>--format</literal></entry>
<entry>생성된 SQL을 스크립트 내에 좋게 형식지운다</entry>
</row>
<row>
<entry><literal>--delimiter=x</literal></entry>
<entry>스크립트를 위한 라인 경계의 끝을 설정한다 </entry>
</row>
</tbody>
</tgroup>
</table>
<para>
당신은 당신의 어플리케이션 내에 <literal>SchemaExport</literal>를 삽입시킬 수도 있다:
</para>
<programlisting><![CDATA[Configuration cfg = ....;
new SchemaExport(cfg).create(false, true);]]></programlisting>
</sect2>
<sect2 id="toolsetguide-s1-4">
<title>프로퍼티들</title>
<para>
데이터베이스 프로퍼티들은 다음과 같이 지정될 수 있다
</para>
<itemizedlist spacing="compact">
<listitem>
<para><literal>-D</literal><emphasis>&lt;property&gt;</emphasis>를 가진 시스템 프로퍼티로서</para>
</listitem>
<listitem>
<para><literal>hibernate.properties</literal> 내에서</para>
</listitem>
<listitem>
<para><literal>--properties</literal>를 가진 명명된 프로퍼티들 내에서</para>
</listitem>
</itemizedlist>
<para>
필요한 프로퍼티들은 다음과 같다:
</para>
<table frame="topbot">
<title>SchemaExport 커넥션 프로퍼티들</title>
<tgroup cols="2">
<colspec colwidth="1.5*"/>
<colspec colwidth="2*"/>
<thead>
<row>
<entry>프로퍼티 이름</entry>
<entry>설명</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>hibernate.connection.driver_class</literal></entry>
<entry>jdbc 드라이버 클래스</entry>
</row>
<row>
<entry><literal>hibernate.connection.url</literal></entry>
<entry>jdbc url</entry>
</row>
<row>
<entry><literal>hibernate.connection.username</literal></entry>
<entry>데이터베이스 사용자</entry>
</row>
<row>
<entry><literal>hibernate.connection.password</literal></entry>
<entry>사용자 패스워드</entry>
</row>
<row>
<entry><literal>hibernate.dialect</literal></entry>
<entry>dialect</entry>
</row>
</tbody>
</tgroup>
</table>
</sect2>
<sect2 id="toolsetguide-s1-5">
<title>Ant 사용하기</title>
<para>
당신은 당신의 Ant 빌드 스크립트에서 <literal>SchemaExport</literal>를 호출할 수 있다:
</para>
<programlisting><![CDATA[<target name="schemaexport">
<taskdef name="schemaexport"
classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
classpathref="class.path"/>
<schemaexport
properties="hibernate.properties"
quiet="no"
text="no"
drop="no"
delimiter=";"
output="schema-export.sql">
<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemaexport>
</target>]]></programlisting>
</sect2>
<sect2 id="toolsetguide-s1-6">
<title>점증하는 스키마 업데이트들</title>
<para>
<literal>SchemaUpdate</literal> 도구는 "점증하는" 변경들을 가진 기존 스키마를 변경시킬 것이다. <literal>SchemaUpdate</literal>
JDBC 메타데이터 API에 무겁게 의존하여서, 그것은 모든 JDBC 드라이버들에 동작하지 않을 것임을 주목하라.
</para>
<para>
<literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
<literal>org.hibernate.tool.hbm2ddl.SchemaUpdate</literal> <emphasis>options mapping_files</emphasis>
</para>
<table frame="topbot">
<title><literal>SchemaUpdate</literal> 명령 라인 옵션들</title>
<tgroup cols="2">
<colspec colwidth="1.5*"/>
<colspec colwidth="2*"/>
<thead>
<row>
<entry>옵션</entry>
<entry>설명</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>--quiet</literal></entry>
<entry>스크립트를 표준출력으로 출력하지 않는다</entry>
</row>
<row>
<entry><literal>--properties=hibernate.properties</literal></entry>
<entry>파일로부터 데이터베이스 프로퍼티들을 읽어 들인다</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
당신은 당신의 어플리케이션 내에 <literal>SchemaUpdate</literal>를 삽입시킬 수 있다:
</para>
<programlisting><![CDATA[Configuration cfg = ....;
new SchemaUpdate(cfg).execute(false);]]></programlisting>
</sect2>
<sect2 id="toolsetguide-s1-7">
<title>점증하는 스키마 업데이트들에 Ant 사용하기</title>
<para>
당신은 Ant 스크립트에서<literal>SchemaUpdate</literal>를 호출할 수 있다:
</para>
<programlisting><![CDATA[<target name="schemaupdate">
<taskdef name="schemaupdate"
classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
classpathref="class.path"/>
<schemaupdate
properties="hibernate.properties"
quiet="no">
<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemaupdate>
</target>]]></programlisting>
</sect2>
</sect1>
</chapter>

View File

@ -0,0 +1,816 @@
<chapter id="transactions" revision="1">
<title>트랜잭션들과 동시성</title>
<para>
Hibernate와 동시성 제어에 대한 가장 중요한 점은 이해하기가 매우 쉽다는 점이다. Hibernate는 어떤 추가적인 잠금 행위 없이
JDBC 커넥션들과 JTA 리소스들을 직접 사용한다. 우리는 당신의 데이터베이스 관리 시스템의 JDBC, ANSI, 그리고 트랜잭션 격리 명세에
약간의 시간을 할애할 것을 매우 권장한다. Hibernate는 단지 자동적인 버전화를 추가하지만 메모리 내에서 객체들을 잠그지 않거나 당신의
데이터베이스 트랜잭션들의 격리 레벨을 변경시키지 않는다. 기본적으로, 당신이 당신의 데이터베이스 리소스들에 직접 JDBC(또는 JTA/CMT)를
사용하는 것처럼 Hibernate를 사용하라.
</para>
<para>
하지만, 자동적인 버전화에 덧붙여, Hibernate는 또한 <literal>SELECT FOR UPDATE</literal> 구문을 사용하여,
행들에 대한 pessimistic 잠금을 위한 (마이너) API를 제공한다. 이 API는 이 장의 뒷 부분에서 논의된다.
</para>
<para>
우리는 <literal>Configuration</literal>, <literal>SessionFactory</literal>, <literal>Session</literal>,
알갱이를 가진 Hibernate에서의 동시성 제어 뿐만 아니라 데이터베이스 트랜잭션과 긴 어플리케이션 트랜잭션에 대한 논의를 시작한다.
</para>
<sect1 id="transactions-basics">
<title>세션 영역과 트랜잭션 영역</title>
<para>
<literal>SessionFactory</literal>는 모든 어플리케이션 쓰레드들에 의해 공유되도록 고안된 생성에 비용이 드는,
쓰레드안전(threadsafe) 객체이다. 그것은 대개 어플리케이션 시작 시에 <literal>Configuration</literal> 인스턴스로부터
한번 생성된다.
</para>
<para>
<literal>Session</literal>은 단일 비지니스 프로세스, 하나의 작업 단위를 위해 한번만 사용되고 나서 폐기될 예정인,
비용이 들지 않는, 쓰레드 안전하지 않은 객체이다. <literal>Session</literal>은 그것이 필요하지 않으면 JDBC
<literal>Connection</literal>(또는 <literal>Datasource</literal>)를 얻지 않을 것이어서, 데이터 접근이
특별한 요청에 서비스할 필요가 있을 것이라고 당신이 확신하지 않을 경우에 당신은 <literal>Session</literal>을 안전하게
열고 닫을 수 있다. (이것은 당신이 요청 인터셉션을 사용하여 다음 패턴들 중 어떤 것을 구현하자마자 중요하게 된다.)
</para>
<para>
이 그림을 완성하기 위해 당신은 또한 데이터베이스 트랜재션들에 대해 생각해야 한다. 데이터베이스 트랜잭션은 데이터베이스에서
잠금 다툼을 줄이기 위해 가능한 짧아야 한다. 긴 데이터베이스 트랜잭션들은 당신의 어플리케이션이 고도의 동시성 로드로의 가용성을
높이는 것을 방해할 것이다.
</para>
<para>
하나의 작업 단위의 영역은 무엇인가? 하나의 Hibernate <literal>Session</literal>은 몇몇 데이터베이스 트랜잭션들에
걸칠 수 있는가 또는 이것은 영역들의 one-to-one 관계인가? 당신은 언제 <literal>Session</literal>을 열고 닫는가
그리고 당신은 데이터베이스 트랜잭션 경계들을 어떻게 한정하는가?
</para>
<sect2 id="transactions-basics-uow">
<title>작업 단위</title>
<para>
첫번째로, <emphasis>session-per-operation</emphasis> anti-패턴을 사용하지 말라. 즉, 단일 쓰레드 내에서
모든 간단한 데이터베이스 호출에 대해 <literal>Session</literal>을 열고 닫지 말라! 물론 같은 것이 데이터베이스
트랜잭션들에 대해서도 참이다. 어플리케이션 내의 데이터베이스 호출들은 계획된 순서를사용하여 행해지며, 그것들은 원자
작업 단위 속으로 그룹지워진다. (이것은 또한 모든 하나의 SQL 문장 뒤의 auto-commit(자동-커밋)이 어플리케이션 내에서
무용지물임을 의미하고, 이 모드가 SQL 콘솔 작업을 돕도록 고안되었음을 노트하라. Hibernate는
의미하고, 이 모드는 Hibernate는 즉시 자동-커밋 모드를 사용 불가능하게 하거나, 어플리케이션 서버가 그렇게 행하고,
즉시 자동-커밋시키는 것을 사용불가능하게 하거나 ,그렇게 행하는 것을 기대한다.)
</para>
<para>
다중 사용자 클라이언트/서버 어플리케이션에서 가장 공통된 패턴은 <emphasis>session-per-request</emphasis>이다.
이 모형에서, 클라이언트로부터의 요청은 (Hibernate 영속 계층이 실행되는) 서버로 전송되고, 새로운 Hibernate
<literal>Session</literal>이 열려지고, 모든 데이터베이스 오퍼레이션들이 이 작업 단위 내에서 실행된다. 일단 그 작업이
완료되었다면(그리고 클라이언트에 대한 응답이 준비되었다면), 그 세션은 flush 되고 닫혀진다. 당신은 또한 당신이
<literal>Session</literal>을 열고 닫을 때 그것을 시작하고 커밋시켜서 클라이언트 요청에 서비스하는데 한 개의 데이터베이스
트랜잭션을 사용하게 될 것이다. 둘 사이의 관계는 일대일 대응이고 이 모형은 많은 어플리케이션들에서 완전하게 적합하다.
</para>
<para>
도전점은 구현에 놓여있다: <literal>Session</literal>과 트랜잭션이 정확하게 시작되고 끝나야 할 뿐만 아니라, 그것들은
또한 데이터 접근 오퍼레이션들에 대해 접근 가능해야 한다. 작업의 단위에 대한 경계 구분은 하나의 요청이 서버에 도착하고
응답이 전송되기 전에 실행되는 인터셉터(예를 들면 <literal>ServletFilter</literal>)를 사용하여 이상적으로 구현된다.
우리는 하나의 <literal>ThreadLocal</literal> 변수를 사용하여 그 <literal>Session</literal>을 요청에 서비스하는
쓰레드에 바인드 시킬 것을 권장한다. 이것은 이 쓰레드 내에서 실행되는 모든 코드에서 (static 변수에 접근하는 것처럼) 쉽게
접근을 허용해준다. 당신이 선택하는 데이터베이스 트랜잭션 경계 구분 메커니즘에 따라, 당신은 또한 <literal>ThreadLocal</literal>
변수 내에 트랜잭션 컨텍스트를 유지할 수도 있다. 이것을 위한 구현 패턴들은<emphasis>ThreadLocal Session</emphasis>
<emphasis> 뷰 내의 Open Session</emphasis>으로 알려져 있다. 당신은 이것을 구현하기 위해 이 문서의 앞 쪽에 보였던
<literal>HibernateUtil</literal> helper 클래스를 쉽게 확장할 수 있다. 물론 당신은 당신의 환경에서 인터셉터를 구현하고 그것을
설정하는 방법을 찾아야 한다. 팁들과 예제들은 Hibernate 웹 사이트를 보라.
</para>
</sect2>
<sect2 id="transactions-basics-apptx">
<title>어플리케이션 트랜잭션들</title>
<para>
session-per-request 패턴은 당신이 작업 단위들을 설계하는데 사용할 수 있는 유일한 유용한 개념이 아니다. 많은 비지니스
프로세스들은 데이터베이스 접근들을 중재하는 사용자 사이의 전체 일련의 상호작용들을 필요로 한다. 웹과 엔터프라이즈
어플리케이션에서 사용자 상호작용에 걸치는 것은 데이터베이스 트랜잭션에 허용되지 않는다. 다음 예제를 검토하자:
</para>
<itemizedlist>
<listitem>
<para>
대화상자의 첫 번째 화면이 열리고, 사용자에게 보여진 데이터는 특정 <literal>Session</literal>과 데이터베이스
트랜잭션 속에 로드되었다. 사용자가 객체들을 변경시키는 것이 자유롭다.
</para>
</listitem>
<listitem>
<para>
사용자는 5분 후에 "저장"을 클릭하고 그의 변경들이 영속화 되기를 기대한다; 그는 또한 그가 이 정보를 편집하는 유일한
개인이고 변경 충돌이 발생하지 않기를 기대한다.
</para>
</listitem>
</itemizedlist>
<para>
우리는 사용자의 관점에서, 이것을 작업 단위, 장기간 실행되는 <emphasis>어플리케이션 트랜잭션</emphasis>이라고 명명한다.
당신이 당신의 어플리케이션에서 이것을 어떻게 구현할 수 있는 많은 방법들이 존재한다.
</para>
<para>
첫 번째 naive 구현은 동시성 변경을 방지하고, 격리와 atomicity(원자 단위성)을 보장하기 위해 데이터베이스에 의해
소유된 잠금으로 사용자가 생각하는동안 <literal>Session</literal>과 데이터베이스 트랜잭션을 유지할 수도 있다.
이것은 물론 anti-패턴이다. 왜냐하면 잠금 다툼은 어플리케이션이 동시 사용자들의 가용 숫자를 높이는 것을 허용하지
않을 것이기 때문이다.
</para>
<para>
명료하게, 우리는 어플리케이션 트랜잭션을 구현하는데 몇몇 데이터베이스 트랜잭션들을 사용해야 한다. 이 경우에,
비지니스 프로세스들의 격리를 유지하는 것은 어플리케이션 티어의 부분적인 책임이 된다. 단일 어플리케이션 트랜잭션은
대개 여러 개의 데이터베이스 트랜잭션들에 걸친다. 그것은 이들 데이터베이스 트랜잭션들 중 오직 한 개(마지막 트랜잭션)가
업데이트된 데이터를 저장하고, 모든 다른 트랜잭션들이 단순히 데이터를 읽는 (예를 들면, 몇몇 요청/응답 주기에 걸치는
마법사 스타일의 대화 상자에서) 경우에만 원자단위가 될 것이다. 특히 당신이 Hibernate의 특징들을 사용할 경우에 , 이것은
들리는 것보다 구현하기가 더 쉽다:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>자동적인 버전화</emphasis> - Hibernate는 당신을 위해 자동적인 optimistic
동시성 제어를 행할 수 있고, 그것은 사용자가 생각하는 시간 동안 동시적인 변경이 발생했는지를 자동적으로 검출할 수 있다..
</para>
</listitem>
<listitem>
<para>
<emphasis>Detached 객체들</emphasis> - 만일 당신이 이미 논의된 <emphasis>session-per-request</emphasis>
패턴을 사용하고자 결정하는 경우, 모든 로드된 인스턴스들은 사용자가 생각하는 시간 동안 detached 상태에 있을 것이다.
Hibernate는 그 객체들을 재첨부시키고 변경들을 영속화 시키는 것을 허용해주며, 그 패턴은
<emphasis>session-per-request-with-detached-objects(detached-객체들을 가진 요청 당 세션)</emphasis>으로 명명된다.
자동적인 버전화는 동시성 변경들을 격리시키는데 사용된다.
</para>
</listitem>
<listitem>
<para>
<emphasis>Long Session</emphasis> - Hibernate <emphasis>Session</emphasis>은 데이터베이스
트랜잭션이 커밋된 후에 기본 JDBC 커넥션이 연결 해제될 수도 있고, 새로운 클라이언트 요청이 발생할 때 다시 연결될 수
있다. 이 패턴은 <emphasis>session-per-application-transaction(어플리케이션 트랜잭션 당 세션)</emphasis>으로
알려져 있고 재첨부를 불필요하게 만든다. 자동적인 버전화는 동시성 변경들을 격리시키는데 사용된다.
</para>
</listitem>
</itemizedlist>
<para>
<emphasis>session-per-request-with-detached-objects</emphasis>
<emphasis>session-per-application-transaction</emphasis> 양자는 장점들과 단점들을 갖는데, 우리는 이 장의
뒷 부분에서 optimistic 동시성 제어 단락에서 그것들을 논의한다.
</para>
</sect2>
<sect2 id="transactions-basics-identity">
<title>객체 identity 고려하기</title>
<para>
어플리케이션은 두 개의 다른 <literal>Session</literal>들 내에 있는 동일한 영속 상태에 동시에 접근할 수도 있다.
하지만 영속 클래스의 인스턴스는 두 개의 <literal>Session</literal> 인스턴스들 사이에 결코 공유되지 않는다.
그러므로 identity에 대한 두 개의 다른 개념들이 존재한다:
</para>
<variablelist spacing="compact">
<varlistentry>
<term>데이터베이스 Identity</term>
<listitem>
<para>
<literal>foo.getId().equals( bar.getId() )</literal>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>JVM Identity</term>
<listitem>
<para>
<literal>foo==bar</literal>
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
그때 (예를 들어 <literal>Session</literal> 영역에서) <emphasis>특정</emphasis> <literal>Session</literal>
첨부된 객체들의 경우 두 개의 개념들은 동등한 것이고, 데이터베이스 identity에 대한 JVM identity가 Hibernate에 의해 보장된다.
하지만, 어플리케이션이 두 개의 다른 세션들에서 "동일한" (영속 identity) 비지니스 객체에 동시에 접근하는 동안, 두 개의 인스턴스들은
실제로 "다르다"(JVM identity). 충돌들은 flush/커밋 시에 (자동적인 버전화)를 사용하여, optimistic 접근법을 사용하여 해결된다.
</para>
<para>
이 접근법은 Hibernate와 데이터베이스가 동시성에 대해 걱정하지 않도록 해준다; 그것은 또한 최상의 scalability를 제공한다.
왜냐하면 단일 쓰레드-작업 단위 내에서 identity 보장은 단지 비용이 드는 잠금이나 다른 동기화 수단들을 필요로 하지 않기 때문이다.
어플리케이션은 그것이 <literal>Session</literal> 당 단일 쓰레드를 강제하는 한, 어떤 비지니스 객체에 대해 결코 동기화 시킬
필요가 없다. 하나의 <literal>Session</literal> 내에서 어플리케이션은 객체들을 비교하는데 <literal>==</literal>
안전하게 사용할 수가 있다.
</para>
<para>
하지만, 하나의 <literal>Session</literal> 외부에서 <literal>==</literal>를 사용하는 어플리케이션은 예기치 않은
결과들을 보게 될 수도 있다. 이것은 어떤 예기치 않은 장소들에서, 예를 들어 당신이 두 개의 detached 인스턴스들을 동일한
<literal>Set</literal> 내에 집어넣을 경우에 발생할 수도 있다. 둘 다 동일한 데이터베이스 identity를 가질 수 있지만
(예를 들어 그것들은 동일한 행을 표현한다), JVM identity는 정의 상 detached 상태에 있는 인스턴스들을 보장하지 않는다.
개발자는 영속 클래스들내에 <literal>equals()</literal> 메소드와 <literal>hashCode()</literal> 메소드를 오버라이드 시켜야 하고
객체 equality에 대한 그 자신의 개념을 구현해야 한다. 하나의 경고가 존재한다: equality를 구현하는데 데이터베이스 identifier를 결코
사용하지 말고, 하나의 비지니스 키, 유일한, 대개 불변인 속성들의 조합을 사용하라. 데이터베이스 식별자는 만일 transient 객체가 영속화되는
경우에 변경될 것이다. 만일 transient 인스턴스가(대개 detached 인스턴스들과 함께) <literal>Set</literal> 내에 보관되는 경우에,
hashcode 변경은 <literal>Set</literal>의 계약을 파기시킨다. 비지니스 키들에 대한 속성들은 데이터베이스 프라이머리 키들 만큼
안정적이어서는 안되며, 당신은 오직 객체들이 동일한 <literal>Set</literal> 내에 있는 한에서 안정성을 보장해야만 한다.
이 쟁점에 관한 논의에 대한 더 많은 것을 Hibernate 웹 사이트를 보라. 또한 이것이 Hibernate 쟁점이 아니며, 단지 자바 객체
identity와 equality가 구현되어야 하는 방법임을 노트하라.
</para>
</sect2>
<sect2 id="transactions-basics-issues">
<title>공통된 쟁점들</title>
<para>
안티-패턴들 <emphasis>session-per-user-session</emphasis> 또는
<emphasis>session-per-application</emphasis>을 결코 사용하지 말라(물론 이 규칙에 대한 드문 예외상황들이 존재한다).
다음 쟁점들 중 몇몇이 또한 권장되는 패턴들로 나타날 수 있음을 노트하고, 당신이 설계 결정을 내리기 전에 내포된 의미들을
확실히 이해하라:
</para>
<itemizedlist>
<listitem>
<para>
<literal>Session</literal>은 쓰레드-안전하지 않다. HTTP 요청들, 세션 빈즈, 또는 Swing worker들처럼
동시에 작업하는 것으로 가정되는 것들은 하나의 <literal>Session</literal> 인스턴스가 공유될 경우에 경쟁 조건들을
발생시킬 것이다. 만일 당신이 당신의 <literal>HttpSession</literal> 내에 Hibernate
<literal>Session</literal>을 유지시키는 경우(나중에 논의됨), 당신은 당신의 Http 세션에 대한 접근을 동기화
시키는 것을 고려해야 한다. 그 밖의 경우, 충분히 빠르게 reload를 클릭하는 사용자는 두 개의 동시적으로 실행되는
쓰레드들 내에서 동일한 <literal>Session</literal>을 사용할 수도 있다.
</para>
</listitem>
<listitem>
<para>
Hibernate에 의해 던져진 예외상황은 당신이 당신의 데이터베이스 트랜잭션을 롤백 시키고 즉시 <literal>Session</literal>
닫아야 함을 의미한다(나중에 상세히 논의됨). 만일 당신의 <literal>Session</literal>이 어플리케이션에 바인드 되어 있는 경우,
당신은 어플리케이션을 중지시켜야 한다. 데이터베이스 트랜잭션 롤백은 당신의 비지니스 객체들을 그것들이 트랜잭션의 시작 시에
머물렀던 상태로 되돌리지는 않는다. 이것은 데이터베이스 상태와 비지니스 객체들이 동기화를 벗어남을 의미한다. 대개 이것은 문제가 아니다.
왜냐하면 예외상황들은 회복가능한 것이 아니고 당신이 어떻게든 롤백 후에 시작해야 하기 때문이다.
</para>
</listitem>
<listitem>
<para>
<literal>Session</literal>은 (Hibernate에 의해 dirty 상태로 관찰되었거나 체크된) 영속 상태에 있는
모든 객체를 캐시 시킨다. 이것은 당신이 오랜 시간 동안 <literal>Session</literal>을 열어둔 채로 유지하거나
단순하게 너무 많은 데이터를 로드시킬 경우에, 당신이 OutOfMemoryException을 얻기 전까지, 그것이 끝없이
성장한다는 점을 의미한다. 이것에 대한 하나의 해결책은 <literal>Session</literal> 캐시를 관리하기 위해
<literal>clear()</literal><literal>evict()</literal>를 호출하는 것이지만, 당신이 대용량 데이터
오퍼레이션들을 필요로 하는 경우에 당신은 대개 내장 프로시저를 고려해야 할 것이다. 몇몇 해결책들이
<xref linkend="batch"/>에 보여져 있다. 사용자 세션 동안에 <literal>Session</literal>을 열려진 채로
유지하는 것은 또한 실효성이 떨어진 데이터에 대한 높은 확률을 의미한다.
</para>
</listitem>
</itemizedlist>
</sect2>
</sect1>
<sect1 id="transactions-demarcation">
<title>데이터베이스 트랜잭션 경계 설정</title>
<para>
데이터베이스 (또는 시스템) 트랜잭션 경계들은 항상 필수적이다. 데이터베이스와의 통신은 데이터베이스 트랜잭션의 외부에서 발생할 수
없다(이것은 자동-커밋 모드로 사용되는 많은 개발자들에게는 혼동스러워 보인다). 항상 심지어 읽기 전용 오퍼레이션들에 대해서도 명료한
트랜잭션 경계들을 사용하라. 당신의 격리 레벨과 데이터베이스 가용성들에 따라, 이것은 필요하지 않을 수 있지만, 만일 당신이 항상
트랜잭션들을 명시적으로 경계 설정할 경우에는 하강하는 결점들이 존재하지 않는다.
</para>
<para>
Hibernate 어플리케이션은 관리되지 않는 환경(예를 들면. 스탠드얼론, 간단히 웹 어플리케이션들 또는 Swing 어플리케이션들)과
관리되는 J2EE 환경에서 실행될 수 있다. 관리되지 않는 환경에서, Hibernate는 대개 그것 자신의 데이터베이스 커넥션 풀에 대한
책임이 있다. 어플리케이션 개발자는 트랜잭션 경계들을 손수 설정해야 한다. 달리 말해, 개발자 스스로 데이터베이스 트랜잭션들을
시작하고, 커밋시키거나 롤백시켜야 한다. 관리되는 환경은 대개 예를 들어 EJB 세션 빈즈의 배치 디스크립터 속에 선언적으로 정의된
트랜잭션 어셈블리를 가진, 컨테이너에 의해-관리되는 트랜잭션들을 제공한다. 그때 프로그램 상의 트랜잭션 경계 설정은 더 이상 필요하지
않다. 심지어 <literal>Session</literal>을 flush 시키는 것이 자동적으로 행해진다.
</para>
<para>
하지만, 당신의 영속 계층이 이식성을 유지하게끔 자주 희망된다. Hibernate는 당신의 배치 환경의 고유한 트랜잭션 시스템 속으로
변환되는 <literal>Transaction</literal>이라 명명되는 wrapper API 를 제공한다. 이 API는 실제로 옵션이지만 우리는
당신이 CMT session bean 속에 있지 않는 한 그것의 사용을 강력하게 권장한다.
</para>
<para>
대개 <literal>Session</literal> 종료는 네 개의 구분되는 단계들을 수반한다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
세션을 flush 시킨다
</para>
</listitem>
<listitem>
<para>
트랜잭션을 커밋 시킨다
</para>
</listitem>
<listitem>
<para>
세션을 닫는다
</para>
</listitem>
<listitem>
<para>
예외상황들을 처리한다
</para>
</listitem>
</itemizedlist>
<para>
세션을 flush 시키는 것은 앞서 논의되었고, 우리는 이제 관리되는 환경과 관리되지 않는 환경 양자에서 트랜잭션 경계 설정과 예외상황을
더 자세히 살펴볼 것이다.
</para>
<sect2 id="transactions-demarcation-nonmanaged">
<title>관리되지 않는 환경</title>
<para>
만일 Hibernate 영속 계층이 관리되지 않는(non-managed) 환경에서 실행될 경우, 데이터베이스 커넥션들은 대개 Hibernate의
풀링 메커니즘에 의해 처리된다. session/transaction 처리 관용구는 다음과 같이 보여진다:
</para>
<programlisting><![CDATA[//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();
}]]></programlisting>
<para>
당신은 <literal>Session</literal>을 명시적으로 <literal>flush()</literal>시키지 말아야 한다 -
<literal>commit()</literal> 호출은 동기화를 자동적으로 트리거 시킨다.
</para>
<para>
<literal>close()</literal> 호출은 세션의 종료를 마크한다. <literal>close()</literal>의 주된 의미는
JDBC 커넥션이 그 세션에 의해 포기될 것이라는 점이다.
</para>
<para>
이 자바 코드는 이식성이 있고 관리되지 않는 환경과 JTA 환경 양자에서 실행된다.
</para>
<para>
당신은 통상의 어플리케이션에서 비지니스 코드 속에 이 관용구를 결코 보지 않을 것이다; 치명적인(시스템) 예외상황들은 항상
"상단"에서 잡혀야 한다. 달리 말해, Hibernate를 실행하는 코드가 (영속 계층에서) 호출되고 <literal>RuntimeException</literal>
처리하는 (그리고 대개 오직 제거하고 빠져나갈 수 있는) 코드는 다른 계층들 속에 있다. 이것은 당신 자신이 설계하는 도전점일
수 있고 당신은 J2EE/EJB 컨테이너 서비스들이 이용 가능할 때마다 J2EE/EJB 컨테이너 서비스들을 사용할 것이다. 예외상황
처리는 이 장의 뒷부분에서 논의된다.
</para>
<para>
당신은 (디폴트인) <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>를 선택해야 함을 노트하라.
</para>
</sect2>
<sect2 id="transactions-demarcation-jta">
<title>JTA 사용하기</title>
<para>
만일 당신의 영속 계층이 어플리케이션 서버에서(예를 들어, EJB 세션 빈즈 이면에서) 실행될 경우, Hibernate에 의해
획득된 모든 데이터소스 커넥션은 자동적으로 전역 JTA 트랜잭션의 부분일 것이다. Hibernate는 이 통합을 위한 두 개의
방도들을 제공한다.
</para>
<para>
만일 당신이 bean-managed transactions(BMT)를 사용할 경우 Hibernate는 당신이 <literal>Transaction</literal>
API를 사용할 경우에 BMT 트랜잭션을 시작하고 종료하도록 어플리케이션 서버에게 알려줄 것이다. 따라서 트랜잭션 관리 코드는
non-managed 환경과 동일하다.
</para>
<programlisting><![CDATA[// 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();
}]]></programlisting>
<para>
CMT의 경우, 트랜잭션 관할[경계 설정]은 프로그램 상이 아닌, session bean 배치 디스크립터들 속에서 행해진다. 당신이 스스로
<literal>Session</literal>을 수작업으로 flush 시키고 닫고자 원하지 않을 경우, 단지
<literal>hibernate.transaction.flush_before_completion</literal><literal>true</literal>로 설정하고,
<literal>hibernate.connection.release_mode</literal><literal>after_statement</literal> 또는
<literal>auto</literal>로 설정하고 <literal>hibernate.transaction.auto_close_session</literal>
<literal>true</literal>로 설정하라. Hibernate는 그때 자동적으로 flush 되고 당신을 위해 <literal>Session</literal>
닫을 것이다. 남아 있는 유일한 작업은 예외상황이 발생할 때 트랜잭션을 롤백시키는 것이다. 다행하게도 CMT bean에서, 이것이 자동적으로
일어난다. 왜냐하면 session bean 메소드에 의해 던져진 처리되지 않은 <literal>RuntimeException</literal>은 전역 트랜잭션을
롤백시키도록 컨테이너에게 통보하기 때문이다. <emphasis>이것은 당신이 CMT에서 Hibernate <literal>Transaction</literal> API를
사용할 필요가 전혀 없음을 의미한다.</emphasis>
</para>
<para>
당신이 Hibernate의 트랜잭션 팩토리를 구성할 때, 당신이 BMT session bean에서
<literal>org.hibernate.transaction.JTATransactionFactory</literal>를 선택해야하고, CMT session bean에서
<literal>org.hibernate.transaction.CMTTransactionFactory</literal>를 선택해야 함을 노트하라. 또한
<literal>org.hibernate.transaction.manager_lookup_class</literal>를 설정하는 것을 염두에 두라.
</para>
<para>
만일 당신이 CMT 환경에서 작업하고 있고, 세션을 자동적으로 flushing하고 닫는 것을 사용할 경우, 당신은 또한 당신의 코드의 다른
부분들에서 동일한 세션을 사용하고자 원할 수도 있다. 일반적으로 관리되지 않는 환경에서 당신은 세션을 소유하는데 하나의
<literal>ThreadLocal</literal> 변수를 사용할 것이지만, 한 개의 EJB 요청은 다른 쓰레드들(예를 들면 또 다른 세션 빈을
호출하는 세션 빈) 내에서 실행될 수도 있다. 만일 당신이 당신의 <literal>Session</literal> 인스턴스를 전달하는 것을
고민하고 싶지 않다면, <literal>SessionFactory</literal>이 JTA 트랜잭션 컨텍스트에 바인드되어 있는 한 개의 세션을
반환하는, <literal>getCurrentSession()</literal> 메소드를 제공한다. 이것은 Hibernate를 어플리케이션 속으로
통합시키는 가장 손쉬운 방법이다! "현재" 세션은 (위의 프로퍼티 설정들에 관계없이) auto-flush와 auto-close, 그리고
auto-connection-release를 항상 이용 가능하게 한다. 우리의 session/transaction 관리 idiom은 다음과 같이 감소된다:
</para>
<programlisting><![CDATA[// CMT idiom
Session sess = factory.getCurrentSession();
// do some work
...
]]></programlisting>
<para>
달리 말해, 당신이 관리 환경에서 행해야 하는 모든 것은 <literal>SessionFactory.getCurrentSession()</literal>
호출하고, 당신의 데이터 접근 작업을 행하고, 그리고 나머지를 컨테이너에게 남겨두는 것이다. 트랜잭션 경계들은 당신의
session bean의 배치 디스크립터들 속에 선언적으로 설정된다. 그 세션의 생명주기는 Hibernate에 의해 완전하게 관리된다.
</para>
<para>
<literal>after_statement</literal> 커넥션의 사용에는 한 가지 단서가 존재한다. JTA 명세서의 분별없는 제약성으로 인해,
<literal>scroll()</literal> 또는 <literal>iterate()</literal>에 의해 반환된 어떤 닫혀지지 않은
<literal>ScrollableResults</literal> 또는 <literal>Iterator</literal> 인스턴스들을 Hibernate가 자동적으로
제거하는 것이 불가능하다. 당신은 <literal>finally</literal> 블록에서 <literal>ScrollableResults.close()</literal>
또는 <literal>Hibernate.close(Iterator)</literal>를 명시적으로 호출하여 기본 데이터베이스 커서를
해제<emphasis>시켜야 한다</emphasis>. (물론 대부분의 어플리케이션들은 CMT 코드에서 <literal>scroll()</literal>
또는 <literal>iterate()</literal> 사용을 쉽게 피할 수 있다.)
</para>
</sect2>
<sect2 id="transactions-demarcation-exceptions">
<title>예외상황 처리</title>
<para>
만일<literal>Session</literal>이 (어떤 <literal>SQLException</literal>을 포함하는) 예외상황을 던질 경우,
당신은 데이터베이스 트랜잭션을 즉시 롤백시키고, <literal>Session.close()</literal>를 호출하고
<literal>Session</literal> 인스턴스를 폐기시켜야한다. <literal>Session</literal>의 어떤 메소드들은
그 세션을 일관된 상태로 남겨두지 <emphasis>않을</emphasis> 것이다. Hibernate에 의해 던져진 예외상황은 복구가능한
것으로 취급될 수 없다. 그 <literal>Session</literal><literal>finally</literal> 블록 내에서
<literal>close()</literal>를 호출하여 닫혀지도록 확실히 하라.
</para>
<para>
Hibernate 영속 계층에서 발생할 수 있는 대부분의 오류들을 포장하는, <literal>HibernateException</literal>
체크되지 않은 예외상황이다(그것은 Hibernate의 이전 버전에는 없었다). 우리의 의견으로, 우리는 낮은 계층에서 복구불가능한
예외상황을 붙잡도록 어플리케이션 개발자에게 강제하지 않을 것이다. 대부분의 시스템들에서, 체크되지 않은 치명적인 예외상황들은
(예를 들어, 더 높은 계층에서) 메소드 호출 스택의 첫 번째 프레임들 중 하나 속에서 처리되고, 한 개의 오류 메시지가 어플리케이션
사용자에게 표시된다(또는 어떤 다른 적절한 액션이 취해진다). Hibernate는 또한 <literal>HibernateException</literal>
아닌, 다른 체크되지 않은 예외상황들을 던질 수도 있음을 노트하라. 다시 이것들은 복구가능하지 않고 적절한 액션이 취해져야 한다.
</para>
<para>
Hibernate는 데이터베이스와 상호작용하는 동안에 던져진 <literal>SQLException</literal>들을 하나의
<literal>JDBCException</literal> 속에 포장한다. 사실, Hibernate는 그 예외상황을
<literal>JDBCException</literal>의 보다 의미있는 서브클래스로 변환하려고 시도할 것이다. 기본
<literal>SQLException</literal><literal>JDBCException.getCause()</literal>를 통해 항상 이용 가능하다.
Hibernate는<literal>SessionFactory</literal>에 첨부된 <literal>SQLExceptionConverter</literal>
사용하여 <literal>SQLException</literal>을 적당한 하나의 <literal>JDBCException</literal> 서브클래스로
변환시킨다. 디폴트로 <literal>SQLExceptionConverter</literal>는 구성된 dialect에 의해 정의된다; 하지만
맞춤 구현 속에 플러그인 시키는 것이 또한 가능하다(상세한 것은 <literal>SQLExceptionConverterFactory</literal>
클래스에 관한 javadocs를 보라). 표준 <literal>JDBCException</literal> 서브타입은 다음과 같다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>JDBCConnectionException</literal> - 기본 JDBC 통신에 대한 오류를 나타낸다.
</para>
</listitem>
<listitem>
<para>
<literal>SQLGrammarException</literal> - 생겨난 SQL에 대한 문법 또는 구문 문제점을 나타낸다.
</para>
</listitem>
<listitem>
<para>
<literal>ConstraintViolationException</literal> - 무결성 제약 위반에 관한 어떤 형식을 나타낸다.
</para>
</listitem>
<listitem>
<para>
<literal>LockAcquisitionException</literal> - 요청된 오퍼레이션을 실행하는데 필수적인 잠금 레벨을
획득하는 오류를 나타낸다.
</para>
</listitem>
<listitem>
<para>
<literal>GenericJDBCException</literal> - 다른 카테고리들 중 어떤 것으로 분류되지 않았던 일반적인 예외상황.
</para>
</listitem>
</itemizedlist>
</sect2>
</sect1>
<sect1 id="transactions-optimistic">
<title>Optimistic 동시성 제어</title>
<para>
고도의 동시성과 고도의 가용성을 일치시키는 유일한 접근법은 버전화를 가진 optimistic동시성 제어이다. 버전 체킹은 업데이트 충돌을
검출하기 위해(그리고 업데이트 손실을 방지하기 위해) 버전 번호들 또는 timestamp들을 사용한다. Hibernate는 optimistic 동시성을
사용하는 어플리케이션 코드 작성에 세 가지 가능한 접근법들을 제공한다. 우리가 보여주는 쓰임새들은 긴 어플리케이션 트랜잭션들의 상황
속에 있지만 버전 체킹 또한 단일 데이터베이스 트랜잭션들에서 업데이트 손실을 방지하는 이점을 갖고 있다.
</para>
<sect2 id="transactions-optimistic-manual">
<title>어플리케이션 버전 체킹</title>
<para>
하나의 구현에서 Hibernate로부터 많은 도움이 없이, 데이터베이스에 대한 각각의 상호작용은 새로운 <literal>Session</literal>
내에서 일어나고, 개발자는 영속 인스턴스들을 처리하기 전에 데이터베이스로부터 모든 영속 인스턴스들을 다시 로드시킬 책임이 있다.
이 접근법은 어플리케이션 트랜잭션을 확실히 격리시키기 위해 그것 자신의 버전 체킹을 수행하도록 어플리케이션에게 강제시킨다.
</para>
<programlisting><![CDATA[// 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();]]></programlisting>
<para>
version 프로퍼티는 <literal>&lt;version&gt;</literal>을 사용하여 매핑되고, Hibernate는 만일 엔티티가 dirty일 경우
flush 동안에 그것을 자동적으로 증가시킬 것이다.
</para>
<para>
물론, 당신이 낮은 데이터 동시성 환경에서 작업하고 있고 버전 체킹을 필요로 하지 않을 경우에, 당신은 이 접근법을 사용할 수 도 있고
단지 버전 체크를 생략할 수도 있다. 그 경우에, <emphasis>마지막의 커밋 성공</emphasis>은 당신의 긴 어플리케이션 트랜잭션들에
대한 디폴트 방도가 될 것이다. 이것이 어플리케이션의 사용자들을 혼동시킬 수 있음을 염두에 두라. 왜냐하면 사용자들은 오류 메시지들
또는 충돌 변경들을 병합시킬 기회 없이 업데이트들 손실을 겪을 수도 있기 때문이다.
</para>
<para>
명료하게 수작업 버전 체킹은 매우 사소한 환경들에서도 공포적이고 대부분의 어플리케이션들에 대해 실제적이지 않다. 흔히 단일
인스턴스 뿐만 아니라 변경된 객체들의 전체 그래프들이 체크되어야 한다. Hibernate는 설계 패러다임으로서 긴 <literal>Session</literal>
또는 detached 인스턴스들에 대해 자동적인 버전 체킹을 제공한다.
</para>
</sect2>
<sect2 id="transactions-optimistic-longsession">
<title>긴 세션과 자동적인 버전화</title>
<para>
하나의 <literal>Session</literal> 인스턴스와 그것의 영속 인스턴스들은 전체 어플리케이션 트랜잭션에 사용된다. Hibernate는
flush 할 때 인스턴스 버전들을 체크하고 만일 동시성 변경이 검출될 경우에 예외상황을 던진다. 이 예외상황을 잡아내고 처리하는 것을
개발자의 몫이다(공통된 옵션들은 변경들을 병합시키거나 또는 쓸모가 없지 않은 데이터로 비지니스 프로세스를 다시 시작하는 기회를
사용자에게 주는 것이다).
</para>
<para>
<literal>Session</literal>은 사용자 상호작용을 기다릴 때 어떤 기본 JDBC 커넥션으로부터 연결해제된다. 이 접근법은
데이터베이스 접근의 관점에서 보면 가장 효율적이다. 어플리케이션은 버전 체킹 또는 detached 인스턴스들을 재첨부하는 것에
그 자체 관계할 필요가 없거나 그것은 모든 데이터베이스 트랜잭션에서 인스턴스들을 다시 로드시킬 필요가 없다.
</para>
<programlisting><![CDATA[// foo is an instance loaded earlier by the Session
session.reconnect(); // Obtain a new JDBC connection
Transaction t = session.beginTransaction();
foo.setProperty("bar");
t.commit(); // End database transaction, flushing the change and checking the version
session.disconnect(); // Return JDBC connection ]]></programlisting>
<para>
<literal>foo</literal> 객체는 그것이 로드되었던 <literal>Session</literal>이 어느 것인지를 여전히 알고 있다.
<literal>Session.reconnect()</literal>은 새로운 커넥션을 획득하고(또는 당신이 커넥션을 제공할 수 있다) 그리고
그 세션을 다시 시작시킨다. <literal>Session.disconnect()</literal> 메소드는 JDBC 커넥션으로부터 세션을
연결 해제하고 (당신이 커넥션을 제공하지 않는 한) 그 커넥션을 풀(pool)로 반환할 것이다. 재연결 후에, 당신이 업데이트하고
있는 데이터에 대한 버전 체킹을 강제시키기 위해, 당신은 또 다른 트랜잭션에 의해 업데이트 되었던 어떤 객체들에 대해
<literal>LockMode.READ</literal>로서 <literal>Session.lock()</literal>을 호출할 수도 있다. 당신은 당신이
업데이트 <emphasis>중인</emphasis> 어떤 데이터에 대한 잠금을 필요로 하지 않는다.
</para>
<para>
만일 <literal>disconnect()</literal><literal>reconnect()</literal>에 대한 명시적인 호출들이 너무 번거러울 경우,
당신은 대신에 <literal>hibernate.connection.release_mode</literal>를 사용할 수도 있다.
</para>
<para>
만일 사용자가 생각하는시간 동안 <literal>Session</literal>이 저장되기에는 너무 큰 경우 이 패턴은 문제성이 있다. 예를 들어
<literal>HttpSession</literal>은 가능한 작은 것으로 유지되어야 한다. 또한 <literal>Session</literal>은 (필수의)
첫 번째 레벨 캐시이고 모든 로드된 객체들을 포함하기 때문에, 우리는 아마 적은 요청/응답 주기들에 대해서만 이 방도를 사용할 수 있다.
<literal>Session</literal>이 곧 실효성이 없는 데이터를 갖게 될 것이므로 이것이 진정으로 권장된다.
</para>
<para>
또한 당신이 연결해제된 <literal>Session</literal>을 영속 계층에 가깝게 유지해야함을 노트하라. 달리말해,
<literal>Session</literal>을 보관하는데 EJB stateful session bean을 사용하고 <literal>HttpSession</literal>
내에 그것을 저장하기 위해서 그것을 웹 계층에 전송하지 말라(또는 그것을 별도의 티어에 직렬화 시키지도 말라).
</para>
</sect2>
<sect2 id="transactions-optimistic-detached">
<title>Detached 객체들과 자동적인 버전화</title>
<para>
영속 저장소에 대한 각각의 상호작용은 새로운 <literal>Session</literal>에서 일어난다. 하지만 동일한 영속 인스턴스들은
데이터베이스와의 각각의 상호작용에 재사용된다. 어플리케이션은 원래 로드되었던 detached 인스턴스들의 상태를 또 다른
<literal>Session</literal> 내에서 처리하고 나서 <literal>Session.update()</literal>,
<literal>Session.saveOrUpdate()</literal>, <literal>Session.merge()</literal>를 사용하여 그것들을
다시 첨부시킨다.
</para>
<programlisting><![CDATA[// 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();]]></programlisting>
<para>
다시, Hibernate는 flush 동안에 인스턴스 버전들을 체크할 것이고 업데이트 충돌이 발생할 경우에 예외상황을 던질 것이다.
</para>
<para>
당신은 또한 <literal>update()</literal>대신에 <literal>lock()</literal>을 호출할 수도 있고 만일 그 객체가
변경되지 않았음을 당신이 확신하는 경우에 (버전 체킹을 수행하고 모든 캐시들을 무시하는) <literal>LockMode.READ</literal>
사용할 수 있다.
</para>
</sect2>
<sect2 id="transactions-optimistic-customizing">
<title>자동적인 버전화를 맞춤화 시키기</title>
<para>
당신은 <literal>optimistic-lock</literal> 매핑 속성을 <literal>false</literal>로 설정함으로써 특정 프로퍼티들과
콜렉션들에 대한 Hibernate의 자동적인 버전 증가를 불가능하도록 할 수도 있다. 그때 Hibernate는 그 프로퍼티가 dirty 일 경우에
더 이상 버전을 증가시키지 않을 것이다.
</para>
<para>
리거시 데이터베이스 스키마들은 자주 static이고 변경될 수 없다. 또는 다른 어플리케이션들은 또한 동일한 데이터베이스에 접근하고
버전 번호들 또는 심지어 timestamp들을 처리하는 방법을 모를 수도 있다. 두 경우들에서, 버전화는 테이블 내의 특정 컬럼에 의지할 수
없다. version 또는 timestamp 프로퍼티 매핑 없이 행 내의 모든 필드들에 대한 상태를 비교하여 버전 체크를 강제시키기 위해서,
<literal>&lt;class&gt;</literal> 매핑 속에 <literal>optimistic-lock="all"</literal>을 표시하라. 만일
Hibernate가 이전 상태와 새로운 상태를 비교할 수 있을 경우에, 예를 들면 당신이 하나의 긴 <literal>Session</literal>
사용하고 session-per-request-with-detached-objects을 사용하지 않을 경우 이것은 개념적으로만 동작함을 노트하라.
</para>
<para>
때때로 행해졌던 변경들이 중첩되지 않는 한 동시적인 변경이 허용될 수 있다. 만일 <literal>&lt;class&gt;</literal>
매핑할 때 당신이 <literal>optimistic-lock="dirty"</literal>를 설정하면, Hibernate는 flush 동안에 dirty 필드들을
비교만 할 것이다.
</para>
<para>
두 경우들에서, 전용 version/timestamp 컬럼의 경우 또는 full/dirty 필드 비교의 경우, Hibernate는 법전 체크를 실행하고
정보를 업데이트하는데 엔티티 당 (적절한 <literal>WHERE</literal> 절을 가진) 한 개의<literal>UPDATE</literal>
문장을 사용한다. 만일 당신이 연관된 엔티티들에 대한 재첨부를 케스케이드 하는데 transitive 영속을 사용할 경우,
Hibernate는 불필요하게 업데이트들을 실행할 수도 있다. 이것은 대개 문제가 아니지만, 심지어 변경들이 detached 인스턴스들에
대해 행해지지 않았을 때에도 데이터베이스 내에서 <emphasis>on update</emphasis> 트리거들이 실행될 수도 있다. 그 행을
업데이트하기 전에 변경들이 실제로 일어났음을 확인하기 위해 인스턴스를 <literal>SELECT</literal>하는 것을 Hibernate에게
강제시키는, <literal>&lt;class&gt;</literal> 매핑 속에 <literal>select-before-update="true"</literal>
설정함으로써 당신은 이 특징을 맞춤화 시킬 수 있다.
</para>
</sect2>
</sect1>
<sect1 id="transactions-locking">
<title>Pessimistic 잠금</title>
<para>
사용자들은 잠금 방도에 대해 걱정하는데 많은 시간을 할애하하려고 생각하지 않는다. 대개 JDBC 커넥션들에 대한 격리 레벨을 지정하는
것으로 충분하고 그런 다음 단순히 데이터베이스로 하여금 모든 작업을 행하도록 한다. 하지만 진일보한 사용자들은 때때로 배타적인
pessimistic 잠금들을 얻거나 또는 새로운 트랜잭션의 시작 시에 잠금들을 다시 얻고자 원할 수도 있다.
</para>
<para>
Hibernate는 결코 메모리 내에 있는 객체들이 아닌, 데이터베이스의 잠금 메커니즘을 항상 사용할 것이다!
</para>
<para>
<literal>LockMode</literal> 클래스는 Hibernate에 의해 획득될 수 있는 다른 잠금 레벨들을 정의한다. 잠금은 다음 메커니즘들에
의해 얻어진다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>LockMode.WRITE</literal>는 Hibernate가 한 행을 업데이트 하거나 insert 할 때 자동적으로 획득된다.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.UPGRADE</literal><literal>SELECT ... FOR UPDATE</literal> 구문을 지원하는
데이터베이스 상에서 <literal>SELECT ... FOR UPDATE</literal>를 사용하여 명시적인 사용자 요청 상에서
얻어질 수 있다.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.UPGRADE_NOWAIT</literal>는 오라클에서 <literal>SELECT ... FOR UPDATE NOWAIT</literal>
사용하여 명시적인 사용자 요청 상에서 얻어질 수도 있다.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.READ</literal>는 Hibernate가 반복 가능한 읽기(Repeatable Read) 또는 Serialization
격리 레벨에서 데이터를 읽어들일 때 자동적으로 얻어질 수도 있다. 명시적인 사용자 요청에 의해 다시 얻어질 수도 있다.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.NONE</literal>은 잠금이 없음을 나타낸다. 모든 객체들은 <literal>Transaction</literal>의 끝에서
이 잠금 모드로 전환된다. <literal>update()</literal> 또는 <literal>saveOrUpdate()</literal>에 대한 호출을 통해
세션과 연관된 객체들이 또한 이 잠금 모드로 시작된다.
</para>
</listitem>
</itemizedlist>
<para>
"명시적인 사용자 요청"은 다음 방법들 중 하나로 표현된다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>LockMode</literal>를 지정한 <literal>Session.load()</literal>에 대한 호출.
</para>
</listitem>
<listitem>
<para>
<literal>Session.lock()</literal>에 대한 호출.
</para>
</listitem>
<listitem>
<para>
<literal>Query.setLockMode()</literal>에 대한 호출.
</para>
</listitem>
</itemizedlist>
<para>
만일 <literal>Session.load()</literal><literal>UPGRADE</literal> 또는 <literal>UPGRADE_NOWAIT</literal>
모드로 호출되고 ,요청된 객체가 아직 이 세션에 의해 로드되지 않았다면, 그 객체는 <literal>SELECT ... FOR UPDATE</literal>
사용하여 로드된다. 만일 요청된 것이 아닌 다소 제한적인 잠금으로 이미 로드되어 있는 객체에 대해 <literal>load()</literal>
호출될 경우, Hibernate는 그 객체에 대해 <literal>lock()</literal>을 호출한다.
</para>
<para>
만일 지정된 잠금 모드가 <literal>READ</literal>, <literal>UPGRADE</literal> 또는
<literal>UPGRADE_NOWAIT</literal> 일 경우에 <literal>Session.lock()</literal>은 버전 번호 체크를 수행한다.
(<literal>UPGRADE</literal> 또는 <literal>UPGRADE_NOWAIT</literal> 인 경우에,
<literal>SELECT ... FOR UPDATE</literal>가 사용된다.)
</para>
<para>
만일 데이터베이스가 요청된 잠금 모드를 지원하지 않을 경우, (예외상황을 던지는 대신에) Hibernate는 적절한 대체 모드를 사용할 것이다.
이것은 어플리케이션이 이식 가능할 것임을 확실히 해준다.
</para>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,273 @@
<chapter id="xml">
<title>XML 매핑</title>
<para><emphasis>
이것은 Hibernate3.0에서 실험적인 특징이고 매우 활동적으로 개발 중에 있음을 노트하라.
</emphasis></para>
<sect1 id="xml-intro" revision="1">
<title>XML 데이터로 작업하기</title>
<para>
Hibernate는 당신이 영속 POJO들로 작업하는 것과 아주 동일한 방법으로 영속 XML 데이터에 작업하도록 해준다. 파싱된 XML 트리는
단지 객체 레벨에서 관계형 데이터를 나타내는 또 다른 방법으로 간주될 수 있다. 하나의 파싱된 XML 트리는 POJO들 대신, 객체 레벨에서
관계형 데이터를 표현하는 단지 또 다른 방법으로 간주될 수 있다.
</para>
<para>
Hibernate는 XML 트리들을 처리하는 API로서 dom4j를 지원한다. 당신은 데이터베이스로부터 dom4j 트리들을 검색하고 당신이 그 트리를
데이터베이스와 자동적으로 동기화시키기 위해 어떤 변경을 행하도록 하는 질의들을 작성할 수 있다. 당신은 심지어 XML 문서를 취하고,
dom4j를 사용하여 그것을 파싱하고, Hibernate의 다음 기본적인 오퍼레이션들 중 어떤 것으로서 그것을 데이터베이스에 저장시킬 수 있다:
<literal>persist(), saveOrUpdate(), merge(), delete(), replicate()</literal>(merging(병합)은 아직 지원되지
않는다).
</para>
<para>
이 특징은 데이터 가져오기/내보내기,JMS 또는 SOAP 그리고 XSLT-기반의 레포팅을 통한 엔티티 데이터의 구체화를 포함하는 많은
어플리케이션들을 갖는다.
</para>
<para>
하나의 매핑은 클래스들의 프로퍼티들과 XML 문서의 노드들을 데이터베이스로 동시에 매핑시키는데 사용될 수 있거나, 만일 매핑할 클래스가
존재하지 않을 경우, 그것은 단지 XML을 매핑시키는데 사용될 수도 있다.
</para>
<sect2 id="xml-intro-mapping">
<title>XML과 클래스 매핑을 함께 지정하기</title>
<para>
다음은 POJO와 XML을 동시에 매핑시키는 예제이다 :
</para>
<programlisting><![CDATA[<class name="Account"
table="ACCOUNTS"
node="account">
<id name="accountId"
column="ACCOUNT_ID"
node="@id"/>
<many-to-one name="customer"
column="CUSTOMER_ID"
node="customer/@id"
embed-xml="false"/>
<property name="balance"
column="BALANCE"
node="balance"/>
...
</class>]]></programlisting>
</sect2>
<sect2 id="xml-onlyxml">
<title>XML 매핑만을 지정하기</title>
<para>
다음은 POJO 클래스가 존재하지 않는 예제이다:
</para>
<programlisting><![CDATA[<class entity-name="Account"
table="ACCOUNTS"
node="account">
<id name="id"
column="ACCOUNT_ID"
node="@id"
type="string"/>
<many-to-one name="customerId"
column="CUSTOMER_ID"
node="customer/@id"
embed-xml="false"
entity-name="Customer"/>
<property name="balance"
column="BALANCE"
node="balance"
type="big_decimal"/>
...
</class>]]></programlisting>
<para>
이 매핑은 dom4j 트리로서 또는 프로퍼티 name/value 쌍들(java <literal>Map</literal>들)의 그래프로서 데이터에
접근하는 것을 당신에게 허용해준다. 프로퍼티 이름들은 HQL 질의들 내에서 참조될 수도 있는 순수하게 논리적인 구조체들이다.
</para>
</sect2>
</sect1>
<sect1 id="xml-mapping" revision="1">
<title>XML 매핑 메타데이터</title>
<para>
많은 Hibernate 매핑 요소들은 <literal>node</literal> 속성을 수용한다. 이것은 당신이 프로퍼티 또는 엔티티 데이터를 소유하는
XML 속성이나 요소의 이름을 지정하도록 한다. <literal>node</literal> 속성의 포맷은 다음 중 하나이어야 한다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para><literal>"element-name"</literal> - 명명된 XML 요소로 매핑시킨다</para>
</listitem>
<listitem>
<para><literal>"@attribute-name"</literal> - 명명된 XML 속성으로 매핑시킨다 </para>
</listitem>
<listitem>
<para><literal>"."</literal> - 부모 요소로 매핑 시킨다</para>
</listitem>
<listitem>
<para>
<literal>"element-name/@attribute-name"</literal> -
명명된 요소의 명명된 속성으로 매핑시킨다
</para>
</listitem>
</itemizedlist>
<para>
콜렉션들과 단일 값 콜렉션들의 경우, 추가적인 <literal>embed-xml</literal> 속성이 존재한다. 만일 <literal>embed-xml="true"</literal>
일 경우, 연관된 엔티티(또는 value 타입을 가진 콜렉션)에 대한 디폴트 XML 트리는 그 연관을 소유하는 엔티티에 대한 XML 트리 속에
직접 삽입될 것이다. 그 밖의 경우 <literal>embed-xml="false"</literal> 일 경우, 참조된 식별자 값 만이 단일 포인트 연관들에
대해 나타날 것이고 콜렉션들은 단순히 전혀 나타나지 않을 것이다.
</para>
<para>
당신은 너무 많은 연관들에 대해 <literal>embed-xml="true"</literal>로 남겨두지 말도록 주의해야 한다. 왜냐하면 XML이
순환적으로 잘 처리하지 못하기 때문이다!
</para>
<programlisting><![CDATA[<class name="Customer"
table="CUSTOMER"
node="customer">
<id name="id"
column="CUST_ID"
node="@id"/>
<map name="accounts"
node="."
embed-xml="true">
<key column="CUSTOMER_ID"
not-null="true"/>
<map-key column="SHORT_DESC"
node="@short-desc"
type="string"/>
<one-to-many entity-name="Account"
embed-xml="false"
node="account"/>
</map>
<component name="name"
node="name">
<property name="firstName"
node="first-name"/>
<property name="initial"
node="initial"/>
<property name="lastName"
node="last-name"/>
</component>
...
</class>]]></programlisting>
<para>
이 경우에, 우리는 실제 account 데이터가 아닌, account id들을 가진 콜렉션을 삽입시키기로 결정했다. 다음 HQL 질의:
</para>
<programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
<para>
는 다음과 같은 데이터셋들을 반환할 것이다:
</para>
<programlisting><![CDATA[<customer id="123456789">
<account short-desc="Savings">987632567</account>
<account short-desc="Credit Card">985612323</account>
<name>
<first-name>Gavin</first-name>
<initial>A</initial>
<last-name>King</last-name>
</name>
...
</customer>]]></programlisting>
<para>
만일 당신이 <literal>&lt;one-to-many&gt;</literal> 매핑에 대해 <literal>embed-xml="true"</literal>를 설정할 경우,
데이터는 다음과 같이 보일 수도 있다:
</para>
<programlisting><![CDATA[<customer id="123456789">
<account id="987632567" short-desc="Savings">
<customer id="123456789"/>
<balance>100.29</balance>
</account>
<account id="985612323" short-desc="Credit Card">
<customer id="123456789"/>
<balance>-2370.34</balance>
</account>
<name>
<first-name>Gavin</first-name>
<initial>A</initial>
<last-name>King</last-name>
</name>
...
</customer>]]></programlisting>
</sect1>
<sect1 id="xml-manipulation" revision="1">
<title>XML 데이터 처리하기</title>
<para>
우리의 어플리케이션 내에서 XML 문서들을 다시 읽어들이고 업데이트 시키자. 우리는 dom4j 세션을 얻어서 이것을 행한다:
</para>
<programlisting><![CDATA[Document doc = ....;
Session session = factory.openSession();
Session dom4jSession = session.getSession(EntityMode.DOM4J);
Transaction tx = session.beginTransaction();
List results = dom4jSession
.createQuery("from Customer c left join fetch c.accounts where c.lastName like :lastName")
.list();
for ( int i=0; i<results.size(); i++ ) {
//add the customer data to the XML document
Element customer = (Element) results.get(i);
doc.add(customer);
}
tx.commit();
session.close();]]></programlisting>
<programlisting><![CDATA[Session session = factory.openSession();
Session dom4jSession = session.getSession(EntityMode.DOM4J);
Transaction tx = session.beginTransaction();
Element cust = (Element) dom4jSession.get("Customer", customerId);
for ( int i=0; i<results.size(); i++ ) {
Element customer = (Element) results.get(i);
//change the customer name in the XML and database
Element name = customer.element("name");
name.element("first-name").setText(firstName);
name.element("initial").setText(initial);
name.element("last-name").setText(lastName);
}
tx.commit();
session.close();]]></programlisting>
<para>
XML 기반의 데이터 가져오기/내보내기를 구현하는데 이 특징과 Hibernate의 <literal>replicate()</literal> 오퍼레이션을
결합시키는 것이 매우 유용하다.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,526 @@
<?xml version="1.0"?>
<!--
This is the XSL FO configuration file for the Hibernate
Reference Documentation. It defines a custom titlepage and
the parameters for the A4 sized PDF printable output.
It took me days to figure out this stuff and fix most of
the obvious bugs in the DocBook XSL distribution. Some of
the workarounds might not be appropriate with a newer version
of DocBook XSL. This file is released as part of Hibernate,
hence LGPL licensed.
christian@hibernate.org
-->
<!DOCTYPE xsl:stylesheet [
<!ENTITY db_xsl_path "../../support/docbook-xsl/">
]>
<xsl:stylesheet
version="1.0"
xmlns="http://www.w3.org/TR/xhtml1/transitional"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
exclude-result-prefixes="#default">
<xsl:import href="&db_xsl_path;/fo/docbook.xsl"/>
<!--###################################################
Custom Title Page
################################################### -->
<xsl:template name="book.titlepage.recto">
<fo:block>
<fo:table table-layout="fixed" width="175mm">
<fo:table-column column-width="175mm"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell text-align="center">
<fo:block>
<fo:external-graphic src="file:images/hibernate_logo_a.png"/>
</fo:block>
<fo:block font-family="가을체" font-size="24pt" padding-before="10mm">
<xsl:value-of select="bookinfo/subtitle"/>
</fo:block>
<fo:block font-family="가을체" font-size="14pt" padding="10mm">
버전:
<xsl:value-of select="bookinfo/releaseinfo"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:block>
</xsl:template>
<!-- Prevent blank pages in output -->
<xsl:template name="book.titlepage.before.verso">
</xsl:template>
<xsl:template name="book.titlepage.verso">
</xsl:template>
<xsl:template name="book.titlepage.separator">
</xsl:template>
<!--###################################################
Header
################################################### -->
<!-- More space in the center header for long text -->
<xsl:attribute-set name="header.content.properties">
<xsl:attribute name="font-family">
<xsl:value-of select="$title.font.family"/>
</xsl:attribute>
<xsl:attribute name="margin-left">-5em</xsl:attribute>
<xsl:attribute name="margin-right">-5em</xsl:attribute>
</xsl:attribute-set>
<!--###################################################
Custom Footer
################################################### -->
<!-- This footer prints the Hibernate version number on the left side -->
<xsl:template name="footer.content">
<xsl:param name="pageclass" select="''"/>
<xsl:param name="sequence" select="''"/>
<xsl:param name="position" select="''"/>
<xsl:param name="gentext-key" select="''"/>
<xsl:variable name="Version">
<xsl:choose>
<xsl:when test="//releaseinfo">
<xsl:text>하이버네이트 </xsl:text>
<xsl:value-of select="//releaseinfo"/>
</xsl:when>
<xsl:otherwise>
<!-- nop -->
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$sequence='blank'">
<xsl:choose>
<xsl:when test="$double.sided != 0 and $position = 'left'">
<xsl:value-of select="$Version"/>
</xsl:when>
<xsl:when test="$double.sided = 0 and $position = 'center'">
<!-- nop -->
</xsl:when>
<xsl:otherwise>
<fo:page-number/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$pageclass='titlepage'">
<!-- nop: other titlepage sequences have no footer -->
</xsl:when>
<xsl:when test="$double.sided != 0 and $sequence = 'even' and $position='left'">
<fo:page-number/>
</xsl:when>
<xsl:when test="$double.sided != 0 and $sequence = 'odd' and $position='right'">
<fo:page-number/>
</xsl:when>
<xsl:when test="$double.sided = 0 and $position='right'">
<fo:page-number/>
</xsl:when>
<xsl:when test="$double.sided != 0 and $sequence = 'odd' and $position='left'">
<xsl:value-of select="$Version"/>
</xsl:when>
<xsl:when test="$double.sided != 0 and $sequence = 'even' and $position='right'">
<xsl:value-of select="$Version"/>
</xsl:when>
<xsl:when test="$double.sided = 0 and $position='left'">
<xsl:value-of select="$Version"/>
</xsl:when>
<xsl:otherwise>
<!-- nop -->
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--###################################################
Custom Toc Line
################################################### -->
<!-- Improve the TOC. -->
<xsl:template name="toc.line">
<xsl:variable name="id">
<xsl:call-template name="object.id"/>
</xsl:variable>
<xsl:variable name="label">
<xsl:apply-templates select="." mode="label.markup"/>
</xsl:variable>
<fo:block text-align-last="justify"
end-indent="{$toc.indent.width}pt"
last-line-end-indent="-{$toc.indent.width}pt">
<fo:inline keep-with-next.within-line="always">
<fo:basic-link internal-destination="{$id}">
<!-- Chapter titles should be bold. -->
<xsl:choose>
<xsl:when test="local-name(.) = 'chapter'">
<xsl:attribute name="font-weight">bold</xsl:attribute>
</xsl:when>
</xsl:choose>
<xsl:if test="$label != ''">
<xsl:copy-of select="$label"/>
<xsl:value-of select="$autotoc.label.separator"/>
</xsl:if>
<xsl:apply-templates select="." mode="titleabbrev.markup"/>
</fo:basic-link>
</fo:inline>
<fo:inline keep-together.within-line="always">
<xsl:text> </xsl:text>
<fo:leader leader-pattern="dots"
leader-pattern-width="3pt"
leader-alignment="reference-area"
keep-with-next.within-line="always"/>
<xsl:text> </xsl:text>
<fo:basic-link internal-destination="{$id}">
<fo:page-number-citation ref-id="{$id}"/>
</fo:basic-link>
</fo:inline>
</fo:block>
</xsl:template>
<!--###################################################
Extensions
################################################### -->
<!-- These extensions are required for table printing and other stuff -->
<xsl:param name="use.extensions">1</xsl:param>
<xsl:param name="tablecolumns.extension">0</xsl:param>
<!-- FOP provide only PDF Bookmarks at the moment -->
<xsl:param name="fop.extensions">1</xsl:param>
<!--###################################################
Table Of Contents
################################################### -->
<!-- Generate the TOCs for named components only -->
<xsl:param name="generate.toc">
book toc
</xsl:param>
<!-- Show only Sections up to level 3 in the TOCs -->
<xsl:param name="toc.section.depth">3</xsl:param>
<!-- Dot and Whitespace as separator in TOC between Label and Title-->
<xsl:param name="autotoc.label.separator" select="'. '"/>
<!--###################################################
Paper & Page Size
################################################### -->
<!-- Paper type, no headers on blank pages, no double sided printing -->
<xsl:param name="paper.type" select="'A4'"/>
<xsl:param name="double.sided">0</xsl:param>
<xsl:param name="headers.on.blank.pages">0</xsl:param>
<xsl:param name="footers.on.blank.pages">0</xsl:param>
<!-- Space between paper border and content (chaotic stuff, don't touch) -->
<xsl:param name="page.margin.top">5mm</xsl:param>
<xsl:param name="region.before.extent">10mm</xsl:param>
<xsl:param name="body.margin.top">10mm</xsl:param>
<xsl:param name="body.margin.bottom">15mm</xsl:param>
<xsl:param name="region.after.extent">10mm</xsl:param>
<xsl:param name="page.margin.bottom">0mm</xsl:param>
<xsl:param name="page.margin.outer">18mm</xsl:param>
<xsl:param name="page.margin.inner">18mm</xsl:param>
<!-- No intendation of Titles -->
<xsl:param name="title.margin.left">0pc</xsl:param>
<!--###################################################
Fonts & Styles
################################################### -->
<!-- Default Font size -->
<xsl:param name="body.font.master">11</xsl:param>
<!-- Line height in body text -->
<xsl:param name="line-height">1.4</xsl:param>
<!-- Monospaced fonts are smaller than regular text -->
<xsl:attribute-set name="monospace.properties">
<xsl:attribute name="font-family">
<xsl:value-of select="$monospace.font.family"/>
</xsl:attribute>
<xsl:attribute name="font-size">0.8em</xsl:attribute>
</xsl:attribute-set>
<!--###################################################
Tables
################################################### -->
<!-- The table width should be adapted to the paper size -->
<xsl:param name="default.table.width">17.4cm</xsl:param>
<!-- Some padding inside tables -->
<xsl:attribute-set name="table.cell.padding">
<xsl:attribute name="padding-left">4pt</xsl:attribute>
<xsl:attribute name="padding-right">4pt</xsl:attribute>
<xsl:attribute name="padding-top">4pt</xsl:attribute>
<xsl:attribute name="padding-bottom">4pt</xsl:attribute>
</xsl:attribute-set>
<!-- Only hairlines as frame and cell borders in tables -->
<xsl:param name="table.frame.border.thickness">0.1pt</xsl:param>
<xsl:param name="table.cell.border.thickness">0.1pt</xsl:param>
<!--###################################################
Labels
################################################### -->
<!-- Label Chapters and Sections (numbering) -->
<xsl:param name="chapter.autolabel">1</xsl:param>
<xsl:param name="section.autolabel" select="1"/>
<xsl:param name="section.label.includes.component.label" select="1"/>
<!-- Label only Sections up to level 2 -->
<xsl:param name="local.l10n.xml" select="document('')"/>
<l:i18n xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0">
<l:l10n language="ko">
<l:context name="title-numbered">
<l:template name="sect3" text="%t"/>
<l:template name="sect4" text="%t"/>
<l:template name="sect5" text="%t"/>
</l:context>
<l:context name="section-xref-numbered">
<l:template name="sect3" text="the section called %t"/>
<l:template name="sect4" text="the section called %t"/>
<l:template name="sect5" text="the section called %t"/>
</l:context>
</l:l10n>
</l:i18n>
<!--###################################################
Titles
################################################### -->
<!-- Chapter title size -->
<xsl:attribute-set name="chapter.titlepage.recto.style">
<xsl:attribute name="text-align">left</xsl:attribute>
<xsl:attribute name="font-weight">bold</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master * 1.8"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
</xsl:attribute-set>
<!-- Why is the font-size for chapters hardcoded in the XSL FO templates?
Let's remove it, so this sucker can use our attribute-set only... -->
<xsl:template match="title" mode="chapter.titlepage.recto.auto.mode">
<fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format"
xsl:use-attribute-sets="chapter.titlepage.recto.style">
<xsl:call-template name="component.title">
<xsl:with-param name="node" select="ancestor-or-self::chapter[1]"/>
</xsl:call-template>
</fo:block>
</xsl:template>
<!-- Sections 1, 2 and 3 titles have a small bump factor and padding -->
<xsl:attribute-set name="section.title.level1.properties">
<xsl:attribute name="space-before.optimum">0.8em</xsl:attribute>
<xsl:attribute name="space-before.minimum">0.8em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.8em</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master * 1.5"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="section.title.level2.properties">
<xsl:attribute name="space-before.optimum">0.6em</xsl:attribute>
<xsl:attribute name="space-before.minimum">0.6em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.6em</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master * 1.25"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="section.title.level3.properties">
<xsl:attribute name="space-before.optimum">0.4em</xsl:attribute>
<xsl:attribute name="space-before.minimum">0.4em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.4em</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master * 1.0"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
</xsl:attribute-set>
<!-- Titles of formal objects (tables, examples, ...) -->
<xsl:attribute-set name="formal.title.properties" use-attribute-sets="normal.para.spacing">
<xsl:attribute name="font-weight">bold</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
<xsl:attribute name="hyphenate">false</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.4em</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.6em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.8em</xsl:attribute>
</xsl:attribute-set>
<!--###################################################
Programlistings
################################################### -->
<!-- Verbatim text formatting (programlistings) -->
<xsl:attribute-set name="verbatim.properties">
<xsl:attribute name="space-before.minimum">1em</xsl:attribute>
<xsl:attribute name="space-before.optimum">1em</xsl:attribute>
<xsl:attribute name="space-before.maximum">1em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
<xsl:attribute name="border-color">#444444</xsl:attribute>
<xsl:attribute name="border-style">solid</xsl:attribute>
<xsl:attribute name="border-width">0.1pt</xsl:attribute>
<xsl:attribute name="padding-top">0.5em</xsl:attribute>
<xsl:attribute name="padding-left">0.5em</xsl:attribute>
<xsl:attribute name="padding-right">0.5em</xsl:attribute>
<xsl:attribute name="padding-bottom">0.5em</xsl:attribute>
<xsl:attribute name="margin-left">0.5em</xsl:attribute>
<xsl:attribute name="margin-right">0.5em</xsl:attribute>
</xsl:attribute-set>
<!-- Shade (background) programlistings -->
<xsl:param name="shade.verbatim">1</xsl:param>
<xsl:attribute-set name="shade.verbatim.style">
<xsl:attribute name="background-color">#F0F0F0</xsl:attribute>
</xsl:attribute-set>
<!--###################################################
Callouts
################################################### -->
<!-- We want to use callouts... -->
<xsl:param name="callout.extensions">1</xsl:param>
<!-- Place callout bullets at this column in programmlisting.-->
<xsl:param name="callout.defaultcolumn">90</xsl:param>
<!--
No, don't use crappy graphics for the callout bullets. This setting
enables some weird Unicode rendering for some fancy bullet points
in callouts. By default, this can only count to 10 and produces
strange results if you ever have more than 10 callouts for one
programlisting. We will fix that next.
-->
<xsl:param name="callout.graphics">0</xsl:param>
<!--
Again, fun with DocBook XSL: The callout bullets are rendered in
two places: In the programlisting itself and in the list below
the listing, with the actual callout text. The rendering in the
programlisting is some XSL transformer extension (e.g. a Saxon
extension), so we can't change that without messing with the
extensions. We only can turn it off by setting this limit to
zero, then, a simple bracket style like "(3)" and "(4)" will
be used in the programlisting.
-->
<xsl:param name="callout.unicode.number.limit" select="'0'"></xsl:param>
<!--
The callout bullets in the actual callout list will be rendered
with an XSL FO template. The default template is broken: limited to 10
nice looking Unicode bullet points and then it doesn't print anything,
the fallback doesn't work. We implement our own template, which is not
as complicated, more ugly, but works. As always, function is more
important than form.
-->
<xsl:template name="callout-bug">
<xsl:param name="conum" select='1'/>
<fo:inline
color="black"
padding-top="0.1em"
padding-bottom="0.1em"
padding-start="0.2em"
padding-end="0.2em"
baseline-shift="0.1em"
font-family="{$monospace.font.family}"
font-weight="bold"
font-size="75%">
<xsl:text>(</xsl:text>
<xsl:value-of select="$conum"/>
<xsl:text>)</xsl:text>
</fo:inline>
</xsl:template>
<!--###################################################
Misc
################################################### -->
<!-- Correct placement of titles for figures and examples. -->
<xsl:param name="formal.title.placement">
figure after
example before
equation before
table before
procedure before
</xsl:param>
<!-- Format Variable Lists as Blocks (prevents horizontal overflow). -->
<xsl:param name="variablelist.as.blocks">1</xsl:param>
<!-- The horrible list spacing problems, this is much better. -->
<xsl:attribute-set name="list.block.spacing">
<xsl:attribute name="space-before.optimum">0.8em</xsl:attribute>
<xsl:attribute name="space-before.minimum">0.8em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.8em</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
</xsl:attribute-set>
<!-- Newer DocBook XSL apparently thinks that some sections are by
default "draft" status, and this idiotic thing is by default
also set to "maybe", so it spits out a lot of errors with the
latest FOP as the XSL/FO styles have references to some draft
watermarks, which you actually don't want in the first place.
Turn this crap off. If you have to work with the "status"
attribute, don't.
-->
<xsl:param name="draft.mode" select="'no'"/>
<!-- Korean related Settings -->
<xsl:param name="hyphenate">false</xsl:param>
<xsl:param name="body.font.family">Gulim</xsl:param>
<!-- xsl:param name="monospace.font.family">가을체</xsl:param-->
<xsl:param name="title.font.family">가을체</xsl:param>
<xsl:param name="saxon.character.representation" select="native"/>
<xsl:param name="callout.unicode" select="1"/>
<xsl:param name="callout.unicode.start.character" select="10102"/>
<xsl:param name="l10n.gentext.default.language" select="ko"/>
</xsl:stylesheet>

View File

@ -0,0 +1,97 @@
A {
color: #003399;
}
A:active {
color: #003399;
}
A:visited {
color: #888888;
}
P, OL, UL, LI, DL, DT, DD, BLOCKQUOTE {
color: #000000;
}
TD, TH, SPAN {
color: #000000;
}
BLOCKQUOTE {
margin-right: 0px;
}
H1, H2, H3, H4, H5, H6 {
color: #000000;
font-weight:500;
margin-top:10px;
padding-top:15px;
}
TABLE {
border-collapse: collapse;
border-spacing:0;
border: 1px thin black;
empty-cells: hide;
}
TD {
padding: 4pt;
}
H1 { font-size: 150%; }
H2 { font-size: 140%; }
H3 { font-size: 110%; font-weight: bold; }
H4 { font-size: 110%; font-weight: bold;}
H5 { font-size: 100%; font-style: italic; }
H6 { font-size: 100%; font-style: italic; }
TT {
font-size: 90%;
font-family: "Courier New", Courier, monospace;
color: #000000;
}
PRE {
font-size: 100%;
padding: 5px;
border-style: solid;
border-width: 1px;
border-color: #CCCCCC;
background-color: #F4F4F4;
}
UL, OL, LI {
list-style: disc;
}
HR {
width: 100%;
height: 1px;
background-color: #CCCCCC;
border-width: 0px;
padding: 0px;
color: #CCCCCC;
}
.variablelist {
padding-top: 10;
padding-bottom:10;
margin:0;
}
.itemizedlist, UL {
padding-top: 0;
padding-bottom:0;
margin:0;
}
.term {
font-weight:bold;
}

View File

@ -0,0 +1,84 @@
<?xml version="1.0"?>
<!--
This is the XSL HTML configuration file for the Hibernate
Reference Documentation.
It took me days to figure out this stuff and fix most of
the obvious bugs in the DocBook XSL distribution. Some of
the workarounds might not be appropriate with a newer version
of DocBook XSL. This file is released as part of Hibernate,
hence LGPL licensed.
christian@hibernate.org
-->
<!DOCTYPE xsl:stylesheet [
<!ENTITY db_xsl_path "../../support/docbook-xsl/">
]>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns="http://www.w3.org/TR/xhtml1/transitional"
exclude-result-prefixes="#default">
<xsl:import href="&db_xsl_path;/html/docbook.xsl"/>
<!--###################################################
HTML Settings
################################################### -->
<xsl:param name="html.stylesheet">../shared/css/html.css</xsl:param>
<!-- These extensions are required for table printing and other stuff -->
<xsl:param name="use.extensions">1</xsl:param>
<xsl:param name="tablecolumns.extension">0</xsl:param>
<xsl:param name="callout.extensions">1</xsl:param>
<xsl:param name="graphicsize.extension">0</xsl:param>
<!--###################################################
Table Of Contents
################################################### -->
<!-- Generate the TOCs for named components only -->
<xsl:param name="generate.toc">
book toc
</xsl:param>
<!-- Show only Sections up to level 3 in the TOCs -->
<xsl:param name="toc.section.depth">3</xsl:param>
<!--###################################################
Labels
################################################### -->
<!-- Label Chapters and Sections (numbering) -->
<xsl:param name="chapter.autolabel">1</xsl:param>
<xsl:param name="section.autolabel" select="1"/>
<xsl:param name="section.label.includes.component.label" select="1"/>
<!--###################################################
Callouts
################################################### -->
<!-- Don't use graphics, use a simple number style -->
<xsl:param name="callout.graphics">0</xsl:param>
<!-- Place callout marks at this column in annotated areas -->
<xsl:param name="callout.defaultcolumn">90</xsl:param>
<!--###################################################
Misc
################################################### -->
<!-- Placement of titles -->
<xsl:param name="formal.title.placement">
figure after
example before
equation before
table before
procedure before
</xsl:param>
</xsl:stylesheet>

View File

@ -0,0 +1,86 @@
<?xml version="1.0"?>
<!--
This is the XSL HTML configuration file for the Hibernate
Reference Documentation.
It took me days to figure out this stuff and fix most of
the obvious bugs in the DocBook XSL distribution. Some of
the workarounds might not be appropriate with a newer version
of DocBook XSL. This file is released as part of Hibernate,
hence LGPL licensed.
christian@hibernate.org
-->
<!DOCTYPE xsl:stylesheet [
<!ENTITY db_xsl_path "../../support/docbook-xsl/">
]>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns="http://www.w3.org/TR/xhtml1/transitional"
exclude-result-prefixes="#default">
<xsl:import href="&db_xsl_path;/html/chunk.xsl"/>
<!--###################################################
HTML Settings
################################################### -->
<xsl:param name="chunk.section.depth">'5'</xsl:param>
<xsl:param name="use.id.as.filename">'1'</xsl:param>
<xsl:param name="html.stylesheet">../shared/css/html.css</xsl:param>
<!-- These extensions are required for table printing and other stuff -->
<xsl:param name="use.extensions">1</xsl:param>
<xsl:param name="tablecolumns.extension">0</xsl:param>
<xsl:param name="callout.extensions">1</xsl:param>
<xsl:param name="graphicsize.extension">0</xsl:param>
<!--###################################################
Table Of Contents
################################################### -->
<!-- Generate the TOCs for named components only -->
<xsl:param name="generate.toc">
book toc
</xsl:param>
<!-- Show only Sections up to level 3 in the TOCs -->
<xsl:param name="toc.section.depth">3</xsl:param>
<!--###################################################
Labels
################################################### -->
<!-- Label Chapters and Sections (numbering) -->
<xsl:param name="chapter.autolabel">1</xsl:param>
<xsl:param name="section.autolabel" select="1"/>
<xsl:param name="section.label.includes.component.label" select="1"/>
<!--###################################################
Callouts
################################################### -->
<!-- Don't use graphics, use a simple number style -->
<xsl:param name="callout.graphics">0</xsl:param>
<!-- Place callout marks at this column in annotated areas -->
<xsl:param name="callout.defaultcolumn">90</xsl:param>
<!--###################################################
Misc
################################################### -->
<!-- Placement of titles -->
<xsl:param name="formal.title.placement">
figure after
example before
equation before
table before
procedure before
</xsl:param>
</xsl:stylesheet>