一、       Introduction

Hibernate是一个开源的对象关系映射(ORM)框架,对JDBC进行了轻量级的封装,将pojo与数据库表建立映射关系,是全自动的orm框架。它可以自动生成sql语句,自动执行。Hibernate可以在java的客户端程序使用,也可以在web端使用,完成数据持久化的任务。

二、       核心API

1.       Session

Session接口负责执行被持久化对象的crud操作。Session对象非线程安全。

2.       SessionFactory

SessionFactory接口负责初始化hibernate。它代理数据存储源,并负责创建session对象。

3.       Transaction

Transaction接口是可选的,可以不使用它,由hibernate自己的底层事务处理代码来取代。

4.       Query

Query接口方便的实现对数据库及持久对象进行查询。有两种表达方式:HQL语言和本地数据库的SQL语句。

5.       Criteria

Criteria接口与Query接口非常类似,不能在session之外使用。

6.       Configuration

Configuration类的作用是对hibernate进行配置,加载核心配置文件,以及启动hibernate。它是启动hibernate遇到的第一个对象。

三、       使用步骤

1.       创建表结构

2.       创建web工程

3.       导入必须的jar包

4.       编写pojo类

5.       创建pojo类与表结构的映射

6.       创建核心配置文件

7.       编写测试代码

8.       例子

Pojo略,注意不要使用基本数据类型,一律使用包装类,因为包装类的默认值为null,而基本类型为0,可能会不符合实际。

Pojo类与表结构的映射,文件名为:类名.hbm.xml

<?xml version="1.0"encoding="UTF-8"?>

<!DOCTYPE hibernate-mappingPUBLIC

    "-//Hibernate/HibernateMapping DTD 3.0//EN"

    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

    <class name="com.hibernate.pojo.Customer"table="cst_customer">

        <id name="cust_id"column="cust_id">

            <generator class="native"/>

        </id>

        <property name="cust_name"column="cust_name"></property>

      <property name="cust_user_id"column="cust_user_id"></property>

      <property name="cust_create_id"column="cust_create_id"></property>

      <property name="cust_source"column="cust_source"></property>

      <property name="cust_industry"column="cust_industry"></property>

      <property name="cust_level"column="cust_level"></property>

      <property name="cust_linkman"column="cust_linkman"></property>

      <property name="cust_phone"column="cust_phone"></property>

      <property name="cust_mobile"column="cust_mobile"></property>

    </class>

</hibernate-mapping>

核心配置文件

<?xml version="1.0"encoding="UTF-8"?>

<!DOCTYPE hibernate-configurationPUBLIC

   "-//Hibernate/HibernateConfiguration DTD 3.0//EN"

   "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-- 配置数据库连接信息 -->

        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

        <property name="hibernate.connection.url">jdbc:mysql:///hibernate_day01</property>

      <property name="hibernate.connection.username">root</property>

      <property name="hibernate.connection.password">1234</property>

      <!-- 数据库的方言: -->

      <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

      <!-- 可选配置 -->

      <!-- 显示SQL语句,在控制台显示. -->

     

        <!-- 配置映射 -->

        <mapping resource="com/hibernate/pojo/Customer.hbm.xml"/>

    </session-factory>

</hibernate-configuration>

测试代码

public class Test1 {

   /**

    * 1,加载配置文件

    * 2,创建sessionfactory对象,生成session对象

    * 3,session对象

    * 4,开启事务

    * 5,编写保存代码

    * 6,提交事务

    * 7,释放资源

    */

   @Test

   public voidtest1(){

      //1,利用configuration对象加载配置文件

      Configuration config=new Configuration();

      //加载src下面的名字叫hibernate.cfg.xml的配置文件,这个文件名固定为这个,不可以改

      config.configure();

      //2,创建sessionfactory对象

      SessionFactory sessionFactory=config.buildSessionFactory();

      //3,开启session会话

      Session session=sessionFactory.openSession();

      //4,开启事务

      Transaction trans=session.beginTransaction();

      //5,编写保存代码

      Customer c=new Customer();

      c.setCust_name("hhh");

        c.setCust_linkman("还好");

        c.setCust_industry("哈哈哈");

        //保存对象,操作对象相应于操作数据库的表结构,因为对象和表做了映射关系,这个是和mybatis不一样的地方

        session.save(c);

      //6,提交事务

      trans.commit();

      //7,释放资源

      session.close();

   }

四、       关于配置文件的其他配置

1.       单表映射关系文件的配置

<class    name=”类全路径”    table=”表名”      catalog=”数据库名”(一般无需配置catalog)>

       <id  name=”类中的id字段对应的属性”   column=”主键的字段”>

              <!- -主键的生成策略 native:如果是mysql,则主键递增;如果是oracle,则序列方式递增 - - >

              <generator   class=”native” />

       </id>

       <!- - 配置其他属性的映射

              length:如果没有设置length,则是使用默认长度;如果设置,则使用设置的长度。利用hibernate,可以通过pojo,生成表结构,这个length就能体现出作用。如果是先有表结构,后配置的映射文件,则length使用表结构中的设置。

              type:type有至少3中写法,java中的数据类型,hibernate的类型,数据库中的字段类型。为了避免麻烦,一般不使用。

- - >

       <property     name=”属性名”        column=”字段名”      length=”设置的长度”

</class>

2.       核心配置文件的配置

关于<session-factory>的可选配置

1)       是否在控制台显示sql语句

<property name="hibernate.show_sql">true</property>

2)       格式化sql语句

<property name="hibernate.format_sql">true</property>

3)       生成数据库表结构

<property name="hibernate.hbm2ddl.auto">create</property>

create,create-drop属性每次都新建表,请不要在开发用啊。

开发请用update或validate

update:有表用原来的表,没有表创建。添加表的字段,在pojo和映射文件中添加好,就可以修改表的字段,不支持删除字段。

validate:如果有表,用原来表。校验在映射文件中,pojo的属性和表的字段是否一致,不一致报错。

4)       配置核心配置文件可以使用properties文件,但是太麻烦,所有开发不用。

五、       详解主要API

1.       Configuration

作用:配置hibernate,加载核心配置文件,提供手动加载映射文件。

      //1,利用configuration对象加载配置文件

      //Configuration config=new Configuration();

      //加载src下面的名字叫hibernate.cfg.xml的配置文件,这个文件名固定为这个,不可以改

      //config.configure();

      //使用方法链简写代码

      new Configuration().configure();

      //如果使用属性文件,需要手动加载映射文件

      //config.addResource("com/hibernate/pojo/Customer.hbm.xml");

2.       SessionFactory

SessionFactory不是一个轻量级的,一个项目一般只需要一个SessionFactory。整合ssh后,利用spring可以是整个项目贯穿一个sessionfactory。

负责初始化hibernate,简单的sql语句缓存在sessionFactory里面,提高效率。在结构组成上,sessionfactory一般分为内部结构和外部结构两部分。内部结构,一般是进行缓存,由框架缓存sql语句,加载的配置。外部结构,一般是二级缓存,由sessionfactory创建的session是一级缓存,因为session的生命周期是从创建到完成sql语句,生命周期短暂,是一级缓存,为了提高效率,可以使用sessionfactory的外部结构作为二级缓存。但是开发中,一般使用更好的技术做缓存,如redis。

封装sessionFactory工具类的例子:

封装sessionfactory工具类

public class HibernateUtil {

   private staticConfiguration CONFIG;

   private staticSessionFactory FACTORY;

   static{

      CONFIG=new Configuration().configure();

      FACTORY=CONFIG.buildSessionFactory();

   }

   public staticSession getSession(){

      return FACTORY.openSession();

   }

}

测试:

   public voidtest2(){

      //加载配置文件,获取sesseionFactory,获取session

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      Customer c=new Customer();

      c.setCust_industry("2");

      c.setCust_name("hah");

      c.setCust_mobile("1324");

      session.save(c);

      tr.commit();

      session.close();  

   }

3.       Session

非线程安全,轻量级,生命周期大概是一个请求过程,session具有一个缓存,一般称之为hibernate的一级缓存。

