1. 开篇说明:今天是春节长假的最后一天,春节过后博客将继续,这篇博客主要用来总结年前的研究结果,研究的主要Hibernate OneToMany 单向和双向配置对数据存取性能的问题。本文按照我测试实验的过程,最后得出结论。
2. 本文实验是基于之前博客EJB 学习阶段总结:JBoss下发布一个Toy企业应用,在此工程基础上,在实体中添加一对独立的一对多关系如下图:
如上图Man和Lover是一对多关系,一个Man可以有多个Lover,为了简单我们设定Man和Lover都只有Id和name属性,我们在com.home.po中添加Entity Man和Lover的代码如下:
@Entity(name="Man")
@Table(name="k_man")
public class Man {
private Long id;
private String name;
private List lovers;
@Column
@Id
@GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getLovers() {
return lovers;
}
public void setLovers(List lovers) {
this.lovers = lovers;
}
}
@Entity(name="Lover")
@Table(name="k_lover")
public class Lover {
private Long id;
private String name;
@Column
@Id
@GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
我要做的测试是像数据库中插入Man和Man对应的Lover,我们通过一个独立的EJB来完成,EJB中SessionBean和接口代码如下:
public interface ManService {
public void insertManListUni(Integer num, Integer loverNum);
public void insertManListDul(Integer num, Integer loverNum);
}
@Stateless
@Remote(ManService.class)
public class ManServiceSession implements ManService {
@PersistenceContext(unitName="com.home.po")
protected EntityManager em;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void insertManListUni(Integer num, Integer loverNum) {
List lovers = getLoverList(loverNum);
for(int i = 0 ; i < num ; i ++) {
}
}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void insertManListDul(Integer num, Integer loverNum) {
List lovers = getLoverList(loverNum);
for(int i = 0 ; i < num ; i ++) {
}
}
public List getLoverList(Integer loverNum) {
List lovers = new ArrayList();
for(int i = 0 ; i < loverNum ; i ++) {
Lover l = new Lover();
l.setName("kylin-test-lover-" + i);
lovers.add(l);
}
return lovers;
}
}
3 配置JBoss的日志文件,让其输出Hibernate数据库操作语句
Hibernate日志输出文件配置在JBoss_Home\server\production\conf下jboss-log4j.xml文件中,要输出Hibernate的SQL语句只需在jboss-log4j.xml的配置文件中添加Category和相应的Appender,如下:
4 一对多单向配置下插入一个Man查看日志
在Man中添加单向配置:
@OneToMany (
targetEntity=com.home.po.Lover.class,
fetch=FetchType.LAZY,
cascade = { CascadeType.ALL })
@Cascade( { org.hibernate.annotations.CascadeType.ALL } )
@ForeignKey(name="MAN_TO_LOVER_FK")
@JoinColumn(name = "MAN_ID")
public List getLovers() {
if(lovers == null) {
lovers = new ArrayList();
}
return lovers;
}
修给ManServiceSession的insertManListUni方法如下:
public void insertManListUni(Integer num, Integer loverNum) {
for(int i = 0 ; i < num ; i ++) {
List lovers = getLoverList(loverNum);
Man m = new Man();
m.setName("kylin-test-man-" + i);
em.persist(m);
for(Lover l : lovers) {
m.getLovers().add(l);
}
}
}
插入一个Man一个Man对应两个Lover后 查看日志如下:
2011-02-08 15:31:58,562 DEBUG [org.hibernate.SQL] select hibernate_sequence.nextval from dual
2011-02-08 15:31:58,750 DEBUG [org.hibernate.SQL] select hibernate_sequence.nextval from dual
2011-02-08 15:31:58,765 DEBUG [org.hibernate.SQL] select hibernate_sequence.nextval from dual
2011-02-08 15:31:58,781 DEBUG [org.hibernate.SQL] insert into k_man (name, id) values (?, ?)
2011-02-08 15:31:58,781 DEBUG [org.hibernate.SQL] insert into k_lover (name, id) values (?, ?)
2011-02-08 15:31:58,781 DEBUG [org.hibernate.SQL] insert into k_lover (name, id) values (?, ?)
2011-02-08 15:31:58,781 DEBUG [org.hibernate.SQL] update k_lover set MAN_ID=? where id=?
2011-02-08 15:31:58,781 DEBUG [org.hibernate.SQL] update k_lover set MAN_ID=? where id=?
如上是插入一个Man,一个Man对应两个Lover,SQL语句统计如下:
3 select语句,1 insert Man语句,2 insert lover语句, 2 update lover语句,总共:8 SQL语句
依次推断得出计算SQL语句公式:假设插入Man的个数用 m表示,一个Man对应的Lover个数用 l表示,SQL总数用s表示:给出公式:S=(m + m*n)*2 + m*n,用此公式
插入1个Man,一个Man对应2个Lover | SQL总数:(1+1*2)*2+1*2 =8 |
插入100个Man,一个Man对应1000个Lover | SQL总数:(100+100*1000)*2+100*1000=300200 |
插入100个Man,一个Man对应1000个Lover 3次,记录操作时间如下:
即完成插入100个Man,一个Man对应1000个Lover 需要时间大概是110秒
5 一对多双向下插入Man及对应的Lover查看日志
修改实体类
Man实体如下:
@OneToMany (
targetEntity=com.home.po.Lover.class,
fetch=FetchType.LAZY,
cascade = { CascadeType.ALL },
mappedBy = "man")
@Cascade( { org.hibernate.annotations.CascadeType.ALL } )
@ForeignKey(name="MAN_TO_LOVER_FK")
public List getLovers() {
if(lovers == null) {
lovers = new ArrayList();
}
return lovers;
}
Lover实体添加Man属性如下:
private Man man;
@ManyToOne
public Man getMan() {
return man;
}
public void setMan(Man man) {
this.man = man;
}
修给ManServiceSession的insertManListDul方法如下
public void insertManListDul(Integer num, Integer loverNum) {
for(int i = 0 ; i < num ; i ++) {
List lovers = getLoverList(loverNum);
Man m = new Man();
m.setName("kylin-test-man-" + i);
em.persist(m);
for(Lover l : lovers) {
l.setMan(m);
em.persist(l);
}
}
}
同样插入一个Man一个Man对应两个Lover后 查看日志如下:
2011-02-08 16:40:11,859 DEBUG [org.hibernate.SQL] select hibernate_sequence.nextval from dual
2011-02-08 16:40:12,031 DEBUG [org.hibernate.SQL] select hibernate_sequence.nextval from dual
2011-02-08 16:40:12,031 DEBUG [org.hibernate.SQL] select hibernate_sequence.nextval from dual
2011-02-08 16:40:12,046 DEBUG [org.hibernate.SQL] insert into k_man (name, id) values (?, ?)
2011-02-08 16:40:12,062 DEBUG [org.hibernate.SQL] insert into k_lover (man_id, name, id) values (?, ?, ?)
2011-02-08 16:40:12,062 DEBUG [org.hibernate.SQL] insert into k_lover (man_id, name, id) values (?, ?, ?)
3 select语句,1 insert Man语句,2 insert lover语句, 总共:6 SQL语句
依次推断得出计算SQL语句公式:假设插入Man的个数用 m表示,一个Man对应的Lover个数用 l表示,SQL总数用s表示:给出公式:S=(m + m*n)*2 ,用此公式
插入1个Man,一个Man对应2个Lover | SQL总数:(1+1*2)*2=6 |
插入100个Man,一个Man对应1000个Lover | SQL总数:(100+100*1000)*2=200200 |
插入100个Man,一个Man对应1000个Lover 3次,记录操作时间如下:
即完成插入100个Man,一个Man对应1000个Lover 需要时间大概是90秒
6 结论:比较步骤4和步骤5,可以看到同样是插入100个Man,一个Man对应1000个Lover 用双向配置节约了20秒时间,即结论:一对多关系一般推荐使用双向配置