【Hibernate】(3)Hibernate的缓存机制

一、缓存技术

1、关于缓存:

缓存是介于物理数据源与应用程序之间,缓存被广泛用于数据库应用领域。缓存的设计就是为了通过存储已经从数据库读取的数据来减少应用程序和数据库之间的数据流量,而数据库的访问只在检索的数据不在当前缓存的时候才需要。

2、为什么用缓存:

Hibernate是一个持久层框架,经常访问物理数据库。为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能。

缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。

3、hibernate缓存原理是什么:

Hibernate 中提供了两级Cache。

/第1级别的缓存是Session级别的缓存/,
(1)即上述事务范围以及应用范围的缓存。这一级别的缓存由Hibernate管理的,一般无需进行干预;缓存的物理介质为内存,由于内存容量有限,必须通过恰当的检索策略和检索方式来限制加载对象的数目。
(2)Session内置不能被卸载,Session的缓存是事务范围的缓存(Session对象的生命周期通常对应一个数据库事务或者一个应用事务)。一级缓存中,持久化类的每个实例都具有唯一的OID。

/第2级别的缓存是SessionFactory级别的缓存/,
(1)属于进程范围或群集范围的缓存。这一级别的缓存可以进行配置和更改,并且可以动态加载和卸载。 第2级缓存的物理介质可以是内存和硬盘,因此第2级缓存可以存放大量的数据,数据过期策略的maxElementsInMemory属性值可以控制内存中的对象数目。
(2)由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。
(3)第二级缓存是可选的,是一个可配置的插件,默认下SessionFactory不会启用这个插件。Hibernate提供了org.hibernate.cache.CacheProvider接口,它充当缓存插件与Hibernate之间的适配器。Hibernate本身并不提供2级缓存的产品化实现,而是为众多支持Hibernate的第三方缓存组件提供整合接口。

4、关于二级缓存中放置的数据:

什么样的数据适合存放到第二级缓存中?   
1) 很少被修改的数据   
2) 不是很重要的数据,允许出现偶尔并发的数据   
3) 不会被并发访问的数据   
4) 常量数据   

不适合存放到第二级缓存的数据?   
1) 经常被修改的数据   
2) 绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发 
3) 与其他应用共享的数据。

5、Hibernate查找对象如何应用缓存:

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

6、一级缓存与二级缓存的对比图:

一级缓存

二级缓存

存放数据的形式

相互关联的持久化对象

对象的散装数据

缓存的范围

事务范围,每个事务都拥有单独的一级缓存

进程范围或集群范围,缓存被同一个进程或集群范围内所有事务共享

并发访问策略

由于每个事务都拥有单独的一级缓存不会出现并发问题,因此无须提供并发访问策略

由于多个事务会同时访问二级缓存中的相同数据,因此必须提供适当的并发访问策略,来保证特定的事务隔离级别

数据过期策略

处于一级缓存中的对象永远不会过期,除非应用程序显示清空或者清空特定对象

必须提供数据过期策略,如基于内存的缓存中对象的最大数目,允许对象处于缓存中的最长时间,以及允许对象处于缓存中的最长空闲时间

物理介质

内存

内存和硬盘,对象的散装数据首先存放到基于内存的缓存中,当内存中对象的数目达到数据过期策略的maxElementsInMemory值,就会把其余的对象写入基于硬盘的缓存中

缓存软件实现

在Hibernate的Session的实现中包含

由第三方提供,Hibernate仅提供了缓存适配器,用于把特定的缓存插件集成到Hibernate中

启用缓存方式

只要通过Session接口来执行保存,更新,删除,加载,查询,Hibernate就会启用一级缓存,对于批量操作,如不希望启用一级缓存,直接通过JDBCAPI来执行

用户可以再单个类或类的单个集合的粒度上配置第二级缓存,如果类的实例被经常读,但很少被修改,就可以考虑使用二级缓存,只有为某个类或集合配置了二级缓存,Hibernate在运行时才会把它的实例加入到二级缓存中

用户管理缓存的方式

一级缓存的物理介质为内存,由于内存的容量有限,必须通过恰当的检索策略和检索方式来限制加载对象的数目,Session的evit()方法可以显示的清空缓存中特定对象,但不推荐

二级缓存的物理介质可以使内存和硬盘,因此第二级缓存可以存放大容量的数据,数据过期策略的maxElementsInMemory属性可以控制内存中的对象数目,管理二级缓存主要包括两个方面:选择需要使用第二级缓存的持久化类,设置合适的并发访问策略;选择缓存适配器,设置合适的数据过期策略。SessionFactory的evit()方法也可以显示的清空缓存中特定对象,但不推荐


7、Hibernate缓存范围以及分类:

(1) 事务范围:缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。在此范围下,缓存的介质是内存。事务可以是数据库事务或者应用事务,每个事务都有独自的缓存。

(2) 应用范围:缓存被应用范围内的所有事务共享的。这些事务有可能是并发访问缓存,因此必须对缓存采取必要的事务隔离机制。缓存的生命周期依赖于应用的生命周期,应用结束时,缓存也就结束了生命周期,二级缓存存在于应用范围。

(3) 集群范围:在集群环境中,缓存被一个机器或者多个机器的进程共享。缓存中的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据的一致性,缓存中的数据通常采用对象的松散数据形式,二级缓存也存在与应用范围。

二、缓存机制如何使用

1、一级缓存的管理 :

(1)evit( Object obj ) 将指定的持久化对象从一级缓存中清除,释放对象所占用的内存资源,指定对象从持久化状态变为脱管状态,从而成为游离对象。

(2)clear( ) 将一级缓存中的所有持久化对象清除,释放其占用的内存资源。

(3) contains(Object obj)判断指定的对象是否存在于一级缓存中。

(4)flush( ) 刷新一级缓存区的内容,使之与数据库数据保持同步。

2、一级缓存的应用:

package cn.hncu.demo;

import org.hibernate.Session;
import org.hibernate.Transaction;

import cn.hncu.demo.domain.Student;
import cn.hncu.demo.util.HibernateSessionFactory;

public class Client {
    public static void main(String[] args) {
        Session session = HibernateSessionFactory.getSessionFactory().openSession();
        /*开启一个事务*/
        Transaction tran = session.beginTransaction();
        /*从数据库中获取id="S001"的Student对象*/
        Student stud = (Student) session.get(Student.class, "S001");
        System.out.println("------------------------------------");
        System.out.println(stud);
        /*提交事务*/
        tran.commit();

        /*开启一个新事务*/
        tran = session.beginTransaction();
        /*从数据库中获取id="S001"的Student对象*/
        Student stud2 = (Student) session.get(Student.class, "S001");
        System.out.println(stud2);
        /*提交事务*/
        tran.commit();

        /**1.比较两个get()方法获取的对象是否是同一个对象*/
        System.out.println("------------------------------------");
        System.out.println("stud == stud2 result is "+ (stud==stud2));
        System.out.println("------------------------------------");

        /**2.contains(Object obj) : 判断指定的对象是否存在于一级缓存中。*/
        boolean boo = session.contains(stud);
        System.out.println("stud 是否存在于一级缓存中?"+boo);
        boo = session.contains(stud2);
        System.out.println("stud2 是否存在于一级缓存中?"+boo);
        System.out.println("------------------------------------");

        /**3.clear() : 将一级缓存中的所有持久化对象清除,释放其占用的内存资源。*/
        session.clear();
        boo = session.contains(stud);
        System.out.println("stud 是否存在于一级缓存中?"+boo);
        boo = session.contains(stud2);
        System.out.println("stud2 是否存在于一级缓存中?"+boo);
        System.out.println("------------------------------------");
        /*开启一个新事务*/
        tran = session.beginTransaction();
        /*从数据库中获取id="S001"的Student对象*/
        stud = (Student) session.get(Student.class, "S001");
        stud2 = (Student) session.get(Student.class, "S001");
        /*提交事务*/
        tran.commit();

        /**4.evit( Object obj ) 将指定的持久化对象从一级缓存中清除,释放对象所占用的内存资源,指定对象从持久化状态变为脱管状态,从而成为游离对象。*/
        session.evict(stud2);/*【注意】stud和stud2是同一个对象,因此,这一句等效于同时清除stud和stud2*/
        boo = session.contains(stud);
        System.out.println("stud 是否存在于一级缓存中?"+boo);
        boo = session.contains(stud2);
        System.out.println("stud2 是否存在于一级缓存中?"+boo);
        System.out.println("------------------------------------");
    }
}

