Hibernate学习笔记

Hibernate

标签(空格分隔): SSH hibernate


一、hibernate基本概念

1、Hibernate是什么?

Hibernate是一种框架,是一个ORM(object relation mapping)框架
Hibernate处于项目的持久层,其实际上对jdbc进行了轻量级封装
Hibernate基础是java反射机制
Hibernate可以用j2ee项目中,也可以用在j2se项目

2、什么是ORM?

对象关系映射(ObjectRelationMapping,简称ORM)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将java程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。

3、什么是POJO

在使用hibernate时,要求和数据库的某张表相互映射的那个java类,是一个POJO类,一般放在com.xxx.domain包下,POJO类翻译过来就是:简单的Java对象(Plain Ordinary Java Objects),实际就是普通JavaBeans,使用POJO名称是为了避免和EJB混淆起来。一个POJO类应当具有:
1. 有一个主键属性,用于唯一标识该对象(这就是为什么hibernate设计者建议要映射的表需要一个主键)。
2. 有其它的属性,有对各个属性操作的get/set方法。
3. 属性一般是private修饰.
4. 一定有一个无参的构造函数(用于hibernate框架反射用.)

二、为什么需要Hibernate?

如果项目需要切换数据库,则业务层需要修改SQL语句,非常麻烦;
开发者希望只关注业务本身,而不用关注数据库
1、分层更清晰、耦合性更小
2、通用性强,可以轻松从一个数据库平台迁移到别的数据库平台
3、对象化:把关系数据库变成java对象,更加方便操作
4、性能保证,hibernate针对不同的数据库时会使用其最优化的SQL语句
5、增加程序的鲁棒性
这里写图片描述

三、hibernate快速入门

1、Hibernate开发方式的三种:
  1. 由Domain object –>mapping–>db(官方推荐)
  2. 由db开始,使用工具生成mapping和Domain Object(使用较多)
  3. 由映射文件开始
2、使用上述第二种方式开发Hibernate项目

(1)建立数据库及表,如:user(id,email,name,pwd)
(2)开发domain对象,即User类,该类需提供无参构造函数、setter/getter方法,属性最好是private的,建议对应的数据表有一个主键(如id)

注:对象关系映射文件:作用是用于指定domain对象和表的映射关系,该文件取名有规范:domain对象名.hbm.xml,一般我们放在 和domain对象同一个文件夹下

注意:domain对象应当序列化,目的是可以唯一的标示某个对象,同时可以在网络上、文件中传送。

(3)配置对象关系映射文件:如User.hbm.xml (和User类放在同一包下)

<?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 package="com.demo.domain">
    <class name="User" table="user">
        <!-- id元素用于指定主键 -->
        <id name="id" column="id" type="java.lang.Integer">
            <!-- generator元素用于指定主键生成策略 -->
            <generator class="identity"></generator>
        </id>
        <!-- property配置其他属性 -->
        <property name="name" type="java.lang.String">
            <column name="name" not-null="true"></column>
        </property>
        <property name="email" type="java.lang.String">
            <column name="email" unique="true" not-null="true"></column>
        </property>
        <property name="passwd" type="java.lang.String">
            <column name="passwd" not-null="true"></column>
        </property>
    </class>
</hibernate-mapping>

(4)配置hibernate.cfg.xml,该文件用于配置 连接的数据库类型及驱动、用户名密码等,同时需要对象关系映射文件User.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC       
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
     <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
     <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
     <property name="connection.url">jdbc:mysql://localhost:3306/user_sys</property>
     <!-- create:每次都重建这个表 -->
     <!-- update:如果没有表则创建新表,如果有表,则看看表结构有没有变化 -->
     <!-- create-drop:在显式关闭SessionFactory时,将drop掉数据库的schema-->
     <!-- validate:每次插入数据之前都会验证数据库中的表结构和hbm文件的结构是否一致-->
     <property name="hbm2ddl.auto">update</property>
     <property name="connection.username">lawen</property>
     <property name="connection.password">lawen123</property>
     <property name="myeclipse.connection.profile">MySQL</property>
     <!-- 指定对象映射文件 -->
     <mapping resource="com/demo/domain/User.hbm.xml"/> 
</session-factory>
</hibernate-configuration>

(5)在main方法中测试:

    //使用Hibernate完成crud操作(这里只见对象,不见表)
    //1、创建configuration对象
    Configuration configuration = new Configuration().configure();
    //2、创建SessionFactory会话工厂,这是一个重量级对象。我们应该保证SessionFactory是单态的,即只有一个实例。
    SessionFactory factory = configuration.buildSessionFactory();
    //3、创建session (不同于web中的session) 相当于是和数据库的连接
    Session session = factory.openSession();
    //对于Hibernate来说,在增加、删除、修改数据时,必须使用事务进行提交
    //4、添加一条数据到数据库。下面方式会出错,因为没使用事务
//  User user = new User();
//  user.setUseremail("ji@163.com");
//  user.setUsername("laoji");
//  user.setUserpwd("123456");
//  session.save(user);
//  session.close();
    Transaction transaction = session.beginTransaction();
    User user = new User();
    user.setUseremail("ji@163.com");
    user.setUsername("laoji");
    user.setUserpwd("123456");
    session.save(user);
    transaction.commit();
    session.close();

(6)保证SessionFactory是单例的(该文件通过MyEclipse工具可以自动生成)

package com.demo.util;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
/**
 * Configures and provides access to Hibernate sessions, tied to the
 * current thread of execution.  Follows the Thread Local Session
 * pattern, see {@link http://hibernate.org/42.html }.
 */
public class HibernateSessionFactory {
    /** 
     * Location of hibernate.cfg.xml file.
     * Location should be on the classpath as Hibernate uses  
     * #resourceAsStream style lookup for its configuration file. 
     * The default classpath location of the hibernate config file is 
     * in the default package. Use #setConfigFile() to update 
     * the location of the configuration file for the current session.   
     */
    private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
    private static org.hibernate.SessionFactory sessionFactory;
    private static Configuration configuration = new Configuration();
    private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
    private static String configFile = CONFIG_FILE_LOCATION;
    static {
        try {
            configuration.configure(configFile);
            sessionFactory = configuration.buildSessionFactory();
        } catch (Exception e) {
            System.err.println("%%%% Error Creating SessionFactory %%%%");
            e.printStackTrace();
        }
    }
    private HibernateSessionFactory() {}
    /**
     * Returns the ThreadLocal Session instance.  Lazy initialize
     * the <code>SessionFactory</code> if needed.
     *
     *  @return Session
     *  @throws HibernateException
     */
    public static Session getSession() throws HibernateException {
        Session session = (Session) threadLocal.get();
        if (session == null || !session.isOpen()) {
            if (sessionFactory == null) {
                rebuildSessionFactory();
            }
        session = (sessionFactory != null) ? sessionFactory.openSession()
                            : null;
        threadLocal.set(session);
        }
        return session;
    }
    /**
     *  Rebuild hibernate session factory
     *
     */
     public static void rebuildSessionFactory() {
        try {
            configuration.configure(configFile);
            sessionFactory = configuration.buildSessionFactory();
        } catch (Exception e) {
            System.err.println("%%%% Error Creating SessionFactory %%%%");
            e.printStackTrace();
        }
    }
    /**
     *  Close the single hibernate session instance.
     *
     *  @throws HibernateException
     */
    public static void closeSession() throws HibernateException {
        Session session = (Session) threadLocal.get();
        threadLocal.set(null);
        if (session != null) {
             session.close();
         }
    }
    /**
     *  return session factory
     *
     */
    public static org.hibernate.SessionFactory getSessionFactory() {
        return sessionFactory;
    }
    /**
     *  return session factory
     *
     *  session factory will be rebuilded in the next call
     */
    public static void setConfigFile(String configFile) {
        HibernateSessionFactory.configFile = configFile;
        sessionFactory = null;
    }
    /**
     *  return hibernate configuration
     *
     */
    public static Configuration getConfiguration() {
        return configuration;
    }
}

