文档章节

hibernate系列(五)Session接口方法

乒乓狂魔
 乒乓狂魔
发布于 2015/02/07 10:24
字数 3398
阅读 90
收藏 1
Session接口方法主要有save、persist、load、get、update、saveOrUpdat、merge、delete等,这里主要是对我看hibernate书籍的一个实践加总结。

首先是save()方法:
以之前的Customer和Order为例,看下类文件:

public class Customer {

	private Long id;
	private String name;
	private String email;
	private Timestamp registeredTime;
	private Set<Order> orders;
//略get、set方法
}

public class Order {
	private Long id;
	private String orderNumber;
	private Customer customer;
//略get、set方法
}

映射文件Customer.hbm.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<!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.ligang.domain.Customer" table="customer">
		<id name="id" column="id" type="long">
			<generator class="identity"/>
		</id>
		<property name="name" column="name" type="string"/>
		<property name="email" column="email" type="string"/>
		<property name="registeredTime" column="registeredTime" type="timestamp"/>
		<set name="orders" cascade="save-update" inverse="true">
			<key column="customer_id"/>
			<one-to-many class="com.ligang.domain.Order"/>
		</set>
	</class>
</hibernate-mapping>

save方法如下所示:
@Test
	public void testSave(){
		Session session=hibernateDao.getSession();
		Transaction tx=session.beginTransaction();
		
		Customer customer=new Customer();
		customer.setName("小明");
		customer.setEmail("917312290@qq.com");
		Timestamp t=new Timestamp(System.currentTimeMillis());
		customer.setRegisteredTime(t);
		
		session.save(customer);
		System.out.println(customer.getId());
		customer.setEmail("sdfvdf@qq.com");
		
		tx.commit();
		session.close();
	}

我们会看到如下的sql语句:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
73
Hibernate: update hibernate.customer set name=?, email=?, registeredTime=? where id=?

会有一个insert语句和一个update语句。这里的save方法执行时,并没有真正的去执行一条insert语句,而是仅仅从数据库中获取下一个id,并赋值给customer,获取当时customer信息的一个快照,计划执行一条insert语句,然后在事务提交时才会去真正执行该语句,在真正执行前,如果你向数据库中插入一条记录,该记录则会使用下一个id,即customer虽然未向数据库插入,但是已经占据一个id了。执行完该insert语句后会发现当前的customer和已经持久化的customer是不一致的,然后就需要执行一次update语句。

再来看下persist方法
该方法和save()方法的作用是一样的都是将一个临时对象转变为持久化对象。但是它和save的区别下面来介绍:

@Test
	public void testSave(){
		Session session=hibernateDao.getSession();
		Transaction tx=session.beginTransaction();
		
		Customer customer=new Customer();
		customer.setName("小明");
		customer.setEmail("917312290@qq.com");
		Timestamp t=new Timestamp(System.currentTimeMillis());
		customer.setRegisteredTime(t);
		
		session.persist(customer);
		System.out.println(customer.getId());
		customer.setEmail("sdfvdf@qq.com");
		customer.setName("萨菲您稍等");
		
		tx.commit();
		session.close();
	}

上述正常情况下和save是一样的,不同之处先来看下官方文档:


这里说明了两点:
第一:persist并不保证一定会给对象的id赋值,这一赋值可能在flush时才会去执行。而save则不同,persist返回void,而save方法是返回id的,即save方法必须从数据库中取出一个可用的id。
第二:persist在事务之外是不会计划执行insert的,而save方法则会计划执行insert的,同时会从数据库中取出一个可用id。
对于第一点,什么情况下persist会为对象的id赋值,我目前还不了解,不再说明。
对于第二点,可做如下实验:

@Test
	public void testSave(){
		Session session=hibernateDao.getSession();
		
		Customer customer=new Customer();
		customer.setName("小明");
		customer.setEmail("917312290@qq.com");
		Timestamp t=new Timestamp(System.currentTimeMillis());
		customer.setRegisteredTime(t);
		
		session.persist(customer);
		System.out.println(customer.getId());
		customer.setEmail("sdfvdf@qq.com");
		customer.setName("萨菲您稍等");
		
		session.close();
	}

此时并没有开启事务,打印的信息如下:
null

没有insert语句,同时没有去获取id。
而对于save()方法:

@Test
	public void testSave(){
		Session session=hibernateDao.getSession();
		
		Customer customer=new Customer();
		customer.setName("小明");
		customer.setEmail("917312290@qq.com");
		Timestamp t=new Timestamp(System.currentTimeMillis());
		customer.setRegisteredTime(t);
		
		session.save(customer);
		System.out.println(customer.getId());
		customer.setEmail("sdfvdf@qq.com");
		customer.setName("萨菲您稍等");
		
		session.close();
	}

