文章出處

        上一節我們完成了EasyUI菜單的實現。這一節我們主要來寫一下CategoryServiceImpl實現類,完成數據庫的級聯查詢。一般項目從后往前做,先做service(我們沒有抽取Dao,最后再抽取),做完了再做上面層。

        在寫之前,先看一下數據庫中的表的情況:

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接口中定義該方法:

public interface CategoryService extends BaseService<Category> {  
    //查詢類別信息,級聯管理員  
    public List<Category> queryJoinAccount(String type); //使用類別的名稱查詢  
} 

  然后我們在CategoryService的實現類CategoryServiceImpl中實現這個方法:

@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中我們配一下關聯注解:

//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;  
} 

   然后我們在測試類中測試一下:

@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實現類:

@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對象了。 like:type";需要注意type后面不能有空格。

3. 完成分頁功能

        hibernate中的分頁很簡單,只需要調用兩個方法setFirstResult和setMaxResults即可:我們修改一下CategoryService接口和它的實現類CategoryServiceImpl:

//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();  
    }  
}

我們在測試類中測試一下:

@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());  
        }  
    }  
}  

 

本篇可能會遇到的錯誤:

 

org.hibernate.hql.internal.ast.QuerySyntaxException: Category is not mapped [from Category c left join fetch c.account where c.type like :type]

Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: Category is not mapped

這個的原因是 如果已經在hibernate.cfg.xml上加上

<mapping class="cn.it.shop.model.Category" />
<mapping class="cn.it.shop.model.Account" />

還是提示無法映射到 是因為在model里面 你需要加上@Entity  @Table 以及id自增@Id // 表示主鍵 @GeneratedValue

    @Column(name = "id", unique = true, nullable = false)和 每個字段的 @Column  (樓主犯錯的原因是因為以為如果是數據庫有表逆向生成model不需要寫,如果先寫model才需要寫這些@Entity和@Table @column逆向生成表)
/**
 * Category entity. @author MyEclipse Persistence Tools
 */

@Entity
@Table(name="category")
public class Category implements java.io.Serializable {


    private Integer id;
    private String type;
    private Boolean hot;
    
    private Account account;
    private Set<Product> products = new HashSet<Product>(0);
    
    
    public Category() {
    
    }
    @Override
    public String toString() {
        return "Category [id=" + id + ", type=" + type + ", hot=" + hot
                + ", account=" + account + "]";
    }
    
    
    public Category(Account account, String type, Boolean hot,
            Set<Product> products) {
        this.account = account;
        this.type = type;
        this.hot = hot;
        this.products = products;
    }
    
    public Category(Integer id, String type, Boolean hot) {
        super();
        this.id = id;
        this.type = type;
        this.hot = hot;
    }
    
    public Category(String type, Boolean hot) {
        super();
        this.type = type;
        this.hot = hot;
    }

    // Property accessors
    @Id              // 表示主鍵
    @GeneratedValue
    @Column(name = "id", unique = true, nullable = false)
    public Integer getId() {
        return this.id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
    
    //多對一 //    @ManyToOne(fetch = FetchType.LAZY)
    //@ManyToOne(fetch=FetchType.EAGER,cascade=CascadeType.ALL) //急加載,加載一個實體時,定義急加載的屬性會立即從數據庫中加載。
    @ManyToOne(fetch=FetchType.EAGER)//insert 會update null問題
    @JoinColumn(name="account_id")//注釋本表中指向另一個表的外鍵。
    public Account getAccount(){
        return this.account;
    }
    public void setAccount(Account account) {
        this.account = account;
    }

    @Column(name="type",length = 20)
    public String getType() {
        return this.type;
    }

    public void setType(String type) {
        this.type = type;
    }
    @Column(name="hot")
    public Boolean getHot() {
        return this.hot;
    }

    public void setHot(Boolean hot) {
        this.hot = hot;
    }

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()