常用的方法:

1)       save(obj)             插入数据

2)       delete(obj)          删除数据

关于修改或删除的标准操作。需要先查要修改的内容,在修改或删除。

例子:

   public voidtestDelete(){

      //加载配置文件,获取sesseionFactory,获取session

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      Customer cid=session.get(Customer.class,3L);

      System.out.println(cid);

      session.delete(cid);

      tr.commit();

      session.close();  

   }

3)       get(Class,id)        根据主键查询映射表的信息

Class,javabean的class类对象。id,对应主键的值。

例子:

   public voidtestGet(){

      //加载配置文件,获取sesseionFactory,获取session

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      Customer cid=session.get(Customer.class,3L);

      System.out.println(cid);

      tr.commit();

      session.close();  

   }

4)       update(obj)         修改数据

例子:

   public voidtestUpdate(){

      //加载配置文件,获取sesseionFactory,获取session

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      Customer cid=session.get(Customer.class,2L);

      System.out.println(cid);

      cid.setCust_name("看看");

      cid.setCust_level("2");

      session.update(cid);

      tr.commit();

      session.close();  

   }

5)       saveOrUpdate(obj)           保存或者修改

注意:不要设置id的值。如果要修改,要先查询再修改。

例子:

   public voidtestSaveOrUpdate(){

      //加载配置文件,获取sesseionFactory,获取session

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      Customer c=new Customer();

      c.setCust_name("hill");

      //这是错误的做法

      //c.setCust_id(2L);

      session.saveOrUpdate(c);

      Customer cid=session.get(Customer.class,2L);

      System.out.println(cid);

      cid.setCust_name("看33看");

      cid.setCust_level("2");

      session.saveOrUpdate(cid);

      tr.commit();

      session.close();  

   }

6)       createQuery()            HQL语句的查询

例子:

   public voidtestquery(){

      //加载配置文件,获取sesseionFactory,获取session

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      Query query=session.createQuery("from Customer");

      List<Customer> clist=query.list();

      for(Customer c:clist){

         System.out.println(c);

      }

      tr.commit();

      session.close();  

   }

4.       Transaction

事务的接口。

常用的方法:commit()   提交事务            rollback()      回滚事务

例子:

   public voidtestTrans(){

      Session session=null;

      Transaction tr=null;

      try{

         session=HibernateUtil.getSession();

         tr=session.beginTransaction();

         Customer c=new Customer();

         c.setCust_name("事务");

         session.save(c);

         int i=10/0;

         tr.commit();

      }catch(Exception e){

         tr.rollback();

         e.printStackTrace();

      }finally{

         if(session!=null){

            session.close();

         }

      }

   }

六、       持久化类

持久化类是一个javabean,当某个javabean与表建立映射关系就成了持久化类。在Mybatis中,没有这个概念。这是hibernate在入门上比mybatis门槛高的一个体现。

持久化类的编写规范:

1.       提供一个无参数public构造器;

2.       提供一个标识属性,映射数据表主键字段;

3.       提供public的set或者get方法

4.       标识属性尽量采用基本数据类型的包装类。

在创建持久化类和数据表的映射关系时,需要设置持久化类的唯一标识oid和数据表主键的映射。Java程序通过引用地址确定对象,持久化类通过唯一标识oid确定数据表的哪条记录。

在创建表的时候,这里指使用hibernate的持久化类生成表的时候。比如,有一个持久化类,是关于人的类,具有属性:姓名,性别,地址,身份证。在这些属性中,身份证是唯一的,可以认为是这个持久化类的自然主键,如果使用自然主键作为生成表的主键,可能会造成耦合,不推荐使用。为了降低耦合,使用代理主键作为对应生成表的主键。

例子:

搭建环境略

创建pojo类

public class User {

   private Integer id;

   private String name;

   private Integer age;

   public Integer getId() {

      return id;

   }

   public voidsetId(Integer id) {

      this.id = id;

   }

   public String getName() {

      return name;

   }

   public voidsetName(String name){

      this.name = name;

   }

   public Integer getAge() {

      return age;

   }

   public voidsetAge(Integer age){

      this.age = age;

   }

}

配置pojo类与表的映射关系,使pojo类成为持久化类

<?xml version="1.0"encoding="UTF-8"?>

<!DOCTYPE hibernate-mappingPUBLIC

    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

    <class name="com.hibernate.pojo.User"table="user">

        <id name="id"column="id">

            <generator class="native"/>

        </id>

        <property name="name"column="name" length="30"/>

        <property name="age"column="age" />

    </class>

</hibernate-mapping>

执行测试文件,自动生成表

public class HibernateUtil {

   private staticConfiguration CONFIG;

   private staticSessionFactory FACTORY;

   static{

      CONFIG=new Configuration().configure();

      FACTORY=CONFIG.buildSessionFactory();

   }

   public staticSession getSession(){

      return FACTORY.openSession();

   }

   public staticvoidmain(String[] args){

      getSession();

   }

}

(一)            映射关系表的主键的生成策略

1.       increment

适用于用short,int,long作为主键,不是使用的数据库的自动增长机制。获取主键的最大值,然后+1,作为执行的持久化类对象的主键值。

这个属性不常用。因为如果在并发访问的情况下,可能产生不安全的数据。

例子:

            <!-- 获取主键最大值,然后+1作为新行的主键值 -->

            <generator class="increment"/>

2.       identity

适用于short,int,long作为主键。这个必须使用在有自动增长功能的数据库中,采用的是数据库底层的自动增长机制。Mysql,db2,有自增功能,但是oracle是没有的。Oracle用不了这个属性。不利于扩展和多平台。

3.       sequence

适用于short,int,long作为主键。底层使用数据库的序列增长方式。Oracle底层是序列的增长可以用。但是mysql用不了。Db2也能用这个sequence属性。不利于扩展和多平台。

4.       native

本地的增长策略,根据底层的数据库,自动选择适用于这个数据库的主键生成策略。适用于short,int,long。

如果底层是mysql数据库,相当于identity;如果是oracle数据库,相当于sequence。

5.       uuid

适用于char,varchar类型作为主键。使用随机的字符串作为主键。

例子:

<hibernate-mapping>

   <class name="com.hibernate.pojo.Person"table="person">

       <id name="id"column="id">

          <generator class="uuid" />

       </id>

       <property name="name" column="name"length="30" />

   </class>

</hibernate-mapping>

6.       assigned

主键的生成不适用hibernate管理,必须手动设置主键。

(二)            持久化类的状态

Hibernate为了管理持久化类,根据持久化类的标识和Session对象对持久化类的管理,将持久化类分为三个状态。

1.       瞬时态:transient object

没有持久化标识,没有被纳入到session对象的管理。Session是一个缓存,如果把对象放入session的缓存,就相当于被session管理。

2.       持久态:persistent object

有持久化标识,已经被纳入到session对象的管理。

3.       脱管态:detached object

有持久化标识, 被纳入到session对象的管理。相当于已经跟session发生过关系了,从session的缓存中退出,或者session的缓存被收回。

例子:

   public voidtest3(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      //持久化User对象

      //在此打断点,用debug检查状态。

      User user=new User();

      user.setName("hh");

      user.setAge(22);

      //瞬时态,现在的user没有oid,没有纳入session对象的管理,此时,user对象是瞬时态的。

      Serializable id=session.save(user);

      System.out.println(id);

      //调用session的方法对持久化对象进行操作,将产生持久化对象的唯一标识oid,同时,也会将这个对象纳入session的缓存里。此时的user对象是持久态了。

      //使用session保存user,

      tr.commit();

      session.close();

      //session被回收后,缓存也随之消失,则缓存中的对象也消失。此时,user对象在缓存的记录消失。user对象仍然存在,则user的oid仍有效。此时,user对象是脱管态。

      System.out.println(user.getId());

      System.out.println(user.getName());

   }

(三)            持久化类三个状态的转换

持久态对象,有更新数据库的能力。利用了session的一级缓存。

