我這裏主要使用到了四個類,專利:product,專利擁有者:user,擁有者的寵物貓:cat,擁有者的身份證:card。他們之間的對應關係爲:
user–》product:一對多
user–》cat:多對多
user–》card:一對一
一、 實體類代碼
- user
package ffcs.cn.peam.user.domain;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import ffcs.cn.peam.card.domain.Card;
import ffcs.cn.peam.cat.domain.Cat;
import ffcs.cn.peam.product.domain.Product;
@Entity
@Table(name = "S_USER")
public class User {
@Id
@GeneratedValue
@Column(name = "ID")
private int id;
@Column(name = "PRODUCT_ID")
private int productId;
private String name;
private String password;
private String authority;
// 如果專利是單向的多對一的關係,則@OneToMany可省略
// 一對多(用戶-專利)
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<Product> product;
// 一對一(用戶-身份號碼)
@OneToOne(mappedBy = "user", fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
private Card card;
// 多對多(用戶-寵物貓)
@ManyToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<Cat> cat;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(name = "PASSWORD")
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Column(name = "AUTHORITY")
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
@Column(name = "PRODUCT_ID")
public int getProductId() {
return productId;
}
public void setProductId(int productId) {
this.productId = productId;
}
public Card getCard() {
return card;
}
public void setCard(Card card) {
this.card = card;
}
public Set<Product> getProduct() {
return product;
}
public void setProduct(Set<Product> product) {
this.product = product;
}
public Set<Cat> getCat() {
return cat;
}
public void setCat(Set<Cat> cat) {
this.cat = cat;
}
}
- cat
package ffcs.cn.peam.cat.domain;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import ffcs.cn.peam.product.domain.Product;
import ffcs.cn.peam.user.domain.User;
@Entity
@Table(name = "S_CAT")
public class Cat {
@Id
@GeneratedValue
@Column(name = "ID")
private int id;
@Column(name = "NAME")
private String name;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "S_USER_CAT", joinColumns = { @JoinColumn(name = "SSCAT") }, inverseJoinColumns = { @JoinColumn(name = "SSUSER") })
private Set<User> user;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<User> getUser() {
return user;
}
public void setUser(Set<User> user) {
this.user = user;
}
}
- product
package ffcs.cn.peam.product.domain;
import java.util.Date;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Formula;
import ffcs.cn.peam.user.domain.User;
@Entity
@Table(name = "PRODUCT")
public class Product {
@Id
@GeneratedValue
@Column(name = "ID")
private int id;
@Column(name = "NAME")
private String name;
@Column(name = "PRICE")
private float price;
@Column(name = "TIME")
private Date time;
@Formula("(select t.name from s_user t where t.id=OWNER)")
private String ownerName;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "owner")
private User user;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getOwnerName() {
return ownerName;
}
public void setOwnerName(String ownerName) {
this.ownerName = ownerName;
}
}
- card
package ffcs.cn.peam.card.domain;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import ffcs.cn.peam.product.domain.Product;
import ffcs.cn.peam.user.domain.User;
@Entity
@Table(name = "S_CARD")
public class Card {
@Id
@GeneratedValue
private int cid;
@Column(name = "CARD_NUMBER")
private String cardNumber;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id")
private User user;
public String getCardNumber() {
return cardNumber;
}
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
二、 懶加載與急加載的測試
這裏爲了方便,是直接使用criteria進行測試的,我將criteria產生的sql語句打印出來,可以比較好的根據產生的sql語句看懶加載的效果。具體配置如果感興趣的可以看看我之前的博客
Resource resource = new ClassPathResource("ffcs/cn/system/applicationContext.xml");
BeanFactory factory = new XmlBeanFactory(resource);
SessionFactory sessionFactory = (SessionFactory) factory.getBean("sessionFactory");
Session s = sessionFactory.openSession();
s.beginTransaction();
Criteria c = s.createCriteria(Card.class);
List<Card> ps = c.list();
for (Card p : ps) {
System.out.println(p);
}
s.getTransaction().commit();
s.close();
sessionFactory.close();
return null;
- 懶加載和急加載初始化區別(product和user)
懶加載: 直接輸出product類,產生的sql查詢語句爲
Hibernate: select this_.ID as ID29_0_, this_.NAME as NAME29_0_, this_.OWNER as OWNER29_0_ from PRODUCT this_
急加載: 直接輸出product類,產生的sql查詢語句爲:
Hibernate: select this_.ID as ID29_1_, this_.NAME as NAME29_1_, this_.OWNER as OWNER29_1_, user2_.ID as ID30_0_,user2_.name as name30_0_, user2_.password as password30_0_, user2_.PRODUCT_ID as PRODUCT5_30_0_ from PRODUCT this_ left outer join S_USER user2_ on this_.owner=user2_.ID
很明顯,急加載的情況下,會直接關聯表查出所有的數據。而懶加載只會查出product數據,user數據只有再需要的時候才查出來。
- 懶加載情況下,user數據被需要時產生的sql(product和user)
user數據怎麼樣纔算是被需要呢?舉個簡單的例子,在product初始化完成後,我們要在控制檯打印出product的user屬性,這樣子就需要去查user數據,產生的sql查詢語句爲:
Hibernate: select this_.ID as ID29_0_, this_.NAME as NAME29_0_, this_.OWNER as OWNER29_0_ from PRODUCT this_
Hibernate: select user0_.ID as ID21_0_, user0_.name as name21_0_, user0_.password as password21_0_, user0_.PRODUCT_ID as PRODUCT5_21_0_ from S_USER user0_ where user0_.ID=?
Hibernate: select user0_.ID as ID21_0_, user0_.name as name21_0_, user0_.password as password21_0_, user0_.PRODUCT_ID as PRODUCT5_21_0_ from S_USER user0_ where user0_.ID=?
Hibernate: select user0_.ID as ID21_0_, user0_.name as name21_0_, user0_.password as password21_0_, user0_.PRODUCT_ID as PRODUCT5_21_0_ from S_USER user0_ where user0_.ID=?
Hibernate: select user0_.ID as ID21_0_, user0_.name as name21_0_, user0_.password as password21_0_, user0_.PRODUCT_ID as PRODUCT5_21_0_ from S_USER user0_ where user0_.ID=?
因爲我本地存在4條user信息,所以這裏調用了四次查詢語句。
- 急加載情況下,再去調用product.user,產生的sql(product和user)
Hibernate: select this_.ID as ID29_1_, this_.NAME as NAME29_1_, this_.OWNER as OWNER29_1_, user2_.ID as ID30_0_,user2_.name as name30_0_, user2_.password as password30_0_, user2_.PRODUCT_ID as PRODUCT5_30_0_ from PRODUCT this_ left outer join S_USER user2_ on this_.owner=user2_.ID
可以看出來,這種情況不會額外產生查詢語句
- 懶加載的情況下,如果要調用user,並且user裏存在急加載字段,產生的sql(product和user、card)
Hibernate: select this_.ID as ID29_0_, this_.NAME as NAME29_0_, this_.OWNER as OWNER29_0_ from PRODUCT this_
Hibernate: select user0_.ID as ID75_1_, user0_.name as name75_1_, user0_.password as password75_1_, user0_.PRODUCT_ID as PRODUCT5_75_1_, card1_.id as id72_0_, card1_.CARD_NUMBER as CARD2_72_0_ from S_USER user0_ left outer join S_CARD card1_ on user0_.ID=card1_.id where user0_.ID=?
Hibernate: select user0_.ID as ID75_1_, user0_.name as name75_1_, user0_.password as password75_1_, user0_.PRODUCT_ID as PRODUCT5_75_1_, card1_.id as id72_0_, card1_.CARD_NUMBER as CARD2_72_0_ from S_USER user0_ left outer join S_CARD card1_ on user0_.ID=card1_.id where user0_.ID=?
Hibernate: select user0_.ID as ID75_1_, user0_.name as name75_1_, user0_.password as password75_1_, user0_.PRODUCT_ID as PRODUCT5_75_1_, card1_.id as id72_0_, card1_.CARD_NUMBER as CARD2_72_0_ from S_USER user0_ left outer join S_CARD card1_ on user0_.ID=card1_.id where user0_.ID=?
Hibernate: select user0_.ID as ID75_1_, user0_.name as name75_1_, user0_.password as password75_1_, user0_.PRODUCT_ID as PRODUCT5_75_1_, card1_.id as id72_0_, card1_.CARD_NUMBER as CARD2_72_0_ from S_USER user0_ left outer join S_CARD card1_ on user0_.ID=card1_.id where user0_.ID=?
可以看出來,這種情況,就是在額外產生的sql語句裏,按照急加載的規範產生sql語句。
三、懶加載和jpa使用注意點
-
@OneToMany,Many的那一方對應的字段不能爲主鍵
-
在@JoinColumn(name = “column”)之後,如果"column"是非主鍵,則外鍵"column"字段可以不用再次聲明。聲明可能會報錯 Repeated column in mapping for entity
-
使用了@ManyToOne,對應的類不一定就要存在@OneToMany,如果是單向關係則可以忽略@OneToMany
-
@OneToOne,即使設置爲懶加載,對象的內置關聯對象也會被查詢出來。 在懶加載後也會默認調用
這個問題困擾了我好久,研究了好久才發現問題。因爲我們OneToOne會涉及到兩個表,所以在說明問題之前,我們需要搞清楚兩個東西:
(1)主表和從表關係:舉個例子,對於user和card來說,user是主表,card是從表。這麼理解,如果有用戶,這個用戶不一定有身份證,但是有身份證,他就一定有所屬的用戶。
(2)懶加載需要一個代理對象:就是說如果用戶需要身份證懶加載,則需要先證明用戶有身份證。而身份證需要懶加載不需要證明有用戶,因爲一個身份證默認有一個用戶。
對於OneToOne來說,如果card實體類加入了@JoinColumn關鍵字,相當於聲明瞭user爲主表,這時候如果我們使用懶加載後查詢card,則只會嘗試一個sql語句;如果查詢user(默認有4個用戶),則會產生
5個sql,即1個懶加載sql+4個根據用戶查詢身份證的sql。同理,如果user類也加入了@JoinColumn關鍵字,則查詢兩個類都會產生5個sql。效果相當於在懶加載的基礎上,調用了內置類, -
一般急加載適用於數據查詢完就需要在展示出來的。如果product的user使用了懶加載,並且在後臺查數據的時候沒有查詢user對象。這時候前臺如果調用product.user.user內置屬性就會報錯。解決辦法就是改爲急加載或者在後臺手動查詢。
-
在ajax請求數據的時候,默認會啓用jackson框架對查詢的屬性進行解析,即會遍歷每個屬性的get方法,就也就造成的懶加載的失效。解決辦法是在不想被get遍歷的屬性前加上@JsonIgnore註解,便可以忽略json解析!