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,都不能处理第一类丢失更新和第二类丢失更新;

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

Hibernate-day04的更多相关文章

  1. Hibernate day04笔记

    整合log4j(了解) slf4j 核心jar : slf4j-api-1.6.1.jar .slf4j是日志框架,将其他优秀的日志第三方进行整合.      整合导入jar包     log4j 核 ...

  2. Spring day04笔记(SVN讲解和回顾昨天知识)

    spring day03回顾 事务管理 基于xml配置 1.配置事务管理器 jdbc:DataSourceTransactionManager hibernate:HibernateTransacti ...

  3. Hibernate-ORM:12.Hibernate中的多对多关联关系

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 本篇博客将讲述Hibernate中的多对多关联关系的操作,准备的篇幅较少,望海涵 一,讲述多对多 多对多的关联 ...

  4. hibernate多对多关联映射

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  5. 解决 Springboot Unable to build Hibernate SessionFactory @Column命名不起作用

    问题: Springboot启动报错: Caused by: org.springframework.beans.factory.BeanCreationException: Error creati ...

  6. hibernate多对一双向关联

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  7. Hibernate中事务的隔离级别设置

    Hibernate中事务的隔离级别,如下方法分别为1/2/4/8. 在Hibernate配置文件中设置,设置代码如下

  8. Hibernate中事务声明

    Hibernate中JDBC事务声明,在Hibernate配置文件中加入如下代码,不做声明Hibernate默认就是JDBC事务. 一个JDBC 不能跨越多个数据库. Hibernate中JTA事务声 ...

  9. spring applicationContext.xml和hibernate.cfg.xml设置

    applicationContext.xml配置 <?xml version="1.0" encoding="UTF-8"?> <beans ...

  10. [原创]关于Hibernate中的级联操作以及懒加载

    Hibernate: 级联操作 一.简单的介绍 cascade和inverse (Employee – Department) Casade用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似 ...

随机推荐

  1. Newton's Dark Secrets《牛顿探索》

    1643年1月4日,在英格兰林肯郡小镇沃尔索浦的一个自耕农家庭里,牛顿诞生了.牛顿是一个早产儿,出生时只有三磅重,接生婆和他的亲人都担心他能否活下来.谁也没有料到这个看起来微不足道的小东西会成为了一位 ...

  2. 异常捕获try----catch

    如果try语句里有return,返回的是try语句块中变量值. 详细执行过程如下: 如果有返回值,就把返回值保存到局部变量中: 执行jsr指令跳到finally语句里执行: 执行完finally语句后 ...

  3. 分布式系列六: WebService简介

    WebSerice盛行的时代已经过去, 这里只是简单介绍下其基本概念, 并用JDK自带的API实现一个简单的服务. WebSerice的概念 WebService是一种跨平台和跨语言的远程调用(RPC ...

  4. 在普通js文件里引入vue实例的方法

    首先是我是写了一个 Loading 插件然后 是挂在打vue.prototype 原型上的. 在main.js中use使用了这个插件. 至此vue原型是就被我挂上 $loadding方法了. 然后我想 ...

  5. eclise开发设置

    eclipse在debug模式下鼠标移动到变量上不显示值的问题 在eclipse中调试时,鼠标移动到变量上不显示值,使用ctrl+shift+i,或者通过配置达到目的:  Window->Pre ...

  6. ORACLE 根据 sql_id 查询绑定变量的传入值

    查询当前查询: select b.NAME,b.POSITION,b.DATATYPE_STRING,b.VALUE_STRING,b.LAST_CAPTUREDfrom v$sql_bind_cap ...

  7. Java Spring Boot VS .NetCore (五)MyBatis vs EFCore

    Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...

  8. 解决git反复输入密码的问题

    打开git命令面板 cd到项目根目录 $ git config --global credential.helper store然后只输入一次密码,后面就不需要了

  9. JavaScript 动态选择方法和属性

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  10. pandas导入导出数据-【老鱼学pandas】

    pandas可以读写如下格式的数据类型: 具体详见:http://pandas.pydata.org/pandas-docs/version/0.20/io.html 读取csv文件 我们准备了一个c ...