hibernate学习手记之使用hql

原创
小哥 3年前 (2022-11-17) 阅读数 43 #大杂烩

3.Session接口

主要功能和作用:
  1. Session一个实例代表与数据库一次操作(可以是crud组合)
  2. Session实例通过SessionFactory获取,用完需要关闭。
  3. Sessions是线程不同步的,因此要保证在同一线程中使用,可以用getCurrentSession().
  4. Session可以看做是持久化管理器,它是与持久化操作相关的接口。
get()和load()的区别:
  1. get()方法直接返回实体类,如果查不到数据就返回null。load()则会返回一个实体代理对象(当前这个对象可以转化为实体对象),当代理对象被调用时,如果没有数据,就会抛出 org.hibernate.ObjectNotFoundException 异常
  2. load()是先在缓存(session缓存/二级缓存)中去找数据,如果缓存中没有,就直接返回实体代理对象,当对这个代理对象操作之后,才会在db中去查询。这就是我们常说的load在默认情况下支撑延迟加载(lazy)。
  3. get先到缓存(session缓存/二级缓存)中去查,如果没有就到db中去查(即马上发出sql语句),总之,如果你确定db中有这个对象就用load(),不确定就用get()。(这样效率高)

4.Query接口查询

针对于 hibernate5 以上的朋友,这个类的导入不再和以前在 import org.hibernate.Query; 中,而是 import org.hibernate.query.Query; 。不然再使用 list() 等一些方法时候,会提示过时。
我们在前面使用查询是通过 load()get() 去查询,详细的可以看上面的文档。但是 hibernate 给我们提供了专门的查询接口 Query 。下面来看下用法。

public class TestDao {
    public static void main(String[] args) {
        //使用Query接口来获得要查询的内容
        Session session = MySessionFactory.getSessionFactory().openSession();
        Transaction transaction = null;
        try {
            transaction = session.beginTransaction();
            //使用Query接口
            Query query = session.createQuery("from Student where name=什么神奇");
            List list = query.getResultList();
            for(Student student :list){
                System.out.println(student.getName()+student.getAge()+" "+student.getCreateDate());
            }

        } catch (Exception e) {
            if(transaction!=null)
                transaction.rollback();//回滚
            throw new RuntimeException(e.getMessage());
        }
        finally {
            if(session!=null&&session.isOpen()){
                session.close();
            }
        }
    }
}

上面的代码中,不难看出, session.createQuery 是重点,然后用 .list() 方法或者是 getResultList() 方法去获取符合条件的记录。需要注意的问题是, createQuery() 的参数字符串里面, 对于类似Student的,一定是domain对象!就是我们在java中建立的那个Student类,而不是数据库的表名。相应的,虽然name也可以写成数据库的字段名(我这里数据库的字段名改成了Sname),但是也最好都写成domain对象中的属性名! 。

5.使用Myeclispse来自动生成hibernate配置文件

参考 这里 ,本来弄了个视频的,我看看能不能传上来。

三:hibernate之hql语句详解

1.为什么使用hql语句?

这个语句能让程序员不用再纠结过多sql语句和数据库的问题。

2.hql是什么?

这是 hibernate 自带的自身的语句,可以理解为是在对象上进行操作的sql语句。我们平时总是说对象关系映射,那么当完成了关系模型到对象的映射就可以使用hql语句来对对象之间操作进而之间操作数据库了。

3.基于学生,课程,成绩表的查询项目

下面,将使用这个3张数据表来完成我们对于hql语句的讲解。

  1. 在数据库中建立这三张表。

    create database justtest; use justtest; create table Student( Sno varchar(20) primary key, Sname varchar(40) not null, Ssex enum(男,女) not null, Class varchar(20) ); insert into Student values(20160912,什么神奇,男,计算机系); insert into Student values(20160913,小明,男,计算机系); insert into Student values(20160914,小红,女,建筑系); insert into Student values(20160915,小丽,女,计算机系); insert into Student values(20160916,小张,男,建筑系); create table Course( Cno varchar(20) primary key, Cname varchar(20) ); insert into Course values(1,java基础); insert into Course values(2,javaWeb); insert into Course values(3,c++); insert into Course values(4,python); insert into Course values(5,建筑结构); create table studentCourse( id int auto_increment primary key, Sno varchar(20) not null, Cno varchar(20) not null, socre varchar(10) not null, foreign key(Sno) references Student(Sno), foreign key(Cno) references Course(Cno) ); insert into studentCourse(Sno,Cno,socre) values(20160912,1,99); insert into studentCourse(Sno,Cno,socre) values(20160912,2,98); insert into studentCourse(Sno,Cno,socre) values(20160912,5,55); insert into studentCourse(Sno,Cno,socre) values(20160913,3,60); insert into studentCourse(Sno,Cno,socre) values(20160913,2,29); insert into studentCourse(Sno,Cno,socre) values(20160914,1,88); insert into studentCourse(Sno,Cno,socre) values(20160914,4,55); insert into studentCourse(Sno,Cno,socre) values(20160914,5,90); insert into studentCourse(Sno,Cno,socre) values(20160915,1,44); insert into studentCourse(Sno,Cno,socre) values(20160916,1,60);

上面的表中,Student表用来指明学号,姓名,性别,所在系。Course表用来指明课程名和课程号。studentCourse表用来指明学号,课程号和分数。
如果你实在不会 sql 语句,请把上面的代码直接复制在mysql中然后执行。
在这里,问题就很明显了,studentCourse表中有外码,很多人就有疑问了,我们在对象模型中该如何写这个外码呢?我们可以先使用Myeclipse的自动生成,详细步骤请看上面的视频,我就不再写了。需要注意的事,当将表反射成对象生成配置文件时,需要注意选择表的顺序,主表一定是先反射成文件的,然后才是附表,就像这里先发Student和Course,再搞studentCourse。
下面我们来看看反射后的情况。

我们这是拿Myeclipse自动生成的,但是每次自动生成都会有些莫名的错误,如果报错,一定要仔细看看是哪里,基本上我都是url那里路径没对上。
我们先创建 Test.java 来测试一下。

package com.zzh.test;

import java.util.List;

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

import com.zzh.dao.Student;
import com.zzh.dbUtil.MySessionFactory;
public class Test {

    public static void main(String[] args) {
        Session session = MySessionFactory.getSessionFactory().openSession();
        Transaction transaction =null;
        try {
            transaction = session.beginTransaction();
            //下面是正常的从学生表中取信息
            Query query =session.createQuery("from Student");
            List list = query.list();
            for(Student student :list){
                System.out.println(student.getSname()+" "+student.getSno()+" "+student.getSsex()+" "
                        +student.getClass_());
            }
            transaction.commit();
        } catch (Exception e) {
            if(transaction!=null){
                transaction.rollback();
            }
            throw new RuntimeException(e.getMessage());
        }
        finally {
            if(session!=null&&session.isOpen()){
                session.close();
            }
        }

    }

}

上面代码我就不再多说了,因为和前面普通的取个表中的数据没啥区别。需要注意的是,我因为在数据表student中,将系名写成 Class ,然后myeclipse自动生成成了 getClass_ ,本应该是 getClass 的,但是多了个下划线,这是因为 getClass 和是object的一个方法,这会冲突的,所以自动加了下划线。(你建表最好别这样搞)。

  1. 我们下面来做些激动人心的事情,没错,你应该猜到hql语句写在哪了吧,写在 createQuery() 的参数里。
    我们先查查名字是 什么神奇 的人

    Query query =session.createQuery("from Student where sname=什么神奇"); //输出 什么神奇 20160912 男 计算机系

这样就ok了,输出结果也没问题。
但是我可不可以写成 select class_sno from Student 呢?答案是可以,但是下面要修改代码,因为,这只会得到一个属性 class_ ,就产生不了完整的 Student 对象了。所以下面的代码需要修改。

Query query =session.createQuery("select class_,sno from Student");
            List list = query.list();
            for(Object[] objs :list){
                System.out.println(objs[0].toString()+" "+objs[1].toString());
            }

上面的代码中,因为转换不了Student了(只有两个属性),所以,只能将本应该转换成的Student对象变成了 Object[] 的数组,每有一条满足条件的记录,就会产生一个这样的数组,然后可以通过Object[0]啊等序列来进行取值。结果如下:

计算机系 20160912
计算机系 20160913
建筑系 20160914
计算机系 20160915
建筑系 20160916

但是,我们最好不要这样写,官方并不推荐这样写hql语句,我们应该直接获得所有的属性,因为这件获得所有的属性是有好处的,这是sql语句所不能做到的。
下面我们来看看另外一种写法。
假如我们要取到什么神奇所选择的课程名,我们应该怎么做?我想仔细观察过自动生成的domain对象文件的人,应该会发现一些不同寻常的东西,没错,有个Set类型的属性,还有Student类型的属性等。很神奇,这,就给我们提供了一个非常快捷的获取方法。

Query query =session.createQuery(" from Studentcourse where student.sname=什么神奇");
            List list = query.list();
            for(Studentcourse studentcourse:list){
                System.out.println(studentcourse.getCourse().getCname());
            }

这很神奇, getCourse() 实际上给我们返回了一个 Course 对象,这个也就对应到了,这条记录相关的所有外码所涉及到的表,甚至在hql语句中,可以直接使用 student.sname ,因为, studentStudentcourse 的一个属性,类型是 Student
再来看稍微复杂一点的查询。
查看什么神奇的最高分和最低分

Query query =session.createQuery(" select max(socre),min(socre) from Studentcourse where student.sname=什么神奇");
List list = query.list();
            for(Object[] objs :list){
                for(Object a : objs){
                    System.out.println(a.toString());
                }
            }

输出

99
55

上面我们使用了聚集函数,hql常用的聚集函数有 count(),avg(),max(),min(),sum()
使用hql输出什么神奇及格了的课程名

Query query =session.createQuery(" select course.cname from Studentcourse where student.sname=什么神奇 and socre>=60");

