Spring RE 10 Spring中使用Hibernate

Spring RE 10 Spring中使用Hibernate

学到这里, 我发现自己又要开启两个大的分支, 一个是PostgreSQL的学习和使用, 一个是Hibernate的学习和使用. 这两块又是两个重要的支线. 果然是学无止境啊. 这篇东西重在Spring中使用Hibernate, 而不是单独研究Hibernate的ORM机制. 说道Hibernate又

学到这里, 我发现自己又要开启两个大的分支, 一个是PostgreSQL的学习和使用, 一个是Hibernate的学习和使用. 这两块又是两个重要的支线. 果然是学无止境啊.

这篇东西重在Spring中使用Hibernate, 而不是单独研究Hibernate的ORM机制. 说道Hibernate又不能不提JPA, 总之想想要学的东西实在是刺激.

最近买了两本幼儿编程书, 给女儿讲的时候发现对于循环, 判断和分支小家伙比以前能够更了解了, 看来我也必须抓紧学习, 后边要装备的知识除了PostgreSQL, Hibernate, 还有Linux使用和Nginx配置, 感觉这些都搞定, 就可以顺利的教女儿了.

好了, 研究过这一部分之后, 接下来是缓存和异步任务, 都完成之后, 总算就具备了开发Web应用的技术准备, 才能够重新学习Spring MVC.

  1. Spring整合Hibernate的方式
  2. 使用HibernateTemplate
  3. 回调接口
  4. 其他知识

Spring整合Hibernate的方式

通过前边的学习, 已经知道了Spring对第三方ORM框架整合的原理不外乎这几板斧:

  1. 为ORM创建基础设施, 也就是XXXTemplate.
  2. 为基础设施统一异常封装
  3. 为不同的基础设施提供对应的事务管理器, 统一进行事务管理

相比之前的JDBC这种需要直接使用DataSource对象的更底层的数据库操作技术, Hibernate是一个完整的ORM框架.

Hibernate的基础设施不再是DataSource, SessionFactory和Session对象, 从名字能看出来, 前者生产出后者. 二者的关系和从DataSource中获取Connection很类似.

先来看Hibernate如何创建SessionFactory.

首先需要创建Hibernate的配置文件, 一般叫做hibernate.cfg.xml, 如果是maven项目, 要将其放入main/resources目录下:

<?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>
        <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/sia5</property>
        <property name="connection.username">root</property>
        <property name="connection.password">fflym0709</property>
        <property name="dialect">org.hibernate.dialect.MySQL8Dialect</property>
        <property name="show_sql">true</property>
        <mapping class="cc.conyli.jdbc.Course"/>
    </session-factory>

</hibernate-configuration>

可见其中封装了需要连接数据库的信息. 和DataSource需要的信息很类似. 注意红色部分, 指定了一个ORM类, 这里没有使用xxx.hbm.xml的方式, 所以来使用注解改造一下Course类:

import javax.persistence.*;

@Entity
@Table(name = "course")
public class Course {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(name = "course_name")
    private String courseName;

    public Course() {

    }

    public Course(int id, String courseName) {
        this.id = id;
        this.courseName = courseName;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getCourseName() {
        return courseName;
    }

    public void setCourseName(String courseName) {
        this.courseName = courseName;
    }

    @Override
    public String toString() {
        return "Course{" +
                "id=" + id +
                ", courseName='" + courseName + '\'' +
                '}';
    }
}

这几个注解就不再多解释了, 毕竟之前也学过一些Hibernate的使用, 意思还是知道的.

然后启动Hibernate即可:

import cc.conyli.jdbc.Course;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

import java.time.LocalDateTime;

public class Test1 {

