Rest-apiV2.0.0 is upgraded to the release of simplest-jpa, an open source framework ecology of simplest-api

what is simplest

simplest pursues simplicity and perfection.

It aims to provide a series of basic capabilities for the rapid development of projects, so that users can quickly expand functions according to project needs

Don't care about some cumbersome things anymore. Duplicate work, but focus on business.

foreword

Program 10 years. as a multi-year program. Knowing that every project and program, there is a lot of repetitive work to do.
I have been in the industry for nearly 10 years, I have written many programs, and I have written many programs to death. . . . .

Witness the golden age of the Internet to the present sluggishness. Fortunately, I am still in the army of programmers. Fight with you!

My story <<Programmer Three Hours>> The official account is looking forward to communicating with you. I hope to give you some inspiration and help when you are confused.

related warehouse

project Introduction gitee address github address
simplest-api The front-end and back-end separation project is based on the simplest-api, which can quickly build a unified communication interaction based on the Web Json API format https://gitee.com/daTouY/simplest-api/tree/main/ https://github.com/coder-amiao/simplest-api
simplest-jpa Based on the flexible and powerful QueryWrapper of QueryDSL syntax, the chained QueryChain is powerful and free to combine queryers, and queries data based on Java API like writing native SQL, which is extremely elegant. https://gitee.com/daTouY/simplest-jpa https://github.com/coder-amiao/simplest-jpa
simplest-boot Business common ecological core components https://gitee.com/daTouY/simplest-boot.git https://github.com/coder-amiao/simplest-boot
simplest-admin Based on the RABC permission model, it is rich in functions and the latest technology stack Vue3.3 + Vite4 + TS + Pinia + Element-Plus management background scaffolding. Out-of-the-box, customizable code generation templates allow you to focus on business development. https://gitee.com/daTouY/simplest-admin.git https://github.com/coder-amiao/simplest-admin

Simplestdevelopment documentation

Here we mainly introduce simplest-jpathe use of

quick start

Introduce dependencies in project pom

<dependency>
    <groupId>cn.soboys</groupId>
    <artifactId>simplest-jpa-spring-boot-starter</artifactId>
    <version>1.0.1</version>
</dependency>

<!-- MySQL -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>

Enable simplest-jpa via the @EnableJPAQuery annotation on the SpringBoot startup class or configuration class

@SpringBootApplication
@EnableJPAQuery
public class SpringbootJpaApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(SpringbootJpaApplication.class, args);
    }

}

At this point you can use all the functions in your project.

database configuration

Configure the corresponding database connection in the project

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/rest-admin?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&serverTimezone=Asia/Shanghai
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      minimum-idle: 10
      maximum-pool-size: 20
      idle-timeout: 600000
      max-life-time: 1800000

  jpa:
    hibernate:
      naming:
        implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
      ddl-auto: update # 控制是否可以基于程序中Entity的定义自动创建或者修改DB中表结构
    show-sql: true #控制是否打印运行时的SQL语句与参数信息
    database-platform: org.hibernate.dialect.MySQLDialect #数据库方言
    open-in-view: true
    properties:
      hibernate:
        enable_lazy_load_no_trans: true

Define the corresponding entity corresponding to the database table. JPA will automatically generate the database for you.

package cn.soboys.springbootjpa.entity;

import cn.soboys.springbootjpa.entity.base.BaseEntity;
import cn.soboys.springbootjpa.entity.dto.TitleVo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

/**
 * @author 公众号 程序员三时
 * @version 1.0
 * @date 2023/7/19 10:44
 * @webSite https://github.com/coder-amiao
 * 内容分类
 */
@Data
@Entity
@Table(name = "cms_category")
public class Category extends BaseEntity {
    
    

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;


    /**
     * 标题
     */
    @Column(nullable = false, length = 64)
    @Schema(description = "标题")
    private String title;


    /**
     * @Embedded 用户映射数据库表到一个实体vo。
     */
    @Embedded
    @Schema(description = "标题信息")
    private TitleVo titleVo;

    /**
     * 描述
     */
    @Schema(description = "描述")
    private String described;

    /**
     * 图标
     */
    @Column( length = 32)
    @Schema(description = "图标",maxLength = 32)
    private String icon;

    /**
     * 图片
     */
    @Column( length = 32)
    @Schema(description = "图片",maxLength = 32)
    private String pic;

    /***
     * 引用关系不填写。默认对应主键。
     */
    @ManyToMany(cascade = {
    
    CascadeType.PERSIST, CascadeType.MERGE},fetch = FetchType.LAZY)
    @JoinTable(name = "cms_module_relation",
            joinColumns = @JoinColumn(name = "resource_id"),
            inverseJoinColumns = @JoinColumn(name = "module_id"))
    private Set<Module> modules=new HashSet<>();


    /**
     * 额外其他属性
     * @Transient 解绑和数据联系。属于实体类属性
     */
    @Transient
    private String other;


}

Generate corresponding query EntityPath

The APT technology based on QueryDSL introduces the corresponding plug-in in pom.xml of maven

<plugin>
    <!--因为QueryDsl是类型安全的,所以还需要加上Maven APT plugin,使用 APT 自动生成Q类:-->
    <groupId>com.mysema.maven</groupId>
    <artifactId>apt-maven-plugin</artifactId>
    <version>1.1.3</version>
    <executions>
        <execution>
            <phase>generate-sources</phase>
            <goals>
                <goal>process</goal>
            </goals>
            <configuration>
                <outputDirectory>target/generated-sources/java</outputDirectory>
                <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
            </configuration>
        </execution>
    </executions>
</plugin>

Then compile the project via maven.

The corresponding query EntityPaht entity will be generated in the directory you specify

simple query

  1. Write your own Repositoryinherit genericBaseRepository
package cn.soboys.springbootjpa.repository;

import cn.soboys.simplestjpa.BaseRepository;
import cn.soboys.springbootjpa.entity.Category;
import org.springframework.stereotype.Repository;

/**
 * @author 公众号 程序员三时
 * @version 1.0
 * @date 2023/7/19 12:02
 * @webSite https://github.com/coder-amiao
 * 数据库 dao层。
 */
@Repository
public interface CategoryRepository extends BaseRepository<Category, Long{
    
    


}

  1. Write your own Serviceinherit common Servicelayer
package cn.soboys.springbootjpa.service;

import cn.soboys.simplestjpa.IService;
import cn.soboys.springbootjpa.entity.Category;
import org.springframework.data.jpa.repository.Query;

/**
 * @author 公众号 程序员三时
 * @version 1.0
 * @date 2023/7/19 17:08
 * @webSite https://github.com/coder-amiao
 */
public interface ICategoryService extends IService<Category,Long> {
    
    



}

Implementation class

package cn.soboys.springbootjpa.service.impl;

import cn.soboys.simplestjpa.ServiceImpl;
import cn.soboys.springbootjpa.entity.Category;
import cn.soboys.springbootjpa.repository.CategoryRepository;
import cn.soboys.springbootjpa.service.ICategoryService;
import org.springframework.stereotype.Service;

/**
 * @author 公众号 程序员三时
 * @version 1.0
 * @date 2023/7/20 14:46
 * @webSite https://github.com/coder-amiao
 */
@Service
public class CategoryServerImpl extends ServiceImpl<CategoryRepository, Category, Long> implements ICategoryService {
    
    


}

In this way, your service has the basic method of adding, deleting, modifying and checking all operational data

package cn.soboys.springbootjpa;

import cn.soboys.simplestjpa.UpdateWrapper;
import cn.soboys.springbootjpa.entity.Category;
import cn.soboys.springbootjpa.entity.QCategory;
import cn.soboys.springbootjpa.entity.dto.QTitleVo;
import cn.soboys.springbootjpa.service.ICategoryService;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Predicate;
import com.querydsl.jpa.impl.JPAUpdateClause;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.core.text.StrUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;


/**
 * @author 公众号 程序员三时
 * @version 1.0
 * @date 2023/7/26 21:54
 * @webSite https://github.com/coder-amiao
 */
@SpringBootTest
@Slf4j
public class ServiceTest {
    
    
    @Autowired
    private ICategoryService categoryService;

    @Test
    void countByExample() {
    
    
        Category category = new Category();
        //category.setTitle("测试");
        long count = categoryService.count(Example.of(category));
        log.info("条件count{}", count);
    }

    @Test
    void getById() {
    
    
        Optional<Category> category = categoryService.getByIdOpt(6l);
        if (category.isPresent()) {
    
    
            log.info(category.get().toString());
        }
    }

    @Test
    void getOne() {
    
    
        QCategory qCategory = QCategory.category;
        QTitleVo vo=QTitleVo.titleVo;
        Predicate query=vo.subTitle.eq("batch1");
        Category category = categoryService.getOne(query);
        log.info(category.toString());
    }

    @Test
    void getPageQuery() {
    
    
        QCategory qCategory = QCategory.category;
        PageRequest pageRequest = PageRequest.of(0, 20); //第一页从零开始
        Predicate query = qCategory.title.like("%" + "batch" + "%");
        Page<Category> categoryList = categoryService.page(pageRequest, query);
        log.info("数量{}", categoryList.getContent().size());
    }

    @Test
    void getPage() {
    
    
        QCategory qCategory = QCategory.category;

      // categoryService.getJPAQueryFactory().select().where(qCategory.)
    }


    @Test
    void save() {
    
    
        Category c = new Category();
       // c.setTitle("保存");
        categoryService.save(c);
    }

    @Test
    void deleteById() {
    
    
        categoryService.removeById(6l);
    }

    @Test
    void deleteAll() {
    
    
        List<Long> ids = new ArrayList<>();
        ids.add(6l);
        ids.add(7l);
        Boolean flag = categoryService.removeByIds(ids);
    }

    /**
     * 实体ID对应存在更新否则添加
     */
    @Test
    void saveOrUpdate() {
    
    
        Category c = new Category();
       // c.setTitle("保存");
        categoryService.saveOrUpdate(c);
    }


    @Test
    @Rollback(value = false)
    @Transactional
    void updateChain() {
    
    
        QCategory qCategory = QCategory.category;
        categoryService.updateChain(qCategory)
                .set(qCategory.title, "测试jpa")
                .where(qCategory.id.eq(6l)).execute();
    }


    @Test
    @Rollback(value = false)
    @Transactional
    void update() {
    
    
        QCategory qCategory = QCategory.category;
        JPAUpdateClause updateWrapper = UpdateWrapper.of(qCategory);
        updateWrapper.set(qCategory.title, "bh").where(qCategory.id.eq(6l));

        Boolean flag = categoryService.update(updateWrapper);

        log.info("更新{}", flag);
    }

    @Test
    @Rollback(value = false)
    @Transactional
    void updateIgnoreNull() {
    
    
        Category category = new Category();
        category.setId(6l);
       // category.setSubTitle("忽略");
        //Category category1 = categoryService.update(category, true);  //会自动忽略实体空属性。

        //category.setTitle("");
        Category category1 = categoryService.update(category, true, new String[]{
    
    "title"});  //自定义不忽略字段,
        log.info("更新{}", category1);
    }

    @Test
    void selectQueryChain() {
    
    
        QCategory qCategory = QCategory.category;
        List<String> categoryList = categoryService.queryChain()
                .select(qCategory.title)
                .from(qCategory).fetch();
        log.info("返回条数{}", categoryList.size());
    }


    @Test
    void selectQuery() {
    
    
        QCategory qCategory = QCategory.category;
        BooleanBuilder booleanBuilder = new BooleanBuilder();

        String subTitle = "88";
        if (StrUtil.isNotEmpty(subTitle)) {
    
    
            booleanBuilder.and(qCategory.described.eq("88"));
        }
        long id = 6l;
        if (!StrUtil.isBlankIfStr(id)) {
    
    
            booleanBuilder.and(qCategory.id.eq(6l));
        }
        List<Category> categories = categoryService.list(booleanBuilder);
        log.info("返回条数{}", categories.size());
    }

}

Simplest-JPA

simplest-jpa has a built-in named BaseRepositoryinterface, which implements the basic addition, deletion, modification and query functions and paging query functions. and correspondingService

new data

Service provides the savemethod _saveBatchsaveOrUpdate

  • save(T entity)Insert entity class data without ignoring nullthe value.
  • saveBatch(Collection<T> entities)Batch insert entity class data
  • saveOrUpdate(T entity)Insert or update, if the primary key has a value, then update, if there is no primary key value, then insert, insert or update will not ignore the null value.
  • saveOrUpdateSelective(T entity)Insert or update, if the primary key has a value, then update, if there is no primary key value, then insert, update will ignore the null value.

delete data

Service Provides the remove, removeAll, methodremoveByIdremoveByIds

  • removeById(ID id)Delete data based on primary key
  • removeById(Collection<? extends ID> ids)Delete data in batches based on multiple primary keys
  • remove(Collection<T> entities)Batch delete based on multiple entities (entities need to have a primary key)
  • remove(T entity)Delete based on entity conditions

update data

Service Provides updatemultiple overloaded methods

  • update(T entity)The query condition is updated based on the entity ID. null values ​​are not ignored
  • update(T entity, Boolean ignore)The query condition is updated based on the entity ID. Custom ignore nul value
  • update(T entity, Boolean ignore, String[] ignoreProperties)Custom ignore entity field attributes
  • update(JPAUpdateClause query)Update data based on query conditions.
