posts - 0,  comments - 0,  trackbacks - 0

 

五一在参加了Spring+Hibernate的课程。虽说大部分课程内容已经掌握但系统听一下还是有收获的。不敢独享下面和大家分享一下。                     
我认为Hibernate主要两大块(对基本应用来说)。主要说一下这两点。一是他的核心API,二是实体之间的关系及ORM。当然这些也不是很深的东西,高手还是研究它的源码,领悟的设计艺术。他的配置网上很多这里我只给出一些参数。
一个典型的hibernate.cfg.xml配置文件如下
<?xml version='1.0' encoding='gb2312'?>
<!DOCTYPE hibernate-configuration    PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.url">
jdbc:mysql://localhost/sample
</property>
<property name="hibernate.connection.driver_class">
org.gjt.mm.mysql.Driver
</property>
<property name="hibernate.connection.username">
User
</property>
<property name="hibernate.connection.password">
Mypass
</property>
<property name="dialect">
net.sf.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.show_sql">
true
</property>
<!—- 是否使用数据库外连接-->
<property name="hibernate.use_outer_join">
True
</property>
<property name="hibernate.transaction.factory_class">
net.sf.hibernate.transaction.JDBCTransactionFactory
</property>
<mapping resource="examples/User.hbm.xml"/>
<mapping resource="examples/Group.hbm.xml"/>
</session-factory>
</hibernate-configuration>
2 Hibernate中的主要配置属性
属性名
用途
hibernate.dialect
一个Hibernate Dialect类名允许Hibernate针对特定的关系数据库生成优化的SQL。取值 full.classname.of.Dialect
hibernate.show_sql
输出所有SQL语句到控制台。取值 true | false
hibernate.default_schema
在生成的SQL中, 将给定的schema/tablespace附加于非全限定名的表名上。取值 SCHEMA_NAME
hibernate.default_catalog
在生成的SQL中, 将给定的catalog附加于没全限定名的表名上。取值 CATALOG_NAME
hibernate.session_factory_name
SessionFactory创建后,将自动使用这个名字绑定到JNDI中。
取值 jndi/composite/name
hibernate.max_fetch_depth
为单向关联(一对一, 多对一)的外连接抓取(outer join fetch)树设置最大深度。值为0意味着将关闭默认的外连接抓取。取值 建议在0到3之间取值
hibernate.default_batch_fetch_size
为Hibernate关联的批量抓取设置默认数量。取值 建议的取值为4, 8, 和16
hibernate.default_entity_mode
为由这个SessionFactory打开的所有Session指定默认的实体表现模式。取值 dynamic-map, dom4j, pojo
hibernate.order_updates
强制Hibernate按照被更新数据的主键,为SQL更新排序。这么做将减少在高并发系统中事务的死锁。 取值 true | false
hibernate.generate_statistics
如果开启, Hibernate将收集有助于性能调节的统计数据。取值 true | false
hibernate.use_identifer_rollback
如果开启, 在对象被删除时生成的标识属性将被重设为默认值。取值 true | false
hibernate.use_sql_comments
如果开启, Hibernate将在SQL中生成有助于调试的注释信息, 默认值为false。取值 true | false

3、Hibernate JDBC和连接(connection)属性
属性名
用途
hibernate.jdbc.fetch_size
非零值,指定JDBC抓取数量的大小 (调用Statement.setFetchSize())。
hibernate.jdbc.batch_size
非零值,允许Hibernate使用JDBC2的批量更新。取值:建议取5到30之间的值
hibernate.jdbc.batch_versioned_data
如果你想让你的JDBC驱动从executeBatch()返回正确的行计数 , 那么将此属性设为true(开启这个选项通常是安全的);同时,Hibernate将为自动版本化的数据使用批量DML。默认值为false。取值为:true | false
hibernate.jdbc.factory_class
选择一个自定义的Batcher. 多数应用程序不需要这个配置属性。取值为:classname.of.Batcher
hibernate.jdbc.use_scrollable_resultset
允许Hibernate使用JDBC2的可滚动结果集,只有在使用用户提供的JDBC连接时,这个选项才是必要的, 否则Hibernate会使用连接的元数据。取值 true | false
hibernate.jdbc.use_streams_for_binary
在JDBC读写binary (二进制)或serializable (可序列化) 的类型时使用流(stream)(系统级属性)。取值 true | false
注意:Oracle限制那些通过JDBC驱动传输的字节数组的数目,如果希望使用二进值或可序列化的类型的大对象,应该开启 hibernate.jdbc.use_streams_for_binary属性,这是系统级属性。
hibernate.jdbc.use_get_generated_keys
在数据插入数据库之后,允许使用JDBC3 PreparedStatement.getGeneratedKeys() 来获取数据库生成的key(键)。需要JDBC3+驱动和JRE1.4+, 如果你的数据库驱动在使用Hibernate的标 识生成器时遇到问题,请将此值设为false. 默认情况下将使用连接的元数据来判定驱动的能力。取值 true|false
hibernate.connection.provider_class
自定义ConnectionProvider的类名, 此类用来向Hibernate提供JDBC连接。取值 classname.of.ConnectionProvider
hibernate.connection.isolation
设置JDBC事务隔离级别,查看java.sql.Connection来了解各个值的具体意义, 但请注意多数数据库都不支持所有的隔离级别。
取值 1, 2, 4, 8
hibernate.connection.autocommit
允许被缓存的JDBC连接开启自动提交(autocommit) (不建议)。
取值 true | false
hibernate.connection.release_mode
指定Hibernate在何时释放JDBC连接,默认情况下,直到Session被显式关闭或被断开连接时,才会释放JDBC连接。
对于应用程序服务器的JTA数据源, 你应当使用after_statement, 这样在每次JDBC调用后,都会主动的释放连接。
对于非JTA的连接, 使用after_transaction在每个事务结束时释放连接是合理的。auto将为JTA和CMT事务策略选择after_statement, 为JDBC事务策略选择after_transaction。
取值 on_close | after_transaction | after_statement | auto
hibernate.connection.<propertyName>
将JDBC属性propertyName传递到DriverManager.getConnection()中去.
hibernate.jndi.<propertyName>
将属性propertyName传递到JNDI InitialContextFactory中去.

