懶加載和急加載的使用與注意點

我這裏主要使用到了四個類,專利:product,專利擁有者:user,擁有者的寵物貓:cat,擁有者的身份證:card。他們之間的對應關係爲:
user–》product:一對多
user–》cat:多對多
user–》card:一對一

一、 實體類代碼

  1. 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;
	}

}
  1. 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;
	}

}
  1. 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;
	}


}
  1. 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;
	
  1. 懶加載和急加載初始化區別(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數據只有再需要的時候才查出來。

  1. 懶加載情況下,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信息,所以這裏調用了四次查詢語句。

  1. 急加載情況下,再去調用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

可以看出來,這種情況不會額外產生查詢語句

  1. 懶加載的情況下,如果要調用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使用注意點

  1. @OneToMany,Many的那一方對應的字段不能爲主鍵

  2. 在@JoinColumn(name = “column”)之後,如果"column"是非主鍵,則外鍵"column"字段可以不用再次聲明。聲明可能會報錯 Repeated column in mapping for entity

  3. 使用了@ManyToOne,對應的類不一定就要存在@OneToMany,如果是單向關係則可以忽略@OneToMany

  4. @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。效果相當於在懶加載的基礎上,調用了內置類,

  5. 一般急加載適用於數據查詢完就需要在展示出來的。如果product的user使用了懶加載,並且在後臺查數據的時候沒有查詢user對象。這時候前臺如果調用product.user.user內置屬性就會報錯。解決辦法就是改爲急加載或者在後臺手動查詢。

  6. 在ajax請求數據的時候,默認會啓用jackson框架對查詢的屬性進行解析,即會遍歷每個屬性的get方法,就也就造成的懶加載的失效。解決辦法是在不想被get遍歷的屬性前加上@JsonIgnore註解,便可以忽略json解析!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章