zoukankan      html  css  js  c++  java
  • Hibernate的查询,二级缓存,连接池

    Hibernate的查询,二级缓存,连接池

    1.Hibernate查询数据

    Hibernate中的查询方法有5中:

    1.1.Get/Load主键查询

    使用get或者load方法来查询,两者之间的区别在前面已经分析过了,这里用代码示例一下:

     
        //1.主键查询
        @Test
        public void all(){
            Session session = new Configuration().configure().buildSessionFactory().openSession();
            session.beginTransaction();
    
            //1.主键查询
             Dept dept= (Dept) session.get(Dept.class, 1);
             Dept dept1= (Dept) session.load(Dept.class, 1);
    
            session.getTransaction().commit();
            session.close();
    
        }
    

    1.2.对象导航查询

        //2.对象导航查询
        @Test
        public void all(){
            Session session = new Configuration().configure().buildSessionFactory().openSession();
            session.beginTransaction();
            
            //2.对象导航查询
            Dept dept= (Dept) session.load(Dept.class, 1);
            System.out.println(dept.getDeptName());
            System.out.println(dept.getEmps());
    
    
            session.getTransaction().commit();
            session.close();
    
        }
    

    1.3.HQL查询

    HQL:Hibernate Query language, hibernate提供的面向对象的查询语
    根据sql语言的查询分类,HQL查询也大致可以分为一下几种:
    1.查询全部列
    2.查询指定列
    3.查询指定的列并封装为对象
    4.条件查询(一个条件/多个条件and or/范围查询between and/模糊查询)
    5.聚合查询
    6.分组查询
    7.连接查询

    前六种方式的查询方式代码示例为:

        //3.HQL查询
        @Test
        public void hql(){
            Session session = sf.openSession();
            session.beginTransaction();
            //注意:使用HQL查询的时候,auto-import="true"要设置为true
            //如果是false,写HQL的时候,要指定类的全名
            SQLQuery q = session.createSQLQuery("FROM Dept");
            System.out.println(q.list());
    
            //1.查询全部列
            Query q = session.createQuery("from a_query.Dept");
            Query q = session.createQuery("select * from  a_query.Dept");//错误,不支持*
            Query q = session.createQuery("select d from a_query.Dept  d");
            System.out.println(q.list());
    
            //2.查询指定的列
            //返回对象数据Object
            Query q = session.createQuery("select d.depId,d.deptName from a_query.Dept as d");
            System.out.println(q.list());//[[Ljava.lang.Object;@11cfad4]
    
            //3.查询指定的列封装为对象【必须要提供带参构造器】
            Query q = session.createQuery("select new a_query.Dept(d.depId,d.deptName) from a_query.Dept  d");
            System.out.println(q.list());
    
            //4.条件查询:一个条件/多个条件and or/between and/模糊查询
            //条件查询:占位符
            Query q = session.createQuery("from Dept d where deptNmae=?");
            q.setString(0,"财务部");//使用下面方式设置参数也可以、
            q.setParameter(0,"财务处")
            System.out.println(q.list());
    
    
            //条件查询:命名参数
            Query q = session.createQuery("from Dept d where depId=:myId and deptName=:name");
            q.setParameter("myId",121);
            q.setParameter("name","财务部");
            System.out.println(q.list());
    
            //范围between and
            Query q = session.createQuery("from Dept d where depId between ? and ?");
            q.setParameter(0,1);
            q.setParameter(1,20);
            System.out.println(q.list());
    
            //模糊
            Query q = session.createQuery("from Dept d where deptName like ?");
            q.setString(0,"%部%");
            System.out.println(q.list());
    
    
            //5.聚合函数统计
            Query q = session.createQuery("select count(*) from Dept");
            Long num = (Long) q.uniqueResult();//不再使用list()方法
            System.out.println(num);
    
            //6.分组查询
            //统计t_employee表中,每个部门的人数
            //数据库写法:SELECT dept_id,COUNT(*) FROM t_employee GROUP BY dept_id;
            // HQL写法
            Query q = session.createQuery("select e.dept,count(*) from Employee e group by e.dept ");
            System.out.println(q.list());
            
            session.getTransaction().commit();
            session.close();
        }
    

    连接查询
    连接查询主要分为内连接和左外连接或者右外连接。
    先看需求:显示员工名称,薪水,部门
    使用SQL来查询可以这样写:
    内连接:
    SELECT e.empName,e.salary,d.deptName FROM t_dept d,t_employee e WHERE d.depId=e.dept_id;
    SELECT e.empName,e.salary,d.deptName FROM t_dept d INNER JOIN t_employee e ON d.depId = e.dept_id;

    左外连接:
    需求:显示部门,以及部门下的员工,如果部门下没有员工,用null
    SELECT e.empName,e.salary,d.deptName FROM t_dept d LEFT JOIN t_employee e ON d.depId = e.dept_id;

    始终显示right join后面表的数据
    SELECT e.empName,e.salary,d.deptName FROM t_employee e LEFT JOIN t_dept d ON d.depId = e.dept_id;

    使用HQL查询为:

        //HQL查询的连接查询
        @Test
        public void join(){
            Session session = sf.openSession();
            session.beginTransaction();
            //1.内连接【映射已经配置好了关系,关联的时候,直接写对象的属性就可以了】
            Query q = session.createQuery("from a_query.Employee e inner join e.dept");
            //数组中第一个元素是Employee,第二个元素是dept
            List<Object[]> list = q.list();//可以遍历得到结果了
            System.out.println(q.list());
    
            //2.左外连接
            q = session.createQuery("from a_query.Dept d left join d.emps");
            System.out.println(q.list());
    
            //右外连接
            q = session.createQuery("from a_query.Employee e right join e.dept");
            System.out.println(q.list());
    
    
            //3.迫切连接【使用fetch,会把右表的数据,填充到左表对象中】
            //迫切内连接
            q = session.createQuery("from Dept d inner join fetch d.emps");
    		q.list();
            //迫切外连接
            Query q = session.createQuery("from Dept d left join fetch d.emps");
            q.list();
            
            session.getTransaction().commit();
            session.close();
        }
    

    HQL的优化操作
    如果sql写在java代码中是写死的,这样的化维护起来特别困难,常用的做法是将HQL的sql语句放在java对应的映射文件各种,然后在java中直接调用。但是sql放在映射文件中需要注意转义的字符
    映射文件:

    	<!--存放sql语句-->
            <query name="getAllDept">
                from Dept d where depId=:myId or deptName=:name
            </query>
            <!--这里的sql需要转义的字符要转义-->
            <query name="getLitter">
                from Dept d where depId &lt; ?
            </query>
            <!--批量转义的时候使用CDATA区域-->
            <query name="getLitter2">
                <![CDATA[
                    from Dept d where depId < ?
                ]]>
            </query>
    

    优化代码示例:

        //HQL查询优化
        @Test
        public void hql_other() {
            Session session = sf.openSession();
            session.beginTransaction();
            // HQL写在java代码中会把HQL写死,不易维护
            //Query q = session.createQuery("from Dept d where depId=:myId or deptName=:name");
    
            // 优化做法是将HQL查询语句放到映射文件中
            //一般都放在对应的映射文件中
            Query q = session.getNamedQuery("getAllDept");//从配置文件中读取sql
            q.setParameter("myId",12); //设置参数
            q.setParameter("name","财务部");
            System.out.println(q.list());
    
            session.getTransaction().commit();
            session.close();
        }
    

    1.4.Criteria查询

    Criteria是完全面向对象的查询:QBC,Query By Criteria

    /**
     * QBC查询
     */
    public class QbcTest {
        private static SessionFactory sf;
        static {
            sf = new Configuration().configure().buildSessionFactory();
        }
    
        @Test
        public void all(){
            Session session = sf.openSession();
            session.beginTransaction();
    
            Criteria criteria = session.createCriteria(Employee.class);//查询了所有的对象
    
            //添加查询条件,字段=值
    //        criteria.add(Restrictions.eq("empId",1));
            //根据主键查询
            criteria.add(Restrictions.idEq(1));
    
            System.out.println(criteria.list());
    
            session.getTransaction().commit();
            session.close();
        }
    }
    

    1.5.SQLQuery,本地SQL查询

    缺点:不能跨数据库平台,如果改了数据库,sql语句有可能需要改。
    使用场景:对于复杂SQL,HQL实现不了的情况可以使用本地sql查询

        //5.SQLQuery, 本地SQL查询
        //不能跨数据库平台: 如果该了数据库,sql语句有肯能要改。
        @Test
        public void sql(){
            Session session = sf.openSession();
            session.beginTransaction();
    
            SQLQuery q = session.createSQLQuery("SELECT * FROM t_dept limit 1")//查询数结果
                    .addEntity(Dept.class);//封装结果
            System.out.println(q.list());
            
            session.getTransaction().commit();
            session.close();
        }
    

    1.6.Hibernate分页查询

    数据库SQL分页的时候,是先查询总记录数,再分页查询。
    Hibernate对这个功能有简化,更方便一些:
    代码示例:

    
    /**
     * 分页查询
     */
    public class QbcTest {
        private static SessionFactory sf;
        static {
            sf = new Configuration().configure().buildSessionFactory();
        }
    
        
        // 分页查询
        @Test
        public void paginator() {
    
            Session session = sf.openSession();
            session.beginTransaction();
    
            //1.获取数据库表里面的数据
            Query q = session.createQuery("from Employee");
    
            // 2.从记录数
            ScrollableResults scroll = q.scroll();  // 得到滚动的结果集
            scroll.last();							//  滚动到最后一行
            int totalCount = scroll.getRowNumber() + 1;// 得到滚到的记录数,即总记录数
    
            // 3.设置分页参数
            q.setFirstResult(0);
            q.setMaxResults(3);
    
            // 4.查询
            System.out.println(q.list());
            System.out.println("总记录数:" + totalCount);
    
            session.getTransaction().commit();
            session.close();
        }
    }
    

    2.Hibernate对连接池的支持

    连接池作用:管理连接,提升连接的利用效率!
    常用的连接池有:C3P0连接池
    Hibernate自带的也有一个连接池,且对c3p0连接池也有支持

    Hibernate自带的连接池,只维护一个连接,比较简陋,可以查询Hibernate.properties文件查看连接池详细配置

    #################################
    ### Hibernate Connection Pool ###     
    #################################
    
    hibernate.connection.pool_size 1        【Hbm 自带连接池: 只有一个连接】
    
    
    
    ###########################
    ### C3P0 Connection Pool###		   【Hbm对C3P0连接池支持】
    ###########################
    
    #hibernate.c3p0.max_size 2				最大连接数
    #hibernate.c3p0.min_size 2				最小连接数
    #hibernate.c3p0.timeout 5000           超时时间
    #hibernate.c3p0.max_statements 100     最大执行的命令的个数
    #hibernate.c3p0.idle_test_period 3000    空闲测试时间
    #hibernate.c3p0.acquire_increment 2     连接不够用的时候, 每次增加的连接数
    #hibernate.c3p0.validate false
    
    【Hbm对C3P0连接池支持,  核心类】
     告诉hib使用的是哪一个连接池技术。
    #hibernate.connection.provider_class org.hibernate.connection.C3P0ConnectionProvider
    

    Hibernate对c3p0连接池支持,用到的核心类

    hibernate.connection.provider_class

    即:org.hibernate.connection.C3P0ConnectionProvider

    Hibernate中配置连接池的地方在Hibernate.cfg.xml 中配置,此处的配置不会对已经配置好的数据库连接有影响,所有直接下数据库连接下面写就可以了.

    
    <!-- 【连接池配置】 -->
    		<!-- 配置连接驱动管理类 -->
    		<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
    		<!-- 配置连接池参数信息 -->
    		<property name="hibernate.c3p0.min_size">2</property>
    		<property name="hibernate.c3p0.max_size">4</property>
    		<property name="hibernate.c3p0.timeout">5000</property>
    		<property name="hibernate.c3p0.max_statements">10</property>
    		<property name="hibernate.c3p0.idle_test_period">30000</property>
    		<property name="hibernate.c3p0.acquire_increment">2</property>
    

    3.二级缓存

    Hibernate提供的缓存有一级缓存,二级缓存.目的是为了减少对数据库的访问次数,提高程序执行效率.

    对于一级缓存:
    基于session的缓存,缓存内容只在当前session有效,session关闭,缓存内容失效!
    特点是:作用范围小,缓存的时间短,缓存效果不明显.

    1.二级缓存概述
    Hibernate提供了基于应用程序级别的缓存,可以跨多个session,即不同的seesion都可以访问缓存数据,这个缓存也叫二级缓存.
    Hibernate提供的二级缓存有默认的实现,且是一种可插配的缓存框架!如果用户想用二级缓存,只需要在Hibernate.cfg.xml中配置即可;不想用,直接移除,不影响代码.
    如果用户觉得Hibernate提供的框架不好用,自己可以换其他的缓存框架或自己实现缓存框架都可以

    2.使用二级缓存
    查看Hibernate.properties配置文件,二级缓存如何配置:

    ##########################
    ### Second-level Cache ###
    ##########################
    
    #hibernate.cache.use_second_level_cache false【二级缓存默认不开启,需要手动开启】
    #hibernate.cache.use_query_cache true      【开启查询缓存】
    
    ## choose a cache implementation		【二级缓存框架的实现】
    
    #hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider
    #hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider
    hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider 默认实现
    #hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider
    #hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider
    #hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider
    

    二级缓存使用步骤:
    1.开始二级缓存
    2.指定缓存框架
    3.指定哪些类加入二级缓存
    4.测试

    3.缓存策略

    <class-cache usage="read-only"/>     放入二级缓存的对象,只读; 
    	<class-cache usage="nonstrict-read-write"/>  非严格的读写
    	<class-cache usage="read-write"/>    读写; 放入二级缓存的对象可以读、写;
    	<class-cache usage="transactional"/>   (基于事务的策略)
    

    4.集合缓存

    <!-- 集合缓存[集合缓存的元素对象,也加加入二级缓存] -->
    		<collection-cache 
    usage="read-write" collection="cn.itcast.b_second_cache.Dept.emps"/>
    
    

    5.查询缓存
    list()默认情况只会放入缓存,不会从一级缓存中取出.
    使用查询缓存,可以让list()查询从二级缓存中取出

    代码示例:
    Hibernate.cfg.xml中配置二级缓存

    <!-- 【二级缓存配置】 -->
    		<!-- a.  开启二级缓存 -->
    		<property name="hibernate.cache.use_second_level_cache">true</property>
    		<!-- b. 指定使用哪一个缓存框架(默认提供的) -->
    		<property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
    		<!-- 开启查询缓存 -->
    		<property name="hibernate.cache.use_query_cache">true</property>
    		<!-- c. 指定哪一些类,需要加入二级缓存 -->
    		<class-cache usage="read-write" class="cn.itcast.b_second_cache.Dept"/>
    		<class-cache usage="read-only" class="cn.itcast.b_second_cache.Employee"/>
    		<!-- 集合缓存[集合缓存的元素对象,也加加入二级缓存] -->
    		<collection-cache usage="read-write" collection="cn.itcast.b_second_cache.Dept.emps"/>
    		
    
    

    测试类:

    public class App {
    	
    	private static SessionFactory sf;
    	static {
    		sf = new Configuration()
    			.configure()
    			.addClass(Dept.class)   
    			.buildSessionFactory();
    	}
    	// 1. 测试二级缓存的使用
    	// 没有/有用 二级缓存
    	@Test
    	public void testCache() {
    		Session session1 = sf.openSession();
    		session1.beginTransaction();
    		// a. 查询一次
    		Dept dept = (Dept) session1.get(Dept.class, 10);
    		dept.getEmps().size();// 集合
    		session1.getTransaction().commit();
    		session1.close();
    		
    		System.out.println("------");
    		
    		// 第二个session
    		Session session2 = sf.openSession();
    		session2.beginTransaction();
    		// a. 查询一次
    		dept = (Dept) session2.get(Dept.class, 10);  // 二级缓存配置好; 这里不查询数据库
    		dept.getEmps().size();
    		
    		session2.getTransaction().commit();
    		session2.close();
    	}
    	
    	
    	@Test
    	public void listCache() {
    		Session session1 = sf.openSession();
    		session1.beginTransaction();
    		// HQL查询  【setCacheable  指定从二级缓存找,或者是放入二级缓存】
    		Query q = session1.createQuery("from Dept").setCacheable(true);
    		System.out.println(q.list());
    		session1.getTransaction().commit();
    		session1.close();
    		
    		
    		Session session2 = sf.openSession();
    		session2.beginTransaction();
    		q = session2.createQuery("from Dept").setCacheable(true);
    		System.out.println(q.list());  // 不查询数据库: 需要开启查询缓存
    		session2.getTransaction().commit();
    		session2.close();
    	}
    }
    

    4.项目中session的管理方式

        //项目中session的管理方式
        @Test
        public void testSession() throws Exception{
            SessionFactory sf = new Configuration().configure().buildSessionFactory();
    
            //openSession:创建Session,每次都会创建一个新的session
            Session session1 = sf.openSession();
            Session session2 = sf.openSession();
            System.out.println(session1==session2);//false
            session1.close();
            session2.close();
    
            //线程方式创建session
            // 一定要配置:<property name="hibernate.current_session_context_class">thread</property>
            //getCurrentSession()创建或者获取session
            Session session3 = sf.getCurrentSession();
            Session session4 = sf.getCurrentSession();
            System.out.println(session3==session4);//true
    
            //关闭:以线程方式创建的session,可以不用关闭,线程结束时session自动结束
    //        session3.close();
    //        session4.close();//报错,因为同一个session已经关闭
            
        }
    
  • 相关阅读:
    求给定数组中最大值和其在数组中的索引并输出
    多线程与多进程
    logging模块
    QWidget上下文菜单处理函数
    python中的yield关键字
    菜单栏(QMenuBar)与菜单(QMenu)
    PyQt5布局管理(1)
    QMainFrame类
    QTP11使用DOM XPath以及CSS识别元素对象
    C# 跨线程访问控件
  • 原文地址:https://www.cnblogs.com/cenyu/p/6286329.html
Copyright © 2011-2022 走看看