Hibernate的核心接口和类

Hibernate的核心类和接口一共有6个,分别为:Session、SessionFactory、 Transaction、Query、Criteria和Configuration。这6个核心和类接口在任何开发中都会用到。通过这些接口,不仅可以对持久化对象进行存取,还能够进行事务控制。下面对这6个核心接口和类分别加以介绍。

Configuration

Configuration类的作用是对Hibernate进行配置,以及对它进行启动。在Hibernate的启动过程中,Configuration 类的实例首先定位映射文档的位置,读取这些配置,然后创建一个SessionFactory对象。虽然Configuration类在整个Hibernate项目中只扮演着一个很小的角色,但它是启动hibernate 时所遇到的每一个对象。简而言之,它负责管理Hiberante的配置信息,它主要用来加载这些配置文件,如hibernate.cfg.xml文件。

//加载的配置文件
Configuration config = new Configuration().configure("/hibernate/hibernate.cfg.xml");
//还可以加载映射文件
Configuration config = new Configuration().addFile("TRegister.hbm.xml");//方法一
Configuration config = new Configuration().addClass(hibernate.PO.TRegister.class);//方法二
Configuration config = new Configuration().addURL(Configuration.class.getResource("TRegister.hbm.xml"));//方法三

SessionFactory

SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。简而言之,SessionFactory负责Session实例的创建。可以通过Configuration实例创建。如下例子:

Configuration config = new Configuration().configure("/hibernate/hibernate.cfg.xml");

SessionFactory    sessionFactory = config.buildSessionFactory();

Congifuration对象会根据当前的配置信息,生成SessionFactory对象。SessionFactory对象一旦构造完毕,即被赋予特定的配置信息,即以后配置改变不会影响到创建的SessionFactory对象。如果要把以后的配置信息赋给SessionFactory对象,需要从新的Configuration对象生成新的SessionFactory对象。SessionFactory是纯种安全的,可以被多线程调用以取得Session,而且构造SessionFactory很消耗资源,所以多数情况下一个应用中只初始化一个SessionFactory,为不同的线程提供Session。在实际应用中,当客户端发送一个请求线程时,SessionFactory生成一个Session对象来处理客户请求。

Session

Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句。)。但需要注意的是Session对象是非线程安全的。同时,Hibernate的session不同于JSP应用中的HttpSession。这里当使用session这个术语时,其实指的是Hibernate中的session,而以后会将HttpSession对象称为用户session。

简而言之,Session是应用程序与数据库之间的一个会话,是Hibernate运作的中心,持久层操作的基础,相当于JDBC中的Connection。Session对象是通过SessionFactory创建的:

  Session session = SessionFactory.openSession();

一个持久化类与普通的JavaBean没有任何区别,但是它与Session关联后,就具有了持久化能力。当然,这种持久化操作是受Session控制的,即通过Session对象的装载,保存,创建或查询持久化对象。Session类的save(),delete()和load()等方法,来分别完成对持久化对象的保存,删除,修改加载等操作!

Session类方法的用途可以分以下五类:

1:取得持久化对象:get()和load()等方法。
2:持久化对象的保存,更新和删除:save(),update()saveOrUpdate()和delete()等方法。
3:createQuery()方法:用来从Session生成的Query对象。
4:beginTransaction()方法:从Session对象生成一个Transaction对象。
5:管理Session的方法:isOpen(),flush(),clear(),evict()和close()等方法,其中isOpen()方法用来检查Session是否仍然打开;flush()用来清理Session缓存,并把缓存中的SQL语句发送出去,clear()用来清除Session中的所有缓存对象evict()方法来清楚Session缓存中的某个对象;close()关闭Session。

取得持久化对象的方法:

取得持久化对象的方法主要有get()和load(),它们通过主键id来取得PO。

public void testDemo(){
        Session session = HibernateUtil.currentSession();//生成Session实例
        TRegister tr = (TRegister)session.get(TRegister.class, new Integer(1));//get()方法
        //TRegister tr = (TRegister)session.load(TRegister.class newInteger(1));//load()方法     
   System.out.print(tr.getUserName());
}

get()与load()的区别:

先来理解两个概念:

立即加载对象:当hibernate在从数据库中取得数据组装好一个对象后会立即再从数据库取得数据此对象所关联的对象;

延迟加载对象:Hibernate从数据库中取得数据组装好一个对象后,不会立即再从数据库取得数据组装此对象所关联的对象,而是等到需要时, 都会从数据库取得数据组装此对象关联的对象;

get()方法的执行顺序如下:

      a):首先通过id在session缓存中查找对象,如果存在此id的对象,直接将其返回;
      b):如果在session中查找不到,就在二级缓存中查找,找到后将 其返回;
      c):如果在session缓存和二级缓存中都找不到此对象,则从数据库中加载有此ID的对象;
      因此get()方法并不总是导致SQL语句,只有缓存中无此数据时,才向数据库发送SQL!         

load()与get()的区别:

      1:在立即加载对象时,如果对象存在,load()和get()方法没有区别,都可以取得已初始化的对象;但如果当对象不存在且是立即加载时,使用get()方法则返回null,而使用load()则抛出一个异常。因此使用load()方法时,要确认查询的主键ID一定是存在的,从这一点讲它没有get方便!
     2:在延迟加载对象时,get()方法仍然使用立即加载的方式发送SQL语句,并得到已初始化的对象,而load()方法则根本不发送SQL语句,它返回一个代理对象,直到这个对象被访问时才被初始化。

持久化对象的保存,更新和删除方法

save()方法:

session的save()方法将一个PO的属性取出放入PreparedStatement语句中,然后向数据库中插入一条记录(或多条记录,如果有级联)。
session保存一个对象时,按如下步骤进行:

1:根本配置文件为主键id设置的生成算法,为PO指定一个ID。
2:将 PO对象纳入session内部缓存(一个Map)内。
3:事务提交时,清理缓存,将新对象通过insert语句持久化到数据库中。

如果要为新的PO强制指定一个ID,可以调用Session的重载方法

save(Objectobj,Serializable id)

例:

session.save(tRegister, new Integer(123));

在调用save()方法时,并不立即执行SQL语句,而是等到清理完毕缓存时才执行。如果在调用save()方法后又修改了PO的属性,则Hibernate将会发送一条insert语句和一条update语句来完成持久化操作,如下:

public ststic void main(String[] args){

        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session session = sf.openSession();
        Transaction tx = session.beginTransaction();
          Person person=new Person();
        /**为对象设定一个ID,但注意此ID是无效的,
         *因为我们在配置文件中ID设置为Increment,
         *这样将被Hibernate的increment算法生成的值覆盖
         */
        person.setId(001);
        person.setAge(23);
        person.setSex("男");
        person.setUserName("lmb");
        person.setPassWord("lmb")
        session.save(person);
        person.setUserName("Tom");//在save()后又修改了PO的名字
        tx.commit();
        session.close();

}

监视上述程序运行会产生二条SQL:

Hibernate:select max(id) from t_register
Hibernate:insert into t_register (userName, userPwd, sex, age, id) values (?, ?, ?, ?, ?)
Hibernate:update t_register set userName=?, userPwd=?, sex=?, age=? where id=?

因此,最好是在对象状态稳定时再调用 save()方法,可以少执行一条update语句。

调用save()方法将临时对象保存到数据库中,对象的临时状态将变为持久化状态。当对象在持久化状态时,它一直位于Session缓存中,对它的任何操作在事物提交时都将同步保存到数据库中,因此,对一个已经持久化的对象调用save()或update()方法是没有意义的,如下:

