2015-01-26 22 views
7

Tôi đang sử dụng dữ liệu Spring JPA 1.7.1 với Hibernate 4.3.7 làm nhà cung cấp JPA của tôi. Tôi có kho dữ liệu mùa xuân JPA sau:NPE trong dữ liệu Spring JPA với mệnh đề IN

@Repository 
public interface CompanyRepository extends JpaRepository<Company, Integer> { 
    @EntityGraph(value = "graph.company.search.results", type = EntityGraph.EntityGraphType.FETCH) 
    @Query("SELECT c FROM Company c WHERE c.id IN :companyIds") 
    List<Company> findByCompanyIdsForSearchResults(@Param("companyIds") Set<Integer> companyIds); 
} 

Các mã sau đây sẽ gọi phương thức kho trên:

Set<Integer> companyIds = new HashSet<>(); 
companyIds.add(100000); 
// companyIds.add(100001); // This line breaks the code 
List<Company> companies = this.companyRepository.findByCompanyIdsForSearchResults(companyIds); 

Tôi đang trải qua hành vi kỳ lạ với ở trên. Trước hết, nếu tôi chỉ đặt một ID trong tập hợp, thì hai sốCompany trường hợp được trả về trong danh sách của tôi, mặc dù ID rõ ràng là duy nhất. Thứ hai, nếu tôi thêm nhiều hơn một ID để các thiết lập, sau đó mã không thành công với những điều sau NullPointerException:

java.lang.NullPointerException 
    org.hibernate.param.NamedParameterSpecification.bind(NamedParameterSpecification.java:67) 
    org.hibernate.loader.hql.QueryLoader.bindParameterValues(QueryLoader.java:616) 
    org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1901) 
    org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1862) 
    org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1839) 
    org.hibernate.loader.Loader.doQuery(Loader.java:910) 
    org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:355) 
    org.hibernate.loader.Loader.doList(Loader.java:2554) 
    org.hibernate.loader.Loader.doList(Loader.java:2540) 
    org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2370) 
    org.hibernate.loader.Loader.list(Loader.java:2365) 
    org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:497) 
    org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:387) 
    org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:236) 
    org.hibernate.internal.SessionImpl.list(SessionImpl.java:1264) 
    org.hibernate.internal.QueryImpl.list(QueryImpl.java:103) 
    org.hibernate.jpa.internal.QueryImpl.list(QueryImpl.java:573) 
    org.hibernate.jpa.internal.QueryImpl.getResultList(QueryImpl.java:449) 
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    java.lang.reflect.Method.invoke(Method.java:483) 
    org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:362) 
    com.sun.proxy.$Proxy217.getResultList(Unknown Source) 
    org.springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:110) 
    org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:74) 
    org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:98) 
    org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:89) 
    org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:421) 
    org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:381) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    org.springframework.data.repository.core.support.RepositoryFactorySupport$DefaultMethodInvokingMethodInterceptor.invoke(RepositoryFactorySupport.java:512) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98) 
    org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262) 
    org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:122) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) 
    com.sun.proxy.$Proxy177.findByCompanyIdsForSearchResults(Unknown Source) 

    ... 

Tôi cũng đã cố gắng để thay đổi Set vào một danh sách, nhưng với kết quả tương tự. Mệnh đề WHERE của truy vấn được tạo ra trông giống như vậy: where company0_.id in (?)

Tôi có làm gì sai, hay đây là lỗi không? Lý do tại sao tôi đang sử dụng @Query thay vì đặt tên phương thức findByIdIn là tôi muốn tự do đặt tên cho phương thức của mình một cái gì đó độc đáo phụ thuộc vào ngữ cảnh (vì biểu đồ thực thể phụ thuộc vào ngữ cảnh mà phương thức được gọi ra). Là những gì tôi đang cố gắng làm thậm chí được hỗ trợ?

Cảm ơn bạn trước.

