Hibernate的核心类和接口一共有6个,分别为:Session、SessionFactory、 Transaction、Query、Criteria和Configuration。这6个核心和类接口在任何开发中都会用到。通过这些接口,不仅可以对持久化对象进行存取,还能够进行事务控制。下面对这6个核心接口和类分别加以介绍。
Configuration类的作用是对Hibernate进行配置,以及对它进行启动。在Hibernate的启动过程中,Configuration 类的实例首先定位映射文档的位置,读取这些配置,然后创建一个SessionFactory对象。虽然Configuration类在整个Hibernate项目中只扮演着一个很小的角色,但它是启动hibernate 时所遇到的每一个对象。简而言之,它负责管理Hiberante的配置信息,它主要用来加载这些配置文件,如hibernate.cfg.xml文件。
//加载的配置文件
Configuration config = new Configuration().configure("/hibernate/hibernate.cfg.xml");
//还可以加载映射文件
Configuration config = new Configuration().addFile("TRegister.hbm.xml");//方法一
Configuration config = new Configuration().addClass(hibernate.PO.TRegister.class);//方法二
Configuration config = new Configuration().addURL(Configuration.class.getResource("TRegister.hbm.xml"));//方法三
SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。简而言之,SessionFactory负责Session实例的创建。可以通过Configuration实例创建。如下例子:
Configuration config = new Configuration().configure("/hibernate/hibernate.cfg.xml");
SessionFactory sessionFactory = config.buildSessionFactory();
Congifuration对象会根据当前的配置信息,生成SessionFactory对象。SessionFactory对象一旦构造完毕,即被赋予特定的配置信息,即以后配置改变不会影响到创建的SessionFactory对象。如果要把以后的配置信息赋给SessionFactory对象,需要从新的Configuration对象生成新的SessionFactory对象。SessionFactory是纯种安全的,可以被多线程调用以取得Session,而且构造SessionFactory很消耗资源,所以多数情况下一个应用中只初始化一个SessionFactory,为不同的线程提供Session。在实际应用中,当客户端发送一个请求线程时,SessionFactory生成一个Session对象来处理客户请求。
Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句。)。但需要注意的是Session对象是非线程安全的。同时,Hibernate的session不同于JSP应用中的HttpSession。这里当使用session这个术语时,其实指的是Hibernate中的session,而以后会将HttpSession对象称为用户session。
简而言之,Session是应用程序与数据库之间的一个会话,是Hibernate运作的中心,持久层操作的基础,相当于JDBC中的Connection。Session对象是通过SessionFactory创建的:
Session session = SessionFactory.openSession();
一个持久化类与普通的JavaBean没有任何区别,但是它与Session关联后,就具有了持久化能力。当然,这种持久化操作是受Session控制的,即通过Session对象的装载,保存,创建或查询持久化对象。Session类的save(),delete()和load()等方法,来分别完成对持久化对象的保存,删除,修改加载等操作!
Session类方法的用途可以分以下五类:
1:取得持久化对象:get()和load()等方法。
2:持久化对象的保存,更新和删除:save(),update()saveOrUpdate()和delete()等方法。
3:createQuery()方法:用来从Session生成的Query对象。
4:beginTransaction()方法:从Session对象生成一个Transaction对象。
5:管理Session的方法:isOpen(),flush(),clear(),evict()和close()等方法,其中isOpen()方法用来检查Session是否仍然打开;flush()用来清理Session缓存,并把缓存中的SQL语句发送出去,clear()用来清除Session中的所有缓存对象evict()方法来清楚Session缓存中的某个对象;close()关闭Session。
取得持久化对象的方法:
取得持久化对象的方法主要有get()和load(),它们通过主键id来取得PO。
public void testDemo(){
Session session = HibernateUtil.currentSession();//生成Session实例
TRegister tr = (TRegister)session.get(TRegister.class, new Integer(1));//get()方法
//TRegister tr = (TRegister)session.load(TRegister.class newInteger(1));//load()方法
System.out.print(tr.getUserName());
}
get()与load()的区别:
先来理解两个概念:
立即加载对象:当hibernate在从数据库中取得数据组装好一个对象后会立即再从数据库取得数据此对象所关联的对象;
延迟加载对象:Hibernate从数据库中取得数据组装好一个对象后,不会立即再从数据库取得数据组装此对象所关联的对象,而是等到需要时, 都会从数据库取得数据组装此对象关联的对象;
get()方法的执行顺序如下:
a):首先通过id在session缓存中查找对象,如果存在此id的对象,直接将其返回;
b):如果在session中查找不到,就在二级缓存中查找,找到后将 其返回;
c):如果在session缓存和二级缓存中都找不到此对象,则从数据库中加载有此ID的对象;
因此get()方法并不总是导致SQL语句,只有缓存中无此数据时,才向数据库发送SQL!
load()与get()的区别:
1:在立即加载对象时,如果对象存在,load()和get()方法没有区别,都可以取得已初始化的对象;但如果当对象不存在且是立即加载时,使用get()方法则返回null,而使用load()则抛出一个异常。因此使用load()方法时,要确认查询的主键ID一定是存在的,从这一点讲它没有get方便!
2:在延迟加载对象时,get()方法仍然使用立即加载的方式发送SQL语句,并得到已初始化的对象,而load()方法则根本不发送SQL语句,它返回一个代理对象,直到这个对象被访问时才被初始化。
持久化对象的保存,更新和删除方法
save()方法:
session的save()方法将一个PO的属性取出放入PreparedStatement语句中,然后向数据库中插入一条记录(或多条记录,如果有级联)。
session保存一个对象时,按如下步骤进行:
1:根本配置文件为主键id设置的生成算法,为PO指定一个ID。
2:将 PO对象纳入session内部缓存(一个Map)内。
3:事务提交时,清理缓存,将新对象通过insert语句持久化到数据库中。
如果要为新的PO强制指定一个ID,可以调用Session的重载方法
save(Objectobj,Serializable id)
例:
session.save(tRegister, new Integer(123));
在调用save()方法时,并不立即执行SQL语句,而是等到清理完毕缓存时才执行。如果在调用save()方法后又修改了PO的属性,则Hibernate将会发送一条insert语句和一条update语句来完成持久化操作,如下:
public ststic void main(String[] args){
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Person person=new Person();
/**为对象设定一个ID,但注意此ID是无效的,
*因为我们在配置文件中ID设置为Increment,
*这样将被Hibernate的increment算法生成的值覆盖
*/
person.setId(001);
person.setAge(23);
person.setSex("男");
person.setUserName("lmb");
person.setPassWord("lmb")
session.save(person);
person.setUserName("Tom");//在save()后又修改了PO的名字
tx.commit();
session.close();
}
监视上述程序运行会产生二条SQL:
Hibernate:select max(id) from t_register
Hibernate:insert into t_register (userName, userPwd, sex, age, id) values (?, ?, ?, ?, ?)
Hibernate:update t_register set userName=?, userPwd=?, sex=?, age=? where id=?
因此,最好是在对象状态稳定时再调用 save()方法,可以少执行一条update语句。
调用save()方法将临时对象保存到数据库中,对象的临时状态将变为持久化状态。当对象在持久化状态时,它一直位于Session缓存中,对它的任何操作在事物提交时都将同步保存到数据库中,因此,对一个已经持久化的对象调用save()或update()方法是没有意义的,如下:
public ststic void main(String[] args){
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Person person=new Person();
/**为对象设定一个ID,但注意此ID是无效的,
*因为我们在配置文件中ID设置为Increment,
*这样将被Hibernate的increment算法生成的值覆盖
*/
person.setId(001);
person.setAge(23);
person.setSex("男");
person.setUserName("lmb");
person.setPassWord("lmb")
session.save(person);
person.setUserName("Tom");//在save()后又修改了PO的名字
session.save(person);//无效
session.update();//无效
tx.commit();
session.close();
}
程序运行效果还是和上面的一样!
update()方法:
Session的update()方法是用来更新脱管对象的。
它的用法如下:
public void updateDemo{
public static void main(String[] args){
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Person person=new Person();
person=(Person)session.get(Person.class,new Integer(1));
person.setUserName("updated");//更显脱管对象
tx.commit();
}
}
调用update()方法时,并不是立即发送SQL语句,对对象的更新操作将积累起来,在事物提交时由flush()清理缓存,然后发送一条SQL语句完成全部的更新操作!
saveOrUpdate()方法:
在实际应用中WEB程序员自言自语不会注意一个对象是脱管对象还是临时对象,而对脱管对象使用save()方法是不对的,对临时对象使用update()方法也是不对的。为了解决这个问题,便产生saveOrUpdate()方法。
saveOrUpdate()方法首先会判断该PO是脱管对象还是临时对象,然后会调用合适的方法。那么saveOrUpdate()方法如何判断PO是临时对象还是脱管对象呢?当满足下载情况之一时,Hibernate就认定它是临时对象。
1:在映射表中为
元素设置了unsaved-valu属性,并且实体对象的ID取值和unsaved-value匹配(默认为null)(注意:int和long型的ID的unsaved-value默认值为0)。
2:在映射文件中为
元素设置了unsaved-value属性,并且实体对象的version取值和unsaved-value匹配(默认为null)。
delete()方法:
session类的delete()方法负责删除一个对象(包括持久对象和脱管对象)。
//删除持久对象
public void deleteDemo{
public static void main(String[] args){
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Person person=new Person();
person=(Person)session.get(Person.class,new Integer(1));
session.delete(person);//删除持久对象
tx.commit();
}
}
监视运行:
Hibernate: select tregister0_.id as id0_0_, tregister0_.userName asuserName0_0_, tregister0_.userPwd as userPwd0_0_, tregister0_.sex assex0_0_, tregister0_.age as age0_0_ from t_register tregister0_where tregister0_.id=?
Hibernate: delete from t_register where id=?
//删除脱管对象
public void deleteDemo2{
public static void main(String[] args){
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Person person=new Person();
person=(Person)session.get(Person.class,new Integer(1));
tx.commit();
session.close();
//再构建一个session
Session session2=sf.openSession();
tx=session2.beginTransaction();
session2.delete(person);//删除脱管对象
tx.commit();
session.close();
}
}
监视运行:
Hibernate: select tregister0_.id as id0_0_, tregister0_.userName asuserName0_0_, tregister0_.userPwd as userPwd0_0_, tregister0_.sex assex0_0_, tregister0_.age as age0_0_ from t_register tregister0_where tregister0_.id=?
Hibernate: delete from t_register where id=?
在上述代码中session2先把tr对象进行关联,纳入Session缓存中,然后删除。需要注意的是,在调用delete()方法时并不是发送SQL语句,而是在提交事务时,清理了缓存才发送SQL。使用delete()删除对象时,会有一些性能上的问题,例如从上面的监视中可以看到,当删除一个对象时,会先调用get()加载这个对象,然后调用delete()方法删除对象,所以发送了一个多余的selete SQL,所以当删除大量数据时对性能影响就比较大了。为了解决批量删除的性能问题,常用的办法是使用批量删除操作,如下:
//批量删除
public void betchDeleteDemo{
public static void main(String[] args){
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Query q=session.createQuery("delete from Person");
q.executeUpdate();//删除对象
tx.commit();
session.close();
}
}
监视运行:
Hibernate: deletefrom t_register只用了一条语句就完成了比量删除的操作。但它也有问题,批量删除后的数据还会存在缓存中,因此程序查询时可能得到脏数据(得到数据库中已不存在的数据),因此在使用批量删除时,也要经窒处世数据一致的问题。
Query接口让你方便地对数据库及持久对象进行查询,它可以有两种表达方式:HQL语言或本地数据库的SQL语句。Query经常被用来绑定查询参数、限制查询记录数量,并最终执行查询操作。在Hibernate3.x中不再使用2.x中的find方法,而是引入了Query接口,用来执行HQL。其实在上面的例子中已经看出,Query接口可以从session对象生成;
Query接口主要方法有三个:
setXXX()方法:用于设置HQL中问题或变量的值。
list()方法:返回查询结果,并把结果转换成List对象。
executeUpdate()方法:执行更新或删除名。
setXXX()方法都有二种重载方法:
1:setString(int position,String value):用于设置HQL中“?”的值:其中position表示?位置,而value自然就是值。如下:
Query query = session.createQuery("from Person person where person.age>? and person.userName like ?");//生成一个Query实例
query.setInteger(0, 18);//设置第一个问号的值为18
query.setString(1, "%lmb%");//设置第二个问题的值为%lmb%
2:setString(String paraName,Stringvalue);用于设置HQL中“:”后跟变量的值;其中paraName代表HQL中“:”后跟变量,value为该变量设置的值。如下:
Query query = session.createQuery("from Person person where person.age>:minAge and person.userName like :userName");//生成一个Query实例
query.setInteger("minAge", 18);//设置变量minAge值为18
query.setString("userName", "%lmb%");//设置变量userName值为%lmb%
在HQL中使用变量代替问号,然后在setXXX()方法中为该变量设值。在HQL中使用变量相比问号有以下好处:
1:变量不依赖于它们在查询字符串中出现 的顺序。
2:在同一个查询中可以多次使用。
3:可读性好。
list()方法:返回查询结果,并把结果转换成List对象。
public void listDemo{
public static void main(String[] args){
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session session = sf.openSession();
Person person=new Person();
Query query=session.createQuery("from Person");
java.util.list list=query.list();
for(int i=0;iget(i);
System.out.println(person);
}
}
}
executeUpdate()方法:执行更新或删除名。
Query的executeUpdate()方法用于更新或删除语句。它常用于批量删除或批量更新操作,如下:
Query q = session.createQuery("delete from Person");
q.executeUpdate();//删除对象
使用命名查询(namedQuery):
其实我们还可以不将 HQL写在程序中,而是写入映射文件中,这样在需要的时候很方便!在映射文件中使用
标记,把HQL语句放入
<hibernate-mapping>
<class name="hibernate.PO.Person" table="t_person">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="increment" />
id>
<property name="userName" type="java.lang.String">
<column name="userName" length="30" />
property>
<property name="userPwd" type="java.lang.String">
<column name="userPwd" length="30" />
property>
<property name="sex" type="java.lang.String">
<column name="sex" length="10" />
property>
<property name="age" type="java.lang.Integer">
<column name="age" />
property>
class>
<query name = "queryUser_byAgeAndName">
:minAge and user.userName like:userName]]>
query>
hibernate-mapping>
nameQueryDemo.java
public void nameQueryDemo{
public static void main(String[] args){
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session session = sf.openSession();
Person person=new Person();
Query query=session.getNameQuery("queryUser_byAgeAndName");
query.setInteger("minAge",18);
query.setString("userName","%x%");
List list=query.list();
for(int i=0;iget(i);
System.out.println(person.getUserName());
}
}
}
Transaction接口是一个可选的API,可以选择不使用这个接口,取而代之的是Hibernate的设计者自己写的底层事务处理代码。 Transaction 接口是对实际事务实现的一个抽象,这些实现包括JDBC的事务、JTA中的UserTransaction、甚至可以是CORBA事务。之所以这样设计是能让开发者能够使用一个统一事务的操作界面,使得自己的项目可以在不同的环境和容器之间方便地移植。简而言之,该接口允许应用等量齐观定义工作单元,同时又可调用JTA或JDBC执行事物管理。它的运行与Session接口相关,可调用Session的beginTransaction()方法生成一个Transaction实例。一个Session实例可以与多个Transaction实例相关联,但是一个特定的Session实例在任何时候必须与至少一个未提交的Transaction实例相关联。
Transaction接口常用如下方法:
1:commit();提交相关联的Session实例。
2:rollback();撤销事物操作。
3:wasCommitted();事物是否提交。