数据库逻辑关系一共三种:
1.一对一
2.一对多
3.多对多
在hibernate中,对数据库关系的操作也是按这三种关系展开,主要是考虑数据读取和维护。
不过今天的目标不在这。
目标:
1,一对多的关联配置(数据库:主表从表,通过外键关联)将数据库中表与表之间的关系给映射到实体类里面
2,懒加载
3,一对多的自关联
4,多对多的关联,数据库中不能直接映射多对多(创建一个桥接表(中间表),将一个多对多关系转换成两个一对多)
一、一对多的关联配置
上一次讲了将数据库中的单个表配置到hibernate中,需要建立实体类,和相关的配置文件,而直接被框架读取,与数据库进行交互的是配置文件。所以,数据库中表的关系描述也在配置文件中。
1.目标效果
如果是一般的不用关联的表,那就只会查出该表的数据,而配置过关联关系的,则可以查出相关联的表的对应数据
举例Order表——OrderItem表
order表字段
orderitem表字段
实体类设计不放了。
实际需求:
在查询order数据时,希望把对应的orderitem数据也查出来。
很显然,只做最简单的表映射,是做不到这点的。所以需要在order和orderitem映射文件中加入关系映射代码。
order映射文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.csf.two.entity.Order" table="t_hibernate_order">
<id name="orderId" type="java.lang.Integer" column="order_id">
<generator class="increment"></generator>
</id>
<property name="orderNo" type="java.lang.String" column="order_no"/>
<!--
cascade:级联属性配置
inverse:关联关系交给对方维护吗?
-->
<set name="orderItems" cascade="save-update" inverse="true">
<key column="oid"></key>
<one-to-many class="com.csf.two.entity.OrderItem"/>
</set>
</class>
</hibernate-mapping>
文中的set标签,便是映射关系。如果是
orderitem映射文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.csf.two.entity.OrderItem" table="t_hibernate_order_item">
<id name="orderItemId" type="java.lang.Integer" column="order_item_id">
<generator class="increment"></generator>
</id>
<property name="productId" type="java.lang.Integer" column="product_id"/>
<property name="quantity" type="java.lang.Integer" column="quantity"/>
<property name="oid" type="java.lang.Integer" column="oid"/>
<!-- Repeated column in mapping for entity: com.zking.four.entity.OrderItem column: oid (should be mapped with insert="false" update="false") -->
<many-to-one name="order" class="com.csf.two.entity.Order" insert="false" update="false" column="oid"></many-to-one>
</class>
</hibernate-mapping>
many-to-one标签,就是关系映射。
现在执行查询,
执行的就是选中的方法
其实打印了两条sql语句,一条是根据id为7,查询order,另一条是根据查出来的order查询orderitem
最后也通过order拿到了相关的订单项。
注:图中的“order.serInitOrderItems(1)”,是定义在实体类中的开启强制加载,这就涉及到了hibernate的懒加载问题。
二、懒加载
在hibernate中,如果有关联关系的表映射出现,默认查询是懒加载,也就是不会查出连带数据,而需要查出连带数据的话,需要强制加载,而强制加载的弊端就是会占用空间,会影响运行速度,所以这里采用在实体类中用一个变量控制。具体查询方法如下。
//并且想要查询出关联的订单项的数据是,采用强制加载
if(o != null && new Integer(1).equals(order.getInitOrderItems())) {
//强制加载订单o关联的订单项数据
Hibernate.initialize(o.getOrderItems());
}
如果不启用强制加载,会报错
could not initialize proxy - no Session
三、一对多的自关联
例如在读取菜单的时候,节点字段是和上下节点相关联的。
因为结果和上面的差异都不大,就只放映射文件和实体类中关于关联关系的代码部分。
实体类中:
private TreeNode parent;
private Set<TreeNode> children = new HashSet<TreeNode>();
private Integer initChildren = 0;
配置文件:
<many-to-one name="parent" class="com.csf.two.entity.TreeNode" column="parent_node_id"/>
<set name="children" cascade="save-update" inverse="true">
<key column="parent_node_id"></key>
<one-to-many class="com.csf.two.entity.TreeNode"/>
</set>
也就是将一对多和多对一全写在这里
四、多对多
例如在老师与班级的关系中,一个老师可能对应着多个班级,而班级会对应这不同的多个任课老师,而一般老师与班级只会建立两张表储存数据,而还会再建立一张中间表,以描述关系,这就构成了多对多的关联关系。
而多对多,以任何一方做参照看来,都是一对一,所以可以将一个多对多关系理解为两个一对多关系,而在hibernate中,也就是双方都有一对多的关联关系。而这里的一对多关系,是和中间表的关系。(中间表不建立实体类)
代码(以Book和Category为例)
实体类
Book
private Set<Category> categories = new HashSet<Category>();
book.hbm.xml
<!--
table :中间表
name:g关联属性
inverse:反转
key:当前表的主键在中间表的外键
many-to-many:当前表的主键在中间表查出另一表的外键
-->
<set table="t_hibernate_book_category" name="categories" cascade="save-update" inverse="false">
<!-- one -->
<key column="bid"></key>
<!-- many -->
<many-to-many column="cid" class="com.csf.two.entity.Category"></many-to-many>
</set>
Category
private Set<Book> books = new HashSet<Book>();
<set table="t_hibernate_book_category" name="books" cascade="save-update" inverse="true">
<key column="cid"></key>
<many-to-many column="bid" class="com.csf.two.entity.Book"></many-to-many>
</set>
ok,
注:在多对多的关联关系中,中间表的数据维护只需要让其中一方做就可以,不能两个同时维护或都不维护。也就是inverse属性,(为true代表维护,反之则代表不维护)