Hibernate介绍
出现背景
在Hibernate等ORM框架出现之前,Java Web应用通常使用JDBC来进行持久层的数据库访问等操作,但是JDBC存在着很多缺点,比如实现业务逻辑的代码和数据库访问的代码掺杂在一起,使程序结构不清晰,可读性差;在程序代码中嵌入面向关系的SQL语句,使开发人员不能完全运用面向对象的思维来编写程序;业务逻辑和关系数据模型绑定,如果关系模型发生变化,需要手工修改程序代码中所有相关的SQL语句,增加了程序维护的难度等等。
如何解决问题
为了解决直接使用JDBC的诸多弊端,以Hibernate为代表的ORM框架应运而生。Hibernate是一个基于Java的开源的持久化中间件,对JDBC做了轻量的封装。它采用ORM映射机制,负责实现Java对象和关系数据库之间的映射,把SQL语句传给数据库,并且把数据库返回的结果封装成对象。Hibernate内部封装了JDBC访问数据库的操作,向上层应用提供了面向对象的数据库访问API,使得Java程序员可以轻松使用面向对象的编程思维去操纵数据库,从 95% 的公共数据持续性编程工作中解放出来。
Why Hibernate
同样比较流行的ORM还有MyBatis等,Hibernate相比这些框架有以下优势,因此也是我们选择它作为持久层框架的原因,至于Hibernate和MyBatis的详细对比以及MyBatis的优势可以见这篇文章:数据库持久层框架iBatis、myBatis、Hibernate对比。
- Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。
- Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。
- Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同的SQL。
- Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。
Hibernate与Spring的整合开发
了解完Hibernate后,我将通过在电影售票系统大作业中整合Hibernate来介绍该框架的基本配置与使用,主要的开发环境为:Intellij IDEA + SpringMVC + Spring +Hibernate + MySQL
数据库准备
我们小组使用了MySQL数据库来存储数据,并通过MySQL Workbench这一GUI界面对数据库进行管理。
在开始应用开发之前,我们先设计了基本的数据库表和相应字段,并在表之间设置外键关联关系。
在IDEA中引入Hibernate
使用Maven创建项目,在Project Structure的Modules中添加Hibernate模块并指定配置文件为hibernate.cfg.xml,Maven会自动帮助我们下载相应的依赖包。
编写Hibernate配置文件
引入Hibernate后,IDE在META-INF目录下为我们自动创建了一个Hibernate的配置文件,我们可以在其中配置数据库及缓存相关的属性,比如设置事务的自动提交属性为true,可以简化业务层的代码编写。
除了上述的配置文件,我们还需要其他的配置文件将Spring与Hibernate整合起来,这里我创建了infrastructure.xml。
可以看到,在该xml配置文件中,我们使用了c3p0数据源,c3p0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。同时我们指定了Spring进行组件扫描的基础包,用来扫描所有Entity注解进而实现Spring的自动装配。再结合上节提到的hibernate.cfg.xml,我们就定义好了Hibernate所需要的session工厂bean,通过session工厂就可以获取session并调用Hibernate内置的CRUD方法。
在该配置文件的最上方还引用了hibernate.properties,该配置文件定义了数据库连接的各种属性,比如url、用户名和密码等供infrastructure.xml引用,可以避免硬编码属性。
将数据库表映射成Java实体类
编写完Hibernate所需的配置文件后,就可以成功连接到MySQL数据库。下一步操作就是编写与数据库表相对应的实体类(持久化类),而IDEA强大的一点在于,它可以直接将数据库表映射成Java实体类并加上基本的注解,从而节省大量的时间。
设置Hibernate关联映射
数据库中表与表之间会存在不同的主外键关联关系,而Hibernate作为ORM框架,自然需要将表之间的关系映射成实体类之间的关联映射关系。Hibernate为我们提供了@OneToMany、@ManyToOne、@ManyToMany等多种注解来表示实体类间的关联关系。
以上图中用户和评论之间的双向一对多关联为例,多端作为关联关系的主控端,涉及到两个实体的增删改查操作都应由多端完成,这样可以减少SQL语句的执行,提高数据访问性能。一端作为关联关系的被控端,这种被控使用mappedBy属性来表明,同时一端设置了cascade级联属性表明在对用户进行删除和保存等操作时,该操作也会级联到关联到该用户的所有评论实体,从而保证业务逻辑上的正确性,并且省去了手动保存评论的麻烦。
而在上图的评论实体中,我们可以相对应地看到针对User的@ManyToOne注解,而@JoinColumn注解指定了comment表中使用user_id作为外键关联到user表的user_id属性,从而在关系层次上建立起了两个表之间的联系。
编写BaseDAO
设置完实体类之间的关联映射后,就可以进入Hibernate的业务代码编写阶段。根据MVC的分层架构,我们将整个应用分为显示层(View),控制层(Controller),业务逻辑层(Service)和数据访问层(DAO)
其中Controller层负责处理请求的路由,并调用Service层的业务逻辑方法执行业务,而Service层需要调用DAO层的数据访问方法来实现与底层数据库的交互,每一层之间都面向接口编程,保持了较松的耦合性,同时架构清晰,增强了项目的可维护性和可伸缩性。
在实际编程时,我们通常都是自底向上从DAO层开始编写代码。DAO层封装了与底层数据库交互的方法,而有一些通用的CRUD方法是每一个实体类都需要的,因此这些方法可以被封装成一个基础的BaseDAO泛型接口,每一个实体类各自的DAO接口只需要继承BaseDAO接口的实现即可根据业务需求继续扩展,这也体现了代码复用的思想。
从上图接口中可以发现BaseDAO定义了很多不同的查询方法,这也就引出了Hibernate另一方面的重要知识,即HQL查询和标准查询。
HQL查询
Hibernate 查询语言(HQL)是一种面向对象的查询语言,类似于 SQL,但不是去对表和列进行操作,而是面向对象和它们的属性。 HQL 查询被 Hibernate 翻译为传统的 SQL 查询从而对数据库进行操作。
尽管能直接使用本地 SQL 语句,但是应该尽可能的使用 HQL 语句,以避免数据库关于可移植性的麻烦,并且体现了 Hibernate 的 SQL 生成和缓存策略。
在 HQL 中一些关键字比如 SELECT ,FROM 和 WHERE 等,是不区分大小写的,但是一些属性比如表名和列名是区分大小写的。
以FROM语句为例展示一下HQL的语法。
如果想要在存储中加载一个完整并持久的对象,可以使用 FROM 语句。
String hql = "FROM Employee";
Query query = session.createQuery(hql);
List results = query.list();
在BaseDAO中我编写了一些工具方法来简化HQL查询代码的编写。
标准查询
Hibernate 提供了操纵对象和相应的 RDBMS 表中可用的数据的替代方法。一种方法是标准的 API,它允许开发者建立一个标准的可编程查询对象来应用过滤规则和逻辑条件。
Hibernate Session 接口提供了 createCriteria() 方法,可用于创建一个 Criteria 对象,使应用程序执行一个标准查询时返回一个持久化对象的类的实例。
以下是一个最简单的标准查询的例子,它只是简单地返回对应于员工类的每个对象:
Criteria cr = session.createCriteria(Employee.class);
List results = cr.list();
同时可以使用 Criteria 对象可用的 add() 方法去添加一个标准查询的限制,限制包括等于(eq)、大于(gt)、小于(lt)等等逻辑谓词,我也在BaseDAO中将其写成了工具方法,即根据条件类型来构建Criterion。
同时我也添加了上述使用到的ConditionType和Condition等辅助类来简化标准查询的代码。
利用这些工具类和工具方法,可以大大简化标准查询的代码,如下所示:
/**
* 根据Criterion列表获取集合对象
*
* @param criterions
* @return
*/
@Override
public List<T> find(Criterion... criterions) {
return createCriteria(getCurrentSession(), getEntityClass(), criterions).list();
}
介绍完BaseDAO中的主要方法后,大作业数据访问层的基础功能就已经搭建好了,之后只需根据业务需求扩展BaseDAO,进而构建Service层和Controller层以完成业务逻辑的处理,这些内容就不再赘述。
至此Hibernate就成功和Spring进行了整合,并展示了它的强大功能。
参考
Hibernate 教程
Hibernate之条件查询(Criteria Queries)
用hibernate+注解实现各种关系映射
数据库持久层框架iBatis、myBatis、Hibernate对比
SSH——hibernate 利用注解实现实体关联映射详解
Spring-SpringMVC-Hibernate在IntelliJ与Maven的环境下搭建
Spring框架教程