在我目前的项目中,我们使用JSF 2.2,JPA 2(Hibernate作为持久性提供程序)和Spring Data JPA。
情况如下,我尽量简化:我们有一个与Car
具有双向关系的实体类Extra
,其中一个Car
引用多个Extra
个实例。
public class Car {
// ...
@OneToMany(mappedBy = "car", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Extra> extras;
// ...
}
Extra
只包含一个String
属性和Car
的反向引用。
在用于编辑单个汽车及其附加组件的支持bean中,我们希望在视图范围内保持bean的状态(我们有一个自定义@ViewScope
基于Spring的注释,它与JEE&#39; s @ViewScoped
具有相同的行为。
我们采取的方法基本上将Car
实例直接存储在辅助bean中。 CarRepository
是一个Spring Data存储库。
@Component
@ViewScope
public class CarEditView {
@Getter @Setter
private Integer id;
@Autowired
private CarRepository carRepository;
@Getter
private Car car;
public void load() {
car = carRepository.findOne(id);
}
public void save(){
carRepository.save(car);
}
// ...
Car
实例直接引用并绑定到相关* .xhtml文件中某些与JSF相关的标记。
但是,Car
实例在第一个请求后会分离。现在让我们在同一视图中考虑将Extra
实例添加到Car
实例的方法。也许现有的可以修改和删除。
在多个请求之间在同一页面上修改与其他实体有关系的分离的JPA实体,直到它们被明确保存为止,JSF项目遵循的最佳做法是什么?
(请注意extras
是一个惰性集合,所以当没有加载和访问此集合时,请说,在第二个请求中,将抛出异常。但是,保留列表新的/修改的/删除的Extra
实例在代码复杂性方面也感觉有点过分。)
答案 0 :(得分:1)
这种情况总是难以管理。我认为你懒得加载Extra
集合,用于列出Cars
的情况,而不是加载所有汽车的额外内容。你有很多解决方案:
急切加载额外的集合,并在显示列表时限制正在加载的汽车数量。这样你就可以在你的bean中使用额外的东西了。
实施一种方法来返回每辆车的额外列表。通过这种方式,您可以删除与car实体本身的关系,并且您可以单独处理每个额外的内容,对于视图,只需保留一个包含当前附加内容(可以具有id或不具有id)的集合,以及其他用于删除的附加内容。保存版本时,请调用更新附加内容所需的服务方法。
如果你在服务中这样做,你甚至可以从视图中抽象出来(考虑到你总是用额外的东西保存汽车):
@Transactional
public Car save(Car car, Collection<Extra> assignedExtras){
Car result = carRepo.save(car);
List<Extra> savedExtras = extraRepo.findByCar(car);
for (Extra extra : assignedExtras){
extra.setCar(car);
extraRepo.save(extra);
savedExtras.remove(extra);
}
//Here, savedExtras contains only the extras you have removed, so let's remove them
for (Extra extra : savedExtras){
extraRepo.delete(extra);
}
return result;
}
延迟加载通常是JPA 2.0的一个问题。您必须在实体上定义是否要使用FetchType.LAZY(默认值)或FetchType.EAGER来加载关系,并始终使用此模式。仅当我们想要始终加载关系时才使用FetchType.EAGER。几乎在所有情况下都使用FetchType.LAZY来获得性能良好且可伸缩的应用程序。 但这并非没有缺点。如果必须使用关系的元素,则需要确保在从数据库加载实体的事务中初始化关系。这可以通过使用从数据库读取实体和所需关系的特定查询来完成。但这将导致特定于用例的查询。另一种选择是访问业务代码中的关系,这将导致对每个关系的附加查询。这两种方法都远非完美。
JPA 2.1实体图是更好的解决方案。实体图的定义独立于查询,并定义从数据库中提取的属性。实体图可以用作提取或加载图。如果使用了获取图,则只有实体图指定的属性才会被视为FetchType.EAGER。所有其他属性都是懒惰的。如果使用加载图,则实体图未指定的所有属性将保留其默认的提取类型。