From 0d8d92570e7907bfd5b17e94866e0c3a675b6a19 Mon Sep 17 00:00:00 2001 From: Shihyu Ho Date: Sat, 23 Apr 2022 16:02:31 +0800 Subject: [PATCH 1/2] feat: add QueryBySpecExecutorAdapter --- .../jpa/spec/repository/support/QueryBySpecExecutorAdapter.java | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 starter/src/main/java/tw/com/softleader/data/jpa/spec/repository/support/QueryBySpecExecutorAdapter.java diff --git a/starter/src/main/java/tw/com/softleader/data/jpa/spec/repository/support/QueryBySpecExecutorAdapter.java b/starter/src/main/java/tw/com/softleader/data/jpa/spec/repository/support/QueryBySpecExecutorAdapter.java new file mode 100644 index 0000000..c3e32fc --- /dev/null +++ b/starter/src/main/java/tw/com/softleader/data/jpa/spec/repository/support/QueryBySpecExecutorAdapter.java @@ -0,0 +1,2 @@ +package tw.com.softleader.data.jpa.spec.repository.support;public interface QueryBySpecExecutorAdapter { +} From 5ea8df32a13d1d8029570ce9773edfce342bd81e Mon Sep 17 00:00:00 2001 From: Shihyu Ho Date: Sat, 23 Apr 2022 16:09:44 +0800 Subject: [PATCH 2/2] style: format --- starter/README.md | 17 ++++- .../SpecMapperAutoConfiguration.java | 11 +-- .../autoconfigure/SpecMapperProperties.java | 7 +- .../support/QueryBySpecExecutorAdapter.java | 76 ++++++++++++++++++- .../support/QueryBySpecExecutorImpl.java | 43 ++--------- 5 files changed, 105 insertions(+), 49 deletions(-) diff --git a/starter/README.md b/starter/README.md index 1cd4f4f..f4779c6 100644 --- a/starter/README.md +++ b/starter/README.md @@ -68,11 +68,17 @@ public class PersonService { 在配置的過程中, QBS 會自動配置 Spring Data JPA 的 [Base Repository](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.customize-base-repository), 預設的實作為 [`QueryBySpecExecutorImpl`](./src/main/java/tw/com/softleader/data/jpa/spec/repository/support/QueryBySpecExecutorImpl.java) -如果你的應用有自己的 Base Repository, 則該 Base Repository 必須改成實作 `QueryBySpecExecutorImpl`, 如: +由於 Java 只能單一繼承, 為了方便應用程式可以保留原有的 Parent Base Repository, QBS 還多提供了 [`QueryBySpecExecutorAdapter`](./src/main/java/tw/com/softleader/data/jpa/spec/repository/support/QueryBySpecExecutorAdapter.java) 擴展點 + +你的應用程式可以視情況選擇繼承 `QueryBySpecExecutorImpl` 或實作 `QueryBySpecExecutorAdapter` 去客製化 Base Repository, 如: ```java -class MyRepositoryImpl - extends QueryBySpecExecutorImpl { +class MyRepositoryImpl extends SimpleJpaRepository + implements QueryBySpecExecutorAdapter { + + @Setter + @Getter + private SpecMapper specMapper; private final EntityManager entityManager; @@ -84,6 +90,11 @@ class MyRepositoryImpl this.entityManager = entityManager; } + @Override + public Class getDomainClass() { + return super.getDomainClass(); + } + @Transactional public S mySave(S entity) { // implementation goes here diff --git a/starter/src/main/java/tw/com/softleader/data/jpa/spec/autoconfigure/SpecMapperAutoConfiguration.java b/starter/src/main/java/tw/com/softleader/data/jpa/spec/autoconfigure/SpecMapperAutoConfiguration.java index e8bd6b3..eff3328 100644 --- a/starter/src/main/java/tw/com/softleader/data/jpa/spec/autoconfigure/SpecMapperAutoConfiguration.java +++ b/starter/src/main/java/tw/com/softleader/data/jpa/spec/autoconfigure/SpecMapperAutoConfiguration.java @@ -43,7 +43,7 @@ import tw.com.softleader.data.jpa.spec.SpecificationResolver.SpecificationResolverBuilder; import tw.com.softleader.data.jpa.spec.SpecificationResolverCodecBuilder; import tw.com.softleader.data.jpa.spec.repository.support.JpaRepositoryFactoryBeanPostProcessor; -import tw.com.softleader.data.jpa.spec.repository.support.QueryBySpecExecutorImpl; +import tw.com.softleader.data.jpa.spec.repository.support.QueryBySpecExecutorAdapter; /** * @author Matt Ho @@ -108,16 +108,17 @@ RepositoryFactoryCustomizer repositoryBaseClassCustomizer(SpecMapperProperties p @Bean RepositoryFactoryCustomizer specMapperCustomizer(SpecMapper specMapper) { return factory -> factory.addRepositoryProxyPostProcessor( - (proxyFactory, repositoryInformation) -> getQueryBySpecExecutorImpl(proxyFactory) + (proxyFactory, repositoryInformation) -> getQueryBySpecExecutorAdapter(proxyFactory) .ifPresent(target -> target.setSpecMapper(specMapper))); } @SneakyThrows @SuppressWarnings({ "rawtypes" }) - private Optional getQueryBySpecExecutorImpl(ProxyFactory factory) { + private Optional getQueryBySpecExecutorAdapter( + ProxyFactory factory) { return ofNullable(factory.getTargetSource().getTarget()) - .filter(QueryBySpecExecutorImpl.class::isInstance) - .map(QueryBySpecExecutorImpl.class::cast); + .filter(QueryBySpecExecutorAdapter.class::isInstance) + .map(QueryBySpecExecutorAdapter.class::cast); } } } diff --git a/starter/src/main/java/tw/com/softleader/data/jpa/spec/autoconfigure/SpecMapperProperties.java b/starter/src/main/java/tw/com/softleader/data/jpa/spec/autoconfigure/SpecMapperProperties.java index b865138..cfe435f 100644 --- a/starter/src/main/java/tw/com/softleader/data/jpa/spec/autoconfigure/SpecMapperProperties.java +++ b/starter/src/main/java/tw/com/softleader/data/jpa/spec/autoconfigure/SpecMapperProperties.java @@ -22,6 +22,7 @@ import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; +import tw.com.softleader.data.jpa.spec.repository.support.QueryBySpecExecutorAdapter; import tw.com.softleader.data.jpa.spec.repository.support.QueryBySpecExecutorImpl; /** @@ -37,9 +38,11 @@ public class SpecMapperProperties { boolean enabled = true; /** - * Configures the repository base class. the given class must extends QueryBySpecExecutorImpl + * Configures the repository base class. the given class must implement + * QueryBySpecExecutorAdapter * + * @see QueryBySpecExecutorAdapter * @see QueryBySpecExecutorImpl */ - Class repositoryBaseClass = QueryBySpecExecutorImpl.class; + Class repositoryBaseClass = QueryBySpecExecutorImpl.class; } diff --git a/starter/src/main/java/tw/com/softleader/data/jpa/spec/repository/support/QueryBySpecExecutorAdapter.java b/starter/src/main/java/tw/com/softleader/data/jpa/spec/repository/support/QueryBySpecExecutorAdapter.java index c3e32fc..6970e4f 100644 --- a/starter/src/main/java/tw/com/softleader/data/jpa/spec/repository/support/QueryBySpecExecutorAdapter.java +++ b/starter/src/main/java/tw/com/softleader/data/jpa/spec/repository/support/QueryBySpecExecutorAdapter.java @@ -1,2 +1,76 @@ -package tw.com.softleader.data.jpa.spec.repository.support;public interface QueryBySpecExecutorAdapter { +/* + * Copyright © 2022 SoftLeader + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package tw.com.softleader.data.jpa.spec.repository.support; + +import java.util.List; +import lombok.NonNull; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.transaction.annotation.Transactional; +import tw.com.softleader.data.jpa.spec.SpecMapper; +import tw.com.softleader.data.jpa.spec.repository.QueryBySpecExecutor; + +/** + * Default implementation of {@code QueryBySpecExecutor} + * + * @author Matt Ho + */ +public interface QueryBySpecExecutorAdapter extends JpaSpecificationExecutor, + QueryBySpecExecutor { + + @Override + @Transactional(readOnly = true) + default List findBySpec(Object spec) { + return findAll(getSpecMapper().toSpec(spec, getDomainClass())); + } + + @Override + @Transactional(readOnly = true) + default List findBySpec(Object spec, @NonNull Sort sort) { + return findAll(getSpecMapper().toSpec(spec, getDomainClass()), sort); + } + + @Override + @Transactional(readOnly = true) + default Page findBySpec(Object spec, @NonNull Pageable pageable) { + return findAll(getSpecMapper().toSpec(spec, getDomainClass()), pageable); + } + + @Override + @Transactional(readOnly = true) + default long countBySpec(Object spec) { + return count(getSpecMapper().toSpec(spec, getDomainClass())); + } + + @Override + @Transactional(readOnly = true) + default boolean existsBySpec(Object spec) { + return count(getSpecMapper().toSpec(spec, getDomainClass())) > 0; + } + + SpecMapper getSpecMapper(); + + void setSpecMapper(SpecMapper specMapper); + + Class getDomainClass(); } diff --git a/starter/src/main/java/tw/com/softleader/data/jpa/spec/repository/support/QueryBySpecExecutorImpl.java b/starter/src/main/java/tw/com/softleader/data/jpa/spec/repository/support/QueryBySpecExecutorImpl.java index d1dfffe..a73be67 100644 --- a/starter/src/main/java/tw/com/softleader/data/jpa/spec/repository/support/QueryBySpecExecutorImpl.java +++ b/starter/src/main/java/tw/com/softleader/data/jpa/spec/repository/support/QueryBySpecExecutorImpl.java @@ -20,33 +20,25 @@ */ package tw.com.softleader.data.jpa.spec.repository.support; -import static lombok.AccessLevel.PROTECTED; - import java.io.Serializable; -import java.util.List; import javax.persistence.EntityManager; import lombok.Getter; import lombok.NonNull; import lombok.Setter; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; -import org.springframework.transaction.annotation.Transactional; import tw.com.softleader.data.jpa.spec.SpecMapper; -import tw.com.softleader.data.jpa.spec.repository.QueryBySpecExecutor; /** - * Default implementation for {@code QueryBySpecExecutor} + * Default implementation of {@code QueryBySpecExecutor} * * @author Matt Ho */ public class QueryBySpecExecutorImpl extends SimpleJpaRepository - implements QueryBySpecExecutor { + implements QueryBySpecExecutorAdapter { @Setter - @Getter(PROTECTED) + @Getter private SpecMapper specMapper; public QueryBySpecExecutorImpl( @@ -56,32 +48,7 @@ public QueryBySpecExecutorImpl( } @Override - @Transactional(readOnly = true) - public List findBySpec(Object spec) { - return findAll(getSpecMapper().toSpec(spec, getDomainClass())); - } - - @Override - @Transactional(readOnly = true) - public List findBySpec(Object spec, @NonNull Sort sort) { - return findAll(getSpecMapper().toSpec(spec, getDomainClass()), sort); - } - - @Override - @Transactional(readOnly = true) - public Page findBySpec(Object spec, @NonNull Pageable pageable) { - return findAll(getSpecMapper().toSpec(spec, getDomainClass()), pageable); - } - - @Override - @Transactional(readOnly = true) - public long countBySpec(Object spec) { - return count(getSpecMapper().toSpec(spec, getDomainClass())); - } - - @Override - @Transactional(readOnly = true) - public boolean existsBySpec(Object spec) { - return count(getSpecMapper().toSpec(spec, getDomainClass())) > 0; + public Class getDomainClass() { + return super.getDomainClass(); } }