0
点赞
收藏
分享

微信扫一扫

SpringDataJPA 整合 QueryDSL

QueryDSL

QueryDSL 简介

QueryDSL 是一个非常活跃的开源项目,目前在 Github 上的发布的 Release 版本已经多达 251 个版本,目前最新版是 4.2.1 ,并且由 Querydsl Google组 和 StackOverflow 两个团队提供支持。

QueryDSL 是一个框架,可用于构造静态类型的类似SQL的查询。可以通过诸如QueryDSL之类的 API 构造查询,而不是将查询编写为内联字符串或将其外部化为XML文件。

例如,与简单字符串相比,使用 API 的好处是

  • IDE中的代码完成
  • 几乎没有语法无效的查询
  • 可以安全地引用域类型和属性
  • 更好地重构域类型的更改

QueryDSL使用

Repository层

继承QuerydslPredicateExecutor接口,实现分页查询

导入依赖

<!--QueryDSL依赖-->
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
    <version>4.4.0</version>
</dependency>

<!--自动生成查询实体依赖-->
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <version>4.4.0</version>
</dependency>

插件配置

添加这个插件是为了让程序自动生成 query type (查询实体,命名方式为:"Q"+对应实体名)。 上文引入的依赖中 querydsl-apt 即是为此插件服务的。

注:在使用过程中,如果遇到 query type 无法自动生成的情况,用maven更新一下项目即可解决(右键项目 -> Maven -> Update Folders)。

<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>

根据实体类自动生成Qxxx的类

SpringDataJPA 整合 QueryDSL_SpringDataJpa

简单的增删改查

package com.du.pojo.service;

import com.du.pojo.entity.QUser;
import com.du.pojo.entity.User;
import com.du.pojo.repository.UserRepository;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.util.StringUtils;

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

@Service
public class UserServiceImpl implements UserService {

    //JPA
    @Autowired
    private UserRepository userRepository;

    //查询工厂实体
    @Autowired
    private EntityManager entityManager;

    private JPAQueryFactory queryFactory;

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


    @Override
    public List<User> findAll(User user) {
        // 使用QUser进行查询
        QUser qUser = QUser.user;
        // 定于获取条件
        BooleanBuilder booleanBuilder = new BooleanBuilder();
        // 要查询的条件
        if (!StringUtils.isEmpty(user.getName())) {
            // 设置要查询的条件
            booleanBuilder.and(qUser.name.contains(user.getName()));
        }
        // queryFactory 是上方定义的工厂实体
        // select(生成的实体类的字段).from(生成实体类的名称).where(上方要查询的条件).fetch()进行查询
        return queryFactory.select(qUser)
                .from(qUser)
                .where(booleanBuilder)
                .fetch();
    }

    @Override
    public User findUserById(Integer id) {
        // 使用QUser查询
        QUser Quser = QUser.user;
        // 条件
        BooleanBuilder booleanBuilder = new BooleanBuilder();
        booleanBuilder.and(Quser.id.eq(id));

        //使用queryFactory查询
        return queryFactory.select(Quser)
                .from(Quser)
                .where(booleanBuilder)
                .fetchOne();
    }

    @Override
    public long dropUserById(Integer id) {
        QUser qUser = QUser.user;
        return queryFactory.delete(qUser)
                .where(new BooleanBuilder().and(qUser.id.eq(id)))
                .execute();
    }

    @Override
    @Transactional
    public Long saveUser(User user) {
        userRepository.save(user);
        return 1L;
    }

    @Override
    public Long updateUser(User user) {
        QUser qUser = QUser.user;
        return queryFactory.update(qUser)
                .set(qUser.name, user.getName())
                .set(qUser.password, user.getPassword())
                .where(qUser.id.eq(user.getId()))
                .execute();
    }
    
    @Override
    public List<EmployeeEntity> page(Long page, Long size) {
        QEmployeeDo qEmployeeDo = QEmployeeDo.employeeDo;
        return jpaQueryFactory.selectFrom(qEmployeeDo)
                .where(qEmployeeDo.deleted.isFalse())
                .offset(page * size)
                .limit(size)
                .fetch()
                .stream()
                .map(EmployeeDo::toEntity)
                .collect(Collectors.toList());
    }
}

连接查询

