Hibernate的一对多和多对一操作真的很方便,如果系统采用Hibernate作为持久层,完全可以把对应的一对多和多对一逻辑关系放在Hibernate里面控制,减少数据库的负担,而且也更清晰。
1、多对一和一对多概念
其实这个概念上来说很简单,比如一个客户可以有多个订单,多个订单属于同一个客户。就是最基本的一对多,和多对一。数据库使用中,感觉多对一和一对多算是比较常见的逻辑关系了。
我曾经做过一些数据库,比如某些政府部门的,其表单很设计的很简单粗糙,甚至连主键都没有,完全靠在事务层补全这些关系。其实通过Hibernate持久层来实现逻辑关系也是很不错的方法。下面的例子,就是数据库逻辑上基本没有定义,主要放在持久层里面。这个也主要是我对数据库操作属于半通水的原因。
2、数据库层
这里面有两个表单,一个CUSTOMER,客户表单,一个是ORDERS,订单表单。生成客户表单,这个是在SQLServer里面做的,其实其他都一样,因为逻辑关系在Hibernate上面,id是主键非空,其他可以为空:
CREATETABLE[dbo].[CUSTOMER](
[id][numeric](18,0)NOTNULL,
[name][varchar](50)NULL,
[age][int]NULL,
CONSTRAINT[PK_CUSTOMER]PRIMARYKEY)
- 1.
- 2.
- 3.
- 4.
- 5.
订单表单
id为主键非空,CUSTOMER_id是对应客户主键,也非空,这里不做外键设置。
CREATETABLE[dbo].[ORDERS](
[id][numeric](18,0)NULLPRIMARYKEY,
[CUSTOMER_id][numeric](18,0)NOTNULL,
[ORDER_NUMBER][varchar](50)NULL,
[PRICE][numeric](18,3)NULL
)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
3、Hibernate设定
HIbernate里面,一对多的对象体现,是客户有一个集合set,set里面放着对应订单,而多对一体现,是订单里面有一个CUSTOMER对象,表明该订单所属的客户。其中,CUSTOMER类为:
publicclassCustomerimplementsjava.io.Serializable{
privateLongid;
privateStringname;
privateIntegerage;
privateSetrderses=newHashSet();
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
后面的getXXX和setXXX方法就省去了,同样订单类就是:
publicclassOrdersimplementsjava.io.Serializable{
privateLongid;
privateCustomercustomer;
privateStringorderNumber;
privateDoubleprice;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
而对应hbm文档,就是map文档如下:
CUSTOMER.hbm.xml
<!DOCTYPEhibernate-mappingPUBLIC"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
MappingfileautogeneratedbyMyEclipsePersistenceTools
-->
<hibernate-mapping>
<classnameclassname="onetomany.Customer"table="CUSTOMER"schema="dbo"catalog="DBTEST">
<idnameidname="id"type="java.lang.Long">
<columnnamecolumnname="id"precision="18"scale="0"/>
<generatorclassgeneratorclass="increment"/>
</id>
<propertynamepropertyname="name"type="java.lang.String">
<columnnamecolumnname="name"length="50"/>
</property>
<propertynamepropertyname="age"type="java.lang.Integer">
<columnnamecolumnname="age"/>
</property>
<setnamesetname="orderses"inverse="true"lazy="true"cascade="all">
<key>
<columnnamecolumnname="CUSTOMER_id"precision="18"scale="0"not-null="true"/>
</key>
<one-to-manyclassone-to-manyclass="onetomany.Orders"/>
</set>
</class>
</hibernate-mapping>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
#p#
这个里面,其他都很简答了,其中<generatorclass="increment"/>表示主键值自动增加,这个主要针对字符串对应的,主要体现多对以的是:
<setnamesetname="orderses"inverse="true"lazy="true"cascade="all">
<key>
<columnnamecolumnname="CUSTOMER_id"precision="18"scale="0"not-null="true"/>
</key>
<one-to-manyclassone-to-manyclass="onetomany.Orders"/>
</set>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
其中,set表示,对应集合;fetch和lazy主要是用来级联查询的,而cascade和inverse主要是用来级联插入和修改的,这几个主要包括对集合的控制。<one-to-manyclass="onetomany.Orders"/>表示对应类,即set里面包含的类,而key主要是用于确定set里面对应表单列。
ORDERS的hbm
<?xmlversionxmlversion="1.0"encoding="utf-8"?>
<!DOCTYPEhibernate-mappingPUBLIC"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
MappingfileautogeneratedbyMyEclipsePersistenceTools
-->
<hibernate-mapping>
<classcatalogclasscatalog="DBTEST"name="onetomany.Orders"schema="dbo"table="ORDERS">
<idnameidname="id"type="java.lang.Long">
<columnnamecolumnname="id"precision="18"scale="0"/>
<generatorclassgeneratorclass="increment"/>
</id>
<many-to-oneclassmany-to-oneclass="onetomany.Customer"fetch="select"name="customer">
<columnnamecolumnname="CUSTOMER_id"precision="18"scale="0"/>
</many-to-one>
<propertygeneratedpropertygenerated="never"lazy="false"name="orderNumber"type="java.lang.String">
<columnlengthcolumnlength="50"name="ORDER_NUMBER"/>
</property>
<propertygeneratedpropertygenerated="never"lazy="false"name="price"type="java.lang.Double">
<columnnamecolumnname="PRICE"precision="18"scale="3"/>
</property>
</class>
</hibernate-mapping>
<many-to-oneclassmany-to-oneclass="onetomany.Customer"fetch="select"name="customer">
<columnnamecolumnname="CUSTOMER_id"precision="18"scale="0"/>
</many-to-one>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
表示CUSTOMER熟悉对应的类,和其作为key的列名,上面这些都可以在MyEclipse里面自动生成。另外注意的一点是,在生成的DAO里面,涉及表单操作的save()和delete()方法,必须要事件提交,数据库才有反映。可以就该Hibernate.xml,或者用下面这样代码来实现:
Sessionse=getSession();
Transactiontx=se.beginTransaction();
se.delete(persistentInstance);
//se.save(instance);
tx.commit();
- 1.
- 2.
- 3.
- 4.
- 5.
4、验证效果
1、新增用户
如果新增一个用户,该用户里面包含有两个表单,那么,由于持久层已经实现了逻辑关系,只要用户类里面的set包含了表单,则表单可以自动增加。实现代码:
CustomerDAOcd=newCustomerDAO();
Customerxd=newCustomer("王小虎",20,null);
Ordersord1=newOrders();
ord1.setCustomer(xd);
ord1.setOrderNumber("王小虎的买单1");
Ordersord2=newOrders();
ord2.setCustomer(xd);
ord2.setOrderNumber("王小虎的买单2");
Setrderses=newHashSet();
orderses.add(ord1);
orderses.add(ord2);
xd.setOrderses(orderses);
cd.save(xd);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
代码里面,加入一个王小虎用户。两个订单,通过setOrderses加入,只使用cd.save这一个对持久层操作。完成后查询:
王小虎
=================================
王小虎的买单1
王小虎的买单2
- 1.
- 2.
- 3.
- 4.
显示,CUSTOMER里面加入了王小虎,ORDERS里面也加入他的订单。
2、删除操作
List<Customer>csList=cd.findByProperty("name","王小虎");
for(Customercs:csList){
cd.delete(cs);
}
- 1.
- 2.
- 3.
- 4.
这个很简单了,通过其中findByProperty("name","王小虎");对应SQL为deletefromtableCUSTOMERwherename=''王小虎';删除了王小虎,而ORDERS里面,王小虎对应的表单也同时被删除。
5、小小总结
Hibernate的多对一和一对多处理,还是挺方便的,如果在减少数据库复杂度的原则来说,把一些逻辑处理放在持久层是一个常见的方法。
【编辑推荐】