1. 程式人生 > >【SSH網上商城專案實戰05】完成資料庫的級聯查詢和分頁

【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的方法了,完成了對商品類別的級聯查詢和分頁功能。