执行结果:

八月 19, 2017 1:31:06 上午 org.hibernate.annotations.common.reflection.java.JavaReflectionManager <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.5.Final}
八月 19, 2017 1:31:06 上午 org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.3.7.Final}
八月 19, 2017 1:31:06 上午 org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
八月 19, 2017 1:31:06 上午 org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
八月 19, 2017 1:31:06 上午 org.hibernate.cfg.Configuration configure
INFO: HHH000043: Configuring from resource: /hibernate.cfg.xml
八月 19, 2017 1:31:06 上午 org.hibernate.cfg.Configuration getConfigurationInputStream
INFO: HHH000040: Configuration resource: /hibernate.cfg.xml
八月 19, 2017 1:31:06 上午 org.hibernate.cfg.Configuration addResource
INFO: HHH000221: Reading mappings from resource: cn/hncu/demo/domain/Student.hbm.xml
八月 19, 2017 1:31:07 上午 org.hibernate.cfg.Configuration doConfigure
INFO: HHH000041: Configured SessionFactory: null
八月 19, 2017 1:31:07 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH000402: Using Hibernate built-in connection pool (not for production use!)
八月 19, 2017 1:31:07 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://127.0.0.1:3306/hib]
八月 19, 2017 1:31:07 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000046: Connection properties: {user=root, password=****}
八月 19, 2017 1:31:07 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000006: Autocommit mode: false
八月 19, 2017 1:31:07 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
八月 19, 2017 1:31:08 上午 org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
八月 19, 2017 1:31:08 上午 org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000399: Using default transaction strategy (direct JDBC transactions)
八月 19, 2017 1:31:08 上午 org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory

------------------------------------
Student [studId=S001, studName=Jack, age=22, deptId=D001]
Student [studId=S001, studName=Jack, age=22, deptId=D001]
------------------------------------
stud == stud2 result is true
------------------------------------
stud 是否存在于一级缓存中?true
stud2 是否存在于一级缓存中?true
------------------------------------
stud 是否存在于一级缓存中?false
stud2 是否存在于一级缓存中?false
------------------------------------
stud 是否存在于一级缓存中?false
stud2 是否存在于一级缓存中?true
------------------------------------

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

三、Hibernate结合缓存的增删改查操作演示

1、前端页面:

(1)index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  </head>
  <body>
  <!-- 进入主页以后直接转发到DemoServlet中,由此Servlet进行处理 -->
    <jsp:forward page="/DemoServlet"></jsp:forward>
  </body>
</html>
(2)css/table.css
table {
    border: 1px solid gray;
    border-collapse: collapse;
    width: 60%;
}
td ,th{
    border: 1px solid gray;
    padding: 5px;
}
(3)js/ajax.js
/*

    综合后的function对象
 *在这里check函数中,写两个成员方法:分别是get方法和post方法 
 * 
 */
