hibernate学习手记之使用hql
原创3.Session接口
主要功能和作用:
- Session一个实例代表与数据库一次操作(可以是crud组合)
- Session实例通过SessionFactory获取,用完需要关闭。
- Sessions是线程不同步的,因此要保证在同一线程中使用,可以用getCurrentSession().
- Session可以看做是持久化管理器,它是与持久化操作相关的接口。
get()和load()的区别:
- get()方法直接返回实体类,如果查不到数据就返回null。load()则会返回一个实体代理对象(当前这个对象可以转化为实体对象),当代理对象被调用时,如果没有数据,就会抛出
org.hibernate.ObjectNotFoundException
异常 - load()是先在缓存(session缓存/二级缓存)中去找数据,如果缓存中没有,就直接返回实体代理对象,当对这个代理对象操作之后,才会在db中去查询。这就是我们常说的load在默认情况下支撑延迟加载(lazy)。
- 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语句的讲解。
-
在数据库中建立这三张表。
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的一个方法,这会冲突的,所以自动加了下划线。(你建表最好别这样搞)。
-
我们下面来做些激动人心的事情,没错,你应该猜到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
上面的代码中,因为转换不了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
,因为, student
是 Studentcourse
的一个属性,类型是 Student
。
再来看稍微复杂一点的查询。
查看什么神奇的最高分和最低分
Query query =session.createQuery(" select max(socre),min(socre) from Studentcourse where student.sname=什么神奇");
List
输出
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)基于步骤化,我先写一个简单的取数据,从数据表中第几条开始,取几条的代码。这里,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当然也行。总共有两种方法。
-
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值。
-
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
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除