Spring 数据 JPA 存储库匹配所有列或整个 pojo
Spring data JPA Repository Match All Columns or whole pojo
我尝试搜索但没有找到准确的解决方案。我有 Address
个实体。对于每个新的地址请求,首先我想检查数据库中是否存在相同的地址。我的申请是仓库申请,同样的地址申请有可能会多次出现。
地址实体
@Entity
@NamedQuery(name="Address.findAll", query="SELECT a FROM Address a")
public class Address implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String firstname;
private String lastname;
private String address1;
private String address2;
private String address3;
private String city;
private String postcode;
@JsonProperty(value="county")
private String state;
private String country;
private String telephoneno;
private String mobileno;
private String email;
//bi-directional many-to-one association to Collection
@OneToMany(mappedBy="address")
@JsonIgnore
private List<Collection> collections;
//bi-directional many-to-one association to Delivery
@OneToMany(mappedBy="address")
@JsonIgnore
private List<Delivery> deliveries;
public Address() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAddress1() {
return this.address1;
}
public void setAddress1(String address1) {
this.address1 = address1;
}
public String getAddress2() {
return this.address2;
}
public void setAddress2(String address2) {
this.address2 = address2;
}
public String getAddress3() {
return this.address3;
}
public void setAddress3(String address3) {
this.address3 = address3;
}
public String getCity() {
return this.city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return this.country;
}
public void setCountry(String country) {
this.country = country;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPostcode() {
return this.postcode;
}
public void setPostcode(String postcode) {
this.postcode = postcode;
}
public String getState() {
return this.state;
}
public void setState(String state) {
this.state = state;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getTelephoneno() {
return telephoneno;
}
public void setTelephoneno(String telephoneno) {
this.telephoneno = telephoneno;
}
public String getMobileno() {
return mobileno;
}
public void setMobileno(String mobileno) {
this.mobileno = mobileno;
}
public List<Collection> getCollections() {
return this.collections;
}
public void setCollections(List<Collection> collections) {
this.collections = collections;
}
public Collection addCollection(Collection collection) {
getCollections().add(collection);
collection.setAddress(this);
return collection;
}
public Collection removeCollection(Collection collection) {
getCollections().remove(collection);
collection.setAddress(null);
return collection;
}
public List<Delivery> getDeliveries() {
return this.deliveries;
}
public void setDeliveries(List<Delivery> deliveries) {
this.deliveries = deliveries;
}
public Delivery addDelivery(Delivery delivery) {
getDeliveries().add(delivery);
delivery.setAddress(this);
return delivery;
}
public Delivery removeDelivery(Delivery delivery) {
getDeliveries().remove(delivery);
delivery.setAddress(null);
return delivery;
}
}
我知道一个解决方案是在存储库中声明一个方法 And
包括所有字段。例如
public Address findByFirstnameAndLastnameAndAddress1AndAddress2AndAddress3AndCityAndPostcode....();
但我想知道是否有更好的方法来做到这一点。有没有什么东西可以让我传递新的 Address
对象来检查数据库中是否存在相同的 Address
。
编辑
根据Manish的回答,我的理解如下:
1> 创建接口 ExtendedJpaRepository
如答案中所述。
2> 为此接口创建实现 class,如下所示(参考:Spring Data Jpa Doc)
public class MyRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> {
List<T> findByExample(T example){
//EclipseLink implementation for QueryByExample
}
}
3> 然后对于每个存储库接口,扩展 ExtendedJpaRepository
。这应该使 findByExample
在每个存储库中都可用。
4> 创建自定义存储库工厂以替换默认的 RepositoryFactoryBean,如 Spring data JPA doc 的第 4 步所述。
5> 声明自定义工厂的 bean。(Spring Data JPA Doc 的第 5 步)
您要找的是Query-by-Example
。如 this post 中所述,JPA 2.0 考虑了此功能,但未包含在最终版本中。 post 还说明大多数 JPA 提供程序都具有实现此功能所必需的功能。
您可以创建自定义 JPA 存储库实现,以提供开箱即用的功能。 Spring Data JPA documentation.
中提供了详细信息
起点是创建一个新界面,例如:
public interface ExtendedJpaRepository<T, ID extends Serializable>
extends JpaRepository<T, ID> {
List<T> findByExample(T example);
}
然后,插入使用底层 JPA 提供程序的此接口的实现。最后,配置您的自定义实现以用于所有存储库接口。
之后,您应该可以调用 addressRepository.findByExample(address)
,前提是 AddressRepository
扩展了 ExtendedJpaRepository
。
您可以使用 Spring-data 开箱即用的规格。并能够使用条件 API 构建查询 programmatically.To 支持规范您可以使用 JpaSpecificationExecutor 接口扩展您的存储库接口
public interface CustomerRepository extends SimpleJpaRepository<T, ID>, JpaSpecificationExecutor {
}
附加接口 (JpaSpecificationExecutor ) 包含允许您以多种方式执行规范的方法。
例如,findAll 方法将 return 所有符合规范的实体:
List<T> findAll(Specification<T> spec);
Specification界面如下:
public interface Specification<T> {
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
CriteriaBuilder builder);
}
好的,那么典型的用例是什么?规范可以很容易地用于在实体之上构建一组可扩展的谓词,然后可以将其与 JpaRepository 组合使用,而无需为每个需要的组合声明查询(方法)。这是一个例子:
示例 2.15。客户规格
public class CustomerSpecs {
public static Specification<Customer> isLongTermCustomer() {
return new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query,
CriteriaBuilder builder) {
LocalDate date = new LocalDate().minusYears(2);
return builder.lessThan(root.get('dateField'), date);
}
};
}
public static Specification<Customer> hasSalesOfMoreThan(MontaryAmount value) {
return new Specification<Customer>() {
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
CriteriaBuilder builder) {
// build query here
}
};
}
}
您在业务需求抽象级别表达了一些标准并创建了可执行规范。因此,客户可能会使用如下规范:
列出客户 = customerRepository.findAll(isLongTermCustomer());
你也可以结合Specification
示例 2.17。综合规格
MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR);
List<Customer> customers = customerRepository.findAll(
where(isLongTermCustomer()).or(hasSalesOfMoreThan(amount)));
As you can see, Specifications offers some glue-code methods to chain
and combine Specifications. Thus extending your data access layer is
just a matter of creating new Specification implementations and
combining them with ones already existing.
您还可以创建复杂的规范,这里是一个示例
public class WorkInProgressSpecification {
public static Specification<WorkInProgress> findByCriteria(final SearchCriteria searchCriteria){
return new Specification<WorkInProgress>() {
@Override
public Predicate toPredicate(Root<WorkInProgress> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<Predicate>();
if(searchCriteria.getView()!=null && !searchCriteria.getView().isEmpty()){
predicates.add(cb.equal(root.get("viewType"), searchCriteria.getView()));
}if(searchCriteria.getFeature()!=null && !searchCriteria.getFeature().isEmpty()){
predicates.add(cb.equal(root.get("title"), searchCriteria.getFeature()));
}if(searchCriteria.getEpic()!=null && !searchCriteria.getEpic().isEmpty()){
predicates.add(cb.equal(root.get("epic"), searchCriteria.getEpic()));
}if( searchCriteria.getPerformingGroup()!=null && !searchCriteria.getPerformingGroup().isEmpty()){
predicates.add(cb.equal(root.get("performingGroup"), searchCriteria.getPerformingGroup()));
}if(searchCriteria.getPlannedStartDate()!=null){
System.out.println("searchCriteria.getPlannedStartDate():" + searchCriteria.getPlannedStartDate());
predicates.add(cb.greaterThanOrEqualTo(root.<Date>get("plndStartDate"), searchCriteria.getPlannedStartDate()));
}if(searchCriteria.getPlannedCompletionDate()!=null){
predicates.add(cb.lessThanOrEqualTo(root.<Date>get("plndComplDate"), searchCriteria.getPlannedCompletionDate()));
}if(searchCriteria.getTeam()!=null && !searchCriteria.getTeam().isEmpty()){
predicates.add(cb.equal(root.get("agileTeam"), searchCriteria.getTeam()));
}
return cb.and(predicates.toArray(new Predicate[]{}));
}
};
}
}
我尝试搜索但没有找到准确的解决方案。我有 Address
个实体。对于每个新的地址请求,首先我想检查数据库中是否存在相同的地址。我的申请是仓库申请,同样的地址申请有可能会多次出现。
地址实体
@Entity
@NamedQuery(name="Address.findAll", query="SELECT a FROM Address a")
public class Address implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String firstname;
private String lastname;
private String address1;
private String address2;
private String address3;
private String city;
private String postcode;
@JsonProperty(value="county")
private String state;
private String country;
private String telephoneno;
private String mobileno;
private String email;
//bi-directional many-to-one association to Collection
@OneToMany(mappedBy="address")
@JsonIgnore
private List<Collection> collections;
//bi-directional many-to-one association to Delivery
@OneToMany(mappedBy="address")
@JsonIgnore
private List<Delivery> deliveries;
public Address() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAddress1() {
return this.address1;
}
public void setAddress1(String address1) {
this.address1 = address1;
}
public String getAddress2() {
return this.address2;
}
public void setAddress2(String address2) {
this.address2 = address2;
}
public String getAddress3() {
return this.address3;
}
public void setAddress3(String address3) {
this.address3 = address3;
}
public String getCity() {
return this.city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return this.country;
}
public void setCountry(String country) {
this.country = country;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPostcode() {
return this.postcode;
}
public void setPostcode(String postcode) {
this.postcode = postcode;
}
public String getState() {
return this.state;
}
public void setState(String state) {
this.state = state;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getTelephoneno() {
return telephoneno;
}
public void setTelephoneno(String telephoneno) {
this.telephoneno = telephoneno;
}
public String getMobileno() {
return mobileno;
}
public void setMobileno(String mobileno) {
this.mobileno = mobileno;
}
public List<Collection> getCollections() {
return this.collections;
}
public void setCollections(List<Collection> collections) {
this.collections = collections;
}
public Collection addCollection(Collection collection) {
getCollections().add(collection);
collection.setAddress(this);
return collection;
}
public Collection removeCollection(Collection collection) {
getCollections().remove(collection);
collection.setAddress(null);
return collection;
}
public List<Delivery> getDeliveries() {
return this.deliveries;
}
public void setDeliveries(List<Delivery> deliveries) {
this.deliveries = deliveries;
}
public Delivery addDelivery(Delivery delivery) {
getDeliveries().add(delivery);
delivery.setAddress(this);
return delivery;
}
public Delivery removeDelivery(Delivery delivery) {
getDeliveries().remove(delivery);
delivery.setAddress(null);
return delivery;
}
}
我知道一个解决方案是在存储库中声明一个方法 And
包括所有字段。例如
public Address findByFirstnameAndLastnameAndAddress1AndAddress2AndAddress3AndCityAndPostcode....();
但我想知道是否有更好的方法来做到这一点。有没有什么东西可以让我传递新的 Address
对象来检查数据库中是否存在相同的 Address
。
编辑
根据Manish的回答,我的理解如下:
1> 创建接口 ExtendedJpaRepository
如答案中所述。
2> 为此接口创建实现 class,如下所示(参考:Spring Data Jpa Doc)
public class MyRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> {
List<T> findByExample(T example){
//EclipseLink implementation for QueryByExample
}
}
3> 然后对于每个存储库接口,扩展 ExtendedJpaRepository
。这应该使 findByExample
在每个存储库中都可用。
4> 创建自定义存储库工厂以替换默认的 RepositoryFactoryBean,如 Spring data JPA doc 的第 4 步所述。
5> 声明自定义工厂的 bean。(Spring Data JPA Doc 的第 5 步)
您要找的是Query-by-Example
。如 this post 中所述,JPA 2.0 考虑了此功能,但未包含在最终版本中。 post 还说明大多数 JPA 提供程序都具有实现此功能所必需的功能。
您可以创建自定义 JPA 存储库实现,以提供开箱即用的功能。 Spring Data JPA documentation.
中提供了详细信息起点是创建一个新界面,例如:
public interface ExtendedJpaRepository<T, ID extends Serializable>
extends JpaRepository<T, ID> {
List<T> findByExample(T example);
}
然后,插入使用底层 JPA 提供程序的此接口的实现。最后,配置您的自定义实现以用于所有存储库接口。
之后,您应该可以调用 addressRepository.findByExample(address)
,前提是 AddressRepository
扩展了 ExtendedJpaRepository
。
您可以使用 Spring-data 开箱即用的规格。并能够使用条件 API 构建查询 programmatically.To 支持规范您可以使用 JpaSpecificationExecutor 接口扩展您的存储库接口
public interface CustomerRepository extends SimpleJpaRepository<T, ID>, JpaSpecificationExecutor {
}
附加接口 (JpaSpecificationExecutor ) 包含允许您以多种方式执行规范的方法。
例如,findAll 方法将 return 所有符合规范的实体:
List<T> findAll(Specification<T> spec);
Specification界面如下:
public interface Specification<T> {
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
CriteriaBuilder builder);
}
好的,那么典型的用例是什么?规范可以很容易地用于在实体之上构建一组可扩展的谓词,然后可以将其与 JpaRepository 组合使用,而无需为每个需要的组合声明查询(方法)。这是一个例子: 示例 2.15。客户规格
public class CustomerSpecs {
public static Specification<Customer> isLongTermCustomer() {
return new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query,
CriteriaBuilder builder) {
LocalDate date = new LocalDate().minusYears(2);
return builder.lessThan(root.get('dateField'), date);
}
};
}
public static Specification<Customer> hasSalesOfMoreThan(MontaryAmount value) {
return new Specification<Customer>() {
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
CriteriaBuilder builder) {
// build query here
}
};
}
}
您在业务需求抽象级别表达了一些标准并创建了可执行规范。因此,客户可能会使用如下规范: 列出客户 = customerRepository.findAll(isLongTermCustomer());
你也可以结合Specification 示例 2.17。综合规格
MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR);
List<Customer> customers = customerRepository.findAll(
where(isLongTermCustomer()).or(hasSalesOfMoreThan(amount)));
As you can see, Specifications offers some glue-code methods to chain and combine Specifications. Thus extending your data access layer is just a matter of creating new Specification implementations and combining them with ones already existing.
您还可以创建复杂的规范,这里是一个示例
public class WorkInProgressSpecification {
public static Specification<WorkInProgress> findByCriteria(final SearchCriteria searchCriteria){
return new Specification<WorkInProgress>() {
@Override
public Predicate toPredicate(Root<WorkInProgress> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<Predicate>();
if(searchCriteria.getView()!=null && !searchCriteria.getView().isEmpty()){
predicates.add(cb.equal(root.get("viewType"), searchCriteria.getView()));
}if(searchCriteria.getFeature()!=null && !searchCriteria.getFeature().isEmpty()){
predicates.add(cb.equal(root.get("title"), searchCriteria.getFeature()));
}if(searchCriteria.getEpic()!=null && !searchCriteria.getEpic().isEmpty()){
predicates.add(cb.equal(root.get("epic"), searchCriteria.getEpic()));
}if( searchCriteria.getPerformingGroup()!=null && !searchCriteria.getPerformingGroup().isEmpty()){
predicates.add(cb.equal(root.get("performingGroup"), searchCriteria.getPerformingGroup()));
}if(searchCriteria.getPlannedStartDate()!=null){
System.out.println("searchCriteria.getPlannedStartDate():" + searchCriteria.getPlannedStartDate());
predicates.add(cb.greaterThanOrEqualTo(root.<Date>get("plndStartDate"), searchCriteria.getPlannedStartDate()));
}if(searchCriteria.getPlannedCompletionDate()!=null){
predicates.add(cb.lessThanOrEqualTo(root.<Date>get("plndComplDate"), searchCriteria.getPlannedCompletionDate()));
}if(searchCriteria.getTeam()!=null && !searchCriteria.getTeam().isEmpty()){
predicates.add(cb.equal(root.get("agileTeam"), searchCriteria.getTeam()));
}
return cb.and(predicates.toArray(new Predicate[]{}));
}
};
}
}