Hibernate框架技术视频课程——笔记(一)

视频课程链接:http://edu.51cto.com/course/10747.html

Hibernate框架技术,主讲:汤小洋

一、Hibernate简介

1. Hibernate是什么?

hibernate单词本义是“冬眠”,是一个持久层框架,或称为ORM(Object Relational Mapping)框架,类似的还有MyBatis

  • 用来访问数据库,做数据持久化操作
  • 本质上只是对JDBC操作进行封装,Java访问数据库的唯一方式就是JDBC
  • 简化JDBC繁琐的操作

版本:hibernate 3.x 4.x 5.x

2. 学习参考

官网

中文官方文档:Hibernate_Reference_zh_CN.pdf

二、回顾

1. JDBC访问数据库的步骤

Class.forName(driverClassName)
Connection conn=DriverManager.getConnection(url,user,password);
PreparedStatement ps=conn.preparedStatement(sql);
//ps.executeUpdate();
ResultSet rs=ps.executeQuery();
while(rs.next){
  //RM(RowMapper)行映射
}
rs.close();
ps.close();
conn.close();

2. 数据库操作中的可变部分

2.1 连接信息

​ driverClassName、url、user、password

​ 也称为数据源datasource

2.2 SQL语句

​ sql

2.3 行映射RM

​ 映射关系:

一条数据 对象
属性

3. MyBatis访问数据库的步骤

3.1 添加jar包

​ mybatis-3.x.jar

​ mysql-connector-java-x.jar

3.2 添加dtd文件

​ mybatis-config.dtd

​ mybatis-mapper.dtd

3.3 两个核心文件

​ 核心配置文件config --> 数据源、mybatis运行时的环境变量、映射文件的路径

​ 映射文件mapper --> sql语句、映射关系

3.4 测试类

1. 获取SessionFactory
    2. 获取Session
    3. 调用Session中的方法

三、Hibernate访问数据库的数据

1. 添加jar包

1.1 下载hibernate官方包

​ hibernate-release-4.3.0.Final.zip

1.2 添加相关库及依赖

​ hibernate-release-4.3.0.Final/lib/required目录下所有的jar包

​ 数据库驱动jar包

1.3 添加hibernate源码

​ 右击"hibernate-core-4.3.0.Final.jar"——>Properties——>Java Source Attachment——>External Folder,选择hibernate官方包中的hibernate-release-4.3.0.Final/project/hibernate-core/src/main/java

2. 关于dtd文件

​ MyEclipse对Hibernate做了集成,内置了Hibernate的dtd文件

3. 两个核心文件

3.1 hibernate.cfg.xml

​ hibernate的核心配置文件,用来配置数据源,指定映射文件等,类似于MyBatis中的config文件

​ 该文件一般位于src目录下,文件名可自定义,一般都默认为hibernate.cfg.xml

<hibernate-configuration>
    <!-- 配置数据库datasource -->
    <session-factory>
        <!-- 配置连接信息 -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/hibernate?useUnicode=true&amp;characterEncoding=utf8</property>
        <property name="connection.username">root</property>
        <property name="connection.password"></property>
        <!-- <property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
        <property name="connection.url">jdbc:oracle:thin:@localhost:1521:orcl</property>
        <property name="connection.username">scott</property>
        <property name="connection.password">tiger</property> -->

        <!-- 方言,详见文档 3.4.1 SQL方言 -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <!-- <property name="dialect">org.hibernate.dialect.OracleDialect</property> -->

        <!-- 在控制台显示执行的sql语句 -->
        <property name="show_sql">true</property>  
        <!-- 格式化sql语句,更易读 -->
        <property name="format_sql">false</property>
        <!-- 更多配置项,详见 3.4 可选的配置项 -->

    </session-factory>
</hibernate-configuration>

3.2 xxx.hbm.xml

3.2.1 hibernate映射文件

​ 用来配置映射关系,类似于MyBatis中的mapper文件

​ 但是hibernate中的映射文件只配置映射关系,不配置sql语句,与MyBatis有所不同

<hibernate-mapping>
    <!-- 配置映射关系,将类与表进行映射 -->
    <class name="day01.User" table="t_user">
        <!-- 将列和属性映射 -->
        <!-- 
            name:指的是类中的属性,准备的来说是属性的set方法
            column:指的是表中的列,也可以使用<column>子元素来指定 
         -->
        <id name="id" column="id">
            <!-- <column name="id"></column>  -->
            <!-- 
                配置主键的生成策略,不同的数据库主键生成方式有所不同
                    mysql:自增长 auto_increment
                    oracle:序列 sequence
                    sql server:自增长 identity
                具体的生成策略,详见文档 5.1.4.1 Generator,常用的如下:
                    native:hibernate根据数据库自动的选择主键生成策略
                            mysql:自增长
                            oracle:序列,hibernate会自动创建序列hibernate_sequence,但只会创建一个,多表共用,从而导致主键不连续
                    increment:自增长,适应于mysql和sql server
                            也可以为oracle设置自增长,增长方式如下:
                                先查询要插入表的主键的最大值,将该最大主键值+1,然后将新值作为要插入记录的主键值
                    sequence:序列,适应于oracle
                            需要指定序列的名称,也就是在oracle中创建的序列
                            如果不指定,则默认使用hibernate自动创建的序列
                    assigned:由用户自己指定主键
                    uuid:使用uuid算法,生成唯一的uuid         
             -->
            <generator class="native"></generator>
            <!-- <generator class="sequence">
                <param name="sequence">seq_user</param>
            </generator> -->
        </id>

        <!-- 其他属性 -->
        <property name="username" column="name"></property>
        <property name="password" column="pwd"></property>
        <property name="age" column="age"></property>
    </class>

</hibernate-mapping>
3.2.2 持久化类

​ 创建持久化类User.java

​ 简单的持久化类也称为POJO(Plain Old Java Object),即简单传统的Java对象

​ 实体类、持久化类、POJO、java bean

4. 测试类

4.1持久化操作的步骤

public static void main(String[] args) {
  // 1. 创建Configuration对象
  Configuration config = new Configuration().configure();
  // 读取hibernate.cfg.xml文件,默认读取src下的hibernate.cfg.xml文件,如果同名则可以省略
  // config.configure("hibernate.cfg.xml");
  // config.configure();

  // 2.读取并解析映射信息,创建会话工厂SessionFactory
  // hibernate3.x中可以调用buildSessionFactory()方法获取SessionFactory,在4.x中已过时
  // SessionFactory sf = config.buildSessionFactory();
  // hiberate4.x中通过ServiceRegistry接口来获取,目的是解耦合
  StandardServiceRegistryBuilder ***b = new StandardServiceRegistryBuilder()
    .applySettings(config.getProperties());
  // ***b.applySettings(config.getProperties());
  StandardServiceRegistry *** = ***b.build();
  SessionFactory sf = config.buildSessionFactory(***);

  // 3. 获取会话Session
  Session session = sf.openSession();

  // 4.开启事务Transaction(增、删、改),Hibernate默认关闭自动提交事务
  Transaction tx = session.beginTransaction();

  try {
    // 5.执行持久化操作
    session.save(new User(null, "tom", "123", 21));
    // 6.提交事务
    tx.commit();
  } catch (Exception e) {
    tx.rollback(); // 回滚事务
    e.printStackTrace();
  } finally {
    // 7.关闭会话
    session.close();
  }
}

4.2 自动生成数据库表结构

<!-- 
   根据映射关系自动生成数据库表结构,常用取值:
    create:每次创建SessionFactory时都执行建表语句
    update:当映射关系与数据库表结构不一致时更新表
   -->
<property name="hbm2ddl.auto">update</property>

5. 解决进程不关闭的问题

​ hibernate 4.x版本中,在持久化操作执行结束并关闭session后,进程并不会关闭,如何解决?