@Test
@Rollback(value = false)
@Transactional
void update() {
    
    
    QCategory qCategory = QCategory.category;
    JPAUpdateClause updateWrapper = UpdateWrapper.of(qCategory);
    updateWrapper.set(qCategory.title, "bh").where(qCategory.id.eq(6l));
    Boolean flag = categoryService.update(updateWrapper);
    log.info("更新{}", flag);
}

updateChain

updateChainIt is a tool class that encapsulates UpdateWrapper, etc., which is convenient for users to perform chain operations.

@Test
@Rollback(value = false)
@Transactional
void updateChain() {
    
    
    QCategory qCategory = QCategory.category;
    categoryService.updateChain(qCategory)
            .set(qCategory.title, "测试jpa")
            .where(qCategory.id.eq(6l)).execute();
}

Simplest-JPA queries and paging queries

basic query

Simplest-jpa provides Servicethe following functions for querying database data

  • getById(ID id)Query data based on primary key.
  • getByIdOpt(ID id)Query data based on primary key. return Optionaltype
  • getOne(Example<T> example)Query 1 piece of data according to the query condition.
  • getOne(Predicate query)Query 1 piece of data according to the query condition.
  • getOneOpt(Example<T> example)Query 1 piece of data according to the query condition. When the return Optionaltype query finds multiple matching data, a NonUniqueResultException will be thrown
  • listByIds(Collection<ID> ids)Query the data collection according to the data primary key.
  • list(Predicate query)Query data collection according to query conditions.
  • list(Example query)Query data collection according to query conditions.
  • list()Query all data.
  • count(Predicate query)Query the amount of data based on query conditions.
  • count(Example<T> example)Query the amount of data based on query conditions.
  • exists(Predicate query)Determine whether the data exists according to the query conditions.
  • existsById(ID id)Determine whether it exists according to the ID

Paging query

  • page(Pageable page)Query all data in pages.
  • page(Pageable page, Predicate query)Query data in pages according to query conditions.

chain query

In simplest-jpa, built-in queryChain and updateChain are used to perform chained query operations and chained data operations (modify and delete) on data.

  • queryChain: chain query
  • updateChain: chain update

queryChain demonstration

@Test
void selectQueryChain() {
    
    
    QCategory qCategory = QCategory.category;
    List<String> categoryList = categoryService.queryChain()
            .select(qCategory.title)
            .from(qCategory)
            .fetch();
    log.info("返回条数{}", categoryList.size());
}

conditional query

@Test
void selectQueryChainWhere() {
    
    
    QCategory qCategory = QCategory.category;
    List<String> categoryList=  categoryService.queryChain()
            .select(qCategory.title)
            .from(qCategory)
            .where(qCategory.id.eq(1l))
            .fetch();
        log.info("返回条数{}", categoryList.size());
}

Paging query

@Test
void selectQueryChainWhere() {
    
    
    QCategory qCategory = QCategory.category;
    List<String> categoryList = categoryService.queryChain()
            .select(qCategory.title)
            .from(qCategory)
            .where(qCategory.id.eq(1l))
            .limit(1)
            .fetch();
    log.info("返回条数{}", categoryList.size());
}

updateChain example

@Test
@Rollback(value = false)
@Transactional
void updateChain() {
    
    
    QCategory qCategory = QCategory.category;
    categoryService.updateChain(qCategory)
            .set(qCategory.title, "测试jpa")
            .where(qCategory.id.eq(6l)).execute();
}

method of queryChain

  • fetch()Get multiple data lazy loading mode
  • fetchAll()Get multiple pieces of data and ignore lazy loading
  • fetchOne()Getting more than one piece of data will report an error
  • fetchFirst()Query the first data
  • fetchCount()Query the number of data

Flexible QueryWrapper

In the addition, deletion, query and paging chapters, we can see at any time QueryWrapper, QueryWrapperit is a powerful tool for constructing Sql, and it is also simplest-jpathe highlight and feature of .

Use of QueryWrapper

@SpringBootTest
@Slf4j
public class JpaQueryDSLTest {
    
    

    @Autowired
    private ICategoryService categoryService;

    @Autowired
    private JPAQueryFactory queryWrapper;


    /**
     * select() 和 fetch() 的常用写法
     * 使用fetch()查询时,数据库没有符合该条件的数据时,返回的是空集合,而不是null
     */

