【SSH網上商城專案實戰05】完成資料庫的級聯查詢和分頁
轉自:https://blog.csdn.net/eson_15/article/details/51320212
上一節我們完成了EasyUI選單的實現。這一節我們主要來寫一下CategoryServiceImpl實現類,完成資料庫的級聯查詢。一般專案從後往前做,先做service(我們沒有抽取Dao,最後再抽取),做完了再做上面層。
在寫之前,先看一下資料庫中的表的情況:
1 drop database if exists shop; 2 /*建立資料庫,並設定編碼*/ 3 create database shop default character set utf8; 4 5 use shop; 6 /*刪除管理員表*/ 7 drop table if exists account; 8 /*刪除商品類別表*/ 9 drop table if exists category; 10 11 /*============================*/ 12 /* Table:管理員表結構 */ 13 /*============================*/ 14 create table account 15 ( 16 /* 管理員編號,自動增長 */ 17 id int primary key not null auto_increment, 18 /* 管理員登入名 */ 19 login varchar(20), 20 /* 管理員姓名 */ 21 name varchar(20), 22 /* 管理員密碼 */ 23 pass varchar(20) 24 ); 25 26 /*============================*/ 27 /* Table:商品類別表結構 */ 28 /*============================*/ 29 create table category 30 ( 31 /* 類別編號,自動增長 */ 32 id int primary key not null auto_increment, 33 /* 類別名稱 */ 34 type varchar(20), 35 /* 類別是否為熱點類別,熱點類別才有可能顯示在首頁*/ 36 hot bool default false, 37 /* 外來鍵,此類別由哪位管理員管理 */ 38 account_id int, 39 constraint aid_FK foreign key(account_id) references account(id) 40 );
主要有兩張表,商品類別表和管理員表,並且商品類別表中提供了一個外來鍵關聯管理員表。也就是商品和管理員是多對一的關係。現在我們開始編寫查詢商品的類別資訊,需要級聯管理員。
1. 實現級聯查詢方法
首先在CategoryService介面中定義該方法:
1 public interface CategoryService extends BaseService<Category> { 2 //查詢類別資訊,級聯管理員 3 public List<Category> queryJoinAccount(String type); //使用類別的名稱查詢 4 } 5 然後我們在CategoryService的實現類CategoryServiceImpl中實現這個方法: 6 7 8 9 10 11 @Service("categoryService") 12 public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService { 13 14 @Override 15 public List<Category> queryJoinAccount(String type) { 16 String hql = "from Category c where c.type like :type"; 17 return getSession().createQuery(hql) 18 .setString("type", "%" + type + "%").list(); 19 } 20 }
在兩個Model中我們配一下關聯註解:
//Category類中
1 package cn.it.shop.model; 2 3 import java.util.Set; 4 5 import javax.persistence.Column; 6 import javax.persistence.Entity; 7 import javax.persistence.FetchType; 8 import javax.persistence.GeneratedValue; 9 import javax.persistence.Id; 10 import javax.persistence.JoinColumn; 11 import javax.persistence.ManyToOne; 12 13 14 /** 15 * Category entity. @author MyEclipse Persistence Tools 16 */ 17 @Entity 18 public class Category implements java.io.Serializable { 19 20 // Fields 21 22 private Integer id; 23 private Account account; 24 private String type; 25 private Boolean hot; 26 // private Set<Product> products = new HashSet<Product>(0); 27 28 29 // Constructors 30 31 /** default constructor */ 32 public Category() { 33 } 34 35 @Override 36 public String toString() { 37 return "Category [id=" + id + ", account=" + account + ", type=" + type 38 + ", hot=" + hot + "]"; 39 } 40 41 /** full constructor */ 42 public Category(Account account, String type, Boolean hot, 43 Set<Product> products) { 44 this.account = account; 45 this.type = type; 46 this.hot = hot; 47 // this.products = products; 48 } 49 50 public Category(Integer id, String type, Boolean hot) { 51 super(); 52 this.id = id; 53 this.type = type; 54 this.hot = hot; 55 } 56 57 public Category(String type, Boolean hot) { 58 super(); 59 this.type = type; 60 this.hot = hot; 61 } 62 63 // Property accessors 64 @Id 65 @GeneratedValue 66 @Column(name = "id", unique = true, nullable = false) 67 public Integer getId() { 68 return this.id; 69 } 70 71 public void setId(Integer id) { 72 this.id = id; 73 } 74 75 @ManyToOne(fetch = FetchType.LAZY) 76 @JoinColumn(name = "aid") 77 public Account getAccount() { 78 return this.account; 79 } 80 81 public void setAccount(Account account) { 82 this.account = account; 83 } 84 85 @Column(name = "type", length = 20) 86 public String getType() { 87 return this.type; 88 } 89 90 public void setType(String type) { 91 this.type = type; 92 } 93 94 @Column(name = "hot") 95 public Boolean getHot() { 96 return this.hot; 97 } 98 99 public void setHot(Boolean hot) { 100 this.hot = hot; 101 } 102 103 // @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "category") 104 // public Set<Product> getProducts() { 105 // return this.products; 106 // } 107 // 108 // public void setProducts(Set<Product> products) { 109 // this.products = products; 110 // } 111 112 }
//Account類中
1 package cn.it.shop.model; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 import javax.persistence.CascadeType; 7 import javax.persistence.Column; 8 import javax.persistence.Entity; 9 import javax.persistence.FetchType; 10 import javax.persistence.GeneratedValue; 11 import javax.persistence.Id; 12 import javax.persistence.OneToMany; 13 import javax.persistence.Table; 14 15 /** 16 * Account entity. @author MyEclipse Persistence Tools 17 */ 18 @Entity 19 public class Account implements java.io.Serializable { 20 21 // Fields 22 23 private Integer id; 24 private String login; 25 private String name; 26 private String pass; 27 // private Set<Category> categories = new HashSet<Category>(0); 28 29 30 // Constructors 31 32 /** default constructor */ 33 public Account() { 34 } 35 36 @Override 37 public String toString() { 38 return "Account [id=" + id + ", login=" + login + ", name=" + name 39 + ", pass=" + pass + "]"; 40 } 41 42 /** full constructor */ 43 public Account(String login, String name, String pass, 44 Set<Category> categories) { 45 this.login = login; 46 this.name = name; 47 this.pass = pass; 48 // this.categories = categories; 49 } 50 51 52 public Account(String login, String name, String pass) { 53 super(); 54 this.login = login; 55 this.name = name; 56 this.pass = pass; 57 } 58 59 // Property accessors 60 @Id 61 @GeneratedValue 62 @Column(name = "id", unique = true, nullable = false) 63 public Integer getId() { 64 return this.id; 65 } 66 67 public void setId(Integer id) { 68 this.id = id; 69 } 70 71 @Column(name = "login", length = 20) 72 public String getLogin() { 73 return this.login; 74 } 75 76 public void setLogin(String login) { 77 this.login = login; 78 } 79 80 @Column(name = "name", length = 20) 81 public String getName() { 82 return this.name; 83 } 84 85 public void setName(String name) { 86 this.name = name; 87 } 88 89 @Column(name = "pass", length = 20) 90 public String getPass() { 91 return this.pass; 92 } 93 94 public void setPass(String pass) { 95 this.pass = pass; 96 } 97 98 // @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "account") 99 // public Set<Category> getCategories() { 100 // return this.categories; 101 // } 102 // 103 // public void setCategories(Set<Category> categories) { 104 // this.categories = categories; 105 // } 106 107 }
然後我們在測試類中測試一下:
1 @RunWith(SpringJUnit4ClassRunner.class) 2 @ContextConfiguration(locations="classpath:beans.xml") 3 public class CategoryServiceImplTest { 4 5 @Resource 6 private CategoryService categoryService; 7 8 @Test 9 public void testQueryJoinAccount() { 10 for(Category c : categoryService.queryJoinAccount("")) { 11 System.out.println(c); 12 System.out.println(c.getAccount()); 13 } 14 } 15 }
2. 級聯查詢存在的問題
我們看一下控制檯的輸出可以看出,它發了不止一條SQL語句,但是我們明明只查詢了一次,為什麼會發這麼多語句呢?這就是常見的1+N問題。所謂的1+N問題,就是首先發出一條語句查詢當前物件,然後發出N條語句查詢關聯物件,因此效率變得很低。這裡就兩個物件,如果有更多的物件,那效率就會大打折扣了,我們該如何解決這個問題呢?
可能大家會想到將fetch設定生FetchType.LAZY就不會發多條語句了,但是這肯定不行,因為設定成LAZY後,我們就拿不到Account物件了,比較好的解決方法是我們自己寫hql語句,使用join fetch。具體看修改後的CategoryServiceImpl實現類:
1 @Service("categoryService") 2 public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService { 3 4 @Override 5 public List<Category> queryJoinAccount(String type) { 6 String hql = "from Category c left join fetch c.account where c.type like :type"; 7 return getSession().createQuery(hql) 8 .setString("type", "%" + type + "%").list(); 9 } 10 }
left join表示關聯Account一起查詢,fetch表示將Account物件加到Category中去,這樣就只會發一條SQL語句了,並且返回的Category中也包含了Account物件了。
3. 完成分頁功能
Hibernate中的分頁很簡單,只需要呼叫兩個方法setFirstResult和setMaxResults即可:我們修改一下CategoryService介面和它的實現類CategoryServiceImpl:
1 //CategoryService 2 public interface CategoryService extends BaseService<Category> { 3 //查詢類別資訊,級聯管理員 4 public List<Category> queryJoinAccount(String type, int page, int size); //並實現分頁 5 } 6 7 //CategoryServiceImpl 8 @Service("categoryService") 9 public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService { 10 11 @Override 12 public List<Category> queryJoinAccount(String type, int page, int size) { 13 String hql = "from Category c left join fetch c.account where c.type like :type"; 14 return getSession().createQuery(hql) 15 .setString("type", "%" + type + "%") 16 .setFirstResult((page-1) * size) //從第幾個開始顯示 17 .setMaxResults(size) //顯示幾個 18 .list(); 19 } 20 }
我們在測試類中測試一下:
1 @RunWith(SpringJUnit4ClassRunner.class) 2 @ContextConfiguration(locations="classpath:beans.xml") 3 public class CategoryServiceImplTest { 4 5 @Resource 6 private CategoryService categoryService; 7 8 @Test 9 public void testQueryJoinAccount() { 10 for(Category c : categoryService.queryJoinAccount("",1,2)) { //顯示第一頁,每頁2條資料 11 System.out.println(c + "," + c.getAccount()); 12 } 13 } 14 }
為此,我們寫完了Service的方法了,完成了對商品類別的級聯查詢和分頁功能。
轉自:https://blog.csdn.net/eson_15/article/details/51320212
上一節我們完成了EasyUI選單的實現。這一節我們主要來寫一下CategoryServiceImpl實現類,完成資料庫的級聯查詢。一般專案從後往前做,先做service(我們沒有抽取Dao,最後再抽取),做完了再做上面層。
在寫之前,先看一下資料庫中的表的情況:
1 drop database if exists shop; 2 /*建立資料庫,並設定編碼*/ 3 create database shop default character set utf8; 4 5 use shop; 6 /*刪除管理員表*/ 7 drop table if exists account; 8 /*刪除商品類別表*/ 9 drop table if exists category; 10 11 /*============================*/ 12 /* Table:管理員表結構 */ 13 /*============================*/ 14 create table account 15 ( 16 /* 管理員編號,自動增長 */ 17 id int primary key not null auto_increment, 18 /* 管理員登入名 */ 19 login varchar(20), 20 /* 管理員姓名 */ 21 name varchar(20), 22 /* 管理員密碼 */ 23 pass varchar(20) 24 ); 25 26 /*============================*/ 27 /* Table:商品類別表結構 */ 28 /*============================*/ 29 create table category 30 ( 31 /* 類別編號,自動增長 */ 32 id int primary key not null auto_increment, 33 /* 類別名稱 */ 34 type varchar(20), 35 /* 類別是否為熱點類別,熱點類別才有可能顯示在首頁*/ 36 hot bool default false, 37 /* 外來鍵,此類別由哪位管理員管理 */ 38 account_id int, 39 constraint aid_FK foreign key(account_id) references account(id) 40 );
主要有兩張表,商品類別表和管理員表,並且商品類別表中提供了一個外來鍵關聯管理員表。也就是商品和管理員是多對一的關係。現在我們開始編寫查詢商品的類別資訊,需要級聯管理員。
1. 實現級聯查詢方法
首先在CategoryService介面中定義該方法:
1 public interface CategoryService extends BaseService<Category> { 2 //查詢類別資訊,級聯管理員 3 public List<Category> queryJoinAccount(String type); //使用類別的名稱查詢 4 } 5 然後我們在CategoryService的實現類CategoryServiceImpl中實現這個方法: 6 7 8 9 10 11 @Service("categoryService") 12 public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService { 13 14 @Override 15 public List<Category> queryJoinAccount(String type) { 16 String hql = "from Category c where c.type like :type"; 17 return getSession().createQuery(hql) 18 .setString("type", "%" + type + "%").list(); 19 } 20 }
在兩個Model中我們配一下關聯註解:
//Category類中
1 package cn.it.shop.model; 2 3 import java.util.Set; 4 5 import javax.persistence.Column; 6 import javax.persistence.Entity; 7 import javax.persistence.FetchType; 8 import javax.persistence.GeneratedValue; 9 import javax.persistence.Id; 10 import javax.persistence.JoinColumn; 11 import javax.persistence.ManyToOne; 12 13 14 /** 15 * Category entity. @author MyEclipse Persistence Tools 16 */ 17 @Entity 18 public class Category implements java.io.Serializable { 19 20 // Fields 21 22 private Integer id; 23 private Account account; 24 private String type; 25 private Boolean hot; 26 // private Set<Product> products = new HashSet<Product>(0); 27 28 29 // Constructors 30 31 /** default constructor */ 32 public Category() { 33 } 34 35 @Override 36 public String toString() { 37 return "Category [id=" + id + ", account=" + account + ", type=" + type 38 + ", hot=" + hot + "]"; 39 } 40 41 /** full constructor */ 42 public Category(Account account, String type, Boolean hot, 43 Set<Product> products) { 44 this.account = account; 45 this.type = type; 46 this.hot = hot; 47 // this.products = products; 48 } 49 50 public Category(Integer id, String type, Boolean hot) { 51 super(); 52 this.id = id; 53 this.type = type; 54 this.hot = hot; 55 } 56 57 public Category(String type, Boolean hot) { 58 super(); 59 this.type = type; 60 this.hot = hot; 61 } 62 63 // Property accessors 64 @Id 65 @GeneratedValue 66 @Column(name = "id", unique = true, nullable = false) 67 public Integer getId() { 68 return this.id; 69 } 70 71 public void setId(Integer id) { 72 this.id = id; 73 } 74 75 @ManyToOne(fetch = FetchType.LAZY) 76 @JoinColumn(name = "aid") 77 public Account getAccount() { 78 return this.account; 79 } 80 81 public void setAccount(Account account) { 82 this.account = account; 83 } 84 85 @Column(name = "type", length = 20) 86 public String getType() { 87 return this.type; 88 } 89 90 public void setType(String type) { 91 this.type = type; 92 } 93 94 @Column(name = "hot") 95 public Boolean getHot() { 96 return this.hot; 97 } 98 99 public void setHot(Boolean hot) { 100 this.hot = hot; 101 } 102 103 // @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "category") 104 // public Set<Product> getProducts() { 105 // return this.products; 106 // } 107 // 108 // public void setProducts(Set<Product> products) { 109 // this.products = products; 110 // } 111 112 }
//Account類中
1 package cn.it.shop.model; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 import javax.persistence.CascadeType; 7 import javax.persistence.Column; 8 import javax.persistence.Entity; 9 import javax.persistence.FetchType; 10 import javax.persistence.GeneratedValue; 11 import javax.persistence.Id; 12 import javax.persistence.OneToMany; 13 import javax.persistence.Table; 14 15 /** 16 * Account entity. @author MyEclipse Persistence Tools 17 */ 18 @Entity 19 public class Account implements java.io.Serializable { 20 21 // Fields 22 23 private Integer id; 24 private String login; 25 private String name; 26 private String pass; 27 // private Set<Category> categories = new HashSet<Category>(0); 28 29 30 // Constructors 31 32 /** default constructor */ 33 public Account() { 34 } 35 36 @Override 37 public String toString() { 38 return "Account [id=" + id + ", login=" + login + ", name=" + name 39 + ", pass=" + pass + "]"; 40 } 41 42 /** full constructor */ 43 public Account(String login, String name, String pass, 44 Set<Category> categories) { 45 this.login = login; 46 this.name = name; 47 this.pass = pass; 48 // this.categories = categories; 49 } 50 51 52 public Account(String login, String name, String pass) { 53 super(); 54 this.login = login; 55 this.name = name; 56 this.pass = pass; 57 } 58 59 // Property accessors 60 @Id 61 @GeneratedValue 62 @Column(name = "id", unique = true, nullable = false) 63 public Integer getId() { 64 return this.id; 65 } 66 67 public void setId(Integer id) { 68 this.id = id; 69 } 70 71 @Column(name = "login", length = 20) 72 public String getLogin() { 73 return this.login; 74 } 75 76 public void setLogin(String login) { 77 this.login = login; 78 } 79 80 @Column(name = "name", length = 20) 81 public String getName() { 82 return this.name; 83 } 84 85 public void setName(String name) { 86 this.name = name; 87 } 88 89 @Column(name = "pass", length = 20) 90 public String getPass() { 91 return this.pass; 92 } 93 94 public void setPass(String pass) { 95 this.pass = pass; 96 } 97 98 // @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "account") 99 // public Set<Category> getCategories() { 100 // return this.categories; 101 // } 102 // 103 // public void setCategories(Set<Category> categories) { 104 // this.categories = categories; 105 // } 106 107 }
然後我們在測試類中測試一下:
1 @RunWith(SpringJUnit4ClassRunner.class) 2 @ContextConfiguration(locations="classpath:beans.xml") 3 public class CategoryServiceImplTest { 4 5 @Resource 6 private CategoryService categoryService; 7 8 @Test 9 public void testQueryJoinAccount() { 10 for(Category c : categoryService.queryJoinAccount("")) { 11 System.out.println(c); 12 System.out.println(c.getAccount()); 13 } 14 } 15 }
2. 級聯查詢存在的問題
我們看一下控制檯的輸出可以看出,它發了不止一條SQL語句,但是我們明明只查詢了一次,為什麼會發這麼多語句呢?這就是常見的1+N問題。所謂的1+N問題,就是首先發出一條語句查詢當前物件,然後發出N條語句查詢關聯物件,因此效率變得很低。這裡就兩個物件,如果有更多的物件,那效率就會大打折扣了,我們該如何解決這個問題呢?
可能大家會想到將fetch設定生FetchType.LAZY就不會發多條語句了,但是這肯定不行,因為設定成LAZY後,我們就拿不到Account物件了,比較好的解決方法是我們自己寫hql語句,使用join fetch。具體看修改後的CategoryServiceImpl實現類:
1 @Service("categoryService") 2 public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService { 3 4 @Override 5 public List<Category> queryJoinAccount(String type) { 6 String hql = "from Category c left join fetch c.account where c.type like :type"; 7 return getSession().createQuery(hql) 8 .setString("type", "%" + type + "%").list(); 9 } 10 }
left join表示關聯Account一起查詢,fetch表示將Account物件加到Category中去,這樣就只會發一條SQL語句了,並且返回的Category中也包含了Account物件了。
3. 完成分頁功能
Hibernate中的分頁很簡單,只需要呼叫兩個方法setFirstResult和setMaxResults即可:我們修改一下CategoryService介面和它的實現類CategoryServiceImpl:
1 //CategoryService 2 public interface CategoryService extends BaseService<Category> { 3 //查詢類別資訊,級聯管理員 4 public List<Category> queryJoinAccount(String type, int page, int size); //並實現分頁 5 } 6 7 //CategoryServiceImpl 8 @Service("categoryService") 9 public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService { 10 11 @Override 12 public List<Category> queryJoinAccount(String type, int page, int size) { 13 String hql = "from Category c left join fetch c.account where c.type like :type"; 14 return getSession().createQuery(hql) 15 .setString("type", "%" + type + "%") 16 .setFirstResult((page-1) * size) //從第幾個開始顯示 17 .setMaxResults(size) //顯示幾個 18 .list(); 19 } 20 }
我們在測試類中測試一下:
1 @RunWith(SpringJUnit4ClassRunner.class) 2 @ContextConfiguration(locations="classpath:beans.xml") 3 public class CategoryServiceImplTest { 4 5 @Resource 6 private CategoryService categoryService; 7 8 @Test 9 public void testQueryJoinAccount() { 10 for(Category c : categoryService.queryJoinAccount("",1,2)) { //顯示第一頁,每頁2條資料 11 System.out.println(c + "," + c.getAccount()); 12 } 13 } 14 }
為此,我們寫完了Service的方法了,完成了對商品類別的級聯查詢和分頁功能。