/*
 * hibernate4.x在创建SessionFactory时,会自动创建一个线程
 * 由该线程创建和管理SessionFactory,所以需要关闭SessionFactory,目的是为了关闭该线程
 */
sf.close();
//销毁StandardServiceRegistry实例
StandardServiceRegistryBuilder.destroy(***);

6. 创建HibernateUtil工具类

/*
 * Hibernate工具类
 */
public class HibernateUtil {

    private static StandardServiceRegistry ***;
    private static SessionFactory sessionFactory;
    private static ThreadLocal<Session> local=new ThreadLocal<Session>();

    static {
        try {
            Configuration config = new Configuration().configure();
            *** = new StandardServiceRegistryBuilder().applySettings(
                    config.getProperties()).build();
            sessionFactory=config.buildSessionFactory(***);
        } catch (HibernateException e) {
            e.printStackTrace();
        }
    }

    public static Session getSession() {
        Session session=local.get();
        if(session==null||!session.isOpen()){
            session=sessionFactory.openSession();
            local.set(session);
        }
        return session;
    }

    public static void close(){
        Session session=local.get();
        if(session!=null){
            session.close();
            local.remove();
            /*
             * 为了程序能够及时结束,此处将sessionFactory关闭并销毁***
             * 实际开发中,该操作都由其他容器(spring框架)负责,无需自己操作
             */
            sessionFactory.close();
            StandardServiceRegistryBuilder.destroy(***);
        }
    }
}

四、Hibernate中对象的状态

1. 简介

​ Hibernate通过Session来管理对象的状态,对于Hibernate而言,对象共有三种状态

2. 三种状态

2.1 瞬时状态(Transient)

​ 内存中有,在数据库中没有对应的记录,对象不受session管理

​ 也称为临时状态

2.2 游离状态(Detached)

​ 内存中有,在数据库中有对应的记录,对象不受session管理

​ 也称为托管状态

2.3 持久状态(Persistent)

​ 内存中有,在数据库有对应的记录,对象受session管理

  • 什么叫有对应的记录

    在数据库中能找到主键id对应的数据

  • 什么叫受session管理

    由session查询出的对象或作为参数交给session处理的对象

特点:如果对象的状态改变,hibernate会自动更新到数据库,保存数据的一致性

3.脏对象和脏检查

​ 脏对象:在hibernate中状态前后发生改变的对象,称为脏对象

​ 脏检查:当提交事务或刷新Session缓存时hibernate会对Session中的持久状态的对象进行检测,判断对象的数据是否发生了改变(与之前的快照做对比),如果发生改变则更新同步到数据库中,以保持数据的一致性,称为刷新缓存

​ 刷新缓存的时机:

  • 提交事务,调用Transaction的commit()
  • 刷新缓存,调用Session的flush()

区别:commit()方法会先调用flush()方法,然后再提交事务

​ flush()方法只会刷新缓存,不会提交事务,所以还需要手动提交事务

五、Session API

1. save方法

执行插入操作

Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
User user=new User(null, "fff", "777", 29); //瞬时状态
user.setAge(40);
session.save(user); //参数为瞬时状态的对象
tx.commit();
HibernateUtil.close();

2. delete方法

执行删除操作,根据主键id进行删除,参数对象的id属性必须有值,与其他属性无关

Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
User user=new User(7, "abc", "777", 29); //游离状态
session.delete(user); //参数为持久状态或游离状态的对象
tx.commit();
HibernateUtil.close();
  • 如果对象id属性有值,且在数据库中存在对应的记录,则正常执行删除
  • 如果对象id属性没有值(id==null),则不会执行删除操作
  • 如果对象id属性有值,但在数据库中没有对应的记录,会报错
 org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

3. load和get方法

3.1 load方法

执行查询操作,根据主键id查询对象,参数1表示对应类的class对象或类的全名,参数2表示主键id

Session session = HibernateUtil.getSession();
// 两个参数,第一个参数表示要查询的表对应的类的Class对象或类的全名,第二个参数表示主键id
// User user=(User) session.load(User.class, 2); //只能根据主键查询
User user = (User) session.load("day01.User", 2);
System.out.println(user);
HibernateUtil.close();

