web进修之—Hibernate 关系映射(3)
概述
Hibernate的关系映射是Hibernate使用的难点或者是重点(别担心,不考试哦~),按照不同的分类方式可以对这些映射关系做一个分类,如:
按对象对应关系分:
- 一对一
- 多对一/一对多
- 多对多
按对象对应关系的方向分:
- 单向
- 双向
按是否使用连接表分(当然了像多对多是必须使用连接表的):
- 使用连接表
- 不使用连接表
在使用Hibernate中常常不会分得这么仔细,常常是集中分类方式糅合起来使用,但是这个分类可以帮助我们理解Hibernate中的映射关系。
在进行详细的介绍之前,首先需要明确几个点:
- Hibernate纷繁复杂的映射关系只是一种面向对象的思维方式,在数据库中就是体现在外键上关联上,所以理解了数据库中的外键关联,再回过头来看映射关系就柳暗花明了;
- index在数据库中的作用:为了提高查询该列的效率;
- constrain的作用:对该列的值进行约束(废话。。。),各个取值的含义如下
MUL:该列的值可以重复
<li><font face="宋体"><font size="3">UNI:该列不能有重复的值</font> </font></li> <li><font face="宋体"><font size="3">FK:外键,一般定义的时候会有参考哪一个表的哪一个字段</font> </font></li>
代码
废话少说,是时候展现真正的代码啦
先看两个类Person,Address(你没看错,这是官方用来举例子的两个类)
- public class Address {
- private long id;
- private Set<Person> people;
- private Person person;
- // setter,getter
- }
- public class Person {
- private long id;
- private Address address;
- private Set<Address> addressList;
- // setter,getter
- }
各种映射关系的配置文件person.hbm.xml,里面包括单向关联的配置(在靠前面),包含双向关联的配置(紧随单向之后)
- <!DOCTYPE hibernate-mapping PUBLIC
- "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
- "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
- <hibernate-mapping package="org.lep.hibernate.model" >
- <class name="Person" table="person">
- <id name="id" column="person_id">
- <generator class="increment">
- </generator>
- </id>
- <!--单向关联 start-->
- <!--多对一是一对多的反向关联,一对多、多对一就构成了多对多-->
- <!--多对一,只需要在many的一方使用many-to-one进行映射即可,和其他id、property一样name表示model里面的属性名称-->
- <!--<many-to-one name="address" column="address_id" not-null="true" />-->
- <!--一对一,基于外键,把many-to-one的unique设置为true之后就是单向一对一,在数据库中的体现就是这个外键的值是唯一(key=UNI,如果没有设置unique的话key=mul)的,那也就是只能对应一个Address-->
- <!--有not-null约束的时候注意save对象的顺序,如果没有不用注意顺序hibernate会在保存了关联的对象之后,update到当前对象对应的表-->
- <!--<many-to-one name="address" column="address_id" not-null="true" unique="true" />-->
- <!--一对一,基于主键,将主键作为外键的时候,关联的对象和本对象的save顺序是有关系的,要先savereference的对象,然后在保存本对象-->
- <!-- constrained说明主键上存在一个约束,即外键,参考address-->
- <!--<id name="id" column="person_id">-->
- <!--<generator class="foreign">-->
- <!--<param name="property">address</param>-->
- <!--</generator>-->
- <!--</id>-->
- <!--<one-to-one name="address" constrained="true" />-->
- <!--一对多-->
- <set name="addressList">
- <key column="person_id" >
- </key>
- <one-to-many class="Address"></one-to-many>
- </set>
- <!--单向关联 end-->
- <!--单向关联——基于连接表 start-->
- <!--使用多对多和join的时候会产生中间表-->
- <!--一对多使用many-to-many会产生一张中间表-->
- <!--<set name="addressList" table="per_addr">-->
- <!--<key column="person_id"></key>-->
- <!--<many-to-many column="address_id" class="Address" unique="true" />-->
- <!--</set>-->
- <!--多对一-->
- <!--<join table="per_addr" optional="true">-->
- <!--<key column="peron_id"></key>-->
- <!--<many-to-one name="address" column="address_id" not-null="true"></many-to-one>-->
- <!--</join>-->
- <!--一对一-->
- <!--<join table="per_addr" optional="true">-->
- <!--<key column="person_id"></key>-->
- <!--<many-to-one name="address" column="address_id" not-null="true" unique="true" />-->
- <!--</join>-->
- <!--多对多-->
- <!--<set name="addressList" table="per_addr">-->
- <!--<key column="person_id"></key>-->
- <!--<many-to-many column="address_id" class="Address"></many-to-many>-->
- <!--</set>-->
- <!--单向关联——基于连接表 end-->
- <!--双向关联 start-->
- <!--多对一/一对多-->
- <!--<many-to-one name="address" column="address_id" not-null="true" />-->
- <!--一对一,基于外键关联-->
- <!--<many-to-one name="address" column="address_id" unique="true" not-null="true" />-->
- <!--一对一,基于主键关联-->
- <!--<one-to-one name="address" />-->
- <!--双向关联 end-->
- <!--双向关联,使用连接表 start-->
- <!--多对一/一对多-->
- <!--<set name="addressList" table="per_addr">-->
- <!--<key column="person_id"></key>-->
- <!--<many-to-many class="Address" column="address_id" unique="true" />-->
- <!--</set>-->
- <!--一对一,必须指定column-->
- <!--<join table="per_addr" optional="true">-->
- <!--<key column="person_id" unique="true"></key>-->
- <!--<many-to-one name="address" column="address_id" class="Address" unique="true" />-->
- <!--</join>-->
- <!--多对多-->
- <!--<set name="addressList" table="per_addr">-->
- <!--<key column="person_id"></key>-->
- <!--<many-to-many column="address_id" class="Address"></many-to-many>-->
- <!--</set>-->
- <!--双向关联,使用连接表 end-->
- </class>
- </hibernate-mapping>
address.hbm.xml(只有双向关联的配置)
- <!DOCTYPE hibernate-mapping PUBLIC
- "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
- "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
- <hibernate-mapping package="org.lep.hibernate.model" >
- <class name="Address" table="address">
- <id name="id" column="address_id">
- <generator class="increment"/>
- </id>
- <!--双向关联 start-->
- <!--多对一/一对多-->
- <!--<set name="people" inverse="true">-->
- <!--<key column="address_id"></key>-->
- <!--<one-to-many class="Person" />-->
- <!--</set>-->
- <!--一对一,基于外键关联-->
- <!--<one-to-one name="person" constrained="true"/>-->
- <!--一对一,基于主键关联-->
- <!--<id name="id" column="person_id">-->
- <!--<generator class="foreign">-->
- <!--<param name="property">person</param>-->
- <!--</generator>-->
- <!--</id>-->
- <!--<one-to-one name="person" constrained="true"/>-->
- <!--双向关联 end-->
- <!--双向关联,使用连接表 start-->
- <!--多对一/一对多-->
- <!--<join table="per_addr" inverse="true" optional="true">-->
- <!--<key column="address_id"></key>-->
- <!--<many-to-one name="person" column="person_id" not-null="true"></many-to-one>-->
- <!--</join>-->
- <!--一对一,必须指定column-->
- <!--<join table="per_addr" inverse="true" optional="true">-->
- <!--<key column="address_id" unique="true"></key>-->
- <!--<many-to-one name="person" column="person_id" unique="true" />-->
- <!--</join>-->
- <!--多对多-->
- <!--<set name="people" inverse="true" table="per_addr">-->
- <!--<key column="address_id"></key>-->
- <!--<many-to-many column="person_id" class="Person"></many-to-many>-->
- <!--</set>-->
- <!--双向关联,使用连接表 end-->
- </class>
- </hibernate-mapping>
试验过这些配置之后,我们可以总结出:
- 一对一可以通过主键关联,也可以使用外键关联
- 通过使用unique属性可以将多(many)的关系变为一(one)的关系,比如一对一,可以使用many-to-one unique=”true”
- 在使用many-to-many或者使用join的时候才会产生连接表(中间表),可以根据实际情况来决定是否采用产生中间表的配置(比如查询的性能,业务逻辑的需要)
- 如果是双向关系,在一边配置之后,另一边使用inverse=”true”来告诉Hibernate有谁来控制
- 推荐外键设置not null,这样设置后就要注意保存对象的顺序了,比如person对address单向关联(Person有一个属性Address),那么person表里面就会有一个address_id的外键,如果这个外键设为not null,那么save的时候应该先save address,再save person
- Person p = new Person();
- Address addr = new Address();
- p.setAddress(addr);
- // 正确
- session.beginTransaction();
- session.save(address);
- session.save(p);
- session.getTransaction().commit();
- // 运行会报错,因为外键address设为了not null,在save p的时候,address_id还没有值,所以不满足非空的约束
- session.beginTransaction();
- session.save(p);
- session.save(address);
- session.getTransaction().commit();
因为外键address设为了not null,在save p的时候,address_id还没有值,所以不满足非空的约束
如果说大家对Hibernate理解比较熟了,只是想看看具体配置文件怎么样写,那么到上面就可以了,如果还想探究一下Hibernate和数据库的对应关系那请继续,先看一个文件(HIbernate在建立上面的映射关系的时候产生的数据库语句)
- 单向
- 多对一
- Hibernate: create table address (address_id bigint not null, primary key (address_id))
- Hibernate: create table person (person_id bigint not null, address_id bigint not null, primary key (person_id))
- Hibernate: alter table person add index FK_o8tnkglv9n1eeqmo7de7em37n (address_id), add constraint FK_o8tnkglv9n1eeqmo7de7em37n foreign key (address_id) references address (address_id)
- 一对一
- 基于外键
- Hibernate: create table address (address_id bigint not null, primary key (address_id))
- Hibernate: create table person (person_id bigint not null, address_id bigint not null, primary key (person_id))
- Hibernate: alter table person add constraint UK_o8tnkglv9n1eeqmo7de7em37n unique (address_id)
- Hibernate: alter table person add index FK_o8tnkglv9n1eeqmo7de7em37n (address_id), add constraint FK_o8tnkglv9n1eeqmo7de7em37n foreign key (address_id) references address (address_id)
- 基于主键
- Hibernate: create table address (address_id bigint not null, primary key (address_id))
- Hibernate: create table person (person_id bigint not null, primary key (person_id))
- Hibernate: alter table person add index FK_acrq16tm1ioc620qk2nm5gwyg (person_id), add constraint FK_acrq16tm1ioc620qk2nm5gwyg foreign key (person_id) references address (address_id)
- 一对多
- Hibernate: create table address (address_id bigint not null, person_id bigint, primary key (address_id))
- Hibernate: create table person (person_id bigint not null, primary key (person_id))
- Hibernate: alter table address add index FK_5k57pkctki2o1wpmk2880r74j (person_id), add constraint FK_5k57pkctki2o1wpmk2880r74j foreign key (person_id) references person (person_id)
- 单向——中间表
- 一对多
- Hibernate: create table address (address_id bigint not null, primary key (address_id))
- Hibernate: create table per_addr (person_id bigint not null, address_id bigint not null, primary key (person_id, address_id))
- Hibernate: create table person (person_id bigint not null, primary key (person_id))
- Hibernate: alter table per_addr add constraint UK_8v3twe5k7nlb8wcjqvcpydab6 unique (address_id)
- Hibernate: alter table per_addr add index FK_8v3twe5k7nlb8wcjqvcpydab6 (address_id), add constraint FK_8v3twe5k7nlb8wcjqvcpydab6 foreign key (address_id) references address (address_id)
- Hibernate: alter table per_addr add index FK_hdwakolgq6oelbfuallvfbcn4 (person_id), add constraint FK_hdwakolgq6oelbfuallvfbcn4 foreign key (person_id) references person (person_id)
- 多对一
- Hibernate: create table address (address_id bigint not null, primary key (address_id))
- Hibernate: create table per_addr (peron_id bigint not null, address_id bigint not null, primary key (peron_id))
- Hibernate: create table person (person_id bigint not null, primary key (person_id))
- Hibernate: alter table per_addr add index FK_7xqe3kidwvogwcohihqla5ehv (peron_id), add constraint FK_7xqe3kidwvogwcohihqla5ehv foreign key (peron_id) references person (person_id)
- Hibernate: alter table per_addr add index FK_8v3twe5k7nlb8wcjqvcpydab6 (address_id), add constraint FK_8v3twe5k7nlb8wcjqvcpydab6 foreign key (address_id) references address (address_id)
- 一对一
- Hibernate: create table address (address_id bigint not null, primary key (address_id))
- Hibernate: create table per_addr (person_id bigint not null, address_id bigint not null, primary key (person_id))
- Hibernate: create table person (person_id bigint not null, primary key (person_id))
- Hibernate: alter table per_addr add constraint UK_8v3twe5k7nlb8wcjqvcpydab6 unique (address_id)
- Hibernate: alter table per_addr add index FK_hdwakolgq6oelbfuallvfbcn4 (person_id), add constraint FK_hdwakolgq6oelbfuallvfbcn4 foreign key (person_id) references person (person_id)
- Hibernate: alter table per_addr add index FK_8v3twe5k7nlb8wcjqvcpydab6 (address_id), add constraint FK_8v3twe5k7nlb8wcjqvcpydab6 foreign key (address_id) references address (address_id)
- 多对多
- Hibernate: create table address (address_id bigint not null, primary key (address_id))
- Hibernate: create table per_addr (person_id bigint not null, address_id bigint not null, primary key (person_id, address_id))
- Hibernate: create table person (person_id bigint not null, primary key (person_id))
- Hibernate: alter table per_addr add index FK_8v3twe5k7nlb8wcjqvcpydab6 (address_id), add constraint FK_8v3twe5k7nlb8wcjqvcpydab6 foreign key (address_id) references address (address_id)
- Hibernate: alter table per_addr add index FK_hdwakolgq6oelbfuallvfbcn4 (person_id), add constraint FK_hdwakolgq6oelbfuallvfbcn4 foreign key (person_id) references person (person_id)
- 双向
- 多对一/一对多
- Hibernate: create table address (address_id bigint not null, primary key (address_id))
- Hibernate: create table person (person_id bigint not null, address_id bigint not null, primary key (person_id))
- Hibernate: alter table person add index FK_o8tnkglv9n1eeqmo7de7em37n (address_id), add constraint FK_o8tnkglv9n1eeqmo7de7em37n foreign key (address_id) references address (address_id)
- 一对一
- 基于外键
- Hibernate: create table address (address_id bigint not null, primary key (address_id))
- Hibernate: create table person (person_id bigint not null, address_id bigint not null, primary key (person_id))
- Hibernate: alter table person add constraint UK_o8tnkglv9n1eeqmo7de7em37n unique (address_id)
- Hibernate: alter table address add index FK_scpdoha0q1mmbp5f9lojr3s9x (address_id), add constraint FK_scpdoha0q1mmbp5f9lojr3s9x foreign key (address_id) references person (person_id)
- Hibernate: alter table person add index FK_o8tnkglv9n1eeqmo7de7em37n (address_id), add constraint FK_o8tnkglv9n1eeqmo7de7em37n foreign key (address_id) references address (address_id)
- 基于主键
- Hibernate: create table address (person_id bigint not null, primary key (person_id))
- Hibernate: create table person (person_id bigint not null, primary key (person_id))
- Hibernate: alter table address add index FK_5k57pkctki2o1wpmk2880r74j (person_id), add constraint FK_5k57pkctki2o1wpmk2880r74j foreign key (person_id) references person (person_id)
- 双向——中间表
- 多对一/一对多
- Hibernate: create table address (address_id bigint not null, primary key (address_id))
- Hibernate: create table per_addr (address_id bigint not null, person_id bigint not null, primary key (person_id, address_id))
- Hibernate: create table person (person_id bigint not null, primary key (person_id))
- Hibernate: alter table per_addr add index FK_8v3twe5k7nlb8wcjqvcpydab6 (address_id), add constraint FK_8v3twe5k7nlb8wcjqvcpydab6 foreign key (address_id) references address (address_id)
- Hibernate: alter table per_addr add index FK_hdwakolgq6oelbfuallvfbcn4 (person_id), add constraint FK_hdwakolgq6oelbfuallvfbcn4 foreign key (person_id) references person (person_id)
- 一对一
- Hibernate: create table address (address_id bigint not null, primary key (address_id))
- Hibernate: create table per_addr (person_id bigint not null, address_id bigint, primary key (address_id))
- Hibernate: create table person (person_id bigint not null, primary key (person_id))
- Hibernate: alter table per_addr add constraint UK_hdwakolgq6oelbfuallvfbcn4 unique (person_id)
- Hibernate: alter table per_addr add index FK_hdwakolgq6oelbfuallvfbcn4 (person_id), add constraint FK_hdwakolgq6oelbfuallvfbcn4 foreign key (person_id) references person (person_id)
- Hibernate: alter table per_addr add index FK_8v3twe5k7nlb8wcjqvcpydab6 (address_id), add constraint FK_8v3twe5k7nlb8wcjqvcpydab6 foreign key (address_id) references address (address_id)
- 多对多
- Hibernate: create table address (address_id bigint not null, primary key (address_id))
- Hibernate: create table per_addr (person_id bigint not null, address_id bigint not null, primary key (person_id, address_id))
- Hibernate: create table person (person_id bigint not null, primary key (person_id))
- Hibernate: alter table per_addr add index FK_8v3twe5k7nlb8wcjqvcpydab6 (address_id), add constraint FK_8v3twe5k7nlb8wcjqvcpydab6 foreign key (address_id) references address (address_id)
- Hibernate: alter table per_addr add index FK_hdwakolgq6oelbfuallvfbcn4 (person_id), add constraint FK_hdwakolgq6oelbfuallvfbcn4 foreign key (person_id) references person (person_id)
通过上面的SQL语句,我们可以得出以下几个结论
- 单向、双向是在对象层面上去考虑的,其实在数据库层面上是一致的,比如单向多对一和双向多对一在表结构上一样的
- 为了提高效率一般在(中间表)外键上都会创建index
Hibernate中的关系映射是最常用、也是最复杂的。在之前的点滴学习中对Hibernate的映射关系也是一知半解,这次特地花时间认真整理学习了一下,特别是对Hibernate和数据库中表的对应关系进行了认真的探究。