【03】SSH练习——hibernate的级联和1+n问题

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值