@Test
    void findI() {
        QDepartmentDo qDepartmentDo = QDepartmentDo.departmentDo;
        QEmployeeDo qEmployeeDo = QEmployeeDo.employeeDo;
        List<Tuple> list = jpaQueryFactory.select(
                qEmployeeDo.id.as("员工id"),
                qEmployeeDo.employee_name.as("员工姓名"),
                qEmployeeDo.dept_name.as("员工部门"),
                qDepartmentDo.id.as("部门id"),
                qDepartmentDo.dept_name.as("部门名称"))
                .from(qDepartmentDo)
                .innerJoin(qEmployeeDo)
                .on(qDepartmentDo.dept_name.eq(qEmployeeDo.dept_name))
                .fetch();
        list.forEach(System.out::println);
    }

    @Test
    void find() {
        QDepartmentDo qDepartmentDo = QDepartmentDo.departmentDo;
        QEmployeeDo qEmployeeDo = QEmployeeDo.employeeDo;
        List<Tuple> list = jpaQueryFactory.select(
                qEmployeeDo.id.as("员工id"),
                qEmployeeDo.employee_name.as("员工姓名"),
                qEmployeeDo.dept_name.as("员工部门"),
                qDepartmentDo.id.as("部门id"),
                qDepartmentDo.dept_name.as("部门名称"))
                .from(qDepartmentDo)
                .rightJoin(qEmployeeDo)
                .on(qDepartmentDo.dept_name.eq(qEmployeeDo.dept_name))
                .fetch();
        list.forEach(System.out::println);
    }

    @Test
    void findL() {
        QDepartmentDo qDepartmentDo = QDepartmentDo.departmentDo;
        QEmployeeDo qEmployeeDo = QEmployeeDo.employeeDo;
        List<Tuple> list = jpaQueryFactory.select(
                qEmployeeDo.id.as("员工id"),
                qEmployeeDo.employee_name.as("员工姓名"),
                qEmployeeDo.dept_name.as("员工部门"),
                qDepartmentDo.id.as("部门id"),
                qDepartmentDo.dept_name.as("部门名称"))
                .from(qDepartmentDo)
                .leftJoin(qEmployeeDo)
                .on(qDepartmentDo.dept_name.eq(qEmployeeDo.dept_name))
                .fetch();
        list.forEach(System.out::println);
    }

自定义返回对象

在我们集成queryDsl时,一般是这样用的

@Override
public List<CityHotelVo> findcityHotel() {
    JPAQuery<CityHotelVo> query = new JPAQuery<>(em);
    QTCity c = QTCity.tCity;
    QTHotel h = QTHotel.tHotel;

    JPAQuery<Tuple> on = query.select(
        c.id,
        c.name,
        h.name,
        h.address).from(c).leftJoin(h).on(c.id.eq(h.city));

    QueryResults<Tuple> rts = on.fetchResults();
    List<Tuple> results = rts.getResults();

    return results.stream().map(CityHotelVo::new).collect(Collectors.toList());
}

转Vo实现

public CityHotelVo(Tuple t) {
    this.id = t.get(QTCity.tCity.id);
    this.cityName = t.get(QTCity.tCity.name);
    this.hotelName = t.get(QTHotel.tHotel.name);
    this.address = t.get(QTHotel.tHotel.address);
}

返回的是一个List,我们还需将tuple手动转成我们自定义的VO对象,以下总结了可自动Tuple转VO的几种实现。

方式一

/**
 * 方式一:使用Bean投影
 * @return
 */
@Override
public List<CityHotelVo> findcityHotel_2() {
    JPAQuery<CityHotelVo> query = new JPAQuery<>(em);
    QTCity c = QTCity.tCity;
    QTHotel h = QTHotel.tHotel;

    List<CityHotelVo> results1 = query.select(Projections.bean(CityHotelVo.class,
                                                               c.id.as("id"),
                                                               c.name.as("cityName"),
                                                               h.name.as("hotelName"),
                                                               h.address.as("address"))).from(c).leftJoin(h).on(c.id.eq(h.city)).fetchResults().getResults();
    return results1;
}

方式二

/**
 * 方式二 fields 投影
 * @return
 */
@Override
public List<CityHotelVo2> projectionsFields() {
    JPAQuery<CityHotelVo> query = new JPAQuery<>(em);
    QTCity c = QTCity.tCity;
    QTHotel h = QTHotel.tHotel;

    JPAQuery<CityHotelVo2> on = query.select(
        Projections.fields(CityHotelVo2.class,
                           c.id,
                           c.name,
                           h.address))
        .from(c).leftJoin(h).on(c.id.eq(h.city));
    List<CityHotelVo2> resultList = on.createQuery().getResultList();
    return resultList;
}

