Hibernate技术

Hibernate中3个重要的类:
配置类(configuration)
负责管理Hibernate的配置信息,包含数据库连接URL、数据库用户、数据库密麻麻、数据库驱动等。
会话工厂类(SessionFactory)
保存当前数据库所有映射关系。
会话类(Session)
对数据库的操作,实现数据库数据增、删、改、查。该类不是 线程安全。

Hibernate配置文件
    配置文件数据库链接的文件有两:Hibernate.properties或Hibernate.cfg.xml
Hibernate.properties文件内容
#数据库驱动
hibernate.connection.driver_class = oracle.jdbc.driver.OracleDriver
#数据库连接的URL
hibernate.connection.url = jdbc:oracle:thin:@127.0.0.1:1521:orcl
#用户名
hibernate.connection.username = system
#密码
hibernate.connection.password = guoyanan
#是否显示SQL语句
hibernate.show_sql=true
#是否自动生存数据表
#hibernate.hbm2ddl.auto=true
#是否格式化SQL
hibernate.format_sql=true
#Hibernate方言
hibernate.dialect = org.hibernate.dialect.OracleDialect 
Hibernate.cfg.xml文件内容
<!-- 数据库驱动 -->
     <property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
     <!-- URL -->
     <property name="connection.url">jdbc:oracle:thin:@127.0.0.1:1521:orcl</property>
     <!-- 数据库登录帐号 -->
     <property name="connection.username">system</property>
     <!-- 数据库登录密码 -->
     <property name="connection.password">guoyanan</property>
     <!-- 数据库方言 -->
     <property name="dialect">org.hibernate.dialect.OracleDialect</property>
数据库方言不同的数据库是不一样的
数据库    方言
Oracle(通用的)    org.hibernate.dialect.OracleDialect
SQLServer    org.hibernate.dialect.SQLServerDialect
MySQL    org.hibernate.dialect.MySQLDialect

编写持久化类
public class Tb_User {
    private int id;
    private String username;
    private String password;
    
    //无参构造函数
    public  Tb_User(){
        
    }
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
…
}        

持久化类必须符合PoJO规范。
Hibernate持久化类中必须符合4条规则:
1、    必须实现一个无参的构造函数,方便Hibernate通过Constructor.nerInstance实例持久化。
2、    提供一个标识属性,就是数据库表中的主键。
3、    使用非final类,如果使用了final类,Hibernate不能使用代理延迟加载。
4、    为属性声明访问器,也就是创建公共的set和get方法。

Hibernate映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 <!-- 员工信息字段配置信息 -->
 <hibernate-mapping>
     <class name="com.hibernate.table.Tb_User" table="tb_user">
         <!-- id值 -->
         <id name="id" column="id" type="int">
            <generator class="native"/>
        </id>
        <!-- 帐号 -->
        <property name="username" type="string" length="20">
            <column name="username"/>
        </property>
        <!-- 密码 -->
        <property name="password" type="string" lenght=20>
        <column name="password"></column>
        </property>
     </class>
 </hibernate-mapping>

<property>元素常用的属性
属性名称    说明
Name    持久化类属性的名称,以小写字母开头
Column    数据库字段名称
Type    数据库字段类型
Length    数据库字段长度
Not-null    设置字段是否可以为空,bool类型
Unique    字段是否唯一,bool类型
Lazy    是否延迟抓取,bool类型

Hibernate主键策略
<generator>元素是java类名设置主键的策略。
属性名称    说明
increment    用于为long、short或int类型生成唯一标识,在集群下不可以使用
indentity    由底层数据库生存主键
sequence    有底层序列生存主键
hilo    根据高/低算法生存,把特定表的字段作为高位值来源,在默认情况下选用hibernate_unique_key表的next_hi字段
native    根据底层数据库对自动生成标识符的支持能力选择identity、sequence、hilo。
assigned    由程序负责主键生存,此时持久化类的唯一标识不能声明为private类型
select    通过数据库触发生存主键
foreign    使用另一个相关的对象的标识符,通常和<one-to-one>一起使用

