对象/关系映射—Hibernate

对象/关系映射—Hibernate
作者:钱安川( Moxie)
     
目录
一、持久化层-O/R Mapping
二、Hibernate入门
三、Hibernate映射申明(Mapping declaration)
四、持久化对象的状态和生命周期
五、Hibernate查询
六、Hibernate最佳实践
一、持久化层-O/R Mapping
基于B/S的典型三层架构
如何进行对象-关系数据库的匹配
public class User {
 private String name;
      private String password;
 private List address;
………
}
create table tbl_user (
 name varchar(255) not null ,
      password varchar(255),
……….
 primary key (name)
)
对象—关系数据库的不匹配范式
粒度(granularity)的问题
子类型(subtypes)的问题
同一性(identity)的问题
与关联(associations)有关的问题
对象结构导航(navigation)的问题

范式不匹配的代价
花费很多时间和精力来手工实现对象和关系的匹配。
甚至要扭曲对象模型直到它与下层的关系技术匹配为止。
JDBC API本身的问题。JDBC和SQL提供了一个面向语句(即命令)的方法从SQL数据库中来回移动数据。至少在三个时刻(Insert,Update,Select)必须指定一个结构化关系,这增加了设计和实现所需要的时间。
基于关系数据库的持久层可选方案
基于关系数据库的持久层可选方案
O/R Mapping -What? Why?
什么是O/R Mapping?
对象-关系映射是一门非常实用的工程技术,它实现了Java应用中的对象到关系数据库中的表的自动的(和透明的)持久化,使用元数据(meta data)描述对象与数据库间的映射。
O/R Mapping的优点
提高生产率(Productivity)
可维护性(Maintainability)
更好性能(Performance)
厂商独立性(Vendor independence)
二、Hibernate入门
Hibernate概述
     Hibernate是非常优秀、成熟的O/R Mapping框架。它提供了强大的对象和关系数据库映射以及查询功能。

Hibernate优势
开源(LGPL)
成熟
流行(约13 000 downloads/month)
自定义API
JBoss 将用Hibernate3实现Entity Beans
Hibernate开发步骤
一、持久化类的设计

二、持久化类和关系数据库的映射

三、应用的开发
持久化Java类必须遵循的原则
为类的持久化类字段申明访问方法(get/set)。Hibernate对JavaBeans风格的属性实行持久化。

实现一个默认的构造方法(constructor)。这样的话Hibernate就可以使用Constructor.newInstance()来实例化它们。

如果是集合类型的属性,它的类型必须定义为集合的接口。例如:List、Set。