例子:

   public voidtest1(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      //获取到一个持久化对象

      User u=session.get(User.class, 23);

      System.out.println(u.getName());

      u.setName("hhii");

      //按照正常的逻辑,是调用session的update方法实现对数据库的更新

      //session.update(u);

      //实际上不用

      tr.commit();

      session.close();

   }

七、       Hibernate的一级缓存

概念:

1.       缓存:在内存中开辟的一块空间,将数据源(数据库或者文件)中的数据存放到这个内存空间,再次获取的时候,直接从缓存中获取,提高程序的性能。内存比硬盘介质速度要快。

2.       Hibernate框架提供了两种缓存

一级缓存:自带自开。一级缓存的生命周期与session一致,周期较短,随收回session而消失。一级缓存又称为session级别的缓存。

二级缓存:为了弥补一级缓存的缺陷而使用二级缓存。默认没有开启,需要手动配置才可以用。二级缓存可以在多个session中共享数据,二级缓存又称为sessionFactory级别的缓存。

3.       Session对象的缓存

Session接口种,有一些列的java的集合,这些集合构成了session级别的缓存。将对象存入一级缓存中,session没有结束生命周期,对象仍存在于session中。

4.       编写查询的代码证明一级缓存的存在

在同一个session 对象中两次查询,可以证明使用了缓存。

例子:

用save方法证明一级缓存的存在

   public voidtest2(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      User u=new User();

      u.setName("hiljjll");

      u.setAge(1234);

      Serializable id=session.save(u);

      //此时,session没有销毁,而且事务没有提交,也就是数据库里面没有存入这个id的user对象,所以,这时是看不到有sql语句执行的,但是现在从session里却可以得到这个id的user对象,说明了什么。

      //说明了在session对象里面保存了这个user对象的数据,这就是一级缓存。

      System.out.println(id);

      User u2=session.get(User.class,id);

      System.out.println(u2.getName());

      tr.commit();

      session.close();

   }

利用查询两次,来证明一级缓存的存在

   /**

    * 查询两次,证明一级缓存的存在

    */

   @Test

   public voidtest3(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      User u1=session.get(User.class,23);

      System.out.println(u1.getName());

      //此时,再次查询,却没有sql语句输出,说明没有从数据库中取,而是从缓存中取了数据

      User u2=session.get(User.class,23);

      System.out.println(u2.getName());

      tr.commit();

      session.close();

   }

5.       持久态对象能够利用缓存更新数据库依据的原理和机制

比如:使用get方法查询到user对象后,然后设置user对象的一个属性,user.setXxxx(xx)。此时,没有进行对象的update操作,而数据库的记录改变了。

这里利用快照机制来完成的。Snapshot,快照机制。

如下面代码:

/**

    * 持久态对象具有更新数据库能力的机制---快照机制

    */

   @Test

   public voidtest4(){

      Session session=HibernateUtil.getSession();

     

      Transaction tr=session.beginTransaction();

     

      User u1=session.get(User.class,23);

     

      u1.setName("sss");

     

      tr.commit();

      session.close();

   }

当session对象创建时,就完成了对session对象的初始化。这时在session对象中形成缓存区域和快照区域,两块分区。当执行sesson.get(User.class,23)时,先会查询数据库,查询后,会将查询结果放入缓存区,同时,在快照区域也存放一份一样的数据。当执行user.setName(“xxx”);时,会将缓存区中的数据做相应的修改。当执行tr.commit时,执行之前,会比对缓存区和快照区的数据,如果一致,继续执行。如果不一致,会将缓存中的数据更新到数据库,同时,把快照区的数据更新成和缓存区一致。这样,就将数据持久化到数据库。当执行session.close()时,session收回,缓存和快照中的数据清除。

6.       操作一级缓存的方法

session.clear()                   清空缓存

例子:

   public voidtest5(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      User u1=session.get(User.class,23);

      System.out.println(u1.getName());

      //清空缓存

      session.clear();

      //第二次从数据库查,此时会产生一条sql语句

      User u2=session.get(User.class,23);

      System.out.println(u2.getName());

      tr.commit();

      session.close();

   }

session.evict(objectentity)             从一级缓存中清除指定的实体对象

例子:

   public voidtest6(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      User u1=session.get(User.class, 23);

      System.out.println(u1.getName());

      //清除指定的实体对象

      session.evict(u1);;

      //第二次从数据库查,此时会产生一条sql语句

      User u2=session.get(User.class, 23);

      System.out.println(u2.getName());

      tr.commit();

      session.close();

   }

session.flush()            刷出缓存,就是刷新缓存

例子:

   public voidtest7(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      User u1=session.get(User.class,23);

      u1.setName("aaaa");

      //自动刷新缓存

      session.flush();

      tr.commit();

      session.close();

   }

八、       Hibernate的事务与并发

(一) 事务的介绍见:http://blog.csdn.net/xingzhishen/article/details/76559566

在hibernate中设置隔离级别

级别的值:

1—Readuncommitted isolation

2—Readcommitted isolation

4—Repeatableread isolation

8—Serializableisolation

在hibernate.cfg.xml中设置:

      <!-- 设置事务的隔离级别,默认为可重复读 -->

      <property name="hibernate.connection.isolation">4</property>

(二) 丢失更新的问题

1.       不考虑隔离性时,也会产生写入数据的问题,这一类的问题叫丢失更新的问题。更新数据时,数据丢失,强调并发访问时的问题。

2.       产生原因:在高并发访问中,同时对同一id的行的不同字段做了修改,导致后写入数据库的数据覆盖了先写入数据库的数据,从而使得先更新的数据丢失的问题。

例子:

利用断点演示丢失更新

   public voidrun1(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      //在u=这行打断点

      User u=session.get(User.class, 23);

      u.setName("lll");

      tr.commit();

      session.close();

   }

   @Test

   public voidrun2(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      //在u=这行打断点

      User u=session.get(User.class,23);

      u.setAge(55);

      tr.commit();

      session.close();

   }

3.       解决方案

1)       悲观锁:采用的数据库提供的锁机制。采用这种机制,会在sql语句的后面添加 for update 字句。比如:当a事务在操作这个记录时,会把这条记录锁起来,其他事务不能操作这条记录。只有当a事务提交后,锁事务才能操作这条记录。这样效率会比较慢。

悲观锁

使用:session.get(User.class,1,LockMode.UPGRADE);方法

2)       乐观锁:采用版本号的机制来解决。给javabean添加一个字段叫version=0,这样就给表结构添加一个字段version=0。比如:当a事务操作这条记录时,提交时,先检查版本号,如果版本号的值相同时,才可以提交,同时更新version=1。如果版本号的值不是version=0,而是version=1,说明已经存在并发访问者修改了这条记录,这时一般就会抛异常,提示重新操作这条记录表,那么就是按照新的version=1来操作,如果仍然异常,说明还有并发访问,如此继续抛异常,重新操作,直到version一致,操作成功。

乐观锁的例子:

在持久化类中添加private Integer version属性,创建set,get方法。

   private Integer version;

   public Integer getVersion() {

      return version;

   }

   public voidsetVersion(Integer version){

      this.version = version;

   }

配置持久化类映射文件的version标签。

<hibernate-mapping>

    <class name="com.hibernate.pojo.User"table="user">

        <id name="id"column="id">

            <!-- <generator class="native" /> -->

            <!-- 获取主键最大值,然后+1作为新行的主键值 -->

            <generator class="increment"/>

        </id>

        <!-- 设置version字段映射在普通字段的前面。在数据库中,首次设置version,默认值为0 -->

        <version name="version"/>

        <property name="name"column="name" length="30"/>

        <property name="age"column="age" />

    </class>

</hibernate-mapping>

测试方式同利用断点演示丢失更新。

九、       绑定本地的session

(一) 关于在javaWEB中开启事务,在业务层使用connection来开启事务,为了保证唯一使用connection,需要将connection传递到持久层。有两种方式:

1.       通过参数传递下去。

2.       将connection绑定到ThreadLocal对象中。

ThreadLocal

java.lang 
类 ThreadLocal<T>

java.lang.Object

java.lang.ThreadLocal<T>

直接已知子类:

