Chapter 4: Using QueryDSL and SpringDataJPA to implement multi-table association query

For the business logic replication system, there is a case of multi-table association query. The content of the returned object of the query is also processed according to the specific business. In this chapter, we mainly focus on the multi-table association and return the single-table object after querying according to the conditions. In the next chapter We will return custom object entities for multi-table queries.

Objectives of this chapter

Based on the SpringBoot framework platform, the SpringDataJPA and QueryDSL multi-table associated query returns a single-table object instance, and the query is completely written in QueryDSL syntax.

Build the project

We use the idea tool to create a SpringBoot project first, and the added dependencies are consistent with Chapter 3: Using QueryDSL and SpringDataJPA to complete Update&Delete . In order to facilitate the separation of the source code of the article, we copied the application.yml configuration file in Chapter 3 and the pom.xml dependencies into the project of this chapter after the creation (please refer to Chapter 3 for the configuration content).

Create data table

Let's first create two tables with a one-to-many relationship based on a simple business logic. Let's first create a product type information table. The code is as follows:

-- ----------------------------
-- Table structure for good_types
-- ----------------------------
DROP TABLE IF EXISTS `good_types`;
CREATE TABLE `good_types` (
  `tgt_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键自增',
  `tgt_name` varchar(30) CHARACTER SET utf8 DEFAULT NULL COMMENT '类型名称',
  `tgt_is_show` char(1) DEFAULT NULL COMMENT '是否显示',
  `tgt_order` int(2) DEFAULT NULL COMMENT '类型排序',
  PRIMARY KEY (`tgt_id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;

Next, let's create a basic product information table. The table structure is shown in the following code:

-- ----------------------------
-- Table structure for good_infos
-- ----------------------------
DROP TABLE IF EXISTS `good_infos`;
CREATE TABLE `good_infos` (
  `tg_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键自增',
  `tg_title` varchar(50) CHARACTER SET utf8 DEFAULT NULL COMMENT '商品标题',
  `tg_price` decimal(8,2) DEFAULT NULL COMMENT '商品单价',
  `tg_unit` varchar(20) CHARACTER SET utf8 DEFAULT NULL COMMENT '单位',
  `tg_order` varchar(255) DEFAULT NULL COMMENT '排序',
  `tg_type_id` int(11) DEFAULT NULL COMMENT '类型外键编号',
  PRIMARY KEY (`tg_id`),
  KEY `tg_type_id` (`tg_type_id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;

Create entity

We create two entities corresponding to the structure of the above two tables and add the corresponding SpringDataJPA annotation configuration. The commodity type entity is as follows:

package com.yuqiyu.querydsl.sample.chapter4.bean;

import lombok.Data;

import javax.persistence.*;
import java.io.Serializable;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/9
 * Time:15:04
 * 码云:http://git.oschina.net/jnyqy
 * ========================
 */
@Entity
@Table(name = "good_types")
@Data
public class GoodTypeBean
    implements Serializable
{
    //主键
    @Id
    @GeneratedValue
    @Column(name = "tgt_id")
    private Long id;
    //类型名称
    @Column(name = "tgt_name")
    private String name;
    //是否显示
    @Column(name = "tgt_is_show")
    private int isShow;
    //排序
    @Column(name = "tgt_order")
    private int order;
}

The commodity basic information entity is as follows:

package com.yuqiyu.querydsl.sample.chapter4.bean;

import lombok.Data;

import javax.persistence.*;
import java.io.Serializable;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/9
 * Time:15:08
 * 码云:http://git.oschina.net/jnyqy
 * ========================
 */
@Entity
@Table(name = "good_infos")
@Data
public class GoodInfoBean
    implements Serializable
{
    //主键
    @Id
    @GeneratedValue
    @Column(name = "tg_id")
    private Long id;
    //商品标题
    @Column(name = "tg_title")
    private String title;
    //商品价格
    @Column(name = "tg_price")
    private double price;
    //商品单位
    @Column(name = "tg_unit")
    private String unit;
    //商品排序
    @Column(name = "tg_order")
    private int order;
    //类型外键
    @Column(name = "tg_type_id")
    private Long typeId;
}

In the commodity table, I did not use the entity of type as the association between the tables, but only the specific type number. Sometimes it is configured according to your needs. If you need to read the basic information of each commodity To get the type of product, it is relatively easy to configure @OneToOne here, and you do not need to operate redundant queries.

Build a QueryDSL query entity

Next, we use the maven compile command to automatically generate the query entity of QueryDSL. When we execute the command, we will automatically look for the JPAAnnotationProcessor plugin in the pom.xml configuration file. If your entity is configured with the @Entity annotation, the query entity will be automatically generated And place the generated entity into target/generated-sources/java.
We find the Maven Projects window of the idea tool, as shown in Figure 1 below:

figure 1
We can execute the construction project by double-clicking the corresponding command. The completed query entity is shown in Figure 2 below:
figure 2
As shown in Figure 2 above, QueryDSL will be created according to the entity's package when it is generated.

Create a controller

Next, let's create a controller. We directly write the QueryDSL query code in the controller. We will not program according to the MVC mode here. In the formal environment, please code according to the MVC mode.
The controller code looks like this:

package com.yuqiyu.querydsl.sample.chapter4.controller;

import com.querydsl.jpa.impl.JPAQueryFactory;
import com.yuqiyu.querydsl.sample.chapter4.bean.GoodInfoBean;
import com.yuqiyu.querydsl.sample.chapter4.bean.QGoodInfoBean;
import com.yuqiyu.querydsl.sample.chapter4.bean.QGoodTypeBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import java.util.List;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/9
 * Time:15:24
 * 码云:http://git.oschina.net/jnyqy
 * ========================
 */
@RestController
public class GoodController
{

    @Autowired
    private EntityManager entityManager;

    //查询工厂实体
    private JPAQueryFactory queryFactory;

    //实例化控制器完成后执行该方法实例化JPAQueryFactory
    @PostConstruct
    public void initFactory()
    {
        System.out.println("开始实例化JPAQueryFactory");
        queryFactory = new JPAQueryFactory(entityManager);
    }

    @RequestMapping(value = "/selectByType")
    public List<GoodInfoBean> selectByType
            (
                    @RequestParam(value = "typeId") Long typeId //类型编号
            )
    {
        //商品查询实体
        QGoodInfoBean _Q_good = QGoodInfoBean.goodInfoBean;
        //商品类型查询实体
        QGoodTypeBean _Q_good_type = QGoodTypeBean.goodTypeBean;
        return
                queryFactory
                .select(_Q_good)
                .from(_Q_good,_Q_good_type)
                .where(
                        //为两个实体关联查询
                        _Q_good.typeId.eq(_Q_good_type.id)
                        .and(
                                //查询指定typeid的商品
                                _Q_good_type.id.eq(typeId)
                        )
                )
                //根据排序字段倒序
                .orderBy(_Q_good.order.desc())
                //执行查询
                .fetch();
    }
}

As you can see in the above code, we have queried two tables, and only returned the fields in the product information (select(_Q_good)). We have associated these two tables in the where condition, using the type number passed as the association Commodity type primary key (equivalent to left join), and finally reverse order according to the sorting field.

run the test

Let's run the project, the console log output is as follows:

.....

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.4.RELEASE)

.....
开始实例化JPAQueryFactory
2017-07-09 15:40:38.454  INFO 11776 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1184ab05: startup date [Sun Jul 09 15:40:36 CST 2017]; root of context hierarchy
2017-07-09 15:40:38.495  INFO 11776 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/selectByType]}" onto public java.util.List<com.yuqiyu.querydsl.sample.chapter4.bean.GoodInfoBean> com.yuqiyu.querydsl.sample.chapter4.controller.GoodController.selectByType(java.lang.Long)
2017-07-09 15:40:38.498  INFO 11776 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2017-07-09 15:40:38.498  INFO 11776 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2017-07-09 15:40:38.515  INFO 11776 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-07-09 15:40:38.515  INFO 11776 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-07-09 15:40:38.536  INFO 11776 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-07-09 15:40:38.721  INFO 11776 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-07-09 15:40:38.723  INFO 11776 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'dataSource' has been autodetected for JMX exposure
2017-07-09 15:40:38.726  INFO 11776 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located MBean 'dataSource': registering with JMX server as MBean [com.alibaba.druid.pool:name=dataSource,type=DruidDataSource]
2017-07-09 15:40:38.765  INFO 11776 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2017-07-09 15:40:38.769  INFO 11776 --- [           main] c.y.q.s.chapter4.Chapter4Application     : Started Chapter4Application in 3.027 seconds (JVM running for 3.683)

