Spring JpaRepository 使用通用实体

4
我正在尝试实现一个通用的DAO,以便像这样使用它与X实体非常相似(id,code,description):
@Repository
public interface GenericDao<T> extends JpaRepository<T, Long> {

    T findByCode(String code);
    T findById(Long id);
}

我的一个实体看起来像这样:
@Getter
@Setter
@Entity
@Table(name = "TEST")
public class Test {...}

我有一个需要使用Dao的服务:

@Service
public class TestServiceImpl implements TestService {

    private GenericDao<Test> testDao;

    @Autowired
    public TestServiceImpl(GenericDao<Test> testDao) {
        this.testDao = testDao;
    }
}

当我使用SpringBoot启动我的应用程序时,应用程序无法启动,错误信息如下:

Caused by: org.springframework.beans.factory.BeanCreationException: Error     creating bean with name 'genericDao': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not an managed type: class java.lang.Object
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1127)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1044)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
... 23 more
Caused by: java.lang.IllegalArgumentException: Not an managed type: class java.lang.Object
at org.hibernate.jpa.internal.metamodel.MetamodelImpl.managedType(MetamodelImpl.java:219)
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:68)
at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getMetadata(JpaEntityInformationSupport.java:67)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:145)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:89)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:69)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:172)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:239)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:225)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:92)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1633)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570)
... 34 more

1
它被称为类型擦除。 - AdamSkywalker
2个回答

6

你不能这样做。Spring试图在启动时创建你的存储库,而不是在自动装配时。但是Spring绝对不知道应该使用什么类型的泛型参数。因此,它尝试使用Object来实例化这个存储库。但是Object不是实体,因此你会得到相应的错误。

结论:你的存储库必须是特定类型,而不是通用类型。


1
你是对的!我试图进行这个更新:`@Repository public interface GenericDao<T extends BaseEntity> extends JpaRepository<T, Long> {T findByCode(String code); T findById(Long id);}。我创建了一个 @Entity public abstract class BaseEntity {...}` 并且所有我的实体都继承了这个类。应用程序启动了,但是我在Hibernate方面遇到了问题,因为从逻辑上讲它不是数据库上的真实表。 - nicola
@nicola 尝试使用MappedSuperclass注解标记您的抽象基类。 - Ken Bekov

5

您可以通过一些调整来实现您想要完成的内容。

不要使用@Repository标记您的GenericDao并将其用作此类,而是使用@NoRepositoryBean注释。正如JavaDocs所述,Spring JPA将尝试初始化附加到JpaRepository的任何内容,但@NoRepositoryBean允许您创建中间扩展,这些扩展不会被初始化,您的真正存储库可以从中继承。您只需要确保实现您的GenericDao类的任何内容都为T提供参数即可。

这意味着您还必须实现GenericDao的扩展,实际上将具有@Repository注释,并在您的服务中使用这些dao代替通用dao。因此,您的代码将如下所示:

通用Dao:

@NoRepositoryBean
public interface GenericDao<T> extends JpaRepository<T, Long> {

    T findByCode(String code);
    T findById(Long id);
}

TestDao,您需要添加的新类,它是参数化实现:

@Repository
public interface TestDao extends GenericDao<Test> {

}

以及服务:

@Service
public class TestServiceImpl implements TestService {

    private TestDao testDao;

    @Autowired
    public TestServiceImpl(TestDao testDao) {
        this.testDao = testDao;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接