4、Hibernate缓存属性
属性名
用途
hibernate.cache.provider_class
自定义的CacheProvider的类名。取值 classname.of.CacheProvider
hibernate.cache.use_minimal_puts
以频繁的读操作为代价, 优化二级缓存来最小化写操作。在Hibernate3中,这个设置对的集群缓存非常有用, 对集群缓存的实现而言,默认是开启的。取值 true|false
hibernate.cache.use_query_cache
允许查询缓存, 个别查询仍然需要被设置为可缓存的。取值 true|false
hibernate.cache.use_second_level_cache
能用来完全禁止使用二级缓存,对那些在类的映射定义中指定<cache>的类,会默认开启二级缓存。取值 true|false
hibernate.cache.query_cache_factory
自定义的实现QueryCache接口的类名, 默认为内建的StandardQueryCache。取值 classname.of.QueryCache
hibernate.cache.region_prefix
二级缓存区域名的前缀,取值 prefix
hibernate.cache.use_structured_entries
强制Hibernate以更人性化的格式将数据存入二级缓存,取值 true|false

5、Hibernate事务属性
属性名
用途
hibernate.transaction.factory_class
一个TransactionFactory的类名, 用于Hibernate Transaction API (默认为JDBCTransactionFactory)。取值 classname.of.TransactionFactory
jta.UserTransaction
一个JNDI名字,被JTATransactionFactory用来从应用服务器获取JTA UserTransaction。取值 jndi/composite/name
hibernate.transaction.manager_lookup_class
一个TransactionManagerLookup的类名 - 当使用JVM级缓存,或在JTA环境中使用hilo生成器的时候需要该类。取值 classname.of.TransactionManagerLookup
hibernate.transaction.flush_before_completion
如果开启, session在事务完成后将被自动清洗(flush), (在Hibernate和CMT一起使用时很有用) 。取值 true | false
hibernate.transaction.auto_close_session
如果开启, session在事务完成后将被自动关闭, (在Hibernate和CMT一起使用时很有用) 。取值 true | false

6、其他属性
属性名
用途
hibernate.query.factory_class
选择HQL解析器的实现。取值 org.hibernate.hql.ast.ASTQueryTranslatorFactory or org.hibernate.hql.classic.ClassicQueryTranslatorFactory
hibernate.query.substitutions
将Hibernate查询中的符号映射到SQL查询中的符号 (符号可能是函数名或常量名字)。取值 hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC
hibernate.hbm2ddl.auto
在SessionFactory创建时,自动将数据库schema的DDL导出到数据库. 使用 create-drop时,在显式关闭SessionFactory时,将drop掉数据库schema。取值 update | create | create-drop
hibernate.cglib.use_reflection_optimizer
开启CGLIB来替代运行时反射机制(系统级属性),反射机制有时在除错时比较有用。注意即使关闭这个优化, Hibernate还是需要CGLIB。你不能在hibernate.cfg.xml中设置此属性。取值 true | false


下面列出Hibernate SQL的方言 (hibernate.dialect)
hibernate.dialect属性用于指定被访问数据库使用的SQL方言,当Hibernate生成SQL查询语句,或者使用native对象标识符生成策略时,都会参考本地数据库的SQL方言。这样能够更好地利用某种数据库的特性。
RDBMS类型
方言
DB2
org.hibernate.dialect.DB2Dialect
DB2 AS/400
org.hibernate.dialect.DB2400Dialect
DB2 OS390
org.hibernate.dialect.DB2390Dialect
PostgreSQL
org.hibernate.dialect.PostgreSQLDialect
MySQL
org.hibernate.dialect.MySQLDialect
MySQL with InnoDB
org.hibernate.dialect.MySQLInnoDBDialect
MySQL with MyISAM
org.hibernate.dialect.MySQLMyISAMDialect
Oracle (any version)
org.hibernate.dialect.OracleDialect
Oracle 9i/10g
org.hibernate.dialect.Oracle9Dialect
Sybase
org.hibernate.dialect.SybaseDialect
Sybase Anywhere
org.hibernate.dialect.SybaseAnywhereDialect
Microsoft SQL Server
org.hibernate.dialect.SQLServerDialect
SAP DB
org.hibernate.dialect.SAPDBDialect
Informix
org.hibernate.dialect.InformixDialect
HypersonicSQL
org.hibernate.dialect.HSQLDialect
Ingres
org.hibernate.dialect.IngresDialect
Progress
org.hibernate.dialect.ProgressDialect
Mckoi SQL
org.hibernate.dialect.MckoiDialect
Interbase
org.hibernate.dialect.InterbaseDialect
Pointbase
org.hibernate.dialect.PointbaseDialect
FrontBase
org.hibernate.dialect.FrontbaseDialect
Firebird
org.hibernate.dialect.FirebirdDialect

 

下图的左面是通过JDBC API访问数据库,而右图则是通过Hibernate API访问数据库。
             

