v3.0.4
git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@6853 1b8cb986-b30d-0410-93ca-fae66ebed9b2
|
@ -0,0 +1,114 @@
|
|||
<!--<!DOCTYPE configuration SYSTEM "config.dtd">-->
|
||||
<!--
|
||||
this file contains templates which allow an user easy
|
||||
configuration of Fop. Actually normally you don't need this configuration
|
||||
file, but if you need to change configuration, you should
|
||||
always use this file and *not* config.xml.
|
||||
Usage: java org.apache.fop.apps.Fop -c userconfig.xml -fo fo-file -pdf pdf-file
|
||||
-->
|
||||
|
||||
|
||||
<configuration>
|
||||
|
||||
<!--
|
||||
baseDir: normally the base directory is the directory where the fo file is
|
||||
located. if you want to specify your own, uncomment this entry.
|
||||
This value can also be a URL. Actually, the value is converted to
|
||||
a URL.
|
||||
-->
|
||||
<!--
|
||||
<entry>
|
||||
<key>baseDir</key>
|
||||
<value></value>
|
||||
</entry>
|
||||
-->
|
||||
|
||||
<!--
|
||||
fontBaseDir: Similar to baseDir, except that this value is used for fonts. If
|
||||
it isn't specified, the value from baseDir is used.
|
||||
-->
|
||||
<!--
|
||||
<entry>
|
||||
<key>fontBaseDir</key>
|
||||
<value></value>
|
||||
</entry>
|
||||
-->
|
||||
|
||||
<!--
|
||||
************************************************************************
|
||||
HYPHENATION
|
||||
************************************************************************
|
||||
-->
|
||||
|
||||
<!--
|
||||
hyphenation directory
|
||||
if you want to specify your own directory with hyphenation pattern
|
||||
then uncomment the next entry and add the directory name
|
||||
-->
|
||||
|
||||
<!--
|
||||
<entry>
|
||||
<key>hyphenation-dir</key>
|
||||
<value>/java/xml-fop/hyph</value>
|
||||
</entry>
|
||||
-->
|
||||
|
||||
<!--
|
||||
************************************************************************
|
||||
Add fonts here
|
||||
************************************************************************
|
||||
-->
|
||||
|
||||
<fonts>
|
||||
<!-- example -->
|
||||
<!--
|
||||
<font metrics-file="arial.xml" kerning="yes" embed-file="arial.ttf">
|
||||
<font-triplet name="Arial" style="normal" weight="normal"/>
|
||||
<font-triplet name="ArialMT" style="normal" weight="normal"/>
|
||||
</font>
|
||||
<font metrics-file="arialb.xml" kerning="yes" embed-file="arialb.ttf">
|
||||
<font-triplet name="Arial" style="normal" weight="bold"/>
|
||||
<font-triplet name="ArialMT" style="normal" weight="bold"/>
|
||||
</font>
|
||||
<font metrics-file="ariali.xml" kerning="yes" embed-file="ariali.ttf">
|
||||
<font-triplet name="Arial" style="italic" weight="normal"/>
|
||||
<font-triplet name="ArialMT" style="italic" weight="normal"/>
|
||||
</font>
|
||||
<font metrics-file="arialbi.xml" kerning="yes" embed-file="arialbi.ttf">
|
||||
<font-triplet name="Arial" style="italic" weight="bold"/>
|
||||
<font-triplet name="ArialMT" style="italic" weight="bold"/>
|
||||
</font>
|
||||
-->
|
||||
<!-- Example Japanese fonts
|
||||
<font metrics-file="msgothic.xml" embed-file="D:\winnt\font\msgothic.ttc" kerning="yes">
|
||||
<font-triplet name="Gothic" style="normal" weight="normal"/>
|
||||
<font-triplet name="Gothic" style="normal" weight="bold"/>
|
||||
<font-triplet name="Gothic" style="italic" weight="normal"/>
|
||||
<font-triplet name="Gothic" style="italic" weight="bold"/>
|
||||
</font>
|
||||
<font metrics-file="msmincho.xml" embed-file="Cyberbit.ttf" kerning="yes">
|
||||
<font-triplet name="Mincho" style="normal" weight="normal"/>
|
||||
<font-triplet name="Mincho" style="normal" weight="bold"/>
|
||||
<font-triplet name="Mincho" style="italic" weight="normal"/>
|
||||
<font-triplet name="Mincho" style="italic" weight="bold"/>
|
||||
</font>
|
||||
-->
|
||||
<font metrics-file="simhei.xml" embed-file="simhei.ttf" kerning="yes">
|
||||
<font-triplet name="simhei" style="normal" weight="normal"/>
|
||||
<font-triplet name="simhei" style="normal" weight="bold"/>
|
||||
<font-triplet name="simhei" style="italic" weight="normal"/>
|
||||
<font-triplet name="simhei" style="italic" weight="bold"/>
|
||||
</font>
|
||||
<font metrics-file="simsun.xml" embed-file="simsun.ttc" kerning="yes">
|
||||
<font-triplet name="simsun" style="normal" weight="normal"/>
|
||||
<font-triplet name="simsun" style="normal" weight="bold"/>
|
||||
<font-triplet name="simsun" style="italic" weight="normal"/>
|
||||
<font-triplet name="simsun" style="italic" weight="bold"/>
|
||||
</font>
|
||||
</fonts>
|
||||
|
||||
|
||||
</configuration>
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
O/R Mapping 对象/关系数据库映射
|
||||
identifier property: 标识属性
|
||||
discriminator: 辨别标志(不使用"鉴别器")
|
||||
|
||||
join-subclass,union-subclass,join: 不翻译
|
||||
|
||||
fine-grained 细粒度
|
||||
domain model 领域模型
|
||||
business object 业务对象(v2的时候翻译为"商业对象",被无数人痛骂....)
|
||||
annotations 注解(这个词还比较新,也见到有翻译为"特殊注释"/"标注"的)
|
||||
|
||||
Aggregate functions 统计函数 (聚集函数?聚合函数? 还是翻译为统计函数才能让人看懂)
|
||||
Criteria Queries 条件查询(标准查询?这里Criteria是"判据"的含义,而非"标准",意译为条件查询更加明白)
|
||||
|
||||
session flush session清洗
|
||||
lazy fetching 延迟加载(懒加载,翻译很直白,但是却不易理解)
|
||||
|
||||
对象状态:
|
||||
persistent class 持久化类
|
||||
persistent object 持久对象 (体现这是与某个持久化设备联系的,掉电之后不消失)
|
||||
transient object 瞬时对象 (其含义是仅在内存中存在的,掉电之后消失,翻译为瞬时对象比较好)
|
||||
detached object 脱管对象(与session脱离的对象)
|
||||
|
||||
outer-join fetching 外连接抓取 (fetch一般翻译为"获取",v2翻译的时候,为了突出其主动获取的特点,翻译为"抓取")
|
||||
|
||||
class hierarchy 类分层结构 http://www.umlchina.com/xprogrammer/glossary1.htm
|
||||
inheritance hierarchy 继承层次 http://www-128.ibm.com/developerworks/cn/java/j-aopwork4/?ca=dwcn-newsletter-java
|
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 9.1 KiB |
|
@ -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 |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 6.6 KiB |
|
@ -0,0 +1,334 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
|
||||
[
|
||||
<!ATTLIST svg
|
||||
xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
|
||||
]>
|
||||
<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="318.898"
|
||||
height="248.031"
|
||||
id="svg1">
|
||||
<defs
|
||||
id="defs3">
|
||||
<linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
id="linearGradient127"
|
||||
gradientUnits="objectBoundingBox"
|
||||
spreadMethod="pad">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop128" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop129" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
id="linearGradient130"
|
||||
xlink:href="#linearGradient127"
|
||||
gradientUnits="objectBoundingBox"
|
||||
spreadMethod="pad" />
|
||||
<radialGradient
|
||||
cx="0.5"
|
||||
cy="0.5"
|
||||
fx="0.5"
|
||||
fy="0.5"
|
||||
r="0.5"
|
||||
id="radialGradient131"
|
||||
xlink:href="#linearGradient127"
|
||||
gradientUnits="objectBoundingBox"
|
||||
spreadMethod="pad" />
|
||||
</defs>
|
||||
<rect
|
||||
width="291.837"
|
||||
height="57.0074"
|
||||
x="17.3169"
|
||||
y="18.646"
|
||||
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect136" />
|
||||
<rect
|
||||
width="291.837"
|
||||
height="57.0074"
|
||||
x="13.9703"
|
||||
y="16.2302"
|
||||
style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect126" />
|
||||
<g
|
||||
transform="matrix(0.326107,0,0,0.765831,9.59261,8.98517)"
|
||||
style="font-size:12;"
|
||||
id="g161">
|
||||
<rect
|
||||
width="285.502"
|
||||
height="118.523"
|
||||
x="16.6979"
|
||||
y="99.2053"
|
||||
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect137" />
|
||||
<rect
|
||||
width="285.502"
|
||||
height="118.523"
|
||||
x="13.4238"
|
||||
y="95.9309"
|
||||
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect132" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.02406,0,0,0.652953,0.223384,39.9254)"
|
||||
style="font-size:12;"
|
||||
id="g164">
|
||||
<rect
|
||||
width="285.502"
|
||||
height="77.2688"
|
||||
x="16.6979"
|
||||
y="222.966"
|
||||
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect138" />
|
||||
<rect
|
||||
width="285.502"
|
||||
height="77.2688"
|
||||
x="14.7335"
|
||||
y="221.002"
|
||||
transform="translate(-1.30962,-1.30992)"
|
||||
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect133" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0.449834,0,0,0.338463,-3.15909,9.73319)"
|
||||
style="font-size:12;"
|
||||
id="g167">
|
||||
<rect
|
||||
width="199.065"
|
||||
height="61.5532"
|
||||
x="61.8805"
|
||||
y="68.4288"
|
||||
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect134" />
|
||||
<rect
|
||||
width="199.065"
|
||||
height="61.5532"
|
||||
x="59.2613"
|
||||
y="65.8095"
|
||||
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect135" />
|
||||
</g>
|
||||
<text
|
||||
x="302.277679"
|
||||
y="65.943230"
|
||||
transform="scale(0.73778,0.73778)"
|
||||
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
|
||||
id="text183">
|
||||
<tspan
|
||||
x="302.277954"
|
||||
y="65.943184"
|
||||
id="tspan360">
|
||||
Application</tspan>
|
||||
</text>
|
||||
<text
|
||||
x="36.235924"
|
||||
y="63.796055"
|
||||
transform="scale(0.73778,0.73778)"
|
||||
style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
|
||||
id="text188">
|
||||
<tspan
|
||||
x="36.235950"
|
||||
y="63.796051"
|
||||
id="tspan427">
|
||||
Transient Objects</tspan>
|
||||
</text>
|
||||
<text
|
||||
x="180.416245"
|
||||
y="290.543701"
|
||||
transform="scale(0.73778,0.73778)"
|
||||
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
|
||||
id="text197">
|
||||
<tspan
|
||||
x="180.415939"
|
||||
y="290.543549"
|
||||
id="tspan421">
|
||||
Database</tspan>
|
||||
</text>
|
||||
<text
|
||||
x="25.037701"
|
||||
y="179.154755"
|
||||
transform="scale(0.73778,0.73778)"
|
||||
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
|
||||
id="text216">
|
||||
<tspan
|
||||
x="25.037655"
|
||||
y="179.154648"
|
||||
id="tspan384">
|
||||
SessionFactory</tspan>
|
||||
</text>
|
||||
<g
|
||||
transform="matrix(0.252763,0,0,0.765831,109.104,8.98517)"
|
||||
style="font-size:12;"
|
||||
id="g386">
|
||||
<rect
|
||||
width="285.502"
|
||||
height="118.523"
|
||||
x="16.6979"
|
||||
y="99.2053"
|
||||
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect387" />
|
||||
<rect
|
||||
width="285.502"
|
||||
height="118.523"
|
||||
x="13.4238"
|
||||
y="95.9309"
|
||||
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect388" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0.297394,0,0,0.572692,101.502,21.6359)"
|
||||
style="font-size:12;"
|
||||
id="g364">
|
||||
<rect
|
||||
width="199.065"
|
||||
height="61.5532"
|
||||
x="61.8805"
|
||||
y="68.4288"
|
||||
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect365" />
|
||||
<rect
|
||||
width="199.065"
|
||||
height="61.5532"
|
||||
x="59.2613"
|
||||
y="65.8095"
|
||||
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect366" />
|
||||
</g>
|
||||
<text
|
||||
x="202.746506"
|
||||
y="102.992203"
|
||||
transform="scale(0.73778,0.73778)"
|
||||
style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
|
||||
id="text367">
|
||||
<tspan
|
||||
x="202.746948"
|
||||
y="102.992249"
|
||||
id="tspan423">
|
||||
Persistent</tspan>
|
||||
<tspan
|
||||
x="202.746948"
|
||||
y="116.992355"
|
||||
id="tspan425">
|
||||
Objects</tspan>
|
||||
</text>
|
||||
<text
|
||||
x="174.458496"
|
||||
y="180.080795"
|
||||
transform="scale(0.73778,0.73778)"
|
||||
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
|
||||
id="text389">
|
||||
<tspan
|
||||
x="174.458618"
|
||||
y="180.080338"
|
||||
id="tspan392">
|
||||
Session</tspan>
|
||||
</text>
|
||||
<g
|
||||
transform="matrix(0.127369,0,0,0.765831,188.675,8.98517)"
|
||||
style="font-size:12;"
|
||||
id="g394">
|
||||
<rect
|
||||
width="285.502"
|
||||
height="118.523"
|
||||
x="16.6979"
|
||||
y="99.2053"
|
||||
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect395" />
|
||||
<rect
|
||||
width="285.502"
|
||||
height="118.523"
|
||||
x="13.4238"
|
||||
y="95.9309"
|
||||
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect396" />
|
||||
</g>
|
||||
<text
|
||||
x="260.413269"
|
||||
y="179.154739"
|
||||
transform="scale(0.73778,0.73778)"
|
||||
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
|
||||
id="text397">
|
||||
<tspan
|
||||
x="260.412964"
|
||||
y="179.154343"
|
||||
id="tspan400">
|
||||
JDBC</tspan>
|
||||
</text>
|
||||
<g
|
||||
transform="matrix(0.127369,0,0,0.765831,229.156,8.98517)"
|
||||
style="font-size:12;"
|
||||
id="g405">
|
||||
<rect
|
||||
width="285.502"
|
||||
height="118.523"
|
||||
x="16.6979"
|
||||
y="99.2053"
|
||||
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect406" />
|
||||
<rect
|
||||
width="285.502"
|
||||
height="118.523"
|
||||
x="13.4238"
|
||||
y="95.9309"
|
||||
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect407" />
|
||||
</g>
|
||||
<text
|
||||
x="320.606903"
|
||||
y="179.154739"
|
||||
transform="scale(0.73778,0.73778)"
|
||||
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
|
||||
id="text408">
|
||||
<tspan
|
||||
x="320.606964"
|
||||
y="179.154343"
|
||||
id="tspan417">
|
||||
JNDI</tspan>
|
||||
</text>
|
||||
<g
|
||||
transform="matrix(0.127369,0,0,0.765831,269.281,8.98517)"
|
||||
style="font-size:12;"
|
||||
id="g411">
|
||||
<rect
|
||||
width="285.502"
|
||||
height="118.523"
|
||||
x="16.6979"
|
||||
y="99.2053"
|
||||
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect412" />
|
||||
<rect
|
||||
width="285.502"
|
||||
height="118.523"
|
||||
x="13.4238"
|
||||
y="95.9309"
|
||||
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect413" />
|
||||
</g>
|
||||
<text
|
||||
x="377.096313"
|
||||
y="179.154739"
|
||||
transform="scale(0.73778,0.73778)"
|
||||
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
|
||||
id="text414">
|
||||
<tspan
|
||||
x="377.096008"
|
||||
y="179.154999"
|
||||
id="tspan145">
|
||||
JTA</tspan>
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 8.4 KiB |
|
@ -0,0 +1,250 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
|
||||
[
|
||||
<!ATTLIST svg
|
||||
xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
|
||||
]>
|
||||
<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="248.031"
|
||||
height="248.031"
|
||||
id="svg1">
|
||||
<defs
|
||||
id="defs3">
|
||||
<linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
id="linearGradient127"
|
||||
gradientUnits="objectBoundingBox"
|
||||
spreadMethod="pad">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop128" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop129" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
id="linearGradient130"
|
||||
xlink:href="#linearGradient127"
|
||||
gradientUnits="objectBoundingBox"
|
||||
spreadMethod="pad" />
|
||||
<radialGradient
|
||||
cx="0.5"
|
||||
cy="0.5"
|
||||
fx="0.5"
|
||||
fy="0.5"
|
||||
r="0.5"
|
||||
id="radialGradient131"
|
||||
xlink:href="#linearGradient127"
|
||||
gradientUnits="objectBoundingBox"
|
||||
spreadMethod="pad" />
|
||||
</defs>
|
||||
<g
|
||||
transform="matrix(0.771934,0,0,0.771934,4.36019,-3.02123)"
|
||||
style="font-size:12;"
|
||||
id="g158">
|
||||
<rect
|
||||
width="285.502"
|
||||
height="77.2688"
|
||||
x="16.6979"
|
||||
y="17.3527"
|
||||
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect136" />
|
||||
<rect
|
||||
width="285.502"
|
||||
height="77.2688"
|
||||
x="14.7335"
|
||||
y="15.3883"
|
||||
transform="translate(-1.30962,-1.30992)"
|
||||
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect126" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0.771934,0,0,0.771934,4.36019,3.04452)"
|
||||
style="font-size:12;"
|
||||
id="g161">
|
||||
<rect
|
||||
width="285.502"
|
||||
height="118.523"
|
||||
x="16.6979"
|
||||
y="99.2053"
|
||||
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect137" />
|
||||
<rect
|
||||
width="285.502"
|
||||
height="118.523"
|
||||
x="13.4238"
|
||||
y="95.9309"
|
||||
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect132" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0.771934,0,0,0.771934,4.36019,8.0993)"
|
||||
style="font-size:12;"
|
||||
id="g164">
|
||||
<rect
|
||||
width="285.502"
|
||||
height="77.2688"
|
||||
x="16.6979"
|
||||
y="222.966"
|
||||
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect138" />
|
||||
<rect
|
||||
width="285.502"
|
||||
height="77.2688"
|
||||
x="14.7335"
|
||||
y="221.002"
|
||||
transform="translate(-1.30962,-1.30992)"
|
||||
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect133" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0.771934,0,0,0.543505,2.59104,21.1103)"
|
||||
style="font-size:12;"
|
||||
id="g167">
|
||||
<rect
|
||||
width="199.065"
|
||||
height="61.5532"
|
||||
x="61.8805"
|
||||
y="68.4288"
|
||||
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect134" />
|
||||
<rect
|
||||
width="199.065"
|
||||
height="61.5532"
|
||||
x="59.2613"
|
||||
y="65.8095"
|
||||
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect135" />
|
||||
</g>
|
||||
<text
|
||||
x="105.392174"
|
||||
y="56.568123"
|
||||
transform="scale(0.771934,0.771934)"
|
||||
style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
|
||||
id="text183">
|
||||
<tspan
|
||||
x="105.392273"
|
||||
y="56.568146"
|
||||
id="tspan186">
|
||||
Application</tspan>
|
||||
</text>
|
||||
<text
|
||||
x="81.820183"
|
||||
y="103.149330"
|
||||
transform="scale(0.771934,0.771934)"
|
||||
style="font-size:20;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
|
||||
id="text188">
|
||||
<tspan
|
||||
x="81.820213"
|
||||
y="103.149727"
|
||||
id="tspan206">
|
||||
Persistent Objects</tspan>
|
||||
</text>
|
||||
<text
|
||||
x="111.548180"
|
||||
y="278.927887"
|
||||
transform="scale(0.771934,0.771934)"
|
||||
style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
|
||||
id="text197">
|
||||
<tspan
|
||||
x="111.547874"
|
||||
y="278.927551"
|
||||
id="tspan200">
|
||||
Database</tspan>
|
||||
</text>
|
||||
<text
|
||||
x="94.436180"
|
||||
y="153.805740"
|
||||
transform="scale(0.771934,0.771934)"
|
||||
style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
|
||||
id="text216">
|
||||
<tspan
|
||||
x="94.436180"
|
||||
y="153.805740"
|
||||
id="tspan221">
|
||||
HIBERNATE</tspan>
|
||||
</text>
|
||||
<g
|
||||
transform="matrix(0.771934,0,0,0.771934,2.59083,1.02261)"
|
||||
style="font-size:12;"
|
||||
id="g254">
|
||||
<g
|
||||
transform="translate(4.58374,2.61928)"
|
||||
id="g176">
|
||||
<g
|
||||
transform="matrix(0.571429,0,0,0.67347,-10.6174,117.093)"
|
||||
id="g170">
|
||||
<rect
|
||||
width="199.065"
|
||||
height="61.5532"
|
||||
x="61.8805"
|
||||
y="68.4288"
|
||||
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect171" />
|
||||
<rect
|
||||
width="199.065"
|
||||
height="61.5532"
|
||||
x="59.2613"
|
||||
y="65.8095"
|
||||
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect172" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0.571429,0,0,0.67347,138.682,117.093)"
|
||||
id="g173">
|
||||
<rect
|
||||
width="199.065"
|
||||
height="61.5532"
|
||||
x="61.8805"
|
||||
y="68.4288"
|
||||
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect174" />
|
||||
<rect
|
||||
width="199.065"
|
||||
height="61.5532"
|
||||
x="59.2613"
|
||||
y="65.8095"
|
||||
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
|
||||
id="rect175" />
|
||||
</g>
|
||||
</g>
|
||||
<text
|
||||
x="47.259438"
|
||||
y="182.367538"
|
||||
style="font-weight:bold;stroke-width:1pt;font-family:Courier;"
|
||||
id="text191">
|
||||
<tspan
|
||||
x="47.259399"
|
||||
y="182.367996"
|
||||
id="tspan212">
|
||||
hibernate.</tspan>
|
||||
<tspan
|
||||
x="47.259399"
|
||||
y="194.367996"
|
||||
id="tspan214">
|
||||
properties</tspan>
|
||||
</text>
|
||||
<text
|
||||
x="198.523010"
|
||||
y="188.260941"
|
||||
style="font-weight:normal;stroke-width:1pt;font-family:helvetica;"
|
||||
id="text194">
|
||||
<tspan
|
||||
id="tspan195">
|
||||
XML Mapping</tspan>
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 6.5 KiB |
|
@ -0,0 +1,436 @@
|
|||
<?xml version='1.0' encoding="UTF-8"?>
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
|
||||
"../support/docbook-dtd/docbookx.dtd"
|
||||
[
|
||||
<!ENTITY quickstart SYSTEM "modules/quickstart.xml">
|
||||
<!ENTITY tutorial SYSTEM "modules/tutorial.xml">
|
||||
<!ENTITY architecture SYSTEM "modules/architecture.xml">
|
||||
<!ENTITY configuration SYSTEM "modules/configuration.xml">
|
||||
<!ENTITY persistent-classes SYSTEM "modules/persistent_classes.xml">
|
||||
<!ENTITY basic-mapping SYSTEM "modules/basic_mapping.xml">
|
||||
<!ENTITY collection-mapping SYSTEM "modules/collection_mapping.xml">
|
||||
<!ENTITY association-mapping SYSTEM "modules/association_mapping.xml">
|
||||
<!ENTITY component-mapping SYSTEM "modules/component_mapping.xml">
|
||||
<!ENTITY inheritance-mapping SYSTEM "modules/inheritance_mapping.xml">
|
||||
<!ENTITY session-api SYSTEM "modules/session_api.xml">
|
||||
<!ENTITY transactions SYSTEM "modules/transactions.xml">
|
||||
<!ENTITY events SYSTEM "modules/events.xml">
|
||||
<!ENTITY batch SYSTEM "modules/batch.xml">
|
||||
<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
|
||||
<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
|
||||
<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
|
||||
<!ENTITY filters SYSTEM "modules/filters.xml">
|
||||
<!ENTITY xml SYSTEM "modules/xml.xml">
|
||||
<!ENTITY performance SYSTEM "modules/performance.xml">
|
||||
<!ENTITY toolset-guide SYSTEM "modules/toolset_guide.xml">
|
||||
<!ENTITY example-parentchild SYSTEM "modules/example_parentchild.xml">
|
||||
<!ENTITY example-weblog SYSTEM "modules/example_weblog.xml">
|
||||
<!ENTITY example-mappings SYSTEM "modules/example_mappings.xml">
|
||||
<!ENTITY best-practices SYSTEM "modules/best_practices.xml">
|
||||
]>
|
||||
|
||||
|
||||
<book lang="zh-cn">
|
||||
|
||||
<bookinfo>
|
||||
<title>HIBERNATE - 符合Java习惯的关系数据库持久化</title>
|
||||
|
||||
<subtitle>Hibernate参考文档</subtitle>
|
||||
<releaseinfo>3.0.4</releaseinfo>
|
||||
</bookinfo>
|
||||
|
||||
<toc/>
|
||||
|
||||
<preface id="preface" revision="2">
|
||||
<title>前言</title>
|
||||
|
||||
<para>
|
||||
WARNING! This is a translated version of the English Hibernate reference
|
||||
documentation. The translated version might not be up to date! However, the
|
||||
differences should only be very minor. Consult the English reference
|
||||
documentation if you are missing information or encounter a translation
|
||||
error. If you like to contribute to a particular translation, contact us
|
||||
on the Hibernate developer mailing list.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Translator(s): RedSaga Translate Team <caoxg@yahoo.com>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
在今日的企业环境中,把面向对象的软件和关系数据库一起使用可能是相当麻烦、浪费时间的。Hibernate是一个面向Java环境的对象/关系数据库映射工具。对象/关系数据库映射(object/relational mapping (ORM))这个术语表示一种技术,用来把对象模型表示的对象映射到基于SQL的关系模型数据结构中去。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Hibernate不仅仅管理Java类到数据库表的映射(包括Java数据类型到SQL数据类型的映射),还提供数据查询和获取数据的方法,可以大幅度减少开发时人工使用SQL和JDBC处理数据的时间。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Hibernate的目标是对于开发者通常的数据持久化相关的编程任务,解放其中的95%。对于以数据为中心的程序来说,它们往往只在数据库中使用存储过程来实现商业逻辑,Hibernate可能不是最好的解决方案;对于那些在基于Java的中间层应用中,它们实现面向对象的业务模型和商业逻辑的应用,Hibernate是最有用的。不管怎样,Hibernate一定可以帮助你消除或者包装那些针对特定厂商的SQL代码,并且帮你把结果集从表格式的表示形式转换到一系列的对象去。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
如果你对Hibernate和对象/关系数据库映射还是个新手,或者甚至对Java也不熟悉,请按照下面的步骤来学习。
|
||||
</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
阅读这个30分钟就可以结束的<xref linkend="quickstart"/>,它使用Tomcat。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
阅读<xref linkend="tutorial"/>,这是一篇较长的指南,包含详细的逐步指导。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
阅读<xref linkend="architecture"/>来理解Hibernate可以使用的环境。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
查看Hibernate发行包中的<literal>eg/</literal>目录,里面有一个简单的独立运行的程序。把你的JDBC驱动拷贝到<literal>lib/</literal>目录下,修改一下<literal>src/hibernate.properties</literal>,指定其中你的数据库的信息。进入命令行,切换到你的发行包的目录,输入<literal>ant eg</literal>(使用了Ant),或者在Windows操作系统中使用<literal>build eg</literal>。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
把这份参考文档作为你学习的主要信息来源。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
在Hibernate 的网站上可以找到经常提问的问题与解答(FAQ)。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
在Hibernate网站上还有第三方的演示、示例和教程的链接。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Hibernate网站的“社区(Community Area)”是讨论关于设计模式以及很多整合方案(Tomcat, JBoss AS, Struts, EJB,等等)的好地方。
|
||||
</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>
|
||||
如果你有问题,请使用Hibernate网站上链接的用户论坛。我们也提供一个JIRA问题追踪系统,来搜集bug报告和新功能请求。如果你对开发Hibernate有兴趣,请加入开发者的邮件列表。
|
||||
(Hibernate网站上的用户论坛有一个中文版面,您可以在那里交流问题与经验。)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
商业开发、产品支持和Hibernate培训可以通过JBoss Inc.获得。(请查阅:http://www.hibernate.org/SupportTraining/)。
|
||||
Hibernate是一个专业的开放源代码项目(Professional Open Source project),也是JBoss Enterprise Middleware System(JEMS),JBoss企业级中间件系统的一个核心组件。
|
||||
</para>
|
||||
|
||||
<sect1 id="preface-translate-comments-zh-cn">
|
||||
<title>翻译说明</title>
|
||||
|
||||
<para>
|
||||
本文档的翻译是在网络上协作进行的,也会不断根据Hibernate的升级进行更新。提供此文档的目的是为了减缓学习Hibernate的坡度,而非代替原文档。我们建议所有有能力的读者都直接阅读英文原文。若您对翻译有异议,或发现翻译错误,敬请不吝赐教,报告到如下email地址:cao at redsaga.com
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Hibernate版本3的翻译由满江红翻译团队(RedSaga Translate Team)集体进行,这也是一次大规模网络翻译的试验。在不到20天的时间内,我们完成了两百多页文档的翻译,这一成果是通过十几位网友集体努力完成的。通过这次翻译,我们也有了一套完整的流程,从初译、技术审核一直到文字审核、发布。我们的翻译团队还会继续完善我们的翻译流程,并翻译其他优秀的Java开源资料,敬请期待。
|
||||
</para>
|
||||
|
||||
<table frame="topbot" id="redsaga-translate-team">
|
||||
<title>
|
||||
Hibernate v3翻译团队
|
||||
</title>
|
||||
<tgroup cols="5" rowsep="1" colsep="1">
|
||||
<colspec colname="c1" colwidth="1*"/>
|
||||
<colspec colname="c2" colwidth="2*"/>
|
||||
<colspec colname="c3" colwidth="2*"/>
|
||||
<colspec colname="c4" colwidth="2*"/>
|
||||
<colspec colname="c5" colwidth="2*"/>
|
||||
<thead>
|
||||
<row>
|
||||
<entry align="center">
|
||||
序号
|
||||
</entry>
|
||||
<entry align="center">
|
||||
标题
|
||||
</entry>
|
||||
<entry align="center">
|
||||
中文标题
|
||||
</entry>
|
||||
<entry align="center">
|
||||
翻译
|
||||
</entry>
|
||||
<entry align="center">
|
||||
一审
|
||||
</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>#1</entry>
|
||||
<entry>Quickstart with Tomcat(*)</entry>
|
||||
<entry>在Tomcat中快速上手</entry>
|
||||
<entry>caoxiaogang</entry>
|
||||
<entry>zoujm</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#2</entry>
|
||||
<entry>Architecture</entry>
|
||||
<entry>体系结构</entry>
|
||||
<entry>Hilton(BJUG)</entry>
|
||||
<entry>厌倦发呆</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#3</entry>
|
||||
<entry>Configuration</entry>
|
||||
<entry>配置</entry>
|
||||
<entry>Goncha</entry>
|
||||
<entry>mochow</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#4</entry>
|
||||
<entry>Persistent Classes(*)</entry>
|
||||
<entry>持久化类</entry>
|
||||
<entry>caoxiaogang</entry>
|
||||
<entry>mochow</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#5</entry>
|
||||
<entry>Basic O/R Mapping</entry>
|
||||
<entry>对象/关系数据库映射基础(上)</entry>
|
||||
<entry>moxie</entry>
|
||||
<entry>Kingfish</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>对象/关系数据库映射基础(下)</entry>
|
||||
<entry>inter_dudu</entry>
|
||||
<entry>vincent</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#6</entry>
|
||||
<entry>Collection Mapping(*)</entry>
|
||||
<entry>集合类映射</entry>
|
||||
<entry>caoxiaogang</entry>
|
||||
<entry>robbin</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#7</entry>
|
||||
<entry>Association Mappings</entry>
|
||||
<entry>关联关系映射</entry>
|
||||
<entry>Robbin</entry>
|
||||
<entry>devils.advocate</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#8</entry>
|
||||
<entry>Component Mapping(*)</entry>
|
||||
<entry>组件映射</entry>
|
||||
<entry>caoxiaogang</entry>
|
||||
<entry>Robbin</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#9</entry>
|
||||
<entry>Inheritance Mappings</entry>
|
||||
<entry>继承映射</entry>
|
||||
<entry>morning(BJUG)</entry>
|
||||
<entry>mochow</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#10</entry>
|
||||
<entry>Working with objects</entry>
|
||||
<entry>与对象共事</entry>
|
||||
<entry>程广楠</entry>
|
||||
<entry>厌倦发呆</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#11</entry>
|
||||
<entry>Transactions And Concurrency</entry>
|
||||
<entry>事务和并发</entry>
|
||||
<entry>Robbin</entry>
|
||||
<entry>mochow</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#12</entry>
|
||||
<entry>Interceptors and events</entry>
|
||||
<entry>继承映射</entry>
|
||||
<entry>七彩狼(BJUG)</entry>
|
||||
<entry>厌倦发呆</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#13</entry>
|
||||
<entry>Batch processing</entry>
|
||||
<entry>批量处理</entry>
|
||||
<entry>Kingfish(BJUG)</entry>
|
||||
<entry>厌倦发呆</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#14</entry>
|
||||
<entry>HQL: The Hibernate Query Language</entry>
|
||||
<entry>HQL: Hibernate查询语言</entry>
|
||||
<entry>郑浩(BJUG)</entry>
|
||||
<entry>zhengshuai</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#15</entry>
|
||||
<entry>Criteria Queries</entry>
|
||||
<entry>条件查询</entry>
|
||||
<entry>nemo(BJUG)</entry>
|
||||
<entry>zhengshuai</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#16</entry>
|
||||
<entry>Native SQL</entry>
|
||||
<entry>Native SQL查询</entry>
|
||||
<entry>似水流年</entry>
|
||||
<entry>zoujm</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#17</entry>
|
||||
<entry>Filters</entry>
|
||||
<entry>过滤数据</entry>
|
||||
<entry>冰云(BJUG)</entry>
|
||||
<entry>Goncha</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#18</entry>
|
||||
<entry>XML Mapping</entry>
|
||||
<entry>XML映射</entry>
|
||||
<entry>edward(BJUG)</entry>
|
||||
<entry>Goncha</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#19</entry>
|
||||
<entry>Improving performance</entry>
|
||||
<entry>性能提升</entry>
|
||||
<entry>Wangjinfeng</entry>
|
||||
<entry>Robbin</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#20</entry>
|
||||
<entry>Toolset Guide(*)</entry>
|
||||
<entry>工具箱指南</entry>
|
||||
<entry>caoxiaogang</entry>
|
||||
<entry>Robbin</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#21</entry>
|
||||
<entry>Example: Parent/Child(*)</entry>
|
||||
<entry>示例:父子关系</entry>
|
||||
<entry>caoxiaogang</entry>
|
||||
<entry>devils.advocate</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#22</entry>
|
||||
<entry>Example: Weblog Application(*)</entry>
|
||||
<entry>示例:Weblog 应用程序</entry>
|
||||
<entry>caoxiaogang</entry>
|
||||
<entry>devils.advocate</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#23</entry>
|
||||
<entry>Example: Various Mappings</entry>
|
||||
<entry>示例:多种映射</entry>
|
||||
<entry>shidu(BJUG)</entry>
|
||||
<entry>冰云</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>#24</entry>
|
||||
<entry>Best Practices(*)</entry>
|
||||
<entry>最佳实践</entry>
|
||||
<entry>caoxiaogang</entry>
|
||||
<entry>冰云</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>
|
||||
关于我们
|
||||
</para>
|
||||
|
||||
|
||||
<variablelist spacing="compact">
|
||||
<varlistentry>
|
||||
<term>满江红.开源, http://www.redsaga.com</term>
|
||||
<listitem>
|
||||
<para>
|
||||
致力于Java开放源代码在中国的传播与发展。目前有两个团队,“OpenDoc团队”与“翻译团队”。OpenDoc团队已经推出包括Hibernate、iBatis、Spring、WebWork的多份开放文档,并于2005年5月在Hibernate开放文档基础上扩充成书,出版了原创书籍:《深入浅出Hibernate》,本书400余页,适合各个层次的Hibernate用户。(http://www.redsaga.com/hibernate_book.html)敬请支持。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>北京Java用户组, http://www.bjug.org</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Beiing Java User Group(http://www.bjug.org),民间技术交流组织,成立于2004年6月。以交流与共享为宗旨,每两周举行一次技术聚会活动。BJUG的目标是,通过小部分人的努力,形成一个技术社群,创建良好的交流氛围,并将新的技术和思想推广到整个IT界,让我们共同进步。
|
||||
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Java视线, http://www.javaeye.com</term>
|
||||
<listitem>
|
||||
<para>
|
||||
前身为Hibernate中文论坛,现在已发展为包括Java,.NET,python等各种技术的高质量论坛。Java视线论坛注重质量更甚于注重人气。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="preface-translate-licence-zh-cn">
|
||||
<title>版权声明</title>
|
||||
|
||||
<para>
|
||||
Hibernate英文文档属于Hibernate发行包的一部分,遵循LGPL协议。本翻译版本同样遵循LGPL协议。参与翻译的译者一致同意放弃除署名权外对本翻译版本的其它权利要求。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
您可以自由链接、下载、传播此文档,或者放置在您的网站上,甚至作为产品的一部分发行。但前提是必须保证全文完整转载,包括完整的版权信息和作译者声明,并不能违反LGPL协议。这里“完整”的含义是,不能进行任何删除/增添/注解。若有删除/增添/注解,必须逐段明确声明那些部分并非本文档的一部分。
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
</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>
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
<chapter id="architecture">
|
||||
|
||||
<title>体系结构(Architecture)</title>
|
||||
<sect1 id="architecture-overview" revision="1">
|
||||
<title>概况(Overview)</title>
|
||||
|
||||
<para>
|
||||
一个非常简要的Hibernate体系结构的概要图:
|
||||
</para>
|
||||
|
||||
<mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata fileref="images/overview.svg" format="SVG" align="center"/>
|
||||
</imageobject>
|
||||
<imageobject role="html">
|
||||
<imagedata fileref="../shared/images/overview.gif" format="GIF" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
|
||||
<para>
|
||||
从这个图可以看出,Hibernater使用数据库和配置信息来为应用程序提供持久化服务(以及持久的对象)。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
我们来更详细地看一下Hibernate运行时体系结构。由于Hibernate非常灵活,且支持数种应用方案,
|
||||
所以我们这只描述一下两种极端的情况。“轻型”的体系结构方案,要求应用程序提供自己的JDBC
|
||||
连接并管理自己的事务。这种方案使用了Hibernate API的最小子集:
|
||||
</para>
|
||||
|
||||
<mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata fileref="images/lite.svg" format="SVG" align="center"/>
|
||||
</imageobject>
|
||||
<imageobject role="html">
|
||||
<imagedata fileref="../shared/images/lite.gif" format="GIF" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
|
||||
<para>
|
||||
“全面解决”的体系结构方案,将应用层从底层的JDBC/JTA API中抽象出来,而让Hibernate来处理这些细节。
|
||||
</para>
|
||||
|
||||
<mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata fileref="images/full_cream.svg" format="SVG" align="center"/>
|
||||
</imageobject>
|
||||
<imageobject role="html">
|
||||
<imagedata fileref="../shared/images/full_cream.gif" format="GIF" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
|
||||
<para>
|
||||
图中各个对象的定义如下:
|
||||
|
||||
<variablelist spacing="compact">
|
||||
<varlistentry>
|
||||
<term>SessionFactory (<literal>org.hibernate.SessionFactory</literal>)</term>
|
||||
<listitem>
|
||||
<para>
|
||||
针对单个数据库映射关系经过编译后的内存镜像,它也是线程安全的(不可变)。
|
||||
它是生成<literal>Session</literal>的工厂,本身要用到<literal>ConnectionProvider</literal>。
|
||||
该对象可以在进程或集群的级别上,为那些事务之间可以重用的数据提供可选的二级缓存。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Session (<literal>org.hibernate.Session</literal>)</term>
|
||||
<listitem>
|
||||
<para>
|
||||
表示应用程序与持久储存层之间交互操作的一个单线程对象,此对象生存期很短。
|
||||
其隐藏了JDBC连接,也是<literal>Transaction</literal>的工厂。
|
||||
其会持有一个针对持久化对象的必选(第一级)缓存,在遍历对象图或者根据持久化标识查找对象时会用到。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>持久的对象及其集合</term>
|
||||
<listitem>
|
||||
<para>
|
||||
带有持久化状态的、具有业务功能的单线程对象,此对象生存期很短。
|
||||
这些对象可以是普通的JavaBeans/POJO,唯一特殊的是他们正与(仅仅一个)<literal>Session</literal>相关联。
|
||||
这个<literal>Session</literal>被关闭的同时,这些对象也会脱离持久化状态,可以被应用程序的任何层自由使用。
|
||||
(例如,用作跟表示层打交道的数据传输对象data transfer object。)
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>瞬态(transient)以及脱管(detached)的对象及其集合</term>
|
||||
<listitem>
|
||||
<para>
|
||||
持久类的没有与<literal>Session</literal>相关联的实例。
|
||||
他们可能是在被应用程序实例化后,尚未进行持久化的对象。
|
||||
也可能是因为实例化他们的<literal>Session</literal>已经被关闭而脱离持久化的对象。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>事务Transaction (<literal>org.hibernate.Transaction</literal>)</term>
|
||||
<listitem>
|
||||
<para>
|
||||
(可选的)应用程序用来指定原子操作单元范围的对象,它是单线程的,生存期很短。
|
||||
它通过抽象将应用从底层具体的JDBC、JTA以及CORBA事务隔离开。
|
||||
某些情况下,一个<literal>Session</literal>之内可能包含多个<literal>Transaction</literal>对象。
|
||||
尽管是否使用该对象是可选的,但是事务边界的开启与关闭(无论是使用底层的API还是使用<literal>Transaction</literal>对象)是必不可少的。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>ConnectionProvider (<literal>org.hibernate.connection.ConnectionProvider</literal>)</term>
|
||||
<listitem>
|
||||
<para>
|
||||
(可选的)生成JDBC连接的工厂(同时也起到连接池的作用)。
|
||||
它通过抽象将应用从底层的<literal>Datasource</literal>或<literal>DriverManager</literal>隔离开。
|
||||
仅供开发者扩展/实现用,并不暴露给应用程序使用。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>TransactionFactory (<literal>org.hibernate.TransactionFactory</literal>)</term>
|
||||
<listitem>
|
||||
<para>
|
||||
(可选的)生成<literal>Transaction</literal>对象实例的工厂。
|
||||
仅供开发者扩展/实现用,并不暴露给应用程序使用。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><emphasis>扩展接口</emphasis></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Hibernate提供了很多可选的扩展接口,你可以通过实现它们来定制你的持久层的行为。
|
||||
具体请参考API文档。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
在一个“轻型”的体系结构中,应用程序可能绕过
|
||||
<literal>Transaction</literal>/<literal>TransactionFactory</literal> 以及
|
||||
<literal>ConnectionProvider</literal> 等API直接跟JTA或JDBC打交道。
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="architecture-states" revision="1">
|
||||
<title>实例状态</title>
|
||||
<para>
|
||||
一个持久化类的实例可能处于三种不同状态中的某一种。
|
||||
这三种状态的定义则与所谓的<emphasis>持久化上下文(persistence context)</emphasis>有关。
|
||||
Hibernate的<literal>Session</literal>对象就是这个所谓的持久化上下文:
|
||||
</para>
|
||||
|
||||
<variablelist spacing="compact">
|
||||
<varlistentry>
|
||||
<term>瞬态(transient)</term>
|
||||
<listitem>
|
||||
<para>
|
||||
该实例从未与任何持久化上下文关联过。它没有持久化标识(相当于主键)。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>持久(persistent)</term>
|
||||
<listitem>
|
||||
<para>
|
||||
实例目前与某个持久化上下文有关联。
|
||||
它拥有持久化标识(相当于主键),并且可能在数据库中有一个对应的行。
|
||||
对于某一个特定的持久化上下文,Hibernate<emphasis>保证</emphasis>持久化标识与Java标识(其值代表对象在内存中的位置)等价。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>脱管(detached)</term>
|
||||
<listitem>
|
||||
<para>
|
||||
实例曾经与某个持久化上下文发生过关联,不过那个上下文被关闭了,
|
||||
或者这个实例是被序列化(serialize)到这个进程来的。
|
||||
它拥有持久化标识,并且在数据库中可能存在一个对应的行。
|
||||
对于脱管状态的实例,Hibernate不保证任何持久化标识和Java标识的关系。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="architecture-jmx" revision="1">
|
||||
<title>JMX整合</title>
|
||||
|
||||
<para>
|
||||
JMX是管理Java组件(Java components)的J2EE规范。 Hibernate 可以通过一个JMX标准服务来管理。
|
||||
在这个发行版本中,我们提供了一个MBean接口的实现,即
|
||||
<literal>org.hibernate.jmx.HibernateService</literal>。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
想要看如何在JBoss应用服务器上将Hibernate部署为一个JMX服务的例子,您可以参考JBoss用户指南。
|
||||
我们现在说一下在Jboss应用服务器上,使用JMX来部署Hibernate的好处:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>Session管理:</emphasis> Hibernate的<literal>Session</literal>对象的生命周期可以
|
||||
自动跟一个JTA事务边界绑定。这意味着你无需手工开关<literal>Session</literal>了, 这项
|
||||
工作会由JBoss EJB 拦截器来完成。你再也不用担心你的代码中的事务边界了(除非你想利用Hibernate提供
|
||||
的<literal>Transaction</literal> API来自己写一个便于移植的的持久层)。
|
||||
你现在要通过 <literal>HibernateContext</literal>来操作<literal>Session</literal>了。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>HAR 部署:</emphasis> 通常情况下,你会使用JBoss的服务部署描述符(在EAR或/和SAR文件中)来部署Hibernate JMX服务。
|
||||
这种部署方式支持所有常见的Hibernate <literal>SessionFactory</literal>的配置选项。
|
||||
不过,你需在部署描述符中,列出你所有的映射文件的名字。如果你使用HAR部署方式, JBoss
|
||||
会自动探测出你的HAR文件中所有的映射文件。
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
这些选项更多的描述,请参考JBoss 应用程序用户指南。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
将Hibernate以部署为JMX服务的另一个好处,是可以查看Hibernate的运行时统计信息。参看
|
||||
<xref linkend="configuration-optional-statistics"/>.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="architecture-jca" revision="1">
|
||||
<title>对JCA的支持</title>
|
||||
<para>
|
||||
Hibernate也可以被配置为一个JCA连接器(JCA connector)。更多信息请参看网站。
|
||||
请注意,Hibernate对JCA的支持,仍处于实验性质。
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
|
@ -0,0 +1,501 @@
|
|||
<chapter id="associations">
|
||||
|
||||
<title>关联关系映射</title>
|
||||
|
||||
<sect1 id="assoc-intro" revision="1">
|
||||
<title>介绍</title>
|
||||
|
||||
<para>
|
||||
关联关系映射通常情况是最难配置正确的。在这个部分中,我们从单向关系映射开始,然后考虑双向关系映射,由浅至深讲述一遍典型的案例。在所有的例子中,我们都使用 <literal>Person</literal>和<literal>Address</literal>。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
我们根据映射关系是否涉及连接表以及多样性来划分关联类型。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
在传统的数据建模中,允许为Null值的外键被认为是一种不好的实践,因此我们所有的例子中都使用不允许为Null的外键。这并不是Hibernate的要求,即使你删除掉不允许为Null的约束,Hibernate映射一样可以工作的很好。
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="assoc-unidirectional" revision="1">
|
||||
<title>单向关联(Unidirectional associations)</title>
|
||||
|
||||
<sect2 id="assoc-unidirectional-m21">
|
||||
<title>多对一(many to one)</title>
|
||||
|
||||
<para>
|
||||
<emphasis>单向many-to-one关联</emphasis>是最常见的单向关联关系。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id" column="personId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<many-to-one name="address"
|
||||
column="addressId"
|
||||
not-null="true"/>
|
||||
</class>
|
||||
|
||||
<class name="Address">
|
||||
<id name="id" column="addressId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
</class>]]></programlisting>
|
||||
<programlisting><![CDATA[
|
||||
create table Person ( personId bigint not null primary key, addressId bigint not null )
|
||||
create table Address ( addressId bigint not null primary key )
|
||||
]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="assoc-unidirectional-121">
|
||||
<title>一对一(one to one)</title>
|
||||
|
||||
<para>
|
||||
<emphasis>基于外键关联的单向一对一关联</emphasis>和<emphasis>单向多对一关联</emphasis>几乎是一样的。唯一的不同就是单向一对一关联中的外键字段具有唯一性约束。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id" column="personId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<many-to-one name="address"
|
||||
column="addressId"
|
||||
unique="true"
|
||||
not-null="true"/>
|
||||
</class>
|
||||
|
||||
<class name="Address">
|
||||
<id name="id" column="addressId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
</class>]]></programlisting>
|
||||
<programlisting><![CDATA[
|
||||
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
|
||||
create table Address ( addressId bigint not null primary key )
|
||||
]]></programlisting>
|
||||
|
||||
<para>
|
||||
<emphasis>基于主键关联的单向一对一关联</emphasis>通常使用一个特定的id生成器。(请注意,在这个例子中我们掉换了关联的方向。)
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id" column="personId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
</class>
|
||||
|
||||
<class name="Address">
|
||||
<id name="id" column="personId">
|
||||
<generator class="foreign">
|
||||
<param name="property">person</param>
|
||||
</generator>
|
||||
</id>
|
||||
<one-to-one name="person" constrained="true"/>
|
||||
</class>]]></programlisting>
|
||||
<programlisting><![CDATA[
|
||||
create table Person ( personId bigint not null primary key )
|
||||
create table Address ( personId bigint not null primary key )
|
||||
]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="assoc-unidirectional-12m">
|
||||
<title>一对多(one to many)</title>
|
||||
|
||||
<para>
|
||||
<emphasis>基于外键关联的单向一对多关联</emphasis>是一种很少见的情况,并不推荐使用。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id" column="personId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<set name="addresses">
|
||||
<key column="personId"
|
||||
not-null="true"/>
|
||||
<one-to-many class="Address"/>
|
||||
</set>
|
||||
</class>
|
||||
|
||||
<class name="Address">
|
||||
<id name="id" column="addressId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
</class>]]></programlisting>
|
||||
<programlisting><![CDATA[
|
||||
create table Person ( personId bigint not null primary key )
|
||||
create table Address ( addressId bigint not null primary key, personId bigint not null )
|
||||
]]></programlisting>
|
||||
|
||||
<para>
|
||||
我们认为对于这种关联关系最好使用连接表。
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="assoc-unidirectional-join" revision="1">
|
||||
<title>使用连接表的单向关联(Unidirectional associations with join tables)</title>
|
||||
|
||||
<sect2 id="assoc-unidirectional-join-12m">
|
||||
<title>一对多(one to many)</title>
|
||||
|
||||
<para>
|
||||
<emphasis>基于连接表的单向一对多关联</emphasis> 应该优先被采用。请注意,通过指定<literal>unique="true"</literal>,我们可以把多样性从多对多改变为一对多。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id" column="personId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<set name="addresses" table="PersonAddress">
|
||||
<key column="personId"/>
|
||||
<many-to-many column="addressId"
|
||||
unique="true"
|
||||
class="Address"/>
|
||||
</set>
|
||||
</class>
|
||||
|
||||
<class name="Address">
|
||||
<id name="id" column="addressId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
</class>]]></programlisting>
|
||||
<programlisting><![CDATA[
|
||||
create table Person ( personId bigint not null primary key )
|
||||
create table PersonAddress ( personId not null, addressId bigint not null primary key )
|
||||
create table Address ( addressId bigint not null primary key )
|
||||
]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="assoc-unidirectional-join-m21">
|
||||
<title>多对一(many to one)</title>
|
||||
|
||||
<para>
|
||||
<emphasis>基于连接表的单向多对一关联</emphasis>在关联关系可选的情况下应用也很普遍。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id" column="personId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<join table="PersonAddress"
|
||||
optional="true">
|
||||
<key column="personId" unique="true"/>
|
||||
<many-to-one name="address"
|
||||
column="addressId"
|
||||
not-null="true"/>
|
||||
</join>
|
||||
</class>
|
||||
|
||||
<class name="Address">
|
||||
<id name="id" column="addressId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
</class>]]></programlisting>
|
||||
<programlisting><![CDATA[
|
||||
create table Person ( personId bigint not null primary key )
|
||||
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
|
||||
create table Address ( addressId bigint not null primary key )
|
||||
]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="assoc-unidirectional-join-121">
|
||||
<title>一对一(one to one)</title>
|
||||
|
||||
<para>
|
||||
<emphasis>基于连接表的单向一对一关联</emphasis>非常少见,但也是可行的。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id" column="personId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<join table="PersonAddress"
|
||||
optional="true">
|
||||
<key column="personId"
|
||||
unique="true"/>
|
||||
<many-to-one name="address"
|
||||
column="addressId"
|
||||
not-null="true"
|
||||
unique="true"/>
|
||||
</join>
|
||||
</class>
|
||||
|
||||
<class name="Address">
|
||||
<id name="id" column="addressId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
</class>]]></programlisting>
|
||||
<programlisting><![CDATA[
|
||||
create table Person ( personId bigint not null primary key )
|
||||
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
|
||||
create table Address ( addressId bigint not null primary key )
|
||||
]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="assoc-unidirectional-join-m2m">
|
||||
<title>多对多(many to many)</title>
|
||||
|
||||
<para>
|
||||
最后,还有 <emphasis>单向多对多关联</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>双向关联(Bidirectional associations)</title>
|
||||
|
||||
<sect2 id="assoc-bidirectional-m21">
|
||||
<title>一对多(one to many) / 多对一(many to one)</title>
|
||||
|
||||
<para>
|
||||
<emphasis>双向多对一关联</emphasis> 是最常见的关联关系。(这也是标准的父/子关联关系。)
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id" column="personId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<many-to-one name="address"
|
||||
column="addressId"
|
||||
not-null="true"/>
|
||||
</class>
|
||||
|
||||
<class name="Address">
|
||||
<id name="id" column="addressId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<set name="people" inverse="true">
|
||||
<key column="addressId"/>
|
||||
<one-to-many class="Person"/>
|
||||
</set>
|
||||
</class>]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[
|
||||
create table Person ( personId bigint not null primary key, addressId bigint not null )
|
||||
create table Address ( addressId bigint not null primary key )
|
||||
]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="assoc-bidirectional-121">
|
||||
<title>一对一(one to one)</title>
|
||||
|
||||
<para>
|
||||
<emphasis>基于外键关联的双向一对一关联</emphasis>也很常见。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id" column="personId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<many-to-one name="address"
|
||||
column="addressId"
|
||||
unique="true"
|
||||
not-null="true"/>
|
||||
</class>
|
||||
|
||||
<class name="Address">
|
||||
<id name="id" column="addressId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<one-to-one name="person"
|
||||
property-ref="address"/>
|
||||
</class>]]></programlisting>
|
||||
<programlisting><![CDATA[
|
||||
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
|
||||
create table Address ( addressId bigint not null primary key )
|
||||
]]></programlisting>
|
||||
|
||||
<para>
|
||||
<emphasis>基于主键关联的一对一关联</emphasis>需要使用特定的id生成器。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id" column="personId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<one-to-one name="address"/>
|
||||
</class>
|
||||
|
||||
<class name="Address">
|
||||
<id name="id" column="personId">
|
||||
<generator class="foreign">
|
||||
<param name="property">person</param>
|
||||
</generator>
|
||||
</id>
|
||||
<one-to-one name="person"
|
||||
constrained="true"/>
|
||||
</class>]]></programlisting>
|
||||
<programlisting><![CDATA[
|
||||
create table Person ( personId bigint not null primary key )
|
||||
create table Address ( personId bigint not null primary key )
|
||||
]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="assoc-bidirectional-join" revision="1">
|
||||
<title>使用连接表的双向关联(Bidirectional associations with join tables)</title>
|
||||
|
||||
<sect2 id="assoc-bidirectional-join-12m">
|
||||
<title>一对多(one to many) /多对一( many to one)</title>
|
||||
|
||||
<para>
|
||||
<emphasis>基于连接表的双向一对多关联</emphasis>。注意<literal>inverse="true"</literal>可以出现在关联的任意一端,即collection端或者join端。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id" column="personId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<set name="addresses"
|
||||
table="PersonAddress">
|
||||
<key column="personId"/>
|
||||
<many-to-many column="addressId"
|
||||
unique="true"
|
||||
class="Address"/>
|
||||
</set>
|
||||
</class>
|
||||
|
||||
<class name="Address">
|
||||
<id name="id" column="addressId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<join table="PersonAddress"
|
||||
inverse="true"
|
||||
optional="true">
|
||||
<key column="addressId"/>
|
||||
<many-to-one name="person"
|
||||
column="personId"
|
||||
not-null="true"/>
|
||||
</join>
|
||||
</class>]]></programlisting>
|
||||
<programlisting><![CDATA[
|
||||
create table Person ( personId bigint not null primary key )
|
||||
create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
|
||||
create table Address ( addressId bigint not null primary key )
|
||||
]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="assoc-bidirectional-join-121">
|
||||
<title>一对一(one to one)</title>
|
||||
|
||||
<para>
|
||||
<emphasis>基于连接表的双向一对一关联</emphasis>极为罕见,但也是可行的。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id" column="personId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<join table="PersonAddress"
|
||||
optional="true">
|
||||
<key column="personId"
|
||||
unique="true"/>
|
||||
<many-to-one name="address"
|
||||
column="addressId"
|
||||
not-null="true"
|
||||
unique="true"/>
|
||||
</join>
|
||||
</class>
|
||||
|
||||
<class name="Address">
|
||||
<id name="id" column="addressId">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<join table="PersonAddress"
|
||||
optional="true"
|
||||
inverse="true">
|
||||
<key column="addressId"
|
||||
unique="true"/>
|
||||
<many-to-one name="address"
|
||||
column="personId"
|
||||
not-null="true"
|
||||
unique="true"/>
|
||||
</join>
|
||||
</class>]]></programlisting>
|
||||
<programlisting><![CDATA[
|
||||
create table Person ( personId bigint not null primary key )
|
||||
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
|
||||
create table Address ( addressId bigint not null primary key )
|
||||
]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="assoc-bidirectional-join-m2m">
|
||||
<title>多对多(many to many)</title>
|
||||
|
||||
<para>
|
||||
最后,还有 <emphasis>双向多对多关联</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>
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
<chapter id="batch">
|
||||
<title>批量处理(Batch processing)</title>
|
||||
<para>
|
||||
使用Hibernate将 100 000 条记录插入到数据库的一个很自然的做法可能是这样的
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Session session = sessionFactory.openSession();
|
||||
Transaction tx = session.beginTransaction();
|
||||
for ( int i=0; i<100000; i++ ) {
|
||||
Customer customer = new Customer(.....);
|
||||
session.save(customer);
|
||||
}
|
||||
tx.commit();
|
||||
session.close();]]></programlisting>
|
||||
|
||||
<para>
|
||||
这段程序大概运行到 50 000 条记录左右会失败并抛出 <literal>内存溢出异常(OutOfMemoryException)</literal> 。
|
||||
这是因为 Hibernate 把所有新插入的 <literal>客户(Customer)</literal>实例在 session级别的缓存区进行了缓存的缘故。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
我们会在本章告诉你如何避免此类问题。首先,如果你要执行批量处理并且想要达到一个理想的性能,
|
||||
那么使用JDBC的批量(batching)功能是至关重要。将JDBC的批量抓取数量(batch size)参数设置到一个合适值
|
||||
(比如,10-50之间):
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
|
||||
|
||||
<para>
|
||||
你也可能想在执行批量处理时关闭二级缓存:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
|
||||
|
||||
<sect1 id="batch-inserts">
|
||||
<title>批量插入(Batch inserts)</title>
|
||||
|
||||
<para>
|
||||
如果要将很多对象持久化,你必须通过经常的调用 <literal>flush()</literal> 以及稍后调用
|
||||
<literal>clear()</literal> 来控制第一级缓存的大小。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Session session = sessionFactory.openSession();
|
||||
Transaction tx = session.beginTransaction();
|
||||
|
||||
for ( int i=0; i<100000; i++ ) {
|
||||
Customer customer = new Customer(.....);
|
||||
session.save(customer);
|
||||
if ( i % 20 == 0 ) { //20, same as the JDBC batch size //20,与JDBC批量设置相同
|
||||
//flush a batch of inserts and release memory:
|
||||
//将本批插入的对象立即写入数据库并释放内存
|
||||
session.flush();
|
||||
session.clear();
|
||||
}
|
||||
}
|
||||
|
||||
tx.commit();
|
||||
session.close();]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="batch-update" >
|
||||
<title>批量更新(Batch updates)</title>
|
||||
|
||||
<para>
|
||||
此方法同样适用于检索和更新数据。此外,在进行会返回很多行数据的查询时,
|
||||
你需要使用 <literal>scroll()</literal> 方法以便充分利用服务器端游标所带来的好处。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Session session = sessionFactory.openSession();
|
||||
Transaction tx = session.beginTransaction();
|
||||
|
||||
ScrollableResults customers = session.getNamedQuery("GetCustomers")
|
||||
.setCacheMode(CacheMode.IGNORE)
|
||||
.scroll(ScrollMode.FORWARD_ONLY);
|
||||
int count=0;
|
||||
while ( customers.next() ) {
|
||||
Customer customer = (Customer) customers.get(0);
|
||||
customer.updateStuff(...);
|
||||
if ( ++count % 20 == 0 ) {
|
||||
//flush a batch of updates and release memory:
|
||||
session.flush();
|
||||
session.clear();
|
||||
}
|
||||
}
|
||||
|
||||
tx.commit();
|
||||
session.close();]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="batch-direct">
|
||||
<title>大批量更新/删除(Bulk update/delete)</title>
|
||||
|
||||
<para>
|
||||
就像已经讨论的那样,自动和透明的 对象/关系 映射(object/relational mapping)关注于管理对象的状态。
|
||||
这就意味着对象的状态存在于内存,因此直接更新或者删除 (使用 SQL 语句 <literal>UPDATE</literal> 和
|
||||
<literal>DELETE</literal>) 数据库中的数据将不会影响内存中的对象状态和对象数据。
|
||||
不过,Hibernate提供通过Hibernate查询语言(<xref linkend="queryhql">HQL</xref>)来执行大批
|
||||
量SQL风格的(<literal>UPDATE</literal>)和(<literal>DELETE</literal>) 语句的方法。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literal>UPDATE</literal> 和 <literal>DELETE</literal>语句的语法为:
|
||||
<literal>( UPDATE | DELETE ) FROM? ClassName (WHERE WHERE_CONDITIONS)?</literal>。
|
||||
有几点说明:
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
在FROM子句(from-clause)中,FROM关键字是可选的
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
在FROM子句(from-clause)中只能有一个类名,并且它<emphasis>不能</emphasis>有别名
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
不能在大批量HQL语句中使用连接(显式或者隐式的都不行)。不过在WHERE子句中可以使用子查询。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
整个WHERE子句是可选的。
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
举个例子,使用<literal>Query.executeUpdate()</literal>方法执行一个HQL
|
||||
<literal>UPDATE</literal>语句:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Session session = sessionFactory.openSession();
|
||||
Transaction tx = session.beginTransaction();
|
||||
|
||||
String hqlUpdate = "update Customer set name = :newName where name = :oldName";
|
||||
int updatedEntities = s.createQuery( hqlUpdate )
|
||||
.setString( "newName", newName )
|
||||
.setString( "oldName", oldName )
|
||||
.executeUpdate();
|
||||
tx.commit();
|
||||
session.close();]]></programlisting>
|
||||
|
||||
<para>
|
||||
执行一个HQL <literal>DELETE</literal>,同样使用 <literal>Query.executeUpdate()</literal> 方法
|
||||
(此方法是为 那些熟悉JDBC <literal>PreparedStatement.executeUpdate()</literal> 的人们而设定的)
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Session session = sessionFactory.openSession();
|
||||
Transaction tx = session.beginTransaction();
|
||||
|
||||
String hqlDelete = "delete Customer where name = :oldName";
|
||||
int deletedEntities = s.createQuery( hqlDelete )
|
||||
.setString( "oldName", oldName )
|
||||
.executeUpdate();
|
||||
tx.commit();
|
||||
session.close();]]></programlisting>
|
||||
|
||||
<para>
|
||||
由<literal>Query.executeUpdate()</literal>方法返回的<literal>整型</literal>值表明了受此操作影响的记录数量。
|
||||
注意这个数值可能与数据库中被(最后一条SQL语句)影响了的“行”数有关,也可能没有。一个大批量HQL操作可能导致多条实际的SQL语句被执行,
|
||||
举个例子,对joined-subclass映射方式的类进行的此类操作。这个返回值代表了实际被语句影响了的记录数量。在那个joined-subclass的例子中,
|
||||
对一个子类的删除实际上可能不仅仅会删除子类映射到的表而且会影响“根”表,还有可能影响与之有继承关系的joined-subclass映射方式的子类的表。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
注意,上述大批量HQL操作的少数限制会在新版本中得到改进;进一步详细信息请参考JIRA里的路线图(roadmap)。
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
|
@ -0,0 +1,148 @@
|
|||
<chapter id="best-practices" revision="2">
|
||||
<title>最佳实践(Best Practices)</title>
|
||||
|
||||
<variablelist spacing="compact">
|
||||
<varlistentry>
|
||||
<term>设计细颗粒度的持久类并且使用<literal><component></literal>来实现映射。</term>
|
||||
<listitem>
|
||||
<para>
|
||||
使用一个<literal>Address</literal>持久类来封装 <literal>street</literal>,
|
||||
<literal>suburb</literal>, <literal>state</literal>, <literal>postcode</literal>.
|
||||
这将有利于代码重用和简化代码重构(refactoring)的工作。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>对持久类声明标识符属性。</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Hibernate中标识符属性是可选的,不过有很多原因来说明你应该使用标识符属性。我们建议标识符应该是“人造”的(自动生成,不涉及业务含义)。虽然原生类型从语法上可能更易于使用,但使用<literal>long</literal>或<literal>java.lang.Long</literal>没有任何区别,。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>为每个持久类写一个映射文件</term>
|
||||
<listitem>
|
||||
<para>
|
||||
不要把所有的持久类映射都写到一个大文件中。把 <literal>com.eg.Foo</literal> 映射到<literal>com/eg/Foo.hbm.xml</literal>中, 在团队开发环境中,这一点显得特别有意义。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>把映射文件作为资源加载</term>
|
||||
<listitem>
|
||||
<para>
|
||||
把映射文件和他们的映射类放在一起进行部署。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>考虑把查询字符串放在程序外面</term>
|
||||
<listitem>
|
||||
<para>
|
||||
如果你的查询中调用了非ANSI标准的SQL函数,那么这条实践经验对你适用。把查询字符串放在映射文件中可以让程序具有更好的可移植性。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>使用绑定变量</term>
|
||||
<listitem>
|
||||
<para>
|
||||
就像在JDBC编程中一样,应该总是用占位符"?"来替换非常量值,不要在查询中用字符串值来构造非常量值!更好的办法是在查询中使用命名参数。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>不要自己来管理JDBC connections</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Hibernate允许应用程序自己来管理JDBC connections,但是应该作为最后没有办法的办法。如果你不能使用Hibernate内建的connections providers,那么考虑实现自己来实现<literal>org.hibernate.connection.ConnectionProvider</literal>
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>考虑使用用户自定义类型(custom type)</term>
|
||||
<listitem>
|
||||
<para>
|
||||
假设你有一个Java类型,来自某些类库,需要被持久化,但是该类没有提供映射操作需要的存取方法。那么你应该考虑实现<literal>net.sf.hibernate.UserType</literal>接口。这种办法使程序代码写起来更加自如,不再需要考虑类与Hibernate type之间的相互转换。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>在性能瓶颈的地方使用硬编码的JDBC</term>
|
||||
<listitem>
|
||||
<para>
|
||||
在对性能要求很严格的一些系统中,一些操作(例如批量更新和批量删除)也许直接使用JDBC会更好,但是请先<emphasis>搞清楚</emphasis>这是否是一个瓶颈,并且不要想当然认为JDBC一定会更快。如果确实需要直接使用JDBC,那么最好打开一个
|
||||
Hibernate <literal>Session</literal> 然后从 <literal>Session</literal>获得connection,按照这种办法你仍然可以使用同样的transaction策略和底层的connection provider。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>理解<literal>Session</literal>清洗( flushing)</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Session会不时的向数据库同步持久化状态,如果这种操作进行的过于频繁,性能会受到一定的影响。有时候你可以通过禁止自动flushing,尽量最小化非必要的flushing操作,或者更进一步,在一个特定的transaction中改变查询和其它操作的顺序。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>在三层结构中,考虑使用 <literal>saveOrUpdate()</literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
当使用一个servlet / session bean 类型的架构的时候, 你可以把已加载的持久对象在session bean层和servlet / JSP 层之间来回传递。使用新的session来为每个请求服务,使用 <literal>Session.update()</literal> 或者<literal>Session.saveOrUpdate()</literal>来更新对象的持久状态。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>在两层结构中,考虑断开session.</term>
|
||||
<listitem>
|
||||
<para>
|
||||
为了得到最佳的可伸缩性,数据库事务(Database Transaction)应该尽可能的短。但是,程序常常需要实现长时间运行的“应用程序事务(Application Transaction)”,包含一个从用户的观点来看的原子操作。这个应用程序事务可能跨越多次从用户请求到得到反馈的循环。请使用脱管对象(与session脱离的对象),或者在两层结构中,把Hibernate Session从JDBC连接中脱离开,下次需要用的时候再连接上。绝不要把一个Session用在多个应用程序事务(Application Transaction)中,否则你的数据可能会过期失效。
|
||||
</para>
|
||||
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>不要把异常看成可恢复的</term>
|
||||
<listitem>
|
||||
<para>
|
||||
这一点甚至比“最佳实践”还要重要,这是“必备常识”。当异常发生的时候,必须要回滚 <literal>Transaction</literal> ,关闭<literal>Session</literal>。如果你不这样做的话,Hibernate无法保证内存状态精确的反应持久状态。尤其不要使用<literal>Session.load()</literal>来判断一个给定标识符的对象实例在数据库中是否存在,应该使用<literal>find()</literal>。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>对于关联优先考虑lazy fetching </term>
|
||||
<listitem>
|
||||
<para>
|
||||
谨慎的使用主动外连接抓取(eager (outer-join) fetching)。对于大多数没有JVM级别缓存的持久对象的关联,应该使用代理(proxies)或者具有延迟加载属性的集合(lazy collections)。对于被缓存的对象的关联,尤其是缓存的命中率非常高的情况下,应该使用<literal>outer-join="false"</literal>,显式的禁止掉eager fetching。如果那些特殊的确实适合使用outer-join fetch 的场合,请在查询中使用<literal>left join</literal>。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>考虑把Hibernate代码从业务逻辑代码中抽象出来</term>
|
||||
<listitem>
|
||||
<para>
|
||||
把Hibernate的数据存取代码隐藏到接口(interface)的后面,组合使用<emphasis>DAO</emphasis>和<emphasis>Thread Local Session</emphasis>模式。通过Hibernate的<literal>UserType</literal>,你甚至可以用硬编码的JDBC来持久化那些本该被Hibernate持久化的类。 (该建议更适用于规模足够大应用软件中,对于那些只有5张表的应用程序并不适合。)
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>使用与业务有关的键值来实现<literal>equals()</literal>和 <literal>hashCode()</literal> .</term>
|
||||
<listitem>
|
||||
<para>
|
||||
如果你在Session外比较对象,你必须要实现<literal>equals()</literal>和 <literal>hashCode()</literal>。在Session内部,Java的对象识别机制是可以保证的。如果你实现了这些方法,不要再使用数据库(主键)辨识!瞬时对象不具有(数据库)标识值,Hibernate会在对象被保存的时候赋予它一个值。如果对象在被保存的时候位于Set内,hash code就会变化,要约就被违背。为了实现用与业务有关的键值编写<literal>equals()</literal>和 <literal>hashCode()</literal>,你应该使用类属性的唯一组合。记住,这个键值只是当对象位于Set内部时才需要保证稳定且唯一,并不是在其整个生命周期中都需要(不需要达到数据库主键这样的稳定性)。绝不要在<literal>equals()</literal>中比较集合(要考虑延迟装载),并且小心对待其他可能被代理过的类。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>不要用怪异的连接映射</term>
|
||||
<listitem>
|
||||
<para>
|
||||
多对多连接用得好的例子实际上相当少见。大多数时候你在“连接表”中需要保存额外的信息。这种情况下,用两个指向中介类的一对多的连接比较好。实际上,我们认为绝大多数的连接是一对多和多对一的,你应该谨慎使用其它连接风格,用之前问自己一句,是否真的必须这么做。
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</chapter>
|
||||
|
|
@ -0,0 +1,343 @@
|
|||
<chapter id="components">
|
||||
<title>组件(Component)映射</title>
|
||||
|
||||
<para>
|
||||
<emphasis>Component</emphasis>这个概念在Hibernate中几处不同的地方为了不同的目的被重复使用.
|
||||
</para>
|
||||
|
||||
<sect1 id="components-dependentobjects">
|
||||
<title>依赖对象(Dependent objects)</title>
|
||||
|
||||
<para>
|
||||
Component是一个被包含的对象,它作为值类型被持久化,而非一个被引用的实体。“component(组件)”这一术语指的是面向对象的合成概念(而并不是系统构架层次上的组件的概念)举个例子, 你可以对人(Person)如以下这样来建模:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[public class Person {
|
||||
private java.util.Date birthday;
|
||||
private Name name;
|
||||
private String key;
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
private void setKey(String key) {
|
||||
this.key=key;
|
||||
}
|
||||
public java.util.Date getBirthday() {
|
||||
return birthday;
|
||||
}
|
||||
public void setBirthday(java.util.Date birthday) {
|
||||
this.birthday = birthday;
|
||||
}
|
||||
public Name getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(Name name) {
|
||||
this.name = name;
|
||||
}
|
||||
......
|
||||
......
|
||||
}]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[public class Name {
|
||||
char initial;
|
||||
String first;
|
||||
String last;
|
||||
public String getFirst() {
|
||||
return first;
|
||||
}
|
||||
void setFirst(String first) {
|
||||
this.first = first;
|
||||
}
|
||||
public String getLast() {
|
||||
return last;
|
||||
}
|
||||
void setLast(String last) {
|
||||
this.last = last;
|
||||
}
|
||||
public char getInitial() {
|
||||
return initial;
|
||||
}
|
||||
void setInitial(char initial) {
|
||||
this.initial = initial;
|
||||
}
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
现在,<literal>姓名(Name)</literal>是作为<literal>人(Person)</literal>的一个组成部分。需要注意的是:需要对<literal>姓名</literal>
|
||||
的持久化属性定义getter和setter方法,但是不需要实现任何的接口或申明标识符字段。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
以下是这个例子的Hibernate映射文件:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="eg.Person" table="person">
|
||||
<id name="Key" column="pid" type="string">
|
||||
<generator class="uuid.hex"/>
|
||||
</id>
|
||||
<property name="birthday" type="date"/>
|
||||
<component name="Name" class="eg.Name"> <!-- class attribute optional -->
|
||||
<property name="initial"/>
|
||||
<property name="first"/>
|
||||
<property name="last"/>
|
||||
</component>
|
||||
</class>]]></programlisting>
|
||||
|
||||
<para>
|
||||
人员(Person)表中将包括<literal>pid</literal>,
|
||||
<literal>birthday</literal>,
|
||||
<literal>initial</literal>,
|
||||
<literal>first</literal>和
|
||||
<literal>last</literal>等字段。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
就像所有的值类型一样, Component不支持共享引用。
|
||||
换句话说,两个人可能重名,但是两个person对象应该包含两个独立的name对象,只不过是具有“同样”的值。
|
||||
Component的值为空从语义学上来讲是<emphasis>专有的(ad hoc)</emphasis>。 每当
|
||||
重新加载一个包含组件的对象,如果component的所有字段为空,那么将Hibernate将假定整个component为
|
||||
空。对于绝大多数目的,这样假定是没有问题的。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Component的属性可以是Hibernate类型(包括Collections, many-to-one 关联, 以及其它Component
|
||||
等等)。嵌套Component不应该作为特殊的应用被考虑(Nested components should not be considered
|
||||
an exotic usage)。 Hibernate趋向于支持设计细致(fine-grained)的对象模型。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literal><component></literal> 元素还允许有 <literal><parent></literal>子元素 ,用来表明component类中的一个属性返回包含它的实体的引用。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="eg.Person" table="person">
|
||||
<id name="Key" column="pid" type="string">
|
||||
<generator class="uuid.hex"/>
|
||||
</id>
|
||||
<property name="birthday" type="date"/>
|
||||
<component name="Name" class="eg.Name" unique="true">>
|
||||
<parent name="namedPerson"/> <!-- reference back to the Person -->
|
||||
<property name="initial"/>
|
||||
<property name="first"/>
|
||||
<property name="last"/>
|
||||
</component>
|
||||
</class>]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="components-incollections" revision="1">
|
||||
<title>在集合中出现的依赖对象</title>
|
||||
|
||||
<para>
|
||||
Hibernate支持component的集合(例如: 一个元素是“姓名”这种类型的数组)。 你可以使用<literal><composite-element></literal>标签替代<literal><element></literal>标签来定义你的component集合。
|
||||
</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>
|
||||
注意,如果你决定定义一个元素是联合元素的<literal>Set</literal>,正确地实现<literal>equals()</literal>和<literal>hashCode()</literal>是非常重要的。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
组合元素可以包含component但是不能包含集合。如果你的组合元素自身包含component, 必须使用<literal><nested-composite-element></literal>标签。这是一个相当特殊的案例 - 组合元素的集合自身可以包含component。 这个时候你就应该考虑一下使用one-to-many关联是否会更恰当。 尝试对这个组合元素重新建模为一个实体-但是需要注意的是,虽然Java模型和重新建模前 是一样的,关系模型和持久性语义上仍然存在轻微的区别。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
请注意如果你使用<literal><set></literal>标签,一个组合元素的映射不支持可能为空的属性. 当删除对象时, Hibernate必须使用每一个字段的来确定一条记录(在组合元素表中,没有单个的关键字段), 如果有为null的字段,这样做就不可能了。你必须作出一个选择,要么在组合元素中使用不能为空的属性, 要么选择使用<literal><list></literal>, <literal><map></literal>,<literal><bag></literal> 或者 <literal><idbag></literal>而不是 <literal><set></literal>。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
组合元素有个特别的案例,是组合元素可以包含一个<literal><many-to-one></literal> 元素。类似这样的映射允许你映射一个many-to-mang关联表作为组合元素额外的字段。(A mapping like this allows you to map extra columns of a many-to-many association table to the composite element class.) 接下来的的例子是从<literal>Order</literal>到<literal>Item</literal>的一个多对多的关联关系,而 <literal>purchaseDate</literal>, <literal>price</literal> 和 <literal>quantity</literal> 是<literal>Item</literal>的关联属性。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="eg.Order" .... >
|
||||
....
|
||||
<set name="purchasedItems" table="purchase_items" lazy="true">
|
||||
<key column="order_id">
|
||||
<composite-element class="eg.Purchase">
|
||||
<property name="purchaseDate"/>
|
||||
<property name="price"/>
|
||||
<property name="quantity"/>
|
||||
<many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->
|
||||
</composite-element>
|
||||
</set>
|
||||
</class>]]></programlisting>
|
||||
<para>
|
||||
当然,在另一方面,无法存在指向purchase的关联,因此不能实现双向关联查询。记住组建是值类型,并且不允许共享关联。单个<literal>Purchase</literal> 可以放在包含<literal>Order</literal>的集合中,但它不能同时被<literal>Item</literal>所关联。
|
||||
</para>
|
||||
|
||||
|
||||
<para>即使三重或多重管理都是可能的:</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="eg.Order" .... >
|
||||
....
|
||||
<set name="purchasedItems" table="purchase_items" lazy="true">
|
||||
<key column="order_id">
|
||||
<composite-element class="eg.OrderLine">
|
||||
<many-to-one name="purchaseDetails" class="eg.Purchase"/>
|
||||
<many-to-one name="item" class="eg.Item"/>
|
||||
</composite-element>
|
||||
</set>
|
||||
</class>]]></programlisting>
|
||||
|
||||
<para>
|
||||
在查询中,组合元素使用的语法是和关联到其他实体的语法一样的。
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="components-asmapindex">
|
||||
<title>组件作为Map的索引(Components as Map indices )</title>
|
||||
|
||||
<para>
|
||||
<literal><composite-map-key></literal>元素允许你映射一个Component类作为<literal>Map</literal>的key, 但是你必须确定你正确的在这个类中重写了<literal>hashCode()</literal> 和 <literal>equals()</literal>方法。
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="components-compositeid" revision="1">
|
||||
<title>组件作为联合标识符(Components as composite identifiers)</title>
|
||||
|
||||
<para>
|
||||
你可以使用一个component作为一个实体类的标识符。 你的component类必须满足以下要求:
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
它必须实现<literal>java.io.Serializable</literal>接口
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
它必须重新实现<literal>equals()</literal>和<literal>hashCode()</literal>方法, 始终和组合关键字在数据库中的概念保持一致
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
<emphasis>注意:在Hibernate3中,第二种要求并非是Hibernate强制必须的。但最好这样做。</emphasis>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
你不能使用一个<literal>IdentifierGenerator</literal>产生组合关键字。作为替代应用程序必须分配它自己的标识符。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
使用<literal><composite-id></literal> 标签(并且内嵌<literal><key-property></literal>元素)代替通常的<literal><id></literal>标签。 比如,<literal>OrderLine</literal>类具有一个依赖<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>
|
||||
|
||||
现在,任何关联到<literal>OrderLine</literal> 的外键都是复合的。在你的映射文件中,必须为其他类也这样声明。指向<literal>OrderLine</literal>的关联可能被这样映射:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<many-to-one name="orderLine" class="OrderLine">
|
||||
<!-- the "class" attribute is optional, as usual -->
|
||||
<column name="lineId"/>
|
||||
<column name="orderId"/>
|
||||
<column name="customerId"/>
|
||||
</many-to-one>]]></programlisting>
|
||||
|
||||
<para>
|
||||
|
||||
(注意在各个地方<literal><column></literal>标签都是<literal>column</literal>属性的替代写法。)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
指向<literal>OrderLine</literal>的<literal>多对多</literal>关联也使用联合外键:
|
||||
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<set name="undeliveredOrderLines">
|
||||
<key column name="warehouseId"/>
|
||||
<many-to-many class="OrderLine">
|
||||
<column name="lineId"/>
|
||||
<column name="orderId"/>
|
||||
<column name="customerId"/>
|
||||
</many-to-many>
|
||||
</set>]]></programlisting>
|
||||
|
||||
<para>
|
||||
在<literal>Order</literal>中, <literal>OrderLine</literal>的集合则是这样:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<set name="orderLines" inverse="true">
|
||||
<key>
|
||||
<column name="orderId"/>
|
||||
<column name="customerId"/>
|
||||
</key>
|
||||
<one-to-many class="OrderLine"/>
|
||||
</set>]]></programlisting>
|
||||
|
||||
<para>
|
||||
(与通常一样,<literal><one-to-many></literal>元素不声明任何列.)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
假若<literal>OrderLine</literal>本身拥有一个集合,它也具有组合外键。
|
||||
</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>动态组件 (Dynamic components)</title>
|
||||
<para>
|
||||
你甚至可以映射<literal>Map</literal>类型的属性:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<dynamic-component name="userAttributes">
|
||||
<property name="foo" column="FOO"/>
|
||||
<property name="bar" column="BAR"/>
|
||||
<many-to-one name="baz" class="Baz" column="BAZ_ID"/>
|
||||
</dynamic-component>]]></programlisting>
|
||||
|
||||
<para>
|
||||
从<literal><dynamic-component></literal>映射的语义上来讲,它和<literal><component></literal>是相同的。
|
||||
这种映射类型的优点在于通过修改映射文件,就可以具有在部署时检测真实属性的能力.利用一个DOM解析器,是有可能在运行时刻操作映射文件的。
|
||||
更好的是,你可以通过<literal>Configuration</literal>对象来访问(或者修改)Hibernate的运行时元模型。
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
|
@ -0,0 +1,227 @@
|
|||
<chapter id="events">
|
||||
<title>
|
||||
拦截器与事件(Interceptors and events)
|
||||
</title>
|
||||
|
||||
<para>
|
||||
应用程序能够响应Hibernate内部产生的特定事件是非常有用的。这样就允许实现某些通用的功能
|
||||
以及允许对Hibernate功能进行扩展。
|
||||
</para>
|
||||
|
||||
<sect1 id="objectstate-interceptors" revision="1">
|
||||
<title>
|
||||
拦截器(Interceptors)
|
||||
</title>
|
||||
|
||||
<para>
|
||||
<literal>Interceptor</literal>接口提供了从会话(session)回调(callback)应用程序(application)的机制,
|
||||
这种回调机制可以允许应用程序在持久化对象被保存、更新、删除或是加载之前,检查并(或)修改其
|
||||
属性。一个可能的用途,就是用来跟踪审核(auditing)信息。例如:下面的这个<literal>拦截器</literal>,会在一个实现了
|
||||
<literal>Auditable</literal>接口的对象被创建时自动地设置<literal>createTimestamp</literal>属性,并在实现了
|
||||
<literal>Auditable</literal>接口的对象被更新时,同步更新<literal>lastUpdateTimestamp</literal>属性。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[package org.hibernate.test;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.hibernate.Interceptor;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
public class AuditInterceptor implements Interceptor, Serializable {
|
||||
|
||||
private int updates;
|
||||
private int creates;
|
||||
|
||||
public void onDelete(Object entity,
|
||||
Serializable id,
|
||||
Object[] state,
|
||||
String[] propertyNames,
|
||||
Type[] types) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public boolean onFlushDirty(Object entity,
|
||||
Serializable id,
|
||||
Object[] currentState,
|
||||
Object[] previousState,
|
||||
String[] propertyNames,
|
||||
Type[] types) {
|
||||
|
||||
if ( entity instanceof Auditable ) {
|
||||
updates++;
|
||||
for ( int i=0; i < propertyNames.length; i++ ) {
|
||||
if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
|
||||
currentState[i] = new Date();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onLoad(Object entity,
|
||||
Serializable id,
|
||||
Object[] state,
|
||||
String[] propertyNames,
|
||||
Type[] types) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onSave(Object entity,
|
||||
Serializable id,
|
||||
Object[] state,
|
||||
String[] propertyNames,
|
||||
Type[] types) {
|
||||
|
||||
if ( entity instanceof Auditable ) {
|
||||
creates++;
|
||||
for ( int i=0; i<propertyNames.length; i++ ) {
|
||||
if ( "createTimestamp".equals( propertyNames[i] ) ) {
|
||||
state[i] = new Date();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void postFlush(Iterator entities) {
|
||||
System.out.println("Creations: " + creates + ", Updates: " + updates);
|
||||
}
|
||||
|
||||
public void preFlush(Iterator entities) {
|
||||
updates=0;
|
||||
creates=0;
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
创建会话(session)的时候可以指定拦截器。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
|
||||
|
||||
<para>
|
||||
你也可以使用<literal>Configuration</literal>来设置一个全局范围的拦截器。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="objectstate-events" revision="2">
|
||||
<title>
|
||||
事件系统(Event system)
|
||||
</title>
|
||||
|
||||
<para>
|
||||
如果需要响应持久层的某些特殊事件,你也可以使用Hibernate3的事件框架。
|
||||
该事件系统可以用来替代拦截器,也可以作为拦截器的补充来使用。
|
||||
|
||||
</para>
|
||||
|
||||
<para>
|
||||
基本上,<literal>Session</literal>接口的每个方法都有相对应的事件。比如
|
||||
<literal>LoadEvent</literal>,<literal>FlushEvent</literal>,等等(查阅XML配置文件
|
||||
的DTD,以及<literal>org.hibernate.event</literal>包来获得所有已定义的事件的列表)。当某个方
|
||||
法被调用时,Hibernate <literal>Session</literal>会生成一个相对应的事件并激活所
|
||||
有配置好的事件监听器。系统预设的监听器实现的处理过程就是被监听的方法要做的(被监听的方法所做的其实仅仅是激活监听器,
|
||||
“实际”的工作是由监听器完成的)。不过,你可以自由地选择实现
|
||||
一个自己定制的监听器(比如,实现并注册用来处理处理<literal>LoadEvent</literal>的<literal>LoadEventListener</literal>接口),
|
||||
来负责处理所有的调用<literal>Session</literal>的<literal>load()</literal>方法的请求。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
监听器应该被看作是单例(singleton)对象,也就是说,所有同类型的事件的处理共享同一个监听器实例,因此监听器
|
||||
不应该保存任何状态(也就是不应该使用成员变量)。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
用户定制的监听器应该实现与所要处理的事件相对应的接口,或者从一个合适的基类继承(甚至是从Hibernate自带的默认事件监听器类继承,
|
||||
为了方便你这样做,这些类都被声明成non-final的了)。用户定制的监听器可以通过编程使用<literal>Configuration</literal>对象
|
||||
来注册,也可以在Hibernate的XML格式的配置文件中进行声明(不支持在Properties格式的配置文件声明监听器)。
|
||||
下面是一个用户定制的加载事件(load event)的监听器:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[public class MyLoadListener extends DefaultLoadEventListener {
|
||||
// this is the single method defined by the LoadEventListener interface
|
||||
public Object onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
|
||||
throws HibernateException {
|
||||
if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
|
||||
throw MySecurityException("Unauthorized access");
|
||||
}
|
||||
return super.onLoad(event, loadType);
|
||||
}
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
你还需要修改一处配置,来告诉Hibernate以使用选定的监听器来替代默认的监听器。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<hibernate-configuration>
|
||||
<session-factory>
|
||||
...
|
||||
<listener type="load" class="MyLoadListener"/>
|
||||
</session-factory>
|
||||
</hibernate-configuration>]]></programlisting>
|
||||
|
||||
<para>
|
||||
看看用另一种方式,通过编程的方式来注册它。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Configuration cfg = new Configuration();
|
||||
cfg.getSessionEventListenerConfig().setLoadEventListener( new MyLoadListener() );]]></programlisting>
|
||||
|
||||
<para>
|
||||
通过在XML配置文件声明而注册的监听器不能共享实例。如果在多个<literal><listener/></literal>节点中使用
|
||||
了相同的类的名字,则每一个引用都将会产生一个独立的实例。如果你需要在多个监听器类型之间共享
|
||||
监听器的实例,则你必须使用编程的方式来进行注册。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
为什么我们实现了特定监听器的接口,在注册的时候还要明确指出我们要注册哪个事件的监听器呢?
|
||||
这是因为一个类可能实现多个监听器的接口。在注册的时候明确指定要监听的事件,可以让启用或者禁用对某个事件的监听的配置工作简单些。
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="objectstate-decl-security">
|
||||
<title>
|
||||
Hibernate的声明式安全机制
|
||||
</title>
|
||||
<para>
|
||||
通常,Hibernate应用程序的声明式安全机制由会话外观层(session facade)所管理。
|
||||
现在,Hibernate3允许某些特定的行为由JACC进行许可管理,由JAAS进行授权管理。
|
||||
本功能是一个建立在事件框架之上的可选的功能。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
首先,你必须要配置适当的事件监听器(event listener),来激活使用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>
|
||||
接下来,仍然在<literal>hibernate.cfg.xml</literal>文件中,绑定角色的权限:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
|
||||
<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
|
||||
|
||||
<para>
|
||||
这些角色的名字就是你的JACC provider所定义的角色的名字。
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
|
@ -0,0 +1,610 @@
|
|||
<chapter id="example-mappings">
|
||||
<title>示例:复杂映射实例</title>
|
||||
<para>
|
||||
本章展示了一些较为复杂的关系映射。
|
||||
</para>
|
||||
|
||||
<sect1 id="example-mappings-emp">
|
||||
<title>Employer(雇主)/Employee(雇员)</title>
|
||||
|
||||
<para>
|
||||
下面关于<literal>Employer</literal> 和 <literal>Employee</literal>的关系模型使用了一个真实的实体类
|
||||
(<literal>Employment</literal>)来表述,这是因为对于相同的雇员和雇主可能会有多个雇佣时间段。
|
||||
对于金额和雇员姓名,用Components建模。
|
||||
|
||||
</para>
|
||||
|
||||
<mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata fileref="images/EmployerEmployee.gif" format="GIF" align="center"/>
|
||||
</imageobject>
|
||||
<imageobject role="html">
|
||||
<imagedata fileref="../shared/images/EmployerEmployee.gif" format="GIF" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
|
||||
<para>
|
||||
映射文件可能是这样:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<hibernate-mapping>
|
||||
|
||||
<class name="Employer" table="employers">
|
||||
<id name="id">
|
||||
<generator class="sequence">
|
||||
<param name="sequence">employer_id_seq</param>
|
||||
</generator>
|
||||
</id>
|
||||
<property name="name"/>
|
||||
</class>
|
||||
|
||||
<class name="Employment" table="employment_periods">
|
||||
|
||||
<id name="id">
|
||||
<generator class="sequence">
|
||||
<param name="sequence">employment_id_seq</param>
|
||||
</generator>
|
||||
</id>
|
||||
<property name="startDate" column="start_date"/>
|
||||
<property name="endDate" column="end_date"/>
|
||||
|
||||
<component name="hourlyRate" class="MonetaryAmount">
|
||||
<property name="amount">
|
||||
<column name="hourly_rate" sql-type="NUMERIC(12, 2)"/>
|
||||
</property>
|
||||
<property name="currency" length="12"/>
|
||||
</component>
|
||||
|
||||
<many-to-one name="employer" column="employer_id" not-null="true"/>
|
||||
<many-to-one name="employee" column="employee_id" not-null="true"/>
|
||||
|
||||
</class>
|
||||
|
||||
<class name="Employee" table="employees">
|
||||
<id name="id">
|
||||
<generator class="sequence">
|
||||
<param name="sequence">employee_id_seq</param>
|
||||
</generator>
|
||||
</id>
|
||||
<property name="taxfileNumber"/>
|
||||
<component name="name" class="Name">
|
||||
<property name="firstName"/>
|
||||
<property name="initial"/>
|
||||
<property name="lastName"/>
|
||||
</component>
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>]]></programlisting>
|
||||
|
||||
<para>
|
||||
用<literal>SchemaExport</literal>生成表结构。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[create table employers (
|
||||
id BIGINT not null,
|
||||
name VARCHAR(255),
|
||||
primary key (id)
|
||||
)
|
||||
|
||||
create table employment_periods (
|
||||
id BIGINT not null,
|
||||
hourly_rate NUMERIC(12, 2),
|
||||
currency VARCHAR(12),
|
||||
employee_id BIGINT not null,
|
||||
employer_id BIGINT not null,
|
||||
end_date TIMESTAMP,
|
||||
start_date TIMESTAMP,
|
||||
primary key (id)
|
||||
)
|
||||
|
||||
create table employees (
|
||||
id BIGINT not null,
|
||||
firstName VARCHAR(255),
|
||||
initial CHAR(1),
|
||||
lastName VARCHAR(255),
|
||||
taxfileNumber VARCHAR(255),
|
||||
primary key (id)
|
||||
)
|
||||
|
||||
alter table employment_periods
|
||||
add constraint employment_periodsFK0 foreign key (employer_id) references employers
|
||||
alter table employment_periods
|
||||
add constraint employment_periodsFK1 foreign key (employee_id) references employees
|
||||
create sequence employee_id_seq
|
||||
create sequence employment_id_seq
|
||||
create sequence employer_id_seq]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="example-mappings-authorwork">
|
||||
<title>Author(作家)/Work(作品)</title>
|
||||
|
||||
<para>
|
||||
|
||||
考虑下面的<literal>Work</literal>,<literal>Author</literal> 和 <literal>Person</literal>模型的关系。
|
||||
我们用多对多关系来描述<literal>Work</literal> 和 <literal>Author</literal>,
|
||||
用一对一关系来描述<literal>Author</literal> 和 <literal>Person</literal>,
|
||||
另一种可能性是<literal>Author</literal>继承<literal>Person</literal>。
|
||||
</para>
|
||||
|
||||
<mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata fileref="images/AuthorWork.gif" format="GIF" align="center"/>
|
||||
</imageobject>
|
||||
<imageobject role="html">
|
||||
<imagedata fileref="../shared/images/AuthorWork.gif" format="GIF" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
|
||||
<para>
|
||||
下面的映射文件正确的描述了这些关系:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<hibernate-mapping>
|
||||
|
||||
<class name="Work" table="works" discriminator-value="W">
|
||||
|
||||
<id name="id" column="id">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<discriminator column="type" type="character"/>
|
||||
|
||||
<property name="title"/>
|
||||
<set name="authors" table="author_work">
|
||||
<key column name="work_id"/>
|
||||
<many-to-many class="Author" column name="author_id"/>
|
||||
</set>
|
||||
|
||||
<subclass name="Book" discriminator-value="B">
|
||||
<property name="text"/>
|
||||
</subclass>
|
||||
|
||||
<subclass name="Song" discriminator-value="S">
|
||||
<property name="tempo"/>
|
||||
<property name="genre"/>
|
||||
</subclass>
|
||||
|
||||
</class>
|
||||
|
||||
<class name="Author" table="authors">
|
||||
|
||||
<id name="id" column="id">
|
||||
<!-- The Author must have the same identifier as the Person -->
|
||||
<generator class="assigned"/>
|
||||
</id>
|
||||
|
||||
<property name="alias"/>
|
||||
<one-to-one name="person" constrained="true"/>
|
||||
|
||||
<set name="works" table="author_work" inverse="true">
|
||||
<key column="author_id"/>
|
||||
<many-to-many class="Work" column="work_id"/>
|
||||
</set>
|
||||
|
||||
</class>
|
||||
|
||||
<class name="Person" table="persons">
|
||||
<id name="id" column="id">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<property name="name"/>
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>]]></programlisting>
|
||||
|
||||
<para>
|
||||
|
||||
映射中有4个表。<literal>works</literal>, <literal>authors</literal> 和 <literal>persons</literal>
|
||||
分别保存着work,author和person的数据。<literal>author_work</literal>是authors和works的关联表。
|
||||
表结构是由<literal>SchemaExport</literal>生成的。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[create table works (
|
||||
id BIGINT not null generated by default as identity,
|
||||
tempo FLOAT,
|
||||
genre VARCHAR(255),
|
||||
text INTEGER,
|
||||
title VARCHAR(255),
|
||||
type CHAR(1) not null,
|
||||
primary key (id)
|
||||
)
|
||||
|
||||
create table author_work (
|
||||
author_id BIGINT not null,
|
||||
work_id BIGINT not null,
|
||||
primary key (work_id, author_id)
|
||||
)
|
||||
|
||||
create table authors (
|
||||
id BIGINT not null generated by default as identity,
|
||||
alias VARCHAR(255),
|
||||
primary key (id)
|
||||
)
|
||||
|
||||
create table persons (
|
||||
id BIGINT not null generated by default as identity,
|
||||
name VARCHAR(255),
|
||||
primary key (id)
|
||||
)
|
||||
|
||||
alter table authors
|
||||
add constraint authorsFK0 foreign key (id) references persons
|
||||
alter table author_work
|
||||
add constraint author_workFK0 foreign key (author_id) references authors
|
||||
alter table author_work
|
||||
add constraint author_workFK1 foreign key (work_id) references works]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="example-mappings-customerorderproduct">
|
||||
<title>Customer(客户)/Order(订单)/Product(产品)</title>
|
||||
|
||||
<para>
|
||||
|
||||
现在来考虑<literal>Customer</literal>,<literal>Order</literal> , <literal>LineItem</literal>
|
||||
和 <literal>Product</literal>关系的模型。<literal>Customer</literal> 和 <literal>Order</literal>之间
|
||||
是一对多的关系,但是我们怎么来描述<literal>Order</literal> / <literal>LineItem</literal> / <literal>Product</literal>呢?
|
||||
我可以把<literal>LineItem</literal>作为描述<literal>Order</literal> 和 <literal>Product</literal>
|
||||
多对多关系的关联类,在Hibernate,这叫做组合元素。
|
||||
</para>
|
||||
|
||||
<mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata fileref="images/CustomerOrderProduct.gif" format="GIF" align="center"/>
|
||||
</imageobject>
|
||||
<imageobject role="html">
|
||||
<imagedata fileref="../shared/images/CustomerOrderProduct.gif" format="GIF" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
|
||||
<para>
|
||||
映射文件如下:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<hibernate-mapping>
|
||||
|
||||
<class name="Customer" table="customers">
|
||||
<id name="id">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<property name="name"/>
|
||||
<set name="orders" inverse="true">
|
||||
<key column="customer_id"/>
|
||||
<one-to-many class="Order"/>
|
||||
</set>
|
||||
</class>
|
||||
|
||||
<class name="Order" table="orders">
|
||||
<id name="id">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<property name="date"/>
|
||||
<many-to-one name="customer" column="customer_id"/>
|
||||
<list name="lineItems" table="line_items">
|
||||
<key column="order_id"/>
|
||||
<list-index column="line_number"/>
|
||||
<composite-element class="LineItem">
|
||||
<property name="quantity"/>
|
||||
<many-to-one name="product" column="product_id"/>
|
||||
</composite-element>
|
||||
</list>
|
||||
</class>
|
||||
|
||||
<class name="Product" table="products">
|
||||
<id name="id">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<property name="serialNumber"/>
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>]]></programlisting>
|
||||
|
||||
<para>
|
||||
<literal>customers</literal>, <literal>orders</literal>, <literal>line_items</literal> 和
|
||||
<literal>products</literal> 分别保存着customer, order, order line item 和 product的数据。
|
||||
<literal>line_items</literal>也作为连接orders 和 products的关联表。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[create table customers (
|
||||
id BIGINT not null generated by default as identity,
|
||||
name VARCHAR(255),
|
||||
primary key (id)
|
||||
)
|
||||
|
||||
create table orders (
|
||||
id BIGINT not null generated by default as identity,
|
||||
customer_id BIGINT,
|
||||
date TIMESTAMP,
|
||||
primary key (id)
|
||||
)
|
||||
|
||||
create table line_items (
|
||||
line_number INTEGER not null,
|
||||
order_id BIGINT not null,
|
||||
product_id BIGINT,
|
||||
quantity INTEGER,
|
||||
primary key (order_id, line_number)
|
||||
)
|
||||
|
||||
create table products (
|
||||
id BIGINT not null generated by default as identity,
|
||||
serialNumber VARCHAR(255),
|
||||
primary key (id)
|
||||
)
|
||||
|
||||
alter table orders
|
||||
add constraint ordersFK0 foreign key (customer_id) references customers
|
||||
alter table line_items
|
||||
add constraint line_itemsFK0 foreign key (product_id) references products
|
||||
alter table line_items
|
||||
add constraint line_itemsFK1 foreign key (order_id) references orders]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="misc">
|
||||
<title>杂例</title>
|
||||
|
||||
<para>
|
||||
这些例子全部来自于Hibernate的test suite,同时你也可以找到其他有用的例子。
|
||||
可以参考Hibernate的<literal>src</literal>目录。
|
||||
</para>
|
||||
|
||||
<para>TODO: put words around this stuff</para>
|
||||
|
||||
<sect2 id="example-mappings-typed-onetone">
|
||||
<title>"Typed" one-to-one association</title>
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="name"/>
|
||||
<one-to-one name="address"
|
||||
cascade="all">
|
||||
<formula>name</formula>
|
||||
<formula>'HOME'</formula>
|
||||
</one-to-one>
|
||||
<one-to-one name="mailingAddress"
|
||||
cascade="all">
|
||||
<formula>name</formula>
|
||||
<formula>'MAILING'</formula>
|
||||
</one-to-one>
|
||||
</class>
|
||||
|
||||
<class name="Address" batch-size="2"
|
||||
check="addressType in ('MAILING', 'HOME', 'BUSINESS')">
|
||||
<composite-id>
|
||||
<key-many-to-one name="person"
|
||||
column="personName"/>
|
||||
<key-property name="type"
|
||||
column="addressType"/>
|
||||
</composite-id>
|
||||
<property name="street" type="text"/>
|
||||
<property name="state"/>
|
||||
<property name="zip"/>
|
||||
</class>]]></programlisting>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="example-mappings-composite-key">
|
||||
<title>Composite key example</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-content-discrimination">
|
||||
<title>Content based discrimination</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>Associations on alternate keys</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>
|
|
@ -0,0 +1,306 @@
|
|||
<chapter id="example-parentchild">
|
||||
<title>示例:父子关系(Parent Child Relationships)</title>
|
||||
<para>
|
||||
刚刚接触Hibernate的人大多是从父子关系(parent / child type relationship)的建模入手的。父子关系的建模有两种方法。由于种种原因,最方便的方法是把<literal>Parent</literal>和<literal>Child</literal>都建模成实体类,并创建一个从<literal>Parent</literal>指向<literal>Child</literal>的<one-to-many>关联,对新手来说尤其如此。还有一种方法,就是将<literal>Child</literal>声明为一个<literal><composite-element></literal>(组合元素)。 事实上在Hibernate中one to many关联的默认语义远没有composite element贴近parent / child关系的通常语义。下面我们会阐述如何使用<emphasis>带有级联的双向一对多关联(bidirectional one to many association with cascades)</emphasis>去建立有效、优美的parent / child关系。这一点也不难!
|
||||
</para>
|
||||
|
||||
<sect1 id="example-parentchild-collections">
|
||||
<title>关于collections需要注意的一点</title>
|
||||
<para>
|
||||
Hibernate collections被当作其所属实体而不是其包含实体的一个逻辑部分。这非常重要!它主要体现为以下几点:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
当删除或增加collection中对象的时候,collection所属者的版本值会递增。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
如果一个从collection中移除的对象是一个值类型(value type)的实例,比如composite element,那么这个对象的持久化状态将会终止,其在数据库中对应的记录会被删除。同样的,向collection增加一个value type的实例将会使之立即被持久化。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
另一方面,如果从一对多或多对多关联的collection中移除一个实体,在缺省情况下这个对象并不会被删除。这个行为是完全合乎逻辑的--改变一个实体的内部状态不应该使与它关联的实体消失掉!同样的,向collection增加一个实体不会使之被持久化。
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
实际上,向Collection增加一个实体的缺省动作只是在两个实体之间创建一个连接而已,同样移除的时候也只是删除连接。这种处理对于所有的情况都是合适的。对于父子关系则是完全不适合的,在这种关系下,子对象的生存绑定于父对象的生存周期。
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
<sect1 id="example-parentchild-bidir">
|
||||
<title>双向的一对多关系(Bidirectional one-to-many)</title>
|
||||
|
||||
<para>
|
||||
假设我们要实现一个简单的从Parent到Child的<one-to-many>关联。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<set name="children">
|
||||
<key column="parent_id"/>
|
||||
<one-to-many class="Child"/>
|
||||
</set>]]></programlisting>
|
||||
|
||||
<para>
|
||||
如果我们运行下面的代码
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Parent p = .....;
|
||||
Child c = new Child();
|
||||
p.getChildren().add(c);
|
||||
session.save(c);
|
||||
session.flush();]]></programlisting>
|
||||
|
||||
<para>
|
||||
Hibernate会产生两条SQL语句:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>一条<literal>INSERT</literal>语句,为<literal>c</literal>创建一条记录</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
一条<literal>UPDATE</literal>语句,创建从<literal>p</literal>到<literal>c</literal>的连接
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
这样做不仅效率低,而且违反了列<literal>parent_id</literal>非空的限制。我们可以通过在集合类映射上指定<literal>not-null="true"</literal>来解决违反非空约束的问题:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<set name="children">
|
||||
<key column="parent_id" not-null="true"/>
|
||||
<one-to-many class="Child"/>
|
||||
</set>]]></programlisting>
|
||||
|
||||
<para>
|
||||
然而,这并非是推荐的解决方法。
|
||||
</para>
|
||||
|
||||
<para> 这种现象的根本原因是从<literal>p</literal>到<literal>c</literal>的连接(外键parent_id)没有被当作<literal>Child</literal>对象状态的一部分,因而没有在INSERT语句中被创建。因此解决的办法就是把这个连接添加到Child的映射中。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
|
||||
|
||||
<para>
|
||||
(我们还需要为类<literal>Child</literal>添加<literal>parent</literal>属性)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
现在实体<literal>Child</literal>在管理连接的状态,为了使collection不更新连接,我们使用<literal>inverse</literal>属性。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<set name="children" inverse="true">
|
||||
<key column="parent_id"/>
|
||||
<one-to-many class="Child"/>
|
||||
</set>]]></programlisting>
|
||||
|
||||
<para>
|
||||
下面的代码是用来添加一个新的<literal>Child</literal>
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
|
||||
Child c = new Child();
|
||||
c.setParent(p);
|
||||
p.getChildren().add(c);
|
||||
session.save(c);
|
||||
session.flush();]]></programlisting>
|
||||
|
||||
<para>
|
||||
现在,只会有一条<literal>INSERT</literal>语句被执行!
|
||||
</para>
|
||||
|
||||
<para>
|
||||
为了让事情变得井井有条,可以为<literal>Parent</literal>加一个<literal>addChild()</literal>方法。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[public void addChild(Child c) {
|
||||
c.setParent(this);
|
||||
children.add(c);
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
现在,添加<literal>Child</literal>的代码就是这样
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
|
||||
Child c = new Child();
|
||||
p.addChild(c);
|
||||
session.save(c);
|
||||
session.flush();]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="example-parentchild-cascades">
|
||||
<title>级联生命周期(Cascading lifecycle)</title>
|
||||
<para>
|
||||
需要显式调用<literal>save()</literal>仍然很麻烦,我们可以用级联来解决这个问题。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<set name="children" inverse="true" cascade="all">
|
||||
<key column="parent_id"/>
|
||||
<one-to-many class="Child"/>
|
||||
</set>]]></programlisting>
|
||||
|
||||
<para>
|
||||
这样上面的代码可以简化为:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
|
||||
Child c = new Child();
|
||||
p.addChild(c);
|
||||
session.flush();]]></programlisting>
|
||||
|
||||
<para>
|
||||
同样的,保存或删除<literal>Parent</literal>对象的时候并不需要遍历其子对象。
|
||||
下面的代码会删除对象<literal>p</literal>及其所有子对象对应的数据库记录。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
|
||||
session.delete(p);
|
||||
session.flush();]]></programlisting>
|
||||
|
||||
<para>
|
||||
然而,这段代码
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
|
||||
Child c = (Child) p.getChildren().iterator().next();
|
||||
p.getChildren().remove(c);
|
||||
c.setParent(null);
|
||||
session.flush();]]></programlisting>
|
||||
|
||||
<para>
|
||||
不会从数据库删除<literal>c</literal>;它只会删除与<literal>p</literal>之间的连接(并且会导致违反<literal>NOT NULL</literal>约束,在这个例子中)。你需要显式调用<literal>delete()</literal>来删除<literal>Child</literal>。 <!--,因为Hibernate并没有设计垃圾回收器!代码如下:-->
|
||||
</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>
|
||||
在我们的例子中,如果没有父对象,子对象就不应该存在,如果将子对象从collection中移除,实际上我们是想删除它。要实现这种要求,就必须使用<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>
|
||||
注意:即使在collection一方的映射中指定<literal>inverse="true"</literal>,级联仍然是通过遍历collection中的元素来处理的。如果你想要通过级联进行子对象的插入、删除、更新操作,就必须把它加到collection中,只调用<literal>setParent()</literal>是不够的。
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="example-parentchild-update">
|
||||
<title>级联与<literal>未保存值</literal>(Cascades and <literal>unsaved-value</literal>)</title>
|
||||
|
||||
<para>
|
||||
假设我们从<literal>Session</literal>中装入了一个<literal>Parent</literal>对象,用户界面对其进行了修改,然后希望在一个新的Session里面调用<literal>update()</literal>来保存这些修改。对象<literal>Parent</literal>包含了子对象的集合,由于打开了级联更新,Hibernate需要知道哪些Child对象是新实例化的,哪些代表数据库中已经存在的记录。我们假设<literal>Parent</literal>和<literal>Child</literal>对象的标识属性都是自动生成的,类型为<literal>java.lang.Long</literal>。Hibernate会使用标识属性的值,和version 或 timestamp 属性,来判断哪些子对象是新的。(参见<xref linkend="objectstate-saveorupdate"/>.) <emphasis>在 Hibernate3 中,显式指定<literal>unsaved-value</literal>不再是必须的了。</emphasis>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
下面的代码会更新<literal>parent</literal>和<literal>child</literal>对象,并且插入<literal>newChild</literal>对象。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[//parent and child were both loaded in a previous session
|
||||
parent.addChild(child);
|
||||
Child newChild = new Child();
|
||||
parent.addChild(newChild);
|
||||
session.update(parent);
|
||||
session.flush();]]></programlisting>
|
||||
|
||||
<para>
|
||||
Well, that's all very well for the case of a generated identifier, but what about assigned identifiers
|
||||
and composite identifiers? This is more difficult, since Hibernate can't use the identifier property to
|
||||
distinguish between a newly instantiated object (with an identifier assigned by the user) and an
|
||||
object loaded in a previous session. In this case, Hibernate will either use the timestamp or version
|
||||
property, or will actually query the second-level cache or, worst case, the database, to see if the
|
||||
row exists.
|
||||
</para>
|
||||
<para>
|
||||
这对于自动生成标识的情况是非常好的,但是自分配的标识和复合标识怎么办呢?这是有点麻烦,因为Hibernate没有办法区分新实例化的对象(标识被用户指定了)和前一个Session装入的对象。在这种情况下,Hibernate会使用timestamp或version属性,或者查询第二级缓存,或者最坏的情况,查询数据库,来确认是否此行存在。</para>
|
||||
|
||||
<!-- undocumenting
|
||||
|
||||
<para>
|
||||
还有一种可能情况,有一个名为<literal>isUnsaved()</literal>的<literal>新的拦截器(Interceptor)</literal>方法,它允许应用程序自己实现新实例的判断。比如,你可以自己定义一个持久类的祖先类:
|
||||
</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>
|
||||
(<literal>saved</literal>属性是不会被持久化的。)
|
||||
现在在<literal>onLoad()</literal>和<literal>onSave()</literal>外,还要实现<literal>isUnsaved()</literal>。
|
||||
</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.
|
||||
别担心,在Hibernate3中,假若你不愿意,你不需要编写任何这类代码。
|
||||
</para>
|
||||
-->
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
<sect1 id="example-parentchild-conclusion">
|
||||
<title>结论</title>
|
||||
<para> 这里有不少东西需要融会贯通,可能会让新手感到迷惑。但是在实践中它们都工作地非常好。大部分Hibernate应用程序都会经常用到父子对象模式。
|
||||
</para>
|
||||
<para>
|
||||
在第一段中我们曾经提到另一个方案。上面的这些问题都不会出现在<literal><composite-element></literal>映射中,它准确地表达了父子关系的语义。很不幸复合元素还有两个重大限制:复合元素不能拥有collections,并且,除了用于惟一的父对象外,它们不能再作为其它任何实体的子对象。
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
|
@ -0,0 +1,424 @@
|
|||
<chapter id="example-weblog">
|
||||
<title>示例:Weblog 应用程序</title>
|
||||
|
||||
<sect1 id="example-weblog-classes">
|
||||
<title>持久化类</title>
|
||||
|
||||
<para> 下面的持久化类表示一个weblog和在其中张贴的一个贴子。他们是标准的父/子关系模型,但是我们会用一个有序包(ordered bag)而非集合(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>Hibernate 映射</title>
|
||||
|
||||
<para>
|
||||
下列的XML映射应该是很直白的。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<?xml version="1.0"?>
|
||||
<!DOCTYPE hibernate-mapping PUBLIC
|
||||
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
|
||||
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
|
||||
|
||||
<hibernate-mapping package="eg">
|
||||
<class
|
||||
name="Blog"
|
||||
table="BLOGS" >
|
||||
|
||||
<id
|
||||
name="id"
|
||||
column="BLOG_ID">
|
||||
|
||||
<generator class="native"/>
|
||||
|
||||
</id>
|
||||
|
||||
<property
|
||||
name="name"
|
||||
column="NAME"
|
||||
not-null="true"
|
||||
unique="true"/>
|
||||
|
||||
<bag
|
||||
name="items"
|
||||
inverse="true"
|
||||
order-by="DATE_TIME"
|
||||
cascade="all">
|
||||
|
||||
<key column="BLOG_ID"/>
|
||||
<one-to-many class="BlogItem"/>
|
||||
|
||||
</bag>
|
||||
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[<?xml version="1.0"?>
|
||||
<!DOCTYPE hibernate-mapping PUBLIC
|
||||
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
|
||||
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
|
||||
|
||||
<hibernate-mapping package="eg">
|
||||
|
||||
<class
|
||||
name="BlogItem"
|
||||
table="BLOG_ITEMS"
|
||||
dynamic-update="true">
|
||||
|
||||
<id
|
||||
name="id"
|
||||
column="BLOG_ITEM_ID">
|
||||
|
||||
<generator class="native"/>
|
||||
|
||||
</id>
|
||||
|
||||
<property
|
||||
name="title"
|
||||
column="TITLE"
|
||||
not-null="true"/>
|
||||
|
||||
<property
|
||||
name="text"
|
||||
column="TEXT"
|
||||
not-null="true"/>
|
||||
|
||||
<property
|
||||
name="datetime"
|
||||
column="DATE_TIME"
|
||||
not-null="true"/>
|
||||
|
||||
<many-to-one
|
||||
name="blog"
|
||||
column="BLOG_ID"
|
||||
not-null="true"/>
|
||||
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="example-weblog-code">
|
||||
<title>Hibernate 代码</title>
|
||||
|
||||
<para>
|
||||
下面的类演示了我们可以使用Hibernate对这些类进行的一些操作。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[package eg;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Query;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.tool.hbm2ddl.SchemaExport;
|
||||
|
||||
public class BlogMain {
|
||||
|
||||
private SessionFactory _sessions;
|
||||
|
||||
public void configure() throws HibernateException {
|
||||
_sessions = new Configuration()
|
||||
.addClass(Blog.class)
|
||||
.addClass(BlogItem.class)
|
||||
.buildSessionFactory();
|
||||
}
|
||||
|
||||
public void exportTables() throws HibernateException {
|
||||
Configuration cfg = new Configuration()
|
||||
.addClass(Blog.class)
|
||||
.addClass(BlogItem.class);
|
||||
new SchemaExport(cfg).create(true, true);
|
||||
}
|
||||
|
||||
public Blog createBlog(String name) throws HibernateException {
|
||||
|
||||
Blog blog = new Blog();
|
||||
blog.setName(name);
|
||||
blog.setItems( new ArrayList() );
|
||||
|
||||
Session session = _sessions.openSession();
|
||||
Transaction tx = null;
|
||||
try {
|
||||
tx = session.beginTransaction();
|
||||
session.persist(blog);
|
||||
tx.commit();
|
||||
}
|
||||
catch (HibernateException he) {
|
||||
if (tx!=null) tx.rollback();
|
||||
throw he;
|
||||
}
|
||||
finally {
|
||||
session.close();
|
||||
}
|
||||
return blog;
|
||||
}
|
||||
|
||||
public BlogItem createBlogItem(Blog blog, String title, String text)
|
||||
throws HibernateException {
|
||||
|
||||
BlogItem item = new BlogItem();
|
||||
item.setTitle(title);
|
||||
item.setText(text);
|
||||
item.setBlog(blog);
|
||||
item.setDatetime( Calendar.getInstance() );
|
||||
blog.getItems().add(item);
|
||||
|
||||
Session session = _sessions.openSession();
|
||||
Transaction tx = null;
|
||||
try {
|
||||
tx = session.beginTransaction();
|
||||
session.update(blog);
|
||||
tx.commit();
|
||||
}
|
||||
catch (HibernateException he) {
|
||||
if (tx!=null) tx.rollback();
|
||||
throw he;
|
||||
}
|
||||
finally {
|
||||
session.close();
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
public BlogItem createBlogItem(Long blogid, String title, String text)
|
||||
throws HibernateException {
|
||||
|
||||
BlogItem item = new BlogItem();
|
||||
item.setTitle(title);
|
||||
item.setText(text);
|
||||
item.setDatetime( Calendar.getInstance() );
|
||||
|
||||
Session session = _sessions.openSession();
|
||||
Transaction tx = null;
|
||||
try {
|
||||
tx = session.beginTransaction();
|
||||
Blog blog = (Blog) session.load(Blog.class, blogid);
|
||||
item.setBlog(blog);
|
||||
blog.getItems().add(item);
|
||||
tx.commit();
|
||||
}
|
||||
catch (HibernateException he) {
|
||||
if (tx!=null) tx.rollback();
|
||||
throw he;
|
||||
}
|
||||
finally {
|
||||
session.close();
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
public void updateBlogItem(BlogItem item, String text)
|
||||
throws HibernateException {
|
||||
|
||||
item.setText(text);
|
||||
|
||||
Session session = _sessions.openSession();
|
||||
Transaction tx = null;
|
||||
try {
|
||||
tx = session.beginTransaction();
|
||||
session.update(item);
|
||||
tx.commit();
|
||||
}
|
||||
catch (HibernateException he) {
|
||||
if (tx!=null) tx.rollback();
|
||||
throw he;
|
||||
}
|
||||
finally {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateBlogItem(Long itemid, String text)
|
||||
throws HibernateException {
|
||||
|
||||
Session session = _sessions.openSession();
|
||||
Transaction tx = null;
|
||||
try {
|
||||
tx = session.beginTransaction();
|
||||
BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
|
||||
item.setText(text);
|
||||
tx.commit();
|
||||
}
|
||||
catch (HibernateException he) {
|
||||
if (tx!=null) tx.rollback();
|
||||
throw he;
|
||||
}
|
||||
finally {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
public List listAllBlogNamesAndItemCounts(int max)
|
||||
throws HibernateException {
|
||||
|
||||
Session session = _sessions.openSession();
|
||||
Transaction tx = null;
|
||||
List result = null;
|
||||
try {
|
||||
tx = session.beginTransaction();
|
||||
Query q = session.createQuery(
|
||||
"select blog.id, blog.name, count(blogItem) " +
|
||||
"from Blog as blog " +
|
||||
"left outer join blog.items as blogItem " +
|
||||
"group by blog.name, blog.id " +
|
||||
"order by max(blogItem.datetime)"
|
||||
);
|
||||
q.setMaxResults(max);
|
||||
result = q.list();
|
||||
tx.commit();
|
||||
}
|
||||
catch (HibernateException he) {
|
||||
if (tx!=null) tx.rollback();
|
||||
throw he;
|
||||
}
|
||||
finally {
|
||||
session.close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Blog getBlogAndAllItems(Long blogid)
|
||||
throws HibernateException {
|
||||
|
||||
Session session = _sessions.openSession();
|
||||
Transaction tx = null;
|
||||
Blog blog = null;
|
||||
try {
|
||||
tx = session.beginTransaction();
|
||||
Query q = session.createQuery(
|
||||
"from Blog as blog " +
|
||||
"left outer join fetch blog.items " +
|
||||
"where blog.id = :blogid"
|
||||
);
|
||||
q.setParameter("blogid", blogid);
|
||||
blog = (Blog) q.uniqueResult();
|
||||
tx.commit();
|
||||
}
|
||||
catch (HibernateException he) {
|
||||
if (tx!=null) tx.rollback();
|
||||
throw he;
|
||||
}
|
||||
finally {
|
||||
session.close();
|
||||
}
|
||||
return blog;
|
||||
}
|
||||
|
||||
public List listBlogsAndRecentItems() throws HibernateException {
|
||||
|
||||
Session session = _sessions.openSession();
|
||||
Transaction tx = null;
|
||||
List result = null;
|
||||
try {
|
||||
tx = session.beginTransaction();
|
||||
Query q = session.createQuery(
|
||||
"from Blog as blog " +
|
||||
"inner join blog.items as blogItem " +
|
||||
"where blogItem.datetime > :minDate"
|
||||
);
|
||||
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.roll(Calendar.MONTH, false);
|
||||
q.setCalendar("minDate", cal);
|
||||
|
||||
result = q.list();
|
||||
tx.commit();
|
||||
}
|
||||
catch (HibernateException he) {
|
||||
if (tx!=null) tx.rollback();
|
||||
throw he;
|
||||
}
|
||||
finally {
|
||||
session.close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
<chapter id="filters">
|
||||
<title>过滤数据</title>
|
||||
<para>
|
||||
|
||||
Hibernate3 提供了一种创新的方式来处理具有“显性(visibility)”规则的数据,那就是使用<emphasis>Hibernate filter</emphasis>。
|
||||
<emphasis>Hibernate filter</emphasis>是全局有效的、具有名字、可以带参数的过滤器,
|
||||
对于某个特定的Hibernate session您可以选择是否启用(或禁用)某个过滤器。
|
||||
</para>
|
||||
|
||||
<sect1 id="objectstate-filters">
|
||||
<title>Hibernate 过滤器(filters)</title>
|
||||
|
||||
<para>
|
||||
Hibernate3新增了对某个类或者集合使用预先定义的过滤器条件(filter criteria)的功能。过滤器条件相当于定义一个
|
||||
非常类似于类和各种集合上的“where”属性的约束子句,但是过滤器条件可以带参数。
|
||||
应用程序可以在运行时决定是否启用给定的过滤器,以及使用什么样的参数值。
|
||||
过滤器的用法很像数据库视图,只不过是在应用程序中确定使用什么样的参数的。
|
||||
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
||||
要使用过滤器,必须首先在相应的映射节点中定义。而定义一个过滤器,要用到位于<literal><hibernate-mapping/></literal>
|
||||
节点之内的<literal><filter-def/></literal>节点:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<filter-def name="myFilter">
|
||||
<filter-param name="myFilterParam" type="string"/>
|
||||
</filter-def>]]></programlisting>
|
||||
|
||||
<para>
|
||||
定义好之后,就可以在某个类中使用这个过滤器:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="myClass" ...>
|
||||
...
|
||||
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
|
||||
</class>]]></programlisting>
|
||||
|
||||
<para>
|
||||
也可以在某个集合使用它:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<set ...>
|
||||
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
|
||||
</set>]]></programlisting>
|
||||
|
||||
<para>
|
||||
可以在多个类或集合中使用某个过滤器;某个类或者集合中也可以使用多个过滤器。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
||||
<literal>Session</literal>对象中会用到的方法有:<literal>enableFilter(String filterName)</literal>,
|
||||
<literal>getEnabledFilter(String filterName)</literal>, 和 <literal>disableFilter(String filterName)</literal>.
|
||||
Session中默认是<emphasis>不</emphasis>启用过滤器的,必须通过<literal>Session.enabledFilter()</literal>方法显式的启用。
|
||||
该方法返回被启用的<literal>Filter</literal>的实例。以上文定义的过滤器为例:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
|
||||
|
||||
<para>
|
||||
注意,org.hibernate.Filter的方法允许链式方法调用。(类似上面例子中启用Filter之后设定Filter参数这个“方法链”)
|
||||
Hibernate的其他部分也大多有这个特性。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
下面是一个比较完整的例子,使用了记录生效日期模式过滤有时效的数据:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<filter-def name="effectiveDate">
|
||||
<filter-param name="asOfDate" type="date"/>
|
||||
</filter-def>
|
||||
|
||||
<class name="Employee" ...>
|
||||
...
|
||||
<many-to-one name="department" column="dept_id" class="Department"/>
|
||||
<property name="effectiveStartDate" type="date" column="eff_start_dt"/>
|
||||
<property name="effectiveEndDate" type="date" column="eff_end_dt"/>
|
||||
...
|
||||
<!--
|
||||
Note that this assumes non-terminal records have an eff_end_dt set to
|
||||
a max db date for simplicity-sake
|
||||
|
||||
注意,为了简单起见,此处假设雇用关系生效期尚未结束的记录的eff_end_dt字段的值等于数据库最大的日期
|
||||
-->
|
||||
<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>
|
||||
定义好后,如果想要保证取回的都是目前处于生效期的记录,只需在获取雇员数据的操作之前先开启过滤器即可:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Session session = ...;
|
||||
session.enabledFilter("effectiveDate").setParameter("asOfDate", new Date());
|
||||
List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
|
||||
.setLong("targetSalary", new Long(1000000))
|
||||
.list();
|
||||
]]></programlisting>
|
||||
|
||||
<para>
|
||||
|
||||
在上面的HQL中,虽然我们仅仅显式的使用了一个薪水条件,但因为启用了过滤器,查询将仅返回那些目前雇用
|
||||
关系处于生效期的,并且薪水高于一百万美刀的雇员的数据。
|
||||
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
||||
注意:如果你打算在使用外连接(或者通过HQL或load fetching)的同时使用过滤器,要注意条件表达式的方向(左还是右)。
|
||||
最安全的方式是使用左外连接(left outer joining)。并且通常来说,先写参数,
|
||||
然后是操作符,最后写数据库字段名。
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
|
@ -0,0 +1,448 @@
|
|||
<chapter id="inheritance">
|
||||
<title>继承映射(Inheritance Mappings)</title>
|
||||
|
||||
<sect1 id="inheritance-strategies" revision="2">
|
||||
<title> 三种策略</title>
|
||||
|
||||
|
||||
<para>
|
||||
Hibernate支持三种基本的继承映射策略:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
每个类分层结构一张表(table per class hierarchy)
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
每个子类一张表(table per subclass)
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
每个具体类一张表(table per concrete class)
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
此外,Hibernate还支持第四种稍有不同的多态映射策略:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
隐式多态(implicit polymorphism)
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
对于同一个继承层次内的不同分支,可以采用不同的映射策略,然后用隐式多
|
||||
态来完成跨越整个层次的多态。但是在同一个<literal><class></literal>根元素
|
||||
下,Hibernate不支持混合了元素<literal><subclass></literal>、
|
||||
<literal><joined-subclass></literal>和<literal><union-subclass></literal>
|
||||
的映射。在同一个<literal><class></literal>元素下,可以混合使用
|
||||
“每个类分层结构一张表”(table per hierarchy) 和“每个子类一张表”(table per subclass)
|
||||
这两种映射策略,这是通过结合元素<literal><subclass></literal>和
|
||||
<literal><join></literal>来实现的(见后)。
|
||||
|
||||
</para>
|
||||
|
||||
<sect2 id="inheritance-tableperclass" >
|
||||
<title>每个类分层结构一张表(Table per class hierarchy)</title>
|
||||
|
||||
|
||||
<para>
|
||||
假设我们有接口<literal>Payment</literal>和它的几个实现类:
|
||||
<literal>CreditCardPayment</literal>, <literal>CashPayment</literal>,
|
||||
和<literal>ChequePayment</literal>。则“每个类分层结构一张表”(Table per
|
||||
class hierarchy)的映射代码如下所示:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
|
||||
<id name="id" type="long" column="PAYMENT_ID">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<discriminator column="PAYMENT_TYPE" type="string"/>
|
||||
<property name="amount" column="AMOUNT"/>
|
||||
...
|
||||
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
|
||||
<property name="creditCardType" column="CCTYPE"/>
|
||||
...
|
||||
</subclass>
|
||||
<subclass name="CashPayment" discriminator-value="CASH">
|
||||
...
|
||||
</subclass>
|
||||
<subclass name="ChequePayment" discriminator-value="CHEQUE">
|
||||
...
|
||||
</subclass>
|
||||
</class>]]></programlisting>
|
||||
|
||||
<para>
|
||||
采用这种策略只需要一张表即可。它有一个很大的限制:要求那些由子类定义的字段,
|
||||
如<literal>CCTYPE</literal>,不能有<literal>非空(NOT NULL)</literal>约束。
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="inheritance-tablepersubclass">
|
||||
|
||||
<title>每个子类一张表(Table per subclass)</title>
|
||||
|
||||
|
||||
<para>
|
||||
对于上例中的几个类而言,采用“每个子类一张表”的映射策略,代码如下所示:
|
||||
</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"/>
|
||||
...
|
||||
</joined-subclass>
|
||||
<joined-subclass name="CashPayment" table="CASH_PAYMENT">
|
||||
<key column="PAYMENT_ID"/>
|
||||
<property name="creditCardType" column="CCTYPE"/>
|
||||
...
|
||||
</joined-subclass>
|
||||
<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
|
||||
<key column="PAYMENT_ID"/>
|
||||
...
|
||||
</joined-subclass>
|
||||
</class>]]></programlisting>
|
||||
<para>
|
||||
需要四张表。三个子类表通过主键关联到超类表(因而关系模型实际上是一对一关联)。
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="inheritance-tablepersubclass-discriminator">
|
||||
|
||||
<title>每个子类一张表(Table per subclass),使用辨别标志(Discriminator)</title>
|
||||
|
||||
<para>
|
||||
注意,对“每个子类一张表”的映射策略,Hibernate的实现不需要辨别字段,而其他
|
||||
的对象/关系映射工具使用了一种不同于Hibernate的实现方法,该方法要求在超类
|
||||
表中有一个类型辨别字段(type discriminator column)。Hibernate采用的方法更
|
||||
难实现,但从关系(数据库)这点上来看,按理说它更正确。若你愿意使用带有辨别字
|
||||
段的“每个子类一张表”的策略,你可以结合使用<literal><subclass></literal>
|
||||
与<literal><join></literal>,如下所示:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
|
||||
<id name="id" type="long" column="PAYMENT_ID">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<discriminator column="PAYMENT_TYPE" type="string"/>
|
||||
<property name="amount" column="AMOUNT"/>
|
||||
...
|
||||
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
|
||||
<join table="CREDIT_PAYMENT">
|
||||
<property name="creditCardType" column="CCTYPE"/>
|
||||
...
|
||||
</join>
|
||||
</subclass>
|
||||
<subclass name="CashPayment" discriminator-value="CASH">
|
||||
<join table="CASH_PAYMENT">
|
||||
...
|
||||
</join>
|
||||
</subclass>
|
||||
<subclass name="ChequePayment" discriminator-value="CHEQUE">
|
||||
<join table="CHEQUE_PAYMENT" fetch="select">
|
||||
...
|
||||
</join>
|
||||
</subclass>
|
||||
</class>]]></programlisting>
|
||||
|
||||
<para>
|
||||
可选的声明<literal>fetch="select"</literal>,是用来告诉Hibernate,在查询超类时,
|
||||
不要使用外部连接(outer join)来抓取子类<literal>ChequePayment</literal>的数据。
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
|
||||
<title>混合使用“每个类分层结构一张表”和“每个子类一张表”</title>
|
||||
|
||||
<para>
|
||||
你甚至可以采取如下方法混和使用“每个类分层结构一张表”和“每个子类一张表”这两种策略:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
|
||||
<id name="id" type="long" column="PAYMENT_ID">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<discriminator column="PAYMENT_TYPE" type="string"/>
|
||||
<property name="amount" column="AMOUNT"/>
|
||||
...
|
||||
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
|
||||
<join table="CREDIT_PAYMENT">
|
||||
<property name="creditCardType" column="CCTYPE"/>
|
||||
...
|
||||
</join>
|
||||
</subclass>
|
||||
<subclass name="CashPayment" discriminator-value="CASH">
|
||||
...
|
||||
</subclass>
|
||||
<subclass name="ChequePayment" discriminator-value="CHEQUE">
|
||||
...
|
||||
</subclass>
|
||||
</class>]]></programlisting>
|
||||
|
||||
<para>
|
||||
对上述任何一种映射策略而言,指向根类<literal>Payment</literal>的
|
||||
关联是使用<literal><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>每个具体类一张表(Table per concrete class)</title>
|
||||
|
||||
<para>
|
||||
对于“每个具体类一张表”的映射策略,可以采用两种方法。第一种方法是使用
|
||||
<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>
|
||||
这里涉及三张表。每张表为对应类的所有属性(包括从超类继承的属性)定义相应字段。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
这种方式的局限在于,如果一个属性在超类中做了映射,其字段名必须与所有子类
|
||||
表中定义的相同。(我们可能会在Hibernate的后续发布版本中放宽此限制。)
|
||||
不允许在联合子类(union subclass)的继承层次中使用标识生成器策略(identity generator strategy),
|
||||
实际上, 主键的种子(primary key seed)不得不为同一继承层次中的全部被联合子类所共用.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="inheritance-tableperconcreate-polymorphism">
|
||||
<title>Table per concrete class, using implicit polymorphism</title>
|
||||
<title>每个具体类一张表,使用隐式多态</title>
|
||||
|
||||
<para>
|
||||
另一种可供选择的方法是采用隐式多态:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
|
||||
<id name="id" type="long" column="CREDIT_PAYMENT_ID">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<property name="amount" column="CREDIT_AMOUNT"/>
|
||||
...
|
||||
</class>
|
||||
|
||||
<class name="CashPayment" table="CASH_PAYMENT">
|
||||
<id name="id" type="long" column="CASH_PAYMENT_ID">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<property name="amount" column="CASH_AMOUNT"/>
|
||||
...
|
||||
</class>
|
||||
|
||||
<class name="ChequePayment" table="CHEQUE_PAYMENT">
|
||||
<id name="id" type="long" column="CHEQUE_PAYMENT_ID">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<property name="amount" column="CHEQUE_AMOUNT"/>
|
||||
...
|
||||
</class>]]></programlisting>
|
||||
|
||||
<para>
|
||||
注意,我们没有在任何地方明确的提及接口<literal>Payment</literal>。同时注意
|
||||
<literal>Payment</literal>的属性在每个子类中都进行了映射。如果你想避免重复,
|
||||
可以考虑使用XML实体(例如:位于<literal>DOCTYPE</literal>声明内的
|
||||
<literal>[ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]</literal>
|
||||
和映射中的<literal>&allproperties;</literal>)。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
这种方法的缺陷在于,在Hibernate执行多态查询时(polymorphic queries)无法生成带
|
||||
<literal>UNION</literal>的SQL语句。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
对于这种映射策略而言,通常用<literal><any></literal>来实现到
|
||||
<literal>Payment</literal>的多态关联映射。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<any name="payment" meta-type="string" id-type="long">
|
||||
<meta-value value="CREDIT" class="CreditCardPayment"/>
|
||||
<meta-value value="CASH" class="CashPayment"/>
|
||||
<meta-value value="CHEQUE" class="ChequePayment"/>
|
||||
<column name="PAYMENT_CLASS"/>
|
||||
<column name="PAYMENT_ID"/>
|
||||
</any>]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="inheritace-mixingpolymorphism">
|
||||
<title>隐式多态和其他继承映射混合使用</title>
|
||||
|
||||
<para>
|
||||
对这一映射还有一点需要注意。因为每个子类都在各自独立的元素<literal><class></literal>
|
||||
中映射(并且<literal>Payment</literal>只是一个接口),每个子类可以很容易的成为另一
|
||||
个继承体系中的一部分!(你仍然可以对接口<literal>Payment</literal>使用多态查询。)
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
|
||||
<id name="id" type="long" column="CREDIT_PAYMENT_ID">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<discriminator column="CREDIT_CARD" type="string"/>
|
||||
<property name="amount" column="CREDIT_AMOUNT"/>
|
||||
...
|
||||
<subclass name="MasterCardPayment" discriminator-value="MDC"/>
|
||||
<subclass name="VisaPayment" discriminator-value="VISA"/>
|
||||
</class>
|
||||
|
||||
<class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">
|
||||
<id name="id" type="long" column="TXN_ID">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
...
|
||||
<joined-subclass name="CashPayment" table="CASH_PAYMENT">
|
||||
<key column="PAYMENT_ID"/>
|
||||
<property name="amount" column="CASH_AMOUNT"/>
|
||||
...
|
||||
</joined-subclass>
|
||||
<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
|
||||
<key column="PAYMENT_ID"/>
|
||||
<property name="amount" column="CHEQUE_AMOUNT"/>
|
||||
...
|
||||
</joined-subclass>
|
||||
</class>]]></programlisting>
|
||||
|
||||
<para>
|
||||
我们还是没有明确的提到<literal>Payment</literal>。
|
||||
如果我们针对接口<literal>Payment</literal>执行查询
|
||||
——如<literal>from Payment</literal>—— Hibernate
|
||||
自动返回<literal>CreditCardPayment</literal>(和它的子类,因为
|
||||
它们也实现了接口<literal>Payment</literal>)、
|
||||
<literal>CashPayment</literal>和<literal>Chequepayment</literal>的实例,
|
||||
但不返回<literal>NonelectronicTransaction</literal>的实例。
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="inheritance-limitations">
|
||||
<title>限制</title>
|
||||
|
||||
<para>
|
||||
对“每个具体类映射一张表”(table per concrete-class)的映射策略而言,隐式多态的
|
||||
方式有一定的限制。而<literal><union-subclass></literal>映射的限制则没有那
|
||||
么严格。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
下面表格中列出了在Hibernte中“每个具体类一张表”的策略和隐式多态的限制。
|
||||
</para>
|
||||
|
||||
<table frame="topbot">
|
||||
<title>继承映射特性(Features of inheritance mappings) </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>继承策略(Inheritance strategy)</entry>
|
||||
<entry>多态多对一</entry>
|
||||
<entry>多态一对一</entry>
|
||||
<entry>多态一对多</entry>
|
||||
<entry>多态多对多</entry>
|
||||
<entry>多态 <literal>load()/get()</literal></entry>
|
||||
<entry>多态查询</entry>
|
||||
<entry>多态连接(join)</entry>
|
||||
<entry>外连接(Outer join)抓取</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
|
||||
<entry>每个类分层结构一张表</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>支持</emphasis></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>每个子类一张表</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>支持</emphasis></entry>
|
||||
</row>
|
||||
<row>
|
||||
|
||||
<entry>每个具体类一张表(union-subclass)</entry>
|
||||
<entry><literal><many-to-one></literal></entry>
|
||||
<entry><literal><one-to-one></literal></entry>
|
||||
<entry><literal><one-to-many></literal> (仅对于<literal>inverse="true"</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>支持</emphasis></entry>
|
||||
</row>
|
||||
<row>
|
||||
|
||||
<entry>每个具体类一张表(隐式多态)</entry>
|
||||
<entry><literal><any></literal></entry>
|
||||
<entry><emphasis>不支持</emphasis></entry>
|
||||
<entry><emphasis>不支持</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>不支持</emphasis></entry>
|
||||
<entry><emphasis>不支持</emphasis></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
|
@ -0,0 +1,31 @@
|
|||
#!/bin/env perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
|
||||
open(F, $ARGV[0]) or die "Can't open file $ARGV[0]: $!\n";
|
||||
|
||||
my $status = 1;
|
||||
|
||||
READLINE:
|
||||
while (<F>) {
|
||||
if (/\s+\+{3,}/) {
|
||||
$status = -1;
|
||||
next READLINE;
|
||||
}
|
||||
if (/\s+-{3,}/) {
|
||||
$status = 1;
|
||||
next READLINE;
|
||||
}
|
||||
if (/\s+={3,}/) {
|
||||
next READLINE;
|
||||
}
|
||||
|
||||
if ($status < 0) {
|
||||
next READLINE;
|
||||
}
|
||||
|
||||
print $_;
|
||||
}
|
||||
|
|
@ -0,0 +1,446 @@
|
|||
<chapter id="persistent-classes" revision="2">
|
||||
<title>持久化类(Persistent Classes)</title>
|
||||
|
||||
<para>
|
||||
在应用程序中,用来实现业务问题实体的(如,在电子商务应用程序中的Customer和Order)
|
||||
类就是持久化类。不能认为所有的持久化类的实例都是持久的状态——一个实例的状态也可能
|
||||
是瞬时的或脱管的。
|
||||
</para>
|
||||
|
||||
|
||||
|
||||
<para>
|
||||
如果这些持久化类遵循一些简单的规则,Hibernate能够工作得最好,这些规则被称作,
|
||||
简单传统Java对象(POJO:Plain Old Java Object)编程模型。但是这些规则没有一个是必需的。
|
||||
实际上,Hibernate3对于你的持久化类几乎不做任何设想。你可以用其他的方法来表达领域模型:
|
||||
比如,使用<literal>Map</literal>实例的树型结构。
|
||||
</para>
|
||||
|
||||
|
||||
<sect1 id="persistent-classes-pojo">
|
||||
<title>一个简单的POJO例子</title>
|
||||
<para>
|
||||
大多数Java程序需要用一个持久化类来表示猫科动物。
|
||||
</para>
|
||||
|
||||
|
||||
<programlisting><![CDATA[package eg;
|
||||
import java.util.Set;
|
||||
import java.util.Date;
|
||||
|
||||
public class Cat {
|
||||
private Long id; // identifier
|
||||
|
||||
private Date birthdate;
|
||||
private Color color;
|
||||
private char sex;
|
||||
private float weight;
|
||||
private int litterId;
|
||||
|
||||
private Cat mother;
|
||||
private Set kittens = new HashSet();
|
||||
|
||||
private void setId(Long id) {
|
||||
this.id=id;
|
||||
}
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
void setBirthdate(Date date) {
|
||||
birthdate = date;
|
||||
}
|
||||
public Date getBirthdate() {
|
||||
return birthdate;
|
||||
}
|
||||
|
||||
void setWeight(float weight) {
|
||||
this.weight = weight;
|
||||
}
|
||||
public float getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
public Color getColor() {
|
||||
return color;
|
||||
}
|
||||
void setColor(Color color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
void setSex(char sex) {
|
||||
this.sex=sex;
|
||||
}
|
||||
public char getSex() {
|
||||
return sex;
|
||||
}
|
||||
|
||||
void setLitterId(int id) {
|
||||
this.litterId = id;
|
||||
}
|
||||
public int getLitterId() {
|
||||
return litterId;
|
||||
}
|
||||
|
||||
void setMother(Cat mother) {
|
||||
this.mother = mother;
|
||||
}
|
||||
public Cat getMother() {
|
||||
return mother;
|
||||
}
|
||||
void setKittens(Set kittens) {
|
||||
this.kittens = kittens;
|
||||
}
|
||||
public Set getKittens() {
|
||||
return kittens;
|
||||
}
|
||||
|
||||
// addKitten not needed by Hibernate
|
||||
public void addKitten(Cat kitten) {
|
||||
kitten.setMother(this);
|
||||
kitten.setLitterId( kittens.size() );
|
||||
kittens.add(kitten);
|
||||
}
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
这里要遵循四条主要的规则:
|
||||
</para>
|
||||
|
||||
<sect2 id="persistent-classes-pojo-accessors" revision="1">
|
||||
<title>为持久化字段声明访问器(accessors)和是否可变的标志(mutators)</title>
|
||||
|
||||
<para>
|
||||
<literal>Cat</literal>为它的所有持久化字段声明了访问方法。很多其他ORM工具直接对
|
||||
实例变量进行持久化。我们相信从持久化机制中分离这种实现细节要好得多。
|
||||
Hibernate持久化JavaBeans风格的属性,认可如下形式的方法名:
|
||||
<literal>getFoo</literal>, <literal>isFoo</literal> 和 <literal>setFoo</literal>。
|
||||
如果需要,你总是可以切换特定的属性的指示字段的访问方法。
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
属性<emphasis>不需要</emphasis>要声明为public的。Hibernate默认使用
|
||||
<literal>protected</literal>或<literal>private</literal>的get/set方法对,
|
||||
对属性进行持久化。
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="persistent-classes-pojo-constructor" revision="1">
|
||||
<title>实现一个默认的(即无参数的)构造方法(constructor)</title>
|
||||
|
||||
<para>
|
||||
<literal>Cat</literal>有一个无参数的构造方法。所有的持久化类都必须有一个
|
||||
默认的构造方法(可以不是public的),这样的话Hibernate就可以使用
|
||||
<literal>Constructor.newInstance()</literal>来实例化它们。
|
||||
我们建议,在Hibernate中,为了运行期代理的生成,构造方法至少是
|
||||
<emphasis>包(package)</emphasis>内可见的。
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="persistent-classes-pojo-identifier" revision="2">
|
||||
<title>提供一个标识属性(identifier property)(可选) </title>
|
||||
|
||||
<para>
|
||||
<literal>Cat</literal>有一个属性叫做<literal>id</literal>。这个属性映射数据库表的主
|
||||
键字段。这个属性可以叫任何名字,其类型可以是任何的原始类型、原始类型的包装类型、
|
||||
<literal>java.lang.String</literal> 或者是 <literal>java.util.Date</literal>。
|
||||
(如果你的老式数据库表有联合主键,你甚至可以用一个用户自定义的类,该类拥有这些类型
|
||||
的属性。参见后面的关于联合标识符的章节。)
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
标识符属性是可选的。可以不用管它,让Hibernate内部来追踪对象的识别。
|
||||
不推荐使用这个属性。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
实际上,一些功能只对那些声明了标识符属性的类起作用:
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
|
||||
<para>
|
||||
托管对象的传播性重新(和session)关联(级联更新或级联合并)
|
||||
——参阅 <xref linkend="objectstate-transitive"/>
|
||||
</para>
|
||||
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>Session.saveOrUpdate()</literal>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>Session.merge()</literal>
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
|
||||
<para>
|
||||
我们建议你对持久化类声明命名一致的标识属性。我们还建议你使用一
|
||||
个可以为空(也就是说,不是原始类型)的类型。
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="persistent-classes-pojo-final">
|
||||
<title>使用非final的类 (可选)</title>
|
||||
<para>
|
||||
<emphasis>代理(proxies)</emphasis>是Hibernate的一个重要的功能,它依赖的条件是,持久
|
||||
化类或者是非final的,或者是实现了一个所有方法都声明为public的接口。
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
你可以用Hibernate持久化一个没有实现任何接口的<literal>final</literal>类,但是你
|
||||
不能使用代理来延迟关联加载,这会限制你进行性能优化的选择。
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
你也应该避免在非final类中声明 <literal>public final</literal>的方法。如果你想使用一
|
||||
个有<literal>public final</literal>方法的类,你必须通过设置<literal>lazy="false"</literal>
|
||||
来明确的禁用代理。
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="persistent-classes-inheritance">
|
||||
<title>实现继承(Inheritance)</title>
|
||||
|
||||
<para>
|
||||
子类也必须遵守第一条和第二条规则。它从超类<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>实现<literal>equals()</literal>和<literal>hashCode()</literal></title>
|
||||
|
||||
<para>
|
||||
如果你有如下需求,你必须重载
|
||||
<literal>equals()</literal> 和 <literal>hashCode()</literal>方法:
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
想把持久类的实例放入<literal>Set</literal>中(当表示多值关联时,推荐这么做)
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
想重用脱管实例
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
Hibernate保证,持久化标识(数据库的行)和仅在特定会话范围内的Java标识是等值的。因此,一旦
|
||||
我们混合了从不同会话中获取的实例,如果我们希望<literal>Set</literal>有明确的语义,我们必
|
||||
须实现<literal>equals()</literal> 和<literal>hashCode()</literal>。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
实现<literal>equals()</literal>/<literal>hashCode()</literal>最显而易见的方法是比较两个对象
|
||||
标识符的值。如果值相同,则两个对象对应于数据库的同一行,因此它们是相等的(如果都被添加到
|
||||
<literal>Set</literal>,则在<literal>Set</literal>中只有一个元素)。不幸的是,对生成的标识不能
|
||||
使用这种方法。Hibernate仅对那些持久化对象赋标识值,一个新创建的实例将不会有任何标识值。此外,
|
||||
如果一个实例没有被保存(unsaved),并且在一个<literal>Set</literal>中,保存它将会给这个对象
|
||||
赋一个标识值。如果<literal>equals()</literal> 和 <literal>hashCode()</literal>是基于标识值
|
||||
实现的,则其哈希码将会改变,违反<literal>Set</literal>的契约。建议去Hibernate的站点看关于这个
|
||||
问题的全部讨论。注意,这不是一个Hibernate问题,而是一般的Java对象标识和相等的语义问题。
|
||||
</para>
|
||||
<para>
|
||||
我们建议使用<emphasis>业务键值相等(Business key equality)</emphasis>来实现<literal>equals()</literal>
|
||||
和 <literal>hashCode()</literal>。业务键值相等的意思是,<literal>equals()</literal>方法
|
||||
仅仅比较来自业务键的属性,一个业务键将标识在真实世界里(一个<emphasis>天生的</emphasis>候选键)
|
||||
的实例。
|
||||
</para>
|
||||
<programlisting><![CDATA[public class Cat {
|
||||
|
||||
...
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) return true;
|
||||
if ( !(other instanceof Cat) ) return false;
|
||||
|
||||
final Cat cat = (Cat) other;
|
||||
|
||||
if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
|
||||
if ( !cat.getMother().equals( getMother() ) ) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = getMother().hashCode();
|
||||
result = 29 * result + getLitterId();
|
||||
return result;
|
||||
}
|
||||
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
注意,业务键不必是象数据库的主键那样是固定不变的(参见<xref linkend="transactions-basics-identity"/>)。
|
||||
对业务键而言,不可变或唯一的属性是好的候选。
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="persistent-classes-dynamicmodels">
|
||||
<title>动态模型(Dynamic models)</title>
|
||||
<para>
|
||||
<emphasis>注意,以下特性在当前是基于实验考虑的,可能会在将来改变。</emphasis>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
运行期的持久化实体没有必要象POJO类或JavaBean对象一样表示。Hibernate也支持动态模型
|
||||
(在运行期使用<literal>Map</literal>的<literal>Map</literal>)和象DOM4J的树模型那
|
||||
样的实体表示。使用这种方法,你不用写持久化类,只写映射文件就行了。
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
Hibernate默认工作在普通POJO模式。你可以使用配置选项<literal>default_entity_mode</literal>,
|
||||
对特定的<literal>SessionFactory</literal>,设置一个默认的实体表示模式。
|
||||
(参见<xref linkend="configuration-optional-properties"/>。)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
下面是用<literal>Map</literal>来表示的例子。首先,在映射文件中,要声明
|
||||
<literal>entity-name</literal>来代替(或外加)一个类名。
|
||||
</para>
|
||||
<programlisting><![CDATA[<hibernate-mapping>
|
||||
|
||||
<class entity-name="Customer">
|
||||
|
||||
<id name="id"
|
||||
type="long"
|
||||
column="ID">
|
||||
<generator class="sequence"/>
|
||||
</id>
|
||||
|
||||
<property name="name"
|
||||
column="NAME"
|
||||
type="string"/>
|
||||
|
||||
<property name="address"
|
||||
column="ADDRESS"
|
||||
type="string"/>
|
||||
|
||||
<many-to-one name="organization"
|
||||
column="ORGANIZATION_ID"
|
||||
class="Organization"/>
|
||||
|
||||
<bag name="orders"
|
||||
inverse="true"
|
||||
lazy="false"
|
||||
cascade="all">
|
||||
<key column="CUSTOMER_ID"/>
|
||||
<one-to-many class="Order"/>
|
||||
</bag>
|
||||
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>]]></programlisting>
|
||||
|
||||
<para>
|
||||
注意,虽然是用目标类名来声明关联的,但是关联的目标类型除了是POJO之外,也可以
|
||||
是一个动态的实体。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
在使用<literal>dynamic-map</literal>为<literal>SessionFactory</literal>
|
||||
设置了默认的实体模式之后,可以在运行期使用<literal>Map</literal>的
|
||||
<literal>Map</literal>。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Session s = openSession();
|
||||
Transaction tx = s.beginTransaction();
|
||||
Session s = openSession();
|
||||
|
||||
// Create a customer
|
||||
Map david = new HashMap();
|
||||
david.put("name", "David");
|
||||
|
||||
// Create an organization
|
||||
Map foobar = new HashMap();
|
||||
foobar.put("name", "Foobar Inc.");
|
||||
|
||||
// Link both
|
||||
david.put("organization", foobar);
|
||||
|
||||
// Save both
|
||||
s.save("Customer", david);
|
||||
s.save("Organization", foobar);
|
||||
|
||||
tx.commit();
|
||||
s.close();]]></programlisting>
|
||||
|
||||
<para>
|
||||
动态映射的好处是,使原型在不需要实体类实现的情况下,快速转变时间。然而,你无法进行
|
||||
编译期的类型检查,并可能由此会处理很多的运行期异常。幸亏有了Hibernate映射,它使得数
|
||||
据库的schema能容易的规格化和合理化,并允许稍后添加正确的领域模型的最新实现。
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
实体表示模式也能在每个<literal>Session</literal>的基础上设置:
|
||||
</para>
|
||||
<programlisting><![CDATA[Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
|
||||
|
||||
// Create a customer
|
||||
Map david = new HashMap();
|
||||
david.put("name", "David");
|
||||
dynamicSession.save("Customer", david);
|
||||
...
|
||||
dynamicSession.flush();
|
||||
dynamicSession.close()
|
||||
...
|
||||
// Continue on pojoSession
|
||||
]]></programlisting>
|
||||
|
||||
|
||||
|
||||
<para>
|
||||
请注意,用<literal>EntityMode</literal>调用<literal>getSession()</literal>是在
|
||||
<literal>Session</literal>的API中,而不是<literal>SessionFactory</literal>。
|
||||
这样,新的<literal>Session</literal>共享底层的JDBC连接,事务,和其他的上下文信
|
||||
息。这意味着,你不需要在第二个<literal>Session</literal>中调用
|
||||
<literal>flush()</literal>和<literal>close()</literal>,同样的,把事务和连接的处理
|
||||
交给原来的工作单元。
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
关于XML表示能力的更多信息可以在<xref linkend="xml"/>中找到。
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
|
||||
<para>
|
||||
TODO:在property和proxy的包里,用户扩展文件框架。
|
||||
</para>
|
||||
|
||||
</chapter>
|
||||
|
|
@ -0,0 +1,376 @@
|
|||
<chapter id="querycriteria">
|
||||
<title>
|
||||
条件查询(Criteria Queries)
|
||||
</title>
|
||||
<para>
|
||||
具有一个直观的、可扩展的条件查询API是Hibernate的特色。
|
||||
</para>
|
||||
|
||||
<sect1 id="querycriteria-creating">
|
||||
<title>创建一个<literal>Criteria</literal> 实例</title>
|
||||
|
||||
<para>
|
||||
<literal>org.hibernate.Criteria</literal>接口表示特定持久类的一个查询。<literal>Session</literal>是
|
||||
<literal>Criteria</literal>实例的工厂。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Criteria crit = sess.createCriteria(Cat.class);
|
||||
crit.setMaxResults(50);
|
||||
List cats = crit.list();]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="querycriteria-narrowing">
|
||||
<title>限制结果集内容</title>
|
||||
|
||||
<para>
|
||||
一个单独的查询条件是<literal>org.hibernate.criterion.Criterion</literal>
|
||||
接口的一个实例。<literal>org.hibernate.criterion.Restrictions</literal>类
|
||||
定义了获得某些内置<literal>Criterion</literal>类型的工厂方法。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||||
.add( Restrictions.like("name", "Fritz%") )
|
||||
.add( Restrictions.between("weight", minWeight, maxWeight) )
|
||||
.list();]]></programlisting>
|
||||
|
||||
<para>
|
||||
约束可以按逻辑分组。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||||
.add( Restrictions.like("name", "Fritz%") )
|
||||
.add( Restrictions.or(
|
||||
Restrictions.eq( "age", new Integer(0) ),
|
||||
Restrictions.isNull("age")
|
||||
) )
|
||||
.list();]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||||
.add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
|
||||
.add( Restrictions.disjunction()
|
||||
.add( Restrictions.isNull("age") )
|
||||
.add( Restrictions.eq("age", new Integer(0) ) )
|
||||
.add( Restrictions.eq("age", new Integer(1) ) )
|
||||
.add( Restrictions.eq("age", new Integer(2) ) )
|
||||
) )
|
||||
.list();]]></programlisting>
|
||||
|
||||
<para>
|
||||
Hibernate提供了相当多的内置criterion类型(<literal>Restrictions</literal>
|
||||
子类), 但是尤其有用的是可以允许你直接使用SQL。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||||
.add( Restrictions.sql("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
|
||||
.list();]]></programlisting>
|
||||
|
||||
<para>
|
||||
<literal>{alias}</literal>占位符应当被替换为被查询实体的列别名。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
||||
<literal>Property</literal>实例是获得一个条件的另外一种途径。你可以通过调用<literal>Property.forName()</literal>
|
||||
创建一个<literal>Property</literal>。
|
||||
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[
|
||||
Property age = Property.forName("age");
|
||||
List cats = sess.createCriteria(Cat.class)
|
||||
.add( Restrictions.disjunction()
|
||||
.add( age.isNull() )
|
||||
.add( age.eq( new Integer(0) ) )
|
||||
.add( age.eq( new Integer(1) ) )
|
||||
.add( age.eq( new Integer(2) ) )
|
||||
) )
|
||||
.add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
|
||||
.list();]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="querycriteria-ordering">
|
||||
<title>结果集排序</title>
|
||||
|
||||
<para>
|
||||
你可以使用<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>关联</title>
|
||||
|
||||
<para>
|
||||
你可以使用<literal>createCriteria()</literal>非常容易的在互相关联的实体间建立
|
||||
约束。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||||
.add( Restrictions.like("name", "F%")
|
||||
.createCriteria("kittens")
|
||||
.add( Restrictions.like("name", "F%")
|
||||
.list();]]></programlisting>
|
||||
|
||||
<para>
|
||||
注意第二个 <literal>createCriteria()</literal>返回一个新的
|
||||
<literal>Criteria</literal>实例,该实例引用<literal>kittens</literal> 集合中的元素。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
接下来,替换形态在某些情况下也是很有用的。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||||
.createAlias("kittens", "kt")
|
||||
.createAlias("mate", "mt")
|
||||
.add( Restrictions.eqProperty("kt.name", "mt.name") )
|
||||
.list();]]></programlisting>
|
||||
|
||||
<para>
|
||||
|
||||
(<literal>createAlias()</literal>并不创建一个新的
|
||||
<literal>Criteria</literal>实例。)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literal>Cat</literal>实例所保存的之前两次查询所返回的kittens集合是
|
||||
<emphasis>没有</emphasis>被条件预过滤的。如果你希望只获得符合条件的kittens,
|
||||
你必须使用<literal>returnMaps()</literal>。
|
||||
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||||
.createCriteria("kittens", "kt")
|
||||
.add( Restrictions.eq("name", "F%") )
|
||||
.returnMaps()
|
||||
.list();
|
||||
Iterator iter = cats.iterator();
|
||||
while ( iter.hasNext() ) {
|
||||
Map map = (Map) iter.next();
|
||||
Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
|
||||
Cat kitten = (Cat) map.get("kt");
|
||||
}]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="querycriteria-dynamicfetching" revision="1">
|
||||
<title>动态关联抓取</title>
|
||||
|
||||
<para>
|
||||
你可以使用<literal>setFetchMode()</literal>在运行时定义动态关联抓取的语义。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||||
.add( Restrictions.like("name", "Fritz%") )
|
||||
.setFetchMode("mate", FetchMode.EAGER)
|
||||
.setFetchMode("kittens", FetchMode.EAGER)
|
||||
.list();]]></programlisting>
|
||||
|
||||
<para>
|
||||
这个查询可以通过外连接抓取<literal>mate</literal>和<literal>kittens</literal>。
|
||||
查看<xref linkend="performance-fetching"/>可以获得更多信息。
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="querycriteria-examples">
|
||||
<title>查询示例</title>
|
||||
|
||||
<para>
|
||||
<literal>org.hibernate.criterion.Example</literal>类允许你通过一个给定实例
|
||||
构建一个条件查询。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Cat cat = new Cat();
|
||||
cat.setSex('F');
|
||||
cat.setColor(Color.BLACK);
|
||||
List results = session.createCriteria(Cat.class)
|
||||
.add( Example.create(cat) )
|
||||
.list();]]></programlisting>
|
||||
|
||||
<para>
|
||||
版本属性、标识符和关联被忽略。默认情况下值为null的属性将被排除。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
你可以自行调整<literal>Example</literal>使之更实用。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Example example = Example.create(cat)
|
||||
.excludeZeroes() //exclude zero valued properties
|
||||
.excludeProperty("color") //exclude the property named "color"
|
||||
.ignoreCase() //perform case insensitive string comparisons
|
||||
.enableLike(); //use like for string comparisons
|
||||
List results = session.createCriteria(Cat.class)
|
||||
.add(example)
|
||||
.list();]]></programlisting>
|
||||
|
||||
<para>
|
||||
你甚至可以使用examples在关联对象上放置条件。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
|
||||
.add( Example.create(cat) )
|
||||
.createCriteria("mate")
|
||||
.add( Example.create( cat.getMate() ) )
|
||||
.list();]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="querycriteria-projection">
|
||||
<title>投影(Projections)、聚合(aggregation)和分组(grouping)</title>
|
||||
<para>
|
||||
<literal>org.hibernate.criterion.Projections</literal>是
|
||||
<literal>Projection</literal> 的实例工厂。我们通过调用
|
||||
<literal>setProjection()</literal>应用投影到一个查询。
|
||||
</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>
|
||||
在一个条件查询中没有必要显式的使用 "group by" 。某些投影类型就是被定义为<emphasis>
|
||||
分组投影</emphasis>,他们也出现在SQL的<literal>group by</literal>子句中。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
你可以选择把一个别名指派给一个投影,这样可以使投影值被约束或排序所引用。下面是两种不同的实现方式:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
|
||||
.setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
|
||||
.addOrder( Order.asc("colr") )
|
||||
.list();]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
|
||||
.setProjection( Projections.groupProperty("color").as("colr") )
|
||||
.addOrder( Order.asc("colr") )
|
||||
.list();]]></programlisting>
|
||||
|
||||
<para>
|
||||
<literal>alias()</literal>和<literal>as()</literal>方法简便的将一个投影实例包装到另外一个
|
||||
别名的<literal>Projection</literal>实例中。简而言之,当你添加一个投影到一个投影列表中时
|
||||
你可以为它指定一个别名:
|
||||
</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>
|
||||
你也可以使用<literal>Property.forName()</literal>来表示投影:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
|
||||
.setProjection( Property.forName("name") )
|
||||
.add( Property.forName("color").eq(Color.BLACK) )
|
||||
.list();]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
|
||||
.setProjection( Projections.projectionList()
|
||||
.add( Projections.rowCount().as("catCountByColor") )
|
||||
.add( Property.forName("weight").avg().as("avgWeight") )
|
||||
.add( Property.forName("weight").max().as("maxWeight") )
|
||||
.add( Property.forName("color").group().as("color" )
|
||||
)
|
||||
.addOrder( Order.desc("catCountByColor") )
|
||||
.addOrder( Order.desc("avgWeight") )
|
||||
.list();]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="querycriteria-detachedqueries">
|
||||
<title>离线(detached)查询和子查询</title>
|
||||
<para>
|
||||
<literal>DetachedCriteria</literal>类使你在一个session范围之外创建一个查询,并且可以使用任意的
|
||||
<literal>Session</literal>来执行它。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
|
||||
.add( Property.forName("sex").eq('F') );
|
||||
|
||||
Session session = ....;
|
||||
Transaction txn = session.beginTransaction();
|
||||
List results = query.getExecutableCriteria(session).setMaxResults(100).list();
|
||||
txn.commit();
|
||||
session.close();]]></programlisting>
|
||||
|
||||
<para>
|
||||
<literal>DetachedCriteria</literal>也可以用以表示子查询。条件实例包含子查询可以通过
|
||||
<literal>Subqueries</literal>或者<literal>Property</literal>获得。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
|
||||
.setProjection( Property.forName("weight").avg() );
|
||||
session.createCriteria(Cat.class)
|
||||
.add( Property.forName("weight).gt(avgWeight) )
|
||||
.list();]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
|
||||
.setProjection( Property.forName("weight") );
|
||||
session.createCriteria(Cat.class)
|
||||
.add( Subqueries.geAll("weight", weights) )
|
||||
.list();]]></programlisting>
|
||||
|
||||
<para>
|
||||
甚至相互关联的子查询也是有可能的:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
|
||||
.setProjection( Property.forName("weight").avg() )
|
||||
.add( Property.forName("cat2.sex").eqProperty("cat.sex") );
|
||||
session.createCriteria(Cat.class, "cat")
|
||||
.add( Property.forName("weight).gt(avgWeightForSex) )
|
||||
.list();]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<!--TODO: ResultSetTransformer + aliasing. AliasToBeanTransformer allow returning arbitrary
|
||||
user objects - similar to setResultClass in JDO2. General use of ResultTransformer
|
||||
could also be explained. -->
|
||||
</chapter>
|
||||
|
|
@ -0,0 +1,397 @@
|
|||
<chapter id="querysql" revision="2">
|
||||
<title>Native SQL查询<!--Native SQL--></title>
|
||||
<para>
|
||||
你也可以使用你的数据库的Native SQL语言来查询数据。这对你在要使用数据库的某些特性的时候(比如说在查询提示或者Oracle中的
|
||||
<literal>CONNECT</literal>关键字),这是非常有用的。这就能够扫清你把原来直接使用SQL/JDBC 的程序迁移到基于
|
||||
Hibernate应用的道路上的障碍。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Hibernate3允许你使用手写的sql来完成所有的create,update,delete,和load操作(包括存储过程)
|
||||
</para>
|
||||
|
||||
<sect1 id="querysql-creating">
|
||||
<title>创建一个基于SQL的<literal>Query</literal></title>
|
||||
|
||||
<para>
|
||||
SQL查询是通过<literal>SQLQuery</literal>接口来控制的,它是通过调用Session.createSQLQuery()方法来获得
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[List cats = sess.createSQLQuery("select {cat.*} from cats cat")
|
||||
.addEntity("cat", Cat.class);
|
||||
.setMaxResults(50);
|
||||
.list();]]></programlisting>
|
||||
|
||||
<para>
|
||||
这个查询指定了:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
SQL查询语句,它带一个占位符,可以让Hibernate使用字段的别名.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
查询返回的实体,和它的SQL表的别名.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
<literal>addEntity()</literal>方法将SQL表的别名和实体类联系起来,并且确定查询结果集的形态。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literal>addJoin()</literal>方法可以被用于载入其他的实体和集合的关联,TODO:examples!
|
||||
</para>
|
||||
|
||||
<para>
|
||||
原生的SQL查询可能返回一个简单的标量值或者一个标量和实体的结合体。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
|
||||
.addScalar("maxWeight", Hibernate.DOUBLE);
|
||||
.uniqueResult();]]></programlisting>
|
||||
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="querysql-aliasreferences">
|
||||
<title>别名和属性引用</title>
|
||||
|
||||
<para>
|
||||
上面使用的<literal>{cat.*}</literal>标记是 "所有属性" 的简写.你可以显式地列出需要的字段,但是你必须让Hibernate
|
||||
为每一个属性注入字段的别名.这些字段的站位符是以字段别名为前导,再加上属性名.在下面的例子里,我们从一个其他的表(<literal>cat_log</literal>)
|
||||
中获取<literal>Cat</literal>对象,而非Cat对象原本在映射元数据中声明的表.注意我们甚至在where子句中也可以使用属性别名.
|
||||
对于命名查询,{}语法并不是必需的.你可以在<xref linkend="querysql-namedqueries"/>得到更多的细节.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[String sql = "select cat.originalId as {cat.id}, " +
|
||||
"cat.mateid as {cat.mate}, cat.sex as {cat.sex}, " +
|
||||
"cat.weight*10 as {cat.weight}, cat.name as {cat.name} " +
|
||||
"from cat_log cat where {cat.mate} = :catId"
|
||||
|
||||
List loggedCats = sess.createSQLQuery(sql)
|
||||
.addEntity("cat", Cat.class)
|
||||
.setLong("catId", catId)
|
||||
.list();]]></programlisting>
|
||||
|
||||
<para>
|
||||
<emphasis>注意:</emphasis>如果你明确地列出了每个属性,你必须包含这个类<emphasis>和它的子类的属性</emphasis>! <emphasis>and its subclasses</emphasis>!
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="querysql-namedqueries" revision="2">
|
||||
<title>命名SQL查询</title>
|
||||
|
||||
<para>
|
||||
可以在映射文档中定义查询的名字,然后就可以象调用一个命名的HQL查询一样直接调用命名SQL查询.在这种情况下,我们<emphasis>不</emphasis>
|
||||
需要调用<literal>addEntity()</literal>方法.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<sql-query name="mySqlQuery">
|
||||
<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 'Hiber%'
|
||||
</sql-query>]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[List people = sess.getNamedQuery("mySqlQuery")
|
||||
.setMaxResults(50)
|
||||
.list();]]></programlisting>
|
||||
|
||||
<para>
|
||||
一个命名查询可能会返回一个标量值.你必须使用<literal><return-scalar></literal>元素来指定字段的别名和
|
||||
Hibernate类型
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<sql-query name="mySqlQuery">
|
||||
<return-scalar column="name" type="string"/>
|
||||
<return-scalar column="age" type="long"/>
|
||||
SELECT p.NAME AS name,
|
||||
p.AGE AS age,
|
||||
FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
|
||||
</sql-query>]]></programlisting>
|
||||
|
||||
<para>
|
||||
<literal><return-join></literal>和<literal><load-collection></literal>元素分别用作
|
||||
外连接和定义那些初始化集合的查询
|
||||
</para>
|
||||
|
||||
<sect2 id="propertyresults">
|
||||
<title>使用return-property来明确地指定字段/别名</title>
|
||||
|
||||
<para>
|
||||
使用<literal><return-property></literal>你可以明确的告诉Hibernate使用哪些字段,这和使用<literal>{}</literal>-语法
|
||||
来让Hibernate注入它自己的别名是相反的.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<sql-query name="mySqlQuery">
|
||||
<return alias="person" class="eg.Person">
|
||||
<return-property name="name" column="myName"/>
|
||||
<return-property name="age" column="myAge"/>
|
||||
<return-property name="sex" column="mySex"/>
|
||||
</return>
|
||||
SELECT person.NAME AS myName,
|
||||
person.AGE AS myAge,
|
||||
person.SEX AS mySex,
|
||||
FROM PERSON person WHERE person.NAME LIKE :name
|
||||
</sql-query>
|
||||
]]></programlisting>
|
||||
<literal><return-property></literal>也可用于多个字段,它解决了使用<literal>{}</literal>-语法不能细粒度控制多个字段的限制
|
||||
<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>
|
||||
注意在这个例子中,我们使用了<literal><return-property></literal>结合<literal>{}</literal>的注入语法.
|
||||
允许用户来选择如何引用字段以及属性.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
如果你映射一个识别器(discriminator),你必须使用<return-discriminator>来指定识别器字段
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="sp_query">
|
||||
<title>使用存储过程来查询</title>
|
||||
|
||||
<para>
|
||||
Hibernate 3引入了对存储过程查询的支持.
|
||||
|
||||
存储过程必须返回一个结果集,作为Hibernate能够使用的第一个外部参数.
|
||||
|
||||
下面是一个Oracle9和更高版本的存储过程例子.
|
||||
|
||||
<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>
|
||||
|
||||
在Hibernate里要要使用这个查询,你需要通过命名查询来映射它.
|
||||
|
||||
<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>
|
||||
|
||||
<para>
|
||||
注意存储过程当前仅仅返回标量和实体.现在不支持<literal><return-join></literal>和<literal><load-collection></literal>
|
||||
|
||||
</para>
|
||||
|
||||
<sect3 id="querysql-limits-storedprocedures">
|
||||
<title>使用存储过程的规则和限制</title>
|
||||
|
||||
<para>
|
||||
为了在Hibernate中使用存储过程,你必须遵循一些规则.不遵循这些规则的存储过程将不可用.如果你仍然想要使用他们,
|
||||
你必须通过<literal>session.connection()</literal>来执行他们.这些规则针对于不同的数据库.因为数据库
|
||||
提供商有各种不同的存储过程语法和语义.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
对存储过程进行的查询无法使用<literal>setFirstResult()/setMaxResults()</literal>进行分页。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
对于Oracle有如下规则:
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
存储过程必须返回一个结果集.它通过返回SYS_REFCURSOR实现(在Oracle9或10),在Oracle里你需要定义一个<literal>REF CURSOR</literal>
|
||||
类型
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
推荐的格式是 <literal>{ ? = call procName(<parameters>) }</literal> 或
|
||||
<literal>{ ? = call procName }</literal>(这更像是Oracle规则而不是Hibernate规则)
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
对于Sybase或者MS SQL server有如下规则:
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
存储过程必须返回一个结果集。.注意这些servers可能返回多个结果集以及更新的数目.Hibernate将取出第一条结果集作为它的返回值,
|
||||
其他将被丢弃。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
如果你能够在存储过程里设定<literal>SET NOCOUNT ON</literal>,这可能会效率更高,但这不是必需的。
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect3>
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="querysql-cud">
|
||||
<title>定制SQL用来create,update和delete</title>
|
||||
|
||||
<para>
|
||||
Hibernate3能够使用定制的SQL语句来执行create,update和delete操作。在Hibernate中,持久化的类和集合已经
|
||||
包含了一套配置期产生的语句(insertsql, deletesql, updatesql等等),这些映射标记 <literal><sql-insert></literal>,
|
||||
<literal><sql-delete></literal>, and <literal><sql-update></literal>重载了
|
||||
这些语句。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id">
|
||||
<generator class="increment"/>
|
||||
</id>
|
||||
<property name="name" not-null="true"/>
|
||||
<sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
|
||||
<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
|
||||
<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
|
||||
</class>]]></programlisting>
|
||||
|
||||
<para>
|
||||
这些SQL直接在你的数据库里执行,所以你可以自由的使用你喜欢的任意语法。但如果你使用数据库特定的语法,
|
||||
这当然会降低你映射的可移植性。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
如果设定<literal>callable</literal>,则能够支持存储过程了。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id">
|
||||
<generator class="increment"/>
|
||||
</id>
|
||||
<property name="name" not-null="true"/>
|
||||
<sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
|
||||
<sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
|
||||
<sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
|
||||
</class>]]></programlisting>
|
||||
|
||||
<para>
|
||||
参数的位置顺序是非常重要的,他们必须和Hibernate所期待的顺序相同。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
你能够通过设定日志调试级别为<literal>org.hiberante.persister.entity</literal>,来查看Hibernate所期待的顺序。在这个级别下,
|
||||
Hibernate将会打印出create,update和delete实体的静态SQL。如果想看到预想中的顺序。记得不要将定制SQL包含在映射文件里,
|
||||
因为他们会重载Hibernate生成的静态SQL。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
在大多数情况下(最好这么做),存储过程需要返回插入/更新/删除的行数,因为Hibernate对语句的成功执行有些运行时的检查。
|
||||
Hibernate常会把进行CUD操作的语句的第一个参数注册为一个数值型输出参数。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
|
||||
RETURN NUMBER IS
|
||||
BEGIN
|
||||
|
||||
update PERSON
|
||||
set
|
||||
NAME = uname,
|
||||
where
|
||||
ID = uid;
|
||||
|
||||
return SQL%ROWCOUNT;
|
||||
|
||||
END updatePerson;]]></programlisting>
|
||||
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="querysql-load">
|
||||
<title>定制装载SQL</title>
|
||||
|
||||
<para>
|
||||
你可能需要声明你自己的SQL(或HQL)来装载实体
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<sql-query name="person">
|
||||
<return alias="p" class="Person" lock-mode="upgrade"/>
|
||||
SELECT NAME AS {p.name}, ID AS {p.id} FROM PERSON WHERE ID=? FOR UPDATE
|
||||
</sql-query>]]></programlisting>
|
||||
|
||||
<para>
|
||||
这只是一个前面讨论过的命名查询声明,你可以在类映射里引用这个命名查询。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Person">
|
||||
<id name="id">
|
||||
<generator class="increment"/>
|
||||
</id>
|
||||
<property name="name" not-null="true"/>
|
||||
<loader query-ref="person"/>
|
||||
</class>]]></programlisting>
|
||||
|
||||
<para>
|
||||
这也可以用于存储过程
|
||||
</para>
|
||||
|
||||
<para>
|
||||
TODO: 未完成的例子
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<sql-query name="organizationEmployments">
|
||||
<load-collection alias="empcol" role="Organization.employments"/>
|
||||
SELECT {empcol.*}
|
||||
FROM EMPLOYMENT empcol
|
||||
WHERE EMPLOYER = :id
|
||||
ORDER BY STARTDATE ASC, EMPLOYEE ASC
|
||||
</sql-query>
|
||||
|
||||
<sql-query name="organizationCurrentEmployments">
|
||||
<return alias="emp" class="Employment"/>
|
||||
<synchronize table="EMPLOYMENT"/>
|
||||
SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
|
||||
STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
|
||||
REGIONCODE as {emp.regionCode}, ID AS {emp.id}
|
||||
FROM EMPLOYMENT
|
||||
WHERE EMPLOYER = :id AND ENDDATE IS NULL
|
||||
ORDER BY STARTDATE ASC
|
||||
</sql-query>]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
|
@ -0,0 +1,461 @@
|
|||
<chapter id="quickstart">
|
||||
<title>在Tomcat中快速上手</title>
|
||||
|
||||
<sect1 id="quickstart-intro" revision="2">
|
||||
<title>开始Hibernate之旅</title>
|
||||
|
||||
<para>
|
||||
这份教程描述如何在Apache Tomcat servlet容器中为web应用程序配置Hibernate 3.0(我们使用Tomcat 4.1版本,与5.0版本差别很小)。Hibernate在大多数主流J2EE应用服务器
|
||||
的运行环境中都可以工作良好,甚至也可以在独立Java应用程序中使用。在本教程中使用的示例数据库系统是PostgreSQL 7.4,只需要修改Hibernate SQL语言配置与连接属性,就可以
|
||||
很容易的支持其他数据库了。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
第一步,我们必须拷贝所有需要的库文件到Tomcat安装目录中。在这篇教程中,我们使用一个独立的web Context配置(<literal>webapps/quickstart</literal>)。我们确认全局库文件(<literal>TOMCAT/common/lib</literal>)和本web应用程序上下文的路径(对于jar来说是<literal>webapps/quickstart/WEB-INF/lib</literal>,对于class文件来说是<literal>webapps/quickstart/WEB-INF/classes</literal>)能够被类装载器检索到。我们把这两个类装载器级别分别称做全局类路径(global classpath)和上下文类路径(context classpath)。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
现在,把这些库文件copy到两个类路径去:
|
||||
</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
把数据库需要的JDBC驱动文件拷贝到全局类路径,这是tomcat捆绑的DBCP连接池所需要的。Hibernate使用JDBC连接数据库方式执行SQL语句,所以你要么提供外部连接池中的连接给Hibernate,或者配置Hibernate自带的连接池(C3PO,Proxool)。对于本教程来说,把<literal>pg74jdbc3.jar</literal>库文件(支持PostgreSQL 7.4和JDK 1.4)到全局类装载路径下即可。如果你希望使用其他的数据库,拷贝其相应的JDBC 驱动文件)。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
永远不要拷贝任何其他东西到Tomcat的全局类路径下,否则你可能在使用其他一些工具上遇到麻烦,比如log4j, commons-logging等等。
|
||||
一定要让每个web应用程序使用自己的上下文类路径,就是说把你自己需要的类库拷贝到<literal>WEB-INF/lib</literal>下去,把配置文件configuration/property等配置文件拷贝到<literal>WEB-INF/classes</literal>下面去。这两个目录都是当前程序缺省的上下文类路径。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Hibernate本身打包成一个JAR类库。将<literal>hibernate3.jar</literal>文件拷贝到程序的上下文类路径下,和你应用程序的其他库文件放一起。在运行时,Hibernate还需要一些第三方类库,它们在Hibernate发行包的<literal>lib/</literal>目录下。参见<xref linkend="3rdpartylibs"/>。把所需要的第三方库文件也拷贝到上下文类路径下。
|
||||
</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
|
||||
<table frame="topbot" id="3rdpartylibs">
|
||||
<title>
|
||||
Hibernate 第三方类库
|
||||
</title>
|
||||
<tgroup cols="2" rowsep="1" colsep="1">
|
||||
<colspec colname="c1" colwidth="1*"/>
|
||||
<colspec colname="c2" colwidth="2*"/>
|
||||
<thead>
|
||||
<row>
|
||||
<entry align="center">
|
||||
类库
|
||||
</entry>
|
||||
<entry align="center">
|
||||
描述
|
||||
</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
antlr (必需)
|
||||
</entry>
|
||||
<entry>
|
||||
Hibernate使用ANTLR来产生查询分析器,这个类库在运行环境下时也是必需的。
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
dom4j (必需)
|
||||
</entry>
|
||||
<entry>
|
||||
Hibernate使用dom4j解析XML配置文件和XML映射元文件。
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
CGLIB ,asm(必需)
|
||||
</entry>
|
||||
<entry>
|
||||
Hibernate在运行时使用这个代码生成库增强类(与Java反射机制联合使用)。
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
Commons Collections, Commons Logging (必需)
|
||||
</entry>
|
||||
<entry>
|
||||
Hibernat使用Apache Jakarta Commons项目提供的多个工具类库。
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
EHCache (必需)
|
||||
</entry>
|
||||
<entry>
|
||||
Hibernate可以使用不同cache缓存工具作为二级缓存。EHCache是缺省的cache缓存工具。
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
Log4j (可选)
|
||||
</entry>
|
||||
<entry>
|
||||
Hibernate使用Commons Logging API,它也可以依次使用Log4j作为底层实施log的机制。如果上下文类目录中存在Log4j库,则Commons Logging使用Log4j和并它在上下文类路径中寻找的<literal>log4j.properties</literal>文件。你可以使用在Hibernate发行包中包含中的那个示例Log4j的配置文件。这样,把log4j.jar和它的配置文件(位于<literal>src/</literal>目录中)拷贝到你的上下文类路径下,就可以在后台看到底程序如何运行的。
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
其他文件是不是必需的?
|
||||
</entry>
|
||||
<entry>
|
||||
请察看Hibernate发行包中的 lib/README.txt文件,这是一个Hibernate发行包中附带的第三方类库的列表,他们总是保持最新的。你可以在那里找到所有必需或者可选的类库(注意:其中的"buildtime required"指的是编译Hibernate时所需要而非编译你自己的程序所必需的类库)。
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>
|
||||
接下来我们来配置在Tomcat和Hibernate中共用的数据库连接池。也就是说Tomcat会提供经过池处理的JDBC连接(用它内置的DBCP连接池),Hibernate通过JNDI方式来请求获得JDBC连接。作为替代方案,你也可以让Hibernate自行管理连接池。Tomcat把连接池绑定到JNDI,我们要在Tomcat的主配置文件(<literal>TOMCAT/conf/server.xml</literal>)中加一个资源声明:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<Context path="/quickstart" docBase="quickstart">
|
||||
<Resource name="jdbc/quickstart" scope="Shareable" type="javax.sql.DataSource"/>
|
||||
<ResourceParams name="jdbc/quickstart">
|
||||
<parameter>
|
||||
<name>factory</name>
|
||||
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
|
||||
</parameter>
|
||||
|
||||
<!-- DBCP database connection settings -->
|
||||
<parameter>
|
||||
<name>url</name>
|
||||
<value>jdbc:postgresql://localhost/quickstart</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>driverClassName</name><value>org.postgresql.Driver</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>username</name>
|
||||
<value>quickstart</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>password</name>
|
||||
<value>secret</value>
|
||||
</parameter>
|
||||
|
||||
<!-- DBCP connection pooling options -->
|
||||
<parameter>
|
||||
<name>maxWait</name>
|
||||
<value>3000</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>maxIdle</name>
|
||||
<value>100</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>maxActive</name>
|
||||
<value>10</value>
|
||||
</parameter>
|
||||
</ResourceParams>
|
||||
</Context>]]></programlisting>
|
||||
|
||||
<para>
|
||||
我们在这个例子中要配置的上下文叫做<literal>quickstart</literal>,它位于<literal>TOMCAT/webapp/quickstart</literal>目录下。如果要访问这个应用程序,在你的浏览器中输入<literal>http://localhost:8080/quickstart</literal>就可以了(当然,在后面加上在你的<literal>web.xml</literal>文件中配置好你的servlet)。你现在可以创建一个只含有空<literal>process()</literal>的简单servlet了。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Tomcat现在通过JNDI的方式:<literal>java:comp/env/jdbc/quickstart</literal>来提供连接。如果你在配置连接池遇到问题,请查阅Tomcat文档。如果你遇到了JDBC驱动所报的exception出错信息,请在没有Hibernate的环境下,先测试JDBC连接池本身是否配置正确。Tomcat和JDBC的配置教程可以在Web上查到。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
下一步就是配置Hibernate。首先Hibernate必须知道它如何获得JDBC连接,在这里我们使用基于XML格式的Hibernate配置文件。当然使用properties文件的进行配置,但缺少一些XML语法的特性。这个XML配置文件必须放在上下文类路径(<literal>WEB-INF/classes</literal>)下面,命名为<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>
|
||||
在这里我们关闭了SQL命令的log,同时告诉Hibernate使用哪种SQL数据库用语(Dialet),以及如何得到JDBC连接(通过Tomcat声明绑定的JNDI地址)。Dialet是必需配置的,因为不同的数据库都和"SQL标准"有一些出入。不用担心,Hibernate会替你处理这些差异,Hibernate支持所有主流的商业和开放源代码数据库。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literal>SessionFactory</literal>是Hibernate的一个概念,表示对应一个数据存储源。通过创建多个XML配置文件并在你的程序中创建多个<literal>Configuration</literal>和<literal>SessionFactory</literal>对象,就可以支持多个数据库了。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
在<literal>hibernate.cfg.xml</literal>中的最后一个元素声明了<literal>Cat.hbm.xml</literal>,这是一个Hibernate XML映射文件,对应于持久化类<literal>Cat</literal>。这个文件包含了把<literal>Cat</literal> POJO类映射到数据库表(或多个数据库表)的元数据。我们稍后就回来看这个文件。下一步让我们先编写这个POJO类,然后在声明它的映射元数据。
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="quickstart-persistentclass" revision="1">
|
||||
<title>第一个持久化类</title>
|
||||
|
||||
<para>
|
||||
Hibernate使用简单的Java对象(Plain Old Java Objects ,就是POJOs,有时候也称作Plain Ordinary Java Objects)这种编程模型来进行持久化。一个POJO很像JavaBean,通过getter和setter方法访问其属性,对外则隐藏了内部实现的细节(假若需要的话,Hibernate也可以直接访问其属性字段)。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[package org.hibernate.examples.quickstart;
|
||||
|
||||
public class Cat {
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
private char sex;
|
||||
private float weight;
|
||||
|
||||
public Cat() {
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
private void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public char getSex() {
|
||||
return sex;
|
||||
}
|
||||
|
||||
public void setSex(char sex) {
|
||||
this.sex = sex;
|
||||
}
|
||||
|
||||
public float getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
public void setWeight(float weight) {
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
Hibernate对属性使用的类型不加任何限制。所有的Java JDK类型和原始类型(比如<literal>String</literal>,<literal>char</literal>和<literal>Date</literal>)都可以被映射,也包括Java 集合(Java collections framework)中的类。你可以把它们映射成为值,值集合,或者与其他实体类相关联。<literal>id</literal>是一个特殊的属性,代表了这个类的数据库标识符(主键),对于类似于<literal>Cat</literal>这样的实体类我们强烈建议使用。Hibernate也可以使用内部标识符,但这样我们会失去一些程序架构方面的灵活性。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
持久化类不需要实现什么特别的接口,也不需要从一个特别的持久化根类继承下来。Hibernate也不需要使用任何编译期处理,比如字节码增强操作,它独立的使用Java反射机制和运行时类增强(通过CGLIB)。所以不依赖于Hibernate,我们就可以把POJO的类映射成为数据库表。
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="quickstart-mapping" revision="1">
|
||||
<title>映射cat</title>
|
||||
|
||||
<para>
|
||||
<literal>Cat.hbm.xml</literal>映射文件包含了对象/关系映射(O/R Mapping)所需的元数据。元数据包含持久化类的声明和属性到数据库的映射(指向字段和其他实体的外键关联)。
|
||||
</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>
|
||||
每个持久化类都应该有一个标识属性(实际上,这个类只代表实体,而不是独立的值类型类,后者会被映射称为实体对象中的一个组件)。这个属性用来区分持久化对象:如果<literal>catA.getId().equals(catB.getId())</literal>结果是true的话,这两个Cat就是相同的。这个概念称为<emphasis>数据库标识</emphasis>。Hiernate附带了几种不同的标识符生成器,用于不同的场合(包括数据库本地的顺序(sequence)生成器、hi/lo高低位标识模式、和程序自己定义的标识符)。我们在这里使用UUID生成器(只在测试时建议使用,如果使用数据库自己生成的整数类型的键值更好),并指定<literal>CAT</literal>表中的<literal>CAT_ID</literal>字段(作为表的主键)存放生成的标识值。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literal>Cat</literal>的其他属性都映射到同一个表的字段。对<literal>name</literal>属性来说,我们把它显式地声明映射到一个数据库字段。如果数据库schema是通过由映射声明使用Hibernate的<emphasis>SchemaExport</emphasis>工具自动生成的(作为SQL DDL指令)的话,这就特别有用。所有其它的属性都用Hibernate的默认值映射,大多数情况你都会这样做。数据库中的<literal>CAT</literal>表看起来是这样的:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[ Column | Type | Modifiers
|
||||
--------+-----------------------+-----------
|
||||
cat_id | character(32) | not null
|
||||
name | character varying(16) | not null
|
||||
sex | character(1) |
|
||||
weight | real |
|
||||
Indexes: cat_pkey primary key btree (cat_id)]]></programlisting>
|
||||
|
||||
<para>
|
||||
你现在可以在你的数据库中手工创建这个表了,如果你需要使用<literal>hbm2ddl</literal>工具把这个步骤自动化,请参阅<xref linkend="toolsetguide"/>。这个工具能够创建完整的SQL DDL,包括表定义,自定义的字段类型约束,惟一约束和索引。
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="quickstart-playingwithcats" revision="2">
|
||||
<title>与Cat同乐</title>
|
||||
|
||||
<para>
|
||||
我们现在可以开始Hibernate的<literal>Session</literal>了。它是一个<emphasis>持久化管理器</emphasis>,我们通过它来从数据库中存取<literal>Cat</literal>。首先,我们要从<literal>SessionFactory</literal>中获取一个<literal>Session</literal>(Hibernate的工作单元)。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[SessionFactory sessionFactory =
|
||||
new Configuration().configure().buildSessionFactory();]]></programlisting>
|
||||
|
||||
<para>
|
||||
通过对<literal>configure()</literal>的调用来装载<literal>hibernate.cfg.xml</literal>配置文件,并初始化成一个<literal>Configuration</literal>实例。
|
||||
在创建 <literal>SessionFactory</literal><emphasis>之前</emphasis>(它是不可变的),你可以访问<literal>Configuration</literal>来设置其他属性(甚至修改映射的元数据)。我们应该在哪儿创建<literal>SessionFactory</literal>,在我们的程序中又如何访问它呢?
|
||||
<literal>SessionFactory</literal>通常只是被初始化一次,比如说通过一个<emphasis>load-on-startup</emphasis> servlet的来初始化。这意味着你不应该在serlvet中把它作为一个实例变量来持有,而应该放在其他地方。进一步的说,我们需要使用<emphasis>单例(Singleton)</emphasis>模式,我们才能更容易的在程序中访问<literal>SessionFactory</literal>。下面的方法就同时解决了两个问题:对<literal>SessionFactory</literal>的初始配置与便捷使用。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
我们实现一个<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>
|
||||
这个类不但在它的静态初始器中使用了<literal>SessionFactory</literal>,还使用了一个<literal>ThreadLocal</literal>变量来保存<literal>Session</literal>做为当前工作线程。在你使用这个辅助类之前,请确保你理解了thread-local变量这个Java概念。你可以在<literal>CaveatEmptor</literal>(http://caveatemptor.hibernate.org/)上找到一个更加复杂和强大的 <literal>HibernateUtil</literal>。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literal>SessionFactory</literal>是安全线程,可以由很多线程并发访问并获取到<literal>Sessions</literal>。单个<literal>Session</literal>不是安全线程对象,它只代表与数据库之间的一次操作。<literal>Session</literal>通过<literal>SessionFactory</literal>获得并在所有的工作完成后关闭。在你servlet的<literal>process()</literal>中可以象是这么写的(省略了异常情况处理):
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Session session = HibernateUtil.currentSession();
|
||||
|
||||
Transaction tx= session.beginTransaction();
|
||||
|
||||
Cat princess = new Cat();
|
||||
princess.setName("Princess");
|
||||
princess.setSex('F');
|
||||
princess.setWeight(7.4f);
|
||||
|
||||
session.save(princess);
|
||||
tx.commit();
|
||||
|
||||
HibernateUtil.closeSession();]]></programlisting>
|
||||
|
||||
<para>
|
||||
在一个<literal>Session</literal>中,每个数据库操作都是在一个事务(transaction)中进行的,这样就可以隔离开不同的操作(甚至包括只读操作)。我们使用Hibernate的<literal>Transaction</literal> API来从底层的事务策略中(本例中是JDBC事务)脱身出来。这样,我们就不需要更改任何源代码,就可以把我们的程序部署到一个由容器管理事务的环境中去(使用JTA)。
|
||||
|
||||
</para>
|
||||
|
||||
<para>
|
||||
这样你就可以随心所欲的多次调用<literal>HibernateUtil.currentSession();</literal>,你每次都会得到同一个当前线程的<literal>Session</literal>。不管是在你的servlet代码中,或者在servlet filter中还是在HTTP结果返回之前,你都必须确保这个<literal>Session</literal>在你的数据库访问工作完成后关闭。这样做还有一个好处就是可以容易的使用延迟装载(lazy initialization):<literal>Session</literal>在渲染view层的时候仍然打开着的,所以你在遍历当前对象图的时候可以装载所需的对象。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Hibernate有不同的方法用来从数据库中取回对象。最灵活的方式就是使用Hibernate查询语言(HQL),这是一种容易学习的语言,是对SQL的面向对象的强大扩展。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Transaction tx= session.beginTransaction();
|
||||
|
||||
Query query = session.createQuery("select c from Cat as c where c.sex = :sex");
|
||||
query.setCharacter("sex", 'F');
|
||||
for (Iterator it = query.iterate(); it.hasNext();) {
|
||||
Cat cat = (Cat) it.next();
|
||||
out.println("Female Cat: " + cat.getName() );
|
||||
}
|
||||
|
||||
tx.commit();]]></programlisting>
|
||||
|
||||
<para>
|
||||
Hibernate也提供一种面向对象的<emphasis>按条件查询</emphasis>API,可以执行简洁安全类型的查询。当然,Hibernate在所有与数据库的交互中都使用<literal>PrepatedStatement</literal>和参数绑定。你也可以使用Hibernate的直接SQL查询特性,或者在特殊情况下从<literal>Session</literal>获取一个原始的JDBC连接。
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="quickstart-summary" revision="1">
|
||||
<title>结语</title>
|
||||
|
||||
<para>
|
||||
在这个短小的教程中,我们对Hibernate浅尝即止。请注意我们没有在例子中包含任何servlet相关代码。你必须自行编写servlet,并插入适合你的Hibernate代码。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
请记住Hibernate作为一个数据库访问层,是与你的程序紧密相关的。通常情况下,所有其他层次都依赖持久机制。请确信你理解了这种设计的内涵。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
若希望学习更复杂的例子,请参阅http://caveatemptor.hibernate.org/ 。在 http://www.hibernate.org/Documentation 也可以得到其他教程的链接。
|
||||
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
|
@ -0,0 +1,406 @@
|
|||
<chapter id="toolsetguide" revision="2">
|
||||
<title>工具箱指南</title>
|
||||
|
||||
<para>
|
||||
可以通过一系列Eclipse插件、命令行工具和Ant任务来进行与Hibernate关联的转换。
|
||||
|
||||
</para>
|
||||
|
||||
<para>
|
||||
除了Ant任务外,当前的<emphasis>Hibernate Tools</emphasis>也包含了Eclipse IDE的插件,用于与现存数据库的逆向工程。
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>
|
||||
|
||||
<emphasis>Mapping Editor:</emphasis> Hibernate XML映射文件的编辑器,支持自动完成和语法高亮。它也支持对类名和属性/字段名的语义自动完成,比通常的XML编辑器方便得多。
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
<emphasis>Console:</emphasis> Console是Eclipse的一个新视图。除了对你的console配置的树状概览,你还可以获得对你持久化类及其关联的交互式视图。Console允许你对数据库执行HQL查询,并直接在Eclipse中浏览结果。
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
<emphasis>Development Wizards:</emphasis> 在Hibernate Eclipse tools中还提供了几个向导;你可以用向导快速生成Hibernate 配置文件(cfg.xml),你甚至还可以同现存的数据库schema中反向工程出POJO源代码与Hibernate 映射文件。反向工程支持可定制的模版。
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
<emphasis>Ant Tasks:</emphasis>
|
||||
</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
要得到更多信息,请查阅 <emphasis>Hibernate Tools</emphasis> 包及其文档。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
同时,Hibernate主发行包还附带了一个集成的工具(它甚至可以在Hibernate“内部”快速运行)<emphasis>SchemaExport</emphasis> ,也就是 <literal>hbm2ddl</literal>。
|
||||
|
||||
</para>
|
||||
|
||||
<sect1 id="toolsetguide-s1" revision="2">
|
||||
<title>Schema自动生成(Automatic schema generation)</title>
|
||||
|
||||
<para>
|
||||
可以从你的映射文件使用一个Hibernate工具生成DDL。 生成的schema包含有对实体和集合类表的完整性引用约束(主键和外键)。涉及到的标示符生成器所需的表和sequence也会同时生成。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
在使用这个工具的时候,你<emphasis>必须</emphasis> 通过<literal>hibernate.dialet</literal>属性指定一个SQL<literal>方言(Dialet)</literal>,因为DDL是与供应商高度相关的。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
首先,要定制你的映射文件,来改善生成的schema。
|
||||
</para>
|
||||
|
||||
<sect2 id="toolsetguide-s1-2" revision="1">
|
||||
<title>对schema定制化(Customizing the schema)</title>
|
||||
|
||||
<para>
|
||||
很多Hibernate映射元素定义了一个可选的<literal>length</literal>属性。你可以通过这个属性设置字段的长度。 (如果是Or, for numeric/decimal data types, the precision.)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
有些tag接受<literal>not-null</literal>属性(用来在表字段上生成<literal>NOT NULL</literal>约束)和<literal>unique</literal>属性(用来在表字段上生成<literal>UNIQUE</literal>约束)。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
有些tag接受<literal>index</literal>属性,用来指定字段的index名字。<literal>unique-key</literal>属性可以对成组的字段指定一个组合键约束(unit key constraint)。目前,<literal>unique-key</literal>属性指定的值<emphasis>并不会</emphasis>被当作这个约束的名字,它们只是在用来在映射文件内部用作区分的。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
示例:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<property name="foo" type="string" length="64" not-null="true"/>
|
||||
|
||||
<many-to-one name="bar" foreign-key="fk_foo_bar" not-null="true"/>
|
||||
|
||||
<element column="serial_number" type="long" not-null="true" unique="true"/>]]></programlisting>
|
||||
|
||||
<para>
|
||||
另外,这些元素还接受<literal><column></literal>子元素。在定义跨越多字段的类型时特别有用。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<property name="foo" type="string">
|
||||
<column name="foo" length="64" not-null="true" sql-type="text"/>
|
||||
</property>
|
||||
|
||||
<property name="bar" type="my.customtypes.MultiColumnType"/>
|
||||
<column name="fee" not-null="true" index="bar_idx"/>
|
||||
<column name="fi" not-null="true" index="bar_idx"/>
|
||||
<column name="fo" not-null="true" index="bar_idx"/>
|
||||
</property>]]></programlisting>
|
||||
|
||||
<para>
|
||||
<literal>sql-type</literal>属性允许用户覆盖默认的Hibernate类型到SQL数据类型的映射。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literal>check</literal>属性允许用户指定一个约束检查。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<property name="foo" type="integer">
|
||||
<column name="foo" check="foo > 10"/>
|
||||
</property>
|
||||
|
||||
<class name="Foo" table="foos" check="bar < 100.0">
|
||||
...
|
||||
<property name="bar" type="float"/>
|
||||
</class>]]></programlisting>
|
||||
|
||||
|
||||
<table frame="topbot" id="schemattributes-summary" revision="1">
|
||||
<title>Summary</title>
|
||||
<tgroup cols="2">
|
||||
<colspec colwidth="1*"/>
|
||||
<colspec colwidth="2.5*"/>
|
||||
<thead>
|
||||
<row>
|
||||
<entry>属性(Attribute)</entry>
|
||||
<entry>值(Values)</entry>
|
||||
<entry>解释(Interpretation)</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><literal>length</literal></entry>
|
||||
<entry>数字</entry>
|
||||
<entry>字段长度/小数点精度</entry>
|
||||
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>not-null</literal></entry>
|
||||
<entry><literal>true|false</literal></entry>
|
||||
<entry>指明字段是否应该是非空的</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>unique</literal></entry>
|
||||
<entry><literal>true|false</literal></entry>
|
||||
<entry>指明是否该字段具有惟一约束</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>index</literal></entry>
|
||||
<entry><literal>index_name</literal></entry>
|
||||
<entry>指明一个(多字段)的索引(index)的名字</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>unique-key</literal></entry>
|
||||
<entry><literal>unique_key_name</literal></entry>
|
||||
<entry>指明多字段惟一约束的名字(参见上面的说明)</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>foreign-key</literal></entry>
|
||||
<entry><literal>foreign_key_name</literal></entry>
|
||||
<entry>
|
||||
指明一个外键的名字,它是为关联生成的。
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>sql-type</literal></entry>
|
||||
<entry><literal>column_type</literal></entry>
|
||||
<entry>
|
||||
覆盖默认的字段类型(只能用于<literal><column></literal>属性)
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>check</literal></entry>
|
||||
<entry>SQL 表达式</entry>
|
||||
<entry>
|
||||
对字段或表加入SQL约束检查
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="toolsetguide-s1-3">
|
||||
<title>运行该工具</title>
|
||||
|
||||
<para>
|
||||
<literal>SchemaExport</literal>工具把DDL脚本写到标准输出,同时/或者执行DDL语句。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
|
||||
<literal>org.hibernate.tool.hbm2ddl.SchemaExport</literal> <emphasis>options mapping_files</emphasis>
|
||||
</para>
|
||||
|
||||
<table frame="topbot">
|
||||
<title><literal>SchemaExport</literal>命令行选项</title>
|
||||
<tgroup cols="2">
|
||||
<colspec colwidth="1.5*"/>
|
||||
<colspec colwidth="2*"/>
|
||||
<thead>
|
||||
<row>
|
||||
<entry>选项</entry>
|
||||
<entry>说明</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><literal>--quiet</literal></entry>
|
||||
<entry>不要把脚本输出到stdout</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>--drop</literal></entry>
|
||||
<entry>只进行drop tables的步骤</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>--text</literal></entry>
|
||||
<entry>不执行在数据库中运行的步骤</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>--output=my_schema.ddl</literal></entry>
|
||||
<entry>把输出的ddl脚本输出到一个文件</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>--config=hibernate.cfg.xml</literal></entry>
|
||||
<entry>从XML文件读入Hibernate配置</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>--properties=hibernate.properties</literal></entry>
|
||||
<entry>从文件读入数据库属性</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>--format</literal></entry>
|
||||
<entry>把脚本中的SQL语句对齐和美化</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>--delimiter=x</literal></entry>
|
||||
<entry>为脚本设置行结束符</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>
|
||||
你甚至可以在你的应用程序中嵌入<literal>SchemaExport</literal>工具:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Configuration cfg = ....;
|
||||
new SchemaExport(cfg).create(false, true);]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="toolsetguide-s1-4">
|
||||
<title>属性(Properties)</title>
|
||||
|
||||
<para>
|
||||
可以通过如下方式指定数据库属性:
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>通过<literal>-D</literal><emphasis><property></emphasis>系统参数</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>在<literal>hibernate.properties</literal>文件中</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>位于一个其它名字的properties文件中,然后用 <literal>--properties</literal>参数指定</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
所需的参数包括:
|
||||
</para>
|
||||
|
||||
<table frame="topbot">
|
||||
<title>SchemaExport 连接属性</title>
|
||||
<tgroup cols="2">
|
||||
<colspec colwidth="1.5*"/>
|
||||
<colspec colwidth="2*"/>
|
||||
<thead>
|
||||
<row>
|
||||
<entry>属性名</entry>
|
||||
<entry>说明</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><literal>hibernate.connection.driver_class</literal></entry>
|
||||
<entry>jdbc driver class</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>hibernate.connection.url</literal></entry>
|
||||
<entry>jdbc url</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>hibernate.connection.username</literal></entry>
|
||||
<entry>database user</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>hibernate.connection.password</literal></entry>
|
||||
<entry>user password</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>hibernate.dialect</literal></entry>
|
||||
<entry>方言(dialect)</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="toolsetguide-s1-5">
|
||||
<title>使用Ant(Using Ant)</title>
|
||||
|
||||
<para>
|
||||
你可以在你的Ant build脚本中调用<literal>SchemaExport</literal>:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<target name="schemaexport">
|
||||
<taskdef name="schemaexport"
|
||||
classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
|
||||
classpathref="class.path"/>
|
||||
|
||||
<schemaexport
|
||||
properties="hibernate.properties"
|
||||
quiet="no"
|
||||
text="no"
|
||||
drop="no"
|
||||
delimiter=";"
|
||||
output="schema-export.sql">
|
||||
<fileset dir="src">
|
||||
<include name="**/*.hbm.xml"/>
|
||||
</fileset>
|
||||
</schemaexport>
|
||||
</target>]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="toolsetguide-s1-6">
|
||||
<title>对schema的增量更新(Incremental schema updates)</title>
|
||||
|
||||
<para>
|
||||
<literal>SchemaUpdate</literal>工具对已存在的schema采用"增量"方式进行更新。注意<literal>SchemaUpdate</literal>严重依赖于JDBC metadata API,所以它并非对所有JDBC驱动都有效。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
|
||||
<literal>org.hibernate.tool.hbm2ddl.SchemaUpdate</literal> <emphasis>options mapping_files</emphasis>
|
||||
</para>
|
||||
|
||||
<table frame="topbot">
|
||||
<title><literal>SchemaUpdate</literal>命令行选项</title>
|
||||
<tgroup cols="2">
|
||||
<colspec colwidth="1.5*"/>
|
||||
<colspec colwidth="2*"/>
|
||||
<thead>
|
||||
<row>
|
||||
<entry>选项</entry>
|
||||
<entry>说明</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><literal>--quiet</literal></entry>
|
||||
<entry>不要把脚本输出到stdout</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>--properties=hibernate.properties</literal></entry>
|
||||
<entry>从指定文件读入数据库属性</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>
|
||||
你可以在你的应用程序中嵌入<literal>SchemaUpdate</literal>工具:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Configuration cfg = ....;
|
||||
new SchemaUpdate(cfg).execute(false);]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="toolsetguide-s1-7">
|
||||
<title>用Ant来增量更新schema(Using Ant for incremental schema updates)</title>
|
||||
|
||||
<para>
|
||||
你可以在Ant脚本中调用<literal>SchemaUpdate</literal>:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<target name="schemaupdate">
|
||||
<taskdef name="schemaupdate"
|
||||
classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
|
||||
classpathref="class.path"/>
|
||||
|
||||
<schemaupdate
|
||||
properties="hibernate.properties"
|
||||
quiet="no">
|
||||
<fileset dir="src">
|
||||
<include name="**/*.hbm.xml"/>
|
||||
</fileset>
|
||||
</schemaupdate>
|
||||
</target>]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
|
@ -0,0 +1,771 @@
|
|||
<chapter id="transactions" revision="1">
|
||||
<title>事务和并发</title>
|
||||
<para>
|
||||
Hibernate的事务和并发控制很容易掌握。Hibernate直接使用JDBC连接和JTA资源,不添加任何附加锁定
|
||||
行为。我们强烈推荐你花点时间了解JDBC编程,ANSI SQL查询语言和你使用
|
||||
的数据库系统的事务隔离规范。Hibernate只添加自动版本管理,而不会锁
|
||||
定内存中的对象,也不会改变数据库事务的隔离级别。基本上,使用
|
||||
Hibernate就好像直接使用JDBC(或者JTA/CMT)来访问你的数据库资源。
|
||||
</para>
|
||||
<para>
|
||||
除了自动版本管理,针对行级悲观锁定,Hibernate也提供了辅助的API,它使用了
|
||||
<literal>SELECT FOR UPDATE</literal>的SQL语法。本章后面会讨论这个API。
|
||||
</para>
|
||||
<para>
|
||||
我们从<literal>Configuration</literal>层、<literal>SessionFactory</literal>层, 和
|
||||
<literal>Session</literal>层开始讨论Hibernate的并行控制、数据库事务和应用
|
||||
程序的长事务。
|
||||
</para>
|
||||
<sect1 id="transactions-basics">
|
||||
<title>Session和事务范围(transaction scopes)</title>
|
||||
<para>
|
||||
一个<literal>SessionFactory</literal>对象的创建代价很昂贵,它是线程安全的对象,它被设计成可以
|
||||
为所有的应用程序线程所共享。它只创建一次,通常是在应用程序启动的时候,由一个
|
||||
<literal>Configuraion</literal>的实例来创建。
|
||||
</para>
|
||||
<para>
|
||||
一个<literal>Session</literal>的对象是轻型的,非线程安全的,对于单个业务进程,单个的
|
||||
工作单元而言,它只被使用一次,然后就丢弃。只有在需要的时候,<literal>Session</literal>
|
||||
才会获取一个JDBC的<literal>Connection</literal>(或一个<literal>Datasource</literal>)
|
||||
对象。所以你可以放心的打开和关闭<literal>Session</literal>,甚至当你并不确定一个特定的请
|
||||
求是否需要数据访问时,你也可以这样做。(一旦你实现下面提到的使用了请求拦截的模式,这就
|
||||
变得很重要了。
|
||||
</para>
|
||||
<para>
|
||||
此外我们还要考虑数据库事务。数据库事务应该尽可能的短,降低数据库锁定造成的资源争用。
|
||||
数据库长事务会导致你的应用程序无法扩展到高的并发负载。
|
||||
</para>
|
||||
<para>
|
||||
一个操作单元(Unit of work)的范围是多大?单个的Hibernate <literal>Session</literal>能跨越多个
|
||||
数据库事务吗?还是一个<literal>Session</literal>的作用范围对应一个数据库事务的范围?应该何时打开
|
||||
<literal>Session</literal>,何时关闭<literal>Session</literal>?,你又如何划分数据库事务的边界呢?
|
||||
</para>
|
||||
<sect2 id="transactions-basics-uow">
|
||||
<title>操作单元(Unit of work)</title>
|
||||
<para>
|
||||
首先,别再用<emphasis>session-per-operation</emphasis>这种反模式了,也就是说,在单个线程中,
|
||||
不要因为一次简单的数据库调用,就打开和关闭一次<literal>Session</literal>!数据库事务也是如此。
|
||||
应用程序中的数据库调用是按照计划好的次序,分组为原子的操作单元。(注意,这也意味着,应用程
|
||||
序中,在单个的SQL语句发送之后,自动事务提交(auto-commit)模式失效了。这种模式专门为SQL控制台操作设计的。
|
||||
Hibernate禁止立即自动事务提交模式,或者期望应用服务器禁止立即自动事务提交模式。)
|
||||
</para>
|
||||
<para>
|
||||
在多用户的client/server应用程序中,最常用的模式是 <emphasis>每个请求一个会话(session-per-request)</emphasis>。
|
||||
在这种模式下,来自客户端的请求被发送到服务器端(即Hibernate持久化层运行的地方),一
|
||||
个新的Hibernate <literal>Session</literal>被打开,并且执行这个操作单元中所有的数据库操作。
|
||||
一旦操作完成(同时发送到客户端的响应也准备就绪),session被同步,然后关闭。你也可以使用单
|
||||
个数据库事务来处理客户端请求,在你打开<literal>Session</literal>之后启动事务,在你关闭
|
||||
<literal>Session</literal>之前提交事务。会话和请求之间的关系是一对一的关系,这种模式对
|
||||
于大多数应用程序来说是很棒的。
|
||||
</para>
|
||||
<para>
|
||||
真正的挑战在于如何去实现这种模式:不仅<literal>Session</literal>和事务必须被正确的开始和结束,
|
||||
而且他们也必须能被数据访问操作访问。用拦截器来实现操作单元的划分,该拦截器在客户端请求达到服
|
||||
务器端的时候开始,在服务器端发送响应(即,<literal>ServletFilter</literal>)之前结束。我们推荐
|
||||
使用一个<literal>ThreadLocal</literal> 变量,把 <literal>Session</literal>绑定到处理客户端请求的线
|
||||
程上去。这种方式可以让运行在该线程上的所有程序代码轻松的访问<literal>Session</literal>(就像访问一
|
||||
个静态变量那样)。你也可以在一个<literal>ThreadLocal</literal> 变量中保持事务上下文环境,不过这依赖
|
||||
于你所选择的数据库事务划分机制。这种实现模式被称之为 <emphasis>ThreadLocal Session</emphasis>和
|
||||
<emphasis>Open Session in View</emphasis>。你可以很容易的扩展本文前面章节展示的
|
||||
<literal>HibernateUtil</literal> 辅助类来实现这种模式。当然,你必须找到一种实现拦截器的方法,并
|
||||
且可以把拦截器集成到你的应用环境中。请参考Hibernate网站上面的提示和例子。
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="transactions-basics-apptx">
|
||||
<title>应用程序事务(Application transactions)</title>
|
||||
<para>
|
||||
session-per-request模式不仅仅是一个可以用来设计操作单元的有用概念。很多业务处理流程都需
|
||||
要一系列完整的和用户之间的交互,即用户对数据库的交叉访问。在基于web的应用和企业
|
||||
应用中,跨用户交互的数据库事务是无法接受的。考虑下面的例子:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
在界面的第一屏,打开对话框,用户所看到的数据是被一个特定的 <literal>Session</literal> 和数据
|
||||
库事务载入(load)的。用户可以随意修改对话框中的数据对象。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
5分钟后,用户点击“保存”,期望所做出的修改被持久化;同时他也期望自己是唯一修改这个信息的人,不会出现
|
||||
修改冲突。
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
从用户的角度来看,我们把这个操作单元称为<emphasis>应用程序长事务</emphasis>(application transaction)。
|
||||
在你的应用程序中,可以有很多种方法来实现它。
|
||||
</para>
|
||||
<para>
|
||||
头一个幼稚的做法是,在用户思考的过程中,保持<literal>Session</literal>和数据库事务是打开的,
|
||||
保持数据库锁定,以阻止并发修改,从而保证数据库事务隔离级别和原子操作。这种方式当然是一个反模式,
|
||||
因为数据库锁定的维持会导致应用程序无法扩展并发用户的数目。
|
||||
</para>
|
||||
<para>
|
||||
很明显,我们必须使用多个数据库事务来实现一个应用程序事务。在这个例子中,维护业务处理流程的
|
||||
事务隔离变成了应用程序层的部分责任。单个应用程序事务通常跨越多个数据库事务。如果仅仅只有一
|
||||
个数据库事务(最后的那个事务)保存更新过的数据,而所有其他事务只是单纯的读取数据(例如在一
|
||||
个跨越多个请求/响应周期的向导风格的对话框中),那么应用程序事务将保证其原子性。这种方式比听
|
||||
起来还要容易实现,特别是当你使用了Hibernate的下述特性的时候:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>自动版本化</emphasis> - Hibernate能够自动进行乐观并发控制 ,如果在用户思考
|
||||
的过程中发生并发修改冲突,Hibernate能够自动检测到。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>脱管对象</emphasis>(Detached Objects)- 如果你决定采用前面已经讨论过的
|
||||
<emphasis>session-per-request</emphasis>模式,所有载入的实例在用户思考的过程
|
||||
中都处于与Session脱离的状态。Hibernate允许你把与Session脱离的对象重新关联到Session
|
||||
上,并且对修改进行持久化,这种模式被称为
|
||||
<emphasis>session-per-request-with-detached-objects</emphasis>。自动版本化被用来隔离并发修改。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>长生命周期的Session</emphasis> (Long Session)- Hibernate 的<literal>Session</literal>
|
||||
可以在数据库事务提交之后和底层的JDBC连接断开,当一个新的客户端请求到来的时候,它又重新连接上底层的
|
||||
JDBC连接。这种模式被称之为<emphasis>session-per-application-transaction</emphasis>,这种情况可
|
||||
能会造成不必要的Session和JDBC连接的重新关联。自动版本化被用来隔离并发修改。
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
<emphasis>session-per-request-with-detached-objects</emphasis> 和
|
||||
<emphasis>session-per-application-transaction</emphasis> 各有优缺点,我们在本章后面乐观并发
|
||||
控制那部分再进行讨论。
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="transactions-basics-identity">
|
||||
<title>关注对象标识(Considering object identity)</title>
|
||||
<para>
|
||||
应用程序可能在两个不同的<literal>Session</literal>中并发访问同一持久化状态,但是,
|
||||
一个持久化类的实例无法在两个 <literal>Session</literal>中共享。因此有两种不同的标识语义:
|
||||
</para>
|
||||
<para>
|
||||
<variablelist spacing="compact">
|
||||
<varlistentry>
|
||||
<term>数据库标识</term>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>foo.getId().equals( bar.getId() )</literal>
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>JVM 标识</term>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>foo==bar</literal>
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para>
|
||||
对于那些关联到 <emphasis>特定</emphasis><literal>Session</literal>
|
||||
(也就是在单个<literal>Session</literal>的范围内)上的对象来说,这
|
||||
两种标识的语义是等价的,与数据库标识对应的JVM标识是由Hibernate来保
|
||||
证的。不过,当应用程序在两个不同的session中并发访问具有同一持久化标
|
||||
识的业务对象实例的时候,这个业务对象的两个实例事实上是不相同的(从
|
||||
JVM识别来看)。这种冲突可以通过在同步和提交的时候使用自动版本化和乐
|
||||
观锁定方法来解决。
|
||||
</para>
|
||||
<para>
|
||||
这种方式把关于并发的头疼问题留给了Hibernate和数据库;由于在单个线程内,操作单元中的对象识别不
|
||||
需要代价昂贵的锁定或其他意义上的同步,因此它同时可以提供最好的可伸缩性。只要在单个线程只持有一个
|
||||
<literal>Session</literal>,应用程序就不需要同步任何业务对象。在<literal>Session</literal>
|
||||
的范围内,应用程序可以放心的使用<literal>==</literal>进行对象比较。
|
||||
</para>
|
||||
<para>
|
||||
不过,应用程序在<literal>Session</literal>的外面使用<literal>==</literal>进行对象比较可能会
|
||||
导致无法预期的结果。在一些无法预料的场合,例如,如果你把两个脱管对象实例放进同一个
|
||||
<literal>Set</literal>的时候,就可能发生。这两个对象实例可能有同一个数据库标识(也就是说,
|
||||
他们代表了表的同一行数据),从JVM标识的定义上来说,对脱管的对象而言,Hibernate无法保证他们
|
||||
的的JVM标识一致。开发人员必须覆盖持久化类的<literal>equals()</literal>方法和
|
||||
<literal>hashCode()</literal> 方法,从而实现自定义的对象相等语义。警告:不要使用数据库标识
|
||||
来实现对象相等,应该使用业务键值,由唯一的,通常不变的属性组成。当一个瞬时对象被持久化的时
|
||||
候,它的数据库标识会发生改变。如果一个瞬时对象(通常也包括脱管对象实例)被放入一
|
||||
个<literal>Set</literal>,改变它的hashcode会导致与这个<literal>Set</literal>的关系中断。虽
|
||||
然业务键值的属性不象数据库主键那样稳定不变,但是你只需要保证在同一个<literal>Set</literal>
|
||||
中的对象属性的稳定性就足够了。请到Hibernate网站去寻求这个问题更多的详细的讨论。请注意,这不是一
|
||||
个有关Hibernate的问题,而仅仅是一个关于Java对象标识和判等行为如何实现的问题。
|
||||
</para>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="transactions-basics-issues">
|
||||
<title>常见问题</title>
|
||||
<para>
|
||||
决不要使用反模式<emphasis>session-per-user-session</emphasis>或者<emphasis>
|
||||
session-per-application</emphasis>(当然,这个规定几乎没有例外)。请注意,
|
||||
下述一些问题可能也会出现在我们推荐的模式中,在你作出某个设计决定之前,请务必理解该模式的应用前提。
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>Session</literal> 是一个非线程安全的类。如果一个<literal>Session</literal>
|
||||
实例允许共享的话,那些支持并发运行的东东,例如HTTP request,session beans,或者是
|
||||
Swing workers,将会导致出现资源争用(race condition)。如果在<literal>HttpSession</literal>中有
|
||||
Hibernate 的<literal>Session</literal>的话(稍后讨论),你应该考虑同步访问你的Http session。
|
||||
否则,只要用户足够快的点击浏览器的“刷新”,就会导致两个并发运行线程使用同一个
|
||||
<literal>Session</literal>。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
一个由Hibernate抛出的异常意味着你必须立即回滚数据库事务,并立即关闭<literal>Session</literal>
|
||||
(稍后会展开讨论)。如果你的<literal>Session</literal>绑定到一个应用程序上,你必
|
||||
须停止该应用程序。回滚数据库事务并不会把你的业务对象退回到事务启动时候的状态。这
|
||||
意味着数据库状态和业务对象状态不同步。通常情况下,这不是什么问题,因为异常是不可
|
||||
恢复的,你必须在回滚之后重新开始执行。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>Session</literal> 缓存了处于持久化状态的每个对象(Hibernate会监视和检查脏数据)。
|
||||
这意味着,如果你让<literal>Session</literal>打开很长一段时间,或是仅仅载入了过多的数据,
|
||||
<literal>Session</literal>占用的内存会一直增长,直到抛出OutOfMemoryException异常。这个
|
||||
问题的一个解决方法是调用<literal>clear()</literal> 和<literal>evict()</literal>来管理
|
||||
<literal>Session</literal>的缓存,但是如果你需要大批量数据操作的话,最好考虑
|
||||
使用存储过程。在<xref linkend="batch"/>中有一些解决方案。在用户会话期间一直保持
|
||||
<literal>Session</literal>打开也意味着出现脏数据的可能性很高。
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="transactions-demarcation">
|
||||
<title>数据库事务声明</title>
|
||||
<para>
|
||||
数据库(或者系统)事务的声明总是必须的。在数据库事务之外,就无法和数据库通讯(这可能会让那些习惯于
|
||||
自动提交事务模式的开发人员感到迷惑)。永远使用清晰的事务声明,即使只读操作也是如此。进行
|
||||
显式的事务声明并不总是需要的,这取决于你的事务隔离级别和数据库的能力,但不管怎么说,声明事务总归有益无害。
|
||||
</para>
|
||||
<para>
|
||||
一个Hibernate应用程序可以运行在非托管环境中(也就是独立运行的应用程序,简单Web应用程序,
|
||||
或者Swing图形桌面应用程序),也可以运行在托管的J2EE环境中。在一个非托管环境中,Hibernate
|
||||
通常自己负责管理数据库连接池。应用程序开发人员必须手工设置事务声明,换句话说,就是手工启
|
||||
动,提交,或者回滚数据库事务。一个托管的环境通常提供了容器管理事务,例如事务装配通过可声
|
||||
明的方式定义在EJB session beans的部署描述符中。可编程式事务声明不再需要,即使是
|
||||
<literal>Session</literal> 的同步也可以自动完成。
|
||||
</para>
|
||||
<para>
|
||||
让持久层具备可移植性是人们的理想。Hibernate提供了一套称为<literal>Transaction</literal>的封装API,
|
||||
用来把你的部署环境中的本地事务管理系统转换到Hibernate事务上。这个API是可选的,但是我们强烈
|
||||
推荐你使用,除非你用CMT session bean。
|
||||
</para>
|
||||
<para>
|
||||
通常情况下,结束 <literal>Session</literal> 包含了四个不同的阶段:
|
||||
</para>
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
同步session(flush,刷出到磁盘)
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
提交事务
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
关闭session
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
处理异常
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
|
||||
<para>
|
||||
session的同步(flush,刷出)前面已经讨论过了,我们现在进一步考察在托管和非托管环境下的事务声明和异常处理。
|
||||
</para>
|
||||
|
||||
<sect2 id="transactions-demarcation-nonmanaged">
|
||||
<title>非托管环境</title>
|
||||
<para>
|
||||
如果Hibernat持久层运行在一个非托管环境中,数据库连接通常由Hibernate的连接池机制
|
||||
来处理。session/transaction处理方式如下所示:
|
||||
</para>
|
||||
<programlisting><![CDATA[//Non-managed environment idiom
|
||||
Session sess = factory.openSession();
|
||||
Transaction tx = null;
|
||||
try {
|
||||
tx = sess.beginTransaction();
|
||||
|
||||
// do some work
|
||||
...
|
||||
|
||||
tx.commit();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
if (tx != null) tx.rollback();
|
||||
throw e; // or display error message
|
||||
}
|
||||
finally {
|
||||
sess.close();
|
||||
}]]></programlisting>
|
||||
<para>
|
||||
你不需要显式<literal>flush()</literal> <literal>Session</literal> -
|
||||
对<literal>commit()</literal>的调用会自动触发session的同步。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
调用 <literal>close()</literal> 标志session的结束。
|
||||
<literal>close()</literal>方法重要的暗示是,<literal>session</literal>释放了JDBC连接。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
这段Java代码是可移植的,可以在非托管环境和JTA环境中运行。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
你很可能从未在一个标准的应用程序的业务代码中见过这样的用法;致命的(系统)异常应该总是
|
||||
在应用程序“顶层”被捕获。换句话说,执行Hibernate调用的代码(在持久层)和处理
|
||||
<literal>RuntimeException</literal>异常的代码(通常只能清理和退出应用程序)应该在不同
|
||||
的应用程序逻辑层。这对于你设计自己的软件系统来说是一个挑战,只要有可能,你就应该使用
|
||||
J2EE/EJB容器服务。异常处理将在本章稍后进行讨论。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
请注意,你应该选择 <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>
|
||||
(这是默认选项).
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="transactions-demarcation-jta">
|
||||
<title>使用JTA</title>
|
||||
<para>
|
||||
如果你的持久层运行在一个应用服务器中(例如,在EJB session beans的后面),Hibernate获取
|
||||
的每个数据源连接将自动成为全局JTA事务的一部分。Hibernate提供了两种策略进行JTA集成。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
如果你使用bean管理事务(BMT),可以通过使用Hibernate的 <literal>Transaction</literal> API来告诉
|
||||
应用服务器启动和结束BMT事务。因此,事务管理代码和在非托管环境下是一样的。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[// BMT idiom
|
||||
Session sess = factory.openSession();
|
||||
Transaction tx = null;
|
||||
try {
|
||||
tx = sess.beginTransaction();
|
||||
|
||||
// do some work
|
||||
...
|
||||
|
||||
tx.commit();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
if (tx != null) tx.rollback();
|
||||
throw e; // or display error message
|
||||
}
|
||||
finally {
|
||||
sess.close();
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
在CMT方式下,事务声明是在session bean的部署描述符中,而不需要编程。
|
||||
除非你设置了属性<literal>hibernate.transaction.flush_before_completion</literal>和
|
||||
<literal>hibernate.transaction.auto_close_session</literal>为<literal>true</literal>,
|
||||
否则你必须自己同步和关闭<literal>Session</literal>。Hibernate可以为你自动同步和关闭
|
||||
<literal>Session</literal>。你唯一要做的就是当发生异常时进行事务回滚。幸运的是,
|
||||
在一个CMT bean中,事务回滚甚至可以由容器自动进行,因为由session bean方法抛出的未处理的
|
||||
<literal>RuntimeException</literal>异常可以通知容器设置全局事务回滚。<emphasis>这意味着
|
||||
在CMT中,你完全无需使用Hibernate的<literal>Transaction</literal> API 。</emphasis>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
请注意,当你配置Hibernate事务工厂的时候,在一个BMT session bean中,你应该选择
|
||||
<literal>org.hibernate.transaction.JTATransactionFactory</literal>,在一个
|
||||
CMT session bean中选择<literal>org.hibernate.transaction.CMTTransactionFactory</literal>。
|
||||
记住,同时也要设置<literal>org.hibernate.transaction.manager_lookup_class</literal>。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
如果你使用CMT环境,并且让容器自动同步和关闭session,你可能也希望在你代码的不同部分使用
|
||||
同一个session。一般来说,在一个非托管环境中,你可以使用一个<literal>ThreadLocal</literal>
|
||||
变量来持有这个session,但是单个EJB方法调用可能会在不同的线程中执行(举例来说,一个session
|
||||
bean调用另一个session bean)。如果你不想在应用代码中被传递<literal>Session</literal>对
|
||||
象实例的问题困扰的话,那么<literal>SessionFactory</literal> 提供的
|
||||
<literal>getCurrentSession()</literal>方法就很适合你,该方法返回一个绑定到JTA事务
|
||||
上下文环境中的session实例。这也是把Hibernate集成到一个应用程序中的最简单的方法!这个“当
|
||||
前的”session总是可以自动同步和自动关闭(不考虑上述的属性设置)。我们的session/transaction
|
||||
管理代码减少到如下所示:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[// CMT idiom
|
||||
Session sess = factory.getCurrentSession();
|
||||
|
||||
// do some work
|
||||
...
|
||||
|
||||
]]></programlisting>
|
||||
|
||||
<para>
|
||||
换句话来说,在一个托管环境下,你要做的所有的事情就是调用
|
||||
<literal>SessionFactory.getCurrentSession()</literal>,然后进行你的数据访问,把其余的工作
|
||||
交给容器来做。事务在你的session bean的部署描述符中以可声明的方式来设置。session的生命周期完全
|
||||
由Hibernate来管理。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
对<literal>after_statement</literal>连接释放方式有一个警告。因为JTA规范的一个很愚蠢的限制,Hibernate不可能自动清理任何未关闭的<literal>ScrollableResults</literal> 或者<literal>Iterator</literal>,它们是由<literal>scroll()</literal>或<literal>iterate()</literal>产生的。你<emphasis>must</emphasis>通过在<literal>finally</literal>块中,显式调用<literal>ScrollableResults.close()</literal>或者<literal>Hibernate.close(Iterator)</literal>方法来释放底层数据库游标。(当然,大部分程序完全可以很容易的避免在CMT代码中出现<literal>scroll()</literal>或<literal>iterate()</literal>。)
|
||||
</para>
|
||||
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="transactions-demarcation-exceptions">
|
||||
<title>异常处理</title>
|
||||
<para>
|
||||
如果 <literal>Session</literal> 抛出异常 (包括任何<literal>SQLException</literal>),
|
||||
你应该立即回滚数据库事务,调用 <literal>Session.close()</literal> ,丢弃该
|
||||
<literal>Session</literal>实例。<literal>Session</literal>的某些方法可能会导致session
|
||||
处于不一致的状态。所有由Hibernate抛出的异常都视为不可以恢复的。确保在
|
||||
<literal>finally</literal> 代码块中调用<literal>close()</literal>方法,以关闭掉
|
||||
<literal>Session</literal>。
|
||||
</para>
|
||||
<para>
|
||||
<literal>HibernateException</literal>是一个非检查期异常(这不同于Hibernate老的版本),
|
||||
它封装了Hibernate持久层可能出现的大多数错误。我们的观点是,不应该强迫应用程序开发人员
|
||||
在底层捕获无法恢复的异常。在大多数软件系统中,非检查期异常和致命异常都是在相应方法调用
|
||||
的堆栈的顶层被处理的(也就是说,在软件上面的逻辑层),并且提供一个错误信息给应用软件的用户
|
||||
(或者采取其他某些相应的操作)。请注意,Hibernate也有可能抛出其他并不属于
|
||||
<literal>HibernateException</literal>的非检查期异常。这些异常同样也是无法恢复的,应该
|
||||
采取某些相应的操作去处理。
|
||||
</para>
|
||||
<para>
|
||||
在和数据库进行交互时,Hibernate把捕获的<literal>SQLException</literal>封装为Hibernate的
|
||||
<literal>JDBCException</literal>。事实上,Hibernate尝试把异常转换为更有实际含义
|
||||
的<literal>JDBCException</literal>异常的子类。底层的<literal>SQLException</literal>可以
|
||||
通过<literal>JDBCException.getCause()</literal>来得到。Hibernate通过使用关联到
|
||||
<literal>SessionFactory</literal>上的<literal>SQLExceptionConverter</literal>来
|
||||
把<literal>SQLException</literal>转换为一个对应的<literal>JDBCException</literal>
|
||||
异常的子类。默认情况下,<literal>SQLExceptionConverter</literal>可以通过配置dialect
|
||||
选项指定;此外,也可以使用用户自定义的实现类(参考javadocs
|
||||
<literal>SQLExceptionConverterFactory</literal>类来了解详情)。标准的
|
||||
<literal>JDBCException</literal>子类型是:
|
||||
</para>
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>JDBCConnectionException</literal> - 指明底层的JDBC通讯出现错误
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>SQLGrammarException</literal> - 指明发送的SQL语句的语法或者格式错误
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>ConstraintViolationException</literal> - 指明某种类型的约束违例错误
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>LockAcquisitionException</literal> - 指明了在执行请求操作时,获取
|
||||
所需的锁级别时出现的错误。
|
||||
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>GenericJDBCException</literal> - 不属于任何其他种类的原生异常
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="transactions-optimistic">
|
||||
<title>乐观并发控制(Optimistic concurrency control)</title>
|
||||
<para>
|
||||
唯一能够同时保持高并发和高可伸缩性的方法就是使用带版本化的乐观并发控制。版本检查使用版本号、
|
||||
或者时间戳来检测更新冲突(并且防止更新丢失)。Hibernate为使用乐观并发控制的代码提供了三种可
|
||||
能的方法,应用程序在编写这些代码时,可以采用它们。我们已经在前面应用程序长事务那部分展示了
|
||||
乐观并发控制的应用场景,此外,在单个数据库事务范围内,版本检查也提供了防止更新丢失的好处。
|
||||
</para>
|
||||
|
||||
<sect2 id="transactions-optimistic-manual">
|
||||
<title>应用程序级别的版本检查(Application version checking)</title>
|
||||
<para>
|
||||
未能充分利用Hibernate功能的实现代码中,每次和数据库交互都需要一个新的
|
||||
<literal>Session</literal>,而且开发人员必须在显示数据之前从数据库中重
|
||||
新载入所有的持久化对象实例。这种方式迫使应用程序自己实现版本检查来确保
|
||||
应用程序事务的隔离,从数据访问的角度来说是最低效的。这种使用方式和
|
||||
entity EJB最相似。
|
||||
</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>
|
||||
<literal>version</literal> 属性使用 <literal><version></literal>来映射,如果对象
|
||||
是脏数据,在同步的时候,Hibernate会自动增加版本号。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
当然,如果你的应用是在一个低数据并发环境下,并不需要版本检查的话,你照样可以使用
|
||||
这种方式,只不过跳过版本检查就是了。在这种情况下,<emphasis>最晚提交生效</emphasis>
|
||||
(<emphasis>last commit wins</emphasis>)就是你的应用程序长事务的默认处理策略。
|
||||
请记住这种策略可能会让应用软件的用户感到困惑,因为他们有可能会碰上更新丢失掉却没
|
||||
有出错信息,或者需要合并更改冲突的情况。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
很明显,手工进行版本检查只适合于某些软件规模非常小的应用场景,对于大多数软件应用场景
|
||||
来说并不现实。通常情况下,不仅是单个对象实例需要进行版本检查,整个被修改过的关
|
||||
联对象图也都需要进行版本检查。作为标准设计范例,Hibernate使用长生命周期
|
||||
<literal>Session</literal>的方式,或者脱管对象实例的方式来提供自动版本检查。
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="transactions-optimistic-longsession">
|
||||
<title>长生命周期session和自动版本化</title>
|
||||
<para>
|
||||
单个 <literal>Session</literal>实例和它所关联的所有持久化对象实例都被用于整个
|
||||
应用程序事务。Hibernate在同步的时候进行对象实例的版本检查,如果检测到并发修
|
||||
改则抛出异常。由开发人员来决定是否需要捕获和处理这个异常(通常的抉择是给用户
|
||||
提供一个合并更改,或者在无脏数据情况下重新进行业务操作的机会)。
|
||||
</para>
|
||||
<para>
|
||||
在等待用户交互的时候, <literal>Session</literal> 断开底层的JDBC连接。这种方式
|
||||
以数据库访问的角度来说是最高效的方式。应用程序不需要关心版本检查或脱管对象实例
|
||||
的重新关联,在每个数据库事务中,应用程序也不需要载入读取对象实例。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[// foo is an instance loaded earlier by the Session
|
||||
session.reconnect(); // Obtain a new JDBC connection
|
||||
Transaction t = session.beginTransaction();
|
||||
foo.setProperty("bar");
|
||||
t.commit(); // End database transaction, flushing the change and checking the version
|
||||
session.disconnect(); // Return JDBC connection ]]></programlisting>
|
||||
|
||||
<para>
|
||||
<literal>foo</literal> 对象始终和载入它的<literal>Session</literal>相关联。
|
||||
<literal>Session.reconnect()</literal>获取一个新的数据库连接(或者
|
||||
你可以提供一个),并且继续当前的session。<literal>Session.disconnect()</literal>
|
||||
方法把session与JDBC连接断开,把数据库连接返回到连接池(除非是你自己提供的数据
|
||||
库连接)。在Session重新连接上数据库连接之后,你可以对任何可能被其他事务更新过
|
||||
的对象调用<literal>Session.lock()</literal>,设置<literal>LockMode.READ</literal>
|
||||
锁定模式,这样你就可以对那些你不准备更新的数据进行强制版本检查。此外,你并不需要
|
||||
锁定那些你<emphasis>准备</emphasis>更新的数据。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
假若对<literal>disconnect()</literal>和<literal>reconnect()</literal>的显式调用发生得太频繁了,你可以使用<literal>hibernate.connection.release_mode</literal>来代替。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
如果在用户思考的过程中,<literal>Session</literal>因为太大了而不能保存,那么这种模式是有
|
||||
问题的。举例来说,一个<literal>HttpSession</literal>应该尽可能的小。由于
|
||||
<literal>Session</literal>是一级缓存,并且保持了所有被载入过的对象,因此
|
||||
我们只应该在那些少量的request/response情况下使用这种策略。而且在这种情况下,
|
||||
<literal>Session</literal> 里面很快就会有脏数据出现,因此请牢牢记住这一建议。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
此外,也请注意,你应该让与数据库连接断开的<literal>Session</literal>对持久层保持
|
||||
关闭状态。换句话说,使用有状态的EJB session bean来持有<literal>Session</literal>,
|
||||
而不要把它传递到web层(甚至把它序列化到一个单独的层),保存在<literal>HttpSession</literal>中。
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="transactions-optimistic-detached">
|
||||
<title>脱管对象(deatched object)和自动版本化</title>
|
||||
|
||||
<para>
|
||||
这种方式下,与持久化存储的每次交互都发生在一个新的<literal>Session</literal>中。
|
||||
然而,同一持久化对象实例可以在多次与数据库的交互中重用。应用程序操纵脱管对象实例
|
||||
的状态,这个脱管对象实例最初是在另一个<literal>Session</literal> 中载入的,然后
|
||||
调用 <literal>Session.update()</literal>,<literal>Session.saveOrUpdate()</literal>, 或者
|
||||
<literal>Session.merge()</literal> 来重新关联该对象实例。
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[// foo is an instance loaded by a previous Session
|
||||
foo.setProperty("bar");
|
||||
session = factory.openSession();
|
||||
Transaction t = session.beginTransaction();
|
||||
session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already
|
||||
t.commit();
|
||||
session.close();]]></programlisting>
|
||||
|
||||
<para>
|
||||
Hibernate会再一次在同步的时候检查对象实例的版本,如果发生更新冲突,就抛出异常。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
如果你确信对象没有被修改过,你也可以调用<literal>lock()</literal> 来设置
|
||||
<literal>LockMode.READ</literal>(绕过所有的缓存,执行版本检查),从而取
|
||||
代 <literal>update()</literal>操作。
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="transactions-optimistic-customizing">
|
||||
<title>定制自动版本化行为</title>
|
||||
<para>
|
||||
对于特定的属性和集合,通过为它们设置映射属性<literal>optimistic-lock</literal>的值
|
||||
为<literal>false</literal>,来禁止Hibernate的版本自动增加。这样的话,如果该属性
|
||||
脏数据,Hibernate将不再增加版本号。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
遗留系统的数据库Schema通常是静态的,不可修改的。或者,其他应用程序也可能访问同一数据
|
||||
库,根本无法得知如何处理版本号,甚至时间戳。在以上的所有场景中,实现版本化不能依靠
|
||||
数据库表的某个特定列。在<literal><class></literal>的映射中设置
|
||||
<literal>optimistic-lock="all"</literal>可以在没有版本或者时间戳属性映射的情况下实现
|
||||
版本检查,此时Hibernate将比较一行记录的每个字段的状态。请注意,只有当Hibernate能够比
|
||||
较新旧状态的情况下,这种方式才能生效,也就是说,
|
||||
你必须使用单个长生命周期<literal>Session</literal>模式,而不能使用
|
||||
session-per-request-with-detached-objects模式。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
有些情况下,只要更改不发生交错,并发修改也是允许的。当你在<literal><class></literal>
|
||||
的映射中设置<literal>optimistic-lock="dirty"</literal>,Hibernate在同步的时候将只比较有脏
|
||||
数据的字段。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
在以上所有场景中,不管是专门设置一个版本/时间戳列,还是进行全部字段/脏数据字段比较,
|
||||
Hibernate都会针对每个实体对象发送一条<literal>UPDATE</literal>(带有相应的
|
||||
<literal>WHERE</literal>语句 )的SQL语句来执行版本检查和数据更新。如果你对关联实体
|
||||
设置级联关系使用传播性持久化(transitive persistence),那么Hibernate可能会执行不必
|
||||
要的update语句。这通常不是个问题,但是数据库里面对<emphasis>on update</emphasis>点火
|
||||
的触发器可能在脱管对象没有任何更改的情况下被触发。因此,你可以在
|
||||
<literal><class></literal>的映射中,通过设置<literal>select-before-update="true"</literal>
|
||||
来定制这一行为,强制Hibernate <literal>SELECT</literal>这个对象实例,从而保证,
|
||||
在更新记录之前,对象的确是被修改过。
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="transactions-locking">
|
||||
<title>悲观锁定(Pessimistic Locking)</title>
|
||||
<para>
|
||||
用户其实并不需要花很多精力去担心锁定策略的问题。通常情况下,只要为JDBC连接指定一下隔
|
||||
离级别,然后让数据库去搞定一切就够了。然而,高级用户有时候希望进行一个排它的悲观锁定,
|
||||
或者在一个新的事务启动的时候,重新进行锁定。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Hibernate总是使用数据库的锁定机制,从不在内存中锁定对象!
|
||||
</para>
|
||||
|
||||
<para>
|
||||
类<literal>LockMode</literal> 定义了Hibernate所需的不同的锁定级别。一个锁定
|
||||
可以通过以下的机制来设置:
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
当Hibernate更新或者插入一行记录的时候,锁定级别自动设置为<literal>LockMode.WRITE</literal>。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
当用户显式的使用数据库支持的SQL格式<literal>SELECT ... FOR UPDATE</literal>
|
||||
发送SQL的时候,锁定级别设置为<literal>LockMode.UPGRADE</literal>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
当用户显式的使用Oracle数据库的SQL语句<literal>SELECT ... FOR UPDATE NOWAIT</literal>
|
||||
的时候,锁定级别设置<literal>LockMode.UPGRADE_NOWAIT</literal>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
当Hibernate在“可重复读”或者是“序列化”数据库隔离级别下读取数据的时候,锁定模式
|
||||
自动设置为<literal>LockMode.READ</literal>。这种模式也可以通过用户显式指定进行设置。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>LockMode.NONE</literal> 代表无需锁定。在<literal>Transaction</literal>结束时,
|
||||
所有的对象都切换到该模式上来。与session相关联的对象通过调用<literal>update()</literal>
|
||||
或者<literal>saveOrUpdate()</literal>脱离该模式。
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
"显式的用户指定"可以通过以下几种方式之一来表示:
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
调用 <literal>Session.load()</literal>的时候指定<literal>锁定模式(LockMode)</literal>。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
调用<literal>Session.lock()</literal>。
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
调用<literal>Query.setLockMode()</literal>。
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
如果在<literal>UPGRADE</literal>或者<literal>UPGRADE_NOWAIT</literal>锁定模式下调
|
||||
用<literal>Session.load()</literal>,并且要读取的对象尚未被session载入过,那么对象
|
||||
通过<literal>SELECT ... FOR UPDATE</literal>这样的SQL语句被载入。如果为一个对象调用
|
||||
<literal>load()</literal>方法时,该对象已经在另一个较少限制的锁定模式下被载入了,那
|
||||
么Hibernate就对该对象调用<literal>lock()</literal> 方法。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
如果指定的锁定模式是<literal>READ</literal>, <literal>UPGRADE</literal> 或
|
||||
<literal>UPGRADE_NOWAIT</literal>,那么<literal>Session.lock()</literal>就
|
||||
执行版本号检查。(在<literal>UPGRADE</literal> 或者<literal>UPGRADE_NOWAIT</literal>
|
||||
锁定模式下,执行<literal>SELECT ... FOR UPDATE</literal>这样的SQL语句。)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
如果数据库不支持用户设置的锁定模式,Hibernate将使用适当的替代模式(而不是扔出异常)。
|
||||
这一点可以确保应用程序的可移植性。
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
|
@ -0,0 +1,270 @@
|
|||
<chapter id="xml">
|
||||
<title>XML映射</title>
|
||||
<para><emphasis>
|
||||
注意这是Hibernate 3.0的一个实验性的特性。这一特性仍在积极开发中。
|
||||
</emphasis></para>
|
||||
|
||||
<sect1 id="xml-intro" revision="1">
|
||||
<title>用XML数据进行工作</title>
|
||||
|
||||
<para>
|
||||
Hibernate使得你可以用XML数据来进行工作,恰如你用持久化的POJO进行工作那样。解析过的XML树
|
||||
可以被认为是另外一种在对象层面上代替POJO来表示关系型数据的途径.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Hibernate支持采用dom4j作为操作XML树的API。你可以写一个查询从数据库中检索出
|
||||
dom4j树,随后你对这颗树做的任何修改都将自动同步回数据库。你甚至可以用dom4j解析
|
||||
一篇XML文档,然后使用Hibernate的任一基本操作将它写入数据库:
|
||||
<literal>persist(), saveOrUpdate(), merge(), delete(), replicate()</literal>
|
||||
(合并操作merge()目前还不支持)。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
这一特性可以应用在很多场合,包括数据导入导出,通过JMS或SOAP表现实体数据以及
|
||||
基于XSLT的报表。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
一个单一的映射就可以将类的属性和XML文档的节点同时映射到数据库。如果不需要映射类,
|
||||
它也可以用来只映射XML文档。
|
||||
</para>
|
||||
|
||||
<sect2 id="xml-intro-mapping">
|
||||
<title>指定同时映射XML和类</title>
|
||||
|
||||
<para>
|
||||
这是一个同时映射POJO和XML的例子:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Account"
|
||||
table="ACCOUNTS"
|
||||
node="account">
|
||||
|
||||
<id name="accountId"
|
||||
column="ACCOUNT_ID"
|
||||
node="@id"/>
|
||||
|
||||
<many-to-one name="customer"
|
||||
column="CUSTOMER_ID"
|
||||
node="customer/@id"
|
||||
embed-xml="false"/>
|
||||
|
||||
<property name="balance"
|
||||
column="BALANCE"
|
||||
node="balance"/>
|
||||
|
||||
...
|
||||
|
||||
</class>]]></programlisting>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="xml-onlyxml">
|
||||
<title>只定义XML映射</title>
|
||||
|
||||
<para>
|
||||
这是一个不映射POJO的例子:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class entity-name="Account"
|
||||
table="ACCOUNTS"
|
||||
node="account">
|
||||
|
||||
<id name="id"
|
||||
column="ACCOUNT_ID"
|
||||
node="@id"
|
||||
type="string"/>
|
||||
|
||||
<many-to-one name="customerId"
|
||||
column="CUSTOMER_ID"
|
||||
node="customer/@id"
|
||||
embed-xml="false"
|
||||
entity-name="Customer"/>
|
||||
|
||||
<property name="balance"
|
||||
column="BALANCE"
|
||||
node="balance"
|
||||
type="big_decimal"/>
|
||||
|
||||
...
|
||||
|
||||
</class>]]></programlisting>
|
||||
|
||||
<para>
|
||||
这个映射使得你既可以把数据作为一棵dom4j树那样访问,又可以作为由属性键值对(java <literal>Map</literal>s)
|
||||
组成的图那样访问。属性名字是纯粹逻辑上的结构,你可以在HQL查询中引用它。
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="xml-mapping" revision="1">
|
||||
<title>XML映射元数据</title>
|
||||
|
||||
<para>
|
||||
许多Hibernate映射元素具有<literal>node</literal>属性。这使你可以指定用来保存
|
||||
属性或实体数据的XML属性或元素。<literal>node</literal>属性必须是下列格式之一:
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para><literal>"element-name"</literal> - 映射为指定的XML元素</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><literal>"@attribute-name"</literal> - 映射为指定的XML属性</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><literal>"."</literal> - 映射为父元素</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>"element-name/@attribute-name"</literal> -
|
||||
映射为指定元素的指定属性
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
对于集合和单值的关联,有一个额外的<literal>embed-xml</literal>属性可用。
|
||||
这个属性的缺省值是真(<literal>embed-xml="true"</literal>)。如果<literal>embed-xml="true"</literal>,
|
||||
则对应于被关联实体或值类型的集合的XML树将直接嵌入拥有这些关联的实体的XML树中。
|
||||
否则,如果<literal>embed-xml="false"</literal>,那么对于单值的关联,仅被引用的实体的标识符出现在
|
||||
XML树中(被引用实体本身不出现),而集合则根本不出现。
|
||||
</para>
|
||||
|
||||
<para>
|
||||
你应该小心,不要让太多关联的embed-xml属性为真(<literal>embed-xml="true"</literal>),因为XML不能很好地处理
|
||||
循环引用!
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="Customer"
|
||||
table="CUSTOMER"
|
||||
node="customer">
|
||||
|
||||
<id name="id"
|
||||
column="CUST_ID"
|
||||
node="@id"/>
|
||||
|
||||
<map name="accounts"
|
||||
node="."
|
||||
embed-xml="true">
|
||||
<key column="CUSTOMER_ID"
|
||||
not-null="true"/>
|
||||
<map-key column="SHORT_DESC"
|
||||
node="@short-desc"
|
||||
type="string"/>
|
||||
<one-to-many entity-name="Account"
|
||||
embed-xml="false"
|
||||
node="account"/>
|
||||
</map>
|
||||
|
||||
<component name="name"
|
||||
node="name">
|
||||
<property name="firstName"
|
||||
node="first-name"/>
|
||||
<property name="initial"
|
||||
node="initial"/>
|
||||
<property name="lastName"
|
||||
node="last-name"/>
|
||||
</component>
|
||||
|
||||
...
|
||||
|
||||
</class>]]></programlisting>
|
||||
|
||||
<para>
|
||||
在这个例子中,我们决定嵌入帐目号码(account id)的集合,但不嵌入实际的帐目数据。下面的HQL查询:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
|
||||
|
||||
<para>
|
||||
返回的数据集将是这样:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<customer id="123456789">
|
||||
<account id="987632567" short-desc="Savings"/>
|
||||
<account id="985612323" short-desc="Credit Card"/>
|
||||
<name>
|
||||
<first-name>Gavin</first-name>
|
||||
<initial>A</initial>
|
||||
<last-name>King</last-name>
|
||||
</name>
|
||||
...
|
||||
</customer>]]></programlisting>
|
||||
|
||||
<para>
|
||||
如果你把一对多映射<literal><one-to-many></literal>的embed-xml属性置为真(<literal>embed-xml="true"</literal>),
|
||||
则数据看上去就像这样:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<customer id="123456789">
|
||||
<account id="987632567" short-desc="Savings">
|
||||
<customer id="123456789"/>
|
||||
<balance>100.29</balance>
|
||||
</account>
|
||||
<account id="985612323" short-desc="Credit Card">
|
||||
<customer id="123456789"/>
|
||||
<balance>-2370.34</balance>
|
||||
</account>
|
||||
<name>
|
||||
<first-name>Gavin</first-name>
|
||||
<initial>A</initial>
|
||||
<last-name>King</last-name>
|
||||
</name>
|
||||
...
|
||||
</customer>]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
<sect1 id="xml-manipulation" revision="1">
|
||||
<title>操作XML数据</title>
|
||||
|
||||
<para>
|
||||
让我们来读入和更新应用程序中的XML文档。通过获取一个dom4j会话可以做到这一点:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Document doc = ....;
|
||||
|
||||
Session session = factory.openSession();
|
||||
Session dom4jSession = session.getSession(EntityMode.DOM4J);
|
||||
Transaction tx = session.beginTransaction();
|
||||
|
||||
List results = dom4jSession
|
||||
.createQuery("from Customer c left join fetch c.accounts where c.lastName like :lastName")
|
||||
.list();
|
||||
for ( int i=0; i<results.size(); i++ ) {
|
||||
//add the customer data to the XML document
|
||||
Element customer = (Element) results.get(i);
|
||||
doc.add(customer);
|
||||
}
|
||||
|
||||
tx.commit();
|
||||
session.close();]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[Session session = factory.openSession();
|
||||
Session dom4jSession = session.getSession(EntityMode.DOM4J);
|
||||
Transaction tx = session.beginTransaction();
|
||||
|
||||
Element cust = (Element) dom4jSession.get("Customer", customerId);
|
||||
for ( int i=0; i<results.size(); i++ ) {
|
||||
Element customer = (Element) results.get(i);
|
||||
//change the customer name in the XML and database
|
||||
Element name = customer.element("name");
|
||||
name.element("first-name").setText(firstName);
|
||||
name.element("initial").setText(initial);
|
||||
name.element("last-name").setText(lastName);
|
||||
}
|
||||
|
||||
tx.commit();
|
||||
session.close();]]></programlisting>
|
||||
|
||||
<para>
|
||||
将这一特色与Hibernate的<literal>replicate()</literal>操作结合起来而实现的基于XML的数据导入/导出将非常有用.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
|
@ -0,0 +1,509 @@
|
|||
<?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"/>
|
||||
|
||||
<!--###################################################
|
||||
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'"/>
|
||||
|
||||
<!-- Simplified Chinese related Settings -->
|
||||
<xsl:param name="hyphenate">false</xsl:param>
|
||||
<xsl:param name="body.font.family">simsun</xsl:param>
|
||||
<xsl:param name="monospace.font.family">simsun</xsl:param>
|
||||
<xsl:param name="title.font.family">simhei</xsl:param>
|
||||
<xsl:param name="saxon.character.representation" select="native"/>
|
||||
<xsl:param name="callout.unicode" select="1"/>
|
||||
<xsl:param name="callout.unicode.start.character" select="10102"/>
|
||||
<xsl:param name="l10n.gentext.default.language" select="zh-cn"/>
|
||||
</xsl:stylesheet>
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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>
|
|
@ -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>
|