String.prototype.trim=function(){
    var p = /^\s*/;
    var str = this.replace(p,"");
    p = /\s*$/;
    str = str.replace(p,"");
    return str;
};
function Ajax(){
    this.get=function(url,succ,failure){
        //1.创建一个ajax对象
        var xhr = null;
        if(window.XMLHttpRequest){
            xhr = new XMLHttpRequest();
        }else{
            xhr = new ActiveXObject("Mircrosoft.XMLHttp");
        }
        //2.设置通讯方式和地址
        xhr.open("GET", url, true);
        //3.设置访问成功后的js对象(回调函数)
        xhr.onreadystatechange=function(){
            if(xhr.readyState==4){
                if(xhr.status==200){
                    var txt = xhr.responseText;
                    succ(txt);
                }else{
                    failure(xhr.status);
                }
            }
        };
        //4.发送(动作的触发)
        xhr.send(null);
    };

    this.post=function(url,data,succ,failure){
        //1.创建ajax对象
        var xhr = null;
        if(window.XMLHttpRequest){
            xhr = new XMLHttpRequest();
        }else{
            xhr = new ActiveObject();
        }

        //2.设置通讯方式和地址
        xhr.open("POST", url,true);
        //3.设置访问成功后的js对象
        xhr.onreadystatechange=function(){
            if(xhr.readyState==4){
                if(xhr.status==200){
                    var txt = xhr.responseText;
                    succ(txt);
                }else{
                    failure(xhr.status);
                }
            }
        };
        //4.发送请求到http服务器并接收回应   
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.send(data);
    };
}
(4)jsps/show.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <script type="text/javascript">
        var path = "<c:url value='/'/>";
    </script>

    <!-- 导入CSS文件 -->
    <link  type="text/css" rel="stylesheet"  href="<c:url value='/css/table.css'/>" media="screen">
    <!-- 导入JS文件 -->
    <script type="text/javascript" src="<c:url value='/js/ajax.js'/>"></script>

    <script type="text/javascript">
        function query(){//用ajax实现页面局部刷新
            var studId = document.getElementsByName("studId")[1].value;
            studId = studId.trim();
            var studName = document.getElementsByName("studName")[1].value;
            studName = studName.trim();
            var deptId = document.getElementsByName("deptId")[1].value;
            deptId = deptId.trim(); 
            var age = document.getElementsByName("age")[1].value;
            deptId = deptId.trim(); 

            /*这里利用Ajax技术进行校验*/
            var ajax = new Ajax();
            var url = path + "DemoServlet?cmd=queryStudents";
            var data = "studId="+studId+"&studName="+studName+"&deptId="+deptId+"&age="+age;
            ajax.post(url, data, succ, failure);    

        }
        function succ(obj){
            if(obj=="1"){
                var ifm = document.getElementById("ifm");
                ifm.src = path + "jsps/qlist.jsp";//刷新ifm中的内容
                //alert(ifm.src);
            }
        }
        function failure(obj){
            alert(obj);
        }

    </script>

  </head>

  <body>
    <h2>显示学生信息</h2>
    <table>
        <tr>
            <th>学号</th>
            <th>姓名</th>
            <th>年龄</th>
            <th>部门编号</th>
            <th>操作</th>
        </tr>
    <c:forEach items="${list}" var="stud">
        <tr>
            <td>${stud.studId}</td>
            <td>${stud.studName}</td>
            <td>${stud.age}</td>
            <td>${stud.deptId}</td>
            <td><a href="<c:url value='/DemoServlet?cmd=delStudent&studId=${stud.studId}'/>">删除</a></td>
        </tr>
    </c:forEach>
    </table>

    <h2>添加/修改学生信息</h2>
    <form action="<c:url value='/DemoServlet?cmd=addStudent'/>" method="post">
        <table>
            <tr>
            <td>学号<font color="red">*</font></td>
            <td><input type="text" name="studId"/></td>
            </tr>

            <tr>
            <td>姓名<font color="red">*</font></td>
            <td><input type="text" name="studName"/></td>
            </tr>

            <tr>
            <td>年龄<font color="red">*</font></td>
            <td><input type="text" name="age"/></td>
            </tr>

            <tr>
            <td>部门编号<font color="red">*</font></td>
            <td><input type="text" name="deptId"/></td>
            </tr>

            <tr >
            <td colspan="2" align="center"><input type="submit" value="提交"/></td>
            </tr>
        </table>
    </form>

    <h2>查询学生信息</h2>
    <table>
        <tr>
        <td>学号</td>
        <td><input type="text" name="studId"/></td>
        </tr>

        <tr>
        <td>姓名</td>
        <td><input type="text" name="studName"/></td>
        </tr>

        <tr>
        <td>年龄</td>
        <td><input type="text" name="age"/></td>
        </tr>

        <tr>
        <td>部门编号</td>
        <td><input type="text" name="deptId"/></td>
        </tr>

        <tr >
        <td colspan="2" align="center"><input type="button" onclick="query();" value="查询"/></td>
        </tr>
    </table>
    <iframe id="ifm"style="width:50%;height:100%;border-width:0px">
    </iframe>   
  </body>
