org.hibernate.ObjectNotFoundException
异常针对于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对象中的属性名!。
参考这里,本来弄了个视频的,我看看能不能传上来。
这个语句能让程序员不用再纠结过多sql语句和数据库的问题。
这是hibernate
自带的自身的语句,可以理解为是在对象上进行操作的sql语句。我们平时总是说对象关系映射,那么当完成了关系模型到对象的映射就可以使用hql语句来对对象之间操作进而之间操作数据库了。
下面,将使用这个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的一个方法,这会冲突的,所以自动加了下划线。(你建表最好别这样搞)。
2. 我们下面来做些激动人心的事情,没错,你应该猜到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;iif(list.get(i) instanceof Object[]){
Object[] objs =(Object[])list.get(i);
for(Object s :objs){
System.out.println(s.toString());
}
}
else if(list.get(i) instanceof Object){
Object obj =list.get(i);
System.out.println(obj.toString());
}
}
上面代码中,除了hql语句,我还写了个判断方法。因为这条hql语句生成的list的元素的类型只是Object
,因为只有course.cname
这一个属性,如果有多个属性,那么list元素的类型就是Object[]
了。我干脆写了个通用的。需要注意的是,加上else if
。因为所有的对象都属于Object的。当然,你可以直接写else这样转换也行,我只是为了大家方便看用于区分。如果返回的是整体的Student对象的话,你可以自己另行加if
进行处理,上面的代码肯定是处理不了的。我这里就不写了。
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
还记得我们在上面讲过
<property name="current_session_context_class">threadproperty>
上面是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;iif(list.get(i) instanceof Student){
Student stu = (Student)list.get(i);
System.out.println(stu.getSno()+" "+stu.getSname()+" "+stu.getSsex()+" "+stu.getClass_());
}
else if(list.get(i) instanceof Studentcourse){
Studentcourse stuc = (Studentcourse)list.get(i);
Student stu = stuc.getStudent();
System.out.println(stu.getSno()+" "+stu.getSname()+" "+stuc.getCourse().getCname()+" "+stuc.getSocre());
}
else if(list.get(i) instanceof Course){
Course cou = (Course)list.get(i);
System.out.println(cou.getCno()+" "+cou.getCname());
}
else if(list.get(i) instanceof Object[]){
Object[] objs =(Object[])list.get(i);
for(Object obj:objs){
System.out.print(obj.toString()+" ");
}
}
else {
for(Object obj:list){
System.out.println(obj.toString());
}
}
}
我们将上面的代码封装到HibernateUtil
文件中,形成一个方法。
public static void getPrintSomeIn(List list){
for(int i=0;iif(list.get(i) instanceof Student){
Student stu = (Student)list.get(i);
System.out.println(stu.getSno()+" "+stu.getSname()+" "+stu.getSsex()+" "+stu.getClass_());
}
else if(list.get(i) instanceof Studentcourse){
Studentcourse stuc = (Studentcourse)list.get(i);
Student stu = stuc.getStudent();
System.out.println(stu.getSno()+" "+stu.getSname()+" "+stuc.getCourse().getCname()+" "+stuc.getSocre());
}
else if(list.get(i) instanceof Course){
Course cou = (Course)list.get(i);
System.out.println(cou.getCno()+" "+cou.getCname());
}
else if(list.get(i) instanceof Object[]){
Object[] objs =(Object[])list.get(i);
for(Object obj:objs){
System.out.print(obj.toString()+" ");
}
}
else {
for(Object obj:list){
System.out.println(obj.toString());
}
}
}
}
上面处理的代码直接复制粘贴吧。
(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;iif(list.get(i) instanceof Student){
Student stu = (Student)list.get(i);
System.out.println(stu.getSno()+" "+stu.getSname()+" "+stu.getSsex()+" "+stu.getClass_());
}
else if(list.get(i) instanceof Studentcourse){
Studentcourse stuc = (Studentcourse)list.get(i);
Student stu = stuc.getStudent();
System.out.println(stu.getSno()+" "+stu.getSname()+" "+stuc.getCourse().getCname()+" "+stuc.getSocre());
}
else if(list.get(i) instanceof Course){
Course cou = (Course)list.get(i);
System.out.println(cou.getCno()+" "+cou.getCname());
}
else if(list.get(i) instanceof Object[]){
Object[] objs =(Object[])list.get(i);
for(Object obj:objs){
System.out.print(obj.toString()+" ");
}
}
else {
for(Object obj:list){
System.out.println(obj.toString());
}
}
}
transaction.commit();
} catch (Exception e) {
if(transaction!=null){
transaction.rollback();
}
throw new RuntimeException(e.getMessage());
}
finally {
if(session!=null&&session.isOpen()){
session.close();
}
}
}
}
上面的代码主要就是hql语句上有点不同。
使用select course.cname,socre from Studentcourse where student.sname=:name and socre<=:socre
中的冒号后面的跟着就是下面对应的变量。也就是setString()中的name值。
2. 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;icatch (Exception e) {
if(tr!=null){
tr.rollback();
}
throw new RuntimeException(e.getMessage());
}
finally{
if(session!=null&&session.isOpen())
session.close();
}
return list;
}
上面的代码需要注意的是:我使用了可变参数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;icatch (Exception e) {
if(tr!=null){
tr.rollback();
}
throw new RuntimeException(e.getMessage());
}
finally{
if(session!=null&&session.isOpen())
session.close();
}
}
然后在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;icatch (Exception e) {
if(tr!=null){
tr.rollback();
}
throw new RuntimeException(e.getMessage());
}
finally{
if(session!=null&&session.isOpen())
session.close();
}
return list;
}
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();
}
}
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;icatch (Exception e) {
if(tr!=null){
tr.rollback();
}
throw new RuntimeException(e.getMessage());
}
finally{
if(session!=null&&session.isOpen())
session.close();
}
}
public static void getPrintSomeIn(List list){
for(int i=0;iif(list.get(i) instanceof Student){
Student stu = (Student)list.get(i);
System.out.println(stu.getSno()+" "+stu.getSname()+" "+stu.getSsex()+" "+stu.getClass_());
}
else if(list.get(i) instanceof Studentcourse){
Studentcourse stuc = (Studentcourse)list.get(i);
Student stu = stuc.getStudent();
System.out.println(stu.getSno()+" "+stu.getSname()+" "+stuc.getCourse().getCname()+" "+stuc.getSocre());
}
else if(list.get(i) instanceof Course){
Course cou = (Course)list.get(i);
System.out.println(cou.getCno()+" "+cou.getCname());
}
else if(list.get(i) instanceof Object[]){
Object[] objs =(Object[])list.get(i);
for(Object obj:objs){
System.out.print(obj.toString()+" ");
}
}
else {
for(Object obj:list){
System.out.println(obj.toString());
}
}
}
}
}