Hibernate中的核心接口API类的介绍---Session接口
(1)它是轻量级的类
轻量级的类
对于Hibernate 开发人员来说它是一个最重要的接口。然而在Hibernate中,实例化的Session是一个轻量级的类,创建和销毁它都不会占用很多资源。这在实际项目中确实很重要,因为在客户程序中,可能会不断地创建以及销毁Session对象,如果Session的开销太大,会给系统带来不良影响。
是非线程安全的
但值得注意的是Session对象是非线程安全的,因此在你的设计中,最好是一个线程只创建一个Session对象。
(2)Session看作介于数据连接与事务管理一种中间接口
是一种中间接口
在Hibernate的设计者的头脑中,他们将session看作介于数据连接与事务管理的一种中间接口。我们可以将session想象成一个持久对象的缓冲区,Hibernate能检测到这些持久对象的改变,并及时刷新数据库。
每一个Session实例和一个数据库事务绑定
通常将每一个Session实例和一个数据库事务绑定,也就是说,每执行一个数据库事务,都应该先创建一个新的Session实例。如果事务执行中出现异常,应该撤销事务。不论事务执行成功与否,最后都应该调用Session的close()方法,从而释放Session实例占用的资源。
(3)如何获得Session对象
首先创建SessionFactory对象
应用程序如果访问多个数据源时,则应该产生多个SessionFactory;但是仅仅为了服务于某个请求你不要创建一个新的SessionFactory,因为创建SessionFactory 需要耗费大量的资源。
然后根据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同时也是一个持久层管理器
持久层管理器
我们有时也称Session是一个持久层管理器,因为它包含这一些持久层相关的操作,诸如存储持久对象至数据库,以及从数据库从获得它们。
持久管理器所提供的服务
基本的CURD操作(save()方法:把Java对象保存数据库中、update()方法:更新数据库中的Java对象、delete()方法:把Java对象从数据库中删除、load()方法:从数据库中加载Java对象 和find()方法:从数据库中查询Java对象);
执行查询;
控制事务;
事务级的缓存管理;
持久管理器所暴露的几个不同的接口
就Hibernate 来说是----Session,Query,Criteria 和Transaction等接口。
所应该注意点
请注意,Hibernate 的session不同于JSP应用中的HttpSession。当我们使用session这个术语时,我们指的是Hibernate中的session,而我们以后会将HttpSesion对象称为用户session。
(5)Session的编程规则---需要随时更新和释放
Session是一个轻量级对象。通常将每一个Session实例和一个数据库事务绑定,也就是说,每执行一个数据库事务,都应该先创建一个新的Session实例。如果事务执行中出现异常,应该撤销事务。
不论事务执行成功与否,最后都应该调用Session的close()方法,从而释放Session实例占用的资源。

(6)Sessin 接口中的常用方法
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();
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();
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;
}
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();

Hibernate中的核心接口API类的介绍---SessionFactory 接口
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不会启用这个缓存插件。外置缓存能存放大量数据库数据的拷贝,外置缓存的物理介质可以是内存或者硬盘。
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进行配置
Configuration接口的作用是对Hibernate进行配置,以及对它进行启动
在Hibernate的启动过程中,Configuration类的实例首先定位映射文档的位置,读取这些配置,然后创建一个SessionFactory对象。
根据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");

Hibernate中的核心接口API类的介绍---Transaction 接口
(1)Transaction接口是一个可选的API
事务将应用代码从下层的事务实现中抽象出来——这可能是一个JDBC事务,一个JTA用户事务或者甚至是一个公共对象请求代理结构(CORBA)——允许应用通过一组一致的API控制事务边界。

(2)为什么要提供该Transaction 接口
Transaction接口是对实际事务实现的一个抽象。之所以这样设计是能让开发者能够使用一个统一事务的操作界面,使得自己的项目可以在不同的环境和容器之间方便地移值。

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接口非常类似,它允许你创建并执行面向对象的标准化查询。


        Hibernate----关联映射
、映射定义文件的各个标签的说明
(1)<hibernate-mapping>为根节点,其可选的属性如下
<hibernate-mapping
         schema="schemaName"                          
         catalog="catalogName"                                 
         default-cascade="cascade_style"                   
         default-access="field|property|ClassName"           
         default-lazy="true|false"                          
         auto-import="true|false"                          
         package="package.name"                         
 />
schema (可选): 数据库schema的名称。
catalog (可选): 数据库catalog的名称。
 注意:
该两个属性指明了这个映射所连接(refer)的表所在的schema和/或catalog名称。
假若指定了这个属性,表名会加上所指定的schema和catalog的名字扩展为全限定名。
假若没有指定,表名就不会使用全限定名。
default-cascade (可选 - 默认为 none): 默认的级联风格。
default-access (可选 - 默认为 property): Hibernate用来访问属性的策略。可以通过实现PropertyAccessor接口 自定义。
default-lazy (可选 - 默认为 true): 指定了未明确注明lazy属性的Java属性和集合类, Hibernate会采取什么样的默认加载风格。
auto-import (可选 - 默认为 true): 指定我们是否可以在查询语言中使用非全限定的类名(仅限于本映射文件中的类)。
注意:
假若我们有两个持久化类,它们的非全限定名是一样的(就是两个类的名字一样,但所在的包名称不一样), 你应该设置auto-import="false"。假若说我们把一个“import过”的名字同时对应两个类, Hibernate会抛出一个异常。 
package (可选): 指定一个包前缀,如果在映射文档中没有指定全限定的类名, 就使用这个作为包名。

(2)<class>子标签
<hibernate-mapping>根节点下一个节点是<class>,最简单的情况下,<class>节点必须有name和table两个属性,前者描述对象所属的类,后者描述映射到的数据表。
<class  name="examples.Cat"   table="CAT">

<class>
注意的问题
所有的持久性实体类(persistent entity classes)都需要一个这样的映射----来映射到我们的SQL 数据库表。
通过它可以告诉Hibernate怎样从数据库表(table)中持久化和加载实体类的对象,每个实体类的对象实例对应数据库里面的一行(一条记录)。
一个持久化类对应一个映射文件
<hibernate-mapping>根节点元素允许嵌套多个如上所示的 <class>映射。但是最好的做法(也许一些工具需要的)是一个持久化类(或一个类的继承层次)对应一个映射文件
对象的属性分为主键类和非主键类

(3)<class>子标签中的主键类属性使用<id>标示
主键类属性使用<id>标示
每个持久化类都应该有一个标识属性(实际上,这个类只代表实体,而不是独立的值类型类,后者会被映射称为实体对象中的一个组件)。这个属性用来区分持久化对象:如果catA.getId().equals(catB.getId())结果是true的话,这两个Cat就是相同的。这个概念称为数据库标识(也就是如果多个Cat类的实例对象拥有相同的id,那它们代表数据库某个表的同一个记录)。
并且需要填写<generator>,即生成主键的方式----指定了标识符的生成策略
Hiernate附带了几种不同的标识符生成器,用于不同的场合-----可以为:Hi/Low、UUID、Identity、Assigned等(请见下面的说明)。在此例中我们在这里使用UUID生成器(只在测试时建议使用,如果使用数据库自己生成的整数类型的键值更好),并指定CAT表中的CAT_ID字段(作为表的主键)存放生成的标识值。
可选的<generator>子标签中的class属性是一个Java类的名字
该<generator>子标签用来为该持久化类的实例生成唯一的标识。如果这个生成器实例需要某些配置值或者初始化参数, 用<param>元素来传递。
<id name="id" type="long" column="cat_id">
        <generator class="org.hibernate.id.TableHiLoGenerator">
                <param name="table">uid_table</param>
                <param name="column">next_hi_value_column</param>
        </generator>
