對象之間的關係:關係映射之間的關係只的是對象之間的關係,並不指數據庫表的關係(外鍵關係),固然,數據庫表該如何映射,編程上如何實現這兩件事密不可分。java
一、對象之間的關聯關係:數據庫
一對多,一對一,多對多編程
在orm中,區分單向和雙向;單向和雙向直接影響數據加載不一樣。app
如1:單向管理就是對應的Java對象中,ObjectA,ObjectB存在關聯關係,可是隻有ObjectA對象包含一個ObjectB對象的成員屬性,而ObjectB僅僅是本身的屬性,而沒有引用ObjectA。ide
如2:一對多關聯映射是指在加載一的一端數據的同時加載多的一端的數據;多對一關聯映射是指在加載多的一端數據的同時加載一的一端的數據fetch
二、一對一實現this
a、【單向】一對一經過主鍵關聯code
@Entity @Table(name = "a") public class A implements Serializable{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @OneToOne @PrimaryKeyJoinColumn private B b; }
@Entity @Table(name = "b") public class B implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; }
b、【單向】一對一經過外鍵關聯orm
A表有B表的外鍵字段對象
@Entity @Table(name = "a") public class A implements Serializable{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @OneToOne @JoinColumn(name="bid") private B b; }
@Entity @Table(name = "b") public class B implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer bid; }
c.[雙向]一對一主鍵關聯
@Entity @Table(name = "a") public class A implements Serializable{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @OneToOne @PrimaryKeyJoinColumn(name="id",referencedColumnName="id") private B b; }
@Entity @Table(name = "b") public class B implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @OneToOne(mappedBy="b") @PrimaryKeyJoinColumn(name="id",columnDefinition="id") private A a; }
d.[雙向]一對一外鍵關聯
實現形式之一:雙向外鍵,即雙方都持有對對方的引用;
@Entity @Table(name = "a") public class A implements Serializable{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @OneToOne @JoinColumn(name="bid") private B b; }
@Entity @Table(name = "b") public class B implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @OneToOne(mappedBy="b") @JoinColumn(name="id") private A a; }
實現形式之二:複合主鍵;
三、一對多關係實現:
a.[單向]一對多:
維護端在多的那一端,在一的一方加集合(Set)。如分組Group表與User表,此處定義爲一對多,即一個分組裏有多個用戶。用戶表中帶有一個groupid外鍵。
@OneToMany @JoinColumn(name="groupid") public Set<User> getUsers() { return users; } public void setUsers(Set<User> users) { this.users = users; }
b.【單向】多對一
@Entity @Table(name="user") public class User implements Serializable{ @ManyToOne @JoinColumn(name="groupid") private Group group; }
c.[雙向]一對多:
雙向一對多關係,一是關係維護端(owner side),可能是關係被維護端(inverse side)。在關係被維護端須要經過@JoinColumn創建外鍵列指向關係維護端的主鍵列。其中默認inverse=false,而mapedBy="xxxx"至關於inverse=true。
( 在一對多中,若是要一方維護關係,就會使在插入或是刪除"一"方時去update"多"方的每個與這個"一"的對象有關係的對象。
而若是讓" 多"方面維護關係時就不會有update操做,由於關係就是在多方的對象中的,直指插入或是刪除多方對象就好了。
固然這時也要遍歷"多"方的每個對象顯示的操做修關係的變化體現到DB中。無論怎樣說,仍是讓"多"方維護關係更直觀一些。)
Order訂單與OrberItem訂單項關係爲一對多映射 public class Order implements Serializable { privateSet<OrderItem> orderItems = new HashSet<OrderItem>(); 。。。。 @OneToMany(mappedBy="order"(有了mappedby不能也不應在此再定義@joincolumn),cascade = CascadeType.ALL, fetch = FetchType.LAZY) @OrderBy(value= "id ASC") public Set<OrderItem> getOrderItems() { returnorderItems; } } //多的那方: publicclass OrderItem implements Serializable { private Orderorder; 。。。。 @ManyToOne(cascade=CascadeType.REFRESH,optional=false) @JoinColumn(name = "order_id") public Order getOrder() { returnorder; } }
樹結構實體實現
public class Category implements java.io.Serializable { @Id @GeneratedValue(strategy=IDENTITY) @Column(name="CAT_ID", unique=true, nullable=false) private Integer id; @ManyToOne(fetch=FetchType.LAZY) @JoinColumn(name="CAT_PARENT_ID") private Category parent; @Column(name="CAT_NAME", nullable=false, length=50) private String name; @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="parent") private Set<Category> children = new HashSet<Category>(0); //省略getter/setter }
@OrderBy(value = "id ASC") 指明加載OrderItem 時按id的升序排序
@OneToMany的屬性:
1>targetEntity
定義關係類的類型,默認是該成員屬性對應的類類型,因此一般不須要提供定義。
2>mappedBy
定義類之間的雙向關係。若是類之間是單向關係,不須要提供定義,若是類和類之間造成雙向關係,咱們就須要使用這個屬性進行定義,不然可能引發數據一致性的問題。
該屬性的值是「多」方class裏的「一」方的變量名
3>cascade
該屬性定義類和類之間的級聯關係。定義的級聯關係將被容器視爲對當前類對象及其關聯類對象採起相同的操做,並且這種關係是遞歸調用的。舉個例子:Order 和OrderItem有級聯關係,那麼刪除Order時將同時刪除它所對應的OrderItem對象。而若是OrderItem還和其餘的對象之間有級聯關係,那麼這樣的操做會一直遞歸執行下去。
cascade的值只能從CascadeType.PERSIST(級聯新建)、CascadeType.REMOVE(級聯刪除)、CascadeType.REFRESH(級聯刷新)、CascadeType.MERGE(級聯更新)中選擇一個或多個。還有一個選擇是使用CascadeType.ALL,表示選擇所有四項。
4>fatch
可選擇項包括:FetchType.EAGER和FetchType.LAZY。前者表示關係類(本例是OrderItem 類)在主類(本例是Order類)加載的時候同時加載,後者表示關係類在被訪問時才加載。默認值是FetchType.LAZY。
@JoinColumn(name = "order_id")註釋指定OrderItem映射表的order_id列做爲外鍵與Order 映射表的主鍵列關聯。
@ManyToOne:指明OrderItem和Order之間爲多對一關係。
@ManyToOne註釋有四個屬性:targetEntity、cascade、fetch 和optional,前三個屬性的具體含義和@OneToMany的同名屬性相同,但@ManyToOne的fetch 屬性默認值是FetchType.EAGER。
optional屬性是定義該關聯類是否必須存在,值爲false 時,關聯類雙方都必須存在,若是關係被維護端不存在,查詢的結果爲null。值爲true 時, 關係被維護端能夠不存在,查詢的結果仍然會返回關係維護端,在關係維護端中指向關係被維護端的屬性爲null。optional屬性的默認值是true。optional屬性實際上指定關聯類與被關聯類的join 查詢關係,如optional=false 時join 查詢關係爲inner join,optional=true 時join 查詢關係爲leftjoin。下面代碼片段解釋以下:
三、延遲加載時,獲取集合數據
有一點須要強調:當業務方法須要把一個實體Bean做爲參數返回給客戶端時,除了實體Bean自己須要實現Serializable 接口以外,若是關聯類(OrderItem)是延遲加載,還需在返回實體Bean以前經過訪問關聯類的方式加載關聯類(見下例)。不然在客戶端訪問關聯類時將會拋出加載例外。
public OrdergetOrderByID(Integer orderid) {
Orderorder = em.find(Order.class, orderid);
//!!!!!由於是延遲加載,經過執行size()這種方式獲取訂單下的全部訂單項
order.getOrderItems().size();
return order;
}
四、Queryquery 使用
另外不論是否延遲加載,經過join fetch 關聯語句均可顯式加載關聯類,以下例:
public ListgetAllOrder() {
Queryquery = em.createQuery("select DISTINCT o from Order o inner
joinfetch o.orderItems order by o.orderid");
List result = query.getResultList();
return result;
}
注:Inverse和Cascade
Inverse:負責控制關係,默認爲false,也就是關係的兩端都能控制,但這樣會形成一些問題,更新的時候會由於兩端都控制關係,因而重複更新。通常來講有一端要設爲true。
Cascade:負責控制關聯對象的級聯操做,包括更新、刪除等,也就是說對一個對象進行更新、刪除時,其它對象也受影響,好比我刪除一個對象,那麼跟它是多對一關係的對象也所有被刪除。
舉例說明區別:刪除「一」那一端一個對象O的時候,若是「多」的那一端的Inverse設爲true,則把「多」的那一端全部與O相關聯的對象外鍵清空;
若是「多」的那一端的Cascade設爲Delete,則把「多」的那一端全部與O相關聯的對象所有刪除。