也没有开启事务,打印信息如下:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
85

save方法在没有事务的情况下,仍然计划执行一条insert语句,同时从数据库中获取一个可用id,虽然最终没有insert,但是此id已被占用。

load和get方法
这个比较好理解,get方法始终返回一个真正对象,而load方法则需要根据lazy属性的true和false采用不同的加载策略,当为lazy=true时采用延迟加载的策略,即返回一个代理对象,内部是由javassist来实现代理的。

update方法:

@Test
	public void testUpdate(){
		Session session1=hibernateDao.getSession();
		Transaction tx=session1.beginTransaction();
		
		Customer customer=new Customer();
		customer.setName("小明");
		customer.setEmail("917312290@qq.com");
		session1.save(customer);
		tx.commit();
		session1.close();
		
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		customer.setName("小红");
		session2.update(customer);
		customer.setEmail("917312290小红@qq.com");
		
		tx2.commit();
		session2.close();
	}

首先使用session1将一个临时对象转化为持久化对象,关闭session1,则该持久化对象变为游离对象(含有主键),通过session2将该游离对象更新为持久化对象。我们可以看到如下sql:

Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
Hibernate: update hibernate.customer set name=?, email=?, registeredTime=? where id=?

虽然我们有多次set来更新cunstomer的内容,但是最终只有一次update语句。update方法所做的内容是,计划执行一条update语句,但是此时的更新参数并没有确定,只是在事务提交时才会确认更新的参数。这里和save方法就不太一样,save方法计划执行一条insert语句,同时将此时的数据的参数也确定下来了,一旦后面再次更新参数就要执行update语句来更新(见上述save介绍)。

对于update还有一个内容就是,当游离对象属性都没发生改变时,调用update语句仍然会执行一条update语句,如下:

@Test
	public void testUpdate(){
		Session session1=hibernateDao.getSession();
		Transaction tx=session1.beginTransaction();
		
		Customer customer=new Customer();
		customer.setName("小明");
		customer.setEmail("917312290@qq.com");
		session1.save(customer);
		tx.commit();
		session1.close();
		
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		session2.update(customer);
		
		tx2.commit();
		session2.close();
	}

Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
Hibernate: update hibernate.customer set name=?, email=?, registeredTime=? where id=?

如果想在游离对象没有任何属性更新时就不进行update更新则需要设置select-before-update="true",即在更新前先执行一次查询,通过对比查询出来的数据和现在的数据是否发生变化来决定是否进行update操作。如下设置映射文件:
<class name="com.ligang.domain.Customer" table="customer" lazy="true" select-before-update="false">

还是上述同样的程序,执行结果如下:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
Hibernate: select customer_.id, customer_.name as name2_0_, customer_.email as email3_0_, customer_.registeredTime as register4_0_ from hibernate.customer customer_ where customer_.id=?

可以看到先执行一条select语句,然后发现数据并没有发生变化,所以就没有执行update语句。如果数据经常发生变化,则不需要设置select-before-update="true",因为会多于执行一条select语句。

一个session不能拥有两个及以上id相同的对象,一旦拥有一个,然后想保存第二个时就会发生NonUniqueObjectException异常,如下:

@Test
	public void testUpdate(){
		Session session1=hibernateDao.getSession();
		Transaction tx=session1.beginTransaction();
		
		Customer customer1=new Customer();
		customer1.setName("小明");
		customer1.setEmail("917312290@qq.com");
		session1.save(customer1);
		tx.commit();
		session1.close();
		
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		Customer customer2=(Customer)session2.get(Customer.class,customer1.getId());
		
		customer1.setName("小红");
		session2.update(customer1);
		
		tx2.commit();
		session2.close();
	}

在session1关闭时,customer1对象就变为游离对象,然后session2加载了id为customer1对象id的持久化对象作为customer2,此时customer1和customer2的id是一样的,同时customer2已作为session2的持久化对象,此时更改customer1,然后去保存customer1,此时就会抛出NonUniqueObjectException异常,如下:
org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [com.ligang.domain.Customer#98]
	at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:617)
	at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:301)
	at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:244)
	at org.hibernate.event.internal.DefaultUpdateEventListener.performSaveOrUpdate(DefaultUpdateEventListener.java:55)
	at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
	at org.hibernate.internal.SessionImpl.fireUpdate(SessionImpl.java:739)
	at org.hibernate.internal.SessionImpl.update(SessionImpl.java:731)
	at org.hibernate.internal.SessionImpl.update(SessionImpl.java:726)
	at com.ligang.test.dao.CustomerDaoTest.testUpdate(CustomerDaoTest.java:47)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)


saveOrUpdate方法:
此方法包含了save和update的功能,当前对象为临时对象时会调用save方法,当前对象是游离对象时调用update方法。它怎么判断当前对象是临时对象和游离对象呢?如果主键id为Integer,则可以判断主键id是否为null来判断,id为null则为临时对象,否则为游离对象。如果主键id为int类型,则需要设置unsaved-value来进行区分,如下:

<id name="id" column="id" type="long" unsaved-value="0">
      <generator class="identity"/>
</id>

当对象的id等于unsaved-value值时就为临时对象,否则为游离对象。

merge方法:
对于上述update方法抛出NonUniqueObjectException异常,如果我们想不抛出异常,并且去更新持久化对象,就要使用merge方法,但是merge方法并不局限于此。merger的处理流程如下:
对于merge(customer1):
(1)如果customer1为游离对象,则根据它的id到session缓存中取持久化对象,如果取到则计划执行一条update语句,测试如下:

@Test
	public void testMerge1(){
		Session session1=hibernateDao.getSession();
		Transaction tx=session1.beginTransaction();
		
		Customer customer1=new Customer();
		customer1.setName("小明");
		customer1.setEmail("917312290@qq.com");
		session1.save(customer1);
		tx.commit();
		session1.close();
		
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		Customer customer2=(Customer)session2.get(Customer.class,customer1.getId());
		
		customer1.setName("小红");
		session2.merge(customer1);
		
		tx2.commit();
		session2.close();
	}

打印的sql如下:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=?
Hibernate: update hibernate.customer set name=?, email=?, registeredTime=? where id=?

(2)如果customer1为游离对象,则根据它的id到session缓存中取持久化对象,如果未取到则从数据库中查找出,如果查到则同样更新属性,计划执行一条update语句,此种情况比情况1多了一步向数据库中查询的操作,测试如下:
@Test
	public void testMerge2(){
		Session session1=hibernateDao.getSession();
		Transaction tx=session1.beginTransaction();
		
		Customer customer1=new Customer();
		customer1.setName("小明");
		customer1.setEmail("917312290@qq.com");
		session1.save(customer1);
		tx.commit();
		session1.close();
		
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		customer1.setName("小红");
		session2.merge(customer1);
		
		tx2.commit();
		session2.close();
	}

打印的sql如下:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=?
Hibernate: update hibernate.customer set name=?, email=?, registeredTime=? where id=?

情况一的查询是我们主动触发的,主要是将数据加载到session2的缓存中,与本情况的查询是不一样的,本情况的查询是因为在缓存中没有对象的持久化对象才触发向数据库中查找。
(3)如果customer1为游离对象,则根据它的id到session缓存中取持久化对象,如果未取到则从数据库中查找出,如果从数据库中也未取到,则会新建一个customer对象,并把customer1的属性复制过去,然后保存该新建的对象,返回该对象的引用,测试如下:

@Test
	public void testMerge3(){
		Session session1=hibernateDao.getSession();
		Transaction tx=session1.beginTransaction();
		
		Customer customer1=(Customer) session1.get(Customer.class,100L);
		session1.delete(customer1);
		tx.commit();
		session1.close();
		
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		customer1.setName("小红的妈妈");
		Customer customer2=(Customer) session2.merge(customer1);
		System.out.println(customer1==customer2);
		tx2.commit();
		session2.close();
	}

打印的sql如下:
Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=?
Hibernate: delete from hibernate.customer where id=?
Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=?
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
false

首先是session1从数据库中加载id为100L的数据,然后删除它,关闭session1,则此时customer1则变为游离对象,更改此游离对象,调用merge方法,session2首先从缓存中找id为100L的对象,没有则从数据库中找id为100L的数据,所以会有一条select语句,然后从数据库中也没找到,则session2创建一个新对象customer2,把customer1的属性值复制给customer2,然后insert customer2,所以会有一条insert语句,由于此时customer2是新建的,并不是直接使用的customer1,所以customer1==customer2是false。
(4)如果customer1是临时对象,则同情况三后半部分一样,直接新建一个新的customer对象,然后复制插入,返回新对象的引用,测试如下:

@Test
	public void testMerge4(){
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		Customer customer1=new Customer();
		customer1.setName("小红的妈妈");
		Customer customer2=(Customer) session2.merge(customer1);
		System.out.println(customer1==customer2);
		System.out.println(customer1.getId());
		
		tx2.commit();
		session2.close();
	}

打印的sql如下:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
false
null

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
delete方法:
(1)首先是删除一个持久化对象,测试如下:

@Test
	public void testDelete1(){
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		Customer customer1=(Customer)session2.get(Customer.class,102L);
		session2.delete(customer1);
		System.out.println(customer1.getId());
		
		tx2.commit();
		session2.close();
		System.out.println(customer1.getId());
	}