</id>
(4)<class>子标签中的非主键类属性使用<property>等标示
Cat的其他属性都映射到同一个表的字段
对name属性来说,我们把它显式地声明映射到一个数据库字段-----同时也告诉Hibernate使用哪个getter和setter方法。
为什么name属性的映射包括column参数,但是sex和weight却没有?
当没有设定column参数的时候,Hibernate缺省使用属性名作为字段(column)名----也就是对sex和weight属性而言,数据库表中的字段(column)名也应该为sex和weight----采用Hibernate的默认值映射,大多数情况你都会这样做。

9、数据库表主键的知识点:
(1)<generator>子标签
<generator>子标签为每个 POJO 的实例提供唯一标识。一般情况,我们使用“native”。class 表示采用由生成器接口org.hibernate.id.IdentifierGenerator 实现的某个实例,该实例可以为下面的某种形式。
(2)<generator >标签中的class属性的取值
所有的生成器都实现org.hibernate.id.IdentifierGenerator接口,Hibernate提供了很多内置的实现。下面是一些内置生成器的快捷名字:
“assigned”
让应用程序在save()之前为对象分配一个标示符。这是 <generator>元素没有指定时的默认生成策略。
如果你需要应用程序分配一个标示符(而非Hibernate来生成),你可以使用assigned 生成器。这种特殊的生成器会使用已经分配给对象的标识符属性的标识符值。 这个生成器使用一个自然键(natural key,有商业意义的列-译注)作为主键,而不是使用一个代理键( surrogate key,没有商业意义的列-译注)。
当选择assigned生成器时,除非有一个version或timestamp属性,或者你定义了 Interceptor.isUnsaved(),否则需要让Hiberante使用 unsaved-value="undefined",强制Hibernatet查询数据库来确定一个实例是瞬时的(transient) 还是脱管的(detached)。
 
“hilo”
使用一个高/低位算法高效的生成long, short 或者 int类型的标识符。给定一个表和字段(默认分别是是 hibernate_unique_key 和next_hi)作为高位值的来源。 高/低位算法生成的标识符只在一个特定的数据库中是唯一的。
“seqhilo”----与hilo 类似,通过hi/lo 算法实现的主键生成机制,需要数据库中的 Sequence,适用于支持 Sequence 的数据库,如Oracle。
“increment”
用于为long, short或者int类型生成 唯一标识。只有在没有其他进程往同一张表中插入数据时才能使用。 在集群下不要使用。
主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加1作为主键。这种方式可能产生的问题是:不能在集群下使用。
注意:此时的数据库的主键必须为long, integer, short等数据类型,并且必须有初始值。否则会出现下面的错误

“identity”
对DB2,MySQL, MS SQL Server, Sybase和HypersonicSQL的内置标识字段提供支持。返回的标识符是long, short 或者int类型的。
<id name="id" type="long" unsaved-value="0">
     <column name=" person_id " sql-type="numeric"  not-null="true" />
     <generator class="identity" />
</id>
也可以写成:
<id name="id" type="long" column="person_id" unsaved-value="0">
      <generator class="identity"/>
</id>
注意:
这里主要是“identity”代表由MSSql Server2000数据库自己提供自增字段。并且此时的数据库的主键必须为long, integer, short等数据类型,否则会出现下面的错误。

“sequence”
采用数据库提供的 sequence 机制生成主键。如 Oralce 中的Sequence。
在DB2,PostgreSQL, Oracle, SAP DB, McKoi中使用序列(sequence), 而在Interbase中使用生成器(generator)。返回的标识符是long, short或者 int类型的。
<id name="id" unsaved-value="0">
      <generator class="sequence">
        <param name="sequence">SEQ_CHILD</param>
      </generator>
</id>
使用的是sequence,适合oracle数据库;
“native”-----由 Hibernate 根据使用的数据库自行判断采用 identity、hilo、sequence 其中一种作为主键生成方式(此时的数据库的主键必须为long, integer, short等数据类型)。
“uuid.hex”
用一个128-bit的UUID算法生成16 进制数值(编码后以长度32 的字符串表示)作为主键, 这在一个网络中是唯一的(使用了IP地址, JVM的启动时间----精确到1/4秒,系统时间和一个计数器值-----在JVM中唯一)。UUID被编码为一个32位16进制数字的字符串。
  <id name="id" type="string" unsaved-value="null">
      <column name="cid" sql-type="char(32)" not-null="true" />
      <generator class="uuid.hex" />
  </id>
如果表中的主键用字符串类型,可以用hibernate自己提供的方法实现主键唯一。使用的是uuid.hex, 采用128位的算法来生成一个32位字符串。最通用的一种方式。适用于所有数据库。

一对一关联在hibernate中有两种方式
主键关联
不需借助外部字段,直接通过两个表的主键进行关联。此时必须保证两个表的主键值一致(也就是id数据的值相等),在Hibernate中通常是借助foreign标识符生成器策略来完成。
简单来说,这种情况就是两个表的主键相等的内连接。 基于主键关联的单向一对一关联通常使用一个特定的id生成器foreign。
<class name="Person">
    <id name="id" column="personId">
        <generator class="native"/>
    </id>
</class>
<class name="Address">
    <id name="id" column="personId">
        <generator class="foreign">
            <param name="property">person</param>
        </generator>
    </id>
    <one-to-one name="person" constrained="true"/>
     </class>
create table Person ( personId bigint not null primary key )
    create table Address ( personId bigint not null primary key )

