您的位置:首页 > 数据库

Hibernate属性中 @JoinColumn与@JoinTable、是否使用ForeignKey的操作数据库的效率区别

2014-08-26 20:39 447 查看
转自:http://kylinsoong.iteye.com/blog/852213

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

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

Java代码


 




@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,   
            <strong><span style="color: rgb(255, 0, 0);"><em>fetch = FetchType.LAZY</em></span></strong>,   
            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;  
    }  
}  

@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,
<strong><span style="color: rgb(255, 0, 0);"><em>fetch = FetchType.LAZY</em></span></strong>,
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代码


 




public interface TransformerService {  
    public void persist(Transformer t);  
    public void analysis(Long id);  
    public Transformer analysisRemote(Long id);  
}  

public interface TransformerService {
public void persist(Transformer t);
public void analysis(Long id);
public Transformer analysisRemote(Long id);
}

  

Java代码


 




public interface TransformerServiceLocal extends TransformerService {  
}  

public interface TransformerServiceLocal extends TransformerService {
}

  

Java代码


 




@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);  
    }  
  
    <span style="color: rgb(0, 0, 0);">private void analysisEntity(Transformer t) {  
        System.out.println(t.getUserCard());  
    }</span>  
  
    @TransactionAttribute(TransactionAttributeType.REQUIRED)  
    public Transformer analysisRemote(Long id) {  
        return em.find(Transformer.class, id);  
    }  
  
}  

@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);
}

<span style="color: rgb(0, 0, 0);">private void analysisEntity(Transformer t) {
System.out.println(t.getUserCard());
}</span>

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

}

 

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

Java代码


 




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);  
}  
      
<span style="color: rgb(0, 0, 0);">private void analysisEntityInRemote(Transformer t) {  
        System.out.println(t.getUserCard());  
}</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);
}

<span style="color: rgb(0, 0, 0);">private void analysisEntityInRemote(Transformer t) {
System.out.println(t.getUserCard());
}</span>

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



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

Java代码


 




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)  

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代码


 




@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;  
}  

@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代码


 




@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;  
}  

@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代码


 




@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;  
}  

@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注解关系映射  

转自:http://blog.163.com/wzx_dd/blog/static/194285072201282095713850/

 
西游记中的领导和被领导
 
excel中设定一列为下拉框,同时设定下拉框内容

Hibernate注解关系映射  

2012-09-20 11:14:08|  分类:java学习
|  标签:hibernate  |举报|字号大中小 订阅

Hibernate Annotation关系映射的几种类型映射用法及使用方法(说明:以前实例的实体是user和role,主键分别是userid和roleid)

1)一对一外键关联映射(单向)
@OneToOne(cascade=CascadeType.ALL)  //一对一外键关联,使用@OneToOne,并设置了级联操作
@JoinColumn(name="userid",unique=true)   //@JoinColum设置了外键的名称为userid(数据库字段名),如果不设置,则默认为另一类的属性名+ _id。外键的值是唯一的(unique),不可重复,与另一类的主键一直
 
2)一对一外键关联映射(双向)
@OneToOne(mappedBy=" role",cascade=CascadeType.ALL)  //一对一双向关联关系,使用@OneToOne。注意:需要加上mappedBy="role",如果不加上的话,role 也会生成一个外键(user_id),mappedby="role"需要指向与他关联对象的一个属性,说明双向关联关系中,有且仅有一端是作为主体(owner)端存在的,主体端负责维护联接列,对于不需要维护这种关系的从表则通过mappedBy属性进行声明,mappedBy的值指向主体的关联属性
//规律:只有是双向关联关系,都加上mappedby,cascade=CascadeType.ALL级联

 3)一对一主键关联映射(不重要)
在实际中很少用,使用注解@PrimaryKeyJoinColumn,意思是说,我的主键去参考另外一张表中的主键,作为我的主键,但是在我测试

 //多对多映射:注解@ManyToMany(单向),默认情况下,hibernate会自动的创建一张中间表来维护多对多关系



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: