多对一 关联映射 --- many-to-one

场景:用户和组;从用户角度来,多个用户属于一个组(多对一 关联)

使用hibernate开发的思路:先建立对象模型(领域模型),把实体抽取出来。

目前两个实体:用户和组两个实体,多个用户属于一个组,那么一个用户都会对应于一个组,所以用户实体中应该有一个持有组的引用。

对象模型图:


关联映射的本质:

将关联关系映射到数据库,所谓的关联关系是对象模型在内存中一个或多个引用。

User实体类:
  1. public class User {
  2. private int id;
  3. private String name;
  4. private Group group;
  5. get...set...
  6. }

Group实体类:

  1. public class Group {
  2. private int id;
  3. private String name;
  4. GET...SET
  5. }

实体类建立完后,开始创建映射文件,先建立简单的映射文件:

Group实体类的映射文件:

  1. <hibernate-mapping>
  2. <class name="h.one.bean.Group" table="t_group">
  3. <id name="id" column="id">
  4. <generator class="native"/>
  5. </id>
  6. <property name="name"/>
  7. </class>
  8. </hibernate-mapping>

User实体类的映射文件:

  1. <hibernate-mapping>
  2. <class name="h.one.bean.User" table="t_user">
  3. <id name="id" column="id">
  4. <generator class="native"/>
  5. </id>
  6. <property name="name"/>
  7. <!--<many-to-one> 关联映射 多对一的关系
  8. name:是维护的属性(User.group),这样表示在多的一端表里加入一个字段名称为group,
  9. 但group与SQL中的关键字重复,所以需要重新命名字段(column="groupid").
  10. 这样这个字段(groupid)会作为外键参照数据库中group表(t_group也叫一的一端),也就是就在多的一
  11. 端加入一个外键指向一的一端。
  12. -->
  13. <many-to-one name="group" column="groupid"/>
  14. </class>
  15. </hibernate-mapping>

※<many-to-one>标签※:

例如:<many-to-one name="group" column="groupid"/>

<many-to-one> 关联映射 多对一的关系

name:是维护的属性(User.group),这样表示在多的一端表里加入一个字段名称为group,但group与SQL中的关键字重复,所以需要重新命名字段(column="groupid").这样这个字段(groupid)会作为外键参照数据库中group表(t_group也叫一的一端),也就是就在多的一端加入一个外键指向一的一端。

多对一 存储(先存储group(对象持久化状态后,再保存user)):

  1. session = HibernateUtils.getSession();
  2. tx = session.beginTransaction();
  3.  
  4. Group group = new Group();
  5. group.setName("wjt276");
  6. session.save(group); //存储Group对象。
  7.  
  8. User user1 = new User();
  9. user1.setName("菜10");
  10. user1.setGroup(group);//设置用户所属的组
  11.  
  12. User user2 = new User();
  13. user2.setName("容祖儿");
  14. user2.setGroup(group);//设置用户所属的组
  15.  
  16. //开始存储
  17. session.save(user1);//存储用户
  18. session.save(user2);
  19.  
  20. tx.commit();//提交事务

注意:如果上面的session.save(group)不执行,则存储不存储不成功。则抛出TransientObjectException异常。

因为Group为Transient状,Object的id没有分配值。

结果:persistent状态的对象是不能引用Transient状态的对象

以上代码操作,必须首先保存group对象,再保存user对象。我们可以利用cascade(级联)方式,不需要先保存group对象。而是直接保存user对象,这样就可以在存储user之前先把group存储了。

利用cascade属性是解决TransientObjectException异常的一种手段。

重要属性-cascade(级联):

级联的意思是指定两个对象之间的操作联运关系,对一个 对象执行了操作之后,对其指定的级联对象也需要执行相同的操作,取值:all、none、save_update、delete

1、 all:代码在所有的情况下都执行级联操作

2、 none:在所有情况下都不执行级联操作

3、 save-update:在保存和更新的时候执行级联操作

4、 delete:在删除的时候执行级联操作。

例如:<many-to-one name="group" column="groupid" cascade="save-update"/>

多对一  加载数据

代码如下:

  1. session = HibernateUtils.getSession();
  2. tx = session.beginTransaction();
  3. User user = (User)session.load(User.class, 3);
  4. System.out.println("user.name=" + user.getName());
  5. System.out.println("user.group.name=" + user.getGroup().getName());
  6. //提交事务
  7. tx.commit();

可以加载Group信息:因为采用了<many-to-one>这个标签,这个标签会在多的一端(User)加一个外键,指向一的一端(Group),也就是它维护了从多到一的这种关系,多指向一的关系。当你加载多一端的数据时,它就能把一的这一端数据加载上来。当加载User对象后hibernate会根据User对象中的groupid再来加载Group信息给User对象中的group属性。

一对一  主键关联映射_单向(one-to-one)

² 两个对象之间是一对一的关系,如Person-IdCard(人—身份证号)

² 有两种策略可以实现一对一的关联映射

Ø 主键关联:即让两个对象具有相同的主键值,以表明它们之间的一一对应的关系;数据库表不会有额外的字段来维护它们之间的关系,仅通过表的主键来关联。

Ø 唯一外键关联:外键关联,本来是用于多对一的配置,但是如果加上唯一的限制之后,也可以用来表示一对一关联关系。

实例场景:人—-> 身份证号(PersonàIdCard),从IdCard看不到Person对象

对象模型(主键关联映射-单向):

(站在人的角度看) 

IdCard实体类:

  1. public class IdCard {
  2. private int id;
  3. private String cardNo;
  4. }

Person实体类:

  1. public class Person {
  2. private int id;
  3. private String name;
  4. private IdCard idCard;//持有IdCard对象的引用
  5. }

因为是person引用idcard,所以idcard要求先有值。而person的主键值不是自己生成的。而是参考idcard的值,person即是主键,同时也是外键。

IdCard实体类的映射文件:

  1. <hibernate-mapping>
  2. <class name="h.one.bean.IdCard" table="t_idcard">
  3. <id name="id" column="id">
  4. <generator class="native"/>
  5. </id>
  6. <property name="cardNo"/>
  7. </class>
  8. </hibernate-mapping>

Persion实体类的映射文件:

  1. <hibernate-mapping package="h.one.bean">
  2. <class name="Person" table="t_person">
  3. <id name="id" column="id">
  4. <!-- 因为主键不是自己生成的,而是作为一个外键(来源于其它值),所以使用foreign生成策略 foreign:使用另外一个相关联的对象的标识符,通常和<one-to-one>联合起来使用。
  5. 再使用元素<param>的属性值指定相关联对象(这里Person相关联的对象为idCard,则标识符为idCard的id)为了能够在加载person数据同时加载IdCard数据,所以需要使用一个标签<one-to-one>来设置这个功能。 -->
  6. <generator class="foreign">
  7. <!-- 元素<param>属性name的值是固定为property -->
  8. <param name="property">idCard</param>
  9. </generator>
  10. </id>
  11. <property name="name" />
  12. <!-- <one-to-one>标签 表示如何加载它的引用对象(这里引用对象就指idCard这里的name值是idCard),同时也说是一对一的关系。
  13. 默认方式是根据主键加载(把person中的主键取出再到IdCard中来取相关IdCard数据。) 我们也说过此主键也作为一个外键引用 了IdCard,所以需要加一个数据库限制(外键约束)constrained="true" -->
  14. <one-to-one name="idCard" constrained="true" />
  15. </class>
  16. </hibernate-mapping>

※<one-to-one>标签※

现在是使用一对一主键关联映射,因为主键不是自己生成的,而是作为一个外键(来源于其它值),所以使用foreign生成策略(使用另外一个相关联的对象的标识符,通常和<one-to-one>联合起来使用)。再使用元素<param>的属性值指定相关联对象(这里Person相关联的对象为idCard,则标识符为idCard的id)为了能够在加载person数据同时加载IdCard数据,所以需要使用一个标签<one-to-one>来设置这个功能。

一对一 主键关联映射 存储测试

  1. @Test
  2. public void testOneToOneSave() {
  3. session = HibernateUtil.getSession();
  4. tx = session.beginTransaction();
  5.  
  6. IdCard idCard = new IdCard();
  7. idCard.setCardNo("88888888888888888888888");
  8.  
  9. Person person = new Person();
  10. person.setName("菜10");
  11. person.setIdCard(idCard);
  12.  
  13. //不会出现TransientObjectException异常
  14. //因为一对一主键关键映射中,默认了cascade属性。
  15. session.save(person);
  16. tx.commit();
  17. }

注:不会出现TransientObjectException异常,因为一对一主键关键映射中,默认了cascade属性。

一对一 主键关联映射 加载测试

  1. @Test
  2. public void testLoad() {
  3. session = HibernateUtil.getSession();
  4. tx = session.beginTransaction();
  5. User user = (User) session.load(User.class,
  6. "4028718146f232890146f2328b660001");
  7. System.out.println("user.name=" + user.getName());
  8. System.out.println("user.group.name=" + user.getGroup().getName());
  9. // 提交事务
  10. tx.commit();
  11. }

一对一  主键关联映射_双向(one-to-one)

² 两个对象之间是一对一的关系,如Person-IdCard(人—身份证号)

² 有两种策略可以实现一对一的关联映射

Ø 主键关联:即让两个对象具有相同的主键值,以表明它们之间的一一对应的关系;数据库表不会有额外的字段来维护它们之间的关系,仅通过表的主键来关联。

Ø 唯一外键关联:外键关联,本来是用于多对一的配置,但是如果加上唯一的限制之后,也可以用来表示一对一关联关系。

实例场景:人<—-> 身份证号(Person<->IdCard)双向:互相持有对方的引用

对象模型(主键关联映射-双向):

IdCard实体类:

  1. public class IdCard {
  2. private int id;
  3. private String cardNo;
  4. private Person person; //持有Person对象的引用
  5. }

Person实体类:

  1. public class Person {
  2. private int id;
  3. private String name;
  4. private IdCard idCard;//持有IdCard对象的引用
  5. }

IdCard实体类映射文件:

  1. <hibernate-mapping>
  2. <class name="h.one.bean.IdCard" table="t_idcard">
  3. <id name="id" column="id">
  4. <generator class="native"/>
  5. </id>
  6. <property name="cardNo"/>
  7. <!—
  8. one-to-one标签的含义:指示hibernate怎么加载它的关联对象(这里的关联对象为person),默认根据主键加载
  9. -->
  10. <one-to-one name="person"/>
  11. </class>
  12. </hibernate-mapping>

Person实体类映射文件不变:

注意:此双向的SQL语句,与单向的SQL语句没有任何变化,也就是说数据库中的表单向双向没有任何区别。<one-to-one>的单向、双向对数据库表没有影响,只是告诉Hibernate如何加载数据对象。

需要在idcard映射文件中加入<one-to-one>标签指向hibernate,指示hibernate如何加载person(默认根据主键加载。)

session_flush

在hibernate中也存在flush这个功能,在默认的情况下session.commit()之前时,其实执行了一个flush命令。

Session.flush功能:

①清理缓存;

②执行sql(确定是执行SQL语句(确定生成update、insert、delete语句等),然后执行SQL语句。)

Session在什么情况下执行flush:

①  默认在事务提交时执行;

② 可以显示的调用flush;

③ 在执行查询前,如:iterate.

注:如果主键生成策略是uuid等不是由数据库生成的,则session.save()时并不会发出SQL语句,只有flush时才会发出SQL语句,但如果主键生成策略是native由数据库生成的,则session.save的同时就发出SQL语句。在flush时会满不在缓存。实体对象只有发出SQL语句保存在数据库中时,session缓存中的一个existsInDatabase才会为true.

uuid主键生成策略:

  1. // 提交事务
  2. // 利用Hibernate将实体类对象保存到数据库中
  3. // 因为user主键生成策略采用的是uuid,所以调用完成save后,只是将user纳入session的管理
  4. // 不会发出insert语句,但是id已经生成,session中的existsInDatabase状态为false
  5. session.save(user);
  6.  
  7. // 调用flush,hibernate会清理缓存,执行sql
  8. // 如果数据库的隔离级别设置为未提交读,那么我们可以看到flush过的数据,并且session中的exitsInDatabase为true
  9. session.flush();
  10.  
  11. // 提交事务
  12. // 默认情况下commit操作会先执行flush清理缓存,所以不用显示调用flush
  13. // commit后数据是无法回滚的。
  14. tx.commit();

native主键生成策略:

  1. // 利用Hibernate将实体类对象保存到数据库中
  2. // 因为user的主键生成策略为native(自动添加),所以调用session.save()后,将执行insert语句,返回由数据库生成的id
  3. // 纳入了session的管理,修改了session中existsInDatabase状态为true。
  4. // 如果数据库的隔离级别设置为未提交读,那么我们可以看到save过的数据
  5. session.save(user);
  6.  
  7. // 提交事务
  8. tx.commit();

数据库的隔离级别:并发性作用。

1、 Read Uncommited(未提交读):没有提交就可以读取到数据(发出了Insert,但没有commit就可以读取到。)很少用

2、 Read Commited(提交读):只有提交后才可以读,常用,

3、 Repeatable Read(可重复读):mysql默认级别, 必需提交才能见到,读取数据时数据被锁住。

4、 Serialiazble(序列化读):最高隔离级别,串型的,你操作完了,我才可以操作,并发性特别不好,

数据库的隔离级别:并发性作用。

1、 Read Uncommited(未提交读):没有提交就可以读取到数据(发出了Insert,但没有commit就可以读取到。)很少用

2、 Read Commited(提交读):只有提交后才可以读,常用,

3、 Repeatable Read(可重复读):mysql默认级别, 必需提交才能见到,读取数据时数据被锁住。

4、 Serialiazble(序列化读):最高隔离级别,串型的,你操作完了,我才可以操作,并发性特别不好,

数据库的隔离级别:并发性作用。

1、 Read Uncommited(未提交读):没有提交就可以读取到数据(发出了Insert,但没有commit就可以读取到。)很少用

2、 Read Commited(提交读):只有提交后才可以读,常用,

3、 Repeatable Read(可重复读):mysql默认级别, 必需提交才能见到,读取数据时数据被锁住。

4、 Serialiazble(序列化读):最高隔离级别,串型的,你操作完了,我才可以操作,并发性特别不好,

隔离级别

是否存在不可重复读

是否存在不可重复读

是否存在幻读

Read Uncommitted(未提交读)

Y

Y

Y

Read Commited(提交读)

N

Y(可采用悲观锁解决)

Y

Repeatable Read(可重复读)

N

N

Y

Serialiazble(序列化读)

 

脏读:没有提交就可以读取到数据称为脏读

不可重复读:再重复读一次,数据与你上的不一样。称不可重复读。

幻读:在查询某一条件的数据,开始查询的后,别人又加入或删除些数据,再读取时与原来的数据不一样了。

Mysql查看数据库隔离级别:

方法:select @@tx_isolation;

Mysql数据库修改隔离级别:

方法:set transaction isolation level 隔离级别名称;

例如:修改为未提交读:set transaction isolation level read uncommitted;

Session.evict(user)方法:

作用:从session缓存(EntityEntries属性)中逐出该对象

但是与commit同时使用,会抛出异常

  1. session = HibernateUtil.getSession();
  2. tx = session.beginTransaction();
  3.  
  4. User1 user = new User1();
  5. user.setName("李四");
  6. user.setPassword("123");
  7. user.setCreateTime(new Date());
  8. user.setExpireTime(new Date());
  9.  
  10. // 利用Hibernate将实体类对象保存到数据库中
  11. // 因为user主键生成策略采用的是uuid,所以调用完成save后,只是将user纳入session的管理
  12. // 不会发出insert语句,但是id已经生成,session中的existsInDatabase状态为false
  13. session.save(user);
  14.  
  15. session.evict(user);// 从session缓存(EntityEntries属性)中逐出该对象
  16. // 无法成功提交,因为hibernate在清理缓存时,在session的临时集合(insertions)中取出user对象进行insert操作后需要更新entityEntries属性中的existsInDatabase为true,而我们采用evict已经将user从session中逐出了,所以找不到相关数据,无法更新,抛出异常。
  17.  
  18. tx.commit();

解决在逐出session缓存中的对象不抛出异常的方法:

在session.evict()之前进行显示的调用session.flush()方法就可以了。

  1. // 利用Hibernate将实体类对象保存到数据库中
  2. // 因为user主键生成策略采用的是uuid,所以调用完成save后,只是将user纳入session的管理
  3. // 不会发出insert语句,但是id已经生成,session中的existsInDatabase状态为false
  4. session.save(user);
  5.  
  6. // flush后hibernate会清理缓存,会将user对象保存到数据库中,将session中的insertions中的user对象清除,并且会设置session中的existsInDatabase状态为false
  7. session.flush();
  8.  
  9. session.evict(user);// 从session缓存(EntityEntries属性)中逐出该对象
  10.  
  11. // 可以成功提交,因为hibernate在清理缓存时,在Session的insertions中集合中无法找到user对象所以不会发出insert语句,也不会更新session中existsInDatabase的状态。
  12. tx.commit();

