PS:Hibernate核心类与接口就是Hibernate框架的核心部分,通过对最底层配置文件的配置,将数据库与Java POJO进行映射,通过Configuration对象加载配置文件,再通过SessionFactory创建Session,得到会话之后就能进行CUD操作,然后可以使用Criteria和Query进行R操作。当然在CRUD操作,请不要忘了Transaction这个事务控制接口。
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文件,带参数的可以自己指定加载文件位置。
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>
对象关系映射文件的作用:
1、该文件主要作用是建立表和类的映射关系,是不可或缺的重要文件
2、一般放在其映射的类同一个目录下,但不是必须的
3、命名方式一般是类名.hbm.xml,但不是必须的
会话工厂接口的作用:
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();
本地事务是针对一个数据库的事务,其所有操作都是在一个数据库上面进行的。
全局事务是指跨数据库的事务,可以是一台主机上面的不同数据库,也可以是不同主机上面的不同数据库。
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。
会话接口的作用:
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接口内容。
// 添加员工 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("[email protected]"); employee.setHiredate(new Date()); // 保存 // --->insert into ...[被hibernate封装] session.save(employee); // 提交 transaction.commit(); session.close(); }
// 删除员工信息 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(); } } }
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()(这样效率高)。
-- 测试是否抛出异常 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);
// 修改员工信息 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,它的对象有三种状态,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是把一个脱管状态的对象保存
这个是比较好理解的,顾名思义,saveOrUpdate基本上就是合成了save和update
引用hibernate reference中的一段话来解释他们的使用场合和区别
通常下面的场景会使用update()或saveOrUpdate():
程序在第一个session中加载对象
该对象被传递到表现层
对象发生了一些改动
该对象被返回到业务逻辑层
程序调用第二个session的update()方法持久这些改动
saveOrUpdate()做下面的事:
如果对象已经在本session中持久化了,不做任何事
如果另一个与本session关联的对象拥有相同的持久化标识(identifier),抛出一个异常
如果对象没有持久化标识(identifier)属性,对其调用save()
如果对象的持久标识(identifier)表明其是一个新实例化的对象,对其调用save()
如果对象是附带版本信息的(通过<version>或<timestamp>) 并且版本属性的值表明其是一个新实例化的对象,save()它。
否则update() 这个对象
这个是最迷离的一对,表面上看起来使用哪个都行,在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内部还是外部。
首先说明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还是托管状态
这两个的区别好理解
update操作的是在脱管状态的对象
而flush是操作的在持久状态的对象。
默认情况下,一个持久状态的对象是不需要update的,只要你更改了对象的值,等待hibernate flush就自动保存到数据库了。hibernate flush发生再几种情况下:
1,调用某些查询的时候
2,transaction commit的时候
3,手动调用flush的时候
update是把一个已经更改过的脱管状态的对象变成持久状态
lock是把一个没有更改过的脱管状态的对象变成持久状态
对应更改一个记录的内容,两个的操作不同:
update的操作步骤是:
(1)更改脱管的对象->调用update
lock的操作步骤是:
(2)调用lock把对象从脱管状态变成持久状态-->更改持久状态的对象的内容-->等待flush或者手动flush
参考博客:http://blog.csdn.net/looyo/archive/2009/07/15/4350937.aspx
事务接口的作用:
1、Transaction对应于数据库中事务这个概念,其具体可以参考之前的博客Oracle事务处理。
2、Hibernate要求显示调用事务(如果仅仅是查询而且是使用openSession()获得会话可以不调用事务)
-- 事务开始 Transaction transaction = session.beginTransaction(); -- 事务提交 transaction.commit(); -- 事务回滚(异常处理) transaction.rollback();
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(); } } }
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(); } }
new一个数据库连接
----------参考《韩顺平.hibernate从入门到精通》