Hibernate的Generator属性有7种class,本文简略描述了这7种class的意义和用法。
1identity:用于MySql数据库。特点:递增 
1.    < id name="id" column="id">   
2.        < generator class="identity"/>   
3.    < /id> 
注:对于MySql数据库使用递增序列时需要在建表时对主键指定为auto_increment属性。 
2、sequence:用于Oracle数据库 
1.    < id name="id" column="id">   
2.       < generator class="sequence">   
3.         < param name="sequence">序列名< /param>   
4.       < /generator>   
5.    < /id> 
3、native:跨数据库时使用,由底层方言产生。 
Default.sequence为hibernate_sequence 
1.    < id name="id" column="id">   
2.       < generator class="native"/>   
3.    < /id>   
注:使用native时Hibernate默认会去查找Oracle中的hibernate_sequence序列。 
如果Oracle中没有该序列,连Oracle数据库时会报错。 
4、hilo:通过高低位合成id,先建表hi_value,再建列next_value。必须要有初始值。 
1.    < id name="id" column="id">   
2.        < generator class="hilo">   
3.          < param name="table">high_val< /param>   
4.           < param name="column">nextval< /param>   
5.          < param name="max_lo">5< /param>   
6.        < /generator>   
7.    < /id>  
5、sequencehilo:同过高低位合成id,建一个sequence序列,不用建表。 
1.    < id name="id" column="id">   
2.    < generator class="hilo">   
3.    < param name="sequence">high_val_seq< /param>   
4.    < param name="max_lo">5< /param>   
5.    < /generator>   
6.    < /id> 
6、assigned:用户自定义id; 
1.    < id name="id" column="id">   
2.    < generator class="assigned"/>   
3.    < /id> 
7foreign:用于一对一关系共享主健时,两id值一样。



Hibernate实例状态
1、    瞬时状态(transient)
通过new关键字创建的java对象,没有被Hibernate Session管理,当没有变量引用是就有JVM自动回收。
2、    持久化状态(Persistent)
对象与数据库中的数据关联,并与Session会话和Transaction事务关联在一起,当持久化状态对象发生改变时并不会立即更新数据库,只有当事务结束时,才会更新数据库。当持久化状态变为脱管状态后,就不在Hibernate持久化成管理范围内。
3、    脱管状态(Detached)
当持久化对象的Session关闭后,对象就进入了脱管状态。当脱管对象,有被Session重新管理,就进入到了持久化状态。

Hibernate持久类
package ssh.gx;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistryBuilder;



public class HibernateUtil {
    private static final ThreadLocal<Session> threadLocal = new ThreadLocal <Session>(); 
private static SessionFactory sessionFactory = null;        //SessionFactory对象
//静态块
static {
        try {
            Configuration cfg = new Configuration().configure();        //加载Hibernate配置文件
            sessionFactory = cfg.buildSessionFactory(new ServiceRegistryBuilder().buildServiceRegistry());
        } catch (Exception e) {
            System.err.println("创建会话工厂失败");
            e.printStackTrace();
        }
    }
    /**
     * 获取Session
     *  @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;
    }
    /**
     * 重建会话工厂
     */
    public static void rebuildSessionFactory() {
        try {
            Configuration cfg = new Configuration().configure();        //加载Hibernate配置文件
            sessionFactory = cfg.buildSessionFactory(new ServiceRegistryBuilder().buildServiceRegistry());
        } catch (Exception e) {
            System.err.println("创建会话工厂失败");
            e.printStackTrace();
        }
    }
    /**
     * 获取SessionFactory对象
     * @return SessionFactory对象
     */
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
    /** 
     * 关闭Session
     *  @throws HibernateException
     */
    public static void closeSession() throws HibernateException {
        Session session = (Session) threadLocal.get();
        threadLocal.set(null);
        if (session != null) {
            session.close();                            //关闭Session
        }
    }
}

