转自: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的方法了,完成了对商品类别的级联查询和分页功能。