(7)增删改操作

    public static void delUser() {
        Session session = HibernateSessionFactory.getSession();
        Transaction transaction = session.beginTransaction();
            //load可以通过主键获取对象
            User user = (User) session.load(User.class, 1); 
            //这里形成select语句
            session.delete(user); 
            transaction.commit();
            session.close();
        }
        public static void changeUser() {
        Session session = HibernateSessionFactory.getSession();
        Transaction transaction = session.beginTransaction();
            //load可以通过主键获取对象
            User user = (User) session.load(User.class, 1); //这里形成select语句
            user.setUsername("hahaha");//这里执行update语句
            transaction.commit();
            session.close();
        }
        public static void addUser() {
            Session session = HibernateSessionFactory.getSession();
            Transaction transaction = session.beginTransaction();
            User user = new User();
            user.setUserid(5);
            user.setUseremail("ji@163.com");
            user.setUsername("laoji");
            user.setUserpwd("123456");
            session.save(user); //insert into
            transaction.commit();
            session.close();
        }
注意:程序异常时注意事务回滚,Session使用完之后需要及时关闭。

三、hibernate核心类及接口

1、Configuration类的作用:

(1)读取Hibernate.cfg.xml
(2)管理对象关系映射文件xxx.hbm.xml
(3) 加载驱动、URL、用户名密码等。
(4)管理Hibernate配置信息。

2、hibernate.cfg.xml

该文件主要用于指定各个参数,是hibernate核心文件
默认放在src目录下,也可以放在别的目录下。
指定连接数据库的驱动、用户名、密码、url、连接池..
指定对象关系映射文件的位置.
也可使用hibernate.properties文件来替代该文件.(推荐使用hibernate.cfg.xml)

3、xxx.hbm.xml

该文件主要作用是建立表和类的映射关系,是不可或缺的重要文件.
一般放在其映射的类同一个目录下,但不是必须的。
命名方式一般是 类名.hbm.xml,但不是必须的。
表和类的映射示意图

4、SessionFactory接口

(1)缓存SQL和某些数据;
(2)重量级类,使用单例模式保证SessionFactory对象只有一份,一种数据库对应一个SessionFactory;
(3)可以获取session实例
(4)如果某个应用访问多个数据库,则要创建多个会话工厂实例,一般
是一个数据库一个会话工厂实例
这里写图片描述

    Configuration cf = new Configuratino().configure();
    SessionFactory sf = cf.buildSessionFactory();
    Session ss = sf.getCurrentSession(); //获取session实例
    //ss = sf.openSession();    //获取session实例
5、Session接口

Session一个实例代表与数据库的一次操作(当然一次操作可以是crud组合)
Session实例通过SessionFactory获取,用完需要关闭。
Session是线程不同步的(不安全),因此要保证在同一线程中使用,可以用getCurrentSession()。
Session可以看做是持久化管理器,它是与持久化操作相关的接口
Session一般以对象的形式来操作:
1. 保存一个对象(记录)—save方法
2. 删除一个对象(记录)—delete方法
3. 查询一个对象(记录)—get/load方法
4. 修改一个对象(记录)—update方法

6、SessionFactory的getCurrentSession()和openSession()的区别和联系?

(1)openSession()是获取一个新的会话;
(2)getCurrentSession()是获取和当前线程绑定的Session,换言之,在同一个线程中,我们获取的Session是同一个,这样利于事务的控制;
注意:如果使用getCurrentSession,需要配置Hibernate.cfg.xml:

 如果是本地事务:
 <property name="current_session_context_class">thread</property>
如果是全局事务:
<property name="current_session_context_class">jta</property>

(1)采用getCurrentSession创建的Session在commit或rollback时,会自动关闭
(2)采用openSession创建的Session必须手动关闭。(建议两种方式都手动关闭)
(3)如果通过getCurrentSession创建的Session进行操作需要在事务中提交(包括查询,openSession创建的Session在查询时可以不用事务)。

7、如何选择getCurrentSession()和openSession()?

如果在同一线程中保证使用同一个session,则用getCurrentSession
如果在一个线程中,需要使用不同的Session,则用openSession;

8、本地事务和全局事务

本地事务:针对一个数据库的事务
全局事务:跨数据库的事务(jta)

9、session的get()和load()的区别?

(1)get方法直接返回实体类,如果查不到数据就返回null。
(2)load方法会返回一个实体代理对象(当前这个对象可以自动转化为实体对象),但当代理对象被调用时,如果数据不存在,则抛出异常
(3)load先到缓存中查,如果查不到则返回一个代理对象,等后面对这个代理对象操作时,才到DB中查询,这就是load的延迟加载;
(4)get先到缓存中查询,如果没有立即到db中查。
(5)通过配置文件(lazy=false)可以取消的load方法的懒加载机制。
总之,确定数据库有该对象时,使用load,不确定使用get

    <class name="User" table="user" lazy="false">
public static void testLoad() {
        Session session = HibernateSessionFactory.getSession();
        User user = (User) session.load(User.class, 2);
        if(user==null){
            System.out.println("没有该用户");
        }else {
            try {
                System.out.println(user.getId() + " "+ user.getName());
            } catch (Exception e) {
                System.out.println("使用load方法时返回一个代理对象,只有真正对该对象进行操作时才会到数据库中去查,"
                        + "因此,使用load时需要注意异常!");
            }
        }
    }
    public static void testGet() {
        Session session = HibernateSessionFactory.getSession();
        User user = (User) session.get(User.class, 2);
        if(user==null){
            System.out.println("没有该用户");
        }else {
            System.out.println(user.getId() + " "+ user.getName());
        }
    }

四、hibernate缓存机制

一、why(为什么要用Hibernate缓存?)
  1. Hibernate是一个持久层框架,经常访问物理数据库。
  2. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能。
  3. 缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。
二、what(Hibernate缓存原理是怎样的?)

1.Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存。

1.Hibernate一级缓存又称为“Session的缓存”。
Session内置不能被卸载,Session的缓存是事务范围的缓存(Session对象的生命 周期通常对应一个数据库事务或者一个应用事务)。
一级缓存中,持久化类的每个实例都具有唯一的OID。

2.Hibernate二级缓存又称为“SessionFactory的缓存”。
由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。

第二级缓存是可选的,是一个可配置的插件,默认下SessionFactory不会启用这个插件。Hibernate提供了org.hibernate.cache.CacheProvider接口,它充当缓存插件与Hibernate之间的适配器。

2.什么样的数据适合存放到第二级缓存中?   

