- 面向对象设计的软件内部运行过程可以理解成就是在不断创建各种新对象、建立对象之间的关系,调用对象的方法来改变各个对象的状态和对象消亡的过程,不管程序运行的过程和操作怎么样,本质上都是要得到一个结果,程序上一个时刻和下一个时刻的运行结果的差异就表现在内存中的对象状态发生了变化。
- 通过数据库保存java程序运行时产生的对象和恢复对象,其实就是实现了java对象与关系数据库记录的映射关系,称为ORM(即Object RelationMapping),人们可以通过封装JDBC代码来实现了这种功能,封装出来的产品称之为ORM框架,Hibernate就是其中的一种流行ORM框架。使用Hibernate框架,不用写JDBC代码,仅仅是调用一个save方法,就可以将对象保存到关系数据库中,仅仅是调用一个get方法,就可以从数据库中加载出一个对象。
- 使用Hibernate的基本流程是:配置Configuration对象、产生SessionFactory、创建session对象,启动事务,完成CRUD操作,提交事务,关闭session。
- 使用Hibernate时,先要配置hibernate.cfg.xml文件,其中配置数据库连接信息和方言等,还要为每个实体配置相应的hbm.xml文件,hibernate.cfg.xml文件中需要登记每个hbm.xml文件。
- 在应用Hibernate时,重点要了解Session的缓存原理,级联,延迟加载和hql查询。
Hibernate的二级缓存
- 缓存就是把以前从数据库中查询出来和使用过的对象保存在内存中,这个数据结构通常是或类似HashMap,当以后要使用某个对象时,先查询缓存中是否有这个对象,如果有则使用缓存中的对象,如果没有则去查询数据库,并将查询出来的对象保存在缓存中,以便下次使用。
- Hibernate的Session就是一种缓存,我们通常将之称为Hibernate的一级缓存,当想使用session从数据库中查询出一个对象时,Session也是先从自己内部查看是否存在这个对象,存在则直接返回,不存在才去访问数据库,并将查询的结果保存在自己内部。
由于Session代表一次会话过程,一个Session与一个数据库连接相关连,所以Session最好不要长时间保持打开,通常仅用于一个事务当中,在事务结束时就应关闭。并且Session是线程不安全的,被多个线程共享时容易出现问题。通常只有那种全局意义上的缓存才是真正的缓存应用,才有较大的缓存价值,因此,Hibernate的Session这一级缓存的缓存作用并不明显,应用价值不大。Hibernate的二级缓存就是要为Hibernate配置一种全局缓存,让多个线程和多个事务都可以共享这个缓存。我们希望的是一个人使用过,其他人也可以使用,session没有这种效果。 - 二级缓存是独立于Hibernate的软件部件,属于第三方的产品,多个厂商和组织都提供有缓存产品,例如,EHCache和OSCache等等。在Hibernate中使用二级缓存,首先就要在hibernate.cfg.xml配置文件中配置使用哪个厂家的缓存产品,接着需要配置该缓存产品自己的配置文件,最后要配置Hibernate中的哪些实体对象要纳入到二级缓存的管理中。明白了二级缓存原理和有了这个思路后,很容易配置起Hibernate的二级缓存。
- 扩展知识:一个SessionFactory可以关联一个二级缓存,也即一个二级缓存只能负责缓存一个数据库中的数据,当使用Hibernate的二级缓存后,注意不要有其他的应用或SessionFactory来更改当前数据库中的数据,这样缓存的数据就会与数据库中的实际数据不一致。
Hibernate的一对多和多对一双向关联的区别
- 一对多关联映射和多对一关联映射实现的基本原理都是一样的,既是在多的一端加入一个外键指向一的一端外键,而主要的区别就是维护端不同。
- 它们的区别在于维护的关系不同:
一对多关联映射是指在加载一的一端数据的同时加载多的一端的数据多对一关联映射是指在加载多的一端数据的同时加载一的一端的数据。
Hibernate是如何延迟加载
Hibernate3 提供了属性的延迟加载功能 当Hibernate在查询数据的时候,数据并没有存在与内存中,当程序真正对数据的操作时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提高了服务器的性能。
环境搭建
配置两个相关xml文件
- 编写hibernate.cfg.xml核心配置文件(放在src 下)
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration >
<session-factory >
<!-- 配置数据库信息 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///hibernate</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123</property>
<!-- 配置hibernate 信息 可选 -->
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<mapping resource="domain/user.hbm.xml"/>
</session-factory>
</hibernate-configuration>
- bean.hbm.xml配置文件(例User.hbm.xml)一般与实体类同一包下
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.yao.domain.User" table="t_user">
<id name="id">
<!-- 主键6种生成策略(identity,native,sequence,uuid,increament
,assigned)-->
<generator class="native"></generator>
</id>
<!-- 实体类的属性 -->
<property name="name"/>
<property name="pwd"/>
</class>
</hibernate-mapping>
- 将*.hbm.xml配置文件加入到hibernate.cfg.xml中
实体类的状态
- 瞬时态
既没有id值,同时与session也没关联 - 持久态
与session有关联 - 托管态
有id值,但与session无关联
细节
一对多
- 一般一个实体类对应一个配置文件
- 在配置一对多外键时,在xml文件中使用使用set来表示集合的关系
一对多:
//实体类中表示学生集合名字
//cascade建立两者关系或者删除时使用。incerse表示放弃一方的维护,提高性能,默认为FALSE,表示不放弃
<set name="setstudent" cascade="save-update,delete" incerse="true">
//设置外键名称
<key column="tsid"></key>
//学生实体类的路径
<one-to-many class="student url"/>
</set>
多对一: <many-to-one name="Teacher" class="teacherURL" column="外键名称"></many-to-one>
多对多
- 第一个配置文件
<class name="domain.Roel" table="roel">
<id name="rid" column="rid">
<generator class="native"></generator>
</id>
<property name="username" column="usernaem"></property>
<set name="user" table="user_roel">
<key column="roelid"></key>
<many-to-many class="domain.User" column="userid"></many-to-many>
</set>
</class>
- 第二个配置文件
<class name="domain.User" table="user">
<id name="uid" column="uid">
<generator class="native"></generator>
</id>
<property name="username" column="username"></property>
<set name="roel" table="user_roel" cascade="save-update" >
<key column="userid"></key>
<many-to-many class="domain.Roel" column="roelid"></many-to-many>
</set>
</class>
- 测试
User user=new User(); user.setUsername("zhangsan"); User user1=new User(); user.setUsername("lisi"); oel roel=new Roel(); roel.setUsername("经理"); Roel roel1=new Roel(); roel.setUsername("总经理"); Roel roel2=new Roel(); roel.setUsername("秘书"); user.getRoel().add(roel1); user.getRoel().add(roel2); user1.getRoel().add(roel1); session.save(user1); session.save(user1);
Hibernate的查询方式
对象导航查询
- 通过id获取老师的对象,查询该老师的所有学生
- Teacher teacher=session.get(Teacher.class,1);
Set<Stu
dent> student=teacher.getStudent();
然后我们可以便利set集合即可;
OID查询
即我们平常使用的session.get(类名.class,cid);
HQL查询
hql:hibernate query lauguage即hibernate提供的一种查询语句
它和sql很相似,但最大的区别:sql 操作数据库表和字段,而hql操作的事实体类和属性。
查询所有
//from 之后为实体类的名字,并不是表名
Query query=session.createQuery("from Teacher");
List<Teacher> teacher=query.list();
条件查询
- 语句:from 实体类名 where 属性名=? ang 属性名=?
from 实体类名 where 属性名 like?
代码演示:
Query query=session.createQuery("from Teacher c where c.tid=? and c.salar=?")
query.setParameter(0,1)
query.setParameter(1,8000)
List<Teacher> teacher=query.list();
for(Teacher t:teacher){
System.out.println(t);}
顺序查询
语句:from 实体类 order by 属性 desc/asc
分页查询
在Hibernate中不能只用limit语句,而是使用两个方法来实现
代码演示:
Query query=session.createQuery("from Teacher”)
query.setFirstResult(0);
query.setMaxResults(3);
List<Teacher> teacher=query.list();
投影查询
投影查询:不是查询所有,而是查询部分属性。不支持select后面跟*
语句: select 属性名称1,属性名称2 from 实体类
Query query=session.createQuery("select name from Teacher”)
List<Object> teacher=query.list();
聚集函数的使用
- count sum avg max min
- 语句:select count(*) from Teacher
代码演示:
Query query=session.createQuery("select count(*) from Teacher")
Object count=query.uniqueQuery();
Lang lang=(lang)count;
int c=lang.intvalue();
QBC查询
查询所有
语句:
Criteral criteria=session.createCriteria(Teacher.class)
List<Teacher> teacher=ctiteria.list();
条件查询
Criteria criteria=session.createCriteria(Teacher.class)
criteria.add(Restrictions.eq("tid",1));
criteria.add(Restrictions.eq("salar",,8000));
List<Teacher> list=criteria.list();
顺序查询(往下只写差异语句)
criteria.addOrder(Order.asc(“tid”))
criteria.addOrder(Order.desc(“tid”))
分页查询
- criteria.setFirstResult(0);
- criteria.setMaxResults(3);
统计查询
Object obj=criteria.setProjection(Projections.rowCount()).uniqueResult();
Lang lang=(lang)count;
int c=lang.intvalue();
离线查询
只有在最后执行时才需要session
DetachedCriteria detachedCriteria=DetachedCriteria.forClass(Teacher.class);
Criteria criteria=detachedCriteria.getExecutableCriteria(session);
List<Teacher>list=criteria.list();
HQL多表查询
内连接
语句:from Teacher t inner join t.setstudent
Criteria criteria=session.createCriteria("from Teacher t inner join t.setstudent")
List list=criteria.list();
list中每一部分都是一个数组
迫切内连接
内部底层实现都是一样的,区别是:内连接返回的list每部分都是一个数组,而迫切内连接返回的是对象
语句: from Teacher t inner join fetch t.setstudent
左外连接
语句:from Teacher t left outer join t.setstudent
迫切左外连接
语句:from Teacher t left outer join fetch t.setstudent
左外连接返回的list每部分都是一个数组,而迫切左外连接返回的是对象
右外连接
语句:from Teacher t right outer join t.setstudent
Hinbernate中检索策略
- Hibernate中的检索策略分两类:
- 立即查询:假如我们要根据id查询,调用get方法,get方法会立刻发送语句查询数据库。
- 延迟查询:假如我们要根据id查询,还可以调用load方法,load方法不会立刻发送语句查询数据库,只有得到对象里面的值的时候,才会发送语句查询数据库
延迟查询
类级别延迟
- 根据id查询,返回实体类对象。load不会立刻发送语句。
关联级别延迟
- 查询某个老师,之后再查询这个老师的所有学生,查询老师的学生的过程是否需要延迟,这个过程称为关联级别延迟
- 在实体类的配置文件中实现
Hibernate的批量抓取
- 通俗的讲:查询所有老师(Teacher类),返回是一个list,然后我们遍历之,得到每一个老师,然后再根据每一位老师,获取所有的学生(Student类)。
- 操作:在老师类的映射文件中配置即可(Set)
在Set中添加属性batch-size=“5”(值越大,性能越好。发送语句的次数就越少)
HQL
- hql(hibernate query language )是完全面向对象的查询语言,可以理解继承,多态,关联等概念,区分大小写。但是对应sql关键字(select,from where)不区分
单属性查询
public void testQuery1(){
//查询所有书名
//Book是实体类的名字
//创建Query对象
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
String hql="select name from Book";
Query query = session.createQuery(hql);
//list()方法返回查询结果
//返回结果的类型 是根据查询的列决定的
List<String> list = query.list();
for(String bookname:list){
System.out.println(bookname);
}
tx.commit();
HibernateUtil.closeSession();
}
多属性查询
public void testQuery2(){
//查询所有书 的名称和价格
//创建Query对象
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
//list()方法返回查询结果
//查询多个列时 返回结果是数组集合 数组中元素的类型 是由查询列来决定
List<Object[]> list = session.createQuery("select name,price from Book").list();
for(Object[] objs:list){
System.out.println(objs[0]+"--"+objs[1]);
}
tx.commit();
HibernateUtil.closeSession();
}
将多个查询列封装为对象
public void testQuery3(){
//查询所有书 的名称和价格
//创建Query对象
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
//Book 大写 表示的是 cn.siggy.pojo.Book类
//name表示的 Book类中的属性名
//list()方法返回查询结果
//查询多个列时 返回结果是数组集合 数组中元素的类型 是由查询列来决定
List<Book> list = session.createQuery("select new Book(name,price) from Book").list();
for(Book b:list){
System.out.println(b);
}
tx.commit();
HibernateUtil.closeSession();
}
别名使用
public void testQuery4(){
//查询所有书 的名称和价格
//创建Query对象
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
//Book 大写 表示的是 cn.siggy.pojo.Book类
//name表示的 Book类中的属性名
//list()方法返回查询结果
//查询多个列时 返回结果是数组集合 数组中元素的类型 是由查询列来决定
List<Book> list = session.createQuery("select new Book(b.name,b.price) from Book as b").list();
for(Book b:list){
System.out.println(b);
}
tx.commit();
HibernateUtil.closeSession();
}
查询所有列 (不使用select)
public void testQuery5(){
//查询所有书 的名称和价格
//创建Query对象
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
List<Book> list = session.createQuery("from Book").list();
for(Book b:list){
System.out.println(b);
}
tx.commit();
HibernateUtil.closeSession();
}
别名使用
//查询所有列2 使用select
//查询所有列2 不能使用* 需要使用别名
@Test
public void testQuery6(){
//查询所有书 的名称和价格
//创建Query对象
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
List<Book> list = session.createQuery("select b from Book b").list();
for(Book b:list){
System.out.println(b);
}
tx.commit();
HibernateUtil.closeSession();
}
hql条件查询
占位符? 从0开始
public void testQuery7(){
//查询所有书 的名称和价格
//创建Query对象
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
List<Book> list = session.createQuery("from Book b where id<?")
.setInteger(0, 4)
.list();
for(Book b:list){
System.out.println(b);
}
tx.commit();
HibernateUtil.closeSession();
}
占位符? 从0开始 使用setParameter
@Test
public void testQuery8(){
//查询所有书 的名称和价格
//创建Query对象
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
List<Book> list = session.createQuery("from Book b where id<?")
.setParameter(0, 4)
.list();
for(Book b:list){
System.out.println(b);
}
tx.commit();
HibernateUtil.closeSession();
}
命名查询
//条件查询 命名查询--设置条件参数的名称 以冒号开头后更名称 设置参数时 只需指定名
@Test
public void testQuery9(){
//查询所有书 的名称和价格
//创建Query对象
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
List<Book> list = session.createQuery("from Book b where id<:id")
.setParameter("id", 4)
.list();
for(Book b:list){
System.out.println(b);
}
tx.commit();
HibernateUtil.closeSession();
}
分页查询
public void testQuery10(){
//查询所有书 的名称和价格
//创建Query对象
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
List<Book> list = session.createQuery("from Book b")
.setFirstResult(3)//开始显示的记录下标(currentPage-1)*pageSize
.setMaxResults(3)//设置每页记录数pageSize
.list();
for(Book b:list){
System.out.println(b);
}
tx.commit();
HibernateUtil.closeSession();
}
统计查询
public void testQuery11(){
//查询图书总数
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
//int,long
Number count = (Number)session.createQuery("select max(b.price) from Book b")
.uniqueResult();
System.out.println("总数:"+count.byteValue());
tx.commit();
HibernateUtil.closeSession();
}
分组查询
public void testQuery12(){
//查询图书总数
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
//int,long
List<Object[]> list = session.createQuery("select b.category.name,count(b.id) from Book b group by b.category.name")
.list();
for(Object[] objs:list){
System.out.println(objs[0]+"--"+objs[1]);
}
tx.commit();
HibernateUtil.closeSession();
}
排序
public void testQuery13(){
//查询图书总数
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
//int,long
List<Book> list = session.createQuery("from Book order by price desc")
.list();
for(Book b:list){
System.out.println(b);
}
tx.commit();
HibernateUtil.closeSession();
}
对象导航
//连接查询
@Test
public void testQuery14(){
//查询 "仙侠"的书籍信息
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
String hql="from Book b where b.category.name=:name";
hql="select b from Book b join b.category c where c.name=:name";
hql="select b from Book b inner join b.category c where c.name=:name";
List<Book> list = session.createQuery(hql)
.setString("name", "仙侠")
.list();
for(Book b:list){
System.out.println(b);
}
tx.commit();
HibernateUtil.closeSession();
}
左外链接
public void testQuery15(){
//查询 "仙侠"的书籍信息
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
String hql="select c.name,b.name from Category c left outer join c.books b";
List<Object[]> list = session.createQuery(hql)
.list();
for(Object[] objs:list){
System.out.println(objs[0]+"----"+objs[1]);
}
tx.commit();
HibernateUtil.closeSession();
}
过滤查询
- 定义过滤器
- 使用:加条件
- 在查询时候 使得过滤器生效
public void testQuery16(){
//查询 "仙侠"的书籍信息
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
//启用过滤器
session.enableFilter("bf").setParameter("id", 4);
List<Book> list =session.createQuery("from Book").list();
for(Book b:list){
System.out.println(b);
}
tx.commit();
HibernateUtil.closeSession();
}
命名查询
public void testQuery17(){
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
List<Book> list =session.getNamedQuery("getByCategoryId")
.setInteger("id", 3)
.list();
for(Book b:list){
System.out.println(b);
}
tx.commit();
HibernateUtil.closeSession();
}
本地SQL查询
public void testQuery18(){
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
String sql="select Name,Price from BOOK";
List<Object[]> list =session.createSQLQuery(sql)
.list();
for(Object[] b:list){
System.out.println(b[0]+"-"+b[1]);
}
tx.commit();
HibernateUtil.closeSession();
}
Hibernate加载策略
- 即时加载 –get加载数据
使用get获取数据,会立即查找(缓存—数据库) - 延迟加载 懒加载 lazy—load,不会立即查找,当需要的时候才会查找
容易造成:LazyInitialaztionException异常:因为session被关闭。 - Load支持延迟加载,get不支持延迟加载。如果没有设置延迟加载,那么load也会立即加载对象。
- class的lazy
a)class默认情况下是支持懒加载如果设置lazy=false,get和load都会立即加载对象。 - set\list默认下是lazy=true的。支持懒加载,但是当使用size()的时候依然要去查询整个set集合的内容。Lazy=false;立即查询所有集合的内容。Lazy=extra比较智能。支持懒加载,当使用size()的时候,不会查询整个集合,仅仅查询集合中元素的个数。当需要使用集合元素的内容时,再去查询集合内容。
- 单端关联上的lazy:(many-to-one,ont-to-one)默认是支持懒加载lazy=proxy
Hibernate抓取策略
抓取策略(fetching strategy) 是指:当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候,
- select抓取:当查询关联对象通过select语句去查询。Select发出时机,是根据lazy的值来决定的。如果lazy=false,那么在获取对象时,就会发出一条select语句,将关联对象查询出来。如果lazy!=false,那么只有在获取关联对象时才会发出select语句去查询。
- join抓取:当查询关联对象时,通过outer join把关联对象一起查询出来,这个时候lazy无效。所有数据立即查询出来。
- subselect抓取:如果要查询关联集合的内容。会查询之前已经查出的对象的所有关联集合。(Category对应了多个Book)如果查询了(”文学”,”历史”);那么在使用(lazy=true)”文学”或”历史”的集合对象(”所对应的书籍信息”).会将(“文学”和”历史”)的书籍信息一起查询。如果lazy=false;在查询“多个分类时”会将所有分类的书籍信息一起查询。
Hibernate缓存机制
一级缓存
- 一级缓存又称为session缓存。生命周期相同。周期较短。事务级别的缓存。get使用了一级缓存,用get查数据时,首先检查缓存中是否有该数据,如果有直接从缓存中取数据,如果没有再查询数据库,并且将数据放入缓存中。load也支持一级缓存。load还支持lazy.当load从数据库中查询数据后,也会将数据放入缓存。
- unique/list查询不会去查看缓存,但是list查询的实体对象将会放入缓存中。
- iterate会执行查询id的操作,当查询对象时,会检查缓存中是否存在。如果存在则从缓存中取数据。Iterate查询出来的对象也会放入缓存。
- 管理一级缓存:flush(),clear(),evict()
二级缓存
- 二级缓存:sessionFactory ;进程级别的缓存。支持集群。
使用步骤:
a)在hibernate.cfg.xml中开启二级缓存(默认开启)
b)配置cache.provider_class
<!-- 使用二级缓存 3.x -->
<property name="cache.use_second_level_cache">true</property>
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<!-- 使用二级缓存 4.3-->
<property name="cache.use_second_level_cache">true</property>
<property name="cache.region.factory_class">
org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
c)导入ehcache.jar
d)将ehcache.xml放入src下(etc下找)
e)在类中指定或在(hibernate.cfg.xml)指定
<hibernate-mapping>
<class name="cn.siggy.pojo.Book" table="book" catalog="hibernate4">
<cache usage="read-only"/>
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<property name="author" type="java.lang.String">
<column name="author" />
</property>
<property name="name" type="java.lang.String">
<column name="name" />
</property>
<property name="price" type="java.lang.Double">
<column name="price" precision="22" scale="0" not-null="true" />
</property>
<property name="pubDate" type="java.sql.Timestamp">
<column name="pubDate" length="19" />
</property>
</class>
</hibernate-mapping>
在配置文件中
<class-cache usage="read-only" class="cn.siggy.pojo.Book"/>
- 使用:
public void testGet(){
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Book book = (Book)session.get(Book.class, 1);
//发出sql语句取数据
System.out.println(book.getName());
HibernateUtil.closeSession();
session = HibernateUtil.getSession();
System.out.println("---------");
book = (Book)session.get(Book.class, 1);
System.out.println(book.getName());
tx.commit();
HibernateUtil.closeSession();
}
查询缓存:在二级缓存基础来的。
- 在hibernate.cfg.xml中配置
<!-- 配置使用查询缓存 -->
<property name="cache.use_query_cache">true</property>
- 使用.