</html>
(5)jsps/qlist.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <link type="text/css" rel="stylesheet" href="<c:url value='/css/table.css'/>" media="screen">
  </head>

  <body>
    <h2>学生信息查询结果</h2>
    <table>
        <tr>
            <th>学号</th>
            <th>姓名</th>
            <th>年龄</th>
            <th>部门编号</th>
        </tr>
    <c:forEach items="${qlist}" var="stud">
        <tr>
            <td>${stud.studId}</td>
            <td>${stud.studName}</td>
            <td>${stud.age}</td>
            <td>${stud.deptId}</td>
        </tr>
    </c:forEach>
    </table>

  </body>
</html>

2、后台

(1)web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <display-name></display-name>
  <servlet>
    <servlet-name>DemoServlet</servlet-name>
    <servlet-class>cn.hncu.demo.DemoServlet</servlet-class>
    <init-param>
        <param-name>queryAll</param-name>
        <param-value>/jsps/show.jsp</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>DemoServlet</servlet-name>
    <url-pattern>/DemoServlet</url-pattern>
  </servlet-mapping>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
(2)cn.hncu.demo.domain.Student.java:
package cn.hncu.demo.domain;

/**
 * 项目名:HibernateDemo
 * 时间 :2017-8-13 下午11:53:41
 * 
 * 1.关于值对象的说明:
 * 【无参数构造函数是一个持久类的要求;Hibernate已为您创建对象,使用java反射。
 *  构造函数可以是私有的,但是运行时代理生成和有效的数据检索所需的包或公共可见性是不需要字节码的。】
 * 
 * 2.进一步说明:
 * 【这个类使用标准的JavaBean命名约定,用于属性获取器和设置方法,以及字段的私有可见性。虽然这是推荐的设计,但不是必需的。
 * Hibernate也可以直接访问字段,访问器方法的好处是鲁棒性的重构。id属性为特定事件保留唯一标识符值。
 * 如果我们想使用Hibernate的全部特性集,所有持久的实体类(还有不太重要的依赖类)都需要这样的标识符属性。
 * 事实上,大多数应用程序,尤其是Web应用程序都需要通过标识符来区分对象,因此您应该考虑这一特性而不是局限性。
 * 但是,我们通常不处理对象的标识,因此set方法应该是私有的。只有Hibernate在保存对象时才会分配标识符。】
 * 
 * 3.【注意】接下来,我们创建一个类来表示要存储在数据库中的事件;它是一个具有一些属性的简单JavaBean类。
 * 
 * 
 */
public class Student {
    private String studId;
    private String studName;
    private Integer age;
    private String deptId;

