日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JPA+QueryDSL

發布時間:2023/12/20 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JPA+QueryDSL 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

工作需要,接觸到了QueryDSL,總結使用方法,參考了幾個大佬的文章,對我幫助很大 參考文章1,參考文章2,參考文章3,參考文章4感謝大佬們!!!

使用場景

  • QueryDSL僅僅是一個通用的查詢框架,專注于通過Java API構建類型安全的SQL查詢。
  • Querydsl可以通過一組通用的查詢API為用戶構建出適合不同類型ORM框架或者是SQL的查詢語句,也就是說QueryDSL是基于各種ORM框架以及SQL之上的一個通用的查詢框架。
  • 借助QueryDSL可以在任何支持的ORM框架或者SQL平臺上以一種通用的API方式來構建查詢。目前QueryDSL支持的平臺包括
    JPA,JDO,SQL,Java,Collections,RDF,Lucene,Hibernate Search。
  • QueryDSL官網:http://querydsl.com/static/querydsl/4.1.3/reference/html_single/

    一些概念

  • EntityManager:
    在 JPA 規范中, EntityManager 是完成持久化操作的核心對象。實體作為普通 Java 對象,只有在調用 EntityManager 將其持久化后才會變成持久化對象。
    EntityManager 對象在一組實體類與底層數據源之間進行 O/R 映射的管理。它可以用來管理和更新 Entity Bean, 根椐主鍵查找 Entity Bean, 還可以通過JPQL語句查詢實體。
  • @PersistenceContext
    Persistence context是由一組受托管的實體對象實例所構成的集合。它受entity manager 的管理。Entity manager追蹤persistence context中所有對象的修改和更新情況,并根據指定的flush模式(本章稍后會做討論)將這些修改保存到數據庫中。一旦persistence context被關閉,所有實體對象實例都會脫離EntityManager而成為非托管對象。對象一旦從persistence context中脫離,就不再受entity manager管理了,任何對此對象的狀態變更也將不會被同步到數據庫。
  • 使用方法

    pom文件的導入

    <!-- queryDSL --><dependency><groupId>com.querydsl</groupId><artifactId>querydsl-jpa</artifactId></dependency><!-- queryDSL --><dependency><groupId>com.querydsl</groupId><artifactId>querydsl-apt</artifactId><scope>provided</scope></dependency><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><!--因為是類型安全的,所以還需要加上Maven APT plugin,使用 APT 自動生成一些類:--><plugin><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</outputDirectory><processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor></configuration></execution></executions></plugin></plugins> </build>

    下面是兩個大佬的文章里給出的例子,根據例子學習非常快,再次感謝大佬們!!!

    例1

  • 創建實體類
  • @Entity @Getter @Setter @ToString @Accessors(fluent = true) @AllArgsConstructor @NoArgsConstructor @Table(name = "t_user") public class User extends IdEntity {/** 用戶名 */private String username;/** 密碼 */private String password;/** 性別 */private String sex;/** 年齡 */private String age;/** 地址 */private String address;/** 角色id */private Long roleId; }
  • 創建repository接口
  • @Repository public interface UserRepository extends JpaRepository<User,Long>, QuerydslPredicateExecutor<User> {}
  • 生成Q類 (舉例: user實體生成實體后會有一個QUser的類,該框架操作的是QUser類,而不是直接的是實體類)
  • IDEA 的Maven面板,項目生命周期里重新編譯項目,會自動生成Q實體類

  • 創建service接口實現類
  • @Service @Transactional public class UserServiceImpl implements UserService {@Autowiredprivate UserRepository userRepository;@PersistenceContextprivate EntityManager entityManager;/*** 添加用戶** @return user*/@Overridepublic User saveUser() {User address = new User().username("admin").password("123456").age("12").sex("男").address("山西省");return userRepository.save(address);}/*** 通過id查詢** @param id 數據id* @return Optional<User>*/@Overridepublic Optional<User> findById(Long id) {QUser qUser = QUser.user;BooleanExpression eq = qUser.id.eq(id);return userRepository.findOne(eq);}/*** 查詢所有** @return List<RoUser>*/@Overridepublic List<RoUser> findAll() {JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);QUser qUser = QUser.user;QRole qRole = QRole.role;QBean<RoUser> bean = Projections.bean(RoUser.class,//返回自定義實體的類型qUser.id,qUser.username,qUser.password,qUser.sex,qUser.age,qUser.address,qRole.roleName,qRole.remark);// 返回分頁參數的信息queryFactory.select(bean) // 返回自定義實體.from(qUser) // 主表.leftJoin(qRole) // 從表.on(qUser.roleId.eq(qRole.id) // on 條件).where(qUser.id.eq(2L)) // where 條件.orderBy(qUser.createTime.desc()) // 排序.offset(1).limit(10) // 分頁.fetchResults();return queryFactory.select(bean) // 返回自定義實體.from(qUser) // 主表.leftJoin(qRole) // 從表.on(qUser.roleId.eq(qRole.id) // on 條件).where(qUser.id.eq(2L)) // where 條件.fetch();}/*** 動態查詢 + 子查詢 + 分頁 + 排序** @return List<RoUser>*/@Overridepublic QueryResults<RoUser> findAll(String username, Long roleId) {JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);QUser qUser = QUser.user;QRole qRole = QRole.role;QBean<RoUser> bean = Projections.bean(RoUser.class,//返回自定義實體的類型qUser.id,qUser.username,qUser.password,qUser.sex,qUser.age,qUser.address,qRole.roleName,qRole.remark);// 定義返回類型JPAQuery<RoUser> from = queryFactory.select(bean) // 返回自定義實體.from(qUser);// 主表// 模糊查詢名字if (StringUtils.isNotBlank(username)) {from.where(qUser.username.like("%" + username + "%"));}if (null != roleId) {from.where(qUser.roleId.eq(roleId));}return from.leftJoin(qRole).on(qUser.roleId.eq(qRole.id)) // on 子查詢.orderBy(qUser.createTime.desc()) // 排序.offset(0) // 起始頁.limit(10) // 限制條數.fetchResults();} }

    例2

    一. 實體類
  • 城市類
  • @Entity @Table(name = "t_city", schema = "test", catalog = "") public class TCity { //省略JPA注解標識 private int id; private String name; private String state; private String country; private String map; }
  • 旅館類
  • @Entity @Table(name = "t_hotel", schema = "test", catalog = "") public class THotel { //省略JPA注解標識 private int id; private String name; private String address; private Integer city;//保存著城市的id主鍵 }
    二. 單表動態分頁查詢

    Spring Data JPA中提供了QueryDslPredicateExecutor接口,用于支持QueryDSL的查詢操作

    public interface tCityRepository extends JpaRepository<TCity, Integer>, QueryDslPredicateExecutor<TCity> {}

    這樣的話單表動態查詢就可以參考如下代碼:

    //查找出Id小于3,并且名稱帶有`shanghai`的記錄. //動態條件 QTCity qtCity = QTCity.tCity; //SDL實體類 //該Predicate為querydsl下的類,支持嵌套組裝復雜查詢條件 Predicate predicate = qtCity.id.longValue().lt(3).and(qtCity.name.like("shanghai")); //分頁排序 Sort sort = new Sort(new Sort.Order(Sort.Direction.ASC,"id")); PageRequest pageRequest = new PageRequest(0,10,sort); //查找結果 Page<TCity> tCityPage = tCityRepository.findAll(predicate,pageRequest);
    三. 多表動態查詢

    QueryDSL對多表查詢提供了一個很好地封裝,看下面代碼:

    /** * 關聯查詢示例,查詢出城市和對應的旅店 * @param predicate 查詢條件 * @return 查詢實體 */ @Override public List<Tuple> findCityAndHotel(Predicate predicate) { JPAQueryFactory queryFactory = new JPAQueryFactory(em); JPAQuery<Tuple> jpaQuery = queryFactory.select(QTCity.tCity,QTHotel.tHotel) .from(QTCity.tCity) .leftJoin(QTHotel.tHotel) .on(QTHotel.tHotel.city.longValue().eq(QTCity.tCity.id.longValue())); //添加查詢條件 jpaQuery.where(predicate); //拿到結果 return jpaQuery.fetch(); }

    城市表左連接旅店表,當該旅店屬于這個城市時查詢出兩者的詳細字段,存放到一個Tuple的多元組中.相比原生sql,簡單清晰了很多.
    那么該怎么調用這個方法呢?

    @Test public void findByLeftJoin(){ QTCity qtCity = QTCity.tCity; QTHotel qtHotel = QTHotel.tHotel; //查詢條件 Predicate predicate = qtCity.name.like("shanghai"); //調用 List<Tuple> result = tCityRepository.findCityAndHotel(predicate); //對多元組取出數據,這個和select時的數據相匹配 for (Tuple row : result) { System.out.println("qtCity:"+row.get(qtCity)); System.out.println("qtHotel:"+row.get(qtHotel)); System.out.println("--------------------"); } System.out.println(result); }

    這樣做的話避免了返回Object[]數組,下面是自動生成的sql語句:

    select tcity0_.id as id1_0_0_, thotel1_.id as id1_1_1_, tcity0_.country as country2_0_0_, tcity0_.map as map3_0_0_, tcity0_.name as name4_0_0_, tcity0_.state as state5_0_0_, thotel1_.address as address2_1_1_, thotel1_.city as city3_1_1_, thotel1_.name as name4_1_1_ from t_city tcity0_ left outer join t_hotel thotel1_ on ( cast(thotel1_.city as signed)=cast(tcity0_.id as signed) ) where tcity0_.name like ? escape '!'
    四 多表動態分頁查詢

    分頁查詢對于queryDSL無論什么樣的sql只需要寫一遍,會自動轉換為相應的count查詢,下面代碼是對上面的查詢加上分頁功能:

    @Override public QueryResults<Tuple> findCityAndHotelPage(Predicate predicate,Pageable pageable) { JPAQueryFactory queryFactory = new JPAQueryFactory(em); JPAQuery<Tuple> jpaQuery = queryFactory.select(QTCity.tCity.id,QTHotel.tHotel) .from(QTCity.tCity) .leftJoin(QTHotel.tHotel) .on(QTHotel.tHotel.city.longValue().eq(QTCity.tCity.id.longValue())) .where(predicate) .offset(pageable.getOffset()) .limit(pageable.getPageSize()); //拿到分頁結果 return jpaQuery.fetchResults(); }

    和上面不同之處在于這里使用了offset和limit限制查詢結果,并且返回一個QueryResults。該類會自動實現count查詢和結果查詢,并進行封裝。
    調用形式如下:

    @Test public void findByLeftJoinPage(){ QTCity qtCity = QTCity.tCity; QTHotel qtHotel = QTHotel.tHotel; //條件 Predicate predicate = qtCity.name.like("shanghai"); //分頁 PageRequest pageRequest = new PageRequest(0,10); //調用查詢 QueryResults<Tuple> result = tCityRepository.findCityAndHotelPage(predicate,pageRequest); //結果取出 for (Tuple row : result.getResults()) { System.out.println("qtCity:"+row.get(qtCity)); System.out.println("qtHotel:"+row.get(qtHotel)); System.out.println("--------------------"); } //取出count查詢總數 System.out.println(result.getTotal()); }

    生成的原生count查詢sql,當該count查詢結果為0的話,則直接返回,并不會再進行具體數據查詢:

    select count(tcity0_.id) as col_0_0_ from t_city tcity0_ left outer join t_hotel thotel1_ on ( cast(thotel1_.city as signed)=cast(tcity0_.id as signed) ) where tcity0_.name like ? escape '!'

    生成的原生查詢sql:

    select tcity0_.id as id1_0_0_, thotel1_.id as id1_1_1_, tcity0_.country as country2_0_0_, tcity0_.map as map3_0_0_, tcity0_.name as name4_0_0_, tcity0_.state as state5_0_0_, thotel1_.address as address2_1_1_, thotel1_.city as city3_1_1_, thotel1_.name as name4_1_1_ from t_city tcity0_ left outer join t_hotel thotel1_ on ( cast(thotel1_.city as signed)=cast(tcity0_.id as signed) ) where tcity0_.name like ? escape '!' limit ?

    查看打印,可以發現對應的city也都是同一個對象,hotel是不同的對象。

    總結

    以上是生活随笔為你收集整理的JPA+QueryDSL的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。