hibernate系列(五)Session接口方法

Wesley13
• 阅读 415

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是一样的,不同之处先来看下官方文档:

hibernate系列(五)Session接口方法
这里说明了两点:
第一: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删除,不再实验。

若想转载请注明出处:  http://lgbolgger.iteye.com/blog/2125015
作者:iteye的乒乓狂魔

点赞
收藏
评论区
推荐文章
浅梦一笑 浅梦一笑
4个月前
初学 Python 需要安装哪些软件?超级实用,小白必看!
编程这个东西是真的奇妙。对于懂得的人来说,会觉得这个工具是多么的好用、有趣,而对于小白来说,就如同大山一样。其实这个都可以理解,大家都是这样过来的。那么接下来就说一下python相关的东西吧,并说一下我对编程的理解。本人也是小白一名,如有不对的地方,还请各位大神指出01名词解释:如果在编程方面接触的比较少,那么对于软件这一块,有几个名词一定要了解,比如开发环
技术小男生 技术小男生
4个月前
linux环境jdk环境变量配置
1:编辑系统配置文件vi/etc/profile2:按字母键i进入编辑模式,在最底部添加内容:JAVAHOME/opt/jdk1.8.0152CLASSPATH.:$JAVAHOME/lib/dt.jar:$JAVAHOME/lib/tools.jarPATH$JAVAHOME/bin:$PATH3:生效配置
光头强的博客 光头强的博客
4个月前
Java面向对象试题
1、请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。创建一个接口A,接口里有一个抽象方法fly()。创建一个Bird类继承Animal类并实现接口A里的方法输出一条有语句“鸟儿飞翔”,重写eat()方法输出一条语句“鸟儿吃虫”。在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方
刚刚好 刚刚好
4个月前
css问题
1、在IOS中图片不显示(给图片加了圆角或者img没有父级)<div<imgsrc""/</divdiv{width:20px;height:20px;borderradius:20px;overflow:h
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
小森森 小森森
4个月前
校园表白墙微信小程序V1.0 SayLove -基于微信云开发-一键快速搭建,开箱即用
后续会继续更新,敬请期待2.0全新版本欢迎添加左边的微信一起探讨!项目地址:(https://www.aliyun.com/activity/daily/bestoffer?userCodesskuuw5n)\2.Bug修复更新日历2.情侣脸功能大家不要使用了,现在阿里云的接口已经要收费了(土豪请随意),\\和注意
晴空闲云 晴空闲云
4个月前
css中box-sizing解放盒子实际宽高计算
我们知道传统的盒子模型,如果增加内边距padding和边框border,那么会撑大整个盒子,造成盒子的宽度不好计算,在实务中特别不方便。boxsizing可以设置盒模型的方式,可以很好的设置固定宽高的盒模型。盒子宽高计算假如我们设置如下盒子:宽度和高度均为200px,那么这会这个盒子实际的宽高就都是200px。但是当我们设置这个盒子的边框和内间距的时候,那
Wesley13 Wesley13
1年前
Java爬虫之JSoup使用教程
title:Java爬虫之JSoup使用教程date:201812248:00:000800update:201812248:00:000800author:mecover:https://imgblog.csdnimg.cn/20181224144920712(https://www.oschin
Wesley13 Wesley13
1年前
MySQL查询按照指定规则排序
1.按照指定(单个)字段排序selectfromtable_nameorderiddesc;2.按照指定(多个)字段排序selectfromtable_nameorderiddesc,statusdesc;3.按照指定字段和规则排序selec
Wesley13 Wesley13
1年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
helloworld_34035044 helloworld_34035044
7个月前
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为