Spring Boot (VI): plug in the wings for the JPA QueryDSL

In the previous article, we introduced the use JPA basis, "the Spring the Boot (three): ORM framework with connection pooling JPA Hikari" , this article, we advanced to the entry for the JPA to introduce the wings QueryDSL.

1 Introduction

It is undeniable that JPA is very easy to use, extremely simplified configuration, only need to use annotations, without any xml configuration file, semantic easy to understand, but, above all are built on the premise of single-table queries, we You can use JPA methods provided by default, simply add easily complete CRUD operations.

But if it comes to multi-table dynamic queries, JPA function looks a little stretched, although we can use annotation @Querystring readability, both in writing SQL or HQL string concatenation in this comment, and the stitching is very the difference, of course, JPA also provides us Specificationto do it, using the experience of speaking from my personal, readability, though not bad, but when beginners to get started, Predicateand CriteriaBuilderuse is estimated to discouraging many people, and If the direct execution of SQL queries even the table, get a Object[], what type? What column names? These are not intuitive to get, we need to manually Object[]mapped to the needs of our Modelclass to go inside, this experience is undoubtedly very bad.

All this in the end after QueryDSL born, QueryDSL SQL syntax is very similar to code readability is very strong, abnormal Introduction beautiful ,, and highly integrated with JPA, without any extra configuration, use the speaking from my personal experience is great of. It can be said, as long as the write SQL, basically just look at the sample code can achieve the level of entry.

2. QueryDSL Profile

QueryDSL is a very active open source project, the current Release version published on Github has as many as 251 version, the latest version is 4.2.1, and supported by a group Querydsl Google and StackOverflow two teams.

QueryDSL is a framework that can be used in a SQL-like structure statically typed queries. API can be constructed, such as QueryDSL like query instead of writing the query as inline string or to an external XML file.

For example, compared to a simple string, the benefits of using the API is

  • Code completion IDE

  • Almost no query syntax is invalid

  • Reference field types and attributes can safely

  • Better reconstruct the domain type of change

3. QueryDSL actual use

3.1 introduces Maven relies

Listing: the Spring-the Boot-JPA-querydsl / pom.xml
***

<!--QueryDSL支持-->
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <scope>provided</scope>
</dependency>
<!--QueryDSL支持-->
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
</dependency>
  • Here without specifying the version number has been spring-boot-dependenciesdefined project.

Add Maven plugin 3.2

Add this plug-in is to allow the program to automatically generate a query type (query entity, named as: "Q" + corresponds to an entity name).
Dependent incorporated above querydsl-apt for this plug-in that is services.

NOTE: During use, if the situation encountered query type can not be automatically generated by maven update about the project can be solved (Right Project -> Maven -> Update Folders).

Listing: the Spring-the Boot-JPA-querydsl / pom.xml
***

<plugins>
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    <plugin>
        <groupId>com.mysema.maven</groupId>
        <artifactId>apt-maven-plugin</artifactId>
        <version>1.1.3</version>
        <executions>
            <execution>
                <goals>
                    <goal>process</goal>
                </goals>
                <configuration>
                    <outputDirectory>target/generated-sources/java</outputDirectory>
                    <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
                </configuration>
            </execution>
        </executions>
    </plugin>
</plugins>

3.3 update, and delete

In JPA it has provided us with a very easy to use way to update and delete, we have no need to use update and delete QueryDSL, but here are a usage for your reference:

代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/UserServiceImpl.java
***

@Override
public Long update(String id, String nickName) {
    QUserModel userModel = QUserModel.userModel;
    // 更新
    return queryFactory.update(userModel).set(userModel.nickName, nickName).where(userModel.id.eq(id)).execute();
}

@Override
public Long delete(String id) {
    QUserModel userModel = QUserModel.userModel;
    // 删除
    return queryFactory.delete(userModel).where(userModel.id.eq(id)).execute();
}

3.2 Queries

QueryDSL query in this regard can be said to play a very spent, such as some relevant select()and fetch()wording used is as follows:

代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/UserServiceImpl.java
***

@Override
public List<String> selectAllNameList() {
    QUserModel userModel = QUserModel.userModel;
    // 查询字段
    return queryFactory.select(userModel.nickName).from(userModel).fetch();
}

@Override
public List<UserModel> selectAllUserModelList() {
    QUserModel userModel = QUserModel.userModel;
    // 查询实体
    return queryFactory.selectFrom(userModel).fetch();
}

@Override
public List<UserDTO> selectAllUserDTOList() {
    QUserModel userModel = QUserModel.userModel;
    QLessonModel lessonModel = QLessonModel.lessonModel;
    // 连表查询实体并将结果封装至DTO
    return queryFactory
            .select(
                    Projections.bean(UserDTO.class, userModel.nickName, userModel.age, lessonModel.startDate, lessonModel.address, lessonModel.name)
            )
            .from(userModel)
            .leftJoin(lessonModel)
            .on(userModel.id.eq(lessonModel.userId))
            .fetch();
}

