Hibernate_映射配置文件详解.ppt
映射文件详解,目标,学习Hibernate的配置文件(hibernate.cfg.xml)学习Hibernate的映射声明(*.hbm.xml),Hibernate配置文件,Hibernate配置文件主要用于配置数据库连接和 Hibernate 运行时所需的各种属性每个 Hibernate 配置文件对应一个 Configuration 对象。Hibernate配置文件可以有两种格式:hibernate.propertieshibernate.cfg.xml,hibernate.cfg.xml的常用属性,connection.url:数据库URL connection.username:数据库用户名connection.password:数据库用户密码 connection.driver_class:数据库JDBC驱动 show_sql:是否将运行期生成的SQL输出到日志以供调试。取值 true|false dialect:配置数据库的方言,根据底层的数据库不同产生不同的sql语句,Hibernate 会针对数据库的特性在访问时进行优化。hbm2ddl.auto:在启动和停止时自动地创建,更新或删除数据库模式。取值 create|update|create-dropmapping resource:映射文件配置,配置文件名必须包含其相对于根的全路径connection.datasource:JNDI数据源的名称,jdbc.fetch_size 和 jdbc.batch_size,jdbc.fetch_size:实质是调用 Statement.setFetchSize()方法设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数。例如一次查询1万条记录,对于Oracle的JDBC驱动来说,是不会 1 次性把1万条取出来的,而只会取出Fetch Size条数,当纪录集遍历完了这些记录以后,再去数据库取Fetch Size条数据。因此大大节省了无谓的内存消耗。当然Fetch Size设的越大,读数据库的次数越少,速度越快;Fetch Size越小,读数据库的次数越多,速度越慢。Oracle数据库的JDBC驱动默认的Fetch Size=10,是一个保守的设定,根据测试,当Fetch Size=50的时候,性能会提升1倍之多,当Fetch Size=100,性能还能继续提升20%,Fetch Size继续增大,性能提升的就不显著了。建议使用Oracle时将Fetch Size设到50。并不是所有的数据库都支持Fetch Size特性,例如MySQL就不支持。MySQL就像上面那种最坏的情况,总是一下就把1万条记录完全取出来,内存消耗会非常非常惊人!这个情况就没有什么好办法了hibernate.jdbc.batch_size:设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小,有点相当于设置Buffer缓冲区大小的意思。Batch Size越大,批量操作的向数据库发送sql的次数越少,速度就越快。测试结果是当Batch Size=0的时候,使用Hibernate对Oracle数据库删除1万条记录需要25秒,Batch Size=50的时候,删除仅仅需要5秒!可见有多么大的性能提升!Oracle数据库 Batch Size=30 的时候比较合适。这两个选项非常重要,将严重影响Hibernate的CRUD(create,read,update,delete)性能!,配置 c3p0数据库连接池,c3p0连接池是Hibernate推荐使用的连接池,若需要使用该连接池时,需要将c3p0的jar包拷贝到 WEB-INF 的 lib 目录下,POJO 类和数据库的映射文件*.hbm.xml,POJO 类和关系数据库之间的映射可以用一个XML文档(XML document)来定义。映射按照POJO的定义来创建,而非表的定义。通过 POJO 类的数据库映射文件,Hibernate可以理解持久化类和数据表之间的对应关系,也可以理解持久化类属性与数据库表列之间的对应关系,映射文件示例,映射文件说明,hibernate-mapping类层次:Class主键。id基本类型:property自定义类:many-to-one|one-to-one集合:set|list|map|arrayone-to-manymany-to-many子类:subclass|joined-subclass其它:component|any等查询语句:query(用来放置查询语句,便于对数据库查询的统一管理和优化)注意:一个Hibernate-mapping中可以同时定义多个类。,hibernate-mapping,hibernate-mapping 是 hibernate 映射文件的根元素schema(可选):数据库schema的名称。catalog(可选):数据库catalog的名称。default-cascade(可选-默认为 none):默认的级联风格。default-access(可选-默认为 property):Hibernate用来访问属性的策略。可以通过实现PropertyAccessor接口自定义。default-lazy(可选-默认为 true):指定了未明确注明lazy属性的Java属性和集合类,Hibernate会采取什么样的默认加载风格。auto-import(可选-默认为 true):指定我们是否可以在查询语言中使用非全限定的类名(仅限于本映射文件中的类)。package(可选):指定一个包前缀,如果在映射文档中没有指定全限定的类名,就使用这个作为包名。,class,Class:定义一个持久化类 name(可选):持久化类(或者接口)的类名table(可选-默认是类的非全限定名):对应的数据库表名discriminator-value(可选-默认和类名一样):一个用于区分不同的子类的值,在多态行为时使用。它可以接受的值包括 null 和 not null。,主键-id,Id:被映射的类必须定义对应数据库表主键字段。大多数类有一个JavaBean风格的属性,为每一个实例包含唯一的标识。元素定义了该属性到数据库表主键字段的映射。name(可选):标识持久化类属性的名字。type(可选):标识Hibernate类型的名字。column(可选-默认为属性名):主键字段的名字。,主键生成策略generator,可选的子元素是一个Java类的名字,用来为该持久化类的实例生成唯一的标识。如果这个生成器实例需要某些配置值或者初始化参数,用元素来传递。,主键生成策略generator,所有的生成器都实现org.hibernate.id.IdentifierGenerator接口。某些应用程序可以选择提供他们自己特定的实现。当然,Hibernate提供了很多内置的实现:,推荐使用,基本类型property,property:为类定义了一个持久化的,JavaBean风格的属性 name:属性的名字,以小写字母开头。column(可选-默认为属性名字):对应的数据库字段名。type(可选):一个Hibernate类型的名字。lazy(可选-默认为 false):指定实例变量第一次被访问时,这个属性是否延迟抓取(fetched lazily)。unique(可选):为该字段添加唯一的约束。not-null(可选):为该字段添加非空约束。optimistic-lock(可选-默认为 true):指定这个属性在做更新时是否需要获得乐观锁定(optimistic lock)。,Hibernate内置映射类型,Hibernate内置映射类型,映射集合属性,集合属性大致有两种:单纯的集合属性,如像List、Set或数组等集合属性Map结构的集合属性,每个属性值都有对应的Key映射 集合映射的元素大致有如下几种:list:用于映射List集合属性 set:用于映射Set集合属性 map:用于映射Map集合性 array:用于映射数组集合属性 bag:用于映射无序集合 idbag:用于映射无序集合,但为集合增加逻辑次序,List集合映射,List是有序集合,因此持久化到数据库时也必须增加一列来表示集合元素的次序。看下面的持久化类,该News类有个集合属性:schools,该属性对应学校。而集合属性只能以接口声明,因此下面代码中,schools的类型能是List,不能是ArrayList,但该集合属性必须使用实现类完成初始化。,List集合映射,在作相应映射时,list元素要求用list-index的子元素来映射有序集合的次序列。集合的属性的值会存放有另外的表中,不可能与持久化类存储在同一个表内。因此须以外键关联,用Key元素来映射该外键列。,List集合映射,测试程序:,List集合映射,生成的表及插入的数据:person_table,List集合映射,生成的表及插入的数据:school_table,Set集合映射,Set集合属性映射与List非常相似,但因为Set是无序的,不可重复的集合。因此set元素无须使用index元素来指定集合元素次序。映射文件与List相似,区别在于使用set元素时,无须增加index列来保存集合的次序,Set集合映射,测试程序,Set集合映射,生成的表及插入的数据:school_table,注:映射 Set 集合属性时,如果 element 元素包括 not-null=“true”属性,则集合属性表以关联持久化类的外键和元素列作为联合主键,否则该表没有主键。但 List 集合属性不会,List 集合属性总是以外键列和元素此序列作为联合主键。,bag元素映射,bag元素既可以为List集合属性映射,也可以为Collection集合属性映射。不管是哪种集合属性,使用bag元素都将被映射成无序集合,而集合属性对应的表没有主键。Bag 元素只需要 key 元素来映射外键列,使用 element 元素来映射集合属性的每个元素。,Map集合属性,Map不仅需要映射属性值,还需要映射属性Key。映射Map集合属性时,同样需要指定外键列,同时还必须指定Map的Key列。系统将以外键列和Key列作为联合主键。Map集合属性使用map元素映射时,该map元素需要key和map-key两个子元素。其中key子元素用于映射外键列,而map-key子元素则用于映射Map集合的Key。而map-key和element元素都必须确定type属性,集合属性的性能的分析,对于集合属性,通常推荐使用延迟加载策略。所谓延迟加载就是当系统需要使用集合属性时才从数据库装载关联的数据。Hibernate对集合属性默认采用延迟加载,在某些特殊的情况下为set,,list,map等元素设置lazy=“false”属性来取消延迟加载。可将集合分成如下两类:有序集合:集合里的元素可以根据Key或Index访问 无序集合:集合里的元素中只能遍历 有序集合拥有由 key 和 index 组成的联合主键,集合的属性在增加、删除及修改中拥有较好的性能表现-主键已经被有效的索引,因此 Hibernate 可以迅速的找到该行数据。映射Set集合属性时,如果element元素包括not-null=“true”属性,则集合属性表以关联持久化类的外键和元素列作为联合主键,否则该表没有主键,因此性能较差。在设计较好的Hiberate domain Object中,集合属性通常都会增加inverse=“true”的属性,此时集合端不再控制关联关系。因此无需考虑集合的更新性能。,映射组件属性,组件属性的意思是持久化类的属性既不是基本数据类型,也不是 String 字符串,而是某个组件变量,该组件属性的类型可以是自定义类。,映射组件属性,显然无法直接用 property 映射 name 属性。为了映射组件属性,Hibernate 提供了 component 元素。每个 component 元素映射一个组件属性,组件属性必须指定该属性的类型,component 元素中的 class 属性用于确定组件的类型。,映射组件属性,测试程序,映射组件属性,生成的表及插入的数据:worker_table,集合组件属性映射,集合除了存放 String 字符串以外,还可以存放组件类型。实际上,更多情况下,集合组件存放的都是组件类型。,集合组件属性映射,对于有集合属性 POJO,需要使用 set,list,bag 等集合元素来映射集合属性。如果集合里的元素是普通字符串,则使用 element 映射集合元素即可。如果集合元素也是定义类,则需使用 composite-element 子元素来映射集合元素。composite-element 元素映射一个组件类型,因此需要 class 元素确定元素的类型,该元素还支持 property 的子元素来定义组件类型的子属性,Hibernate 的关联关系映射,客观世界中的对象很少有孤立存在的。关联关系是面向对象分析,面向对象设计最重要的知识。关联关系大致有如下两个分类:单向关系:只需要单向访问关联端单向 1-1单向 1-N(不推荐使用)单向 N-1单向 N-N双向关系:关联的两端可以相互访问双向 1-1双向 1-N双向 N-N,单向 N-1,单向 N-1 关联只需从 N 的一端可以访问 1 的一端。模型:多个人(Person)对应同一个地址(Address)。只需要从人实体端找到相应的地址实体。无须关心从某个地址找到全部住户。,单向 N-1,Person 端增加了 Address 属性,该属性不是一个普通的组件属性,而是引用了另外一个持久化类,使用 many-to-one 元素映射 N-1 的持久化属性。many-to-one 元素的作用类似于 property 元素,用于映射持久化类的某个属性,区别是改元素映射的是关联持久化类。与 property 元素类似,many-to-one 元素也必须拥有 name 属性,用于确定该属性的名字,column 属性确定外键列的列名.,单向 N-1,生成的表person_tableAddress_table,基于外键的单向 1-1,单向 1-1,POJO 与 N-1 没有丝毫区别。基于外键的单向 1-1 映射文件:只需要在原有的 many-to-one 元素添加 unique=“true”,用以表示 N 的一端必须唯一即可,N的一端增加了唯一约束,即成为单向 1-1.person_table,基于主键的单向 1-1,基于主键关联的持久化类不能拥有自己的主键生成器,它的主键由关联类负责生成。增加one-to-one元素来映射关联属性,必须为one-to-one元素增加constrained=true属性,表明该类的主键由关联类生成。,基于主键的单向 1-1,person_table,单向的 1-N,单向 1-N 关联的 POJO 需要使用集合属性。因为一的一端需要访问 N 的一端,而 N 的一端将以集合的形式表现。不推荐使用单向的 1-N 关联:使用 1 的一端控制关联关系时,会额外多出 update 语句。插入数据时无法同时插入外键列,因而无法为外键列添加非空约束,单向的 N-N,单向 N-N,POJO 与 1-N 没有丝毫区别。与映射集合属性类似,必须为set,list等集合元素添加 key 子元素,用以映射关联的外键列。与集合映射不同的是,建立 N-N 关联时,集合中的元素使用 many-to-many,而不是使用 element 子元素N-N 的关联必须使用连接表。,单向的 N-N 的测试程序,单向的 N-N,生成的表及插入的数据:address_tableperson_tableperson_address_table,双向 1-N,对于 1-N 的关联,Hibernate 推荐使用双向关联,而且不要让 1 的一端控制关联关系,而是使用 N 的一端控制关联关系。双向 1-N 与 N-1 是完全相同的两种情形,双向 1-N,1 的一端需要使用集合属性元素来映射关联关系。集合属性元素同样需要增加 key 元素,还需要使用 one-to-many 元素来映射关联属性,双向 1-N,N 的一端需要增加 many-to-one 元素来映射关联属性。,注意:在上面的配置文件中,两个持久化类的配置文件都需要指定外键列的列名,此时不可以省略。因为不使用连接表的1-N关联的外键,而外键只保存在N一端的表中,如果两边指定的外键列名不同,将导致关联映射出错。如果不指定外键列的列名,该列名由系统自动生成,而系统很难保存自动生成的两个列名相同。,双向 1-N,测试程序1:,双向 1-N,生成的表及插入的数据:customers_tableorders_table,双向 1-N,测试程序1产生的 sql 语句:仅仅save(customer),并没有save(order)但却执行了三条SQL,由生成的SQL语句可以知道,将用户 TongGang 添加到Customers表中的同时也将 order1及order2添加到Orders表中!这是因为在 Customers.hbm.xml 映射配置中,set节点的设置了cascade=save-update,所以当保存或更新Customers的时候也会自动保存相应的Orders对象!,双向 1-N,注释掉测试程序 1 中的订单关联用户的代码:生成同样的 sql 语句,同样的数据表customers_table 中插入的数据orders_table中插入的数据在Customers.hbm.xml的set节点中加了属性 inverse=true,这句话的意思是将主控制权交出去:交给了Orders,也就是用户与订单之间从属关系主要是由Orders对象来确定,也即订单自己来决定它属于哪个对象。所以在这里,将订单关联用户的代码注释掉后,虽然后面用户关联了订单,但因为用户已经将主动权交出,所以Hibernate在save订单的时候并不知道订单是属于哪个用户,自然Customers_ID字段填空值。,双向 1-N,将Customers.hbm.xml中的 inverse=true 去掉 生成的 sql 语句:数据库表及插入的数据跟第一种情况相同原来这种情况,Hibernate是先将订单持久化到表中,因为注释了订单关联用户的代码,所以Hibernate还是先插入空值,然后再根据用户关联订单来更新Orders表将 Customers_ID字段修改为正确的值!当数据量很大的时候,这样的操作会影响性能,同时不能将customers_ID字段定义为not null。,双向 1-N,将inverse=true加上,而用户关联订单的注释掉 生成的 sql 语句:运行结果是仅仅将 用户添加到表中去了.关联是仅仅减缓到订单属于哪个用户,也就是关联订单的 customers_ID 字段!但用户类里,属性 Set orders=new HashSet();初始是为空的,这样虽然订单关联了用户,但用户对象内的orders属性还是为空,订单并没有产生,这样Hibernate在保存用户的时候,判断集合为空,不会去添加订单.,inverse,只有集合标记(set/map/list/array/bag)才有inverse属性在 Hibernate 中,inverse 指定了关联关系的方向。关联 关系中 inverse=false 的为主动方,由主动方负责维护 关联关系在没有设置 inverse=true 的情况下,父子两边都维护父子 关系 在 1-N 关系中,将 many 方设为主控方(inverse=false)将有助于性能改善(如果要国家元首记住全国人民的名字,不 是太可能,但要让全国人民知道国家元首,就容易的多)在 1-N 关系中,若将 1 方设为主控方会额外多出 update 语句。插入数据时无法同时插入外键列,因而无法为外键列添加非空约束,cascade,只有关系标记才有cascade属性:many-to-one,one-to-one,set(map,bag,idbag,list,array)+one-to-many(many-to-many)级联指的是当主控方执行操作时,关联对象(被动方)是否 同步执行同一操作。pojo和它的关系属性的关系就是“主控方-被动方”的关 系,如果关系属性是一个set,那么被动方就是set中的每一 个元素。一个操作因级联cascade可能触发多个关联操作。前一个操作 叫“主控操作”,后一个操作叫“关联操作”。inverse 指的是关联关系的控制方向,而cascade指的是层级之间的连锁操作。,cascade,cascade属性的可选值:all:所有情况下均进行关联操作。none:所有情况下均不进行关联操作。这是默认值。save-update:在执行save/update/saveOrUpdate时进行关联操作。delete:在执行delete时进行关联操作delete-orphan:表示删除孤儿,delete-orphan在前者的基础上增加了一点,针对持久化对象,如果它和它所关联的对象的引用关系不存在了,则进行级联删除。all-delete-orphan:包含all和delete-orphan的行为,双向N-N关联,双向N-N关联需要两端都使用集合属性,两端都增加对集合属性的访问。双向N-N关联也必须使用连接表,双向N-N关联,双向N-N的关联映射需要在两边增加集合元素,用于映射集合属性。集合属性应增加key子元素用以映射外键列,集合元素里还应增加many-to-many子元素关联实体类,注意:在双向N-N关联的两边都需定连接表的表名及外键列的列名。两个集合元素set的table元素的值必须指定,而且必须相同。set元素的两个子元素:key和many-to-many都必须指定column属性,其中,key和many-to-many分别指定本持久化类和关联类在连接表中的外键列名,因此两边的key与many-to-many的column属性交叉相同。也就是说,一边的set元素的key的cloumn值为a,many-to-many的column为b;则另一边的set元素的key的column值b,many-to-many的column值为a.,双向1-1关联,单向的1-1关联有三种映射策略:基于主键,基于外键和使用连接表。双向的1-1关联同样有这三种映射策略。双向的1-1关联需要修改POJO类,让两边都增加对关联类的访问,基于外键的双向1-1关联,对于基于外键的1-1关联,其外键可以存放在任意一边,在需要存放外键一端,增加many-to-one元素。为many-to-one元素增加unique=“true”属性来表示为1-1关联,并用name属性来指定关联属性的属性名。另一端需要使用one-to-one元素,则该元素使用name属性指定关联的属性名。为了让系统懂得不再为本表增加一列,因此使用外键关联,用property-ref属性来引用关联类的主键。property-ref:指定目标实体的表中外键引用的列。如果引用表的外键不引用关系的”多”端的主键,可以使用p该属性指定它应用的列。这应该只用于已有数据库的数据库设计-在创建新的数据库模式时,外键总是应该引用相关表的主键,基于外键的双向1-1关联,基于外键的双向1-1关联,生成的表person_tableaddress_table,基于主键的双向1-1关联,基于主键的映射策略:指一端的主键生成器使用foreign略,表明根据对方的主键来生成自己的主键,自己并不能独立生 成主键。任意一边都可以采用foreign主键生成器,表明根据对方主键 生成自己的主键。采用foreign主键生成器策略的一端增加one-to-one元素映射 关联属性,其one-to-one属性还应增加constrained=“true”属 性;另一端增加one-to-one元素映射关联属性。constrained(约束):表明该类对应的表对应的数据库表,和被关联的对象所对应的数据库表之间,通过一个外键引用对主键进行约束。,基于主键的双向1-1关联,基于主键的双向1-1关联,生成的表person_tableaddress_table,继承映射,对于面向对象的程序设计语言而言,继承和多态是两个最基本的概念。Hibernate 的继承映射可以理解持久化类之间的继承关系。例如:人和学生之间的关系。学生继承了人,可以认为学生是一个特殊的人,如果对人进行查询,学生的实例也将被得到。,继承映射,Hibernate支持三种继承映射策略:每个具体类一张表(table per concrete class)将域模型中的每一个实体对象映射到一个独立的表中,也就是说不用在关系开数据模型中考虑域模型中的继承关系和多态。每个类分层结构一张表(table per class hierarchy)对于继承关系中的子类使用同一个表,这就需要在数据库表中增加额外的区分子类类型的字段。每个子类一张表(table per subclass)域模型中的每个类映射到一个表,通过关系数据模型中的外键来描述表之间的继承关系。这也就相当于按照域模型的结构来建立数据库中的表,并通过外键来建立表之间的继承关系。,采用 subclass 元素的继承映射,采用 subclass 元素的继承映射可以实现对于继承关系中的子类使用同一个表在这种映射策略下,整个继承树的所有实例都保保存在同一个表内。因为父类和子类的实例全部保存在同一个表中,因此需要在该表内增加一列,使用该列来区分每行记录到低是哪个类的实例-这个列被称为辨别者列(discriminator).在这种映射策略下,使用 subclass 来映射子类,使用 discriminator-value 指定辨别者列的值,采用 subclass 元素的继承映射,采用 subclass 元素的继承映射,生成的表及插入的数据person_table,注:所有子类定义的字段都不能有非空约束。如果为那些字段添加非空约束,那么父类的实例在那些列根本没有值,这将引起数据库完整性冲突,导致父类的实例无法保存到数据库中,采用 joined-subclass 元素的继承映射,采用 joined-subclass 元素的继承映射可以实现每个子类一张表采用这种映射策略时,父类实例保存在父类表中,子类实例由父类表和子类表共同存储。因为子类实例也是一个特殊的父类实例,因此必然也包含了父类实例的属性。于是将子类和父类共有的属性保存在父类表中,子类增加的属性,则保存在子类表中。在这种映射策略下,无须使用鉴别者列,但需要为每个子类使用 key 元素映射共有主键,该主键必须与父类标识属性的列名相同。但如果继承树的深度很深,可能查询一个子类实例时,需要跨越多个表,因为子类的数据一次保存在多个父类中。子类增加的属性可以添加非空约束。因为子类的属性和父类的属性没有保存在同一个表中,采用 joined-subclass 元素的继承映射,采用 joined-subclass 元素的继承映射,生成的表及插入的数据person_tablestudent_table,采用 union-subclass 元素的继承映射,采用 union-subclass 元素可以实现将每一个实体对象映射到一个独立的表中。union-subclass 与 joined-subclass 映射策略类似:子类增加的属性也可以有非空约束-即父类实例的数据保存在父表中,而子类实例的数据保存在子类表中。与 joined-subclass 不同的是,子类实例的数据仅保存在子类表中,而在父类表中没有任何记录。在这种映射策略下,子类表的字段会比父类表的映射字段要多,因为子类表的字段等于父类表的字段加子类增加属性的总和在这种映射策略下,既不需要使用鉴别者列,也无须使用 key 元素来映射共有主键.,采用 union-subclass 元素的继承映射,采用 union-subclass 元素的继承映射,生成的表及插入的数据person_tablestudent_table,三种继承映射方式的比较,小结,Hibernate配置文件jdbc.fetch_sizejdbc.batch_sizePOJO 类和数据库的映射文件*.hbm.xml主键生成策略generator映射集合属性延迟加载策略映射组件属性关联关系映射:双向 1-N继承映射,