如何基于实体接口声明存储库?
在一个新项目中,我们希望使用Spring Data JPA并为所有JPA实体定义接口,例如:
public interface Person extends Serializable { void setId(Long id); Long getId(); void setLastName(String lastName); String getLastName(); void setFirstName(String firstName); String getFirstName(); // ... } @Entity @Table(name = "t_persons") public class PersonEntity implements Person { private static final long serialVersionUID = 1L; @Id @Column @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column private String firstName; @Column private String lastName; // ... }
但是,在声明基于类似接口的Spring Data存储库时
public interface PersonRepository extends JpaRepository { }
Spring上下文无法初始化,原因是exception
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'personRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not an managed type: interface com.example.Person at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1513) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191) at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:917) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:860) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:775) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:489) ... 24 more Caused by: java.lang.IllegalArgumentException: Not an managed type: interface com.example.Person at org.hibernate.ejb.metamodel.MetamodelImpl.managedType(MetamodelImpl.java:171) at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.(JpaMetamodelEntityInformation.java:70) at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getMetadata(JpaEntityInformationSupport.java:65) at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:146) at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:84) at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:67) at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:150) at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:224) at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:210) at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:84) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1572) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1510) ... 34 more
我没有找到依赖于接口而不是具体类型的Repository的任何示例,所以这有可能吗? 如果有,怎么样?
似乎如果我们不能使用接口声明存储库,那么根本不会使用这些接口,因为我们最终会在我们的服务中到处都有显式的强制转换,甚至在我们处理generics时都会进行未经检查的强制转换( List
, Iterable
……)。
这是您的问题的解决方案。 我不知道为什么Spring的人决定将他们的存储库建立在具体的类上。 但至少他们有可能改变这种状况。
- 您需要在
EnableJpaRepositories
提供自定义repositoryFactoryBeanClass
,例如:
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; /** * @author goraczka */ @EnableJpaRepositories( repositoryFactoryBeanClass = InterfaceBasedJpaRepositoryFactoryBean.class ) public class DatabaseConfig { }
- 然后,您需要实现
InterfaceBasedJpaRepositoryFactoryBean
。 它是一个Spring钩子,可以为存储库bean创建自定义工厂。
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import javax.persistence.EntityManager; /** * @author goraczka */ public class InterfaceBasedJpaRepositoryFactoryBean, S, ID> extends JpaRepositoryFactoryBean { public InterfaceBasedJpaRepositoryFactoryBean(Class extends T> repositoryInterface) { super(repositoryInterface); } protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { return new InterfaceBasedJpaRepositoryFactory(entityManager); } }
- 最后但并非最不重要的是,自定义存储库bean工厂尝试将存储库本身定义的接口与在
EntityManager
注册的实体类进行匹配。
import org.springframework.data.jpa.repository.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport; import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; import org.springframework.util.Assert; import javax.persistence.EntityManager; import java.util.AbstractMap; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; /** * @author goraczka */ public class InterfaceBasedJpaRepositoryFactory extends JpaRepositoryFactory { private final Map extends Class>, ? extends Class>> interfaceToEntityClassMap; private final EntityManager entityManager; public InterfaceBasedJpaRepositoryFactory(EntityManager entityManager) { super(entityManager); this.entityManager = entityManager; interfaceToEntityClassMap = entityManager .getMetamodel() .getEntities() .stream() .flatMap(et -> Arrays.stream(et.getJavaType().getInterfaces()) .map(it -> new AbstractMap.SimpleImmutableEntry<>(it, et.getJavaType()))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (possibleDuplicateInterface, v) -> v)); } @Override @SuppressWarnings("unchecked") public JpaEntityInformation getEntityInformation(Class domainClass) { Assert.isTrue(domainClass.isInterface(), "You are using interface based jpa repository support. " + "The entity type used in DAO should be an interface"); Class domainInterface = domainClass; Class> entityClass = interfaceToEntityClassMap.get(domainInterface); Assert.notNull(entityClass, "Entity class for a interface" + domainInterface + " not found!"); return (JpaEntityInformation) JpaEntityInformationSupport.getEntityInformation(entityClass, entityManager); } }
不要因为任何错误而抨击我。 我在阅读完这个问题后10分钟就做到了,并意识到目前尚无解决方案。 我真的需要一个。 我没有为它创建任何测试,但似乎工作。 欢迎改进。
接口Person缺少@Entity注释,因此不会被识别为托管对象。 我认为将@Entity注释放在Person接口上也无济于事,因为这个注释不会被inheritance。 我想你应该忘记Person接口,或者只是在存储库声明中使用PersonEntity。 实际上没有检查代码 – 如果这个答案是错误的,那就非常抱歉……
- 如何使用JPA连接到多个数据库?
- 调用getNextException以查看原因:如何使Hibernate / JPA显示数据库服务器消息以查找exception
- Hibernate将NULL值粘贴到列表中
- 使用hibernate计算group in中的其他字段
- Hibernate返回BigIntegers而不是long
- 嵌套事务用例中的外部事务没有看到数据库中持久存在的更新(JPA,MySQL,Spring Framework和Hibernate)
- 通过hibernate的Joda-Money持久性
- HQL查询是否始终命中数据库并获得结果?
- IllegalArgumentException:为TypedQuery 指定的类型与查询返回类型不兼容