SessionFactory是重量级的对象,创建需要耗费大量的资源,所以,在static块中创建,程序运行过程中就只创建一次。

添加数据
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        
        //声明Session
        Session session=null;
        //设置内容类型,防止中文乱码
        response.setContentType("text/html;charset=utf-8");
        
        //实例化Tb_User类
        Tb_User user=new Tb_User();
        //添加数据信息
        user.setUsername("xiaoming");
        user.setPassword("123xiao");
        
        //Hibernate持久化
        try{
            //获取session
            session=HibernateUtil.getSession();
            //开启事务
            session.beginTransaction();
            //执行数据操作
            session.save(user);
            //事务提交
            session.getTransaction().commit();
            
            //获取out对象
            PrintWriter out =response.getWriter();
            out.println("<script>alert('ok');</script>");
            
        }catch(Exception e){
            System.out.println("数据持久化失败");
            e.printStackTrace();
        }
        doGet(request, response);
    }

查询数据
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        //声明Session
        Session session=null;
        //防止中文乱码
        response.setContentType("text/html;charset=utf-8");
        try{
            //获取session
            session=HibernateUtil.getSession();
            //装载对象
            Tb_User user=(Tb_User)session.get(Tb_User.class, new Integer("6"));
            
            //输出查询结果
            System.out.println(user.getId());
            System.out.println(user.getUsername());
            System.out.println(user.getPassword());
            
        }catch(Exception e){
            System.out.println("查询对象失败");
            e.printStackTrace();
        }
        
        
        
        doGet(request, response);
    }

修改或删除数据
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        //声明session
        Session session=null;
        //设置响应头信息,防止中文乱码
        response.setContentType("text/html;charset=utf-8");
        
        try{
            //获取session对象
            session=HibernateUtil.getSession();
            //装载对象
            Tb_User user=(Tb_User)session.get(Tb_User.class, new Integer("6"));
            
            //修改对象部分
            user.setUsername("小明");
            user.setPassword("ewq123");
            //强制刷新提交
            session.flush();
            //提交事务
            session.getTransaction().commit();
            
            /*
            //对象删除
            session.delete(user);
            //强制刷新提交
            session.flush();
            //提交事务
            session.getTransaction().commit();
            */
            
        }catch(Exception e){
            System.out.println("失败");
            e.printStackTrace();
        }
        
        
        
        doGet(request, response);
    }

延迟加载
    Hibernate调用代理的某个方法来访问数据库。非延迟加载,Hibernate直接访问数据库。
延迟加载的策略
    当被加载的对象,长时间没有被调用,JVM(垃圾回收器)自动回收资源。

配置和使用二级缓存
    Hibernate的二级缓存由从属于一个SessionFactory的所有Session对象共享,当程序使用Session加载持久化对象时,Session首先会根据加载的数据类和唯一标识在缓存中查找是否存在此对象的缓存实例。如果存在,将其作为结果返回;否则继续在二级缓存中查找。如果无匹配对象,Hibernate将直接访问数据库。
Hibernate.cfg.xml配置开启二级缓存
<!-- 开启二级缓存 -->
     <property name="hibernate.cache.use_second_level_cache">true</property>
     <!-- 指定缓存产品提供商 -->
     <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
     
持久化类的映射文件指定缓存的同步策略:
<hibernate-mapping>
     <class name="com.hibernate.table.Tb_User" table="tb_user">
     <!-- 指定的缓存的同步策略 -->
     <cache usage="read-only"></cache>
…

添加缓存配置文件ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <diskStore path="java.io.tmpdir"/>
    <!-- maxElementsInMemory缓存允许保存的最大数据库实例量;
    eternal缓存中的数据是否为常量;timeToIdleSeconds缓存钝化时间;
    timeToLiveSeconds缓存数据生存时间;overflowToDisk设置是否启用磁盘缓存。 -->
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />
</ehcache>

