【问题标题】:Customizing Param Binding for QueryDSL Support为 QueryDSL 支持自定义参数绑定
【发布时间】:2017-03-16 00:18:01
【问题描述】:

我有一个 Spring Data Rest 存储库,它利用此处概述的 QueryDSL 支持:

https://spring.io/blog/2015/09/04/what-s-new-in-spring-data-release-gosling#spring-data-rest

默认是使用equals查询所有指定的参数。在同一篇文章中提供了一种将参数绑定覆盖到 equals 之外的机制,但它需要 Java 8。

https://spring.io/blog/2015/09/04/what-s-new-in-spring-data-release-gosling#querydsl-web-support

Java 7 中是否有任何简洁的方法来实现相同的功能?

更新

我可以让绑定自定义工作如下:

@Override
public void customize(QuerydslBindings bindings, QMember member) {
    bindings.bind(member.forename).first(new SingleValueBinding<StringPath, String>() {
        @Override
        public Predicate bind(StringPath path, String value) {
            return path.like(value);
        }
    });

    bindings.bind(member.surname).first(new SingleValueBinding<StringPath, String>() {
        @Override
        public Predicate bind(StringPath path, String value) {
            return path.startsWith(value);
        }
    });
}

但是,这些示例都使用存储库接口上的 Java 8 默认方法来应用它们。

public interface MemberRepository extends JpaRepository<Member, Long>, 
         QueryDslPredicateExecutor<Member>,QuerydslBinderCustomizer<QMember> {

    default void customize(QuerydslBindings bindings, QMember member) {
      ....
    }
}

在 Java 7 中,这显然是不可能的。我尝试过使用自定义存储库,但是失败并出现错误:

org.springframework.data.mapping.PropertyReferenceException:没有为类型成员找到自定义属性!

public interface MemberRepositoryCustom {

    public void customize(QuerydslBindings bindings, QMember member);
}

public class MemberRepositoryCustomImpl implements MemberRepositoryCustom {

    @Override
    public void customize(QuerydslBindings bindings, QMember member) {
        bindings.bind(member.forename).first(new SingleValueBinding<StringPath, String>() {
            @Override
            public Predicate bind(StringPath path, String value) {
                return path.like(value);
            }
        });

        bindings.bind(member.surname).first(new SingleValueBinding<StringPath, String>() {
            @Override
            public Predicate bind(StringPath path, String value) {
                return path.startsWith(value);
            }
        });
    }
}

【问题讨论】:

    标签: spring-data spring-data-rest


    【解决方案1】:

    有两种方法可以使用 Java 7 完成此操作。第一种方法是创建一个通用基础存储库,它将自定义绑定应用到所有已实现的模型存储库。例如:

    public class GenericModelRepository<T, ID extends Serializable, S extends EntityPath<T>> extends QueryDslJpaRepository<T, ID> implements QuerydslBinderCustomizer<S> {
    
        public GenericModelRepository(
                JpaEntityInformation<T, ID> entityInformation,
                EntityManager entityManager) {
            super(entityInformation, entityManager);
        }
    
        public GenericModelRepository(
                JpaEntityInformation<T, ID> entityInformation,
                EntityManager entityManager,
                EntityPathResolver resolver) {
            super(entityInformation, entityManager, resolver);
        }
    
        @Override 
        public void customize(QuerydslBindings bindings, S t) {
                bindings.bind(String.class).first(new SingleValueBinding<StringPath, String>() {
                    @Override 
                    public Predicate bind(StringPath path, String s) {
                        return path.equalsIgnoreCase(s);
                    }
                });
        }
    }
    

    要告诉 Spring Data 在实现所有自定义存储库接口时使用此基础存储库,只需将其作为 repositoryBaseClass 添加到 @EnableJpaRepositories 注释中:

    @Configuration
    @EnableJpaRepositories(basePackages = { "me.woemler.project.repositories" }, repositoryBaseClass = GenericModelRepository.class)
    @EnableTransactionManagement
    public class RepositoryConfig { ... }
    
    @RepositoryRestResource
    public interface PersonRepository extends JpaRepository<Person, Long>, 
            QueryDslPredicateExecutor<Person>,
            QuerydslBinderCustomizer<EntityPath<Person>> {
    }
    

    现在所有 Web 服务 StringPath 查询操作都将是不区分大小写的相等测试:

    GET http://localhost:8080/persons?name=joe%20smith
    
        
        "_embedded": {
            "persons": [
              {
                "name": "Joe Smith",
                "gender": "M",
                "age": 35,
                "_links": {
                  "self": {
                    "href": "http://localhost:8080/persons/1"
                  },
                  "person": {
                    "href": "http://localhost:8080/persons/1"
                  }
                }
              }
            ]
          },
          "_links": {
            "self": {
              "href": "http://localhost:8080/persons"
            },
            "profile": {
              "href": "http://localhost:8080/profile/persons"
            }
          },
          "page": {
            "size": 20,
            "totalElements": 1,
            "totalPages": 1,
            "number": 0
          }
        }
    

    如果您想更精细地控制每个存储库如何处理其绑定,则第二个选项是创建您希望自定义的存储库的 Impl 版本:

     public class PersonRepositoryImpl implements QuerydslBinderCustomizer<EntityPath<Person>> {
        @Override
        public void customize(QuerydslBindings bindings, EntityPath<Person> t) {
            bindings.bind(String.class).first(new SingleValueBinding<StringPath, String>() {
                @Override
                public Predicate bind(StringPath path, String s) {
                    return path.equalsIgnoreCase(s);
                }
            });
        }
    }
       
    

    然后您可以正常使用@EnableJpaRepositories 注释,但您必须为您希望自定义的每个存储库接口创建一个Impl 实例。

    【讨论】:

    • 嗨@woemler。扩展单个 repo 的第二种方法似乎是最好的,但这基本上是我最初尝试的方法 - 除了 Impl 之外,您还需要一个接口并且存储库必须扩展它。这总是失败并出现错误:org.springframework.data.mapping.PropertyReferenceException: No property custom found for type 'Member'!所以看起来框架将 custom(...) 方法视为某种查询方法。我会尝试全局扩展方法。
    • 谢谢@woemler。方法 1 按预期工作,但是在每个存储库的基础上应用它似乎是一个更好的解决方案。我会进一步调查。
    • 你的接口和Impl类不需要相互引用。只要类命名一致,Spring就会知道调用模型端点时要调用哪些方法。在这里查看一个工作示例:github.com/woemler/spring-data-rest-example
    • 是的,这行得通!所以我几乎到了那里,但为了破坏一切的无关接口。这实际上是否记录在您可以创建一个名为 XxxRepositoryImpl 的类的任何地方?谢谢。
    • 是的,它在 Spring Data 文档中。如果您想要Impl 以外的其他内容,还可以使用@EnableJpaRepositories(repositoryImplementationPostfix='xxx') 自定义使用的后缀。 docs.spring.io/spring-data/jpa/docs/1.10.5.RELEASE/reference/…
    【解决方案2】:

    你可以使用

    布尔构造器
    导入 com.mysema.query.BooleanBuilder;

    构造 queryDsl 谓词

    QUser quser=QUser.User;
    BooleanBuilder whereClause=new BooleanBuilder();
    whereClause.and(quser.property1.eq("some"));
    whereClause.and(quser.property2.in(listOfValues));
    
    springRepository.find(whereClause);
    

    脚注:java 8 将有助于减少上述情况下的大量击键

    【讨论】:

      猜你喜欢
      • 2014-01-18
      • 1970-01-01
      • 2014-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-12
      相关资源
      最近更新 更多