1、数据库表设计
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)
);
2、在entity中用注解配置级联关系
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "account")
public Set<Category> getCategories() {
return this.categories;
}
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "account_id")
public Account getAccount() {
return this.account;
}
3、在service中添加一个方法用于测试
import java.util.List;
import org.springframework.stereotype.Service;
import com.cqb.bean.Category;
@Service("categoryService")
public class CategoryServiceImp extends BaseServiceImp<Category> implements CategoryService {
@Override
public List<Category> queryJoinAccount(String type) {
// TODO Auto-generated method stub
//String hql = "from Category c where c.type like :type";
String hql = "from Category c left join fetch c.account where c.type like :type";
return getSession().createQuery(hql).setString("type", "%"+type+"%").list();
}
}
4、用Junit测试
import java.util.Date;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.cqb.bean.Category;
import com.cqb.service.CategoryService;
import com.cqb.service.CategoryServiceImp;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:beans.xml")
public class SSHTest {
/*
@Resource
private CategoryService categoryService;
@Resource
private Date date;
public void setCategoryService(CategoryService categoryService) {
this.categoryService = categoryService;
}
public CategoryService getCategoryService() {
return categoryService;
}
@Test //测试Spring IOC的开发环境
public void springIoc() {
System.out.println(date);
}
@Test //测试Hibernate的开发环境,因为没有整合,可以直接new
public void hihernate() {
Category category = categoryService.get(1);
category.setType("chenqingb");
categoryService.update(category);
System.out.println("hahahahaha");
} */
@Resource
private CategoryService categoryService;
@Test
public void testQueryJoinAccount() {
for(Category c : categoryService.queryJoinAccount("")) {
System.out.println(c);
System.out.println(c.getAccount());
}
}
}
控制台信息:
Hibernate: select category0_.id as id1_1_, category0_.account_id as account_4_1_, category0_.hot as hot2_1_, category0_.type as type3_1_ from test.category category0_ where category0_.type like ?
Hibernate: select account0_.id as id1_0_0_, account0_.login as login2_0_0_, account0_.name as name3_0_0_, account0_.pass as pass4_0_0_ from test.account account0_ where account0_.id=?
com.cqb.bean.Category@7b60c3e
com.cqb.bean.Account@3bcd426c
发现出现了两条hql语句(数据库中category表和account表各只有一条信息 所以此处1+n 为1+1)
由此 我们开始研究hibernate的抓取策略
1+N问题也可以叫N+1问题,什么是1+N问题呢?
如果在一个对象里关联另一个对象,并且fetch = FetchType.EAGER。
比如说ManyToOne(OneToMany也存在这种问题)关联,本来只需要取Many里的对象属性,可是Many里关联的对象都会单独再发一条语句取关联对象的属性。
本来只用发一条就可以查出Many里的对象属性,可是它发了一条语句后,再发N条语句取关联对象的数据。
解决办法:
1、 设置多的那方的FetchType为LAZY :在合适的时候才发出语句(按需要发语句)。
2、BatchSize:在One对象设置Size后,取出Many里的数据后,再发N/Size条语句取关联对象的数据,从而达到少发语句的目的。(----不能彻底解决~)
3、使用Join Fetch:(使用Criteria或者写HQL语句时在后面加上left join fetch w.category)将Many与One做外连接,因此只要发一条语句就可以查出Many与其相关联的One对象数据,Criteria默认就是这种做法。
如何选择解决办法:
如果只要用Many里的对象,不用关联对象的属性,那就用方法1解决;
如果要Many里的对象属性,也想要关联的对象属性就用方法3解决。
在本次测试中 需要获取关联对象account 若在account中设置未lazy 则取不出account 所以用第三种方案
import java.util.List;
import org.springframework.stereotype.Service;
import com.cqb.bean.Category;
@Service("categoryService")
public class CategoryServiceImp extends BaseServiceImp<Category> implements CategoryService {
@Override
public List<Category> queryJoinAccount(String type) {
// TODO Auto-generated method stub
//String hql = "from Category c where c.type like :type";
String hql = "from Category c left join fetch c.account where c.type like :type";
return getSession().createQuery(hql).setString("type", "%"+type+"%").list();
}
}
控制台信息:
Hibernate: select category0_.id as id1_1_0_, account1_.id as id1_0_1_, category0_.account_id as account_4_1_0_, category0_.hot as hot2_1_0_, category0_.type as type3_1_0_, account1_.login as login2_0_1_, account1_.name as name3_0_1_, account1_.pass as pass4_0_1_ from test.category category0_ left outer join test.account account1_ on category0_.account_id=account1_.id where category0_.type like ?
com.cqb.bean.Category@15723761
com.cqb.bean.Account@65327f5