一对一
现实生活中一对一情况:人和自己的身份证
一对一的实现有两种情况基于外键和基于主键
基于外键
创建人和身份证的模型,身份证模型中书写属性对应的get,set方法、无参的构造函数、和以cardNumber为参数的构造函数。
package com.hibernate.entity;
public class IdCard implements java.io.Serializable{
private static final long serialVersionUID = 1L;
private String id;
private String cardNumber;
}
人的模型,包含一个身份证类型的引用,并添加属性以及引用的set、get方法。
package com.hibernate.entity;
public class Person implements java.io.Serializable{
private static final long serialVersionUID = 1L;
private String id;
private String perName;
private IdCard idCard;
}
映射文件,身份证的映射文件只是普通属性的配置,人的配置文件需要添加多对一的标签,并在其中添加一个属性unique,让外键不重复。
<?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="com.hibernate.entity.Person">
<id name="id">
<generator class="uuid" />
</id>
<property name="perName" />
<many-to-one name="idCard" class="com.hibernate.entity.IdCard"
column="fk_p_i" unique="true" lazy="false" cascade="all"/>
</class>
</hibernate-mapping>
通过测试类完成表的创建,通过查看数据库,可以发现它与多对一生成的表是一样的,人表中生成了外键。
测试添加功能
创建一个人的对象,设置该对象的姓名和身份证,并将该对象保存到数据库中。
public static void saveOneToOne(){
Person person=new Person();
person.setPerName("张三2");
person.setIdCard(new IdCard("1234567892"));
BaseDao.saveOrUpdate(person);
}
通过主函数两次调用该方法(第二次调用是更改身份证号和姓名),查看其结果可以发现,外键的内容都来自身份证表,不重复且与身份证相对应。
基于主键
与基于外键的不同之处在于映射文件中:引用的配置用one–to-one标签,id的主键生成器变成foreign并在里面配置一个参数告诉主键是从哪里来的。
<id name="id">
<generator class="foreign">
<!-- name属性的值固定-->
<param name="property">idCard</param>
</generator>
</id>
<one-to-one name="idCard" class="com.hibernate.entity.IdCard" constrained="true" cascade="all" lazy="false" />
注意参数配置一定要在主键生成标签内不然会报错,比如配置文件采取如下方法书写。
<id name="id">
<generator class="foreign"/>
<param name="property">idCard</param>
</id>
在创建表的时候有如下报错:Exception in thread “main” org.hibernate.InvalidMappingException: Could not parse mapping document from resource com/hibernate/entity/Person.hbm.xml。
更改为正确的配置方法后,创建表可以看到基于外键创建的表有些许不同,人的表中没有新生成一列最为外键,而是主键列有变化,他既是自身的主键,又是身份证表的外键。
测试添加
测试添加的方式不变,测试结果如下,从中可以看出人表中的id均来自身份证表。
多对多
现实生活中的情况:一个演员可以演多个角色,一个角色可以由多个演员来演。多对多其实是数据库的复合主键。一般会将多对多通过转换用多对一实现。
新建两个模型角色和演员,模型中包含一些基本属性、对方的集合、成员的set和get方法、无参的构造函数、以string类型为参数的构造函数,下面分别列出两个模型的属性。
package com.hibernate.entity;
import java.util.HashSet;
import java.util.Set;
public class Actor implements java.io.Serializable{
private Integer id;
private String actorName;
private Set<Roler>setRoler=new HashSet<Roler>();
}
package com.hibernate.entity;
import java.util.HashSet;
import java.util.Set;
public class Roler implements java.io.Serializable{
private Integer id;
private String rolerName;
private Set<Actor>actorSet=new HashSet<Actor>();
}
映射文件的配置
主要用的是many-to-many标签,会生成一个中间表,(中间表存的就是两个表的外键两表之间并没有直接关系,都是通过中间表来关联的,中间表其实就是一个复合主键表,即做中间表的外键又做本身的主键)。
角色模型的配置
先是一个set标签配置集合,其中的name属性:模型中的变量名称;table属性:中间表名;key标签中间表的主键;many-to-many标签配置关系本表的外键;column属性都是列名。添加lazy和cascade属性防止出错。
<?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="com.hibernate.entity.Roler">
<id name="id">
<generator class="native" />
</id>
<property name="rolerName" />
<set name="actorSet" table="tb_actor_roler" lazy="false" cascade="all">
<key column="fk_r_a" />
<many-to-many class="com.hibernate.entity.Actor" column="fk_a_r" lazy="false" />
</set>
</class>
</hibernate-mapping>
配置演员模型
观察两个模型的配置文件需要注意其中的一些细节中要中间表的表名一致,主键和外键的列名对调,这样才可以保证关系的正确对应。
<class name="com.hibernate.entity.Actor">
<id name="id">
<generator class="native" />
</id>
<property name="actorName" length="50" />
<set name="setRoler" table="tb_actor_roler" lazy="false" cascade="all">
<key column="fk_a_r" />
<many-to-many class="com.hibernate.entity.Roler" column="fk_r_a"
lazy="false" />
</set>
</class>
将映射文件配到链接文件中,检查是否书写正确的方法:按住ctrl键点resource属性的值如果可以跳到对应的文件,说明正确。
测试表的生成,测试结果的sql语句包括3张表、和两个外键的生成,生成的表格如下。
测试添加功能
添加演员
创建演员角色,设置演员名称,得到演员的角色集合,给集合装入角色,将其设置到演员中,调用数据库保存演员。
public static void saveActor(){
Actor actor=new Actor();
actor.setActorName("赵丽颖");
Set<Roler> rolers=actor.getSetRoler();
rolers.add(new Roler("楚乔"));
rolers.add(new Roler("花千骨"));
actor.setSetRoler(rolers);
BaseDao.saveOrUpdate(actor);
}
在主函数中运行该方法。可以看到演员表中添加了一条数据,角色表中添加了两条数据,中间表中添加了两条数据,且一列是演员的主键值,另一列是角色的主键值,与添加的内容一致,同一个演员的主键对应了两个角色的主键。
添加角色
方法中的内容与添加演员基本一致。
public static void saveRoler(){
Roler roler=new Roler();
roler.setRolerName("皇上");
Set<Actor> actors=roler.getActorSet();
actors.add(new Actor("秦凯"));
actors.add(new Actor("唐国强"));
roler.setActorSet(actors);
BaseDao.saveOrUpdate(roler);
}
在主函数中运行该方法。可以看到演员表中添加了两条数据,角色表中添加了一条数据,中间表中添加了两条数据,且一列是演员的主键值,另一列是角色的主键值,与添加的内容一致,同一个角色的主键对应了两个演员的主键。
查询以查询角色为例
通过dao层得到单条数据,获取角色的名称,演员集合并输出。
public static void findRoler(){
Roler roler=(Roler) BaseDao.getUser("from Roler where id=3");
System.out.println(roler.getRolerName());
Iterator<Actor> actors=roler.getActorSet().iterator();
while(actors.hasNext()){
System.out.println(actors.next().getActorName());
}
}
Hibernate的sql不由自己控制,性能太低