1) 很少被修改的数据   
2) 不是很重要的数据,允许出现偶尔并发的数据   
3) 不会被并发访问的数据   
4) 常量数据   

3.不适合存放到第二级缓存的数据?   

1) 经常被修改的数据   
2) 绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发   
3) 与其他应用共享的数据。

4.Session的延迟加载实现要解决两个问题:正常关闭连接和确保请求中访问的是同一个session。

Hibernate session就是java.sql.Connection的一层高级封装,一个session对应了一个Connection。http请求结束后正确的关闭session(过滤器实现了session的正常关闭);延迟加载必须保证是同一个session(session绑定在ThreadLocal)。

5.Hibernate查找对象如何应用缓存?

当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;
查不到,如果配置了二级缓存,那么从二级缓存中查;
如果都查不到,再查询数据库,把结果按照ID放入到缓存删除、更新、增加数据的时候,同时更新缓存。

6.一级缓存与二级缓存的对比图。
hibernate-cache

三、how(Hibernate的缓存机制如何应用?)

1.一级缓存的管理:

  1. evit(Object obj)将指定的持久化对象从一级缓存中清除,释放对象所占用的内存资源,指定对象从持久化状态变为脱管状态,从而成为游离对象。
  2. clear() 将一级缓存中的所有持久化对象清除,释放其占用的内存资源。
  3. contains(Object obj) 判断指定的对象是否存在于一级缓存中。
  4. flush() 刷新一级缓存区的内容,使之与数据库数据保持同步。

2.一级缓存应用: save()。

当session对象调用save()方法保存一个对象后,该对象会被放入到session的缓存中。get()和load()。当session对象调用get()或load()方法从数据库取出一个对象后,该对象也会被放入到session的缓存中。 使用HQL和QBC等从数据库中查询数据。

public class Client
{
    public static void main(String[] args)
    {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction tx = null;
        try
        {
            /*开启一个事务*/
            tx = session.beginTransaction();
            /*从数据库中获取id="402881e534fa5a440134fa5a45340002"的Customer对象*/
            Customer customer1 = (Customer)session.get(Customer.class, "402881e534fa5a440134fa5a45340002");
            System.out.println("customer.getUsername is"+customer1.getUsername());
            /*事务提交*/
            tx.commit();

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

            /*开启一个新事务*/
            tx = session.beginTransaction();
            /*从数据库中获取id="402881e534fa5a440134fa5a45340002"的Customer对象*/
            Customer customer2 = (Customer)session.get(Customer.class, "402881e534fa5a440134fa5a45340002");
            System.out.println("customer2.getUsername is"+customer2.getUsername());
            /*事务提交*/
            tx.commit();

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

            /*比较两个get()方法获取的对象是否是同一个对象*/
            System.out.println("customer1 == customer2 result is "+(customer1==customer2));
        }
        catch (Exception e)
        {
            if(tx!=null)
            {
                tx.rollback();
            }
        }
        finally
        {
            session.close();
        }
    }
}
结果
Hibernate: 
    select
        customer0_.id as id0_0_,
        customer0_.username as username0_0_,
        customer0_.balance as balance0_0_ 
    from
        customer customer0_ 
    where
        customer0_.id=?
customer.getUsername islisi
-------------------------------------
customer2.getUsername islisi
-------------------------------------
customer1 == customer2 result is true

输出结果中只包含了一条SELECT SQL语句,而且customer1 == customer2 result is true说明两个取出来的对象是同一个对象。其原理是:第一次调用get()方法, Hibernate先检索缓存中是否有该查找对象,发现没有,Hibernate发送SELECT语句到数据库中取出相应的对象,然后将该对象放入缓存中,以便下次使用,第二次调用get()方法,Hibernate先检索缓存中是否有该查找对象,发现正好有该查找对象,就从缓存中取出来,不再去数据库中检索。

3.二级缓存的管理:
(1) evict(Class arg0, Serializable arg1)将某个类的指定ID的持久化对象从二级 缓存中清除,释放对象所占用的资源。
sessionFactory.evict(Customer.class, new Integer(1));
(2) evict(Class arg0) 将指定类的所有持久化对象从二级缓存中清除,释放其占用的
内存资源。
sessionFactory.evict(Customer.class);
(3) evictCollection(String arg0) 将指定类的所有持久化对象的指定集合从二级缓
存中清除,释放其占用的内存资源。
sessionFactory.evictCollection("Customer.orders");

4.二级缓存的配置
常用的二级缓存插件

  1. EHCache org.hibernate.cache.EhCacheProvider
  2. OSCache org.hibernate.cache.OSCacheProvider
  3. SwarmCahe org.hibernate.cache.SwarmCacheProvider
  4. JBossCache org.hibernate.cache.TreeCacheProvider
<!-- EHCache的配置,hibernate.cfg.xml --> 
<hibernate-configuration>
    <session-factory>
       <!-- 设置二级缓存插件EHCache的Provider类-->
       <property name="hibernate.cache.provider_class">
          org.hibernate.cache.EhCacheProvider
       </property>
       <!-- 启动"查询缓存" -->
       <property name="hibernate.cache.use_query_cache">
          true
       </property>
    </session-factory>
  </hibernate-configuration>
<!-- ehcache.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <!--
        缓存到硬盘的路径
    -->
    <diskStore path="d:/ehcache"></diskStore>
    <!--
        默认设置
        maxElementsInMemory : 在內存中最大緩存的对象数量。
        eternal : 缓存的对象是否永远不变。
        timeToIdleSeconds :可以操作对象的时间。
        timeToLiveSeconds :缓存中对象的生命周期,时间到后查询数据会从数据库中读取。
        overflowToDisk :内存满了,是否要缓存到硬盘。
    -->
    <defaultCache maxElementsInMemory="200" eternal="false" 
        timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></defaultCache>
    <!--
        指定缓存的对象。
        下面出现的的属性覆盖上面出现的,没出现的继承上面的。
    -->
    <cache name="com.suxiaolei.hibernate.pojos.Order" maxElementsInMemory="200" eternal="false" 
        timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></cache>
</ehcache>
<!-- *.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>
       <!-- 设置该持久化类的二级缓存并发访问策略 read-only read-write nonstrict-read-write transactional-->
       <cache usage="read-write"/>    
   </class>
</hibernate-mapping>
若存在一对多的关系,想要在在获取一方的时候将关联的多方缓存起来,需要在集合属性下添加<cache>子标签,这里需要将关联的对象的hbm文件中必须在存在<class>标签下也添加<cache>标签,不然Hibernate只会缓存OID。
<hibernate-mapping>
        <class name="com.suxiaolei.hibernate.pojos.Customer" table="customer">
            <!-- 主键设置 -->
            <id name="id" type="string">
                <column name="id"></column>
                <generator class="uuid"></generator>
            </id>
            <!-- 属性设置 -->
            <property name="username" column="username" type="string"></property>
            <property name="balance" column="balance" type="integer"></property>
            <set name="orders" inverse="true" cascade="all" lazy="false" fetch="join">
                <cache usage="read-only"/>
                <key column="customer_id" ></key>
                <one-to-many class="com.suxiaolei.hibernate.pojos.Order"/>
            </set>
        </class>
    </hibernate-mapping>

五、Hibernate各种保存方式的区别