Chỉnh sửa # 3: Nó chỉ ra rằng ngoại lệ được ném vì chú thích @EntityGraph. Điều này khá lạ vì tôi đã sử dụng biểu đồ thực thể với các phương thức lưu trữ khác có chú thích @Query và mọi thứ hoạt động tốt. Có vẻ như, tuy nhiên, như thể sự kết hợp của @EntityGraph và mệnh đề IN gây ra sự cố. Nó có vẻ là một lỗi. Nếu tôi xóa mệnh đề IN và thay đổi phương pháp để tìm kiếm một công ty, mọi thứ sẽ hoạt động tốt với biểu đồ thực thể. Có ai có bất kỳ ý tưởng cho một giải pháp hoặc workaround? Tôi có thể "theo cách thủ công" JOIN FETCH các liên kết của tôi, nhưng điều này gần như không đẹp bằng cách sử dụng biểu đồ thực thể.

Chỉnh sửa # 2: Điều thú vị là mọi thứ hoạt động như mong đợi nếu tôi viết phương thức lưu trữ tùy chỉnh as described here. Mã sau hoạt động tốt:

public List<Company> test(Set<Integer> companyIds) { 
    String jpql = "select c from Company c where c.id in :companyIds"; 
    Query q = this.getEntityManager().createQuery(jpql); 
    q.setParameter("companyIds", companyIds); 
    List results = q.getResultList(); // Contains X entities, and works with > 1 company IDs as well 

    return null; 
} 

Rõ ràng vấn đề có liên quan đến việc thực hiện tự động phương thức giao tiếp của JPA trong dữ liệu Spring. Tôi có thể chỉ sử dụng triển khai "tùy chỉnh", nhưng sẽ đẹp hơn nếu tôi có thể sử dụng phương pháp tiếp cận đầu tiên vì vậy tôi vẫn đang tìm giải pháp cho vấn đề ban đầu.

Chỉnh sửa # 1: Dưới đây là mã nguồn của thực thể Company (trừ getters và setters).

@Entity 
@Table(name = "company", uniqueConstraints = { 
     @UniqueConstraint(columnNames = { "slug" }) 
}) 
@NamedEntityGraphs({ 
     @NamedEntityGraph(
       name = "graph.company.profile.view", 
       attributeNodes = { 
         @NamedAttributeNode(value = "city"), 
         @NamedAttributeNode(value = "acknowledgements"), 
         @NamedAttributeNode(value = "industries"), 
         @NamedAttributeNode(value = "companyServices", subgraph = "companyServices") 
       }, 
       subgraphs = { 
         @NamedSubgraph(name = "companyServices", attributeNodes = { 
           @NamedAttributeNode(value = "service") 
         }) 
       } 
     ), 

     @NamedEntityGraph(
     name = "graph.company.search.results", 
     attributeNodes = { 
       @NamedAttributeNode(value = "city"), 
       @NamedAttributeNode(value = "acknowledgements"), 
       @NamedAttributeNode(value = "industries"), 
       @NamedAttributeNode(value = "companyServices", subgraph = "companyServices") 
     }, 
     subgraphs = { 
       @NamedSubgraph(name = "companyServices", attributeNodes = { 
         @NamedAttributeNode(value = "service") 
       }) 
     } 
) 
}) 
public class Company { 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column 
    private int id; 

    @NotEmpty 
    @Length(min = 5, max = 100) 
    @Column(length = 100, nullable = false) 
    private String name; 

    @Length(min = 3, max = 50) 
    @Column(length = 50) 
    private String slug; 

    @Column 
    private Double rating; 

    @Column(name = "number_of_reviews") 
    private int numberOfReviews; 

    @Length(min = 5, max = 50) 
    @Column(name = "street_name", length = 50) 
    private String streetName; 

    @Length(max = 25) 
    @Column(name = "street_number", length = 25) 
    private String streetNumber; 