    /**
     * 查询字段-select()
     */
    @Test
    public void fetchColum() {
    
    
        QCategory qCategory = QCategory.category;
        List<String> a = queryWrapper
                .select(qCategory.title)
                .from(qCategory)
                .fetch();
        log.info("返回数量{}", a.size());
    }

    /**
     * 查询实体-selectFrom()
     */
    @Test
    public void fetchEntity() {
    
    
        QCategory qCategory = QCategory.category;
        List<Category> categories = queryWrapper.selectFrom(qCategory).fetch();
        log.info("返回数量{}", categories.size());

    }

    /**
     * 查询并将结果封装至dto中
     */
    @Test
    public void fetchDto() {
    
    
        QCategory qCategory = QCategory.category;
        List<CategoryDto> categoryDtos = queryWrapper.select(
                        Projections.bean(CategoryDto.class, qCategory.title)
                )
                .from(qCategory).fetch();
        log.info("返回数量{}", categoryDtos.size());

    }

    /**
     * 去重查询-selectDistinct()
     */
    @Test
    public void fetchDistinct() {
    
    
        QCategory qCategory = QCategory.category;
        List<String> c = queryWrapper
                .selectDistinct(qCategory.title)
                .from(qCategory)
                .fetch();
        log.info("返回数量{}", c.size());
    }

    /**
     * 获取首个查询结果-fetchFirst() 单条记录 limit 1
     */
    @Test
    public void fetchFirst() {
    
    
        QCategory qCategory = QCategory.category;
        Category category = queryWrapper
                .selectFrom(qCategory)
                .fetchFirst();
        log.info("返回数量{}", category.toString());
    }

    /**
     * 获取唯一查询结果-fetchOne()
     * 当fetchOne()根据查询条件从数据库中查询到多条匹配数据时,会抛`NonUniqueResultException`
     */
    @Test
    public void fetchOne() {
    
    
        QCategory qCategory = QCategory.category;
        Category category = queryWrapper
                .selectFrom(qCategory)
                .fetchOne();
        log.info("返回数量{}", category.toString());
    }


    /**
     * where 子句查询条件的常用写法
     */
    @Test
    public void fetchWhere() {
    
    
        QCategory qCategory = QCategory.category;
        List<Category> categories = queryWrapper
                .selectFrom(qCategory)
                .where(qCategory.title.eq("更新")
                        .and(qCategory.subTitle.like('%' + "测试" + '%')))
                .fetch();
        log.info("返回数量{}", categories.size());

    }

    /**
     * where 动态条件查询
     */

    /**
     * 使用QueryDSL提供的BooleanBuilder来进行查询条件管理。
     */
    @Test
    public void fetchWhereDynamic() {
    
    
        QCategory qCategory = QCategory.category;
        BooleanBuilder builder = new BooleanBuilder();
        String title = "a";
        if (StrUtil.isNotEmpty(title)) {
    
    
            builder.and(qCategory.title.eq(title));
        }
        String subTitle = "";
        if (StrUtil.isNotEmpty(subTitle)) {
    
    
            builder.and(qCategory.subTitle.eq(subTitle));
        }
        List<Category> categories = queryWrapper
                .selectFrom(qCategory)
                .where(builder)
                .fetch();
        log.info("返回数量{}", categories.size());

    }

    /**
     * 复杂的查询关系
     */
    @Test
    public void fetchWhereDynamicComplex() {
    
    
        QCategory qCategory = QCategory.category;

        BooleanBuilder builder = new BooleanBuilder();
        builder.or(qCategory.id.eq(1l));

        String title = "a";
        if (StrUtil.isNotEmpty(title)) {
    
    
            builder.and(qCategory.title.eq(title));
        }
        String subTitle = "";
        if (StrUtil.isNotEmpty(subTitle)) {
    
    
            builder.and(qCategory.subTitle.eq(subTitle));
        }


        List<Category> categories = queryWrapper
                .selectFrom(qCategory)
                .where(builder)
                .fetch();
        log.info("返回数量{}", categories.size());
    }


    /**
     * 自定义封装查询的结果集
     * JPAQueryFactory查询工厂的select方法可以将Projections方法返回的QBean作为参数,通过Projections的bean方法来构建返回的结果集映射到实体内,有点像Mybatis内的ResultMap的形式,不过内部处理机制肯定是有着巨大差别的!
     * <p>
     * bean方法第一个参数需要传递一个实体的泛型类型作为返回集合内的单个对象类型,如果QueryDSL查询实体内的字段与DTO实体的字段名字不一样时,可以采用as方法来处理,为查询的结果集指定的字段添加别名,这样就会自动映射到DTO实体内。
     */