    public static void main(String[] args) {
        Configuration configuration = new Configuration().configure();
        SessionFactory sessionFactory = configuration.buildSessionFactory();

        //获取一个会话
        Session session = sessionFactory.openSession();
        //启动事务
        Transaction transaction = session.beginTransaction();

        //创建一个新对象
        Course newCourse = new Course();
        newCourse.setCourseName(LocalDateTime.now().toString());

        //更新或者保存
        session.saveOrUpdate(newCourse);

        //提交事务
        transaction.commit();

        //关闭会话
        session.close();
    }
}

SessionFactory就是Hibernate的最基础的设施. 之后的获取会话像极了JDBC获取连接. 当然, 为什么叫ORM框架, 就是可以直接存取Course对象.

好了, 如何使用Hibernate本身要另外去研究, 这里要看Spring如何创建基础设施.

其实看到这里也就明白了, 既然Hibernate可以使用XML文件去创建一个SessionFactory, 那么Spring用IOC容器, 也可以根据配置文件创建一个基于SessionFactory的Bean, 在容器里使用这个Bean, 就相当于整合了Hibernate.

Spring确实这么做了, 干这个活的就是org.springframework.orm.hibernate5.LocalSessionFactoryBean.

这个Bean可以使用Hibernate的配置文件, 也可以完全不使用. 最后会创建一个Hibernate的SessionFactory的代理对象, 能够符合Spring事务管理机制. 所以本质就是这么简单.

在Spring的XML配置中添加一个Bean:

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"
      p:configLocation="hibernate.cfg.xml"/>

然后启动:

public static void main(String[] args) {

    SessionFactory sessionFactory = (SessionFactory) Util.getIOCContainer().getBean("sessionFactory");
    System.out.println(sessionFactory);

    Session session = sessionFactory.openSession();

    Course newCourse = new Course();
    newCourse.setCourseName("New" + LocalDateTime.now().toString());

    session.saveOrUpdate(newCourse);
    session.close();
}

可以看到神奇的先出现了Hibernate5的启动日志, 后边的使用, 就和使用一个Hibernate原生的SessionFactory没有任何区别.

当然, Bean里写了要去读取Hibernate的配置文件, 那也没有什么意思了, LocalSessionFactoryBean最强的地方就是完全不需要额外读取Hibernate的配置文件, 只要在Spring的配置文件中写上相应的信息就可以了. 配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <util:properties id="properties" location="config.properties"/>
    <context:property-placeholder properties-ref="properties"/>
    <context:component-scan base-package="cc.conyli"/>

    <!--DataSource还是需要的-->
    <bean class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close" p:defaultAutoCommit="true" id="source"
          p:driverClassName="com.mysql.cj.jdbc.Driver"
          p:url="jdbc:mysql://localhost:3306/sia5"
          p:username="root"
          p:password="fflym0709"
    />

    <!--实体类映射文件-->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"
          p:dataSource-ref="source">
        <property name="annotatedClasses">
            <list>
                <value>cc.conyli.jdbc.Course</value>
            </list>
        </property>

        <!--Hibernate一些配置属性-->
        <property name="hibernateProperties">
            <props>
                <prop key="dialect">org.hibernate.dialect.MySQL8Dialect</prop>
                <prop key="show_sql">true</prop>
            </props>
        </property>
    </bean>
</beans>

对于已经熟悉了XML的我们, 其实就和用代码去配置差不多, LocalSessionFactoryBean的属性也确实有这些内容. 现在完全不需要hibernate.cfg.xml文件了. 直接再启动程序, 就会发现成功执行了.

这就是Spring整合Hibernate的方式. 在容器中, 实际使用的就是这个LocalSessionFactoryBean代理类, 这个代理类符合Spring事务管理要求, 也能够从其中取得线程绑定的Session.

当然这只是最简单的XML文件配置, LocalSessionFactoryBean也可以配置成载入其他hbm.xml文件, 或者加载通配符标注的多个类, 只要参数是数组类型, 都可以使用通配符. 还记得之前说过的, 如果要使用Hibernate, 必须要配置Hibernate的事务管理器.

要使用HibernateTemplate, 就需要配置HibernateTemplate和事务管理器, 这样就不用通过具体代码来管理事务了.

所以把上边的XML文件补全:

<!--配置HibernateTemplate, 要使用到SessionFactory对象-->
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate"
      p:sessionFactory-ref="sessionFactory"/>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"
      p:sessionFactory-ref="sessionFactory"/>
<tx:annotation-driven transaction-manager="transactionManager"/>

经过这么配置之后, 事务管理器和HibernateTemplate都OK了, 就可以通过容器直接获取HibernateTemplate对象来进行操作了. 通过配置也可以看出, SessionFactory封装DataSource, HibernateTemplate 和事务管理器又依赖SessionFactory这样一个体系.

其和JDBC的配置数据源, 事务管理器依赖数据源, 也是非常类似的.

使用HibernateTemplate

这里目前只能简单写过, 就是因为Hibernate的详细增删改查以及缓存的秘密还没有系统的学习.

