分页查询
MySQL实现分页
- 使用关键字 limit 实现
select * from user limit 0,3
在hql中实现分页
- 在hql操作中,在语句里面不能写limit。
@Test
public void showSelectPage(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction=session.beginTransaction();
//查询要分页的数据
Query query=session.createQuery("from Role");
//设置开始位置
query.setFirstResult(2);
//设置每页的数据量
query.setMaxResults(3);
List<Role> roles=query.list();
for(Role i:roles){
System.out.println(i);
}
transaction.commit();
}
查询通过分页查询,查询两个字段:
@Test
public void showSelectPage(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction=session.beginTransaction();
//查询要分页的数据
Query query=session.createQuery("select rname,rmemo from Role");
//设置开始位置
query.setFirstResult(2);
//设置每页的数据量
query.setMaxResults(3);
List<Object[]> roles=query.list();
for(Object i[]:roles){
System.out.println((String)i[0]+"----"+(String)i[1]);
}
transaction.commit();
}
//在里面查询单个值的时候可以用String,Integer等,查询多个值的时候要用Object。
投影查询
投影查询:查询不是所有字段值,而是部分字段的值。
投影查询hql语法:
- select 实体类属性名称1,实体类属性名称2 from 实体类名称
- select 后面不能写 “*” 号,Hibernate是不支持的。
其实在上面我们已经用到了投影查询。
面再作演示:
@Test
public void showTouYingAgain(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction=session.beginTransaction();
Query<String> query=session.createQuery("select rname from Role");//Query加泛型的原因是笔者用的版本从5.1更新到5.2.8后,Hibernate对Query加了泛型。
for(String i:query.list()){
System.out.println(i);
}
transaction.commit();
}
HQL多表查询
HQL多表查询:
- 内连接
- 左外连接
- 右外连接
- 迫切内连接
- 迫切左外连接
//MySQL语句写法
//内连接查询,查询左右表同时满足的数据
//内连接不作条件处理,相当于对两个表做了排列组合。
select * from customer c,linkman l where c.cid=l.clid
select * from customer c inner join linkman l on c.cid=l.clid
//左外连接
//把左表的数据全部输出来,右表只做关联显示,不管是否满足都会显示。
select * from customer c left outer join linkman l on c.cid=l.clid
//右外连接
//同样,右边显示全部,左边显示关联数据
select * from customer c right outer join linkman l on c.cid=l.clid
什么是左外连接和右外连接:
两个表:
A(id,name)
数据:(1,张三)(2,李四)(3,王五)
B(id,name)
数据:(1,学生)(2,老师)(4,校长)
左连接结果:
select A.*,B.* from A left join B on A.id=B.id;
1 张三 1 学生
2 李四 2 老师
3 王五 NULL NULL
右链接结果:
select A.*,B.* from A right join B on A.id=B.id;
1 张三 1 学生
2 李四 2 老师
NULL NULL 4 校长
****************
补充:下面这种情况就会用到外连接
比如有两个表一个是用户表,一个是交易记录表,如果我要查询每个用户的交易记录就要用到左外外连接,因为不是每个用户都有交易记录。
用到左外连接后,有交易记录的信息就会显示,没有的就显示NULL,就像上面我举得例子一样。
如果不用外连接的话,比如【王五】没有交易记录的话,那么用户表里的【王五】的信息就不会显示,就失去了查询所有用户交易记录的意义了。
****************
看一下结果就能明白左右连接的区别了。
HQL内连接:
- 这里是用的多对多表关系。
//通过where限制
@Test
public void showAgain(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
Query<Object> query=session.createQuery("select r.rname from Role r inner join r.userSet c where r.rname!=c.uname");
for(Object i:query.list()){
System.out.println(i);
}
transaction.commit();
}
//输出结果:
Hibernate:
select
role0_.rname as col_0_0_
from
role role0_
inner join
user_role userset1_
on role0_.rid=userset1_.frid
inner join
user user2_
on userset1_.fuid=user2_.uid
where
role0_.rname<>user2_.uname
开发工程师
架构师
狼神总部副总裁
图像算法师
骇客
@Test
public void showNormal(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
Query<Object> query=session.createQuery("select r.rname from Role r inner join r.userSet");
for(Object i:query.list()){
System.out.println(i);
}
transaction.commit();
}
//注意这个查询并没有写条件。所以这个hql语句只是把,三张表连接起来了而已。
//输出结果:
Hibernate:
select
role0_.rname as col_0_0_
from
role role0_
inner join
user_role userset1_
on role0_.rid=userset1_.frid
inner join
user user2_
on userset1_.fuid=user2_.uid
开发工程师
架构师
狼神总部副总裁
图像算法师
骇客
//通过 on 来进行限制
@Test
public void showAgain(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
Query<Object> query=session.createQuery("select r.rname from Role r inner join r.userSet c on r.rname!=c.uname");
for(Object i:query.list()){
System.out.println(i);
}
transaction.commit();
}
//输出结果:
Hibernate:
select
role0_.rname as col_0_0_
from
role role0_
inner join
(
user_role userset1_
inner join
user user2_
on userset1_.fuid=user2_.uid
)
on role0_.rid=userset1_.frid
and (
role0_.rname<>user2_.uname
)
开发工程师
架构师
狼神总部副总裁
图像算法师
骇客
迫切内连接
- 迫切内连接和内连接底层实现都一样
- 区别:使用内连接返回list中每部分是数组,迫切内连接返回list每部分是对象。
hql的语法写法:
from Role c inner join fetch c.userSet
@Test
public void shoFetchLink(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction=session.beginTransaction();
Query<Role> query=session.createQuery("from Role r inner join fetch r.userSet");//注意:用迫切内连接是不能够只查表中的一部分数据。否则返回的是一个数组。
List<Role> result=query.list();
for(Role i:result){
System.out.println(i);
}
transaction.commit();
}
//输出结果:
Hibernate:
select
role0_.rid as rid1_0_0_,
user2_.uid as uid1_1_1_,
role0_.rname as rname2_0_0_,
role0_.rmemo as rmemo3_0_0_,
user2_.uname as uname2_1_1_,
user2_.pword as pword3_1_1_,
userset1_.frid as frid1_2_0__,
userset1_.fuid as fuid2_2_0__
from
role role0_
inner join
user_role userset1_
on role0_.rid=userset1_.frid
inner join
user user2_
on userset1_.fuid=user2_.uid
Role [rid=1, rname=开发工程师, rmemo=专注于网页前后台开发,和图像算法!!]
Role [rid=2, rname=架构师, rmemo=进行程序开发架构!]
Role [rid=3, rname=狼神总部副总裁, rmemo=国际知名企业!!]
Role [rid=4, rname=图像算法师, rmemo=通过代码对程序的算法处理!以及识别!!]
Role [rid=6, rname=骇客, rmemo=网络安全工程师]
HQL左外连接
左外连接HQL语句
from Customer c left outer join c.setLinkMan
- 迫切左外连接:
from Customer c left outer join fetch c.setLinkMan
//左外连接
@Test
public void shoFetchLink(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction=session.beginTransaction();
Query<Role> query=session.createQuery("from Role r inner join fetch r.userSet");
List<Role> result=query.list();
for(Role i:result){
System.out.println(i);
}
transaction.commit();
}
//因为我们这里的主表是Role所以我们会查出四个结果,同时也会输出两个表的信息。
//输出结果:
Hibernate:
select
role0_.rid as rid1_0_0_,
user2_.uid as uid1_1_1_,
role0_.rname as rname2_0_0_,
role0_.rmemo as rmemo3_0_0_,
user2_.uname as uname2_1_1_,
user2_.pword as pword3_1_1_
from
role role0_
left outer join
user_role userset1_
on role0_.rid=userset1_.frid
left outer join
user user2_
on userset1_.fuid=user2_.uid
Role [rid=1, rname=架构师, rmemo=进行程序开发架构!]
Hibernate:
select
roleset0_.fuid as fuid2_2_0_,
roleset0_.frid as frid1_2_0_,
role1_.rid as rid1_0_1_,
role1_.rname as rname2_0_1_,
role1_.rmemo as rmemo3_0_1_
from
user_role roleset0_
inner join
role role1_
on roleset0_.frid=role1_.rid
where
roleset0_.fuid=?
User [uid=1, uname=LangSheng, pword=123456, roleSet=[Role [rid=1, rname=架构师, rmemo=进行程序开发架构!], Role [rid=2, rname=开发工程师, rmemo=专注于网页前后台开发,和图像算法!!]]]
Role [rid=2, rname=开发工程师, rmemo=专注于网页前后台开发,和图像算法!!]
User [uid=1, uname=LangSheng, pword=123456, roleSet=[Role [rid=1, rname=架构师, rmemo=进行程序开发架构!], Role [rid=2, rname=开发工程师, rmemo=专注于网页前后台开发,和图像算法!!]]]
Role [rid=3, rname=图像算法师, rmemo=通过代码对程序的算法处理!以及识别!!]
Hibernate:
select
roleset0_.fuid as fuid2_2_0_,
roleset0_.frid as frid1_2_0_,
role1_.rid as rid1_0_1_,
role1_.rname as rname2_0_1_,
role1_.rmemo as rmemo3_0_1_
from
user_role roleset0_
inner join
role role1_
on roleset0_.frid=role1_.rid
where
roleset0_.fuid=?
User [uid=2, uname=FireLang, pword=456123, roleSet=[Role [rid=4, rname=狼神总部副总裁, rmemo=国际知名企业!!], Role [rid=3, rname=图像算法师, rmemo=通过代码对程序的算法处理!以及识别!!]]]
Role [rid=4, rname=狼神总部副总裁, rmemo=国际知名企业!!]
User [uid=2, uname=FireLang, pword=456123, roleSet=[Role [rid=4, rname=狼神总部副总裁, rmemo=国际知名企业!!], Role [rid=3, rname=图像算法师, rmemo=通过代码对程序的算法处理!以及识别!!]]]
//在第二个for循环里面,有两个对象,一个是Role,另外一个是User
迫切左外连接:
@Test
public void showLeftOuterJoinfetch(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction=session.beginTransaction();
Query<Role> query=session.createQuery("from Role r left outer join fetch r.userSet");
List<Role> users=query.list();
for(Role i:users){
System.out.println(i);
}
transaction.commit();
}
Hibernate:
select
role0_.rid as rid1_0_0_,
user2_.uid as uid1_1_1_,
role0_.rname as rname2_0_0_,
role0_.rmemo as rmemo3_0_0_,
user2_.uname as uname2_1_1_,
user2_.pword as pword3_1_1_,
userset1_.frid as frid1_2_0__,
userset1_.fuid as fuid2_2_0__
from
role role0_
left outer join
user_role userset1_
on role0_.rid=userset1_.frid
left outer join
user user2_
on userset1_.fuid=user2_.uid
Role [rid=1, rname=架构师, rmemo=进行程序开发架构!]
Role [rid=2, rname=开发工程师, rmemo=专注于网页前后台开发,和图像算法!!]
Role [rid=3, rname=图像算法师, rmemo=通过代码对程序的算法处理!以及识别!!]
Role [rid=4, rname=狼神总部副总裁, rmemo=国际知名企业!!]
右外连接:
from Customer c right outer join c.setLinkMan
QBC查询
查询所有
- 创建Criteria对象
- 调用方法就可以得到
Java代码:
@Test
public void showCriteria(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction=session.beginTransaction();
Criteria criteria=session.createCriteria(Customer.class);
List<Customer> customers=criteria.list();
for(Customer i:customers){
System.out.println(i);
}
transaction.commit();
}
条件查询:
@Test
public void showCriteriaTiaoJian(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction=session.beginTransaction();
Criteria criteria=session.createCriteria(Customer.class);
//使用add方法,表示设置条件值。
criteria.add(Restrictions.like("custName", "%科技%"));
//这里可以写多个add来进行添加条件。
List<Customer> customers=criteria.list();
for(Customer i:customers){
System.out.println(i);
}
transaction.commit();
}
排序查询:
@Test
public void orderX(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction=session.beginTransaction();
Criteria criteria=session.createCriteria(Customer.class);
criteria.addOrder(Order.desc("cid"));//这里Order有两个静态方法,一个是desc(),一个是asc();
List<Customer> customers=criteria.list();
for(Customer i:customers){
System.out.println(i);
}
transaction.commit();
}
分页查询:
@Test
public void showLimit(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction=session.beginTransaction();
Criteria criteria=session.createCriteria(Customer.class);
//设置开始的位置
criteria.setFirstResult(0);
//设置每页显示的个数
criteria.setMaxResults(3);
List<Customer> customers=criteria.list();
for(Customer i:customers){
System.out.println(i);
}
transaction.commit();
}
//开始位置计算公式:(当前页-1)*每页记录数
统计查询:
@Test
public void showCount(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction=session.beginTransaction();
Criteria criteria=session.createCriteria(Customer.class);
//设置要使用的聚集函数
criteria.setProjection(Projections.rowCount());
Object count=criteria.uniqueResult();
int countnum=((Long)count).intValue();
System.out.println(countnum);
transaction.commit();
}
离线查询:
- 跟session没有关联的操作就是离线查询。
- 离线查询能够和Criteria一样,能够查询很多东西,比如统计查询、条件查询等。
- 但是最终我们要进行获取数据库中的数据时,还是会通过session来查询数据库。
//这是一个离线查询的简单演示。到了后面我们还会详细介绍。
@Test
public void showDatched(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction=session.beginTransaction();
DetachedCriteria detachedCriteria=DetachedCriteria.forClass(Customer.class);
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
List<Customer> customers=criteria.list();
for(Customer i:customers){
System.out.println(i);
}
transaction.commit();
}
为什么要用离线查询??大家可以看到一个特点,那就是离线查询能够延迟查询的时间或者说是能够让查询的逻辑代码在未执行前与session无关,也就是说我们能够在Servlet层或者其它层把查询的逻辑代码写出来,再传到Dao层进行查询。
session.createCriteria(Role.class);//这个方法以及过时了,官方说要用Jpa Criteria。其中有很多Jpa概念笔者还不懂,在后续我会更新的。
SQLQuery查询
SQLQuery在Hibernate5.2.8里面已经过时了。取而代之的是NativeQuery,但是这个NativeQuery是个接口,而且这个接口里面的方法不能够作基本用途,所以我们要用它的实现类来做载体,NativeQueryImpl。下面是个简单例子:
我这里用的是Hibernate5.2.8的jar包
@Test
public void showSigleQuery(){
Session session=HibernateUtils.getSessionFactory().openSession();
Transaction transaction=session.beginTransaction();
NativeQueryImpl<User> nativeQuery=(NativeQueryImpl<User>)session.createNativeQuery("select * from user");
List<User> list=nativeQuery.addEntity(User.class).list();
for(User i:list){
System.out.println(i);
}
transaction.commit();
}
几种查询后我想说个东西:对于查询的方法我们已经学了三个了QBC查询、HQL查询、SQLQuery查询。这三个里面有很多都能够互相替代。所以在这两个里面不管我们用什么,只要我们熟悉那个就用哪个。公司要求用哪个就用哪个。没有绝对的限制。
Hibernate检索策略
Hibernate检索策略分为两类:
- 立即查询:根据id查询,调用get方法,一调用get方法马上发送语句查询数据库。
Customer customer=session.get(Customer.class,1);
System.out.println(customer.getCid());
- 延迟查询:根据id查询,调用load方法,调用load方法并不会马上发送语句查询数据库,只有要得到对象里面的值的时候才会发送语句查询数据库。其中调用load方法会返回一个对象
该对象里面只有一个值,那就是id。当我们要得到id的时候并不会发送语句查询数据库,只有要得到除了id以外的数据时才会查询数据库。
Customer customer=session.load(Customer.class,1);
System.out.println(customer.getCid());
System.out.println(customer.getCustName());
延迟查询分成两类:
类级别延迟:根据id查询返回实体类对象,调用load方法不会马上发送语句。上面调用load方法就是类级别延迟。
关联级别延迟:查询某个客户,在查询这个客户的所有联系人,查询客户的所有联系人的过程是否需要延迟,这个过程称为关联级别延迟。
默认延迟效果:
//这个时候会查询id为1的客户
Customer customer = session.get(Customer.class,1);
//这个步骤在Hibernate中作了优化的。不会发送语句查询数据库。
Set<LinkMan> linkman=customer.getSetLinkMan();
//发送语句
System.out.println(linkman.size());
关联级别延迟操作设置
在映射文件中进行配置实现
这里是一对多操作,客户一,联系人多。所以在客户里面进行配置。
在set标签上使用属性
- fetch:值select(默认)
- lazy:值
true:延迟(默认)
false:不延迟
extra:极其延迟
fetch="select" lazy="true"
//效果和上面的关联延迟效果一样。
fetch="select" lazy="false"
//不延迟,查询值的时候直接把值给查询出来,比如查询上面Set<LinkMan> linkman=customer.getSetLinkMan();的时候就会发送语句查询数据库。并不会发生延迟
fetch="select" lazy="extra"
//极其延迟,要什么给什么值,比如上面我们Set<LinkMan> linkman=customer.getSetLinkMan();这个步骤不会发送语句查询数据库。在linkman.size()的时候才会发送语句查询数据库
//但是查询数量的时候并不是查询出所有的联系人再数数量,而是通过sql函数count查询出值。明显效率比较高。
批量抓取
现在我们有个需求:查询所有客户,返回list集合,遍历list集合,得到每个客户,得到每个客户的所有联系人。
演示问题:
这里使用的是一对多的关系。
配置文件:
<set name="linkMans" cascade="save-update" inverse="true" >
<key column="clid"></key>
<one-to-many class="cn.domarvel.entity.LinkMan"/>
</set>
//一般我们写的代码:
@Test
public void showAll(){
Session session=HibernateUtils.getCurrentSession();
Transaction transaction=session.beginTransaction();
Query query=session.createQuery("from Customer");
List<Customer> customers=query.list();
for(Customer i:customers){
System.out.println(i.getCustName());
for(LinkMan j:i.getLinkMans()){
System.out.println(j);
}
System.out.println("##############################");
}
transaction.commit();
}
//据说这样效率不行。查询数据库次数太多。
为了改善查询数据库次数太多:
我们这里引入一个配置属性:batch-size,属性值为>=1,指的是一次查询数据的多少。1为一条,2为2条,默认为1条。值的设置和你本身的需求有关。
示例配置:
<set name="linkMans" cascade="save-update" inverse="true" batch-size="10">
<key column="clid"></key>
<one-to-many class="cn.domarvel.entity.LinkMan"/>
</set>