public ststic void main(String[] args){
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session session = sf.openSession();
        Transaction tx = session.beginTransaction();
         Person person=new Person();
        /**为对象设定一个ID,但注意此ID是无效的,
         *因为我们在配置文件中ID设置为Increment,
         *这样将被Hibernate的increment算法生成的值覆盖
         */
        person.setId(001);
        person.setAge(23);
        person.setSex("男");
        person.setUserName("lmb");
        person.setPassWord("lmb")
        session.save(person);
        person.setUserName("Tom");//在save()后又修改了PO的名字
        session.save(person);//无效
        session.update();//无效
        tx.commit();
        session.close();                     
}

程序运行效果还是和上面的一样!

update()方法:

Session的update()方法是用来更新脱管对象的。

它的用法如下:

public void updateDemo{

     public static void main(String[] args){
            SessionFactory sf = new Configuration().configure().buildSessionFactory();
            Session session = sf.openSession();
            Transaction tx = session.beginTransaction();
            Person person=new Person();
            person=(Person)session.get(Person.class,new Integer(1));
            person.setUserName("updated");//更显脱管对象
            tx.commit();                   
        }
}

调用update()方法时,并不是立即发送SQL语句,对对象的更新操作将积累起来,在事物提交时由flush()清理缓存,然后发送一条SQL语句完成全部的更新操作!

saveOrUpdate()方法:

在实际应用中WEB程序员自言自语不会注意一个对象是脱管对象还是临时对象,而对脱管对象使用save()方法是不对的,对临时对象使用update()方法也是不对的。为了解决这个问题,便产生saveOrUpdate()方法。

saveOrUpdate()方法首先会判断该PO是脱管对象还是临时对象,然后会调用合适的方法。那么saveOrUpdate()方法如何判断PO是临时对象还是脱管对象呢?当满足下载情况之一时,Hibernate就认定它是临时对象。

1:在映射表中为<id>元素设置了unsaved-valu属性,并且实体对象的ID取值和unsaved-value匹配(默认为null)(注意:int和long型的ID的unsaved-value默认值为0)。
2:在映射文件中为<version>元素设置了unsaved-value属性,并且实体对象的version取值和unsaved-value匹配(默认为null)。

delete()方法:

session类的delete()方法负责删除一个对象(包括持久对象和脱管对象)。

//删除持久对象
public void deleteDemo{
        public static void main(String[] args){
               SessionFactory sf = new Configuration().configure().buildSessionFactory();
               Session session = sf.openSession();
               Transaction tx = session.beginTransaction();
               Person person=new Person();
               person=(Person)session.get(Person.class,new Integer(1));
               session.delete(person);//删除持久对象</span>
               tx.commit();                   
        }
}

监视运行:

Hibernate: select tregister0_.id as id0_0_, tregister0_.userName asuserName0_0_, tregister0_.userPwd as userPwd0_0_, tregister0_.sex assex0_0_, tregister0_.age as age0_0_ from t_register tregister0_where tregister0_.id=?
Hibernate: delete from t_register where id=?
//删除脱管对象
public void deleteDemo2{
        public static void main(String[] args){
               SessionFactory sf = new Configuration().configure().buildSessionFactory();
               Session session = sf.openSession();
               Transaction tx = session.beginTransaction();
               Person person=new Person();     
               person=(Person)session.get(Person.class,new Integer(1));
               tx.commit();   
               session.close();
               //再构建一个session
               Session session2=sf.openSession();
               tx=session2.beginTransaction();
               session2.delete(person);//删除脱管对象
               tx.commit();
               session.close();         
        }
}

监视运行:

Hibernate: select tregister0_.id as id0_0_, tregister0_.userName asuserName0_0_, tregister0_.userPwd as userPwd0_0_, tregister0_.sex assex0_0_, tregister0_.age as age0_0_ from t_register tregister0_where tregister0_.id=?
Hibernate: delete from t_register where id=?

