Hibernate4之一对一关联

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

对于延迟加载,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这个属性就生效了,就可以达到延迟加载的效果。



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

}


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

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

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

你可能感兴趣的:(Hibernate,jpa,一对一,共享主键)