hibernate 7 高级实体关联映射 | hibernate 实战(第二版) 第7章高级实体关联映射 | 笔记...

1单值的实体关联

共享的主键关联:

由主键关联而相关的两张表中的行共享相同的主键值。这种方法的主要困难在于,确保被关联的
实例在保存对象时分配了相同的主键值。
第一步:用xml映射主键关联
  1. <classname="User"
  2. ....
  3. <one-to-one name="address"class="Address"cascade="update">
  4. </one-to-one>

第二步:外标识符生成器
如果Address实例被保存,它就需要获取User对象的主键值。你无法启动一个常规的标示符生成器,
假设是数据库序列。用于Address的这个特殊的foreign标示符生成器,必须知道从哪里获取正确
的主键值。
创建Address和User之间的这个标示符绑定,起第一步是双向关联。把一个新的user属性添加
到Address属性:
  1. <classname="Address"
  2. ...
  3. <one-to-one name="user"class="User"constrained="true"/>

这个映射让关联变成了双向,constrained="true"声明了把address的主键添加到user主键的外键约束。
现在可以给Address对象使用特殊的foreign标示符生成器了:
  1. <classname="Address"table="ADDRESS">
  2. <id name="addressId"column="ADDRESS_ID"type="long">
  3. <generatorclass="foreign">
  4. <param name="property">user</param>
  5. </generator>
  6. </id>
  7. <property name="street"type="string"column="street"/>
  8. <property name="city"type="string"column="city"/>
  9. <one-to-one name="user"class="User"constrained="true"/>
  10. </class>

(当Address被保存时,从属性中提取主键值。user属性是对User对象的一个引用;因而,插入的主键值与
这个实例的主键值相同。)
需要双向才可以保存成功:
  1. User user =newUser();
  2. user.setUserName("test");
  3. Address address =newAddress("street","city");
  4. user.setAddress(address);
  5. //不添加报错:attempted to assign id from null one-to-one property: user
  6. address.setUser(user);
  7. Integer msgId = (Integer) session.save(user);






