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>();
}
Đ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
@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
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