唯一外键关联
在主动方加入外键进行关联,这样主动方与被动方的影射关系实际上就成了多对一的关联(请见后面的“多对一的关联”示例)。
基于外键关联的单向一对一关联和单向多对一关联几乎是一样的。唯一的不同就是单向一对一关联中的外键字段具有唯一性约束。
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="examples.EBook" table="EBook">
       <id name="id" type="java.lang.Integer" >
            <column name="ebook_id" not-null="true"/>
            <generator class="foreign">
                                        <param name="property">publish</param>
                </generator>
        </id>
        <property name="ebookName">
            <column name="ebookName" length="32" not-null="true"/>
        </property>
        <property name="ebookKind" />
                <property name="ebookPrice" />
<one-to-one name="publish" class="examples.Publish" cascade="none" outer-join="auto" constrained="true" />
    </class>
</hibernate-mapping>
<one-to-one>标签的说明
参考hibernate的官方指导手册,下面给出<one-to-one>标签的说明
<one-to-one
    name="propertyName"                                                                        (1)
    class="ClassName"                                                                                (2)
    cascade="all|none|save-update|delete"                                                (3)
    constrained="true|false"                                                                        (4)
    outer-join="true|false|auto"                                                                (5)
    property-ref="propertyNameFromAssociatedClass"                        (6)
    access="field|property|ClassName"                                                (7)
/>
其中的各个属性的说明如下:
(1) name:映射属性的名称。
(2) class(可选):被关联的类的名称,如果省略此属性,则通过反射机制得到与此属性名称一致的类。
(3) cascade(可选)表明操作是否从父对象级联到被关联的对象。
(4) constrained(可选):表明该类对应的表对应的数据库表,和被关联的对象所对应的数据库表之间,通过一个外键 引用对主键进行约束。这个选项影响save()和delete()在级联执行时的先后顺序。
(5) outer-join(可选):是否允许外连接抓取;默认是auto,关联对象没有采用proxy机制时使用外联接。
(6) property-ref(可选):指定关联类的一个属性,这个属性将会和本外键相对应。默认为关联类的主键。
(7) access(可选):Hibernate用来访问属性的策略,默认是property.
“一对多”关联
“一对多”关系分为“单向一对多”关系和“双向一对多关系”,单向一对多关系只需在“一”方进行配置,双向一对多关系需要在关联双方均加以配置。
(1)“一对多”关联:基于外键关联的一对多关联
<class name="Person">
    <id name="id" column="personId">
        <generator class="native"/>
    </id>
    <set name="addresses" table="Address">
        <key column="personId"   not-null="true"/>
        <one-to-many class="Address"/>
    </set>
</class>
<class name="Address">
    <id name="id" column="addressId">
        <generator class="native"/>
    </id>
</class>
set的table参数决定用于关联的数据库表名,并且其name属性的值“addresses”应该与Person类中的addresses属性同名。key元素定义了在集合表中使用的外键。
create table Person ( personId bigint not null primary key )
create table Address ( addressId bigint not null primary key, personId bigint not null )

 


Person.hbm.xml映射文件的内容
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
     <class name="examples.Person" table="PERSON">
        <id name="id" column="PERSON_ID"  unsaved-value="0">(主键属性不是对象型,而是基本类型,如int/long/                                                                                                                         double/...,那么我们需要指定一个数值型的unsaved-                                                                                                                        value)
            <generator class="increment"/>
        </id>
        <property name="age"/>
        <property name="firstname"/>
        <property name="lastname"/>

<set name="cats" table="CAT" inverse="true">
    <key column="PERSON_ID"/>
    <one-to-many  class="examples.Cat"/>
</set>
</class>
</hibernate-mapping>

关于inverse="true"或者inverse="false"的说明
Inverse,直译为“反转”。在Hibernate语义中,Inverse指定了关联关系中的方向。关联关系中,inverse=”false”的为主动方,由主动方负责维护关联关系。而inverse="true"则表示由被动方负责维护关联关系。
但要注意的是,一般在“one-to-many”关系中,将“many”一方设为主动方(inverse=true)将有助性能的改善。(现实中也一样,如果要让胡锦涛记住全国人民的名字,估计花个几十年也不可能,但要让全国人民知道胡锦涛,可就不需要那么多时间了)。

多对一(many to one) 实体之间的关联示例
设计实体之间的关联时,我们需要考虑的问题是关联的方向(directionality),阶数(multiplicity)和集合(collection)的行为。
<class name="Person">
            <id name="id" column="personId">
                <generator class="native"/>
    </id>
            <many-to-one name="address"         column="addressId"  not-null="true"/>
</class>
<class name="Address">
            <id name="id" column="addressId">
                <generator class="native"/>
    </id>
</class>

create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )


一个BankUser可以拥有一系列的Card,而某个Card也只可以属于一个主人(BankUser)。


注意:
        为了能够实现Card-BankUser的“多对一”关联,则应该在这个“多端”的Card表的一个外键(PERSON_ID)引用“一端”的目标表(BankUser)的主键字段(PERSON_ID)。

多对一(many-to-one)
通过many-to-one元素,可以定义一种常见的与另一个持久化类的关联(这个表的一个外键引用目标表的主键字段)。
<many-to-one
        name="propertyName"                                          (1)
        column="column_name"                                         (2)
        class="ClassName"                                                  (3)
        cascade="cascade_style"                                                   (4)
        fetch="join|select"                                                                (5)
        update="true|false"                                              (6)
        insert="true|false"                                               (6)
        property-ref="propertyNameFromAssociatedClass"                    (7)
        access="field|property|ClassName"                                 (8)
        unique="true|false"                                              (9)
        not-null="true|false"                                             (10)
        optimistic-lock="true|false"                                       (11)
        lazy="true|proxy|false"                                           (12)
        not-found="ignore|exception"                                     (13)
        entity-name="EntityName"                                       (14)
        node="element-name|@attribute-name|element/@attribute|."
        embed-xml="true|false" />
(1) name: 属性名。 
(2) column (可选): 外间字段名。它也可以通过嵌套的 <column>元素指定。 
(3) class (可选 - 默认是通过反射得到属性类型): 关联的类的名字。 
(4) cascade(级联) (可选): 指明哪些操作会从父对象级联到关联的对象。 
cascade属性设置为除了none以外任何有意义的值, 它将把特定的操作传播到关联对象中。这个值就代表着Hibernate基本操作的名称, persist, merge, delete, save-update, evict, replicate, lock, refresh, 以及特别的值delete-orphan和all,并且可以用逗号分隔符 来合并这些操作,例如,cascade="persist,merge,evict"或 cascade="all,delete-orphan"。
(5) fetch (可选 - 默认为 select): 在外连接抓取(outer-join fetching)和序列选择抓取(sequential select fetching)两者中选择其一。 
(6) update, insert (可选 - defaults to true) 指定对应的字段是否包含在用于UPDATE 和/或 INSERT 的SQL语句中。如果二者都是false,则这是一个纯粹的 “外源性(derived)”关联,它的值是通过映射到同一个(或多个)字段的某些其他属性得到 或者通过trigger(触发器)、或其他程序。 
(7) property-ref: (可选) 指定关联类的一个属性,这个属性将会和本外键相对应。 如果没有指定,会使用对方关联类的主键。 
(8) access (可选 - 默认是 property): Hibernate用来访问属性的策略。 
(9) unique (可选): 使用DDL为外键字段生成一个唯一约束。此外, 这也可以用作property-ref的目标属性。这使关联同时具有 一对一的效果。 
(10) not-null (可选): 使用DDL为外键字段生成一个非空约束。 
(11) optimistic-lock (可选 - 默认为 true): 指定这个属性在做更新时是否需要获得乐观锁定(optimistic lock)。 换句话说,它决定这个属性发生脏数据时版本(version)的值是否增长。 
(12) lazy (可选 - 默认为 proxy): 默认情况下,单点关联是经过代理的。lazy="true"指定此属性应该在实例变量第一次被访问时应该延迟抓取(fetche lazily)(需要运行时字节码的增强)。 lazy="false"指定此关联总是被预先抓取。 
(13) not-found (可选 - 默认为 exception): 指定外键引用的数据不存在时如何处理: ignore会将数据不存在作为关联到一个空对象(null)处理。 
(14) entity-name (optional): 被关联的类的实体名。 

多对多关联
对于多对多关联的实现一般有两种可选方案:
类似一对多情形中的最常用方案,为关联的双方增加到对方的外键,该方案的好处是取数据时不需链接操作,只要读一张表就行,操作比较简单,缺点是会造成数据冗余;
新增一张包含关联双方主键的关联表,这时,在取数据时,需要链接该关联表和数据表,优点是没有数据冗余,缺点是带来了一定的时限复杂度。
所应该注意的是,由于多对多关联的性能不佳(因为引入了中间表,一次读取操作需要反复数次查询),因此在设计中应该避免大量使用。

一。基于连接表的多对多关联
注意:多对多的关联也同样可以采用Set、List等多种方式来配置。
<class name="Person">
    <id name="id" column="personId">
        <generator class="native"/>
    </id>
    <set name="addresses" table="PersonAddress">
        <key column="personId"/>
        <many-to-many column="addressId"   class="Address"/>
    </set>
</class>

<class name="Address">
    <id name="id" column="addressId">
        <generator class="native"/>
    </id>
</class>

create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
create table Address ( addressId bigint not null primary key )


二。决定BookAuthor和Book之间的关系
BookAuthor可以拥有一系列的Book,而某个Book也可以属于不同的作者BookAuthor(合作写的书)。
   这个关联是双向的,而且在这个关联的两端都是“多”。 我们叫这个为:多对多(many-to-many)关联。因此,下面我们将使用Hibernate的many-to-many映射。

 

BookAuthor.hbm.xml映射文件的内容
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
     <class name="examples.BookAuthor" table="BookAuthor">
        <id name="id" column="author_id"  unsaved-value="0" >
            <generator class="increment"/>
        </id>
        <property name="authorAge"/>
        <property name="firstName"/>
        <property name="lastName"/>

    <set name="books" table="Author_Book">
        <key column="author_id"/>
        <many-to-many column="book_id" class="examples.Book"/>
    </set>
</class>
</hibernate-mapping>

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="examples.Book" table="Book">
   <!-- A 32 hex character is our surrogate key. It's automatically
            generated by Hibernate with the UUID pattern. -->
        <id name="id" type="string" unsaved-value="null" >
            <column name="book_id" sql-type="char(32)" not-null="true"/>
            <generator class="uuid.hex"/>
        </id>
        <property name="bookName">
            <column name="bookName" length="32" not-null="true"/>
        </property>
        <property name="bookKind" />
                <property name="bookPrice" />
                <set name="authors" table="Author_Book" inverse="true">
                    <key column="book_id"/>
                    <many-to-many column="author_id" class="examples.BookAuthor"/>
                </set>
    </class>
</hibernate-mapping>

Hibernate的各种集合映射技术
一、Component 映射---这就允许在进行持久层设计的时候采用细粒度级的领域模型进行设计
1、组件(Component)
(1)组件的含义
它是一个被包含的对象,在持久化的过程中,它被当作值类型,而并非一个实体的引用。在本文档中的组件这一术语指的是面向对象的合成概念(而并不是系统构架层次上的组件的概念)。
(2)应用示例
举个例子,我们对人(Person)这个概念可以像下面这样来建模:
public class Person
{
    private java.util.Date birthday;
    private Name name;
    private String key;
    public String getKey() {
        return key;
    }
    private void setKey(String key) {
        this.key=key;
    }
    public java.util.Date getBirthday() {
        return birthday;
    }
    public void setBirthday(java.util.Date birthday) {
        this.birthday = birthday;
    }
    public Name getName() {
        return name;
    }
    public void setName(Name name) {
        this.name = name;
    }
    //......
    //......
}
public class Name
{
    char initial;
    String first;
    String last;
    public String getFirst() {
        return first;
    }
    void setFirst(String first) {
        this.first = first;
    }
    public String getLast() {
        return last;
    }
    void setLast(String last) {
        this.last = last;
    }
    public char getInitial() {
        return initial;
    }
    void setInitial(char initial) {
        this.initial = initial;
    }
}
在持久化的过程中,姓名(Name)可以作为人(Person)的一个组件。需要注意的是:我们应该为姓名的持久化属性定义getter和setter方法,但是我们不需要实现任何的接口或申明标识符字段。