hibernate--关联映射(多对一,一对一)的更多相关文章

  1. Hibernate关联映射之_一对一

    数据库模型 一般对一对一的关系而言,会存在一个主从关系.如 人 与 身份证,就是一个一对一关系, 人 是主,身份证 是从 Person PK:id name age Id_Card PK.FK:id ...

  2. Hibernate关联映射(多对一 --- many-to-one)

    转自:https://blog.csdn.net/fengxuezhiye/article/details/7369786?utm_source=blogxgwz9 Hibernate的关联映射关系有 ...

  3. Hibernate关联映射(一对多/多对多)

    版权声明:翀版 https://blog.csdn.net/biggerchong/article/details/843401053.  Hibernate关联映射上接Hibernate持久化类:h ...

  4. (转)Hibernate关联映射——一对多(多对一)

    http://blog.csdn.net/yerenyuan_pku/article/details/70152173 Hibernate关联映射——一对多(多对一) 我们以客户(Customer)与 ...

  5. Java三大框架之——Hibernate关联映射与级联操作

    什么是Hibernate中的关联映射? 简单来说Hibernate是ORM映射的持久层框架,全称是(Object Relational Mapping),即对象关系映射. 它将数据库中的表映射成对应的 ...

  6. (转)Hibernate关联映射——对象的三种关系

    http://blog.csdn.net/yerenyuan_pku/article/details/70148618 Hibernate关联映射——对象的三种关系 Hibernate框架基于ORM设 ...

  7. Hibernate关联映射关系

    Hibernate关联映射关系 一.双向一对多关联映射关系:当类与类之间建立了关联,就可以方便的从一个对象导航到另一个或另一组与它关联的对象(一对多双向关联和多对一双向关联是完全一样的) 1.1创建实 ...

  8. 第六章 Hibernate关联映射

    第六章 hibernate关联映射一.本章知识点分为2部分:1.关联关系:单向多对一关联关系,双向一对多关联关系(含一对多关联关系),多对多关联关系2.延迟加载:类级别加载策略,一对多加载策略,多对一 ...

  9. 【学习笔记】Hibernate关联映射(Y2-1-6)

    Hibernate关联映射 关联映射就是将关联关系映射到数据库里,在对象模型中就是一个或多个引用. 1.单向多对一关联 准备数据库 部门表和员工表 其中部门表有两列 部门编号和名称 员工表有三列 员工 ...

  10. 第三章Hibernate关联映射

    第三章Hibernate关联映射 一.关联关系 类与类之间最普通的关系就是关联关系,而且关联是有方向的. 以部门和员工为列,一个部门下有多个员工,而一个员工只能属于一个部门,从员工到部门就是多对一关联 ...

随机推荐

  1. 微信消息处理JAXP-sax解析

    package cn.zhaokai.sax; import java.io.IOException; import java.io.InputStream; import java.io.Print ...

  2. Daily Scrum 11.6

    摘要:在本次meeting时,所有代码的修改工作已经接近尾声,接下来是进行的就是单元测试以及进行alpha版本的改进.本次的Task列表如下: Task列表 出席人员 Today's Task Tom ...

  3. iOS刷新第三方MJRefresh的基本使用

    iOS开发中最好用的刷新第三方框架 MJRefresh GitHub : https://github.com/CoderMJLee/MJRefresh UIRefreshControl的介绍 1,U ...

  4. 对frameset、frame、iframe的js操作

    框架编程概述一个HTML页面可以有一个或多个子框架,这些子框架以<iframe>来标记,用来显示一个独立的HTML页面.这里所讲的框架编程包括框架的自我控制以及框架之间的互相访问,例如从一 ...

  5. android开发修改相机扫描二维码框的高宽

    我用的是网上一个现成的例子,可以直接用,但是高宽不合适,现在主流都是大屏幕手机了,所以需要更改. 找到CameraManager 类,更改下面的方法 public Rect getFramingRec ...

  6. C# type - IsPrimitive

    Type t = typeof(string); if (t.IsPrimitive)//not { Console.WriteLine("string is a Primitive&quo ...

  7. HTML5 本地裁剪图片

    下面奉上我自己写的一个demo,代码写得比较少,很多细节不会处理.如果有不得当的地方恳请指教,谢谢啦 ^_^ ^_^   功能实现步奏:   一:获取文件,读取文件并生成url   二:根据容器的大小 ...

  8. 【BZOJ】【1076】【SCOI2008】奖励关

    状压DP+数学期望 蒟蒻不会啊……看题跑…… Orz了一下Hzwer,发现自己现在真是太水了,难道不看题解就一道题也不会捉了吗? 题目数据范围不大……100*(2^16)很容易就跑过去了…… DP的时 ...

  9. 【BZOJ】【1051】【HAOI2005】受欢迎的牛

    按B->A连边,tarjan缩点,然后找入度为0的连通分量,如果有1个,则ans=size[i],如果大于一个则ans=0: 当然如果按A->B连边就是找出度为0的(表示没有被它喜欢的,这 ...

  10. oracle——分析函数——排序值分析函数

    一.问题描述 查询列表时,我们有时需要对查询结果依据某个字段进行排名. 如果每条记录在排序字段上都不相同,我们可以将原查询作为一个视图,查询其rownum,便可以实现简单排序,例如: select r ...