    public Student() {
        super();
    }
    public String getStudId() {
        return studId;
    }
    public void setStudId(String studId) {
        this.studId = studId;
    }
    public String getStudName() {
        return studName;
    }
    public void setStudName(String studName) {
        this.studName = studName;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getDeptId() {
        return deptId;
    }
    public void setDeptId(String deptId) {
        this.deptId = deptId;
    }
    @Override
    public String toString() {
        return "Student [studId=" + studId + ", studName=" + studName
                + ", age=" + age + ", deptId=" + deptId + "]";
    }

}
(2)cn/hncu/demo/domain/Student.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.hncu.demo.domain" >
    <class name="Student" table="students" catalog="hib">
        <id name="studId" type="java.lang.String" >
            <column name="id" length="8"></column>
        </id>
        <property name="studName" type="java.lang.String">
            <column name="name" length="40"></column>
        </property>
        <property name="age" type="java.lang.Integer">
            <column name="age" ></column>
        </property>
        <property name="deptId" type="java.lang.String">
            <column name="deptId" length="8"></column>
        </property>
    </class>
</hibernate-mapping>
<!--
1.关于mapping file的说明: 
【Hibernate需要知道如何加载和存储持久类的对象。这就是Hibernate映射文件发挥作用的地方。
映射文件告诉Hibernate它必须访问数据库中的表,以及它应该使用的表中的哪些列。】

2.mapping file的 基本结构:
【<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.hncu.demo.domain">
[...]
</hibernate-mapping>】

 -->
(3)src/hibernate.cfg.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>

        <!-- 数据库的连接设置 -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://127.0.0.1:3306/hib</property>
        <property name="connection.username">root</property>
        <property name="connection.password">1234</property>

        <!-- SQL的方言 -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

        <property name="hibernate.cache.use_second_level_cache">false</property>
        <!-- 一定要将对象映射文件配置进来 -->
        <mapping resource="cn/hncu/demo/domain/Student.hbm.xml"/>

    </session-factory>
</hibernate-configuration>
<!-- 
1.Hibernate configuration:Hibernate配置文件

2.此时,应该将持久类及其映射文件放在适当的位置。现在来配置Hibernate配置文件。
 -->
(5)cn.hncu.utils.BaseServlet.java:
package cn.hncu.utils;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public abstract class BaseServlet extends HttpServlet{
    /**
     * 这个execute方法是baseServlet方法中默认执行的方法
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public abstract void  execute(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException ;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doPost(req, resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        /*一定要设置编码,被坑了好久*/
        req.setCharacterEncoding("utf-8");
        String cmd = req.getParameter("cmd");
        if(cmd==null || cmd.trim().equals("")){
            cmd = "execute";
        }


        try {
            Method method = this.getClass().getMethod(cmd, HttpServletRequest.class,HttpServletResponse.class);
            if(method.getName().equals(cmd)){
                try {
                    method.invoke(this, req,resp);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("一个非法存取异常被抛出时,应用程序尝试性创建一个实例(除数组),设置或获取一个字段,或调用一个方法,但目前执行的方法不能访问指定的类,字段的定义、方法或构造函数。", e);
                } catch (IllegalArgumentException e) {
                    throw new RuntimeException("抛出以指示方法已通过非法或不适当的参数。", e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException("将异常的调用的方法或构造函数抛出。", e);
                }
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("当无法找到特定方法时抛出。", e);
        } catch (SecurityException e) {
            throw new RuntimeException("由安全管理器抛出以指示安全性违规。", e);
        }
    }
}
(6)cn.hncu.utils.HibernateSessionFactory.java:
package cn.hncu.utils;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;

public class HibernateSessionFactory {
    private static  SessionFactory sessionFactory = null;
    /**利用本地线程池管理对象*/
    private static ThreadLocal<Session> t = new ThreadLocal<Session>();
    /**
     * 用单例的形式初始化工厂类
     */
    static{
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            Configuration config = new Configuration().configure();
            StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings( config.getProperties() ).build();
            sessionFactory = config.buildSessionFactory(serviceRegistry);
        } catch (Throwable ex) {
        }

    }

    /**
     * 获得SessionFactory 工厂类
     * @return
     */
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    /**
     * 获得Session类
     * @return
     */
    public static Session getSession() {
        Session session = t.get();/*先到本地线程池中去拿Session对象,如果有就拿到*/
        if(session==null || sessionFactory==null){/*如果session为空或者sessionFactory为空,一定要开启一个session*/
            session = (sessionFactory!=null)?sessionFactory.openSession():null;/*如果sessionFactory不为空,一定开启一个session*/
            t.set(session);/*将拿到的session放入到线程池中*/
        }
        return session;
    }

     //关闭与数据库的会话
    public static void closeSession(){
        t.set(null);
    }
}
(7)cn.hncu.demo.DemoServlet.java:
package cn.hncu.demo;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.hncu.demo.domain.Student;
import cn.hncu.demo.service.DemoService;
import cn.hncu.demo.service.DemoServiceImpl;
import cn.hncu.utils.BaseServlet;

public class DemoServlet extends BaseServlet {

