Hibernate / MySQL基于唯一键连接表

时间:2016-01-21 12:27:16

标签: mysql hibernate hql

我正在使用hibernate 4.3.11.Final和MySql 5.6。

我试图理解以下行为:

表格

create table table_a (
    id integer not null auto_increment,
    code_table_a varchar(12) not null,
    desc_table_a varchar(50) not null,
    primary key (id),
    constraint ux_code_table_a unique (code_table_a)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

create table table_b (
    id integer not null auto_increment,
    code_table_b varchar(12) not null,
    desc_table_b varchar(50) not null,
    code_table_a varchar(12) not null,
    primary key (id),
    constraint ux_code_table_b unique (code_table_b),
    constraint ix_table_b_code_table_a_fk foreign key (code_table_a) references table_a (code_table_a)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

请注意,table_b上的外键引用table_a中的唯一键(不是主键)。

映射:

@Entity
@Table(name = "table_a")
public class TableA implements Serializable {

    private static final long serialVersionUID = 8419151902341044850L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "code_table_a")
    private String code;

    @Column(name = "desc_table_a")
    private String description;

    // hashCode and equals are based on the "code" property only

    // getters / setters
}

@Entity
@Table(name = "table_b")
public class TableB implements Serializable {

    private static final long serialVersionUID = 943565980437511902L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "code_table_b")
    private String code;

    @Column(name = "desc_table_b")
    private String description;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "code_table_a", referencedColumnName = "code_table_a", unique = true)
    private TableA tableA;

    // hashCode and equals are based on the "code" property only

    // getters / setters
}

HQL 1:

public List<TableB> findByCodeTableA(String codeTableA) {
    StringBuilder select = new StringBuilder("select tableB from TableB tableB ");
    select.append("where tableB.tableA.code = :codeTableA ");
    // select.append("inner join tableB.tableA tableA ");
    // select.append("where tableA.code = :codeTableA ");

    Session session = sessionFactory.getCurrentSession();

    Query query = session.createQuery(select.toString());
    query.setParameter("codeTableA", codeTableA);

    List<TableB> lista = new ArrayList<>();
    lista.addAll(query.list());

    return lista;
}

记录HQL 1:

21 Jan 2016 09:33:17,505 DEBUG SQL - 
    /* select
        tableB 
    from
        TableB tableB 
    where
        tableB.tableA.code = :codeTableA  */ select
            tableb0_.id as id1_1_,
            tableb0_.code_table_b as code_tab2_1_,
            tableb0_.desc_table_b as desc_tab3_1_,
            tableb0_.code_table_a as code_tab4_1_ 
        from
            table_b tableb0_ cross 
        join
            table_a tablea1_ 
        where
            tableb0_.code_table_a=tablea1_.code_table_a 
            and tablea1_.code_table_a=?
21 Jan 2016 09:33:17,560 DEBUG SQL - 
    /* load com.baldotech.hibernatepg.model.TableA */ select
        tablea0_.id as id1_0_0_,
        tablea0_.code_table_a as code_tab2_0_0_,
        tablea0_.desc_table_a as desc_tab3_0_0_ 
    from
        table_a tablea0_ 
    where
        tablea0_.code_table_a=?
21 Jan 2016 09:33:17,577 DEBUG SQL - 
    /* load com.baldotech.hibernatepg.model.TableA */ select
        tablea0_.id as id1_0_0_,
        tablea0_.code_table_a as code_tab2_0_0_,
        tablea0_.desc_table_a as desc_tab3_0_0_ 
    from
        table_a tablea0_ 
    where
        tablea0_.code_table_a=?

HQL 2:

public List<TableB> findByCodeTableA(String codeTableA) {
    StringBuilder select = new StringBuilder("select tableB from TableB tableB ");
    // select.append("where tableB.tableA.code = :codeTableA ");
    select.append("inner join tableB.tableA tableA ");
    select.append("where tableA.code = :codeTableA ");

    Session session = sessionFactory.getCurrentSession();

    Query query = session.createQuery(select.toString());
    query.setParameter("codeTableA", codeTableA);

    List<TableB> lista = new ArrayList<>();
    lista.addAll(query.list());

    return lista;
}

HQL 2的日志输出:

21 Jan 2016 09:32:17,733 DEBUG SQL - 
    /* select
        tableB 
    from
        TableB tableB 
    inner join
        tableB.tableA tableA 
    where
        tableA.code = :codeTableA  */ select
            tableb0_.id as id1_1_,
            tableb0_.code_table_b as code_tab2_1_,
            tableb0_.desc_table_b as desc_tab3_1_,
            tableb0_.code_table_a as code_tab4_1_ 
        from
            table_b tableb0_ 
        inner join
            table_a tablea1_ 
                on tableb0_.code_table_a=tablea1_.code_table_a 
        where
            tablea1_.code_table_a=?
21 Jan 2016 09:32:17,777 DEBUG SQL - 
    /* load com.baldotech.hibernatepg.model.TableA */ select
        tablea0_.id as id1_0_0_,
        tablea0_.code_table_a as code_tab2_0_0_,
        tablea0_.desc_table_a as desc_tab3_0_0_ 
    from
        table_a tablea0_ 
    where
        tablea0_.code_table_a=?
21 Jan 2016 09:32:17,797 DEBUG SQL - 
    /* load com.baldotech.hibernatepg.model.TableA */ select
        tablea0_.id as id1_0_0_,
        tablea0_.code_table_a as code_tab2_0_0_,
        tablea0_.desc_table_a as desc_tab3_0_0_ 
    from
        table_a tablea0_ 
    where
        tablea0_.code_table_a=?

问题1:

如您所见,@ ManyToOne关联被标记为LAZY:

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "code_table_a", referencedColumnName = "code_table_a", unique = true)
private TableA tableA;

但是日志显示Hibernate正在为每个实体TableB加载实体TableA。那是为什么?

问题2:

HQL1使用隐式连接:

"where tableB.tableA.code = :codeTableA "

HQL2使用显式连接:

"inner join tableB.tableA tableA where tableA.code = :codeTableA "

日志显示hibernate为HQL1使用&#34; CROSS JOIN&#34; ,为HQL2使用&#34; INNER JOIN&#34;

我不明白为什么它在HQL1上使用交叉连接。

问题3:

如果使用主键进行连接,则一切正常:hibernate遵循LAZY关联e没有CROSS JOIN用于隐式连接。

那么,我应该避免使用唯一键作为外键吗?

为什么hibernate对每种情况的工作方式都不同?

谢谢!

1 个答案:

答案 0 :(得分:0)

Q1 Hibernate的行为与请求完全相同。

您正在获取给定父级(表A)的所有子级(表A),这些子级由您要求获取父级 lazy 的parant键标识。

这导致

1)将子节点连接到父节点(您需要连接才能按键识别父节点),但由于 lazy 策略,您将忽略父节点数据。

2)在下一步中,您可以通过键获取父数据(只有一个选择,因为关系是多对一)。

我的意见,在这种情况下 - 懒惰的一个识别实体 - 是反作用的。