  1. Serializable save(Object entity), 保存实体对象并返回主键值
  2. void update(Object entity), 更新对象
  3. void saveOrUpdate(Object entity), 保存或更新一个实体, 还有一个类似的 T merge(T entity), 是JPA规定的方法.
  4. void delete(Object entity), 删除一个实体.
  5. <T> List<T> findByExample(T exampleEntity), 传入要查找的一个对象, 会查找这个对象所在表的所有对象, 有很多详细控制的方法.
  6. List<?> findByCriteria(DetachedCriteria var1), 按条件查询, 由于现在没有学Hibernate, 所以不知道如何使用.

读到这里我也会想, 如何判断一个实体是不是已经存在数据库中, 如何做到save Or Update呢, 剩下的这些内容估计要好好的去学习Hibernate5的具体使用了.

回调接口

Spring提供了一个回调接口HibernateCallback, 其中的唯一一个方法doInHibernate(Session session), 其中可以操作Session对象来进行更底层的操作.

HibernateTemplate中有三个方法可以传入回调对象:

  1. public <T> T execute(HibernateCallback<T> action)
  2. public <T> T executeWithNativeSession(HibernateCallback<T> action)
  3. protected <T> T doExecute(HibernateCallback<T> action, boolean enforceNativeSession)

第三个方法很显然不是为了外部使用, 所以只有前两个方法可以使用这个回调接口. 使用这个接口的目的就是可以更底层的操作数据, 看一个例子:

public static void main(String[] args) {
    HibernateTemplate template = (HibernateTemplate) Util.getIOCContainer().getBean("hibernateTemplate");

    Course result = template.execute(new HibernateCallback<Course>() {
        @Override
        public Course doInHibernate(Session session) throws HibernateException {
            Transaction transaction = session.beginTransaction();
            String hql = "from Course course where course.id = 4";
            Course course = session.createQuery(hql, Course.class).getSingleResult();
            transaction.commit();
            session.close();
            return course;
        }
    });
    System.out.println(result);
}

这实际上就是详细的去控制了如何查询一个对象.

其他知识

其他一些细节比如Hibernate的事件监听器, 以及Hibernate 3.0开始的新特性, 可以使用SessionFactory.getCurrentSession()来获取线程绑定的Session, 让直接使用原生的Hibernate API变得可能.

Hibernate支持JPA的注解, 所以看到的注解都是Hibernate实现的javax.persistance其中的类.

事务只要配置了事务管理器, 加载了@Transactional注解就可以了, 简单易用. 这里看了一点关于Spring boot的自动配置, 发现只要配置了start-jdbc, 就会自动创建jdbc的事务管理器, 如果配置了JPA, 则会自动创建Hibernate的事务管理器.

至于延迟加载的技术, 就等学习了Hibernate 5再说吧.

HibernateTemplate的内容比较少, 是因为目前Hibernate这条大支线任务还没有开始做.

看来必须要马上提上日程了, 看一看这个JPA标准的推动和实现者, 外加完整ORM支持的Hibernate到底有什么样的魅力.

LICENSED UNDER CC BY-NC-SA 4.0
Comment