2016-05-02 11 views
0

Ich versuche, QueryDSL für eine dynamische Abfrage zu verwenden, die ich tun muss. Dies ist ein Spring Boot-Projekt mit QueryDSL 3.7.2 (com.mysema.querydsl). Ich habe das Beispiel vereinfacht, aber im Grunde habe ich eine Item-Abstract-Klasse, die mit einer Inheritance.JOINED-Strategie versehen ist. Dies ist die Artikelklasse:Spring Data JPA mit QueryDSL Problem mit JPA Inheritance.JOINED Strategie, wenn Unterklassen den gleichen Eigenschaftennamen haben

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 + "]"; 
    } 

} 

Es gibt auch 2 Unterklassen, die Artikel erweitern. Das sind Buch und CD.

Buch:

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() + "]"; 
    } 

} 

Und 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() + "]"; 
    } 

} 

Dies ist die Test-Klasse, die ich zum Testen verwenden:

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)); 
    } 

} 

Also im Grunde jede der Unterklassen Buch und CD haben eine Genre Eigenschaft, die anders getippt wird. Das Buchgenre verwendet den BookGenre-Typ und das CD-Genre den CDGenre-Typ. Das Problem tritt auf, weil die Eigenschaften exakt gleich benannt sind.

Also, wenn ich den Test, der versucht, für ein Buch zu suchen, ausführen, bekomme ich die folgende Stapel-Trace, die im Grunde angibt, dass es Genre von Type CDGenre erwartet.

Wenn ich den Test mit einer Abfrage für eine CD ausführen, funktioniert die Abfrage ohne Probleme.

Auch wenn ich die Buch- und CD-Genre-Eigenschaften umbenennen, so dass sie wie BookGenre und CDGenre einzigartig sind, dann funktioniert die Buchabfrage definitiv.

Also, um diese Art der Vererbung zu verwenden, muss ich sicherstellen, dass jede der Unterklassen unterschiedlich benannte Eigenschaften haben. Aber in diesem Beispiel sollte ein Genre für Musik-CDs nicht dasselbe wie ein Genre für Bücher sein, und ich sehe es nicht unbedingt falsch, dass sie beide genre heißen.

Ich bin mir also nicht sicher, ob es ein schlechtes Domain-Design meinerseits ist, ob es ein Problem mit Spring Data JPA und/oder QueryDSL gibt.

Danke,

Frank

Stapelüberwachung:

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 

Antwort

0

Es ist alles funktioniert ohne QueryDSL in einem Frühlings-Boot-PPV/Web-Framework in Ordnung, obwohl ich das ItemType Feldes erhalten hatte zu befreien in Item seit JPA bietet das für Sie.

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

und

@Override 
public void getBook() { 
    Page<Book> books = bookRepository.findByGenre(BookGenre.SCIENCE, new PageRequest(0, 20)); 
    System.out.println(books.getContent().get(0)); 
} 

gibt

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] 
Verwandte Themen