    /**
     * 使用Projections的Bean方法
     */
    @Test
    public void fetchBean() {
    
    
        QCategory qCategory = QCategory.category;
        QModule qModule = QModule.module;

        List<CategoryDto> categoryDtos = queryWrapper
                .select(
                        Projections.bean(CategoryDto.class
                                , qCategory.title, qModule.code)
                ).from(qCategory, qModule).fetch();
        log.info("返回数量{}", categoryDtos.size());
    }

    /**
     * 使用Projections的fields方法
     */
    @Test
    public void fetchFields() {
    
    
        QCategory qCategory = QCategory.category;
        List<CategoryDto> categoryDtos = queryWrapper
                .select(
                        Projections.fields(CategoryDto.class
                                , qCategory.title)
                ).from(qCategory).fetch();
        log.info("返回数量{}", categoryDtos.size());
    }

    /**
     * 使用集合的stream转换
     * 从方法开始到fetch()结束完全跟QueryDSL没有任何区别,采用了最原始的方式进行返回结果集,但是从fetch()获取到结果集后处理的方式就有所改变了。
     * <p>
     * fetch()方法返回的类型是泛型List(List),List继承了Collection,完全存在使用Collection内非私有方法的权限,通过调用stream方法可以将集合转换成Stream泛型对象,该对象的map方法可以操作集合内单个对象的转换,具体的转换代码可以根据业务逻辑进行编写。
     * <p>
     * 在map方法内有个lambda表达式参数tuple,通过tuple对象get方法就可以获取对应select方法内的查询字段。
     * ————————————————
     */
    @Test
    public void selectWithStream() {
    
    
        QCategory qCategory = QCategory.category;
        List<CategoryDto> categoryDtos = queryWrapper
                .select(qCategory.title, qCategory.subTitle)
                .from(qCategory)
                .fetch().stream().map(tuple -> {
    
    
                    CategoryDto c = new CategoryDto();
                    c.setTitle(tuple.get(qCategory.title));
                    return c;
                }).collect(Collectors.toList());

        log.info("返回数量{}", categoryDtos.size());
    }


    @Test
    public void findByQuery() {
    
    
        QCategory qCategory = QCategory.category;
        //该Predicate为querydsl下的类,支持嵌套组装复杂查询条件
        BooleanBuilder builder = new BooleanBuilder();
        String title = "a";
        if (StrUtil.isNotEmpty(title)) {
    
    
            builder.and(qCategory.title.eq(title));
        }
        String subTitle = "";
        if (StrUtil.isNotEmpty(subTitle)) {
    
    
            builder.and(qCategory.subTitle.eq(subTitle));
        }
        List<Category> c = categoryService.list(builder);
       log.info("条数{}",c.size());
    }

    @Test
    public void findByQueryWrapper(){
    
    
        QCategory qCategory = QCategory.category;
        JPAQueryFactory queryWrapper=QueryWrapper.of();
        List<String> c = queryWrapper
                .selectDistinct(qCategory.title)
                .from(qCategory)
                .fetch();
        log.info("返回数量{}", c.size());
    }

}

example

  1. single table query
@Service
@Transactional
public class UserService {
    
    

	@Autowired
    private JPAQueryFactory queryFactory;

	/**
	 * attention:
	 * Details:查询user表中的所有记录
	 */
	public List<User> findAll(){
    
    
		QUser quser = QUser.user;
		return queryFactory.selectFrom(quser)
					.fetch();
	}

	/**
	 * Details:单条件查询
	 */
	public User findOneByUserName(final String userName){
    
    
		QUser quser = QUser.user;
		return queryFactory.selectFrom(quser)
			.where(quser.name.eq(userName))
			.fetchOne();
	}

	/**
	 * Details:单表多条件查询
	 */
	public User findOneByUserNameAndAddress(final String userName, final String address){
    
    
		QUser quser = QUser.user;
		return queryFactory.select(quser)
			.from(quser) // 上面两句代码等价与selectFrom
			.where(quser.name.eq(userName).and(quser.address.eq(address)))// 这句代码等同于where(quser.name.eq(userName), quser.address.eq(address))
			.fetchOne();
	}

	/**
	 * Details:使用join查询
	 */
	public List<User> findUsersByJoin(){
    
    
		QUser quser = QUser.user;
		QUser userName = new QUser("name");
		return queryFactory.selectFrom(quser)
			.innerJoin(quser)
			.on(quser.id.intValue().eq(userName.id.intValue()))
			.fetch();
	}

	/**
	 * Details:将查询结果排序
	 */
	public List<User> findUserAndOrder(){
    
    
		QUser quser = QUser.user;
		return queryFactory.selectFrom(quser)
			.orderBy(quser.id.desc())
			.fetch();
	}

	/**
	 * Details:Group By使用
	 */
	public List<String> findUserByGroup(){
    
    
		QUser quser = QUser.user;
		return queryFactory.select(quser.name)
					.from(quser)
					.groupBy(quser.name)
					.fetch();
	}

	/**
	 * Details:删除用户
	 */
	public long deleteUser(String userName){
    
    
		QUser quser = QUser.user;
		return queryFactory.delete(quser).where(quser.name.eq(userName)).execute();
	}

	/**
	 * Details:更新记录
	 */
	public long updateUser(final User u, final String userName){
    
    
		QUser quser = QUser.user;
		return queryFactory.update(quser).where(quser.name.eq(userName))
			.set(quser.name, u.getName())
			.set(quser.age, u.getAge())
			.set(quser.address, u.getAddress())
			.execute();
	}

	/**
	 * Details:使用原生Query
	 */
	public User findOneUserByOriginalSql(final String userName){
    
    
		QUser quser = QUser.user;
		Query query = queryFactory.selectFrom(quser)
				.where(quser.name.eq(userName)).createQuery();
		return (User) query.getSingleResult();
	}


    /**
     *分页查询所有的实体,根据uIndex字段排序
     *
     * @return
     */
    public QueryResults<User> findAllPage(Pageable pageable) {
    
    
        QUser user = QUser.user;
        return jpaQueryFactory
                .selectFrom(user)
                .orderBy(user.uIndex.asc())
                .offset(pageable.getOffset())   //偏移量,计算:offset = ( 当前页 - 1) * 每页条数,这里直接使用的是Pageable中的Offset
                .limit(pageable.getPageSize())  //每页大小
                .fetchResults();    //获取结果,该结果封装了实体集合、分页的信息,需要这些信息直接从该对象里面拿取即可
    }


    /**
     * 部分字段映射查询
     * 投影为UserRes,lambda方式(灵活,类型可以在lambda中修改)
     *
     * @return
     */
    public List<UserDTO> findAllUserDto(Pageable pageable) {
    
    
        QUser user = QUser.user;
        List<UserDTO> dtoList = jpaQueryFactory
                .select(
                        user.username,
                        user.userId,
                        user.nickName,
                        user.birthday
                )
                .from(user)
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .fetch()
                .stream()
                .map(tuple -> UserDTO.builder()
                        .username(tuple.get(user.username))
                        .nickname(tuple.get(user.nickName))
                        .userId(tuple.get(user.userId).toString())
                        .birthday(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(user.birthday)))
                        .build()
                )
                .collect(Collectors.toList());
        return dtoList;
    }


    /**
     * 部分字段映射查询
     * 投影为UserRes,自带的Projections方式,不能转换类型,但是可以使用as转换名字
     *
     * @return
     */
    public List<UserDTO> findAllDto2() {
    
    
        QUser user = QUser.user;
        List<UserDTO> dtoList = jpaQueryFactory
                .select(
                        Projections.bean(
                                UserDTO.class,
                                user.username,
                                user.userId,
                                user.nickName,
                                user.birthday
                        )
                )
                .from(user)
                .fetch();
        return dtoList;
    }


}
  1. multi-table query


/**
 * @Description 查询全部
 * @Author 程序员三时
 * @Date  10:53
 * @return java.util.List<com.cs.querydsl.model.Loc>
 **/
@Override
public List<Loc> findAll(Loc loc) {
    
    
    // 使用 QueryDSL 进行查询
    QLoc qLoc = QLoc.loc1;
    QUser qUser = QUser.user;
    // 定于获取条件
    BooleanBuilder booleanBuilder = new BooleanBuilder();
    // 要查询的条件
    if(!StringUtils.isEmpty(loc.getLoc())){
    
    
        // 放入要查询的条件信息
        booleanBuilder.and(qLoc.loc.contains(loc.getLoc()));
    }
    //连接查询条件(Loc.id = User.id )
    booleanBuilder.and(qLoc.id.eq(qUser.id));
    // 使用 QueryDSL 进行多表联合查询
    QueryResults<Tuple> listResult = queryFactory
            .select(QLoc.loc1,QUser.user)
            .from(qLoc, qUser)//查询两表
            .where(booleanBuilder)
            .fetchResults();
    //遍历 java8 自带流转换成集合
    List<Loc> collect = listResult.getResults().stream().map(tuple -> {
    
    
        Loc lcs = tuple.get(qLoc);
        return lcs;
    }).collect(Collectors.toList());
    return collect;
}



