`

对Hibernate属性(CascadeType、JoinColumn、JoinTable、ForeignKey等)的研究

 
阅读更多

http://kylinsoong.iteye.com/blog/852213

 

 

本文列出几个“EJB 学习阶段总结:JBoss下发布一个Toy企业应用”开发测试过程中遇到的几个问题。

1. Hibernate 懒加载有一定局限性:EJB远程调运时Hibernate懒加载Session失效

通过实例说明:给Entity类中添加Transformer类,Transformer与UserCard存在一对一的单向关联,如下:

Java代码 复制代码 收藏代码
  1. @Entity(name="Transformer")  
  2. @Table(name="k_transformer")  
  3. public class Transformer implements Serializable{  
  4.       
  5.     private Long id;  
  6.     private UserCard userCard;  
  7.     @Column  
  8.     @Id  
  9.     @GeneratedValue  
  10.     public Long getId() {  
  11.         return id;  
  12.     }  
  13.     public void setId(Long id) {  
  14.         this.id = id;  
  15.     }  
  16.     @OneToOne(  
  17.         targetEntity = com.home.po.UserCard.class,   
  18.             <strong><span style="color: rgb(255, 0, 0);"><em>fetch = FetchType.LAZY</em></span></strong>,   
  19.             cascade = { CascadeType.ALL })  
  20.     @Cascade( { org.hibernate.annotations.CascadeType.ALL } )             
  21.     @JoinColumn(name = "UserCard_id")  
  22.     @ForeignKey(name = "TRANSFORMER_TO_USERCARD_FK")   
  23.     public UserCard getUserCard() {  
  24.         return userCard;  
  25.     }  
  26.     public void setUserCard(UserCard userCard) {  
  27.         this.userCard = userCard;  
  28.     }  
  29. }  
@Entity(name="Transformer")
@Table(name="k_transformer")
public class Transformer implements Serializable{
	
	private Long id;
	private UserCard userCard;
	@Column
	@Id
	@GeneratedValue
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	@OneToOne(
		targetEntity = com.home.po.UserCard.class, 
    		fetch = FetchType.LAZY, 
    		cascade = { CascadeType.ALL })
    @Cascade( { org.hibernate.annotations.CascadeType.ALL } )    		
	@JoinColumn(name = "UserCard_id")
	@ForeignKey(name = "TRANSFORMER_TO_USERCARD_FK") 
	public UserCard getUserCard() {
		return userCard;
	}
	public void setUserCard(UserCard userCard) {
		this.userCard = userCard;
	}
}

注意OneToOne属性设定FetchType必须是LAZY,因为我们测试的是Hibernate懒加载

 添加一个HibernateTest Session Bean,如下:

Java代码 复制代码 收藏代码
  1. public interface TransformerService {  
  2.     public void persist(Transformer t);  
  3.     public void analysis(Long id);  
  4.     public Transformer analysisRemote(Long id);  
  5. }  
public interface TransformerService {
	public void persist(Transformer t);
	public void analysis(Long id);
	public Transformer analysisRemote(Long id);
}

  

Java代码 复制代码 收藏代码
  1. public interface TransformerServiceLocal extends TransformerService {  
  2. }  
public interface TransformerServiceLocal extends TransformerService {
}

  

Java代码 复制代码 收藏代码
  1. @Stateless  
  2. @Remote(TransformerService.class)  
  3. @Local(TransformerServiceLocal.class)  
  4. public class TransformerServiceSession implements TransformerServiceLocal {  
  5.       
  6.     @PersistenceContext(unitName="com.home.po")   
  7.     protected EntityManager em;  
  8.   
  9.     @TransactionAttribute(TransactionAttributeType.REQUIRED)  
  10.     public void persist(Transformer t) {  
  11.         em.persist(t);  
  12.     }  
  13.   
  14.     @TransactionAttribute(TransactionAttributeType.REQUIRED)  
  15.     public void analysis(Long id) {  
  16.         Transformer t = em.find(Transformer.class, id);  
  17.         analysisEntity(t);  
  18.     }  
  19.   
  20.     <span style="color: rgb(0, 0, 0);">private void analysisEntity(Transformer t) {  
  21.         System.out.println(t.getUserCard());  
  22.     }</span>  
  23.   
  24.     @TransactionAttribute(TransactionAttributeType.REQUIRED)  
  25.     public Transformer analysisRemote(Long id) {  
  26.         return em.find(Transformer.class, id);  
  27.     }  
  28.   
  29. }  
@Stateless
@Remote(TransformerService.class)
@Local(TransformerServiceLocal.class)
public class TransformerServiceSession implements TransformerServiceLocal {
	
	@PersistenceContext(unitName="com.home.po") 
	protected EntityManager em;

	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public void persist(Transformer t) {
		em.persist(t);
	}

	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public void analysis(Long id) {
		Transformer t = em.find(Transformer.class, id);
		analysisEntity(t);
	}

	private void analysisEntity(Transformer t) {
		System.out.println(t.getUserCard());
	}

	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public Transformer analysisRemote(Long id) {
		return em.find(Transformer.class, id);
	}

}

 

在客户端先向数据库中插入一条数据,在运行如下代码段:

Java代码 复制代码 收藏代码
  1. public void analysis() throws NamingException {  
  2.        ……  
  3.       TransformerService service = (TransformerService) ctx.lookup("home-test-all/TransformerServiceSession/remote");  
  4.         service.analysis(new Long(1));  
  5.         Transformer tra = service.analysisRemote(new Long(1));  
  6.         analysisEntityInRemote(tra);  
  7. }  
  8.       
  9. <span style="color: rgb(0, 0, 0);">private void analysisEntityInRemote(Transformer t) {  
  10.         System.out.println(t.getUserCard());  
  11. }</span>  
public void analysis() throws NamingException {
       ……
      TransformerService service = (TransformerService) ctx.lookup("home-test-all/TransformerServiceSession/remote");
        service.analysis(new Long(1));
        Transformer tra = service.analysisRemote(new Long(1));
        analysisEntityInRemote(tra);
}
	
private void analysisEntityInRemote(Transformer t) {
        System.out.println(t.getUserCard());
}

  注意上述加红倾斜的代码段描述的方法analysisEntity,analysisEntityInRemote作用都是相同的,取出Transformer中UserCard属性,但是运行结果却如下,TransformerServiceSession 中的analysisEntity运行良好,Jboss Console控制台打印输出如下信息:

 而Remote端Eclipse Console口抛出Session无效的异常:

Java代码 复制代码 收藏代码
  1. Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session  
  2.     at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:62)  
  3.     at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:116)  
  4.     at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:166)  
  5.     at com.home.po.UserCard_$$_javassist_0.toString(UserCard_$$_javassist_0.java)  
  6.     at java.lang.String.valueOf(Unknown Source)  
  7.     at java.io.PrintStream.println(Unknown Source)  
  8.     at com.home.ear.test.HibernateTestClient.analysisEntityInRemote(HibernateTestClient.java:41)  
  9.     at com.home.ear.test.HibernateTestClient.analysis(HibernateTestClient.java:37)  
  10.     at com.home.ear.test.HibernateTestClient.main(HibernateTestClient.java:47)  
Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
	at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:62)
	at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:116)
	at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:166)
	at com.home.po.UserCard_$$_javassist_0.toString(UserCard_$$_javassist_0.java)
	at java.lang.String.valueOf(Unknown Source)
	at java.io.PrintStream.println(Unknown Source)
	at com.home.ear.test.HibernateTestClient.analysisEntityInRemote(HibernateTestClient.java:41)
	at com.home.ear.test.HibernateTestClient.analysis(HibernateTestClient.java:37)
	at com.home.ear.test.HibernateTestClient.main(HibernateTestClient.java:47)

 具体原因,继续研究中……

2 hibernate.jdbc.batch_size与 hibernate.jdbc.fetch_size初始值大小对Hibernate工作效率的影响:

修改Persistence Entity EJB模块中persistence.xml中Properties属性可以改变hibernate.jdbc.batch_size与 hibernate.jdbc.fetch_size的值,我做了如下两组测试,如下:

如下一,改变hibernate.jdbc.batch_size的大小,插入1000条User连续三次,记录每次插入时间,并计算出三次平均时间:

 如下二改变Change hibernate.jdbc.fetch_size大小,从数据库中取出2000条和4000条数据,记录取出时间,如下:

 如上两组数据,可以得出结论,在这个应用中hibernate.jdbc.batch_size设置为20-30之间Hibernate工作效率最好;而hibernate.jdbc.fetch_size是值设置为40左右Hibernate的工作效率最好

 

3  比较@OneToMany下@JoinColumn和@JoinTable的差别(对性能的影响)

@JoinColumn不产生级联表,将一方的主键存放在另一方的表中,如下,为JoinColumn的Entity Bean配置

以User与Friend为例:

Java代码 复制代码 收藏代码
  1. @OneToMany (  
  2.     targetEntity=com.home.po.Friend.class,  
  3.     fetch=FetchType.LAZY,  
  4.     cascade = { CascadeType.ALL })  
  5.     @Cascade( { org.hibernate.annotations.CascadeType.ALL } )   
  6.     @JoinColumn(name="userId")    
  7.     @ForeignKey(name="USER_TO_FRIEND_FK")  
  8. public List<Friend> getFriends() {  
  9.         return friends;  
  10. }  
@OneToMany (
	targetEntity=com.home.po.Friend.class,
	fetch=FetchType.LAZY,
	cascade = { CascadeType.ALL })
	@Cascade( { org.hibernate.annotations.CascadeType.ALL } ) 
	@JoinColumn(name="userId")  
	@ForeignKey(name="USER_TO_FRIEND_FK")
public List<Friend> getFriends() {
		return friends;
}

 这种配置下 产生表如下图:



 @JoinTable产生级联表,将双方的主键存放在一张独立的表中,如下为@JoinTable的配置

Java代码 复制代码 收藏代码
  1. @OneToMany (  
  2.              targetEntity=com.home.po.Friend.class,  
  3.         fetch=FetchType.LAZY,  
  4.         cascade = { CascadeType.ALL })  
  5. @Cascade( { org.hibernate.annotations.CascadeType.ALL } )   
  6. @JoinTable(name="k_user_friend",   
  7.         joinColumns = @JoinColumn(name = "USER_ID"),   
  8.         inverseJoinColumns = @JoinColumn(name = "FRIEND_ID"))  
  9. @ForeignKey(name = "k_user_friend_FK",   
  10.         inverseName = "k_user_friend_FK_R")  
  11. public List<Friend> getFriends() {  
  12.     return friends;  
  13. }  
	@OneToMany (
              targetEntity=com.home.po.Friend.class,
			fetch=FetchType.LAZY,
			cascade = { CascadeType.ALL })
	@Cascade( { org.hibernate.annotations.CascadeType.ALL } ) 
	@JoinTable(name="k_user_friend", 
			joinColumns = @JoinColumn(name = "USER_ID"), 
			inverseJoinColumns = @JoinColumn(name = "FRIEND_ID"))
	@ForeignKey(name = "k_user_friend_FK", 
			inverseName = "k_user_friend_FK_R")
	public List<Friend> getFriends() {
		return friends;
	}

 这种配置下 产生表如下图:



 

 如上用直线标出的5张表就是5对一对多关系产生的级联表,它里面存储两个表的主键。

这两种处理对数据的插入和存取有什么影响,就这一问题做如下测试:

      1  分别在两种配置下通过EJB向数据库中插入2000个User 5次,记录每次时间,求出平均时间;

      2  分别在两种配置下通过EJB从数据库中取出10000条数据5次,记录每次时间,求出平均时间;

结果如下两组图分别是JoinTable和JoinColumn下的测试数据



 

 从上图比较可以得出结论:

      1 JoinTable下操作数据库花费的时间要长于JolinColumn;

      2 JolinColumn下节省的时间约为JoinTable下的2.5%;

分析原因:JoinTable花费时间多的原因是JoinTable下生成的中间表,要存取数据时都要查询中间的级联表,所以花费时间多;

 

4 测试建立外键与不建立外键两种情况下对存取数据库的影响

如上面3中都是设置外键的情况下测试的,下面我们在JoinColumn下做一组不设置外键的测试,不设置外键Annotation相当简单就是在原来的基础上去掉@ForeignKey标记,如下

Java代码 复制代码 收藏代码
  1. @OneToMany (  
  2.     targetEntity=com.home.po.Friend.class,  
  3.     fetch=FetchType.LAZY,  
  4.     cascade = { CascadeType.ALL })  
  5.     @Cascade( { org.hibernate.annotations.CascadeType.ALL } )   
  6.     @JoinColumn(name="userId")    
  7. public List<Friend> getFriends() {  
  8.         return friends;  
  9. }  
@OneToMany (
	targetEntity=com.home.po.Friend.class,
	fetch=FetchType.LAZY,
	cascade = { CascadeType.ALL })
	@Cascade( { org.hibernate.annotations.CascadeType.ALL } ) 
	@JoinColumn(name="userId")  
public List<Friend> getFriends() {
		return friends;
}

 为了对比我们假设两种情况,一是JolinColumn下配置ForeignKey,二是JolinColumn下不配置ForeignKey,在两种情况下做如下测试:

      1  分别在两种配置下通过EJB向数据库中插入2000个User 5次,记录每次时间,求出平均时间;

      2  分别在两种配置下通过EJB从数据库中取出10000条数据5次,记录每次时间,求出平均时间;

测试结果如下面两张表所示:



 

  从上面两组图我们可以得出如下结论:

      1 ForeignKey对数据库的存取影响比较大,特别是数据库的查询

      2 设置ForeignKey可以减少数据库存取的时间

      3 设置ForeignKey插入数据库节省的时间是不设置ForeignKey的5.5%,查询时则可以节省6.5%

 

分享到:
评论

相关推荐

    hibernate的_CascadeType属性说明

    Hibernate的CascadeType属性说明 Hibernate框架中,CascadeType是hibernate的重要属性之一,它控制着实体之间的级联操作。CascadeType是hibernate中的枚举类型,定义了多种级联操作,包括REFRESH、PERSIST、REMOVE...

    Hibernate关于注解的一对多,多对多,一对一

    2. **@JoinTable**:多对多关系通常需要一个中间表来存储双方的关联,`@JoinTable`用来定义这个中间表,包括它的名字、连接的列名等。 ```java @Entity public class User { @ManyToMany @JoinTable(name = "user...

    Hibernate一对一,一对多,多对多实例

    在Hibernate中,可以通过`@ManyToMany`注解定义多对多关系,并使用`@JoinTable`定义连接表。例如,Student实体和Course实体的多对多关系: ```java @Entity public class Student { @ManyToMany @JoinTable...

    详解Hibernate cascade级联属性的CascadeType的用法

    详解Hibernate cascade级联属性的CascadeType的用法 cascade(级联) 级联在编写触发器时经常用到,触发器的作用是当 主控表信息改变时,用来保证其关联表中数据同步更新。若对触发器来修改或删除关联表相记录,必须...

    hibernate 关系注解

    在Hibernate中,关系注解用于在实体类中定义不同类型的数据库关系,如一对一、一对多、多对多等。以下是关于Hibernate关系注解的详细说明: 1. **一对一外键关联映射(单向)** 使用`@OneToOne`注解实现一对一关系...

    hibernate 一对一 多对多的关系

    #### 外键(ForeignKey) ```java @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 其他属性... @OneToOne(cascade = CascadeType.ALL) @...

    Hibernate一对一唯一外键关联映射(双向关联)

    本篇将详细讲解如何使用Hibernate实现一对一唯一外键(Unique Foreign Key)关联映射,并以双向关联为例进行深入探讨。 一、Hibernate一对一关联类型 一对一关联在现实世界中很常见,例如一个人只有一个身份证,一...

    Hibernate映射集合属性List

    在Java持久化框架Hibernate中,映射集合属性是常见的需求,特别是对于那些具有一对多、多对多关系的实体对象。这里的"List"是Java集合框架中的一个接口,用于存储一组有序的元素,而在Hibernate中,它常用来映射...

    hibernate多对多

    2. **定义中间表**:如果需要自定义中间表,可以使用`@JoinTable`注解来指定连接表的名称、外键列名等信息。 3. **双向关联**:在两个关联的实体类中都需要添加`@ManyToMany`注解,分别表示双方的关联关系。 4. **...

    详解Hibernate一对一映射配置

    在Java持久化框架Hibernate中,一对一(One-to-One)映射是对象关系映射的一种常见方式,用于表示两个实体之间一对一的关系。这种关系通常出现在一个实体的实例只能与另一个实体的单个实例相关联的情况。下面我们将...

    hibernate(多对多关系映射)

    在Java的持久化框架Hibernate中,多对多(Many-to-Many)关系映射是一种常见的数据库交互模式,它用于表示两个实体之间复杂的关系。在这个场景中,一个实体可以与多个其他实体相关联,反之亦然。例如,在学生和课程...

    hibernate组件之间的关联

    @JoinTable 注解用于定义中间表的详细信息,包括表名、连接列等。 **级联操作(Cascading)** 级联操作允许在操作一个实体时,自动处理与其关联的其他实体。例如,当删除一个实体时,可以选择是否同时删除与其关联...

    Hibernate多对多关联添加及查询示例

    在上述代码中,`@ManyToMany`注解定义了多对多关联,`@JoinTable`指定了中间表的名称和连接列。`mappedBy`属性用于指定另一端的关联字段,这表示`Course`实体的`students`集合是由`Student`实体的`courses`集合维护...

    Hibernate 多对多(MaryToMary)

    通过上述讨论,我们可以看到在Hibernate中处理多对多关联并不复杂,关键在于正确理解和配置`@ManyToMany`以及`@JoinTable`注解,并注意数据一致性。在实际项目中,根据业务需求选择合适的关联类型,能够更有效地管理...

    hibernate的表关联操作-hibernate02

    在实际开发中,我们需要根据业务需求灵活运用这些关联关系,同时注意优化数据访问性能,比如合理使用懒加载(`@LazyCollection(LazyCollectionOption.EXTRA)`)和级联操作(`cascade=CascadeType.ALL`)等特性。...

    hibernate一对多项目

    本项目“hibernate一对多项目”旨在演示如何在JavaWeb应用中使用Hibernate处理一对多的关系映射。这里我们将深入探讨 Hibernate 的一对多关系、配置以及在实际项目中的应用。 首先,一对多关系在数据库中很常见,...

    hibernate多对多关联映射(单项关联)

    在Java的持久化框架Hibernate中,多对多关联映射是一种常见的关系模型,它用于处理两个实体类之间存在多个对应关系的情况。这篇博客"hibernate多对多关联映射(单项关联)"深入探讨了如何在Hibernate中实现这种映射...

    Hibernate的一对一,一对多,多对多映射

    本文将深入探讨如何利用Hibernate实现数据库表间的一对一、一对多和多对多映射,并通过示例代码演示如何向数据库添加数据。 ### 1. 一对一映射 一对一映射通常用于表示两个实体之间有唯一对应关系的情况。在...

    Hibernate 一对一外键单向关联

    &lt;one-to-one name="profile" class="com.example.Profile" foreign-key="profile_id"/&gt; &lt;/hibernate-mapping&gt; &lt;hibernate-mapping&gt; &lt;class name="com.example.Profile" table="profile"&gt; &lt;!-- 其他属性映射......

    hibernate一对多、多对一、一对一、多对多配置实例

    本文将深入探讨Hibernate中的一对多、多对一、一对一以及多对多四种关系映射的配置实例,帮助开发者更好地理解和运用这些关系。 ### 一对多关系(One-to-Many) 在数据库设计中,一对多关系是最常见的一种关联,...

Global site tag (gtag.js) - Google Analytics