查询操作无需开启事务

3.2 get方法

与load类似

User user = (User) session.get(User.class, 2); // 只能根据主键查询

3.3 get和load区别

  • 当数据库不存在对应的记录

    get:不报错,返回null

    load:报错,抛异常org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [day01.User#22]

  • 查询操作执行的时机

    get:调用get方法时立即执行查询操作

    load:第一次访问查询对象时才真正执行查询操作,称为延迟加载或懒加载

4. update方法

4.1 执行更新操作

​ 根据主键id进行更新

Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();

User user=new User(3,"ggg","999",18); //游离状态
session.update(user);

tx.commit();
System.out.println("success");
HibernateUtil.close();

如果参数对象的主键id在数据库中不存在对应的记录,会报错

org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

4.2 存在的问题

//存在的问题:只想修改age,但会发现username和password也被修改了,如何解决?
User user=new User();
user.setId(3);
user.setAge(10);

两种解决方式:

  • 方式1:先使用load/get获取对象,然后修改对象,最后再update更新对象
//方式1:需要注意的是该方式仍会更新所有字段,只不过已获取所有字段的原数据
User user=(User) session.load(User.class, 4);
user.setAge(20);
  • 方式2:动态DML操作

4.3 动态DML操作

​ 主要是用来优化sql性能,默认值都为false

  • dynamic-insert="false" 只插入不为null的字段

  • dynamic-update="false" 只更新发生变化的字段

    ​ 要求对象必须是持久状态,即只针对持久状态对象的更新才有效

// 方式2:只更新发生变化的字段,SQL优化
// User user=(User) session.load(User.class, 4);
// user.setAge(40);

4.4 禁止插入或更新特定字段

<property name="password" column="pwd" update="false" insert="false"></property>

5. saveOrUpdate方法

执行插入或更新操作,具体情况如下:

  • 如果传入参数是瞬时对象,且对象没有主键id,则执行save操作
  • 如果传入参数是游离或持久对象,则执行update操作
  • 如果传入参数是瞬时对象,但对象有主键id,则执行update操作,此时会报错,因为数据库没有对应的主键记录
// User user=new User(null,"itany","123",20); //瞬时状态
// User user=new User(13,"welcome","111",20); //游离状态
User user = new User(40, "hello", "111", 20); // 瞬时状态
session.saveOrUpdate(user);

总结:如果对象id为null,执行save操作;如果对象id不为null,则执行update操作

6. merge方法

与saveOrUpdate类似,区别在于merge方法不会修改传入的参数对象状态,而是返回操作后的对象副本(修改后,持久状态)

User user = new User(null, "world", "123", 20); // 瞬时状态
User u = (User) session.merge(user);
// 此时user仍为瞬时状态,返回的对象u为持久状态
System.out.println(user.getId());
System.out.println(u.getId());

六、关于no session的问题

1. 创建事务管理类

public class TransactionManager {
    public static void beginTransaction(){
        HibernateUtil.getSession().beginTransaction();
    }

    public static void commit(){
        HibernateUtil.getSession().getTransaction().commit();
        HibernateUtil.close();
    }

    public static void rollback(){
        HibernateUtil.getSession().getTransaction().rollback();
        HibernateUtil.close();
    }
}

2. no session问题

分层开发中,在DAO层获取数据,在Service层处理事务并关闭Session,在Action中获取并操作数据时如果是懒加载,则会报错:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session

原因:懒加载,获取的是代理对象,当真正访问数据并进行查询时发现session已关闭

解决:使用get方法

Session session=HibernateUtil.getSession();
User user=(User) session.load(User.class, 1); //class day01.User_$$_jvst724_0 代理对象
//      User user=(User) session.get(User.class, 1); //使用get,立即加载数据
System.out.println(user.getClass());

猜你喜欢

转载自blog.51cto.com/12402007/2159433