上一节我们完成了EasyUI菜单的实现。这一节我们主要来写一下CategoryServiceImpl实现类,完成数据库的级联查询。一般项目从后往前做,先做service(我们没有抽取Dao,最后再抽取),做完了再做上面层。
  在写之前,先看一下数据库中的表的情况:
SQL代码
    - 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接口中定义该方法:
Java代码
    - public interface CategoryService extends BaseService<Category> {    
-       
-     public List<Category> queryJoinAccount(String type);   
- }   
  然后我们在CategoryService的实现类CategoryServiceImpl中实现这个方法:
Java代码
    - @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中我们配一下关联注解:
Java代码
    -   
- @ManyToOne(fetch = FetchType.EAGER)    
- @JoinColumn(name = "account_id")    
- public Account getAccount() {    
-     return this.account;    
- }    
-   
- @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "account")    
- public Set<Category> getCategories() {    
-     return this.categories;    
- }    
  然后我们在测试类中测试一下:
Java代码
    - @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实现类:
Java代码
    - @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:
Java代码
    -   
- public interface CategoryService extends BaseService<Category> {    
-       
-     public List<Category> queryJoinAccount(String type, int page, int size);   
- }    
-     
-   
- @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();    
-     }    
- }    
  我们在测试类中测试一下:
Java代码
    - @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)) {   
-             System.out.println(c + "," + c.getAccount());    
-         }    
-     }    
- }    
  为此,我们写完了Service的方法了,完成了对商品类别的级联查询和分页功能。