自定義JpaUtil,快速完成Hql執行邏輯(一)


 這段時間學習Spring Data JPA功能模塊。Java持久性API(簡稱JAP)是類和方法的集合,以海量數據關系映射持久並存儲到數據庫,這是由Oracle公司提供方案技術。在JAVA社區,深受愛戴,作為老少皆宜,大小通吃的存在,可以快速實現訪問數據庫功能。其官方推崇的是通過繼承JpaRepository<T, ID>接口,實現一個個的領域倉儲(即基礎表的增刪改查方法組),JpaRepository<T, ID>接口定義了絕大部分單表操作的方法,可支持常規的業務操作。

   不過個人考慮,實際項目中或許還有部分關聯查詢,或比較復雜的動態查詢功能,這時在倉庫中定義接口方法,聲明@Query注解的sql字符串的這種方法就不是特別優美的。基於這個考慮,我通過查詢資料、分析源碼,實現了一個簡單的JpaUtil類,提供list和page兩個泛型方法,可直接接收sql語句,傳入參數,完成查詢列表或分頁數據。

   本次先介紹一下JpaUtil的實現,下次分享實體關聯查詢時延時加載和即時加載的研究心得(FetchType(LAZY,EAGER))。

   一、標准做法,繼承JpaRepository<T, ID>接口,實現自己的業務倉儲。

  • 定義實體,與數據庫表映射。具體的數據庫連接,與配置這里不再贅述,如有不清楚,可以翻看我前面的博文。
  1. package simm.spring.entity;
    
    @Entity(name = "nbpm_processblock")
    @NamedQuery(name = "ProcessBlock.findByName", query = "select p from nbpm_processblock p where p.name=?1")
    public class ProcessBlock implements Serializable {
        private static final long serialVersionUID = 1L;
        @Id
        @Column(name = "id")
        long id;
        @Column(name = "name")
        String name;
        @Column(name = "description")
        String description;
        @OneToMany(mappedBy="processblock",cascade=CascadeType.ALL,fetch = FetchType.EAGER)
        //@JoinColumn(name="processblock_id")
        Set<Node> nodeSet = new HashSet<Node>();
        public long getId() {
            return id;
        }
    
        public void setId(long id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String desc) {
            this.description = desc;
        }
    
        public Set<Node> getNodeSet() {
            return nodeSet;
        }
    }
  • 創建業務倉儲,繼承JpaRepository接口,並在倉儲接口上添加注解@Repository,這樣就可以被spring組件掃描器自動掃描。
  • package simm.spring.dao.repositories;
    
    import java.util.List;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.Query;
    import org.springframework.data.repository.query.Param;
    import org.springframework.stereotype.Repository;
    
    import simm.spring.entity.ProcessBlock;
    
    @Repository
    public interface ProcessBlockRepository extends JpaRepository<ProcessBlock,Long> {
        //數據庫直接查詢方式
        List<ProcessBlock> findByName(String name);
        //HQL 查詢方式 => Hibernate查詢方式
        @Query(value = "select u from simm.spring.entity.ProcessBlock u where u.name=:name") List<ProcessBlock> findByName1(@Param("name") String name); //原生SQL查詢方式 #{#entityName} 變量可以替換 數據庫表名
        @Query(value = "select * from #{#entityName} u where u.name=?1", nativeQuery = true)
        List<ProcessBlock> findByName2(String name);
    }
  • 調用存儲方法。關於ApplicationContextUtil.instance.getBean的實現請看《spring 代碼中獲取ApplicationContext(@AutoWired,ApplicationListener)》
  •     @RequestMapping(value = "/list", method = RequestMethod.GET)
        public String list(ModelMap model,HttpSession session) {
            ProcessBlockRepository processBlock = ApplicationContextUtil.instance.getBean(ProcessBlockRepository.class); List<ProcessBlock> list = processBlock.findByName1("主干流程");

   二、實現自己JpaUtil工具類。在實現這個功能過程中,通過調試Jpa的調度,發現繼承JpaRepository接口的倉儲,在實際運行過程中,框架會通過一系列的反射最終調用org.springframework.data.jpa.repository.support.SimpleJpaRepository<T, ID>基礎類里的方法。這個類實現了一系列增刪改查的方法,可做為參考進行功能擴展。

  • JpaUtil類代碼展示
  • @Transactional(readOnly=true)
    @Repository
    public class JpaUtil{
        @PersistenceContext
        private EntityManager em;
        /**
         * 獲取列表
         * @param sql
         * @param params
         * @param requiredType
         * @return
         */
        public <T> List<T> list(String sql,Map<String, Object> params,Class<T> requiredType) {
            //String hql = "select o.uuid,o.name from UserModel o where 1=1 and o.uuid=:uuid";
            Query query = em.createQuery(sql);
            if (params != null) {  
                for (String key : params.keySet()) {  
                    query.setParameter(key, params.get(key));  
                }  
            } 
            return query.getResultList();
        }
        /**
         * 獲取分頁數據
         * @param sql
         * @param params
         * @param pageable
         * @param requiredType
         * @return
         */
        @SuppressWarnings("unchecked")
        public <T> Page<T> page(String sql,Map<String, Object> params,Pageable pageable,Class<T> requiredType) {
            Query query = em.createQuery(sql);
            if (params != null) {  
                for (String key : params.keySet()) {  
                    query.setParameter(key, params.get(key));  
                }  
            }
            if (pageable.isPaged()) {
                query.setFirstResult((int) pageable.getOffset());
                query.setMaxResults(pageable.getPageSize());
            }
            /**
             * 生成獲取總數的sql
             */
            TypedQuery<Long> cQuery = (TypedQuery<Long>) em.createQuery(QueryUtils.createCountQueryFor(sql));
            return PageableExecutionUtils.getPage(query.getResultList(), pageable, ()->executeCountQuery(cQuery));
            //return new PageImpl<T>(query.getResultList(), pageable, executeCountQuery(cQuery));
        }
        
        /**
         * Executes a count query and transparently sums up all values returned.
         *
         * @param query must not be {@literal null}.
         * @return
         */
        private static Long executeCountQuery(TypedQuery<Long> query) {
            Assert.notNull(query, "TypedQuery must not be null!");
            List<Long> totals = query.getResultList();
            Long total = 0L;
            for (Long element : totals) {
                total += element == null ? 0 : element;
            }
            return total;
        }
    }
  • org.springframework.data.jpa.repository.query.QueryUtils 這個工具是spring data jpa提供的基礎的sql語句處理類,通過正則完成sql語句的檢索或處理。我這里用QueryUtils.createCountQueryFor完成查詢根據sql自動生成count語句,計算查詢總數據量的功能。
  • org.springframework.data.repository.support.PageableExecutionUtils 是JPA提供的分頁工具類。這里使用PageableExecutionUtils.getPage完成分頁功能。
  • 代碼調用示例
  • @RequestMapping(value = "/list2", method = RequestMethod.GET)
        public String list2(ModelMap model,HttpSession session) {
            TProcessBlockRepository processBlock = ApplicationContextUtil.instance.getBean(TProcessBlockRepository.class);
            Pageable pageable = new PageRequest(0, 5);
            //Page<ProcessBlock> list = processBlock.findAll(pageable);
            Page<ProcessBlock> list = ApplicationContextUtil.instance.getBean(JpaUtil.class).page( "select u from simm.spring.entity.ProcessBlock u", null,pageable,ProcessBlock.class);
            
            model.addAttribute("list", list.getContent());
            return "/process/list";
        }

     

  • List<Node> nodeList = ApplicationContextUtil.instance.getBean(JpaUtil.class).list(
                    "select n from simm.spring.entity.Node n", null, Node.class);

  最終用JpaUtil來實現數據查詢的話,就可以省略掉定義倉儲接口的過程。但是倉儲還是要定義的,畢竟其提供的一系列的基本實現還是很重要的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM