Chapter 2: Using QueryDSL and SpringDataJPA to implement single-table common conditional query

There are many kinds of ORM frameworks in enterprise development, such as: Hibernate, Mybatis, JdbcTemplate, etc. The design concept of each framework is different. Hibernate is the same framework as the Spring Data JPA we explained in this chapter. Both the fully automatic concept is the core of the design, allowing users to write less SQL statements and realize various Inquire. The Mybatis framework is based on the semi-automatic concept as the design core, and SQL allows users to define their own to achieve better flexibility.

Objectives of this chapter

In this chapter, our goal is to implement the QueryDSL general query language to integrate Spring Data JPA to complete the query diversification of a single table.

Build the project

Let's first create a SpringBoot project. For details on how to use Maven to integrate QueryDSL, please visit Chapter 1 of the QueryDSL Learning Catalog . The dependencies when creating a project are also the same as those in Chapter 1. The pom.xml configuration file is shown in the following code block:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.yuqiyu.querydsl.sample</groupId>
    <artifactId>chapter2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>chapter2</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--阿里巴巴数据库连接池,专为监控而生 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.26</version>
        </dependency>
        <!-- 阿里巴巴fastjson,解析json视图 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.15</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <!--<scope>provided</scope>-->
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--queryDSL-->
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
            <version>${querydsl.version}</version>
        </dependency>
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-apt</artifactId>
            <version>${querydsl.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.16</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <!--添加QueryDSL插件支持-->
            <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>
    </build>
</project>

If you do not understand the configuration of the code block in the above figure, please refer to Chapter 1: How to configure the QueryDSL environment in the Maven environment .

Build database information table

Let's create a common information table first. The SQL for creating the table is as follows:

DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `t_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
  `t_name` varchar(30) DEFAULT NULL COMMENT '名称',
  `t_age` int(10) DEFAULT NULL COMMENT '年龄',
  `t_address` varchar(100) DEFAULT NULL COMMENT '家庭住址',
  `t_pwd` varchar(100) CHARACTER SET latin1 DEFAULT NULL COMMENT '用户登录密码',
  PRIMARY KEY (`t_id`)
) ENGINE=MyISAM AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;

Test data is not added here.

Add application.yml configuration file

In the resource directory, we add the application.yml configuration file instead of the application.properties file, and add the configuration information of the corresponding database connection pool. The code is as follows:

spring:
  datasource:
      type: com.alibaba.druid.pool.DruidDataSource
      url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8
      username: root
      password: root
      driver-class-name: com.mysql.jdbc.Driver
      filters: stat
      maxActive: 20
      initialSize: 1
      maxWait: 60000
      minIdle: 1
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: select 'x'
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      maxOpenPreparedStatements: 20
  jpa:
    properties:
      hibernate:
        show_sql: true
        format_sql: true

Create entity

We create an entity class based on the corresponding fields in the database and add the corresponding SpringDataJPA annotations. The entity class code is as follows:

package com.yuqiyu.querydsl.sample.chapter2.bean;
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/2
 * Time:18:31
 * 码云:http://git.oschina.net/jnyqy
 * ========================
 */
@Data
@Entity
@Table(name = "t_user")
public class UserBean implements Serializable
{
    @Id
    @Column(name = "t_id")
    @GeneratedValue
    private Long id;
    @Column(name = "t_name")
    private String name;
    @Column(name = "t_age")
    private int age;
    @Column(name = "t_address")
    private String address;
    @Column(name = "t_pwd")
    private String pwd;
}

There is a special annotation @Data in the entity. Maybe you have not used it before. Of course, you must have found that there is no Getter/Setter method for the corresponding field in my entity class. If the @Data annotation is not added, it will appear when SpringDataJPA maps data. The Setter method of the corresponding field could not be found, resulting in the exception that the data mapping to the entity could not be completed!
In the above entity source code, you can see that the @Data annotation is in the lombok package. Lombok is actually an elegant third-party plug-in, which can make your entity concise and greatly improve the readability. When using this plug-in, you need the support of your Idea development tools. You must install the corresponding Plugin. I will not talk about the configuration of lombok here. You can use the form of Getter/Setter methods when contacting this chapter. in place of @Data.

Create base class JPA

Here we simply encapsulate JPA. We add an interface to inherit the JPA interface we need and let all subclasses inherit our base class interface. The base class JPA code is as follows:

package com.yuqiyu.querydsl.sample.chapter2.jpa;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;

/**
 * 核心JPA
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/2
 * Time:18:23
 * 码云:http://git.oschina.net/jnyqy
 * ========================
 */
@NoRepositoryBean
public interface BaseJPA<T>
        extends JpaRepository<T,Long>,
        JpaSpecificationExecutor<T>,
        QueryDslPredicateExecutor<T>
{
}

The above annotation @NoRepositoryBean is added to avoid automatic instantiation by SpringDataJPA.

Create logical JPA

Next, we start to create the data logic interface JPA corresponding to the User module. It is very simple. We only need to create an interface to inherit our BaseJPA. The code is as follows:

package com.yuqiyu.querydsl.sample.chapter2.jpa;
import com.yuqiyu.querydsl.sample.chapter2.bean.UserBean;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/2
 * Time:18:39
 * 码云:http://git.oschina.net/jnyqy
 * ========================
 */
public interface UserJPA
        extends BaseJPA<UserBean>
{
  //../可以添加命名方法查询
}

We use generics when inheriting BaseJPA, because the interfaces we inherit in BaseJPA all require us to pass a specific entity class type, so we use generics to deal with this, only the specific logic JPA inherits BaseJPA's When passing a specific entity type on it.

Automatically generate Q-structure query entities

We said before that the magic of QueryDSL is that it is a structural query entity that can automatically generate entity types through the Maven plugin. Then we use the maven compile command to let the JPAAnnotationProcessor we configured do its own mission.
The idea tool comes with a relatively comprehensive command for our maven project, we can use it directly, as shown in Figure 1 below:
figure 1
Double-click the command in the red box to execute it. Before that, if you want to use the local maven environment The home dirctory needs to be modified on the Setting->Build Tools->maven page. After the command is executed, we can see that the target directory is automatically generated and some directories are created for us. After expanding the directory, we can see the query entity automatically generated by QueryDSL for us, as shown in Figure 2 below: The
figure 2
maven plugin will automatically create a query entity for us. Heap directory, the location of our query entity is based on the directory configured in our pom.xml configuration file. After opening the automatically created entity, you can see the query fields and constructors that QueryDSL automatically created for us. The meaning of the specific query fields will be explained later.

write query

Let's start writing the query. Before writing, we create a controller to view the data we query. The code is as follows:

package com.yuqiyu.querydsl.sample.chapter2.controller;
import com.yuqiyu.querydsl.sample.chapter2.jpa.UserJPA;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/2
 * Time:18:38
 * 码云:http://git.oschina.net/jnyqy
 * ========================
 */
@RestController
@RequestMapping(value = "/user")
public class UserController
{
    @Autowired
    private UserJPA userJPA;

}

Here I did not pursue the design concept of MVC three layers, because this is only the test sample writing of the article, everyone still needs to design the architecture according to the MVC design pattern in the actual development project.

Query all data and sort

Let's start with a simple query of all the data in the table and reverse the order according to the primary key. The code is as follows:

@RestController
@RequestMapping(value = "/user")
public class UserController
{
    @Autowired
    private UserJPA usrJPA;

    //----以下是新添加内容

    //实体管理者
    @Autowired
    private EntityManager entityManager;

    //JPA查询工厂
    private JPAQueryFactory queryFactory;

    @PostConstruct
    public void initFactory()
    {
        queryFactory = new JPAQueryFactory(entityManager);
        System.out.println("init JPAQueryFactory successfully");
    }

    /**
     * 查询全部数据并根据id倒序
     * @return
     */
    @RequestMapping(value = "/queryAll")
    public List<UserBean> queryAll()
    {
        //使用querydsl查询
        QUserBean _Q_user = QUserBean.userBean;
        //查询并返回结果集
        return queryFactory
                .selectFrom(_Q_user)//查询源
                .orderBy(_Q_user.id.desc())//根据id倒序
                .fetch();//执行查询并获取结果集
    }

Before using QueryDSL to query, we declare the injection of EntityManager and the creation of the JPAQueryFactory factory object, and the instantiation of the JPAQueryFactory object is completed when the class is initialized through the @PostConstruct annotation.

We first obtained the query entity QUserBean corresponding to UserBean in the queryAll method, and obtained it through the fields automatically generated in QUserBean. We used the selectFrom method of the JPAQueryFactory factory object to simplify the query, which replaced the two methods of select&from. Note: it is also limited to It can be used for single table operation.

The way we reverse the order looks simpler. This implementation is exactly like writing raw SQL. If it is sorted according to the asc method, it can be modified as: orderBy(_Q_user.id.asc()) , doesn't it seem very simple?

After a series of conditions are added, call the fetch method to execute our conditional query and obtain the type collection of the corresponding selectFrom query entity. Note that if the entity type of the selectFrom parameter is not UserBean, the type of the collection returned by the fetch method is not List.

Note: Before we start the project, we need to modify the pom.xml configuration file to add the dependency of the relevant inject, which is not provided for us by SpringBoot.

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

After adding this dependency, the instantiation of JPAQueryFactory can be completed. Let's start the project access address and see the output content of the interface as shown in Figure 3 below:
image 3

You can see that the output content data in Figure 3 above is completely executed according to our query conditions. We open the console to see the SQL that SpringDataJPA automatically generates for us.

Hibernate: 
    select
        userbean0_.t_id as t_id1_0_,
        userbean0_.t_address as t_addres2_0_,
        userbean0_.t_age as t_age3_0_,
        userbean0_.t_name as t_name4_0_,
        userbean0_.t_pwd as t_pwd5_0_ 
    from
        t_user userbean0_ 
    order by
        userbean0_.t_id desc

It can be seen that Hibernate is output on the SQL, and StringDataJPA uses Hibernate to generate SQL, so we can completely use the HQL query language to write JPA queries.

Query a single piece of data based on the primary key

The query details method is one of the commonly used queries, which is generally used for deletion and update. Next, let's write a detail method to see how QueryDSL completes the query of singled-out data.

Full QueryDSL style

The code looks like this:

 /**
     * 查询详情
     * @param id 主键编号
     * @return
     */
    @RequestMapping(value = "/detail/{id}")
    public UserBean detail(@PathVariable("id") Long id)
    {
        //使用querydsl查询
        QUserBean _Q_user = QUserBean.userBean;
        //查询并返回结果集
        return queryFactory
                .selectFrom(_Q_user)//查询源
                .where(_Q_user.id.eq(id))//指定查询具体id的数据
                .fetchOne();//执行查询并返回单个结果
    }

SpringDataJPA integrates QueryDSL style

The code looks like this:

    /**
     * SpringDataJPA & QueryDSL实现单数据查询
     * @param id
     * @return
     */
    @RequestMapping(value = "/detail_2/{id}")
    public UserBean detail_2(@PathVariable("id") Long id)
    {
        //使用querydsl查询
        QUserBean _Q_user = QUserBean.userBean;
        //查询并返回指定id的单条数据
        return userJPA.findOne(_Q_user.id.eq(id));
    }

The above two styles can return the specified single data according to the id field. Of course, in the above writing, it is simpler to use SpringDataJPA & QueryDSL, but the simple query integration style is easier than pure QueryDSL, but if you add It is simpler to write pure QueryDSL when sorting and fuzzy query.

When querying the specified primary key, we use the where method and specify that the id field requires the eq parameter id. This eq is a built-in method of QueryDSL for querying the specified value data. Of course, other fields can also use the eq method to complete conditional queries. All are adaptable.

After restarting the project, let's take a look at the running effect of the query details, as shown in Figure 4 below:
Figure 4

Let's take a look at the generated SQL output from the console, as shown in the following code:

Hibernate: 
    select
        userbean0_.t_id as t_id1_0_,
        userbean0_.t_address as t_addres2_0_,
        userbean0_.t_age as t_age3_0_,
        userbean0_.t_name as t_name4_0_,
        userbean0_.t_pwd as t_pwd5_0_ 
    from
        t_user userbean0_ 
    where
        userbean0_.t_id=?

You can see that the data is retrieved according to the fields we specify as query conditions, and we return a result through the fetchOne method.

Fuzzy query by name

Let's complete the module query based on the field name. Let's first look at our query condition code as follows:

/**
     * 根据名称模糊查询
     * @param name
     * @return
     */
    @RequestMapping(value = "/likeQueryWithName")
    public List<UserBean> likeQueryWithName(String name)
    {
        //使用querydsl查询
        QUserBean _Q_user = QUserBean.userBean;

        return queryFactory
                .selectFrom(_Q_user)//查询源
                .where(_Q_user.name.like(name))//根据name模糊查询
                .fetch();//执行查询并返回结果集
    }

It can be seen that our where condition is a fuzzy query based on the like method of the name field. The like method is also a built-in method of QueryDSL. We only need to pass in the content of the query to realize the fuzzy query. Let's run the access to see the interface output. The content is shown in Figure 5 below:
Figure 5
We can see that only the data related to the name we passed in 'admin' is output, then let's see how the console generates the SQL for us:

Hibernate: 
    select
        userbean0_.t_id as t_id1_0_,
        userbean0_.t_address as t_addres2_0_,
        userbean0_.t_age as t_age3_0_,
        userbean0_.t_name as t_name4_0_,
        userbean0_.t_pwd as t_pwd5_0_ 
    from
        t_user userbean0_ 
    where
        userbean0_.t_name like ? escape '!'

It is also completely generated according to the fuzzy query fields we specified. So far, we can see that QueryDSL has reduced too many complicated queries for us, allowing us to better invest in business logic processing.

Summarize

The above content is the whole explanation of this chapter. This chapter mainly describes the use of QueryDSL to query a single table after SpringDataJPA integrates QueryDSL. Using QueryDSL, you can write query conditions completely in accordance with the idea of ​​​​writing original SQL, which is easy to use, convenient and fast. Due to the powerful functions of QueryDSL, there are many things that have not been explained. I hope you can get a summary in the later projects.

The source code of this chapter has been uploaded to the code cloud:
SpringBoot supporting source code address: https://gitee.com/hengboy/spring-boot-chapter
SpringCloud supporting source code address: https://gitee.com/hengboy/spring-cloud-chapter
SpringBoot related For the series of articles, please visit: Catalog: SpringBoot Learning Catalog
For QueryDSL related articles, please visit: QueryDSL General Query Framework Learning Catalog
For Spring DataJPA related articles, please visit: Catalog: Spring Data JPA Learning Catalog
Thank you for reading!
Welcome to join the QQ technical exchange group and make progress together.
QQ technical exchange group

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324736224&siteId=291194637