一对多双向关联:数据库和二级缓存的差异

时间:2014-09-15 14:59:53

标签: hibernate ehcache

以下是两种类型的部分,

public class Address {
    Set<Employee>employees=new HashSet<Employee>();

public class Employee {
    private Address address;;

例如,员工在地址上工作。它们对Hibernate的映射是(部分),

<class name="Address">
    <cache usage="read-write"/>
    <set name="employees" inverse="false">
         <cache usage="read-write"/>    
             <key column="address_" not-null="true"/>
             <one-to-many class="Employee"/>
    </set>
</class>

<class name="Employee" >
    <cache  usage="read-write"/>
    <many-to-one name="address" column="address_" not-null="true" insert="false" update="false"/>

所以我们有一个双向的一对多关联。关联的持久性由收集方确定,而不是由员工中的Address引用确定。为了检查这一点,我故意向两位员工添加地址引用。当两个员工都坚持到数据库时,我确实看到了这种情况。但是这两个地址引用被持久化到第二层高速缓存,这是我不理解的。

Employee employee=new Employee(1l,"Foo1",1.00);
Employee employee2=new Employee(2l,"Foo2",2.00);
Address address=new Address(12l,"foostreet", "12 foo", "FooCity12");
Address address3=new Address(34l,"foostreet", "34 foo", "FooCity34");

address.getEmployees().add(employee);
address.getEmployees().add(employee2);

employee.setAddress(address3);                                 
employee2.setAddress(address3);                          

session.save(address);
session.save(address3);
session.save(employee);
session.save(employee2);

tx.commit();

然后在新的会话中,

employee= (Employee) session.get(Employee.class, 1l)l
address=employee.getAddress();
employee= (Employee) session.get(Employee.class, 2l);
address=employee.getAddress();

没有Ehcache地址是(代理)地址#12l,所以员工所属的集合的容器,地址#12l。但是Ehcache的地址是地址#34l的aproxy,所以我们故意设置了地址的引用。

我不知道这是怎么回事,因为

BackrefPropertyAccessor$BackrefSetter.set(Object, Object, SessionFactoryImplementor){
}

1 个答案:

答案 0 :(得分:0)

问题在于你不保持关系的双向性,而是依靠Hibernate(以及数据库的底层关系模型)为你“修复”它。一旦使用有状态的OO表示,问题就会暴露出来。您很可能已经仅使用Session观察它。

基本上发生的事情是你只更新关系的“拥有”方面,而不是两者。鉴于底层数据库是关系型的,因此只在一端持有“FK”,当您从该模型重新加载时,问题就会消失。

您需要做的是更新关系的两端,即employee.setAddress() AND address.addEmployee()(而不是将原始Set<Employee>暴露给用户) 。此外,如果员工已经与另一个地址相关联,那么它也需要从该集合中移除......这就是OO非常方便的地方:因为这个逻辑实际上应该是.setAddress()的一部分或者模型上的.addEmployee()方法,用于检查是否有任何要清理的内容,并回调到另一端,将该逻辑整齐地封装在Address或{{1}的消费者之外} class。