• hibernate注解(三)1+N问题


    一。什么时候会遇到1+N的问题?

    前提:Hibernate默认表与表的关联方法是fetch="select",不是fetch="join",这都是为了懒加载而准备的。

    1)一对多(<set><list>) ,在1的这方,通过1条sql查找得到了1个对象,由于关联的存在 ,那么又需要将这个对象关联的集合取出,所以合集数量是n还要发出n条sql,于是本来的1条sql查询变成了1 +n条 。

    2)多对一<many-to-one>  ,在多的这方,通过1条sql查询得到了n个对象,由于关联的存在,也会将这n个对象对应的1 方的对象取出, 于是本来的1条sql查询变成了1 +n条 。

    3)iterator 查询时,一定先去缓存中找(1条sql查集合,只查出ID),在没命中时,会再按ID到库中逐一查找, 产生1+n条SQL

    二。怎么解决1+N 问题?


    1 )lazy=true, hibernate3开始已经默认是lazy=true了;lazy=true时不会立刻查询关联对象,只有当需要关联对象(访问其属性,非id字段)时才会发生查询动作。

    2)使用二级缓存, 二级缓存的应用将不怕1+N 问题,因为即使第一次查询很慢(未命中),以后查询直接缓存命中也是很快的。刚好又利用了1+N 。

    3) 当然你也可以设定fetch="join",一次关联表全查出来,但失去了懒加载的特性。

    三。示例

    1.两张表

    create table t_group (
    id integer not null auto_increment,
    name varchar(255),
    primary key (id)
    )

    create table t_user (
    id integer not null auto_increment,
    name varchar(255),
    myGroup integer,
    primary key (id)
    )

    alter table t_user
      add index FKCB63CCB6D4A4BEC0 (myGroup),
      add constraint FKCB63CCB6D4A4BEC0
      foreign key (myGroup)
      references t_group (id)

    2.代码如下:

    @Entity
    @Table(name="t_group")
    public class Group {
        private int id;
        private String name;
        
        private Set<User> users = new HashSet<User>();
        public Group(){
            
        }
        public Group(String name) {
            this.name = name;
        }
        @Id
        @GeneratedValue
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        @OneToMany(mappedBy="group")
        public Set<User> getUsers() {
            return users;
        }
        public void setUsers(Set<User> users) {
            this.users = users;
        }
    }
    @Entity
    @Table(name="t_user")
    public class User {
        private int id;
        private String name;
        private Group group;
        
        public User(){
            
        }
        public User(String name) {
            this.name = name;
        }
    
        @ManyToOne
        @JoinColumn(name="myGroup")
        public Group getGroup() {
            return group;
        }
        public void setGroup(Group group) {
            this.group = group;
        }
        @Id
        @GeneratedValue
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }

    3.数据如下

    4.默认情况下

        @Test
        public void getAllUser(){
            Session session = sf.openSession();
            Transaction tx =session.beginTransaction();
            List<User> groups = session.createQuery("from User").list();    //只要一条语句就可以取出全部内容。但hibernate默认会把关联的Group也取出来。
            String string = "---------------------------- ";             //由于每个user都不同group,所以会发出N条语句查询group.
                                            //本例会发出1+10条
    for(User g : groups){ string += g.getName(); } System.out.println(string); tx.commit(); session.close(); }

    5.解决:

    (1).在User类中标注group----fetch=FetchType.LAZY

        @ManyToOne(fetch=FetchType.LAZY)
        @JoinColumn(name="myGroup")
        public Group getGroup() {
            return group;
        }

    (2).在Group类中写---------@BatchSize(size=10)

    @Entity
    @Table(name="t_group")
    @BatchSize(size=10)
    public class Group {}

    (3)使用连接查询

    List<User> groups = session.createQuery("from User u left join fetch u.group g").list();

    注意:group是属性名,而不是数据库字段名。 不然出错

    6.实验花絮:

        @Test
        public void saveGroupAndUser2(){
            Session session = sf.openSession();
            Transaction tx =session.beginTransaction();
    
            for(int i=1; i<=10; i++) {
                Group g= new Group();
                g.setName("g"+i);
                session.save(g);
                
                User u = new User();
                u.setName("u"+i);
                u.setGroup(g);
                session.save(u);
            }
            
            tx.commit();
            session.close();
        }

    参考下面的代码,上面红色字体的session.save(g)可以省略.不然org.hibernate.TransientObjectException: object references an unsaved transient instance 

    在User类中
        @ManyToOne(cascade=CascadeType.ALL)
        @JoinColumn(name="myGroup")
        public Group getGroup() {
            return group;
        }    
  • 相关阅读:
    关于spring security的若干事情
    .net2005 datagridview 如何获取值改变的单元格的集合??(小弟没有为datagridview添加数据源,也就是说单元格中的数据是手工录入的)
    关于做一个通用打印类的设想,大家谈谈看法
    请教C#,两个类中的变量互访问题
    刚发现了一个问题,关于vs2005 datagridview的,我发现在设计行标头的HeaderCell.Value的时候要是设置RowTemplate.Height 的值>= 17则行标头的那个黑三角就显示出来了,要是小于17就不能显示了,想问问大家,是怎么回事?
    软件架构模式基本概念及三者区别
    以英雄联盟的方式建模,谈对依赖注入(DI)的理解以及Autofac的用法(一)
    适配器模式
    [翻译] WCF运行时架构
    关于synchronized 影响可见性的问题
  • 原文地址:https://www.cnblogs.com/yuyutianxia/p/3287702.html
Copyright © 2020-2023  润新知