Servlet测试二级缓存
Session session=null;
        Session session2=null;
        //防止中文乱码
        response.setContentType("text/html;charset=utf-8");
        try{
            //获取session
            session=HibernateUtil.getSession();
            session2=HibernateUtil.getSession();
            //装载对象
            Tb_User user=(Tb_User)session.get(Tb_User.class, new Integer("6"));
            Tb_User user2=(Tb_User)session2.get(Tb_User.class, new Integer("6"));
            //输出查询结果
            System.out.println(user.getId());
            System.out.println(user.getUsername());
            System.out.println(user.getPassword());
            
        }catch(Exception e){
            System.out.println("查询对象失败");
            e.printStackTrace();
        }finally{
            HibernateUtil.closeSession();
        }

在输出控制台输出:可以看到,程序只执行了一次sql语句。第二次session2执行查询的是二级缓存。
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Hibernate: 
    select
        tb_user0_.id as id0_0_,
        tb_user0_.username as username0_0_,
        tb_user0_.password as password0_0_ 
    from
        tb_user tb_user0_ 
    where
        tb_user0_.id=?
6
xiaoming
123xiao

二级缓存是常用与数据更新频率低且系统频繁使用的非关键数据,以防止用户频繁访问数据库而过度消耗系统资源。

实体关联关系映射
实体类的关系属性必须以关联类声明。
private Tb_User userid;
xml属性
        <!-- 银行卡号与用户多对一关系 -->
        <many-to-one name="userid" class="com.hibernate.table.Tb_User">
        <column name="userid"></column>
        </many-to-one>

Servlet使用
//声明Session
        Session session=null;
        
        //防止中文乱码
        response.setContentType("text/html;charset=utf-8");
        try{
            //获取session
            session=HibernateUtil.getSession();
            
            //装载对象
            Tb_Card card=(Tb_Card)session.get(Tb_Card.class, new Integer("999"));
            
            //输出查询结果
            System.out.println(card.getCardid());
            System.out.println(card.getUserid().getBankcard());
        }catch(Exception e){
            System.out.println("查询对象失败");
            e.printStackTrace();
        }finally{
            HibernateUtil.closeSession();
        }

配置多对一的双向关联
持久类里添加如下代码
public class Tb_User {
    private int id;
    private String username;
    private String password;
    private String bankcard;
    private Set<Tb_Card> tbcard;
…省略set和get

配置文件内容
<!-- 定义一对多 inverse属性用于控制反向反转 -->
        <set name="tbcard" inverse="true">
        <!-- 设置多对一的实体字段 -->
        <key column="userid"></key>
        <one-to-many class="com.hibernate.table.Tb_Card"/>
        </set>

Servlet执行代码
//声明Session
        Session session=null;
        
        //防止中文乱码
        response.setContentType("text/html;charset=utf-8");
        try{
            //获取session
            session=HibernateUtil.getSession();
            //装载对象
            Tb_User user=(Tb_User)session.get(Tb_User.class, new Integer("6"));
            //输出查询结果
            System.out.println(user.getUsername());
            //获取Tb_Card的集合
            Set<Tb_Card> cards=user.getTbcard();
            //通过迭代器输出信息
            for(Iterator<Tb_Card> it=cards.iterator();it.hasNext();){
                //获取产品信息
                Tb_Card card=(Tb_Card)it.next();
                //输出信息
                System.out.println(card.getCardid());
            }
        }catch(Exception e){
            System.out.println("查询对象失败");
            e.printStackTrace();
        }finally{
            HibernateUtil.closeSession();
        }

配置一对一的主键关联

主控实体类
public class Tb_User {
    private int id;
    private String username;
    private String password;
    private String bankcard;
    //数据库对应的表中并没有cardnoid字段,此字段为实体类需要实现一对一关系而添加
    private tbCardNo cardnoid;
…省略set和get方法

被控实体类
public class tbCardNo {
    private int id;
    private String CardNo;
…省略set和get

主控配置文件
        <!-- 一对一的属性 cascade级联操作属性-->
        <one-to-one name="cardnoid" class="com.hibernate.table.tbCardNo"    cascade="delete"></one-to-one>
        

被控配置文件
<hibernate-mapping>
    <class name="com.hibernate.table.tbCardNo" table="tb_CardNo">
        <id name="id" column="id" type="int">
            <generator class="foreign">
            <param name="property">user</param>
            </generator>
        </id>
…

执行代码
//声明Session
        Session session=null;
        