List list = query.list();
            for(int  i=0;i

上面代码中,除了hql语句,我还写了个判断方法。因为这条hql语句生成的list的元素的类型只是 Object ,因为只有 course.cname 这一个属性,如果有多个属性,那么list元素的类型就是 Object[] 了。我干脆写了个通用的。需要注意的是,加上 else if 。因为所有的对象都属于Object的。当然,你可以直接写else这样转换也行,我只是为了大家方便看用于区分。如果返回的是整体的Student对象的话,你可以自己另行加 if 进行处理,上面的代码肯定是处理不了的。我这里就不写了。

4. 实现分页
  1. 最基础的分页实现
    下面将通过函数的形式来实现函数参数是一页显示的记录数,然后实现将所有数据做分页。
    (1)基于步骤化,我先写一个简单的取数据,从数据表中第几条开始,取几条的代码。这里,hibernate给我们提供了两个方法。
    setFirstResult(int) :这个方法是设置你要取的数据的起始位置,默认0是第一条。
    setMaxResults(int) :这个方法设置你取的数据的条数,就是记录的数目。
    依据上面两个方法,我们简单看看

    public class Test {

    public static void main(String[] args) {
        Session session = MySessionFactory.getSessionFactory().openSession();
        Transaction transaction =null;
        try {
            transaction = session.beginTransaction();
            List list =session.createQuery("from Student").setFirstResult(0).setMaxResults(2).list();
            for(Student stu:list){
                System.out.println(stu.getSno()+" "+stu.getSname()+" "
                        +stu.getSsex()+" "+stu.getClass_());
            }
            transaction.commit();
        } catch (Exception e) {
            if(transaction!=null){
                transaction.rollback();
            }
            throw new RuntimeException(e.getMessage());
        }
        finally {
            if(session!=null&&session.isOpen()){
                session.close();
            }
        }
    
    }

    } //输出 //20160912 什么神奇 男 计算机系 //20160913 小明 男 计算机系

输出了两条记录数。
(2)基于上面的方法,实现一个函数,参数是一页显示的数据条数,然后在控制台中将所有页码和页面内容打出
需要注意的是,分页的算法问题。
四个变量要牢记于心。
pageNow :当前页面页码
pageSize :当前页面显示的记录条数
pageCount :所有要显示的数据共有几页
dataCount :总共有多少条数据(记录)
需要知道的是依据总共有的数据(记录)条数来算出要显示几页。
pageCount = (dataCount-1)/pageSize+1 :这就是要显示几页的算法。
上面的算法也不难理解吧,dataCount只有为刚好除尽 pageSize 和除不尽两种情况(dataCount算0的也会同样得出0),无论是否,这个算法都是可行的。你可以自己举出例子。明白了这些,我们来开始写这个函数。

public static void getSomePageData(int pageSize){
        Session session = MySessionFactory.getSessionFactory().openSession();
        Transaction transaction =null;
        int pageNow =1;
        int pageCount=1;
        int dataCount=1;
        try {
            transaction = session.beginTransaction();
            dataCount = Integer.parseInt(session.createQuery("select count(*) from Student").list().get(0).toString());
            pageCount = (dataCount-1)/pageSize + 1;

            for(int i =1;i<=pageCount;i++){
            List list =session.createQuery("from Student")
                    .setFirstResult((i-1)*pageSize)
                    .setMaxResults(pageSize)
                    .list();
            System.out.println("**********"+"第"+i+"页"+"**********");
            for(Student stu :list){
                System.out.println(stu.getSno()+" "
                + stu.getSname()+" "
                + stu.getSsex()+" "
                + stu.getClass_());
            }
            }

            transaction.commit();
        } catch (Exception e) {
            if(transaction!=null){
                transaction.rollback();
            }
            throw new RuntimeException(e.getMessage());
        }
        finally {
            if(session!=null&&session.isOpen()){
                session.close();
            }
        }
    }

上面的代码中 pageNow 我们是没用上的,但是我写它是为了方便在下面的项目中去使用。先不理会这个变量。至于中间的 (i-1)*pageSize 需要慢慢体会吧,你带个值进去就知道了,其它的代码倒是没啥难点,如果你其它代码都没看懂,请返回本文章的开始去看。
(3) 使用线程局部模式来获得最近的 session
还记得我们在上面讲过

thread

上面是hibernate对于session上下文的配置,然后能使用 getCurrentSession 来获得当前线程的 session ,这个是必须要使用事物来进行提交的,而普通的 openSession 是不需要的,但是我们在上面讲的一些代码中,不论是 getCurrentSession 还是 openSession 都使用了事物提交的那个模板,其实也是没问题的,防止一些错误的发生总是没错的。
好,我们现在不想在xml文件里面配置了,我自己写一个叫做 HibernateUtil 工具类去完成这项操作。

package com.zzh.dbUtil;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
//使用线程局部模式
    private static SessionFactory sessionFactory= null;
    private static ThreadLocal threadlocal = new ThreadLocal();

    static{
        sessionFactory = new Configuration().configure().buildSessionFactory();
    }
    private HibernateUtil(){
    }
    public static Session openSession(){
        return sessionFactory.openSession();
    }
    public static Session getCurrentSession(){
        Session session = threadlocal.get();
        if(session==null){
            session = sessionFactory.openSession();
            threadlocal.set(session);
        }
        return session;
    }
}

上面代码需要注意的就是 ThreadLocal ,这玩意就是说你可以往里面添加一个属于本线程的变量,然后可以在这个线程内设置或者获得到它。只需要要清楚 get()set() 方法,一个是得到,一个是往里面添加。
然后我们把以前的获得session的代码换成是

Session session = HibernateUtil.getCurrentSession();

就ok了。
整个项目图看起来像这样

(4) 无聊时写了一个对于hql list返回的值类型学不同的处理

