学会用20%时间和记忆去完成80%的工作!
Hibernate中实体对象的生命周期
l Transient-瞬间状态
当直接使用new创建出对象,例如用User类所衍生出的对象,在还没有使用save()方法之前都是暂存对象,这些对象还没有与数据库发生任何关系,不与数据库的任何记录对应。
l Persistent-持久状态
当对象与数据库中的数据有对应关系,与session实例(尚未关闭)有关联。如将对象用session.save()方法存储,或用session.get()/load()方法从数据库加载数据并封装为对象,此对象进入持久状态。
l Detached-分离状态
当Persistent状态的对象的事务执行commit()之后或一级缓存执行了flush()操作,数据库中的对应数据也会跟着更新。如果你将session实例关闭close(),则Persistent状态的对象会成为Detached状态;而当你使用session的实例的delete()方法删除数据,Persistent状态的对象由于失去了对应的数据,成为Transient状态。
Detached状态的对象可以使用update()方法使之与数据库中的对应数据再度发生关联,此时Detached状态的对象会变为Persistent状态。
简要的Hibernate体系结构的概要图
Hibernate核心接口
l SessionFactory(org.hibernate.SessionFactory)
针对单个数据库映射关系经过编译后的内存镜像,是线程安全的(不可变)。他是生成Sessinon的工厂,本身要用到ConnectionProvider。该对象可以在进程或集群的级别上,为那些事务之间可以重用的数据提供可循的二级缓存。
l Session(org.hibernate.Session)
表示应用程序与持久储存层之间交换操作的一个单线程对象,此对象生存期很短。其隐藏了JDBC连接,也是Transaction的工厂。其会吃用一个针对持久话对象的必选(第一级)缓存,在遍历对象图或跟据持久化标识查找对象时会用到。
l 持久对象及其集合
带有持久化状态的、具有业务功能的单线程对象,此对象生存期很短。这些对象可能是普通的JavaBean/POJO,唯一特殊的是他们正与(仅仅一个)Session相关联,一旦这个Session被关闭,这些对象就会脱离持久化状态,这样就可被应用程序的任何层自由使用(例如作表示层的数据传输对象)。
l 瞬态(Transient)和脱管(Detached)的对象及其集合
l 事务(Transaction(org.hibernate.Transaction))
(可选的)应用程序用来制定原子操作单元范围的对象,它是单线程的,生命周期很短。他通过抽象将用用从底层具体的JDBC、JTA以及CORBA事务隔离开。某些情况下,一个Session之内可能包含多个Transaction对象。尽管是否使用该对象是可选的,但无论是使用底层的API还是使用Transaction对象。事务边界的开启与关闭是必不可少的。
l ConnectionProvider(org.hibernate.connection.ConnectionProvider)
(可选的)申城JDBC连接的工厂(同时也起到连接池的作用)。它通过抽象将应用从底层的Datasource或DriverManager隔离开。仅供开发者扩张/实现用,并不暴露给应用程序使用。
l TransactionFactory(org.hibernate.TransactionFactory)
(可选的)生成Transaction对象实例的工厂。仅供开发者扩展/实现用,并不暴露给应用程序使用。
配置MyEclipse环境使用Hibernate
hibernate3.jar是必要的,而在lib目录中还包括了许多jar文件,您可以在 Hibernate 3.0官方的参考手册 上找到这些jar的相关说明,其中必要的是 antlr、dom4j、CGLIB、asm、Commons Collections、Commons Logging、 EHCache,Hibernate底层还需要Java Transaction API,所以您还需要jta.jar,到这边为止,总共需要以下的jar文件:
Hibernate可以运行于单机之上,也可以运行于Web应用程序之中,如果是运行于单机,则将所有用到的jar文件(包括JDBC驱动程序)设定至 CLASSPATH中,如果是运行于Web应用程序中,则将jar文件置放于WEB-INF/lib中。
如果您还需要额外的Library,再依需求加入,例如JUnit、Proxool等等,接下来可以将etc目录下的log4j.properties复制至Hibernate项目的Classpath下,并修改一下当中的log4j.logger.org.hibernate为error,也就是只在在错误发生时显示必要的信息。
接着设置基本的Hibernate配置文件,可以使用XML或Properties文件,这边先使用XML,文件名预设为 hibernate.cfg.xml:
说明:这边以一个简单的单机程序来示范Hibernate的配置与功能。
准备工作
l MyEclipse配置如前所述
l 建立持久化类
l 数据库的准备工作,在MySQL中新增一个demo数据库,并建立user表格:
CREATE TABLE user( id INT(11) NOT NULL auto_increment PRIMARY KEY,name VARCHAR(100) NOT NULL default '',age INT);
l 建立持久化类
l 建立持久化类的映射文件
l 编写Java的测试应用程序
建立持久化类
public class User {
private Integer id;
private String name;
private Integer age;
// 必须要有一个预设的建构方法
// 以使得Hibernate可以使用Constructor.newInstance()建立对象
public User() {
}
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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
其中id是个特殊的属性,Hibernate会使用它来作为主键识别,您可以定义主键产生的方式,这是在XML映像文件中完成,为了告诉 Hibernate您所定义的User实例如何映射至数据库表格,您撰写一个XML映射文件名是User.hbm.xml,如下所示:
建立持久化类的映射文件User.hbm.xml;
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.User" table="user">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="name" column="name" type="java.lang.String"/>
<property name="age" column="age" type="java.lang.Integer"/>
</class>
</hibernate-mapping>
<class>标签的name属性为所映像的对象,而table为所映像的表格;
<id>中 column属性指定了表格字段,而 type属性指定了User实例的中的id的类型,这边type中所设定的是直接指定Java中的对象类型,Hibernate也定义有自己的映射类型,作为Java对象与SQL型态的标准对应型态(因为语言所提供的类型并不一定与数据库的类型对应),这之后会再说明。
<id>中主键的产生方式在这边设定为"native",表示主键的生成方式由Hibernate根据数据库Dialect 的定义来决定,之后还会介绍其它主键的生成方式。
同样的,<property>标签中的column与type都各自指明了表格中字段与对象中属性的对应。
接着必须在Hibernate配置文件hibernate.cfg.xml中指明映射文件的位置,如下加入映射文件位置:
修改Hibernate的配置文件hibernate.cfg.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>
....
<!-- 对象与数据库表格映像文件 -->
<mapping resource=“com/cstp/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
接下来编写一个测试的程序,这个程序直接以Java程序设计人员熟悉的语法方式来操作对象,而实际上也直接完成对数据库的操作,程序将会将一条数据存入表格之中:
public class FirstHibernate {
public static void main(String[] args) {
// Configuration 负责管理 Hibernate 配置讯息
Configuration config = new Configuration().configure();
// 根据 config 建立 SessionFactory
// SessionFactory 将用于建立 Session
SessionFactory sessionFactory = config.buildSessionFactory();
// 将持久化的对象
User user = new User();
user.setName(“hhp”);
user.setAge(new Integer(30));
// 开启Session,相当于开启JDBC的Connection
Session session = sessionFactory.openSession();
// Transaction表示一组会话操作
Transaction tx= session.beginTransaction();
// 将对象映像至数据库表格中储存
session.save(user);
tx.commit();
session.close();
sessionFactory.close();
System.out.println(“新增记录OK!请先用MySQL观看结果!");
}
}
持久性保存对象的一般步骤总结
l 打开到数据源的一个会话
Session snSession = factory.openSession();
l 开始一个事务
Transaction tTransaction = snSession.beginTransaction();
l 创建希望持久性保存的对象,并且使用数据填充它们
User user = new User();
user.setName("Michael");
user.setAge(40);
l 保存对象到会话
snSession.save(user);
l 提交修改到数据源并且关闭链接
tTransaction.commit();
snSession.close();
FirstHibernate.java测试程序
如您所看到的,程序中只需要直接操作User对象,并进行Session与Transaction的相关操作,Hibernate就会自动完成对数据库的操作,您看不到任何一行JDBC或SQL的声明产生,编写好以上的各个文件之后,各文件的放置位置如下:
着可以开始运行程序,结果如下:
Hibernate: insert into user (name, age) values (?, ?)
新增记录OK!请先用MySQL观看结果!
执行结果中显示了Hibernate所实际使用的SQL,由于这个程序还没有查询功能,所以要进入MySQL中看看新增的数据,如下:
mysql> select * from user;
+----+-------------+------+
| id | name | age |
+----+-------------+------+
| 1 | caterpillar | 30 |
+----+-------------+------+
1 row in set (0.03 sec)
在 第一个 Hibernate 中介绍如何使用Hibernate在不使用SQL的情况下,以Java中操作对象的习惯来插入数据至数据库中,当然储存数据之后,更重要的是如何将记录读出,Hibernate中也可以让您不写一句SQL,而以Java中操作对象的习惯来查询数据。
Hibernate例子-条件查询
public class SecondHibernate {
public static void main(String[] args) {
Configuration config = new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession();
Criteria criteria = session.createCriteria(User.class);
// 查询user所有字段
List users = criteria.list();
Iterator iterator = users.iterator();
System.out.println("id \t name/age");
while(iterator.hasNext()) {
User user = (User) iterator.next();
System.out.println(user.getId() +
" \t " + user.getName() +
"/" + user.getAge());
}
// 查询user中符合条件的字段
criteria.add(Expression.eq("name", “hhp"));
users = criteria.list();
iterator = users.iterator();
System.out.println("id \t name/age");
while(iterator.hasNext()) {
User user = (User) iterator.next();
System.out.println(user.getId() +
" \t " + user.getName() +
"/" + user.getAge());
}
session.close();
sessionFactory.close();
}
}
Criteria对SQL进行封装,对于不甚了解SQL的开发人员来说,使用Criteria也可以轻易的进行各种数据的检索
您也可以使用 Expression设定查询条件,并将之加入Criteria中对查询结果作限制,Expression.eq()表示设定符合条件的查询,
例如 Expression.eq("name", “hhp")表示设定查询条件为"name"字段中为"caterpillar"的数据。
先来看一下执行结果:
Hibernate: select this_.id as id0_, this_.name as name0_0_, this_.age as age0_0_ from user this_
id name/age
1 hhp/30
3 tiantian/5
2 zhangy/26
Hibernate: select this_.id as id0_, this_.name as name0_0_, this_.age as age0_0_ from user this_ where this_.name=?
id name/age
1 hhp/30
Criteria是对象导向式的查询方式,让不了解SQL的开发人员也可以轻易进行各项查询,但Criteria的API目前还不是很完善,而 Hibernate鼓励的查询方式,是透过HQL(Hibernate Query Language)来进行,直接来看个实例:
Hibernate例子-HQL查询
public class SecondHibernate {
public static void main(String[] args) {
Configuration config = new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession();
// 使用HQL建立查询
Query query = session.createQuery("from User");
List users = query.list();
Iterator iterator = users.iterator();
System.out.println("id \t name/age");
while(iterator.hasNext()) {
User user = (User) iterator.next();
System.out.println(user.getId() +
" \t " + user.getName() +
"/" + user.getAge());
}
System.out.println();
// 使用HQL建立查询
query = session.createQuery("from User user where user.name like ?");
// 设定查询参数
query.setParameter(0, “hhp");
users = query.list();
iterator = users.iterator();
System.out.println("id \t name/age");
while(iterator.hasNext()) {
User user = (User) iterator.next();
System.out.println(user.getId() +
" \t " + user.getName() +
"/" + user.getAge());
}
session.close();
sessionFactory.close();
}
}
通过Query接口,您可以先设定查询参数,之后通过setXXX()等方法,将指定的参数值填入,而不用每次都编写完整的HQL,Query的 setParameter()方法第一个参数是指定 ? 出现的位置,从 0 开始,第二个参数则是设定查询条件。
Hibernate.cfg.xml配置文件详解
Hibernate可以使用XML文件或properties文件来配置SessionFactory,预设的配置文件为 hibernate.cfg.xml或hibernate.properties,XML提供较好的结构与配置方式,Hibernate建议使用XML文件进行配置。
上节课所示范的为使用XML文件的方式,一个XML文件的例子如下:
<hibernate-configuration>
<session-factory>
<!-- 显示实际操作数据库时的SQL -->
<property name="show_sql">true</property>
<!-- SQL方言,这边设定的是MySQL -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- JDBC驱动程序 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- JDBC URL -->
<property name="connection.url">jdbc:mysql://localhost/demo</property>
<!-- 数据库使用者 -->
<property name="connection.username">hhp</property>
<!-- 数据库密码 -->
<property name="connection.password">123456</property>
<!-- 对象与数据库表格映像文件 -->
<mapping resource=“com/cstp/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
使用XML文件进行配置时,可以在当中指定对象与数据库表格的映像文件位置,XML配置文件的位置必须在Classpath下,使用下面的方式来读入 XML文件以配置Hibernate:
Configuration config = new Configuration().configure();
Configuration的实例管理Hibernate的配置信息,通常用于建立SessionFactory,例如:
SessionFactory sessionFactory = config.buildSessionFactory();
SessionFactory一旦建立,就被赋予当时Configuration的配置信息,之后您改变Configuration并不会影响已建立的 SessionFactory实例,如果对Configuration改动后,则要建立一个新的SessionFactory实例,新的实例中会包括新的配置信息。
SessionFactory中包括了数据库配置及映射关系,它的建立相当复杂,所以使用时需考虑到重用已建立的SessionFactory实例, SessionFactory是被设计为「线程安全的」(Thread-safe)。
预设的XML配置文件名称是hibernate.cfg.xml,您也可以自行指定文件,即采用程序配置方式例如:
Configuration config = new Configuration().configure("db.cfg.xml");
SessionFactory sf = config.buildSessionFactory();
除了使用XML文件进行配置,您也可以使用属性文件进行配置,文件名称是hibernate.properties,一个例子如下:
hibernate.show_sql = true
hibernate.dialect = org.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class = com.mysql.jdbc.Driver
hibernate.connection.url = jdbc:mysql://localhost/demo
hibernate.connection.username = hhp
hibernate.connection.password = 123456
hibernate.properties的位置必须在Classpath下,由于properties文件中不包括映射文件的名称,为了要取得对象至数据库表格的映射文件,您必须在程序中如下加载:
Configuration cfg = new Configuration()
.addClass(com.cstp.User.class)
.addClass(com.cstp.Item.class);
第一个addClass()加入位于Classpath根目录下的User.hbm.xml,第二个addClass()加入Item类别的映射文件,该文件必须位于与User类别同一个目录,也就是com/cstp/Item.hbm.xml。
注意:在Hibernate下载文件中的etc目录下,有hibernate.cfg.xml与hibernate.properties可供设定参考
Hibernate.cfg.xml配置文件——数据库连结配置
Hibernate的配置文件中有一部份是在设定数据库连结,例如使用XML文件进行配置:
数据库连接池配置
<hibernate-configuration>
<session-factory>
<!-- 显示实际操作数据库时的SQL -->
<property name=“show_sql”>true</property>
<!-- SQL方言,这边设定的是MySQL -->
<property name=“dialect”>org.hibernate.dialect.MySQLDialect</property>
<!-- JDBC驱动程序 -->
<property name=“connection.driver_class”>com.mysql.jdbc.Driver</property>
<!-- JDBC URL -->
<property name=“connection.url”>jdbc:mysql://localhost/demo</property>
<!-- 数据库使用者 -->
<property name=“connection.username”>hhp</property>
<!-- 数据库密码 -->
<property name=“connection.password”>123456</property>
<!设置数据库的连接池属性>
<!-- Hibernate 预设的Connection pool -->
<property name=“connection.pool_size”>2</property>
<!-- 对象与数据库表格映射文件 -->
<mapping resource=“com/cstp/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
其中设定的connection.pool_size是Hibernate预设的连接池设定,通常只用于开发阶段测试之用。如果使用properties 文件的话则如下:
hibernate.properties
hibernate.show_sql = true
hibernate.dialect = org.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class = com.mysql.jdbc.Driver
hibernate.connection.url = jdbc:mysql://localhost/demo
hibernate.connection.username = hhp
hibernate.connection.password = 123456
hibernate.connection.pool_size = 2
Hibernate在数据库连接池的使用上是可选的,您可以使用C3P0连接池,例如XML的配置方式如下:
<hibernate-configuration>
<session-factory>
<!-- 显示实际操作数据库时的SQL -->
<property name="show_sql">true</property>
<!-- SQL方言,这边设定的是MySQL -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- JDBC驱动程序 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- JDBC URL -->
<property name="connection.url">jdbc:mysql://localhost/demo</property>
<!-- 数据库使用者 -->
<property name="connection.username">hhp</property>
<!-- 数据库密码 -->
<property name="connection.password">123456</property>
<!-- C3P0 连接池设定 -->
<property name="c3p0.min_size">5</property>
<property name="c3p0.max_size">20</property>
<property name="c3p0.timeout">1800</property>
<property name="c3p0.max_statements">50</property>
<!-- 对象与数据库表格映像文件 -->
<mapping resource=“com/cstp/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
记得您的Classpath中必须包括c3p0-*.jar,属性文件hibernate.properties的配置范例如下:
hibernate.properties
hibernate.show_sql = true
hibernate.dialect = org.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class = com.mysql.jdbc.Driver
hibernate.connection.url = jdbc:mysql://localhost/demo
hibernate.connection.username = hhp
hibernate.connection.password = 123456
hibernate.c3p0.min_size=5
hibernate.c3p0.max_size=20
hibernate.c3p0.timeout=1800
hibernate.c3p0.max_statements=50
您也可以使用Proxool或DBCP连接池,只要在配置文件中配置hibernate.proxool.*或hibernate.dbcp.*等相关选项,这可以在hibernate的etc目录中找hibernate.properties中的配置例子来参考,当然要记得在Classpath中加入相关的jar文件。
如果您使用Tomcat的话,您也可以通过它提供的DBCP连接池来取得连接,您可以先参考 使用 DBCP 的文章来设定Tomcat的DBCP连接池。
设定好容器提供的DBCP连接池之后,您只要在配置文件中加入connection.datasource属性,例如在 hibernate.cfg.xml中加入:
<property name="connection.datasource">java:comp/env/jdbc/dbname</property>
如果是在hibernate.properties中的话,则加入:
hibernate.connection.datasource = java:comp/env/jdbc/dbname
Session管理和缓存
数据库每一次的查询都是一次不小的开销,例如连结的开启、执行查询指令,当数据库与应用服务器不在同一个服务器上时,还必须有远程调用、Socket的建立等开销,在Hibernate这样的ORM框架中,还有数据的封装等开销必须考虑进去
缓存(Cache)是数据库在内存中的临时容器,从数据库中读取的数据在缓存中会有一份临时拷贝,当您查询某个数据时,会先在缓存中寻找是否有相对应的拷贝,如果有的话就直接返回数据,而无需连接数据库进行查询,只有在缓存中找不到数据时,才从数据库中查询数据,藉由缓存,可以提升应用程序读取数据时的效能。(需要注意在一些状态中,缓存中的数据可能与数据库中的不一致!)
对于Hibernate这样的ORM框架来说,缓存的机制更为重要,在Hibernate中缓存分作两个层级:Session level与SessionFactory level(又称Second level缓存)。
Session Level缓存
这边先介绍Session level的缓存,在Hibernate中Session level缓存会在使用主键加载数据或是延迟初始(Lazy Initialization) 时作用,Session level的缓存随着Session建立时建立,而Session销毁时销毁。
Session会维护一个Map容器,并保留与目前Session发生关系的资料,当您透过主键来加载数据时,Session会先依据所要加载的类别与所给定的主键,看看Map中是否已有数据,如果有的话就返回,若没有就对数据库进行查询,并在加载数据后在Map中维护。
可以通过==来比较两个名称是否参考至同一个对象,以检验这个事实:
Session session = sessionFactory.openSession();
User user1 = (User) session.load(User.class, new Integer(1));
User user2 = (User) session.load(User.class, new Integer(1));
System.out.println(user1 == user2);
session.close();
第二次查询数据时,由于在缓存中找到数据对象,于是直接返回,这与第一次查询到的数据对象是同一个实例,所以会显示true的结果。
可以通过evict()将某个对象从缓存中移去,例如:
Session session = sessionFactory.openSession();
User user1 = (User) session.load(User.class, new Integer(1));
session.evict(user1);
User user2 = (User) session.load(User.class, new Integer(1));
System.out.println(user1 == user2);
session.close();
由于user1所参考的对象被从缓存中移去了,在下一次查询时,Session在Map容器中找不到对应的数据,于是重新查询数据库并再封装一个对象,所以user1与user2参考的是不同的对象,结果会显示false。
也可以使用clear()清除缓存中的所有对象,例如:
Session session = sessionFactory.openSession();
User user1 = (User) session.load(User.class, new Integer(1));
session.clear();
User user2 = (User) session.load(User.class, new Integer(1));
System.out.println(user1 == user2);
session.close();
同样的道理,这次也会显示false。
Session level的缓存随着Session建立与销毁,看看下面这个程序片段:
Session session1 = sessionFactory.openSession();
User user1 = (User) session1.load(User.class, new new Integer(1));
session1.close();
Session session2 = sessionFactory.openSession();
User user2 = (User)session2.load(User.class, new Integer(1));
session2.close();
System.out.println(user1 == user2);
第一个Session在关闭后,缓存也关闭了,在第二个Session的查询中并无法用到第一个Session的缓存,两个Session阶段所查询到的并不是同一个对象,结果会显示false。
在加载大量数据时,Session level 缓存的内容会太多,记得要自行执行clear()清除缓存或是用evict()移去不使用对象,以释放缓存所占据的资源。
Session在使用save()储存对象时,会将要储存的对象纳入Session level缓存管理,在进行大量数据储存时,缓存中的实例大量增加,最后会导致OutOfMemoryError,可以每隔一段时间使用Session的 flush()强制储存对象,并使用clear()清除缓存,
例如:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
while(....) { // 大量加载对象时的循环示意
....
session.save(someObject);
if(count % 100 == 0) { // 每100条记录
session.flush(); // 送入数据库
session.clear(); // 清除缓存
}
}
tx.commit();
session.close();
在SQL Server、Oracle等数据库中,可以在Hibernate设定文件中设定属性hibernate.jdbc.batch_size来控制每多少条数据就送至数据库,例如:
....
<hibernate-configuration>
<session-factory>
....
<property name="hibernate.jdbc.batch_size">100</property>
....
</session-factory>
<hibernate-configuration>
注意:在MySQL中则不支持这个功能
事务管理简单说明
事务是一组原子(Atomic)操作(一组SQL执行)的工作单元,这个工作单元中的所有原子操作在进行期间,与其它事务隔离,免于数据来源的交相更新而发生混乱,事务中的所有原子操作,要么全部执行成功,要么全部失败(即使只有一个失败,所有的原子操作也要全部撤消)。
JDBC中的事务管理方式
在JDBC中,可以用Connection来管理事务,可以将Connection的AutoCommit设定为false,在下达一连串的SQL语句后,自行调用Connection的commit()来送出变更,如果中间发生错误,则撤消所有的执行,例如:
JDBC中的事务管理方式举例
try {
.....
connection.setAutoCommit(false);
.....
// 一连串SQL操作
connection.commit();
} catch(Exception) {
// 发生错误,撤消所有变更
connection.rollback();
}
Hibernate本身没有事务管理功能,它依赖于JDBC或JTA的事务管理功能,预设是使用JDBC事务管理,可以在配置文件中加上 hibernate.transaction.factory_class属性来指定Transaction的工厂类别,例如:
<hibernate-configuration>
<session-factory>
....
<!-- 设定事务管理的工厂类 -->
<property name="hibernate.transaction.factory_class">
org.hibernate.transaction.JDBCTransactionFactory
</property>
<!-- 对象与数据库表格映像文件 -->
<mapping resource=“com/cstp/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
基于JDBC的事务管理是最简单的方式,事实上,Hibernate基于JDBC的事务管理只是对JDBC作了个简单的封装:
try {
session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
....
tx.commit(); // 必须commit才会更新数据库
} catch(HibernateException e) {
tx.rollback();
}
在一开始的openSession()取得Session时,JDBC的Connection实例之AutoCommit就被设定为false,在 beginTransaction()时,会再度检查Connection实例的AutoCommit为false,在操作过程中,最后要commit (),否则的话对数据库的操作不会有作用,如果操作过程中因发生例外,则最后commit()不会被执行,之前的操作取消,执行rollback()可撤消之前的操作,一个实际的程序如下所示:
public class FirstHibernate {
public static void main(String[] args) {
Configuration config = new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
User user = new User();
user.setName(“zhangy");
user.setAge(new Integer(24));
Session session = null;
Transaction tx= null;
try {
session = sessionFactory.openSession();
tx = session.beginTransaction();
session.save(user);
tx.commit();
}
catch(Exception e) {
e.printStackTrace();
if(tx != null) {
try {
tx.rollback();
}
catch(HibernateException ee) {
ee.printStackTrace();
}
}
}
finally {
if(session != null) {
try {
session.close();
}
catch(HibernateException e) {
e.printStackTrace();
}
}
}
sessionFactory.close();
}
}
注意: 要使用MySQL中的事务处理,必须建立事务表类型的表格,例如InnoDB的表格:
CREATE TABLE user (
.....
....
) TYPE = InnoDB;
Hibernate映射文件
Hibernate 中将对象与数据库表格映射关系连接起来的是映射文件,通常以*.hbm.xml作为文件名称,映射文件可以手工编写,或是通过工具程序从数据库表格自动生成,或是通过工具程序从Java类别自动生成。
<hibernate-mapping>
<!--类别名称与表格名称映像-->
<class name=“com.cstp.User" table="user">
<!--id与主键映射-->
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<!--类别属性与表格字段的映像-->
<property name="name" column="name" type="java.lang.String"/>
<property name="age" column="age" type="java.lang.Integer"/>
</class>
</hibernate-mapping>
映射文件中主要包括三个部份:
类别名称与表格名称的映射、
id属性与主键的映射、
类别属性与表格字段的映射。
这份映像文件对应于以下的类别与表格:
User.java
public class User {
private Integer id;
private String name;
private Integer age;
// 必须要有一个预设的建构方法
// 以使得Hibernate可以使用Constructor.newInstance()建立对象
public User() {}
public Integer getId() {
return id;
}
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
在<id>的设定上,name设定类别上的属性名,而column对应至表格字段,在type上可以设定Java类别的数据类型,但由于 Java的数据类型与数据库的数据类型并不是一对一对应的,为此Hibernate提供它自己的数据类型,作为Java数据类型与数据库数据类型的连接类型,下面的表格列出类型之间的对应:
Java的数据类型与Hibernate的数据类型对应问题
Hibernate的主键(标识符)生成方法
l Hibernate自动生成主键
l Generator生成策略说明
l Hibernate复合主键
*.hbm.xml中需要说明主键生成方式
<generator>设定主键的生成方式,
可以设定“native”表示由Hibernate自动根据Dialect选择采用 identity、hilo、sequence等作为主键生成方式,
也可以考虑采用uuid由Hibernate根据128位UUID算法(128- bit UUID algorithm)生成16进位制数值,并编码为32位长度的字符串,
还有其它的主键生成方式,可以参考官方手册的 Generator 说明
数据库提供的主键生成机制
identity-采用数据库提供的主键生成机制
如DB2,SQL Server,MySQL,返回的是long,short,或int类型
sequence-采用数据库提供的sequence生成机制
如Oracle的sequence
外部程序提供的主键生成机制
increment-递增
hilo-高低位算法实现的
seqhilo
uuid.hex-用一个128位的UUID算法生成字符串类型的主键,UUID被编码为一个32位16进制数字的字符串
uuid.string-用一个128位的UUID算法生成字符串类型的主键,UUID被编码为一个32位16个字符长的任意ASCII字符组成的字符串
其它主键生成机制
native-根据底层数据库的能力选择identity,sequence,hilo任意一种
assigned-由程序在save()之前自己分配主键
foreign-外部引用,使用另外一个相关联的对象的标识符,和<one-to-one>一起联合使用
Generator生成器模板
<id
name = "propertyName"
type = "typeName"
column = "columnName"
unsaved-value="any|none|null|id_value"
access = "field"|property|ClassName">
<generator class = "generateClass">
</generator>
</id>
Generator生成器-Hilo举例
<id name = "id" type = "long" column = "uid" unsaved-value="0">
<generator class = "net.sf.hibernate.id.TableHiLoGenerator">
<param name="table"> uid_table </param>
<param name="column"> next_hi_value_column </param>
</generator>
</id>
<property
name=“propertyName”
column=“columnName”
type=“typeName”
>
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/welcomejzh/archive/2008/12/29/3638976.aspx