Hibernate4之一对一关联

[size=medium]每个用户注册信息就对应一个登录信息,双方是相互依存的,由于主键共享,如果有任意表的一条记录被删除或者新增,那么另一张表也会删除对应的记录或者新增一条记录。[b]而且一对一不管是save还是update都必须手工维护双向关系[/b]。
[color=red]在一对一中,关联关系的对象默认都是使用延迟加载[/color],<many-to-one>默认的属性是lazy="proxy",此时默认是会延迟加载的.在指定了lazy="true"之后,需要动态字节码增强才能实现延迟加载。
在这里不管是加载USER还是LOGIN都应该是延迟加载彼此关联对象,在实际业务需求上两者也没有关系的。

[color=blue]对于延迟加载,Hibernate无法知道什么时候会调用到延迟加载的属性/字段的get方法,所以对于延迟加载的属性/字段,Hibernate会通过建立代理Proxy来包装(Wrapper)一下。

代理可能会根据实体本身建立,也可以是根据一个集合建立,如果是根据一个集合建立,延迟加载一般都能成功,如果是根据实体建立,null是不能建立代理的,如果能够确定代理类一定存在,那延迟加载就能成功,相关的映射放置的是代理类,如果不能确定映射的属性是否存在,那就会去数据库中进行查询,这就导致的延迟失败。比如查询自身对象时查完之后,没有加载关联对象,然后当需要用到关联属性的时候,Hibernate又会去数据库查询,但是这时候session已经关闭,因为如果是currentsession的话在第一次查询后commit就关闭了(如果是web项目配置了spring的一请求一事务就不会报错),所以会报:org.hibernate.LazyInitializationException: could not initialize proxy - no Session异常,外键定义可以让Hibernate知道映射的属性是否存在,也可以通过JPA的optional=false属性来告诉hibernate这个对象肯定是有值的,映射的属性一定存在。那么这个时候fetch = FetchType.LAZY这个属性就生效了,就可以达到延迟加载的效果。[/color]

[/size]

/**
* @author Chou
* @since 2012-07-07
* ...用户注册信息实体
*
*/

@Entity
@Table(name = "TAB_USER_INFO")
@SequenceGenerator(name = "seq", sequenceName = "SEQ_USER_INFO", allocationSize = 1)
public class User {

private Long id;
private String username;
private Boolean sex;
private String telephone;
private String email;
private Login login;

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq")
public Long getId() {
return id;
}

......

@OneToOne(mappedBy = "user", fetch = FetchType.LAZY, optional = false, cascade = {CascadeType.REMOVE})
@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
public Login getLogin() {
return login;
}

public void setLogin(Login login) {
this.login = login;
}

}




/**
* @author Chou
* @since 2012-07-07
* ...登录帐号表实体
*
*/
@Entity
@Table(name = "TAB_LOGIN_INFO")
public class Login {
private Long id;
private String loginName;
private String loginPwd;
private User user;

@Id
@GeneratedValue(generator = "sharePK")
@GenericGenerator(name = "sharePK", strategy = "foreign", parameters = @Parameter(name = "property", value = "user"))
public Long getId() {
return id;
}

......

@OneToOne(fetch = FetchType.LAZY, optional = false, cascade = {CascadeType.REMOVE})
@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
@PrimaryKeyJoinColumn
public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}

}


[size=medium][b]SaveOrUpdate和merge方法的级联设置[/b]
对于这两个方法有什么不同,先去我的《Hibernate4之Session一级缓存》这个帖子看看。
上面代码设置级联保存或者更新的时候为什么不用CascadeType.MERGE,而是用org.hibernate.annotations.CascadeType.SAVE_UPDATE呢?
研究一下代码,@OneToMany来自JPA,它期望一个JPA级联—javax.persistence.CascadeType。然而,当使用Hibernate session保存它时,org.hibernate.engine.Cascade将做如下的检查:[/size]

 if ( style.doCascade( action ) ) {
  cascadeProperty(
    persister.getPropertyValue( parent, i, entityMode ),
    types[i],
    style,
    anything,
    false
  );
 }

[size=medium]Hibernate的保存过程将引起一个ACTION_SAVE_UPDATE动作,但是JPA将传递一个ACTION_PERSIST和ACTION_MERGE,这与Hibernate的要求不匹配,并引起级联的执行失败。
还有一种方法就是用CascadeType.ALL也可以解决此问题。但是这也意味着所有操作都会有级联效应,并不能满足所有程序的需求。[/size]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值