Как реализовать собственный репозиторий на основе JpaRepository весной? - PullRequest
0 голосов
/ 05 декабря 2018

Контекст: я использовал queryDSL в контроллере API, который связывает запрос с получением базы данных.В настоящее время у меня есть две таблицы с отношением OneToOne, и мы можем назвать их Таблица A и Таблица B. Если в A есть 3 строки и 2 строки в B, когда я получаю список A с некоторыми условиями, и queryDSL сгенерирует SQL-запроскак A CROSS JOIN B WHERE 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 {}

LabelTaskRepository

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

Результат: приЯ запускаю весеннее приложение, оно выдаст следующее сообщение об ошибке:

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)

1 Ответ

0 голосов
/ 12 декабря 2018

Не найдено подходящего конструктора в интерфейсе some.company.utils.JoinFetchCapableRepository для соответствия заданным аргументам: [class 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

...