Hibernate OneToMany 单向和双向配置对数据存取性能的比较

1. 开篇说明:今天是春节长假的最后一天,春节过后博客将继续,这篇博客主要用来总结年前的研究结果,研究的主要Hibernate OneToMany 单向和双向配置对数据存取性能的问题。本文按照我测试实验的过程,最后得出结论。

 

2. 本文实验是基于之前博客EJB 学习阶段总结:JBoss下发布一个Toy企业应用,在此工程基础上,在实体中添加一对独立的一对多关系如下图:
Hibernate OneToMany 单向和双向配置对数据存取性能的比较
 如上图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<Lover> 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<Lover> getLovers() {
		return lovers;
	}
	
	public void setLovers(List<Lover> 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<Lover> lovers = getLoverList(loverNum);
		for(int i = 0 ; i < num ; i ++) {
			
		}
	}

	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public void insertManListDul(Integer num, Integer loverNum) {
		List<Lover> lovers = getLoverList(loverNum);
		for(int i = 0 ; i < num ; i ++) {
			
		}
	}
	
	public List<Lover> getLoverList(Integer loverNum) {
		List<Lover> lovers = new ArrayList<Lover>();
		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,如下:

<appender name="SWFILE" class="org.jboss.logging.appender.DailyRollingFileAppender">
      <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/>
      <param name="File" value="${jboss.server.log.dir}/FFTrace.log"/>
      <param name="Encoding" value="UTF-8"/>
      <param name="Append" value="false"/>

      <param name="DatePattern" value="'.'yyyy-MM-dd"/>

      <layout class="org.apache.log4j.PatternLayout">
         <param name="ConversionPattern" value="%d %-5p [%c] %m%n"/>
      </layout>
   </appender>

<category name="org.hibernate.SQL" additivity="false">
   		<priority value="debug"/>
		<appender-ref ref="SWFILE" />
   	</category>

 

 

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<Lover> getLovers() {
		if(lovers == null) {
			lovers = new ArrayList<Lover>();
		}
		return lovers;
	}

修给ManServiceSession的insertManListUni方法如下:

public void insertManListUni(Integer num, Integer loverNum) {	
		for(int i = 0 ; i < num ; i ++) {
			List<Lover> 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<Lover> getLovers() {
		if(lovers == null) {
			lovers = new ArrayList<Lover>();
		}
		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<Lover> 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秒时间,即结论:一对多关系一般推荐使用双向配置

你可能感兴趣的:(sql,sql,Hibernate,log4j,jboss,server)