jpa的好伙伴QueryDSL快速入门
Querydsl定義了一種常用的靜態類型語法,用于在持久域模型數據之上進行查詢。JDO和JPA是Querydsl的主要集成技術。本文旨在介紹如何使用Querydsl與JPA組合使用。JPA的Querydsl是JPQL和Criteria查詢的替代方法。QueryDSL僅僅是一個通用的查詢框架,專注于通過Java API構建類型安全的SQL查詢。
一、準備工作
1、引依賴
querydsl 相關jar包
<!--query dsl --> <dependency><groupId>com.querydsl</groupId><artifactId>querydsl-jpa</artifactId> </dependency> <dependency><groupId>com.querydsl</groupId><artifactId>querydsl-apt</artifactId><scope>provided</scope> </dependency>編譯插件:
<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>該插件會 查找使用javax.persistence.Entity注解的域類型,并為它們生成對應的查詢類型,生成地址對應:<outputDirectory> 配置
生成命令:
mvn clean complie2、讓 Spring管理 JPAQueryFactory
使用QueryDSL的功能時,會依賴使用到JPAQueryFactory,而JPAQueryFactory在這里依賴使用EntityManager,所以在主類中做如下配置,使得Spring自動幫我們注入EntityManager與自動管理JPAQueryFactory:
@SpringBootApplication public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}//讓Spring管理JPAQueryFactory@Beanpublic JPAQueryFactory jpaQueryFactory(EntityManager entityManager){return new JPAQueryFactory(entityManager);} }二、案例
1、JpaSpecificationExecutor
直接讓你的Repository繼承QueryDslPredicateExecutor即可,使用的話和繼承JpaRepository差不太多,我這就不講了,本文主要講解JPAQueryFactory。
QueryDslPredicateExecutor下面主要有這幾個方法:
public interface QueryDslPredicateExecutor<T> {T findOne(Predicate var1);Iterable<T> findAll(Predicate var1);Iterable<T> findAll(Predicate var1, Sort var2);Iterable<T> findAll(Predicate var1, OrderSpecifier... var2);Iterable<T> findAll(OrderSpecifier... var1);Page<T> findAll(Predicate var1, Pageable var2);long count(Predicate var1);boolean exists(Predicate var1); }這里面的方法大多數都是可以傳入Predicate類型的參數,說明還是圍繞著QUser來進行操作的,如傳入quser.username.eq(“123”)的方式,操作都非常簡單,如下:
public User findUserByUserName(final String userName){/*** 該例是使用spring data QueryDSL實現*/QUser quser = QUser.user;Predicate predicate = quser.name.eq(userName);// 根據用戶名,查詢user表return repository.findOne(predicate); }2、JPAQueryFactory
單表
package com.chhliu.springboot.jpa.service;import java.util.List;import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import javax.transaction.Transactional;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort;import com.chhliu.springboot.jpa.entity.QUser; import com.chhliu.springboot.jpa.entity.User; import com.querydsl.core.types.Predicate; import com.querydsl.jpa.impl.JPAQueryFactory;@Service @Transactional public class UserService {@Autowiredprivate 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;}}多表
/*** @Description 查詢全部* @Author LinLuoChen* @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(); }三、我的個人建議
分情況使用:
-
增刪改:直接使用 JPA Repository 實現即可
-
查詢
-
單表:
-
有動態篩選條件:使用 QueryDSL
-
無動態條件:使用 JPA Repository、QueryDSL均可。
-
-
多表:有無動態條件均使用 QueryDSL
JpaSpecificationExecutor也可以實現動態條件查詢,但是使用起來過于繁瑣,不推薦使用。
-
四、其他
聚合函數、子查詢、Template等進階使用看這里:https://www.jianshu.com/p/69dcb1b85bbb
總結
以上是生活随笔為你收集整理的jpa的好伙伴QueryDSL快速入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: unity 字体 素材_unity中文字
- 下一篇: C#中用委托实现C++的回调函数