2017-07-04 149 views
0

我想了解Spring Boot和JPA的魔术。我有以下类:使用JPA和Spring Boot查询实体类型(鉴别器)

基地Asset类(也可能是抽象的在我的情况下) 类Model继承自Asset

// ASSET CLASS 

import javax.persistence.DiscriminatorColumn; 
import javax.persistence.DiscriminatorType; 
import javax.persistence.DiscriminatorValue; 
import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.GenerationType; 
import javax.persistence.Id; 
import javax.persistence.Inheritance; 
import javax.persistence.InheritanceType; 

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING) 
@DiscriminatorValue(value = "asset") 
public class Asset { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private long id; 
    private String name; 

    public long getId() { 
     return id; 
    } 

    public void setId(long id) { 
     this.id = id; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 
} 

和我的Model类。

// MODEL CLASS 

import javax.persistence.DiscriminatorValue; 
import javax.persistence.Entity; 


@Entity 
@DiscriminatorValue(value = "model") 
public class Model extends Asset { 

    private long version; 

    public long getVersion() { return version; } 

    public void setVersion(long version) { this.version = version; } 
} 

我创建了一个存储库,我想查询findAllAssetsByType(),但我似乎做了一些完全错误的事情。 这里是AssetRepository接口

//ASSET REPOSITORY 

import assetservice.entity.Asset; 
import java.util.List; 
import org.springframework.data.jpa.repository.JpaRepository; 

public interface AssetRepository extends JpaRepository<Asset, Long> { 

    //big problem - store specific - if discriminiator changes this has to change too... 
    //@Query("select a from Asset a where type = ?1") 
    List<Asset> findAssetsByType(String type); 
} 

也是我写的一个小测试,来说明我想做的事:

// TEST REPOSITORY 
import static org.assertj.core.api.Assertions.assertThat; 

import assetservice.core.AppLogger; 
import assetservice.dao.AssetRepository; 
import java.util.ArrayList; 
import java.util.List; 
import org.apache.logging.log4j.Logger; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 
import org.springframework.test.context.junit4.SpringRunner; 

@RunWith(SpringRunner.class) 
@DataJpaTest 
public class TestAsset { 

    private static final Logger logger = AppLogger.getLogger(); 

    @Autowired 
    private AssetRepository ar; 

    @Test 
    public void doSomething1() { 
     Asset expected = new Asset(); 
     expected.setName("MyTestAsset"); 
     ar.save(expected); 
     Asset actual = this.ar.findOne(expected.getId()); 
     assertThat(actual).isEqualTo(expected); 
    } 

    @Test 
    public void doSomething2() { 
     Asset expected = new Model(); 
     expected.setName("MyTestModel"); 
     ar.save(expected); 
     Asset actual = this.ar.findOne(expected.getId()); 
     assertThat(actual).isEqualTo(expected); 
    } 