(3)以下是这个例子的Hibernate映射文件
<class name="eg.Person" table="person">
    <id name="Key" column="pid" type="string">
        <generator class="uuid"/>
    </id>
    <property name="birthday" type="date"/>
    <component name="name" class="eg.Name"> <!-- class attribute optional -->
        <property name="initial"/>
        <property name="first"/>
        <property name="last"/>
    </component>
</class>
注意:也就是人员(Person)表中将包括pid, birthday, initial, first和 last等字段。

二、Set 映射
1、Java中的各种集合类(collection)映射
(1)要求:Hibernate要求持久化集合值字段必须声明为接口(请见下面的示例中的说明)。
(2)示例代码
public class Product {
    private String serialNumber;
    private Set parts = new HashSet();   //Set为接口,而HashSet为Set的实现类
   
    public Set getParts() { return parts; }
    void setParts(Set parts) { this.parts = parts; }
    public String getSerialNumber() { return serialNumber; }
    void setSerialNumber(String sn) { serialNumber = sn; }
}
注意:
也可以是其它类型的集合接口(可能是java.util.Set, java.util.Collection, java.util.List, java.util.Map, java.util.SortedSet, java.util.SortedMap 等)。
用于映射集合类的Hibernate映射元素取决于接口的类型。比如, <set> 元素用来映射Set类型的属性(因为set 不包含重复的元素及与我们无关的排序)。
除了<set>,还有<list>, <map>, <bag>, <array> 和 <primitive-array> 映射元素。
import java.util.HashSet;
import java.util.Set;
public class UserInfo {
  private String name;
  private String id;
  private Set addrs = new HashSet();
  public Set getAddrs() {
    return addrs;
  }
  public void setAddrs(Set addrs) {
    this.addrs = addrs;
  }
  public String getId() {
    return id;
  }
  private void setId(String id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void addAddress(String addr) {
    addrs.add(addr);
  }
  public boolean equals(Object other) {
    if (this == other) {
      return true;
    }
    if (! (other instanceof UserInfo)) {
      return false;
    }
    return true;
  }
}
注意:上面的addAddress()方法是为了加入一个一个的邮件地址而另外增加的方法,我们也可以在外部设定好Set对象,再使用setAddrs()方法设定給UserInfo对象。
(4)该类的映射文件
在映射文件上,为了进行Set的映射,我们使用<set>标签来进行设定,如下所示UserInfo.hbm.xml。
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
       <class name="examples.UserInfo" table="UserInfo">
        <id name="id" type="string" unsaved-value="null">          
            <column name="USER_ID" sql-type="char(32)" not-null="true"/>
            <generator class="uuid.hex"/>
        </id>
        <property name="name" type="string" not-null="true">
            <column name="NAME" length="16" not-null="true"/>
        </property>        
        <set name="addrs" table="AddressInfo">
            <key column="USER_ID"/>
            <element type="string" column="Address" not-null="true"/>
        </set>
    </class>
</hibernate-mapping>
从映射文件中我们可以看到,我们使用另一个数据库表AddressInfo来记录Set中真正记录的对象,为了表明AddressInfo中的每一个邮件资料是属于哪一个UserInfo的,我们通过AddressInfo的外鍵USER_ID引用至UserInfo的USER_ID,AddressInfo的USER_ID与UserInfo的USER_ID的內容将会是相同的,而<element>中设定Set所包括的对象的类型,以及它将记录在哪一个字段中。

3、List 映射
(1)问题
下面介绍如果在对象中包括有List类型的属性时如何进行映射,首先我们假设要制作一个文件管理系统,使用者上传的文件名称可能是重复的、具有相同名称。
而在前面的Set集合的示例中所使用的Set是不允许有重复的元素內容,所以这次我们改用List集合。
我们的UserInfo类的代码如下
package examples;
import java.util.*;
public class UserInfo {
  private String id;
  private String name;
  private List files = new ArrayList();
  public List getFiles() {
    return files;
  }
  public void setFiles(List files) {
    this.files = files;
  }
  public String getId() {
  return id;
}
private void setId(String id) {
  this.id = id;
}

  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void addFiles(String name) {
    files.add(name);
  }