@Override
public List<String> selectDistinctNameList() {
    QUserModel userModel = QUserModel.userModel;
    // 去重查询
    return queryFactory.selectDistinct(userModel.nickName).from(userModel).fetch();
}

@Override
public UserModel selectFirstUser() {
    QUserModel userModel = QUserModel.userModel;
    // 查询首个实体
    return queryFactory.selectFrom(userModel).fetchFirst();
}

@Override
public UserModel selectUser(String id) {
    QUserModel userModel = QUserModel.userModel;
    // 查询单个实体,如果结果有多个,会抛`NonUniqueResultException`。
    return queryFactory.selectFrom(userModel).fetchOne();
}

3.4 complex query operations

Listed above a simple query, but actually we encounter quite complex operations, such as sub-queries, multi-criteria query, multi-table even check, using the example below:

代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/LessonServiceImpl.java
***

@Service
public class LessonServiceImpl implements LessonService {

    @Autowired
    JPAQueryFactory queryFactory;

    @Override
    public List<LessonModel> findLessonList(String name, Date startDate, String address, String userId) throws ParseException {
        QLessonModel lessonModel = QLessonModel.lessonModel;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        // 多条件查询示例
        return queryFactory.selectFrom(lessonModel)
                .where(
                        lessonModel.name.like("%" + name + "%")
                        .and(lessonModel.address.contains(address))
                        .and(lessonModel.userId.eq(userId))
                        .and(lessonModel.startDate.between(simpleDateFormat.parse("2018-12-31 00:00:00"), new Date()))
                )
                .fetch();
    }

    @Override
    public List<LessonModel> findLessonDynaList(String name, Date startDate, String address, String userId) throws ParseException {
        QLessonModel lessonModel = QLessonModel.lessonModel;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

        // 动态查询示例
        BooleanBuilder builder = new BooleanBuilder();

        if (!StringUtils.isEmpty(name)){
            builder.and(lessonModel.name.like("%" + name + "%"));
        }

        if (startDate != null) {
            builder.and(lessonModel.startDate.between(simpleDateFormat.parse("2018-12-31 00:00:00"), new Date()));
        }

        if (!StringUtils.isEmpty(address)) {
            builder.and(lessonModel.address.contains(address));
        }

        if (!StringUtils.isEmpty(userId)) {
            builder.and(lessonModel.userId.eq(userId));
        }

        return queryFactory.selectFrom(lessonModel).where(builder).fetch();
    }

    @Override
    public List<LessonModel> findLessonSubqueryList(String name, String address) {
        QLessonModel lessonModel = QLessonModel.lessonModel;
        // 子查询示例,并无实际意义
        return queryFactory.selectFrom(lessonModel)
                .where(lessonModel.name.in(
                        JPAExpressions
                                .select(lessonModel.name)
                                .from(lessonModel)
                                .where(lessonModel.address.eq(address))
                ))
                .fetch();
    }
}

3.5 Mysql aggregate function

QueryDSL has built some common Mysql aggregate functions, if they do not provide an aggregate function QueryDSL need not panic, QueryDSL provides us with Expressionsthis class, we can use this class like a manual splicing, the following example:

代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/UserServiceImpl.java
***

@Override
public String mysqlFuncDemo(String id, String nickName, int age) {

    QUserModel userModel = QUserModel.userModel;

    // Mysql 聚合函数示例

    // 聚合函数-avg()
    Double averageAge = queryFactory.select(userModel.age.avg()).from(userModel).fetchOne();

    // 聚合函数-sum()
    Integer sumAge = queryFactory.select(userModel.age.sum()).from(userModel).fetchOne();

    // 聚合函数-concat()
    String concat = queryFactory.select(userModel.nickName.concat(nickName)).from(userModel).fetchOne();

    // 聚合函数-contains()
    Boolean contains = queryFactory.select(userModel.nickName.contains(nickName)).from(userModel).where(userModel.id.eq(id)).fetchOne();

    // 聚合函数-DATE_FORMAT()
    String date = queryFactory.select(Expressions.stringTemplate("DATE_FORMAT({0},'%Y-%m-%d')", userModel.createDate)).from(userModel).fetchOne();

    return null;
}

4. Summary

Introduction to QueryDSL to the end here, I do not know you readers read the above example, there is not a direct read SQL feeling, and this is the idea of ​​using SQL OOM will be given Hibernate had not done a thing a rather perfect solution, simple to use and easy to operate, but without writing SQL, we actually operate or object classes.

5. Sample Code

Sample Code -Github

Sample Code -Gitee

6. References

"QueryDSL official documents"

Guess you like

Origin www.cnblogs.com/babycomeon/p/11605809.html