添加Querydsl支持

提示由 @omrzljak 提交, @arnaud-deprez 更新

在某些情况下,Spring Data query possibilities 不能满足查询需求。 您可以使用 @Query 注解和 write your own 。有些人喜欢编写类型安全的查询,例如 Querydsl

生成的Predicate类

Querydsl的重要部分是为查询生成的domain类,即Predicate。 对于spring-data-mongodb,它们是由Java注释后处理工具生成的。

Gradle插件

还有Querydsl的Gradle插件,它支持spring-data-mongodb的配置。

Maven插件

还有一个Maven插件。 在文档Maven集成 一章中全面介绍了Maven配置。 您还需要执行以下步骤。

注意: 不要包含org.slf4j依赖,因为它已包含在Spring Boot中。

变化内容

build.gradle

build.gradle中,将依赖项添加到Querydsl plugin

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.9"
    }
}

apply from: 'gradle/querydsl.gradle'

定义 Querydsl versiongradle.properties中。

querydsl_version=4.1.4

然后创建一个文件 gradle/querydsl.gradle ,内容为:

apply plugin: "com.ewerk.gradle.plugins.querydsl"

sourceSets {
    main {
        java {
            srcDir "$buildDir/generated/source/apt/main"
        }
    }
}

querydsl {
    // we use mongodb
    springDataMongo = true
    querydslSourcesDir = "$buildDir/generated/source/apt/main"
}

dependencies {
    compile "com.querydsl:querydsl-mongodb:${querydsl_version}"
    compileOnly "com.querydsl:querydsl-apt:${querydsl_version}"
}

注意 我们使用MongoDB,但Querydsl插件还支持更多选项

如果您运行gradle build,您将看到这样的输出 Note: Generating net.jogat.names.domain.QName for [net.jogat.names.domain.Name]

对于使用@Document注释的每个域类,Querydsl插件将生成一个Predicate类。

修改Repository类

如果您有一个实体类,例如Name,那么您也有一个NameRepository类。 您必须更改每个Repository类以从QueryDslPredicateExecutor扩展。

public interface NameRepository extends MongoRepository<Name, String>, QueryDslPredicateExecutor<Name> {

这将通过支持Querydsl的其他方法扩展您的Repository类(参见

Web支持

要扩展剩余控制器以支持参数化请求,您必须在方法参数中添加以org.springframework.data.querydsl.binding.QuerydslPredicate注解的com.mysema.query.types.Predicate

@RestController
@RequestMapping("/api")
class NameResource {

    private final NameRepository nameRepository;
    
    public NameResource(NameRepository nameRepository) {
        this.nameRepository = nameRepository;
    }

    @RequestMapping(value = "/names",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
    @Timed
    public ResponseEntity<List<Name>> getAllNames(@QuerydslPredicate(root = Name.class) Predicate predicate,
                                                    Pageable pageable) {
        log.debug("REST request to get a page of Name");
        Page<Name> page = nameRepository.findAll(predicate, pageable);
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/names");
        return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
    }
    ...
}

同样在 NameResourceIntTest 你必须支持 QuerydslPredicateArgumentResolver:

public class NameResourceIntTest {
    ...
    @Autowired
    private NameRepository nameRepository;
    @Autowired
    private QuerydslPredicateArgumentResolver querydslPredicateArgumentResolver;

    @PostConstruct
    public void setup() {
        MockitoAnnotations.initMocks(this);
        NameResource nameResource = new nameResource(nameRepository);
        this.restNameMockMvc = MockMvcBuilders.standaloneSetup(nameResource)
            .setCustomArgumentResolvers(pageableArgumentResolver, querydslPredicateArgumentResolver)
            .setMessageConverters(jacksonMessageConverter).build();
    }
    ...
}

可以在 相关文档 中找到更多详细信息。

编写类型安全的查询

Gradle或Maven插件生成了QName类,该类可用于编写Name.class的查询。 这是Java示例:

QName name = QName.name;

// count all names whose list "categorie" contains string "TOP_EVER"
nameRepository.count(name.categories.contains("TOP_EVER"));