繁体   English   中英

春天如何实现基于JpaRepository的自定义存储库?

[英]How to implement custom repository based on JpaRepository in spring?

上下文:我在API控制器中使用了queryDSL,它将查询绑定到数据库get。 当前,我有两个具有OneToOne关系的表,我们可以将它们称为表A和表B。如果A中有3行,B中有2行,当我在有条件的情况下获得列表A时,queryDSL将生成查询SQL像A交叉连接B在哪里A.id = B.a_id ,但是它将丢失A中的一项。因此,我将实现自定义存储库以在生成SQL语句时支持更改连接类型。 以下是我的代码的某些部分:(表A命名为LabelTask​​,表B命名为AuditTask)

并且生成的sql段是

    from
    label_task labeltask0_ cross 
join
    audit_task audittask1_ 
where
    labeltask0_.id=audittask1_.label_task 

我的代码有问题吗?或者针对这种情况有其他好的解决方案吗?

JoinDescriptor.java

public class JoinDescriptor {

    public final EntityPath path;
    public final JoinType type;

    private JoinDescriptor(EntityPath path, JoinType type) {
        this.path = path;
        this.type = type;
    }

    public static JoinDescriptor innerJoin(EntityPath path) {
        return new JoinDescriptor(path, JoinType.INNERJOIN);
    }

    public static JoinDescriptor join(EntityPath path) {
        return new JoinDescriptor(path, JoinType.JOIN);
    }

    public static JoinDescriptor leftJoin(EntityPath path) {
        return new JoinDescriptor(path, JoinType.LEFTJOIN);
    }

    public static JoinDescriptor rightJoin(EntityPath path) {
        return new JoinDescriptor(path, JoinType.RIGHTJOIN);
    }

    public static JoinDescriptor fullJoin(EntityPath path) {
        return new JoinDescriptor(path, JoinType.FULLJOIN);
    }
}

JoinFetchCapableQueryDslJpaRepositoryFactoryBean.java

public class JoinFetchCapableQueryDslJpaRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable>
    extends JpaRepositoryFactoryBean<R, T, I> {

    public JoinFetchCapableQueryDslJpaRepositoryFactoryBean(Class<? extends R> repositoryInterface) {
        super(repositoryInterface);
    }

    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
        return new JoinFetchCapableQueryDslJpaRepositoryFactory(entityManager);
    }

    private static class JoinFetchCapableQueryDslJpaRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {

        private EntityManager entityManager;

        public JoinFetchCapableQueryDslJpaRepositoryFactory(EntityManager entityManager) {
            super(entityManager);
            this.entityManager = entityManager;
        }

        protected Object getTargetRepository(RepositoryMetadata metadata) {
            return new JoinFetchCapableRepositoryImpl<>(getEntityInformation(metadata.getDomainType()), entityManager);
        }

        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
            return JoinFetchCapableRepository.class;
        }
    }
}

JoinFetchCapableRepository.java

@NoRepositoryBean
public interface JoinFetchCapableRepository<T, ID extends Serializable> extends
        JpaRepository<T, ID>,
        QuerydslPredicateExecutor<T> {

    Page<T> findAll(Predicate predicate,
                Pageable pageable,
                JoinDescriptor... joinDescriptors);
}

JoinFetchCapableRepositoryImpl.java