        //防止中文乱码
        response.setContentType("text/html;charset=utf-8");
        try{
            //获取session
            session=HibernateUtil.getSession();
            session.beginTransaction();
            //装载对象
            Tb_User user=(Tb_User)session.get(Tb_User.class, new Integer("7"));
            
            session.delete(user);
            session.getTransaction().commit();
            
        }catch(Exception e){
            System.out.println("查询对象失败");
            e.printStackTrace();
        }finally{
            HibernateUtil.closeSession();
        }

级联操作
主要是由cascade属性控制,cascade属性属于关系标签<one-to-one><many-to-one><one-to-many>。
参数    说明
All    所以情况下均采用级联操作
None    默认,所有情况下均不采用级联操作
Save-update    在执行save-update方法时执行级联操作
Delete    在执行delete方法时执行级联操作

HQL语句
HQL语句与SQL语句是十分相似的。一般的Sql语句可以执行的都可以使用HQL语句实现查询。
区别:HQL是通过实体查询,SQL是在数据库里查询表。
HQL语句可以通过from子句查询实体。
From Tb_User;

HQL语句查询实例
//声明Session
        Session session=null;
        
        //防止中文乱码
        response.setContentType("text/html;charset=utf-8");
        try{
            //获取session
            session=HibernateUtil.getSession();
            //编辑Hql查询语句
            String hql="select user.id,user.username,user.password from Tb_User user where user.username=? and user.password=:password";
            //使用sql语句查询
            String sql="select t.id,t.username,t.password from tb_user t where t.username=? and t.password=:password";
            Query q=session.createQuery(hql);
            //使用sql语句查询
            Query sq=session.createSQLQuery(sql);
            
            //使用站位符“?”替代参数
            q.setParameter(0, "qwe");
            //使用站位符“:parameter”替代变量
            q.setParameter("password", "123");
            
            //使用站位符“?”替代参数
            sq.setParameter(0, "qwe");
            //使用站位符“:parameter”替代变量
            sq.setParameter("password", "123");
            //查询的是一个实体对象,也就是如Tb_User可以使用以下代码
            List<Tb_User> list=new ArrayList<Tb_User>();
            //查询多个实体对象时使用以下代码,使用Object对象数组
            List<Object[]> lists=new ArrayList<Object[]>();
            
            //lists=sq.list();使用sql查询获取list结果
             lists=q.list();
             //使用迭代器
             Iterator it =lists.iterator();
             while(it.hasNext()){
                 Object[] oj=(Object[])it.next();
                 
                 System.out.println(oj[0].toString());
                 System.out.println(oj[1].toString());
                 System.out.println(oj[2].toString());
                 
             }
             
            
        }catch(Exception e){
            System.out.println("查询对象失败");
            e.printStackTrace();
        }finally{
            HibernateUtil.closeSession();
        }

Hql语句可以使用排序查询、聚会函数查询、分组查询、联合查询与SQL语句使用是一样的,就不介绍了。
通过Hibernate执行sql语句查询,只需要将上面的代码里session.createQuery(hql)替换为session.createSQLQuery(sql)。
原文地址:https://www.cnblogs.com/gynbk/p/6556254.html