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

来源:互联网 发布:淘宝页面改版 编辑:程序博客网 时间:2024/05/11 18:50

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 {@Overridepublic 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 {  /*@Resourceprivate 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 {@Overridepublic 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

0 0