HIbernate中的HQL查询

Hibernate中的查询方式:
1,使用HQL:使用hibernate提供的面向对象的查询语句;
2,使用SQL:在hibernate中允许使用原生的SQL直接查询;
3,使用Criteria:hibernate提供的完全的面向对象的查询方式;

1,HQL:
HQL的学习方法HQL是面向对象的,但是HQL借鉴了SQL的语法结构,把SQL中关系模型的概念替换成面向对象的概念;

		//HQL是模型对象的,但是HQL借鉴了SQL的语法结构,把SQL中的关系模型的概念替换为面向对象的概念
String hql = "SELECT e FROM Employee e WHERE e.name LIKE ? AND e.id BETWEEN ? AND ? ";
List<Employee> ems = session.createQuery(hql)
.setParameter(0, "%a%")
.setParameter(1, 1L)
.setParameter(2, 10L).list();
System.out.println(ems);

2,SQL:使用session.createQuery来创建基于SQL的查询,
查询出来的结果是Object[]的集合;

		//使用SQL查询到的结果集是Object[] 的列表(list)
String sql = "select * from employee where name like ? and id between ? and ?";
List<Object[]> ret = session.createSQLQuery(sql)
.setParameter(0, "%a%")
.setParameter(1, 1L)
.setParameter(2, 10L).list();
for (int i = 0; i < ret.size(); i++) {
System.out.println(Arrays.toString(ret.get(i)));
}

3,Criteria:完全的面向对象的查询,所有的查询及条件的拼装都是通过Criteria对象的方法完成的(使用较少);

		//select * from employee
//List<Employee> ret = session.createCriteria(Employee.class).list();
List<Employee> ret = session.createCriteria(Employee.class)
.add(Restrictions.like("name", "a",MatchMode.ANYWHERE))
.add(Restrictions.between("id", 1L, 10L)).list();
System.out.println(ret);

选择:
1,HQL:面向对象的查询,查询出来的实体都是持久化的,hibernate为HQL做了很多的查询相关的优化,一般来说,对于简单的查询,我们都可以使用HQL(我们都是学过SQL的人)
2,SQL:对于性能要求较高的查询,我们一般直接使用SQL来完成查询;
3,Criteria:完全面向对象的,学习非常简单,对于某些简单的查询,可以直接使用Criteria,对于稍微复杂一点的查询,Criteria完全没有办法处理

分页查询:
1,分页需要些什么东西?总条数,每一页需要多少条数据,当前是第几页,当前页的数据;
2,查询当前页的数据,对于mysql来说,LIMIT ?,?
3,使用query.setFirstResult()方法来设置从第几条数据开始查询;
4,使用query.setMaxResult()方法来设置查询多少条数据;
5,setFristResult和setMaxResult对于SQL和Criteria的查询都有效;

		String hql = "select e from Employee e where e.name like ? and e.id between ? and ?";
List<Employee> ems = session.createQuery(hql).setParameter(0, "%a%").setParameter(1, 1L).setParameter(2, 10L)
.setFirstResult((currentPage -1)*pageSize)//setFirstResult==limit的第一个参数,代表从第几条数据开始查询
.setMaxResults(pageSize)//setMaxResults==limit的第二个参数,代表最大查询多少条数据
.list();
System.out.println(ems);

查询总条数:
但是使用这种方式非常的不方便,因为我们知道我们查询出来的结果就只有一行数据;

		String hql = "select count(e) from Employee e";
//在count中写e比写e.id要好,因为hibernate可以自动根据映射文件找到Employee的主键列,并使用主键列来替换count的内容
List<Long> count = session.createQuery(hql).list();
System.out.println(count.get(0));

使用query.uniqueResult()方法;

		//使用uniqueResult,这个方法可以真正的去执行查询
//注意,这个方法只能用在我们确定结果集只有一行数据的时候,如果查询结果多于一行,则报错
//uniqueResult方法对HQL和Criteria都有效
Long count = (Long) session.createQuery("select count(e) from Employee e").uniqueResult();
System.out.println(count);

  

查询参数设置:
位置占位符:就是使用?号来代表查询参数,通过setParameter(index,object)来根据?的位置来设置参数的;
1,写HQL的时候很方便;
2,如果参数值不多,还是比较容易识别;
3,如果参数值过多,会造成索引不容易识别;如果调整参数位置,所有的设置参数的位置都要变;如果一个参数在多个条件中使用,必须重复设置;