It can be seen that the JPAQueryFactory query factory object is instantiated when the project starts, and then we directly use the JPAQueryFactory instance object and it is OK. Let's visit: http://127.0.0.1:8080/selectByType?typeId=1
The output content of the interface is shown in Figure 3 below:

image 3
Let's take a look at the SQL output from the console, as follows:

Hibernate: 
    select
        goodinfobe0_.tg_id as tg_id1_0_,
        goodinfobe0_.tg_order as tg_order2_0_,
        goodinfobe0_.tg_price as tg_price3_0_,
        goodinfobe0_.tg_title as tg_title4_0_,
        goodinfobe0_.tg_type_id as tg_type_5_0_,
        goodinfobe0_.tg_unit as tg_unit6_0_ 
    from
        good_infos goodinfobe0_ cross 
    join
        good_types goodtypebe1_ 
    where
        goodinfobe0_.tg_type_id=goodtypebe1_.tgt_id 
        and goodtypebe1_.tgt_id=? 
    order by
        goodinfobe0_.tg_order desc

The SQL automatically generated by QueryDSL uses Cross Join to obtain the "Cartesian set" of the two tables and then returns the fields according to the entities configured in the select. We use where goodinfobe0_.tg_type_id=goodtypebe1_.tgt_id instead of on goodinfobe0_.tg_type_id=goodtypebe1_.tgt_id achieves the same effect.

Summarize

The content of this chapter is relatively simple. We use QueryDSL to complete two entity related queries and return a single entity instance method. There are also LeftJoin, InnerJoin and other related queries in QueryDSL, but they are all based on specific entity types, so this chapter will not explain them. , which are more complicated and complicated to use. They follow the HQL syntax.

The 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 Catalogue
QueryDSL related series of articles, please visit: QueryDSL General Query Framework Learning Catalog
Spring DataJPA-related series of articles, please visit: Catalog: Spring DataJPA 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=324736183&siteId=291194637