2016-12-28 59 views
1

假設我有下面的類:(簡化到了極點)左連接在Spring數據JPA的規範

@Entity 
@Table(name = "USER") 
public class User { 

    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL) 
    private BillingAddress billingAddress; 

    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL) 
    private ShippingAddress shippingAddress; // This one CAN be null 

} 

,並從這個抽象既*Address繼承:(再次,它的超簡化的)

public abstract class Address { 

    @OneToOne(optional = false, fetch = FetchType.LAZY) 
    @JoinColumn(name = "USER_ID") 
    private User user; 

    @NotEmpty 
    @Size(max = 32) 
    @Column(name = "ADDR_TOWN") 
    private String town; 

} 

我嘗試了JPA規範,如春的博客文章解釋說:

/** 
* User specifications. 
* 
* @see <a href="https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl">Advanced Spring Data JPA - Specifications and Querydsl</a> 
*/ 
public class UserSpecifications { 
    public static Specification<User> likeTown(String town) { 
     return new Specification<User>() { 
      @Override 
      public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) { 
       return cb.like(cb.lower(root.get("billingAddress").get("town")), '%' + StringUtils.lowerCase(town) + '%'); 
      } 
     }; 
    } 

使用此「規範」如下:

List<User> users = userRepository.findAll(UserSpecifications.likeTown(myTown)); 

但現在,我也想搜索的鎮爲shippingAddress,這可能不存在。 我試圖在cb.or中合併cb.like,但事實證明生成的SQL查詢對於shippingAddress有一個INNER JOIN,這是不正確的,因爲如上所述,它可能爲空,所以我想要一個LEFT JOIN。

如何做到這一點?

謝謝。

回答

0

不知道它是否有幫助。

我有同樣的問題。我能解決的唯一方法就是使用子查詢。由於我大量使用了規格在我的情況下,它幫助:

例如,這將類似於這樣的事情:

JPASubQuery subquery = new JPASubQuery(); 
subquery = subquery .from(/* tableB */); 
subquery .where(/* conditions */); 

然後用我的子查詢添加謂詞:

predicate.and(subquery.exists()); 

NB 。在大多數情況下,性能影響似乎並不大。

編輯: 我才意識到,前者例如只曾在我的情況下,我使用query-dsl

在你的情況,看看JPA 2.0, Criteria API, Subqueries, In Expressions來創建一個子查詢並將它加入到你的謂詞條件中。

2

指定連接類型:

town = '%' + StringUtils.lowerCase(town) + '%'; 
return cb.or(
    cb.like(cb.lower(root.join("billingAddress", JoinType.LEFT).get("town")), town), 
    cb.like(cb.lower(root.join("shippingAddress", JoinType.LEFT).get("town")), town));