在上述代码中session2先把tr对象进行关联,纳入Session缓存中,然后删除。需要注意的是,在调用delete()方法时并不是发送SQL语句,而是在提交事务时,清理了缓存才发送SQL。使用delete()删除对象时,会有一些性能上的问题,例如从上面的监视中可以看到,当删除一个对象时,会先调用get()加载这个对象,然后调用delete()方法删除对象,所以发送了一个多余的selete SQL,所以当删除大量数据时对性能影响就比较大了。为了解决批量删除的性能问题,常用的办法是使用批量删除操作,如下:

    //批量删除
    public void betchDeleteDemo{

        public static void main(String[] args){
               SessionFactory sf = new Configuration().configure().buildSessionFactory();
               Session session = sf.openSession();
               Transaction tx = session.beginTransaction();
               Query q=session.createQuery("delete from Person");
               q.executeUpdate();//删除对象  
               tx.commit();   
               session.close();
        }
}

监视运行:
Hibernate: deletefrom t_register只用了一条语句就完成了比量删除的操作。但它也有问题,批量删除后的数据还会存在缓存中,因此程序查询时可能得到脏数据(得到数据库中已不存在的数据),因此在使用批量删除时,也要经窒处世数据一致的问题。

Query

Query接口让你方便地对数据库及持久对象进行查询,它可以有两种表达方式:HQL语言或本地数据库的SQL语句。Query经常被用来绑定查询参数、限制查询记录数量,并最终执行查询操作。在Hibernate3.x中不再使用2.x中的find方法,而是引入了Query接口,用来执行HQL。其实在上面的例子中已经看出,Query接口可以从session对象生成;

Query接口主要方法有三个:

setXXX()方法:用于设置HQL中问题或变量的值。
list()方法:返回查询结果,并把结果转换成List对象。
executeUpdate()方法:执行更新或删除名。

setXXX()方法都有二种重载方法:

1:setString(int position,String value):用于设置HQL中“?”的值:其中position表示?位置,而value自然就是值。如下:

Query query = session.createQuery("from Person person where person.age>? and person.userName like ?");//生成一个Query实例
query.setInteger(0, 18);//设置第一个问号的值为18
query.setString(1, "%lmb%");//设置第二个问题的值为%lmb%

2:setString(String paraName,Stringvalue);用于设置HQL中“:”后跟变量的值;其中paraName代表HQL中“:”后跟变量,value为该变量设置的值。如下:

Query query = session.createQuery("from Person person where person.age>:minAge and person.userName like :userName");//生成一个Query实例
query.setInteger("minAge", 18);//设置变量minAge值为18
query.setString("userName", "%lmb%");//设置变量userName值为%lmb%

在HQL中使用变量代替问号,然后在setXXX()方法中为该变量设值。在HQL中使用变量相比问号有以下好处:

1:变量不依赖于它们在查询字符串中出现 的顺序。
2:在同一个查询中可以多次使用。
3:可读性好。

list()方法:返回查询结果,并把结果转换成List对象。

public void listDemo{

        public static void main(String[] args){
               SessionFactory sf = new Configuration().configure().buildSessionFactory();
               Session session = sf.openSession();
               Person person=new Person();

               Query query=session.createQuery("from Person");
               java.util.list list=query.list();
               for(int i=0;i<list.size();i++){
                       person=list.get(i);
               System.out.println(person);
            }

        }
}       

executeUpdate()方法:执行更新或删除名。

Query的executeUpdate()方法用于更新或删除语句。它常用于批量删除或批量更新操作,如下:

Query q = session.createQuery("delete from Person");
q.executeUpdate();//删除对象

使用命名查询(namedQuery):
其实我们还可以不将 HQL写在程序中,而是写入映射文件中,这样在需要的时候很方便!在映射文件中使用<query>标记,把HQL语句放入

