1 / 53

对象/关系映射— Hibernate

对象/关系映射— Hibernate. 作者:钱安川( Moxie ). 学员要求 : 熟悉 Java、SQL、JDBC, 掌握面向对象的开发方法,并有实际项目开发经验 课程目标: 理解 O/R Mapping 原理,掌握 Hibernate 开发的相关知识,并能使用 Hibernate 进行实际项目开发. Email:achqian@yahoo.com.cn. 目录. 一、持久化层- O/R Mapping 二、 Hibernate 入门 三、 Hibernate 映射申明( Mapping declaration ) 四、持久化对象的状态和生命周期

lucie
Download Presentation

对象/关系映射— Hibernate

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 对象/关系映射—Hibernate 作者:钱安川( Moxie) • 学员要求:熟悉Java、SQL、JDBC,掌握面向对象的开发方法,并有实际项目开发经验 • 课程目标:理解O/R Mapping原理,掌握Hibernate开发的相关知识,并能使用Hibernate进行实际项目开发 Email:achqian@yahoo.com.cn

  2. 目录 一、持久化层-O/R Mapping 二、Hibernate入门 三、Hibernate映射申明(Mapping declaration) 四、持久化对象的状态和生命周期 五、Hibernate查询 六、Hibernate最佳实践

  3. 一、持久化层-O/R Mapping 基于B/S的典型三层架构 • 如何分层? • 业务逻辑层和持久化层绝不要依赖于展现层。 • 使用假设法测试。 例子:1、一个显示课程考试分数的列表。现在要将不及格的分数用红色字体显示(低于60分)。 2、搜索。

  4. 如何进行对象-关系数据库的匹配 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) ) • 如何进行对象-关系数据库的匹配?

  5. 对象—关系数据库的不匹配范式 • 粒度(granularity)的问题 • 子类型(subtypes)的问题 • 同一性(identity)的问题 • 与关联(associations)有关的问题 • 对象结构导航(navigation)的问题 • 范式不匹配的代价 • 花费很多时间和精力来手工实现对象和关系的匹配。 • 甚至要扭曲对象模型直到它与下层的关系技术匹配为止。 • JDBC API本身的问题。JDBC和SQL提供了一个面向语句(即命令)的方法从SQL数据库中来回移动数据。至少在三个时刻(Insert,Update,Select)必须指定一个结构化关系,这增加了设计和实现所需要的时间。

  6. 基于关系数据库的持久层可选方案

  7. 基于关系数据库的持久层可选方案

  8. 什么是O/R Mapping? 对象-关系映射是一门非常实用的工程技术,它实现了Java应用中的对象到关系数据库中的表的自动的(和透明的)持久化,使用元数据(meta data)描述对象与数据库间的映射。 O/R Mapping的优点 提高生产率(Productivity) 可维护性(Maintainability) 更好性能(Performance) 厂商独立性(Vendor independence) O/R Mapping -What? Why?

  9. 二、Hibernate入门 • Hibernate概述 Hibernate是非常优秀、成熟的O/R Mapping框架。它提供了强大的对象和关系数据库映射以及查询功能。 • Hibernate优势 • 开源(LGPL) • 成熟 • 流行(约13 000 downloads/month) • 自定义API • JBoss 将用Hibernate3实现Entity Beans

  10. Hibernate开发步骤 一、持久化类的设计 二、持久化类和关系数据库的映射 三、应用的开发

  11. 持久化Java类必须遵循的原则 • 为类的持久化类字段申明访问方法(get/set)。Hibernate对JavaBeans风格的属性实行持久化。 • 实现一个默认的构造方法(constructor)。这样的话Hibernate就可以使用Constructor.newInstance()来实例化它们。 • 如果是集合类型的属性,它的类型必须定义为集合的接口。例如:List、Set。 • 提供一个标识属性(identifier property)。如果没有该属性,一些功能不起作用,比如:级联更新(Cascaded updates)Session.saveOrUpdate()。

  12. 持久化类和关系数据库的映射 Middlegen:从数据库中已有 的表结构中生成Hibernate映射文 件。当前版本是2.1可以去http://boss.bekk.no/boss/middlegen下载。 XDoclet:它通过在Java源代码中加入特定的JavaDoc tag,从而为其添加特定的附加语义,之后通过XDoclet工具对代码中JavaDoc Tag进行分析,自动生成与代码对应的配置文件(http://xdoclet.sourceforge.net/)。 XDoclet提供了对Hibernate的支持,这样我们可以直接由Java代码生成Hibernate映射文件。

  13. Hibernate核心接口

  14. 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();

  15. 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

  16. SessionFactory • 概述:应用程序从SessionFactory(会话工厂)里获得Session(会话)实例。它在多个应用线程间进行共享。通常情况下,整个应用只有唯一的一个会话工厂——例如在应用初始化时被创建。然而,如果你使用Hibernate访问多个数据库,你需要对每一个数据库使用一个会话工厂。 会话工厂缓存了生成的SQL语句和Hibernate在运行时使用的映射元数据。 • 调用代码: SessionFactory sessionFactory = cfg.buildSessionFactory();

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

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

  19. Query • 概述: Query(查询)接口允许你在数据库上执行查询并控制查询如何执行。查询语句使用HQL或者本地数据库的SQL方言编写。 • 调用代码: Query query = session.createQuery(“from User”);

  20. 用户的例子 映射文件-User.hbm.xml <hibernate-mapping> <class name="com.test.um.User" table="TBL_USER"> <id name="id" column="ID"> <generator class="native"/> </id> <property name="name" column="NAME"/> <property name="birthday" column="BIRTHDAY"/> <property name="email" column="EMAIL"/> </class> </hibernate-mapping> 持久化类-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 }

  21. 应用-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(); }

  22. 应用-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);

  23. 三、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非常简单,主要是用来放置查询语句,便于对数据库查询的统一管理和优化。

  24. 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作为类名的前缀。

  25. 一、类层次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) />

  26. 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):这个值用来判断对象是否要保存。

  27. 1.1主键生成策略generator • generator 主键生成器,每个主键都必须定义相应的主键生成策略。它用来为持久化类实例生成唯一的标识。 • Hibernate内置的主键生成策略 • 数据库提供的主键生成机制。identity、sequence(序列) 。 • 外部程序提供的主键生成机制。increment (递增) ,hilo(高低位),seqhilo(使用序列的高低位),uuid.hex(使用了IP地址+JVM的启动时间(精确到1/4秒)+系统时间+一个计数器值(在JVM中唯一) ),uuid.string。 • 其它。native(本地),assigned(手工指定),foreign(外部引用)。

  28. 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) 属性的值。计算属性没有和它对应的数据库字段。

  29. 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: (可选) 指定关联类的一个属性,这个属性将会和本外相对应。

  30. 例子: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>

  31. 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>

  32. 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) >

  33. (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)是否使用外联接

  34. 4.1、one-to-many • 概念:一对多关联直接连接两个类对应的表,而没有中间集合表。(实现了一个一对多的关系模型,例如:User-Address) • 这里的Java集合必须满足下面的语义: • map,set或list中不能包含null值 • 一个被包含的实体的实例只能被包含在一个集合的实例中 <one-to-many class="ClassName"/> (1) class(必须):被关联类的名称。

  35. 例子(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>

  36. 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(必需):当前表的关联字段

  37. 例子(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>

  38. 5、继承 • 继承实现的三中策略 • 单表继承。每棵类继承树使用一个表(table per class hierarchy) • 具体表继承。每个子类一个表(table per subclass) • 类表继承。每个具体类一个表(table per concrete class)(有一些限制) • 单表继承 <discriminator column="discriminator_column" (1) type="discriminator_type" (2) /> <subclass>

  39. 例子(继承):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>

  40. 双向关联 • 概念:双向关联允许通过关联的任一端访问另外一端。在Hibernate中, 支持两种类型的双向关联。 • 一对多(one-to-many),Set或者bag值在一端, 单独值(非集合)在另外一端 。 • 多对多(many-to-many),两端都是set或bag值。

  41. 例子(双向关联):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>

  42. 例子:从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);

  43. 四、持久化对象的状态 • 瞬时对象(Transient Objects):使用new 操作符初始化的对象不是立刻就持久的。它们的状态是瞬时的,也就是说它们没有任何跟数据库表相关联的行为,只要应用不再引用这些对象(不再被任何其它对象所引用),它们的状态将会丢失,并由垃圾回收机制回收。 • 持久化对(Persist Objects):持久实例是任何具有数据库标识的实例。它有持久化管理器Session统一管理,持久实例是在事务中进行操作的——它们的状态在事务结束时同数据库进行同步。当事务提交时,通过执行SQL的INSERT、UPDATE和DELETE语句把内存中的状态同步到数据库中。 • 离线对象(Detached Objects):Session关闭之后,持久化对象就变为离线对象。离线表示这个对象不能再与数据库保持同步,它们不再受Hibernate管理。

  44. 持久化对象的生命周期(lifecycle)

  45. 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();

  46. 持久化对象必须唯一 • 一个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);

  47. 五、Hibernate查询 • 概述:数据查询与检索是Hibernate中的一个亮点。相对其他ORM实现而言,Hibernate提供了灵活多样的查询机制。 • 标准化对象查询(Criteria Query):以对象的方式进行查询,将查询语句封装为对象操作。优点:可读性好,符合Java 程序员的编码习惯。缺点:不够成熟,不支持投影(projection)或统计函数(aggregation) • Hibernate语言查询(Hibernate Query Language,HQL):它是完全面向对象的查询语句,查询功能非常强大,具备继承、多态和关联等特性 。Hibernate官方推荐使用HQL进行查询。 • Native SQL Queries(原生SQL查询):直接使用数据库提供的SQL方言进行查询。

  48. 例子:标准化对象查询(Criteria Query) • 简单例子:查询用户名以“J”开头的所有用户。 Criteria criteria = session.createCriteria(User.class); criteria.add(Expression.like("name","J%")); List users = criteria.list();

  49. 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)

  50. 例子: 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)

More Related