??xml version="1.0" encoding="utf-8" standalone="yes"?>
在吸取了q大用户的徏议的基础上发布了 1.2 版本。这个版本中最重要的新Ҏ(gu)是“远E对象持久化(Remote Object Persistence)”,是?Cayenne 转变成一个独立的数据服务器,以便q程应用能访问。目前,仅仅用于 Java 客户端程序(常常用于 Swing ?SWT“富客户端”应用)Q但开发者计划将客户端用其他语言来实现。ORM-?WS 把熟(zhn)的 ORM 世界提供l远E客L(fng)。即关系的gq粒度、本地缓存、数据请求、单Ҏ(gu)调用多对象的提交、等{。也是服务器和客户端层׃n同样的抽象对象模型和相同?Cayenne 持久 API?br />
Cayenne 最早由几名E序员在 2001 q的亚特兰大构思。这个项目目的在于构Z个开源的 ORM 框架Q当时还没有q样的品)Q最初的设计灉|源于 NeXT/Apple 企业对象架构QEnterprise Objects Framework,EOFQ。在 2002 q的U天QCayenne 1.0 alpha 版成功地?NHL.com |站Q全国曲球联合?x))上运用。第一个官方稳定版?2003 q?9 月发布。从那以后,Cayenne 在全球大大小的组l(译注Q这些机构、组l的|站Q上部v?006 q春QCayenne 被接Uؓ(f) Apache 软g基金?x)孵化器目Qƈ立志成ؓ(f) Apache 目Q译注:(x)也许是指?iBATIS 一P?br />
在了解了 Cayenne 的前世今生后Q来听听用户的声韟?br />
“我们?Cayenne 已经?6 个月了,Zq个框架构徏了多?web 应用Q发现它是一U生产效率很高的工具。基?Cayenne 建模和开发是非常奇的事情。你可以创徏新表或更新已存在的表Q接着点击很少的按钮你的数据库构建好了,所有类也生成了。在q行E序快速开发时Q这一分钟的持久化变动是非常有效率的。下面我?Cayenne 1.2 ?Hibernate 3.0 做一比较QCayenne 是更有生产力的工具集Q主要是׃它的数据库生成和代码生成Ҏ(gu)上Q用h需书写和维?XML 映射文g。?Hibernate 有着更加强大的查询语a。Cayenne 的设计更加适合 web 应用E序。对?ThreadLocal ?HttpSession 的支持,事务模式以及(qing)q接池都可以直接拿来q。如果是 Hibernate 的话Q用户必自己找q些插g或者利?Spring ?Hibernate 的支持。Cayenne ?DataObjects 一直都处于q接状态,无需遭受 Hibernate 的“lazy initialization exception”之苦。Hibernate 更好的支持了 PK ?FK 设计的关联数据表。如果用户遵循设计模式,?Cayenne 中也可以直接?PK 代理。?br /> —?Malcolm Edgar
“我x好的Ҏ(gu)是无需关闭数据库连接或?x)话。如果我使用 HibernateQ我必须手工关闭?x)话Q我没用 springframeworkQ,q样太繁琐了Q而且Ҏ(gu)误操作。换?Cayenne 的话Q我无需兛_数据库连接或 JDBC 异常。?br /> —?lyo Yashnoo
“我?Cayenne 差不多一q半了,我想说的是真的太好了Q徏模工具很强大q且架构也非常成熟、高效!?br /> —?Pedro Costa
h意!引用、{贴本文应注明原作者:(x)Rosen Jiang 以及(qing)出处Q?/font>http://m.tkk7.com/rosen
S/N | ProductReleaseID |
SubProductAllowable |
... |
1 | 11 | 1 | ¡ |
2 | 601 | 0 | ¡ |
S/N | ProductID |
ProductReleaseID |
... |
1 | 10000001 | 11 | ¡ |
2 | 20000001 | 601 | ... |
此外Qformula 也能协助从基于当前记录的特定值向其它表检索数据。例如:(x)
q将由助于从 currency 表检?currency name。正如你所看到的,q样直接映射可消除许多{换编码?BR>
map-key
formula 允许 map-key 持有M可能的倹{下列范例(?3Q,我们惌 Role_roleID 成ؓ(f)对象模型?map-keyQ图 4Q?BR>
?3. 用户角色数据 schema
?4. 用户角色对象模型
在前面的数据 schema 中,User ?Role ?User_has_Role 通过 many-to-many 关系兌调用。ؓ(f)了获取某?User 所有的指派角色Q我们进行如下映:(x)
Role_RoleID 通常用于q接 many-to-many 元素的栏位倹{Hibernate 通常不允?Role_RoleID 出现?map-key ?many-to-many 的栏位属性中。但是有?formulaQRole_RoleID 也能用于 map-key?BR>
element、map-key-many-to-many 以及(qing)其他
element ?property cMQ能从Q何有效的 formula 表达式赋予评估倹{?BR>
map-key-many-to-many ?formula 的用法类g map-key。但是,map-key-many-to-many 通常用于三重关系Qmap key 是一个被自己参考的对象Q而不是被参考的属性?BR>
那么Q到底哪些情况下 formula 不支持呢Q有些数据库Q例?Oracle 7Q就不支持嵌入式 select 语句Q也是说一?select SQL 内嵌在另外一?select SQL 语句中)Q这U情?formula ׃支持评估l果。因此,你应该首先检查嵌入式 select SQL 语句是否被支持?BR>
一?Hibernate 的映拿 formula 表达式作?select SQL 选取的一部分Q请认数据?SQL 方言是否允许使用 formulaQ尽这样将降低代码M性?BR>
范畴 2Qformula 用于q接
many-to-one
另一个普遍的场景是真实世界的数据模型是所有者关pL,q意味着映射是不同于 one-to-one、one-to-many 以及(qing) many-to-many 关系的,formula 是提供所有者关pȝ理元素中的一个。图 5 展示了某公司可有多个联系人,但是其中只有一个ؓ(f)默认联系人的范例。一个公司有多个联系人是典型?one-to-many 关系。但是,Z标识默认联系人,ContactPerson 表用了 defaultFlag 参数Q? ?yes, 0?noQ?BR>
?5. 用户角色数据 schema
?6. 用户角色对象模型
Z说明对象模型Q图 6Q中默认联系人关p,我们使用如下映射文gQ?FONT color=#000080>
如上Q我们把 companyID ?defaultFlag l织到名?defaultContactPerson ?properties 元素中,做ؓ(f) Person 表的 unique key。Company cM?many-to-one 元素q接 Person 表的 defaultContactPerson properties 元素。输出的 SQL 像这P(x)
select c.id, p.id from Company c, Person p where p.companyID=c.id and p.defaultFlag=1
one-to-one
Hibernate 中,one-to-one 主要用于两张表共享同一主键的情c(din)对于外键关联,我们通常使用 many-to-one 来代ѝ但是,有了 formulaQone-to-one 可以通过外键q接表。上面的 many-to-one 范例可以通过 one-to-one 来映:(x)
many-to-many
formula 用于?many-to-many 元素为关p表和实体表q接的特D关p,管通常没有必要q样用?BR>
ȝ
文章范例展示了大部分 formula 的适用情景。当需?formula 评估值时Qformula 表达式将出现?产生?SQL 语句?select 部分。当 formula 用于q接Ӟ它出现在产生?SQL 语句?where 部分。此外,formula 表达式可用于M SQL 方言Q只要目标数据库支持。最后,formula 可协助完成从数据模型到对象模型的无代码细_度映射?BR>
h意!引用、{贴本文应注明原译者:(x)Rosen Jiang 以及(qing)出处Q?/FONT>http://m.tkk7.com/rosen
CREATE TABLE USER_PROPERTY ( |
VO如下Q?/FONT>
package org.mystuff.db; |
映射文g如下Q?BR>
<?xml version='1.0'?> <!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN" "http://www.ibatis.com/dtd/sql-map-2.dtd"> <!-- WARNING: This is an autogenerated file --> <sqlMap name="UserProperty"> <cacheModel id="userproperty-cache" type="MEMORY"> <flushInterval hours="24"/> <flushOnExecute statement="insertUserProperty"/> <flushOnExecute statement="updateUserProperty"/> <flushOnExecute statement="deleteUserProperty"/> <property name="reference-type" value="WEAK" /> </cacheModel> <resultMap class="org.mystuff.db.UserProperty" id="userproperty-result" > <result property="firstName" column="FIRST_NAME" /> <result property="birthDate" column="BIRTH_DATE" /> <result property="lastName" column="LAST_NAME" /> <result property="userId" column="USER_ID" /> </resultMap> <select id="getUserProperty" resultClass="org.mystuff.db.UserProperty" parameterClass="org.mystuff.db.UserProperty" resultMap="userproperty-result" > <![CDATA[ select * from USER_PROPERTY where USER_ID = #userId# ]]> </select> <update id="updateUserProperty" parameterClass="org.mystuff.db.UserProperty"> <![CDATA[ update USER_PROPERTY set FIRST_NAME = #firstName# , BIRTH_DATE = #birthDate# , LAST_NAME = #lastName# where USER_ID = #userId# ]]> </update> <insert id="insertUserProperty" parameterClass="org.mystuff.db.UserProperty"> <![CDATA[ insert into USER_PROPERTY(FIRST_NAME, BIRTH_DATE, LAST_NAME, USER_ID) values(#firstName#, #birthDate#, #lastName#, #userId#) ]]> </insert> <delete id="deleteUserProperty" parameterClass="org.mystuff.db.UserProperty"> <![CDATA[ delete from USER_PROPERTY where USER_ID = #userId# ]]> </delete> </sqlMap> |
Jasmine IDE iBATIS GUIQ?/FONT>http://jasmineide.sourceforge.netQ。在构徏映射文g、VO、DAO c(不是 iBATIS ?DAOQ而是一个调?sqlclient Ҏ(gu)?java c)斚wq是最好的了,通过点击几下鼠标可生成。这个工具允怽建立一个项目ƈ讄 DB q接q同所有通常用到的配|文件。(首先你需要设|自q DB q接Q确定你的驱动程序和所?jar 都在pȝc\径中Q?BR>
我想Q随着旉的推U,?x)有更多的开发者加入ؓ(f) iBATIS ȝ加瓦的行列?BR>
重要改进议题
http://wiki.apache.org/ibatis/Improved_Dynamic_SQL_Whiteboard q里正在讨论关于改进动?SQL 的白板,?iBATIS Data Mapper 在动?SQL 斚w更强大。另外,当开发某个有着大量查询和大量数据的pȝӞ通常也会(x)对这些数据的子集q行开发。“记录长旉q行的查询”功能将加快开发进度,能帮助开发者或 DBA 了解哪个查询慢或未优化Q找出潜在瓶颈。ؓ(f)了引入这U新Ҏ(gu),也许?x)修?sqlmap 配置文gQ比如默认(f)界|Q也?x)?mapped statement 中增加新属性(用来覆盖默认临界|?BR>
h意!引用、{贴本文应注明原作者:(x)Rosen Jiang 以及(qing)出处Q?/FONT>http://m.tkk7.com/rosen
来看看新需求:(x)
张三后来做生意,自己l营得很好,打算再买辆R跑运输。对于第二次买RQR辆管理系l的 PEOPLE 表原本已l记录了他的基本信息Q遂不对 PEOPLE 表操作。只?/span> AUTO_INFO ?/span> insert 一条R辆记录即可?/span>
新需求所用到的技术要炚w在?/span> iBATIS SQL MapsQ二Q?/span> 》和?/span> iBATIS SQL MapsQ三Q?/span> 》中出现q了。请看映文Ӟ(x)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap
<sqlMap>
<insert id="insertAutoInfo" parameterClass="bo.AutoInfo">
<resultMap id="get-autoInfo-result" class="bo.AutoInfo">
<resultMap id="get-people-result" class="bo.People">
<select id="getPeople" resultMap="get-people-result" parameterClass="bo.People">
<select id="getAutoInfo" resultMap="get-autoInfo-result" parameterClass="int">
PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
"
http://www.ibatis.com/dtd/sql-map-2.dtd
">
<![CDATA[
insert into auto_info (license_plate, owner_no) values (#licensePlate#, #ownerNo.id#)
]]>
</insert>
<result property="id" column="auto_id"/>
<result property="licensePlate" column="license_plate"/>
</resultMap>
<result property="id" column="owner_id"/>
<result property="name" column="name"/>
<result property="address" column="address"/>
<result property="autoInfoList" column="owner_id" select="getAutoInfo"/>
</resultMap>
<![CDATA[
select * from people
]]>
<dynamic prepend="where">
<isNotNull property="id">
<![CDATA[
owner_id=#id#
]]>
</isNotNull>
</dynamic>
</select>
<![CDATA[
select * from auto_info where owner_no=#id#
]]>
</select>
</sqlMap>
单地l合一下以前出现过的映文Ӟp满新需求。相应程序代码如下:(x)
package test;
import java.io.Reader;
import com.ibatis.sqlmap.client.*;
import bo.*;
public class AutoMag {
private Reader reader;
AutoInfo autoInfo=new AutoInfo();
import com.ibatis.common.resources.*;
private SqlMapClient sqlMap;
private String resource = "SqlMapConfig.xml";
public void insertPeople() throws Exception{
try{
reader = Resources.getResourceAsReader(resource);
sqlMap=SqlMapClientBuilder.buildSqlMapClient(reader);
sqlMap.startTransaction();
People people=new People();
people.setId(new Integer("1"));
people=(People)sqlMap.queryForObject("getPeople",people);
autoInfo.setLicensePlate("A00002");
autoInfo.setOwnerNo(people);
sqlMap.insert("insertAutoInfo",autoInfo);
sqlMap.commitTransaction();
}finally{
sqlMap.endTransaction();
}
}
}
E序代码也是单组合一下而已Q想?/span> Hibernate 又是怎么写的呢?怿?/span> JDBC l验的程序员应该更喜?/span> iBATIS SQL Maps Q?/span>
斗{星移、峰回\转, 张三在经历过生意U火之后Q接下来的一q内生意Zq连告负Q不得不把自q摊子收羃一下。这W一件事要把跑运输的车卖掉,是 那辆牌照?/span> ?/span> A00002?/span> ?/span> ?/span>
相应映射文g只需小修改Q?/span>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap
<sqlMap>
<resultMap id="get-autoInfo-result" class="bo.AutoInfo">
<resultMap id="get-people-result" class="bo.People">
<select id="getPeople" resultMap="get-people-result" parameterClass="bo.People">
<select id="getAutoInfo" resultMap="get-autoInfo-result" parameterClass="int">
PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
"
http://www.ibatis.com/dtd/sql-map-2.dtd
">
<delete id="deleteAutoInfo" parameterClass="bo.AutoInfo">
<![CDATA[
delete from auto_info where license_plate=#licensePlate# and owner_no=#ownerNo.id#
]]>
</delete>
<result property="id" column="auto_id"/>
<result property="licensePlate" column="license_plate"/>
</resultMap>
<result property="id" column="owner_id"/>
<result property="name" column="name"/>
<result property="address" column="address"/>
<result property="autoInfoList" column="owner_id" select="getAutoInfo"/>
</resultMap>
<![CDATA[
select * from people
]]>
<dynamic prepend="where">
<isNotNull property="id">
<![CDATA[
owner_id=#id#
]]>
</isNotNull>
</dynamic>
</select>
<![CDATA[
select * from auto_info where owner_no=#id#
]]>
</select>
</sqlMap>
d了一?/span> delete cd?/span> Mapped Statement 。同P也无需再解释了。相应程序代码:(x)
package test;
import java.io.Reader;
import com.ibatis.sqlmap.client.*;
import bo.*;
public class AutoMag {
private Reader reader;
AutoInfo autoInfo=new AutoInfo();
import com.ibatis.common.resources.*;
private SqlMapClient sqlMap;
private String resource = "SqlMapConfig.xml";
public void delPeople() throws Exception{
try{
reader = Resources.getResourceAsReader(resource);
sqlMap=SqlMapClientBuilder.buildSqlMapClient(reader);
sqlMap.startTransaction();
People people=new People();
people.setId(new Integer("1"));
people=(People)sqlMap.queryForObject("getPeople",people);
autoInfo.setLicensePlate("A00002");
autoInfo.setOwnerNo(people);
sqlMap.delete("deleteAutoInfo",autoInfo);
sqlMap.commitTransaction();
}finally{
sqlMap.endTransaction();
}
}
}
到这里,
iBATIS SQL Maps
之旅也就l束了。是的,很有意犹未尽的感觉,q没按照
one-to-many
方式删除张三和他所有的车辆呢?/span>
q个工作ql你来完成吧Q?/span>
我不x?/span>
Hibernate
?/span>
iBATIS SQL Maps
C孰劣Q因U比较实在无聊!从我开始写W一?/span>
Delphi
代码开始,充斥着?/span>
VC
好?q是
Delphi
好?”这L(fng)比较Q如果你未涉q?/span>
JDBC
Q我?/span>
Hibernate
?x)更适合?/span>
??br />
h意!引用、{贴本文应注明原作者:(x)Rosen Jiang 以及(qing)出处Q?/font>http://m.tkk7.com/rosen
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap
<sqlMap>
<resultMap id="get-people-result" class="bo.People">
<select id="getPeople" resultMap="get-people-result" parameterClass="bo.People">
<select id="getAutoInfo" resultMap="get-autoInfo-result" parameterClass="int">
PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
"
http://www.ibatis.com/dtd/sql-map-2.dtd
">
<resultMap id="get-autoInfo-result" class="bo.AutoInfo">
<result property="id" column="auto_id"/>
<result property="licensePlate" column="license_plate"/>
</resultMap>
<result property="id" column="owner_id"/>
<result property="name" column="name"/>
<result property="address" column="address"/>
<result property="autoInfoList" column="owner_id" select="getAutoInfo"/>
</resultMap>
<![CDATA[
select * from people where owner_id=#id#
]]>
</select>
<![CDATA[
select * from auto_info where owner_no=#id#
]]>
</select>
</sqlMap>
resultMap id="get-autoInfo-result" class="bo.AutoInfo"
resultMap
?/span>
iBATIS SQL Maps
框架中重要组件之一Q?/span>
你也许还记得
resultClass
吧?两者概念基本一致?/span>
resultMap
则是可定?/span>
Mapped Statement
q回对象的。可定制表现在:(x)比如我有一张数据表包含
10
个字D,但我?/span>
POJO
只定义了
5
个属性,q时Q只要取出我兛_的字D就行。和
Hibernate
cMQ?/span>
resultMap
?/span>
result
元素定义?/span>
POJO
属性到数据表字D늚映射?/span>
需要说明的?/span> result 元素 select 属性,该属性类g加蝲了一个子查询Qƈ自动载入Q如果这个子查询包含多条l果Q就自动把结果装载进 List cd。该 result 元素 对应 People 中的属性ؓ(f) ?/span> autoInfoList?/span> Q而由 Hibernate 生成?/span> People 却是 Set cd ?/span> autoInfoSet?/span> Q这两种cd无法直接互{Q所以我修改?/span> POJO 属性?/span>
select id="getPeople" resultMap="get-people-result" parameterClass="bo.People"
此ؓ(f) select cd Mapped Statement 。传?/span> People 实例Q返回ؓ(f)自己定制、包?/span> ?/span> autoInfoList?/span> ?/span> People 实例。传?/span> People cd参数是ؓ(f)了便于多字段匚w查询Q今天也许我只需利用 People.id 单字D匹配就能得到结果,但下ơ的新需求也许是 People.address ?/span> People.name 联合匚wQ?/span>
相应E序代码Q?/span>
package test;
import java.io.Reader;
import com.ibatis.sqlmap.client.*;
import bo.*;
public class AutoMag {
private Reader reader;
import java.util.List;
import com.ibatis.common.resources.*;
private People people=new People();
private SqlMapClient sqlMap;
private String resource = "SqlMapConfig.xml";
public People findPeople() throws Exception{
reader = Resources.getResourceAsReader(resource);
sqlMap=SqlMapClientBuilder.buildSqlMapClient(reader);
people.setId(new Integer("1"));
people=(People)sqlMap.queryForObject("getPeople",people);
return people;
}
}
sqlMap.queryForObject(String arg0, Object arg1)
此方法类?/span> Hibernate ?/span> session.load(? Q传?/span> Mapped Statement id Q再传入包含主键的对象实例。除我上面列丄 sqlMap. queryForObject(String arg0, Object arg1) 外,q有重蝲Ҏ(gu) Q?/span>
people=(People)sqlMap.queryForObject("getPeople",people); 替换?/font> sqlMap.queryForObject("getPeople", people, people); |
下面我会(x)讲到如何以集合类持有多个 People 对象实例?/span>
在R辆管理应用中Q需要把人员一一列出Q选中某个再显Cl内宏V类gq样的需求,
iBATIS SQL Maps
引入
sqlMap.queryForList(String arg0, Object arg1)
来满?/span>
q记得我们的映射文g怎么写的Q对了,传入主键值再查询Q?/span>
但是Q新需求不要Q何条Ӟ直接列出人员啊!Nq要再添加新?/span> Mapped Statement 来满I动?/span> Mapped Statement 能满_不改变映文件的前提下提供有参数和无差数查询Q?/span>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap
<sqlMap>
<resultMap id="get-people-result" class="bo.People">
<select id="getPeople" resultMap="get-people-result" parameterClass="bo.People">
<select id="getAutoInfo" resultMap="get-autoInfo-result" parameterClass="int"
PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
"
http://www.ibatis.com/dtd/sql-map-2.dtd
">
<resultMap id="get-autoInfo-result" class="bo.AutoInfo">
<result property="id" column="auto_id"/>
<result property="licensePlate" column="license_plate"/>
</resultMap>
<result property="id" column="owner_id"/>
<result property="name" column="name"/>
<result property="address" column="address"/>
<result property="autoInfoList" column="owner_id" select="getAutoInfo"/>
</resultMap>
<![CDATA[
select * from people
]]>
<dynamic prepend="where">
<isNotNull property="id">
<![CDATA[
owner_id=#id#
]]>
</isNotNull>
</dynamic>
</select>
resultClass="bo.AutoInfo">
<![CDATA[
select * from auto_info where owner_no=#id#
]]>
</select>
</sqlMap>
dynamic prepend="where"
select cd Mapped Statement 的本质ƈ无改变,只是通过 dynamic 元素定义了动?/span> where 子句?/span> dynamic 元素属?/span> prepend=”where?/span> 在一元判定元?/span> isNotNull q回 ”true?/span> 时有效?/span> isNotNull 元素属?/span> property="id" 用于判定 People.id 是否?/span> null Q假如未传入包含主键值的 People 实例Q?/span> dynamic 元素会(x)无效Q反之则亦然Q这样就辑ֈ了在不改变映文件的前提下实现动?/span> SQL 的目的。关于一元判定元素和二元判定元素的详l资料请查阅官方文档Q如能恰当利用这些判定元素,动?/span> SQL 会(x)更灵zR?/span>
相应E序代码Q?/span>
package test;
import java.io.Reader;
import com.ibatis.sqlmap.client.*;
import bo.*;
public class AutoMag {
private Reader reader;
import java.util.List;
import com.ibatis.common.resources.*;
private People people=new People();
private SqlMapClient sqlMap;
private String resource = "SqlMapConfig.xml";
public List findPeople() throws Exception{
reader = Resources.getResourceAsReader(resource);
sqlMap=SqlMapClientBuilder.buildSqlMapClient(reader);
List list=sqlMap.queryForList("getPeople",null);
return list;
}
}
传入
Mapped Statement id
Q再传入未包含主键的对象实例Q也可以q脆?/span>
null
q去。重载方?/span>
sqlMap
.queryForList(String arg0, Object arg1, int arg2, int arg3
)
用于分页
Q?/span>
arg2
?/span>
arg3
分别代表늠和每个数?/span>
好了Q用动?/span>
Mapped Statement
Q再试试
queryForObject(String arg0, Object arg1)
吧!
h意!引用、{贴本文应注明原作者:(x)Rosen Jiang 以及(qing)出处Q?/font>http://m.tkk7.com/rosen
Z么选择
iBATIS
Q?/span>
也许各位看官已在各种不同的技术资料上了解到它的优ѝ但是对于我来说Q选择它的理由只有一?/span>
——?/span>
利用原有资源
?/span>
?/span>
Hibernate
的却优秀Q但是用来整合原有系l,它却很难胜Q。例如,以前在进行数据徏模时使用了复合主键、跨多表生的几十甚至上百行的查询语句、利用原有存储过E?/span>
??
当面临这一pd问题?/span>
Hibernate
显得力不从心了Q要想?/span>
Hibernate
只能改造原有系l!
当我面(f)pȝ整合问题Ӟ整合的要求很单:(x)只需要保留原有系l查询部分)Q?/span> iBATIS q入了我的视Uѝ原有系l中?/span> SQL 语句需要小的修改外,数据表、查询结果都不需要改变!也不用像 Hibernate 那样映射Z多的配置文g?/span> POJO Q一下子清爽了很多?/span> BTW Q?/span> Hibernate q种做法没有错!只是我只需要查询功能,仅仅是取我所好而已Q避免了杀鸡用牛刀?/span>
目前Q系l整合已l结束,׃一个月旉。如果?/span>
Hibernate
Q恐怕我现在q在为怎么设计数据表、怎样?/span>
HQL
而和同事争论?/span>
开始另一?/span>
O/R Mapping
之旅
上次
O/R Mapping
之旅?/span>
BO
、数据表q可以重用,只是把数据库q移C
MySQL
?span style="COLOR: black">打开
Eclipse
Q新Z?/span>
Resin
目。把
ibatis-sqlmap-2.jar
?/span>
ibatis-common-2.jar
拯?/span>
lib
目录下,再导入项目。和
Hibernate
自动配置、自动映相比,
iBATIS
的一切都是手工完成的?/span>
?/span>
src
下徏立配|文?/span>
SqlMapConfig.xml
Q数据库链接、连接池?/span>
SqlMap
映射文g
??
q些都要靠它了。官方参考手册对怎样q行讄有很详细的描qͼ我只对要用到的地方进行粗略说明?br />
<?xml version="1.0" encoding="UTF-8" ?> <sqlMapConfig>
<settings
<transactionManager type="JDBC">
<sqlMap resource="bo/mapping/AutoMag.xml"/> |
JDBC
Q通过传统
JDBC
来管理事务?/span>
JTA Q用一?/span> JTA 全局事务Q iBATIS 的事务包括在更大的事务范围内Q跨数据?/span> Session 的)Q这个更大的事务范围可能包括了其他的数据库和事务资源。这个配|需要一?/span> UserTransaction 属性,以便?/span> JNDI 获得一?/span> UserTransaction ?/span>
EXTERNAL
Q调?/font>
iBATIS
以外的其他事务管理器来管理事务?/span>
SIMPLE
Q?/span>
SIMPLE
?/span>
iBATIS
内置?/span>
dataSource
实现Q其中实C一个简单的数据库连接池Q当无容器提?/span>
DataSource
服务时可以用该选项Q对?/span>
iBATIS
实现cMؓ(f)
com.ibatis.sqlmap.engine.datasource.SimpleDataSourceFactory
?/span>
?span style="mso-spacerun: yes"> DBCP:
Z Apache DBCP q接?/span> API 实现?/span> DataSource Q当无容器提?/span> DataSource 服务Ӟ可以使用该选项Q对?/span> iBATIS 实现cMؓ(f) com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory ?/span>
?b style="mso-bidi-font-weight: normal">JNDI
Q?/span>
J2EE
容器提供?/span>
DataSource
实现Q?/span>
DataSource
通过指定?/span>
JNDI Name
从容器中获取。对?/span>
iBATIS
实现cMؓ(f)
com.ibatis.sqlmap.engine.datasource.JndiDataSourceFactory
?/span>
注意Q?/span>
每种
dataSource
元素?/font>
property
都有不同的地方,不能光把
type
名字改了了事?/span>
<sqlMap resource="mapping/AutoMag1.xml"/>
<sqlMap url="
file:///c:/eclipse/workspace/iBATISTest/src/bo/
AutoMag2.xml "/> |
你也许已发现Q我只定义了单个映射文g。不错,?/font>
Hibernate
的一个表一个映文件不同,
iBATIS
的映文件个数可以h为控Ӟ颗粒度自己掌握?/span>
以下?span lang="EN-US"> iBATIS SQL Maps 工作程Q对于理解概念很有帮助。大意是 1、你可以?JavaBean、Map cd、原始变量(或者它们的Wrapper ClassQ、XML 数据作ؓ(f)传入对象Q?、通过配置文g载入映射文gQ?、利用框架翻译成 JDBC 来访问数据库Q?、执行结果可以是 JavaBean、Map cd、原始变量的 Wrapper Class、XML 数据?br />
h意!引用、{贴本文应注明原作者:(x)Rosen Jiang 以及(qing)出处Q?/font>http://m.tkk7.com/rosen
在R辆管理系l中Q代表着一个拥有者拥有多台R辆。以 java.util.Set cd表示?inverse 用于标识双向兌中的被动方一端。inverse=false 的一方(L方)负责l护兌关系Q?STRONG>在R辆管理系l中Q?AutoInfo 作ؓ(f)L方,应该把它设ؓ(f)“true”?/FONT>q就好比你(被动?oneQ在某个聚会(x)上散发了许多名片Q但是有可能你不清楚接收者(d?manyQ的具体背景Q这个不要紧Q接收者在必要的时候会(x)和你联系是了(dl护关系Q?/FONT> |
在R辆管理系l中QAtuoInfo 作ؓ(f)L方,应该?People 中设|?inverse =“true”?/FONT> |
AutoInfo ai=new AutoInfo(); People people=new People(); public void DoTest() { try { Configuration cfg = new Configuration().configure(); SessionFactory sessions = cfg.buildSessionFactory(); Session session = sessions.openSession(); Transaction tx = session.beginTransaction(); ai.setLicensePlate("A00001"); ai.setOwnerNo(people); people.setAddress("中国"); people.setName("张三"); people.addToAutoInfoSet(ai); session.save(people); tx.commit(); session.close(); } catch (Exception e) { System.out.println(e); } } |
把“ai.setOwnerNo(people)”注解了试试Q由?AutoInfo 没有dl护关系Q导?AUTO_INFO 表中 OWNER_NO 字段为“Null”。自?AutoInfo ?Poople ׃存在M联系了?/P>
人类的求知欲很强烈!
Z么非要用 AutoInfo 作ؓ(f)L方?People 作主控方不行Q好吧,?People.hbm.xml 删除inverse=”true”,再运行以上程序,其实也能保存Q只是多了一条SQLQ“update auto_info set OWNER_NO=? where AUTO_ID=?”,q就?AutoInfo 被动C改和 People 的联pR多执行一?SQL 意味着多了一些开销Q这是对性能不利的!
《我?O/R Mapping 之旅(??/FONT>Q有一D对张三W二ơ买车的E序和描qͼ(x)
AutoInfo ai = new AutoInfo(); People people = new People(); public void DoTest() { try { Configuration cfg = new Configuration().configure(); SessionFactory sessions = cfg.buildSessionFactory(); Session session = sessions.openSession(); Transaction tx = session.beginTransaction(); people = (People) session .find( "from People where OWNER_ID=1") .get(0); ai.setLicensePlate("A00002"); ai.setOwnerNo(people); people.getAutoInfoSet().add(ai); session.save(people); tx.commit(); session.close(); } catch (Exception e) { System.out.println(e); } } |
到这里,也许你会(x)有这L(fng)xQ“应该可以直接向 AUTO_INFO 表插入记录,不通过 People 对象中{Q像?SQL 一?Easy。?错了Q以前直接写 SQL 是可以办到的Q不q现在我们用的可?Hibernate Q一切都要以对象行事Q看?ai.setOwnerNo(people) 了吗Q传入参数是?People 对象实例Q不是简单的字段喔?/FONT> |
AutoInfo ai = new AutoInfo(); People people = new People(); public void DoTest() { try { Configuration cfg = new Configuration().configure(); SessionFactory sessions = cfg.buildSessionFactory(); Session session = sessions.openSession(); Transaction tx = session.beginTransaction(); people = (People) session .find( "from People where OWNER_ID=1") .get(0); ai.setLicensePlate("A00002"); ai.setOwnerNo(people); session.save(ai); tx.commit(); session.close(); } catch (Exception e) { System.out.println(e); } } |
《我?O/R Mapping 之旅(??/FONT>Q删?PEOPLE 表及(qing)其关联的 AUTO_INFO 表时Q程序是没有错,不过有更单的办法来删除:(x)
try { Configuration cfg = new Configuration().configure(); SessionFactory sessions = cfg.buildSessionFactory(); Session session = sessions.openSession(); Transaction tx = session.beginTransaction(); session.delete("from People where OWNER_ID=1"); tx.commit(); session.close(); } catch (Exception e) { System.out.println(e); } |
市场是无情的Q机遇和危机无处不在。张三在l历q生意红火之后,接下来的一q内生意Zq连告负Q不得不把自q摊子收羃一下。这W一件事要把跑运输的车卖掉,是
那辆牌照为?/span>
A00002”的
?br /> package com.dao; import java.util.*; import net.sf.hibernate.*; import bo.*; public class Test { AutoInfo ai;
import net.sf.hibernate.cfg.*;
People people;
public void DoTest() {
try {
Configuration cfg = new Configuration().configure();
SessionFactory sessions = cfg.buildSessionFactory();
Session session = sessions.openSession();
Transaction tx = session.beginTransaction();
ai =
(AutoInfo) session.find(
"from AutoInfo where LICENSE_PLATE='A00002'").get(
0);
people = ai.getOwnerNo();
people.getAutoInfoSet().remove(ai);
session.delete(ai);
tx.commit();
session.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
Z么要?/span> People 对象中移除某?/span> AutoInfo 对象Q?/span>
问得好!传统
JDBC
E序可以直接删除以?/span>
A00002
”ؓ(f)条g查询出的记录Q这h有问题。但如果?/span>
Hibernate
中用同样的方式直接删除,?x)引起不的ȝQ?br />net.sf.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): 2, of class: bo.AutoInfo
造成无法删除的原因是 PEOPLE ?/span> AUTO_INFO 表存在着一对多Q?/span> one-to-many Q关p,惌?/span> AUTO_INFO 删除一条记录,必ȝ people.getAutoInfoSet().remove(ai) Ҏ(gu)?/span> People U除以?/span> A00002 ”ؓ(f)条g查询出的 AutoInfo 对象Q才能真正删除该 AutoInfo 对象?/span>
从张三的p中回q头来,q次
Hibernate
之旅也即结束了。最后要体验的是删除
PEOPLE
表及(qing)其关联的
AUTO_INFO
表?br /> package com.dao; import java.util.*; import net.sf.hibernate.*; import bo.*; public class Test { People people;
import net.sf.hibernate.cfg.*;
public void DoTest() {
try {
Configuration cfg = new Configuration().configure();
SessionFactory sessions = cfg.buildSessionFactory();
Session session = sessions.openSession();
Transaction tx = session.beginTransaction();
people =
(People) session.load(People.class,new Integer(1));
session.delete(people);
tx.commit();
session.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
Hibernate 的优势又一ơ体现出来。我们只需把一对多Q?/span> one-to-many Q关pM?/span> one ”这方删除,与之相关联的所有其他记录会(x)一q删除?/span>
最后,通过q次旅程Q也把
Hibernate
的特性体验了一把。作ZU?/span>
O/R Mapping
实现Q它是很优秀的,希望我们都可以用好它?br />
h意!引用、{贴本文应注明原作者:(x)Rosen Jiang 以及(qing)出处Q?/font>http://m.tkk7.com/rosen
要让 Hibernate 跑v来,q要了解其中几个关键对象Q?/SPAN>
net.sf.hibernate.cfg.Configuration 的实例负责管?Hibernate 配置信息Q比如数据库q接、数据库 dialectQ还有最重要的映文件初始化工作?/SPAN>
E序Z得到 Session 实例Q必d要得到它的工?nbsp;net.sf.hibernate.SessionFactoryQSessionFactory 的实?/SPAN>?/SPAN> Configuration 构造?
net.sf.hibernate.Session 是一切数据库操作的基Q和 JDBC ?Connection 意义一PSession 实例?SessionFactory 获得?SPAN lang=EN-US>
net.sf.hibernate.Transaction 的实例由 Session 获得?/SPAN>Hibernate 不具备事务管理能力,Hibernate 其委托l底层的 JDBC 或?/SPAN> JTAQ以实现事务理和调度功能。在本文中?JDBC 事务理?/SPAN>
把以上的知识串v来吧Q?SPAN lang=EN-US>
Configuration cfg = new Configuration().configure();
SessionFactory sessions = cfg.buildSessionFactory();
Session session = sessions.openSession();
有位叫张三的人,他买了辆车。由于是W一ơ买车,q入车辆理pȝ后要?/SPAN>AUTO_INFO ?/SPAN> PEOPLE 表都q行 insert 操作?/SPAN> 形成的一对多Q?/SPAN>one-to-manyQ关pM存如下:(x)
package com.dao; import java.util.*; import net.sf.hibernate.*; import bo.*; public class Test { List list; 分别讄?AutoInfo?SPAN lang=EN-US>People 对象属性后Q调? E序W二q出场?/SPAN> 张三后来做生意,自己l营得很好,打算再买辆R跑运输。对于第二次买RQR辆管理系l的 PEOPLE 表原本已l记录了他的基本信息Q遂不对 PEOPLE 表操作。只?/SPAN> AUTO_INFO?/SPAN> insert 一条R辆记录即可?/SPAN> 形成的一对多Q?/SPAN>one-to-manyQ关pM存如下:(x)
package com.dao; import java.util.*; import net.sf.hibernate.*; import bo.*; public class Test { List list; session.find() Ҏ(gu)q回一?List 接口的实例,里面装着 POQ其中的 ?/SPAN>from People where OWNER_ID=1?/SPAN> 是大名鼎鼎?SPAN lang=EN-US> HQL(Hibernate Query Language) 了,是不是很?SQL 呢?PEOPLE 表和 AUTO_INFO 表存在一对多关系Q也需?People 对象来持有多?AutoInfo 对象Q以 Set 接口?/SPAN>实例装Q参?SPAN lang=EN-US>PeoplecL代码Q,再通过 people.getAutoInfoSet().add(ai) ?AUTO_INFO 表添加一条新U录。好了,执行完以后,再检查数据库吧?/SPAN> q段代码
到这里,也许你会(x)有这L(fng)xQ“应该可以直接向 AUTO_INFO 表插入记录,不通过 People 对象中{Q像?SQL 一?Easy。?错了Q以前直接写 SQL 是可以办到的Q不q现在我们用的可?Hibernate Q一切都要以对象行事Q看?ai.setOwnerNo(people) 了吗Q传入参数是?SPAN lang=EN-US> People 对象实例Q不是简单的字段喔?/SPAN>
E序W三q出场?/SPAN> 呵呵Q这?/SPAN>W三q是最单的了,W一U一对多Q?/SPAN>one-to-manyQ的查询Q?/SPAN>
package com.dao; import java.util.*; import net.sf.hibernate.*; import bo.*; public class Test { List list; 对于一对多Q?/SPAN>one-to-manyQ关p,q有W二U查询方式:(x)
package com.dao; import java.util.*; import net.sf.hibernate.*; import bo.*; public class Test { AutoInfo ai = new AutoInfo(); people = (People) session.load(People.class, new Integer(1)) 取出单个 People 对象Q其中持有以 Set 装的若q?AutoInfo 对象实例QIterator iterator = people.getAutoInfoSet().iterator() ?Set 立即转化?Iterator Q最后用 while (iterator.hasNext()) 循环取出其中的R牌号码?/SPAN> W一U一对多Q?/SPAN>one-to-manyQ的查询实际上我把它转换成了多对一Q?/SPAN>many -to- oneQ的查询。这U{换后有两个缺点:(x)一、由 HQL 转ؓ(f) SQL 输出Ӟ打印?/SPAN> SQL 条数多于直接一对多Q?/SPAN>one-to-manyQ关pL询;二、其中的 People 对象存在着大量冗余Q只需要一个实例,l果取出?List.size() 个相同的实例Q。我们知道,数据库的性能是有限的Q构造对象的代h(hun)是高昂的Q所以应量减少不必要的性能开销?/SPAN> 虽然我个Z?/SPAN>一对多Q?/SPAN>one-to-manyQ查询{换成多对一Q?/SPAN>many -to- oneQ查询,但事实上有些开发团队却乐意采用Q即使他们知道性能有略微的降低。在q没有更深入研究之前Q各位看官有何看法呢Q?BR>
E序W一q马上出场?/SPAN>
import net.sf.hibernate.cfg.*;
AutoInfo ai=new AutoInfo();
People people=new People();
public void DoTest() {
try {
Configuration cfg = new Configuration().configure();
SessionFactory sessions = cfg.buildSessionFactory();
Session session = sessions.openSession();
Transaction tx = session.beginTransaction();
people.setAddress("中国");
people.setName("张三");
people.addToAutoInfoSet(ai);
ai.setLicensePlate("A00001");
ai.setOwnerNo(people);
session.save(people);
tx.commit();
session.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
import net.sf.hibernate.cfg.*;
AutoInfo ai = new AutoInfo();
People people = new People();
public void DoTest() {
try {
Configuration cfg = new Configuration().configure();
SessionFactory sessions = cfg.buildSessionFactory();
Session session = sessions.openSession();
Transaction tx = session.beginTransaction();
people =
(People) session
.find(
"from People where OWNER_ID=1")
.get(0);
ai.setLicensePlate("A00002");
ai.setOwnerNo(people);
people.getAutoInfoSet().add(ai);
session.save(people);
tx.commit();
session.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
people = (People) session.find("from People where OWNER_ID=1").get(0);
可以?/SPAN>
people =(People) session.load(People.class,new Integer(1));
互换?/SPAN>在本例中 List 接口实例 size() ?1Q即其中只有一?POQ?session.load() 是根据持久对象和主键来返回相?POQ也只是单个。所以这两种方式q回的都是相?PO。采用哪U方式由你决定?/SPAN>
import net.sf.hibernate.cfg.*;
AutoInfo ai = new AutoInfo();
People people = new People();
public void DoTest() {
try {
Configuration cfg = new Configuration().configure();
SessionFactory sessions = cfg.buildSessionFactory();
Session session = sessions.openSession();
List list =
session.find(
"select ai from AutoInfo as ai where ai.OwnerNo.Id=1");
for (int i = 0; i < list.size(); i++) {
ai=(AutoInfo)list.get(i);
System.out.println(ai.getLicensePlate());
people=ai.getOwnerNo();
System.out.println(people.getName());
System.out.println(people.getAddress());
}
session.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
Cq底Q查查张三一共有多少辆R?/SPAN>?/SPAN>select ai from AutoInfo as ai where ai.OwnerNo.Id=1?/SPAN>Q这?SPAN lang=EN-US> HQL 要找的是 AutoInfo 对象Q而传l?SQL q么写:(x)“select p.name,p.address,ai.license_plate from people p,auto_info ai where p.owner_id=1”。由?AUTO_INFO 表是“many”表Q?PEOPLE 表是“one”表Q我的做法是以“many”表为基q回它的多个 POQ其中再持有“one”表的信息。“ai.OwnerNo.Id=1”就是说通过主键参数为?”的 PEOPLE 表记录来取出相应 AUTO_INFO 表记录?/SPAN>
import net.sf.hibernate.cfg.*;
People people = new People();
public void DoTest() {
try {
Configuration cfg = new Configuration().configure();
SessionFactory sessions = cfg.buildSessionFactory();
Session session = sessions.openSession();
people = (People) session.load(People.class, new Integer(1));
Iterator iterator = people.getAutoInfoSet().iterator();
System.out.println(people.getName());
System.out.println(people.getAddress());
while (iterator.hasNext()) {
ai = (AutoInfo) iterator.next();
System.out.println(ai.getLicensePlate());
}
session.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
h意!引用、{贴本文应注明原作者:(x)Rosen Jiang 以及(qing)出处Q?/FONT>http://m.tkk7.com/rosen
]]>
在没有正式开始旅行之前,让我们区分几个名词?/span>
POJO
Q在
Hibernate
中代表包?/span>
Seter
?/span>
Geter
q些最基本操作的值对象。?/span>
BO
Q代表包含一些业务逻辑的值对象,它的作用域很大,也就是说
BO
在充当持久类的同时可以传?/span>
UI
层?/span>
PO
Q代表持久对象,是纳?/span>
Hibernate
理框架中的Q在一定程度上可以和值对象的概念互换Q值对象经q?/span>
Hibernate
q行处理Q就变成?/span>
PO
?/span>
Hibernate
配置文gQ?/span>
hibernate.cfg.xml
?/span>
hibernate.properties
Q不q推荐?/span>
XML
格式。映文?/span>
*.hbm.xml
Q映文件的作用是将
POJO
与关pd数据库数据相l定Q作Z个桥梁。另外,为数据库中的表进行手工编写映文件可不是件好差事Q幸好开源社Z也有一同h法的人,他们开发了
hibernateSynchronizer
映射工具Q可?/span>
http://www.binamics.com/hibernatesync/eclipse2.1/
下蝲?/span>
好了Q现在去 http://www.hibernate.org 下蝲 Hibernate 的开发包Q目前的版本?/span> 2.1.6 Q。接着打开 Eclipse 2.1 Q在更新理器中安装 hibernateSynchronizer 。据?/span> Eclipse 3.0 已内|映工P不过我没有试。安装完毕后Q新Z?/span> Web 应用E序 HibernateTest Q接着个应用程序添?/span> Hibernate cd hibernate-2.1.6\hibernate-2.1\hibernate2.jar ?qing)其依赖cd、数据库q接包,强烈?/span> hibernate-2.1.6\hibernate-2.1\lib 下的所有类库全部加载,如下图:(x)
接着使用
hibernateSynchronizer
来生?/span>
hibernate.cfg.xml
文gQ新建—?/span>
>
其他—?/span>
>Hibernate Configuration File
Q我使用的是
SQL Server
数据库,各项配置参数见下图:(x)
生成出来?/span>
hibernate.cfg.xml
文gQ?br />
在项目的 src 目录下新建四个包Q分别是 bo ?/span> bo.base ?/span> bo.mapping ?/span> com.dao Q具体什么作用,到时候他们都?x)一一呈献。在开始映文件前q要做一件事Qؓ(f)应用E序 HibernateTest 配置 hibernateSynchronizer Q我更喜Ƣ自己写 DAO Q所以没有配|?/span> Data Access Objects Q其他各参数如下图Q?/span>
被映的?/span>
AutoInfo
l构如下Q?/span>
id
为其主键Q?/span>
新徏—?/span>
>
其他—?/span>
>Hibernate Mapping File
Q配|好参数再?/span>
Refresh
”后Q选择要映的表,注意千万不要?/span>
800*600
下映文Ӟ否则有些按钮不会(x)出现Q各w|参数见下图Q?/span>
l于、终于,映射文g
AutoInfo.hbm.xml
l于出来了!
<?xml version="1.0"?>
<hibernate-mapping package="bo.base">
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"
http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd
" >
<class name="AutoInfo" table="AutoInfo">
<property
column="owner_dept"
length="500"
name="OwnerDept"
not-null="false"
type="string"
/>
<property
column="license_plate"
length="50"
name="LicensePlate"
not-null="false"
type="string"
/>
<property
column="owner"
length="50"
name="Owner"
not-null="false"
type="string"
/>
<property
column="owner_adderss"
length="1000"
name="OwnerAdderss"
not-null="false"
type="string"
/>
<property
column="id"
length="18"
name="Id"
not-null="true"
type="integer"
/>
</class>
</hibernate-mapping>
需要修改一?AutoInfo.hbm.xml 文g为其定义主键Q把Q?
<property
column="id"
length="18"
name="Id"
not-null="true"
type="integer"
/>
替换为:(x)
<id name="Id" column="id" type="integer">
<generator class="native"/>
</id>
再接再厉Qؓ(f)映射文g生成 POJO。修?hibernate.cfg.xml 文g?</session-factory> 标签的上面加上刚才映的文g <mapping resource="bo/mapping/AutoInfo.hbm.xml" />。接着在“包资源理器”中点击 AutoInfo.hbm.xml 右键QHibernate Synchronizer—?gt;Synchronize Files。再看看 src 中的包:(x)
O/R Mapping 的旅E先到这里吧Q后面的路将?x)越来越坎坷Q你准备好了吗?
h意!引用、{贴本文应注明原作者:(x)Rosen Jiang 以及(qing)出处Q?/font>http://m.tkk7.com/rosen