Hibernate学习总结(非常详细的总结,希望对初学者有帮助)

来源:互联网 发布:thinkphp js跳转页面 编辑:程序博客网 时间:2024/05/17 00:10

Hibernate学习总结

       花了几天的时间把Hibernate学习了一下,在这里做一个总结,也算是对学习内容的一个梳理,如果能帮助到看这篇文章的童鞋,那自然是最好不过了。

 用java语言去操作数据库,javaAPI自带的有JDBC,但是,JDBC无法直接面向对象,弊端是:

1.开发效率低
2.代码冗余
3.重复性工作多

     Hibernate对JDBC进行了非常轻量级的对象封装,使我们能够用面向对象的思维去操作数据库。


一、Hibernate核心接口和类

Hibernate的核心接口一共有6个,分别为:Session、SessionFactory、Transaction、Query、Criteria和Configuration。

Session接口:  用来执行执行被持久化对象的CRUD(增删改查)操作.

SessionFactory接口:  SessionFactory接口负责初始化HibernateSessionFactory是重量级的,所以一个项目通常只需要一个SessionFactory了,然后由SessionFactory创建Session对象。

Transaction接口用来处理事务的接口。

Configuration类Configuration对象用来配置和引导Hibernate,一个Hibernat
e应用使用一个Configuration实例来指定主   配置文件的位置,然后创建会话工厂。


二、Hibernate使用前的准备

使用Hibernate前需要搭建项目框架编写hibernate配置文件、实体映射文件。

1)hibernate配置文件默认文件名为“hibernate.cfg.xml”。配置如下

<?xml version='1.0' encoding='utf-8'?><!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><!-- 数据库连接URL --><property name="connection.url">jdbc:oracle:thin:@localhost:1521:XE</property><!-- 数据库用户名  --><property name="connection.username">oa</property><!-- 数据库密码 --><property name="connection.password">oa123</property><!-- 数据库JDBC驱动类名 --><property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property><!-- 数据库方言 --><property name="dialect">org.hibernate.dialect.Oracle10gDialect</property><!-- ddl语句自动建表 --><property name="hbm2ddl.auto">none</property><!-- 是否输出Hibernate生成的SQL语句,开发阶段一般需要开启 --><property name="show_sql">true</property><!-- 是否对输出SQL进行格式化 --><property name="format_sql">true</property><!-- 连接池配置 --><property name="hibernate.connection.provider_class">org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider</property><!-- 这是C3P0随时准备好的最少的JDBC连接数量 --><property name="hibernate.c3p0.min_size">5</property><!-- 连接池中JDBC连接的最大数量 --><property name="hibernate.c3p0.max_size">20</property><!-- 超时周期,在它之后,闲置连接将从池中移除 --><property name="hibernate.c3p0.timeout">300</property><!-- 最多高速缓存100个预编译语句,该属性是使Hibernate获得较好性能的要素。 --><property name="hibernate.c3p0.max_statements">100</property><!-- 连接被自动验证前,以秒为单位的闲置时间 --><property name="hibernate.c3p0.idle_test_period">3000</property><!-- 注册ORM实体类映射文件--><mapping resource="实体类映射文件路径" />…</session-factory></hibernate-configuration>
注:数据库不同连接信息也不同,这里使用的是Oracle10g。mapping resource可以在下面写多条。

2)编写实体映射文件。

<1>创建实体类实体类(也称持久化类)是一个带有一些属性的JavaBean类,实体类对属性的存取方法(getter and setter method)使用了标准JavaBean命名约定,同时把类属性的访问级别设成私有的。为了通过反射机制来实例化类的对象,我们需要提供一个无参的构造器,所有的实体类都要求有无参的构造器,因为Hibernate需要使用Java反射机制来为你创建对象。

最后要为实体类实现Java.io.Serializable 接口,以便Hibernate能更好的缓存实体对象。 通过实体映射文件,Hibernate知道怎样去加载和存储实体类的对象,知道应该访问数据库里面的哪个表及应该使用表里面的哪些字段。

