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

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

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

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

对象模型图:


关联映射的本质:

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

User实体类:
public class User {
private int id;
private String name;
private Group group;
get...set...
}

Group实体类:

public class Group {
private int id;
private String name;
GET...SET
}

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

Group实体类的映射文件:

<hibernate-mapping>
<class name="h.one.bean.Group" table="t_group">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>

User实体类的映射文件:

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

            session = HibernateUtils.getSession();
tx = session.beginTransaction(); Group group = new Group();
group.setName("wjt276");
session.save(group); //存储Group对象。 User user1 = new User();
user1.setName("菜10");
user1.setGroup(group);//设置用户所属的组 User user2 = new User();
user2.setName("容祖儿");
user2.setGroup(group);//设置用户所属的组 //开始存储
session.save(user1);//存储用户
session.save(user2); 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"/>

多对一  加载数据

代码如下:

            session = HibernateUtils.getSession();
tx = session.beginTransaction();
User user = (User)session.load(User.class, 3);
System.out.println("user.name=" + user.getName());
System.out.println("user.group.name=" + user.getGroup().getName());
//提交事务
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实体类:

public class IdCard {
private int id;
private String cardNo;
}

Person实体类:

public class Person {
private int id;
private String name;
private IdCard idCard;//持有IdCard对象的引用
}

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

IdCard实体类的映射文件:

<hibernate-mapping>
<class name="h.one.bean.IdCard" table="t_idcard">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="cardNo"/>
</class>
</hibernate-mapping>

Persion实体类的映射文件:

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

※<one-to-one>标签※

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

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

    @Test
public void testOneToOneSave() {
session = HibernateUtil.getSession();
tx = session.beginTransaction(); IdCard idCard = new IdCard();
idCard.setCardNo("88888888888888888888888"); Person person = new Person();
person.setName("菜10");
person.setIdCard(idCard); //不会出现TransientObjectException异常
//因为一对一主键关键映射中,默认了cascade属性。
session.save(person);
tx.commit();
}

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

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

    @Test
public void testLoad() {
session = HibernateUtil.getSession();
tx = session.beginTransaction();
User user = (User) session.load(User.class,
"4028718146f232890146f2328b660001");
System.out.println("user.name=" + user.getName());
System.out.println("user.group.name=" + user.getGroup().getName());
// 提交事务
tx.commit();
}

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

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

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

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

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

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

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

IdCard实体类:

public class IdCard {
private int id;
private String cardNo;
private Person person; //持有Person对象的引用
}

Person实体类:

public class Person {
private int id;
private String name;
private IdCard idCard;//持有IdCard对象的引用
}

IdCard实体类映射文件:

<hibernate-mapping>
<class name="h.one.bean.IdCard" table="t_idcard">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="cardNo"/>
<!—
one-to-one标签的含义:指示hibernate怎么加载它的关联对象(这里的关联对象为person),默认根据主键加载
-->
<one-to-one name="person"/>
</class>
</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主键生成策略:

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

native主键生成策略:

       // 利用Hibernate将实体类对象保存到数据库中
// 因为user的主键生成策略为native(自动添加),所以调用session.save()后,将执行insert语句,返回由数据库生成的id
// 纳入了session的管理,修改了session中existsInDatabase状态为true。
// 如果数据库的隔离级别设置为未提交读,那么我们可以看到save过的数据
session.save(user); // 提交事务
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同时使用,会抛出异常

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

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

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

       // 利用Hibernate将实体类对象保存到数据库中
// 因为user主键生成策略采用的是uuid,所以调用完成save后,只是将user纳入session的管理
// 不会发出insert语句,但是id已经生成,session中的existsInDatabase状态为false
session.save(user); // flush后hibernate会清理缓存,会将user对象保存到数据库中,将session中的insertions中的user对象清除,并且会设置session中的existsInDatabase状态为false
session.flush(); session.evict(user);// 从session缓存(EntityEntries属性)中逐出该对象 // 可以成功提交,因为hibernate在清理缓存时,在Session的insertions中集合中无法找到user对象所以不会发出insert语句,也不会更新session中existsInDatabase的状态。
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. Oracle体系结构知识点的运用

    体系结构方面的优化问题: 设数据库很大,访问量非常高,共享池很小:这样共享池里面就无法存储很多解析过得sql语句,导致很多硬解析,这样数据库就非常缓慢.这个时候要加大共享池.如果是自动管理,就加大SG ...

  2. Javascript Array.prototype.some()

    当我们使用数组时,查找数组中包含某个特殊的项是非常常见的动作.下面例子是一个简单的实现: 01 planets = [ 02     "mercury", 03     " ...

  3. 【js】js 让图片旋转

     转http://www.cnblogs.com/ustcyc/p/3760116.html 核心: canvas.style.filter = "progid:DXImageTransfo ...

  4. 【android】uiselectoer 自动化测试

    转:http://blog.csdn.net/sasoritattoo/article/details/17579739 Android自动化测试主要分为Monkeyrunner.Rubotium.U ...

  5. 【nodejs】 npm 注意事项

    官网:https://www.npmjs.com/ 1.安装时要切换到nodejs根目录,  否则就会安装到安装时所在的目录 2.要有管理员权限(win),如需指定版本,如npm install ex ...

  6. Nginx开启gzip压缩功能

    在Nginx安装完成之后,我们可以开启Gzip压缩功能,这里Nginx默认只能对text/html类型的文件进行压缩.下面的指令为开启Gzip的指令: gzip on; gzip_http_versi ...

  7. 通过注册表检测UAC是否处于关闭状态(不弹窗)

    注册表路径: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System UAC各级别对应的注册表值: 从 ...

  8. 一个简单的PHP登录演示(SESSION版 与 COOKIE版)

    //==============COOKIE版本的简单登录================ if ($_GET[out]){ setcookie('id',''); setcookie('pw','' ...

  9. How to Enable 64-bit Processes for Enhanced Protected Mode in Internet Explorer 11 (IE11)

       Information Enhanced Protected Mode (EPM) adds additional security to Protected Mode and includes ...

  10. Thread的第三天学习

    线程互斥 public void method1() {  synchronized(this) {  ... } } 等同于 public synchronized void method1() { ...