[Hibernate开发之路](1)Hibernate配置
最后更新于:2022-04-01 09:59:07
###一. 准备工作
首先我们将创建一个简单的基于控制台的(console-based)Hibernate应用程序。
我们所做的第一件事就是创建我们的开发目录,并且把所有需要用到的Java库文件放进去。解压缩从Hibernate网站下载的Hibernate发布包,并把所有需要的库文件拷到我们项目中去。
学习建User-library-hibernate,并加入相应的jar包
(a)项目右键-buildpath-configure build path-add library
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-18_56eba2a467505.jpg)
(b)选择User-library,在其中新建 hibernate,命名为HibernateLibraray
(c)在该library中加入hibernate所需jar包
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-18_56eba2a487a40.jpg)
到编写本文时为止,这些是Hibernate运行所需要的最小库文件集合(注意我们也拷贝了 Hibernate3.jar,这个是最主要的文件)。
你正使用的Hibernate版本可能需要比这更多或少一些的库文件。请参见发布包中的lib/目录下的README.txt,以获取更多关于所需和可选的第三方库文件信息(事实上,Log4j并不是必须的库文件,但被许多开发者所喜欢)。
(d) 引入sqlserver的JDBC驱动包
在sqlserver中创建表StudentInfo
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-18_56eba2a4988b9.jpg)
接下来我们创建一个类,用来代表那些我们希望储存在数据库里的student。
### 二 .持久化类
我们的第一个持久化类是一个带有一些属性(property)的简单JavaBean类:
~~~
package com.model;
public class StudentInfo {
private int id;
private String name;
private int age;
private String sex;
//构造方法
public StudentInfo(){
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
~~~
你可以看到这个类对属性的存取方法(getter and setter method)使用了标准JavaBean命名约定,同时把类属性(field)的访问级别设成私有的(private)。
这是推荐的设计,但并不是必须的。Hibernate也可以直接访问这些field,而使用访问方法(accessor method)的好处是提供了重构时的健壮性(robustness)。
为了通过反射机制(Reflection)来实例化这个类的对象,我们需要提供一个无参的构造器(no-argument constructor)。
对一特定的StudentInfo, id 属性持有唯一的标识符(identifier)的值。如果我们希望使用Hibernate提供的所有特性, 那么所有的持久化实体(persistent entity)类(这里也包括一些次要依赖类)都需要一个这样的标识符属性。
而事实上,大多数应用程序(特别是web应用程序)都需要通过标识符来区别对象,所以你应该考虑使用标识符属性而不是把它当作一种限制。
然而,我们通常不会操作对象的标识(identity),因此它的setter方法的访问级别应该声明private。这样当对象被保存的时候,只有Hibernate可以为它分配标识符值。
你可看到Hibernate可以直接访问public,private和protected的访问方法和field。所以选择哪种方式完全取决于你,你可以使你的选择与你的应用程序设计相吻合。
所有的持久化类(persistent classes)都要求有无参的构造器,因为Hibernate必须使用Java反射机制来为你创建对象。
构造器(constructor)的访问级别可以是private,然而当生成运行时代理(runtime proxy)的时候则要求使用至少是package 级别的访问控制, 这样在没有字节码指令(bytecode instrumentation)的情况下,从持久化类里获取数据会更有效率。
下一步,我们把这个持久化类的信息告诉Hibernate。
### 三 .映射文件
Hibernate需要知道怎样去加载(load)和存储(store)持久化类的对象。这正是Hibernate映射文件发挥作用的地方。
映射文件告诉Hibernate它,应该访问数据库(database)里面的哪个表(table)及应该使用表里面的哪些字段(column)。
一个映射文件的基本结构看起来像这样:
~~~
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
[...]
</hibernate-mapping>
~~~
注意Hibernate的DTD是非常复杂的。你的编辑器或者IDE里使用它来自动完成那些用来映射的XML元素(element)和属性(attribute)。
你也可以在文本编辑器里打开DTD-这是最简单的方式来概览所有的元素和attribute,并查看它们的缺省值以及注释。注意Hibernate不会从web加载DTD文件,但它会首先在应用程序的classpath中查找。DTD文件已包括在hibernate3.jar里,同时也在Hibernate发布包的src/目录下。
为缩短代码长度,在以后的例子里我们会省略DTD的声明。当然,在实际的应用程序中,DTD声明是必须的。
在hibernate-mapping标签(tag)之间,含有一个class元素。所有的持久化实体类(再次声明,或许接下来会有依赖类,就是那些次要的实体)都需要一个这样的映射,来把类对象映射到SQL数据库里的表。
~~~
<hibernate-mapping>
<class name="com.model.StudentInfo" table="StudentInfo">
</class>
</hibernate-mapping>
~~~
到目前为止,我们告诉了Hibernate怎样把StudentInfo类的对象持久化到数据库的StudentInfo表里,以及怎样从StudentInfo表加载到StudentInfo类的对象。
每个实例对应着数据库表中的一行。现在我们将继续讨论有关唯一标识符属性到数据库表的映射。另外,由于我们不关心怎样处理这个标识符,我们就配置由Hibernate的标识符生成策略来产生代理主键字段。
~~~
<hibernate-mapping>
<class name="com.model.StudentInfo" table="StudentInfo">
<id name="id" column="ID">
<generator class="native"/>
</id>
</class>
</hibernate-mapping>
~~~
id元素是标识符属性的声明,name="id" 声明了Java属性的名字 - Hibernate会使用getId()和setId()来访问它。
column属性则告诉Hibernate,我们使用StudentInfo表的哪个字段作为主键。嵌套的generator元素指定了标识符生成策略,在这里我们指定native,它根据已配置的数据库(方言)自动选择最佳的标识符生成策略。Hibernate支持由数据库生成,全局唯一性(globally unique)和应用程序指定(或者你自己为任何已有策略所写的扩展)这些策略来生成标识符。
最后我们在映射文件里面包含需要持久化属性的声明。默认情况下,类里面的属性都被视为非持久化的:
~~~
<hibernate-mapping>
<class name="com.model.StudentInfo" table="StudentInfo">
<id name="id" column="ID">
<generator class="native"/>
</id>
<property name="date" type="timestamp" column="EnterDate"/>
<property name="title"/>
</class>
</hibernate-mapping>
~~~
和`id`元素一样,`property`元素的`name`属性告诉Hibernate使用哪个getter和setter方法。在此例中,Hibernate会寻找`getDate()/setDate()`, 以及`getTitle()/setTitle()`。
为什么`date`属性的映射含有`column` attribute,而`title`却没有?当没有设定`column` attribute 的时候,Hibernate缺省地使用JavaBean的属性名作为字段名。对于`title`,这样工作得很好。然而,`date`在多数的数据库里,是一个保留关键字,所以我们最好把它映射成一个不同的名字。
另一有趣的事情是`title`属性缺少一个`type` attribute。我们在映射文件里声明并使用的类型,却不是我们期望的那样,是Java数据类型,同时也不是SQL数据库的数据类型。这些类型就是所谓的Hibernate 映射类型*(mapping types)*,它们能把Java数据类型转换到SQL数据类型,反之亦然。再次重申,如果在映射文件中没有设置`type`属性的话,Hibernate会自己试着去确定正确的转换类型和它的映射类型。在某些情况下这个自动检测机制(在Java 类上使用反射机制)不会产生你所期待或需要的缺省值。`date`属性就是个很好的例子,Hibernate无法知道这个属性(`java.util.Date`类型的)应该被映射成:SQL`date`,或`timestamp`,还是`time` 字段。在此例中,把这个属性映射成`timestamp` 转换器,这样我们预留了日期和时间的全部信息。
应该把这个映射文件保存为`Event.hbm.xml`,且就在StudentInfo Java类的源文件目录下。映射文件可随意地命名,但`hbm.xml`的后缀已成为Hibernate开发者社区的约定。
对于StudentInfo的配置如下:
~~~
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.model.StudentInfo" table="StudentInfo">
<id name="id" column="ID">
<generator class="native"/>
</id>
<property name="name" column = "Name"/>
<property name="age" column = "Age"/>
<property name="sex" column = "Sex"/>
</class>
</hibernate-mapping>
~~~
我们继续进行Hibernate的主要配置。
### 四. Hibernate配置
现在我们已经有了一个持久化类和它的映射文件,该是配置Hibernate的时候了。在此之前,我们需要一个数据库。
Hibernate是你的应用程序里连接数据库的那层,所以它需要连接用的信息。连接(connection)是通过一个也由我们配置的JDBC连接池(connection pool)来完成的。
Hibernate的发布包里包含了许多开源的(open source)连接池。注意,如果你希望使用一个产品级(production-quality)的第三方连接池软件,你必须拷贝所需的库文件到你的classpath下,并使用不同的连接池设置。
为了保存Hibernate的配置,我们可以使用一个简单的hibernate.properties文件,或者一个稍微复杂的hibernate.cfg.xml,甚至可以完全使用程序来配置Hibernate。多数用户更喜欢使用XML配置文件:
~~~
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<!-- MySql配置 -->
<!--<property name="connection.driver_class">com.mysql.jdbc.Driver</property> -->
<!--<property name="connection.url">jdbc:mysql://localhost/hibernate</property> -->
<!-- SQL dialect -->
<!--<property name="dialect">org.hibernate.dialect.MySQLDialect</property>-->
<!-- SqlServer配置 -->
<property name="connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="connection.url">jdbc:sqlserver://localhost/Hibernate</property>
<!-- 数据库用户名 -->
<property name="connection.username">sa</property>
<!-- 数据库密码 -->
<property name="connection.password">123</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.SQLServerDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<mapping resource="com/model/StudentInfo.hbm.xml"/>
</session-factory>
</hibernate-configuration>
~~~
注意这个XML配置使用了一个不同的DTD。在这里,我们配置了Hibernate的SessionFactory-一个关联于特定数据库全局的工厂(factory)。
如果你要使用多个数据库,就要用多个的<session-factory>,通常把它们放在多个配置文件中(为了更容易启动)。
最开始的4个property元素包含必要的JDBC连接信息。方言(dialect)的property元素指明Hibernate 生成的特定SQL变量。你很快会看到,Hibernate对持久化上下文的自动session管理就会派上用场。
打开hbm2ddl.auto选项将自动生成数据库模式(schema)- 直接加入数据库中。当然这个选项也可以被关闭(通过去除这个配置选项)或者通过Ant任务SchemaExport的帮助来把数据库schema重定向到文件中。
最后,在配置中为持久化类加入映射文件。
Demo下载地址:[点击打开链接](http://download.csdn.net/detail/sunnyyoona/7792073)
### 五.启动和辅助类
是时候来加载和储存一些StudentInfo对象了,但首先我们得编写一些基础的代码以完成设置。
我们必须启动Hibernate,此过程包括创建一个全局的SessoinFactory,并把它储存在应用程序代码容易访问的地方。SessionFactory可以创建并打开新的Session。一个Session代表一个单线程的单元操作,SessionFactory则是个线程安全的全局对象,只需要被实例化一次。
我们将创建一个HibernateUtil辅助类(helper class)来负责启动Hibernate和更方便地操作SessionFactory。让我们来看一下它的实现:
~~~
package com.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static
{
try
{
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = new Configuration().configure().buildSessionFactory();
}
catch (Throwable ex)
{
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
~~~
这个类不但在它的静态初始化过程(仅当加载这个类的时候被JVM执行一次)中产生全局的SessionFactory,而且隐藏了它使用了静态singleton的事实。
它也可能在应用程序服务器中的JNDI查找SessionFactory。
示例的基本框架完成了 - 现在我们可以用Hibernate来做些真正的工作。
### 六. 加载并存储对象
我们终于可以使用Hibernate来加载和存储对象了,编写一个带有main()方法的StudentManager类:
~~~
package com.test;
import org.hibernate.Session;
import com.model.StudentInfo;
import com.util.HibernateUtil;
public class StudentManager {
public static void main(String[] args) {
StudentManager mgr = new StudentManager();
mgr.createAndStoreEvent();
HibernateUtil.getSessionFactory().close();
}
private void createAndStoreEvent() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
StudentInfo student = new StudentInfo();
student.setName("无情");
student.setAge(24);
student.setSex("女");
session.save(student);
session.getTransaction().commit();
}
}
~~~
我们创建了个新的StudentInfo对象并把它传递给Hibernate。现在Hibernate负责与SQL打交道,并把INSERT命令传给数据库。在运行之前,让我们看一下处理Session和Transaction的代码。 一个Session就是个单一的工作单元。我们暂时让事情简单一些,并假设HibernateSession和数据库事务是一一对应的。为了让我们的代码从底层的事务系统中脱离出来(此例中是JDBC,但也可能是JTA),我们使用Hibernate Session中的Transaction API。
sessionFactory.getCurrentSession()是干什么的呢?首先,只要你持有SessionFactory(幸亏我们有HibernateUtil,可以随时获得),大可在任何时候、任何地点调用这个方法。getCurrentSession()方法总会返回“当前的”工作单元。记得我们在hibernate.cfg.xml中把这一配置选项调整为"thread"了吗?因此,因此,当前工作单元被绑定到当前执行我们应用程序的Java线程。但是,这并非是完全准确的,你还得考虑工作单元的生命周期范围 (scope),它何时开始,又何时结束.
Session在第一次被使用的时候,即第一次调用getCurrentSession()的时候,其生命周期就开始。然后它被Hibernate绑定到当前线程。当事务结束的时候,不管是提交还是回滚,Hibernate会自动把Session从当前线程剥离,并且关闭它。假若你再次调用getCurrentSession(),你会得到一个新的Session,并且开始一个新的工作单元。这种线程绑定(thread-bound)的编程模型(model)是使用Hibernate的最广泛的方式,因为它支持对你的代码灵活分层(事务划分可以和你的数据访问代码分离开来,在本教程的后面部分就会这么做)。 和工作单元的生命周期这个话题相关,Hibernate Session是否被应该用来执行多次数据库操作?上面的例子对每一次操作使用了一个Session,这完全是巧合,这个例子不是很复杂,无法展示其他方式。Hibernate Session的生命周期可以很灵活,但是你绝不要把你的应用程序设计成为每一次数据库操作都用一个新的Hibernate Session。
###七. Annotation注解方式
我们用Annotation注解方式来代替XML配置方式。
在持久化对象时进行注解:
~~~
package com.model;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* Hibernate Annotation注解方式
* @author xiaosi
*
*/
//注解为一个实体
@ Entity
public class TeacherInfo {
private int id;
private String name;
private String sex;
private int age;
public TeacherInfo(){
}
//注解主键
@ Id
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
~~~
在Hibernate.cfg.xml配置中:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-18_56eba2a4a8b36.jpg)
启动辅助类中有一个不同点:Configuration替换为AnnotationConfiguration
~~~
package com.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
public class HibernateAnoUtil {
private static final SessionFactory sessionFactory;
static
{
try
{
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
}
catch (Throwable ex)
{
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
~~~
加载存储对象:
~~~
package com.test;
import org.hibernate.Session;
import com.model.TeacherInfo;
import com.util.HibernateAnoUtil;
public class TeacherManager {
public static void main(String[] args) {
TeacherManager mgr = new TeacherManager();
mgr.createAndStoreEvent();
HibernateAnoUtil.getSessionFactory().close();
}
private void createAndStoreEvent() {
Session session = HibernateAnoUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
TeacherInfo teacher = new TeacherInfo();
teacher.setId(1);
teacher.setName("王倩");
teacher.setAge(25);
teacher.setSex("女");
session.save(teacher);
session.getTransaction().commit();
}
}
~~~
数据库插入的数据:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-18_56eba2a4ba3dc.jpg)