自定义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>接口,实现自己的业务仓储。
- 定义实体,与数据库表映射。具体的数据库连接,与配置这里不再赘述,如有不清楚,可以翻看我前面的博文。
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来实现数据查询的话,就可以省略掉定义仓储接口的过程。但是仓储还是要定义的,毕竟其提供的一系列的基本实现还是很重要的。
自定义JpaUtil,快速完成Hql执行逻辑(一)的更多相关文章
- 使用Style自定义ListView快速滑动图标
一.显示ListView快速滑动块图标 设想这样一个场景,当ListView的内容有大于100页的情况下,如果想滑动到第80页,用手指滑动到指定位置,无疑是一件很费时的事情,如果想快速滑动到指定的位置 ...
- ETL-kettle 核心执行逻辑
一.大数据下的ETL工具是否还使用Kettle kettle 作为通用的ETL工具,非常成熟,应用也很广泛,这里主要讲一下 目前我们如何使用kettle的? 在进行大数据处理时,ETL也是大数据处理的 ...
- 大数据学习day22------spark05------1. 学科最受欢迎老师解法补充 2. 自定义排序 3. spark任务执行过程 4. SparkTask的分类 5. Task的序列化 6. Task的多线程问题
1. 学科最受欢迎老师解法补充 day21中该案例的解法四还有一个问题,就是当各个老师受欢迎度是一样的时候,其排序规则就处理不了,以下是对其优化的解法 实现方式五 FavoriteTeacher5 p ...
- 绝对干货:自定义msi安装包的执行过程
有时候我们需要在程序中执行另一个程序的安装,这就需要我们去自定义msi安装包的执行过程. 比如我要做一个安装管理程序,可以根据用户的选择安装不同的子产品.当用户选择了三个产品时,如果分别显示这三个产品 ...
- scala(二) Future执行逻辑解读
在scala中是没有原生线程的,其底层使用的是java的Thread机制.但是在scala中对java Thread进行了封装,实现了更便于操作线程的Future. 官方文档: Futures pro ...
- Android 自定义支持快速搜索筛选的选择控件(一)
Android 自定义支持快速搜索筛选的选择控件 项目中遇到选择控件选项过多,需要快速查找匹配的情况. 做了简单的Demo,效果图如下: 源码地址:https://github.com/whieenz ...
- defer、return、返回值,这三者的执行逻辑
defer.return.返回值,这三者的执行逻辑是: return 最先执行,return 负责将结果写入返回值中:接着defer执行,可能修改返回值:最后函数携带当前返回值退出.
- {Django基础九之中间件} 一 前戏 二 中间件介绍 三 自定义中间件 四 中间件的执行流程 五 中间件版登陆认证
Django基础九之中间件 本节目录 一 前戏 二 中间件介绍 三 自定义中间件 四 中间件的执行流程 五 中间件版登陆认证 六 xxx 七 xxx 八 xxx 一 前戏 我们在前面的课程中已经学会了 ...
- 自定义msi安装包的执行过程
有时候我们需要在程序中执行另一个程序的安装,这就需要我们去自定义msi安装包的执行过程. 比如我要做一个安装管理程序,可以根据用户的选择安装不同的子产品.当用户选择了三个产品时,如果分别显示这三个产品 ...
随机推荐
- 史上最全的判断android,ios还是ipad访问,附上多种语言的实现方式
js判断: (function(a,b){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elain ...
- linux_思想
linux有哪些重要的思想? 1. 做的越多错的越多 2. 纸包不住火 3. 操作重要文件前备份,操作后查看结果 4. 看到命令输出结果,可能命令有个选择直接获得对应值 5. 先定行,再定列
- char (*p)[]和char *p[]的区别
理解的关键在于: 1. []的优先级高于*,(*p)[]理解为指向一个数组,*(p[])存放指针的数组 2. char (*p)[SIZE]:指向一维数组的指针,一维数组只能有SIZE个元素 char ...
- android 中string.xml中的%1$s
idView.setText(getString(R.string.estate_id, mCollectParamObj.getPlotNo())); estate_id:小区号%1$s %d ...
- 常用API
1 System类 System类包含一些有用的类和字段.它不能被实例化. 属性和方法都是静态的. 1.1 常见方法 返回以毫秒为单位的当前时间 public static long currentT ...
- Linux安装JDK、MySQL和Tomcat
1 依赖的安装 因为JDK.Tomcat和MySQL的安装过程中需要从网上下载部分支持包才可以继续,所以需要提前安装好依赖. yum install glibc.1686 yum -y install ...
- input标签的美化
css input checkbox和radio样式美化:http://www.haorooms.com/post/css_mh_ck_radio 自定义 css checkbox 样式 :http ...
- matlab获取文件夹中的所有文件名(dir)
当前目录中包含文件及目录如下: abc111.txt abc112.txt abc113.txt a\ (文件夹) CODE: >> dir('test') %目录 . ...
- 怎么使用IDEA
war 和 war exploded war部署首先通过IDEA生成.war工程文件,然后将WEB工程以包的形式上传到服务器,因此会替代服务器本来同名的web app项目. war exploded模 ...
- [UWP]实现Picker控件
1. 前言 在WPF中,很多打开下拉框(Popup或Flyout)选择一个结果值的控件,除了ComboBox等少数例外,这种控件都以-Picker做名称后缀.因为要打开关闭下拉框和计算下拉框的弹出位置 ...