什么是类之间的关联
Hibernate在持久化对象的同时,也将对象之间的关系持久化到数据库。
类之间的关联,当一个类的对象与另一个类的一些特定对象存在固定的对应关系,这种对应的关联关系,大多数用来表示一个对象持有着其他对象的引用。
根据是否在关联关系所链接的双方都能导航到对方,将关联关系分为单向关联和双向关联。
多对一关联映射 - many-to-one
例如:一个人可以有多张卡,比如校园卡,银行卡,购物卡等。
在面向对象中,card类中每张卡都要有这个人的信息,数据库中应该在多的一方(card表)加外键
多个用户属于某个组 User-Group
从代码上体现为:
1.Group类:
package cn.hrbust.pojo;
public class Group {
int id;
String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.在User类中添加
Group group;
public Group getGroup() {
return group;
}
public void setGroup(Group group) {
this.group = group;
}
3.建立Group.hbm.xml映射关系
<hibernate-mapping>
<class name="cn.hrbust.pojo.Group" table="t_group"
select-before-update="true" dynamic-update="true">
<id name="id" column="id">
<generator class="identity" />
<!-- increment:由hibernate负责对象标识(oid)的自动生成 native/identity由database负责对象标识(oid)的自动生成 -->
</id>
<property name="name" column="groupname" length="50" />
</class>
</hibernate-mapping>
4.many-to-one元素
在“多”方的映射文件中使用元素来配置多对一关联关系。
<many-to-one
name=“propertyName” (1)
column=“column_name” (2)
class=“ClassName” (3)
cascade=“all|none|save-update|delete” (4)
property-ref=“propertyNameFromAssociatedClass” (5)
not-null=“true|false” (6)
/>
(1)name:待映射的持久化类的属性名。
(2)column:column用于指定持久化类对应的表的外键字段名。(可选)
(3) class:关联的类的名字。 (可选)
(4) cascade(级联):指明哪些操作会从父对象级联到关联的对象。 (可选)
(5) property-ref:指定关联类的一个属性,这个属性将会和本外键相对应。 如果没有指定,会使用对方关联类的主键。 (可选)
(6) not-null:使用DDL为外键字段生成一个非空约束。(可选)
由此建立User.hbm.xml映射关系
<many-to-one name="group" class="cn.hrbust.pojo.Group" column="groupid" not-null="true"></many-to-one>
5.最后配置hibernate.cfg.xml
<mapping resource="cn/hrbust/pojo/Group.hbm.xml"/>
6.根据数据生成表
public static void main(String[] args) {
// TODO Auto-generated method stub
//建一个服务注册对象
ServiceRegistry sr = new StandardServiceRegistryBuilder().configure().build();
//通过服务注册对象创建元数据
Metadata metadata = new MetadataSources(sr).buildMetadata();
//创建Schema,表的对象,目的是根据数据生成表
SchemaExport se = new SchemaExport();
se.create(EnumSet.of(TargetType.DATABASE), metadata);
}
保存瞬时用户对象和组织
//保存瞬时用户对象和组织
public void testSaveUserAndGroup() {
Configuration cfg = null;
SessionFactory sf = null;
Session session = null;
Transaction ts = null;
Group g = new Group();
g.setName("软件工程");
User u = new User();//创建user
u.setName("李四");
u.setGender("女");
u.setAge(18);
u.setBirthday(Date.valueOf("2001-1-1"));
u.setName("赵六");//u:瞬时对象,没有对象id,没有纳入session管理,他的任何变化不会自动地发送SQL语句
u.setGroup(g);
try {
sf = HibernateUtil.getSessionFactory();//使用单例模式创建Configuration对象和Session工厂
session = sf.getCurrentSession();//保证每个读写线程有唯一的session实例
ts = session.beginTransaction();//创建事务
session.save(g);
session.save(u);//持久化操作:session保存对象
//u:持久对象,有对象id,纳入session管理,他的任何变化会自动地发送SQL语句
//姓名并没有发生改变,只执行一次insert
u.setName("王五");//属性改变,持久化对象的属性变化会发送update语句
//提交事务
ts.commit();
u.setName("zhaowu");//事务提交后,session关闭,属性改变,此时的对象的属性变化不会发送update语句
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
if(ts != null) {
ts.rollback();
}
}finally {
//关闭session
//session.close();
//sf.close();
}
}
重要属性cascade
重要属性 - cascade(级联)
级联的意思是指定两个对象之间的操作联动关系,对一个主控对象执行了操作之后,对其指定的级联对象也需要执行相同的操作
总共可以取值为:all、none、save-update、delete
all-代表在所有的情况下都执行级联操作
none-在所有情况下都不执行级联操作,默认
save-update-在保存和更新的时候执行级联操作
delete-在删除的时候执行级联操作
如:
<many-to-one name="group" class="cn.hrbust.pojo.Group" column="groupid" not-null="true" cascade="save-update" ></many-to-one>
编写实际例子测试many-to-one以及cascade属性的配置
//级联(一起)保存用户对象和组织
public void testCascadeSaveUserAndGroup() {
Configuration cfg = null;
SessionFactory sf = null;
Session session = null;
Transaction ts = null;
Group g = new Group();
g.setName("软件工程");
User u = new User();//创建user
u.setName("李四");
u.setGender("女");
u.setAge(18);
u.setBirthday(Date.valueOf("2001-1-1"));
u.setName("赵六");//u:瞬时对象,没有对象id,没有纳入session管理,他的任何变化不会自动地发送SQL语句
u.setGroup(g);
try {
sf = HibernateUtil.getSessionFactory();//使用单例模式创建Configuration对象和Session工厂
session = sf.getCurrentSession();//保证每个读写线程有唯一的session实例
ts = session.beginTransaction();//创建事务
//session.save(g);
session.save(u);//持久化操作:session保存对象
//u:持久对象,有对象id,纳入session管理,他的任何变化会自动地发送SQL语句
//姓名并没有发生改变,只执行一次insert
//u.setName("王五");//属性改变,持久化对象的属性变化会发送update语句
//提交事务
ts.commit();
u.setName("zhaowu");//事务提交后,session关闭,属性改变,此时的对象的属性变化不会发送update语句
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
if(ts != null) {
ts.rollback();
}
}finally {
//关闭session
//session.close();
//sf.close();
}
}
查询所有的用户以及关联的组织
//1.查询所有的用户以及关联的组织
public void testQueryUser() {
Configuration cfg = null;
SessionFactory sf = null;
Session session = null;
Transaction ts = null;
try {
sf = HibernateUtil.getSessionFactory();//使用单例模式创建Configuration对象和Session工厂
session = sf.getCurrentSession();//保证每个读写线程有唯一的session实例
ts = session.beginTransaction();//创建事务
Query query = session.createQuery("from User");
List users = query.getResultList();
//查询方法一
for(int i=0;i<users.size();i++)
{
User u =(User)users.get(i);
System.out.println(u.toString());
//System.out.println(u.getGroup().getName());
}
//查询方法二
// for(User u: users)
// {
// System.out.println(u.toString());
// }
//查询方法三
// Iterator iterator = users.iterator();
// while(iterator.hasNext()) {
// User u=(User)iterator.next();
// System.out.println(u.toString());
// }
ts.commit();//提交事务
} catch (Exception e) {
e.printStackTrace();
if(ts != null) {
ts.rollback();
}
}finally {
}
}
一对一关联映射 (one-to-one)
两个对象之间是一对一的关系,如Person-IdCard
有两种策略可以实现一对一的关联映射
主键关联:即让两个对象具有相同的主键值,以表明它们之间的一一对应的关系;数据库表不会有额外的字段来维护它们之间的关系,仅通过表的主键来关联,将一个表的主键同时作为外键参照另一个表的主键而实现的。
唯一外键关联:外键关联,本来是用于多对一的配置,但是如果加上唯一(unique)的限制之后,也可以用来表示一对一关联关系;
基于主键关联的单向一对一
在定义基于主键的“单向一对一”关联时,可将标记定义在任意一方,同时还要定义标记的这一方所对应的持久化类中添加导航到另一方的声明(引用),并且这一方的持久化标识符必须使用外键标识符生成策略。
标记中应该定义constrained=“true”,以约束一个表的主键同时作为外键参照另一个表的主键。
1.IdCard类
package cn.hrbust.pojo;
public class IdCard {
private int id;
private String info;
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
2.配置IdCard.hbm.xml
<class name="cn.hrbust.pojo.IdCard" table="t_Card"
select-before-update="true" dynamic-update="true">
<id name="id" column="id">
<generator class="foreign">
<param name="property"> user </param>
</generator>
<!-- class foreign:约束T_Card一个表的主键同时作为外键参照另一个表T_User的主键 -->
</id>
<property name="info" length="100" />
<one-to-one name="user" constrained="true"></one-to-one>
</class>
3.配置hibernate.cfg.xml
<mapping resource="cn/hrbust/pojo/IdCard.hbm.xml"/>
4.根据数据生成表
public static void main(String[] args) {
// TODO Auto-generated method stub
//建一个服务注册对象
ServiceRegistry sr = new StandardServiceRegistryBuilder().configure().build();
//通过服务注册对象创建元数据
Metadata metadata = new MetadataSources(sr).buildMetadata();
//创建Schema,表的对象,目的是根据数据生成表
SchemaExport se = new SchemaExport();
se.create(EnumSet.of(TargetType.DATABASE), metadata);
}
5.测试
//保存瞬时用户对象和身份证
public void testSaveUserAndIdCard() {
Configuration cfg = null;
SessionFactory sf = null;
Session session = null;
Transaction ts = null;
IdCard idcard = new IdCard();
idcard.setInfo("王五的身份证123456");
User u = new User();//创建user
u.setName("李四");
u.setGender("女");
u.setAge(18);
u.setBirthday(Date.valueOf("2001-1-1"));
idcard.setUser(u);
try {
sf = HibernateUtil.getSessionFactory();//使用单例模式创建Configuration对象和Session工厂
session = sf.getCurrentSession();//保证每个读写线程有唯一的session实例
ts = session.beginTransaction();//创建事务
session.save(idcard);//持久化操作:session保存对象
ts.commit();
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
if(ts != null) {
ts.rollback();
}
}finally {
//关闭session
//session.close();
//sf.close();
}
}
6.查询
public void testQueryUser() {
Configuration cfg = null;
SessionFactory sf = null;
Session session = null;
Transaction ts = null;
try {
sf = HibernateUtil.getSessionFactory();//使用单例模式创建Configuration对象和Session工厂
session = sf.getCurrentSession();//保证每个读写线程有唯一的session实例
ts = session.beginTransaction();//创建事务
Query query = session.createQuery("from User");
List users = query.getResultList();
for(int i=0;i<users.size();i++)
{
User u =(User)users.get(i);
System.out.println(u.toString());
//System.out.println(u.getGroup().getName());
}
ts.commit();//提交事务
} catch (Exception e) {
e.printStackTrace();
if(ts != null) {
ts.rollback();
}
}finally {
}
}
一对一关联映射会自动级联
基于主键关联的双向一对一
基于主键关联的双向一对一关联与基于主键关联的单向一对一关联类似,区别在于在没有标记的一方增加了标记,从而建立了双向的一对一关联。
在定义主键关联的双向一对一关联时,只需在一方的标记中定义constrained=“true”,以约束一个表的主键同时作为外键参照另一个表的主键。
1.User类
private IdCard card;
public IdCard getCard() {
return card;
}
public void setCard(IdCard card) {
this.card = card;
}
2.User.hbm.xml
<one-to-one name="card"></one-to-one>
3.保存瞬时用户对象和身份证
public void testSaveUserAndIdCard() {
Configuration cfg = null;
SessionFactory sf = null;
Session session = null;
Transaction ts = null;
IdCard idcard = new IdCard();
idcard.setInfo("aa的身份证12340987");
User u = new User();//创建user
u.setName("小米");
u.setGender("女");
u.setAge(18);
u.setBirthday(Date.valueOf("2001-1-1"));
idcard.setUser(u);
u.setCard(idcard);
try {
sf = HibernateUtil.getSessionFactory();//使用单例模式创建Configuration对象和Session工厂
session = sf.getCurrentSession();//保证每个读写线程有唯一的session实例
ts = session.beginTransaction();//创建事务
session.save(idcard);//持久化操作:session保存对象
ts.commit();
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
if(ts != null) {
ts.rollback();
}
}finally {
//关闭session
//session.close();
//sf.close();
}
}
4.查询
public void testQueryUser() {
Configuration cfg = null;
SessionFactory sf = null;
Session session = null;
Transaction ts = null;
try {
sf = HibernateUtil.getSessionFactory();//使用单例模式创建Configuration对象和Session工厂
session = sf.getCurrentSession();//保证每个读写线程有唯一的session实例
ts = session.beginTransaction();//创建事务
Query query = session.createQuery("from User");
List users = query.getResultList();
//查询方法一
for(int i=0;i<users.size();i++)
{
User u =(User)users.get(i);
System.out.println(u.toString());
//System.out.println(u.getGroup().getName());
}
//查询方法二
// for(User u: users)
// {
// System.out.println(u.toString());
// }
//查询方法三
// Iterator iterator = users.iterator();
// while(iterator.hasNext()) {
// User u=(User)iterator.next();
// System.out.println(u.toString());
// }
ts.commit();//提交事务
} catch (Exception e) {
e.printStackTrace();
if(ts != null) {
ts.rollback();
}
}finally {
}
}
基于外键关联的单向一对一
基于外键关联的单向一对一其实是单向多对一关联的一个特例,只需要在映射文件的标记中增加一个unique属性即可,并且将unique属性值设置为true。(外键唯一性约束)
在定义基于外键的“单向一对一”关联时,可将标记定义在任意一方,同时要在定义标记的这一方所对应的持久类中添加导航到另一方的声明。
基于外键关联的双向一对一
在定义基于外键关联的“双向一对一”关联时,需要在关联双方分别添加一个对方类型的属性,用于实现双向导航。
Hibernate通过标记和标记结合使用来定义这种关联。
这种关联的映射文件与基于外键关联的单向一对一关联基本一致,区别在于在已有的的基础上,在另一方增加标记进行关联。
注意:在使用标记的一方,必须指定property-ref属性,否则变成了使用主键关联。
一对多关联映射 (one-to-many)
在对象模型中,一对多的关联关系,使用集合来表示
比如Classes(班级)和Student(学生)之间是一对多的关系
一对多关联映射文件
1.Group类
private Set user = new HashSet();
public Set getUser() {
return user;
}
public void setUser(Set user) {
user = user;
}
2.group.hbm.xml
<set name = "users" cascade="all" inverse = "true">
<key column = "groupid"/><!-- 多方对应表的外键 -->
<one-to-many class = "cn.hrbust.pojo.User"/><!-- 多方对应的类 -->
</set>
3.测试
public void testSaveGroupAndUser() {
Configuration cfg = null;
SessionFactory sf = null;
Session session = null;
Transaction ts = null;
Group g = new Group();
g.setName("软件工程");
User u1 = new User();//创建user
u1.setName("李1");
u1.setGender("女");
u1.setAge(1);
u1.setBirthday(Date.valueOf("2001-1-1"));
u1.setGroup(g);
User u = new User();//创建user
u.setName("李2");
u.setGender("女");
u.setAge(8);
u.setBirthday(Date.valueOf("2001-1-1"));
u.setGroup(g);
g.getUsers().add(u);
g.getUsers().add(u1);
try {
sf = HibernateUtil.getSessionFactory();//使用单例模式创建Configuration对象和Session工厂
session = sf.getCurrentSession();//保证每个读写线程有唯一的session实例
ts = session.beginTransaction();//创建事务
session.save(g);
session.save(u);//持久化操作:session保存对象
session.save(u1);
//u:持久对象,有对象id,纳入session管理,他的任何变化会自动地发送SQL语句
//姓名并没有发生改变,只执行一次insert
u.setName("王五");//属性改变,持久化对象的属性变化会发送update语句
//提交事务
ts.commit();
u.setName("zhaowu");//事务提交后,session关闭,属性改变,此时的对象的属性变化不会发送update语句
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
if(ts != null) {
ts.rollback();
}
}finally {
//关闭session
//session.close();
//sf.close();
}
}
关于inverse属性
inverse – 标记由哪一方来维护关联关系(双向关联中会用到)
inverse默认值为false
如果inverse设置为true,表示将由对方维护两者之间的关联关系
举例说明
<set name="students“ lazy=“false” inverse=“true”>
多对多关联映射 (many-to-many)
一般的设计中,多对多关联映射,需要一个中间表
Hibernate会自动生成中间表
Hibernate使用many-to-many标签来表示多对多的关联
多对多的关联映射,在实体类中,跟一对多一样,也是用集合来表示的
1.User.java
Set<Group> groups = new HashSet();
public Set<Group> getGroups() {
return groups;
}
public void setGroups(Set<Group> groups) {
this.groups = groups;
}
2.配置group.hbm.xml映射文件
<set name = "users" cascade="save-update" table="T_USER_GROUP">
<key column = "groupid"/><!-- 中间表的外键参考T_GROUP的主键 -->
<many-to-many class = "cn.hrbust.pojo.User" column="userid"/><!-- 中间表的外键参考T_USER的主键 -->
</set>
3.配置user.hbm.xml映射文件
<set name="groups" cascade="save-update" table="T_USER_GROUP">
<key column = "userid"/><!-- 中间表的外键参考T_USER的主键 -->
<many-to-many class = "cn.hrbust.pojo.Group" column="groupid"/><!-- 中间表的外键参考T_GROUP的主键 -->
</set>
4.重新生成表
5.测试
public void testSaveGroupAndUser() {
Configuration cfg = null;
SessionFactory sf = null;
Session session = null;
Transaction ts = null;
Group g1 = new Group();
g1.setName("软件工程");
Group g2 = new Group();
g2.setName("管理专业");
User u = new User();//创建user
u.setName("李2");
u.setGender("女");
u.setAge(8);
u.setBirthday(Date.valueOf("2001-1-1"));
u.getGroups().add(g1);
u.getGroups().add(g2);
User u1 = new User();//创建user
u1.setName("李1");
u1.setGender("女");
u1.setAge(1);
u1.setBirthday(Date.valueOf("2001-1-1"));
u1.getGroups().add(g1);
Set users = new HashSet();
users.add(u);
users.add(u1);
g1.setUsers(users);
g2.getUsers().add(u);
try {
sf = HibernateUtil.getSessionFactory();//使用单例模式创建Configuration对象和Session工厂
session = sf.getCurrentSession();//保证每个读写线程有唯一的session实例
ts = session.beginTransaction();//创建事务
session.save(u);//持久化操作:session保存对象
session.save(u1);
ts.commit();
u.setName("zhaowu");//事务提交后,session关闭,属性改变,此时的对象的属性变化不会发送update语句
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
if(ts != null) {
ts.rollback();
}
}finally {
//关闭session
//session.close();
//sf.close();
}
}
6.查询
public void testQueryUsersOfGroup() {
Configuration cfg = null;
SessionFactory sf = null;
Session session = null;
Transaction ts = null;
try {
sf = HibernateUtil.getSessionFactory();//使用单例模式创建Configuration对象和Session工厂
session = sf.getCurrentSession();//保证每个读写线程有唯一的session实例
ts = session.beginTransaction();//创建事务
Query query = session.createQuery("from Group g where g.name = '软件工程'");
List<Group> groups=query.getResultList();
//默认是延迟加载组织关联的用户
for(Group g : groups) {
System.out.println(g.getName()+"包括的用户:");
Set<User> users=g.getUser();
for(User u : users) {
System.out.println(u.getName());
}
}
//提交事务
ts.commit();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
if(ts != null) {
ts.rollback();
}
}finally {
//关闭session
//session.close();
//sf.close();
}
}
总结
使用Hibernate,通过简单的配置即可获得对象间关联的能力
常用的关联有one-to-many关联和many-to-one关联。many-to-many在特定场合下也会用到。
根据业务需要确定关联的配置
根据业务需要配置单向或双向关联
根据业务需要配置inverse属性和cascade属性