组件属性包含的关联实体
前面已经提到过,组件里的属性不仅可以是基本类型、字符串、日期类型等,也可以是值类型行
的组件,甚至可以是关联实体。
对于组件的属性是关联实体的情形,可以使用@OneToOne、@ OneToMany、@ManyToOne
@ ManyToMany修饰代表关联实体的属性。如果程序采用基于外键的映射策略,还需要配合
@ loinColumn注解——该注解用于映射外键列:如果程序采用基于连接表的映射策略,还需要配@ JoinTable注解—该注解用于映射连接表
下面的 Person实体定义包含一个 Address类型的属性,但这个 Address类型的属性并非代表关,只是一个组件
@Entity
@Table(name="person_inf")
public class Person {
//标识属性
@Id @Column(name="person_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
//定义一个组件
private Address address;
}
上面程序中粗体字代码定义了 Address类型的组件属性, Address组件中包含了一个Set类型的属性,该Set类型的属性负责维护与 School实体之间的1-N关联。
如果程序使用不带连接表的关联策略来维护 Address与 School之间的关联关系,则需要使用aoneToMany、 @JoinColum修饰代表关联实体的Set属性。
如果程序使用带连接表的关联策略来维护 Address与 School之间的关联关系,则需要使用@OneToMany、@ JoinTable修饰代表关联实体的set属性
下面使用不带连接表的关联策略来维护Adess与 School之间的关联关系,因此需要使用@OneToMany、@ JoinColumn修饰 Address里代表关联实体的set属性。下面是Adess类的源代码。
@Entity
@Table(name="address_inf")
public class Address {
Id @Column(name="address_inf")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int addressId;
//定义地址详细信息的成员变量
private String addressDetail;
//定义该组件属性所在的包含实体
@parent
@OneToMany(targetEntity=School.class)
//映射外键列,此处告诉Hibernate在school实体对应的表中增加外键列
//该外键列的列名为address_id,参照person_inf表 的person_id主键列
@JoinColumn(name="address_id",referencedColumnName="person_id")
private Set<School> schools = new HashSet<>();
}
下面师范如何保存Person对象和Address对象
//保存 Person和 Schoo1对象
private void test Person (
Session session- Hibernateutil currentsession ()
Transaction tx- session. beginTransaction ( )i
//创建一个 Person对象
Person p= new Person (
为 crazyit
//设置 Person的
name
P setName("crazyit")i
p staGe(21)i
session. save(p)i
//创建一个 Address对象
Address a= new Address("广州天河")
/设置 Person对象的 Address属性
p. setAddress (a)i
//创建两个 Schoo1对象
School s1= new Schoo1("疯狂os训练营”);
Schoo1s2= new Schoo1("疯狂Java训练营”)
//保存两个 Schoo1实体
session. save(s1)i
session. save(s2)i
//设置 Address对象和两个 Schoo1的关联关系
a. getSchools().add(sl)i
a.getschools ().add(s2)i
tx commitor
Hibernateutil.closesession()
基于符合主键的关联关系
对于1-N的双向关联,1的一端将两个属性结合起来作为符合主键,N的一端依然使用Integer类型的普通主键。
下面是1的一端的源码
@Entity
@Table(name="person_inf")
public class Person implements java.io.Serializable{
//定义first成员变量,作为标识属性的成员
private String first;
//定义last成员变量,作为标识属性的成员
@Id
private String last;
//记录该Person实体关联的所有Address实体
@OneToMany(targetEntity=Address.class,mappedBy="person",cascade=CascadeType.All)
private Set<Address> addresses = new HashSet<>();
//重写equals和hashCode方法
}
下面是Address实体类的代码
@Entity
@Table(name="address_inf")
public class Address {
Id @Column(name="address_inf")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int addressId;
//定义地址详细信息的成员变量
private String addressDetail;
//定义该组件属性所在的包含实体
@ManyToOne(targetEntity=Person.class)
//使用@JoinColumns包含多个@JoinColumn定义外键列
@JoinColumns({//由于主表使用了符合主键(有两个主键列)
//因此需要使用两个@JoinColumn定义外键列来参照person_inf表的两个主键列
@JoinColumn(name="person_first",referencedColumnName="first",nullable=false),
@JoinColumn(name="person_last",referencedColumnName="last",nullable=false)
})
private Person person;
}
复合主键的成员属性为关联实体
例如:订单,商品,订单项
一个订单包含多个订单项,一个订单项用于订购某个商品,以及订购数量,一个商品可以多次出现在不同的订单中
Product类没有保留与其他实体的关联关系
@Entity
@Table(name="product_id")
public class Product {
//定义标识属性
@Id @Column(name="product_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer productId;
private String name;
//五参数的构造器
public Product() {}
//
}
Order持久化类
OrderItem需要维护与订单项的1-N关联关系,因策Order类中需要增加Set类型的属性,该属性负责管理与多个OrderItem之间的关联关系,应该使用@OneToMany修饰该Set集合属性
@Entity
@Table(name="order_inf")
public class Order {
//定义标识属性
@Id @Column(name="product_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer orderId;
private Date orderDate;
//关联的订单项
@OneToMany(targetEntity=OrderItem.class,mappedBy="order")
private Set<OrderItem> items = new HashSet<>();
}
OrderItem需要维护与Product的N-1关联关系,还需要维护与Order之间的N-1关联关系,因此使用@ManyToOne修饰这两个代表关联实体的属性,并配置@JointColumn来映射底层的外键列
下面是OrderItem的实体
@Entity
@Table(name="order_item_inf")
public class OrderItem implements java.io.Serializable{
//下面三个属性将作为联合主键
//定义关联的Order实体
@ManyToOne(targetEntity=Order.class)
//映射名为order_id的外键列,参照order_inf的order_id主键列
@JoinColumn(name="order_id",referencedColumnName="order_id")
@Id
private Order order;
//定义关联的Product实体
@ManyToOne(targetEntity=Product.class)
//映射名我product_id的外键列,参照product_inf的product_id主键列
@JoinColumn(name="product_id",referencedColumnName="product_id")
@Id
private Product product;
private Product product;
//该订单订购的产品数量
@Id
private int count;
//有无参数构造器 重写equals方法和hashcode方法
}
持久化的传播性
正如在前面的程序中看到的,当程序中有两个关联实体时,程序需要主动保存、除或重关个化实体:如果需要处理许多彼此关联的实体,则需要依次保存每个实体,这会让人感觉机点
从数据库建模的角度来看,两个表之间的1-N关联关系总是用外键约束米表示,其中保留外键的表称为从表,被从表参照的数据表称为主表,对于这种主从表约束关系, Hibemate则们两种映射策略。
1>将从表记录映射成持久化类的组件
2>将从表记录映射成持久化类的实体
将从表记录映射成持久化类的组件,这些组件的生命周期总是依赖于父对象, Hibernate会默
联操作,不需要额外的动作。当父对象被保存时,这些组件子对象也将被保存,父对象被删除,子对象也将被删除
如果将从表记录映射成持久化实体,则从表实体也有了自己的生命周期,从而应该允许其他实体共对它的引用,例如,从集合中移除一个实体,不意味着它可以被酬除。所以 Hibernate默认不启用实其他关联实体之间的级联操作
对于关联实体面言, Hibernate默认不会启用级联操作,当父对象被保存时,它关联的子实体不会保存:父对象被删除时,它关联的子实体不会被删除,为了启用不同持久化操作的级联行为, Hibernate定义了如下级联风格
1>CascadeType .ALLt指定 Hibernate将所有的持久化操作都级联到关联实体
2>CascadeType .MERGE:指定 Hibemate将 merge操作级联到关联实体
3>CascadeType .PERSIST:指定 Hibernate将pet操作级联到关联实体
4>CascadeType.REFRESH:指定 Hibernate将resh操作级联到关联实体
5>CascadeType.REMOVE:指定 Hibernate将 remove操作级联到关联实体
如变程序着望某个操作能被级联传播到关联实体,则可以在配置@ OneToMany、@One ToOne、@MenyToMany时通过 cascade属性来指定,例如:
//指定persist操作将级联到关联实体
@OneToOne(cascade=CascadeType.PERSIST)
级联风格是可组合的,如下面配置所示:
//指定persist()操作将级联关联到实体
@OneToOne(cascade={ CascadeType.PERSIST, CascadeType, DELTE})
可以使用 cascade= CascadeType.ALL指定所有的持久化操作都被级联到关联实体。 Hibernate对关联实体默认不使用任何级联,即任何操作都不会被级联到关联实体,
Hibernate还支持一个特殊的级联策略:删除“孤儿”记录(可通过 @oneToMany, @One ToOne的orphanRemoval属性来启动该级联策略),该级联策略只对当前实体是1的一端,且底层数据表为主表有效,对于启用了 ophanRemoval策略的级联操作而言,当程序通过主表实体切断与从表实体的关联关系时,虽然此时主表实体对应的记录井没有删除,但由于从表实体失去了对主表实体的引用,因此这从表实体就变成了“孤儿”记录, Hibemate会自动删除这些记录
对于级联的设定, Hibernate有如下建议
1>在 ManyToOne中指定级联没什么意义,级联通常在 @OneToOne和@ OneToMany关系中比较有用---一因为级联操作应该是由主表记录传播到从表记录,通常从表记录则不应该传播到主表记录,因此 @ManyToOne不支持指定 cascade属性,但在某些极端情况下,如果程序就是希望为 @ManyToOne指定级联策略,则也可使用 Hibernate提供的 @cascade注解
////////////////期待完善中