部分字段映射的投影查询:

当使用`@ManyToOne`、`@ManyToMany`建立关联时:

/**
 * 根据部门的id查询用户的基本信息+用户所属部门信息,并且使用UserDeptDTO进行封装返回给前端展示
 * @param departmentId
 * @return
 */
public List<UserDeptDTO> findByDepatmentIdDTO(int departmentId) {
    
    
    QUser user = QUser.user;
    QDepartment department = QDepartment.department;
    //直接返回
    return jpaQueryFactory
            //投影只去部分字段
            .select(
                    user.username,
                    user.nickName,
                    user.birthday,
                    department.deptName,
                    department.createDate

            )
            .from(user)
            //联合查询
            .join(user.department, department)
            .where(department.deptId.eq(departmentId))
            .fetch()
            //lambda开始
            .stream()
            .map(tuple ->
                    //需要做类型转换,所以使用map函数非常适合
                    UserDeptDTO.builder()
                            .username(tuple.get(user.username))
                            .nickname(tuple.get(user.nickName))
                            .birthday(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(user.birthday)))
                            .deptName(tuple.get(department.deptName))
                            .deptBirth(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(department.createDate)))
                            .build()
            )
            .collect(Collectors.toList());
}


当使用id建立关联时:
/**
 * 根据部门的id查询用户的基本信息+用户所属部门信息,并且使用UserDeptDTO进行封装返回给前端展示
 *
 * @param departmentId
 * @return
 */
public List<UserDeptDTO> findByDepatmentIdDTO(int departmentId) {
    
    
    QUser user = QUser.user;
    QDepartment department = QDepartment.department;
    //直接返回
    return jpaQueryFactory
            //投影只去部分字段
            .select(
                    user.username,
                    user.nickName,
                    user.birthday,
                    department.deptName,
                    department.createDate

            )
            .from(user, department)
            //联合查询
            .where(
                    user.departmentId.eq(department.deptId).and(department.deptId.eq(departmentId))
            )
            .fetch()
            //lambda开始
            .stream()
            .map(tuple ->
                    //需要做类型转换,所以使用map函数非常适合
                    UserDeptDTO.builder()
                            .username(tuple.get(user.username))
                            .nickname(tuple.get(user.nickName))
                            .birthday(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(user.birthday)))
                            .deptName(tuple.get(department.deptName))
                            .deptBirth(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(department.createDate)))
                            .build()
            )
            .collect(Collectors.toList());
}


使用 Projections 自定义返回 Bean/**
 * Details:方式一:使用Bean投影
 */
public List<PersonIDCardDto> findByDTOUseBean(){
    
    
    Predicate predicate = (QPerson.person.id.intValue()).eq(QIDCard.iDCard.person.id.intValue());
    return queryFactory.select(
            Projections.bean(PersonIDCardDto.class, QIDCard.iDCard.idNo, QPerson.person.address, QPerson.person.name))
            .from(QIDCard.iDCard, QPerson.person)
            .where(predicate)
            .fetch();
}

/**
 * Details:方式二:使用fields来代替setter
 */
public List<PersonIDCardDto> findByDTOUseFields(){
    
    
    Predicate predicate = (QPerson.person.id.intValue()).eq(QIDCard.iDCard.person.id.intValue());
    return queryFactory.select(
            Projections.fields(PersonIDCardDto.class, QIDCard.iDCard.idNo, QPerson.person.address, QPerson.person.name))
            .from(QIDCard.iDCard, QPerson.person)
            .where(predicate)
            .fetch();
}

/**
 * Details:方式三:使用构造方法,注意构造方法中属性的顺序必须和构造器中的顺序一致
 */
public List<PersonIDCardDto> findByDTOUseConstructor(){
    
    
    Predicate predicate = (QPerson.person.id.intValue()).eq(QIDCard.iDCard.person.id.intValue());
    return queryFactory.select(
            Projections.constructor(PersonIDCardDto.class, QPerson.person.name, QPerson.person.address, QIDCard.iDCard.idNo))
            .from(QIDCard.iDCard, QPerson.person)
            .where(predicate)
            .fetch();
}

Guess you like

Origin blog.csdn.net/u011738045/article/details/132079116