InheritableThreadLocal

public class ThreadLocal<T>
extends Object

该类提供了线程局部(thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。

Method

 T

get() 
          返回此线程局部变量的当前线程副本中的值。

protected  T

initialValue() 
          返回此线程局部变量的当前线程的“初始值”。

 void

remove() 
          移除此线程局部变量当前线程的值。

 void

set(T value) 
          将此线程局部变量的当前线程副本中的值设置为指定值。

ThreadLocal底层采用的是Map集合。

(二) Hibernate绑定session

Hibernate提供了threadlocal的方式。

步骤:

1.       在hibernate.cfg.xml中配置开启绑定本地的session

      <!-- 开启绑定本地的session -->

      <property name="hibernate.current_session_context_class">thread</property>

2.       重新编写HibernateUtil工具类,使用SessionFactory的getCurrentSession()方法,获取当前的session对象。这个sesssion对象不需要手动关闭,线程结束,它自动关闭。

   public static SessiongetCurrentSession(){

      return FACTORY.getCurrentSession();

   }

例子:

配置hibernate.cfg.xml文件

      <!-- 开启绑定本地的session -->

      <property name="hibernate.current_session_context_class">thread</property>

在HibernateUtil工具类中,从threadlocal中获取currentSession。

   public staticSession getCurrentSession(){

      return FACTORY.getCurrentSession();

   }

Pojo略

编写servlet

   protected voiddoGet(HttpServletRequest request, HttpServletResponse response) throwsServletException, IOException {

      User u1=new User();

      u1.setName("哈哈");

      User u2=new User();

      u2.setName("哈哈2");

      UserService us=new UserService();

      us.save(u1, u2);

   }

编写service层,在service层获取session,开启事务,并制造异常

   public voidsave(User u1,User u2){

      UserDao ud=new UserDao();

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      try{

         ud.save1(u1);

         int a=110/0;

         ud.save2(u2);

         tr.commit();

      }catch(Exception e){

         e.printStackTrace();

         tr.rollback();

      }

   }

修改dao层方法,从currentSession中取得session,取消开启事务和提交,不要关闭session。

   public voidsave1(User u1){

      Session session=HibernateUtil.getCurrentSession();

      session.save(u1);

   }

   public voidsave2(User u2){

      Session session=HibernateUtil.getCurrentSession();

      session.save(u2);

   }

十、       Hibernate中的查询

(一)            Query接口的HQL语句查询

常用方法:

1.       查询所有记录。list()

例子:

   public voidquery1(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      Query query=session.createQuery("from User");

      List<User> ulist=query.list();

      for(User u:ulist){

         System.out.println(u);

      }

      tr.commit();

      session.close();

   }

2.       按照条件查询,使用?

在创建query接口的实例时,将查询条件的hql语句传入。HQL:hibernate sql language。Hibernate框架专用的sql语句。

Queryquery=new createQuery(“from User where id = ? and age = ?”);

注意:?的索引从0开始。

?的类型是什么类型,就赋值为什么类型。比如id的类型为Long,就使用query.setLong赋值。有几个参数就设置几个set赋值。

例子1:

   public voidquery2(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      Query query=session.createQuery("from User where id = ?");

      query.setLong(0, 23);

      List<User> ulist=query.list();

      for(User u:ulist){

         System.out.println(u);

      }

      tr.commit();

      session.close();

   }

例子2:

   public voidquery3(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      Query query=session.createQuery("from User where age > ?and name like ?");

      query.setLong(0, 15);

      query.setString(1, "%h%");

      List<User> ulist=query.list();

      for(User u:ulist){

         System.out.println(u);

      }

      tr.commit();

      session.close();

   }

3.       按照条件查询,使用:xxx

?可以使用:xxx的方式替代。

例子:

   public voidquery4(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      Query query=session.createQuery("from User where age >:age and name like :name");

      query.setLong("age",15);

      query.setString("name", "%h%");

      List<User> ulist=query.list();

      for(User u:ulist){

         System.out.println(u);

      }

      tr.commit();

      session.close();

   }

(二)            Criteria接口

Criteria接口完全的面向对象。非常合适做条件查询。一般称为QBC查询。

通过session获得criteria对象,同时将要操作的对象的类作为参数传入。

Criteriacriteria=session.createCriteria(User.class);

1.       不设置添加criteria的参数,默认查询所有。

例子:

   public voidcriteria1(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      Criteria criteria=session.createCriteria(User.class);

      List<User> ulist=criteria.list();

      for(User u:ulist){

         System.out.println(u);

      }

      tr.commit();

      session.close();

   }

2.       设置相关参数,按条件查询

使用Restrictions的静态方法,拼接查询的条件。

例子:

   public voidcriteria2(){

      Session session=HibernateUtil.getSession();

      Transaction tr=session.beginTransaction();

      Criteria criteria=session.createCriteria(User.class);

      //Criterion是Hibernate提供的条件查询的对象,使用Restrictions可以获得这个对象。

      criteria.add(Restrictions.gt("age",15));

      criteria.add(Restrictions.like("name", "%h%"));

      List<User> ulist=criteria.list();

      for(User u:ulist){

         System.out.println(u);

      }

      tr.commit();

      session.close();

   }

十一、           Hibernate关联关系映射

(一)            针对数据库表中的一对多关系,如一个用户有多个订单。需要建立一对多映射。

1.       使用步骤:

1)       搭建开发环境,创建项目,导入jar包,日志文件。

2)       创建数据库表

3)       编写一对多的javabean。如编写客户和联系人的javabean。联系人以客户的主键为外键,形成一对多的关系。

创建一方的javabean时,注意将多方作为属性放入一方,而且Hibernate默认的集合时set集合,所以要使用set集合。而且要初始化这个set集合。

创建多方的javabean时,为了易于扩展,将一方的对象作为属性放入多方。而且,这个属性一定不能初始化,hibernate框架会自动初始化的,否则报错。

4)       编写一对多的映射配置文件

5)       测试

例子:

搭建开发环境和创建数据库表略

一对多的javabean

一方Customer

   private Long cust_id;

   private String cust_name;

   private Long cust_user_id;

   private Long cust_create_id;

   private String cust_source;

   private String cust_industry;

   private String cust_level;

   private String cust_linkman;

   private String cust_phone;

   private String cust_mobile;

   private String info;

   private Set<Linkman> linkmans=newHashSet<Linkman>();

多方Linkman

    private Long lkm_id;

    private String lkm_name;

    private String lkm_gender;

    private String lkm_phone;

    private String lkm_mobile;

    private String lkm_email;

    private String lkm_qq;

    private String lkm_position;

    private String lkm_memo;

    private Customer customer;

测试,以双向关联为例

   public voidtestMapper(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //保存客户和联系人

      Customer c=new Customer();

      c.setCust_name("哈哈哈");

      Linkman l1=new Linkman();

      l1.setLkm_name("看看");

      Linkman l2=new Linkman();

      l2.setLkm_name("上述");

      //双向关联

      //首先用客户关联系人

      c.getLinkmans().add(l1);

      c.getLinkmans().add(l2);

      //然后用联系人管理客户

      l1.setCustomer(c);

      l2.setCustomer(c);

      session.save(c);

      session.save(l1);

      session.save(l2);

      tr.commit();

   }

2.       一对多的主要操作

1)       双向关联保存

见上例。

2)       级联保存

保存在一方的时候,同时将一方关联的多方也保存进数据库,也就是保存一方可以级联多方;反之,如果保存多方的时候,同时将一方也保存进数据库,即保存多方可以级联一方。级联保存具有方向性。单象的级联保存,需要进行设置,才可以实现。

步骤:

在执行发放的一方的javabean的映射文件中设置级联的对象的属性cascade。

如上述例子中,采用级联保存,保存客户,级联联系人:

在客户的映射文件中设置

      <set name="linkmans"cascade="save-update">

          <key column="lkm_cust_id" />

          <one-to-many class="com.hibernate.pojo.Linkman" />

      </set>

测试

   public voidtestMapper2(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //保存客户和联系人

      Customer c=new Customer();

      c.setCust_name("哈哈哈");

      Linkman l1=new Linkman();

      l1.setLkm_name("看看");

      Linkman l2=new Linkman();

      l2.setLkm_name("上述");

      //单向关联

      //用客户关联联系人

      c.getLinkmans().add(l1);

      c.getLinkmans().add(l2);

      session.save(c);

      tr.commit();

   }

反之,如果保存联系人,级联客户:

在联系人的映射文件中设置

   <many-to-one name="customer"class="com.hibernate.pojo.Customer" column="lkm_cust_id" cascade="save-update"/>

测试

   public voidtestMapper3(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //保存客户和联系人

      Customer c=new Customer();

      c.setCust_name("哈哈哈");

      Linkman l1=new Linkman();

      l1.setLkm_name("看看");

      Linkman l2=new Linkman();

      l2.setLkm_name("上述");

      //单向关联

      //用联系人关联客户

      l1.setCustomer(c);

      l2.setCustomer(c);

      session.save(l1);

      session.save(l2);

      tr.commit();

   }

单向链式级联保存

在涉及到下行有级联的对象的javabean的映射文件的被级联的标签中,设置cascade属性。

<!– 下面是客户级联了联系人,所以在客户的映射中设置联系人标签的cascade属性 -- >

<set name="linkmans"cascade="save-update">

          <key column="lkm_cust_id" />

          <one-to-many class="com.hibernate.pojo.Linkman"/>

</set>

<!– 下面是联系人级联了客户,所以在联系人的映射中设置客户标签的cascade属性 -- >

   <many-to-one name="customer"class="com.hibernate.pojo.Customer" column="lkm_cust_id" cascade="save-update"/>

测试

   public voidtestMapper4(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //保存客户和联系人

      Customer c=new Customer();

      c.setCust_name("哈哈哈");

      Linkman l1=new Linkman();

      l1.setLkm_name("看看");

      Linkman l2=new Linkman();

      l2.setLkm_name("上述");

      //单向链式关联

      //用联系人1关联客户,再用客户关联联系人

      l1.setCustomer(c);

      c.getLinkmans().add(l2);

      session.save(l1);

      tr.commit();

   }

3)       级联删除

删除某一方的时候,通过级联可以删除这一方有映射关系的一方的数据,是级联删除。

直接使用sql语句,在含有外键的表中删除,会报错。

如果使用hibernate框架可以直接删除含有外键的数据。

例子:

   public voidtestMapper6(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //先查询到id为2的客户

      Customer c=session.get(Customer.class, 2L);

      session.delete(c);

      tr.commit();

   }

如果某个表中如,订单中数据是隶属于用户的,那么,当删除用户时,保留订单就没有意义,可以使用级联删除,删掉级联的对象。

例子:

删除客户级联联系人

在客户的映射文件中配置,联系人标签的cascade属性。

      <set name="linkmans"cascade="save-update,delete">

          <key column="lkm_cust_id" />

          <one-to-many class="com.hibernate.pojo.Linkman"/>

      </set>

测试

@Test

   public voidtestMapper6(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //先查询到id为2的客户

      Customer c=session.get(Customer.class, 2L);

      session.delete(c);

      tr.commit();

   }

删除联系人级联客户

在联系人的映射文件中配置,客户标签的cascade属性。

<many-to-one name="customer"class="com.hibernate.pojo.Customer" column="lkm_cust_id" cascade="save-update,delete"/>

测试

   public voidtestMapper8(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //先查询到id为2的客户

      Linkman l1=session.get(Linkman.class, 6L);

      session.delete(l1);

      tr.commit();

   }

注意级联删除的链式单向特性。比如上述级联删除中,如果配置了联系人的映射文件中客户标签的cascade属性,同时也配置了客户的映射文件中联系人标签的cascade属性,则执行删除联系人时,会把客户删除,同时把该客户所有的联系人也删除。注意慎用。

4)       级联中cascade的值

含义

none

不使用级联

save-update

级联保存或更新

delete

级联删除

delete-orphan

孤儿删除(注意:只能应用在一对多关系)

all

除了delete-orphan的所有情况,(包含save-update   delete)

all-delete-orphan

包含了delete-orphan的所有情况,(包含save-update   delete   delete-orphan)

重点:delete-orphan       孤儿删除

又叫孤子删除,只有在一对多的情况下才有。在一对多的关系中,可以将一方认为是父方,将多方认为是子方。孤儿删除指,在解除父子关系的时候,将子方记录就直接删除。

例子:

解除关系的操作

   public voidtestMapper9(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //解除一方与多方的关系

      //获得一方

      Customer c=session.get(Customer.class, 5L);

      //获得多方

      Linkman l1=session.get(Linkman.class, 8L);

      //解除关系

      c.getLinkmans().remove(l1);

      tr.commit();

   }

单纯的解除关系,而没有设置孤儿删除,不会删除解除掉关系的多方,只是将多方的外键设置为null。

在一方的orm文件中设置cascade=”delete-orphan”。

      <set name="linkmans"cascade="delete-orphan">

          <key column="lkm_cust_id" />

          <one-to-many class="com.hibernate.pojo.Linkman"/>

      </set>

5)       级联操作中的外键维护

在一对多的关系中,如果做双向关联的操作,双方都会维护外键,产生多余的sql语句。原因是:session的一级缓存中的快照机制,会让双方都更新数据库,产生多余的sql语句。如果不想产生多余的sql语句,需要一方放弃外键的维护。语法:比如在一方的orm文件的set标签中设置inverse=”true”。true:放弃维护;false:不放弃。默认值是false。放弃维护的好处是速度快。

注意:在一对多中,只有一方具有选择是否放弃外键维护的权利,它可以选择不放弃外键维护。

注意:在多对多的orm中,必须放弃外键维护,否则报错。

例子:

在没有设置放弃维护的情况下,会产生多余的sql语句

   public voidtestMapper10(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //解除一方与多方的关系

      //获得一方

      Customer c=session.get(Customer.class, 6L);

      //获得多方

      Linkman l1=session.get(Linkman.class, 9L);

      //做双向级联

      c.getLinkmans().add(l1);

      l1.setCustomer(c);

      tr.commit();

   }

设置一方的orm的set标签中,放弃外键维护。因为一方需要对多方维护,维护量大,操作时可能要查看多方的情况。而多方只有一个一方,维护量小,有利于效率提高。

      <set name="linkmans"inverse="true">

          <key column="lkm_cust_id" />

          <one-to-many class="com.hibernate.pojo.Linkman"/>

      </set>

6)       区分cascade和inverse的区别

功能不同:

cascade用于做级联操作。

inverse用于做外键维护。

例子:如果在一方的orm的set标签中设置了cascade的save或update属性,有设置了inverse的true属性。当通过保存或修改一方向数据库写入数据时,由于放弃了对多方的外键的维护,则多方的外键为null。反之,如果通过保存或修改多方向数据库写入数据时,外键不会为null。

(二)            针对数据库表中的多对多关系,如一个用户有多个角色,一个角色可以被多个用户拥有。需要建立多对多映射。

多对多的关系一般由三个表构成,其中两个表存在逻辑上多对多关系,第三张中间表,维护这两张表的多对多关系。如用户和角色,用户有多个角色,角色也可以同时属于多个用户。利用中间表指向这两张表的外键建立多对多关系。

在没有使用框架之前,中间表需要自己创建。如果使用hibernate,需要配置好javabean和orm文件,hibernate框架会自动创建中间表。

1.       配置步骤:

创建两个javabean,相互设置对方为自己的多方对象。

省略set和get方法。

public class User {

    private Long user_id;

    private String user_code;

    private String user_name;

    private String user_password;

    private String user_state;

    private Set<Role> roles = new HashSet<Role>();

}

public class Role {

    private Long role_id;

    private String role_name;

    private String role_memo;

private Set<User> users = new HashSet<User>();

}

配置各自的orm文件,配置完请放入核心文件的映射。

重点是配置多对多的集合

table属性,如果有表请对应表名,没有可以自定义。

<set      name=”属性名” table=”中间表名”>

       <key       column=”当前对象在中间表的外键的名称” />

       <many-to-many         class=”集合中对象的去路径”              column=”集合中对象的外键的名称” />   

</set>
<hibernate-mapping>

    <class name="com.hibernate.pojo.Role"table="sys_role"  >

        <id name="rid"column="rid">

            <generator class="native"/>

        </id>

        <property name="role_name"column="role_name"/>

        <property name="role_memo"column="role_memo"/>



        <set name="users"table="sys_user_role">

            <!--

              key:当前对象在中间表的外键的名称

              class:集合中的对象的全路径

              comlumn:集合中对象在中间表的外键的名称

             -->

            <key column="role_id"/>

            <many-to-many class="com.hibernate.pojo.User"column="user_id"/>

        </set>

    </class>

</hibernate-mapping>
<hibernate-mapping>

    <class name="com.hibernate.pojo.User"table="sys_user"  >

        <id name="uid"column="uid">

            <generator class="native"/>

        </id>

        <property name="user_code"column="user_code"/>

        <property name="user_name"column="user_name"/>

        <property name="user_password"column="user_password"/>

        <property name="user_state"column="user_state"/>

      <!-- 配置多对多映射 -->

        <set name="roles"table="sys_user_role">

            <!--

              key:当前对象在中间表的外键的名称

              class:集合中的对象的全路径

              comlumn:集合中对象在中间表的外键的名称

             -->

            <key column="user_id"/>

            <many-to-many class="com.hibernate.pojo.Role"column="role_id"/>

        </set>

    </class>

</hibernate-mapping>

使用HibernateUtil工具类的main方法测试略

一般在企业开发中,不使用外键,而是设置一个字段作为逻辑外键,只是逻辑上知道它是一个外键的存在,实际上没有设置它为外键。

2.       多对多映射关系的主要操作

多对多的操作中一定要放弃一种一方的外键维护,可以根据效率,让维护上付出更多的一方放弃,以提高效率。

1)       双向级联

例子:

比如让角色放弃外键维护

        <set name="users"table="sys_user_role" inverse="true">

            <!--

              key:当前对象在中间表的外键的名称

              class:集合中的对象的全路径

              comlumn:集合中对象在中间表的外键的名称

             -->

            <key column="role_id"/>

            <many-to-many class="com.hibernate.pojo.User"column="user_id"/>

        </set>

测试保存

   public voidtest1(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //多对多的双向级联

      User u1=new User();

      u1.setUser_name("看看");

      User u2=new User();

      u2.setUser_name("看看2");

      Role r1=new Role();

      r1.setRole_name("信息");

      Role r2=new Role();

      r2.setRole_name("信息2");

      //将u1关联r1和r2

      u1.getRoles().add(r1);

      u1.getRoles().add(r2);

      //将r1关联u1

      r1.getUsers().add(u1);

      r2.getUsers().add(u2);

      //设置u2的关联

      u2.getRoles().add(r1);

      r1.getUsers().add(u1);

      session.save(u1);

      session.save(u2);

      session.save(r1);

      session.save(r2);

      tr.commit();

   }

2)       级联保存

主要配置级联者的cascade属性

例子:

设置用户的级联保存cascade属性

        <set name="roles"table="sys_user_role" cascade="save-update">

            <!--

              key:当前对象在中间表的外键的名称

              class:集合中的对象的全路径

              comlumn:集合中对象在中间表的外键的名称

             -->

            <key column="user_id"/>

            <many-to-many class="com.hibernate.pojo.Role"column="role_id"/>

        </set>

测试

   public voidtest2(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //多对多的单关联

      User u1=new User();

      u1.setUser_name("级联保存1");

      User u2=new User();

      u2.setUser_name("级联保存2");

      Role r1=new Role();

      r1.setRole_name("信息几包");

      Role r2=new Role();

      r2.setRole_name("信息几包2");

      //将u1关联r1和r2

      u1.getRoles().add(r1);

      u1.getRoles().add(r2);

      //设置u2的关联

      u2.getRoles().add(r1);

      session.save(u1);

      session.save(u2);

      tr.commit();

   }

注意:如果多方的一方中是放弃外键维护的一方,请在没有放弃外键维护的一方做cascade属性,否则没有外键约束。

特别注意:要慎用级联删除,慎用。

3)       中间表操作

如果只需要修改中间表,只要修改orm的集合就可以。操作集合就可以操作中间表。

比如删除中间表的数据

   public voidtest3(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //查询要做修改的用户

      User user=session.get(User.class, 3L);

      //查询要删除的角色

      Role role=session.get(Role.class,4L);

      //执行删除

      user.getRoles().remove(role);

      tr.commit();

   }

十二、           Hibernate的查询方式

(一)            唯一标识oid的检索方式

比如:session.get(类名.class,oid);oid:指主键。

(二)            对象的导航的方式

即通过对象的对象找到对象。

如用户中有角色属性。那么,new User().getRole().getRolename()。这就是对象的导航。

在hibernate中,根据对象的属性类的对象就可以查询这个属性类对象。

例子1:

   public voidtest4(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //使用oid查询客户

      Customer c=session.get(Customer.class, 5L);

      //查看这个客户的联系人,由于联系人类是客户的一个属性,对于联系人对象得到这个属性类的对象,可以查询这个属性类(也是一个持久化类)。

      int a=c.getLinkmans().size();

      System.out.println(a);  

      tr.commit();

   }

例子2:

   public voidtest5(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //使用oid查询联系人

      Linkman l=session.get(Linkman.class, 9L);

      //查看这联系人所属的客户,由于联系人类是客户的一个属性,对于联系人对象得到这个属性类的对象,可以查询这个属性类(也是一个持久化类)。

      Customer c=l.getCustomer();

      System.out.println(c);  

      tr.commit();

   }

(三)            HQL的检索方式

1.       简介

HibernateQuery Language。Hibernate的查询语言。是面向对象的查询语言。和sql语言有些类似。为hibernate所独有。使用hibernate的query接口。

HQL和SQL的关系:

1)       HQL面向对象,hibernate负责解析hql语句,然后根据orm文件中的信息,把hql翻译成相应的sql语句。

2)       hql查询语句中的主体是域模型中的类及类的属性

3)       Sql语句与关系型数据库绑定在一起,sql查询的主体是数据库表及表的字段

2.       基本查询

1)       支持方法链,调用list()方法

例子:

   public voidtestHql1(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Query query=session.createQuery("from Customer");

      List<Customer> clist=query.list();

      for(Customer c:clist){

         System.out.println(c);

      }

      tr.commit();

   }

方法链的方式:

   public voidtestHql2(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      List<Customer> clist=session.createQuery("from Customer").list();

      for(Customer c:clist){

         System.out.println(c);

      }

      tr.commit();

   }

2)       使用别名的方式

Hql支持别名。多表的查询使用别名会很方便。

例子:

   public voidtestHql3(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      List<Customer> clist=session.createQuery("select c from Customer c").list();

      for(Customer c:clist){

         System.out.println(c);

      }

      tr.commit();

   }

3)       排序查询

语法同sql语句。

Sql:orderby 字段 asc,字段 desc

Hql:orderby 属性 asc,属性 desc

asc可以省略。

例子:

   public voidtestHql4(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      List<Linkman> clist=session.createQuery("from Linkman order by lkm_id").list();

      for(Linkman c:clist){

         System.out.println(c);

      }

      tr.commit();

   }

4)       分页查询

Sql语法:limit 起始记录,查询条数               起始记录从0开始,索引0表示第一条记录。

Hibernate提供了分页的方法,如下:

setFirstResult(a)         从哪条记录开始,如果查询是从第一条开启,值是0;

setMaxResults(b)              每页查询的记录数。

例子:

   public voidtestHql5(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Query query=session.createQuery("from Linkman order bylkm_id");

      query.setFirstResult(0);

      query.setMaxResults(2);

      List<Linkman> clist=query.list();

      for(Linkman c:clist){

         System.out.println(c);

      }

      tr.commit();

   }

方法链的方法:

   public voidtestHql6(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      List<Linkman> clist=session.createQuery("from Linkman order by lkm_id").setFirstResult(0).setMaxResults(2).list();

      for(Linkman c:clist){

         System.out.println(c);

      }

      tr.commit();

   }

5)       带条件的查询

Hql语法1:where属性=(like)?

Hql语法2:where属性=(like):自定义名称

两种都可以。如果是?,比如属性的类型为Long,那么在setLong(0,1L)中,索引从0开始。

如果是自定义名称,那么在setLong(“自定义名称”,值)。

例子:

   public voidtestHql7(){

      Session session=HibernateUtil.getCurrentSession();

      Transactiontr=session.beginTransaction();

      //:自定义名称的方式

      Query query=session.createQuery("from Linkman  l where l.lkm_id = :id");

      query.setLong("id",2);

      //使用?的方式

//    Queryquery=session.createQuery("from Linkman l where l.lkm_id = ?");

//    query.setLong(0, 2);

      List<Linkman> clist=query.list();

      for(Linkman c:clist){

         System.out.println(c);

      }

      tr.commit();

   }

通用方法:使用setParameter(),可以不需要判断传入参数的类型。

例子:

   public voidtestHql8(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //:自定义名称的方式

      Query query=session.createQuery("from Linkman  l where l.lkm_id > ? and l.lkm_gender =:gender");

      query.setParameter(0, 2L);

      query.setParameter("gender", "1");

      List<Linkman> clist=query.list();

      for(Linkman c:clist){

         System.out.println(c);

      }

      tr.commit();

   }

3.       HQL的投影查询

投影查询就是按照设置查询某一个字段或某几个字段。

查询多个字段是,list()方法返回的是object对象。

1)       查询多个字段例子:

   public voidtestHql9(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //:自定义名称的方式

      Query query=session.createQuery("select lkm_id,lkm_name fromLinkman");

      List<Object[]> clist=query.list();

      for(Object c:clist){

         System.out.println(c);

      }

      tr.commit();

   }

2)       将查询结果封装到对象中

首先,在持久化类中提供相应字段的构造方法;然后,在hql语句中new这个构造方法的实例。

在持久化类中提供对应的构造方法。注意持久化类必须要有空的构造方法,如果自定义了有参构造方法,定义无参构造方法,否则报错。

    public Linkman(Long lkm_id, String lkm_name) {

      super();

      this.lkm_id = lkm_id;

      this.lkm_name = lkm_name;

   }

   public Linkman(){

      

}

编写hql语句测试

   public voidtestHql10(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //:自定义名称的方式

      Query query=session.createQuery("select newLinkman(lkm_id,lkm_name) from Linkman");

      List<Object[]> clist=query.list();

      for(Object c:clist){

         System.out.println(c);

      }

      tr.commit();

   }

4.       HQL的聚合函数查询

Hql可以直接使用聚合函数。

例子1:

   public voidtestHql11(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //:自定义名称的方式

      Query query=session.createQuery("select count(*) fromLinkman");

      List<Number> clist=query.list();

      Long num=clist.get(0).longValue();

      System.out.println(num);

      tr.commit();

   }

例子2:

   public voidtestHql12(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      //:自定义名称的方式

      Query query=session.createQuery("select sum(lkm_id) fromLinkman");

      List<Number> clist=query.list();

      Long num=clist.get(0).longValue();

      System.out.println(num);

      tr.commit();

   }

(四)            QBC的检索方式

Query By Criteria     按条件查询。适合数据查询中按条件查询。

1. 基本查询

使用list()方法

例子:

   public voidtestCriteria1(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Criteria criteria=session.createCriteria(Linkman.class);

      List<Linkman> list=criteria.list();

      for(Linkman l:list){

         System.out.println(l);

      }

      tr.commit();

   }

方法链的方式:

   @Test

   public voidtestCriteria2(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      List<Linkman> list=session.createCriteria(Linkman.class).list();

      for(Linkman l:list){

         System.out.println(l);

      }

      tr.commit();

   } 

2.       排序查询

使用addOrder()方法

例子:

   public voidtestCriteria3(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Criteria criteria=session.createCriteria(Linkman.class);

      criteria.addOrder(Order.desc("lkm_id"));

      List<Linkman> list=criteria.list();

      for(Linkman l:list){

         System.out.println(l);

      }

      tr.commit();

   }

3.       分页查询

也是使用两个方法:setFirstResult();         setMaxResults();

例子:

   public voidtestCriteria4(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Criteria criteria=session.createCriteria(Linkman.class);

      criteria.addOrder(Order.desc("lkm_id"));

      criteria.setFirstResult(0);

      criteria.setMaxResults(3);

      List<Linkman> list=criteria.list();

      for(Linkman l:list){

         System.out.println(l);

      }

      tr.commit();

   }

4.       条件查询

Criterion是查询条件的接口,Restriction类是Hibernate框架提供的工具类,使用该工具类来设置查询条件。

使用Criteria接口的add方法,来传入条件。使用Restrictions来添加条件。

Restrictions的方法列表:

Restrictions.eq

相等

Restrictions.gt

大于

Restrictions.ge

大于等于

Restrictions.lt

小于

Restrictions.le

小于等于

Restrictions.between

在之间

Restrictions.like

模糊查询

Restrictions.in

范围

Restrictions.and

并且

Restrictions.or

或者

例子1:

   public voidtestCriteria5(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Criteria criteria=session.createCriteria(Linkman.class);

      criteria.addOrder(Order.desc("lkm_id"));

      criteria.add(Restrictions.eq("lkm_gender", "1"));

      criteria.add(Restrictions.gt("lkm_id", 2L));

      List<Linkman> list=criteria.list();

      for(Linkman l:list){

         System.out.println(l);

      }

      tr.commit();

   }

例子2:

add的用法

   public voidtestCriteria6(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Criteria criteria=session.createCriteria(Linkman.class);

      criteria.addOrder(Order.desc("lkm_id"));

      criteria.add(Restrictions.and(Restrictions.eq("lkm_gender","1"),Restrictions.gt("lkm_id",2L)));

      List<Linkman> list=criteria.list();

      for(Linkman l:list){

         System.out.println(l);

      }

      tr.commit();

   }

例子3:

注意between的用法,包括左和右的值。

   public voidtestCriteria7(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Criteria criteria=session.createCriteria(Linkman.class);

      criteria.addOrder(Order.desc("lkm_id"));

      criteria.add(Restrictions.between("lkm_id", 1L,10L));

      List<Linkman> list=criteria.list();

      for(Linkman l:list){

         System.out.println(l);

      }

      tr.commit();

   }

例子4:

注意in的用法,先创建一个集合或者数组,把需要in的值放入集合或者数组。

   public voidtestCriteria8(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Criteria criteria=session.createCriteria(Linkman.class);

      criteria.addOrder(Order.desc("lkm_id"));

      List<Long> idlist=new ArrayList<Long>();

      idlist.add(2L);

      idlist.add(3L);

      idlist.add(4L);

      criteria.add(Restrictions.in("lkm_id", idlist));

      List<Linkman> list=criteria.list();

      for(Linkman l:list){

         System.out.println(l);

      }

      tr.commit();

   }

例子5:

or的用法

   public voidtestCriteria9(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Criteria criteria=session.createCriteria(Linkman.class);

      criteria.addOrder(Order.desc("lkm_id"));

      criteria.add(Restrictions.or(Restrictions.eq("lkm_gender","1"),Restrictions.gt("lkm_id",2L)));

      List<Linkman> list=criteria.list();

      for(Linkman l:list){

         System.out.println(l);

      }

      tr.commit();

   }

例子6:

判断值是否为空的方法isNull();

5.       聚合函数查询

Projection是聚合函数的接口。hibernate也提供了Projections工具类,使用这个工具类可以设置聚合函数查询。

具体是是使用criteria的setProjection()方法。

例子:

   public voidtestCriteria11(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Criteria criteria=session.createCriteria(Linkman.class);

      criteria.setProjection(Projections.count("lkm_id"));

      List<Number> list=criteria.list();

      Long num=list.get(0).longValue();

      System.out.println(num);

      tr.commit();

   }

