Hibernate细节
Hibernate核心类与接口
PS:Hibernate核心类与接口就是Hibernate框架的核心部分,通过对最底层配置文件的配置,将数据库与Java POJO进行映射,通过Configuration对象加载配置文件,再通过SessionFactory创建Session,得到会话之后就能进行CUD操作,然后可以使用Criteria和Query进行R操作。当然在CRUD操作,请不要忘了Transaction这个事务控制接口。
1、Configuration类
Configuration类的作用如下:
1、负责管理hibernate的配置信息
2、读取hibernate.cfg.xml
3、加载hibernate.cfg.xml配置文件中配置的驱动,url,用户名,密码,连接池等
4、管理 *.hbm.xml对象映射文件
基本语法:
Configuration configuration = new Configuration().configure();
PS:请注意configure()方法是个重载方法,常用的有不带参数的和带一个String参数的,不带参数的默认加载src目录下的hibernate.cfg.xml文件,带参数的可以自己指定加载文件位置。
2、Hibernate.cfg.xml文件
Hibernate.cfg.xml文件的作用如下:
1、该文件主要用于指定各个参数,是hibernate核心文件
2、默认放在src目录下,也可以放在别的目录下
3、指定连接数据库的驱动、用户名、密码、url、连接池等
4、指定对象关系映射文件的位置
5、也可使用hibernate.properties文件来替代该文件.(推荐使用hibernate.cfg.xml)
hibernate.cfg.xml案例:
<?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>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">2</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<!-- Enable Hibernate's current session context -->
<property name="current_session_context_class">org.hibernate.context.ManagedSessionContext</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>
<mapping resource="org/hibernate/tutorial/domain/Person.hbm.xml"/>
</session-factory>
</hibernate-configuration>
3、对象关系映射文件(*.hbm.xml)
对象关系映射文件的作用:
1、该文件主要作用是建立表和类的映射关系,是不可或缺的重要文件
2、一般放在其映射的类同一个目录下,但不是必须的
3、命名方式一般是类名.hbm.xml,但不是必须的
对象关系映射文件示意图
4、SessionFactory(会话工厂接口)
会话工厂接口的作用:
1、可以缓存SQL语句和数据(Session级缓存[一级缓存])
2、SessionFactory是一个重量级对象,所以最好是保证一个数据库对应于一个SessionFactory。示意图如下:
3、获取Session,有openSession()和getCurrentSession()两个方法。
1、openSession()获取一个新的Session。
2、getCurrentSession()获取和当前线程绑定的Session,也就是说,在同一个线程中,获取的Session一定是同一个Session,这样的话有利于事务控制。如果使用getCurrentSession()的话,需要在hibernate.cfg.xml中进行配置。
3、如何选择使用openSession()还是getCurrentSession()
3.1、如果需要在同一线程中,保证使用同一个Session,则使用getCurrentSession()。
3.2、如果需要使用不同的Session,则使用openSession()。
4、通过getCurrentSession()获取的Session在事务提交后,会自动关闭,通过openSession()获得的session则必须手动关闭。
5、通过getCurrentSession()获取Session,CRUD都要进行事务处理,通过openSession(),查询操作可以不进行事务处理。
6、在 SessionFactory启动的时候,Hibernate 会根据配置创建相应的 CurrentSessionContext,在getCurrentSession()被调用的时候,实际被执行的方法是 CurrentSessionContext.currentSession()。在currentSession()执行时,如果当前Session为空,currentSession会调用SessionFactory的openSession。
基本语法:
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
补充:全局事务和本地事务
本地事务是针对一个数据库的事务,其所有操作都是在一个数据库上面进行的。
全局事务是指跨数据库的事务,可以是一台主机上面的不同数据库,也可以是不同主机上面的不同数据库。
补充:getCurrentSession()线程会话
补充:获取Session工具类
package com.pc.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
/**
*
* @author Switch
* @function 生成会话
* @description 获取当前会话或者是创建新会话
*
*/
public final class HibernateUtil {
// 会话工厂
private static SessionFactory sessionFactory = null;
// 线程局部变量模式
private static ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
// 单例化,构造方法私有
private HibernateUtil(){}
// 静态代码块创建会话工厂
static {
sessionFactory = new Configuration().configure().buildSessionFactory();
}
// 获取全新的Session
public static Session getOpenSession(){
return sessionFactory.openSession();
}
// 获取和线程关联的Session
public static Session getCurrentSession(){
// 判断是否得到
Session session = threadLocal.get();
if(session == null){
session = sessionFactory.openSession();
// 把Session对象设置到threadLocal,相当于该session和线程绑定
threadLocal.set(session);
}
return session;
}
}
PS:通过ThreadLocal类可以将变量的作用域扩展为线程级,也就是说在本线程使用的都是同一个变量。遗憾的是该类只支持本地Session。
5、Session接口
会话接口的作用:
1、Session一个实例代表与数据库的一次操作(一次操作可以是crud组合),相当于JDBC里面的Connection接口实例。
2、Session实例通过SessionFactory获取,用完需要关闭。
3、Session是线程不同步的(不安全的),因此要保证在同一线程中使用,可以使用getCurrentSessiong()。
4、Session可以看做是持久化管理器,它是与持久化操作相关的接口。
基本语法:
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.getCurrentSession(); // 需要配置
// 或者是: Session session = sessionFactory.openSession();
PS:还可以写工具类来实现,具体参照SessionFactory接口内容。
重要方法
1、保存一个对象(记录)——sava方法
// 添加员工
public static void addEmployee() {
// 1.创建Configuration对象,该对象用于读取hibernate.cfg.xml,并完成初始化
// configure默认载入hibernate.cfg.xml文件,可以指定需要载入的xml
Configuration configuration = new Configuration().configure();
// 2.创建SessionFactory对象(这是一个会话工厂,是一个重量级的对象)
SessionFactory sessionFactory = configuration.buildSessionFactory();
// 3.创建Session(用于与数据库的对话,相当于JDBC中的Connection)
Session session = sessionFactory.openSession();
// 4.对Hibernate而言,要求程序员,在进行增加、删除、修改的时候使用事务提交,否则不生效
Transaction transaction = session.beginTransaction();
// 添加一个雇员
Employee employee = new Employee();
employee.setName("zs");
employee.setEmail("123456@switch.com");
employee.setHiredate(new Date());
// 保存
// --->insert into ...[被hibernate封装]
session.save(employee);
// 提交
transaction.commit();
session.close();
}
2、删除一个对象(记录)——delete方法
// 删除员工信息
public static void deleteEmployee() {
// 获取一个会话
Session session = HibernateUtil.getCurrentSession();
// 创建一个事务
Transaction transaction = null;
try {
transaction = session.beginTransaction();
// 1.获取该雇员
Employee employee = (Employee) session.load(Employee.class, 3);
// 2.删除该雇员
session.delete(employee);
// 提交
transaction.commit();
} catch (Exception e) {
// TODO: handle exception
if (transaction != null) {
transaction.rollback();
}
throw new RuntimeException(e.getMessage());
} finally {
if (session != null && session.isOpen()) {
session.close();
}
}
}
3、查询一个对象(记录)——get/load方法
get()和load()的区别
1、get()方法直接返回实体类,如果查不到数据则返回null。load()会返回一个实体代理对象(当前这个对象可以自动转化为实体对象),但当代理对象被调用时,如果没有数据不存在,就会抛出个org.hibernate.ObjectNotFoundException异常
2、load先到缓存(session缓存/二级缓存)中去查,如果没有则返回一个代理对象(不马上到DB中去找),等后面使用这个代理对象操作的时候,才到DB中查询,这就是我们常说的load在默认情况下支持延迟加载(lazy)。可通过在Xxx.hbm.xml中class元素中将lazy属性设置为false来取消延迟加载。
3、 get先到缓存(session缓存/二级缓存)中去查,如果没有就到DB中去查(即马上发出sql)。
4、如果确定DB中有这个对象就用load(),不确定就用get()(这样效率高)。
Hibernate缓存原理示意图
案例
-- 测试是否抛出异常
try {
Session session = sessionFactory.openSession();
//不会抛出异常.如果用load当查询不到则抛出异常
Employee e=(Employee) session.get(Employee.class, 51);
System.out.println(e);
} catch (Exception e) {
e.printStackTrace();
// TODO: handle exception
}finally{
}
-- 测试是否立即执行
-- 默认懒加载则不会立即执行,直到使用时才会执行
Employee emp=(Employee) session.load(Employee.class, 19);
4、修改一个对象(记录)——update方法
// 修改员工信息
public static void updateEmployee() {
// 获取一个会话
Session session = HibernateUtil.getCurrentSession();
// 创建事务对象
Transaction transaction = null;
try {
transaction = session.beginTransaction();
// 1.获取要修改的雇员
// load是通过主键属性,获取该对象实例(表的记录)
Employee employee = (Employee) session.load(Employee.class, 2);
// 2.修改该雇员
employee.setName("ls");
// 提交
transaction.commit();
} catch (Exception e) {
// TODO: handle exception
if (transaction != null) {
transaction.rollback();
}
throw new RuntimeException(e.getMessage());
} finally {
if (session != null && session.isOpen()) {
session.close();
}
}
}
补充:Hibernate各种保存方式的区别(save,persist,update,saveOrUpdate,merge,flush,lock)等
一、预备知识:
在所有之前,说明一下,对于hibernate,它的对象有三种状态,transient、persistent、detached
下边是常见的翻译办法:
transient:瞬态或者自由态
persistent:持久化状态
detached:脱管状态或者游离态
脱管状态的实例可以通过调用save()、persist()或者saveOrUpdate()方法进行持久化。
持久化实例可以通过调用 delete()变成脱管状态。通过get()或load()方法得到的实例都是持久化状态的。
脱管状态的实例可以通过调用 update()、saveOrUpdate()、lock()或者replicate()进行持久化。
游离或者自由状态下的实例可以通过调用merge()方法成为一个新的持久化实例。
save()和persist()将会引发SQL的INSERT,delete()会引发SQL的DELETE, 而update()或merge()会引发SQL的UPDATE。对持久化(persistent)实例的修改在刷新提交的时候会被检测到,它也会引起SQL的UPDATE。saveOrUpdate()或者replicate()会引发SQL的INSERT或者UPDATE
二、save 和update区别
把这一对放在第一位的原因是因为这一对是最常用的。
save的作用是把一个新的对象保存
update是把一个脱管状态的对象保存
三、update 和saveOrUpdate区别
这个是比较好理解的,顾名思义,saveOrUpdate基本上就是合成了save和update
引用hibernate reference中的一段话来解释他们的使用场合和区别
通常下面的场景会使用update()或saveOrUpdate():
程序在第一个session中加载对象
该对象被传递到表现层
对象发生了一些改动
该对象被返回到业务逻辑层
程序调用第二个session的update()方法持久这些改动
saveOrUpdate()做下面的事:
如果对象已经在本session中持久化了,不做任何事
如果另一个与本session关联的对象拥有相同的持久化标识(identifier),抛出一个异常
如果对象没有持久化标识(identifier)属性,对其调用save()
如果对象的持久标识(identifier)表明其是一个新实例化的对象,对其调用save()
如果对象是附带版本信息的(通过<version>或<timestamp>) 并且版本属性的值表明其是一个新实例化的对象,save()它。
否则update() 这个对象
四、persist和save区别
这个是最迷离的一对,表面上看起来使用哪个都行,在hibernate reference文档中也没有明确的区分他们.
这里给出一个明确的区分。(可以跟进src看一下,虽然实现步骤类似,但是还是有细微的差别)
这里参考http://opensource.atlassian.com/projects/hibernate/browse/HHH-1682中的一个说明:
1,persist把一个瞬态的实例持久化,但是并"不保证"标识符被立刻填入到持久化实例中,标识符的填入可能被推迟到flush的时间。
2,persist"保证",当它在一个transaction外部被调用的时候并不触发一个SQL的insert,这个功能是很有用的,当我们通过继承Session/persistence context来封装一个长会话流程的时候,一个persist这样的函数是需要的。
3,save"不保证"第2条,它要返回标识符,所以它会立即执行SQL的insert,不管是不是在transaction内部还是外部。
五、saveOrUpdateCopy,merge和update区别
首先说明merge是用来代替saveOrUpdateCopy的,这个详细见这里
http://www.blogjava.net/dreamstone/archive/2007/07/28/133053.html
然后比较update和merge
update的作用上边说了,这里说一下merge的
如果session中存在相同持久化标识(identifier)的实例,用用户给出的对象的状态覆盖旧有的持久实例
如果session没有相应的持久实例,则尝试从数据库中加载,或创建新的持久化实例,最后返回该持久实例
用户给出的这个对象没有被关联到session上,它依旧是脱管的
重点是最后一句:
当我们使用update的时候,执行完成后,我们提供的对象A的状态变成持久化状态
但当我们使用merge的时候,执行完成,我们提供的对象A还是脱管状态,hibernate或者new了一个B,或者检索到一个持久对象B,并把我们提供的对象A的所有的值拷贝到这个B,执行完成后B是持久状态,而我们提供的A还是托管状态
六、flush和update区别
这两个的区别好理解
update操作的是在脱管状态的对象
而flush是操作的在持久状态的对象。
默认情况下,一个持久状态的对象是不需要update的,只要你更改了对象的值,等待hibernate flush就自动保存到数据库了。hibernate flush发生再几种情况下:
1,调用某些查询的时候
2,transaction commit的时候
3,手动调用flush的时候
七、lock和update区别
update是把一个已经更改过的脱管状态的对象变成持久状态
lock是把一个没有更改过的脱管状态的对象变成持久状态
对应更改一个记录的内容,两个的操作不同:
update的操作步骤是:
(1)更改脱管的对象->调用update
lock的操作步骤是:
(2)调用lock把对象从脱管状态变成持久状态-->更改持久状态的对象的内容-->等待flush或者手动flush
参考博客:http://blog.csdn.net/looyo/archive/2009/07/15/4350937.aspx
6、Transaction接口
事务接口的作用:
1、Transaction对应于数据库中事务这个概念,其具体可以参考之前的博客Oracle事务处理。
2、Hibernate要求显示调用事务(如果仅仅是查询而且是使用openSession()获得会话可以不调用事务)
基本语法:
-- 事务开始
Transaction transaction = session.beginTransaction();
-- 事务提交
transaction.commit();
-- 事务回滚(异常处理)
transaction.rollback();
7、Query接口
Query接口类型的对象可以对数据库操作,它可以使用HQL,QBC,QBE和原生SQL(native Sql)对数据库操作.官方推荐使用HQL语句。
案例:
// Query接口测试
public static void queryEmployee() {
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
// 获取Query引用[Employee指的是domain类名而不是表名]
// where后面的条件可以是类的属性名,也可以是表的字段
// 按照hibernate的规定,还是应该使用类的属性名
Query query = session.createQuery("from Employee where id=1");
// 通过list方法获取结果,这个list会自动将结果封装成对应的domain对象
// 所以JDBC进行二次封装的工作就没有了
List<Employee> list = query.list();
for(Employee e : list){
System.out.println(e.getName() + " " + e.getEmail());
}
transaction.commit();
} catch (Exception e) {
// TODO: handle exception
if(transaction != null){
transaction.rollback();
}
throw new RuntimeException(e.getMessage());
} finally {
if (session != null && session.isOpen()) {
session.close();
}
}
}
8、Criteria接口
Criteria接口也可以用于面向对象方式的查询。
案例:
Session session = HibernateUtil.getCurrentSession();
Transaction ts = null;
try {
ts = session.beginTransaction();
Criteria cri = session.createCriteria(Employee.class)
.setMaxResults(2).addOrder(Order.desc("id"));
List<Employee> list = cri.list();
for (Employee e : list) {
System.out.println(e.getAaaid());
}
ts.commit();
} catch (Exception e) {
if (ts != null) {
ts.rollback();
}
throw new RuntimeException(e.getMessage());
} finally {
// 关闭session
if (session != null && session.isOpen()) {
session.close();
}
}
使用MyEclipse开发Hibernate
开发步骤:
1、建库建表
2、创建一个项目
3、使用myeclipse自带的db browser连接到数据库
new一个数据库连接
4、引入hibernate支持(框架)[这里会自动创建hibernate.cfg.xml]
5、使用hibernate反向工程创建POJO类和对象映射文件
6、对各配置文件作相应的修改
7、编写测试类
----------参考《韩顺平.hibernate从入门到精通》