    @Length(min = 8, max = 11) 
    @Column(name = "phone_number", length = 11) 
    private String phoneNumber; 

    @Length(min = 8, max = 11) 
    @Column(name = "second_phone_number", length = 11) 
    private String secondPhoneNumber; 

    @Length(min = 50, max = 175) 
    @Column 
    private String teaser; 

    @Length(min = 50, max = 5000) 
    @Column 
    private String description; 

    @Length(min = 8, max = 8) 
    @Column(name = "ci_number", nullable = false) 
    private long ciNumber; 

    @ManyToOne(fetch = FetchType.EAGER, targetEntity = City.class, optional = false) 
    @JoinColumn(name = "postal_code") 
    private City city; 

    @ManyToMany(fetch = FetchType.LAZY) 
    @JoinTable(name = "company_acknowledgement", joinColumns = @JoinColumn(name = "company_id"), inverseJoinColumns = @JoinColumn(name = "acknowledgement_id")) 
    @OrderBy("name") 
    private Set<Acknowledgement> acknowledgements = new HashSet<Acknowledgement>(); 

    @ManyToMany(fetch = FetchType.LAZY) 
    @JoinTable(name = "company_industry", joinColumns = @JoinColumn(name = "company_id"), inverseJoinColumns = @JoinColumn(name = "industry_id")) 
    @OrderBy("name") 
    private Set<Industry> industries = new HashSet<Industry>(); 

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "company") 
    private Set<CompanyService> companyServices = new HashSet<CompanyService>(); 

    @OneToMany(fetch = FetchType.LAZY, targetEntity = Review.class, mappedBy = "company") 
    private Set<Review> reviews = new HashSet<Review>(); 

    @OneToMany(fetch = FetchType.LAZY, targetEntity = Like.class, mappedBy = "company") 
    private Set<Like> likes = new HashSet<Like>(); 

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "company") 
    private Set<AccountCompany> accountCompanies = new HashSet<AccountCompany>(); 
} 
+0

Điều khoản in hoạt động mà không cần niềng răng - bạn nên loại bỏ chúng. Chỉ có phiên bản cũ của Hibernate yêu cầu đó và nó luôn luôn là một lỗi mà nó đã làm. Và điều gì sẽ xảy ra nếu bạn sử dụng một kiểu dữ liệu khác, chẳng hạn như Danh sách? Về mặt lịch sử, loại đối tượng nào được mong đợi cho tham số của mệnh đề trong luôn luôn là một chút khó tính. – Gimby

+0

@Gimby Kết quả là như nhau nếu tôi sử dụng Danh sách. Tôi đặt dấu ngoặc đơn vì tôi gặp lỗi nếu không. Nó chỉ ra rằng lỗi là cùng một 'NullPointerException' mà tôi nhận được ở trên. Nhưng không có dấu ngoặc đơn trong mệnh đề 'IN', tôi nhận được điều này ngay cả khi tôi chỉ có một số nguyên trong Danh sách/Bộ. Nếu tôi thêm dấu ngoặc đơn, nó chỉ xảy ra với> 1 số nguyên. Nội dung thú vị. Tôi sẽ loại bỏ chúng, nhưng ngoại lệ NPE vẫn bị ném. – Andy0708

+0

Funky thực sự. Bạn có thể thử không sử dụng mùa xuân nhưng chỉ cần gọi một truy vấn JPA thông thường bằng cách sử dụng một thực thể để xem nếu ít nhất là hoạt động như mong đợi? – Gimby

Trả lời

3

Hãy thử nó với một tham số chỉ mục để thay thế và nếu không có sự @EntityGraph:

@Query("SELECT c FROM Company c WHERE c.id IN ?1") 
List<Company> findByCompanyIdsForSearchResults(Set<Integer> companyIds); 

Nếu nó hoạt động với tham số chỉ mục, thử thêm @EntityGraph và xem nó làm việc.