  public void addFiles(int i, String name) {
    files.add(i, name);
  }
}
由于我们在代码中使用的是ArrayList,要在Hibernate中将它映射至数据库表中,基本上与映射Set相同,我们使用<list>标签替代<set>标签,而由于List中存储的对象是具有索引值的,所以我们必須額外增加一个字段(如上面的posIndex)来记录对象在List中的位置,这可以使用<index>标签来指定。
(4)我们的映射文件如下
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
       <class name="examples.UserInfo" table="UserInfo">
        <id name="id" type="string" unsaved-value="null">          
            <column name="USER_ID" sql-type="char(32)" not-null="true"/>
            <generator class="uuid.hex"/>
        </id>
        <property name="name" type="string" not-null="true">
            <column name="NAME" length="16" not-null="true"/>
        </property>        
        <list name="files" table="FileInfo">
            <key column="USER_ID"/>
            <index column="posIndex"/>
            <element type="string" column="FileName" not-null="true"/>
        </list>
    </class>
</hibernate-mapping>

注意:与Set类似的是,记录List的FileInfo数据库表中,使用与UserInfo表相同的USER_ID值。
4、Map 映射
(1)问题的背景
假設我们現在要设计一个在线文件管理系统,每一个用户可以上传自己的文件,并为每个文件加上描述。此时我们可以使用Map类型的对象来记录所上传的各个文件的信息。下面我们以文件的描述作为鍵(key),以文件的名称作为值(value)。
UserInfo类的设计如下
package examples;
import java.util.*;
public class UserInfo {
  private String id;
  private String name;
  public String getId() {
  return id;
}
private void setId(String id) {
  this.id = id;
}
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  private Map files = new HashMap();
  public Map getFiles() {
    return files;
  }
  public void setFiles(Map files) {
    this.files = files;
  }
  public void addFiles(String des, String name) {
    files.put(des, name);
  }
}
(4)映射文件
现在需要在数据库中映射Map,我们在映射文件中使用<map>标签,而索引行則使用<index>标签指定,并使用Hibernate中的string类型,作为记录文件描述信息的行,我们的映射文件如下:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
       <class name="examples.UserInfo" table="UserInfo">
        <id name="id" type="string" unsaved-value="null">          
            <column name="USER_ID" sql-type="char(32)" not-null="true"/>
            <generator class="uuid.hex"/>
        </id>
        <property name="name" type="string" not-null="true">
            <column name="NAME" length="16" not-null="true"/>
        </property>         
       <map name="files" table="FileInfo">
            <key column="USER_ID"/>
            <index column="Description" type="string"/>
            <element type="string" column="FileName" not-null="true"/>
        </map>
    </class>
</hibernate-mapping>

我们使用两个数据库表来分別映射UserInfo与其中包括的Map类型的数据,其中两个数据库表共用相同的USER_ID。

关于Set与Map集合的排序问题
在查询对象的Set或Map成員时,我们可以对其进行排序,排序可以在两个层次上进行,一个是在Java的执行环境中进行,另一个是利用数据库本身的排序功能。
如果要在Java执行环境中进行排序,可以在映射文件中设定sort属性,例如若为Set,则如下设定:
        <set name="addrs" table="AddressInfo" sort="natural">
            <key column="USER_ID"/>
            <element type="string" column="Address" not-null="true"/>
        </set>
在上面指定sort为natural,Hibernate在载入数据库的数据时,将使用java.util.SortedSet类型的对象,如果是String,则根据compareTo()方法来进行排序,上面的设定将会根据addrs進行排序。
如果是Map集合的話,则下设定:
<map name="files" table="FileInfo" sort="natural">
            <key column="USER_ID"/>
            <index column="Description" type="string"/>
            <element type="string" column="FileName" not-null="true"/>
        </map>
上面的设定将使用java.util.SortedTree,根据Description进行排序,sort除了设定natural之外,也可以指定一个实现了java.util.Comparator的类名称。
如果是利用数据库本身的排序功能,則使用order-by设定排序的方式,Hibernate会使用SQL语句在数据库中进行排序,例如在Set中是这么设定的:
        <set name="addrs" table="AddressInfo" order-by="Address desc">
            <key column="USER_ID"/>
            <element type="string" column="Address" not-null="true"/>
        </set>
         在Map中也是相同的设定方式,我们也可以利用数据库中的函数功能,例如:
<map name="files" table="FileInfo" order-by="lower(FileName)">
            <key column="USER_ID"/>
            <index column="Description" type="string"/>
            <element type="string" column="FileName" not-null="true"/>
        </map>
使用这个方法进行排序时,Hibernate会使用LinkedHashSet或LinkedHashMap实现查询时的排序,所以这个方法仅仅适用于JDK 1.4或以上的版本。

Hibernate中的继承关系的映射实现
1、继承关系的描述
在域模型中,类与类之间除了关联关系和聚集关系,还可以存在继承关系,在下面的图中所示的域模型中,Company类和Employee类之间为一对多的双向关联关系(假定不允许雇员同时在多个公司兼职),Employee类为抽象类,因此它不能被实例化,它有两个具体的子类:HourlyEmployee类和SalariedEmployee类。
由于Java只允许一个类最多有一个直接的父类,因此Employee类、HourlyEmployee类和SalariedEmployee类构成了一棵继承关系树。

2、OOP中的多态的概念---OOP中的多态
在面向对象的范畴中,还存在多态的概念,多态建立在继承关系的基础上。简单地理解,多态是指当一个Java应用变量被声明为Employee类时,这个变量实际上既可以引用HourlyEmployee类的实例,也可以引用SalariedEmployee类的实例。
以下这段程序代码就体现了多态:
List employees= businessService.findAllEmployees();
Iterator it=employees.iterator();
while(it.hasNext())
{
          Employee oneEmployee =(Employee)it.next();
     if(oneEmployee instanceof HourlyEmployee)
{
System.out.println(oneEmployee.getName()+" "+((HourlyEmployee) oneEmployee).getRate());
        }
else
System.out.println(oneEmployee.getName()+" "+((SalariedEmployee) oneEmployee).getSalary());
  }
3、多态查询
(1)多态查询
BusinessService类的findAllEmployees()方法通过Hibernate API从数据库中检索出所有Employee对象。在findAllEmployees()方法返回的集合中既包含有HourlyEmployee类的实例,同时也包含SalariedEmployee类的实例,这种查询被称为多态查询。
以上程序中变量oneEmployee被声明为Employee类型,它实际上既可能引用HourlyEmployee类的实例,也可能引用SalariedEmployee类的实例。
(2)多态关联
此外,从Company类到Employee类为多态关联,因为Company类的employees集合中可以包含HourlyEmployee类和SalariedEmployee类的实例。从Employee类到Company类不是多态关联,因为Employee类的company属性只会引用Company类本身的实例。
数据库表之间并不存在继承关系,那么如何把域模型的继承关系映射到关系数据模型中呢
4、实现继承关系的三种映射方式
(1)继承关系树的每个具体类对应一个表
关系数据模型完全不支持域模型中的继承关系和多态。
(2)继承关系树的根类对应一个表
对关系数据模型进行非常规设计,在数据库表中加入额外的区分子类型的字段。通过这种方式,可以使关系数据模型支持继承关系和多态。
(3)继承关系树的每个类对应一个表
在关系数据模型中用外键参照关系来表示继承关系。
以上每种映射方式都有利有弊,下面介绍每种映射方式的具体实现步骤,同时也还介绍了它们的适用范围。


上面是我对老师文档的一个摘要整理如果想得到完整文档(上面没有包括的有缓存管理,事务管理,查询语言HQL,利用Query等接口实现查询等等)请给我发邮件wangj@hcycom.com.cn。如果文档有不妥之处请大虾们不吝赐教。如果转载请说明出处。
作者:北京华晨阳软件工程师王璟

posted on 2006-05-09 12:06 wangj 阅读(1240) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航:
博客园   IT新闻   Chat2DB   C++博客   博问  
 
<2025年7月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

留言簿

文章档案

搜索

  •  

最新评论