for(int i=0;i

我们将上面的代码封装到 HibernateUtil 文件中,形成一个方法。

public static void getPrintSomeIn(List list){
        for(int i=0;i

上面处理的代码直接复制粘贴吧。
(4) 使用预处理和防止sql注入的hql。
对于jdbc来说都有预处理让我们在先输入sql语句,然后输入变量的值。hql当然也行。总共有两种方法。

  1. setString(String name,String val) (hibernate5已经是setParameter)

    package com.zzh.test;

    import java.util.List;

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

    import com.zzh.dao.Course; import com.zzh.dao.Student; import com.zzh.dao.Studentcourse; import com.zzh.dbUtil.HibernateUtil; import com.zzh.dbUtil.MySessionFactory; public class Test {

    public static void main(String[] args) {
        Session session = HibernateUtil.getCurrentSession();
        Transaction transaction =null;
        try {
            transaction = session.beginTransaction();
            Query query = session.createQuery("select course.cname,socre from Studentcourse where student.sname=:name and socre<=:socre");
            query.setString("name", "什么神奇");
            query.setString("socre", "60");
            List list = query.list();
            for(int i=0;i

    }

上面的代码主要就是hql语句上有点不同。
使用 select course.cname,socre from Studentcourse where student.sname=:name and socre<=:socre 中的冒号后面的跟着就是下面对应的变量。也就是setString()中的name值。

  1. setString(int index,String val) (hibernate已经是setParameter)
    同样可以将冒号变成 ?+index 号来处理

    Query query = session.createQuery("select course.cname,socre from Studentcourse where student.sname=?0 and socre<=?1"); query.setString(0, "什么神奇"); query.setString(1, "60");

注意:如果纯粹的写 ? ,现在的hibernate已经不支持了。其实在hibernate5中,setString()方法已经过时了。我这里只是给大家举出来用用方便理解。使用最新的方法,请用 setParameter() 。在上面的代码中直接将 setString 替换成 setParameter 。我将在下面的教程中全部使用 setParameter
(5) 封装预处理
上面就是使用预处理而已,但是这样总是要 setParameter 去设定参数,很麻烦,我们可以自己写一个方法,来直接将我们要传的参数传进去。

public static List getQueryList(String hql,String... parameter){
        Session session =null;
        Transaction tr = null;
        List list=null;
        try {
            session =getCurrentSession();
            tr = session.beginTransaction();
            Query query = session.createQuery(hql);
            for(int i=0;i

上面的代码需要注意的是:我使用了可变参数 parameter ,其实这玩意就是一个 String[] 类型的数组而已。后面的代码新学的只有 setParameter 了吧。其它没啥好说的。
(6) 封装添加(增)
有了查询,不能没有添加数据啊。

public static void saveData(Object obj){
        Session session =null;
        Transaction tr = null;
        try {
            session =getCurrentSession();
            tr = session.beginTransaction();
            session.save(obj);

            tr.commit();
        } catch (Exception e) {
            if(tr!=null){
                tr.rollback();
            }
            throw new RuntimeException(e.getMessage());
        }
        finally{
            if(session!=null&&session.isOpen())
            session.close();
        }
    }

然后在 main 中运行一波

Student  stu = new Student();
stu.setSno("201609121");
stu.setSname("小青");
stu.setSsex("女");
stu.setClass_("计算机系");
HibernateUtil.saveData(stu);

而后一切ok。
如果你封装的代码看不懂,请回到教程开头。
(7) 封装修改和删除(改和删)

public static void executeUpdate(String hql,String... parameter){
        Session session =null;
        Transaction tr = null;
        try {
            session =getCurrentSession();
            tr = session.beginTransaction();
            Query query = session.createQuery(hql);
            for(int i=0;i

然后在main中写个

String hql ="delete from Student where sname=?0";
HibernateUtil.executeUpdate(hql, "小青");

发现删除成功
(8)可以在直接在配置文件中配置sql语句,详细如下图

(9) 项目图和源代码
项目图:

源代码
我这里只针对 HibernateUtil

package com.zzh.dbUtil;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;

import com.zzh.dao.Course;
import com.zzh.dao.Student;
import com.zzh.dao.Studentcourse;

public class HibernateUtil {
//使用线程局部模式
    private static SessionFactory sessionFactory= null;
    private static ThreadLocal threadlocal = new ThreadLocal();

    static{
        sessionFactory = new Configuration().configure().buildSessionFactory();
    }
    private HibernateUtil(){
    }
    public static Session openSession(){
        return sessionFactory.openSession();
    }
    public static Session getCurrentSession(){
        Session session = threadlocal.get();
        if(session==null){
            session = sessionFactory.openSession();
            threadlocal.set(session);
        }
        return session;
    }
    public static List getQueryList(String hql,String... parameter){
        Session session =null;
        Transaction tr = null;
        List list=null;
        try {
            session =getCurrentSession();
            tr = session.beginTransaction();
            Query query = session.createQuery(hql);
            for(int i=0;i
版权声明

所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除

热门