Spring数据+打开jpa + Lazy loading + OneToOne关系

时间:2014-10-20 02:49:34

标签: jpa spring-data openjpa

我们在项目中使用Spring Data(版本1.3.2)和OpenJPA(版本2.2.2)。对于OneToOne关系,我们在实体中遇到延迟加载字段的问题。我将用一个例子来描述这个问题:

我们有两个实体(比如人和地址)。人与地址之间存在一对一的关系。下面给出的是Person和Address实体的代码示例(为了简洁而省略了getter和setter):

@Entity
public class Person {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;

private String firstName;

private String lastName;

@OneToOne(mappedBy = "person", fetch = FetchType.LAZY)
private Address address;
}

@Entity

public class Address {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;

private String streetAddress;

private String city;

private String zipCode;

@OneToOne
@JoinColumn(name="PERSON_ID")
private Person person;
}

然后我们通过从JpaRepository扩展来创建AddressRepository和PersonRepository,为我们提供这两个实体的基本CRUD操作。

在我们的应用程序中,首先创建Person实体,而不使用任何与之关联的地址。然后,通过在Address实例中设置适当的Person实体,在稍后的时间点创建地址实体。在此之前,所有工作都按预期进行。

现在,如果通过传递适当的id来查询Person实体使用PersonRepository的findOne方法,则返回的Person实例不会填充地址字段。当我们访问PersonRepository返回的Person的地址字段时,它恰好为null。如果存在与Person关联的Address实体,我们期望Person中的地址字段可用。鉴于fetch类型设置为LAZY,只是意味着只有在我们尝试访问地址字段时才会加载它。当我们直接使用JPA API(使用EntityManager.find()方法)时,这确实是这样的。

当我们使用hibernate时,这段相同的代码工作正常。由于某些法律原因,我们不得不将我们的JPA提供程序更改为OpenJPA,这已停止工作。在使用OpenJPA以便延迟加载OneToOne时,我们应该做些什么吗?

注意:我们已打开L2缓存。我们发现OpenJPA中存在L2缓存问题,并在OpenJPA(https://issues.apache.org/jira/browse/OPENJPA-2522)上打开了这个缺陷。然而,即使存在这种缺陷,也有一种解决方法(使用entitymanagerfactory的evict方法将实体从L2缓存中逐出)并使用JPA API,我们可以使其工作。但是,对于spring数据,我们发现即使我们使用此变通方法也存在此问题。我们通过在jpa-repositories中提供entity-manager-factory-ref然后在我们的bean中注入相同的emf来从缓存中驱逐实体来实现这一点:

<jpa:repositories base-package="com.abc.dsp.spring.data.repository" entity-manager-factory-ref="emf"/>

由于在我们的项目中广泛使用spring数据,很难转向使用纯JPA API。另外,正如一些答案herehere中提到的那样,我们正试图避免由于广泛的代码影响而更改为急切的提取类型或自定义设置器方法。

更新: 使用自定义setter方法也无法正常工作。什么工作是将获取类型更改为EAGER。但是,我们希望将fetchType保持为LAZY,因为在大多数情况下我们不需要关联的对象,并且该对象非常重。

1 个答案:

答案 0 :(得分:0)

好的,所以我弄清楚为什么解决方法(从缓存中逐出实体)不起作用。我试图从事务边界之外的人访问地址字段。我使用findOne查询Person并从使用@Transactional注释注释的方法返回该实例。我试图在调用方法中访问返回的Person实例的地址字段,该实例未使用@Transactional注释进行注释。一旦我使用@Transactional注释访问方法中的地址字段,那么一旦我使用了变通方法,一切都能正常工作。我只是再次在这里捕获解决方法,以方便其他人参考:

1)通过在jpa:repositories标记中指定实体管理器ref,在JPA存储库中注入EntityManagerFactory

<jpa:repositories base-package="com.ge.dsp.spring.data.repository" entity-manager-factory-ref="emf"/>

此emf是在spring应用程序上下文中创建的。

2)现在在您的类中注入相同的emf,调用使用@Transactional注释注释的DAO方法。通过在DAO上调用相应的save / create方法成功创建实体后,在emf上调用以下方法来驱逐该实体:

emf.getCache().evict(Class cls, Object primaryKey)

这将确保下一个findOne / findAll调用将获得缓存未命中并返回正确构造的实体。请注意,由于openJPA(https://issues.apache.org/jira/browse/OPENJPA-2522

中的错误,必须这样做