实例如下:

<?xml version="1.0" encoding="UTF-8"?><!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="org.ijob.bean.Seeker" table="ijob_seeker"><!-- 主键映射 --><id name="id" type="string"><column name="id" length="32"></column><generator class="uuid" /></id>      <!—属性映射 --><property name="email" type="string"><column name="email" length="100"></column></property><property name="password" type="string"><column name="password" length="20"></column></property><property name="name" type="string"><column name="name" length="10"></column></property><!—此处省略系列属性映射 --></class></hibernate-mapping>
注:也可以不用写类型和长度,<property name="name" column name="name"></property>,映射文件名后缀hbm.xml

<2>主键生成策略,为了不用在向数据库输入数据的时候每次都要给主键一个ID,可以让系统自动生成,这样可以避免人为输入的重复。如下:

<generator class="sequence" ><param name="sequence">sequence_name</param><!--sequence对象的名字 --></generator>

这样配置之后,每次往数据库中插入新对象,都调用数据库的“sequence_name”所指定的sequence对象来产生其标识符。uuid每次新增对象时,用一个128-bitUUID算法生成长度为32的字符串类型的标识符。当然这也可选的,不配置也可以。

映射文件配置好以后,将映射文件的路径信息添加hibernate.cfg.xml中。

<mapping resource=“***/***/***.hbm.xml"/>


注意问题:映射文件在配置的时候要注意Hibernate关联映射,对于实体之间的关系通过一个对象持有另一个对象的实例,但是在映射文件中是怎么样来表示的呢?

1.单向多对一关联,many的一端应持有one的一端的对象(引用)代码示例

2.单向一对多关联,one的一端应持有many端的对象集合

解释:多本书对应一种书籍类型,体现多对一。

         一本书应该有很多的借阅记录,体现一对多。

<?xml version="1.0"?><!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="entity.Book" table = "books"><!-- 属性和主键 --><id name="isbn" column="book_id"><!-- 主键生成策略 --><generator class="sequence"><param name="sequence">lib_seq</param><!--sequence对象的名字 --></generator></id><!-- 属性和字段 --><property name="name" column="name" length="20"></property><property name="price" column="price"  ></property><property name="author" column="author" length="20"></property><property name="count" column="count"></property><property name="press" column="press" length="20"></property><!-- 多本书一种类型 --><many-to-one name="type" class="entity.Type" >  <column name="type_id" ></column></many-to-one> <!-- 一本书可以有多条记录,书的映射文件,先看书对应 --><set name="records"><key column="book_id"></key><one-to-many class="entity.Record"/></set></class></hibernate-mapping>



三、数据库操作

这些准备工作都做完以后就可以对数据库进行操作了。流程为

获取配置信息>>创建SessionFactory>>打开Session>>开始一个事物>>持久化操作>>提交事物>>关闭Session

事例代码:

        // 1、获取配置Configuration cfg = new Configuration().configure();ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();// 2、创建SessionFactorySessionFactory factory = cfg.buildSessionFactory(serviceRegistry);// 3、获取sessionSession session = factory.openSession();Transaction tx = session.getTransaction();try {// 4、开启事务tx.begin();Seeker seeker = new Seeker();seeker.setEmail("abc@163.com");seeker.setPassword("abc");// 5、保存seeker对象session.save(seeker);// 6、提交事务session.getTransaction().commit();} catch (Exception e) {tx.rollback();e.printStackTrace();} finally {// 7、关闭sessionsession.close();}}
注:session.close()写在finally里,以确保这个方法可以被执行。

session可操作的对象为,持久态的对象,session中的三种状态为瞬时状态,持久状态,游离状态,我们直接new一个对象为还没有被持久化,而且不处于Session的缓存中为瞬时态,我们可以通过save,update使瞬时太转为持久态,如果我们delete这个对象,则又放回瞬时态,session关闭后持久态转为游离态,不过可以通过get重新使游离态变为持久态。


每次我们操作数据都要写前面的一些获取session的信息,我们可以将相同的代码封装,建立一个工具类HibernateUtils,

这样通过方法一句就可以获得这个Session的实例。

难点

Hibernate默认的加载数据的方法为延迟加载,也就是对于一些属性里面有关联对象或者集合的类,当你去查询获得这个类的属性的时候,这些关联的对象或者集合不会直接返回给你。示例:

public void test() {Session session = HibernateUtils.currentSession();Type type = (Type) session.get(Type.class, 1);System.out.println(type.getName());System.out.println("=========================");HibernateUtils.closecurrentSession();Set<Book> books = type.getBooks();System.out.println(books);}
我们先不看其他的东西,有一个书籍类型的类,这个类里面包含了书的集合(一个书籍类型包含很多书);我们用session方法或者这个ID为1的书籍类型,然后关闭Session,从打印结果可以看出,关闭Session之前,我们可以直接获得Type类的属性(除关联对象或者集合),打印输出。关闭后,我们不能获得,Book类的集合。这就是延迟加载,不会立刻返回所有数据给你。

如果直接返回所有数据,可能一个表里面关联其他对象和集合,这样都一次加载完,有些可能我们不需要的数据也都给返回了,造成内存的浪费,效率也不高。

解决Session关闭在调用对象方法的办法

1.在一个线程内只有一个Session,在我们把这个调用链路全部走完最后关闭Session,这样我们可以任意调用数据。参考文章http://blog.csdn.net/zhengwei223/article/details/9376299;这个最好写在工具类HibernateUtils,示例代码

import org.hibernate.HibernateException;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.cfg.Configuration;import org.hibernate.service.ServiceRegistry;import org.hibernate.service.ServiceRegistryBuilder;public class HibernateUtils {public static final SessionFactory SESSION_FACTORY; // 静态变量public static final ThreadLocal<Session> HIBERNATE_SESSION_LOCAL = new ThreadLocal<Session>(); //线程的变量static {// 静态初始化块try {Configuration cfg = new Configuration().configure();// Hibernate4提供的新方式ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();SESSION_FACTORY = cfg.buildSessionFactory(serviceRegistry);} catch (Throwable ex) {throw new ExceptionInInitializerError(ex);}}  /**   * 创建一个session制造器   * @return 返回一个SessionFactory   * */  public static SessionFactory getSessionFactory() {return SESSION_FACTORY;}/** * 关闭该session工厂 * */public static void shutdown() {getSessionFactory().close();}/** *  * @return返回一个线程的session * */    public static Session currentSession() throws HibernateException {         Session s = HIBERNATE_SESSION_LOCAL.get();         if(s == null) {         s = SESSION_FACTORY.openSession();         HIBERNATE_SESSION_LOCAL.set(s);         }         return s;         }         /**     * 关闭一个线程的session     * */    public static void closecurrentSession() throws HibernateException {         Session s = HIBERNATE_SESSION_LOCAL.get();         if(s != null) {         s.close();         }         HIBERNATE_SESSION_LOCAL.set(null);         }     }
注:SessionFactory是重量级的,创建很费资源,在一个线程中只需要一个就够了。而且是共享的。

2.第一次获得该对象的时候用get而不用load,因get不支持懒加载,在get该对象的同时会想数据库发出sql语句,取出该对象的相应信息放入缓存中,下次即使session已经关闭,因缓存中有相应的数据,查找该对象时会首先从缓存中查询,直接拿来用就ok了。

3.在映射文件中lazy设为false,这样在load该对象的同时就会发sql语句取出该对象放到缓存中,以后再用可以直接从缓存中取。但是这种方法一般不推荐使用。

四、Hibernate的缓存机制

一级缓存:session级的缓存也叫事务级的缓存,只缓存实体,生命周期和session一致。不能对其进行管理。
在Session的save()、update()、saveOrUpdate()、get()或load(),如果在Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。


二级缓存:sessionFactory缓存,也叫进程级的缓存,也值缓存实体,生命周期和sessionFactory一致,可以进行管理。