名称占位符:
1,使用 :参数名称 格式来添加名称占位符;

		String hql = "select e from Employee e where e.name like :name and e.id between :low and :hi";
List<Employee> ret = session.createQuery(hql)
.setParameter("name", "%a%")
.setParameter("low", 1L)
.setParameter("hi", 10L).list();
System.out.println(ret);

2,使用setParamter(String name,object)这个方法为名称占位符添加参数;
3,可以为多个参数起相同名字的名称占位符,在设置参数的时候只需要设置一次值,就可以在所有的位置设置好参数;
4,使用名称占位符可以给参数传列表值进去,很容易的完成in等查询;但是使用位置占位不行(只能直接拼在HQL里面);

		String hql = "select e from Employee e where e.id in (:ids)";
List<Employee> ret = session.createQuery(hql).setParameterList("ids", new Long[]{1L, 2L,3L}).list();
System.out.println(ret);

3,可以通过setEntity方法直接给HQL设置一个实体对象的参数,hibernate会自动的根据实体的关系,创建好对应的SQL

		Department dept = new Department();
dept.setId(1L);
//在hibernate中可以直接给查询语句的参数设置一个实体对象(可以使用位置占位符或者名称占位符)
String hql = "select e from Employee e where e.dept = ?";
List<Employee> ret = session.createQuery(hql).setEntity(0, dept).list();
System.out.println(ret);

  

查询结果:
1,查询一个实体对象;
  直接查询实体对象返回的是实体对象的列表;注意,这个列表中所有的对象都是持久化对象,所以如果查询的数据量过大,记得分页+及时清空一级缓存;
2,投影查询;
  1,查询一个简单属性;

		//查询一个简单属性,返回该属性类型的list集合
List<String> ret = session.createQuery("select e.name from Employee e").list();
System.out.println(ret); //如果查询的属性是一个实体对象,返回这个实体对象的列表
//这个列表里的所有对象也都是持久化对象
//使用属性的导航查询(e.dept),此处使用join来连接查询
List<Department> dept = session.createQuery("select e.dept from Employee e").list();
System.out.println(dept); //查询多个简单属性,返回Object[]类型的list集合
List<Object[]> ret = session.createQuery("select e.name,e.salary from Employee e").list();
for (int i = 0; i < ret.size(); i++) {
System.out.println(Arrays.toString(ret.get(i)));
} //查询多个简单属性并且其中包含实体对象属性,返回Object[]类型的list集
//查询出来的实体对象都是持久化的对象
List<Object[]> ret = session.createQuery("select e.name,e.salary,e.dept from Employee e").list();
for (int i = 0; i < ret.size(); i++) {
System.out.println(Arrays.toString(ret.get(i)));
}

hibernate查询结果的封装:
员工的id,员工的工资,员工的姓名,员工对应部门的编号和部门名称,员工所在的城市

		//返回一个Object[]类型的list集合(1)
String hql = "select e.id,e.name,e.salary,e.dept.name,e.dept.address.city,e.dept.sn from Employee e";
List<Object[]> ret = session.createQuery(hql).list();
for (Object[] os : ret) {
System.out.println(Arrays.toString(os));
} //使用new list把每一行数据包装成list对象(2)
String hql = "select new list(e.id,e.name,e.salary,e.dept.name,e.dept.address.city,e.dept.sn) from Employee e";
List<List<Object>> ret = session.createQuery(hql).list();
for (List<Object> os : ret){
System.out.println(os);
} //使用new map 把每一行数据包装成map对象(3)
//1.默认情况下,把查询的属性的位置作为map的key
//2.可以给查询的属性添加别名,别名作为map的key,查询结果作为map的value
String hql = "select new Map(e.id as eid,e.name as ename,e.salary as esalary,e.dept.name as dname,e.dept.address.city as dcity,e.dept.sn as dsn) from Employee e";
List<Map<String, Object>> ret = session.createQuery(hql).list();
for (Map<String, Object> os : ret){
System.out.println(os);
} //直接通过new VO对象来把结果包装成一个VO对象(4)
//注意,VO对象需要一个构造方法,这个构造方法参数的顺序必须和查询的顺序匹配
String hql = "select new EmployeeVO(e.id,e.name,e.salary,e.dept.name,e.dept.address.city,e.dept.sn) from Employee e";
List<EmployeeVO> ret = session.createQuery(hql).list();
for (EmployeeVO os : ret){
System.out.println(os);
}

  

