我正在使用 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 使用“CROSS JOIN”,对 HQL2 使用“INNER JOIN”。
我不明白为什么它在 HQL1 上使用交叉连接。
问题 3:
如果连接是使用主键进行的,则一切正常:hibernate 遵守 LAZY 关联 e 隐式连接不使用 CROSS JOIN。
那么,我应该避免使用唯一键作为外键吗?
为什么 hibernate 在不同情况下的工作方式不同?
谢谢!
最佳答案
Q1 Hibernate 的行为完全符合要求。
您正在获取由父项键标识的给定父项(表 A)的所有子项(表 B)并且您请求获取父项惰性。 p>
这导致
1) 子级与父级的连接(您需要连接才能通过键识别父级),但由于惰性策略,您忽略了父级数据。
2) 在下一步中,您通过键获取父数据(因为关系是多对一,所以只能选择一个)。
我的意见,在这种情况下——懒惰地使用一个识别实体——是适得其反的。
关于mysql - Hibernate/MySQL 基于唯一键连接表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34923834/