第四部分:使用hibernate操作数据
包括:Session缓存,持久化对象状态,hibernate的持久化操作,使用OID加载对象,HQL查询
Session缓存:
Hibernate对其持久化对象实现了缓存管理,来提高系统性能,hibernate支持两级缓存管理,一级缓存是由Session提供的,是Session内置的,不能被卸载
Hibernate的二级缓存是由SessionFactory提供的,默认是不支持的,必须明确配置。
Session缓存的作用:
降低访问数据库的频率
保证缓存中的对象与数据库中的相关记录保持同步
持久化对象的状态:
临时状态(Transient):刚刚用new创建,还没有被持久化,不处于Session缓存中
临时对象的特征:
不处于Session的缓存中
在数据库中没有对应的记录
进入临时状态的情况:
当通过new语句刚创建了一个对象
Session的delete方法会使一个对象从(持久化/ 游离)状态进入临时状态
持久化状态(Persistent):已经被持久化,加入到Session缓存中
持久化对象的特征:
位于一个Session实例的缓存中
持久化对象和数据库中相关的记录对应
Session在清理缓存时,会根据持久对象的属性的变化来同步更新数据库
Session的save方法把临时对象变为持久化对象
Session的load或get方法返回的是持久化对象,find方法返回的List元素
Session的update,saveOrUpdate,lock使对象从游离状态变为持久化状态
游离状态(Detached):已经被持久化,但不再处于Session缓存中
游离对象的特征:
不再处于Session的缓存中
游离对象是从持久化对象转变过来的
调用Session的close方法时,Session的缓存被清空,缓存中的持久化对象会变成游离对象
持久化对象:
save()://保存对象
把临时对象加入到缓存中,使之成为持久化对象
使用指定的ID生成器为持久化对象分配OID
当对象处于持久化状态,Hibernate不允许修改其OID
update()://修改对象
把游离对象加入到缓存中,使一个游离对象变成持久化对象
计划执行update SQL语句,根据对象的OID来更新对象所对象的数据
update()不能更新一个在session中已经关联的相同OID的对象
saveOrUpdate()://保存或修改对象
同时包含了save()和update()方法的功能,根据对象的状态自动执行相应的功能
若对象为临时状态,执行save()方法,若为游离状态则执行update()方法
merge() //合并对象
直接修改表中的记录,并不影响该对象原来的状态
contains() //判断实体对象是否与Sesison关联
可以用此方法判断对象是否处理持久化状态
evict() //把实体对象从Session缓存中移除
clear() //清除Session缓存中所有的持久化对象
delete() //删除指定的对象
配置级联删除:
1. 设置cascade为“delete” 或 “all” 或 “save-update,delete”。 “all”与“save-update,delete”相同
学生和地址之间存在一对一的关系,如果希望删除学生信息时,同时删除地址信息,可以在学生这一头这样配置。
2.设置cascade=“delete-orphan”
班级和学生是一对多的关系,在班级的set元素中进行配置。更新班级对象时,检查set集合中缺少的学生对象,删除数据库中对应的学生记录。
根据OID加载对象:
get()
load()
两个方法的区别:
都是根据指定的OID从数据库中加载一个持久化状态对象
当数据库中不存在与OID相对应的记录时,get()返回一个null引用,而load()方法会抛出ObjectNotFoundException异常
load()方法可以返回一个实体的代理类实例(当class的lazy=true时),而get()方法只是直接返回实体类对象。
Hibernate查询:
三种查询方式:
HQL(Hibernate QueryLanguage)检索
QBC(Query ByCriteria)检索
Native SQL检索
注:以上三种查询方式都是通过Session对象来操作的
HQL查询:
是完全面向对象的查询语句,查询功能非常强大,具备继承,多态和关联等特性
Hibernate使用Query接口来执行HQL语句,类似JDBC中使用的PreparedStatement
HQL语句不区分大小写,但实体类及其属性除外
HQL查询步骤:
创建Query对象 Query query=session.createQuery(“hql”);
绑定动态参数
使用占位符?指定参数 eg:”from User u whereu.age>?”
query.setInteger(0,20);
参数的索引从0开始
使用命名参数指定参数 eg:”from User uwhere u.age>:age”
query.setInteger(“age”,20);
每种参数类型都有两种绑定形式
设置时间类型:
query.setTime 时分秒
query.setDate 日期
query.setTimestamp 日期+时分秒
设置实体属性:
query.setEntity
执行查询/更新语句:
list():返回List对象,可以按索引随机访问对象
iterator():返回Iterator对象,只能按从前到后的顺序依次访问对象
uniqueResult():返回一个结果对象,一般确认结果集中只有一个符合条件的对象才使用此方法
技巧:可以使用setMaxResults()来限制个数
executeUpdate() :执行delete/update语句
HQL实体查询:
查询一个实体的信息,返回包含全部属性的实体对象的实例,也可以是一个List集合对象,其元素是实体对象的实例
如果是同一种类型的实体查询,可以省略select子句。
如果实体类命名冲突,可使用完全类名形式“from com.ourchr.hib.entity.User ”
查询所有用户信息session.createQuery(“fromUser”).list();
查询年龄大于20的所有用户信息
session.createQuery(“fromUser u where u.age > 20”).list();
单个属性查询:
session.createQuery(“selectu.name from User u”).list();
返回的结果集中的对象是String类型
多属性查询:
session.createQuery(“selectu.name, u.password from User u”).list();
返回的结果集中是Object[]类型的数据,其长度等于属性列的个数
实体和属性混合查询:
session.createQuery(“selectu, u.name from User u”).list();
返回一个Object[]类型结果集,第一个元素是User对象,第二个是一个String对象
使用Query.executeUpdate()来执行操作:
示例:让所有用户的年龄加1
session.createQuery(“update Userset age = age + 1”).executeUpdate();
示例:删除所有年龄小于10的用户
session.createQuery(“delete User where age <10”).executeUpdate();
HQL-方法链编程:
Query接口支持方法链编程风格,它的setXXX()等方法都返回Query自身的实例,这样可直接通过“.”来调用下一个方法,从而使用程序更简洁
示例:
常规风格
Query query =session.createQuery(“from User u where u.age > ?”);
query.setInteger(0, 10);
List users = query.list();
方法链风格
List users =session.createQuery(“from User u where u.age > ?”).setInteger(0, 10).list();
第五部分:使用hibernate查询数据
包括:分页查询,级联查询,条件查询,聚合查询,表达式
分页查询:
设置最多查询多少条记录
query.setMaxResults(n)
设置从原始结果集中取最终结果的开始下标
query.setFirstResult(n)
Eg: 查询第10到19条记录
query.setFirstResult(10).setMaxResult(10)
级联查询:
session.createQuery("from Student s left joinfetch s.clazz where s.name=:name"
注意不能使用left join Clazz con …
以下配置对HQL查询不起作用:
<set name="students" fetch="join">…
级联查询:一对一
如果Student与Address之间存在一对的关联,则只要查询Student,系统会自动查询出对应的Address,不管是使用session.get还是query进行查询。
使用get查询时,如果希望使用连接查询,则需要配置fetch=“join”
使用query查询时,如果希望使用连接查询,则查询方式类似如下:
from Student s left join fetch s.address where …
级联查询:一对多
假定Clazz与Student存在一对多关联,则查询Clazz时并不会自动查询Student。
如果希望使用get方法查询 Clazz时同时查询到Student,则有如下两种方案:
方案一:
Clazz c = (Clazz)session.get(Clazz.class, id);
c.getStudents().size();
//以上第一行代码将查询到班级信息,第二行代码将查询到班级对应的学生信息,共执行两次查询。
方案二:
在配置文件中的set元素上配置属性:fetch=“join”
这时使用get查询clazz时,将自动通过左外连接查询对应的学生信息。共执行一次查询。
使用query查询对应的班级信息时,set元素中的fetch配置将不起作用。默认情况下也不会级联查询学员信息。可以使用如下方案来实现级联查询。
方案一:
Clazz c = (Clazz)session.createQuery(“from Clazz cwhere …”).uniqueResult();
c.getStudents().size();
//以上第一行代码将查询到班级信息,第二行代码将查询到班级对应的学生信息,共执行两次查询。
方案二:
Clazz c = (Clazz)session.createQuery(“from Clazz c left join fetch c.studentswhere …”).uniqueResult();
//以上查询班级时将自动通过左外连接查询对应的学生信息。共执行一次查询
级联查询:多对多
多对多的情况与一对多类似。可以通过配置fetch=“join”或者执行两次查询来实现。
假设课程与教师存在多对多的关系
在教师对应的配置文件中配置fetch=“join”,可以实现通过get获取教师对象的同时获取到所有的课程。
通过query查询教师时,使用left join fetch可以在查询教师对象的同时查询到课程。
条件查询:
条件中的属性:
一对一的情况:查询学生时希望根据学生的地址信息来查询
from Student s where s.name = :n and s.address.info= :I
注:我们可以直接使用关联的对象的属性来进行查询
一对多的情况:查询班级时希望根据学生的信息来查询
from Clazz c left join c.students s where s.name =:n
聚合查询:
支持的聚合函数
avg(...), sum(...),min(...), max(...), count(*)
支持groupby
select s.sex, count(*)from Student s group by s.sex
表达式:
查询学生人数多于3的班级
from Clazz c where size(c.students)> 3
or
from Clazz c where c.students.size> 3
其他问题:
多表查询
查询班级名和学生名相同的班级
select c from Clazz c, Student s where c.name = s.name
直接查询得到对象
select new entity.Some(c.id, c.name) from Clazz c
第六部分:使用hibernate其他查询语句
包括:OBC,Example,Native Query
QBC查询:
在QBC中,使用Criteria接口来查询,条件由Restrictions定义,排序则以Order指定,Criteria本身只是一个查询容器,具体的查询条件通过调用Criteria的add()方法进行添加。
Eg: 查询年龄大于20的所有姓wang的用户信息,并按年龄升序排序。
List users =session.createCriteria(User.class)
.add(Restrictions.like(“name”,”wang%”))
.add(Restrictions.gt(“age”,20))
.addOrder(Order.asc(“age”))
.list();
Restrictions:
用于生成Criteria接口执行数据库查询时所使用的查询条件Criterion对象,它提供了一系列的静态方法来生成各种Criterion接口的实现类对象。
Criterion restriction= Restrictions.between(“age”,10,20);
String[] address = {“hefei”,”nanjing”};
Criteria criteria = session.createCriteria(User.class)
.add(restriction).add(Restrictions.in(“address”, address ));
//再增加email不为空的条件
criteria.add(Restrictions.isNotNull(“email”));
Order类:用于使用Criteria设置查询时的排序规则一般只用到两个静态方法asc()、desc()。
List list = Criteria.createCriteria(User.class)
.add(Order.asc(“age”)).add(Order.asc(“name”)).list();
使用QBC级联查询:
例如,查询名称为“class1”的班级并关联查询该班级的所有学生信息。
List<Clazz>clazz = (List<Clazz> )session.createCriteria(Clazz.class)
.add(Restrictions.eq(“name”,”class1”))
.setFetchMode(“students”,FetchMode.JOIN)
.list();
使用Criteria动态获取关联实体或实体集合与HQL不同,它不会忽略映射文件中指定的获取策略。如果在配置students集合属性时加上fetch=“join”,则以下的查询也会通过外连接获取关联的Student集合:
List<Clazz> clazz= (List<Clazz> )session.createCriteria(Clazz.class) .add(Restrictions.eq(“name”,”class1”)).list();
使用setFetchMode动态获取关联对象和集合是通过连接来实现的,这样导致返回的List中包含重复的记录。
解决方案:
1、将查询结果列表clazz进行如下转换:
Set c = newLinkedHashSet(clazz);
clazz = newArrayList(c);
使用ResultTransformer对结果过滤,使用方式如下:
List<Clazz>clazz = session.createCriteria(Clazz.class) .add(Restrictions.eq("name",name)) .setFetchMode("students",FetchMode.JOIN).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
QBC查询—使用关联指定约束:
例如,查询有姓wang学生的班级:
Criteria criteria =session.createCriteria(Clazz.class)
.createAlias("students","s")
.add(Restrictions.like("s.name","wang", MatchMode.START))
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
List<Clazz> c =criteria.list();
投影查询:
Projections类主要用于帮助Criteria接口完成数据的分组查询和统计功能。
List users =session.createCriteria(User.class)
.setProjection(Projections.projectionList()
.add(Projections.rowCount())
.add(Projections.avg(“age”))
.add(Projections.max(“age”))
.add(Projections.min(“age”))
.add(Projections.groupProperty(“sex”))//设置分组字段
).list();
Example查询:
Example对象提供了另一种指定查询条件的方式,只要用于创建Example对象的某属性不为空,就会成为查询条件的一部分。
Clazz cla = newClazz(); cla.setName("class1"); cla.setInfo(“info”);
cla.setId(1L);
Example e =Example.create(cla);
List<Clazz> r =session.createCriteria(Clazz.class).add(e).list();
注:上例中用一个clazz对象做为参数创建了Example,因为clazz对象中属性name和info不为null,因此查询条件中会有name=“class1”及info=“info”这二项。
Example会忽略主键属性,因此上例中设置的id值不会包含到查询条件中。
Example对象还可以指定是否在查询条件中使用like进行比较,是否忽略大小写,及指定哪些属性不包括到查询条件中。
Example example = Example.create(cat)
.excludeZeroes() //排除值为0的属性
.excludeProperty(“color”) //不包括“color”属性
.ignoreCase() //比较时不区分大小写
.enableLike(); //使用like进行比较
Native Query查询:
有时我们并不能通过HQL和QBC解决一些特殊的问题,如调用存储过程,或访问数据库提供的一些特性。Hibernate允许我们直接使用sql来访问数据库。
SQLQuery query = session.createSQLQuery("select * fromClAZZ");
List<Object[]> r = query.list();
SQLQuery也可以查询出实体对象:
List<Clazz> r =
session.createSQLQuery(“select *from ClAZZ”)
.addEntity(Clazz.class)
.list();
前提是Clazz类进行了配置,同HQL一样。