利用注解的共享主键:
JPA用@OneToOne注解支持一对一的实体关联。要映射User类中address的关联为共享主键关联,还需要@PrimaryKeyJoinColumn注解:
  1. @Entity
  2. publicclassUserimplementsSerializable {
  3. @Id
  4. @GeneratedValue
  5. privateInteger userId;
  6. @Column(name="USER_NAME",length=100)
  7. privateString userName;
  8. @OneToOne(cascade=CascadeType.PERSIST)
  9. @PrimaryKeyJoinColumn
  10. privateAddress address;

JPA规范没有包括处理共享主键生成问题的标准方法,这意味着在保存Address实例的标示符之前,你要负责正确的设置他。
hibernate的扩展注解:
  1. @Entity
  2. publicclassAddressimplementsSerializable {
  3. @Id
  4. @GeneratedValue(generator="userForeignGenerator")
  5. @org.hibernate.annotations.GenericGenerator(name="userForeignGenerator",
  6. strategy="foreign",
  7. parameters=@Parameter(name="property",value="user"))
  8. @Column(name="ADDRESS_ID")
  9. privateInteger addressId;
  10. privateString street;
  11. privateString city;
  12. @OneToOne
  13. privateUser user;

保存:
  1. User user =newUser("name");
  2. Address address =newAddress("street","city");
  3. user.setAddress(address);
  4. //不添加报错:attempted to assign id from null one-to-one property: user
  5. address.setUser(user);
  6. em.persist(user);








一对一的外键关联:
不共享主键,而是两表可以有一个外键关系。一张表有着引用被关联表的主键的一个外键列。
  1. <classname="User"table="USER"dynamic-insert="true"dynamic-update="true"
  2. mutable="false">
  3. <id name="userId"column="USER_ID"type="integer">
  4. <generatorclass="native"/>
  5. </id>
  6. <property name="userName"type="string"column="user_Name"/>
  7. <!-- 在User里面添加维护信息,也就是把User作为维护端 -->
  8. <many-to-one name="address"class="Address"
  9. column="ADDRESS_ID"unique="true"cascade="save-update"/>

(这里用的many-to-one,而不是我们共享主键里面的one-to-one,因为:你不在乎关联的目标端是什么,
因此可以像对待没有多端的対一关联对待他。通过unique来强制说明只存在一的对应关系)
反向属性引用:
  1. <classname="Address"table="ADDRESS">
  2. <id name="addressId"column="ADDRESS_ID"type="integer">
  3. <generatorclass="native">
  4. </generator>
  5. </id>
  6. <property name="street"type="string"column="street"/>
  7. <property name="city"type="string"column="city"/>
  8. <!-- property-ref="address"告诉hibernateAddress -->
  9. <!-- 基于外键的的唯一约束,一般不建议这么做,这个是不需要的 -->
  10. <one-to-one name="user"class="User"property-ref="address"/>

插入:
  1. Address address =newAddress("street","city");
  2. user.setAddress(address);
  3. //不需要
  4. address.setUser(user);
  5. Integer msgId = (Integer) session.save(user);





通过注解映射外键:
JPA映射注解也支持基于外键列的实体之间的一对一关系。与前面的非实体类型的区别就是,@JoinColumn替代了
  1. @PrimaryKeyJoinColumn.
  2. @Entity
  3. publicclassUserimplementsSerializable {
  4. @Id
  5. @GeneratedValue
  6. privateInteger userId;
  7. @Column(name="USER_NAME",length=100)
  8. privateString userName;
  9. @OneToOne(cascade=CascadeType.PERSIST)
  10. //加入一个外键关联字段
  11. @JoinColumn(name="ADDRESS_ID")
  12. privateAddress address

如果需要双向的:
  1. @Entity
  2. publicclassAddressimplementsSerializable {
  3. @Id
  4. @GeneratedValue
  5. @Column(name="ADDRESS_ID")
  6. privateInteger addressId;
  7. privateString street;
  8. privateString city;
  9. //如果需要双向的
  10. //表示被User里面的address字段进行关联管理
  11. //mappedBy相当于xml的property-ref="address"(关联的一个简单的方向声明,就是在目标实体段指定了一种属性)
  12. //加上他,在数据库上是没有任何影响的,紧紧是告诉JPA容器一种关系
  13. @OneToOne(mappedBy="address")
  14. privateUser user;






用联接表映射:
表示从Shipment到Item关联的属性成为auction:
  1. <classname="Shipment"table="Shipment">
  2. <id name="shipmentId"column="SHIPMENT_ID"type="integer">
  3. <generatorclass="native"/>
  4. </id>
  5. <property name="money"type="double"column="MONEY"/>
  6. <!-- 因为必须用一个外键列 映射这个关联,因此在XML中你需要
  7. <many-to-one>映射元素。然而,外键列不在SHIPMENT表中,
  8. 而在ITEM_SHIPMENT连接表中。通过<join>映射元素把它移动到那里。 -->
  9. <!-- cascade="save-update":不添加报错object references an unsavedtransientinstance - save thetransientinstance before flushing: -->
  10. <join table="ITEM_SHIPMENT"optional="true">
  11. <key column="SHIPMENT_ID"/>
  12. <many-to-one name="auction"column="ITEM_ID"class="Item"
  13. not-null="true"unique="true"
  14. cascade="save-update"></many-to-one>
  15. </join>
  16. </class>

(可以在另一端使用相同的方法,吧在合格关联映射为双向。然而,可选的一对一关联大多数时候都是单向的。)
插入:
  1. Shipment shipment =newShipment(12D);
  2. Item item =newItem("hello");
  3. shipment.setAuction(item);
  4. session.persist(shipment);





通过注解映射二级联接表:
  1. @Entity
  2. publicclassShipmentimplementsSerializable {
  3. @Id
  4. @GeneratedValue
  5. @Column(name="SHIPMENT_ID")
  6. privateInteger shipmentId;
  7. privateDouble money;
  8. @OneToOne(cascade=CascadeType.PERSIST)
  9. //和外键关联的区别就是那个是加入的字段@JoinColumn(name="ADDRESS_ID")
  10. //和hibernate的区别就是joinColumns替代了key,inverseJoinColumns替代了many-to-one
  11. //@JoinTable加入维护表,且本实体为维护端
  12. @JoinTable(name="ITEM_SHIPMENT",
  13. joinColumns=@JoinColumn(name="SHIPMENT_ID"),
  14. inverseJoinColumns=@JoinColumn(name="ITEM_ID"))
  15. privateItem auction;

插入:
  1. Shipment shipment =newShipment(12D);
  2. Item item =newItem("hello");
  3. shipment.setAuction(item);
  4. em.persist(shipment);




通过注解映射二级联接表(可以连接多个字段):
  1. @Entity
  2. //相当于前面的<key column="..."/>和@JoinTable中的joinColumn()。如果没指定名称,那么就会默认使用主键SHIPMENT_ID
  3. @SecondaryTable(name="ITEM_SHIPMENT")
  4. publicclassShipmentimplementsSerializable {
  5. @Id
  6. @GeneratedValue
  7. @Column(name="SHIPMENT_ID")
  8. privateInteger shipmentId;
  9. privateDouble money;
  10. @ManyToOne(cascade=CascadeType.PERSIST)
  11. @JoinColumn(table="ITEM_SHIPMENT",name="ITEM_ID")
  12. privateItem auction;










2多值的实体关联


多值:是指实体引用的为一个集合。
一对多关联是涉及集合的一种最重要的实体关联。如果 简单的双向多对一活一对多能够完成任务时,目前为止我们并不鼓励使用更加
怪异的关联方式。多对多关联始终可以表示为对中间类的两个多对一关联。这个模型通常更容易于扩展,因此我们趋向于不在应用
中使用多对多关联。也要记住:如果不想的话,你不必映射实体的任何集合;你可以始终编写显示查询来替代通过迭代的直接访问。




一对多关联:
1、考虑bag
一般来说,对于一对多的关联,我们认为bag是它最好的方向集合:
因为bag不必维持起元素的索引(如list),或者检查重复的元素(如set),可以添加新元素给bag,而不触发加载。
但是,bag无法同时抓取两个集合(如Item中同时存在bids和imags两个一对多的bag集合)
bag允许重复,但是由于方向映射,hibernate也会忽略它。
配置文件:
  1. <classname="Item"table="ITEM">
  2. <id name="itemId"column="ITEM_ID"type="integer">
  3. <generatorclass="native"/>
  4. </id>
  5. <property name="itemName"type="string"column="ITEM_NAME"/>
  6. <bag name="bids"inverse="true"cascade="save-update">
  7. <!-- 注意column="ITEM_ID_M"名称故意和Item主键不一致,这里是维护端生成的字段名称 -->
  8. <key column="ITEM_ID_M"></key>
  9. <one-to-manyclass="Bid"/>
  10. </bag>
  11. </class>
  1. <classname="Bid"table="BID">
  2. <id name="bidId"column="BID_ID"type="integer">
  3. <generatorclass="native"/>
  4. </id>
  5. <property name="bidMoeny"type="double"></property>
  6. <!-- 注意column="ITEM_ID_M"名称故意和Item主键不一致,这里是说明一的一端的表,需要生成的对应关联字段是什么
  7. KEY `FK100DD8434F24F` (`ITEM_ID_M`),
  8. CONSTRAINT `FK100DD8434F24F` FOREIGN KEY (`ITEM_ID_M`) REFERENCES `item` (`ITEM_ID`) -->
  9. <many-to-one name="item"column="ITEM_ID_M"
  10. class="Item"not-null="true"></many-to-one>
  11. </class>

插入:
  1. Item item =newItem("hello");
  2. Bid bid =newBid(12D);
  3. item.getBids().add(bid);
  4. //不添加这个报错:not-null property references a null or transient value
  5. bid.setItem(item);
  6. session.save(item);





单向和双向的列表:
  1. <classname="Item"table="ITEM">
  2. <id name="itemId"column="ITEM_ID"type="integer">
  3. <generatorclass="native"/>
  4. </id>
  5. <property name="itemName"type="string"column="ITEM_NAME"/>
  6. <!-- 没有在集合中加入inverse="true,因为hibernate忽略反向集合的状态!
  7. 但是这一次,集合包含了正确的更新数据库所需要的信息:它的元素的位置。
  8. 如果只有每个Bid实例的状态被认为是同步的,集合又是方向的并且被忽略,
  9. 那么hibernate就没有值给BID_POSITOIN了-->
  10. lt;list name="bids"cascade="save-update">
  11. <key column="ITEM_ID"not-null="true"></key>
  12. <list-index column="BID_POSITOIN"></list-index>
  13. <one-to-manyclass="Bid"/>
  14. lt;/list>
  15. </class>


  1. <classname="Bid"table="BID">
  2. <id name="bidId"column="BID_ID"type="integer">
  3. <generatorclass="native"/>
  4. </id>
  5. <property name="bidMoeny"type="double"></property>
  6. <!-- 如果通过被索引的集合映射双向的一对多实体关联(映射和数组也是这样),
  7. 就必须转换为方向段。无法被索引的集合变成inverse="true"。集合变成了
  8. 负责状态同步,并且一的端Bid必须变成反向。然而,多对一的映射没有
  9. inverse="true",因此需要在<many-to-one>中模拟这一模拟这一属性:
  10. 设置insert="false"update="false"
  11. 这俩个属性一起使用,实际上使属性变成了只读。关联的这一端因此被任何写操作忽略,
  12. 当内存状态与数据库同步时,集合的状态就是相关的状态。你已经转换了关联的方向/非方向端,
  13. 如果从set或者bag转换为list,这个是必要的条件。
  14. -->
  15. <many-to-one name="item"column="ITEM_ID_M"
  16. class="Item"not-null="true"
  17. insert="false"update="false"></many-to-one>
  18. </class>






利用联合表的可选一对多关联:
  1. <classname="User"table="USER">
  2. <id name="userId"column="USER_ID"type="integer">
  3. <generatorclass="native"/>
  4. </id>
  5. <property name="userName"type="string"column="USER_NAME"/>
  6. <set name="items"table="ITEM_USER">
  7. <key column="USER_ID"></key>
  8. <many-to-manyclass="Item"column="ITEM_ID"unique="true"></many-to-many>
  9. </set>
  10. </class>
  1. <classname="Item"table="ITEM">
  2. <id name="itemId"column="ITEM_ID"type="integer">
  3. <generatorclass="native"/>
  4. </id>
  5. <property name="itemName"type="string"column="ITEM_NAME"/>
  6. <join table="ITEM_USER"optional="true"
  7. inverse="true">
  8. <key column="ITEM_ID"unique="true"not-null="true"></key>
  9. <many-to-one name="user"column="USER_ID"></many-to-one>
  10. </join>
  11. </class>

JPA实现:
  1. @Entity
  2. publicclassUserimplementsSerializable {
  3. @Id
  4. @GeneratedValue
  5. @Column(name="USER_ID")
  6. privateInteger userId;
  7. @Column(name="USER_NAME",length=100)
  8. privateString userName;
  9. @OneToMany(mappedBy="user")
  10. privateSet<Item> items =newHashSet<Item>();
  1. @Entity
  2. publicclassItemimplementsSerializable {
  3. @Id
  4. @GeneratedValue
  5. @Column(name="ITEM_ID")
  6. privateInteger itemId;
  7. @Column(name="ITEM_NAME",length=100)
  8. privateString itemName;
  9. @ManyToOne
  10. @JoinTable(name="ITEM_USER",
  11. joinColumns=@JoinColumn(name="ITEM_ID"),
  12. inverseJoinColumns=@JoinColumn(name="USER_ID"))
  13. privateUser user;








多对多关联:
简单的多对多关联:
会生成一个中间表:
  1. <classname="Category"table="Category">
  2. <id name="categoryId"column="CATEGORY_ID"type="integer">
  3. <generatorclass="native"/>
  4. </id>
  5. <property name="name"type="string"column="name"/>
  6. <set name="items"table="CATEGORY_ITEM">
  7. <key column="CATEGORY_ID"></key>
  8. <many-to-manyclass="Item"column="ITEM_ID"></many-to-many>
  9. </set>
  10. </class>
  1. <classname="Item"table="ITEM">
  2. <id name="itemId"column="ITEM_ID"type="integer">
  3. <generatorclass="native"/>
  4. </id>
  5. <property name="itemName"type="string"column="ITEM_NAME"/>
  6. </class>

JPA实现:
  1. @Entity
  2. publicclassItemimplementsSerializable {
  3. @Id
  4. @GeneratedValue
  5. @Column(name="ITEM_ID")
  6. privateInteger itemId;
  7. @Column(name="ITEM_NAME",length=100)
  8. privateString itemName;
  1. @Entity
  2. publicclassCategoryimplementsSerializable {
  3. @Id
  4. @GeneratedValue
  5. @Column(name="CATEGORY_ID")
  6. privateInteger categoryId;
  7. @Column(length=100)
  8. privateString name;
  9. @ManyToMany
  10. @JoinTable(name="CATEGORY_ITEM",
  11. joinColumns=@JoinColumn(name="CATEGORY_ID"),
  12. inverseJoinColumns=@JoinColumn(name="ITEM_ID"))
  13. privateSet<Item> items =newHashSet<Item>();




idbag方式:
  1. <classname="Item"table="ITEM">
  2. <id name="itemId"column="ITEM_ID"type="integer">
  3. <generatorclass="native"/>
  4. </id>
  5. <property name="itemName"type="string"column="ITEM_NAME"/>
  6. </class>
  1. <classname="Category"table="Category">
  2. <id name="categoryId"column="CATEGORY_ID"type="integer">
  3. <generatorclass="native"/>
  4. </id>
  5. <property name="name"type="string"column="name"/>
  6. lt;idbag name="items"table="CATEGORY_ITEM">
  7. <collection-id type="integer"column="CATEGORY_ITEM_ID">
  8. <generatorclass="native"></generator>
  9. </collection-id>
  10. <key column="CATEGORY_ID"></key>
  11. <many-to-many column="ITEM_ID"class="Item"></many-to-many>
  12. lt;/idbag>
  13. </class>

JPA实现:
  1. @Id
  2. @GeneratedValue
  3. @Column(name="ITEM_ID")
  4. privateInteger itemId;
  5. @Column(name="ITEM_NAME")
  6. privateString itemName;
  1. @Id
  2. @GeneratedValue
  3. @Column(name="CATEGORY_ID")
  4. privateInteger categoryId;
  5. @Column(length=100)
  6. privateString name;
  7. @ManyToMany
  8. @CollectionId(columns=@Column(name="CATEGORY_ITEM_ID"),
  9. type=@Type(type="integer"),
  10. generator="native")
  11. @JoinTable(name="CATEGORY_ITEM",
  12. joinColumns=@JoinColumn(name="CATEGORY_ID"),
  13. inverseJoinColumns=@JoinColumn(name="ITEM_ID"))
  14. privateCollection<Item> items =newArrayList<Item>();







带有索引的单向多对多映射:
  1. <classname="Category"table="Category">
  2. <id name="categoryId"column="CATEGORY_ID"type="integer">
  3. <generatorclass="native"/>
  4. </id>
  5. <property name="name"type="string"column="name"/>
  6. lt;list name="items"table="CATEGORY_ITEM"cascade="save-update">
  7. <key column="CATEGORY_ID"></key>
  8. <list-index column="POSISTION"></list-index>
  9. <many-to-manyclass="Item"column="ITEM_ID"></many-to-many>
  10. lt;/list>
  11. </class>


JPA实现:
  1. @Entity
  2. publicclassCategoryimplementsSerializable {
  3. @Id
  4. @GeneratedValue
  5. @Column(name="CATEGORY_ID")
  6. privateInteger categoryId;
  7. @Column(length=100)
  8. privateString name;
  9. @ManyToMany
  10. @IndexColumn(name="POSITION")
  11. @JoinTable(name="CATEGORY_ITEM",
  12. joinColumns=@JoinColumn(name="CATEGORY_ID"),
  13. inverseJoinColumns=@JoinColumn(name="ITEM_ID"))
  14. privateList<Item> items =newArrayList<Item>();









双向的多对多关联:
双向关联的一端必须映射为反向,因为你已经对(一个或多个)外键列命名了两次。给双向的多对多关联应用
相同的原则:联接表的每一行都由两个集合元素表示,关联的两端各一个元素。
配置:
  1. <classname="Item"table="ITEM">
  2. <id name="itemId"column="ITEM_ID"type="integer">
  3. <generatorclass="native"/>
  4. </id>
  5. <property name="itemName"type="string"column="ITEM_NAME"/>
  6. <set name="categories"table="CATEGORY_ITEM"cascade="save-update">
  7. <key column="ITEM_ID"></key>
  8. <many-to-many column="CATEGORY_ID"class="Category"></many-to-many>
  9. </set>
  10. </class>
  1. <classname="Category"table="Category">
  2. <id name="categoryId"column="CATEGORY_ID"type="integer">
  3. <generatorclass="native"/>
  4. </id>
  5. <property name="name"type="string"column="name"/>
  6. <set name="items"table="CATEGORY_ITEM"inverse="true"cascade="save-update">
  7. <key column="CATEGORY_ID"></key>
  8. <many-to-many column="ITEM_ID"class="Item"></many-to-many>
  9. </set>
  10. </class>

插入1:
  1. Item item =newItem("item");
  2. Category category1 =newCategory("category1");
  3. item.getCategories().add(category1);
  4. Category category2 =newCategory("category2");
  5. item.getCategories().add(category2);
  6. session.save(item);
  7. //Hibernate: insert into ITEM (ITEM_NAME) values (?)
  8. //Hibernate: insert into Category (name) values (?)
  9. //Hibernate: insert into Category (name) values (?)
  10. //Hibernate: insert into CATEGORY_ITEM (ITEM_ID, CATEGORY_ID) values (?, ?)
  11. //Hibernate: insert into CATEGORY_ITEM (ITEM_ID, CATEGORY_ID) values (?, ?)

插入2:
  1. Item item1 =newItem("item1");
  2. category.getItems().add(item1);
  3. Item item2 =newItem("item1");
  4. category.getItems().add(item2);
  5. session.save(category);
  6. //inverse="true"的一端,忽略对中间库的影响
  7. //Hibernate: insert into Category (name) values (?)
  8. //Hibernate: insert into ITEM (ITEM_NAME) values (?)
  9. //Hibernate: insert into ITEM (ITEM_NAME) values (?)

插入3:
  1. Category category =newCategory("category1");
  2. Item item1 =newItem("item1");
  3. category.getItems().add(item1);
  4. Item item2 =newItem("item1");
  5. category.getItems().add(item2);
  6. //必须存在,才会影响中间表,因为inverse="false"的一端,才会影响中间库的变化
  7. item1.getCategories().add(category);
  8. item2.getCategories().add(category);
  9. session.save(category);
  10. //Hibernate: insert into Category (name) values (?)
  11. //Hibernate: insert into ITEM (ITEM_NAME) values (?)
  12. //Hibernate: insert into ITEM (ITEM_NAME) values (?)
  13. //Hibernate: insert into CATEGORY_ITEM (ITEM_ID, CATEGORY_ID) values (?, ?)
  14. //Hibernate: insert into CATEGORY_ITEM (ITEM_ID, CATEGORY_ID) values (?, ?)


JPA实现:
  1. @Entity
  2. publicclassItemimplementsSerializable {
  3. @Id
  4. @GeneratedValue
  5. @Column(name="ITEM_ID")
  6. privateInteger itemId;
  7. @Column(name="ITEM_NAME",length=100)
  8. privateString itemName;
  9. @ManyToMany
  10. @JoinTable(name="CATEGORY_ITEM",
  11. joinColumns=@JoinColumn(name="ITEM_ID"),
  12. inverseJoinColumns=@JoinColumn(name="CATEGORY_ID"))
  13. privateSet<Category> categories =newHashSet<Category>();
  1. @Entity
  2. publicclassCategoryimplementsSerializable {
  3. @Id
  4. @GeneratedValue
  5. @Column(name="CATEGORY_ID")
  6. privateInteger categoryId;
  7. @Column(length=100)
  8. privateString name;
  9. //被维护端(反相端),只需要简单的生命一下mappedBy,相当于inverse="true"
  10. @ManyToMany(mappedBy="categories")
  11. privateSet<Item> items =newHashSet<Item>();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值