资源描述
基于Hibernate框架的
数据持久层的研究及其应用
软件工程
学 院:
专 业:
指导教师:
2014年9月
基于Hibernate框架的数据持久层的研究及其应用
摘 要
在开发J2EE项目的过程中,数据持久层的设计往往是一个关键的问题。众所周知,对象只能存储在内存中,但内存不能永久保存数据,如果要永久保存对象的状态,需要进行对象的持久化,即把对象存储到专门的数据存储库中,这就需要进行对象-关系的映射(Object/Relation Mapping,简称ORM)。
Hibernate是一个开放源代码的对象关系ORM映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。它利用反射的机制实现持久化一个对象的各种操作,保证了源码的简练和完全的面向对象风格,将持久层中的数据源管理和数据操作进行有效的分离,提高了系统的稳定性,改善了系统的性能。
本课题探讨了目前流行的Hibernate映射框架,结合图书管理系统的开发,提出了基于Hibernate的数据持久层解决方案,并给出具体实现。根据系统实施效果,分析了该方案的优缺点并总结了一些开发方面的建议。
关键词:数据持久层,ORM,Hibernate,图书管理系统
中北大学2014届毕业生优秀毕业设计(论文)摘要
Researching Data Persistence Layer
And Its Application Based On Hibernate
Abstract
During J2EE project development process, data persistence layer design is often a key issue. As we all know, the object only in memory, while memory can not store data permanently, if we want to save the state of an object permanently, the object needs to be persistent, that is stored in an object into specialized data repository, which requires an object - mapping (Object / Relation Mapping, referred to as the ORM).
Hibernate is a powerful tool for open source ORM, it has carried on the lightweight object to the JDBC encapsulation, using of reflection mechanism to achieve a variety of an object persistence operations at runtime, which ensure that code is concise and object-oriented completely, data management and its operation on the persistence layer can separate effectively, Using hibernate can improve the stability and performance of the system.
This paper researches Hibernate mapping framework, combined with the development of books management system, the paper is proposed an solution of data persistence based on Hibernate, and gives concrete realization steps. According to the result of system implementation, I was analyzed the advantages and disadvantages of the Hibernate and summarized some development suggestions.
Keywords: data persistence layer, ORM, Hibernate, Books management system
2
目 录
1 前言 1
2 Java应用分层框架 3
2.1 应用程序的分层体系结构 3
2.2 Java应用的持久化层 3
2.3 软件的模型 4
2.3.1 域对象的分类 5
2.3.2 域对象之间的关系 6
3 Java对象持久化技术概述 8
3.1 Java对象持久化概念 8
3.2 直接通过JDBC API来持久化对象 9
3.3 ORM简介 13
3.3.1 对象-关系映射的概念 15
3.3.2 ORM中间件的使用 17
3.4 对象的其他持久化模式 17
3.4.1 主动域对象模式 18
3.4.2 JDO模式 20
3.4.3 CMP模式 20
3.5 Hibernate 持久化框架简介 21
3.5.1 Hibernate简介 21
3.5.2 Hibernate原理 22
3.5.3 Hibernate接口 23
4 Hibernate在图书管理系统中的应用 25
4.1 系统需求分析 25
4.1.1 设计的目的及意义 25
4.1.2 系统功能分析 25
4.1.3 系统数据流图 26
4.1.4 数据字典 28
4.1.5 设计平台 30
4.1.6 数据库表间关系设计 31
4.2 系统概念和逻辑结构设计 31
4.3 系统详细设计 33
4.3.1 Hibernate实现系统中数据持久化操作 33
4.3.2 系统各功能详细设计实现 39
5 结论 46
参 考 文 献 47
致 谢 48
第II页 共II页
1 前言
在现今的企业级应用开发环境中,面向对象开发已成为主流。众所周知,对象只能存在于内存当中,但内存却不能永远保存数据。如果要永久保存对象的状态,需要对对象进行持久化,即把对象储存到专门的数据存储库中。目前关系数据库仍然是使用最广泛的数据存储库。关系数据库中存放的是关系数据,它是非面向对象的。
对象和关系数据库其实是业务实体的两种表现形式。业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在着关联和继承的关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,把对象持久化到关系数据库中,需要进行对象-关系的映射(Object/Relation Mapping,简称ORM),这是一项烦琐耗时的工作。
在实际应用中,除了要把内存中的对象持久化到数据库之外,还要把数据库中的关系数据再重新加载到内存中,以满足用户查询业务数据的需要。频繁地访问数据库会对应用的性能造成很大影响。为了降低访问数据库的频率,可以把需要经常被访问的业务数据存放在缓存中,并且通过特定的机制来保证缓存中的数据与数据库中的数据同步。
在Java领域,我们可以直接通过JDBC编程来访问数据库。JDBC可以说是访问关系数据库最原始、最直接的方法。这种方式有运行效率高的优点,缺点是在Java程序代码中嵌入大量的SQL语句,使得项目难以维护。在开发企业级应用时,可以通过JDBC编程来开发单独的持久化层,把数据库访问操作封装起来,提供简洁的API,供业务层统一调用。但是,如果关系数据模型非常复杂,那么直接通过JDBC编程来实现持久化层就需要有专业的知识。对于企业应用的开发人员来说,花费大量时间从头开发自己的持久化层不是很可行的。
幸运地是,目前在持久化层中已经有好多种现代的持久化中间件可供选用,有些是商业性的,如TopLink;有些是非商业性的,如JDO和Hibernate。 Hibernate是一个基于Java的开放源代码的持久化中间件,它对JDBC做了轻量级封装,不仅提供ORM映射服务,还提供数据查询和数据缓存功能,Java开发人员可以方便地通过Hibernate API来操纵数据库。
现在,越来越多的Java开发人员把Hibernate作为企业应用和关系数据库之间的中间件,以节省和对象持久化有关的30%的JDBC编程工作量。2005年Hibernate作为优秀的类库和组件,荣获了第15届Jolt大奖。Hibernate之所以能够流行,归功于它的以下优势。
l 它是开放源代码的,允许开发人员在需要的时候研究源代码,改写源代码,定制客户化功能。
l 具有详细的参考文档。
l 对JDBC仅做了轻量级封装,若有必要的话,用户还可以绕过Hibernate, 直接访问JDBC API。
l 具有可扩展性。
l 使用方便,容易上手。
l Hibernate既适用于独立的Java程序,也适用于Java Web应用,而且还可在J2EE构架中取代CMP ( Container-managered Persistence, 由容器管理持久化),完成对象持久化的重任,Hibernate能集成到会话EJB和基于BMP的实体EJB中,BMP(Bean-managered Persistence) 是由实体EJB本身管理持久化。
l Hibernate可以和多种Web服务器、应用服务器良好集成,并且支持几乎所有流行的数据库服务器。
2 Java应用分层框架
2.1 应用程序的分层体系结构
纵观40多年来计算机应用软件的演变过程,可以看出,应用程序逐渐由单层体系结构发展为多层系统结构。最初的应用软件只是在大型机上的单层应用程序,许多程序采用文件系统来存储数据。20世纪70年代数据库得到普及,20世纪80年代PC和局域网的出现使数据库技术飞速发展,原来的单层应用发展为双层应用。在双层应用中,数据库层存放持久性业务数据,应用程序作为单独的一层,在这个层中负责生成用户界面的代码和负责业务逻辑的代码混合在一起,这使得程序结构不清晰,而且维护很困难,对于大型复杂的应用软件,这一问题显得尤为突出。在这种情况下,三层结构应运而生,通常意义上的三层架构就是将整个业务应用划分为表示层、业务逻辑层和数据访问层,三层结构是目前典型的一种应用软件的结构。
l 表示层(UI):主要对用户的请求接受,以及数据的返回,为客户端提供应用程序的访问。位于最外层(最上层),最接近用户。用于显示数据和接收用户输入的数据,为用户提供一种交互式操作的界面。
l 业务逻辑层(BBL):主要是针对具体的问题的操作,也可以理解成对数据层的操作,对数据业务逻辑处理,如果说数据层是积木,那逻辑层就是对这些积木的搭建。
l 数据访问层(DAL):主要是对原始数据(数据库或者文本文件等存放数据的形式)的操作层,而不是指原始数据,也就是说,是对数据的操作,而不是数据库,具体为业务逻辑层或表示层提供数据服务。
2.2 Java应用的持久化层
在三层软件结构中,业务逻辑层起到了数据交换中承上启下的作用,不仅负责业务逻辑关系,而且直接访问数据库,提供对业务数据的增删改查操作。为了把数据访问细节和业务逻辑分开,同时解决对象/关系之间的不匹配,可以把数据持久层单独作为J2EE体系的一个层提出来,重新分层的软件结构参见图2.1
表现层
业务逻辑层
持久化层
表现层
业务逻辑层
数据访问层
数据访问层
图2.1从业务逻辑层分离出持久化层
持久化层既封装了数据访问细节,也为业务逻辑层提供了面向对象的API。在设计中使用持久层能够为项目的开发带来以下好处:
l 把数据持久逻辑与业务逻辑分开,降低了它们之间的耦合。
l 通过对象-关系映射向业务逻辑提供了面向对象的数据访问。
l 抽象数据库中存储数据的物理细节和数据库表、视图之间的关系。
l 简化开发过程,隐藏打开的数据库连接、发出数据读取与操纵命令和事物管理细节。
l 优化数据的访问操作。
l 系统的扩展性很强,可以比较从容地面对变更。
2.3 软件的模型
在软件开发领域,模型用来表示真实世界的实体。在软件开发的不同阶段,需要为目标系统创建不同类型的模型。在分析阶段,需要创建概念模型,在设计阶段要创建域模型和数据模型。如图2.2显示了这几个模型之间的关系。
软件分析阶段
概念模型
域模型
(面向对象)
数据模型
(面向关系)
关系-对象映射
软件设计阶段
图2.2 3种模型之间的关系
(1)概念模型:在软件分析阶段创建的用来模拟问题域中的真实实体,描述了每个实体的概念和属性,以及实体之间的关系。如图2.3描述了图书管理系统的概念模型。
t_book
name
zuozhe
…….
t_user
name
jiehao
…….
t_catelog
name
jieshao
…….
t_admin
name
password
.
t_jieyue
user_id
book_id
jieshuriqi
……
……
.
图2.3 图书管理系统的概念模型
(2)关系数据模型:用于描述关系数据的静态结构,由以下内容组成:
l 一个或多个表
l 表的所有索引
l 视图
l 触发器
l 表和表之间的参照完整性
(3)域模型:在软件设计阶段需要在概念模型的基础上创建域模型,它是面向对象的,在面向对象术语中,域模型也可称为设计模型。
构成域模型的基木元素就是域对象。即Domain Object,是对真实世界的实体的软件抽象。域对象还可叫做业务对象,即Business Object。
2.3.1 域对象的分类
域对象可以代表业务领域中的人、地点、事物、概念及业务,可分为实体域对象、过程域对象、事件域对象。
l 实体域对象:可以把业务领域中的名词如用户、书籍等作为实体域对象。在J2EE应用中,这些名词可以作为实体EJB,在非J2EE应用中,这些名词可以作为包含状态和行为的JavaBean。采用JavaBean形式的实体域对象也称为POJO
l 过程域对象:代表应用中的业务逻辑或流程,可以把业务领域中的动词如读者借书、管理员登录系统等作为过程域对象,在J2EE应用中,它们通常作为会话EJB。
l 事件域对象:代表应用中的一件事件,如异常、警告等。
在三层应用结构中,以上3种域对象都位于业务逻辑层,实体域对象时应用的业务数据在内存中的表现形式,而过程域对象用于执行业务逻辑。
2.3.2 域对象之间的关系
在域模型中,类之间存在着4种关系:
(1)关联(Association):指类与类之间的引用关系,可分为一对一、一对多、多对多关联。根据上图1.3可出看出图书管理系统中的实体之间存在以下关系。
l User(读者)和book实体:一对多。
l Catelog(类别)和book实体:多对多。
l Admin(管理员)和book、catelog、user:1对多。本系统设计中考虑到在同一时间只有一个管理员在操作,所以定义admin实体和其他实体的关联为一对多。
l jieyue和book实体:一对一。一本书只对应一天借阅信息。
l user和jieyue实体:一对多。一个读者可以有多条借阅信息。
关联是有方向的,分为单向关联和双向关联
l 单向关联:仅建立从admin到book的一对多关联,即仅在Admin类中定义book属性,在本系统中,admin实体和其他实体之间仅建立从admin到其他实体之间的一对多关联。
l 双向关联:既建立从book到catelog的多对一关联,又建立从catelog到book的一对多关联。
(2)依赖(Dependency):依赖指的是类之间的访问关系。如果类A访问类B的属性或方法,或者类A负责实例化类B,那么可以说类B依赖于类A。如业务逻辑层的类依赖持久化层的类或接口。
(3)聚集(Aggregation):是整体与部分之间的关系,如Person类中有个hands集合,它存放被聚集的Hand对象。
(4)一般化(Generalization):指类之间的继承关系。例如,HourlyEmployee(按小时拿工资的雇员)和SalariedEmployee类(按月拿薪水的雇员)都继承Employee类。
3 Java对象持久化技术概述
3.1 Java对象持久化概念
对比前面介绍的域模型和关系数据模型,可以看出业务数据有两种表现形式:
l 在内存中表现为实体域对象,以及实体域对象之间的各种关系。
l 在关系数据库中表现为表,以及表与表之间的参照关系。
当Java程序在内存中创建了实体域对象后,它们不可能永远存在。最后,它们要么从内存中清除,要么被持久化到数据存储设备中。内存无法永久地保存数据,因此必须对对象进行持久化。否则,如果对象没有被持久化,那么用户在应用程序运行时创建的信息将在应用程序结束运行后随之消失。图书管理系统中的书籍、图书类别和读者信息都应该被持久化。一旦对象被持久化,它们可以在应用程序再次运行时被重新读入到内存,并重新构造出域对象。图3.1显示了对象的持久化过程。
业务数据分别在内存和数据库中的不同表现形式
数据库
持久化
重新加载到内存
关联
Book对象
User对象
内存
t_book表
t_user表
参照
图3.1域对象的持久化
狭义的理解,“持久化”仅指把域对象永久保存到数据库中,广义的理解,“持久化”包括和数据库相关的各种操作。
l 保存:把域对象永久保存到数据库中。
l 更新:更新数据库中域对象的状态。
l 删除:从数据库中删除一个域对象。
l 加载:根据特定的对象ID,把一个域对象从数据库加载到内存中。
l 查询:根据特定的查询条件,把符合查询条件的一个或多个域对象从数据库加载到内存中。
3.2 直接通过JDBC API来持久化对象
DriverManager:驱动程序管
理器,负责创建数据库连接
Connection:代表数据库连接
ResultSet:代表SQL查询语句的查询结果集
Statement:
负责执行SQL语句
PreparedStatement:
负责执行SQL语句
对象的持久化最终必须通过数据访问代码来实现。Java应用中访问数据库最直接的方式就是通过JDBC API来访问,JDBC是Java Database Connectivity的缩写。Java.sql包提供了JDBC API,程序员可通过它编写访问数据库的程序代码。在java.sql包中常用的接口和类如图2.2所示。
图3.2 java.sql包中主要的类框图
下面我们以saveBook()方法为例,持久化book对象:
public class BookService {
String driver ="oracle.jdbc.driver.OracleDriver";
static String url = "jdbc:oracle:thin:@localhost:1521:XE";
static String userName = "briup";
static String passwd = "briup";
public BookService() {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
Connection conn = null;
try {
conn = DriverManager.getConnection(url,userName,passwd);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
public static void saveBook(TBook book) throws Exception{
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();//获取数据库连接对象
conn.setAutoCommit(false);//开始一个数据库事务
//下面的业务逻辑判断图书ISBN是否存在,如果存在则抛出异常
String isbm = book.getIsbm();
if(isbm!=null){
throw new BookServiceException("录入书籍的ISBN错误");
}
//持久化Book对象,
String sql = "insert into t_book values(SEQ_COMMON.nextval,?,?,?,?,?,?,?,?,?,?)";
pstmt=conn.prepareStatement(sql);
pstmt.setInt(1, book.getCatelog_id());
pstmt.setString(2,book.getName());
pstmt.setString(3,book.getZuozhe());
pstmt.setString(4,book.getChubanshe());
pstmt.setString(5,book.getChubanriqi());
pstmt.setString(6,book.getIsbm());
pstmt.setString(7,book.getPrice());
pstmt.setString(8,book.getYeshu());
pstmt.setString(9,book.getKucun());
pstmt.setString(10,book.getDel());
pstmt.execute();
String sql1="select catalog_id, name,zuozhe,chubanshe,price from t_book";
rs = pstmt.executeQuery(sql1);
while(rs.next()){
int catelog_id = rs.getInt("catelog_id");
String name = rs.getString("name");
String zuozhe = rs.getString("zuozhe");
String chubanshe = rs.getString("chubanshe");
String price = rs.getString("price");
System.out.println("catelog_id"+catelog_id+" name="+name+" zuozhe="+zuozhe+" chubanshe"+chubanshe+" price"+price);
}
}catch(Exception e){
e.printStackTrace();
}finally{
//6、释放资源先构建的后释放
try {
if(rs!=null)rs.close();
if(pstmt !=null)pstmt.close();
if(conn!=null)conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
BookService service = new BookService();
TBook book = new TBook("晓说", "高晓松", "清华大学出版", "2014-02-10", "02010", "35", "345", "20", 2, "no");
saveBook(book);
}
}
从以上代码中可以看出,在业务方法中直接嵌入大量SQL语句,SQL语句是面向关系的,并依赖于关系数据模型。这给应用程序带来以下缺点:
l 实现业务逻辑的代码和数据库访问代码掺杂在一起,使程序结构不清晰,造成可读性差的结果。
l 在程序代码中嵌入面向关系的SQL语句,使开发人员不能完全运用面向对象的思维来编写程序。
l 业务逻辑被迫和关系数据模型绑定,如果关系数据模型发生变化,例如修改了t_book表的结构,那么就必须手工修改程序代码中所有涉及t_book表的相关SQL语句,这增加了维护软件的难度。
l 如果程序代码中的SQL语句中包含语法错误,在编译时不能检查这种错误,只有在运行时才能发现这种错误,这给程序员调试程序增加了难度。
为了使程序中的业务逻辑和数据访问细节分离,在Java领域已经出现了好几种解决数据持久化的现成的模式:
l ORM模式
l JDO模式
l CMP模式
3.3 ORM简介
对象-关系映射(ORM,即Object-Relation Mapping)模式指的是在单个组件中负责所有实体域对象的持久化,封装数据访问细节。在2.2节介绍了把数据访问细从业务逻辑层分离,把它单独划分到持久化层的设计思想。那么,到底如何来实现持久化层呢?一种简单的方案是采用硬编码的方式,为每一种可能的数据库访问操作提供单独的方法。持久化层向业务逻辑层提供的API类似于以下
形式:
public interface IBookService {
// 书籍模块
TBook findTBookById(int id) throws BookServiceException;
TBook findBookByIsbm(String isbm) throws BookServiceException;
public List<TBook> listBook() throws BookServiceException;
void saveBook(TBook book) throws BookServiceException;
void saveOrupdateBook(TBook book) throws BookServiceException;
void deleleBook(int id) throws BookServiceException;
public List<TBook> getBookByCatelogId(int catelog_id) throws BookServiceException;
……
}
业务逻辑层的BookServiceImp类bookAdd ()方法不必直接访问JDBC,只需要session的saveBook()方法保存Book对象:
public String bookAdd() {
try {
TBook boo = service.findBookByIsbm(isbm);
if(boo!=null){
throw new BookServiceException("本书已存在");
}
TBook book = new TBook();
book.setName(name);
book.setZuozhe(zuozhe);
book.setChubanshe(chubanshe);
book.setChubanriqi(chubanriqi);
book.setIsbm(isbm);
book.setPrice(price);
book.setKucun(kucun);
book.setCatelog_id(catelog_id);
service.saveBook(book);
req.setAttribute("message", "操作成功");
return "addsuccessly";
} catch (BookServiceException e) {
e.printStackTrace();
req.setAttribute("message", e.getMessage());
return "error";
}
}
尽管以上方案是可行的,但存在以下不足:
l 持久化层产生大量冗余代码。如findBookById()、findBookByIsbm()方法,它们的程序代码都很相似,仅仅是生成的SQL select语句中的查询条件不一样。
l 持久化层缺乏弹性。一旦出现业务需求的变更,如新增加了按照书名检索图书的要求,就必须修改持久化层的接口,增加findBookByName()方法。
l 持久化层同时与关系数据模型绑定。如果关系数据模型发生变化,就得修改持久化层的相关程序代码,增加了软件维护的难度。
对于以上第一条缺陷,一种看似可行的改进措施如图3.3
public List findBook(String sqlstr);
public List findBookByName(String name)
public List findBookById(int id)
public List findBookByIsbm (String isbm)
改进
图3.3 把3个selectCustomerByXXX()方法合并为一个方法
以上措施在持久化层减少了一些重复代码,只需一个findBook()方法,就能完成原来的3个方法的任务。在findBook()方法中不必组装SQL语句,只需直接使用参数sqlstr提供的SQL语句即可。但这又带来了一个新的问题,那就是findBook()方法是供业务逻辑层调用的,因此业务逻辑层必须负责生成SQL语句,所以业务逻辑层还必须了解关系数据库的结构,这就使得业务逻辑层仍然和数据访问细节及关系数据模型纠缠在一起。
由此可见,对于复杂的关系数据模型,直接通过JDBC编程来实现健壮的持久化层需要有高超的开发技巧,而且编程量很大。
ORM提供了实现持久化层的另一种模式,它采用映射源数据(Mapping Meta Data)来描述对象一关系的映射细节,使得ORM中间件能在任何一个Java应用的业务逻辑层和数据库层之间充当桥梁,参见图3.4。
域模型
(对象、属性、关联、继承和多态)
关系数据模型
(表、字段、索引、主键和外键)
ORM API
ORM实现
业务逻辑层
持久化层
数据库层
对象-关系映射文件(XML)
参考
图3.4 ORM充当业务逻辑层和数据层之间的桥梁
3.3.1 对象-关系映射的概念
ORM解决的主要问题就是对象-关系的映射、域模型和关系数据模型分别建立在概念模型的基础上,域模型是面向对象的,而关系数据模型是面向关系的。一般情况下,一个持久化类和一张表对应,类的每个实例对应表中的一条记录。但是域模型和关系数据模型之间存在许多不匹配之处,如User类有两个Address类型的属性:homeAddress(家庭地址)属性和schooAddress(学校地址)。Address类代表地址,它包含province、city、street属性。User类与Address类之间为聚集关系。而在数据库中只有t_user一张表,它的home_province和home_city等字段表示家庭地址,而school_province和school_city等表示学校地址。如图3.5 所示,
关系数据模型
域模型
t_user表
NAME
……
HOME_PROVINCE
HOME_CITY
HOME_STREET
SCHOOL_PROVINCE
SCHOOL_CITY
SCHOOL_STREET
Address类
province
city
street
User类
name
……
homeAddress
comAddress
图3.5 域模型中类的数目比关系数据模型中表的数目多
此外,域模型中类之间的多对多关联关系和继承关系都不能够直接在关系数据库模型中找到对应的等价物。在关系数据模型中,表之间只存在外键参照关系,有点类似于域模型中多对一或一对一的单向关联关系。因此,ORM中间件需要采用各种映射方案,来建立两种模型直接的映射关系。例如,当ORM中间件保存一个User对象时,它必须把User对象及被聚集的Address对象映射为t_user表中的关系数据,需要执行类似如下代码:
String sql = "insert into t_user(id,name,sex,age,tel,email,jiehao,del," +
"home_province,home_city,home_street,school_provi
展开阅读全文