下面是一个宠物店的简单模型…
宠物类
@Entity
@Table(name = "pet")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public abstract class Pet {
@Column(name = "id", nullable = false)
private Long id;
@Column(name = "name", nullable = false)
private String name;
@Column(name = "birth_date", nullable = false)
private LocalDate birthDate;
@Column(name = "death_date")
private LocalDate deathDate;
@ManyToOne
@JoinColumn(name = "pet_shop_id", nullable = false, referencedColumnName = "id")
@Setter(AccessLevel.NONE)
private PetShop petShop;
public void setPetShop(PetShop petShop) {
setPetShop(petShop, true);
}
public void setPetShop(PetShop petShop, boolean add) {
this.petShop= petShop;
if (petShop!= null && add) {
petShop.addPet(this, false);
}
}
<<p>PetShop类/strong>@Entity
@Table(name = "pet_shop")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class PetShop {
@Column(name = "id", nullable = false)
private Long id;
...
@OneToMany(
mappedBy = "petShop",
fetch = FetchType.LAZY,
cascade = {CascadeType.ALL})
private List<Pet> pets= new ArrayList<>();
public void addPet(final Pet pet) {
addPet(pet, true);
}
public void addPet(final Pet pet, boolean set) {
if (pet!= null) {
if (pets.contains(pet)) {
pets.set(pets.indexOf(pet), pet);
} else {
pets.add(pet);
}
if (set) {
pet.setPetShop(this, false);
}
}
}
}
<<p>PetShopRepository接口/strong>public interface PetShopRepository
extends JpaRepository<PetShop, Long> {
@Query(
"SELECT DISTINCT ps FROM PetShop ps"
+ " JOIN ps.pets p"
+ " WHERE ps.id = :id AND p.deathDate IS NULL")
@Override
Optional<PetShop> findById(@NonNull Long id);
}
…下面是如何创建具有2个Pet
实例(一个活的,另一个死的)的PetShop
:
final Pet alive = new Pet();
alive.setName("cat");
alive.setCall("meow");
alive.setBirthDate(LocalDate.now());
final Pet dead = new Pet();
dead.setName("cat");
dead.setCall("meow");
dead.setBirthDate(LocalDate.now().minusYears(15L));
dead.setDeathDate(LocalDate.now());
final PetShop petShop = new PetShop();
petShop.getPets().add(alive);
petShop.getPets().add(dead);
petShopRepositiry.save(petShop);
现在我想检索PetShop
,我假设它只包含活着的宠物:
final PetShop petShop = petShopRepository.findById(shopId)
.orElseThrow(() -> new ShopNotFoundException(shopId));
final int petCount = petShop.getPets().size(); // expected 1, but is 2
根据我在PetShopRepository
中的自定义查询,我希望petShop.getPets()
返回一个具有1个元素的列表,但它实际上返回一个具有2个元素的列表(它还包括死宠物)。
Am I missing something? Any hint would be really appreciated :-)
这是因为尽管有您的查询,Jpa仍然保持关系的一致性。
。:您的查询将返回至少有一只宠物存活的商店。但是,Jpa将带着整套宠物返回商店。您可能会看到Jpa发送的额外sql查询(如果您设置了show_sql=true
),以在返回的商店中重新收集宠物。
从根本上说,不是因为你想让这些商店有活宠物,这些商店才会放弃他们的死宠物。
要做到这一点,你必须设计宠物收集,使其能够过滤死亡宠物。Hibernate提供了这样的注释(@Filter
和@FilterDef
),但显然JPA没有。
我不认为在@Postload
进行过滤是一个好主意,因为你必须在数据库中的任何刷新之前将过滤过的死宠物放回集合中。在我看来这很冒险。