方式三

/**
 * @return
 */
@Override
public List<CityHotelVo2> findcityHotel_31() {
    QTCity c = QTCity.tCity;
    QTHotel h = QTHotel.tHotel;
    JPAQueryFactory queryFactory = new JPAQueryFactory(em);

    JPAQuery<CityHotelVo2> on = queryFactory.select(
        Projections.constructor(CityHotelVo2.class,
                                c.id,
                                c.name,
                                h.address))
        .from(c).leftJoin(h).on(c.id.eq(h.city));
    List<CityHotelVo2> results = on.fetchResults().getResults();
    return results;
}

注意这种构造器方式,只支持对数值和String类型的映射处理,当你定义了Date等等类型,需要在构造函数中,构造如下

@Data
@Accessors(chain = true)
public class CityHotelVo4 implements Serializable {
    private static final long serialVersionUID = 2546523L;
    private Integer id;
    private String cityName;
    private String hotelName;
    private String address;

    private LocalDateTime formatTime;
    public CityHotelVo4(Integer id, String cityName, String hotelName, String address, String formatTime) throws ParseException {
        this.id = id;
        this.cityName = cityName;
        this.hotelName = hotelName;
        this.address = address;
        this.formatTime = DateUtils.parseLocalDateTime(formatTime);
    }
}

字符串操作API

//	字符串拼接
/**
this + str
     * Create a {@code concat(this, str)} expression
     *
     * <p>Get the concatenation of this and str</p>
     *
     * @param str string to append
     * @return this + str
     */
public StringExpression append(Expression<String> str) {
    return Expressions.stringOperation(Ops.CONCAT, mixin, str);
}
/**
this + str
     * Create a {@code concat(this, str)} expression
     *
     * <p>Get the concatenation of this and str</p>
     *
     * @param str string to append
     * @return this + str
     */
public StringExpression concat(Expression<String> str) {
    return append(str);
}
/**
str + this
     * Create a {@code concat(str, this)} expression
     *
     * <p>Prepend the given String and return the result</p>
     *
     * @param str string
     * @return str + this
     */
public StringExpression prepend(Expression<String> str) {
    return Expressions.stringOperation(Ops.CONCAT, str, mixin);
}

//是否包含某个字符串
/**
     * Create a {@code this.contains(str)} expression
     *
     * <p>Returns true if the given String is contained</p>
     *
     * @param str string
     * @return this.contains(str)
     * @see java.lang.String#contains(CharSequence)
     */
public BooleanExpression contains(Expression<String> str) {
    return Expressions.booleanOperation(Ops.STRING_CONTAINS, mixin, str);
}
/**
是否包含某个字符串,忽略大小写
     * Create a {@code this.containsIgnoreCase(str)} expression
     *
     * <p>Returns true if the given String is contained, compare case insensitively</p>
     *
     * @param str string
     * @return this.containsIgnoreCase(str) expression
     */
public BooleanExpression containsIgnoreCase(Expression<String> str) {
    return Expressions.booleanOperation(Ops.STRING_CONTAINS_IC, mixin, str);
}

//是否以某个字符串结尾
/**
     * Create a {@code this.endsWith(str)} expression
     *
     * <p>Returns true if this ends with str</p>
     *
     * @param str string
     * @return this.endsWith(str)
     * @see java.lang.String#endsWith(String)
     */
public BooleanExpression endsWith(Expression<String> str) {
    return Expressions.booleanOperation(Ops.ENDS_WITH, mixin, str);
}
//是否以某个字符串结尾,忽略大小写
public BooleanExpression endsWithIgnoreCase(Expression<String> str) {
    return Expressions.booleanOperation(Ops.ENDS_WITH_IC, mixin, str);
}

Number操作API

//比较
max(最大值),min(最小值)
goe(this >= num),goeAll(this >= nums),goeAny(this >= any num)
gt(this > num),gtAll(this > nums),gtAny(this > any num)
loe(this <= num),loeAll(this <= nums),loeany(this <= any num)
lt(this < num),ltAll(this < nums),ltAny(this < any num)
between(from <= this <= to),notBetween(this < from || this > to this < from || this > to)

//运算
add(求和),avg(平均值),divide(除法,this / num)
    
//字节转换
byteValue(num转为byte)
    
//Expression表达式
mod(return this, num),multiply(this * num),negate(this * -1 取反),subtract(this - right)

举报

相关推荐

0 条评论