hibernate关联映射
1. 对象之间关联关系:
2. 单向N-1关联(many-to-one)
单向N-1关系,比如多个人对应一个地址,只需从人实体端可以找到对应的地址实体,无须关联某个地址的全部住户。
单向 n-1 关联只能从 n 的一端可以访问 1 的一端。
Demo:
表结构:
Customer(顾客)和Order(订单),其中订单和顾客是N-1关系。所以order中包含Customer的主键:
实体对象:
public class Customer { private String customerId; private String customerName; /*省略get、set方法*/ } ------------------------------------------------------------------------------- public class Order { private String orderId; private String orderName; private Customer customer; /*省略get、set方法*/ } |
映射文件:
只在order中配置many-to-one关系
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping default-lazy="false" package="com.fukai.hibernate">
<class name="Customer" table="customer"> <id name="customerId" type="java.lang.String" column="customerId"> <generator class="uuid" /> </id> <property name="customerName" column="customerName"/> </class> </hibernate-mapping> ---------------------------------------------------------------------------------------------------------------------------------- <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping default-lazy="false" package="com.fukai.hibernate">
<class name="Order" table="order"> <id name="orderId" type="java.lang.String" column="order_id"> <generator class="uuid" /> </id> <property name="orderName" column="order_name"/> <!-- cascade="all",即:保存order时,会首先自动保存关联的customer对象 --> <many-to-one name="customer" class="com.fukai.hibernate.Customer" column="customerId" cascade="all"></many-to-one> </class> </hibernate-mapping> |
3. 双向1-N关联
对于1-N关联,Hibernate推荐使用双向关联,而且不要让1的一端控制关联关系,而使用N的一端控制关联关系。
双向的N-1关联与1-N关联是完全相同的两种情形。两端都需要增加对关联属性的访问,N的一端增加引用到关联实体的属性,1的一端增加集合属性,集合元素为关联实体。
当Session从数据库中加载Java集合时,创建的是Hibernate内置集合类的实例,因此,在持久化类中定义集合属性时,必须把属性声明为Java接口
l Hibernate的内置集合类具有集合代理功能,支持延迟检索策略
l 事实上,Hibernate的内置集合类封装了JDK中的集合类,这使得Hibernate能够对缓存中的集合对象进行脏检查,按照集合对象的状态来同步更新数据库。
在定义集合属性时,通常把它初始化为集合实现类的一个实例,这样可以提高程序的健壮性,避免应用程序访问取值为null的集合的方法。
例如:private Set<Order> orders = new HashSet<Order>();
Demo:
表结构:
实体对象:
Order是N,customer是1。
public class Customer { private String customerId; private String customerName; private Set<Order> orders = new HashSet<Order>();; /*省略get、set方法*/ } ------------------------------------------------------------------------------- public class Order { private String orderId; private String orderName; private Customer customer; /*省略get、set方法*/ } |
配置文件:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping default-lazy="false" package="com.fukai.hibernate">
<class name="Customer" table="customer"> <id name="customerId" type="java.lang.String" column="customerId"> <generator class="uuid" /> </id> <property name="customerName" column="customerName"/> <!-- inverse 属性: 1.在hibernate中通过对 inverse 属性的来决定是由双向关联的哪一方来维护表 和表之间的关系。 inverse = false 的为主动方,inverse = true 的为被动方, 由主动方负责维护关联关系。在没有设置 inverse=true 的情况下,父子两边都维护父子关系 2.在 1-N 关系中,将 N 方设为主控方将有助于性能改善(如果要国家元首记住全国人民的名字,不是太可能,但要让全国人民知道国家元首,就容易的多) 3.在 1-N 关系中,若将 1 方设为主控方,会额外多出 update 语句。插入数据时无法同时插入外键列,因而无法为外键列添加非空约束. order-by 属性: 1.如果设置了该属性, 当 Hibernate 通过 select 语句到数据库中检索集合对象时, 利用 order by 子句进行排序 2.order-by 属性中还可以加入 SQL 函数 --> <set name="orders" inverse="true"> <key column="customer_id"></key> <one-to-many class="Order"/> </set> </class> </hibernate-mapping> ------------------------------------------------------------------------------- <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping default-lazy="false" package="com.fukai.hibernate">
<class name="Order" table="order"> <id name="orderId" type="java.lang.String" column="order_id"> <generator class="uuid" /> </id> <property name="orderName" column="order_name"/> <many-to-one name="customer" class="com.fukai.hibernate.Customer" column="customerId" cascade="all"></many-to-one> </class> </hibernate-mapping> |
4. 双向1-1关联
双向1-1关联需要修改两边的持久化类代码,让两个持久化类都增加引用关联实体的属性,并为该属性提供get和set方法。
双向1-1关联有三种映射模式:
l 基于主键
l 基于外键
l 使用连接表
基于外键:
对于基于外键的1-1关联,外键可以存放在任意一边。需要存放外键的一端,需要增加<many-to-one.../>元素,并且为<many-to-one.../>元素增加unique="true"属性来表示该实体实际上是1的一端。<many-to-one name="manager" class="Manager" column="MGR_ID" unique="true"></many-to-one>
对于1-1的关联关系,两个实体原本处于平等状态,但当我们选择任意一个表来增加外键后(增加<many-to-one.../>元素的实体端),该表即变成从表,而另一个表则成为主表。
另一端需要使用<one-to-one.../>元素,该<one-to-one.../>元素需要使用name属性指定关联属性名。为了让系统不再为本表增加一列,而是使用外键关联,使用property-ref属性指定引用关联类的属性。<one-to-one name="department" class="Department" property-ref="manager"></one-to-one>
Demo:
表结构:
实体对象:
public class Customer { private String customerId; private String customerName; private Order order; /*省略get、set方法*/ } ------------------------------------------------------------------------------- public class Order { private String orderId; private String orderName; private Customer customer; /*省略get、set方法*/ } |
配置文件:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping default-lazy="false" package="com.fukai.hibernate">
<class name="Customer" table="customer"> <id name="customerId" type="java.lang.String" column="customerId"> <generator class="uuid" /> </id> <property name="customerName" column="customerName"/> <one-to-one name = "order" class="Order" property-ref="customer"></one-to-one> </class> </hibernate-mapping> ------------------------------------------------------------------------------- <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping default-lazy="false" package="com.fukai.hibernate">
<class name="Order" table="order"> <id name="orderId" type="java.lang.String" column="order_id"> <generator class="uuid" /> </id> <property name="orderName" column="order_name"/> <many-to-one name="customer" class="com.fukai.hibernate.Customer" column="customerId" unique="true" cascade="all"></many-to-one> </class> </hibernate-mapping> |
基于主键:
两张表的主键保持一致。
如果采用基于主键的映射策略,则一端的主键生成器需要使用foreign策略,表明将根据对方的主键来生成自己的主键,本实体不能拥有自己的主键声称策略。<param>子元素指定使用当前持久化类的哪个属性作为“对方”。
<generator class="foreign" > <param name="property">manager</param> </generator> |
当然,任意一端都可以采用foreign主键生成器策略,表明将根据对方主键来生成自己的主键。
采用foreign主键生成器策略的一端增加one-to-one元素映射相关属性,其ont-to-one属性还应增加constrained=true属性;另一端增加one-to-one元素映射关联属性。
constrained:指定为当前持久化类对应的数据库表的主键添加一个外键约束,引用被关联对象所对应的数据库主键。
表结构:
customerId和order_id相同
实体对象:
public class Customer { private String customerId; private String customerName; private Order order; /*省略get、set方法*/ } ------------------------------------------------------------------------------- public class Order { private String orderId; private String orderName; private Customer customer; /*省略get、set方法*/ } |
配置文件:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping default-lazy="false" package="com.fukai.hibernate">
<class name="Customer" table="customer"> <id name="customerId" type="java.lang.String" column="customerId"> <generator class="foreign"> <param name="property">order</param> </generator> </id> <property name="customerName" column="customerName"/> <one-to-one name = "order" class="Order" constrained="true"></one-to-one> </class> </hibernate-mapping> ------------------------------------------------------------------------------- <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping default-lazy="false" package="com.fukai.hibernate">
<class name="Order" table="order"> <id name="orderId" type="java.lang.String" column="order_id"> <generator class="uuid" /> </id> <property name="orderName" column="order_name"/> <one-to-one name="customer" class="com.fukai.hibernate.Customer"></one-to-one> </class> </hibernate-mapping> |
5. N-N关联
N-N关联关系的建立,需要建立中间表来支持,中间表中使用关联双方实体表的主键作为外键。
单向N-N关联
N-N关联映射增加一张表才完成基本映射。
与1-N映射相似,必须为set集合元素添加key子元素,指定CATEGORIES_ITEMS表中参照CATEGORIES表的外键为CATEGORIY_ID。
与1-N不同的是,建立N-N关联时,集合中的元素使用many-to-many。
表结构:
实体类:
public class Customer { private String customerId; private String customerName; private Set<Order> orders = new HashSet<Order>(); /*省略get、set方法*/ } ------------------------------------------------------------------------------- public class Order { private String orderId; private String orderName; /*省略get、set方法*/ } |
配置文件:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping default-lazy="false" package="com.fukai.hibernate">
<class name="Customer" table="customer"> <id name="customerId" type="java.lang.String" column="customerId"> <generator class="uuid" /> </id> <property name="customerName" column="customerName"/> <set name="orders" table="customer_order"> <key> <column name="customerId"></column> </key> <many-to-many class="order" column="order_id"></many-to-many> </set> </class> </hibernate-mapping> ------------------------------------------------------------------------------- <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping default-lazy="false" package="com.fukai.hibernate">
<class name="Order" table="order"> <id name="orderId" type="java.lang.String" column="order_id"> <generator class="uuid" /> </id> <property name="orderName" column="order_name"/> </class> </hibernate-mapping> |
双向N-N关联
双向N-N关联需要两端都使用set集合属性,两端都增加对集合属性的访问。
在双向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, 否则两端都维护关联关系可能会造成主键冲突。
表结构:
实体类:
public class Customer { private String customerId; private String customerName; private Set<Order> orders = new HashSet<Order>(); /*省略get、set方法*/ } ------------------------------------------------------------------------------- public class Order { private String orderId; private String orderName; private Set<Customer> sustomers = new HashSet<Customer>(); /*省略get、set方法*/ } |
配置文件:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping default-lazy="false" package="com.fukai.hibernate">
<class name="Customer" table="customer"> <id name="customerId" type="java.lang.String" column="customerId"> <generator class="uuid" /> </id> <property name="customerName" column="customerName"/> <set name="orders" table="customer_order"> <key> <column name="customerId"></column> </key> <many-to-many class="order" column="order_id"></many-to-many> </set> </class> </hibernate-mapping> ------------------------------------------------------------------------------- <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping default-lazy="false" package="com.fukai.hibernate">
<class name="Order" table="order"> <id name="orderId" type="java.lang.String" column="order_id"> <generator class="uuid" /> </id> <property name="orderName" column="order_name"/> <set name = "customers" table = "customer_order" inverse="true"> <key> <column name="order_id"></column> </key> <many-to-many class="Customer" column="customerId"></many-to-many> </set> </class> </hibernate-mapping> |
6. 关联关系注释
1)一对一外键关联映射(单向)
@OneToOne(cascade=CascadeType.ALL) //一对一外键关联,使用@OneToOne,并设置了级联操作
@JoinColumn(name="userid",unique=true) //@JoinColum设置了外键的名称为userid(数据库字段名),如果不设置,则默认为另一类的属性名+ _id。外键的值是唯一的(unique),不可重复,与另一类的主键一直
2)一对一外键关联映射(双向)
@OneToOne(mappedBy=" role",cascade=CascadeType.ALL) //一对一双向关联关系,使用@OneToOne。注意:需要加上mappedBy="role",如果不加上的话,role 也会生成一个外键(user_id),mappedby="role"需要指向与他关联对象的一个属性,说明双向关联关系中,有且仅有一端是作为主体(owner)端存在的,主体端负责维护联接列,对于不需要维护这种关系的从表则通过mappedBy属性进行声明,mappedBy的值指向主体的关联属性
//规律:只有是双向关联关系,都加上mappedby,cascade=CascadeType.ALL级联
3)一对一主键关联映射(不重要)
在实际中很少用,使用注解@PrimaryKeyJoinColumn,意思是说,我的主键去参考另外一张表中的主键,作为我的主键,但是在我测试使用注解一对一主键关联映射,在生成表的时候,数据库中并没有生成关联,使用XML映射可以生成。Annotation注解一对一主键关联映,有些bug。不过没空去研究它。因为在实际开发中一对一很少用。在实际开发中我机会没有用过,主键关联就更少了
4)多对一关联映射
多端配置
@ManyToOne(targetEntity=role.class) //多对一注解@ManyToOne;targetEntity指定了关联对象
@JoinColumn(name="userid") //@JoinColumn(name="userid")指定生产的外键的字段名,默认是org_id
5)一对多关联映射(单向)
@OneToMany //一对多注解@OneToMany(单向),如果只写@OneToMany的话,hibernate会建一张中间表来维护他们之间的关系
@JoinColumn(name="roleid") //加上@JoinColumn(name="roleid"),则不会建中间表,他会在多的一端加上外键roleid,来维护他们之间的关系
6)一对多关联映射(双向)
一端配置
@OneToMany(mappedBy="role") //一对多双向,在一的一端中设置mappedBy,说明多的一端为主导
@JoinColumn(name="roleid") //如果指定了外键字段名称,则多的一端也需要指定相同的字段名称
多端配置
@ManyToOne //一对多双向
@JoinColumn(name=" roleid ") //需要指定外键与一的一端给的外键名称一致,@JoinColumn(name=" roleid "),也可以不指定,如果在多的一端不指定,则一的一端也不能指定,否则为生成两个外键
7)多对多关联映射(单向)
@ManyToMany //多对多映射:注解@ManyToMany(单向),默认情况下,hibernate会自动的创建一张中间表来维护多对多关系
默认中间表的名称 :user_role中间表,字段的名称user_id role_id,如果想更换表名和字段名称,注解如下:
@JoinTable(name="t_u_r",joinColumns={@JoinColumn(name="u_id")},inverseJoinColumns={@JoinColumn(name="r_id")})
8)多对多关联映射(双向)
user端
@ManyToMany //多对多映射:注解@ManyToMany(单向);默认情况下,hibernate会自动的创建一张中间表,来维护多对多关系;默认中间表的名称 :user_role中间表,字段的名称user_id role_id
如果想更换表名和字段名称,注解如下:
@JoinTable(name="t_u_r",joinColumns={@JoinColumn(name="u_id")},inverseJoinColumns={@JoinColumn(name="r_id")}) //@JoinTable(name="t_u_r"),指定中间表的表名;joinColumns={@JoinColumn(name="u_id")},指定当前对象的外键;inverseJoinColumns={@JoinColumn(name="r_id")},指定关联对象的外键
role端
@ManyToMany(mappedBy="role") //多对多,双向关联映射