我正在尝试使用QueryDSL进行我需要执行的动态查询。这是一个使用QueryDSL 3.7.2(com.mysema.querydsl)的Spring Boot项目。我已经简化了示例,但基本上我有一个使用Inheritance.JOINED策略注释的Item抽象类。这是Item类:
package org.porthos.concepts.domain;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
@Entity
@Table(name = "item")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "item_type")
public abstract class Item {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "item_type", insertable = false, updatable = false)
private ItemType type;
@Column(name = "title")
private String title;
protected Item() {}
public Long getId() {
return id;
}
public ItemType getType() {
return type;
}
public String getTitle() {
return title;
}
@Override
public String toString() {
return "Item [id=" + id + ", type=" + type + "]";
}
}
还有2个扩展Item的子类。那些是Book和CD。
图书:
package org.porthos.concepts.domain;
import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table;
@Entity
@Table(name = "book")
@DiscriminatorValue("BOOK")
public class Book extends Item {
@Column(name = "genre")
@Enumerated(EnumType.STRING)
private BookGenre genre;
@Column(name = "title")
private String title;
public static enum BookGenre {
MYSTERY, HISTORY, SCIENCE, COMPUTER;
}
protected Book() {}
public Book(BookGenre genre, String title) {
this.title = title;
this.genre = genre;
this.title = title;
}
public BookGenre getGenre() {
return genre;
}
@Override
public String toString() {
return "Book [genre=" + genre + ", title=" + title + ", getId()=" + getId() + ", getType()="
+ getType() + "]";
}
}
和CD:
package org.porthos.concepts.domain;
import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table;
@Entity
@Table(name = "cd")
@DiscriminatorValue("CD")
public class CD extends Item {
@Column(name = "genre")
@Enumerated(EnumType.STRING)
private CDGenre genre;
@Column(name = "title")
private String title;
public static enum CDGenre {
CLASSICAL, POP, ROCK, BLUES;
}
protected CD() {}
public CD(CDGenre genre, String title) {
this.title = title;
this.genre = genre;
this.title = title;
}
public CDGenre getGenre() {
return genre;
}
@Override
public String toString() {
return "CD [genre=" + genre + ", title=" + title + ", getId()=" + getId() + ", getType()="
+ getType() + "]";
}
}
这是我用于测试的Test类:
package org.porthos.concepts;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.porthos.concepts.domain.Book;
import org.porthos.concepts.domain.Book.BookGenre;
import org.porthos.concepts.domain.CD;
import org.porthos.concepts.domain.CD.CDGenre;
import org.porthos.concepts.domain.Item;
import org.porthos.concepts.domain.QBook;
import org.porthos.concepts.domain.QItem;
import org.porthos.concepts.repository.ItemRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import com.mysema.query.BooleanBuilder;
import com.mysema.query.types.Predicate;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ConceptsApplication.class)
@Transactional
public class QueryDslTest {
@Autowired
private ItemRepository itemRepository;
@Before
public void setUp() throws Exception {}
@Test
public void test() {
// Persist a Book and a CD for test
Book book = new Book(BookGenre.SCIENCE, "How To Use Your Microscope");
book = itemRepository.save(book);
CD cd = new CD(CDGenre.BLUES, "The Wind Cries Mary");
cd = itemRepository.save(cd);
Predicate isScienceBook = QItem.item.as(QBook.class).genre.eq(BookGenre.SCIENCE);
BooleanBuilder builder = new BooleanBuilder();
builder.or(isScienceBook);
Page<Item> itemsPage = itemRepository.findAll(builder, new PageRequest(0, 10));
assertThat(itemsPage.getContent().size(), is(1));
}
}
基本上,每个子类Book和CD都有一个流派属性,其输入方式不同。 Book类型使用BookGenre类型,CD类型具有CDGenre类型。出现问题是因为属性的名称完全相同。
因此,当我运行试图查询Book的测试时,我得到以下堆栈跟踪,这基本上表明它希望流派属于Type CDGenre。
如果我使用查询CD运行测试,则查询无任何问题。
此外,如果我重命名Book和CD类型属性以使它们是唯一的,例如bookGenre和cdGenre,那么Book查询肯定有效。
因此,为了使用这种类型的继承,我必须确保每个子类具有不同的命名属性。但是在这个例子中,音乐CD的类型不应该与书籍的类型相同,我认为它们都被命名为流派并不一定是错的。
所以我不确定我的域设计是否糟糕,如果Spring Data JPA和QueryDSL存在问题。
谢谢,
谢
堆栈追踪:
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [SCIENCE] did not match expected type [org.porthos.concepts.domain.CD$CDGenre (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [SCIENCE] did not match expected type [org.porthos.concepts.domain.CD$CDGenre (n/a)]
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:384)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:227)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:436)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:131)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
at com.sun.proxy.$Proxy113.findAll(Unknown Source)
at org.porthos.concepts.QueryDslTest.test(QueryDslTest.java:54)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:254)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.lang.IllegalArgumentException: Parameter value [SCIENCE] did not match expected type [org.porthos.concepts.domain.CD$CDGenre (n/a)]
at org.hibernate.jpa.spi.BaseQueryImpl.validateBinding(BaseQueryImpl.java:874)
at org.hibernate.jpa.internal.QueryImpl.access$000(QueryImpl.java:80)
at org.hibernate.jpa.internal.QueryImpl$ParameterRegistrationImpl.bindValue(QueryImpl.java:248)
at org.hibernate.jpa.internal.QueryImpl$JpaPositionalParameterRegistrationImpl.bindValue(QueryImpl.java:337)
at org.hibernate.jpa.spi.BaseQueryImpl.setParameter(BaseQueryImpl.java:674)
at org.hibernate.jpa.spi.AbstractQueryImpl.setParameter(AbstractQueryImpl.java:198)
at org.hibernate.jpa.spi.AbstractQueryImpl.setParameter(AbstractQueryImpl.java:49)
at com.mysema.query.jpa.impl.JPAUtil.setConstants(JPAUtil.java:55)
at com.mysema.query.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:130)
at com.mysema.query.jpa.impl.AbstractJPAQuery.count(AbstractJPAQuery.java:81)
at org.springframework.data.jpa.repository.support.QueryDslJpaRepository.findAll(QueryDslJpaRepository.java:141)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:483)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:468)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:440)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
... 38 more
答案 0 :(得分:0)
在Spring Boot JPA / Web框架中没有QueryDSL,一切正常,但我必须摆脱ItemType
中的Item
字段,因为JPA会为您提供。{ / p>
@Override
public void createBook() {
Book book = new Book(BookGenre.SCIENCE, "How To Use Your Microscope");
bookRepository.save(book);
CD cd = new CD(CDGenre.BLUES, "The Wind Cries Mary");
cdRepository.save(cd);
}
和
@Override
public void getBook() {
Page<Book> books = bookRepository.findByGenre(BookGenre.SCIENCE, new PageRequest(0, 20) );
System.out.println( books.getContent().get(0) );
}
给出
Hibernate: select count(book0_.id) as col_0_0_ from book book0_ inner join item book0_1_ on book0_.id=book0_1_.id where book0_.genre=?
Hibernate: select book0_.id as id2_2_, book0_1_.title as title3_2_, book0_.genre as genre1_0_, book0_.title as title2_0_ from book book0_ inner join item book0_1_ on book0_.id=book0_1_.id where book0_.genre=? limit ?
Book [genre=SCIENCE, title=How To Use Your Microscope, getId()=1]