本文所有内容都有源码介绍,在资料中很详细
1、谈到hibernate,我们首先来看下百度百科的定义
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,
是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
由此我们知道hibernate是对java三层架构dao层的分装,谈到对jdbc的操作,无不就是这几个知识点,然后掌握其实现及其原理,随着经验的曾加,
对细节理解的也会越来越透彻
hibernate有哪些知识点呢首先是缓存一级缓存,二级缓存,查询缓存,懒加载。
一级缓存无需配置,通过session的Person person = (Person)session.get(Person.class, 1L);即可将数据放入到一级缓存
另外还有些方法也可以session.save(person); session.delete(person);session.update(person);等。
清空一级缓存的方法有session.clear();session.evict(person);
最后一级缓存的生命周期是session级别的,session.close(),关闭时,自然而然一级缓存不存在了。
同时我们在这里还谈到一个知识点,
持久化状态
理解 :当一个对象被持久化时,他们的状态在事务结束时同数据库进行同步,
因此当持久化对象的数据更新时,不需要调用方法保存,会自动更新到数据库。那么哪些方法可以
Classes classes = (Classes)session.get(Classes.class, 3L);
Iterator<Classes> iterator = session.createQuery("from Classes").iterate();
System.out.println(sessionFactory.getStatistics().getEntityLoadCount());
while(iterator.hasNext()){
Classes classes2 = iterator.next();
System.out.println(classes2.getName());
}
* 但是list方法不利用二级缓存查询数据,这里我想我以前一直有一个误区,以为list查询不到数据,
* list是把查询出来的数据放到二级缓存,他肯定是有数据的,但是如果我在查询的时候用list有什么意义呢
* 我每次查出来放到二级缓存,在这里我认为是没有任何意义的
* iterate方法的查询策略:
* 1、先查找该表中所有的id的值
* 2、再根据id值从二级缓存中查找对象,如果有,则利用二级缓存,如果没有则根据id查询该表中的所有的属性的值
*/
* session.get方法不仅要把数据放入到一级缓存,而且要放入到二级缓存
* 该方法在提取数据的时候,先从一缓存中查找,再从二级缓存中查找,如果找不到,则查询数据库
*/
而且二级缓存解决了二级缓存数据更新无法感知的问题,那么查询缓存是如何进行缓存的呢
查询缓存
一级缓存和二级缓存都是对象缓存:就是把该对象对应的数据库表中的所有的字段
全部查询出来了,这种查询在某些场合下会让效率降低。例如:表中的字段特别多,
但是程序中所需要的字段却很少。
查询缓存也叫数据缓存:内存(页面)中需要多少数据就把多少数据放入到查询缓存
(这个很重要,查询出来的是字段,不是bean对象,不能通过 select * from classes这样查询,只能通过select id ,name form classes这样查询一个
字段),
中。
生命周期
只要一些数据放入到查询缓存中,该缓存会一直存在,直到缓存中的数据被修改了,该
缓存的生命周期就结束了。
好了讲了这么多,我们来几个代码配置,掌握这些才能进入实质性的开发阶段。
下面我们来看session是如何创建的
public static SessionFactory sessionFactory;
static{
Configuration configuration = new Configuration();
configuration.configure();
sessionFactory = configuration.buildSessionFactory();
}
Session session = sessionFactory.openSession();
接着我们看下查询缓存的代码
/**
* "select name from Classes"
* 查询出来的数据能够放入到查询缓存中
* 但是不能放入到二级缓存中,因为不是对象
*/
@Test
public void testList_2(){
Session session = sessionFactory.openSession();
Query query = session.createQuery("select name from Classes");
query.setCacheable(true);//query要使用查询缓存了
query.list();//把数据放入到查询缓存中
System.out.println(sessionFactory.getStatistics().getEntityLoadCount());
session.close();
session = sessionFactory.openSession();
query = session.createQuery("select name from Classes");
query.setCacheable(true);
query.list();
session.close();
}
下面我们来看一下如何配置查询缓存的配置环境,其实掌握了这个,也就掌握了其它的
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<!--
一个sessionFactory代表数据库的一个连接
-->
<session-factory>
<!-- 链接数据库的用户名 -->
<property name="connection.username">root</property>
<!-- 链接数据库的密码 -->
<property name="connection.password">123</property>
<!-- 链接数据库的驱动 -->
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- 链接数据库的url -->
<property name="connection.url">
jdbc:mysql://localhost:3306/itheima12_hibernate
</property>
<!--
方言
告诉hibernate使用什么样的数据库,hibernate就会在底层拼接什么样的sql语句
-->
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<!--
二级缓存的供应商
-->
<property name="cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
<!--
开启二级缓存
-->
<property name="cache.use_second_level_cache">true</property>
<!--
<class-cache usage="read-only" class=""/>
-->
<!--
根据持久化类生成表的策略
validate 通过映射文件检查持久化类与表的匹配
update 每次hibernate启动的时候,检查表是否存在,如果不存在,则创建,如果存在,则什么都不做了
create 每一次hibernate启动的时候,根据持久化类和映射文件生成表
create-drop
-->
<property name="hbm2ddl.auto">update</property>
<property name="show_sql">true</property>
<property name="current_session_context_class">thread</property>
<property name="format_sql">true</property>
<!--
开启二级缓存的统计机制
-->
<property name="generate_statistics">true</property>
<!--
开启了查询缓存
-->
<property name="cache.use_query_cache">true</property>
<mapping resource="com/itheima12/hibernate/domain/Classes.hbm.xml" />
<mapping resource="com/itheima12/hibernate/domain/Student.hbm.xml" />
</session-factory>
</hibernate-configuration>
hibernate的另外一个重点就是表与表之间的关系,无非就是一对一,多对多,多对一,一对一等,学会配置文件就可以了,在这里我就重点介绍下
下面几个知识点
inverse与cascade的关系
cascade指的是级联操作,操作的是一般属性,指的是对象与对象的操作
级联操作,是指保存一方的时候,会级联保存另一方,会不会构建关系,需要由inverse确定,如果inverse为false,为维护关系,会发出update语句,将外键加入另一张表。
inverse指的是关系操作,针对的就是外建,默认是false,true极为由另外一方维护关系,
取消关系实际是是设置外键为空,级联是指两个对象之间的操作,
级联删除同时会把另外一张表中的数据删除。
级联和关系并没有太多的关系。
注意删除的时候,
<!--
set元素针对的就是Classes类中的Set属性
cascade 级联操作
null 默认值
save-update
在保存classes对象的时候,针对student进行保存或者更新的操作
在更新classes对象的时候,针对student进行保存或者更新的操作
all
delete
inverse 关系操作
default:classes维护classes与student之间的关系
true: classes不维护classes与student之间的关系
false: classes维护classes与student之间的关系
-->
一对多的双向
当多的一方维护关系时,不会发出更新关系的update语句,(原因列子,再插入学生的时候,级联插入一个班级,如果是维护关系,会同时插入外键,所以不会发出update语句)
而且多的一方没有 inverse 者一属性,因为你在构建关系,或者解除关系时,都是会对,本表中的外键进行操作。所以有无inverse都没关系。
而一的一方维护关系时,需要发出维护关系的update语句,因为它会首先插入对象,而后跟新外键。,所以在这里,一般情况下,多的一方维护关系效率
比较高。
具体参数配置看笔记,这里不一一介绍了
类的懒加载
Classes classes = (Classes)session.load(Classes.class, 1L);
集合需要配置文件
<set name="students" cascade="save-update" inverse="true" lazy="extra">
<cache usage="read-write"/>
具体看源码案例吧,这里也不介绍了
这里设计到级联查询,级联查询有个抓取策略,如下
<set name="students" cascade="save-update" inverse="true" lazy="extra" fetch="join">
<cache usage="read-write"/>
抓取策略
join:左外连接
select:默认的值
subselect:子查询
下面将hibernate的hql查询,直接上代码吧,在这里我们会发现查询出来的都是对象,非常的方便好用,hql的对象关系映射在这里可谓是运用的
凌厉之境
public class OneToManyTest extends HibernateUtils{
/**
* 实现classes与student的内连接
* select s.*,c.*
from student s inner join classes c on(s.cid=c.cid)
*/
@Test
public void testInnerJoin(){
Session session = sessionFactory.openSession();
List list = session.createQuery("from Classes c inner join c.students").list();
session.close();
}
/**
* 迫切内连接
*/
@Test
public void testInnerJoin_fetch(){
Session session = sessionFactory.openSession();
List list = session.createQuery("from Classes c inner join fetch c.students").list();
session.close();
}
/*
* 左外连接
* select c.*,s.*
from classes c left outer join student s on(s.cid=c.cid)
*/
@Test
public void testLeftJoin(){
Session session = sessionFactory.openSession();
List list = session.createQuery("from Classes c left outer join c.students").list();
session.close();
}
/**
* 迫切左外连接
*/
@Test
public void testLeftJoin_fetch(){
Session session = sessionFactory.openSession();
List list = session.createQuery("from Classes c left outer join fetch c.students").list();
session.close();
}
/**
* 要查询的属性来自于两个持久化类
* 带构造函数的查询和fetch的查询不能同时存在
*/
@Test
public void testQueryPropertiesFromClassesAndStudent(){
Session session = sessionFactory.openSession();
List<ClassesView> classesViews = session.createQuery("select new com.itheima12.hibernate.domain.ClassesView" +
"(c.name,s.name) from Classes c " +
"inner join c.students s").list();
session.close();
}
}
看一个设置参数的案例
@Test
public void testQueryPrepare_2(){
Session session = sessionFactory.openSession();
Query query = session.createQuery("from Classes where cid=:cid and name=:name");
query.setParameter("cid", 1L);
query.setParameter("name", "adsfasfd");
Classes classes = (Classes)query.uniqueResult();
System.out.println(classes.getName());
session.close();
}
还有分页参数的设置
query.setFirstResult(3);//表示在集合中索引的位置
query.setMaxResults(3);//表示设置一页显示多少个
最后hibernate也能通过sql查询,这里sql查询出来的就没有对象关系映射了,是数据
List list = session.createSQLQuery(sql).
addScalar("person_id",StandardBasicTypes.INTEGER).
addScalar("name", StandardBasicTypes.STRING).
addScalar("age",StandardBasicTypes.INTEGER).list();
for(Iterator iterator = list.iterator();iterator.hasNext();){
//每个集合元素都是一个数组,数组元素师person_id,person_name,person_age三列值
Object[] objects = (Object[]) iterator.next();
System.out.println("id="+objects[0]);
System.out.println("name="+objects[1]);
System.out.println("age="+objects[2]);
System.out.println("----------------------------");
}