【SSH網上商城項目實戰05】完成數據庫的級聯查詢和分頁

轉自:http://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. );  
drop database if exists shop;
/*創建數據庫,並設置編碼*/
create database shop default character set utf8;

use shop;
/*刪除管理員表*/
drop table if exists account;
/*刪除商品類別表*/
drop table if exists category;

/*============================*/
/*      Table:管理員表結構                       */
/*============================*/
create table account
(
	/* 管理員編號,自動增長 */
	id int primary key not null auto_increment,
	/* 管理員登錄名 */
	login varchar(20),
	/* 管理員姓名 */
	name varchar(20),
	/* 管理員密碼 */
	pass varchar(20)
);

/*============================*/
/*     Table:商品類別表結構                      */
/*============================*/
create table category
(
   /* 類別編號,自動增長 */
   id  int primary key not null auto_increment,
   /* 類別名稱 */
   type varchar(20),
   /* 類別是否爲熱點類別,熱點類別纔有可能顯示在首頁*/
   hot  bool default false,
   /* 外鍵,此類別由哪位管理員管理 */
   account_id int,
   constraint aid_FK foreign key(account_id) references account(id)
);
        主要有兩張表,商品類別表和管理員表,並且商品類別表中提供了一個外鍵關聯管理員表。也就是商品和管理員是多對一的關係。現在我們開始編寫查詢商品的類別信息,需要級聯管理員。

1. 實現級聯查詢方法

        首先在CategoryService接口中定義該方法:

  1. public interface CategoryService extends BaseService<Category> {  
  2.     //查詢類別信息,級聯管理員  
  3.     public List<Category> queryJoinAccount(String type); //使用類別的名稱查詢  
  4. }  
public interface CategoryService extends BaseService<Category> {
	//查詢類別信息,級聯管理員
	public List<Category> queryJoinAccount(String type); //使用類別的名稱查詢
}
        然後我們在CategoryService的實現類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 where c.type like :type";  
  7.         return getSession().createQuery(hql)  
  8.                 .setString("type""%" + type + "%").list();  
  9.     }  
  10. }  
@Service("categoryService")
public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {

	@Override
	public List<Category> queryJoinAccount(String type) {
		String hql = "from Category c where c.type like :type";
		return getSession().createQuery(hql)
				.setString("type", "%" + type + "%").list();
	}
}
        在兩個Model中我們配一下關聯註解:

  1. //Category類中  
  2. @ManyToOne(fetch = FetchType.EAGER)  
  3. @JoinColumn(name = "account_id")  
  4. public Account getAccount() {  
  5.     return this.account;  
  6. }  
  7. //Account類中  
  8. @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "account")  
  9. public Set<Category> getCategories() {  
  10.     return this.categories;  
  11. }  
//Category類中
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "account_id")
public Account getAccount() {
	return this.account;
}
//Account類中
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "account")
public Set<Category> getCategories() {
    return this.categories;
}
        然後我們在測試類中測試一下:

  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. }  
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:beans.xml")
public class CategoryServiceImplTest {

	@Resource
	private CategoryService categoryService;
	
	@Test
	 public void testQueryJoinAccount() {
 		for(Category c : categoryService.queryJoinAccount("")) {
			 System.out.println(c);
			 System.out.println(c.getAccount());
 		}
 	}
}

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. }  
@Service("categoryService")
public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {

	@Override
	public List<Category> queryJoinAccount(String type) {
		String hql = "from Category c left join fetch c.account where c.type like :type";
		return getSession().createQuery(hql)
				.setString("type", "%" + type + "%").list();
	}
}
        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. }  
//CategoryService
public interface CategoryService extends BaseService<Category> {
	//查詢類別信息,級聯管理員
	public List<Category> queryJoinAccount(String type, int page, int size); //並實現分頁
}

//CategoryServiceImpl
@Service("categoryService")
public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {

    @Override
    public List<Category> queryJoinAccount(String type, int page, int size) {
        String hql = "from Category c left join fetch c.account where c.type like :type";
        return getSession().createQuery(hql)
                .setString("type", "%" + type + "%")
                .setFirstResult((page-1) * size) //從第幾個開始顯示
                .setMaxResults(size) //顯示幾個
                .list();
    }
}
        我們在測試類中測試一下:

  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. }  
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:beans.xml")
public class CategoryServiceImplTest {

	@Resource
	private CategoryService categoryService;

	@Test
	public void testQueryJoinAccount() {
		for(Category c : categoryService.queryJoinAccount("",1,2)) { //顯示第一頁,每頁2條數據
			System.out.println(c + "," + c.getAccount());
		}
	}
}
        爲此,我們寫完了Service的方法了,完成了對商品類別的級聯查詢和分頁功能。


       相關閱讀:http://blog.csdn.net/column/details/str2hiberspring.html

        整個項目的源碼下載地址:http://blog.csdn.net/eson_15/article/details/51479994

_____________________________________________________________________________________________________________________________________________________

-----樂於分享,共同進步!

-----更多文章請看:http://blog.csdn.net/eson_15


9
1

我的同類文章

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