    private DemoService service = new DemoServiceImpl();
    /**
     * 进入主页后执行的默认方法
     */
    @Override
    public void execute(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        String showPage = getInitParameter("queryAll");
        List<Student> list = service.queryAll();
        req.getSession().setAttribute("list", list);
        resp.sendRedirect(getServletContext().getContextPath()+showPage);
    }

    /**
     * 删除指定的学生
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void delStudent(HttpServletRequest req,HttpServletResponse resp) 
            throws ServletException,IOException{
        String studId = req.getParameter("studId");
        System.out.println(studId);
        Student stud = new Student();
        stud.setStudId(studId);
        service.delStudent(stud);
        resp.sendRedirect(getServletContext().getContextPath());
    }

    /**
     * 增加或者修改指定的学生
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void addStudent(HttpServletRequest req ,HttpServletResponse resp)
        throws ServletException,IOException{
        String studId = req.getParameter("studId");
        String studName = req.getParameter("studName");
        String strAge = req.getParameter("age");
        String deptId = req.getParameter("deptId");

        Student stud = new Student();
        if(studId!=null && studId.trim().length()>0){
            stud.setStudId(studId);
        }
        if(studName!=null && studName.trim().length()>0){
            stud.setStudName(studName);
        }
        if(strAge!=null && strAge.trim().length()>0){
            stud.setAge(Integer.valueOf(strAge));
        }else{
            stud.setAge(0);
        }
        if(deptId!=null && deptId.trim().length()>0){
            stud.setDeptId(deptId);
        }

        service.addStudent(stud);
        resp.sendRedirect(getServletContext().getContextPath());
    }

    /**
     * 按条件查询部分学生
     * @param req
     * @param resp
     * @return
     * @throws ServletException
     * @throws IOException
     */
    public void queryStudents(HttpServletRequest req ,HttpServletResponse resp)
            throws ServletException,IOException{
        String studId = req.getParameter("studId").trim();
        String studName = req.getParameter("studName").trim();
        String deptId = req.getParameter("deptId").trim();
        String strAge = req.getParameter("age").trim();

        Student stud = new Student();
        stud.setStudId(studId);
        stud.setStudName(studName);
        stud.setDeptId(deptId);
        if(strAge!=null && strAge.trim().length()>0){
            stud.setAge(Integer.valueOf(strAge));
        }else{
            stud.setAge(0);
        }

        List<Student> qlist = service.queryStudents(stud);
        req.getSession().setAttribute("qlist", qlist);
        resp.getWriter().print("1");// 坑: 此处不能用out.println("1");
    }
}
(8)cn.hncu.demo.service.DemoService.java:
package cn.hncu.demo.service;

import java.util.List;

import cn.hncu.demo.domain.Student;

public interface DemoService {
    public List<Student> queryAll();

    public void delStudent(Student stud);

    public void addStudent(Student stud);

