O/R Mapping基础
1、模型
(1)模型用来表示真实世界的实体
在科学和工程技术领域,模型是一个很有用途的概念,它可以用来模拟一个真实的系统。在软件开发领域,模型用来表示真实世界的实体。
(2)不同类型的模型
在软件开发的不同阶段,需要为目标系统创建不同类型的模型。
l 在分析阶段----需要创建概念模型
l 在设计阶段----需要创建域模型和数据模型
ü 其中,域模型是面向对象的,数据模型是面向关系的
ü 域模型和数据模型之间存在一种对象-关系映射。
(3)概念模型
l 概念模型清楚地显示了问题域中的实体
不管是技术人员还是非技术人员都能看得懂概念模型,他们可以很容易地提出模型中存在的问题,帮助分析人员及早对模型进行修改。
在软件设计域开发周期中,模型的变更需求提出得越晚,所耗费得开发成本就越大。
l 概念模型描述了每个实体的概念和属性,以及实体之间的关系:一对一、一对多和多对多
在现实生活中都可以找到相应的例子,例如一只母鸡有很多小鸡是一对多关系,一位客户选购了很多商品,而这些商品也可以被许多客户选购,这是多对多关系。
(4)关系数据模型
到目前为止,关系数据库仍然是使用最广泛的数据库,它存储的是关系数据。关系数据模型是在概念模型的基础上建立起来的,用于描述这些关系数据的静态结构,它由以下内容组成:
l 一个或多个表
l 表的所有索引
l 视图
l 触发器
l 表与表之间的参照完整性
(5)域模型
域模型由以下内容组成:具有状态和行为的域对象;域对象之间的关系。
2、域对象
(1)实体域对象
代表人、地点、事物或概念。通常,可以把业务领域中的名词,例如客户、订单、商品,当然也包括前面提到过的母鸡,作为实体域对象;
(2)过程域对象
代表应用中的业务逻辑或流程。它通常依赖于实体域对象。
(3)事件域对象
代表应用中的一些事件,例如异常、警告或超时等。
3、域对象之间的关系
在域模型中,类之间存在下面下面的的几种关系。
(1)关联(Association)
关联指的是类之间的引用关系(它使一个类指到另一个类的属性),这是实体域对象之间最普遍的一种关系。关联可以分为一对一、一对多和多对多关联。
(2)依赖(Dependency)
依赖指的是类之间的访问关系。如果类A访问类B的属性或方法,或者说是A负责实例化B,那么可以说类A依赖类B。
依赖对象通过调用被依赖对象的方法来获得服务。一种比较松散的关系,并且是短期的。我们的业务处理过程与对象往往依赖于我们的实体域对象。如在struts 的 action中调用模型层的方法。
(3)聚集(Aggregation)
聚集指的是整体与部分之间的关系,在实体域对象之间也很常见。其实聚合关系是关联关系的一种,是强的关联关系。聚合同时也体现整体和部分之间的关系。
例如,人与手就是聚集关系,在Person类中由一个hands集合,它存放被聚集的Hand对象:
public class Person
{
private Set hands = new HashSet();
…………
}
(4)组合
也叫合成关系,组成关系是关联关系的一种,是比聚合关系强的关系。对象负责代表部分的对象的生命周期。
(5)一般化(Generalization)
一般化指的是类之间的继承关系。
4、既然聚合,组合关系属于关联关系,那么如何区分一般关联关系,聚合关系和组合关系呢?
(1)一般关联
只要一个对象联系到另外一个对象就形成了关联关系。如:人和他的猫,黑豹乐队和窦魏,pc机和显示器。
(2)聚合关系
一种强关联关系,它要求有部分和整体的关系,并且没有了整体,部分也可以独立存在。
在上面例子中人和它的猫显然没有部分和整体的关系,所以只能是一般的关联关系。而黑豹乐队和窦魏,窦魏等人组成,了黑豹乐队即:窦魏和黑豹是整体和部分的关系。而窦魏脱离了黑豹(早就离开了)更或者黑豹不存在了那么窦魏仍然可以以音乐人的身份存在(即对象仍然可以独立存在)所以它属于聚合关系。
组成关系是可以共享的。(窦魏也可以加入其他乐队)。
(3)组合关系
一种更强的整体和部分的关系。它并且要求代表整体的对象负责代表部分的对象的生命周期,组成关系是不能共享的。如:pc机和显示器的关系。
(4)注意的问题
如果两个实体是整体和部分的关系,那么它们到底是聚合还是组合,这取决于你的需求。比如说:pc机和显示器的关系,如果你的系统中,显示器脱离了pc机就不存在意义了也可以说:所有显示器的访问都是通过pc机进行的,那么你可以把关系设定为组合(如你在为一个只买品牌机的代理商作系统你可能是可以这么作的)。
如果你的显示器脱离的pc机仍然可以独立存在,也就是说在系统中可以直接访问显示器对象,那么你可以将关系设为聚合(如你在为一个买散件的代理商作系统你可能是可以这么作的)
5、域对象的持久化概念
(1)什么是持久化
狭义的理解,“持久化”仅仅指把域对象永久保存到数据库中;广义的理解,“持久化”包括和数据库相关的各种操作。
(2)为什么要进行持久化
当实体域对象在内存中创建后,它们不可能永远存在。最后,他们要么从内存中清除,要么被持久化到数据存储库中。内存无法永久地保存数据,因此必须对实体域对象进行持久化。否则,如果对象没有被持久化,用户在应用运行时创建地订单信息将在应用结束运行后随之消失。
(3)什么域对象需要持久化
当然,并不是所有的域对象都需要持久化,通常只有实体域对象才需要持久化,另外,有些实体域对象也不需要持久化。
(4)持久化类
持久化类是指其实例需要被Hibernate持久化到数据库中的类。持久化类通常都是域模型中的实体域类,同时持久化类符合JavaBean的规范,包含一些属性,以及与之对应的getXXX()和setXXX()方法。
6、数据访问有哪些模式
(1)业务逻辑层包含了业务数据和业务过程
l 实体域对象
在分层的软件结构中,业务逻辑层代表了业务数据和业务逻辑。域对象位于业务逻辑层,实体域对象代表应用运行时的业务数据,它存在于内存中,并借助于数据库实现用于存放永久性的业务数据。
业务数据在内存中表现为实体域对象形式,而在关系数据库中表现为关系数据形式。数据访问代码负责把实体域对象持久化到关系数据库中。
l 过程域对象
过程域对象代表应用的业务逻辑。
(2)数据访问模式----业务逻辑和数据访问耦合
在此种模式种的过程域对象中,业务逻辑和数据访问代码混杂在一起,参见下图。
(3)数据访问模式----主动域对象模式
由实体域对象负责自身的数据访问细节,这种实体域对象也被称为主动域对象,参见下图种的说明。J2EE平台中的BMP EJB组件就是采用主动域对象模式的一种应用示例。
(4)数据访问模式----ORM模式
在此种模式中,采用在单独的持久化层由ORM中间件封装数据访问细节,参见下图。而ORM中间件提供对象---关系映射服务,当向数据库保存一个域对象时,把业务数据由对象形式映射为关系数据形式;当从数据库加载一个域对象时,把业务数据由关系数据形式映射为对象形式。
(5)数据访问模式---- JDO模式
Java Data Objects(JDO)是SUN公司制定的描述对象持久化语义的标准API。因此采用JDO模式时,整个应用为四层应用结构,参见下图所示。
严格的说,JDO并不是对象-关系映射接口,因为它支持把对象持久化到任意一种存储系统中,包括: 关系数据库和面向对象的数据库。
(6)数据访问模式----CMP模式
在J2EE架构中,CMP(Container-managed Persistence)表示由EJB容器来管理实体EJB的持久化,EJB容器封装了对象-关系的映射以及数据访问细节。
CMP与ORM的相似之处在于,两者都提供对象-关系映射服务,都把对象持久化的任务从业务逻辑程序中分离出来;区别在于CMP负责持久化实体EJB组件,而ORM负责持久化POJO,它是普通的基于Java Bean形式的实体域对象。
CMP和ORM相比,前者有以下不足:
l 开发人员开发的实体EJB必须遵守复杂的J2EE规范,而多数ORM中间件不强迫域对象必须满足特定的规范。
l 实体EJB只能运行在EJB容器中,而POJO可以运行在任何一种Java环境中。
l 目前,对于复杂的域模型,EJB容器提供的对象-关系映射能力很有限。相比之下,许多ORM中间件提供了完善的对象-关系映射服务。
l 尽管按照J2EE的规范,EJB应该是一种可移植的组件,实际上却受到很大限制。因为不同厂商生产的CMP引擎差异很大,它们使用的对象-关系映射元数据各不相同,使得EJB不能顺利的从一个EJB容器移植到另一个EJB容器中。
使用ORM中间件就不存在这样的问题,以Hibernate为例,它可以无缝集成到任何一个Java系统中。在Java软件架构领域,在出现基于CMP的实体EJB之前,基于JavaBean形式的实体域对象早就存在了。但是把基于JavaBean形式的实体域对象称为POJO,却是最近才发生的事。
POJO(Plain Old Java Object----又普通又古老的Java对象),它和基于CMP的实体EJB相比,既简单,又具有很高的可移植性,因此联合使用ORM映射工具和POJO,已经成为一种越来越受欢迎的,用于取代CMP的持久化方案。
7、Java平台中的各种数据持久化技术
大多数应用程序都需要处理数据。Java应用程序运行时,往往把数据封装为相互连接的对象网络,但是当程序结束时,这些对象就会消失在一团逻辑中,所以需要有一些保存它们的方法。
有时候,甚至在编写应用程序之前,数据就已经存在了,所以需要有读入它们和将其表示为对象的方法。手动编写代码来执行这些任务不仅单调乏味、易于出错,而且会占用整个应用程序的很大一部分开发工作量。
(1)JDBC
大多数Java开发员都是用JDBC来和数据库进行通信,它可以通过DAO(Data Access Object)模式来进行改善和提高,然而,这种方式在大型应用程序中则会造成维护的"高消费"。JDBC 提供了还算不错的数据库抽象,但是需要用痛苦的API。这些问题包括:
l 需要冗长的错误处理代码来确保ResultSets,Statements以及(最重要的)Connections在使用后关闭。这意味着对JDBC的正确使用可以快速地导致大量的代码量。它还是一个常见的错误来源。Connection leak可以在有负载的情况下快速宕掉应用程序。
l SQLException相对来说不能说明任何问题。JDBC不提供异常的层次,而是用抛出SQLException来响应所有的错误。找出到底哪里出错了——例如,问题是死锁还是无效的SQL?——要去检查SQLState或错误代码。这意味着这些值在数据库之间是变化的。
(2)EJB
据调查EJB通常是在数据持久技术上的第二个选择,它是通过EntityBeans来对数据进行持久化。但我们首先必须购买一个价位合理的EJB容器--J2EE应用服务器;
其次全面采用EntityBean需要花“大量”的时间来理解EJB规范。在采用EJB之前你通常想在熟练掌握它的API;
再有就是,你需要知道在每一个容器除了ejb-jar.xml以外所专有的部署描述符。对于JAVA开发员,在EJB中实现JDBC也比较复杂。
EJB中最为被关注的可能是无状态的会话BEAN(stateless-Session beans)和消息驱动BEAN(messaging driver beans)
(3)Hibernate技术
如何实现使这些工作自动化?如果每一种数据库管理系统都有它自己的方言,我们如何达到可移植性?答案是使用Hibernate技术
Hibernate技术的目标是成为Java中管理持续性数据问题的一种完整的解决方案。它协调应用与关系数据库的交互,让开发者解放出来专注于手中的业务问题。
Hibernate是一种非强迫性的解决方案。我们的意思是指在写业务逻辑与持续性类时,不会被要求遵循许多Hibernate特定的规则和设计模式。这样,Hibernate就可以与大多数新的和现有的应用平稳地集成,而不需要对应用的其余部分作破坏性的改动。
8、ORM技术
(1)什么是ORM?
简单地说,对象-关系映射就是Java应用中的对象到关系数据库中的表的自动的(和透明的)持续化,使用元数据对对象与数据库间的映射进行了描述。本质上,ORM的工作是将数据从一种表示(双向)转换为另一种。
(2)ORM解决方案有以下四部分组成
l 在持续类的对象上执行基本的CRUD操作的一组API。
l 用于指定查询的一种语言或一组API,这些查询会引用类和类属性。
l 用于指定映射元数据的工具。
l 实现ORM的一项技术,用来与事务对象交互以完成脏检查、懒关联存取和其它优化功能。
O/R Mapping工具----Hibernate技术基础
1、Hibernate基本概念
(1)Hibernate技术本质上是一个提供数据库服务的中间件
l Hibernate 是一个面向Java 环境的对象/ 关系数据库映射工具,把Java类对应到数据库的table中,并且采用了xml技术、Java Reflection技术等。
l Hibernate技术本质上也是遵守的ODMG标准的,它的出色源于不断的升级、不断的维护、修改。以及完善的文档、人气很旺的论坛。还有很重要的一点就是它的易学易用性。
l Hibernate它不仅提供了从Java类到数据表的映射,也提供了数据查询和恢复等机制。可以大幅度减少开发时人工使用SQL 和JDBC 处理数据的时间。
(2)Hibernate是一个开放源代码的对象关系映射框架
l 以OO的方式来操纵数据库
它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
l Hibernate可以应用在任何使用JDBC的场合
Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序实用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。
try
{
stmt=con.createStatement();
rs=stmt.executeQuery("select * from student");
metaData = rs.getMetaData();
for(int column = 0; column < metaData.getColumnCount(); column++)
{
System.out.print(metaData.getColumnName(column+1)+"/t");
}
System.out.println("");
while(rs.next())
{
for (int i = 1; i <= metaData.getColumnCount(); i++)
{
System.out.print(rs.getObject(i)+"/t");
}
System.out.println("");
}
stmt.close(); //must close
con.close();
}
catch(SQLException e)
{
}
(3)主要的特点及与Entity EJB Bean的不同点
l Hibernate是一个可以与Entity Bean同台较劲的技术,能够提供强大的Object/Relational Mapping能力,不需要透过任何J2EE的容器(Container),就能为我们提供Entity Bean的功能,但是又沒有Entity Bean的高复杂度。
l 实际上Hibernate与诸如EJB容器这样的环境在持久层实现的方式上有很大的不同。我们的持久类可以用在与容器无关的环境中,不像EJB必须要有某种EJB容器并且依赖该容器才能执行
注意:
无论是基于CMP的实体EJB,还是基于BMP的实体EJB,它们的共同特点是都必须运行在EJB容器中。而Hibernate支持的持久化类不过是普通的Java类,它们能够运行在任何一种Java环境中。
l 可以看出Hibernate 的最大特点就是数据持久化。
(4)应用层和持久层的关系
l 持久(Persistence)。简单来讲,也就是把数据保存到可掉电式存储设备中供之后使用。
l 所谓“持久层”,也就是在系统逻辑层面上,专注于实现数据持久化的一个相对独立的领域(Domain)。
l 我们一般将应用层放在持久层的上部,实际上在传统的项目中,应用层充当着持久层的一个客户端角色。但对于一些简单的项目来说,应用层和持久层并没有区分得那么清楚,在这种情况下可以将应用层和持久层合并成为一层。
(5)持久层设计与解耦合
l 何谓耦合:通俗地讲,就是事物之间的相互关联关系。
l 何谓解耦合:即采用一些手段降低关联的紧密程度。
l 解耦合设计的目标
ü 应用层解耦合――应用逻辑与数据逻辑相分离
ü 资源曾解耦合――逻辑结构与物力结构相分离
(6)利用Hibernate技术不仅提高了编码的效率,同时也提高了数据库访问性能优化
l 提高了编码的效率
Hibernate是一个ORM工具,它不仅仅是实现了数据库访问性能优化和与数据库交互的常用操作(CRUD),还将数据表与对象进行了关联,让我们可以脱离数据表,而直接针对对象来与数据库交互,我们不再需要用字符串去描述表中字段,不再需要一个个”+“号去 组装Sql语句。这使得编码中可书写性提高。
l 提高了数据库访问性能优化
同时也不需要我们考虑与数据库交互的性能问题,如连接池、数据缓存等等
2、为什么要使用Hibernate工具
(1)为应用系统提供统一的一致的数据库访问操作
l JDBC技术
在常规的Java应用系统中的数据库访问技术使用的是JDBC并且进行JDBC编程。当项目越来越大时,觉得JDBC到处建立连接,或者使用直接的sql语句,修改或者项目变更起来很不方便;于是产生了数据访问的程序的应用需求,所有的数据访问都通过这个程序转换为JDBC的代码来访问数据库。
l 使用Hibernate API访问数据库
下图的左面是通过JDBC API访问数据库,而右图则是通过Hibernate API访问数据库。
l 使用Hibernate API访问数据库的基本的过程
首先应该在应用的启动阶段对Hibernate进行初始化,然后就可以通过Hibernate的Session接口来访问数据库。
l 优点
这种技术的采用确实给开发工作带来了十分的改观,所有的对数据库的操作变成了对某个对象的操作,并且采用Java语言特有的Reflection机制,动态的取得某个JavaBean的属性,然后对应到(mapping)某个数据库的表中,这样,客户端的代码就变得非常简单。
(2)Hibernate 的最大特点就是数据持久化
l 提高开发效率
Hibernate 不仅仅管理Java 类到数据库表的映射,还提供数据查询和获取数据的方法,可以大幅度减少开发时人工使用SQL 和JDBC 处理数据的时间;同时Hibernate封装了数据库访问、事务管理、数据缓存等工作。省去了自己去编写这些代码。
l 可以看出Hibernate 的最大特点就是数据持久化。
(3)不用写一句SQL语句,甚至不需要懂得SQL语言!
在应用Hibernate时,只要我们定义好了持久类与数据库中表字段的对应关系,Hibernate会自动帮你生成SQL语句来对持久类对象进行插入、更新、删除、查找工作,你可以不写一句SQL语句,甚至不需要懂得SQL语言!
l 传统的数据库访问代码
insert into table (field_1,field_2,field_n) values('"+ field_value1 +"','" + field_value2 + "','" + field_value3 + "'")
l 借助Hibernate后数据库访问代码
session.save(table_Object)
由代码比较可以看出,数据表可以跟对象一样被操作,这样代码显得更加简洁,可读性也增强。在实际开发中,这里是业务变动 频繁的地方,保证代码的可读性和易维护,很有价值。
(4)支持各种主流的数据源
目前所支持数据源包括:HypersonicSQL、PostgreSQL、DB2、MySQL、Oracle、Sybase、Mckoi SQL、SAP DB、MS SQL Server、JSQL Driver、JTURBO Driver、WebLogic Driver、纯Java驱动程序等。
(5)Hibernate的一些弱点
Hibernate作为流行的企业应用和关系数据库之间的持久化中间件,受到越来越多的关注。虽然使用Hibernate可以使得项目易于维护,帮助开发人员更好地处理复杂关系模型,提供了很强的方便性,但却失去了JDBC原有的灵活性。如何在“灵活”与“方便”之间取舍、平衡显得重要起来。
主要体现在比如对象关系的映射配置过多,控制复杂;事务处理面临各种兼容问题;HQL语言增加学习成本等。也就是“灵活”的背后隐藏着“复杂”,“方便”的背后隐藏着“不便”,如何取舍与平衡,还是看实际需要。
l 提取表单中字典Value的不便。
字典一般由ID和NAME两个字段组成,其ID号存储于数据库其他表中,当查询这些表信息时,Hibernate以List或Set形式返回的结果,没有办法将ID号显示为对应的NAME。在JDBC中,可以直接通过Map来存储字典,通过map.getValue()来返回字典的值。
l Hibernate内置映射类型复杂化
在开发过程中,时常会查找Hibernate映射类型--Java类型--标准SQL类型之间的关系。繁杂之处体现在两方面,一是各种数据库的数据类型和标准SQL之间会有一定的出入,二是Hibernate映射类型虽然大部分和Java类型相同,但也存在比较晦涩的地方,例如character类型对应Java的char / java.lang.Character / java.lang.String,text对应着Java的java.lang.String。
l ID规定化生成
Hibernate中内置标识符生成器给表单ID自动生成提供了方便,但却不能自定义各种ID形式。
3、Hibernate系统
其实Hibernate本身是个独立的框架,它不需要任何web server或application server的支持。
(1)Hibernate的设计者Gavin King
Gavin King是Hibernate的作者,EJB3.0专家委员会成员,JBoss核心成员之一。Hibernate诞生在2001年11月。Gavin King 1974年出生于澳大利亚,现在居住在澳大利亚默尔本市。
(2)下载
hibernate是sourceforge的一个子项目,我们可以从Hibernate 的网站http://www.hibernate.org/(Hibernate 的中文网站是http://www.hibernate.org.cn/)或者http://hibernate.sourceforge.net上面获得下载的连接。
(3)架构
l 上图显示了hibernate的工作原理
它是利用数据库以及其他一些配置文件如hibernate.properties,XML Mapping等来为应用程序提供数据持久服务的。
l Hibernate是Java应用和关系数据库之间的桥梁
它负责Java对象和关系数据之间的映射。Hibernate内部封装了通过JDBC访问数据库的操作,向上层应用提供了面向对象的数据访问API。
(4)“轻型”的体系结构方案
由于Hibernate非常灵活,且支持数种应用方案,。“轻型”的体系结构方案,要求应用程序提供自己的JDBC 连接并管理自己的事务。这种方案使用了Hibernate API的最小子集。
(5)“全面解决”的体系结构方案
它是将应用层从底层的JDBC/JTA API中抽象出来,而让Hibernate来处理这些细节。
(6)几个主要的工具功能
l hbm2java是根据映射文件自动生成java源文件
l hbm2ddl 是根据映射文件自动生成数据库schema
l XDoclet 根据带有XDoclet的标记的java源文件生成映射文件
l Middlegen 根据数据库Schema自动生成映射文件
4、Hibernate API中包含的一些主要的类
(1)Hibernate的接口大致可以分为以下几种类型:
l 一些被用户的应用程序调用的,用来完成基本的创建、读取、更新、删除操作以及查询操作的接口。这些接口是Hibernate实现用户程序的商业逻辑的主要接口,它们包括Session、Transaction和Query等。
l Hibernate用来读取诸如映射表这类配置文件的接口,典型的代表有Configuration类。
l 回调(Callback)接口。它允许应用程序能对一些事件的发生作出相应的操作,例如Interceptor、Lifecycle和Validatable都是这一类接口。
l 一些可以用来扩展Hibernate的映射机制的接口,例如UserType、CompositeUserType和IdentifierGenerator。这些接口可由用户程序来实现(如果有必要)。
Hibernate使用了J2EE架构中的如下技术:JDBC、JTA、JNDI。其中JDBC是一个支持关系数据库操作的一个基础层;它与JNDI和JTA一起结合,使得Hibernate可以方便地集成到J2EE应用服务器中去。
(2)Hibernate一共包括了几十个jar包,令人眼花缭乱
下文将讲解Hibernate主要的jar包的作用,便于你在应用中根据自己的需要进行取舍。
l SessionFactory (net.sf.hibernate.SessionFactory):包含已经编译的映射(mappings),是制造session的工厂,可能含有一些可以在各个事务(transaction)之间共享的数据。
l Session (net.sf.hibernate.Session) :单线程的,短寿命的对象,代表了一次会话的过程。实际上是把一个JDBC Connection打包了,它可以包含一些持久化对象的缓存。
l Persistent Objects and Collections:单线程的,短寿命的对象,包含了各种属性字段以及一些商业逻辑,它们可能仅仅是一些普通的javabeans,但是它必然在某个时刻只能和一个session相关联。
5、有关Hibernate的中文资源
(1)中文论坛(http://www.hibernate.org.cn)
Hibernate中文论坛是中国最早的Hibernate专业用户论坛,为Hibernate在中国的推广做出了巨大的贡献,目标是成为一个高品质的,有思想深度的、原创精神的Java技术交流网站,为软件从业人员提供一个自由的交流技术,交流思想和交流信息的平台。
(2)中文参考书
《深入浅出Hibernate》是国内第一本围绕Hibernate3进行讲解的技术书籍。
6、Hibernate的应用场合---Hibernate可以配置成可在任何Java环境中运行
Hibernate可以配置成可在任何Java环境中运行,一般说来,它通常被用在2-3层的C/S或者B/S模式的项目中,并被部署在服务端。
在这种项目中,Web浏览器、或Java GUI程序充当者客户端。尽管我们的焦点主要是集中在多层Web应用,但实际上在一些基于命令行的应用中也可以使用Hibernate。
7、Hibernate运行在两种环境下:受管理环境(Managed environment)和不受管理环境(Non-managed enviroment)
(1)受管理环境――应用系统的整个执行过程在容器的控制之下
此时容器可为应用系统管理如下资源:池资源管理,诸如数据库连接池、还有事务管理、安全定义。一些典型的J2EE服务器(JBoss、Weblogic、WebSphere)已经实现了这些。
(2)不受管理环境――应用系统的整个执行过程不在容器的控制之下
由应用本身负责管理数据库连接、定义事务边界以及管理安全。独立的桌面应用或命令行应用都运行在不受管理环境中。Servlet容器会负责管理线程池,有些Servlet容器,如Tomcat,还会管理数据库连接池,但是Servlet容器不会管理事务,因此它提供的仍然是不受管理的运行环境。
(3)Hibernate允许Java应用在不同的环境中移植
当Java应用从一个环境移植到另一个环境中时,只需要修改Hibernate的配置文件,而不需要修改或者只需要修改极少量的Java源代码。
8、如何学习Hibernate
(1)Hibernate文档
Hibernate文档处处都是持久层设计的经验和最佳实践。Hibernate文档准确的来说,绝大部分内容都在讲对象的持久层设计,而不是简单的Hibernate使用。
(2)学习要点
l 所以学习Hibernate,主要是在学习持久层的设计模式,如果你把Hibernate文档都看完了,还整天只会提那些 Hibernate的配置问题,Hibernate的类调用问题,我觉得这样的人还没有真正的入门,算是白学了
l 在学习Hibernate的时候即集中所有精力来理解Hibernate的运行原理,集中精力来掌握持久层设计应该把握的原则和技巧,这些才对我是最重用的东西----而如果在学习Hibernate时间,主要侧重于怎么配置,用工具怎么生成hbm文件,如果你把重点放在这里,基本上等于白学了Hibernate。
因为,如果这样进行学习,学习完Hibernate,肯定会对JDBC的编程也提高了一大截,更不要说对于J2EE架构的持久层的框架设计,基本上是了然于胸了,即使将来换了API,不用Hibernate的,改用JDO,Castor什么的,这些经验一样照用。
(3)Hibernate的精华:在于无与伦比的灵巧的对象持久层设计
这些持久层设计经验不会因为你不用Hibernate而丧失掉,我自己学习Hibernate,已经明显感觉到对持久层设计能力已经长了很多经验值了,这些经验甚至不光可以用在Java上,用在.net上也是一样。
因为,不管JDO也好,Hibernate也好,TopLink也好,CocoBase也好,还是Castor,还是什么Torque,OJB,软件的使用和配置方法可以各异,但本质上都是ORM,都是对JDBC的对象持久层封装,所以万变不离其宗。
9、持久化生命周期
(1)持久化类的实例的生命周期状态
对于持久化生命周期,不同的ORM 实现使用不同的术语,定义不同的状态及状态转换。Hibernate 仅仅定义了三种状态:瞬时、持久和脱管,对客户端代码隐藏了其内部实现的复杂性。
一个持久化类的实例可能处于三种不同状态中的某一种。这三种状态的定义则与所谓的持久化上下文(persistence context)有关---而 Hibernate的Session对象就是这个所谓的持久化上下文。
当应用程序通过new语句创建了一个对象,这个对象的生命周期就开始了,当不再有任何引用变量引用它,这个对象就结束生命周期,它占用的内存就可以被JVM的垃圾回收器回收。对于需要被持久化的Java对象,在它的生命周期中,可处于以下三个状态之一。下面是三种状态的切换过程:
从图中可以看出,Session的特定方法触发Java对象由一个状态转换到另一个状态;当Java对象处于临时状态或游离状态,只要不被任何变量引用,就会结束生命周期,它占用的内存就可以被JVM的垃圾回收器回收;当处于持久化状态,由于Session的缓存会引用它,因此它始终处于生命周期中。
(2)瞬态(transient,临时)
l 瞬时对象
在Hibernate中,使用new 操作符初始化的对象(或者使用Session的delete方法使游离态,持久化的对象也转变为临时状态)不是立刻就是持久的。它们的状态是瞬时的,也就是说它们没有跟任何数据库表的行相关联,只要应用不再引用这些对象(不再被任何其它对象所引用),它们的状态将会丢失。这时,那些对象的生命期将会有效地终止,变得不可访问,交给垃圾回收机制来回收。
l 特点
它也称为临时对象 ----不处于session的缓存中,不和任何的session实例关联,数据库中无记录。该实例从未与任何持久化上下文关联过。它没有持久化标识(相当于主键)。 例如:
Order order = new Order();
l 瞬时实例都是非事务的
Hibernate 认为所有的瞬时实例都是非事务的,瞬时实例状态的修改不能在任何事务的上下文中执行。这就意味着Hibernate 不能对瞬时对象提供任何回滚功能----实际上Hibernate 不回滚任何对象所做的修改。
l 如何将对象从瞬时状态转换为持久状态
默认地,仅仅被其他瞬时实例引用的对象也是瞬时的。把实例从瞬时状态转换为持久状态有两种方式:调用持久管理器的save()方法或者从已经存在的持久实例中创建引用。
(3)持久(persistent)
l 持久实例
持久实例是任何具有数据库标识的实例,也就是持久实例有一个主键值设为数据库标识符。
l 特点
ü 持久实例目前与某个持久化上下文有关联
它拥有持久化标识(相当于主键),并且可能在数据库中有一个对应的行(位于session的缓存中,总是被一个session实例关联,和数据库中记录对应)。
ü 持久实例通常是同Session相关的,是事务的
持久实例是在事务中进行操作的—它们的状态在事务结束时同数据库进行同步。当事务提交时,通过执行SQL的INSERT、UPDATE和DELETE语句把内存中的状态同步到数据库中。
l 如何将对象转变为持久实例
对于某一个特定的持久化上下文,Hibernate保证持久化标识与Java标识(其值代表对象在内存中的位置)等价。
ü session的save方法使临时态变为持久化
ü session的load,get方法返回对象总是处于持久化状态
ü session的find方法返回list存放的都是持久化对象
ü session的update,saveOrUpdate,lock方法使游离态转换为持久态对象
ü 一个持久化对象和临时对象有级联保存的时候session在清空缓存的时候会把临时对象转化为持久对象
l 注意点
持久实例通过调用持久管理器API 的delete()方法使其变成瞬时的,导致删除数据库中相应的行。
(4)脱管(detached或者称为“游离态”)
l 脱管对象
在Hibernate 中,当实例曾经与某个持久化上下文发生过关联,不过那个上下文Session被关闭了, 或者这个实例是被序列化(serialize)到这个进程来的----不位于session的缓存中,不被session关联,数据库中可能有记录。
l 特点
它拥有持久化标识,并且在数据库中可能存在一个对应的行。
l 转换的方法
session的close方法使缓存清空,缓存中的所有持久化对象变为游离态
l 注意点
对于脱管状态的实例,Hibernate不保证任何持久化标识和Java标识的关系。
10、Hibernate中的核心接口API类的介绍--- Session接口
以下的各个个核心接口几乎在任何实际开发中都会用到。通过这些接口,你不仅可以存储和获得持久对象,并且能够进行事务控制。
(1)它是轻量级的类
l 轻量级的类
对于Hibernate 开发人员来说它是一个最重要的接口。然而在Hibernate中,实例化的Session是一个轻量级的类,创建和销毁它都不会占用很多资源。这在实际项目中确实很重要,因为在客户程序中,可能会不断地创建以及销毁Session对象,如果Session的开销太大,会给系统带来不良影响。
l 是非线程安全的
但值得注意的是Session对象是非线程安全的,因此在你的设计中,最好是一个线程只创建一个Session对象。
(2)Session看作介于数据连接与事务管理一种中间接口
l 是一种中间接口
在Hibernate的设计者的头脑中,他们将session看作介于数据连接与事务管理的一种中间接口。我们可以将session想象成一个持久对象的缓冲区,Hibernate能检测到这些持久对象的改变,并及时刷新数据库。
l 每一个Session实例和一个数据库事务绑定
通常将每一个Session实例和一个数据库事务绑定,也就是说,每执行一个数据库事务,都应该先创建一个新的Session实例。如果事务执行中出现异常,应该撤销事务。不论事务执行成功与否,最后都应该调用Session的close()方法,从而释放Session实例占用的资源。
(3)如何获得Session对象
l 首先创建SessionFactory对象
应用程序如果访问多个数据源时,则应该产生多个SessionFactory;但是仅仅为了服务于某个请求你不要创建一个新的SessionFactory,因为创建SessionFactory 需要耗费大量的资源。
l 然后根据SessionFactory再创建Session对象
Session session = sessionFactory.openSession();
Transaction tx;
try
{ //开始一个事务
tx = session.beginTransaction();
//执行事务
...
//提交事务
tx.commit();
}
catch (Exception e)
{ //如果出现异常,就撤销事务
if (tx!=null)
{
tx.rollback();
}
throw e;
}
finally
{ //不管事务执行成功与否,最后都关闭Session并且放在finally中以提高安全性
session.close();
}
(4)Session同时也是一个持久层管理器
l 持久层管理器
我们有时也称Session是一个持久层管理器,因为它包含这一些持久层相关的操作,诸如存储持久对象至数据库,以及从数据库从获得它们。
l 持久管理器所提供的服务
ü 基本的CURD操作(save()方法:把Java对象保存数据库中、update()方法:更新数据库中的Java对象、delete()方法:把Java对象从数据库中删除、load()方法:从数据库中加载Java对象 和find()方法:从数据库中查询Java对象);
ü 执行查询;
ü 控制事务;
ü 事务级的缓存管理;
l 持久管理器所暴露的几个不同的接口
就Hibernate 来说是----Session,Query,Criteria 和Transaction等接口。
l 所应该注意点
请注意,Hibernate 的session不同于JSP应用中的HttpSession。当我们使用session这个术语时,我们指的是Hibernate中的session,而我们以后会将HttpSesion对象称为用户session。
(5)Session的编程规则---需要随时更新和释放
Session是一个轻量级对象。通常将每一个Session实例和一个数据库事务绑定,也就是说,每执行一个数据库事务,都应该先创建一个新的Session实例。如果事务执行中出现异常,应该撤销事务。
不论事务执行成功与否,最后都应该调用Session的close()方法,从而释放Session实例占用的资源。
(6)Sessin 接口中的常用方法
l save()方法:save() 和persist()方法产生SQL INSERT
session = HibernateUtil.currentSession();
tx = session.beginTransaction();
Book oneBook = new Book();
oneBook.setBookName(new String("J2EE应用开发".getBytes("gb2312"), "ISO8859-1"));
oneBook.setBookKind('1');
oneBook.setBookPrice(7.4f);
session.save(oneBook);
tx.commit();
l Sessin 接口中的常用方法---- updata()和merge()方法产生SQL UPDATE
该方法调用Session的load()方法,加载Customer对象,然后再修改Customer对象的属性。
session = HibernateUtil.currentSession();
tx = session.beginTransaction();
Book oneBook=(Book)session.load(Book.class,bookID);
oneBook.setBookName(new String("Java应用开发".getBytes("gb2312"), "ISO8859-1"));
oneBook.setBookKind('1');
oneBook.setBookPrice(10.4f);
tx.commit();
l Sessin 接口中的常用方法---- load和get方法
session的load和get方法根据给定的OID从数据库中加载一个对象,load方法在没有找 到对象时抛出notFoundException异常,get方法返回null;
get和load和其他查询方法返回的对象位于session的缓存中,修改了对象的属性后, session清理缓存时,会根据持久化对象的属性来更新数据库。
用来对数据库中检索对象,load()和get()方法按照给定的OID加载一个持久化对象
public Iterator getAllCourses()throws HibernateException
{
String queryString = "select courses from Course as courses";
beginTransaction();
Query query = session.createQuery(queryString);
Iterator it= query.iterate();
return it;
}
/**
*按course的名字进行模糊查找,返回的是包含有Course持久对象的Iterator。
*/
public Iterator getSomeCourse(String name)throws HibernateException
{
String queryString = "select c from Course as c where c.name like :name" ;
beginTransaction();
Query query = session.createQuery(queryString);
query.setString("name", "%"+name+"%");
Iterator it= query.iterate();
return it;
}
l Sessin 接口中的常用方法---- delete()方法产生SQL DELETE
由于从数据库中删除对象对应的记录,如果出入的是持久化对象session就计划执行一个delete语句。
如果出入的参数是游离态对象,先使它成为持久化对象,然后计划执行一个delete语句。session只有在清理缓存的时候才会执行delete语句。只有当调用session的close()方法时才会从session的缓存中删除对象。
session = HibernateUtil.currentSession();
tx = session.beginTransaction();
Book oneBook=(Book)session.load(Book.class,bookID);
session.delete(oneBook);
tx.commit();
11、Hibernate中的核心接口API类的介绍---SessionFactory 接口
(1)利用工厂类SessionFactory中取得Session的实例
这里用到了一个设计模式――工厂模式,用户程序从工厂类SessionFactory中取得Session的实例。
(2)SessionFactory并不是轻量级的
但要注意的是SessionFactory并不是轻量级的!实际上它的设计者的意图是让它能在整个应用中共享。
注意:
由于Java语言是纯面向对象的语言,因此不可能像C语言那样直接操纵内存,例如声明一段可用的内存空间。以上所提到的缓存的概念其实指的是Java对象中的属性(通常是一些集合类型的属性)占用的内存空间。
(3)只有一个数据存储源,只需创建一个SessionFactory
SessionFactory就是个重量级对象,如果应用只有一个数据存储源,只需创建一个SessionFactory实例,因为随意地创建SessionFactory实例会占用大量内存空间。
但是当你的项目要操作多个数据库时,那你必须为每个数据库指定一个SessionFactory。
(4)SessionFactory的实现类
SessionFactory的实现类中定义了许多集合类型的属性,这些属性用于存放Hibernate配置信息、映射元数据信息等。
public final class SessionFactoryImpl implements SessionFactory, SessionFactoryImplementor {
private final transient Map classPersisters;
private final transient Map classPersistersByName;
private final transient Map classMetadata;
private final transient Map collectionPersisters;
private final transient Map collectionMetadata;
private final transient Map namedQueries;
private final transient Map namedSqlQueries;
private final transient Map imports;
private final transient Templates templates;
private final transient Interceptor interceptor;
private final transient Settings settings;
private final transient Properties properties;
private transient SchemaExport schemaExport;
private final transient TransactionManager transactionManager;
private final transient QueryCache queryCache;
private final transient UpdateTimestampsCache updateTimestampsCache;
private final transient Map queryCaches;
……
}
(5)SessionFactory的缓存
可分为两类:内置缓存和外置缓存。
SessionFactory的内置缓存中存放了Hibernate配置信息和映射元数据信息、同时也缓存了Hibernate自动生成的SQL语句等;
SessionFactory的外置缓存是一个可配置的缓存插件,在默认情况下,SessionFactory不会启用这个缓存插件。外置缓存能存放大量数据库数据的拷贝,外置缓存的物理介质可以是内存或者硬盘。
12、Hibernate中的核心接口API类的介绍---Configuration 接口
Configuration是hibernate的入口,在新建一个Configuration的实例的时候,hibernate会在classpath里面查找hibernate.properties文件,如果该文件存在,则将该文件的内容加载到一个Properties的实例GLOBAL_PROPERTIES里面,如果不存在,将打印信息:hibernate.properties not found
然后是将所有系统环境变量(System.getProperties())也添加到GLOBAL_PROPERTIES里面。如果hibernate.properties文件存在,系统还会验证一下这个文件配置的有效性,对于一些已经不支持的配置参数,系统将打印警告信息。
(1)作用---实现对Hibernate进行配置
l Configuration接口的作用是对Hibernate进行配置,以及对它进行启动
在Hibernate的启动过程中,Configuration类的实例首先定位映射文档的位置,读取这些配置,然后创建一个SessionFactory对象。
l 根据Configuration对象创建一个SessionFactory对象
为了能创建一个SessionFactory对象,你必须在Hibernate初始化时创建一个Configuration类的实例,并将已写好的映射文件交由它处理。这样,Configuration对象就可以创建一个SessionFactory对象,当SessionFactory对象创建成功后,Configuration对象就没有用了,你可以简单地抛弃它。
(2)定义
(3)它是启动hibernate的对象
虽然Configuration接口在整个Hibernate项目中只扮演着一个很小的角色,但它是启动hibernate时你所遇到的第一个对象。
(4)编程示例
Configuration config = new Configuration();
config.addClass(Customer.class);
sessionFactory = config.buildSessionFactory();
或者:
使用方法链编程风格,可以改写为:
sessionFactory = new Configuration()
.buildSessionFactory()
.addClass(Customer.class)
.buildSessionFactory();
注意:
方法链编程风格能使应用程序代码更加简捷。在使用这种编程风格时,最好把每个调用方法放在不同的行,否则在跟踪程序时,无法跳入每个调用方法中。
(5)Configuration的其他用法
Configuration的configure ()方法还支持带参数的访问方式,你可以指定hbm.xml文件的位置,而不是使用默认的classpath下面的hibernate.cfg.xml这种方式,例如:
Configuration cfg = new Configuration().configure("myexample.xml");
13、Hibernate中的核心接口API类的介绍---Transaction 接口
(1)Transaction接口是一个可选的API
事务将应用代码从下层的事务实现中抽象出来——这可能是一个JDBC事务,一个JTA用户事务或者甚至是一个公共对象请求代理结构(CORBA)——允许应用通过一组一致的API控制事务边界。
(2)为什么要提供该Transaction 接口
Transaction接口是对实际事务实现的一个抽象。之所以这样设计是能让开发者能够使用一个统一事务的操作界面,使得自己的项目可以在不同的环境和容器之间方便地移值。
14、Hibernate中的核心接口API类的介绍---Query和Criteria接口
(1)Query接口
Query接口让你方便地对数据库及持久对象进行查询,它可以有两种表达方式:HQL语言或本地数据库的SQL语句。Query经常被用来绑定查询参数、限制查询记录数量,并最终执行查询操作。
值得注意的是Query接口也是轻量级的,它不能在Session之外使用。
Query query = session.createQuery("from Customer c where c.name =:name");
query.setParameter("name","tom",Hibernate.STRING);
(2)Criteria接口
Criteria接口与Query接口非常类似,它允许你创建并执行面向对象的标准化查询。
15、Hibernate中的核心接口API类的介绍---其它的接口
(1)Callback 方面的接口
当一些有用的事件发生时――例如持久对象的载入、存储、删除时,Callback接口会通知Hibernate去接收一个通知消息---它允许应用程序能对一些事件的发生作出相应的操作。
例如Interceptor、Lifecycle和Validatable都是这一类接口。一般而言,Callback接口在用户程序中并不是必须的,但你要在你的项目中创建审计日志时,你可能会用到它。
(2)策略接口
l 高度的可扩展性
Hibernate与某些其它开源软件不同的还有一点――高度的可扩展性,这通过它的内置策略机制来实现。当你感觉到Hibernate的某些功能不足,或者有某些缺陷时,你可以开发一个自己的策略来替换它,而你所要做的仅仅只是继承它的某个策略接口,然后实现你的新策略就可以了。
l 以下是它的策略接口
ü 主键的生成 (IdentifierGenerator 接口)
ü 本地SQL语言支持 (Dialect 抽象类)
ü 缓冲机制 (Cache 和CacheProvider 接口)
ü JDBC 连接管理 (ConnectionProvider接口)
ü 事务管理 (TransactionFactory, Transaction, 和 TransactionManagerLookup 接口)
ü ORM 策略 (ClassPersister 接口)
ü 属性访问策略 (PropertyAccessor 接口)
ü 代理对象的创建 (ProxyFactory接口)
Hibernate为以上所列的机制分别创建了一个缺省的实现,因此如果你只是要增强它的某个策略的功能的话,只需简单地继承这个类就可以了,没有必要从头开始写代码。
16、一个重要的术语:Type
(1)一个Type对象能将一个Java类型映射到数据库中一个表的字段中
l Hibernate的设计者们发明了一个术语:Type,它在整个构架中是一个非常基础、有着强大功能的元素。一个Type对象能将一个Java类型映射到数据库中一个表的字段中去(实际上,它可以映射到表的多个字段中去)。
l 它是Hibernate mapping types
并不像我们假想的那样,是Java data type, 同时也不是SQL database type。这些类型被称作Hibernate mapping types,它们把数据类型从Java转换到SQL data types。
l 持久类的所有属性都对应一个type
这种设计思想使用Hibernate有着高度的灵活性和扩展性。
(2)关于缺省的type类型
如果映射的参数没有设置type类型,Hibernate也将尝试去确定正确的类型转换和它的映射类型。 在某些情况下这个自动检测(在Java class上使用反射机制)不会产生你所期待或者需要的缺省值----因此,最好我们来设定其类型。
上面的例子是关于date属性。Hibernate无法知道这个属性应该被映射成下面这些类型中的哪一个: SQL date,timestamp,time。 我们通过声明属性映射timestamp来表示我们希望保存所有的关于日期和时间的信息。
(3)Hibernate内置的主要type类型
l integer, long, short, float, double, character, byte, boolean, yes_no, true_false
这些类型都对应Java的原始类型或者其封装类,来符合(特定厂商的)SQL 字段类型。boolean, yes_no 和 true_false都是Java 中boolean 或者java.lang.Boolean的另外说法。
l string
从java.lang.String 到 VARCHAR (或者 Oracle的 VARCHAR2)的映射。
l date, time, timestamp
从java.util.Date和其子类到SQL类型DATE, TIME 和TIMESTAMP (或等价类型)的映射。
l calendar, calendar_date
从java.util.Calendar 到SQL 类型TIMESTAMP和 DATE(或等价类型)的映射。
l big_decimal, big_integer
从java.math.BigDecimal和java.math.BigInteger到NUMERIC (或者 Oracle 的NUMBER类型)的映射。
l locale, timezone, currency
从java.util.Locale, java.util.TimeZone 和java.util.Currency 到VARCHAR (或者 Oracle 的VARCHAR2类型)的映射. Locale和 Currency 的实例被映射为它们的ISO代码。TimeZone的实例被影射为它的ID。
l class
从java.lang.Class 到 VARCHAR (或者 Oracle 的VARCHAR2类型)的映射。Class被映射为它的全限定名。
l binary
把字节数组(byte arrays)映射为对应的 SQL二进制类型。
l text
把长Java字符串映射为SQL的CLOB或者TEXT类型。
l serializable
把可序列化的Java类型映射到对应的SQL二进制类型。你也可以为一个并非默认为基本类型的可序列化Java类或者接口指定Hibernate类型serializable。
l clob, blob
JDBC 类 java.sql.Clob 和 java.sql.Blob的映射。某些程序可能不适合使用这个类型,因为blob和clob对象可能在一个事务之外是无法重用的。(而且, 驱动程序对这种类型的支持充满着补丁和前后矛盾。)
(4)示例
注意:上面使用一个元素类型是 string(这里使用小写的名字是向你表明它是一个Hibernate的映射类型或者类型转换器)。
(5)Java类型、Hibernate映射类型以及SQL类型之间的对应关系
Customer类的属性 |
Java类型 |
Hibernate映射类型 |
CUSTOMERS表的字段 |
SQL类型 |
name |
java.lang.String |
string |
NAME |
VARCHAR(15) |
|
java.lang.String |
string |
|
VARCHAR(128) |
password |
java.lang.String |
string |
PASSWORD |
VARCHAR(8) |
phone |
int |
int |
PHONE |
INT |
address |
java.lang.String |
string |
ADDRESS |
VARCHAR(255) |
sex |
char |
character |
SEX |
CHAR(1) |
married |
boolean |
boolean |
IS_MARRIED |
BIT |
description |
java.lang.String |
text |
DESCRIPTION |
TEXT |
image |
byte[] |
binary |
IMAGE |
BLOB |
birthday |
java.sql.Date |
date |
BIRTHDAY |
DATE |
registeredTime |
java.sql.Timestamp |
timestamp |
REGISTERED_TIME |
TIMESTAMP |
(6)Java类型、Hibernate映射类型以及SQL类型之间的对应关系的应用示例
l 如果Customer类的属性为java.lang.String类型,并且与此对应的CUSTOMERS表的字段为VARCHAR类型,那么应该把Hibernate映射类型设为string,例如:
l 如果Customer类的属性为java.lang.String类型,并且与此对应的CUSTOMERS表的字段为TEXT类型,那么应该把Hibernate映射类型设为text,例如:
l 如果Customer类的属性为byte[]类型,并且与此对应的CUSTOMERS表的字段为BLOB类型,那么应该把Hibernate映射类型设为binary,例如:
l 如果没有显式设定映射类型,Hibernate会运用Java反射机制先识别出持久化类的属性的Java类型,然后自动使用与之对应的默认的Hibernate映射类型。
例如,Customer类的address属性为java.lang.String类型,与java.lang.String对应的默认的映射类型为string,因此以下两种设置方式是等价的:
或者:
对于Customer类的description属性,尽管它是java.lang.String类型,由于CUSTOMERS表的DESCRIPTION字段为text类型,因此必须显式地把映射类型设为text。
17、用户自定义的type
(1)应用的目的
开发者创建属于他们自己的值类型也是很容易的。比如说,你可能希望持久化java.lang.BigInteger类型的属性,持久化成为VARCHAR字段。Hibernate没有内置这样一种类型。
我们可以利用这种特色让我们的项目中使用自定义的诸如Address、Name这样的type,这样你就可以获得更大的便利,让你的代码更优雅。自定义type在Hibernate中是一项核心特色,它的设计者鼓励你多多使用它来创建一个灵活、优雅的项目!
(2)要求
要实现一个自定义类型,可以实现org.hibernate.UserType或org.hibernate.CompositeUserType中的任一个,并且使用类型的Java全限定类名来定义属性。
注意:
l 自定义类型能够映射一个属性(或集合元素)到不止一个数据库表字段。比如说,你可能有这样的Java属性:getName()/setName(),这是java.lang.String类型的,对应的持久化到三个字段:FIRST_NAME, INITIAL, SURNAME。
l 使用
18、在EJB中使用Hibernate技术
(1)在管理环境中配置Hibernate
管理环境处理特定的“cross-cutting”关系,例如应用安全(授权与验证),连接池和事务管理。J2EE应用服务器是典型的管理环境。虽然应用服务器通常是为了支持EJB而设计的,但即使你不使用EJB实体Bean,你仍然可以利用它提供的其它服务。
(2)Hibernate经常与会话或消息驱动EJB一起使用
像servlet、JSP和独立的应用一样,EJB调用相同的Hibernate API:Session(会话)、Transaction(事务)和Query(查询)。
与Hibernate相关的代码在非管理与管理环境之间是完全可移植的。Hibernate透明地处理了不同的连接与事务策略。
(3)使用应用服务器中的一些资源
应用服务器将连接池对外显示为JNDI绑定数据源,它是javax.jdbc.Datasource类的一个实例。你需要提供一个JNDI全限定名来告诉Hibernate,到哪里去查找JNDI数据源。这种情况下的一个Hibernate配置文件的例子参见下面的清单。
hibernate.connection.datasource = java:/comp/env/jdbc/AuctionDB
hibernate.transaction.factory_class = net.sf.hibernate.transaction.JTATransactionFactory
hibernate.transaction.manager_lookup_class =net.sf.hibernate.transaction.JBossTransactionManagerLookup
hibernate.dialect = net.sf.hibernate.dialect.PostgreSQLDialect
这个文件首先给出了数据源的JNDI名。数据源必须在J2EE企业应用的配置描述符中进行配置;这是一个厂商特定的设置。接着,你允许Hibernate与JTA集成。为了与容器事务完全集成, Hibernate需要定位应用服务器的事务管理器。J2EE规范没有定义标准的方法,但是Hibernate包含了对所有流行的应用服务器的支持。当然,最后还需要指定SQL方言。
(4)Hibernate中定义了一组控制事务的API
Java早就有了一个标准的事务API:JTA,它用来在J2EE管理环境中控制事务。这被称作容器管理的事务(CMT)。如果存在JTA事务管理器,JDBC连接将被它支持并完全位于它的控制之下。
这与非管理环境不同,在非管理环境中应用(或连接池)直接管理JDBC连接和JDBC事务。因此,管理环境与非管理环境可以使用不同的事务方法。
因为Hibernate需要在这两种环境之间保证可移植性,因此它定义了一组控制事务的API。Hibernate的事务接口抽象了下层的JTA或JDBC事务(或者,甚至潜在的CORBA事务)。
(5)这个下层的事务策略可以使用属性hibernate. connection.factory_class来设置,它可以取下列两值之一
■ net.sf.hibernate.transaction.JDBCTransactionFactory代表了直接的JDBC事务
这种策略应该与非管理环境中的连接池一起使用,并且如果没有指定任何策略时它就是缺省
值。
■ net.sf.hibernate.transaction.JTATransactionFactory代表了JTA
对于CMT这是正确的策略,此时连接由JTA支持。注意如果调用beginTransaction()时已经有一个JTA事务在运行,随后的工作将发生在那个事务的上下文中(否则,将会开始一个新的JTA事务)。
(6)使用J2EE服务器工作时必需的两个步骤
我们只要记住使用J2EE服务器工作时必需的两个步骤:像前面描述的那样为Hibernate事务API设置工厂类以支持JTA,并且声明你的应用服务器特定的事务管理器的查找策略。只有你使用了Hibernate的二级缓存系统时查找策略才是必需的,但即使你没有使用缓存设上它也没有坏处。
Hibernate中所需要的JAR类库
1、全局类路径和上下文类路径
(1)全局类路径(global classpath)
对Tomcat来说全局库文件在下面的目录中(TOMCAT/common/lib)
(2)上下文类路径(context classpath)
本Web应用程序上下文的路径(对于jar来说是webapps/XXX/WEB-INF/lib,对于class文件来说是webapps/XXX/WEB-INF/classes)能够被类装载器检索到。
注意:
尽可能不要拷贝任何其他东西到Tomcat的全局类路径下,否则你可能在使用其他一些工具上遇到麻烦,比如log4j, commons-logging等等。
一定要尽可能让每个web应用程序使用自己的上下文类路径,就是说把你自己需要的类库拷贝到WEB-INF/lib下去,把配置文件configuration/property等配置文件拷贝到WEB-INF/classes下面去。因为这两个目录都是当前程序缺省的上下文类路径。
2、Hibernate中所需要的JAR类库
(1)Hibernate本身打包成一个JAR类库
将hibernate3.jar文件拷贝到程序的上下文类路径下,和你应用程序的其他库文件放一起。
(2)Hibernate还需要一些第三方类库
在运行时,Hibernate还需要一些第三方类库,它们在Hibernate发行包的lib/目录下。
参见下表中的“Hibernate 第三方类库”列表和其作用。
类库 |
描述 |
antlr-2.7.5H3.jar(必需) |
Hibernate使用ANTLR来产生查询分析器,这个类库在运行环境下时也是必需的。 |
dom4j-1.6.1.jar (必需) jta.jar (必需) |
jta.jar为事务管理的API所在的包(JTA规范,当Hibernate使用JTA的时候需要)。 dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的。dom4j是一个非常非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件,可以在SourceForge上找到它。 它们都是必须使用的jar包,Hibernate用它来读写配置文件。 |
asm.jar、cglib-2.1.2.jar(必需) |
Hibernate在运行时使用这个代码生成库增强类(与Java反射机制联合使用)。其中的CGLIB库是Hibernate用它来实现PO字节码的动态生成,非常核心的库,必须使用的jar包。 |
commons-collections-2.1.1.jar、commons-logging-1.0.4.jar(必需)、commons-beanutils.jar、commons-lang.jar |
Hibernat使用Apache Jakarta Commons项目提供的多个工具类库。 其中的commons-collections包含了一些Apache开发的集合类,功能比java.util.*强大。必须使用的jar包。 而commons-beanutils.jar:包含了一些Bean工具类类。必须使用的jar包; commons-lang.jar为Apache Commons包中的一个,包含了一些数据类型工具类,是java.lang.*的扩展。必须使用的jar包。 |
ehcache-1.1.jar(必需) |
Hibernate可以使用不同cache缓存工具作为二级缓存。EHCache是缺省的cache缓存工具。 |
log4j-1.2.11.jar (可选) |
Hibernate使用Commons Logging API,它也可以依次使用Log4j作为底层实施log的机制。如果上下文类目录中存在Log4j库,则Commons Logging使用Log4j和并它在上下文类路径中寻找的log4j.properties文件。你可以使用在Hibernate发行包中包含中的那个示例Log4j的配置文件。这样,把log4j.jar和它的配置文件(位于src/目录中)拷贝到你的上下文类路径下,就可以在后台看到底程序如何运行的。 |
odmg.jar |
ODMG是一个ORM的规范,Hibernate实现了ODMG规范,这是一个核心的库,必须使用的jar包。 |
其他文件是不是必需的? |
请察看Hibernate发行包中的 lib/README.txt文件,这是一个Hibernate发行包中附带的第三方类库的列表,他们总是保持最新的。你可以在那里找到所有必需或者可选的类库(注意:其中的"buildtime required"指的是编译Hibernate时所需要而非编译你自己的程序所必需的类库)。 |
把所需要的第三方库文件也拷贝到上下文类路径下。请参考下面的各个文件列表。