目录
Hibernate 配置文件解析
Hibernate通过读取默认的配置文件加载数据库配置信息,该配置文件应放于classpath根目录下,默认的配置文件名称为Hibernate.cfg.xml。在Hibernate.cfg.xml文件中,不仅包含数据库的配置信息,还包含了用户对Hibernate所设置的属性信息,如打印SQL语句、自动建表等。其配置方法如下:
<?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">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<session-factory>
<!-- 数据库驱动 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 数据库连接的URL -->
<property name="connection.url">jdbc.mysql://localhost:3306/test</property>
<!-- 数据库连接用户 -->
<property name ="connection.username">root</property>
<!-- 数据库连接密码 -->
<property name="connection.password">111</property>
<!-- Hibernate 方言 -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 打印SQL语句 -->
<property name="show_sql">true</property>
<!-- 映射文件 -->
<mapping resource ="con/lyq/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Hibernate.cfg.xml文件的根元素为,每个元素可以有多个和子元素,通常情况下只有一个,每一个对应一个数据库;元素用来配置Hibernate属性信息;元素用来配置持久化类映射文件的相对路径。元素的常用属性及功能如表1所示。
在项目实施过程中,当底层数据库发生变化时,不需要更改程序的源代码,只需要更改配置文件Hibernate.cfg.xml即可,提高了Hibernate使用的灵活性,同样也体现了Hibernate对数据库的跨平台性。
建议在调试程序时将Hibernate.show_sql属性设置为true,这样在运行程序时,会在控制台输出SQL语句,便于程序的调试;在发布应用时,应将该属性设置为false,以减少信息的输出量,提高软件的运行性能。
编写持久化类
对象-关系映射(ORM)是Hibernate的基础。在Hibernate中,持久化类是Hibernate操作的对象,它与数据库中的数据表相对应,描述数据表的结构信息。在持久化类中,其属性信息与数据表中的字段相匹配。下面来看一个简单的持久化类。
public class User {
private Integer id; //ID编号
private String name; //姓名
private boolean sex; //性别
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
}
类User为用户持久化类,此类中定义了用户的基本属性,并提供相应的getXXX()与setXXX()方法。从这个类可以看出,持久化类遵循JavaBean命名约定。由于持久化类只是一个普通的类,并没有特殊的功能,也就是说它不依赖于任何对象(没有实现任何接口,也没有继承任何类),又被称为POJO类。Hibernate持久化类的编写遵循一定的规范,创建时需要注意以下几点。
l 声明一个默认的无参的构造方法
Hibernate在创建持久化类时,通过默认且没有参数的构造方法进行实例化,所以必须要提供一个无参的构造方法。
l 类的声明是非final类型的
如果Hibernate的持久化类声明为final类型,那么将不能使用延迟加载等设置,因为Hibernate的延迟加载通过代理实现;它要求持久化类是非final的。
l 拥有一个标识属性
标识属性通常对应数据表中的主键。此属性是可选的。为了更好地使用Hibernate,推荐加入此属性,如User类中的id属性。
l 为属性声明访问器
Hibernate在加载持久化类时,需要对其进行创建并赋值,所以在持久化类中属性声明getXXX()方法与setXXX()方法,这些方法为public类型。
编写映射文件
Hibernate的映射文件与持久化类相互对应,映射文件指定持久化类与数据表之间的映射关系,如数据表的主键生成策略、字段的类型、一对一关联关系等。它与持久化类的关系密切,两者之间相互关联。在Hibernate中,映射文件的类型为.xml格式,其命名方式规范为*.hbm.xml。例如,创建持久化类User对象的映射文件,代码如下:
<?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.lyq.model.User" table="tb_user">
<id name="id" column="id" type="int">
<generator class="native"></generator>
</id>
<property name="name" type="string" not-null="true" length="50">
<column name="name"></column>
</property>
<property name="sex" type="boolean">
<column name="sex"/>
</property>
</class>
</hibernate-mapping>
(1)元素
元素是Hibernate映射文件的根元素,其他元素嵌入在元素内,其常用属性主要有package属性,用于指定包名。
(2)元素
元素用于指定持久化类和数据表的映射。其name属性指定持久类的完整类名(包含包名);table属性用于指定数据表的名称,如果不指定此属性,Hibernate将使用类名作为表名。
在元素中包含一个元素及多个元素,其中元素对应数据表中的标识,指定持久化类的OID和表主键的映射;元素描述数据表中字段的属性。
元素
通过name属性指定持久化类中的属性,column属性指定数据表中的字段名称,type属性用于指定字段的类型。元素的子元素用于配置数据表主键的生成策略,它通过class属性进行设置。Hibernate常用内置主键生成策略如表1所示。
元素元素用于配置数据表中字段的属性信息,通过此元素能够详细地对数据表的字段进行描述。元素的常用配置属性如表2所示。
在实际开发过程中,可以省略column属性及type属性的配置,在尚未配置它们的情况下,Hibernate默认使用持久化类中属性名及属性类型去映射数据表中的字段。但要注意,当持久化类中的属性名与数据库中SQL关键字相同时(如sum、group等),应该使用column属性指定具体的字段名称以示区分。
从映射文件可看出,它在持久化类与数据库之间起着桥梁的作用,映射文件的建立描述了持久化类与数据表之间的映射关系,同样也告知了Hibernate数据表的结构等信息。
Hibernate 基本数据类型的映射
Hibernate 的基本映射数据类型是Java基本类型与标准SQL类型间相互转换的桥梁.在前面的讲解中大家已经知道了.元素的type属性指定的就是映射类型.通过HIbernate 的映射关系可以非常方便的将数据从一种形式转换成另一种形式,完成高质量的ORM任务,表1列出了常用的Hibernate映射类型,Java基本类型与标准SQL 类型的对应关系 .
##hibernate 自动建表技术
面向对象的编程思想在Hibernate框架中体现得淋漓尽致,它将数据库中的数据表看作是对象,对数据的操作同样以对象的方式进行处理。在Hibernate中,对于数据表存在着面向对象中的继承等关系,因此在开发Hibernate项目时确定实体对象及实体与实体之间的关系极其重要。
在确定实体对象及关系后,可以通过Hibernate提供的自动建表技术导出数据表。具体实现方式有两种,本节将对其进行详细介绍。
- 手动导出数据表
手动导出数据表用到org.hibernate.tool.hbm2ddl.SchemaExport类,其create()方法用于数据表的导出。
手动导出数据表,代码如下。
public class ExportTables{
public static void main(String[] args){
Configuration cfg=new Configuration().configure(); //加载配置信息
SchemaExport export=new SchemaExport(cfg);
export.create(true,true);
}
}
- Hibernate 配置文件自动建表
使用Hibernate配置文件进行自动建表,只需在Hibernate配置文件中加入配置代码即可。此种方法简单而又适用。
例如:
<property name="hibernate.hbm2ddl.auto">create</property>
"
在Hibernate 配置文件中,hibernate.hbm2ddl.auto 属性用于设置自动建表,其取值有3种情况:
l create:使用此值,每次创建SessionFactory时都会重新创建数据表;如果数据表已存在将进行删除操作。要慎用。
l update:如果数据表不存在,则创建数据表;如果数据表存在,则检查数据表是否与映射文件相匹配,当不匹配时,更新数据表信息。
l none:使用此值,无论任何时候都不会创建或更新数据表。
在Hibernate框架的使用中,自动建表技术经常被用到,因为Hibernate对数据库操作进行了封装,符合Java面向对象的思维模式,当确定实体对象后,数据表也将自动被确定,从而为开发和测试提供了极大的方便。
编写Hibernate的初始化类
Hibernate的运行离不开Session对象,对于数据的增 删 改 查都要用到Session, 而Session对象依赖于SessionFactory对象,它需要通过SessionFactory进行获取,那么它是如何创建 又如何管理的Session呢?下面将对其进行详细讲解
- SessionFactory 的创建过程
Hibernate 通过Configuration类价值HIbernate配置信息,这主要是通过调用Configuration对象的configure(0方法来实现的,在默认情况下,Hibernate加载classpath目录下的”hibernate.cfg.xml” 文件.例如:
Configuration cfg=new Configuration().configure();
加载完毕后通过Configuration对象的buildSessionFactory()方法创建SessionFactory对象,例如:
SessionFactory factory=cfg.bulidSessionFactory();
编写Hibernate 初始化类
Session对象是操作数据库的关键对象,与SessionFactory对象关系密切.SessionFacroty对象并非轻量级,其创建过程需占用大量资源,而Session对象虽然是轻量级对象,但要做到及时获取与及时关闭,因此需要编写一个类对二者进行管理。
Hibernate的初始化类,代码如下。
public class HibernateUtil {
private static SessionFactory factory = null; //SessionFactory对象
static {
try {
Configuration cfg = new Configuration().configure(); //加载Hibernate配置文件
factory = cfg.buildSessionFactory(); //实例化SessionFactory
} catch (HibernateException e) {
e.printStackTrace();
}
}
public static Session getSession() { //编写获取session对象方法
Session session = (factory != null) ? factory.openSession() : null; //如果SessionFacroty不为空,则开启Session
return session;
}
public static SessionFactory getSessionFactory() { //编写获取SessionFactory对象方法
return factory;
}
public static void closeSession(Session session) { //关闭session方法
if (session != null) {
if (session.isOpen()) {
session.close(); //关闭Session
}
}
}
}
SessionFactoty对象是重量级的对象,其创建过程比较耗时及占用资源,可以将其理解为是一个生产Session对象的工厂,当需要Session对象时从此工厂中获取即可,所以在整个程序的应用过程中最好只创建一次。例如,当用到一根钢筋时,可以到一个已存在的钢铁厂中去购买,而不需要去创建一个钢铁厂,再来生产所用的钢筋,这样的做法显得非常离谱。对程序而言,Session对象的应用是非常频繁的,如果用到Session对象就去创建一个SessionFactoty对象,将会对程序的性能产生一定的负作用。因此,在Hibernate初始化类中应将SessionFactoty对象的创建置于静态块中,实现在程序的应用过程中对其只创建一次,从而节省资源的占用。
但要注意Session对象并不是线程安全的,在涉及到多线程问题时,应该借助于ThreadLocal对象进行管理。
添加数据
添加数据用到Session接口的save()方法,其入口参数为Object类型,代表持久化对象。语法如下:
public Serializable save(Object obj)throws HibernateException
obj:持久化对象,返回值:所生成的标识。
向数据库中添加药品信息,代码如下。
public class Save {
public static void main(String[] args) {
Session session = null; //声明Session对象
try {
session = HibernateUtil.getSession(); //获取Session
session.beginTransaction(); //开启事务
Medicine medicine = new Medicine(); //实例化药品对象,并对其属性赋值
medicine.setName("感冒药XX");
medicine.setPrice(5.00);
medicine.setFactoryAdd("XX制药一厂");
medicine.setDescription("最新感冒药");
session.save(medicine); //保存药品对象
session.getTransaction().commit(); //提交事物务
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback(); //出错将回滚事务
}finally{
HibernateUtil.closeSession(session); //关闭Session对象
}
}
}
在执行save()方法之前,首先创建药品对象medicine,并对其属性赋值。此时medicine处于Transient(瞬时)状态,并没有在Session的管理之中。当提交事务后,medicine处于Persistent(持久)状态,已经在Session的管理之中,且数据库中存在与之匹配的数据,如图14.6所示。当Session关闭后,medicine脱离Session的管理,处于Detached(脱管)状态。
删除数据
在Hibernate中删除数据与添加、查询数据有所不同,因为要删除的对象并不在Session的管理之中,通过Session并不能对其进行删除操作,所以需要将要删除的对象转换为持久状态,使其处于Session的管理之内,然后再通过delete()方法进行删除。语法如下:
public void delete(Object object) throws HibernateException
object:要删除的对象。
通过delete()方法删除药品信息,代码如下。
public class Delete {
public static void main(String[] args) {
Session session = null; //声明Session对象
try {
session = HibernateUtil.getSession(); //获取Session
session.beginTransaction(); //开启事务
Medicine medicine = (Medicine)session.load(Medicine.class, new Integer(1)); //加载对象
session.delete(medicine); //输出药品信息
session.getTransaction().commit(); //提交事务
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback(); //出错将回滚事务
}finally{
HibernateUtil.closeSession(session); //关闭Session对象
}
}
}
在进行删除操作之前,程序首先加载了药品对象medicine,使其处于Persistent(持久)状态,然后再进行删除操作。
修改数据
Hibernate对数据的修改主要有两种情况:当实例对象处于Persistent(持久)状态时,对于它所发生的任何更新操作,Hibernate在更新缓存时都将会对其进行自动更新;另一种情况是Session接口提供了update()方法,调用此方法可对数据进行手动更新。
.自动更新
自动更新数据的方法与删除数据相似,在操作之前都需要加载数据,因为要修改的数据并没有处于Session的管理之内。当通过load()方法、get()方法加载数据后,持久化对象便处于Session的管理之内,即处于持久状态,在进行数据修改时,Hibernate将自动对数据进行更新操作。
修改药品信息,代码如下。
public class Update {
public static void main(String[] args) {
Session session = null; //声明Session对象
try {
session = HibernateUtil.getSession(); //获取Session
session.beginTransaction(); //开启事务
Medicine medicine = (Medicine)session.load(Medicine.class, new Integer(1)); //加载药品对象
medicine.setName("感冒胶囊"); //修改药品名称
medicine.setPrice(10.05); //修改药品价格
session.getTransaction().commit(); //提交事务
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback(); //出错将回滚事务
}finally{
HibernateUtil.closeSession(session); //关闭Session对象
}
}
}
对于Persistent(持久)状态的对象,Hibernate在更新缓存时将对数据进行对比,当对象发生变化时,Hibernate将更新数据。运行程序,Hibernate将发出两条SQL语句,如图1所示。
2.手动更新
手动更新主要是通过调用Session接口的update()方法来实现。语法如下:
public void update(Object obj) throws HibernateException
object:要更新的对象
通过update()方法修改药品信息,代码如下。
public class Update {
public static void main(String[] args) {
Session session = null; //声明Session对象
try {
session = HibernateUtil.getSession(); //获取Session
session.beginTransaction(); //开启事务
Medicine medicine = new Medicine(); //手动创建的Detached状态的药品对象
medicine.setId(1); //药品ID
medicine.setName("感冒胶囊001"); //药品名称
session.update(medicine); //更新药品信息
session.getTransaction().commit(); //提交事务
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback(); //出错将回滚事务
}finally{
HibernateUtil.closeSession(session); //关闭Session对象
}
}
}
实例运行前,数据表中的数据如图2所示。由于程序中手动创建了Detached状态的药品对象,当更新数据时,对于持久化对象中没有值的属性也将会同步到数据库。
使用此种方法,虽然Hibernate只发出一条SQL语句,但对没有设置值的属性,数据表中将会同步为空值,所以应该慎用。
查询数据
Session接口提供了两个加载数据的方法,分别为get()方法与load()方法,它们都用于加载数据,但二者之间存在一定的区别。get()方法返回实际对象,而load()方法返回对象的代理,只有在被调用的时候,Hibernate才会发出SQL语句去查询对象。
1.get()方法
语法:
public Object get(Class entityClass,Serializable id) throws HibernateException
说明:
entityClass:持久化对象的类,id:标识,返回值:持久化对象或null值。
例如,获取编号为1的药品信息,代码如下:
Medicine medicine = (Medicine)session.get(Medicine.class, new Integer(1));
//查询药品ID为1的药品
2.load()方法
语法:
public Object load(Class entityClass, Serializable id) throws HibernateException
说明:
entityClass:持久化对象的类,id:标识,返回值:持久化对象或null值。
例如,获取编号为1的商品信息,代码如下:
Medicine medicine = (Medicine)session.load(Medicine.class, new Integer(1));
一级缓存
一级缓存是Session级的缓存,其生命周期很短,与Session相互对应。一级缓存由Hibernate进行管理,属于事务范围的缓存。
当程序调用Session的load()方法、get()方法、save()方法、saveOrUpdate()方法、update()方法或查询接口方法时,Hibernate会对实体对象进行缓存;当通过load()方法或get()方法查询实体对象时,Hibernate会首先到缓存中查询,在找不到实体对象的情况下,Hibernate才会发出SQL语句到数据库中查询,从而提高了Hibernate的使用效率。下面通过实例来了解一下一级缓存。
public class Test {
public static void main(String[] args) {
Session session = null; //声明Session对象
try {
session = HibernateUtil.getSession(); //获取Session
session.beginTransaction(); //开启事务
System.out.println("第一次查询:");
Medicine medicine1 = (Medicine)session.get(Medicine.class, new Integer(1)); //查询药品
System.out.println("药品名称:" + medicine1.getName()); //输出药品名称
System.out.println("第二次查询:");
Medicine medicine2 = (Medicine)session.get(Medicine.class, new Integer(1)); //查询药品
System.out.println("药品名称:" + medicine2.getName()); //输出药品名称
session.getTransaction().commit(); //提交事务
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtil.closeSession(session);
}
}
程序通过get()方法第一次查询药品对象时,Hibernate会发出一条SQL语句进行查询,此时Hibernate对其药品对象进行了一级缓存;当再次通过get()方法查询时,Hibernate就不会发出SQL语句了,因为药品已存在于一级缓存中。
一级缓存的生命周期与Session相对应,它并不会在Session之间共享,在不同的Session中不能得到在其他Session中缓存的实体对象。
二级缓存
二级缓存是SessionFactory级的缓存,其生命周期与SessionFactory一致。二级缓存可以在多个Session间共享,属于进程范围或群集范围的缓存。
二级缓存是一个可插拔的缓存插件,它的使用需要第三方缓存产品的支持。在Hibernate框架中,通过Hibernate配置文件配置二级缓存的使用策略。
下面通过EHCache演示二级缓存的使用,其中主要包括以下两步。
l 加入缓存配置文件ehcache.xml。
ehcache.xml用于设置二级缓存的缓存策略,此文件位于下载的Hibernate的ZIP包下的etc目录中,在使用过程中需要将此文件加入到项目的src目录中。
l 设置Hibernate配置文件。
在配置文件hibernate.cfg.xml中,设置开启二级缓存及指定缓存产品提供商,同时还需要指定二级缓存应用到的实体对象,示例代码如下:
<!-- 开启二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- 指定缓存产品提供商 -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<!-- 映射文件 -->
<mapping resource="com/lyq/model/Medicine.hbm.xml" />
<!-- 指定二级缓存应用到的实体对象 -->
<class-cache class="com.lyq.model.Medicine" usage="read-only" />
对于二级缓存,可以使用一些不经常更新的数据或参考的数据,此时其性能会得到明显的提升。例如一个新闻网站,当发布一条热点新闻时,会有成千上万的访问量,而此条新闻并没有发生任何的变化,如果每一个用户访问都要查询数据库,将会在性能方面造成一定的问题,此时便可以考虑应用二级缓存。如果经常变化的数据应用二级缓存,则将失去意义。
Lazy 策略
Lazy策略为延迟加载策略,Hibernate通过JDK代理机制对其进行实现,它意味着使用延迟加载的对象,在获取对象的时候返回的是对象的代理,并不是对象的真正引用,只有在对象真正被调用的时候,Hibernate才会对其进行查询,返回真正的对象。
Lazy策略好比现实生活中用钱去买东西,如图1所示。在没有购买任何具体的物品时,“钱”只是一个代理对象,在购买成功之后,“钱”变成了实际的“物品”,也就相当于对象的代理返回了真正的对象引用;在购买物品之前,由于天气、时间等因素,将推迟一天购买此物品,此时相当于使用了Lazy策略进行延迟加载;在购买物品之前或延迟加载过程中,可能会有不需要此物品的情况发生,这样就不必购买此物品,从而节省了时间,这相当于JVM对其进行了垃圾回收。因此,使用Hibernate的延迟加载策略,在某种情况下将会对性能起到一定的优化作用。
此外,Hibernate的延迟加载还可以减少程序与数据库的连接次数,因为使用了延迟加载,Hibernate将延缓执行SQL语句,减少数据库的访问次数,提高执行的效率。