    @Test 
    public void doSomething3() { 
     Asset expectedModel = new Model(); 
     Asset expectedAsset = new Asset(); 
     expectedModel.setName("MyTestModel"); 
     expectedAsset.setName("MyTestAsset"); 
     ar.save(expectedModel); 
     ar.save(expectedAsset); 
     Asset actualModel = this.ar.findOne(expectedModel.getId()); 
     assertThat(actualModel).isEqualTo(expectedModel); 

     Asset actualAsset = this.ar.findOne(expectedAsset.getId()); 
     assertThat(actualAsset).isEqualTo(actualAsset); 

     List<Asset> expectedAssetList = new ArrayList<>(); 
     expectedAssetList.add(this.ar.findOne(expectedModel.getId())); 
     List<Asset> actualAssetList = this.ar.findAssetsByType("model"); 

     logger.info("\n\n\n\n\n\n\n\n"); 
     logger.info(expectedAssetList); 
     logger.info(actualAssetList); 
     logger.info("\n\n\n\n\n\n\n\n"); 
    } 
} 

测试doSomething1doSomething2按预期方式工作。测试doSomething3失败

Failed to load ApplicationContext 
java.lang.IllegalStateException: Failed to load ApplicationContext 
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) 
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83) 
    at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:47) 
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287) 
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) 
    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:191) 
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:114) 
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:57) 
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:66) 
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51) 
    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.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) 
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) 
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32) 
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93) 
    at com.sun.proxy.$Proxy1.processTestClass(Unknown Source) 
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:109) 
    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.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) 
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) 
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:146) 
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:128) 
    at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404) 
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63) 
    at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:46) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55) 
    at java.lang.Thread.run(Thread.java:748) 
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'assetRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property type found for type Asset! 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) 
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) 
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:742) 
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) 
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) 
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) 
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) 
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:120) 
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98) 
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116) 
    ... 47 more 
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property type found for type Asset! 
    at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:77) 
    at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:329) 
    at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:309) 
    at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:272) 
    at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:243) 
    at org.springframework.data.repository.query.parser.Part.<init>(Part.java:76) 
    at org.springframework.data.repository.query.parser.PartTree$OrPart.<init>(PartTree.java:247) 
    at org.springframework.data.repository.query.parser.PartTree$Predicate.buildTree(PartTree.java:398) 
    at org.springframework.data.repository.query.parser.PartTree$Predicate.<init>(PartTree.java:378) 
    at org.springframework.data.repository.query.parser.PartTree.<init>(PartTree.java:89) 
    at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:64) 
    at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:103) 
    at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:214) 
    at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:77) 
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.<init>(RepositoryFactorySupport.java:436) 
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:221) 
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:277) 
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:263) 
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:101) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624) 
    ... 62 more 

这似乎是因为Caused by: org.springframework.data.mapping.PropertyReferenceException: No property type found for type Asset!失败。

我的问题是,我在这种情况下解决自定义查询或querydsl?正如你所看到的,我已经通过@Query("select a from Asset a where type = ?1")得到了它的工作,但是这并不是自动使用Spring Boots ...可以说我有更多的资产类型,在我看来,查询特定类型非常麻烦,因为我需要实例化对于每个查询我想去做相应的实体类型:

@Autowire 
private ModelRepository mr; 

... 
List<Model> models = mr.findAll(); 

这将待办事项的东西更容易这样的:

@Autowire 
private AssetRepository ar; 

... 
List<Model> models = ar.findAllByType("models"); 

我应该使用的EntityManager查询?有没有比@Query更好的方法?我应该创建一个“资产”表,并使用“类型”这样的列而不是定义所有的实体?

回答

1

您的资产类别中没有包含列类型。 因此,你的类应该是这个样子

@Id 
@GeneratedValue(strategy = GenerationType.AUTO) 
private long id; 
private String name; 
private String type; 

public long getId() { 
    return id; 
} 

public void setId(long id) { 
    this.id = id; 
} 

public String getName() { 
    return name; 
} 

public void setName(String name) { 
    this.name = name; 
} 

public String getType() { 
    return type; 
} 

public void setType(String type) { 
    this.type = type; 
} 

让我知道,如果这能解决问题。

+0

它的确如此。我添加了 @Column(insertable = false,updatable = false) private String type; 但这是一个很好的做法?你怎么看待我的另一个问题:我应该创建一个“资产”表,并使用“类型”这样的列而不是定义所有的实体? – maiksensi

+0

如果你想为每种类型设置不同的类(如果它方便您的业务需求)。同时,您可以在实体资产中拥有一个列类型,并且您可以定义所有扩展资产的实体。现在,当您定义列表 models = ar.findAllByType(“models”);将返回的列表实际上是Model的列表。您无需为每种不同的资产类型调用mr.findAll()。您可以使用列表 models = ar.findAllByType(“models”);实际返回模型列表 models = ar。findAllByType( “测试1”);返回test1等 –

+0

很好,谢谢你的答案。 – maiksensi