Đối với EntityGraph, bạn phải nhận thức được những vấn đề sản phẩm Descartes bạn đang tạo:

@NamedAttributeNode(value = "acknowledgements"), 
@NamedAttributeNode(value = "industries"), 
@NamedAttributeNode(value = "companyServices", subgraph = "companyServices") 

Tất cả các hiệp hội được một-nhiều mối quan hệ, vì vậy khi bạn lấy Công ty bạn sẽ kết thúc với một sản phẩm Descartes giữa:

  • Công ty
  • Lời cảm ơn
  • nghiệp
  • CompanyService

Điều này sẽ thực hiện rất tồi tệ và bạn luôn phải tham gia FETCH at most one one-to-many association.

Tôi đề nghị bạn loại bỏ EntityGraph và sử dụng JOIN FETCH cho tất cả liên kết nhiều người và nhiều nhất một-nhiều.

Để khởi tạo các hiệp hội khác mà bạn có thể một trong hai:

+0

Nó chỉ ra rằng ngoại lệ được ném vì chú thích '@ EntityGraph'. Không có nó, kết quả sẽ được tải chính xác. Điều này là lạ bởi vì tôi đã sử dụng chú thích với các phương thức lưu trữ khác có chú thích '@ Query' và nó hoạt động tốt. Nhưng có vẻ như có điều gì đó sai khi được sử dụng kết hợp với mệnh đề 'IN'. Tôi đoán đây là một lỗi. Tôi đoán tôi có thể sử dụng 'JOIN FETCH' trong truy vấn của tôi, nhưng cách tiếp cận đồ thị thực thể thì sạch hơn nhiều. Bạn có bất kỳ ý tưởng cho một giải pháp/workaround? – Andy0708

+0

Kiểm tra câu trả lời cập nhật của tôi. –

+0

Lý do tại sao tôi chấp nhận lấy lại sản phẩm Descartes của tổ chức của tôi và các hiệp hội của nó là số lượng đối tượng (hàng) trong mỗi bộ sưu tập sẽ thấp (1-5 cho hầu hết trong số họ), bởi vì điều này bị hạn chế bởi logic nghiệp vụ của tôi . Tôi đã không thử nghiệm nó, nhưng tôi đã nghĩ rằng chi phí của việc tạo ra các sản phẩm Descartes không phải là quá xấu so với việc phát hành nhiều truy vấn đối với cơ sở dữ liệu của tôi. Tôi không chắc chắn chính xác thời điểm nào một cách tiếp cận nhanh hơn phương pháp kia, vì tôi phải kiểm tra thêm. Cảm ơn bạn rất nhiều vì đã đề cập đến điều này. – Andy0708

5

Sau khi phải đối mặt với cùng một vấn đề và googling một chút, tôi thấy đó là lỗi Hibernate: https://hibernate.atlassian.net/browse/HHH-9230

Nhưng nó không dường như họ đang làm việc trên nó ... không có người được chuyển nhượng cho vấn đề này :-(

Có vẻ như những người ở Hibernate không quan tâm quá nhiều đến việc thực hiện JPA của họ ... họ có một số lỗi trực tiếp cho một thời gian dài và có vẻ như họ không có kế hoạch sửa chữa chúng :-(

+0

Cảm ơn bạn đã cung cấp thông tin! Rất hữu ích để biết rằng nó thực sự là một lỗi trong Hibernate và không phải trên kết thúc của tôi. Thật không may tôi nhận thấy rằng các lỗi đã được báo cáo vào giữa năm 2014, do đó, nó có thể sẽ là một thời gian dài cho đến khi nó được cố định. : ( – Andy0708

+1

Đây không phải là thái độ đúng đắn đối với các dự án PMNM. Hibernate là nguồn mở, vì vậy tất cả chúng ta nên đóng góp. Thực tế là Red Hat trả tiền cho một số phát triển, điều đó không có nghĩa là chúng ta không phải tham gia. –

Các vấn đề liên quan