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的类
简单的增删改查
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)