Added Spanish translation of reference docs HHH-491
git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@7121 1b8cb986-b30d-0410-93ca-fae66ebed9b2
@ -27,6 +27,7 @@
|
|||||||
<!-- TRANSLATOR: Duplicate this line for your language -->
|
<!-- TRANSLATOR: Duplicate this line for your language -->
|
||||||
<antcall target="lang.all"><param name="lang" value="en"/></antcall>
|
<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="zh-cn"/></antcall>
|
||||||
|
<antcall target="lang.all"><param name="lang" value="es"/></antcall>
|
||||||
|
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
@ -34,8 +35,8 @@
|
|||||||
description="Generates a diff report for all translated versions.">
|
description="Generates a diff report for all translated versions.">
|
||||||
|
|
||||||
<!-- TRANSLATOR: Duplicate this line for your language -->
|
<!-- TRANSLATOR: Duplicate this line for your language -->
|
||||||
<antcall target="lang.revdiff"><param name="lang" value="de"/></antcall>
|
|
||||||
<antcall target="lang.revdiff"><param name="lang" value="zh-cn"/></antcall>
|
<antcall target="lang.revdiff"><param name="lang" value="zh-cn"/></antcall>
|
||||||
|
<antcall target="lang.revdiff"><param name="lang" value="es"/></antcall>
|
||||||
|
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
BIN
reference/es/images/AuthorWork.gif
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
reference/es/images/AuthorWork.zargo
Normal file
BIN
reference/es/images/CustomerOrderProduct.gif
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
reference/es/images/CustomerOrderProduct.zargo
Normal file
BIN
reference/es/images/EmployerEmployee.gif
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
reference/es/images/EmployerEmployee.zargo
Normal file
BIN
reference/es/images/full_cream.gif
Normal file
After Width: | Height: | Size: 9.1 KiB |
429
reference/es/images/full_cream.svg
Normal 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 |
BIN
reference/es/images/hibernate_logo_a.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
reference/es/images/lite.gif
Normal file
After Width: | Height: | Size: 6.6 KiB |
334
reference/es/images/lite.svg
Normal 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.2 KiB |
BIN
reference/es/images/overview.gif
Normal file
After Width: | Height: | Size: 8.4 KiB |
250
reference/es/images/overview.svg
Normal 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.2 KiB |
203
reference/es/master.xml
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
<?xml version='1.0' encoding="iso-8859-1"?>
|
||||||
|
<!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="es">
|
||||||
|
|
||||||
|
<bookinfo>
|
||||||
|
<title>HIBERNATE - Persistencia Relacional para Java Idiomático</title>
|
||||||
|
<subtitle>Documentación de Referencia de Hibernate</subtitle>
|
||||||
|
<releaseinfo>3.0.5</releaseinfo>
|
||||||
|
</bookinfo>
|
||||||
|
|
||||||
|
<toc/>
|
||||||
|
|
||||||
|
<preface id="preface" revision="2">
|
||||||
|
<title>Prefacio</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Advertencia! Esta es una versión traducida del inglés de
|
||||||
|
la documentacién de referencia de Hibernate. La versión
|
||||||
|
traducida puede no estar actualizada! Sin embargo, las diferencias
|
||||||
|
deberían ser sólo menores. Consulta la documentación
|
||||||
|
de referencia en inglés si estás perdiendo información
|
||||||
|
o encuentras algún error de traducción. Si quieres colaborar con
|
||||||
|
una traducción en particular, contáctanos en la lista de correo
|
||||||
|
de desarrolladores de Hibernate.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Traductor(es): Bernardo Antonio Buffa Colomé <kreimer@bbs.frc.utn.edu.ar>
|
||||||
|
<!--,
|
||||||
|
Antonio López Gota <antoniogota@gmail.com> -->
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Trabajar con software orientado a objetos y una base de datos relacional puede ser
|
||||||
|
incómodo y consumir tiempo en los entornos de empresa de hoy. Hibernate es una
|
||||||
|
herramienta de mapeo objeto/relacional para entornos Java. El término mapeo
|
||||||
|
objeto/relacional (MOR) hace referencia a la técnica de mapear una
|
||||||
|
representación de datos desde un modelo de objetos a un modelo de datos relacional
|
||||||
|
con un esquema basado en SQL.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate no sólo se encarga de mapear de clases Java a tablas de base de datos
|
||||||
|
(y de tipos de datos de Java a tipos de datos SQL), sino que también provee
|
||||||
|
facilidades de consulta y recuperación de datos y puede reducir significativamente
|
||||||
|
el tiempo de desarrollo que de otra forma se gasta en el manejo de los datos en SQL y JDBC.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La meta de Hibernate es relevar al desarrollador del 95 por ciento de las tareas comunes
|
||||||
|
relacionadas a la programación de la persistencia de los datos.
|
||||||
|
Hibernate puede no ser la mejor solución para aplicaciones que usan solamente
|
||||||
|
procedimientos almacenados para implementar la lógica de negocio en la base de
|
||||||
|
datos, es mas útil con modelos de dominio orientados a objetos y lógica de
|
||||||
|
negocio en middle-tier basada en Java. Sin embargo, Hibernate ciertamente puede ayudarte
|
||||||
|
a quitar o encapsular código SQL específico de vendedor y ayudará
|
||||||
|
con la tarea común de traducción de resultados desde una representación
|
||||||
|
tabular a un grafo de objetos.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Si eres nuevo en Hibernate y lo del Mapeo Objeto/Relacional o incluso en Java,
|
||||||
|
sigue por favor estos pasos:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<orderedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Lee <xref linkend="quickstart"/> para un tutorial de 30 minutos, usando Tomcat.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Lee <xref linkend="architecture"/> para entender los entornos en los que
|
||||||
|
puede ser usado Hibernate.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Dale una mirada al directorio <literal>eg/</literal> en la distribución
|
||||||
|
de Hibernate, contiene una aplicación independiente simple.
|
||||||
|
Copia tu driver JDBC al directorio <literal>lib/</literal> y edita
|
||||||
|
<literal>etc/hibernate.properties</literal>, especificando los valores
|
||||||
|
correctos para tu base de datos. Desde línea de comandos en el
|
||||||
|
directorio de la distribución, tipea <literal>ant eg</literal>
|
||||||
|
(usando Ant), o bajo Windows, tipea <literal>build eg</literal>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Usa esta documentación de referencia como tu fuente de información
|
||||||
|
primaria. Ten en consideración leer <emphasis>Hibernate in Action</emphasis>
|
||||||
|
(http://www.manning.com/bauer) si necesitas mas ayuda con el diseño
|
||||||
|
de aplicaciones o si prefieres un tutorial paso a paso.
|
||||||
|
Visita también http://caveatemptor.hibernate.org y descarga la aplicación
|
||||||
|
de ejemplo para Hibernate in Action.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Los FAQs son respondidos en el sitio web de Hibernate.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
En el sitio web de Hibernate hay enlaces a demos de terceros, ejemplos
|
||||||
|
y tutoriales.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
El Area de Comunidad en el sitio web de Hibernate es una buena fuente
|
||||||
|
de patrones de diseño y varias soluciones de integración
|
||||||
|
(Tomcat, JBoss, Struts, EJB, etc.).
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</orderedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Si tienes preguntas, usa el foro de usuarios enlazado en el sitio web de Hibernate.
|
||||||
|
También proveemos un sistema de seguimiento JIRA para reportes de defectos y
|
||||||
|
peticiones de nuevas características.
|
||||||
|
Si estas interesado en el desarrollo de Hibernate, únete a la lista de correo
|
||||||
|
de desarrolladores. Si estas interesado en traducir esta documentación a tu
|
||||||
|
lenguaje, contáctanos en la lista de correo de desarrolladores.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
A través de JBoss Inc. (see http://www.hibernate.org/SupportTraining/) hay
|
||||||
|
disponibilidad de soporte comercial de desarrollo, soporte de producción y
|
||||||
|
entrenamiento en Hibernate.
|
||||||
|
Hibernate es un proyecto de la suite de productos de código abierto
|
||||||
|
JBoss Professional.
|
||||||
|
</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>
|
||||||
|
|
279
reference/es/modules/architecture.xml
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
<chapter id="architecture">
|
||||||
|
|
||||||
|
<title>Arquitectura</title>
|
||||||
|
|
||||||
|
<sect1 id="architecture-overview" revision="1">
|
||||||
|
<title>Visión General</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una visión a (muy) alto nivel de la arquitectura de 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>
|
||||||
|
Este diagrama muestra a Hibernate usando la base de datos y los datos de
|
||||||
|
configuración para proveer servicios de persistencia (y objetos
|
||||||
|
persistentes) a la aplicación.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Nos gustaría mostrar una vista más detallada de la arquitectura de tiempo
|
||||||
|
de ejecución. Desafortunadamente, Hibernate es flexible y soporta diferentes
|
||||||
|
enfoques. Mostraremos los dos extremos. En la arquitectura "sencilla", es la
|
||||||
|
aplicación la que provee su propias conexiones JDBC y gestiona sus propias
|
||||||
|
transacciones. Este enfoque usa un mínimo subconjunto de la API de Hibernate:
|
||||||
|
</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>
|
||||||
|
La arquitectura "full cream" abstrae a la aplicación de las APIs
|
||||||
|
de JDBC/JTA y deja que Hibernate se encargue de los detalles.
|
||||||
|
</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>
|
||||||
|
He aquí algunas definiciones de los objetos en los diagramas:
|
||||||
|
<variablelist spacing="compact">
|
||||||
|
<varlistentry>
|
||||||
|
<term>SessionFactory (<literal>org.hibernate.SessionFactory</literal>)</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Caché threadsafe (inmutable) de mapeos compilados para
|
||||||
|
una sola base de datos. Es una fábrica de <literal>Session</literal>
|
||||||
|
y un cliente de <literal>ConnectionProvider</literal>. Opcionalmente,
|
||||||
|
puede mantener una caché (de segundo nivel) de datos reusables
|
||||||
|
entre transacciones, a un nivel de proceso o de cluster.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Session (<literal>org.hibernate.Session</literal>)</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Objeto mono-hebra, de corta vida que representa una conversación
|
||||||
|
entre la aplicación y el almacenamiento persistente. Envuelve una
|
||||||
|
conexión JDBC. Es una fábrica de <literal>Transaction</literal>.
|
||||||
|
Mantiene una caché requerida (de primer nivel) de objetos persistentes,
|
||||||
|
usada mientras se navega el grafo de objetos o se recuperen objetos por
|
||||||
|
identificador.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Objetos y colecciones persistentes</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Objetos de corta vida, mono-hebra conteniendo estado persistente y
|
||||||
|
funciónalidad de negocio. Estos pueden ser JavaBeans/POJOs
|
||||||
|
(Plain Old Java Objects, o sea, cualquier objeto Java), la única
|
||||||
|
cosa especial en ellos es que estan asociados actualmente con una
|
||||||
|
(y sólo una) <literal>Session</literal>. Tan pronto como la
|
||||||
|
<literal>Session</literal> sea cerrada, serán separados y
|
||||||
|
estarán libres para ser usados en cualquier capa de aplicación.
|
||||||
|
(por ejemplo, directamente como objetos de transferencia de datos hacia
|
||||||
|
y desde la capa de presentación).
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Objetos y colecciones transitorios y separados</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Instancias de clases persistentes que no estan acutualmente asociadas
|
||||||
|
con una <literal>Session</literal>. Pueden haber sido instanciadas por
|
||||||
|
la aplicación y (aún) no haber sido hechas persistentes,
|
||||||
|
o pueden haber sido instanciadas por una <literal>Session</literal> cerrada.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Transaction (<literal>org.hibernate.Transaction</literal>)</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
(Opcional) Un objeto de corta vida, mono-hebra, usado por la aplicación
|
||||||
|
para especificar unidades atómicas de trabajo. Abstrae a la aplicación
|
||||||
|
de las subyacentes transacciones JDBC, JTA o CORBA. En algunos casos, una
|
||||||
|
<literal>Session</literal> puede extenderse sobre varias <literal>Transaction</literal>s.
|
||||||
|
Sin embargo, la demarcación de la transacción, ya sea usando la API
|
||||||
|
subyacente o <literal>Transaction</literal>, nunca es opcional!
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>ConnectionProvider (<literal>org.hibernate.connection.ConnectionProvider</literal>)</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
(Opcional) Una fábrica (y pool) de conexiones JDBC. Abstrae a la aplicación
|
||||||
|
del <literal>Datasource</literal> o <literal>DriverManager</literal> subyacente.
|
||||||
|
No se expone a la aplicación, pero puede ser extendido/implementado por
|
||||||
|
el desarrollador.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>TransactionFactory (<literal>org.hibernate.TransactionFactory</literal>)</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
(Opcional) Una fábrica de instancias de <literal>Transaction</literal>.
|
||||||
|
No se expone a la aplicación, pero puede ser extendido/implementado por
|
||||||
|
el desarrollador.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><emphasis>Interfaces de Extensión</emphasis></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Hibernate ofrece muchas interfaces de extensión opcional que puedes
|
||||||
|
implementar para modificar a medida el comportamiento de tu capa de persistencia.
|
||||||
|
Para más detalles, mira la documentación de la API.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Dada una arquitectura "sencilla", la aplicación pasa por alto las APIs
|
||||||
|
de <literal>Transaction</literal>/<literal>TransactionFactory</literal> y/o
|
||||||
|
<literal>ConnectionProvider</literal>, para hablar directamente a JTA o JDBC.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="architecture-states" revision="1">
|
||||||
|
<title>Estados de instancia</title>
|
||||||
|
<para>
|
||||||
|
Una instancia de una clase persistente puede estar en uno de tres estados
|
||||||
|
diferentes, definidos respecto de su <emphasis>contexto de persistencia</emphasis>.
|
||||||
|
El objeto <literal>Session</literal> de Hibernate es el contexto de persistencia:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<variablelist spacing="compact">
|
||||||
|
<varlistentry>
|
||||||
|
<term>transitorio</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
La instancia no está y nunca estuvo asociada con
|
||||||
|
un contexto de persistencia. No tiene identidad persistente
|
||||||
|
(valor de clave primaria).
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>persistente</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
La instancia está actualmente asociada con un
|
||||||
|
contexto de persistencia. Tiene una identidad persistente
|
||||||
|
(valor de clave primaria) y, quizás, una fila
|
||||||
|
correspondiente en la base de datos. Para un contexto de
|
||||||
|
persistencia en particular, Hibernate <emphasis>garantiza</emphasis>
|
||||||
|
que la identidad persistente es equivalente a la identidad
|
||||||
|
Java (localización en memoria del objeto).
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>separado</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
La instancia estuvo una vez asociada con un contexto
|
||||||
|
de persistencia, pero ese contexto fue cerrado, o la
|
||||||
|
instancia fue serializada a otro proceso. Tiene una
|
||||||
|
identidad persistente y, quizás, una fila correspondiente
|
||||||
|
en la base de datos. Para las instancias separadas,
|
||||||
|
Hibernate no establece ninguna garantía sobre
|
||||||
|
la relación entre identidad persistente e identidad Java.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="architecture-jmx" revision="1">
|
||||||
|
<title>Integración JMX</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
JMX es el estándar J2EE para la gestión de componentes Java. Hibernate
|
||||||
|
puede ser gestionado por medio de un servicio estándar JMX.
|
||||||
|
Proveemos una implementación de MBean en la distribución,
|
||||||
|
<literal>org.hibernate.jmx.HibernateService</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Para ejemplo de cómo desplegar Hibernate como un servicio JMX en un Servidor
|
||||||
|
de Aplicaciones JBoss, por favor, mira la Guía del Usuario de JBoss.
|
||||||
|
En JBoss AS, tienes además estos beneficios si despliegas usando JMX:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<emphasis>Gestión de Sesión:</emphasis> El ciclo de vida de la <literal>Session</literal>
|
||||||
|
de Hibernate puede estar automáticamente ligado al ámbito de una transacción
|
||||||
|
JTA. Esto significa que ya no tienes que abrir ni cerrar la <literal>Session</literal> manualmente,
|
||||||
|
esto pasa a ser trabajo de un interceptor EJB de JBoss. Además tampoco tienes
|
||||||
|
que preocuparte más de la demarcación de la transacción (a menos que
|
||||||
|
que quieras escribir una capa de persitencia portable, por supuesto, usa la API de
|
||||||
|
<literal>Transaction</literal> de Hibernate para esto). Para acceder a una
|
||||||
|
<literal>Session</literal> llama al <literal>HibernateContext</literal>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<emphasis>Despliegue de HAR:</emphasis> Usualmente despliegas el servicio JMX de Hibernate
|
||||||
|
usando un descriptor de despliegue de servicio de JBoss (en un fichero EAR y/o SAR), que soporta
|
||||||
|
todas las opciones de configuración usuales de una <literal>SessionFactory</literal> de
|
||||||
|
Hibernate. Sin embargo, todavía tienes que nombrar todos tus ficheros de mapeo en el
|
||||||
|
descriptor de despliegue. Si decides usar el depliegue de HAR opcional, JBoss detectará
|
||||||
|
automáticamente todos los ficheros de mapeo en tu fichero HAR.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Para más información sobre estas opciones, consulta la
|
||||||
|
Guía de Usuario del JBoss AS.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Otra funcionalidad disponible como un servicio JMX son las estadísticas en
|
||||||
|
tiempo de ejecución de Hibernate. Mira <xref linkend="configuration-optional-statistics"/>.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="architecture-jca" revision="1">
|
||||||
|
<title>Soporte JCA:</title>
|
||||||
|
<para>
|
||||||
|
Hiberate puede además ser configurado como un conector JCA. Por favor mira el
|
||||||
|
sitio web para más detalles. Por favor ten en cuenta que el soporte de JCA
|
||||||
|
de Hibernate está aún considerado experimental.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
</chapter>
|
||||||
|
|
527
reference/es/modules/association_mapping.xml
Normal file
@ -0,0 +1,527 @@
|
|||||||
|
<chapter id="associations">
|
||||||
|
|
||||||
|
<title>Mapeos de Asociación</title>
|
||||||
|
|
||||||
|
<sect1 id="assoc-intro" revision="1">
|
||||||
|
<title>Introducción</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Los mapeos de asociación son frecuentemente las cosas mas difíciles
|
||||||
|
de hacer correctamente. En esta sección iremos a través de los casos
|
||||||
|
canónicos uno a uno, comenzando con los mapeos unidireccionales, y considerando
|
||||||
|
luego los casos bidireccionales. Usaremos <literal>Person</literal> y <literal>Address</literal>
|
||||||
|
en todos los ejemplos.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Clasificaremos las asociaciones por cuanto mapeen o no a una tabla
|
||||||
|
de unión interviniente, y por su multiplicidad.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Las claves foráneas que aceptan valores nulos (en adelante, nullables)
|
||||||
|
no son consideradas una buena práctica en el modelado tradicional de datos,
|
||||||
|
así que todos nuestros ejemplos usan claves foráneas no nullables.
|
||||||
|
Esto no es un requerimiento de Hibernate, y todos los mapeos funcionarán
|
||||||
|
si quitas las restricciones de nulabilidad.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="assoc-unidirectional" revision="1">
|
||||||
|
<title>Asociaciones Unidireccionales</title>
|
||||||
|
|
||||||
|
<sect2 id="assoc-unidirectional-m21">
|
||||||
|
<title>muchos a uno</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <emphasis>asociación unidireccional muchos-a-uno</emphasis> es el tipo
|
||||||
|
más común de asociaciones unidireccionales.
|
||||||
|
</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>uno a uno</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <emphasis>asociación unidireccional uno-a-uno en una clave primaria</emphasis>
|
||||||
|
es casi idéntica. La única diferencia es la restricción de unicidad
|
||||||
|
de la columna.
|
||||||
|
</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>
|
||||||
|
Usualmente, una <emphasis>asociación unidireccional uno-a-uno en una
|
||||||
|
clave primaria</emphasis> usa un generador de id especial. (Observa que hemos
|
||||||
|
invertido el sentido de la asociación en este ejemplo).
|
||||||
|
</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>uno a muchos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <emphasis>asociación unidireccional uno-a-muchos en una clave foránea</emphasis>
|
||||||
|
es un caso muy inusual, y realmente no está recomendada.
|
||||||
|
</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>
|
||||||
|
Creemos que es mejor usar una tabla de unión para este tipo de asociación.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="assoc-unidirectional-join" revision="1">
|
||||||
|
<title>Asociaciones unidireccionales con tablas de unión</title>
|
||||||
|
|
||||||
|
<sect2 id="assoc-unidirectional-join-12m">
|
||||||
|
<title>uno a muchos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <emphasis>asociación unidireccional uno-a-muchos en una tabla de unión</emphasis>
|
||||||
|
es más preferible. Observa que especificando <literal>unique="true"</literal>, hemos
|
||||||
|
cambiado la multiplicidad de muchos-a-muchos a uno-a-muchos.
|
||||||
|
</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>muchos a uno</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <emphasis>asociación unidireccional muchos-a-uno en una tabla de unión</emphasis>
|
||||||
|
es bastante común cuando la asociación es opcional.
|
||||||
|
</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>uno a uno</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <emphasis>asociación unidireccional uno-a-uno en una tabla de unión</emphasis>
|
||||||
|
es inusual en extremo, pero posible.
|
||||||
|
</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>muchos a muchos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Finalmente, tenemos una <emphasis>asociación unidireccional muchos-a-muchos</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>Asociaciones Bidireccionales</title>
|
||||||
|
|
||||||
|
<sect2 id="assoc-bidirectional-m21">
|
||||||
|
<title>uno a muchos / muchos a uno</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <emphasis>asociación bidireccional muchos-a-uno</emphasis> es
|
||||||
|
el tipo más común de asociación. (Esta es la relación
|
||||||
|
estándar padre/hijo.)
|
||||||
|
</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>uno a uno</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <emphasis>asociación bidireccional uno-a-uno en una clave foránea</emphasis>
|
||||||
|
es bastante común.
|
||||||
|
</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>
|
||||||
|
Una <emphasis>asociación bidireccional uno-a-uno en una clave primaria</emphasis>
|
||||||
|
usa el generador de id especial.
|
||||||
|
</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>Asociaciones bidireccionales con tablas de unión</title>
|
||||||
|
|
||||||
|
<sect2 id="assoc-bidirectional-join-12m">
|
||||||
|
<title>uno a muchos / muchos a uno</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <emphasis>asociación bidireccional uno-a-muchos en una tabla de unión</emphasis>.
|
||||||
|
Observa que el <literal>inverse="true"</literal> puede ir a cualquier lado de la asociación,
|
||||||
|
en la colección, o en la unión.
|
||||||
|
</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>uno a uno</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <emphasis>asociación bidireccional uno-a-uno en una tabla de unión</emphasis>
|
||||||
|
es inusual en extremo, pero posible.
|
||||||
|
</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>muchos a muchos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Finalmente, tenemos una <emphasis>asociación bidireccional muchos-a-muchos</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>
|
||||||
|
|
3222
reference/es/modules/basic_mapping.xml
Normal file
192
reference/es/modules/batch.xml
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
<chapter id="batch">
|
||||||
|
<title>Procesamiento por lotes</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Un enfoque ingenuo para insertar 100.000 filas en la base de datos usando Hibernate podría verse así:
|
||||||
|
</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>
|
||||||
|
Esto podría caer sobre una <literal>OutOfMemoryException</literal> en algún sitio
|
||||||
|
cerca de la fila 50.000. Esto es porque Hibernate tiene en caché todas las instancias
|
||||||
|
de <literal>Customer</literal> recién instanciadas en el caché de nivel de sesión.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
En este capítulo te mostraremos cómo evitar este problema. Primero, sin embargo,
|
||||||
|
si estás haciendo procesamiento por lotes (batch processing), es absolutamente crítico
|
||||||
|
que habilites el uso de loteo JDBC, si pretendes lograr un rendimiento razonable.
|
||||||
|
Establece el tamaño de lote JDBC a un número razonable (digamos 10-50):
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Podrías además querer hacer este tipo de trabajo en un proceso donde la interacción con el caché de
|
||||||
|
segundo nivel esté completamente deshabilitado:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
|
||||||
|
|
||||||
|
<sect1 id="batch-inserts">
|
||||||
|
<title>Inserciones en lote</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Al hacer persistentes objetos nuevos, debes limpiar con <literal>flush()</literal> y
|
||||||
|
llamar a <literal>clear()</literal> en la sesión regularmente, para controlar el tamaño
|
||||||
|
del caché de primer nivel.
|
||||||
|
</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>Actualizaciones en lote</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Para recuperar y actualizar datos se aplican las mismas ideas. Adicionalmente, necesitas usar
|
||||||
|
<literal>scroll()</literal> para sacar ventaja de los cursores del lado del servidor en consultas
|
||||||
|
que devuelvan muchas filas de datos.
|
||||||
|
</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 en masa</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Como ya se ha discutido, el mapeo objeto/relacional automático y transparente se refiere
|
||||||
|
al manejo de estado de objetos. Esto implica que el estado del objeto está disponible
|
||||||
|
en memoria, por lo tanto actualizar o borrar (usando <literal>UPDATE</literal> y
|
||||||
|
<literal>DELETE</literal> de SQL) datos directamente en la base de datos no afectará el
|
||||||
|
estado en memoria. Sin embargo, Hibernate provee métodos para la ejecución de sentencias
|
||||||
|
del estilo de <literal>UPDATE</literal> y <literal>DELETE</literal> de SQL que se realizan
|
||||||
|
a través del Lenguaje de Consulta de Hibernate (Hibernate Query Language o
|
||||||
|
<xref linkend="queryhql">HQL</xref>).
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La pseudo-sintáxis para sentencias <literal>UPDATE</literal> y <literal>DELETE</literal> es:
|
||||||
|
<literal>( UPDATE | DELETE ) FROM? ClassName (WHERE WHERE_CONDITIONS)?</literal>. Algunos puntos
|
||||||
|
a tener en cuenta:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist spacing="compact">
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
En la cláusula-from, la palabra clave FROM es opcional
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Puede haber sólo una clase mencionada en la cláusula-from, y <emphasis>no puede</emphasis>
|
||||||
|
tener un alias.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
No puede especificarse ningún join (bien implícito o explícito) en una consulta masiva de HQL.
|
||||||
|
Pueden usarse subconsultas en la cláusula-where.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
La cláusula-where es también opcional.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Como un ejemplo, para ejecutar un <literal>UPDATE</literal> HQL, usa el
|
||||||
|
método <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>
|
||||||
|
Para ejecutar un <literal>DELETE</literal> HQL, usa el mismo método <literal>Query.executeUpdate()</literal>
|
||||||
|
(el método está nombrado para aquellos familiarizados con
|
||||||
|
<literal>PreparedStatement.executeUpdate()</literal> de JDBC):
|
||||||
|
</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>
|
||||||
|
El valor <literal>int</literal> devuelto por el método <literal>Query.executeUpdate()</literal>
|
||||||
|
indica el número de entidades afectadas por la operación. Considera que esto puede o no
|
||||||
|
correlacionarse al número de filas afectadas en la base de datos. Una operación masiva HQL podría
|
||||||
|
resultar en que se ejecuten múltiples sentencias de SQL reales, para joined-subclass, por ejemplo.
|
||||||
|
El número devuelto indica el número de entidades reales afectadas por la sentencia. Volviendo al
|
||||||
|
ejemplo de joined-subclass, un borrado contra una de las subclases puede resultar realmente en
|
||||||
|
borrados contra no sólo la tabla a la que está mapeada esa subclase, sino también la tabla "raíz"
|
||||||
|
y potencialmente tablas de joined-subclass más debajo en la jerarquía de herencia.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Ten en cuenta que existen actualmente unas pocas limitaciones con las operaciones HQL masivas,
|
||||||
|
que serán atendidas en lanzamientos futuros; consulta la hoja de ruta de JIRA para más detalles.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
</chapter>
|
229
reference/es/modules/best_practices.xml
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
<chapter id="best-practices" revision="3">
|
||||||
|
<title>Mejores Prácticas</title>
|
||||||
|
|
||||||
|
<variablelist spacing="compact">
|
||||||
|
<varlistentry>
|
||||||
|
<term>Escribe clase finamente granularizadas y mapealas usando <literal><component></literal>.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Usa una clase <literal>Dirección</literal> para encapsular <literal>calle</literal>,
|
||||||
|
<literal>distrito</literal>, <literal>estado</literal>, <literal>código postal</literal>.
|
||||||
|
Esto alienta la reutilización de código y simplifica el refactoring.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Declara las propiedades identificadoras en clases persistentes.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Hibernate hace opcionales las propiedades identificadoras. Existen todo tipo de razones
|
||||||
|
por las que debes usarlas. Recomendamos que los identificadores sean 'sintéticos'
|
||||||
|
(generados, sin ningún significado de negocio).
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Identifica las claves naturales.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Identifica las claves naturales de todas las entidades, y mapealas usando
|
||||||
|
<literal><natural-id></literal>. Implementa <literal>equals()</literal> y
|
||||||
|
<literal>hashCode()</literal> para comparar las propiedades que componen la clave natural.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Coloca cada mapeo de clase en su propio fichero.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
No uses un solo documento monolítico de mapeo. Mapea <literal>com.eg.Foo</literal> en
|
||||||
|
el fichero <literal>com/eg/Foo.hbm.xml</literal>. Esto tiene sentido particularmente en un
|
||||||
|
ambiente de equipo.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Carga los mapeos como recursos.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Despliega los mapeos junto a las clases que mapean.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Considera externalizar las cadenas de consulta.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Esta es una buena práctica si tus consultas llaman a funciones SQL que no son del
|
||||||
|
estándar ANSI. Externalizar las cadenas de consulta a ficheros de mapeo hará la
|
||||||
|
aplicación más portable.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Usa variables de ligado.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Igual que en JDBC, siempre remplaza valores no constantes con "?". ¡Nunca uses manipulación
|
||||||
|
de cadenas para ligar un valor no constante en una consulta! Incluso mejor, considera usar
|
||||||
|
parámetros con nombre en las consultas.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>No manejes tus propias conexiones JDBC.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Hibernate deja a la aplicación administre las conexiones JDBC. Este enfoque debe considerarse
|
||||||
|
como último recurso. Si no puedes usar los provedores de conexión prefabricados, considera
|
||||||
|
prover tu propia implementación de <literal>org.hibernate.connection.ConnectionProvider</literal>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Considera usar un tipo personalizado.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Supón que tienes un tipo Java, digamos de alguna biblioteca, que necesita hacerse persistente
|
||||||
|
pero no provee los métodos de acceso necesarios para mapearlo como un componente. Debes considerar
|
||||||
|
implementar <literal>org.hibernate.UserType</literal>. Este enfoque libera al código de aplicación
|
||||||
|
de implementar transformaciones a / desde un tipo Hibernate.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Usa JDBC codificado a mano en cuellos de botella.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
En áreas del sistema de rendimiento crítico, algunos tipos de operaciones podrían beneficiarse
|
||||||
|
del JDBC directo. Pero por favor, espero hasta que <emphasis>sepas</emphasis> que algo es
|
||||||
|
un cuello de botella. Y no asumas que el JDBC directo es necesariamente más rápido. Si necesitas
|
||||||
|
usar JDBC directo, podría ser valioso abrir una <literal>Session</literal> de Hibernate y usar esa
|
||||||
|
conexión JDBC. De esta forma puedes usar aún la misma estrategia de transacción y el mismo
|
||||||
|
proveedor de conexiones subyacente.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Comprende la limpieza (flushing) de <literal>Session</literal>.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
De vez en cuando la sesión sincroniza su estado persistente con la base de datos. El rendimiento
|
||||||
|
se verá afectado si este proceso ocurre demasiado frecuentemente. A veces puedes minimizar
|
||||||
|
limpieza innecesaria deshabilitando la limpieza automática o incluso cambiando el orden de las
|
||||||
|
consultas u otras operaciones en una transacción en particular.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>En una aplicación en tres gradas, considera usar objetos separados.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Al usar una arquitectura de servlet / sesión, puedes pasar objetos persistentes en el bean de
|
||||||
|
sesión hacia y desde la capa de servlet / JSP. Usa una sesión nueva para atender el servicio de cada
|
||||||
|
petición. Usa <literal>Session.merge()</literal> o <literal>Session.saveOrUpdate()</literal> para
|
||||||
|
sincronizar los objetos con la base de datos.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>En una arquitectura en dos gradas, considera usar contexto de persistencia largos.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Las transacciones de base de datos tienen que ser tan cortas como sea posible. Sin embargo,
|
||||||
|
frecuentemente es necesario implementar <emphasis>transacciones de aplicación</emphasis>
|
||||||
|
ejecutándose en largo, una sola unidad de trabajo desde el punto de vista de un usuario.
|
||||||
|
Una transacción de aplicación puede abarcar muchos ciclos petición/respuesta del cliente.
|
||||||
|
Es común usar objetos separados para implementar transacciones de aplicación. Una alternativa,
|
||||||
|
extremadamente apropiada en arquitecturas en dos gradas, es mantener un solo contacto de persistencia
|
||||||
|
abierto (sesión) para todo el ciclo de vida de la transacción de aplicación y simplemente
|
||||||
|
desconectar de la conexión JDBC al final de cada petición, y reconectar al comienzo de la
|
||||||
|
petición subsecuente. Nunca compartas una única sesión a través de más de una transacción
|
||||||
|
de aplicación, o estarás trabajando con datos añejos.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>No trates la excepciones como recuperables.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Esto es más una práctica necesaria que una "mejor" práctica. Cuando ocurra una excepción,
|
||||||
|
deshaz (rollback) la <literal>Transaction</literal> y cierra la <literal>Session</literal>.
|
||||||
|
Si no lo haces, Hibernate no puede garantizar que el estado en memoria representa con exactitud
|
||||||
|
el estado persistente. Como un caso especial de esto, no uses <literal>Session.load()</literal>
|
||||||
|
para determinar si una instancia con el identificador dado existe en la base de datos. En cambio,
|
||||||
|
usa <literal>Session.get()</literal> o una consulta.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Prefiere la recuperación perezosa para las asociaciones.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Usa escasamente la recuperación temprana. Usa proxies y colecciones perezosas para la mayoría
|
||||||
|
de asociaciones a clases probablemente no estén mantenidas en el caché de segundo nivel. Para
|
||||||
|
las asociaciones a clases en caché, donde hay una probabilidad de acceso a caché extremadamente
|
||||||
|
alta, deshabilita explícitamente la recuperación temprana usando <literal>lazy="false"</literal>.
|
||||||
|
Cuando sea apropiada la recuperación por unión (join fetching) para un caso de uso en particular,
|
||||||
|
usa una consulta con un <literal>left join fetch</literal>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
Usa el patrón <emphasis>sesión abierta en vista</emphasis>, o una <emphasis>fase de ensamblado</emphasis>
|
||||||
|
disciplinada para evitar problemas con datos no recuperados.
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Hibernate liberal al desarrollador de escribir <emphasis>Objetos de Transferencia de Datos
|
||||||
|
(Data Transfer Objects)</emphasis> (DTO). En una arquitectura tradicional de EJB, los DTOs tienen
|
||||||
|
un propósito doble: primero, atacan el problema que los beans de entidad no son serializables.
|
||||||
|
Segundo, definen implícitamente una fase de ensamblado cuando se recuperan y se forman (marshalling)
|
||||||
|
todos los datos a usar por la vista en los DTOs antes de devolver el control a la grada de
|
||||||
|
presentación. Hibernate elimina el primer propósito. Sin embargo, aún necesitas una fase
|
||||||
|
de ensamblado (piensa en tus métodos de negocio como si tuviesen un contrato estricto con la grada
|
||||||
|
de presentación sobre qué datos están disponibles en los objetos separados) a menos que estés
|
||||||
|
preparado para tener el contexto de persistencia (la sesión) abierto a través del proceso
|
||||||
|
de renderización de la vista. ¡Esta no es una limitación de Hibernate! Es un requerimiento
|
||||||
|
fundamental de acceso seguro a datos transaccionales.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Considera abstraer tu lógica de negocio de Hibernate</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Oculta el código de acceso a datos (Hibernate) detrás de una interface. Combina los patrones
|
||||||
|
<emphasis>DAO</emphasis> y <emphasis>Sesión de Hebra Local</emphasis>. Incluso puedes tener
|
||||||
|
algunas clases hechas persistentes por JDBC escrito a mano, asociadas a Hibernate por medio
|
||||||
|
de un <literal>UserType</literal>. (Este consejo está pensado para aplicaciones "suficientemente
|
||||||
|
grandes"; ¡no es apropiado para una aplicación con cinco tablas!)
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>No uses mapeos de asociación exóticos.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Son raros los casos de uso de asociaciones reales muchos-a-muchos. La mayor parte del tiempo
|
||||||
|
necesitas información adicional almacenada en una "tabla de enlace". En este caso, es mucho
|
||||||
|
mejor usar dos asociaciones uno-a-muchos a una clase de enlace intermedia. De hecho, pensamos
|
||||||
|
que la mayoría de asociaciones son uno-a-muchos y muchos-a-uno, debes ser cuidadoso al usr
|
||||||
|
cualquier otro estilo de asociación y preguntarte si es realmente necesario.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Prefiere las asociaciones bidireccionales.</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Las asociaciones unidireccionales son más difíciles de consultar. En una aplicación grande,
|
||||||
|
casi todas las asociaciones deben ser navegables en ambas direcciones en consultas.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
|
||||||
|
</chapter>
|
||||||
|
|
1173
reference/es/modules/collection_mapping.xml
Normal file
403
reference/es/modules/component_mapping.xml
Normal file
@ -0,0 +1,403 @@
|
|||||||
|
<chapter id="components">
|
||||||
|
<title>Mapeo de Componentes</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La noción de un <emphasis>componente</emphasis> es reusada en muchos contextos diferentes,
|
||||||
|
para propósitos diferentes, a través de Hibernate.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect1 id="components-dependentobjects">
|
||||||
|
<title>Objetos dependientes</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Un componente es un objeto contenido que es persistido como un tipo de valor, no una
|
||||||
|
referencia de entidad. El término "componente" hace referencia a la noción orientada a
|
||||||
|
objetos de composición (no a componentes a nivel de arquitectura). Por ejemplo, podrías
|
||||||
|
modelar una persona como:
|
||||||
|
</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>
|
||||||
|
Ahora <literal>Name</literal> puede ser persistido como un componente de
|
||||||
|
<literal>Person</literal>. Observa que <literal>Name</literal> define métodos
|
||||||
|
getter y setter para sus propiedades persistentes, pero no necesita declarar
|
||||||
|
ninguna interface ni propiedades identificadoras.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Nuestro mapeo de Hibernate se vería así:
|
||||||
|
</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>
|
||||||
|
La tabla person tendría las columnas <literal>pid</literal>,
|
||||||
|
<literal>birthday</literal>,
|
||||||
|
<literal>initial</literal>,
|
||||||
|
<literal>first</literal> y
|
||||||
|
<literal>last</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Como todos los tipos de valor, los componentes no soportan referencias compartidas.
|
||||||
|
En otras palabras, dos personas pueden tener el mismo nombre, pero los dos objetos
|
||||||
|
persona contendrían dos objetos nombre independientes, sólo "iguales" en valor.
|
||||||
|
La semántica de valor nulo de un componente es <emphasis>ad hoc</emphasis>.
|
||||||
|
Cuando se recargue el objeto contenedor, Hibernate asumirá que si todas las columnas del
|
||||||
|
componente son nulas, el componente entero es nulo. Esto debe estar bien para la mayoría
|
||||||
|
de propósitos.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Las propiedades de un componentes pueden ser de cualquier tipo de Hibernate
|
||||||
|
(colecciones, muchos-a-uno, asociaciones, otros componentes, etc). Los componentes
|
||||||
|
anidados <emphasis>no</emphasis> deben ser considerados un uso exótico. Hibernate está
|
||||||
|
concebido para soportar un modelo de objetos granularizado en fino.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El elemento <literal><component></literal> permite un subelemento
|
||||||
|
<literal><parent></literal> que mapee una propiedad de la clase del componente
|
||||||
|
como una referencia de regreso a la entidad contenedora.
|
||||||
|
</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>Colecciones de objetos dependientes</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Las colecciones de componentes están soportadas (por ejemplo,
|
||||||
|
un array de tipo <literal>Name</literal>). Declara tu colección
|
||||||
|
de componentes remplazando la etiqueta <literal><element></literal>
|
||||||
|
por una etiqueta <literal><composite-element></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>
|
||||||
|
Nota: si defines un <literal>Set</literal> de elementos compuestos, es muy
|
||||||
|
importante implementar <literal>equals()</literal> y <literal>hashCode()</literal>
|
||||||
|
correctamente.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Los elementos compuestos pueden contener componentes pero no colecciones.
|
||||||
|
Si tu elemento compuesto contiene a su vez componentes, usa la etiqueta
|
||||||
|
<literal><nested-composite-element></literal>. Este es un caso bastante
|
||||||
|
exótico - una colección de componentes que a su vez tienen componentes. A esta
|
||||||
|
altura debes estar preguntándote si una asociación uno-a-muchos es más
|
||||||
|
apropiada. Intenta remodelar el elemento compuesto como una entidad - pero
|
||||||
|
observa que aunque el modelo Java es el mismo, el modelo relacional y la
|
||||||
|
semántica de persistencia siguen siendo ligeramente diferentes.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Por favor observa que un mapeo de elemento compuesto no soporta
|
||||||
|
propiedades nulables si estás usando un <literal><set></literal>.
|
||||||
|
Hibernate tiene que usar cada columna para identificar un registro
|
||||||
|
al borrar objetos (no hay una columna clave primaria separada en la tabla del
|
||||||
|
elemento compuesto), lo que es imposible con valores nulos. Tienes que, o bien usar
|
||||||
|
sólo propiedades no nulas en un elemento compuesto o elegir un
|
||||||
|
<literal><list></literal>, <literal><map></literal>,
|
||||||
|
<literal><bag></literal> o <literal><idbag></literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Un caso especial de un elemento compuesto es un elemento compuesto con un
|
||||||
|
elemento anidado <literal><many-to-one></literal>. Un mapeo como este
|
||||||
|
te permite mapear columnas extra de una tabla de asociación muchos-a-muchos
|
||||||
|
a la clase del elemento compuesto. La siguiente es una asociación muchos-a-muchos
|
||||||
|
de <literal>Order</literal> a <literal>Item</literal> donde
|
||||||
|
<literal>purchaseDate</literal>, <literal>price</literal> y
|
||||||
|
<literal>quantity</literal> son propiedades de la asociación:
|
||||||
|
</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>
|
||||||
|
Por supuesto, no puede haber una referencia a la compra del otro lado para la
|
||||||
|
navegación bidireccional de la asociación. Recuerda que los componentes son tipos de
|
||||||
|
valor no permiten referencias compartidas. Una sola <literal>Purchase</literal> puede
|
||||||
|
estar en el conjunto de una <literal>Order</literal>, pero no puede ser referenciada
|
||||||
|
por el <literal>Item</literal> al mismo tiempo.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>Incluso son posibles las asociaciones ternarias (o cuaternarias, etc):</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>
|
||||||
|
Los elementos compuestos pueden aparecer en consultas usando la misma
|
||||||
|
sintáxis que las asociaciones a otras entidades.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="components-asmapindex">
|
||||||
|
<title>Componentes como índices de Map</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El elemento <literal><composite-map-key></literal> te permite mapear
|
||||||
|
una clase componente como la clave de un <literal>Map</literal>. Asegúrate que
|
||||||
|
sobrescribes <literal>hashCode()</literal> y <literal>equals()</literal>
|
||||||
|
correctamente en la clase componente.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="components-compositeid" revision="1">
|
||||||
|
<title>Componentes como identificadores compuestos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes usar un componente como un identidicador de una clase entidad. Tu clase
|
||||||
|
componente debe satisfacer ciertos requerimientos:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist spacing="compact">
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Debe implementar <literal>java.io.Serializable</literal>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Debe re-implementar <literal>equals()</literal> y
|
||||||
|
<literal>hashCode()</literal>, consistentemente con la
|
||||||
|
noción de base de datos de igualdad de clave compuesta.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<emphasis>Nota: en Hibernat3, el segundo requerimiento no es absolutamente un
|
||||||
|
requerimiento rígido de Hibernate. Pero de todas formas, házlo.</emphasis>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
No puedes usar un <literal>IdentifierGenerator</literal> para generar claves
|
||||||
|
compuestas. La aplicación debe, en cambio, asignar sus propios identificadores.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Usa la etiqueta <literal><composite-id></literal> (con elementos
|
||||||
|
anidados <literal><key-property></literal>) en lugar de la usual
|
||||||
|
declaración <literal><id></literal>. Por ejemplo, la clase
|
||||||
|
<literal>OrderLine</literal> tiene una clave primaria que depende de
|
||||||
|
la clave primaria (compuesta) de <literal>Order</literal>.
|
||||||
|
</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>
|
||||||
|
Ahora, cualquier clave foránea que referencie la tabla de <literal>OrderLine</literal>
|
||||||
|
es también compuesta. Debes declarar esto en tus mapeos de otras clases. Una asociación
|
||||||
|
a <literal>OrderLine</literal> sería mapeado así:
|
||||||
|
</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>
|
||||||
|
(Nota que la etiqueta <literal><column></literal> es una alternativa al
|
||||||
|
atributo <literal>column</literal> en cualquier sitio.)
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una asociación <literal>muchos-a-muchos</literal> a <literal>OrderLine</literal>
|
||||||
|
también usa la clave foránea compuesta:
|
||||||
|
</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>
|
||||||
|
La colección de <literal>OrderLine</literal>s en <literal>Order</literal> usaría:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[<set name="orderLines" inverse="true">
|
||||||
|
<key>
|
||||||
|
<column name="orderId"/>
|
||||||
|
<column name="customerId"/>
|
||||||
|
</key>
|
||||||
|
<one-to-many class="OrderLine"/>
|
||||||
|
</set>]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
(El elemento <literal><one-to-many></literal>, como es usual, no declara columnas.)
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Si <literal>OrderLine</literal> posee una colección por sí misma, tiene también
|
||||||
|
una clave foránea compuesta.
|
||||||
|
</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>Componentes dinámicos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes incluso mapear una propiedad de tipo <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>
|
||||||
|
La semántica de un mapeo <literal><dynamic-component></literal> es ídentica
|
||||||
|
a la de <literal><component></literal>. La ventaja de este tipo de mapeos es
|
||||||
|
la habilidad para determinar las propiedades reales del bean en tiempo de despliegue,
|
||||||
|
sólo con editar el documento de mapeo. La manipulación del documento de mapeo en tiempo
|
||||||
|
de ejecución es también posible, usando un analizador DOM. Incluso mejor, puedes acceder
|
||||||
|
(y cambiar) el metamodelo de tiempo de configuración de Hibernate por medio del objeto
|
||||||
|
<literal>Configuration</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
</chapter>
|
1761
reference/es/modules/configuration.xml
Normal file
233
reference/es/modules/events.xml
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
<chapter id="events">
|
||||||
|
<title>Interceptores y eventos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Frecuentemente es útil para la aplicación reaccionar a ciertos eventos que ocurran dentro de Hibernate.
|
||||||
|
Esto permite la implementación de ciertos tipos de funcionalidade genérica, y extensión de la
|
||||||
|
funcionalidad de Hibernate.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect1 id="objectstate-interceptors" revision="1">
|
||||||
|
<title>Interceptores</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La interface <literal>Interceptor</literal> provee callbacks desde la sesión a la aplicación
|
||||||
|
permitiendo a ésta última inspeccionar y/o manipular las propiedades de un objeto persistente
|
||||||
|
antes que sea salvado, actualizado, borrado o cargado. Un uso posible de esto es seguir la pista
|
||||||
|
de información de auditoría. Por ejemplo, el siguiente <literal>Interceptor</literal> establece
|
||||||
|
automáticamente el <literal>createTimestamp</literal> cuando un <literal>Auditable</literal> es
|
||||||
|
creado y actualiza la propiedad <literal>lastUpdateTimestamp</literal> cuando un
|
||||||
|
<literal>Auditable</literal> es acutalizado.
|
||||||
|
</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>
|
||||||
|
El interceptor podría ser especificado cuando se crea la sesión:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes además establecer un interceptor a un nivel global, usando la <literal>Configuration</literal>:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="objectstate-events" revision="2">
|
||||||
|
<title>Sistema de eventos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Si tienes que reaccionar a eventos particulares en tu capa de persistencia, puedes también la
|
||||||
|
arquitectura de <emphasis>eventos</emphasis> de Hibernate3. El sistema de eventos puede ser usado
|
||||||
|
en adición o como un remplazo a los interceptores.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Esencialmente todos los métodos de la interface <literal>Session</literal> se correlacionan
|
||||||
|
con un evento. Tienes un <literal>LoadEvent</literal>, un <literal>FlushEvent</literal>, etc
|
||||||
|
(consulta el DTD del fichero de configuración XML o el paquete <literal>org.hibernate.event</literal>
|
||||||
|
para la lista completa de tipos de evento definidos). Cuando se hace una petición de uno de estos
|
||||||
|
métodos, la <literal>Session</literal> de Hibernate genera un evento apropiado y se lo pasa
|
||||||
|
al oyente (listener) de eventos configurado para ese tipo. De fábrica, estos oyentes implementan
|
||||||
|
el mismo procesamiento en los que siempre resultan aquellos métodos. Sin embargo, eres libre de
|
||||||
|
implementar una personalización de una de las interfaces oyentes (es decir, el
|
||||||
|
<literal>LoadEvent</literal> es procesado por la implementación registrada de la interface
|
||||||
|
<literal>LoadEventListener</literal>), en cuyo caso su implementación sería responsable
|
||||||
|
de procesar cualquier petición <literal>load()</literal> hecha a la <literal>Session</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Los oyentes deben ser considerados efectivamente singletons; quiere decir, que son compartidos
|
||||||
|
entre las peticiones, y por lo tanto no guardan ningún estado en variables de instancia.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Un oyente personalizado debe implementar la interface apropiada para el evento que quiere procesar y/o
|
||||||
|
extender una de las clases base de conveniencia (o incluso los oyentes de eventos por defecto
|
||||||
|
usados por Hibernate de fábrica al ser éstos declarados non-final para este propósito). Los
|
||||||
|
oyentes personalizados pueden ser registrados programáticamente a través del objeto
|
||||||
|
<literal>Configuration</literal>, o especificados en el XML de configuración de Hibernate
|
||||||
|
(la declaración declarativa a través del fichero de propiedades no está soportada).
|
||||||
|
He aquí un ejemplo de un oyente personalizado de eventos 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>
|
||||||
|
Necesitas además una entrada de configuración diciéndole a Hibernate que use el
|
||||||
|
oyente en vez del oyente por defecto:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[<hibernate-configuration>
|
||||||
|
<session-factory>
|
||||||
|
...
|
||||||
|
<listener type="load" class="MyLoadListener"/>
|
||||||
|
</session-factory>
|
||||||
|
</hibernate-configuration>]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
En cambio, puedes registrarlo programáticamente:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[Configuration cfg = new Configuration();
|
||||||
|
cfg.getSessionEventListenerConfig().setLoadEventListener( new MyLoadListener() );]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Los oyentes registrados declarativamente no pueden compartir instancias. Si el mismo nombre de clase es
|
||||||
|
usado en múltiples elementos <literal><listener/></literal>, cada referencia resultará en una instancia
|
||||||
|
separada de esa clase. Si necesitas la capacidad de compartir instancias de oyentes entre tipos de oyente
|
||||||
|
debes usar el enfoque de registración programática.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
¿Por qué implementar una interface y definir el tipo espcífico durante la configuración?
|
||||||
|
Bueno, una implementación de oyente podría implementar múltiples interfaces de oyente
|
||||||
|
de eventos. Teniendo el tipo definido adicionalmente durante la registración lo hace más
|
||||||
|
fácil para activar o desactivar oyentes personalizados durante la configuración.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="objectstate-decl-security">
|
||||||
|
<title>Seguridad declarativa de Hibernate</title>
|
||||||
|
<para>
|
||||||
|
Usualmente, la seguridad declarativa en aplicaciones Hibernate es manejada en una capa de fachada
|
||||||
|
de sesión. Ahora, Hibernate3 permite que ciertas acciones sean permitidas vía JACC, y autorizadas vía
|
||||||
|
JAAS. Esta en una funcionalidad opcional construída encima de la arquitectura de eventos.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Primero, debes configurar los oyentes de eventos apropiados, para habilitar el uso de
|
||||||
|
autorización JAAS.
|
||||||
|
</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>
|
||||||
|
Seguido, aún en <literal>hibernate.cfg.xml</literal>, liga los permisos a roles:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
|
||||||
|
<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Los nombres de role son los roles entendidos por tu proveedor de JACC.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
</chapter>
|
||||||
|
|
654
reference/es/modules/example_mappings.xml
Normal file
@ -0,0 +1,654 @@
|
|||||||
|
<chapter id="example-mappings">
|
||||||
|
<title>Ejemplo: Varios Mapeos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Este capítulo muestra mapeos de asociaciones más complejos.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect1 id="example-mappings-emp">
|
||||||
|
<title>Empleador/Empleado</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El siguiente modelo de la relación entre <literal>Employer</literal> y <literal>Employee</literal>
|
||||||
|
usa una clase de entidad real (<literal>Employment</literal>) para representar la asociación.
|
||||||
|
Esto se ha hecho esto porque podría haber más de un período de empleo para los mismos dos participantes.
|
||||||
|
Se usan componentes para modelar valores monetarios y nombres de empleado.
|
||||||
|
</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>
|
||||||
|
He aquí un documento de mapeo posible:
|
||||||
|
</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>
|
||||||
|
Y he aquí el esquema de tablas generado por <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>Autor/Obra</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Considera el siguiente modelo de las relaciones entre <literal>Work</literal>,
|
||||||
|
<literal>Author</literal> y <literal>Person</literal>. Representamos la relación entre <literal>Work</literal>
|
||||||
|
y <literal>Author</literal> como una asociación muchos-a-muchos. Elegimos representar la relación entre
|
||||||
|
<literal>Author</literal> y <literal>Person</literal> como una asociación uno-a-uno. Otra posibilidad
|
||||||
|
hubiese sido que <literal>Author</literal> extendiera <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>
|
||||||
|
El siguiente documento de mapeo representa estas relaciones correctamente:
|
||||||
|
</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>
|
||||||
|
Hay cuatro tablas en este mapeo. <literal>works</literal>, <literal>authors</literal> y <literal>persons</literal>
|
||||||
|
tienen los datos de obra, autor y persona respectivamente. <literal>author_work</literal> es una tabla de
|
||||||
|
asociación enlazando autores a obras. He aquí el esquema de tablas, tal como fue generado por
|
||||||
|
<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>Cliente/Orden/Producto</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Ahora considera un modelo de las relaciones entre <literal>Customer</literal>,
|
||||||
|
<literal>Order</literal> y <literal>LineItem</literal> y <literal>Product</literal>.
|
||||||
|
Hay una asociación uno-a-muchos entre <literal>Customer</literal> y <literal>Order</literal>,
|
||||||
|
pero, ¿cómo deberíamos representar <literal>Order</literal> / <literal>LineItem</literal> / <literal>Product</literal>?
|
||||||
|
He elegido mapear <literal>LineItem</literal> como una clase de asociación representando la
|
||||||
|
asociación muchos-a-muchos entre <literal>Order</literal> y <literal>Product</literal>. En Hibernate,
|
||||||
|
esto se llama un elemento compuesto.
|
||||||
|
</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>
|
||||||
|
El documento de mapeo:
|
||||||
|
</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> y
|
||||||
|
<literal>products</literal> tienen los datos de cliente, orden, ítem de línea de orden y producto
|
||||||
|
respectivamente. Además <literal>line_items</literal> actúa como una tabla de asociación enlazando
|
||||||
|
órdenes con productos.
|
||||||
|
</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>Mapeos misceláneos de ejemplo</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Todos estos ejemplos están tomados de la batería de pruebas de Hibernate.
|
||||||
|
Encontrarás muchos otros mapeos de ejemplo útiles allí. Mira en la carpeta
|
||||||
|
<literal>test</literal> de la distribución de Hibernate.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>POR HACER: poner palabras alrededor de este material</para>
|
||||||
|
|
||||||
|
<sect2 id="example-mappings-typed-onetone">
|
||||||
|
<title>Asociación uno-a-uno "Tipificada"</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>Ejemplo de clave compuesta</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>Muchos-a-muchos con atributo de clave compuesta compartido</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>Discriminación basada en contenido</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>Asociaciones sobre claves alternativas</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>
|
||||||
|
|
362
reference/es/modules/example_parentchild.xml
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
<chapter id="example-parentchild">
|
||||||
|
<title>Ejemplo: Padre/Hijo</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una de las primerísimas cosas que los usuarios nuevos intentan hacer con Hibernate es modelar una relación de
|
||||||
|
tipo padre / hijo. Para esto hay dos enfoques diferentes. Por varias razones, el enfoque más conveniente,
|
||||||
|
especialmente para usuarios nuevos, es modelar tanto <literal>Parent</literal> como <literal>Child</literal>
|
||||||
|
como clases de entidad con una asociación <literal><one-to-many></literal> desde <literal>Parent</literal>
|
||||||
|
a <literal>Child</literal>. (El enfoque alternativo es declarar el <literal>Child</literal> como un
|
||||||
|
<literal><composite-element></literal>.) Ahora, resulta que la semántica por defecto de una asociación
|
||||||
|
uno a muchos (en Hibernate) es mucho menos cercana a la semántica usual de una relación padre / hijo que aquellas
|
||||||
|
de un mapeo de elementos compuestos. Explicaremos cómo usar una <emphasis>asociación uno a muchos bidireccional
|
||||||
|
con tratamiento en cascada</emphasis> para modelar una relación padre / hijo eficiente y elegantemente.
|
||||||
|
¡No es para nada difícil!
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect1 id="example-parentchild-collections">
|
||||||
|
<title>Una nota sobre las colecciones</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Se considera que las colecciones de Hibernate son una parte lógica de la entidad que las posee; nunca de
|
||||||
|
las entidades contenidas. ¡Esta es una distinción crucial! Esto tiene las siguientes consecuencias:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Cuando se quita / añade un objeto desde / a una colección, se incrementa el número de versión del
|
||||||
|
dueño de la colección.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Si un objeto que fue quitado de una colección es una instancia de un tipo de valor (por ejemplo, un
|
||||||
|
elemento compuesto), ese objeta cesará de ser persistente y su estado será completamente quitado de la
|
||||||
|
base de datos. Asimismo, añadir una instancia de tipo de valor a la colección causará que su estado
|
||||||
|
sea inmediatamente persistente.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Por otro lado, si se quita una entidad de una colección (una asociación uno-a-muchos o muchos-a-muchos),
|
||||||
|
no será borrado, por defecto. Este comportamiento es completamente consistente. ¡Un cambio en el
|
||||||
|
estado interno de otra entidad no hace desaparecer la entidad asociada! Asimismo, añadir una entidad a
|
||||||
|
una colección no causa que la entidad se vuelva persistente, por defecto.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
En cambio, el comportamiento por defecto es que al añadir una entidad a una colección se crea meramente
|
||||||
|
un enlace entre las dos entidades, mientras que al quitarla se quita el enlace. Esto es muy apropiado para
|
||||||
|
todos los tipos de casos. Donde no es para nada apropiado es en el caso de una relación padre / hijo. donde
|
||||||
|
la vida del hijo está ligada al ciclo de vida del padre.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="example-parentchild-bidir">
|
||||||
|
<title>Uno-a-muchos bidirectional</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Supón que empezamos con una asociación simple <literal><one-to-many></literal> desde
|
||||||
|
<literal>Parent</literal> a <literal>Child</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[<set name="children">
|
||||||
|
<key column="parent_id"/>
|
||||||
|
<one-to-many class="Child"/>
|
||||||
|
</set>]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Si ejecutásemos el siguiente código
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[Parent p = .....;
|
||||||
|
Child c = new Child();
|
||||||
|
p.getChildren().add(c);
|
||||||
|
session.save(c);
|
||||||
|
session.flush();]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate publicaría dos sentencias SQL:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>un <literal>INSERT</literal> para crear el registro de <literal>c</literal></para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
un <literal>UPDATE</literal> para crear el enlace desde <literal>p</literal> a
|
||||||
|
<literal>c</literal>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Esto no es sólo ineficiente, sino que además viola cualquier restricción <literal>NOT NULL</literal> en la
|
||||||
|
columna <literal>parent_id</literal>. Podemos reparar la violación de restricción de nulabilidad
|
||||||
|
especificando <literal>not-null="true"</literal> en el mapeo de la colección:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[<set name="children">
|
||||||
|
<key column="parent_id" not-null="true"/>
|
||||||
|
<one-to-many class="Child"/>
|
||||||
|
</set>]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Sin embargo, esta no es la solución recomendada.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
El caso subyacente de este comportamiento es que el enlace (la clave foránea <literal>parent_id</literal>)
|
||||||
|
de <literal>p</literal> a <literal>c</literal> no es considerado parte del estado del objeto
|
||||||
|
<literal>Child</literal> y por lo tanto no es creada en el <literal>INSERT</literal>. De modo que la
|
||||||
|
solución es hacer el enlace parte del mapeo del <literal>Child</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
(Necesitamos además añadir la propiedad <literal>parent</literal> a la clase <literal>Child</literal>.)
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Ahora que la entidad <literal>Child</literal> está gestionando el estado del enlace, le decimos a la
|
||||||
|
colección que no actualice el enlace. Usamos el atributo <literal>inverse</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[<set name="children" inverse="true">
|
||||||
|
<key column="parent_id"/>
|
||||||
|
<one-to-many class="Child"/>
|
||||||
|
</set>]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El siguiente código podría ser usado para añadir un nuevo <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>
|
||||||
|
Y ahora, ¡Sólo se publicaría un <literal>INSERT</literal> de SQL!
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Para ajustar un poco más las cosas, podríamos crear un método <literal>addChild()</literal> en
|
||||||
|
<literal>Parent</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[public void addChild(Child c) {
|
||||||
|
c.setParent(this);
|
||||||
|
children.add(c);
|
||||||
|
}]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Ahora, el código para añadir un <literal>Child</literal> se ve así
|
||||||
|
</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>Ciclo de vida en cascada</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La llamada explícita a <literal>save()</literal> es aún molesta. Apuntaremos a esto usando tratamientos
|
||||||
|
en cascada.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[<set name="children" inverse="true" cascade="all">
|
||||||
|
<key column="parent_id"/>
|
||||||
|
<one-to-many class="Child"/>
|
||||||
|
</set>]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Esto simplifica el código anterior a
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
|
||||||
|
Child c = new Child();
|
||||||
|
p.addChild(c);
|
||||||
|
session.flush();]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Similarmente, no necesitamos iterar los hijos al salvar o borrar un <literal>Parent</literal>.
|
||||||
|
Lo siguiente quita <literal>p</literal> y todos sus hijos de la base de datos.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
|
||||||
|
session.delete(p);
|
||||||
|
session.flush();]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Sin embargo, este código
|
||||||
|
</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>
|
||||||
|
no quitará <literal>c</literal> de la base de datos; sólo quitará el enlace a <literal>p</literal>
|
||||||
|
(y causará una violación a una restricción <literal>NOT NULL</literal>). Necesitas borrar el hijo
|
||||||
|
explícitamente llamando a <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>
|
||||||
|
Ahora, en nuestro caso, un <literal>Child</literal> no puede existir realmente sin su padre. De modo que
|
||||||
|
si quitamos un <literal>Child</literal> de la colección, realmente queremos que sea borrado. Para esto,
|
||||||
|
debemos usar <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>
|
||||||
|
Nota: aunque el mapeo de la colección especifique <literal>inverse="true"</literal>, el tratamiento en
|
||||||
|
cascada se procesa aún al iterar los elementos de colección. De modo que si requieres que un objeto sea
|
||||||
|
salvado, borrado o actualizado en cascada, debes añadirlo a la colección. No es suficiente con simplemente
|
||||||
|
llamar a <literal>setParent()</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="example-parentchild-update">
|
||||||
|
<title>Tratamiento en cascada y <literal>unsaved-value</literal></title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Supón que hemos cargado un <literal>Parent</literal> en una <literal>Session</literal>, hemos hecho algunos
|
||||||
|
cambios en una acción de UI y deseamos hacer persistentes estos cambios en una nueva sesión llamando a
|
||||||
|
<literal>update()</literal>. El <literal>Parent</literal> contendrá una colección de hijos y, ya que
|
||||||
|
está habilitado el tratamiento en cascada, Hibernate necesita saber qué hijos están recién instanciados
|
||||||
|
y cuáles representan filas existentes en la base de datos. Asumamos que tanto <literal>Parent</literal> como
|
||||||
|
<literal>Child</literal> tienen propiedades identificadoras generadas de tipo <literal>Long</literal>.
|
||||||
|
Hibernate usará el identificador y el valor de la propiedad de versión/timestamp para determinar cuáles de
|
||||||
|
los hijos son nuevos. (Ver <xref linkend="objectstate-saveorupdate"/>.) <emphasis>En Hibernate3, no es
|
||||||
|
más necesario especificar un <literal>unsaved-value</literal> explícitamente.</emphasis>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The following code will update <literal>parent</literal> and <literal>child</literal> and insert
|
||||||
|
<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>
|
||||||
|
Bueno, todo eso está muy bien para el caso de un identificador generado, pero ¿qué de los
|
||||||
|
identificadores asignados y de los identificadores compuestos? Esto es más difícil, ya que Hibernate
|
||||||
|
no puede usar la propiedad identificadora para distinguir entre un objeto recién instanciado (con un
|
||||||
|
identificador asignado por el usuario) y un objeto cargado en una sesión previa. En este caso, Hibernate
|
||||||
|
bien usará la propiedad de versión o timestamp, o bien consultará realmente el caché de segundo nivel,
|
||||||
|
o bien, en el peor de los casos, la base de datos, para ver si existe la fila.
|
||||||
|
</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>Conclusión</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hay que resumir un poco aquí y podría parecer confuso a la primera vez. Sin embargo, en la práctica,
|
||||||
|
todo funciona muy agradablemente. La mayoría de las aplicaciones de Hibernate usan el patrón
|
||||||
|
padre / hijo en muchos sitios.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hemos mencionado una alternativa en el primer párrafo. Ninguno de los temas anteriores existe en el caso
|
||||||
|
de los mapeos <literal><composite-element></literal>, que tienen exactamente la semántica de una
|
||||||
|
relación padre / hijo. Desafortunadamente, hay dos grandes limitaciones para las clases de elementos
|
||||||
|
compuestos: los elementos compuestos no pueden poseer sus propias colecciones, y no deben ser el hijo
|
||||||
|
de cualquier otra entidad que no sea su padre único.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
</chapter>
|
429
reference/es/modules/example_weblog.xml
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
<chapter id="example-weblog">
|
||||||
|
<title>Ejemplo: Aplicación de Weblog</title>
|
||||||
|
|
||||||
|
<sect1 id="example-weblog-classes">
|
||||||
|
<title>Clases Persistentes</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Las clases persistentes representan un weblog, y un ítem enviado a un weblog. Van a ser modelados como una
|
||||||
|
relación padre/hijo estñndar, pero usaremos un bag ordenado, en vez de un conjunto (set).
|
||||||
|
</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>Mapeos de Hibernate</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Los mapeos XML ahora deben ser absolutamente directos.
|
||||||
|
</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>Código Hibernate</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La siguiente clase demuestra algunos de los tipos de cosas que podemos haces con estas clases,
|
||||||
|
usando 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>
|
||||||
|
|
130
reference/es/modules/filters.xml
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<chapter id="filters">
|
||||||
|
<title>Filtrando datos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate3 provee un nuevo enfoque innovador para manejar datos con reglas de "visibilidad".
|
||||||
|
Un <emphasis>filtro de Hibernate</emphasis> es un filtro global, con nombre y parametrizado
|
||||||
|
que puede ser habilitado o deshabilitado para una sesión de Hibernate en particular.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect1 id="objectstate-filters">
|
||||||
|
<title>Filtros de Hibernate</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate3 añade la habilidad de predefinir criterios de filtros y unir esos filtros tanto a
|
||||||
|
nivel de una clase como de una colección. Un criterio de filtro es la habilidad de definir una
|
||||||
|
cláusula de restricción muy similar al atributo existente "where" disponible en el elemento
|
||||||
|
class y varios elementos de colección. Excepto en que estos filtros pueden ser parametrizados.
|
||||||
|
La aplicación puede tomar la decisión en tiempo de ejecución de qué filtros deben estar
|
||||||
|
habilitados y cuáles deben ser sus parámetros. Los filtros pueden ser usados como vistas de
|
||||||
|
base de datos, pero parametrizados dentro de la aplicación.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Para usar los filtros, éstos deben primero ser definidos y luego unidos a los elementos de mapeo
|
||||||
|
apropiados. Para definir un filtro, usa el elemento <literal><filter-def/></literal> dentro
|
||||||
|
de un elemento <literal><hibernate-mapping/></literal>:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[<filter-def name="myFilter">
|
||||||
|
<filter-param name="myFilterParam" type="string"/>
|
||||||
|
</filter-def>]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Entonces este filtro puede ser unido a una clase:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[<class name="myClass" ...>
|
||||||
|
...
|
||||||
|
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
|
||||||
|
</class>]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
o a una colección:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[<set ...>
|
||||||
|
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
|
||||||
|
</set>]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
o incluso a ambos (o muchos de cada uno) al mismo tiempo.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Los métodos en <literal>Session</literal> son: <literal>enableFilter(String filterName)</literal>,
|
||||||
|
<literal>getEnabledFilter(String filterName)</literal>, y <literal>disableFilter(String filterName)</literal>.
|
||||||
|
Por defecto, los filtros <emphasis>no</emphasis> están habilitados para una sesión dada; deben ser
|
||||||
|
habilitados explícitamente por medio del uso del método <literal>Session.enableFilter()</literal>,
|
||||||
|
que devuelve una instancia de la interface <literal>Filter</literal>. Usando el filtro simple definido
|
||||||
|
arriba, esto se vería así:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Nota que los métodos en la interface org.hibernate.Filter permiten el encadenamiento de métodos
|
||||||
|
común en gran parte de Hibernate.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Un ejemplo completo, usando datos temporales con un patrón efectivo de fechas de registro:
|
||||||
|
</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>
|
||||||
|
Entonces, en orden de asegurar que siempre tendrás de vuelta registros actualmente efectivos,
|
||||||
|
simplemente habilita el filtro en la sesión previo a recuperar los datos de empleados:
|
||||||
|
</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>
|
||||||
|
En el HQL de arriba, aunque sólo hemos mencionado explícitamente una restricción de salario en
|
||||||
|
los resultados, debido al filtro habilitado la consulta sólo devolverá empleados actualmente activos
|
||||||
|
que tengan un salario mayor que un millón de dólares.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Nota: si planeas usar filtros con unión externa (outer joining) (bien a través de HQL, o bien
|
||||||
|
de recuperación de carga) sé cuidadoso en la dirección de expresión de la condición. Lo más seguro
|
||||||
|
es establecer esto para unión externa izquierda (left outer joining). En general, coloca el primer
|
||||||
|
parámetro seguido del nombre(s) de columna(s) después del operador.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
</chapter>
|
||||||
|
|
464
reference/es/modules/inheritance_mapping.xml
Normal file
@ -0,0 +1,464 @@
|
|||||||
|
<chapter id="inheritance">
|
||||||
|
<title>Mapeo de Herencia</title>
|
||||||
|
|
||||||
|
<sect1 id="inheritance-strategies" revision="2">
|
||||||
|
<title>Las Tres Estrategias</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate soporta las tres estrategias básicas de mapeo de herencia:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
tabla por jerarquía de clases
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
tabla por subclase
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
tabla por clase concreta
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
En adición, Hibernate soporta un cuarto, ligeramente diferente tipo
|
||||||
|
de polimorfismo:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
polimorfismo implícito
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Es posible usar estrategias de mapeo diferentes para diferentes
|
||||||
|
ramificaciones de la misma jerarquía de herencia, y entonces usar
|
||||||
|
polimorfismo implícito para conseguir polimorfismo a través de
|
||||||
|
toda la jerarquía. Sin embargo, Hibernate no soporta la mezcla de
|
||||||
|
mapeos <literal><subclass></literal>,
|
||||||
|
y <literal><joined-subclass></literal>
|
||||||
|
y <literal><union-subclass></literal> bajo el mismo elemento
|
||||||
|
<literal><class></literal> raíz. Es posible mezclar juntas las
|
||||||
|
estrategias de tabla por jerarquía y tabla por subclase, bajo el mismo
|
||||||
|
elemento <literal><class></literal>, combinando los elementos
|
||||||
|
<literal><subclass></literal> y <literal><join></literal>
|
||||||
|
(ver debajo).
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect2 id="inheritance-tableperclass" >
|
||||||
|
<title>Tabla por jerarquía de clases</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Supón que tenemos una interface <literal>Payment</literal>, con
|
||||||
|
los implementadores <literal>CreditCardPayment</literal>,
|
||||||
|
<literal>CashPayment</literal>, <literal>ChequePayment</literal>.
|
||||||
|
El mapeo de tabla por jerarquía se vería así:
|
||||||
|
</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>
|
||||||
|
Se requiere exactamente una tabla. Hay una gran limitación de esta estrategia de mapeo:
|
||||||
|
las columnas declaradas por las subclases, como <literal>CCTYPE</literal>, no pueden
|
||||||
|
tener restricciones <literal>NOT NULL</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="inheritance-tablepersubclass">
|
||||||
|
<title>Tabla por subclase</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Un mapeo de tabla por sublclase se vería así:
|
||||||
|
</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>
|
||||||
|
Se requieren cuatro tablas. Las tres tablas de subclase tienen
|
||||||
|
asociaciones de clave primaria a la tabla de superclase (de modo
|
||||||
|
que en el modelo relacional es realmente una asociación uno-a-uno).
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
|
||||||
|
<title>Tabla por subclase, usando un discriminador</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Observa que la implementación de Hibernate de tabla por subclase
|
||||||
|
no requiere ninguna columna discriminadora. Otros mapeadores
|
||||||
|
objeto/relacional usan una implementación diferente de tabla por
|
||||||
|
subclase que requiere una columna discriminadora de tipo en la tabla
|
||||||
|
de superclase. Este enfoque es mucho más difícil de implementar
|
||||||
|
pero discutiblemente más correcto desde un punto de vista relacional.
|
||||||
|
Si quisieras usar una columna discriminadora con la estrategia de
|
||||||
|
tabla por subclase, puedes combinar el uso de <literal><subclass></literal>
|
||||||
|
y <literal><join></literal>, como sigue:
|
||||||
|
</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>
|
||||||
|
la declaración opcional <literal>fetch="select"</literal> dice a Hibernate
|
||||||
|
que no recupere los datos de la subclase <literal>ChequePayment</literal>
|
||||||
|
usando una unión externa (outer join) al consultar la superclase.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
|
||||||
|
<title>Mezclando tabla por jerarquía de clases con tabla por subclase</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes incluso mezclar las estrategias de tabla po jerarquía y tabla por
|
||||||
|
subclase usando este enfoque:
|
||||||
|
</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>
|
||||||
|
Para cualquiera de estas estrategias de mapeo, una asociación polimórfica
|
||||||
|
a la clase raíz <literal>Payment</literal> es mapeada usando <literal><many-to-one></literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="inheritance-tableperconcrete" revision="1">
|
||||||
|
<title>Tabla por clase concreta</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Podríamos ir de dos maneras a la estrategia de mapeo de tabla por clase
|
||||||
|
concreta. La primera es usar <literal><union-subclass></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>
|
||||||
|
Están implicadas tres tablas. Cada tabla define columnas para todas las
|
||||||
|
propiedades de la clase, inccluyendo las propiedades heredadas.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La limitación de este enfoque es que si una propiedad es mapeada en la
|
||||||
|
superclase, el nombre de columna debe ser el mismo en todas las tablas
|
||||||
|
de subclase. (Podríamos relajar esto en un lanzamiento futuro de Hibernate.)
|
||||||
|
La estrategia de generador de indentidad no está permitida en la herencia
|
||||||
|
de unión de subclase, de hecho la semilla de clave primaria tiene que ser
|
||||||
|
compartida a través de todas las subclases unidas de una jerarquía.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="inheritance-tableperconcreate-polymorphism">
|
||||||
|
<title>Tabla por clase concreta, usando polimorfismo implícito</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Un enfoque alternativo es hacer uso de polimorfismo implícito:
|
||||||
|
</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>
|
||||||
|
Nota que en ningún sitio mencionamos la interface <literal>Payment</literal>
|
||||||
|
explícitamente. Nota además que las propiedades de <literal>Payment</literal>
|
||||||
|
son mapeadas en cada una de las subclases. Si quieres evitar duplicación,
|
||||||
|
considera usar entidades XML. (por ejemplo,
|
||||||
|
<literal>[ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]</literal>
|
||||||
|
en la declaración <literal>DOCTYPE</literal> y <literal>&allproperties;</literal>
|
||||||
|
en el mapeo).
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La desventaja de este enfoque es que Hibernate no genera <literal>UNION</literal>s
|
||||||
|
de SQL al realizar consultas polimórficas.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Para esta estrategia de mapeo, una asociación polimórfica a <literal>Payment</literal>
|
||||||
|
es mapeada generalmente usando <literal><any></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>Mezclando polimorfismo implícito con otros mapeos de herencia</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hay una cosa más por notar acerca de este mapeo. Ya que las subclases se mapean
|
||||||
|
cada una en su propio elemento <literal><class></literal> (y ya que
|
||||||
|
<literal>Payment</literal> es sólo una interface), cada una de las subclases
|
||||||
|
podría ser parte de otra jerarquía de herencia! (Y todavía puedes seguir usando
|
||||||
|
consultas polimórficas contra la interface <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>
|
||||||
|
Una vez más, no mencionamos a <literal>Payment</literal> explícitamente.
|
||||||
|
Si ejecutamos una consulta contra la interface <literal>Payment</literal>
|
||||||
|
- por ejemplo, <literal>from Payment</literal> - Hibernate devuelve
|
||||||
|
automáticamente instancias de <literal>CreditCardPayment</literal>
|
||||||
|
(y sus subclases, ya que ellas también implementan <literal>Payment</literal>),
|
||||||
|
<literal>CashPayment</literal> y <literal>ChequePayment</literal> pero
|
||||||
|
no instancias de <literal>NonelectronicTransaction</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="inheritance-limitations">
|
||||||
|
<title>Limitaciones</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Existen ciertas limitaciones al enfoque de "polimorfismo implícito" en
|
||||||
|
la estrategia de mapeo de tabla por clase concreta. Existen limitaciones
|
||||||
|
algo menos restrictivas a los mapeos <literal><union-subclass></literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La siguiente tabla muestra las limitaciones de mapeos de tabla por
|
||||||
|
clase concreta, y de polmorfismo implícito, en Hibernate.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<table frame="topbot">
|
||||||
|
<title>Funcionalidades de mapeo de herencia</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>Estrategia de herencia</entry>
|
||||||
|
<entry>muchos-a-uno polimórfica</entry>
|
||||||
|
<entry>uno-a-uno polimórfica</entry>
|
||||||
|
<entry>uno-a-muchos polimórfica</entry>
|
||||||
|
<entry>mushos-a-muchos polimórfica</entry>
|
||||||
|
<entry><literal>load()/get()</literal> polimórficos</entry>
|
||||||
|
<entry>Consultas polimórficas</entry>
|
||||||
|
<entry>Uniones polimórficas</entry>
|
||||||
|
<entry>Recuperación por unión externa (outer join)</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry>tabla por jerarquía de clases</entry>
|
||||||
|
<entry><literal><many-to-one></literal></entry>
|
||||||
|
<entry><literal><one-to-one></literal></entry>
|
||||||
|
<entry><literal><one-to-many></literal></entry>
|
||||||
|
<entry><literal><many-to-many></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>soportada</emphasis></entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>tabla por subclase</entry>
|
||||||
|
<entry><literal><many-to-one></literal></entry>
|
||||||
|
<entry><literal><one-to-one></literal></entry>
|
||||||
|
<entry><literal><one-to-many></literal></entry>
|
||||||
|
<entry><literal><many-to-many></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>soportada</emphasis></entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>tabla por clase concreta (union-subclass)</entry>
|
||||||
|
<entry><literal><many-to-one></literal></entry>
|
||||||
|
<entry><literal><one-to-one></literal></entry>
|
||||||
|
<entry><literal><one-to-many></literal> (para <literal>inverse="true"</literal> solamente)</entry>
|
||||||
|
<entry><literal><many-to-many></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>soportada</emphasis></entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>tabla por clase concreta (polimorfismo implícito)</entry>
|
||||||
|
<entry><literal><any></literal></entry>
|
||||||
|
<entry><emphasis>no soportada</emphasis></entry>
|
||||||
|
<entry><emphasis>no soportada</emphasis></entry>
|
||||||
|
<entry><literal><many-to-any></literal></entry>
|
||||||
|
<entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
|
||||||
|
<entry><literal>from Payment p</literal></entry>
|
||||||
|
<entry><emphasis>no suportadas</emphasis></entry>
|
||||||
|
<entry><emphasis>no soportada</emphasis></entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
</chapter>
|
1334
reference/es/modules/performance.xml
Normal file
478
reference/es/modules/persistent_classes.xml
Normal file
@ -0,0 +1,478 @@
|
|||||||
|
<chapter id="persistent-classes" revision="2">
|
||||||
|
<title>Clases Persistentes</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Clases presistentes son clases en una aplicación que implementan las
|
||||||
|
entidades del problema de negocio (por ejemplo, Customer y Order en una
|
||||||
|
aplicación de comercio electrónico). No todas las instancias de una
|
||||||
|
clase persistente se considera que estén en el estado persistente,
|
||||||
|
una instancia puede en cambio ser transitoria o estar separada.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate funciona mejor si las clases siguen algunas simples reglas, también
|
||||||
|
conocidas como el modelo de programación de Viejas Clases Java Planas
|
||||||
|
(Plain Old Java Object o POJO). Sin embargo, ninguna de estas reglas son
|
||||||
|
requerimientos rígidos. En cambio, Hibernate3 asume muy poco acerca de
|
||||||
|
la naturaleza de tus objetos persistentes. Puedes expresar un modelo de dominio en
|
||||||
|
otras formas: usando árboles de instancias de <literal>Map</literal>,
|
||||||
|
por ejemplo.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect1 id="persistent-classes-pojo">
|
||||||
|
<title>Un ejemplo simple de POJO</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La mayoría de aplicaciones Java requieren una clase
|
||||||
|
representando felinos.
|
||||||
|
</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>
|
||||||
|
Aquí hay cuatro reglas principales a seguir:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect2 id="persistent-classes-pojo-constructor" revision="1">
|
||||||
|
<title>Implementa un constructor sin argumentos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<literal>Cat</literal> tiene un contructor sin argumentos. Todas las clases persistentes
|
||||||
|
deben tener un constructor por defecto (que puede no ser público) de modo que Hibernate
|
||||||
|
pueda instanciarlas usando <literal>Constructor.newInstance()</literal>. Recomendamos fuertemente tener
|
||||||
|
un constructor por defecto con al menos visibilidad de <emphasis>package</emphasis> para la
|
||||||
|
generación de proxies en tiempo de ejecución en Hibernate.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="persistent-classes-pojo-identifier" revision="2">
|
||||||
|
<title>Provee una propiedad identificadora (opcional)</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<literal>Cat</literal> tiene una propiedad llamada <literal>id</literal>. Esta
|
||||||
|
propiedad mapea a la columna clave primaria de la tabla de base de datos. La propiedad
|
||||||
|
podría llamarse cualquierCosa, y su tipo podría haber sido cualquier tipo
|
||||||
|
primitivo, cualquier tipo de "envoltura" primitivo, <literal>java.lang.String</literal>
|
||||||
|
o <literal>java.util.Date</literal>. (Si tu tabla de base de datos heredada tiene claves
|
||||||
|
compuestas, puedes incluso usar una clase definida por el usuario con propiedades de
|
||||||
|
estos tipos, ver la sección sobre identificadores compuestos luego.)
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La propiedad identificadora es estrictamente opcional. Puedes olvidarla y dejar que Hibernate
|
||||||
|
siga internamente la pista de los identificadores del objeto. Sin embargo, no recomendamos esto.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
De hecho, alguna funcionalidad está disponible sólo para clases que
|
||||||
|
declaran una propiedad identificadora:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist spacing="compact">
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Reasociación transitiva de objetos separados (actualizaciones o
|
||||||
|
fusiones en cascada) - ver <xref linkend="objectstate-transitive"/>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>Session.saveOrUpdate()</literal>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>Session.merge()</literal>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Recomendamos que declares propiedades identificadoras nombradas-consistentemente
|
||||||
|
en clases persistentes. Mas aún, recomendamos que uses un tipo nulable
|
||||||
|
(es decir, no primitivo).
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="persistent-classes-pojo-final">
|
||||||
|
<title>Prefiere las clases no finales (opcional)</title>
|
||||||
|
<para>
|
||||||
|
Un aspecto central de Hibernate, <emphasis>proxies</emphasis>, depende de que
|
||||||
|
las clases persistentes sean ya no finales, o sean ya la implementación
|
||||||
|
de una interface que declare todos los métodos públicos.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Puedes persistir con Hibernate clases <literal>final</literal> que no implementen una
|
||||||
|
interface, pero no serás capaz de usar proxies para recuperación perezosa
|
||||||
|
de asociaciones, lo que limitará tus opciones para afinar el rendimiento.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Debes también evitar declarar métodos <literal>public final</literal>
|
||||||
|
en clases non-final. Si quieres usar una clase con un método <literal>public
|
||||||
|
final</literal>, debes deshabilitar explícitamente el uso de proxies estableciendo
|
||||||
|
<literal>lazy="false"</literal>.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="persistent-classes-pojo-accessors" revision="2">
|
||||||
|
<title>Declara métodos de acceso y modificación para los campos persistentes (opcional)</title>
|
||||||
|
<para>
|
||||||
|
<literal>Cat</literal> declara métodos de acceso para todos sus campos persistente.
|
||||||
|
Muchas otras herramientas ORM persisten directamente variables de instancia. Creemos que
|
||||||
|
es mejor proveer una indirección entre el esquema relacional y las estructuras internas de la clase.
|
||||||
|
Por defecto, Hibernate persiste propiedades del estilo JavaBeans, y reconoce nombres de método
|
||||||
|
de la forma <literal>getFoo</literal>, <literal>isFoo</literal> y <literal>setFoo</literal>.
|
||||||
|
Puedes cambiar a acceso directo a campos para propiedades en particular, de ser necesario.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Las propiedades <emphasis>no</emphasis> necesitan ser declaradas públicas. Hibernate puede
|
||||||
|
persistir una propiedad con un par get / set <literal>protected</literal> o <literal>private</literal>.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="persistent-classes-inheritance">
|
||||||
|
<title>Implementando herencia</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una subclase puede a su vez observar la primera y segunda regla. Hereda su
|
||||||
|
propiedad identificadora de la superclase, <literal>Cat</literal>.
|
||||||
|
</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>Implementando <literal>equals()</literal> y <literal>hashCode()</literal></title>
|
||||||
|
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Tienes que sobrescribir los métodos <literal>equals()</literal> y <literal>hashCode()</literal>
|
||||||
|
si :
|
||||||
|
</para>
|
||||||
|
<itemizedlist spacing="compact">
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
piensas poner instancias de clases persistentes en un <literal>Set</literal>
|
||||||
|
(la forma recomendada de representar asociaciones multivaluadas)
|
||||||
|
<emphasis>y</emphasis>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
piensas usar reasociación de instancias separadas.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate garantiza la equivalencia de identidad persistente (fila de base de datos) y
|
||||||
|
identidad Java sólo dentro del ámbito de una sesión en particular.
|
||||||
|
De modo que en el momento que mezclamos instancias recuperadas en sesiones diferentes,
|
||||||
|
debemos implementar <literal>equals()</literal> y <literal>hashCode()</literal> si
|
||||||
|
deseamos tener una semántica significativa de <literal>Set</literal>s.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La forma más obvia es implementar <literal>equals()</literal>/<literal>hashCode()</literal>
|
||||||
|
comparando el valor identificador de ambos objetos. Si el valor es el mismo, ambos deben ser
|
||||||
|
la misma fila de base de datos, por lo tanto son iguales (si ambos son agregados a un
|
||||||
|
<literal>Set</literal>, sólo tendremos un elemento en el <literal>Set</literal>).
|
||||||
|
Desafortunadamente, no podemos usar este enfoque con identificadores generados! Hibernate sólo
|
||||||
|
asignará valores identificadores a objetos que son persistentes, una instancia recién
|
||||||
|
creada no tendrá ningún valor identificador! Además, si una instancia no está
|
||||||
|
salvada y está actualmente en un <literal>Set</literal>, salvarla asignará un
|
||||||
|
valor identificador al objeto. Si <literal>equals()</literal> and <literal>hashCode()</literal>
|
||||||
|
están basados en el valor identificador, el código hash podría cambiar,
|
||||||
|
rompiendo el contrato de <literal>Set</literal>. Ver el sitio web de Hibernate para una
|
||||||
|
discusión completa de este problema. Observa que esto no es una incidencia de Hibernate,
|
||||||
|
sino la semántica normal de Java de identidad de objeto e igualdad.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Recomendamos implementar <literal>equals()</literal> y <literal>hashCode()</literal>
|
||||||
|
usando <emphasis>igualdad de clave de negocio (Business key equality)</emphasis>.
|
||||||
|
Igualdad de clave de negocio significa que el método <literal>equals()</literal>
|
||||||
|
compara sólo las propiedades que forman la clave de negocio, una clave que podría
|
||||||
|
identificar nuestra instancia en el mundo real (una clave candidata
|
||||||
|
<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>
|
||||||
|
Nota que una clave de negocio no tiene que ser tan sólida como
|
||||||
|
una clave primaria candidata de base de datos (ver
|
||||||
|
<xref linkend="transactions-basics-identity"/>). Las propiedades inmutables o
|
||||||
|
únicas son usualmente buenas candidatas para una clave de negocio.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="persistent-classes-dynamicmodels">
|
||||||
|
<title>Modelos dinámicos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<emphasis>Ten en cuenta que las siguientes funcionalidades están
|
||||||
|
consideradas actualmente experimentales y pueden cambiar en el futuro
|
||||||
|
cercano.</emphasis>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Las entidades persistentes no necesariamente tienen que estar representadas
|
||||||
|
como clases POJO o como objetos JavaBean en tiempo de ejecución. Hibernate
|
||||||
|
soporta además modelos dinámicos (usando <literal>Map</literal>s de
|
||||||
|
<literal>Map</literal>s en tiempo de ejecución) y la representación
|
||||||
|
de entidades como árboles de DOM4J. Con este enfoque no escribes clases
|
||||||
|
persistentes, sólo ficheros de mapeo.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Por defecto, Hibernate funciona en modo POJO normal. Puedes establecer una
|
||||||
|
representación de entidad por defecto para una <literal>SessionFactory</literal>
|
||||||
|
en particular usando la opción de configuración
|
||||||
|
<literal>default_entity_mode</literal>
|
||||||
|
(ver <xref linkend="configuration-optional-properties"/>).
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Los siguientes ejemplos demuestran la representación usando
|
||||||
|
<literal>Map</literal>s. Primero, en el fichero de mapeo,
|
||||||
|
tiene que declararse un <literal>entity-name</literal> en vez de
|
||||||
|
(o como agregado a) un nombre de clase:
|
||||||
|
</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>
|
||||||
|
Ten en cuenta que aunque las asociaciones se declaran usando nombres
|
||||||
|
de clase objetivo, el tipo objetivo de una asociación puede
|
||||||
|
ser además una entidad dinámica en vez de un POJO.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Después de establecer el modo de entidad por defecto a
|
||||||
|
<literal>dynamic-map</literal> para la <literal>SessionFactory</literal>,
|
||||||
|
podemos trabajar en tiempo de ejecución con <literal>Map</literal>s
|
||||||
|
de <literal>Map</literal>s:
|
||||||
|
</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>
|
||||||
|
Las ventajas de un mapeo dinámico es rápido tiempo de ciclo
|
||||||
|
de prototipado sin la necesidad de implementación de clases de entidad.
|
||||||
|
Sin embargo, pierdes chequeo de tipos en tiempo de compilación y
|
||||||
|
muy probablemente tratarás con muchas excepciones en tiempo de ejecución.
|
||||||
|
Gracias al mapeo de Hibernate, el esquema de base de datos puede estar facilmente
|
||||||
|
sano y normalizado, permitiendo agregar una implementación apropiada del
|
||||||
|
modelo de dominio más tarde.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Los modos de representación de entidad pueden ser establecidos
|
||||||
|
por <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>
|
||||||
|
Por favor, ten en cuenta que la llamada a <literal>getSession()</literal>
|
||||||
|
usando un <literal>EntityMode</literal> está en la API de
|
||||||
|
<literal>Session</literal>, no en la de <literal>SessionFactory</literal>.
|
||||||
|
De esta forma, la nueva <literal>Session</literal> comparte la conexión
|
||||||
|
JDBC, transacción y otra información de contexto. Esto significa
|
||||||
|
que no tienes que llamar a <literal>flush()</literal> ni a <literal>close()</literal>
|
||||||
|
en la <literal>Session</literal> secundaria, y tembién dejar el manejo
|
||||||
|
de la transacción y de la conexión a la unidad de trabajo primaria.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puede encontrarse más información sobre las capacidades de
|
||||||
|
representación XML en <xref linkend="xml"/>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
PORHACER: Documentar el framework de extensiones del usuario en los paquetes
|
||||||
|
de propiedad y proxies.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</chapter>
|
||||||
|
|
431
reference/es/modules/query_criteria.xml
Normal file
@ -0,0 +1,431 @@
|
|||||||
|
<chapter id="querycriteria">
|
||||||
|
<title>Consultas por Criterios</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Acompaña a Hibernate una API de consultas por criterios intuitiva y extensible.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect1 id="querycriteria-creating">
|
||||||
|
<title>Creando una instancia de <literal>Criteria</literal></title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La interface <literal>org.hibernate.Criteria</literal> representa una consulta contra
|
||||||
|
una clase persistente en particular. La <literal>Session</literal> es una fábrica de instancias
|
||||||
|
de <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>Estrechando el conjunto resultado</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Un criterio individual de consulta es una instancia de la interface
|
||||||
|
<literal>org.hibernate.criterion.Criterion</literal>. La clase
|
||||||
|
<literal>org.hibernate.criterion.Restrictions</literal> define métodos de fábrica para obtener ciertos tipos
|
||||||
|
prefabricados de <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>
|
||||||
|
Las restricciones pueden ser agrupadas lógicamente.
|
||||||
|
</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>
|
||||||
|
Hay un gran rango de tipos de criterio prefabricados (subclases de <literal>Restrictions</literal>),
|
||||||
|
pero uno que es especialmente útil te deja especificar SQL directamente.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||||||
|
.add( Restrictions.sql("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
|
||||||
|
.list();]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El sitio <literal>{alias}</literal> será remplazado por el alias de fila de la entidad consultada.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Un enfoque alternativo para obtener un criterio es tomarlo de una instancia de
|
||||||
|
<literal>Property</literal>. Puedes crear una <literal>Property</literal> llamando a
|
||||||
|
<literal>Property.forName()</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>Ordenando los resultados</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes ordenar los resultados usando <literal>org.hibernate.criterion.Order</literal>.
|
||||||
|
</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>Asociaciones</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes especificar fácilmente restricciones sobre las entidades relacionadas al navegar asociaciones
|
||||||
|
usando <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>
|
||||||
|
nota que el segundo <literal>createCriteria()</literal> devuelve una nueva instancia de
|
||||||
|
<literal>Criteria</literal>, que hace referencia a los elementos de la colección
|
||||||
|
<literal>kittens</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La siguiente forma alternativa es útil en ciertas circunstancias.
|
||||||
|
</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> no crea una nueva instancia de
|
||||||
|
<literal>Criteria</literal>.)
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
¡Observa que las colecciones de gatitos tenidas por las instancias de <literal>Cat</literal> devueltas
|
||||||
|
por las dos consultas previas <emphasis>no</emphasis> están prefiltradas por los criterios! Si deseas
|
||||||
|
recuperar sólo los gatitos que emparejen los criterios, debes usar <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>Recuperación dinámica de asociaciones</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes especificar la semántica de recuperación de asociaciones en tiempo de ejecución usando
|
||||||
|
<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>
|
||||||
|
Esta consulta recuperará tanto <literal>mate</literal> como <literal>kittens</literal> por
|
||||||
|
unión exterior (outer join). Ver <xref linkend="performance-fetching"/> para más información.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="querycriteria-examples">
|
||||||
|
<title>Consultas por ejemplos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La clase <literal>org.hibernate.criterion.Example</literal> te permite construir un criterio de consulta
|
||||||
|
a partir de una instancia dada.
|
||||||
|
</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>
|
||||||
|
Las propiedades de versión, los identificadores y las asociaciones son ignorados. Por defecto,
|
||||||
|
las propiedades valuadas a nulo son excluídas.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes ajustar cómo se aplica el <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>
|
||||||
|
Puedes incluso usar ejemplos para colocar criterios sobre objetos asociados.
|
||||||
|
</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>Proyecciones, agregación y agrupamiento</title>
|
||||||
|
<para>
|
||||||
|
La clase <literal>org.hibernate.criterion.Projections</literal> es una fábrica de instancias de
|
||||||
|
<literal>Projection</literal>. Aplicamos una proyección a una consulta llamando a
|
||||||
|
<literal>setProjection()</literal>.
|
||||||
|
</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>
|
||||||
|
No es necesario ningún "group by" explícito en una consulta por criterios.
|
||||||
|
Ciertos tipos de proyecciones son definidos para ser <emphasis>proyecciones agrupadas</emphasis>,
|
||||||
|
que además aparecen en la cláusula SQL <literal>group by</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puede opcionalmente asignarse un alias a una proyección, de modo que el valor proyectado pueda
|
||||||
|
ser referido en restricciones u ordenamientos. Aquí hay dos formas diferentes de hacer esto:
|
||||||
|
</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>
|
||||||
|
Los métodos <literal>alias()</literal> y <literal>as()</literal> simplemente envuelven una instancia
|
||||||
|
de proyección en otra instancia de <literal>Projection</literal> con alias. Como un atajo, puedes asignar
|
||||||
|
un alias cuando agregas la proyección a una lista de proyecciones:
|
||||||
|
</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>
|
||||||
|
Puedes también usar <literal>Property.forName()</literal> para expresar proyecciones:
|
||||||
|
</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>Consultas y subconsultas separadas</title>
|
||||||
|
<para>
|
||||||
|
La clase <literal>DetachedCriteria</literal> te deja crear una consulta fuera del ámbito de una sesión,
|
||||||
|
y entonces ejecutarla luego usando alguna <literal>Session</literal> arbitraria.
|
||||||
|
</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>
|
||||||
|
También una <literal>DetachedCriteria</literal> puede usarse para expresar una subconsulta.
|
||||||
|
Las instancias de Criterion implicando subconsultas pueden obtenerse vía <literal>Subqueries</literal> o
|
||||||
|
<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>
|
||||||
|
Incluso son posibles las subconsultas correlacionadas:
|
||||||
|
</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 allow returning arbitrary
|
||||||
|
user objects - similar to setResultClass in JDO2. General use of ResultTransformer
|
||||||
|
could also be explained. -->
|
||||||
|
|
||||||
|
<sect1 id="query-criteria-naturalid">
|
||||||
|
<title>Consultas por identificador natural</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Para la mayoría de consultas, incluyendo las consultas por criterios, el caché de consulta no es
|
||||||
|
muy eficiente, debido a que la invalidación del caché de consulta ocurre demasiado frecuentemente.
|
||||||
|
Sin embargo, hay un tipo especial de consulta donde podemos optimizar el algoritmo de invalidación
|
||||||
|
de caché: búsquedas por una clave natural constante. En algunas aplicaciones, este tipo de consulta,
|
||||||
|
ocurre frecuentemente. La API de criterios brinda especial provisión para este caso de uso.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Primero, debes mapear la clave natural de tu entidad usando
|
||||||
|
<literal><natural-id></literal>, y habilitar el uso del caché de segundo nivel.
|
||||||
|
</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>
|
||||||
|
Nota que esta funcionalidad no está pensada para uso con entidades con claves naturales
|
||||||
|
<emphasis>mutable</emphasis>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Seguido, habilita el caché de consulta de Hibernate.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Ahora, <literal>Restrictions.naturalId()</literal> nos permite hacer uso de el algoritmo de caché
|
||||||
|
más eficiente.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[session.createCriteria(User.class)
|
||||||
|
.add( Restrictions.naturalId()
|
||||||
|
.set("name", "gavin")
|
||||||
|
.set("org", "hb")
|
||||||
|
).setCacheable(true)
|
||||||
|
.uniqueResult();]]></programlisting>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
</chapter>
|
1081
reference/es/modules/query_hql.xml
Normal file
477
reference/es/modules/query_sql.xml
Normal file
@ -0,0 +1,477 @@
|
|||||||
|
<chapter id="querysql" revision="2">
|
||||||
|
<title>SQL Nativo</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes también expresar consultas en el dialecto SQL nativo de tu base de datos. Esto es útil si quieres
|
||||||
|
utilizar aspectos específicos de base de datos tal como consejos (hints) de consulta o la palabra clave
|
||||||
|
<literal>CONNECT</literal> en Oracle. Provee además una clara ruta de migración desde una aplicación
|
||||||
|
basada en SQL/JDBC directo a Hibernate.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate3 te permite especificar SQL escrito a mano (incluyendo procedimientos almacenados) para todas
|
||||||
|
las operaciones de creación, actualización, borrado y carga.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect1 id="querysql-creating">
|
||||||
|
<title>Creando una <literal>Query</literal> de SQL nativo</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Las consultas SQL se controlan por medio de la interface <literal>SQLQuery</literal>, que se obtiene
|
||||||
|
llamando a <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>
|
||||||
|
Esta consulta especificada:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
la cadena de consulta SQL, con un lugar para que Hibernate inyecte los alias de columnas
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
la entidad devuelta por la consulta, y sus alias de tablas SQL
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El método <literal>addEntity()</literal> asocia alias de tablas SQL con clases de entidad,
|
||||||
|
y determina la forma del conjunto resultado de la consulta.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El método <literal>addJoin()</literal> puede ser usado para cargar asociaciones a otras entidades y
|
||||||
|
colecciones.
|
||||||
|
</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>
|
||||||
|
Una consulta SQL nativa podría devolver un valor escalar simple o una combinación de escalares y entidades.
|
||||||
|
</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 y referencias de propiedad</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La notación <literal>{cat.*}</literal> usada arriba es un atajo para "todas las propiedades".
|
||||||
|
Alternativamente, puedes listar las columnas explícitamente, pero incluso en este caso dejamos
|
||||||
|
que Hibernate inyecte los alias de columnas SQL para cada propiedad. El lugar para un alias de columna
|
||||||
|
es sólo el nombre de propiedad cualificado por el alias de la tabla. En el siguiente ejemplo,
|
||||||
|
recuperamos <literal>Cat</literal>s de una tabla diferente (<literal>cat_log</literal>) a una
|
||||||
|
declarada en los metadatos de mapeo. Nota que podríamos incluso usar los alias de propiedad en la
|
||||||
|
cláusula where si quisieramos.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
La sintáxis <literal>{}</literal> <emphasis>no</emphasis> es requerida para consultas con nombre.
|
||||||
|
Ver <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>Nota:</emphasis> si listas cada propiedad explícitamente, ¡debes incluir todas las
|
||||||
|
propiedades de la clase <emphasis>y sus subclases</emphasis>!
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="querysql-namedqueries" revision="2">
|
||||||
|
<title>Consultas SQL con nombre</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Las consultas SQL con nombre pueden definirse en el documento de mapeo y llamadas exactamente
|
||||||
|
en la misma forma en que a una consulta HQL con nombre. En este caso, <emphasis>no</emphasis>
|
||||||
|
necesitamos llamar a <literal>addEntity()</literal>.
|
||||||
|
</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>
|
||||||
|
Los elementos <literal><return-join></literal> y <literal><load-collection></literal>
|
||||||
|
se usan para unir asociaciones y definir consultas que inicialicen colecciones, respectivamente.
|
||||||
|
</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>
|
||||||
|
Una consulta SQL con nombre puede devolver un valor escalar. Debes especificar el alias de columna y
|
||||||
|
tipo Hibernate usando el elementp <literal><return-scalar></literal>:
|
||||||
|
</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>Usando return-property para especificar explícitamente nombres de columna/alias</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Con <literal><return-property></literal> puedes decirle explícitamente a Hibernate qué
|
||||||
|
alias de columna usar, en vez de usar la sintáxis <literal>{}</literal> para dejar que Hibernate
|
||||||
|
inyecte sus propios alias.
|
||||||
|
</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><return-property></literal> también trabaja con múltiples columnas. Esto resuelve una
|
||||||
|
limitación de la sintáxis <literal>{}</literal>, la cual no puede permitir un control fino de propiedades
|
||||||
|
multi-columna.
|
||||||
|
</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>
|
||||||
|
Nota que en este ejemplo hemos usado <literal><return-property></literal> en combinación con
|
||||||
|
la sintáxis <literal>{}</literal> para inyección, permitiendo a los usuarios elejir cómo quieren
|
||||||
|
referirse a las columnas y propiedades.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Si tu mapeo tiene un discriminador debes usar <literal><return-discriminator></literal>
|
||||||
|
para especificar la columna discriminadora.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="sp_query">
|
||||||
|
<title>Usando procedimientos almacenados para consultar</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate3 introduce soporte para consultas vía procedimientos almacenados. Los procedimientos
|
||||||
|
almacenados deben devolver un conjunto resultado como el primer parámetro de salida para ser
|
||||||
|
capaces de funcionar con Hibernate. Un ejemplo de uno procedimiento almacenado en Oracle 9
|
||||||
|
o superior es así:
|
||||||
|
</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>
|
||||||
|
Para usar esta consulta en Hibernate necesitas mapearla por medio de una consulta con nombre.
|
||||||
|
</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>
|
||||||
|
Nota que los procedimientos almacenados sólo devuelven escalares y entidades.
|
||||||
|
No están soportados <literal><return-join></literal> y <literal><load-collection></literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect3 id="querysql-limits-storedprocedures">
|
||||||
|
<title>Reglas/limitaciones para usar procedimientos almacenados</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Para usar procedimientos almacenados con Hibernate los procedimientos tienen que seguir algunas reglas.
|
||||||
|
Si no siguen esas reglas no son usables por Hibernate. Si aún quisieras usar estos procedimientos
|
||||||
|
tendrías que ejecutarlos por medio de <literal>session.connection()</literal>. Las reglas son
|
||||||
|
diferentes para cada base de datos, ya que los vendedores de base de datos tienen diferentes
|
||||||
|
semánticas/sintáxis de procedimientos almacenados.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Las consultas de procedimientos almacenados no pueden ser paginadas con
|
||||||
|
<literal>setFirstResult()/setMaxResults()</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Para Oracle se aplican las siguientes reglas:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist spacing="compact">
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
El procedimiento debe devolver un conjunto resultado. Esto se hace devolviendo un
|
||||||
|
<literal>SYS_REFCURSOR</literal> en Oracle 9 o 10. En Oracle necesitas definir un
|
||||||
|
tipo <literal>REF CURSOR</literal>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
La forma recomendada es <literal>{ ? = call procName(<parameters>) }</literal> o
|
||||||
|
<literal>{ ? = call procName }</literal> (esto es más una regla de Oracle que una regla de Hibernate).
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Para Sybase o MS SQL server se aplican las siguientes reglas:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist spacing="compact">
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
El procedimiento debe devolver un conjunto resultado. Nota que ya que estos servidores pueden
|
||||||
|
y devolverán múltiples conjuntos resultados y cuentas de actualización, Hibernate iterará
|
||||||
|
los resultados y tomará el primer resultado que sea un conjunto resultado como su valor
|
||||||
|
a devolver. Todo lo demás será descartado.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Si habilitas <literal>SET NOCOUNT ON</literal> en tu procedimiento será probablemente más
|
||||||
|
eficiente, pero esto no es un requerimiento.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</sect3>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="querysql-cud">
|
||||||
|
<title>SQL personalizado para crear, actualizar y borrar</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate3 puede usar sentencias SQL personalizadas para las operaciones de
|
||||||
|
crear, actualizar y borrar. Los persistidores de clases y colecciones en Hibernate
|
||||||
|
ya contienen un conjunto de cadenas generadas en tiempo de configuración (insertsql,
|
||||||
|
deletesql, updatesql, etc.). Las etiquetas de mapeo <literal><sql-insert></literal>,
|
||||||
|
<literal><sql-delete></literal>, y <literal><sql-update></literal> sobrescriben
|
||||||
|
estas cadenas:
|
||||||
|
</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>
|
||||||
|
El SQL se ejecuta directamente en tu base de datos, de modo que eres libre de usar cualquier
|
||||||
|
dialecto que quieras. Esto reducirá, por supuesto, la portabilidad de tu mapeo si usas SQL
|
||||||
|
específico de la base de datos.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Los procedimientos almacenados son soportados si está establecido el atributo
|
||||||
|
<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>
|
||||||
|
El orden de los parámetros posicionales son actualmente vitales, ya que deben estar en la
|
||||||
|
misma secuencia en que las espera Hibernate.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes ver el orden esperado habilitando el registro de depuración para el nivel
|
||||||
|
<literal>org.hibernate.persister.entity</literal>. Con este nivel habilitado, Hibernate
|
||||||
|
imprimirá el SQL estático que se usa para crear, actualizar, borrar, etc. las entidades.
|
||||||
|
(Para ver la secuencia esperada, recuerda no incluir tu SQL personalizado en los ficheros
|
||||||
|
de mapeo ya que sobrescribirán el sql estático generado por Hibernate.)
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Los procedimientos almacenados son, en la mayoría de los casos (léase, mejor hacerlo que no hacerlo),
|
||||||
|
obligados a devolver el número de filas insertadas/actualizadas/borradas, ya que Hibernate tiene algunas
|
||||||
|
comprobaciones en tiempo de ejecución del éxito de la sentencia. Hibernate siempre registra el primer
|
||||||
|
parámetro de la sentencia como un parámetro de salida numérico para las operaciones 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 personalizado para carga</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes también declarar tu propias consultas SQL (o HQL) para cargar entidades:
|
||||||
|
</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>
|
||||||
|
Esto es sólo una declaración de consulta con nombrem como se ha discutido anteriormente.
|
||||||
|
Puedes hacer referencia a esta consulta con nombre en un mapeo de clase:
|
||||||
|
</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>
|
||||||
|
Esto incluso funciona con procedimientos almacenados.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes incluso definit una consulta para la carga de colecciones:
|
||||||
|
</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>
|
||||||
|
Podrías incluso definir un cargador de entidades que cargue una colección por
|
||||||
|
recuperación por unión (join fetching):
|
||||||
|
</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>
|
666
reference/es/modules/quickstart.xml
Normal file
@ -0,0 +1,666 @@
|
|||||||
|
<chapter id="quickstart">
|
||||||
|
<title>Comienzo rápido con Tomcat</title>
|
||||||
|
|
||||||
|
<sect1 id="quickstart-intro" revision="2">
|
||||||
|
<title>Empezando con Hibernate</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Este tutorial explica una instalación de Hibernate con el
|
||||||
|
contenedor de servlets Apache Tomcat (hemos usado la versión 4.1,
|
||||||
|
las diferencias con la 5.0 deben ser mínimas) para una aplicación
|
||||||
|
basada en web. Hibernate trabaja bien en un entorno manejado con
|
||||||
|
todos los servidores de aplicaciones J2EE importantes, o incluso en aplicaciones
|
||||||
|
Java independientes. El sistema de base de datos es sólo una cuestión
|
||||||
|
de cambiar la configuración del dialecto SQL de Hibernate y las
|
||||||
|
propiedades de conexión.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Primero, tenemos que copiar todas las bibliotecas requeridas a la instalación
|
||||||
|
de Tomcat. Usamos un contexto web separado (<literal>webapps/quickstart</literal>)
|
||||||
|
para este tutorial, de modo que tenemos que considerar tanto la ruta de búsqueda
|
||||||
|
de bibliotecas global (<literal>TOMCAT/common/lib</literal>) como también
|
||||||
|
el cargador de clases a nivel de contexto en <literal>webapps/quickstart/WEB-INF/lib</literal>
|
||||||
|
(para ficheros JAR) y <literal>webapps/quickstart/WEB-INF/classes</literal>.
|
||||||
|
Nos referiremos a ambos niveles de cargador de clases como el classpath global y el classpath
|
||||||
|
de contexto, respectivamente.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Ahora, copia las bibliotecas a los dos classpaths:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<orderedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Copia el driver JDBC para la base de datos al classpath global. Esto se
|
||||||
|
requiere para el software de pool de conexiones DBCP que se distribuye
|
||||||
|
con Tomcat. Hibernate usa conexiones JDBC para ejecutar SQL sobre la base de
|
||||||
|
datos, de modo que, o bien tienes que proveer conexiones JDBC en pool,
|
||||||
|
o bien configurar Hibernate para que use uno de los pools soportados
|
||||||
|
directamente (C3P0, Proxool). Para este tutorial, copia la biblioteca
|
||||||
|
<literal>pg74jdbc3.jar</literal> (para PostgreSQL 7.4 y JDK 1.4) al
|
||||||
|
classpath del cargador global. Si quisieras usar una base de datos diferente,
|
||||||
|
simplemente copia su apropiado driver JDBC.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Nunca copies nada más dentro de la ruta del cargador de clases global
|
||||||
|
en Tomcat, o tendrás problemas con varias herramientas, incluyendo
|
||||||
|
Log4J, commons-logging y otras. Siempre usa el classpath de contexto para
|
||||||
|
cada aplicación web, esto es, copia las bibliotecas a
|
||||||
|
<literal>WEB-INF/lib</literal> y tus propias clases y ficheros de
|
||||||
|
configuración/propiedades a <literal>WEB-INF/classes</literal>.
|
||||||
|
Ambos directorios están a nivel del classpath de contexto por defecto.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Hibernate está empaquetado como una biblioteca JAR. El fichero
|
||||||
|
<literal>hibernate3.jar</literal> debe ser copiado en el classpath de contexto
|
||||||
|
junto a las otras clases de la aplicación. Hibernate requiere algunas
|
||||||
|
bibliotecas de terceros en tiempo de ejecución; éstas vienen
|
||||||
|
incluídas con la distribución de Hibernate en el directorio
|
||||||
|
<literal>lib/</literal>. Ver <xref linkend="3rdpartylibs"/>. Copia las
|
||||||
|
bibliotecas de terceros requeridas al classpath de contexto.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</orderedlist>
|
||||||
|
|
||||||
|
<table frame="topbot" id="3rdpartylibs">
|
||||||
|
<title>
|
||||||
|
Bibliotecas de terceros de Hibernate
|
||||||
|
</title>
|
||||||
|
<tgroup cols="2" rowsep="1" colsep="1">
|
||||||
|
<colspec colname="c1" colwidth="1*"/>
|
||||||
|
<colspec colname="c2" colwidth="2*"/>
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry align="center">
|
||||||
|
Biblioteca
|
||||||
|
</entry>
|
||||||
|
<entry align="center">
|
||||||
|
Descripción
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
antlr (requerida)
|
||||||
|
</entry>
|
||||||
|
<entry>
|
||||||
|
Hibernate usa ANTLR para producir analizadores de consultas,
|
||||||
|
esta biblioteca también se necesita en tiempo de ejecución.
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
dom4j (requerida)
|
||||||
|
</entry>
|
||||||
|
<entry>
|
||||||
|
Hibernate usa dom4j para analizar ficheros de configuración
|
||||||
|
XML y ficheros de metadatos de mapeo XML.
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
CGLIB, asm (requerida)
|
||||||
|
</entry>
|
||||||
|
<entry>
|
||||||
|
Hibernate usa la biblioteca de generación de código
|
||||||
|
para aumentar las clases en tiempo de ejecución
|
||||||
|
(en combinación con reflección Java).
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
Commons Collections, Commons Logging (requeridas)
|
||||||
|
</entry>
|
||||||
|
<entry>
|
||||||
|
Hibernate usa varias bibliotecas de utilidad del proyecto
|
||||||
|
Jakarta Commons de Apache.
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
EHCache (requerida)
|
||||||
|
</entry>
|
||||||
|
<entry>
|
||||||
|
Hibernate puede usar varios provedores de caché para
|
||||||
|
el caché de segundo nivel. EHCache es el provedor de
|
||||||
|
caché por defecto si no se cambia en la configuración.
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
Log4j (opcional)
|
||||||
|
</entry>
|
||||||
|
<entry>
|
||||||
|
Hibernate usa la API de Commons Logging, que a su vez puede
|
||||||
|
usar Log4J como el mecanismo de logging subyacente. Si la
|
||||||
|
biblioteca Log4J está disponible en el directorio de
|
||||||
|
bibliotecas del contexto, Commons Logging usará Log4J
|
||||||
|
y la configuración <literal>log4j.properties</literal>
|
||||||
|
en el classpath de contexto. Un fichero de propiedades de ejemplo
|
||||||
|
para Log4J se incluye con la distribución de Hibernate.
|
||||||
|
Así que copia log4j.jar y el fichero de configuración
|
||||||
|
(de <literal>src/</literal>) a tu classpath de contexto si quieres
|
||||||
|
ver que ocurre tras escénas.
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
¿Requerida o no?
|
||||||
|
</entry>
|
||||||
|
<entry>
|
||||||
|
Echa una mirada al fichero <literal>lib/README.txt</literal> en la
|
||||||
|
distribución de Hibernate. Esta es una lista actualizada
|
||||||
|
de bibliotecas de terceros distribuídas con Hibernate.
|
||||||
|
Encontrarás listadas ahí todas las bibliotecas
|
||||||
|
requeridas y opcionales (Observa que "buildtame required" significa
|
||||||
|
aquí para la construcción de Hibernate, no de tu
|
||||||
|
aplicación).
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Ahora instalamos el pooling y modo compartido de conexiones de base de datos
|
||||||
|
tanto en Tomcat como Hibernate. Esto significa que Tomcat proveerá
|
||||||
|
conexiones JDBC en pool (usando su funcionalidad prefabricada de pooling DBCP).
|
||||||
|
Hibernate pide esas conexiones a través de JNDI. Alternativamente,
|
||||||
|
puedes dejar que Hibernate maneje el pool de conexiones. Tomcat liga su pool
|
||||||
|
de conexiones a JNDI; agregamos una declaración de recurso al fichero
|
||||||
|
de configuración principal de 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>
|
||||||
|
El contexto que configuramos en este ejemplo se llama <literal>quickstart</literal>,
|
||||||
|
su base es el directorio <literal>TOMCAT/webapp/quickstart</literal>. Para acceder
|
||||||
|
a cualquier servlet, llama a la ruta <literal>http://localhost:8080/quickstart</literal>
|
||||||
|
en tu navegador (por supuesto, agregando el nombre del servlet como se mapee en tu
|
||||||
|
<literal>web.xml</literal>). Puedes también ir más allá y crear
|
||||||
|
ahora un servlet simple que tenga un método <literal>process()</literal>
|
||||||
|
vacío.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Tomcat provee ahora conexiones a través de JNDI en
|
||||||
|
<literal>java:comp/env/jdbc/quickstart</literal>. Si tienes problemas obteniendo
|
||||||
|
el pool de conexiones en ejecución, refiérete a la documentación
|
||||||
|
de Tomcat. Si obtienes mensajes de excepción del driver JDBC, intenta instalar
|
||||||
|
primero el pool de conexiones JDBC sin Hibernate. Hay disponibles en la Web
|
||||||
|
tutoriales de Tomcat y JDBC.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Tu próximo paso es configurar Hibernate. Hibernate tiene que saber cómo
|
||||||
|
debe obtener conexiones JDBC. Usamos la configuración de Hibernate basada en XML.
|
||||||
|
El otro enfoque, usando un ficheros de propiedad, es casi equivalente pero pierde unas
|
||||||
|
pocas funcionalidades que sí permite la sintaxis XML. El fichero de configuración
|
||||||
|
XML se ubica en el classpath de contexto (<literal>WEB-INF/classes</literal>), como
|
||||||
|
<literal>hibernate.cfg.xml</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>
|
||||||
|
Desactivamos el registro (logging) de comandos SQL y decimos a Hibernate
|
||||||
|
qué dialecto SQL de base de datos se usa y dónde obtener
|
||||||
|
conexiones JDBC (declarando la dirección JNDI del pool ligado a
|
||||||
|
Tomcat). El dialecto es una configuración requerida, las bases de
|
||||||
|
datos difieren en su interpretación del "estándar" de SQL.
|
||||||
|
Hibernate cuidará de las diferencias y viene con dialectos incluídos
|
||||||
|
para todas las principales bases de datos comerciales y de código
|
||||||
|
abierto.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <literal>SessionFactory</literal> es el concepto de Hibernate
|
||||||
|
de un almacén de datos solo. Pueden usarse múltiples
|
||||||
|
bases de datos creando múltiples ficheros de configuración
|
||||||
|
XML y creando múltiples objetos <literal>Configuration</literal>
|
||||||
|
y <literal>SessionFactory</literal> en tu aplicación.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El último elemento del <literal>hibernate.cfg.xml</literal>
|
||||||
|
declara <literal>Cat.hbm.xml</literal> como el nombre de un fichero
|
||||||
|
de mapeo XML para la clase persistente <literal>Cat</literal>. Este
|
||||||
|
fichero contiene los metadatos para el mapeo de la clase POJO
|
||||||
|
<literal>Cat</literal> a una tabla (o tablas) de base de datos.
|
||||||
|
Volveremos a este fichero pronto. Escribamos primero la clase POJO
|
||||||
|
y luego declaremos los metadatos de mapeo para ella.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="quickstart-persistentclass" revision="1">
|
||||||
|
<title>Primera clase persistente</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate trabaja mejor con el modelo de programación de los
|
||||||
|
Viejos Objetos Planos de Java (POJOs, a veces llamados Ordinarios Objetos Planos de Java)
|
||||||
|
para clases persistentes. Un POJO es como un JavaBean, con las propiedades
|
||||||
|
de la clase accesible vía métodos getter y setter,
|
||||||
|
encapsulando la representación interna de la interfaz publicamente
|
||||||
|
visible (Hibernate puede también acceder a los campos directamente, si se
|
||||||
|
necesita):
|
||||||
|
</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 no está restringido en su uso de tipos de propiedad, todos
|
||||||
|
los tipos y tipos primitivos del JDK de Java (como <literal>String</literal>,
|
||||||
|
<literal>char</literal> y <literal>Date</literal>) pueden ser mapeados, incluyendo
|
||||||
|
clases del framework de colecciones de Java. Puedes mapearlos como valores,
|
||||||
|
colecciones de valores, o asociaciones a otras entidades. El <literal>id</literal>
|
||||||
|
es una propiedad especial que representa el identificador de base de datos (clave
|
||||||
|
primaria) de la clase. Es altamente recomendado para entidades como un
|
||||||
|
<literal>Cat</literal>. Hibernate puede usar identificadores sólo
|
||||||
|
internamente, pero perderíamos algo de la flexibilidad en nuestra
|
||||||
|
arquitectura de aplicación.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
No tiene que implementarse ninguna interface especial para las clases persistentes
|
||||||
|
ni tienes que subclasear de una clase persistente raíz en especial. Hibernate
|
||||||
|
tampoco requiere ningún procesamiento en tiempo de construcción,
|
||||||
|
como manipulación del byte-code. Se basa solamente en reflección de Java
|
||||||
|
y aumentación de clases en tiempo de ejecución (a través de CGLIB).
|
||||||
|
De modo que, sin ninguna dependencia de la clase POJO en Hibernate, podemos mapearla
|
||||||
|
a una tabla de base de datos.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="quickstart-mapping" revision="1">
|
||||||
|
<title>Mapeando el gato</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El fichero de mapeo <literal>Cat.hbm.xml</literal> contiene los
|
||||||
|
metadatos requeridos para el mapeo objeto/relacional. Los metadatos
|
||||||
|
incluyen la declaración de clases persistentes y el mapeo de
|
||||||
|
propiedades (a columnas y relaciones de claves foráneas a otras
|
||||||
|
entidades) a tablas de base de datos.
|
||||||
|
</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>
|
||||||
|
Cada clase persistente debe tener un atributo identificador (realmente,
|
||||||
|
sólo las clases que representen entidades, no las clases dependientes
|
||||||
|
de tipo-valor, que son mapeadas como componentes de una entidad). Esta propiedad
|
||||||
|
es usada para distinguir los objetos persistentes: Dos gatos son iguales si
|
||||||
|
<literal>catA.getId().equals(catB.getId())</literal> es verdadero. Este concepto
|
||||||
|
se llama <emphasis>identidad de base de datos (database identity)</emphasis>.
|
||||||
|
Hibernate viene empaquetado con varios generadores de identificador para diferentes
|
||||||
|
escenarios (incluyendo generadores nativos para secuencias de base de datos, tablas
|
||||||
|
de identificadores alto/bajo, e identificadores asignados por aplicación).
|
||||||
|
Usamos el generador UUID (recomendado sólo para pruebas, pues deben
|
||||||
|
preferirse las claves enteras delegadas generadas por la base de datos) y
|
||||||
|
también especificamos la columna <literal>CAT_ID</literal> de la tabla
|
||||||
|
<literal>CAT</literal> para el valor identificador generado por Hibernate
|
||||||
|
(como una clave primaria de la tabla).
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Todas las demás propiedades de <literal>Cat</literal> son mapeadas a la
|
||||||
|
misma tabla. En el caso de la propiedad <literal>name</literal>, la hemos mapeado
|
||||||
|
con una declaración explícita de columna de base de datos. Esto es
|
||||||
|
especialmente útil cuando el esquema de base de datos es generado
|
||||||
|
automáticamente (como sentencias DDL de SQL) desde la declaración
|
||||||
|
de mapeo con la herramienta <emphasis>SchemaExport</emphasis> de Hibernate.
|
||||||
|
Todas las demás propiedades son mapeadas usando la configuración
|
||||||
|
por defecto de Hibernate, que es lo que necesitas la mayoría del tiempo.
|
||||||
|
La tabla <literal>CAT</literal> en la base de datos se ve así como:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[ Columna | Tipo | Modificadores
|
||||||
|
--------+-----------------------+-----------
|
||||||
|
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>
|
||||||
|
Ahora debes crear esta tabla manualmente en tu base de datos, y luego leer el
|
||||||
|
<xref linkend="toolsetguide"/> si quieres automatizar este paso con la
|
||||||
|
herramienta <literal>hbm2ddl</literal>. Esta herramienta puede crear un
|
||||||
|
DDL SQL completo, incluyendo definición de tablas, restricciones
|
||||||
|
personalizadas de tipo de columnas, restricciones de unicidad e índices.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="quickstart-playingwithcats" revision="2">
|
||||||
|
<title>Jugando con gatos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Ahora estamos listos para comenzar la <literal>Session</literal> de Hibernate.
|
||||||
|
Es el <emphasis>manejador de persistencia</emphasis> que usamos para almacenar
|
||||||
|
y traer <literal>Cat</literal>s hacia y desde la base de datos. Pero primero,
|
||||||
|
tenemos que obtener una <literal>Session</literal> (unidad de trabajo de Hibernate)
|
||||||
|
de la <literal>SessionFactory</literal>:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[SessionFactory sessionFactory =
|
||||||
|
new Configuration().configure().buildSessionFactory();]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La llamada a <literal>configure()</literal> carga el fichero de
|
||||||
|
configuración <literal>hibernate.cfg.xml</literal> e
|
||||||
|
inicializa la instancia de <literal>Configuration</literal>.
|
||||||
|
Puedes establecer otras propiedades (e incluso cambiar los metadatos de mapeo)
|
||||||
|
accediendo a la <literal>Configuration</literal> <emphasis>antes</emphasis>
|
||||||
|
que construyas la <literal>SessionFactory</literal> (que es inmutable).
|
||||||
|
¿Dónde creamos la <literal>SessionFactory</literal> y cómo
|
||||||
|
accedemos a ella en nuestra aplicación?
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <literal>SessionFactory</literal> usualmente se construye una vez,
|
||||||
|
por ejemplo, al arrancar con un servlet <emphasis>load-on-startup</emphasis>.
|
||||||
|
Esto significa también que no debes mantenerla en una variable de instancia
|
||||||
|
en tus servlets, sino en alguna otro sitio. Además, necesitamos algún
|
||||||
|
tipo de <emphasis>Singleton</emphasis>, de modo que podamos acceder a la
|
||||||
|
<literal>SessionFactory</literal> fácilmente en el código de
|
||||||
|
aplicación. El siguiente enfoque mostrado resuelve ambos problemas:
|
||||||
|
configuración de arranque y fácil acceso a una
|
||||||
|
<literal>SessionFactory</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Implementamos una clase de ayuda <literal>HibernateUtil</literal>:
|
||||||
|
</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>
|
||||||
|
Esta clase no sólo cuida de la <literal>SessionFactory</literal>
|
||||||
|
con su inicializador static, sino que además tiene una variable
|
||||||
|
<literal>ThreadLocal</literal> que tiene la <literal>Session</literal>
|
||||||
|
para la hebra actual. Asegúrate de entender el concepto Java de una
|
||||||
|
variable local a una hebra antes de intentar usar esta ayuda. Una clase
|
||||||
|
<literal>HibernateUtil</literal> más compleja y potente puede
|
||||||
|
encontrarse en <literal>CaveatEmptor</literal>, http://caveatemptor.hibernate.org/
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <literal>SessionFactory</literal> es segura entre hebras, muchas hebras pueden
|
||||||
|
acceder a ella concurrentemente y pedirle <literal>Session</literal>s. Una
|
||||||
|
<literal>Session</literal> no es un objeto seguro entre hebras que representa
|
||||||
|
una sola unidad-de-trabajo con la base de datos. Las <literal>Session</literal>s
|
||||||
|
se abren desde una <literal>SessionFactory</literal> y son cerradas cuando
|
||||||
|
todo el trabajo está completo. Un ejemplo en el método
|
||||||
|
<literal>process()</literal> de tu servlet podría parecerse a esto
|
||||||
|
(sin manejo de excepciones):
|
||||||
|
</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>
|
||||||
|
En una <literal>Session</literal>, cada operación de base de datos
|
||||||
|
ocurre dentro de una transacción que aísla las operaciones
|
||||||
|
de base de datos (incluso operaciones de sólo lectura).
|
||||||
|
Usamos la API de <literal>Transaction</literal> de Hibernate para
|
||||||
|
abstraer de la estrategia de transacciones subyacente (en nuestro caso,
|
||||||
|
transacciones JDBC). Esto permite que nuestro código sea desplegado
|
||||||
|
con transacciones manejadas por contenedor (usando JTA) sin cambio alguno.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Observa que puedes llamar <literal>HibernateUtil.currentSession();</literal>
|
||||||
|
tantas veces como quieras, siempre obtendrás la <literal>Session</literal>
|
||||||
|
actual de esta hebra. Tienes que asegurarte que la <literal>Session</literal>
|
||||||
|
sea cerrada después que se complete tu unidad-de-trabajo, ya sea en
|
||||||
|
código de tu servlet o en un filtro de servlet antes que la respuesta HTTP
|
||||||
|
sea enviada. El bonito efecto colateral de la segunda opción es la
|
||||||
|
fácil inicialización perezosa: la <literal>Session</literal> todavía
|
||||||
|
está abierta cuando se dibuja la vista, de modo que Hibernate puede cargar
|
||||||
|
objetos no inicializados mientras navegas tu actual grafo de objetos.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate tiene varios métodos que pueden ser usados para traer
|
||||||
|
objetos desde la base de datos. La forma más flexible es usando
|
||||||
|
el Lenguaje de Consulta de Hibernate (Hibernate Query Language o HQL),
|
||||||
|
que es una extensión orientada a objetos de SQL fácil de
|
||||||
|
aprender:
|
||||||
|
</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 también ofrece una API <emphasis>consulta por criterios</emphasis>
|
||||||
|
orientada a objetos que puede ser usada para formular consultas de tipo seguro.
|
||||||
|
Por supuesto, Hibernate usa <literal>PreparedStatement</literal>s y ligado de
|
||||||
|
parámetros para toda la comunicación SQL con la base de datos.
|
||||||
|
También puedes usar la funcionalidad de consulta SQL directa de Hibernate
|
||||||
|
u obtener una conexión plana de JDBC de una <literal>Session</literal>
|
||||||
|
en casos raros.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="quickstart-summary" revision="1">
|
||||||
|
<title>Finalmente</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Rasguñamos solamente la superficie de Hibernate en este pequeño
|
||||||
|
tutorial. Por favor, observa que no incluimos ningún código
|
||||||
|
específico de servlet en nuestros ejemplos. Tienes que crear un servlet
|
||||||
|
por tí mismo e insertar el código de Hibernate como lo veas
|
||||||
|
ubicado.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Ten en mente que Hibernate, como capa de acceso a datos, está firmemente
|
||||||
|
integrado dentro de tu aplicación. Usualmente, todas las otras capas dependen
|
||||||
|
del mecanismo de persistencia. Asegúrate de entender las implicaciones
|
||||||
|
de este diseño.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Para un ejemplo de aplicación más compleja, ver
|
||||||
|
http://caveatemptor.hibernate.org/ y echa una mirada a los
|
||||||
|
otros tutoriales con links en http://www.hibernate.org/Documentation
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
</chapter>
|
1227
reference/es/modules/session_api.xml
Normal file
459
reference/es/modules/toolset_guide.xml
Normal file
@ -0,0 +1,459 @@
|
|||||||
|
<chapter id="toolsetguide" revision="2">
|
||||||
|
<title>Guía del Conjunto de Herramientas</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La ingeniería de ida y vuelta con Hibernate es posible usando un conjunto de plugins de Eclipse,
|
||||||
|
herramientas de línea de comandos, así como tareas de Ant.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Las <emphasis>Herramientas de Hibernate</emphasis> actualmente incluyen plugins para la IDE de
|
||||||
|
Eclipse así como tareas de Ant para la ingeniería inversa de bases de datos existentes:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para>
|
||||||
|
<emphasis>Editor de Mapeo:</emphasis> Un editor de ficheros de mapeo XML, que soporta autocompleción
|
||||||
|
y resaltado de sintáxis. Soporta también autocompleción semántica de nombres de clases y nombres de
|
||||||
|
campos/propiedades, haciéndolo mucho más versátil que un editor de XML normal.
|
||||||
|
</para></listitem>
|
||||||
|
<listitem><para>
|
||||||
|
<emphasis>Consola:</emphasis> La consola es una nueva vista en Eclipse. Además de la vista de
|
||||||
|
árbol de tus configuraciones de consola, tienes también una vista interactiva de tus clases
|
||||||
|
persistentes y sus relaciones. La console te permite ejecutar consultas HQL contra tu base de datos y
|
||||||
|
navegar el resultado directamente en Eclipse.
|
||||||
|
</para></listitem>
|
||||||
|
<listitem><para>
|
||||||
|
<emphasis>Asistentes de Desarrollo:</emphasis> Se proveen muchos asistentes con las herramientas
|
||||||
|
de Eclipse. Puedes usar un asistente para generar rápidamente ficheros de configuración de Hibernate
|
||||||
|
(cfg.xml), o incluso puedes haceruna ingeniería inversa completa de un esquema de base de datos existente
|
||||||
|
en ficheros de código de POJO y ficheros de mapeo de Hibernate. El asistente de ingeniería inversa soporta
|
||||||
|
plantillas personalizables.
|
||||||
|
</para></listitem>
|
||||||
|
<listitem><para>
|
||||||
|
<emphasis>Tareas de Ant:</emphasis>
|
||||||
|
</para></listitem>
|
||||||
|
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Por favor refiérete al paquete <emphasis>Herramientas de Hibernate</emphasis> y su documentación para
|
||||||
|
más información.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Sin embargo, el paquete principal de Hibernate viene incluyendo una herramienta integrada
|
||||||
|
(puede ser usada incluso "dentro" de Hibernate on-the-fly): <emphasis>SchemaExport</emphasis>
|
||||||
|
también conocido como <literal>hbm2ddl</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect1 id="toolsetguide-s1" revision="2">
|
||||||
|
<title>Generación automática de esquemas</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una utilidad de Hibernate puede generar DDL desde tus ficheros de mapeo. El esquema generado incluye
|
||||||
|
restricciones de integridad referencial (claves primarias y foráneas) para las tablas de entidades y
|
||||||
|
colecciones. Las tablas y secuencias también son creadas para los generadores de identificadores mapeados.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<emphasis>Debes</emphasis> especificar un <literal>Dialecto</literal> SQL vía la propiedad
|
||||||
|
<literal>hibernate.dialect</literal> al usar esta herramienta, ya que el DDL es altamente específico del
|
||||||
|
vendedor.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
First, customize your mapping files to improve the generated schema.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect2 id="toolsetguide-s1-2" revision="1">
|
||||||
|
<title>Personalizando el esquema</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Muchos elementos de mapeo de Hibernate definen un atributo opcional llamado <literal>length</literal>.
|
||||||
|
Con este atributo puedes establecer el tamaño de una columna. (O, para tipos de datos
|
||||||
|
numéricos/decimales, la precisión.)
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Algunas etiquetas también aceptan un atributo <literal>not-null</literal> (para generar una restricción
|
||||||
|
<literal>NOT NULL</literal> en columnas de tablas) y y un atributo <literal>unique</literal> (para generar
|
||||||
|
restricciones <literal>UNIQUE</literal> en columnas de tablas).
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Algunas etiquetas aceptan un atributo <literal>index</literal> para especificar el nombre de un índice
|
||||||
|
para esa columna. Se puede usar un atributo <literal>unique-key</literal> para agrupar columnas en una
|
||||||
|
restricción de clave de una sola unidad. Actualmente, el valor especificado del atributo
|
||||||
|
<literal>unique-key</literal> <emphasis>no</emphasis> es usado para nombrar la restricción, sólo para
|
||||||
|
agrupar las columnas en el fichero de mapeo.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Ejemplos:
|
||||||
|
</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>
|
||||||
|
Alternativamente, estos elementos aceptan tambíen un elemento hijo <literal><column></literal>.
|
||||||
|
Esto es particularmente útil para tipos multicolumnas:
|
||||||
|
</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>
|
||||||
|
El atributo <literal>sql-type</literal> permite al usuario sobrescribir el mapeo por defecto de
|
||||||
|
tipo Hibernate a tipo de datos SQL.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El atributo <literal>check</literal> te permite especificar una comprobación de restricción.
|
||||||
|
</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>Resumen</title>
|
||||||
|
<tgroup cols="3">
|
||||||
|
<colspec colwidth="1*"/>
|
||||||
|
<colspec colwidth="1*"/>
|
||||||
|
<colspec colwidth="2.5*"/>
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry>Atributo</entry>
|
||||||
|
<entry>Valores</entry>
|
||||||
|
<entry>Interpretación</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry><literal>length</literal></entry>
|
||||||
|
<entry>number</entry>
|
||||||
|
<entry>largo de columna/precisión decimal</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>not-null</literal></entry>
|
||||||
|
<entry><literal>true|false</literal></entry>
|
||||||
|
<entry>especifica que la columna debe ser no nulable</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>unique</literal></entry>
|
||||||
|
<entry><literal>true|false</literal></entry>
|
||||||
|
<entry>especifica que la columna debe tener una restricción de unicidad</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>index</literal></entry>
|
||||||
|
<entry><literal>index_name</literal></entry>
|
||||||
|
<entry>especifica el nombre de un índice (multicolumna)</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>unique-key</literal></entry>
|
||||||
|
<entry><literal>unique_key_name</literal></entry>
|
||||||
|
<entry>especifica el nombre de una restricción de unicidad multicolumna</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>foreign-key</literal></entry>
|
||||||
|
<entry><literal>foreign_key_name</literal></entry>
|
||||||
|
<entry>
|
||||||
|
especifica el nombre de la restricción de clave foránea generada por una
|
||||||
|
asociación, úsalo en los elementos de mapeo <one-to-one>, <many-to-one>,
|
||||||
|
<key>, y <many-to-many>. Nota que los lados
|
||||||
|
<literal>inverse="true"</literal> no serán considerados por
|
||||||
|
<literal>SchemaExport</literal>.
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>sql-type</literal></entry>
|
||||||
|
<entry><literal>column_type</literal></entry>
|
||||||
|
<entry>
|
||||||
|
sobrescribe el tipo de columna por defecto (sólo atributo del elemento
|
||||||
|
<literal><column></literal>)
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>check</literal></entry>
|
||||||
|
<entry>expresión SQL</entry>
|
||||||
|
<entry>
|
||||||
|
crea una restricción de comprobación SQL en columna o tabla
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El elemento <literal><comment></literal> te permite especificar un comentario para el esquema
|
||||||
|
generado.
|
||||||
|
</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>
|
||||||
|
Esto resulta en una sentencia <literal>comment on table</literal> o <literal>comment on column</literal>
|
||||||
|
en el DDL generado (donde esté soportado).
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="toolsetguide-s1-3">
|
||||||
|
<title>Ejecutando la herramienta</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La herramienta <literal>SchemaExport</literal> escribe un guión DDL a la salida estándar y/o
|
||||||
|
ejecuta las sentencias DDL.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<literal>java -cp </literal><emphasis>classpaths_de_hibernate</emphasis>
|
||||||
|
<literal>org.hibernate.tool.hbm2ddl.SchemaExport</literal> <emphasis>opciones ficheros_de_mapeo</emphasis>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<table frame="topbot">
|
||||||
|
<title>Opciones de Línea de Comandos de <literal>SchemaExport</literal></title>
|
||||||
|
<tgroup cols="2">
|
||||||
|
<colspec colwidth="1.5*"/>
|
||||||
|
<colspec colwidth="2*"/>
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry>Opción</entry>
|
||||||
|
<entry>Descripción</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry><literal>--quiet</literal></entry>
|
||||||
|
<entry>no enviar a salida estándar el guión</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>--drop</literal></entry>
|
||||||
|
<entry>sólo desechar las tablas</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>--text</literal></entry>
|
||||||
|
<entry>no exportar a la base de datos</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>--output=my_schema.ddl</literal></entry>
|
||||||
|
<entry>enviar la salida del guión ddl a un fichero</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>--config=hibernate.cfg.xml</literal></entry>
|
||||||
|
<entry>lee la configuración de Hibernate de un fichero XML</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>--properties=hibernate.properties</literal></entry>
|
||||||
|
<entry>lee las propiedades de base de datos de un fichero</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>--format</literal></entry>
|
||||||
|
<entry>formatea agradablemente el SQL generado en el guión</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>--delimiter=x</literal></entry>
|
||||||
|
<entry>establece un delimitador de fin de línea para el guión</entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes incluso encajar <literal>SchemaExport</literal> en tu aplicación:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[Configuration cfg = ....;
|
||||||
|
new SchemaExport(cfg).create(false, true);]]></programlisting>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="toolsetguide-s1-4">
|
||||||
|
<title>Propiedades</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Las propiedades de base de datos pueden especificarse
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist spacing="compact">
|
||||||
|
<listitem>
|
||||||
|
<para>como propiedades de sistema con <literal>-D</literal><emphasis><property></emphasis></para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>en <literal>hibernate.properties</literal></para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>en un fichero de propiedades mencionado con <literal>--properties</literal></para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Las propiedades necesarias son:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<table frame="topbot">
|
||||||
|
<title>Propiedades de Conexión de SchemaExport</title>
|
||||||
|
<tgroup cols="2">
|
||||||
|
<colspec colwidth="1.5*"/>
|
||||||
|
<colspec colwidth="2*"/>
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry>Nombre de Propiedad</entry>
|
||||||
|
<entry>Descripción</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry><literal>hibernate.connection.driver_class</literal></entry>
|
||||||
|
<entry>clase del driver jdbc</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>hibernate.connection.url</literal></entry>
|
||||||
|
<entry>url de jdbc</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>hibernate.connection.username</literal></entry>
|
||||||
|
<entry>usuario de base de datos</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>hibernate.connection.password</literal></entry>
|
||||||
|
<entry>contraseña de usuario</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>hibernate.dialect</literal></entry>
|
||||||
|
<entry>dialecto</entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="toolsetguide-s1-5">
|
||||||
|
<title>Usando Ant</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes llamar a <literal>SchemaExport</literal> desde tu guión de construcción de Ant:
|
||||||
|
</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>Actualizaciones incrementales de esquema</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La herramienta <literal>SchemaUpdate</literal> actualizará un esquema existente con cambios
|
||||||
|
"incrementales". Nota que <literal>SchemaUpdate</literal> depende fuertemente de la API de metadatos
|
||||||
|
de JDBC, de modo que no funcionará con todos los drivers JDBC.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<literal>java -cp </literal><emphasis>classpaths_de_hibernate</emphasis>
|
||||||
|
<literal>org.hibernate.tool.hbm2ddl.SchemaUpdate</literal> <emphasis>opciones ficheros_de_mapeo</emphasis>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<table frame="topbot">
|
||||||
|
<title>Opciones de Línea de Comandos de <literal>SchemaUpdate</literal></title>
|
||||||
|
<tgroup cols="2">
|
||||||
|
<colspec colwidth="1.5*"/>
|
||||||
|
<colspec colwidth="2*"/>
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry>Opción</entry>
|
||||||
|
<entry>Descripción</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry><literal>--quiet</literal></entry>
|
||||||
|
<entry>no enviar a salida estándar el guión</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal>--properties=hibernate.properties</literal></entry>
|
||||||
|
<entry>lee las propiedades de base de datos de un fichero</entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes encajar <literal>SchemaUpdate</literal> en tu aplicación:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[Configuration cfg = ....;
|
||||||
|
new SchemaUpdate(cfg).execute(false);]]></programlisting>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="toolsetguide-s1-7">
|
||||||
|
<title>Usando Ant para actualizaciones incrementales de esquema</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes llamar a <literal>SchemaUpdate</literal> desde el guión de Ant:
|
||||||
|
</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>
|
||||||
|
|
925
reference/es/modules/transactions.xml
Normal file
@ -0,0 +1,925 @@
|
|||||||
|
<chapter id="transactions" revision="1">
|
||||||
|
<title>Transacciones y Concurrencia</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El punto más importante sobre Hibernate y el control de concurrencia es que muy fácil
|
||||||
|
de comprender. Hibernate usa directamente conexiones JDBC y recursos JTA sin agregar
|
||||||
|
ningún comportamiento de bloqueo adicional. Recomendamos altamente que gastes algo de
|
||||||
|
tiempo con la especificación de JDBC, ANSI, y el aislamiento de transacciones de tu sistema
|
||||||
|
de gestión de base de datos. Hibernate sólo añade versionado automático pero no bloquea
|
||||||
|
objetos en memoria ni cambia el nivel de aislamiento de tus transacciones de base de datos.
|
||||||
|
Básicamente, usa Hibernate como usarías JDBC directo (o JTA/CMT) con tus recursos de base de
|
||||||
|
datos.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Sin embargo, además del versionado automático, Hibernate ofrece una API (menor) para
|
||||||
|
bloqueo pesimista de filas, usando la sintáxis <literal>SELECT FOR UPDATE</literal>.
|
||||||
|
Esta API se discute más adelante en este capítulo:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Comenzamos la discusión del control de concurrencia en Hibernate con la granularidad
|
||||||
|
de <literal>Configuration</literal>, <literal>SessionFactory</literal>, y
|
||||||
|
<literal>Session</literal>, así como la base de datos y las transacciones de aplicación
|
||||||
|
largas.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect1 id="transactions-basics">
|
||||||
|
<title>Ámbitos de sesión y de transacción</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <literal>SessionFactory</literal> es un objeto seguro entre hebras caro-de-crear
|
||||||
|
pensado para ser compartido por todas las hebras de la aplicación. Es creado una sola vez,
|
||||||
|
usualmente en el arranque de la aplicación, a partir de una instancia de <literal>Configuration</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una <literal>Session</literal> es un objeto barato, inseguro entre hebras que debe
|
||||||
|
ser usado una sola vez, para un solo proceso de negocio, una sola unidad de trabajo,
|
||||||
|
y luego descartado. Una <literal>Session</literal> no obtendrá una <literal>Connection</literal>
|
||||||
|
JDBC (o un <literal>Datasource</literal>) a menos que sea necesario, de modo que puedas
|
||||||
|
abrir y cerrar seguramente una <literal>Session</literal> incluso si no estás seguro
|
||||||
|
que se necesitará acceso a los datos para servir una petición en particular. (Esto se
|
||||||
|
vuelve importante en cuanto estés implementando alguno de los siguientes patrones usando
|
||||||
|
intercepción de peticiones).
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Para completar este cuadro tienes que pensar también en las transacciones de base de
|
||||||
|
datos. Una transacción de base de datos tiene que ser tan corta como sea posible, para
|
||||||
|
reducir la contención de bloqueos en la base de datos. Las transacciones largas de base de
|
||||||
|
datos prevendrán a tu aplicación de escalar a una carga altamente concurrente.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
¿Qué es el ámbito de una unidad de trabajo? ¿Puede una sola <literal>Session</literal> de Hibernate
|
||||||
|
extenderse a través de varias transacciones de base de datos o es ésta una relación uno-a-uno
|
||||||
|
de ámbitos? ¿Cuándo debes abrir y cerrar una <literal>Session</literal> y cómo demarcas los
|
||||||
|
límites de la transacción de base de datos?
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect2 id="transactions-basics-uow">
|
||||||
|
<title>Unidad de trabajo</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Primero, no uses el antipatrón <emphasis>sesión-por-operación</emphasis>, esto es,
|
||||||
|
¡no abras y cierres una <literal>Session</literal> para cada simple llamada a la base
|
||||||
|
de datos en una sola hebra! Por supuesto, lo mismo es verdad para transacciones de base de
|
||||||
|
datos. Las llamadas a base de datos en una aplicación se hacen usando una secuencia
|
||||||
|
prevista, que están agrupadas dentro de unidades de trabajo atómicas. (Nota que esto
|
||||||
|
también significa que el auto-commit después de cada una de las sentencias SQL es inútil
|
||||||
|
en una aplicación, este modo está pensado para trabajo ad-hoc de consola SQL.
|
||||||
|
Hibernate deshabilita, o espera que el servidor de aplicaciones lo haga, el modo
|
||||||
|
auto-commit inmediatamente.)
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El patrón más común en una aplicación mutiusuario cliente/servidor es
|
||||||
|
<emphasis>sesión-por-petición</emphasis>. En este modelo, una petición del cliente
|
||||||
|
es enviada al servidor (en donde se ejecuta la capa de persistencia de Hibernate),
|
||||||
|
se abre una nueva <literal>Session</literal> de Hibernate, y todas las operaciones
|
||||||
|
de base de datos se ejecutan en esta unidad de trabajo. Una vez completado el trabajo
|
||||||
|
(y se ha preparado la respuesta para el cliente) la sesión es limpiada y cerrada.
|
||||||
|
Podrías usar una sola transacción de base de datos para servir a petición del cliente,
|
||||||
|
comenzándola y comprometiéndola cuando abres y cierras la <literal>Session</literal>.
|
||||||
|
La relación entre las dos es uno-a-uno y este modelo es a la medida perfecta de muchas
|
||||||
|
aplicaciones.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El desafío yace en la implementación: no sólo tienen que comenzarse y terminarse correctamente
|
||||||
|
la <literal>Session</literal> y la transacción, sino que además tienen que estar accesibles
|
||||||
|
para las operaciones de acceso a datos. La demarcación de una unidad de trabajo se implementa
|
||||||
|
idealmente usando un interceptor que se ejecuta cuando una petición llama al servidor y anter que
|
||||||
|
la respuesta sea enviada (es decir, un <literal>ServletFilter</literal>). Recomendamos ligar la
|
||||||
|
<literal>Session</literal> a la hebra que atiende la petición, usando una variable
|
||||||
|
<literal>ThreadLocal</literal>. Esto permite un fácil acceso (como acceder a una variable static)
|
||||||
|
en tódo el código que se ejecuta en esta hebra. Dependiendo del mecanismo de demarcación de
|
||||||
|
transacciones de base de datos que elijas, podrías mantener también el contexto de la transacción
|
||||||
|
en una variable <literal>ThreadLocal</literal>. Los patrones de implementación para esto son
|
||||||
|
conocidos como <emphasis>Sesión Local de Hebra (ThreadLocal Session)</emphasis> y
|
||||||
|
<emphasis>Sesión Abierta en Vista (Open Session in View)</emphasis>. Puedes extender fácilmente
|
||||||
|
la clase de ayuda <literal>HibernateUtil</literal> mostrada anteriormente para encontrar
|
||||||
|
una forma de implementar un interceptor e instalarlo en tu entorno. Ver el sitio web de Hibernate
|
||||||
|
para consejos y ejemplos.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="transactions-basics-apptx">
|
||||||
|
<title>Transacciones de aplicación</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El patrón sesión-por-petición no es el único concepto útil que puedes usar para diseñar unidades
|
||||||
|
de trabajo. Muchos procesos de negocio requiere una serie completa de interacciones con el
|
||||||
|
usuario intercaladas con accesos a base de datos. En aplicaciones web y de empresa no es aceptable
|
||||||
|
que una transacción de base de datos se extienda a través de la interacción de un usuario.
|
||||||
|
Considera el siguiente ejemplo:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Se abre la primera pantalla de un diálogo, los datos vistos por el usuario han sido
|
||||||
|
cargados en una <literal>Session</literal> y transacción de base de datos particular.
|
||||||
|
El usuario es libre de modificar los objetos.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
El usuario hace click en "Salvar" después de 5 minutos y espera que sus modificaciones
|
||||||
|
sean hechas persistentes. También espera que él sea la única persona editando esta
|
||||||
|
información y que no puede ocurrir ninguna modificación en conflicto.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Llamamos a esto unidad de trabajo, desde el punto de vista del usuario, una larga
|
||||||
|
<emphasis>transacción de aplicación</emphasis> ejecutándose. Hay muchas formas en
|
||||||
|
que puedes implementar esto en tu aplicación.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una primera implementación ingenua podría mantener abierta la <literal>Session</literal>
|
||||||
|
y la transacción de base de datos durante el tiempo de pensar del usuario, con bloqueos
|
||||||
|
tomados en la base de datos para prevenir la modificación concurrente, y para garantizar
|
||||||
|
aislamiento y atomicidad. Esto es, por supuesto, un antipatrón, ya que la contención de
|
||||||
|
bloqueo no permitiría a la aplicación escalar con el número de usuarios concurrentes.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Claramente, tenemos que usar muchas transacciones de base de datos para implementar la transacción
|
||||||
|
de aplicación. En este caso, mantener el aislamiento de los procesos de negocio se vuelve una
|
||||||
|
responsabilidad parcial de la capa de aplicación. Una sola transacción de aplicación usualmente
|
||||||
|
abarca varias transacciones de base de datos. Será atómica si sólo una de estas transacciones de
|
||||||
|
base de datos (la última) almacena los datos actualizados, todas las otras simplemente leen datos
|
||||||
|
(por ejemplo, en un diálogo estilo-asistente abarcando muchos ciclos petición/respuesta).
|
||||||
|
Esto es más fácil de implementar de lo que suena, especialmente si usas las funcionalidades de
|
||||||
|
Hibernate:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<emphasis>Versionado Automático</emphasis> - Hibernate puede llevar un control automático de
|
||||||
|
concurrencia optimista por ti, puede detectar automáticamente si una modificación concurrente
|
||||||
|
ha ocurrido durante el tiempo de pensar del usuario.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<emphasis>Objetos Separados</emphasis> - Si decides usar el ya discutido patrón
|
||||||
|
de <emphasis>sesión-por-petición</emphasis>, todas las instancias cargadas estarán
|
||||||
|
en estado separado durante el tiempo de pensar del usuario. Hibernate te permite
|
||||||
|
volver a unir los objetos y hacer persistentes las modificaciones. El patrón se
|
||||||
|
llama <emphasis>sesión-por-petición-con-objetos-separados</emphasis>. Se usa
|
||||||
|
versionado automático para aislar las modificaciones concurrentes.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<emphasis>Sesión Larga</emphasis> - La <literal>Session</literal> de Hibernate puede ser
|
||||||
|
desconectada de la conexión JDBC subyacente después que se haya sido comprometida la
|
||||||
|
transacción de base de datos, y reconectada cuando ocurra una nueva petición del cliente.
|
||||||
|
Este patrón es conocido como <emphasis>sesión-por-transacción-de-aplicación</emphasis>
|
||||||
|
y hace la re-unión innecesaria. Para aislar las modificaciones concurrentes se usa el
|
||||||
|
versionado automático.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Tanto <emphasis>sesión-por-petición-con-objetos-separados</emphasis> como
|
||||||
|
<emphasis>sesión-por-transacción-de-aplicación</emphasis>, ambas tienen
|
||||||
|
ventajas y desventajas, las discutimos más adelante en este capítulo en el contexto
|
||||||
|
del control optimista de concurrencia.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="transactions-basics-identity">
|
||||||
|
<title>Considerando la identidad del objeto</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una aplicación puede acceder concurrentemente a el mismo estado persistente en dos
|
||||||
|
<literal>Session</literal>s diferentes. Sin embargo, una instancia de una clase
|
||||||
|
persistente nunca se comparte entre dos instancias de <literal>Session</literal>.
|
||||||
|
Por lo tanto existen dos nociones diferentes de identidad:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<variablelist spacing="compact">
|
||||||
|
<varlistentry>
|
||||||
|
<term>Identidad de Base de Datos</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>foo.getId().equals( bar.getId() )</literal>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Identidad JVM</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>foo==bar</literal>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Entonces para objetos unidos a una <literal>Session</literal> <emphasis>en particular</emphasis>
|
||||||
|
(es decir en el ámbito de una <literal>Session</literal>) las dos nociones son equivalentes, y
|
||||||
|
la identidad JVM para la identidad de base de datos está garantizada por Hibernate. Sin embargo,
|
||||||
|
mientras la aplicación acceda concurrentemente al "mismo" (identidad persistente) objeto de negocio
|
||||||
|
en dos sesiones diferentes, las dos instancias serán realmente "diferentes" (identidad JVM).
|
||||||
|
Los conflictos se resuelven (con versionado automático) en tiempo de limpieza (flush) usando un
|
||||||
|
enfoque optimista.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Este enfoque deja que Hibernate y la base de datos se preocupen sobre la concurrencia. Además
|
||||||
|
provee la mejor escalabilidad, ya que garantizando la identidad un unidades de trabajo monohebra
|
||||||
|
no se necesitan bloqueos caros u otros medios de sincronización. La aplicación nunca necesita
|
||||||
|
sincronizar sobre ningún objeto de negocio, siempre que se apegue a una sola hebra por
|
||||||
|
<literal>Session</literal>. Dentro de una <literal>Session</literal> la aplicación puede usar
|
||||||
|
con seguridad <literal>==</literal> para comparar objetos.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Sin embargo, una aplicación que usa <literal>==</literal> fuera de una <literal>Session</literal>,
|
||||||
|
podría ver resultados inesperados. Esto podría ocurrir incluso en sitios algo inesperados,
|
||||||
|
por ejemplo, si pones dos instancias separadas dentro del mismo <literal>Set</literal>.
|
||||||
|
Ambas podrían tener la misma identidad de base de datos (es decir, representar la misma fila),
|
||||||
|
pero la identidad JVM, por definición, no está garantizada para las instancias en estado separado.
|
||||||
|
El desarrollador tiene que sobrescribir los métodos <literal>equals()</literal> y
|
||||||
|
<literal>hashCode()</literal> en las clases persistentes e implementar su propia noción de igualdad
|
||||||
|
de objetos. Hay una advertencia: Nunca uses el identificador de base de datos para implementar
|
||||||
|
la igualdad, usa una clave de negocio, una combinación de atributos únicos, usualmente inmutables.
|
||||||
|
El identificador de base de datos cambiará si un objeto transitorio es hecho persistente.
|
||||||
|
Si la instancia transitoria (usualmente junta a instancias separadas) es mantenida en un
|
||||||
|
<literal>Set</literal>, cambiar el código hash rompe el contrato del <literal>Set</literal>.
|
||||||
|
Los atributos para las claves de negocio no tienen que ser tan estables como las claves primarias
|
||||||
|
de base de datos, sólo tienes que garantizar estabilidad en tanto los objetos estén en el mismo
|
||||||
|
<literal>Set</literal>. Mira el sitio web de Hibernate para una discusión más cuidadosa de este
|
||||||
|
tema. Nota también que éste no es un tema de Hibernate, sino simplemente cómo la identidad y la igualdad
|
||||||
|
de los objetos Java tiene que ser implementada.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="transactions-basics-issues">
|
||||||
|
<title>Temas comunes</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Nunca uses los antipatrones <emphasis>sesión-por-sesión-de-usuario</emphasis> o
|
||||||
|
<emphasis>sesión-por-aplicación</emphasis> (por supuesto, hay raras excepciones a esta
|
||||||
|
regla). Nota que algunis de los siguientes temas podrían también aparecer con los patrones
|
||||||
|
recomendados. Asegúrate que entiendes las implicaciones antes de tomar una decisión de
|
||||||
|
diseño:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Una <literal>Session</literal> no es segura entre hebras. Las cosas que se suponen
|
||||||
|
que funcionan concurrentemente, como peticiones HTTP, beans de sesión, o workers de
|
||||||
|
Swing, provocarán condiciones de competencia si una instancia de <literal>Session</literal>
|
||||||
|
fuese compartida. Si guardas tu <literal>Session</literal> de Hibernate en tu
|
||||||
|
<literal>HttpSession</literal> (discutido más adelante), debes considerar sincronizar
|
||||||
|
el acceso a tu sesión HTTP. De otro modo, un usuario que hace click lo suficientemente
|
||||||
|
rápido puede llegar a usar la misma <literal>Session</literal> en dos hebras ejecutándose
|
||||||
|
concurrentemente.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Una excepción lanzada por Hibernate significa que tienes que deshacer (rollback) tu
|
||||||
|
transacción de base de datos y cerrar la <literal>Session</literal> inmediatamente
|
||||||
|
(discutido en más detalle luego). Si tu <literal>Session</literal> está ligada a la
|
||||||
|
aplicación, tienes que parar la aplicación. Deshacer (rollback) la transacción de base
|
||||||
|
de datos no pone a tus objetos de vuelta al estado en que estaban al comienzo de la
|
||||||
|
transacción. Esto significa que el estado de la base de datos y los objetos de negocio
|
||||||
|
quedan fuera de sincronía. Usualmente esto no es un problema, pues las excepciones no
|
||||||
|
son recuperables y tienes que volver a comenzar después del rollback de todos modos.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
La <literal>Session</literal> pone en caché todo objeto que esté en estado persistente
|
||||||
|
(vigilado y chequeado por estado sucio por Hibernate). Esto significa que crece sin
|
||||||
|
fin hasta que obtienes una OutOfMemoryException, si la mantienes abierta por un largo
|
||||||
|
tiempo o simplemente cargas demasiados datos. Una solución para esto es llamar a
|
||||||
|
<literal>clear()</literal> y <literal>evict()</literal> para gestionar el caché de la
|
||||||
|
<literal>Session</literal>, pero probalemente debas considerar un procedimiento almacenado
|
||||||
|
si necesitas operaciones de datos masivas. Se muestran algunas soluciones en
|
||||||
|
<xref linkend="batch"/>. Mantener una <literal>Session</literal> abierta por la duración
|
||||||
|
de una sesión de usuario significa también una alta probabilidad de datos añejos.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="transactions-demarcation">
|
||||||
|
<title>Demarcación de la transacción de base de datos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Los límites de las transacciones de base de datos (o sistema) son siempre necesarios. Ninguna comunicación
|
||||||
|
con la base de datos puede darse fuera de una transacción de base de datos (esto parece confundir muchos
|
||||||
|
desarrolladores acostumbrados al modo auto-commit). Siempre usa límites de transacción claros, incluso
|
||||||
|
para las operaciones de sólo lectura. Dependiendo del nivel de aislamiento y las capacidades de base de
|
||||||
|
datos, esto podría o no ser requerido, pero no hay un merma si siempre demarcas explícitamente
|
||||||
|
las transacciones.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una aplicación Hibernate puede ejecutarse en entornos no manejados (es decir, como independiente,
|
||||||
|
Web simple, o aplicaciones Swing) y entornos manejados J2EE. En un entorno no manejado, Hibernate es
|
||||||
|
usualmente responsable de su propio pool de conexiones de base de datos. El desarrollador de aplicaciones
|
||||||
|
tiene que establecer manualmente los límites de transacción, en otras palabras, hacer begin, commit, o
|
||||||
|
rollback las transacciones de base de datos por sí mismo. Un entorno manejado usualmente provee transacciones
|
||||||
|
gestionadas por contenedor, con el ensamble de transacción definido declarativamente en descriptores de
|
||||||
|
despliegue de beans de sesión EJB, por ejemplo. La demarcación programática de transacciones no es más
|
||||||
|
necesario, incluso limpiar (flush) la <literal>Session</literal> es hecho automáticamente.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Sin embargo, frecuentemente es deseable mantener portable tu capa de persistencia. Hibernate ofrece
|
||||||
|
una API de envoltura llamada <literal>Transaction</literal> que se traduce al sistema de transacciones
|
||||||
|
nativo de tu entorno de despliegue. Esta API es realmente opcional, pero recomendamos fuertemente su uso
|
||||||
|
salvo que estés en un bean de sesión CMT.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Usualmente, finalizar una <literal>Session</literal> implica cuatro fases distintas:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist spacing="compact">
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
limpiar (flush) la sesión
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
comprometer la transacción
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
cerrar la sesión
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
manejar excepciones
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Limpiar la sesión ha sido discutido anteriormente, tendremos ahora una mirada más de cerca
|
||||||
|
a la demarcación de transacciones y manejo de excepciones en sendos entornos manejado y no manejados.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
|
||||||
|
<sect2 id="transactions-demarcation-nonmanaged">
|
||||||
|
<title>Entorno no manejado</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Si una capa de persistencia Hibernate se ejecuta en un entorno no manejado, las conexiones
|
||||||
|
de base de datos son manejadas usualmente por el mecanismo de pooling de Hibernate. El idioma
|
||||||
|
manejo de sesión/transacción se ve así:
|
||||||
|
</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>
|
||||||
|
No tienes que limpiar con <literal>flush()</literal> la <literal>Session</literal> explícitamente -
|
||||||
|
la llamada a <literal>commit()</literal> automáticamente dispara la sincronización.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una llamada a <literal>close()</literal> marca el fin de una sesión. La principal implicación
|
||||||
|
de <literal>close()</literal> es que la conexión JDBC será abandonada por la sesión.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Este código Java es portable y se ejecuta tanto en entornos no manejados como en entornos JTA.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Muy probablemente nunca veas este idioma en código de negocio en una aplicación normal;
|
||||||
|
las excepciones fatales (sistema) deben siempre ser capturadas en la "cima". En otras palabras,
|
||||||
|
el código que ejecuta las llamadas de Hibernate (en la capa de persistencia) y el código que
|
||||||
|
maneja <literal>RuntimeException</literal> (y usualmente sólo puede limpiar y salir) están en
|
||||||
|
capas diferentes. Esto puede ser un desafío de diseñarlo tú mismo y debes usar los servicios
|
||||||
|
de contenedor J2EE/EJB en cuanto estuviesen disponibles. El manejo de excepciones se dicute
|
||||||
|
más adelante en este capítulo.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Nota que debes seleccionar <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>
|
||||||
|
(que es el por defecto).
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="transactions-demarcation-jta">
|
||||||
|
<title>Usando JTA</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Si tu capa de persistencia se ejecuta en un servidor de aplicaciones (por ejemplo, detrás
|
||||||
|
de beans de sesión EJB), cada conexión de datasource obtenida por Hibernate será parte
|
||||||
|
automáticamente de la transacción JTA global. Hibernate ofrece dos estrategias para esta
|
||||||
|
integración.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Si usas transacciones gestionadas-por-bean (BMT) Hibernate le dirá al servidor de aplicaciones
|
||||||
|
que comience y finalice una transacción BMT si usas la API de <literal>Transaction</literal>.
|
||||||
|
De modo que, el código de gestión de la transacción es idéntico al de un entorno no manejado.
|
||||||
|
</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>
|
||||||
|
Con CMT, la demarcación de la transacción se hace en descriptores de despliegue de beans de sesión,
|
||||||
|
no programáticamente. Si no quieres limpiar (flush) y cerrar manualmente la <literal>Session</literal>
|
||||||
|
por ti mismo, solamente establece <literal>hibernate.transaction.flush_before_completion</literal> a
|
||||||
|
<literal>true</literal>, <literal>hibernate.connection.release_mode</literal> a
|
||||||
|
<literal>after_statement</literal> o <literal>auto</literal> y
|
||||||
|
<literal>hibernate.transaction.auto_close_session</literal> a <literal>true</literal>. Hibernate
|
||||||
|
limpiará y cerrará entonces automáticamente la <literal>Session</literal> para ti. Lo único que resta
|
||||||
|
es deshacer (rollback) la transacción cuando ocurra una excepción. Afortunadamente, en un bean CMT,
|
||||||
|
incluso esto ocurre automáticamente, ya que una <literal>RuntimeException</literal> no manejada
|
||||||
|
disparada por un método de un bean de sesión le dice al contenedor que ponga a deshacer la transacción
|
||||||
|
global. <emphasis>Esto significa que, en CMT, no necesitas usar en absoluto la API de
|
||||||
|
<literal>Transaction</literal> de Hibernate.</emphasis>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Nota que debes elegir <literal>org.hibernate.transaction.JTATransactionFactory</literal> en un
|
||||||
|
bean de sesión BMT, y <literal>org.hibernate.transaction.CMTTransactionFactory</literal> en un
|
||||||
|
bean de sesión CMT, cuando configures la fábrica de transacciones de Hibernate. Recuerda además
|
||||||
|
establecer <literal>org.hibernate.transaction.manager_lookup_class</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Si trabajas en un entorno CMT, y usas limpieza (flushing) y cierre automáticos de la sesión,
|
||||||
|
podrías querer también usar la misma sesión en diferentes partes de tu código. Típicamente,
|
||||||
|
en un entorno no manejado, usarías una variable <literal>ThreadLocal</literal> para tener la sesión,
|
||||||
|
pero una sola petición de EJB puede ejecutarse en diferentes hebras (por ejemplo, un bean de sesión
|
||||||
|
llamando a otro bean de sesión). Si no quieres molestarte en pasar tu <literal>Session</literal>
|
||||||
|
por alrededor, la <literal>SessionFactory</literal> provee el método
|
||||||
|
<literal>getCurrentSession()</literal>, que devuelve una sesión que está pegada al contexto de
|
||||||
|
transacción JTA. ¡Esta es la forma más fácil de integrar Hibernate en una aplicación!
|
||||||
|
La sesión "actual" siempre tiene habilitados limpieza, cierre y liberación de conexión automáticos
|
||||||
|
(sin importar la configuración de las propiedades anteriores). Nuestra idioma de gestión de
|
||||||
|
sesión/transacción se reduce a:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[// CMT idiom
|
||||||
|
Session sess = factory.getCurrentSession();
|
||||||
|
|
||||||
|
// do some work
|
||||||
|
...
|
||||||
|
|
||||||
|
]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
En otras palabras, todo lo que tienes que hacer en un entorno manejado, es llamar a
|
||||||
|
<literal>SessionFactory.getCurrentSession()</literal>, hacer tu trabajo de acceso a datos,
|
||||||
|
y dejar el resto al contenedor. Los límites de transacción se establecen declarativamente
|
||||||
|
en los descriptores de despliegue de tu bean de sesión. El ciclo de vida de la sesión es
|
||||||
|
manejado completamente por Hibernate.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Existe una advertencia al uso del modo de liberación de conexión <literal>after_statement</literal>.
|
||||||
|
Debido a una limitación tonta de la especificación de JTA, no es posible para Hibernate
|
||||||
|
limpiar automáticamente ningún <literal>ScrollableResults</literal> no cerrado ni
|
||||||
|
instancias de <literal>Iterator</literal> devueltas por <literal>scroll()</literal> o
|
||||||
|
<literal>iterate()</literal>. <emphasis>Debes</emphasis> liberar el cursor de base de datos
|
||||||
|
subyacente llamando a <literal>ScrollableResults.close()</literal> o
|
||||||
|
<literal>Hibernate.close(Iterator)</literal> explícitamente desde un bloque <literal>finally</literal>.
|
||||||
|
(Por supuesto, la mayoría de las aplicaciones pueden evitarlo fácilmente no usando en absoluto ningún
|
||||||
|
<literal>scroll()</literal> o <literal>iterate()</literal> desde el código CMT.)
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="transactions-demarcation-exceptions">
|
||||||
|
<title>Manejo de excepciones</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Si la <literal>Session</literal> lanza una excepción (incluyendo cualquier
|
||||||
|
<literal>SQLException</literal>), debes inmediatamente deshacer (rollback) la
|
||||||
|
transacción de base de datos, llamar a <literal>Session.close()</literal> y
|
||||||
|
descartar la instancia de <literal>Session</literal>. Ciertos métodos de
|
||||||
|
<literal>Session</literal> <emphasis>no</emphasis> dejarán la sesión en un
|
||||||
|
estado consistente. Ninguna excepción lanzada por Hibernate puede ser tratada
|
||||||
|
como recuperable. Asegúrate que la <literal>Session</literal> sea cerrada llamando
|
||||||
|
a <literal>close()</literal> en un bloque <literal>finally</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La <literal>HibernateException</literal>, que envuelve la mayoría de los errores que
|
||||||
|
pueden ocurrir en la capa de persistencia de Hibernate, en una excepción no chequeada
|
||||||
|
(no lo era en versiones anteriores de Hibernate). En nuestra opinión, no debemos forzar
|
||||||
|
al desarrollador de aplicaciones a capturar una excepción irrecuperable en una capa baja.
|
||||||
|
En la mayoría de los sistemas, las excepciones no chequeadas y fatales son manejadas
|
||||||
|
en uno de los primeros cuadros de la pila de llamadas a métodos (es decir, en las capas
|
||||||
|
más altas) y se presenta un mensaje de error al usuario de la aplicación (o se toma alguna
|
||||||
|
otra acción apropiada). Nota que Hibernate podría también lanzar otras excepciones no chequeadas
|
||||||
|
que no sean una <literal>HibernateException</literal>. Una vez más, no son recuperables y debe
|
||||||
|
tomarse una acción apropiada.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate envuelve <literal>SQLException</literal>s lanzadas mientras se interactúa con la base
|
||||||
|
de datos en una <literal>JDBCException</literal>. De hecho, Hibernate intentará convertir la excepción
|
||||||
|
en una subclase de <literal>JDBCException</literal> más significativa. La <literal>SQLException</literal>
|
||||||
|
está siempre disponible vía <literal>JDBCException.getCause()</literal>. Hibernate convierte la
|
||||||
|
<literal>SQLException</literal> en una subclase de <literal>JDBCException</literal> apropiada usando
|
||||||
|
el <literal>SQLExceptionConverter</literal> adjunto a la <literal>SessionFactory</literal>. Por defecto,
|
||||||
|
el <literal>SQLExceptionConverter</literal> está definido para el dialecto configurado; sin embargo,
|
||||||
|
es también posible enchufar una implementación personalizada (ver los javadocs de la clase
|
||||||
|
<literal>SQLExceptionConverterFactory</literal> para los detalles). Los subtipos estándar de
|
||||||
|
<literal>JDBCException</literal> son:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist spacing="compact">
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>JDBCConnectionException</literal> - indica un error con la comunicación JDBC subyacente.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>SQLGrammarException</literal> - indica un problema de gramática o sintáxis con el
|
||||||
|
SQL publicado.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>ConstraintViolationException</literal> - indica alguna forma de violación de restricción
|
||||||
|
de integridad.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>LockAcquisitionException</literal> - indica un error adquiriendo un nivel de bloqueo
|
||||||
|
necesario para realizar una operación solicitada.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>GenericJDBCException</literal> - una excepción genérica que no cayó en ninguna de las
|
||||||
|
otras categorías.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="transactions-optimistic">
|
||||||
|
<title>Control optimista de concurrencia</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
El único enfoque que es consistente con alta concurrencia y alta escalabilidad es el control
|
||||||
|
optimista de concurrencia con versionamiento. El chuequeo de versión usa números de versión,
|
||||||
|
o timestamps, para detectar actualizaciones en conflicto (y para prevenir actualizaciones perdidas).
|
||||||
|
Hibernate provee para tres enfoques posibles de escribir código de aplicación que use concurrencia
|
||||||
|
optimista. Los casos de uso que hemos mostrado están en el contexto de transacciones de aplicación
|
||||||
|
largas pero el chequeo de versiones tiene además el beneficio de prevenir actualizaciones perdidas
|
||||||
|
en transacciones de base de datos solas.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect2 id="transactions-optimistic-manual">
|
||||||
|
<title>Chequeo de versiones de aplicación</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
En una implementación sin mucha ayuda de Hibernate, cada interacción con la base de datos ocurre en una
|
||||||
|
nueva <literal>Session</literal> y el desarrollador es responsable de recargar todas las intancias
|
||||||
|
persistentes desde la base de datos antes de manipularlas. Este enfoque fuerza a la aplicación a
|
||||||
|
realizar su propio chequeo de versiones para asegurar el aislamiento de transacciones de base de datos.
|
||||||
|
Es el enfoque más similar a los EJBs de entidad.
|
||||||
|
</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>
|
||||||
|
La propiedad <literal>version</literal> se mapea usando <literal><version></literal>,
|
||||||
|
e Hibernate la incrementará automáticamente durante la limpieza si la entidad está sucia.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Por supuesto, si estás operando un entorno de baja-concurrencia-de-datos y no requieres
|
||||||
|
chequeo de versiones, puedes usar este enfoque y simplemente saltar el chequeo de versiones.
|
||||||
|
En ese caso, <emphasis>el último compromiso (commit) gana</emphasis> será la estrategia por
|
||||||
|
defecto para tus transacciones de aplicación largas. Ten en mente que esto podría confundir
|
||||||
|
a los usuarios de la aplicación, pues podrían experimentar actualizaciones perdidas sin
|
||||||
|
mensajes de error ni chance de fusionar los cambios conflictivos.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Claramente, el chequeo manual de versiones es factible solamente en circunstancias muy triviales,
|
||||||
|
y no es práctico para la mayoría de aplicaciones. Frecuentemente, no sólo intancias solas, sino grafos
|
||||||
|
completos de objetos modificados tienen que ser chequeados. Hibernate ofrece chequeo de versiones
|
||||||
|
automático con el paradigma de diseño de <literal>Session</literal> larga o de instancias separadas.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="transactions-optimistic-longsession">
|
||||||
|
<title>Sesión larga y versionado automático</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Una sola instancia de <literal>Session</literal> y sus instancias persistentes
|
||||||
|
son usadas para toda la transacción de aplicación. Hibernate chequea las versiones
|
||||||
|
de instancia en el momento de limpieza (flush), lanzando una excepción si se detecta
|
||||||
|
una modificación concurrente. Concierne al desarrollador capturar y manejar esta excepción
|
||||||
|
(las opciones comunes son la oportunidad del usuario de fusionar los cambios, o recomenzar el
|
||||||
|
proceso de negocio sin datos añejos).
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La <literal>Session</literal> se desconecta de cualquier conexión JDBC subyacente
|
||||||
|
al esperar por una interacción del usuario. Este enfoque es el más eficiente en términos
|
||||||
|
de acceso a base de datos. La aplicación no necesita tratar por sí misma con el chequeo de
|
||||||
|
versiones, ni re-uniendo instancias separadas, ni tiene que recargar instancias en cada
|
||||||
|
transacción de base de datos.
|
||||||
|
</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>
|
||||||
|
El objeto <literal>foo</literal> todavía conoce en qué <literal>Session</literal> fue cargado.
|
||||||
|
<literal>Session.reconnect()</literal> obtiene una nueva conexión (o puedes proveer una) y
|
||||||
|
reasume la sesión. El método <literal>Session.disconnect()</literal> desconectará la sesión
|
||||||
|
de la conexión JDBC y la devolverá la conexión al pool (a menos que hayas provisto la conexión).
|
||||||
|
Después de la reconexión, para forzar un chequeo de versión en datos que no estés actualizando,
|
||||||
|
puedes llamar a <literal>Session.lock()</literal> con <literal>LockMode.READ</literal> sobre
|
||||||
|
cualquier objeto que pudiese haber sido actualizado por otra transacción. No necesitas bloquear
|
||||||
|
ningún dato que <emphasis>sí estés</emphasis> actualizando.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Si las llamadas explícitas a <literal>disconnect()</literal> y <literal>reconnect()</literal>
|
||||||
|
son muy onerosas, puedes usar en cambio <literal>hibernate.connection.release_mode</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Este patrón es problemático si la <literal>Session</literal> es demasiado grande para ser almacenada
|
||||||
|
durante el tiempo de pensar del usuario, por ejemplo, una <literal>HttpSession</literal> debe
|
||||||
|
mantenerse tan pequeña como sea posible. Ya que la <literal>Session</literal> es también el caché
|
||||||
|
(obligatorio) de primer nivel y contiene todos los objetos cargados, podemos probablemente cargar
|
||||||
|
esta estrategia sólo para unos pocos ciclos petición/respuesta. Esto está de hecho recomendado, ya que
|
||||||
|
la <literal>Session</literal> tendrá pronto también datos añejos.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Nota también que debes mantener la <literal>Session</literal> desconectada próxima a la capa
|
||||||
|
de persistencia. En otras palabras, usa una sesión de EJB con estado para tener la
|
||||||
|
<literal>Session</literal> y no transferirla a la capa web para almacenarla en la
|
||||||
|
<literal>HttpSession</literal> (ni incluso serializarla a una capa separada).
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="transactions-optimistic-detached">
|
||||||
|
<title>Objetos separados y versionado automático</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Cada interacción con el almacén persistente ocurre en una nueva <literal>Session</literal>.
|
||||||
|
Sin embargo, las mismas instancias persistentes son reusadas para cada interacción con la base de
|
||||||
|
datos. La aplicación manipula el estado de las instancias separadas originalmente cargadas en otra
|
||||||
|
<literal>Session</literal> y luego las readjunta usando <literal>Session.update()</literal>,
|
||||||
|
<literal>Session.saveOrUpdate()</literal>, o <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>
|
||||||
|
De nuevo, Hibernate chequeará las versiones de instancia durante la limpieza (flush),
|
||||||
|
lanzando una excepción si ocurrieron actualizaciones en conflicto.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes también llamar a <literal>lock()</literal> en vez de <literal>update()</literal>
|
||||||
|
y usar <literal>LockMode.READ</literal> (realizando un chequeo de versión, puenteando
|
||||||
|
todos los cachés) si estás seguro que el objeto no ha sido modificado.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="transactions-optimistic-customizing">
|
||||||
|
<title>Personalizando el versionado automático</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Puedes deshabilitar el incremento de versión automático de Hibernate para propiedades en particular
|
||||||
|
y colecciones estableciendo el atributo de mapeo <literal>optimistic-lock</literal> a
|
||||||
|
<literal>false</literal>. Hibernate entonces no incrementará ya más las versiones si la propiedad está
|
||||||
|
sucia.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Los esquemas de base de datos heredados son frecuentemente estáticos y no pueden ser modificados.
|
||||||
|
U otras aplicaciones podrían también acceder la misma base de datos y no saber cómo manejar los números
|
||||||
|
de versión ni incluso timestamps. En ambos casos, el versionado no puede confiarse a una columna en
|
||||||
|
particular en una tabla. Para forzar un chequeo de versiones sin un mapeo de propiedad de versión o
|
||||||
|
timestamp, con una comparación del estado de todos los campos en una fila, activa
|
||||||
|
<literal>optimistic-lock="all"</literal> en el mapeo de <literal><class></literal>.
|
||||||
|
Nota que esto conceptualmente funciona solamente si Hibernate puede comparar el estado viejo y nuevo,
|
||||||
|
es decir, si usas una sola <literal>Session</literal> larga y no
|
||||||
|
sesión-por-petición-con-instancias-separadas.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
A veces las modificaciones concurrentes pueden permitirse, en cuanto los cambios que hayan sido
|
||||||
|
hechos no se traslapen. Si estableces <literal>optimistic-lock="dirty"</literal> al mapear la
|
||||||
|
<literal><class></literal>, Hibernate sólo comparará los campos sucios durante la limpieza.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
En ambos casos, con columnas de versión/timestamp dedicadas o con comparación de campos
|
||||||
|
completa/sucios, Hibernate usa una sola sentencia <literal>UPDATE</literal>
|
||||||
|
(con una cláusula <literal>WHERE</literal> apropiada) por entidad para ejecutar el chequeo
|
||||||
|
de versiones y actualizar la información. Si usas persistencia transitiva para la re-unión
|
||||||
|
en cascada de entidades asociadas, Hibernate podría ejecutar actualizaciones innecesarias.
|
||||||
|
Esto usualmente no es un problema, pero podrían ejecutarse disparadores (triggers)
|
||||||
|
<emphasis>on update</emphasis> en la base de datos incluso cuando no se haya hecho ningún cambio
|
||||||
|
a las instancias separadas. Puedes personalizar este comportamiento estableciendo
|
||||||
|
<literal>select-before-update="true"</literal> en el mapeo de <literal><class></literal>,
|
||||||
|
forzando a Hibernate a <literal>SELECT</literal> la instancia para asegurar que las actualizaciones
|
||||||
|
realmente ocurran, antes de actualizar la fila.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="transactions-locking">
|
||||||
|
<title>Bloqueo pesimista</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
No se pretende que los usuarios gasten mucho tiempo preocupándose de las estrategias de bloqueo.
|
||||||
|
Usualmente es suficiente con especificar un nivel de aislamiento para las conexiones JDBC y entonces
|
||||||
|
simplemente dejar que la base de datos haga todo el trabajo. Sin embargo, los usuarios avanzados pueden
|
||||||
|
a veces obtener bloqueos exclusivos pesimistas, o reobtener bloqueos al comienzo de una nueva
|
||||||
|
transacción.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
¡Hibernate siempre usará el mecanismo de bloqueo de la base de datos, nunca bloqueo
|
||||||
|
de objetos en memoria!
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La clase <literal>LockMode</literal> define los diferentes niveles de bloqueo que pueden ser adquiridos
|
||||||
|
por Hibernate. Un bloqueo se obtiene por los siguientes mecanismos:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist spacing="compact">
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>LockMode.WRITE</literal> se adquiere automáticamente cuando Hibernate actualiza o
|
||||||
|
inserta una fila.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>LockMode.UPGRADE</literal> puede ser adquirido bajo petición explícita del usuario
|
||||||
|
usando <literal>SELECT ... FOR UPDATE</literal> en base de datos que soporten esa sintáxis.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>LockMode.UPGRADE_NOWAIT</literal> puede ser adquirido bajo petición explícita del usuario
|
||||||
|
usando un <literal>SELECT ... FOR UPDATE NOWAIT</literal> bajo Oracle.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>LockMode.READ</literal> es adquirido automáticamente cuando Hibernate lee datos
|
||||||
|
bajo los niveles de aislamiento Repeatable Read o Serializable. Puede ser readquirido por
|
||||||
|
pedido explícito del usuario.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>LockMode.NONE</literal> representa la ausencia de un bloqueo. Todos los objetos se pasan
|
||||||
|
a este modo de bloqueo al final de una <literal>Transaction</literal>. Los objetos asociados con una
|
||||||
|
sesión vía una llamada a <literal>update()</literal> o <literal>saveOrUpdate()</literal> también
|
||||||
|
comienzan en este modo de bloqueo.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
La "petición explícita del usuario" se expresa en una de las siguientes formas:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist spacing="compact">
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Una llamada a <literal>Session.load()</literal>, especificando un <literal>LockMode</literal>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Una llamada a <literal>Session.lock()</literal>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Una llamada a <literal>Query.setLockMode()</literal>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Si se llama a <literal>Session.load()</literal> con <literal>UPGRADE</literal> o
|
||||||
|
<literal>UPGRADE_NOWAIT</literal>, y el objeto pedido no ha sido aún cargado por la sesión, el objeto es
|
||||||
|
cargado usando <literal>SELECT ... FOR UPDATE</literal>. Si se llama a <literal>load()</literal> para
|
||||||
|
un objeto que ya esté cargado con un bloqueo menos restrictivo que el pedido, Hibernate llama a
|
||||||
|
<literal>lock()</literal> para ese objeto.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<literal>Session.lock()</literal> realiza un chequeo de número de versión si el modo de bloqueo especificado
|
||||||
|
es <literal>READ</literal>, <literal>UPGRADE</literal> o <literal>UPGRADE_NOWAIT</literal>. (En el caso de
|
||||||
|
<literal>UPGRADE</literal> o <literal>UPGRADE_NOWAIT</literal>, se usa
|
||||||
|
<literal>SELECT ... FOR UPDATE</literal>.)
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Si la base de datos no soporta el modo de bloqueo solicitado, Hibernate usará un modo alternativo
|
||||||
|
apropiado (en vez de lanzar una excepción). Esto asegura que las aplicaciones serán portables.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
</chapter>
|
||||||
|
|
1270
reference/es/modules/tutorial.xml
Normal file
282
reference/es/modules/xml.xml
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
<chapter id="xml">
|
||||||
|
<title>Mapeo XML</title>
|
||||||
|
|
||||||
|
<para><emphasis>
|
||||||
|
Nota que esta es una funcionalidad experimental en Hibernate 3.0 y está
|
||||||
|
bajo un desarrollo extremadamente activo.
|
||||||
|
</emphasis></para>
|
||||||
|
|
||||||
|
<sect1 id="xml-intro" revision="1">
|
||||||
|
<title>Trabajando con datos XML</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate te permite trabajar con datos XML persistentes en casi la misma forma
|
||||||
|
que trabajas con POJOs persistentes. Un árbol XML analizado (parsed) puede ser
|
||||||
|
pensado como sólo otra forma de representar los datos relacionales a nivel de objetos,
|
||||||
|
en vez de POJOs.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate soporta dom4j como API para manipular árboles XML. Puedes escribir
|
||||||
|
consultas que traigan árboles dom4j de la base de datos y tener cualquier modificación
|
||||||
|
que hagas al árbol sincronizada automáticamente a la base de datos. Puedes incluso tomar
|
||||||
|
un documento XML, analizarlo usando dom4j, y escribirlo a la base de datos con cualquiera
|
||||||
|
de las operaciones básicas de Hibernate: <literal>persist(), saveOrUpdate(), merge(),
|
||||||
|
delete(), replicate()</literal> (la fusión no está aún soportada).
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Esta funcionalidad tiene muchas aplicaciones incluyendo la importación/exportación de datos,
|
||||||
|
externalización de datos de entidad vía JMS o SOAP y reportes basados en XSLT.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Un solo mapeo puede ser usado para mapear simultáneamente las propiedades de una clase y los nodos de un
|
||||||
|
documento XML a la base de datos, o, si no hay ninguna clase a mapear, puede ser usado para mapear sólo
|
||||||
|
el XML.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect2 id="xml-intro-mapping">
|
||||||
|
<title>Especificando los mapeos de XML y de clase juntos</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
He aquí un ejemplo de mapear un POJO y XML simultáneamente:
|
||||||
|
</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>Especificando sólo un mapeo XML</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
He aquí un ejemplo donde no hay ninguna clase 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>
|
||||||
|
Este mapeo te permite acceder a los datos como un árbol dom4j, o como un grafo de pares nombre/valor de
|
||||||
|
propiedad (<literal>Map</literal>s de Java). Los nombres de propiedades son construcciones puramente
|
||||||
|
lógicas a las que se puede hacer referencia en consultas HQL.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="xml-mapping" revision="1">
|
||||||
|
<title>Mapeo de metadatos XML</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Muchos elementos de mapeo de Hibernate aceptan el atributo <literal>node</literal>. Esto te permite espcificar
|
||||||
|
el nombre de un atributo o elemento XML que contenga los datos de la propiedad o entidad. El formato del
|
||||||
|
atributo <literal>node</literal> debe ser uno de los siguientes:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<itemizedlist spacing="compact">
|
||||||
|
<listitem>
|
||||||
|
<para><literal>"element-name"</literal> - mapea al elemento XML mencionado</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para><literal>"@attribute-name"</literal> - mapea al atributo XML mencionado</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para><literal>"."</literal> - mapea al elemento padre</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>"element-name/@attribute-name"</literal> -
|
||||||
|
mapea al atributo mencionado del elemento mencionado
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Para las colecciones y asociaciones monovaluadas, existe un atributo adicional <literal>embed-xml</literal>.
|
||||||
|
Si <literal>embed-xml="true"</literal>, que es el valor por defecto, el árbol XML para la entidad
|
||||||
|
asociada (o colección de tipo de valor) será embebida directamente en el árbol XML para la entidad que
|
||||||
|
posee la asociación. En otro caso, si <literal>embed-xml="false"</literal>, sólo el valor identificador
|
||||||
|
referenciado aparecerá en el XML para asociaciones de punto único y para las colecciones simplemente
|
||||||
|
no aparecerá en absoluto.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
¡Debes ser cuidadoso de no dejar <literal>embed-xml="true"</literal> para demasiadas asociaciones,
|
||||||
|
ya que XML no trata bien la circularidad!
|
||||||
|
</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>
|
||||||
|
en este caso, hemos decidido embeber la colección de ids de cuenta, pero no los datos reales de cuenta.
|
||||||
|
La siguiente consulta HQL:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
devolvería conjuntos de datos como estos:
|
||||||
|
</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>
|
||||||
|
Si estableces <literal>embed-xml="true"</literal> en el mapeo <literal><one-to-many></literal>, los datos
|
||||||
|
podrían verse así:
|
||||||
|
</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>Manipulando datos XML</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Vamos a releer y actualizar documentos XML en la aplicación. Hacemos esto obteniendo una sesión 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>
|
||||||
|
Es extremadamente útil combinar esta funcionalidad con la operación <literal>replicate()</literal>
|
||||||
|
de Hibernate para implementar la importación/exportación de datos basada en XML.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
</chapter>
|
||||||
|
|
516
reference/es/styles/fopdf.xsl
Normal file
@ -0,0 +1,516 @@
|
|||||||
|
<?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="Helvetica" font-size="22pt" padding-before="10mm">
|
||||||
|
<xsl:value-of select="bookinfo/subtitle"/>
|
||||||
|
</fo:block>
|
||||||
|
<fo:block font-family="Helvetica" font-size="12pt" padding="10mm">
|
||||||
|
Version:
|
||||||
|
<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="$body.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>Hibernate </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="en">
|
||||||
|
<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'"/>
|
||||||
|
|
||||||
|
</xsl:stylesheet>
|
97
reference/es/styles/html.css
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
84
reference/es/styles/html.xsl
Normal 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>
|
86
reference/es/styles/html_chunk.xsl
Normal 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>
|