删除了持久化对象customer1,则从数据库中将其删除,同时将其从session持久化缓存中移除,但是没有删除其id值,真正的删除是发生在session清理缓存的时候,打印的sql如下:
Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=?
102
Hibernate: delete from hibernate.customer where id=?
102

若想在删除一个对象时同时删除其id属性的值,则需要设置hibernate的hibernate.cfg.xml配置文件的hibernate.use_identifier_rollback属性,将其设置为true。同样的测试例子打印的sql如下:
Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=?
0
Hibernate: delete from hibernate.customer where id=?
0

这里有个疑问就是,为什么不把id置为null,而是赋值为0(Customer的id类型为Long类型)。
(2)删除一个游离对象,测试如下:

@Test
	public void testDelete2(){
		Session session1=hibernateDao.getSession();
		Transaction tx=session1.beginTransaction();
		
		Customer customer1=(Customer) session1.get(Customer.class,99L);
		tx.commit();
		session1.close();
		
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		System.out.println(customer1.getId());
		session2.delete(customer1);
		System.out.println(customer1.getId());
		tx2.commit();
		session2.close();
		System.out.println(customer1.getId());
	}

打印的sql如下:
Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=?
99
99
Hibernate: delete from hibernate.customer where id=?
99

customer1在session1关闭后成为一个游离对象,具有id值,因此和删除持久化对象的区别并不大,同理设置hibernate.use_identifier_rollback属性将id删除,不再实验。


若想转载请注明出处
作者:乒乓狂魔

© 著作权归作者所有

共有 人打赏支持
乒乓狂魔
粉丝 1004
博文 105
码字总数 271356
作品 0
长宁
程序员
MyBatis原理简介和小试牛刀

在我看来mybatis的原理与hibernate在某些方面是一致的,先回顾一下Hibernate原理(原理主要上是要掌握并理解下列六个对象: Hibernate中重要的六个对象: Configuration:读取配置文件(主要指h...

youcongtech
2017/11/18
0
0
Hibernate入门

Hibernate简介 Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的ORM框架,hibernate可以自动生成SQL语句...

iborder
2016/10/31
34
0
Hibernate面试问题集锦: 概述

ImportNew注: 本文是ImportNew编译整理的Java面试题系列文章之一。你可以从这里查看全部的Java面试系列。 Q.怎么配置Hibernate? A.Configuration类使用配置hibernate.cfg.xml(或者hiber...

白志华
2015/09/27
105
0
译文: Hibernate 查询结果映射 Result Set Mapping 处理

这是我的系列中第四篇也是最后一篇关于 SQL 结果映射设置: Result Set Mapping: 基础映射 Result Set Mapping: 复杂映射 * Result Set Mapping: 构造结果映射器 * Result Set Mapping: Hibe...

littlebrain4solving
2017/11/07
0
0
Hibernate系列——总结篇(九)

概念 Hibernate是一个对象关系映射框架,当然从分层的角度看,我们也说它是数据持久层的框架。 我们从上一句话可以看出Hibernate的核心:面向对象、关系映射以及数据持久化。前面两个概念很容...

architect刘源源
01/11
5
0

没有更多内容

加载失败,请刷新页面

加载更多

用户体验要素——以用户为设计中心

一、用户体验是什么 产品会与外界发生联系,人们如何去使用产品,人们使用产品无非解决两种问题,一,提高效率;二娱乐。而用户体验兼顾着功能和界面两个方面,为的是“提高人们的工作效率”...

铸剑为犁413
41分钟前
0
0
学习设计模式——代理模式

1. 认识代理模式 1. 定义:为其他对象提供一种代理以控制对这个对象的访问。 2. 组织结构: Proxy:代理对象,要实现与目标代理对象的相同的接口,这样就可以使用代理来代替具体的目标对象,...

江左煤郎
今天
1
0
java JDK动态代理

本篇随笔是对java动态代理中的JDK代理方式的具体实现。 首先需要定义一个接口,为其定义了两个方法:   public interface UserService { public void add(); public void delete(); } 然后需...

编程SHA
今天
2
0
轻松理解Dubbo分布式服务框架

Dubbo是什么? Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。简单的说,dubbo就是个服务框架,如果没有分布式的需求,其实是不需要用的...

别打我会飞
今天
3
0
TypeScript基础入门之JSX(一)

转发 TypeScript基础入门之JSX(一) 介绍 JSX是一种可嵌入的类似XML的语法。 它旨在转换为有效的JavaScript,尽管该转换的语义是特定于实现的。 JSX在React框架中越来越受欢迎,但此后也看到了...

durban
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部