Hibernate上手指南

如题所述


Hibernate上手
Hibernate,很久以前我就听说过这个名词,但是似乎没什么动力让我去接近它,感觉它是一个很复杂的东西,一直没搞明白它到底是用来做什么的。直到接手了一个项目在技术选型的时候我再一次的看到了Hibernate。我尝试着去使用它,发现它并不是我想像中的那么深奥,它很易用。你并不需要了解它的内部结构,它一样能为你工作的很好,如果你理解了它到底能为你做什么的话
本文着重讲述了为什么要使用Hibernate,此外也简单的介绍了如何使用Hibernate,以及Hibernate中的一些基本概念。我想借这篇文章来向还没有接触过Hibernate的开发者推荐款优秀的开源ORM产品,如果你已经实践过Hibernate,那么我想你没有必要再看下去。
一、Why Hibernate?
现在流行“测试驱动开发”,相似的我觉得“目的驱动学习”是一种比较好的接受新技术,新知识的途径。在学习一样新的技术之前,首先得明确到底有没有必要学习,已有的技术是否已经工作的很好,学习这个新的技术是为了解决什么问题。如果你明确了以上问题,那么寻找并学习新的技术将会事半功倍,并且能快速应用到实际的开发当中来提高效益。
要说Hibernate,就得先介绍一下Object/Relation Mapper(ORM),中文翻译为对象关系映射。之所以会产生这样的概念是源于目前软件开发中的一些不协调的思想。目前流行的编程模型是OOP(Object Oriented Programming),面向对象的编程,而目前流行的数据库模型是Relational Database,这两者思考的方式不一样,这必然产生了开发过程中的不协调。ORM框架(也称为持久层框架,)的出现就是为了解决这样的问题,屏蔽底层数据库的操作,以面向对象的方式提供给开发者操作数据库中数据的接口。目前流行的ORM框架有Apach OJB,Hibernate,iBatis等等,当然最完善,最好用的是Hibernate,至少我这样认为。或许你对“持久层”感到迷惑,其实说白了很简单,把数据放到数据库中叫做持久化(内存种的数据当然不是持久的),那么负责这一操作的结构层面就叫做持久层。你以前应该听说过表现层,业务层,数据层,那么持久层是在业务层和数据层之间的一层,或者说持久层是数据层的一部分。
接下来,我想通过一个实际开发中的例子来说明ORM带给我们的好处。先来讲一下我们的需求,数据库中有三张表,一张student,一张course,另外一张course_slection。其中student用来保存学生信息,course用来表示课程信息,course_selection用来表示学生的选课信息。(表的详细结构这里我就省略了,因为这并不重要)现在要求编写一个程序,用来选出指定学号学生所选的课程名字,那么可能会出现以下几种程序编写的方式:
1. 菜鸟级
代码片段1:
public List selectCourses(String studentId)
{
Connection con = null;
Statement sta = null;
try
{
Class.forName("oracle.jdbc.driver.OracleDriver");
con = DriverManager.getConnection("jdbc:oracle:thin:@10.85.33.199:1521:glee",
"test", "test");
String sql = "select * from course_selection";
String sql2 = "select name from course where id='";
sta = con.createStatement();
ResultSet rs = sta.executeQuery(sql);
List list = new LinkedList();
while (rs.next())
{
ResultSet rs2 = sta.executeQuery(sql2 +
rs.getString("course_id") + "'");
if (rs2.next())
{
list.add(rs2.getString("name"));
}
}
return list;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
这段程序你一定看的很晕吧,什么乱七八糟的都搞在一起,那么接下来看一段改进过的程序。
2. 改进后的代码
代码片段2:
class DBHelper
{
public static Connection getConnection()
{
try
{
Class.forName(Constants.DB_DRIVER);
return DriverManager.getConnection(Constants.DB_URL,
Constants.DB_USER, Constants.DB_PWD);
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
}
public List selectCourses(String studentId)
{
Connection con = null;
Statement sta = null;
try
{
con = DBHelper.getConnection();
String sql = "select * from course_selection";
String sql2 = "select name from course where id='";
sta = con.createStatement();
ResultSet rs = sta.executeQuery(sql);
List list = new LinkedList();
while (rs.next())
{
ResultSet rs2 = sta.executeQuery(sql2 + rs.getString("course_id") + "'");
if (rs2.next())
{
list.add(rs2.getString("name"));
}
}
return list;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
这段代码的形式是一种被广泛采用的形式,相对第一段代码来说,应该已经有所进步,分离了数据库连接操作,并把数据库连接信息交给单独的类完成(一般放在配置文件里面),往往在开发中还会引入数据库连接池(Connection Pool)来提高性能,我这里都尽量简化了。但这些并不能从根本上改善程序的结构,在业务代码中仍然混杂了很多数据库操作,结构不清晰。下面来看一段彻底分离数据库操作的代码:
3. DAO模式
代码片段3:
public List selectCourses(String studentId)
{
StudentDAO sd = new StudentDAO();
Student student = sd.findById(studentId);
Set set = student.getCourseSelections();
List courseNames = new LinkedList();
for (Iterator iter = set.iterator(); iter.hasNext();)
{
CourseSelection element = (CourseSelection) iter.next();
courseNames.add(element.getCourse()。getName());
}
return courseNames;
}
是不是感觉代码少了很多?或许你对这段代码有点迷惑,没关系,后文会详细解释。我想先解释一下DAO。其实DAO和Hibernate没有必然联系,只不过一般用Hibernate的程序都用DAO模式。DAO的全称是Data Access Object,程序要访问数据库中的数据(包括获取,更新,删除)都通过DAO来访问,实际上DAO才是真正屏蔽了所有数据库操作的东西,这样在业务代码中就可以完全隔离数据层的代码。如果我告诉你,在真正用Hibernate开发的时候,要完成上文提到的功能,需要手写的代码就是“代码片段3”这么多,甚至更少,你是不是有很大的动力去学习Hibernate?那么好吧,让我们开始Hibernate之旅。
二、持久层的组成
这一节的名字应该换成“基于Hibernate的持久层的组成”更合适一点,可是它太长了。既然Hibernate是用来开发持久层,那么我先介绍一下这个持久层中的各个元素。
1. POJO:Plain Old Java Object,你可以把它看作是简单的JavaBean。一般说来,一张数据库表对应一个POJO,也就是对象/关系的一一映射。
2. DAO:对于每一个POJO,一般都有一个DAO与之对应,承担所有关于该POJO的访问控制。实际上也就是控制了对数据库中一张表的访问控制。
3. *.hbm.xml文件:这个文件定义了POJO和数据库中的表是如何映射的,比如POJO中的字段对应数据库表中的哪个字段等等。一般每个映射都用单独的文件来描述,也就是有一个POJO就有一个*.hbm.xml文件。
4. *.cfg.xml文件:这个文件定义了Hibernate的基本信息,比如数据库驱动,用户名,密码等等连接信息,也包括了所有要用的*.hbm.xml文件,在初始化的时候,Hibernate会读取这个文件来找相应的映射文件完成对象/关系。
我们还是以上文的例子来详细描述一下这里提到的各个元素的内容。
1. Student.java:
代码片段4:
public class Student implements java.io.Serializable
{
private String id;
private String name;
private Set courseSelections = new HashSet(0);
public Student()
{
}
public String getId()
{
return this.id;
}
public void setId(String id)
{
this.id = id;
}
public String getName()
{
return this.name;
}
public void setName(String name)
{
this.name = name;
}
public Set getCourseSelections()
{
return this.courseSelections;
}
public void setCourseSelections(Set courseSelections)
{
this.courseSelections = courseSelections;
}
}
#p#副标题#e#
这个类就是一个POJO,你可以很明显的看出来它就是一个JavaBean。我想解释它的courseSelection字段。很显然,在数据库表student中,没有这个字段。这里的这个字段是因为一个外键引用,course_selection的student_id是一个外键,引用了student表中的id字段。那么在Student类中courseSelection来记录这样的外键关系,也就是说,当我们获取了Student对象以后,就可以直接获取他的选课记录,这样就为上层的调用提供了很大的方便。这里有点模糊没关系,我在介绍映射定义文件(*.hbm.xml)的时候还会提到这个问题。
2. StudentDAO.java
代码片段5:
public class StudentDAO
{
Session session;
public StudentDAO()
{
Configuration cfg = new Configuration();
cfg.configure("/hibernate.cfg.xml");
SessionFactory sessionFactory = cfg.buildSessionFactory();
session = sessionFactory.openSession();
}
public void save(Student transientInstance)
{
session.save(transientInstance);
}
public void delete(Student persistentInstance)
{
session.delete(persistentInstance);
}
public Student findById(java.lang.String id)
{
List list = session.createCriteria(Student.class)。add(Expression.eq("id", id))。list();
if (list.size()
0)
{
return (Student)list.get(0);
}
return null;
}
}
这里的构造函数是用来启动Hibernate,并获取session。打开一个session就相当于打开了一个数据库连接,然后我们就可以对这个session进行操作,完成数据库操作,完全不用写SQL语句。我这里Hibernate的启动方式写的很不规范,系统应该只需要完成一次Hibernate启动就可以在不同的DAO中使用,我把它写在构造函数里面纯粹是为了简化演示代码。
你可以看到save和delete方法都很简单直接对对象操作,而findById就有些麻烦,因为这里有一个查询过程在里面。Hibernate里面查询可以用Criteria这个类来完成,我们也常用Hibernate独有的HQL(Hibernate Query Language)来完成查询。当然Hibernate也是支持原生SQL的。关于查询的详细信息请参考其他文章或书籍,我只是演示一个流程,介绍一些概念。
3. Student.hbm.xml
代码片段6:
hibernate-mapping
class name="Student" table="STUDENT"
id name="id" type="string"
column name="ID" length="10" /
generator class="assigned" /
/id
property name="name" type="string"
column name="NAME" not-null="true" /
/property
set name="courseSelections" inverse="true"
key
column name="STUDENT_ID" length="10"
not-null="true" /
/key
one-to-many class="CourseSelection" /
/set
/class
/hibernate-mapping
这个文件定义了Student类和Student表是如何映射的。class元素定义了Sudent类和STUDENT表映射,然后就定义了各个属性是如何映射的。如果一个属性是数据库的key,那么会用id标签来定义,column定义了当前类的属性和数据库中的哪个字段对应,generator是id特有的。一般来说id是自增的,由于我的数据库是用的Oracle,它没有自增字段,要实现自增必须用Sequence,这超出了本文的范围,所以我就用assigned来简化示例代码。assigned表示id是用户给定的。
有一个比较特别的标签是set,它对应着数据库中的外键关系,上文我提到的通过Student对象可以获得所有相关的选课记录就是通过这里的定义实现的。name属性对应了Student类中的字段名,key表示哪个字段是外键,one-to-many表示Student和CourseSelection是一对多关系,这和事实相符。类似的还有many-to-one,many-to-many,不过这些都不常用,我不介绍了。Hibernate根据这个映射定义文件,在实例化一个POJO(比如Student)的时候,会自动的把定义过映射的属性用数据库中的数据填充,set也包括在内。
4. hibernate.cfg.xml
代码片段7:
hibernate-configuration
session-factory
property name="connection.username"test/property
property name="connection.url"
jdbc:oracle:thin:@10.85.33.199:1521:glee/property
property name="dialect"
org.hibernate.dialect.Oracle9Dialect/property
property name="connection.password"test/property
property name="connection.driver_class"
oracle.jdbc.OracleDriver/property
mapping resource="Student.hbm.xml"/mapping
mapping resource="CourseSelection.hbm.xml"/mapping
mapping resource="Course.hbm.xml"/mapping
/session-factory
/hibernate-configuration
这个文件我不解释了,自己看吧。结合上文StudentDAO的例子,我想你应该能看明白。
看了这么多,或许你会有点头皮发麻,POJO,DAO,配置文件好像要写的东西还是很多。值得庆幸的是现在Hibernate已经发展的比较成熟了,有很多工具来帮助我们完成这些工作,比如MiddleGen,Hibernate Synchronizer等等。我使用的开发工具是Eclipse+MyEclipse,我所要做的只是把数据库表建好,然后MyEclipse提供的工具会自动根据数据库表生成POJO,DAO,*.hbm.xml,甚至hibernate.cfg.xml都是自动完成的(前提是MyEclipse知道你的数据库连接信息)。我并不打算介绍如何用IDE来开发Hibernate,你可以参考IDE的帮助文档。
到这里为止,使用Hibernate进行开发的基本组成元素我都介绍好了,强烈建议你马上实践一遍,即使有些不理解,也先依葫芦画瓢一个。对了,别忘了把Hibernate的包down下来放到classpath里面。
三、Session与SessionFactory
Session可以说是Hibernate的核心,Hibernate对外暴露的接口就是Session。所以我这里讲一下有关Session的常用函数和特性。
在讲Session之前,我想先提一下SessionFactory,这个东西不复杂,只要配置好就行了。顾名思义,SessionFactory就是用来创建Session的。SessionFactory是线程安全的,也就是说对于同一个数据库的所有操作共享一个SessionFactory就行了。回头看代码片段5,我们可以看到SessionFactory的常用配置方式。
代码片段8:
Configuration cfg = new Configuration();
cfg.configure("/hibernate.cfg.xml");
SessionFactory sessionFactory = cfg.buildSessionFactory();
我们通过Configuration来读取配置文件,然后就可以创建SessionFactory,这段代码在 所有系统中都大同小异,一般就是xml配置文件的名字不一样,所以也没什么好说的。
当我们有了SessionFactory以后就可以获取Session了。调用SessionFactory.openSession()就会返回一个Session实例,然后我们操作这个Session来访问数据库。值得一提的是Session并不是线程安全的,也就是每一个线程都必须有自己的Session。所以我们一般通过以下方法来获取和关闭Session:
代码片段9:
public static Session currentSession() throws HibernateException
{
Session session = (Session) threadLocal.get();
if (session == null || !session.isOpen())
{
if (sessionFactory == null)
{
try
{
cfg.configure(CONFIG_FILE_LOCATION);
sessionFactory = cfg.buildSessionFactory();
}
catch (Exception e)
{
e.printStackTrace();
}
}
session = (sessionFactory != null) ?
sessionFactory.openSession(): null;
threadLocal.set(session);
}
return session;
}
public static void closeSession() throws HibernateException
{
温馨提示:答案为网友推荐,仅供参考
相似回答