NamedQuery查询:
在hibernate中,执行查询需要先将HQL先翻译成SQL,再执行SQL。如果HQL比较复杂翻译的效率是比较低的。如果一条HQL重复执行,会重复翻译。效率低下。
如果在代码不同的地方重复使用到了相同的HQL,需要在不同的地方反复重写HQL;
hibernate提供了NamedQuery方式,来稍微提高静态HQL语句的执行效率。和对HQL的统一管理
NamedQuery使用:
在实体映射文件中添加:
<!--为HQL起名为findCustomersByName,该HQL在hibernate启动的时候就会翻译成SQL -->
<query name="findCustomersByName">
<![CDATA[from Customer c where c.name like :name]]>
</query>
查询的时候使用:
//通过getNamedQuery,得到的就是已经翻译为SQL的query对象,只需要设置参数查询就行了
NamedQuery的使用限制:NamedQuery里面只能配置静态的HQL。

二级缓存概念:
1,生命周期为整个应用的缓存(二级缓存是sessionFactory上的缓存,能提供整个应用中所有的session使用。)
2,所有的get,load方法,总是先查一级缓存,再查二级缓存,如果都没有,在去数据库里面查询。
3,不是所有的对象都适合放到二级缓存中。(读>>>写)
4,二级缓存有一些性能的指标
  1),命中率(总的从二级缓存中取得的数量/总的取的数量)
  2),最大对象数量;
  3),最大空闲时间;
5,二级缓存实际上就是一个缓存,所以,hibernate并没有实现自己的二级缓存框架,而是用的开源的。
对象缓存策略:
  1),usage="read-only" :放到二级缓存里面的对象是只读(性能最高)
  2),usage="read-write":允许读写(对并发支持较好)
  3),usage="nonstrict-read-write":允许读写,但是在并发事务情况下会产生脏数据
  4),usage="transactional" :允许读写,并且支持全事务(只能在ApplicationServer环境下有用)
ehcache的配置:
<defaultCache>:默认的cache,相当于公用cache;
<cache>:自定义的cache;
共同的配置:
1,maxElementsInMemory:该缓存池放在内存中最大的缓存对象个数;
2,eternal:是否永久有效,如果设置为true,内存中对象永不过期;
3,timeToIdleSeconds:缓存对象最大空闲时间,单位:秒;
4,timeToLiveSeconds:缓存对象最大生存时间,单位:秒;
5,overflowToDisk:当内存中对象超过最大值,是否临时保存到磁盘;
6,maxElementsOnDisk:能保存到磁盘上最大对象数量;
7,diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒
8,memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。

默认策略是LRU(最近最少使用),可以设置为FIFO(先进先出)或是LFU(较少使用)
在默认的情况下,不同类型的对象都是放在defaultCache中的;
自定义二级缓存(把某一个对象放到某一个特定的二级缓存区域)
1,在hibernate.cfg.xml文件中的class-cache添加region; (添加二级缓存区域)
<class-cache usage="nonstrict-read-write" class="com._520it.hibernate.day4.query.Employee" region="EMPLOYEE"/>
2,在hibernate.cfg.xml文件中的hibernate.propertie属性上添加region_prefix (添加二级缓存前缀)
<property name="cache.region_prefix">hibernate</property>
3,在ehcache.xml中配置一个cache,名字为region_prefix.region (为自己的对象配置一个缓存区域 :前缀.缓存区域)
<cache name="hibernate.EMPLOYEE"
maxElementsInMemory="10000"
eternal="true"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
二级缓存的操作
//得到二级缓存对象
Cache cache=sf.getCache();
//剔除一个实例
cache.evictEntity(User.class, 1L);
//剔除某种类型的所有实例
cache.evictEntityRegion(User.class);
//剔除所有二级缓存实例
cache.evictEntityRegions();

查询缓存:(使用非常少,因为可能带来非常大的负面性能影响)
1,默认情况下,hibernate没有打开查询缓存;
2,使用查询缓存:
  1),打开查询缓存:
  2),在查询的时候,使用Query对象的.setCacheable(true)方法;
3,查询缓存使用的条件:
  1,两条查询的HQL和查询参数必须完全一致;

Hibernate中的事务管理
使用Hibernate的锁机制主要是用来避免第一类丢失更新和第二类丢失更新;
Hibernate使用悲观锁其实就是使用数据库锁:
如果数据库不支持设置的锁机制,hibernate会使用该数据库提供的合适的锁机制来完成,而不会报错。