注意:如果做了聚合函数查询之后,又想查询所有的,需要在查询所有的之前,清空setProjection中的内容。

例子:

   public voidtestCriteria12(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Criteria criteria=session.createCriteria(Linkman.class);

      criteria.setProjection(Projections.count("lkm_id"));

      List<Number> list=criteria.list();

      Long num=list.get(0).longValue();

      criteria.setProjection(null);

      List<Linkman> listman=criteria.list();

      for(Linkman l:listman){

         System.out.println(l);

      }

      System.out.println(num);

      tr.commit();

   }

6.       离线条件查询

离线条件查询使用的是DetachedCriteria接口,离线条件查询对象在创建的时候,不需要使用session对象,在查询的时候直接使用session对象即可。

离线条件查询的意义:一般在三层的开发,开启业务是在service层,开启业务需要session接口,criteria通过session对象创建,而查询条件也是在criteria对象创建后,通过criteria对象设置。但是,一般查询条件是通过web层传到service层的,如果在条件比较复杂的情况下,传参的形式就显得比较繁琐,为了简化代码,优化维护,考虑在web层就设置好查询条件,将criteria对象传到service层,由于session是在service层创建的,以便于开启事务,没有离线之前,criteria的创建依赖session,为了达到在不依赖session的目的,所以有了离线条件设置的概念,所以有了DetachedCriteria的接口,使用这个接口可以创建criteria对象。

例子:

DetachedCriteria的使用

   public voidtestCriteria13(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      DetachedCriteria criteria=DetachedCriteria.forClass(Linkman.class);

      criteria.setProjection(Projections.count("lkm_id"));

      List<Number> list=criteria.getExecutableCriteria(session).list();

      Long num=list.get(0).longValue();

      criteria.setProjection(null);

      List<Linkman> listman=criteria.getExecutableCriteria(session).list();

      for(Linkman l:listman){

         System.out.println(l);

      }

      System.out.println(num);

      tr.commit();

   }

(五)            SQL的查询方式

Hibernate中可以使用sql语句来做操作。然后,框架的意义是高内聚,低耦合。在代码中使用sql就削弱了这个效果,比如使用sql就不好使用离线查询。一般在开发中,不是sql语句的方式。除非不使用sql就不行,那就用了。

在hibernate中提供了SQLQuery接口做sql语句的查询。

例子:

不使用封装对象,直接使用sqlquery对象的list()方法的返回值—数组。

   public void testsq1(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      SQLQuery sqlquery=session.createSQLQuery("select * fromcst_linkman where lkm_id = ?");

      sqlquery.setParameter(0, 2L);

      List<Object[]> list=sqlquery.list();

      for(Object[] l:list){

         System.out.println(Arrays.toString(l));

      }

      tr.commit();

   }

使用封装对象的方式

   public voidtestsq2(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      SQLQuery sqlquery=session.createSQLQuery("select * fromcst_linkman where lkm_id = ?");

      sqlquery.setParameter(0, 2L);

      sqlquery.addEntity(Linkman.class);

      List<Linkman> list=sqlquery.list();

      for(Linkman l:list){

         System.out.println(l);

      }

      tr.commit();

   }

(六)            HQL的多表查询

关于sql的多表查询,见:http://blog.csdn.net/xingzhishen/article/details/76944442

HQL的多表查询分为:迫切和非迫切。

非迫切返回的是Object[]

迫切返回的是对象,比如把客户的信息封装到客户对象中。

例子:

多表非迫切查询

   public voidtestHQLMultyT1(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Query query=session.createQuery("from Customer c inner joinc.linkmans");

      List<Object[]> list=query.list();

      for(Object[] o:list){

         System.out.println(Arrays.toString(o));

      }

      tr.commit();

   }

多表迫切查询。请使用fetch关键字将数据封装到对象。使用手动创建set集合,重装数据,过滤重复项。

例子:

   public voidtestHQLMultyT2(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Query query=session.createQuery("from Customer c inner joinfetch c.linkmans");

      List<Customer> list=query.list();

      Set<Customer> set=newHashSet<Customer>(list);

      for(Customer o:set){

         System.out.println(o);

      }

      tr.commit();

   }

十三、           Hibernate框架查询的性能优化—延迟加载

(一)            介绍

延迟加载时hibernate框架提高性能的方法。底层采用动态代理技术,生成一个动态代理对象。当使用这个代理对象的属性的时候,才会发送sql语句。减少对系统资源可能产生的不必要的占用。两种类型的延迟加载,类级别和关联级别的。

(二)            类级别的延迟加载

使用这个类的属性的时候,产生的延迟加载。使用session的load()方法。

例子:

   public voidtest2(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Customer c=session.load(Customer.class, 2L);

      System.out.println(c.getCust_name());

      tr.commit();

   }

设置类级别的延迟加载。

在设置延迟加载的持久化类的orm文件中,<class>标签,有一个lazy属性,lazy=”true”,使用延迟加载,lazy=”false”,不使用延迟加载。不设置lazy,默认是true。例子:

<class name="com.hibernate.pojo.Customer"table="cst_customer" lazy="false" >  

     ……

    </class>

第二种设置延迟加载的方法,使用Hibernate.initialize(Object o)方法使延迟加载失效。

例子:

   public voidtest2(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Customer c=session.load(Customer.class, 2L);

      Hibernate.initialize(c);

      System.out.println("-------------");

      System.out.println(c.getCust_name());

      tr.commit();

   }

(三)            关联级别的延迟加载

比如在客户下面有很多联系人,一般在持久化类下面有联系人的集合。在使用延迟加载的情况下,当查询客户的时候,只发送查客户的sql语句,当查询联系人的时候,才发送联系人的sql语句。默认下是设置了使用延迟加载的。

例子:

   public voidtest3(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Customer c=session.load(Customer.class, 5L);

     

      System.out.println("-------------");

      System.out.println(c.getLinkmans().size());

      tr.commit();

   }

(四)            关联级别延迟加载的优化策略

使用lazy和fetch两个属性。

如果是一对多的映射,这两个属性都是在持久化类的orm文件的<set>标签里设置和<many-to-one>标签里设置。如果是多对多,

1.       在set标签中设置

fetch的值

作用

select

默认值,发送查询语句

join

连接查询,发送一条迫切左外连接。也就是将一对多以左外连接执行sql。如果配置join。则lazy的设置失效。

subselect

子查询,发送子查询查询其关联对象(使用返回值为list的方法测试)

 

lazy的值

作用

true

默认值,延迟加载

false

不延迟

extra

极其懒惰

例子:

      <set name="linkmans"inverse="true" fetch="select"lazy="false">

          <key column="lkm_cust_id" />

          <one-to-many class="com.hibernate.pojo.Linkman"/>

      </set>

测试

   public voidtest5(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Customer c=session.get(Customer.class, 5L);

      System.out.println(c.getLinkmans().size());

      tr.commit();

   }

2.       在一对多的多方的orm的<many-to-one>的标签中设置fetch和lazy的值,以设置查询多方时的延迟加载策略。

fetch的值

作用

select

默认值,发送查询语句

join

连接查询,发送一条迫切左外连接。也就是将一对多以左外连接执行sql。如果配置join。则lazy的设置失效。

 

lazy的值

作用

false

不延迟

proxy

默认值,代理,现在是否采用延迟。有一方的orm的<lazy>的lazy确定,如果那边的lazy=”true”,proxy的值为true,代表延迟加载;如果那边的lazy=”false”。Proxy的值就是false,代表不采用延迟。

例子:

在<many-to-one>中设置延迟加载策略。

<many-to-one name="customer"class="com.hibernate.pojo.Customer" column="lkm_cust_id" fetch="select" lazy="proxy"/>

测试

   public voidtest6(){

      Session session=HibernateUtil.getCurrentSession();

      Transaction tr=session.beginTransaction();

      Linkman c=session.get(Linkman.class, 2L);

      System.out.println(c.getCustomer().getCust_name());

      tr.commit();

   }

 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