    public List<Student> queryStudents(Student stud);
}
(8)cn.hncu.demo.service.DemoServiceImpl.java:
package cn.hncu.demo.service;

import java.util.List;

import cn.hncu.demo.dao.DemoDAO;
import cn.hncu.demo.dao.DemoDaoJdbc;
import cn.hncu.demo.domain.Student;

public class DemoServiceImpl implements DemoService{
    private DemoDAO dao = new DemoDaoJdbc();
    @Override
    public List<Student> queryAll() {
        return dao.queryAll();
    }
    @Override
    public void delStudent(Student stud) {
        dao.delStudent(stud);
    }
    @Override
    public void addStudent(Student stud) {
        dao.addStudent(stud);
    }
    @Override
    public List<Student> queryStudents(Student stud) {
        return dao.queryStudents(stud);
    }
}
(9)cn.hncu.demo.dao.DemoDAO.java:
package cn.hncu.demo.dao;

import java.util.List;

import cn.hncu.demo.domain.Student;

public interface DemoDAO {
    public List<Student> queryAll();

    public void delStudent(Student stud);

    public void addStudent(Student stud);

    public List<Student> queryStudents(Student stud);
}
(9)cn.hncu.demo.dao.DemoDaoJdbc.java:
package cn.hncu.demo.dao;

import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

import cn.hncu.demo.domain.Student;
import cn.hncu.utils.HibernateSessionFactory;

public class DemoDaoJdbc {

    public List<Student> queryAllStudents(){

        Session session = HibernateSessionFactory.getSession();
        Query query= session.createQuery("from Student"); //HQL = SQL中的关键字 + Java(类名,属性名)
        List<Student> list= query.list();
        return list;

    }

    public void delStudent(Student stud){
        //Hibernate中建议,每次操作时重新获取session
        Session session = HibernateSessionFactory.getSession();
        session.clear();//清缓存!!!!

        Transaction tran = session.beginTransaction();
        //stud.setStudId("S1234");
        session.delete(stud);//如果对应id的stud在数据库中存在则删除,否则删除不了但不会抛出异常!
        tran.commit();

        session.clear();//清缓存!!!!
    }

    public void addStudent(Student stud) {
        //Hibernate中建议,每次操作时重新获取session
        Session session = HibernateSessionFactory.getSession();
        session.clear();//清缓存!!!!
        Transaction tran = session.beginTransaction();

        //如果存在,则只修改姓名(ID是一定不能变的)
        Student stud2 = (Student) session.get(Student.class, stud.getStudId());//获取单个对象
        //System.out.println(stud2.getStudId()+","+stud2.getStudName());
        stud.setAge(stud2.getAge());
        stud.setDepId(stud2.getDepId());
        session.evict(stud2); //在缓存中要手动清除stud2对象,否则会在缓存中出现两份ID相同的不同instance,因此要删除没用的那个,否则出异常!
        //缓存中的对象(instance,实例)是以id来标识的,必须唯一,不允许在缓存中出现两个id相同的instance

        session.saveOrUpdate(stud);//如果对应id的stud在数据库中存在则是修改,否则是添加!
        tran.commit();

        session.clear();//清缓存!!!!
    }

    public List<Student> queryStudents(Student stud) {
        Session session = HibernateSessionFactory.getSession();

        boolean f1=false,f2=false,f3=false;
        String hql = "from Student s where 1=1";
        if(stud.getStudId()!=null && stud.getStudId().trim().length()>0){
            hql = hql + " and s.studId=:studId";
            f1=true;
        }

        if(stud.getStudName()!=null && stud.getStudName().trim().length()>0){
            hql = hql + " and s.studName like :studName";
            f2=true;
        }

        if(stud.getDepId()!=null && stud.getDepId().trim().length()>0){
            hql = hql + " and s.depId=:deptId";
            f3=true;
        }

        Query query = session.createQuery(hql);
        if (f1) {
            query.setParameter("studId",stud.getStudId().trim());
        }
        if (f2) {
            query.setParameter("studName", "%"+stud.getStudName().trim()+"%");
        }
        if (f3) {
            query.setParameter("deptId",stud.getDepId().trim());
        }
        List<Student> list = query.list();
        System.out.println("hql = "+hql);
        System.out.println("list = "+list);
        return list;
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值