提供一个标识属性(identifier property)。如果没有该属性,一些功能不起作用,比如:级联更新(Cascaded updates)Session.saveOrUpdate()。
持久化类和关系数据库的映射
XDoclet:它通过在Java源代码中加入特定的JavaDoc tag,从而为其添加特定的附加语义,之后通过XDoclet工具对代码中JavaDoc Tag进行分析,自动生成与代码对应的配置文件(http://xdoclet.sourceforge.net/)。
XDoclet提供了对Hibernate的支持,这样我们可以直接由Java代码生成Hibernate映射文件。


Hibernate核心接口
Configuration
概述:Configuration 类负责管理Hibernate 的配置信息。它包括如下内容:
Hibernate运行的底层信息:数据库的URL、用户名、密码、JDBC驱动类,数据库Dialect,数据库连接池等。
Hibernate映射文件(*.hbm.xml)。

Hibernate配置的两种方法:
属性文件(hibernate.properties)。
调用代码:Configuration cfg = new Configuration();
Xml文件(hibernate.cfg.xml)。
调用代码:Configuration cfg = new Configuration().configure();

Configuration-例子
数据库连接的配置
hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class com.mysql.jdbc.Driver
hibernate.connection.url jdbc:mysql://localhost/hibernate
hibernate.connection.username root
hibernate.connection.password
数据库连接池的配置-DBCP(App Server连接池首选)
hibernate.connection.provider_class net.sf.hibernate.connection.DBCPConnectionProvider
配置DBCP连接池
其它
hibernate.show_sql true
hibernate.jdbc.fetch_size 50
hibernate.jdbc.batch_size 25
SessionFactory
概述:应用程序从SessionFactory(会话工厂)里获得Session(会话)实例。它在多个应用线程间进行共享。通常情况下,整个应用只有唯一的一个会话工厂——例如在应用初始化时被创建。然而,如果你使用Hibernate访问多个数据库,你需要对每一个数据库使用一个会话工厂。
     会话工厂缓存了生成的SQL语句和Hibernate在运行时使用的映射元数据。

调用代码:
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session(会话)
概述:
Session不是线程安全的,它代表与数据库之间的一次操作,它的概念介于Connection和Transaction之间。
 Session也称为持久化管理器,因为它是与持久化有关的操作接口。
 Session通过SessionFactory打开,在所有的工作完成后,需要关闭。
它与Web层的HttpSession没有任何关系。

调用代码
Session session = sessionFactory.openSession();

Transaction(事务)
概述:
      它将应用代码从底层的事务实现中抽象出来——这可能是一个JDBC事务,一个JTA用户事务或者甚至是一个公共对象请求代理结构(CORBA)——允许应用通过一组一致的API控制事务边界。这有助于保持Hibernate应用在不同类型的执行环境或容器中的可移植性。
调用代码:
    Transaction tx = session.beginTransaction();

注:使用Hibernate进行操作时(增、删、改)必须显示的调用Transaction(默认:autoCommit=false)。
Query
概述:
Query(查询)接口允许你在数据库上执行查询并控制查询如何执行。查询语句使用HQL或者本地数据库的SQL方言编写。

调用代码:
Query query = session.createQuery(“from User”);
用户的例子
持久化类-User.java
public class User {
    private Long id;
    private String name;
    private Date birthday;
    private String email;
    public User(){
    }
    public User(String name,Date birthday,String email){
    .....…Get/Set
}
应用-UserTest.java
public void testCreate() throws Exception{
        Configuration cfg = new Configuration();
        cfg.addURL(UserTest.class.getResource("/com/test/um/User.hbm.xml"));
        SessionFactory sessionFactory = cfg.buildSessionFactory();
       
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
       
        SimpleDateFormat format = new SimpleDateFormat("yyyy-mm-dd");
        User user = new User("Jack",format.parse("1980-04-12"),"Jack@yahoo.com.cn");
       
        session.save(user);
        tx.commit();
       
        assertNotNull(user.getId());
        session.clear();
        User user_2 = (User)session.get(User.class,user.getId());
        assertNotNull(user_2);
       
        session.close();
    }
应用-UserTest.java
保存用户:session.save(user);
修改用户:session.update(user);
保存或修改用户:session.saveOrUpdate(user);
删除用户:session.delete(user);
删除所有用户:session.delete(“from User ”);

查询用户名为“test”的用户:
Query query = session.createQuery("from User where user.name = :name");
query.setParameter(“test",user.getName());
 User findUser = (User) query.list().get(0);
三、Hibernate映射申明(Mapping declaration)
hibernate-mapping
 一、类层次。class
  1、主键。id
  2、基本类型。property
  3、自定义类。many-to-one  |  one-to-one
  4、集合。set | list | map | array
   4.1、one-to-many
   4.2、many-to-many
  5、子类。subclass | joined-subclass
  6、其它。component | any等
 二、查询语句。Query
说明:一个Hibernate-mapping中可以同时定义多个类。Query非常简单,主要是用来放置查询语句,便于对数据库查询的统一管理和优化。
hibernate-mapping
<hibernate-mapping
         schema="schemaName"                    (1)
         default-cascade="none|save-update"           (2)
         auto-import="true|false"                    (3)
         package="package.name"                   (4)
 />
(1)、schema(可选):数据库Schema Name
(2)、default-cascade(可选,默认为none):默认的级联风格
(3)、auto-import(可选,默认为true):是否在查询中只使用类名。不用加package名字。
(4)、package(可选),如果该映射文件中定义的类名不包含package,则使用这里定义的package作为类名的前缀。
一、类层次class
<class
        name="ClassName"                        (1)
        table="tableName"                        (2)
        mutable="true|false"                       (3)
   dynamic-update="true|false"                 (4)
        dynamic-insert="true|false"                  (5)
        select-before-update="true|false"             (6)
   where="arbitrary sql where condition"         (7)
        persister="PersisterClass"                   (8)
/>

1、主键-id
<id
        name="propertyName"                      (1)
        type="typename"                          (2)
        column="column_name"                     (3)
        unsaved-value="any|none|null|id_value"   (4)
 
        <generator class="generatorClass"/>
</id>
(1)、name (可选) :标识属性的名称。
(2)、type(可选):标识Hibernate类型的名字。
(3)、column(可选-默认为属性名):对应数据库表的主键字段的名字。
(4)、unsaved-value(可选-默认为null):这个值用来判断对象是否要保存。
1.1主键生成策略generator
generator
主键生成器,每个主键都必须定义相应的主键生成策略。它用来为持久化类实例生成唯一的标识。

Hibernate内置的主键生成策略
数据库提供的主键生成机制。identity、sequence(序列) 。
外部程序提供的主键生成机制。increment (递增) ,hilo(高低位) ,seqhilo(使用序列的高低位 ),uuid.hex(使用了IP地址+JVM的启动时间(精确到1/4秒)+系统时间+一个计数器值(在JVM中唯一) ),uuid.string。
其它。native(本地),assigned(手工指定),foreign(外部引用)。
2、基本类型-property
<property
        name="propertyName"                 (1)
        column="column_name"                (2)
        type="typename"                     (3)
        update="true|false"                 (4)
        insert="true|false"                 (4)
        formula="arbitrary SQL expression"  (5)
/>
(4) update, insert (可选 - 默认为 true) :表明在用于UPDATE 和/或 INSERT的SQL语句中是否包含这个字段。
(5) formula (可选): 一个SQL表达式,定义了这个计算(computed) 属性的值。计算属性没有和它对应的数据库字段。
3.1、自定义类-many-to-one
<many-to-one
        name="propertyName"                                (1)
        column="column_name"                               (2)
        class="ClassName"                                  (3)
        cascade="all|none|save-update|delete"              (4)
        outer-join="true|false|auto"                       (5)
        property-ref="propertyNameFromAssociatedClass"     (7)
/>
(3) class (可选 - 默认是通过反射得到属性类型): 关联的类的名字。
(4) cascade(级联) (可选): 指明哪些操作会从父对象级联到关联的对象。
(5) outer-join(外连接) (可选 - 默认为 自动) hibernate.use_outer_join
(7) property-ref: (可选) 指定关联类的一个属性,这个属性将会和本外相对应。
例子:User-Group
<class name=" com.test.hibernate .User" table="TBL_USER">
 <id name="id" column="userId"><generator class="native"/></id>
 <many-to-one name=“group” column=“groupId” outer-join="false"/>
</class>

<class name=" com.test.hibernate .Group" table="TBL_GROUP">
 <id name="id" column="groupId"><generator class="native"/></id>
</class>
3.2、自定义类-one-to-one
一对一关联:主键关联和惟一外键关联两种方式。
例子:User-IdCard(外键关联)
<class name=" com.test.hibernate .User" table="TBL_USER">
 <id name="id" column="userId"><generator class="native"/></id>
 <many-to-one name=“idCard” column=“idCardId” outer-join="false“ unique=“true”/>
</class>

<class name="com.test.hibernate.IdCard" table="TBL_IDCARD">
 <id name="id" column="idCardId"><generator class="native"/></id>
</class>

4、集合-Set
<set
    name="propertyName"                                         (1)
    table="table_name"                                          (2)
    lazy="true|false"                                           (3)
    inverse="true|false"                                        (4)
    cascade="all|none|save-update|delete|all-delete-orphan"     (5)
order-by="column_name asc|desc"                             (6)
    where="arbitrary sql where condition"                       (7)
    outer-join="true|false|auto"                                (8)
>

(1) name 集合属性的名称
(2) table (可选)目标关联数据库表
(3) lazy (可选——默认为false)允许延迟加载(lazy initialization )
(4) inverse (可选——默认为false) 标记有哪一方来维护关联关系(双向关联中使用)。
(5) cascade (可选——默认为none) 让操作级联到子实体
(6) order-by (可选, 仅用于jdk1.4) 指定表的字段(一个或几个)再加上asc或者desc(可选), 定义Map,Set和Bag的迭代顺序
(7) where (可选) 指定任意的SQL where条件
(8) outer-join(可选-默认为auto)是否使用外联接
4.1、one-to-many
 
例子(one-to-many):User-Address
<class name="com.test.hibernate.User" table="TBL_USER">
  <id name="id" column="userId"><generator class="native"/></id>
  <set name="addresses" lazy="true" cascade="all">
   <key column="addressId"/>
   <one-to-many class="com.test.hibernate.Address"/>
  </set>
 </class>

     <class name="com.test.hibernate.Address" table="TBL_ADDRESS">
     <id name="id" column="addressId"> <generator class="native"/></id>
 </class>
4.2、many-to-many
<many-to-many
        column="column_name"                               (1)
        class="ClassName"                                  (2)
        outer-join="true|false|auto"                       (3)
/>
(1) column(必需): 中间映射表中,关联目标表的关联字段
(2) class (必需): 类名,关联目标类
(3) outer-join (可选 - 默认为auto)

<key column="column_name"/>   (1)
(1) column(必需):当前表的关联字段
例子(many to many):student-trainClass
<class name="com.test.hibernate.Student">
 <id name="id" column="userId"><generator class="native"/></id>
 <set name="trainClasses" lazy="true" cascade="save-update">
  <key column="studentId"/>
  <many-to-many class="com.test.hibernate.TrainClass"      column="trainClassId"/>
 </set>
</class>
<class name="com.test.hibernate.TrainClass" table="TBL_TRAIN_CLASS">
 <id name="id" column="trainClassId"><generator class="native"/></id>
</class>
5、继承
继承实现的三中策略
单表继承。每棵类继承树使用一个表(table per class hierarchy)
具体表继承。每个子类一个表(table per subclass)
类表继承。每个具体类一个表(table per concrete class)(有一些限制)
单表继承
<discriminator
        column="discriminator_column"  (1)
        type="discriminator_type"      (2)
/>
<subclass>
例子(继承):user-student
<class name="com.test.hibernate.User" table="TBL_USER">
  <id name="id" column="userId">
   <generator class="native"/>
  </id>
  <discriminator type="string" column="inherit"/>
  
  <subclass name="com.test.hibernate.Student">
   <property name="number" column="studentNumber"/>
  </subclass>
 </class>
双向关联
概念:双向关联允许通过关联的任一端访问另外一端。在Hibernate中, 支持两种类型的双向关联。
一对多(one-to-many),Set或者bag值在一端, 单独值(非集合)在另外一端 。
多对多(many-to-many),两端都是set或bag值。
例子(双向关联):group-user
<class name="com.test.hibernate.Group" table="TBL_GROUP">
 <id name="id" column="groupId"><generator class="native“></id>
 <set name="users" lazy="true" cascade="save-update" inverse="true">
    <key column="groupId"/>
  <one-to-many class="com.test.hibernate.User"/>
 </set>
</class>

<class name="com.test.hibernate.User" table="TBL_USER">
 <id name="id" column="userId"><generator class="native"/></id>
 <many-to-one name="group" column="groupId" outer-join="false"/>
</class>
例子:从Java代码看group-user双向关联的inverse
概念:inverse用来标识双向关联的关联关系由哪一端维护。默认inverse的值为false,由主动方负责维护关联关系;如果设为true,则由反向一端维护关联关系。
用例:我们假设已经有一个Group类的实例:adminGroup,现在我们要新增一个用户,并且将用户分配到adminGroup中。
inverse=“false”,由主动方Group负责维护group-user的关联关系.
User user = new User(“Jak”);
adminGroup.getUsers.add(user);
session.save(user); session.update(group);
inverse=“true”,由Group的反向段User负责维护关联关系。
User user = new User(“Jak”);
user .setGroup(adminGroup); session.save(user);
四、持久化对象的状态
瞬时对象(Transient Objects):使用new 操作符初始化的对象不是立刻就持久的。它们的状态是瞬时的,也就是说它们没有任何跟数据库表相关联的行为,只要应用不再引用这些对象(不再被任何其它对象所引用),它们的状态将会丢失,并由垃圾回收机制回收。
持久化对(Persist Objects):持久实例是任何具有数据库标识的实例。它有持久化管理器Session统一管理,持久实例是在事务中进行操作的——它们的状态在事务结束时同数据库进行同步。当事务提交时,通过执行SQL的INSERT、UPDATE和DELETE语句把内存中的状态同步到数据库中。
离线对象(Detached Objects):Session关闭之后,持久化对象就变为离线对象。离线表示这个对象不能再与数据库保持同步,它们不再受Hibernate管理。

持久化对象的生命周期(lifecycle)
Hibernate脏数据字段捡入
检索一个用户,并将它的用户名更改为“Mary”
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user = (User) session.get(User.class, userId);
user.setName("Mary");
tx.commit();
session.close();
持久化对象必须唯一
一个Session中不能同时存在两个ID相同的持久化对象
例如:id为userId的user_1对象已经存在Session中,这时如果Session中试图产生一个user_2对象!!
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user_1 = (User)session.get(User.class,userId);
User user_2 = new User(userId,”Mary”);
session.update(user_2);
最后将会产生异常:net.sf.hibernate.NonUniqueObjectException:
a different object with the same identifier value was already associated with the session
解决办法: 使用evict ()方法将user_1实例从session中去除。session.evict(user_1);
五、Hibernate查询
概述:数据查询与检索是Hibernate中的一个亮点。相对其他ORM实现而言,Hibernate提供了灵活多样的查询机制。
标准化对象查询(Criteria Query):以对象的方式进行查询,将查询语句封装为对象操作。优点:可读性好,符合Java 程序员的编码习惯。缺点:不够成熟,不支持投影(projection)或统计函数(aggregation)
Hibernate语言查询(Hibernate Query Language,HQL):它是完全面向对象的查询语句,查询功能非常强大,具备继承、多态和关联等特性 。Hibernate官方推荐使用HQL进行查询。
Native SQL Queries(原生SQL查询):直接使用数据库提供的SQL方言进行查询。
例子:标准化对象查询(Criteria Query)
简单例子:查询用户名以“J”开头的所有用户。
 Criteria criteria = session.createCriteria(User.class);
      criteria.add(Expression.like("name","J%"));
      List users = criteria.list();
Hibernate语言查询(Hibernate Query Language,HQL)
HQL用面向对象的方式生成SQL
以类和属性来代替表和数据列
支持多态
支持各种关联
减少了SQL的冗余
HQL支持所有的关系数据库操作
连接(joins,包括Inner/outer/full joins),笛卡尔积(cartesian products)
投影(projection)
聚合(Aggregation,max, avg)和分组(group)
排序(Ordering)
子查询(Subqueries)
SQL函数(SQL function calls)

例子: Hibernate语言查询(Hibernate Query Language,HQL)
简单例子:查询用户名以“J”开头的所有用户。
 Query query = session.createQuery(
   "from User user where user.name like 'J%'");
        List users = query.list();
复杂例子:从User和Group中查找属于“admin”组的所有用户。
 Query query = session.createQuery(
   “from User user where user.group.name=‘admin’”);

如果用传统的SQL则查询语句如下:
select user.userId as userId, user.name as name, user.groupId as groupId, user.idCardId as idCardId  from TBL_USER user, TBL_GROUP group where (group.groupName='admin'  and user.groupId=group.groupId)

六、Hibernate最佳实践(Best Practices)
1、使用Configuration装载映射文件时,不要使用绝对路径装载。最好的方式是通过getResourceAsStream()装载映射文件,这样Hibernate会从classpath中寻找已配置的映射文件。
2、SessionFactory的创建非常消耗资源,整个应用一般只要一个SessionFactory就够了,只有多个数据库的时候才会使用多个SessionFactory。
3、在整个应用中,Session和事务应该能够统一管理。(Spring为Hibernate提供了非常好的支持)
4、将所有的集合属性配置设置为懒加载(lazy=”true”)。在hibernate2.x版本中,lazy默认值是“false”,但hibernate3.x已经将lazy的默认改为“true”了。
Hibernate最佳实践(Best Practices)
5、在定义关联关系时,集合首选Set,如果集合中的实体存在重复,则选择List(在定义配置文件时,可以将List定义为bag),数组的性能最差。
6、在一对多的双向关联中,一般将集合的inverse属性设置为true,让集合的对方维护关联关系。例如:Group-User,由User来维护Group和User的关联关系。
7、HQL子句本身大小写无关,但是其中出现的类名和属性名必须注意大小写区分。
8、在非分布式架构中,不需要使用DTO来向上层传输数据。直接使用POJO的Entity就可以了。
9、如果要精通Hibernate,熟练掌握关系数据库理论和SQL是前提条件。
Hibernate资源
官方网站:http://www.hibernate.org
国内网站:http://www.hibernate.org.cn
Java新视线论坛:http://forum.hibernate.org
《Hibernate 中文开发指南》作者夏昕(http://www.redsaga.com/
《深入浅出Hibernate》作者:夏昕 曹晓钢 唐勇
http://www.china-pub.com/computers/common/info.asp?id=24500
《Hibernate in Action》作者:Christian Bauer and Gavin King(http://www.javafan.net/可下载)
《Hibernate: A Developer's Notebook》作者:James Elliott

结束,谢谢!
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值