save,persist,update,saveOrUpdte,merge,flush,lock等hibernate的保存方式,他们之间有很多不同,这里细说一下,以便区别。

(1)预备知识

在所有之前,说明一下,对于hibernate,它的对象有三种状态,transient、persistent、detached。
下边是常见的翻译办法:

1. transient:瞬态或者自由态
数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器回收,一般是new出来且与session没有关联的对象。
2. persistent:持久化状态
数据库中有数据与之对应,当前与session有关联,并且相关联的session没有关闭,事务没有提交;持久对象状态发生改变,在事务提交时会影响到数据库(hibernate能检测到)。
3. detached:脱管状态或者游离态
数据库中有数据与之对应,但当前没有session与之关联;脱管对象状态发生改变,hibernate不能检测到。

这里写图片描述

这里写图片描述

  1. 瞬时状态的实例可以通过调用save()、persist()或者saveOrUpdate()方法进行持久化

  2. 持久化实例可以通过调用delete()变成脱管状态。通过get()或load()方法得到的实例都是持久化状态的。

  3. 脱管状态的实例可以通过调用update()、saveOrUpdate()、lock()或者replicate()进行持久化。

  4. 游离或者自由状态下的实例可以通过调用merge()方法成为一个新的持久化实例

  5. save()和persist()将会引发SQL的INSERT,delete()会引发SQL的DELETE,
    而update()或merge()会引发SQL的UPDATE。对持久化(persistent)实例的修改在刷新提交的时候会被检测到,它也会引起SQLUPDATE。saveOrUpdate()或者replicate()会引发SQLINSERT或者UPDATE

(2)save 和update区别

把这一对放在第一位的原因是因为这一对是最常用的。
1. save的作用是把一个新的对象保存。
2. update是把一个脱管状态的对象保存

(3)update 和saveOrUpdate区别

这个是比较好理解的,顾名思义,saveOrUpdate基本上就是合成了save和update
引用hibernate reference中的一段话来解释他们的使用场合和区别:

通常下面的场景会使用update()或saveOrUpdate():
程序在第一个session中加载对象,该对象被传递到表现层,对象发生了一些改动,该对象被返回到业务逻辑层,程序调用第二个session的update()方法持久这些改动.

saveOrUpdate()做下面的事:
1. 如果对象已经在本session中持久化了,不做任何事;
2. 如果另一个与本session关联的对象拥有相同的持久化标识(identifier),抛出一个异常;
3. 如果对象没有持久化标识(identifier)属性,对其调用save();
4. 如果对象的持久标识(identifier)表明其是一个新实例化的对象,对其调用save();
5. 如果对象是附带版本信息的(通过或),并且版本属性的值表明其是一个新实例化的对象,save()它。否则update() 这个对象.

(4)persist和save区别

这个是最迷离的一对,表面上看起来使用哪个都行,在hibernate reference 文档中也没有明确的区分他们.这里给出一个明确的区分。(可以跟进src看一下,虽然实现步骤类似,但是还是有细微的差别)。这里参考http://opensource.atlassian.com/projects/hibernate/browse/HHH-1682中的一个说明:

I found that a lot of people have the same doubt. To help to solve this issue 
I'm quoting Christian Bauer: 
"In case anybody finds this thread... 

persist() is well defined. It makes a transient instance persistent. However, 
it doesn't guarantee that the identifier value will be assigned to the persistent 
instance immediately, the assignment might happen at flush time. The spec doesn't say 
that, which is the problem I have with persist(). 

persist() also guarantees that it will not execute an INSERT statement if it is 
called outside of transaction boundaries. This is useful in long-running conversations 
with an extended Session/persistence context.A method like persist() is required. 

save() does not guarantee the same, it returns an identifier, and if an INSERT 
has to be executed to get the identifier (e.g. "identity" generator, not "sequence"), 
this INSERT happens immediately, no matter if you are inside or outside of a transaction. This is not good in a long-running conversation with an extended Session/persistence context." 

简单翻译一下上边的句子的主要内容:
1,persist把一个瞬态的实例持久化,但是并”不保证”标识符被立刻填入到持久化实例中,标识符的填入可能被推迟到flush的时间。
2,persist”保证”,当它在一个transaction外部被调用的时候并不触发一个Sql Insert,这个功能是很有用的,当我们通过继承Session/persistence context来封装一个长会话流程的时候,一个persist这样的函数是需要的。
3,save”不保证”第2条,它要返回标识符,所以它会立即执行Sql insert,不管是不是在transaction内部还是外部

(5)saveOrUpdateCopy,merge和update区别

首先说明merge是用来代替saveOrUpdateCopy的,这个详细见这里
http://www.blogjava.net/dreamstone/archive/2007/07/28/133053.html
然后比较update和merge,update的作用上边说了,这里说一下merge的

  1. 如果session中存在相同持久化标识(identifier)的实例,用用户给出的对象的状态覆盖旧有的持久实例
  2. 如果session没有相应的持久实例,则尝试从数据库中加载,或创建新的持久化实例,最后返回该持久实例,用户给出的这个对象没有被关联到session上,它依旧是脱管的

    重点是最后一句:
    当我们使用update的时候,执行完成后,我们提供的对象A的状态变成持久化状态
    但当我们使用merge的时候,执行完成,我们提供的对象A还是脱管状态,hibernate或者new了一个B,或者检索到一个持久对象B,并把我们提供的对象A的所有的值拷贝到这个B,执行完成后B是持久状态,而我们提供的A还是托管状态

(6)flush和update区别

这两个的区别好理解,update操作的是在脱管状态的对象,而flush是操作的在持久状态的对象。默认情况下,一个持久状态的对象是不需要update的,只要你更改了对象的值,等待hibernate flush就自动保存到数据库了。hibernate flush 发生再几种情
况下:
1,调用某些查询的时候
2,transaction commit的时候
3,手动调用flush的时候

(7)lock和update区别

update是把一个已经更改过的脱管状态的对象变成持久状态
lock是把一个没有更改过的脱管状态的对象变成持久状态
对应更改一个记录的内容,两个的操作不同:
update的操作步骤是:
(1)更改脱管的对象->调用update
lock的操作步骤是:
(2)调用lock把对象从脱管状态变成持久状态–>更改持久状态的对象的内容–>等待flush或者手动flush

六、使用Query接口进行查询?

    Session session = SingleSessionFactory.getInstance().openSession();
    Transaction transaction = session.beginTransaction();
    Query query = (Query) session.createQuery("from User where useremail='long@163.com'"); //这里是domain类User,不是表名;要查询的列既可以为User类的属性名,也可以是表的列名
    //list方法获取结果,结果被封装成domain对象
    List<User> list = query.list();
    for(User user : list){
        System.out.println("user_name="+user.getUsername()+" user_email="+user.getUseremail());
    }
    transaction.commit();
    session.close();

(1)使用query查询时,如果只有一列数据,则返回list中是list< object > 而不是list< object[ ] >,因此需要注意;

(2)使用query查询时,采用参数绑定的好处:可读性高 、效果好 、防止SQL注入;

(3)参数绑定有两种形式

    Query q = session.createQuery("from user where name=:name and age=:age");
     (1) q.setParameter(参数名,值);
     (2) q.setString() , q.setInteger()

     另外:Query q = session.createQuery("from user where name=?and age=?");
    q.setString(1,"sss")  ,  q.setInteger(2,12)

七、Hibernate中的关系映射

1、hibernate中对象的三种关系

  1. one-to-one
  2. one-to-many (many-to-one)
  3. many-to-many
    并且还有单向和双向之分;举例:
    Student表和Course表就是多对多,多对多一般都会通过一个中间表转成one-to-many 或者many-to-one,比如我们使用一个中间表StudCourse把关系简化成one-to-many 和many-to-one

七、HQL(Hibernate Query Language)

1、HQL是什么

面向对象的查询语言,与SQL不同,HQL中的对象名是区分大小写的(除了Java类和 属性,其他部分不区分大小写);HQL中查的是对象而不是表,并且支持多态;HQL主 要通过Query来操作。Query的创建方式:
Query q = session.createQuery(hql);
1. from person
2. from User user where user.name = :name
3. from User user where user.name = :name and user.birthday < :birthday

2、HQL使用
    1、大小写敏感性问题
    除了Java类与属性的名称外,查询语句对大小写并不敏感

    2、from子句
    (1) from Cat
    (2) from Cat as cat ( From Cat cat )
    (3) from Student as stu,Course as course

    3、关联(Association)和连接(join)
    (1) inner join (内连接)
    (2) left outer join (左外连接)
    (3) right outer join (右外连接)
    (4) full join (全连接)

    4、select子句
    (1) 查询语句可以返回值为任何类型的属性,包括返回类型为某种组件的属性
     select  cat.name from Cat cat
    (2) 查询语句可以返回多个对象和属性,存放在object[]队列中
    select mother,offspr,mate.name
    from DomesticCat as mother 
        inner join mother.mate as mate
        left outer join mother.kittens as offspr
    (3) 或存放在一个List对象中
    select new list(mother,offspr,mate.name)
    from DomesticCat as mother 
        inner join mother.mate as mate
        left outer join mother.kittens as offspr
    (4) 也可以直接返回一个实际的类型安全的Java对象
    select new Family(mother,offspr,mate)
    from DomesticCat as mother 
        inner join mother.mate as mate
        left outer join mother.kittens as offspr

    5、聚集函数
    avg、sum、min、max、count(* or distinct or all)
    可以使用数学操作符、连接以及经过验证的的SQL函数

    6、多态查询
    from Cat as cat:不仅返回Cat类的实例,也同时返回子类DomesticCat的实例。Hibernate可以在from子句中指定任何Java类或接口。查询会返回继承了该类的所有持久化子类的实例或返回声明了该接口的所有的持久化类的实例。

    7、where子句
    (1) 如果没有指定别名,你可以使用属性名来直接引用属性;如果指派了别名,需要使用完整的属性名:
    from Cat as cat where cat.name="ABC"

    (2) =运算符不仅可以被用来比较属性的值,也可以用来比较实例:
    select cat,mat
    from Cat cat,Cat mate
    where cat.mate = mate

    (3) 特殊属性class在进行多态持久化的情况下呗用来存取一个实例的鉴别值。一个嵌入到where子句中的Java类的名字将被转换为该类的鉴别值。
    from Cat cat where cat.class = DomesticCat

    (4) 一个“任意”类型有两个特殊的属性id和class。
    from AuditLog log, Payment payment
    where log.item.class = "Payment" and log.item.id = payment.id

    8、表达式
    可以使用:数学运算符、二进制比较运算符、逻辑运算符、字符串函数、SQL标量函数、HQL函数、JDBC风格的参数传入?、命名参数:name、SQL直接常量、Java public static final类型的常量。

    9、Order by子句
    查询返回的列表可以按照一个返回的类或组件中的任何属性进行排序。

    10、group by子句
    一个返回聚集值的查询可以按照一个返回的类或组件中的任何属性进行分组。
    having子句也可以在这里使用。
    SQL的一般函数与聚集函数也可以出现在having与order by子句中。

    注意:group by子句与order by子句都不能包含算数表达式

    11、子查询
    子查询在圆括号内,支持嵌套

    12、批量处理(更新、插入、删除)
    使用Hibernate将10000条记录插入到数据库的一个很自然的做法可能是:
    Transaction tx = session.beginTransaction();
    for(i=0; i<10000; i++){
        Customer customer = new Customer(...);
        session.save(customer);
    }
    tx.commit();
    session.close();
    这段程序容易抛出异常(OutofMemoryException),这是因为Hibernate把所有新插入的数据再session进行了缓存。

    (1) JDBC批量专区参数设置:
    Hibernate.jdbc.batch_size 20
    Hibernate.cache.use_second_level_cache false

    (2) 批量插入
    如果要将批量对象持久化,你必须通过经常的调用flush()以及稍后调用clear()来控制第一季缓存的大小。
    Transaction tx = session.beginTransaction();
    for(i=0; i<10000; i++){
        Customer customer = new Customer(...);
        session.save(customer);
        if(i%20==0){
            //将本批插入的对象立即写入数据库并释放内存
            session.flush();
            session.clear();
        }
    }
   tx.commit();
   session.close(); 

   (3) 批量更新
   此方法同样适用于检索和更新数据。此外,在进行会返回很多行数据的查询时,你需要适用scroll()方法以便充分利用服务器端有表所带来的好处。
   Transaction tx = session.beginTransaction();
   ScrollableResults customers = session.getNamedQuery("GetCustomers")
            .setCacheMode(CacheMode.IGNORE)
            .scroll(ScrollMode.FORWARD_ONLY);
    int count = 0;
    while(customers.next()){
        Customer customer = (Customer)customers.get(0);
        customer.updateStuff(...);
        if(++count%20==0){
            session.flush();
            session.clear();
        }
    }
   tx.commit();
   session.close(); 

   13 防止SQL注入
    参数绑定有两种:
    1 query.setParameter();
    2 query.setInteger()、setString()、setLong()
    参数绑定的优点:可读性好、效果好、防止SQL注入
3 具体案例
/**
     * 查询所有属性
     */
    public static void query() {
        Session session = null;
        Transaction tx = null;
        try{
            session = HibernateSessionFactory.getSession();
            tx = session.beginTransaction();
            //在Hibernate中建议把所有属性查询出来,而不仅仅查部分属性,否则无法发挥Hibernate的优势
            ArrayList<Student> list = (ArrayList<Student>) session
                    .createQuery("from Student where sage between ? and ?")
                    .setInteger(0, 20).setInteger(1, 23)
                    .list();
            /*ArrayList<Student> list = (ArrayList<Student>) session
                    .createQuery("from Student where sage between :age1 and :age2")
                    .setInteger("age1", 20).setInteger("age2", 23)
                    .list();*/
            for(Student student : list){
                System.out.println(student.getSname()+"\t"+student.getSaddress()+"\t"+
                        student.getStudcourses().size());
            }
            tx.commit();
        }catch(Exception e){
            e.printStackTrace();
            if(tx!=null)
                tx.rollback();
        }finally{
            if(session!=null && session.isOpen()){
                session.close();
            }
        }
    }

    /**
     * 级联删除
     */
    public static void delete() {
        Session session = null;
        Transaction tx = null;
        try{
            session = HibernateSessionFactory.getSession();
            tx = session.beginTransaction();
            Student student = (Student) session.get(Student.class,20040001);
            session.delete(student);
            tx.commit();
        }catch(Exception e){
            e.printStackTrace();
            if(tx!=null)
                tx.rollback();
        }finally{
            if(session!=null && session.isOpen()){
                session.close();
            }
        }
    }

    /**
     * 懒加载问题
     */
    public static void queryLazy() {
        Session session = null;
        Transaction tx = null;
        try{
            session = HibernateSessionFactory.getSession();
            tx = session.beginTransaction();
            //在Hibernate中建议把所有属性查询出来,而不仅仅查部分属性,否则无法发挥Hibernate的优势
            ArrayList<Studcourse> list = (ArrayList<Studcourse>) session
                    .createQuery("from Studcourse where grade between ? and ?")
                    .setInteger(0, 0).setInteger(1, 100)
                    .list();

            for(Studcourse studcourse : list){
                System.out.println(studcourse.getStuCourseId()+"\t"+studcourse.getStudent().getSname()+"\t"+
                        studcourse.getCourse().getCname());
            }
            tx.commit();
        }catch(Exception e){
            e.printStackTrace();
            if(tx!=null)
                tx.rollback();
        }finally{
            if(session!=null && session.isOpen()){
                session.close();
            }
        }
    }

    /**
     * 查询部分属性
     */
    public static void query2() {
        Session session = null;
        Transaction tx = null;
        try{
            session = HibernateSessionFactory.getSession();
            tx = session.beginTransaction();
            //只查询部分属性无法封装成实体类,会形成object[]或object(只取一列数据时)
            //List list = (List) session.createQuery("select student.sname,course.cname,grade from Studcourse where grade<60").list();
            List list = (List) session.createQuery("select course.cname,count(*) from Studcourse where grade<60 group by course.cid").list();
            for(int i=0; i<list.size(); i++){
                Object [] objects = (Object[]) list.get(i);
                //System.out.println(objects[0].toString()+"\t"+objects[1].toString()+"\t"+objects[2].toString());
                System.out.println(objects[0].toString()+"\t"+objects[1].toString());
            }

            tx.commit();
        }catch(Exception e){
            e.printStackTrace();
            if(tx!=null)
                tx.rollback();
        }finally{
            if(session!=null && session.isOpen()){
                session.close();
            }
        }
    }

    /**
     * 分页查询
     */
    public static void query3() {
        Session session = null;
        Transaction tx = null;
        int pageSize = 3;
        int totalCount = 0;
        int pageCount = 0;
        try{
            session = HibernateSessionFactory.getSession();
            tx = session.beginTransaction();
            totalCount = Integer.parseInt(session.createQuery("select count(*) from Student").uniqueResult().toString());
            pageCount = (totalCount-1)/pageSize+1;
            for(int i=0;i<pageCount; i++){
                ArrayList<Student> list = (ArrayList<Student>) session
                        .createQuery("from Student order by sage")
                        .setFirstResult(i*pageSize).setMaxResults(pageSize)
                        .list();
                System.out.println("*********第"+(i+1)+"页**********");
                for(Student student : list){
                    System.out.println(student.getSname()+"\t"+student.getSage());
                }
            }

            tx.commit();
        }catch(Exception e){
            e.printStackTrace();
            if(tx!=null)
                tx.rollback();
        }finally{
            if(session!=null && session.isOpen()){
                session.close();
            }
        }
    }

    /**
     * 批量插入
     */
    public static void batchInsert() {
        Session session = null;
        Transaction tx = null;

        try{
            session = HibernateSessionFactory.getSession();
            tx = session.beginTransaction();
            for(int i=0; i<1000000; i++){
                Student student = new Student();
                student.setSid(20120001+i);
                student.setSname("xxx");
                student.setSdept("xxx");
                student.setSage(25);
                student.setSaddress("xxxxx");
                student.setSsex("F");
                session.save(student);
                if(i%20==0){
                    session.flush();
                    session.clear();
                }
            }
            tx.commit();
            System.out.println("成功插入记录");
        }catch(Exception e){
            e.printStackTrace();
            if(tx!=null)
                tx.rollback();
            System.out.println("插入记录失败,已回滚");
        }finally{
            if(session!=null && session.isOpen()){
                session.close();
            }
        }

    }

    /**
     * 查询批量数据的最后几条数据
     */
    public static void query4() {
        Session session = null;
        Transaction tx = null;
        int pageSize = 10;
        int pageCount = 10000;
        try{
            session = HibernateSessionFactory.getSession();
            tx = session.beginTransaction();

                ArrayList<Student> list = (ArrayList<Student>) session
                        .createQuery("from Student order by sid")
                        .setFirstResult(pageCount*pageSize).setMaxResults(pageSize)
                        .list();
                System.out.println("*********第"+(pageCount)+"页**********");
                for(Student student : list){
                    System.out.println(student.getSid()+"\t"+student.getSname());
                }

            tx.commit();
        }catch(Exception e){
            e.printStackTrace();
            if(tx!=null)
                tx.rollback();
        }finally{
            if(session!=null && session.isOpen()){
                session.close();
            }
        }
    }

    /**
     * 防止SQL注入,参数绑定有两种:
     * 1 query.setParameter();
     * 2 query.setInteger()\setString()\setLong()
     */
    public static void query5() {
        Session session = null;
        Transaction tx = null;
        try{
            session = HibernateSessionFactory.getSession();
            tx = session.beginTransaction();
            Query query = session.createQuery("from Student where sname=:name");
            query.setParameter("name", "xxx or 1=1 ");

            //在Hibernate中建议把所有属性查询出来,而不仅仅查部分属性,否则无法发挥Hibernate的优势
            ArrayList<Student> list = (ArrayList<Student>) query.list();
            /*ArrayList<Student> list = (ArrayList<Student>) session
                    .createQuery("from Student where sage between :age1 and :age2")
                    .setInteger("age1", 20).setInteger("age2", 23)
                    .list();*/
            for(Student student : list){
                System.out.println(student.getSname()+"\t"+student.getSaddress()+"\t"+
                        student.getStudcourses().size());
            }
            tx.commit();
        }catch(Exception e){
            e.printStackTrace();
            if(tx!=null)
                tx.rollback();
        }finally{
            if(session!=null && session.isOpen()){
                session.close();
            }
        }
    }

八、使用Criteria接口

1、Criteria是什么

Criteria是一种比HQL更面向对象的查询方式;Criteria的创建方式:
Criteria cri = session.createCriteria(DomainClass.class);
简单属性条件如:cri.add(Restrictions.eq(propertyName,value)),
cri.add(Order.asc(“id”));

public static void get() {
        Session session = HibernateSessionFactory.getSession();
        Criteria criteria = session.createCriteria(User.class);
        criteria.add(Property.forName("email").isNotNull())
                .addOrder(Order.asc("id")).setMaxResults(3);
        ArrayList<User> list = (ArrayList<User>) criteria.list();
        for(User user:list){
            System.out.println("id: "+user.getId()+" name: "+user.getName());
        }
    }
2、使用
    //1、基本使用
    List cats = session.createCriteria(Cat.class)
                .add(Restrictions.like("name","Cat%"))
                .add(Restrictions.between("weight",1,5))
                .list();
    //2、约束可以按逻辑分组            
    List cats = session.createCriteria(Cat.class)
                .add(Restrictions.like("name","Cat%"))
                .add(Restrictions.or(
                    Restrictions.eq("age",15),
                    Restrictions.isNull("age")
                ))
                .list();
    //3、直接使用SQL            
    List cats = session.createCriteria(Cat.class)
                .add(Restrictions.sqlRestriction("lower(cat.name) like lower(?)","Cat%",Hibernate.STRING))
                .list();
    //4、使用Property获得一个条件
    Property age = Property.forName("age");
    List cats = session.createCriteria(Cat.class)
                .add(Restrictions.like("name","Cat%"))
                .add(age.eq(15))
                .add(Property.forName("name").in(new Strings[]{"1","2"}))
                .list();
    //5、结果集排序
    List cats = session.createCriteria(Cat.class)
                .add(Restrictions.like("name","Cat%"))
                .addOrder(Order.asc("age"))
                .addOrder(Order.desc("id"))
                .setMaxResults(50)
                .list();
    //6、关联
    List cats = session.createCriteria(Cat.class)
                .add(Restrictions.like("name","Cat%"))
                .createCriteria("kittens")
                    .add(Rstrictions.like("name","kit%"))
                .list();
    //注意第二个createCriteria()返回一个新的Criteria实例,该实例引用kittens集合中的元素。

    List cats = session.createCriteria(Cat.class)
                .createAlias("kittens","kt")
                .createAlias("mate","mt")
                .add(Rstrictions.eqProperty("kt.name","mate.name"))
                .list();
    //createAlias并不创建一个新的Criteria实例

    List cats = session.createCriteria(Cat.class)
                .createCriteria("kittens","kt")
                    .add(Rstrictions.like("name","kit%"))
                .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
                .list();
    Iterator iter = cats.iterator();
    while( iter.hasNext() ){
        Map map = (Map)iter.next();
        Cat cat = (Cat)map.get(Criteria.ROOT_ALIAS);
        Cat kitten = (Cat)map.get("kt");
    }

    //7、动态关联抓取
    List cats = session.createCriteria(Cat.class)
                .add(Restrictions.like("name","Cat%"))
                .setFetchMode("mate",FetchMode.EAGER)
                .setFetchMode("kittens",FetchMode.EAGER)
                .list();

    //8、查询示例
    //org.hibernate.criterion.Example类允许你通过一个给定实例构建一个条件查询
    Cat cat = new Cat();
    cat.setSex("F");
    cat.setColor(Color.BLACK);
    List results = session.createCriteria(Cat.class)
                .add(Example.create(cat))
                .list();
    //版本属性、标识符和关联被忽略。默认情况下值为null的属性将被排除。

    Example example = Example.create(cat)
        .excludeZeroes()            //exclude zero valued properties
        .excludeProperty("color")   //exclude property named "color"
        .ignoreCase()               
        .enableLike();
    List results = session.createCriteria(Cat.class)
                   .add(example)
                   .list();

    List results = session.createCriteria(Cat.class)
                   .add(Example.create(cat))
                   .createCriteria("mate")
                     .add(Example.create(cat.getMate()))
                   .list();

   //9、投影、聚合和分组
    List cats = session.createCriteria(Cat.class)
                .setProjection(Projections.projectionList())
                .add(Restrictions.eq("color",Color.BLACK))
                .list();     //计算黑猫的数量

    List cats = session.createCriteria(Cat.class)
        .setProjection(Projections.projectionList()
            .add(Projections.rowCount())
            .add(Projections.avg("weight"))
            .add(Projections.max("weight"))
            .add(Projections.groupProperty("color"))
        )
        .list();//按颜色分组,计算数量、重量均值、最大值

    //把别名指派给一个投影,可以使投影值被约束或排序引用
    List cats = session.createCriteria(Cat.class)
        .setProjection(Projections.alias(Projections.groupProperty("color"),"colr"))
        .addOrder(Order.asc("colr"))
        .list(); 
    List cats = session.createCriteria(Cat.class)
        .setProjection(Projections.alias(Projections.groupProperty("color").as("colr"))
        .addOrder(Order.asc("colr"))
        .list();
     //别名   
    List cats = session.createCriteria(Cat.class)
        .setProjection(Projections.projectionList()
            .add(Projections.rowCount(),"count")
            .add(Projections.avg("weight"),"avg_w")
            .add(Projections.max("weight"),"max_w")
            .add(Projections.groupProperty("color"),"color")
        )
        .addOrder(Order.desc("count"))
        .addOrder(Order.asc("avg_w"))
        .list();

    //10、离线查询和子查询
    DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
            .add(Property.forName("sex").eq("F"));
    Transaction tr = session.beginTransaction();
    List results = query.getExecutableCriteria(session).setMaxResults(100)
                   .list();
    tr.commit();
    session.close();

    DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
            .add(Property.forName("weight").avg());
    List results = session.createCriteria(Cat.class)
                  .add(Property.forName("weight").gt(avgWeight))
                  .list();

九、对象的三种状态

1、transient(瞬态):数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器回收,一般是new出来与session无关联的对象
2、持久(persistent):数据库中有数据与之对应,当前对象与session有关联,并且相关联的session没有关闭,事务没有提交;持久对象状态发生改变,在事务提交时会影响数据库(hibernate能检测到。)
3、脱管/游离(detached):数据库中有数据与之对应,但当前对象与session无关联,脱管对象状态发生变化时,Hibernate不能检测到。

十、多对一

在处理多对一关系时,常常出现懒加载问题,下面举例说明:
部门Dept和员工Employee之间是多对一的关系:

    class Dept{
        private Integer id;
        private String name;
    }
    class Employee{
        private Integer id;
        private String name;
        private Dept dept;
    }

    void query(){
        Session session = HibernateUtil.getSession();
        Transaction transaction = session.beginTransaction();
        Employee emp = session.get(Employee.class,1);
        System.out.println(emp.getName()+"\t"+emp.getDept().getName());
    }

在上述query()中会抛出异常,因为Hibernate懒加载机制。因此为了解决这种情况可以采取以下几种方法:
(1) 显示的初始化Hibernate initialize(代理对象)

    Employee emp = session.get(Employee.class,1);
    Hibernate.initialize(emp.getDept());
    System.out.println(emp.getName()+"\t"+emp.getDept().getName());

(2) 修改对象关系文件Dept.hbm.xml 中Dept类属性中设置 lazy=false

    <class name="Dept" lazy="false">
    ... ...
    </class>

(3) 通过过滤器openSessionInView (???),只在web项目中使用,后续会讲解到。

十一、一对多

员工Employee和部门Dept之间是一对多的关系:

    class Dept{
        private Integer id;
        private String name;
        private Set<Employee> employees;
    }
    class Employee{
        private Integer id;
        private String name;
    }

    void query(){
        Session session = HibernateUtil.getSession();
        Transaction transaction = session.beginTransaction();
        Dept dept = session.get(Dept.class,1);
        Set<Employee> empls = dept.getEmployees();
        for(Employee e:empls){
            System.out.println(e.getName());
        }

    }
    Dept.hbm.xml:
    <set name="集合对象属性名employees">
        <key column="外键名id 即dept_id"/>
        <one-to-many class="放入的集合的类全路径com.xxx.Employee"/>
    </set>

十二、多对多

学生和课程之间是多对多关系:

<!--studcourse.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.demo.domain.Studcourse" table="studcourse" catalog="student_course">
        <id name="stuCourseId" type="java.lang.Integer">
            <column name="stuCourseId" />
            <generator class="identity" />
        </id>
        <many-to-one name="student" class="com.demo.domain.Student" fetch="select">
            <column name="sid" />
        </many-to-one>
        <many-to-one name="course" class="com.demo.domain.Course" fetch="select">
            <column name="cid" />
        </many-to-one>
        <property name="grade" type="java.lang.Integer">
            <column name="grade" not-null="true" />
        </property>
    </class>
</hibernate-mapping>
<!--student.hbm.xml,course.hbm.xml和student配置类似-->
<?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.demo.domain.Student" table="student" catalog="student_course">
        <id name="sid" type="java.lang.Integer">
            <column name="sid" />
            <generator class="assigned" />
        </id>
        <property name="sname" type="java.lang.String">
            <column name="sname" length="45" not-null="true" />
        </property>
        <property name="ssex" type="java.lang.String">
            <column name="ssex" length="2" not-null="true" />
        </property>
        <property name="sdept" type="java.lang.String">
            <column name="sdept" length="10" not-null="true" />
        </property>
        <property name="sage" type="java.lang.Integer">
            <column name="sage" />
        </property>
        <property name="saddress" type="java.lang.String">
            <column name="saddress" length="45" />
        </property>
        <set name="studcourses" inverse="true">
            <key>
                <column name="sid" />
            </key>
            <one-to-many class="com.demo.domain.Studcourse" />
        </set>
    </class>
</hibernate-mapping>

十三、级联(Cascade)

所谓级联,就是比如:Department<<<—->>>Employee对象关系,当删除一个department时,hibernate自动删除该部门所有的Employee。Cascade用来说明当对主对象进行某种操作时是否对其关联的从对象也做类似的操作。

  1. 常用的Cascade:none,all,save-update,delete,lock,refresh,evict,repicate,persist,merge,delete-orphan(one-to-many).一般对many-to-one,many-to-many不设置级联,在one-to-one,one-to-many中设置级联。
  2. 在集合属性和普通属性中都能使用Cascade
  3. 一般讲Cascade配置在one-to-many(one的一方,比如Employee-Department),和one-to-one(主对象一方)。

举例:删除某个学生,则需级联删除该生的成绩信息

<!--student.hbm.xml-->
    <set name="studcourses" inverse="true" cascade="delete">
        <key>
            <column name="sid" />
        </key>
         <one-to-many class="com.demo.domain.Studcourse" />
    </set>
    public static void delete() {
        Session session = null;
        Transaction tx = null;
        try{
            session = HibernateSessionFactory.getSession();
            tx = session.beginTransaction();
            Student student = (Student) session.get(Student.class,20040001);
            session.delete(student);
            tx.commit();
        }catch(Exception e){
            e.printStackTrace();
            if(tx!=null)
                tx.rollback();
        }finally{
            if(session!=null && session.isOpen()){
                session.close();
            }
        }
    }

十四、懒加载

Domain Object是非final的,才能实现懒加载。解决懒加载的方法有:

  1. 明确初始化。在session还没关闭时,访问一次xxx.getXXX(),强制访问数据库。或者使用Hibernate.initialize(xxx).
  2. openSessionView.这个往往需要过滤器配合使用(Web程序)
  3. 在ssh中,可以实现在service层,标注方式解决懒加载。
  4. 在对象映射文件中配置,lazy=”false”

十五、缓存

一级缓存是Session级共享的,一级缓存不需要配置即可使用,一级缓存没有保护机制。session关闭后,一级缓存自动销毁。一级缓存不能控制缓存的数量,所以在进行大批量操作数据时可能造成内存溢出,可以用evict、clear、flush方法及时清除缓存中的内容。

注意:query.list(),query.uniqueResult()不会从一级缓存中取数据,但是会把查询的数据放入一级缓存中。

二级缓存是SessionFactory级共享的。二级缓存需要通过配置(第三方插件)才可以使用。

十六、主键生成策略

1、hibernate对象标识符OID

hibernate中的持久化对象对应数据库中的一张数据表,因此区分不同的持久化对象,在hibernate中是通过OID来完成的,从表的角度看,OID对应表的主键,从类的角度看OID对应类的主键属性。

2、hibernate中的主键生成策略

1 、increment
由hibernate自动以递增方式生成标识符,每次增量为1.OID必须为数值类型,如long,int,short等
优点:不依赖底层数据库系统,适用于所有数据库系统。
缺点:适用于单进程环境下,在集群(多进程)下可能生成相同的主键值。
2、identity
由底层数据库生成标识符。
前提:数据库支持自动增长字段类型,OID必须为数值类型,如long、int、short等
支持的数据库有:DB2、MySQL、SQL Server、Sybase、HypersonicSQL。
3、sequence
依赖于底层数据库系统的序列
前提:需要数据库支持序列机制,而且OID必须为数值类型。
支持的数据库有:oracle、DB2、SAP DB、PostgreSQL、Mckoi等
4、native
native生成器能根据底层数据库系统的类型,自动选择合适的标识符生成器,因此非常适用于跨数据库平台开发,它会有hibernate根据数据库适配器中的定义,自动采用identity、Hilo、sequence的其中一种作为主键生成方式,但是OID必须为数值类型。
5、Hilo
Hilo标识符生成器有hibernate按照一种高低位算法生成标识符,他从数据库中的特定表(my_hi_value)的字段(next_value)中获取high值,因此需要额外的数据库表保存主键生成的历史状态,Hilo生成方法不依赖与底层数据库,因此适用于每种数据库,但是OID必须为数值类型。
·xml
<id name="id" type="java.lang.Integer" clolumn="ID">
<generator class="hilo">
<param name="table">my_hi_value</param>
<param name="column">next_value</param>
</generator>
</id>

6、seqhilo
使用一个高低位算法来高效生成数值类型的标识符,需要给定一个数据库序列的名字
xml
<id name="id" type="java.lang.Integer" clolumn="ID">
<generator class="hilo">
<param name="sequence">my_seq</param>
<param name="max_lo">100</param>
</generator>
</id>

7、UUID
由hibernate基于128-bit的UUID算法产生字符串类型标识符,该算法会根据当前设备的IP、JVM启动时间、系统时间、内部自增量等4个参数生成16禁止数值作为主键,一般而言,利用UUID方式生成的主键提供最好的数据插入性能和数据库平台适应性。
8、assigned
由应用程序指定主键标识符,OID类型没有限制。
9、foreign
在one-to-one的关系中,有另一张表的主键来决定自己的主键。
10、guid
在MySQL和SQL Server中使用数据库生成的GUID字串。
11、select、sequence-identity

十七、hibernate不适合的场景

  • 不适合OLAP(On-Line Analytical Processing联机分析处理),以查询分析数据为主的系统;
  • 适合OLTP(On-Line Transaction Processing联机事务处理)
  • 对于关系模型设计不合理的老系统,也不能发挥hibernate的优势
  • 数据量巨大,性能要求苛刻的系统,hibernate也很难达到要求,批量操作数据的效率也不高。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值