在SpringDataJPA中使用Querydsl(kotlin版)

LOVELETTERD·2023-04-03 23:15·486 次阅读

在SpringDataJPA中使用Querydsl(kotlin版)

前言

我们在做日常开发中经常会进行数据库的操作,ORM框架可以帮助我们更便捷的进行数据的操作。SpringDataJPA就是我们经常用到的ORM框架,我们只需要定义一些实体类以及实现一些接口,它便为我们生成了一些丰富的SQL操作功能。但是如果涉及到多表动态查询, JPA 的功能就显得有些捉襟见肘了,虽然我们可以使用注解 @Query ,在这个注解中写 SQL ,但是这样我们又需要创建Model对象以及Repository接口。好在Spring可以方便的集成QueryDSL,让我们方便的实现这些功能。

QueryDSL仅仅是一个通用的查询框架,专注于通过Java API构建类型安全的SQL查询。
QueryDSL可以通过一组通用的查询API为用户构建出适合不同类型ORM框架或者是SQL的查询语句,也就是说QueryDSL是基于各种ORM框架以及SQL之上的一个通用的查询框架。
借助QueryDSL可以在任何支持的ORM框架或者SQL平台上以一种通用的API方式来构建查询。目前QueryDSL支持的平台包括JPA,JDO,SQL,Mongodb 等等。

环境准备

我们采用spirngboot作为基础框架,开发语言选择kotlin,数据库选择postgre,构建工具选择maven,引入web、jpa、QueryDSL、openapi等相关依赖。

pom.xml

Copy
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.5</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.bliietsdoux</groupId> <artifactId>jpa_learn_maven</artifactId> <version>0.0.1-SNAPSHOT</version> <name>jpa_learn_maven</name> <description>jpa_learn_maven</description> <properties> <java.version>17</java.version> <kotlin.version>1.7.10</kotlin.version> </properties> <dependencies> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-reflect</artifactId> </dependency> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-stdlib-jdk8</artifactId> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-jpa</artifactId> <version>5.0.0</version> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <version>5.0.0</version> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory> <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-maven-plugin</artifactId> <configuration> <args> <arg>-Xjsr305=strict</arg> </args> <compilerPlugins> <plugin>spring</plugin> <plugin>jpa</plugin> </compilerPlugins> </configuration> <dependencies> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-maven-allopen</artifactId> <version>${kotlin.version}</version> </dependency> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-maven-noarg</artifactId> <version>${kotlin.version}</version> </dependency> </dependencies> </plugin> <plugin> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-maven-plugin</artifactId> <configuration> <args> <arg>-Xjsr305=strict</arg> </args> <!--这个spring和jpa的插件必不可少,尤其是使用data class作entity的时候,不然首先是需要你在程序入口启动类上添加open属性,而且data class编译不会产生默认无参构造函数,即使加了no-args依赖也没用 --> <compilerPlugins> <plugin>spring</plugin> <plugin>jpa</plugin> </compilerPlugins> </configuration> <executions> <execution> <id>compile</id> <!-- 这个phase必不可少,这里的compile阶段是处理源码,--> <phase>process-sources</phase> <goals> <goal>compile</goal> </goals> </execution> <execution> <id>kapt</id> <!-- 这个phase必不可少,这里的阶段是产生Q类,--> <phase>generate-sources</phase> <goals> <goal>kapt</goal> </goals> <configuration> <sourceDirs> <sourceDir>src/main/kotlin</sourceDir> </sourceDirs> <annotationProcessorPaths> <annotationProcessorPath> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <version>${querydsl.version}</version> <!--这个jpa也比不可少 --> <classifier>jpa</classifier> </annotationProcessorPath> </annotationProcessorPaths> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-maven-allopen</artifactId> <version>${kotlin.version}</version> </dependency> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-maven-noarg</artifactId> <version>${kotlin.version}</version> </dependency> </dependencies> </plugin> </plugins> </build> </project>

接下来我们做一些环境配置

做好数据配置、jpa配置

Copy
spring: mvc: pathmatch: matching-strategy: ant_path_matcher datasource: password: 123456 url: jdbc:postgresql://localhost:5432/daily username: postgres jpa: hibernate: ddl-auto: update show-sql: true properties: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect format_sql: true logging: level: org.hibernate.type.descriptor.sql.BasicBinder: trace

项目开发

项目整体结构如下

openapi的配置bean#

我们先要做一下openapi的配置,创建一个config类

Entity#

我们再创建一个实体类 User

Repository#

因为我们是需要用到Dsl进行查询所以需要我们的Repository接口实现QuerydslPredicateExecutor接口

Service#

这里注入EntityManager对象是因为我们后面要通过JPAQueryFactory直接进行查询,而不通过Repository的接口

Controller#

我们这里先定义个UserController

QueryDSL对象#

我们想要在代码中使用到QueryDSL对象,首先需要先编译下对象。它跟lombok一样会在编译期给我们生成一些对象,所以我们想要用到这些对象及方法需要先编译下项目。
它会在target目录下为我们生成这么一个类

通过Repository对象查询#

我们先构建这么一个场景,前端传过来条件查询,然后我们通过Usr对象接受查询条件,对于非空字段进行条件筛选,其他的username跟nickname为模糊匹配,sex为精准匹配。

我们首先需要通过生成的QUser类来创建一个对象。然后根据查询条件构建查询表达式,查询的是所有字段。params.isNotNull.or(params.isNull) 相当于我们平时查询经常用到的 where 1=1,然后再根据具体的需求构建表达式。最后调用repository.findAll()方法查询结果

再简单的在controller中开一个接口来测试一下

我们先看一下表中数据情况:

打开openapi然后调用接口:

可以看到我们已经成功根据我们的需求查询到了对应的结果

然后我们在后台看一下打印的sql语句,因为我们这里只有sex这个参数有值,所以为我们生成的sql也只有这个字段的筛选条件。

通过JPAQueryFactory查询#

我们可以Repository的查询接口在其中传入查询条件这样的方式来查询,但是这种方式查询的是所有字段并且表也是Repository所对应的表,如果我们想更自由的查询可以通过JPAQueryFactory来进行。
我们首先构建一个JPAQueryFactory对象跟QUser对象,这样我们就可以指定查询的字段跟表进行查询。但是他查询的结果是一个Tuple对象我们要进行map一下变成我们需要的User对象

简单的创建接口测试一下

可以看到只返回了部分字段的结果

再查看一下后台的sql语句,确实只用到了部分的字段进行查询

后记

其实相较于JPA来说我更喜欢MyBatisPlus,究其原因是因为它既可以像JPA那样为我们提供了开箱即用的简单查询方法,又可以方便我们自己写SQL。但是SpringData为我们抽象了一层统一的api,不仅是对于关系型数据库mysql、postgre等,还有非关系型数据库如redis、mogodb、elasticsearch等。都提供了很好的支持,所有具体用什么看具体的项目。

最后吐槽一下:对于kotlin来说JPA查询时候返回的Optional太鸡肋了,kotlin可以用?来直接判空,所以我们可为Repository接口添加扩展方法

Copy
fun<T,ID : Any> CrudRepository<T, ID>.findUserById(id:ID):T? = findById(id).orElse(null)

这样我们在调用的时候就舒服多了

posted @   loveletters  阅读(505)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示
目录