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