<?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="hibernate.PO.Person" table="t_person">
        <id name="id" type="java.lang.Integer">
            <column name="id" />
            <!-- 我在MYSQL中并没有设置该字段递增,但在Hibernate中依然可以设置为递增 -->
            <generator class="increment" />
        </id>
        <property name="userName" type="java.lang.String">
            <column name="userName" length="30" />
        </property>
        <property name="userPwd" type="java.lang.String">
            <column name="userPwd" length="30" />
        </property>
        <property name="sex" type="java.lang.String">
            <column name="sex" length="10" />
        </property>
        <property name="age" type="java.lang.Integer">
            <column name="age" />
        </property>
    </class>    
    <query name = "queryUser_byAgeAndName"><!--此查询被调用的名字-->
    <![CDATA[from Person user where user.age >:minAge and user.userName like:userName]]>    
    </query>
</hibernate-mapping>

nameQueryDemo.java

public void nameQueryDemo{

        public static void main(String[] args){

               SessionFactory sf = new Configuration().configure().buildSessionFactory();
               Session session = sf.openSession();
               Person person=new Person();

               Query query=session.getNameQuery("queryUser_byAgeAndName");
               query.setInteger("minAge",18);
               query.setString("userName","%x%");
               List list=query.list();               

               for(int i=0;i<list.size();i++){
                       person=list.get(i);
                       System.out.println(person.getUserName());
               }                                 
        }
}

Transaction

Transaction接口是一个可选的API,可以选择不使用这个接口,取而代之的是Hibernate的设计者自己写的底层事务处理代码。 Transaction 接口是对实际事务实现的一个抽象,这些实现包括JDBC的事务、JTA中的UserTransaction、甚至可以是CORBA事务。之所以这样设计是能让开发者能够使用一个统一事务的操作界面,使得自己的项目可以在不同的环境和容器之间方便地移植。简而言之,该接口允许应用等量齐观定义工作单元,同时又可调用JTA或JDBC执行事物管理。它的运行与Session接口相关,可调用Session的beginTransaction()方法生成一个Transaction实例。一个Session实例可以与多个Transaction实例相关联,但是一个特定的Session实例在任何时候必须与至少一个未提交的Transaction实例相关联。

Transaction接口常用如下方法:

1:commit();提交相关联的Session实例。
2:rollback();撤销事物操作。
3:wasCommitted();事物是否提交。

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
1、概述 在我开始构思这几篇关于“自己动手设计ESB中间件”的文章时,曾有好几次动过放弃的念头。原因倒不是因为对冗长的文章产生了惰性,而是ESB中所涉及到的技术知识和需要突破的设计难点实在是比较多,再冗长的几篇博文甚至无法对它们全部进行概述,另外如果在思路上稍微有一点差池就会误导读者。 一个可以稳定使用的ESB中间件凝聚了一个团队很多参与者的心血,一个人肯定是无法完成这些工作的 。但是笔者思索再三,还是下决心将这这即便文章完成,因为这是对本专题从第19篇文章到第39篇文章中所介绍的知识点的最好的总结。我们
1、Exactly_once简介 Exactly_once语义是Flink的特性之一,那么Flink到底提供了什么层次的Excactly_once?有人说是是每个算子保证只处理一次,有人说是每条数据保证只处理一次。其实理解这个语义并不难,直接在官方文档中就可以看出: 从图中可以看出:Exactly_once是为有状态的计算准备的! 换句话说,没有状态的算子操作(operator),Flink无法也无需保证其只被处理Exactly_once!为什么无需呢?因为即使失败的情况下,无状态的operator(ma
简单工厂模式是指专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。 从图中我们可以清楚的看到,该模式中主要包含下面3种 角色: 工厂(Creator)角色 它是工厂模式的核心,负责实现创建所有势力的内部逻辑。工厂类可以被外界直接调用,创建所需的产品的对象。 抽象(Product)角色 简单工厂模式所创建的所有对象的父类,负责描述所有实例所共有的公共接口。 具体产品(Concrete Product)角色 是该模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。一般来讲是

Mybatis学习第一天 - 2016-07-22 17:07:07

  Mybatis第一天 2      MyBatis介绍 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。 MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集

IP数据报的分片和组装过程 - 2016-07-22 17:07:07

        一份数据从一个主机通过以太网发送到里一个主机时,是要经过很多层路由转发的。其中过程相对比较的复杂,在这里我们要讨论的是IP在路由中转发时是以怎样的形式转发的和目的主机在接受到这写数据报时又是怎样处理的。        首先我们需要了解的是整个 IP数据报的格式 : IP的转发控制都是由IP数据报的头部决定的。在这里我们就不详细的讨论首部的所有字段,我们就讨论一下个分片有关的总长度字段。        在IP数据报中,总长度是16位的字段,依次数据报的最大长度为2^16-1=65535字节,

Java的纤程库 - Quasar - 2016-07-22 17:07:06

最近遇到的一个问题大概是微服务架构中经常会遇到的一个问题: 服务  A  是我们开发的系统,它的业务需要调用  B  、  C  、  D  等多个服务,这些服务是通过http的访问提供的。 问题是  B  、  C  、  D  这些服务都是第三方提供的,不能保证它们的响应时间,快的话十几毫秒,慢的话甚至1秒多,所以这些服务的Latency比较长。幸运地是这些服务都是集群部署的,容错率和并发支持都比较高,所以不担心它们的并发性能,唯一不爽的就是就是它们的Latency太高了。 系统A会从Client接收

操作系统知识点整理 - 2016-07-22 17:07:03

作业 用户在一次解题或一个事务处理过程中要求计算机系统所做工作的集合。它包括用户程序、所需要的数据及控制命令等。作业是由一系列有序的步骤组成的。 进程 一个程序在一个数据集合上的一次运行过程。所以一个程序在不同数据集合上运行,乃至一个程序在同样数据集合的多次运行都是不同的进程。 线程 线程是进程中的一个实体,是被系统独立调度和执行的基本单位。 进程和线程的区别 进程是程序的一次执行。线程可以理解为进程中执行的一段程序片段。 进程是独立的,这表现在内存空间、上下文环境上;线程运行在进程空间内。一般来讲(不适

Spring MVC Web简单入门实例 - 2016-07-22 17:07:52

本文通过一个简单的用户登录例子带你入门Spring MVC Web开发。 开发环境 1、STS 3.7.3(Spring Tool Suit), 下载 。STS其实是一个包装过的Eclipse,由Spring小组开发的,专门用于Spring项目的开发。老规矩,安装之前先要安装jdk,并配置好环境变量。 2、Tomcat 7, 下载Tomcat 7 。sts已经集成了一个叫Pivotal tc Server的web服务器,不过我们一般都使用Tomcat作为我们的Web服务器。 Tomcat配置 。 创建项目
1 简介 Ubuntu分区方案一般有下面三种: /boot 200M、/ 30G、/home 剩余全部空间、swap 8G / 剩余全部空间、swap 8G / 30G、/home 剩余全部空间、swap 8G 第一种分区方案是为了将/boot独立出来,防止操作系统无法启动,这样分的好处博主没体会到,好像/boot没什么用,而且把磁盘搞得支离破碎的,所以博主一般不用这种分法。 第二种分区方案是懒人方案,或者说新手方案,简单粗暴,对于装系统像家常便饭一样的博主来说,这样分区最快啦,毕竟在实体机上操作分区需要

7个变革DevOps的工具 - 2016-07-22 17:07:34

1. 简介 随着公司业务的不断迅速增长,使得管理复杂的IT基础设施需求变得更为艰难。解决应对这一复杂变幻的挑战的最佳方法是让开发团队和运维团队紧密协作,实现灵活应对。拥有一个DevOps专家团队可以实现在最少时间服务中断的情况下实现IT基础设施的动态伸缩。 DevOps团队执行各种任务, 如: 新虚拟机的配置 配置网络设备和服务器 应用程序部署 收集和聚合的日志 性能监视服务、网络和应用程序 报警和自动修复的问题 服务器和服务可用性监控 如果不使用正确的工具集来执行这些任务将会是一件即费时又费钱的事。某些