HQL(Hibernate Query Language)的学习
面向对象的查询语言,与SQL不同,HQL中的对象名是区分大小写的(除了JAVA类和属性其他部分不区分大写);HQL中查的是对象而不是表,并且支持多态;HQL主要通过Query来操作,Query的创建方式:
Query q =session.createQuery(hql);
hibernate 设计者 推荐我们在设计表的时候,应当每张表有一个主键,而且该主键最好不含业务逻辑
之所以要用到HQL语句是因为只靠前边的那些语句没办法做批量的增删改查,因为只能是一个一个的来做,这个是很不切实际的,真正应用的时候肯定是要出问题的。
举一个简单的例子来说明(同时保存查询的方法以供后续使用),例子是模拟学生选课系统,数据库方面需要三张表,分别是student course 和 studcourse(相当于当年数据库书上经典的enroll表,哈哈)
同样像是学习笔记(五)的流程,添加hibernate、逆向工程等等,现在就开始写查询了。
首先是检索所有学生:
private static void query1() {
Session session=null;
Transaction tx=null;
try {
session=HibernateUtil.getCurrentSession();
tx=session.beginTransaction();
//hql
//1.检索所有的学生
List<Student> list=session.createQuery("from Student").list();
//取出1. for 增强
for(Student s:list){
System.out.println(s.getSname()+" "+s.getSaddress());
}
System.out.println("*****************");
//2. 使用iterator
Iterator<Student> iterator=list.iterator();
while(iterator.hasNext()){
Student s=iterator.next();
System.out.println(s.getSname()+" "+s.getSage());
}
tx.commit();
} catch (Exception e) {
e.printStackTrace();
if(tx!=null){
tx.rollback();
}
throw new RuntimeException(e.getMessage());
// TODO: handle exception
}finally{
if(session!=null&&session.isOpen()){
session.close();
}
}
}
这里的查询使用了两种方法,分别是用 for循环和iterator迭代器,这种方式跟JDBC很相似,整个这个查询函数使用的也是之前提到的模板(try catch代码块)
接下来我们来检索部分字段,以前我们JDBC的时候,一般是要哪个直接就查询哪个,不要直接select * from 表名,因为那样会浪费很多也会慢,但是hibernate不是这样的,建议大家直接查询所有的东西放到list里,然后用到哪个数据字段再使用object取出需要的字段,这个浪费是有一点的,但是不会很严重,因为基本现实中的使用都会有分页,但是好处会很多:
private static void query2() {
Session session=null;
Transaction tx=null;
try {
session=HibernateUtil.getCurrentSession();
tx=session.beginTransaction();
//hql
//1.检索的学生名字和所在系
//原则: 在讲解jdbc我们曾说过, 要查询什么字段就查询什么字段,不要select * from 表
//但是在hibernate ,可以不遵循这个规则,建议把整个对象的属性都查询
//但是还是要讲解如何取出部分属性
List list=session.createQuery("select sname,sdept from Student").list();
for(int i=0;i<list.size();i++){
Object [] objs=(Object[]) list.get(i);
System.out.println(objs[0].toString()+" "+objs[1].toString());
}
System.out.println("****************");
//如果使用iterator
Iterator it=list.iterator();
while(it.hasNext()){
Object [] objs=(Object[]) it.next();
System.out.println(objs[0].toString()+" "+objs[1].toString());
}
tx.commit();
} catch (Exception e) {
e.printStackTrace();
if(tx!=null){
tx.rollback();
}
throw new RuntimeException(e.getMessage());
// TODO: handle exception
}finally{
if(session!=null&&session.isOpen()){
session.close();
}
}
}
这个里边object把list的每一个查询到的字段都拿出来了,我们用哪个直接就拿object的index来控制就可以了
List list=session.createQuery("select sname,sdept from Student").list();
我们要注意如果我们确定知道了查找返回的结果一定是只有一条记录或者是null的时候(例如使用主键查找),使用uniqueResult这个东西是更高效的,因为list的意思是整个过一遍,找到一条以后还会继续向后查找,但是如果你知道只有一条记录的话继续向后查找的工作肯定是浪费的,因此使用uniqueRequest的效率会更高:
Student s = (Student) session.createQuery("select * from Student where sid=1111").uniqueRequest();
下面的问题是过滤重复值的问题(有点像数据库课的作业,哈哈):
1.当要过滤重复的数据时,可以使用distinct关键字:(比如查年龄和性别难免会重复)
List list=session.createQuery("select distinct sage,ssex from Student").list();
for(int i=0;i<list.size();i++){
Object [] objs=(Object[]) list.get(i);
System.out.println(objs[0].toString()+" "+objs[1].toString());
}
2.计算年龄在20~22之间的学生:
List list=session.createQuery("select distinct sage,ssex,sname from Student where sage between 20 and 22").list();
for(int i=0;i<list.size();i++){
Object [] objs=(Object[]) list.get(i);
System.out.println(objs[0].toString()+" "+objs[1].toString()+objs[2].toString());
3.查询计算机系和外语系的学生信息
List<Student> list=session.createQuery("from Student where sdept in ('计算机系','外语系')").list();
//取出1. for 增强
for(Student s:list){
System.out.println(s.getSname()+" "+s.getSaddress()+" "+s.getSdept());
}
查找全部的时候可以直接返回对象,也可以同时省略select *
4.显示各个系的学生的平均年龄
List<Object[]> list=session.createQuery("select avg(sage),sdept from Student group by sdept").list();
//取出1. for 增强
for(Object[] obj:list){
System.out.println(obj[0].toString()+" "+obj[1].toString());
}
查找的是部分(只要年龄)没办法了,只能再回到object这个用法(查询语句和MySQL的sql语句用法就是一样的)
5.对分组查询后的结果,进行筛选:比如请显示人数大于3的系名称
List<Object[]> list=session.createQuery("select count(*) as c1,sdept from Student group by sdept having count(*)>3").list();
//取出1. for 增强
for(Object[] obj:list){
System.out.println(obj[0].toString()+" "+obj[1].toString());
6.查询女生少于200人的系
<span style="font-size:18px;">List<Object[]> list=session.
createQuery("select count(*) as c1,sdept from Student where ssex='F'
group by sdept").list();
//取出1. for 增强
for(Object[] obj:list){
System.out.println(obj[0].toString()+" "+obj[1].toString());
}</span>
7.按照学生年龄大小取出第3到第5个学生:
<span style="font-size:18px;">List<Object[]> list=session.createQuery("from Student order by sage").setFirstResult(2)
.setMaxResult(3).list();
//取出1. for 增强
for(Object[] obj:list){
System.out.println(obj[0].toString()+" "+obj[1].toString());
}</span>
这个setFirstResult是设置你一开始时的那个角标,setMaxResult是你一共要几个数据
分页显示查询结果的方法及代码:
public static void showResultByPage(int pageSize){
//设置分页变量
int pageNow = 1;
int pageCount = 1;//计算
int rowCount = 1;//这个需要自己查询
Session session=null;
Transaction tx=null;
try {
session=HibernateUtil.getCurrentSession();
tx=session.beginTransaction();
//查询出rowCount这个东西
rowCount = Integer.parseInt(session.createQuery("select COUNT(*) from students").uniqueResult().toString() );
pageCount = (rowCount - 1) / pageSize + 1;
//现在可以循环的显示每一页的信息
for (int i = 0; i < pageCount; i++) {
System.out.println("********第"+i+"页********");
List<Student> list = session.createQuery("from Student").setFirstResult((i-1)*pageSize).setMaxResults(pageSize).list();
for (Student s:list) {
System.out.println(s.getSname()+" "+s.getSdept());
}
}
tx.commit();
} catch (Exception e) {
e.printStackTrace();
if(tx!=null){
tx.rollback();
}
throw new RuntimeException(e.getMessage());
// TODO: handle exception
}finally{
if(session!=null&&session.isOpen()){
session.close();
}
}
}
这个函数传进去的参数是要把结果分成几页,注意pageCount(每页有几个数据)的计算过程