public class JoinFetchCapableRepositoryImpl <T, ID extends Serializable>
    extends QuerydslJpaRepository<T, ID>
    implements JoinFetchCapableRepository<T, ID> {

   private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;

   private final EntityPath<T> path;
   private final PathBuilder<T> builder;
   private final Querydsl querydsl;

   public JoinFetchCapableRepositoryImpl(JpaEntityInformation<T, ID> entityInformation,
                                         EntityManager entityManager) {
       this(entityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER);
   }

   public JoinFetchCapableRepositoryImpl(JpaEntityInformation<T, ID> entityInformation,
                                         EntityManager entityManager,
                                         EntityPathResolver resolver) {
       super(entityInformation, entityManager, resolver);
       this.path = resolver.createPath(entityInformation.getJavaType());
       this.builder = new PathBuilder<>(path.getType(), path.getMetadata());
       this.querydsl = new Querydsl(entityManager, builder);
   }

   @Override
   public Page<T> findAll(Predicate predicate, Pageable pageable, JoinDescriptor... joinDescriptors) {
       JPQLQuery countQuery = createQuery(predicate);
       JPQLQuery query = querydsl.applyPagination(pageable, createFetchQuery(predicate, joinDescriptors));

       Long total = countQuery.fetchCount();
       List<T> content = total > pageable.getOffset()
                                           ? query.fetch()
                                           : Collections.emptyList();

       return new PageImpl<>(content, pageable, total);
   }

   private JPQLQuery createFetchQuery(Predicate predicate, JoinDescriptor... joinDescriptors) {
       JPQLQuery query = querydsl.createQuery(path);
       for(JoinDescriptor joinDescriptor: joinDescriptors)
           join(joinDescriptor, query);
       return (JPQLQuery) query.where(predicate);
   }

   private JPQLQuery join(JoinDescriptor joinDescriptor, JPQLQuery query) {
       switch(joinDescriptor.type) {
           case DEFAULT:
               throw new IllegalArgumentException("cross join not supported");
           case INNERJOIN:
               query.innerJoin(joinDescriptor.path);
               break;
           case JOIN:
               query.join(joinDescriptor.path);
               break;
           case LEFTJOIN:
               query.leftJoin(joinDescriptor.path);
               break;
           case RIGHTJOIN:
               query.rightJoin(joinDescriptor.path);
               break;
           case FULLJOIN:
               query.join(joinDescriptor.path);
               break;
       }
       return query.fetchAll();
   }
}

JpaConfig.java

@Configuration
@EnableJpaRepositories(
        basePackages = "com.some.company.service.repository",
        repositoryFactoryBeanClass =JoinFetchCapableQueryDslJpaRepositoryFactoryBean.class)
public class JpaConfig {}

LabelTask​​Repository

@Repository
public interface LabelTaskRepository extends
    JoinFetchCapableRepository<LabelTask, String>,
    QuerydslBinderCustomizer<QLabelTask> {

    @Override
    default void customize(QuerydslBindings bindings, QLabelTask qLabelTask){
        this.bindQueryByTaskType(bindings, qLabelTask);
        this.bindQueryByCreatedDateRange(bindings, qLabelTask);
        // TODO: should remove this when task could be able to assign
        bindings.excluding(qLabelTask.status);
    }
    ...
}

结果:当我启动spring应用程序时,它将返回以下错误消息:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'auditTaskController' defined in file [/.../some/company/service/controllers/AuditTaskController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'auditTaskService' defined in file [/.../some/company/service/AuditTaskService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'auditTaskRepository': Invocation of init method failed; nested exception is java.lang.IllegalStateException: No suitable constructor found on interface some.company.utils.JoinFetchCapableRepository to match the given arguments: [class org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation, class com.sun.proxy.$Proxy182]. Make sure you implement a constructor taking these
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:733)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:198)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1266)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1123)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:535)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:548)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:386)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:127)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)

在some.company.utils.JoinFetchCapableRepository接口上找不到合适的构造函数来匹配给定的参数:[类org.springframework.data.jpa.repository.support。 JpaMetamodelEntityInformation ,类com.sun.proxy。$ Proxy182]。

根据异常消息,JoinFetchCapableRepositoryImpl需要一个构造函数,该构造函数接收两个参数:JpaMetamodelEntityInformation,$ Proxy182。

我添加了一个像这样的构造函数:

       public JoinFetchCapableRepositoryImpl(
                JpaMetamodelEntityInformation<T, ID> entityInformation,
                EntityManager entityManager) {

           this(entityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER);
       }

此后,它对我有用,并且能够更改查询dsl的联接类型

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM