标签:
•建立域模型和关系数据模型有着不同的出发点:
•Hibernate 把持久化类的属性分为两种:
•显然无法直接用 property 映射 pay 属性
•Hibernate 使用 <component> 元素来映射组成关系, 该元素表名 pay 属性是 Worker 类一个组成部分, 在 Hibernate 中称之为组件
<!-- 映射组成关系 --> <component name="pay" class="Pay"> <parent name="worker"/> <!-- 指定组成关系的组件的属性 --> <property name="monthlyPay" column="MONTHLY_PAY"></property> <property name="yearPay" column="YEAR_PAY"></property> <property name="vocationWithPay" column="VOCATION_WITH_PAY"></property> </component>
注意parent元素指定组件属性所属的整体类,–name: 整体类在组件类中的属性名。所以在Pay中,要有个属性 Woker worker。
•单向 n-1 关联只需从 n 的一端可以访问 1 的一端
•域模型: 从 Order 到 Customer 的多对一单向关联需要在Order 类中定义一个 Customer 属性, 而在 Customer 类中无需定义存放 Order 对象的集合属性
•关系数据模型:ORDERS 表中的 CUSTOMER_ID 参照 CUSTOMER 表的主键
<!-- 映射多对一的关联关系。 使用 many-to-one 来映射多对一的关联关系 name: 多这一端关联的一那一端的属性的名字 class: 一那一端的属性对应的类名 column: 一那一端在多的一端对应的数据表中的外键的名字 --> <many-to-one name="customer" class="Customer" column="CUSTOMER_ID"></many-to-one>
查询操作:若查询多的一端的一个对象, 则默认情况下, 只查询了多的一端的对象. 而没有查询关联的 1 的那一端的对象! 在需要使用到关联的对象时, 才发送对应的 SQL 语句. 在查询 Customer 对象时, 由多的一端导航到 1 的一端时, 若此时 session 已被关闭, 则默认情况下会发生 LazyInitializationException 异常
获取 Order 对象时, 默认情况下, 其关联的 Customer 对象是一个代理对象!
删除操作: 在不设定级联关系的情况下, 且 1 这一端的对象有 n 的对象在引用, 不能直接删除 1 这一端的对象
•域模型:从 Order 到 Customer 的多对一双向关联需要在Order 类中定义一个 Customer 属性, 而在 Customer 类中需定义存放 Order 对象的集合属性
•关系数据模型:ORDERS 表中的 CUSTOMER_ID 参照 CUSTOMER 表的主键
•当 Session 从数据库中加载 Java 集合时, 创建的是 Hibernate 内置集合类的实例, 因此在持久化类中定义集合属性时必须把属性声明为 Java 接口类型•在定义集合属性时, 通常把它初始化为集合实现类的一个实例. 这样可以提高程序的健壮性, 避免应用程序访问取值为 null 的集合的方法抛出 NullPointerException
•Hibernate 使用 <set> 元素来映射 set 类型的属性
<!-- 映射 1 对多的那个集合属性 --> <!-- set: 映射 set 类型的属性, table: set 中的元素对应的记录放在哪一个数据表中. 该值需要和多对一的多的那个表的名字一致 --> <!-- inverse: 指定由哪一方来维护关联关系. 通常设置为 true, 以指定由多的一端来维护关联关系 --> <!-- cascade 设定级联操作. 开发时不建议设定该属性. 建议使用手工的方式来处理 --> <!-- order-by 在查询时对集合中的元素进行排序, order-by 中使用的是表的字段名, 而不是持久化类的属性名 --> <set name="orders" table="ORDERS" inverse="true" order-by="ORDER_NAME DESC"> <!-- 执行多的表中的外键列的名字 --> <key column="CUSTOMER_ID"></key> <!-- 指定映射类型 --> <one-to-many class="Order"/> </set>
<many-to-one name="customer" class="Customer" column="CUSTOMER_ID"></many-to-one>
•对于基于外键的1-1关联,其外键可以存放在任意一边,在需要存放外键一端,增加many-to-one元素。为many-to-one元素增加unique=“true” 属性来表示为1-1关联
<!-- 使用 many-to-one 的方式来映射 1-1 关联关系 --> <many-to-one name="mgr" class="com.atguigu.hibernate.one2one.foreign.Manager" column="MGR_ID" unique="true"></many-to-one>
•另一端需要使用one-to-one元素,该元素使用 property-ref 属性指定使用被关联实体主键以外的字段作为关联字段
<one-to-one name="dept" class="com.atguigu.hibernate.one2one.foreign.Department" property-ref="mgr"></one-to-one>
注意: 在两个持久化类里都要有另一方的成员变量及Getter,Setter 方法。
保存操作:建议先保存没有外键列的那个对象. 这样会减少 UPDATE 语句
在查询没有外键的实体对象时, 使用的左外连接查询, 一并查询出其关联的对象并已经进行初始化.
•基于主键的映射策略:指一端的主键生成器使用 foreign 策略,表明根据”对方”的主键来生成自己的主键,自己并不能独立生成主键. <param> 子元素指定使用当前持久化类的哪个属性作为 “对方”
•constrained(约束):指定为当前持久化类对应的数据库表的主键添加一个外键约束,引用被关联的对象(“对方”)所对应的数据库表主键
1 <hibernate-mapping package="com.atguigu.hibernate.one2one.primary"> 2 3 <class name="Department" table="DEPARTMENTS"> 4 5 <id name="deptId" type="java.lang.Integer"> 6 <column name="DEPT_ID" /> 7 <!-- 使用外键的方式来生成当前的主键 --> 8 <generator class="foreign"> 9 <!-- property 属性指定使用当前持久化类的哪一个属性的主键作为外键 --> 10 <param name="property">mgr</param> 11 </generator> 12 </id> 13 14 <property name="deptName" type="java.lang.String"> 15 <column name="DEPT_NAME" /> 16 </property> 17 18 <!-- 19 采用 foreign 主键生成器策略的一端增加 one-to-one 元素映射关联属性, 20 其 one-to-one 节点还应增加 constrained=true 属性, 以使当前的主键上添加外键约束 21 --> 22 <one-to-one name="mgr" class="Manager" constrained="true"></one-to-one> 23 24 </class> 25 </hibernate-mapping>
1 <hibernate-mapping> 2 3 <class name="com.atguigu.hibernate.one2one.primary.Manager" table="MANAGERS"> 4 5 <id name="mgrId" type="java.lang.Integer"> 6 <column name="MGR_ID" /> 7 <generator class="native" /> 8 </id> 9 10 <property name="mgrName" type="java.lang.String"> 11 <column name="MGR_NAME" /> 12 </property> 13 14 <one-to-one name="dept" 15 class="com.atguigu.hibernate.one2one.primary.Department"></one-to-one> 16 17 </class> 18 19 </hibernate-mapping>
注意: 同样, 在两个持久化类里都要有另一方的成员变量及Getter,Setter 方法。
保存操作: 先插入哪一个都不会有多余的 UPDATE,以为Dept表的主键要Manager 生成后才有。
•n-n 的关联必须使用连接表
•与 1-n 映射类似,必须为 set 集合元素添加 key 子元素,指定 CATEGORIES_ITEMS 表中参照 CATEGORIES 表的外键为 CATEGORIY_ID. 与 1-n 关联映射不同的是,建立 n-n 关联时, 集合中的元素使用 many-to-many. many-to-many 子元素的 class 属性指定 items 集合中存放的是 Item 对象, column 属性指定 CATEGORIES_ITEMS 表中参照 ITEMS 表的外键为 ITEM_ID
<!-- table: 指定中间表 --> <set name="items" table="CATEGORIES_ITEMS"> <key> <column name="C_ID" /> </key> <!-- 使用 many-to-many 指定多对多的关联关系. column 执行 Set 集合中的持久化类在中间表的外键列的名称 --> <many-to-many class="Item" column="I_ID"></many-to-many> </set>
而 Item 的映射文件里只要正常的属性就行了。
•双向 n-n 关联需要两端都使用集合属性
•双向n-n关联必须使用连接表
•在双向 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.
•对于双向 n-n 关联, 必须把其中一端的 inverse 设置为 true, 否则两端都维护关联关系可能会造成主键冲突.
在上个Category.hbm.xml上,Item.hbm.xm
<set name="categories" table="CATEGORIES_ITEMS" inverse="true"> <key column="I_ID"></key> <many-to-many class="com.atguigu.hibernate.n2n.Category" column="C_ID"></many-to-many> </set>
继承映射
对于面向对象的程序设计语言而言,继承和多态是两个最基本的概念。Hibernate 的继承映射可以理解持久化类之间的继承关系。
•采用 subclass 的继承映射可以实现对于继承关系中父类和子类使用同一张表
•因为父类和子类的实例全部保存在同一个表中,因此需要在该表内增加一列,使用该列来区分每行记录到低是哪个类的实例----这个列被称为辨别者列(discriminator).
•在这种映射策略下,使用 subclass 来映射子类,使用 class 或 subclass 的 discriminator-value 属性指定辨别者列的值
•所有子类定义的字段都不能有非空约束。如果为那些字段添加非空约束,那么父类的实例在那些列其实并没有值,这将引起数据库完整性冲突,导致父类的实例无法保存到数据库中
1 <hibernate-mapping package="com.atguigu.hibernate.subclass"> 2 <class name="Person" table="PERSONS" discriminator-value="PERSON"> 3 4 <id name="id" type="java.lang.Integer"> 5 <column name="ID" /> 6 <generator class="native" /> 7 </id> 8 9 <!-- 配置辨别者列 --> 10 <discriminator column="TYPE" type="string"></discriminator> 11 12 <property name="name" type="java.lang.String"> 13 <column name="NAME" /> 14 </property> 15 16 <property name="age" type="int"> 17 <column name="AGE" /> 18 </property> 19 20 <!-- 映射子类 Student, 使用 subclass 进行映射 --> 21 <subclass name="Student" discriminator-value="STUDENT"> 22 <property name="school" type="string" column="SCHOOL"></property> 23 </subclass> 24 </class> 25 </hibernate-mapping>
插入操作:
* 1. 对于子类对象只需把记录插入到一张数据表中.查询:
* 1. 查询父类记录, 只需要查询一张数据表缺点:
* 1. 使用了辨别者列.•采用 joined-subclass 元素的继承映射可以实现每个子类一张表
•采用这种映射策略时,父类实例保存在父类表中,子类实例由父类表和子类表共同存储。因为子类实例也是一个特殊的父类实例,因此必然也包含了父类实例的属性。于是将子类和父类共有的属性保存在父类表中,子类增加的属性,则保存在子类表中。
•在这种映射策略下,无须使用鉴别者列,但需要为每个子类使用 key 元素映射共有主键。
•子类增加的属性可以添加非空约束。因为子类的属性和父类的属性没有保存在同一个表中
1 <hibernate-mapping package="com.atguigu.hibernate.joined.subclass"> 2 3 <class name="Person" table="PERSONS"> 4 5 <id name="id" type="java.lang.Integer"> 6 <column name="ID" /> 7 <generator class="native" /> 8 </id> 9 10 <property name="name" type="java.lang.String"> 11 <column name="NAME" /> 12 </property> 13 14 <property name="age" type="int"> 15 <column name="AGE" /> 16 </property> 17 18 <joined-subclass name="Student" table="STUDENTS"> 19 <key column="STUDENT_id"></key> 20 <property name="school" type="string" column="SCHOOL"></property> 21 </joined-subclass> 22 23 </class> 24 </hibernate-mapping>
插入操作: 对于子类对象至少需要插入到两张数据表中.
查询:
* 1. 查询父类记录, 做一个左外连接查询优点:
* 1. 不需要使用了辨别者列.•采用 union-subclass 元素可以实现将每一个实体对象映射到一个独立的表中。
•子类增加的属性可以有非空约束 --- 即父类实例的数据保存在父表中,而子类实例的数据保存在子类表中。
•子类实例的数据仅保存在子类表中, 而在父类表中没有任何记录
•在这种映射策略下,子类表的字段会比父类表的映射字段要多,因为子类表的字段等于父类表的字段、加子类增加属性的总和
•在这种映射策略下,既不需要使用鉴别者列,也无须使用 key 元素来映射共有主键.
•使用 union-subclass 映射策略是不可使用 identity 的主键生成策略, 因为同一类继承层次中所有实体类都需要使用同一个主键种子, 即多个持久化实体对应的记录的主键应该是连续的. 受此影响, 也不该使用 native 主键生成策略, 因为 native 会根据数据库来选择使用 identity 或 sequence.
1 <hibernate-mapping package="com.atguigu.hibernate.union.subclass"> 2 3 <class name="Person" table="PERSONS"> 4 5 <id name="id" type="java.lang.Integer"> 6 <column name="ID" /> 7 <generator class="hilo" /> 8 </id> 9 10 <property name="name" type="java.lang.String"> 11 <column name="NAME" /> 12 </property> 13 14 <property name="age" type="int"> 15 <column name="AGE" /> 16 </property> 17 18 <union-subclass name="Student" table="STUDENTS"> 19 <property name="school" column="SCHOOL" type="string"></property> 20 </union-subclass> 21 22 </class> 23 </hibernate-mapping>
插入操作: 对于子类对象只需把记录插入到一张数据表中.
查询:
* 1. 查询父类记录, 需把父表和子表记录汇总到一起再做查询. 性能稍差.缺点:
* 1. 存在冗余的字段
* 2. 若更新父表的字段, 则更新的效率较低
优点:
* 1. 无需使用辨别者列.
* 2. 子类独有的字段能添加非空约束.
标签:
原文地址:http://www.cnblogs.com/linyueshan/p/5808622.html