,使用session.load(class,id,LockOptions);加悲观锁,相当于发送SELECT ... FOR UPDATE

,使用session.get(class,id,LockOptions);加悲观锁,相当于发送SELECT ... FOR UPDATE

,使用session.buildLockRequest(LockOptions).lock(entity);加悲观锁,相当于发送SELECT id FROM ... FOR UPDATE

,使用query.setLockOptions(LockOptions);加悲观锁,相当于发送SELECT... FOR UPDATE

使用一个额外的版本控制字段来防止第二类丢失更新(乐观锁机制);

1,给表添加一个额外的数字类型字段version;

2,在insert一个对象的时候初始化version值为0;

3,在select的时候,查询出对象的版本号;

4,在update的时候,

  1),更新版本号,version = version+1;

  2),在update的where条件中带上当前更新对象的版本号 where .. and version = ?

  3),如果update返回影响条数>0,说明更新成功;

  4),如果update返回影响条数=0,说明更新的对象已经被其他事务更新或者删除,抛出异常,回滚当前事务;

在hibernate中使用乐观锁,推荐使用version方式;

1,给对象添加一个int version字段,最好设置属性为private;

2,在mapping文件中添加<version>元素即可;

事务并发5类问题(如果数据库没有做任何并发处理的情况下):

  第一类丢失更新:两个事务更新相同数据,如果一个事务提交,另一个事务回滚,第一个事务的更新会被回滚

  脏读:第二个事务查询到第一个事务未提交的更新数据,第二个事务根据该数据执行,但第一个事务回滚,第二个事务操作脏数据

  虚读:一个事务查询到了另一个事务已经提交的新数据,导致多次查询数据不一致

  不可重复读:一个事务查询到另一个事务已经修改的数据,导致多次查询数据不一致

  第二类丢失更新:多个事务同时读取相同数据,并完成各自的事务提交,导致最后一个事务提交会覆盖前面所有事务对数据的改变

一般情况,数据库都会处理一些事务并发的问题,数据库提供了不同的事务隔离级别来处理不同的事务并发问题,事务隔离级别定义如下:

READ_UNCOMMITED:允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读(相当于没有做任何事务隔离)

READ_COMMITTED:允许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生(ORACLE默认级别)

REPEATABLE_READ:对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。(MYSQL默认级别)

SERIALIZABLE:完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的。(ORACLE支持)

所以,数据库的隔离级别除了SERIALIZABLE,都不能处理第一类丢失更新和第二类丢失更新;

所以,数据库提供了锁机制来防止第一类丢失更新和第二类丢失更新;

最新文章

  1. django获取指定列的数据
  2. [转载]QQ通讯原理及QQ是怎么穿透内网的
  3. u-boot 之配置分析 (2)
  4. tcp断开的4次挥手
  5. Nginx源码研究八:nginx监听socket实现流程
  6. Virtualbox下实现Ubuntu虚拟机和win7主机文件共享(很简单,亲自试用,按此步骤一般都会成功)
  7. flume-hdfs sinks报错
  8. Mysql相关知识点总结(一)
  9. redis 连接idea一直被拒绝
  10. [是男人就过8题——Pony.ai]Perfect N-P Arrays
  11. Android星球效果实现
  12. Luogu P1020 导弹拦截
  13. css box-reflect投影实例讲解
  14. 【刷题】BZOJ 1823 [JSOI2010]满汉全席
  15. B样条
  16. T-sql 编程
  17. 根据Bool值挑选数组中元素
  18. ATL控件签名之后页面还提示“在此页面上的Activex控件和本页上的其他部分及交互可能不安全”
  19. C++ 基类指针和子类指针相互赋值
  20. 机器学习笔记—混合高斯和 EM 算法

热门文章

  1. FM(Factorization Machines)
  2. Log日志
  3. 计算int数组中的最大,最小,平均值
  4. crowdstrike提供的应急响应工具
  5. ASP.NET Web API系列教程(目录)(转)
  6. PHP生成HTML文件, SummerHtml
  7. 【原创】大叔问题定位分享(4)Kafka集群broker节点从zookeeper上消失
  8. div的onclick事件怎么失效了?
  9. PHP 必知的 16 个编程法则
  10. python-argparse批量修改后缀名-batch_file_rename.py