• 自定义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来实现数据查询的话,就可以省略掉定义仓储接口的过程。但是仓储还是要定义的,毕竟其提供的一系列的基本实现还是很重要的。

  • 相关阅读:
    nodeJS grunt karma+jasmine+require 快速构建前台自动化测试环境搭建
    js闭包
    白鹭 接入vivo小游戏
    白鹭 字节跳动 接入小游戏
    白鹭声音播放问题汇总
    白鹭 有人好奇为什么hashcode 增长的那么快么?都创建了些什么?
    Mac node 安装
    白鹭 修改底层 egret.js 库后再次编译 成 新的库
    js 获取字典长度
    egret 性能优化
  • 原文地址:https://www.cnblogs.com/MrSi/p/8074460.html
Copyright © 2020-2023  润新知