Spring整合Hibernate实现Spring Data JPA

2023-10-27

在上一篇文章《Spring整合Hibernate实现JPA持久化》中,我们已经介绍了怎样在Spring容器中合理地集成Hibernate来实现JPA的ORM机制。但是,细心的读者会发现,上一篇文章中使用了EntityManager来直接与数据库交互,存在这一定的耦合度,更重要的是每当新增或修改新的实体Entity的管理时,都需要重复的实现EntityManager在Dao层的逻辑,有些过于模版化,重复劳动的问题存在,所以这篇文章就来介绍下解决这个问题所采用的Spring Data来自动化JPA的实现,听着很牛是吧!

l 基本储备技术介绍

l Spring Data如何实现JPA

l Spring Data JPA重构实例

一、基础储备技术介绍

在介绍如何实现之前,很有必要先介绍些储备内容。

首先,我们需要在Maven中引入spring-data-jpa软件包,参看如下内容:

<dependency>

<groupId>org.springframework.data</groupId>

<artifactId>spring-data-jpa</artifactId>

<version>1.10.1.RELEASE</version>

</dependency>

这里使用了1.10.1.RELEASE版本,注意不同版本与Spring和Hibernate版本的兼容,读者只需要按照上篇文章及这里的介绍实现即可。在这个软件包里,提供了JpaRepository接口(继承自Repository),它的作用就是留给开发者自定义Dao层接口时进行拓展继承的,其用来绑定@Entity模型以及模型对应的ID类型的,这样绑定声明后,在程序或容器启动时进行初始化时,Spring Data根据@Entity注解找到该实体,并通过反射和注解技术来获取实体类的类名字、属性以及方法等参数,然后通过其默认提供的18个方法API(这里不做介绍,读者可执行查阅资料了解),来实现对实体的管理及持久化的封装。

其次,Spring Data默认生成的实体持久化方法遵循着一种模式,有助于生成针对数据库的查询,比如:

findUserByFirstnameAndLastnameOrderByLastname()

针对这个方法,SpringData是这样的规定的:find/get/read是查询动词,不论使用哪个,功能都是一样的;User代表主题,指的是被定义为@Entity实体的类名;By仅仅是一个连接符而已,其主要是用来连接查询条件(断言),以及断言内查询条件的连接,比如:OrderByLastname;FirstnameAndLastnameOrderByLastname则比较明显,是查询SQL的条件断言,这里指的根据User的Firstname和Lastname查询记录,并根据Lastname生序排列结果,另外,还可以忽略断言条件的大小写,以及判断过滤等,具体里面的内容较多,建议读者查阅相关资料熟悉了解。

最后,我们来简单介绍下DSL(领域驱动语言):其思想主要是“求专不求全”,不像其它通用语言那样涵盖一切范围的问题,而是专门针对某一特定问题的计算机语言。在本质上,Spring Data中已经定义了一组小规模的领域驱动语言,并且持久化的细节支持,都是通过Repository方法的签名来描述的。但是,DSL也有其局限性,在其所提供的实体查询方法无法满足需求时,我们就得借助于@Query等来实现更为复杂的需求,具体会在下面实现部分介绍。

二、Spring Data如何实现JPA

1、定义查询方法,JpaRepository提供的CURD方法

UserRepository.java:

public interface UserRepository extends JpaRepository<User,String> {

// 这里不需要任何方法

}

使用JpaRepository绑定User实体(@Entity标注的实体类),并指定主键(ID)类型为String类型。此接口体内,不需要声明任何方法,直接使用由Spring Data默认提供的18个方法。

2、定义查询方法,JpaRepository规则支持的方法

UserRepository.java:

public interface UserRepository extends JpaRepository<User,String> {

// 定义查询方法,供JpaRepository默认18个方法无法满足时

User findByUsername(String userName);

// 定对查询方法,根据email或mobile查询多条记录

List<User> findByEmailOrMobile(String email,String mobile);

}

这里的两个方法,虽然是开发者自行定义的,但是它们满足Spring Data的规则,所以开发者不需要具体实现这些方法,所有的工作都交给SpringData自动完成。

3、声明自定义查询方法,采用@Query封装SQL操作

UserRepository.java:

public interface UserRepository extends JpaRepository<User,String> {

// 声明自定义查询,根据email模糊查询多条记录

@Query("select u from User u where u.email like '123456@%'")

List<User> findAllEmailUsers();

}

需要特殊说明的是,在SpringData默认的DSL不能满足需求时,那么就需要结合其支持的拓展注解@Query来灵活自定义更为复杂的SQL操作,当然这里只是为了演示用法,仅仅是实现了模糊匹配的查询。另外,findAllEmailUsers方法是用户自定义的,此时可以不需要符合Spring Data的规则,可随意命名。

4、混合自定义操作方法,与降级EntityManager结合

UserRepository.java:

public interface UserRepository extends JpaRepository<User,String> , UserExtRepository {

// 这里不需要任何方法

}

这里主要是在SpringData默认方法,以及@Query都不能满足需求时(虽然很少),就需要降级使用老版的EntityManager来实现了,当然,虽然结合使用了EntityManager,也就需要实现对应的接口,这里新建立了一个UserExtRepository接口,具体内容如下:

public interface UserExtRepository {

// 单一的Spring Data JPA无法完成的操作,

// 需要降低版本,结合EntityManager协同完成

public int batchUpdate(String email);

}

那么,接下来就得提供一个它的实现类:UserRepositoryImpl了,内容如下:

@Repository("UserRepositoryImpl")

public class UserRepositoryImpl implements UserExtRepository {

@PersistenceContext

private EntityManager em;

@Override

public intbatchUpdate(String email) {

String update = "update User u set u.status=1 where u.email='"+email+"'";

return em.createQuery(update).executeUpdate();

}

}

需要注意的是,SpringData在结合EntityManager实现JPA时,需要嵌入了EntityManager的实现类,如:UserRepositoryImpl,需要满足Spring Data的命名规则,其默认必须是采用xxxImpl的形式才能被识别,如果需要定义为别的格式命名,只需要在@EnableJpaRepositories开支持时(在Spring上下文配置中声明,这里使用的是JavaConfig方式),指定命名格式即可,具体如下实现:

@EnableJpaRepositories(basePackages="com.cwteam.orm.dao",repositoryImplementationPostfix="Helper")

同时,还有个需要注意的是,既然你想在Spring Data中混合使用EntityManager的功能,那么你的实现类的命名需要有一定规则,如:UserRepositoryImpl,其与我们继承JpaRepository的自定义类UserRepository相对应吧!另外,这里的basePackages的作用比较显示,用来指定扫描初始化Entity的位置。

三、Spring Data JPA重构实例

在完成了上面所有工作之后,接下来具体介绍下针对上一篇文章的例子重构,实际上改动量较小,主要修改Dao相关,以及控制器调用相关,具体各个新增或修改的内容已经在上面第二部分有介绍,这里直接罗列代码!

1、Dao层相关

新增UserRepository.java:

public interface UserRepository extends JpaRepository<User,String>,UserExtRepository {

// 定义查询方法,供JpaRepository默认18个方法无法满足时

User findByUsername(String userName);

// 定对查询方法,根据email或mobile查询多条记录

List<User> findByEmailOrMobile(String email,String mobile);

// 声明自定义查询,根据email模糊查询多条记录

@Query("select u from User u where u.email like '123456@%'")

List<User> findAllEmailUsers();

}

新增UserExtRepository.java:

public interface UserExtRepository {

// 单一的Spring Data JPA无法完成的操作,

// 需要降低版本,结合EntityManager协同完成

public int batchUpdate(String email);

}

新增UserRepositoryImpl.java:

@Repository("UserRepositoryImpl")

public class UserRepositoryImpl implements UserExtRepository {

@PersistenceContext

private EntityManager em;

@Override

public int batchUpdate(String email) {

String update = "update User u set u.status=1 where u.email='"+email+"'";

return em.createQuery(update).executeUpdate();

}

}

2、Service层

新增UserRepositoryService.java:

@Service("UserRepositoryService")

public class UserRepositoryService {

@Autowired

private UserRepository userRepository;

// 新增记录

@Transactional

public void saveUser(User user) {

userRepository.save(user);

}

// 更新记录

@Transactional

public void updateUser(User user) {

User result = findUserByID(user);

result.setUsername(user.getUsername());

result.setPassword(user.getPassword());

result.setEmail(user.getEmail());

result.setMobile(user.getMobile());

}

// 根据ID查询记录

public User findUserByID(User user) {

return userRepository.findOne(user.getId());

}

// 根据用户名查询

public User findByUsername(String username) {

return userRepository.findByUsername(username);

}

// 根据邮箱或手机查询

public List<User> findByEmailOrMobile(String email,String mobile) {

return userRepository.findByEmailOrMobile(email, mobile);

}

// 根据email模糊匹配

public List<User> findAllEmailUsers() {

return userRepository.findAllEmailUsers();

}

// 查询所有记录

public List<User> findUsersNoPage() {

return userRepository.findAll();

}

// 根据用户名删除记录

@Transactional

public void delete(User user) {

userRepository.delete(user);

}

// 批量修改status状态

@Transactional

public int batchUpdate(String email) {

return userRepository.batchUpdate(email);

}

}

3、Controller层

UserController.java:

@Controller

@RequestMapping("/user")

public class UserController {

@Autowired

UserRepositoryService userRepositoryService;

// 操作页面

@RequestMapping(value="/show",method=RequestMethod.GET)

public ModelAndView show() throws Exception {

// 业务逻辑处理

List<User> result = userRepositoryService.findUsersNoPage();

// 页面数据渲染

ModelAndView mav = new ModelAndView("test/user_operation");

mav.addObject("result", result);

return mav;

}

// 新增记录

@RequestMapping(value="/save",method=RequestMethod.POST)

public ModelAndView save(HttpServletRequest request) throws Exception {

User user = new User();

user.setUsername(request.getParameter("username"));

user.setPassword(request.getParameter("password"));

user.setMobile(request.getParameter("mobile"));

user.setEmail(request.getParameter("email"));

// 业务逻辑处理

userRepositoryService.saveUser(user);

// 页面数据渲染

ModelAndView mav = new ModelAndView("test/user_operation");

mav.addObject("result", userRepositoryService.findUsersNoPage());

return mav;

}

// 更新记录

@RequestMapping(value="/update",method=RequestMethod.POST)

public ModelAndView update(HttpServletRequest request) throws Exception {

User user = new User();

user.setId(request.getParameter("id"));

user.setUsername(request.getParameter("username"));

user.setPassword(request.getParameter("password"));

user.setMobile(request.getParameter("mobile"));

user.setEmail(request.getParameter("email"));

// 业务逻辑处理

userRepositoryService.updateUser(user);

// 页面数据渲染

ModelAndView mav = new ModelAndView("test/user_operation");

mav.addObject("result", userRepositoryService.findUsersNoPage());

return mav;

}

// 根据ID查询

@RequestMapping(value="/find",method=RequestMethod.GET)

public ModelAndView find(HttpServletRequest request) throws Exception {

User user = new User();

user.setId(request.getParameter("id"));

// 业务逻辑处理

User user2 = userRepositoryService.findUserByID(user);

List<User> result = new ArrayList<User>();

result.add(user2);

// 页面数据渲染

ModelAndView mav = new ModelAndView("test/user_operation");

mav.addObject("result", result);

return mav;

}

// 根据用户名查询

@RequestMapping(value="/findbyusername",method=RequestMethod.GET)

public ModelAndView findByUsername(HttpServletRequest request) throws Exception {

User user = new User();

user.setUsername(request.getParameter("username"));

// 业务逻辑处理

User userTmp = userRepositoryService.findByUsername(user.getUsername());

List<User> result = new ArrayList<User>();

result.add(userTmp);

// 页面数据渲染

ModelAndView mav = new ModelAndView("test/user_operation");

mav.addObject("result", result);

return mav;

}

// 根据email或mobile查询

@RequestMapping(value="/findbyemailormobile",method=RequestMethod.GET)

public ModelAndView findByEmailOrMobile(HttpServletRequest request) throws Exception {

User user = new User();

user.setEmail(request.getParameter("email"));

user.setMobile(request.getParameter("mobile"));

// 业务逻辑处理

List<User> result = userRepositoryService.findByEmailOrMobile(user.getEmail(), user.getMobile());

// 页面数据渲染

ModelAndView mav = new ModelAndView("test/user_operation");

mav.addObject("result", result);

return mav;

}

// 根据email模糊匹配查询

@RequestMapping(value="/findallemailusers",method=RequestMethod.GET)

public ModelAndView findAllEmailUsers(HttpServletRequest request) throws Exception {

// 业务逻辑处理

List<User> result = userRepositoryService.findAllEmailUsers();

// 页面数据渲染

ModelAndView mav = new ModelAndView("test/user_operation");

mav.addObject("result", result);

return mav;

}

// 根据ID删除记录

@RequestMapping(value="/delete",method=RequestMethod.GET)

public ModelAndView delete(HttpServletRequest request) throws Exception {

User user = new User();

user.setId(request.getParameter("id"));

// 业务逻辑处理

userRepositoryService.delete(user);

// 页面数据渲染

ModelAndView mav = new ModelAndView("test/user_operation");

mav.addObject("result", userRepositoryService.findUsersNoPage());

return mav;

}

// 根据email匹配批量修改

@RequestMapping(value="/batchupdate",method=RequestMethod.POST)

public ModelAndView extUpdate(HttpServletRequest request) throws Exception {

String email = request.getParameter("email");

// 业务逻辑处理

userRepositoryService.batchUpdate(email);

// 页面数据渲染

ModelAndView mav = new ModelAndView("test/user_operation");

mav.addObject("result", userRepositoryService.findUsersNoPage());

return mav;

}

}

4、View页面

具体的页面部分,请读者结合上一篇的jsp页面进行修改实现,具体实现的效果会在下面逻辑,读者也可以自行设计布局。

整体效果如下:

A、新增一条记录

新增cwteam7记录

B、修改一条记录

修改cwteam7的mobile值

C、根据ID查询

根据ID查询cwteam7记录

D、删除一条记录

删除cwteam7这条记录

E、根据用户名查询


根据用户名查询cwteam5记录

F、根据email或mobile查询

G、根据email模糊查询

查询条件,请查看UserRepository代码部分。

H、匹配email批量修改

修改的细节,请查看UserRepositoryImpl代码部分。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring整合Hibernate实现Spring Data JPA 的相关文章

随机推荐

  • MATLAB卡尔曼数字滤波惯性测量单元数据(行驶距离和速度估计)

    惯性测量单元 IMU 是惯性导航系统 INS 的一个组件 惯性导航系统是一种导航设备 用于在没有外部参考的情况下计算移动物体的位置 速度和方向 该项目开发了一种方法 用于消除加速度计测量中的偏差 并估计移动物体的行进距离和速度 估计是使用卡
  • linux 服务状态命令,Linux 查看服务列表,查看服务状态

    使用service查看命令说明 ubuntu VM 0 17 ubuntu service Usage service status all service name command full restart ubuntu VM 0 17
  • ElasticSearch聚合查询返回结果buckets取值

    ElasticSearch聚合查询返回结果buckets取值 1 聚合查询如下 size 0 query bool must wildcard county company keyword wildcard 3 boost 1
  • Stm32-SysTick详解

    写在最前 本文是个人学习Stm32时所做笔记 没有写过C51 但学校学过 微机原理 但没学好 实验套件是正点原子Stm32zet6精英板 参考资料为正点原子所提供 本文所涉及代码均使用固件库 本文供自己日后需要时复习所用 同时希望可以给有需
  • 若依框架注册新用户,且赋角色

    若依框架注册新用户 且赋角色 若依官网 1 如何开启注册功能 开启前端注册开关 不管使用下那种方式开启注册功能 首先 先在前端里面views下找到login vue 将启注册开关先给设置为true 保存重启即可 如下图 2 两种开启新增用户
  • 【每日算法 && 数据结构(C++)】—— 06

    文章目录 01 题目描述 02 解题思路 03 代码片段 Time waits for no one cherish every moment 岁月不居 时光荏苒 珍惜每分每秒 01 题目描述 给你若干个有序链表 请将他们合并为一个有序的链
  • TP-LINK 路由器设置内网穿透

    TP LINK 路由器设置内网穿透 开发中经常遇到调用第三方软件回调调试的情况 例如微信开发 支付回调等测试 用内网穿透是一种简单的方式也是偷懒的方式 以TP LINK路由器为例实现内网穿透 登录路由器 2 找到路由器虚拟服务器 添加映射
  • tsconfig之forceConsistentCasingInFileNames属性

    文章来源 Wflynn forceConsistentCasingInFileNames 作用 是否强制代码中使用的模块文件名必须和文件系统中的文件名保持大小写一致 示例 假设我们有一个文件名为 FileManager ts 我们从这个文件
  • ECharts合并地图上的区域

    对于某些特定需求 官方下载的地图数据可能并不能完全满足 例如 要求显示中国地图 但需要将山东江苏和浙江这3个省合并起来 显示 东部区域 其他省份不变 于是就需要对官方提供的地图数据进行修改 一个思路是借助第三方工具 生成新区域的轮廓点 然后
  • 刷脸支付打造数字小镇应用的全新探索

    刷脸支付打造数字小镇是特色小镇数字化应用的一项全新探索 主要是依托一部手机游云南平台 在精准治理 惠民服务 生态宜居等领域进行数字化应用 丽江大研古城数字小镇启动建设以来已经取得初步成效 1月16日 从云南省发展和改革委员会举行的1月定时定
  • 28、宏任务与微任务

    原理图 setImmediate 也是宏任务 在 Node 环境下 微任务还有 process nextTick JS 中用来存储待执行回调函数的队列包含 2 个不同特定的列队 宏列队 用来保存待执行的宏任务 回调 比如 定时器回调 DOM
  • VMware Workstation 15虚拟机使用教程

    VMware Workstation 15虚拟机使用教程 前言 一 在虚拟机中安装win7 1 1新建虚拟机一个win7虚拟机 1 2插入虚拟机光盘 即指定ISO镜像文件 1 3设置虚拟机的BIOS 光驱为第一启动 1 4开始在虚拟机中安装
  • 数据库中的字段名与实体类中的属性名不能一一对应时的三种处理方式

    当查询结果的列名和java对象的属性名对应不上时需要采用下列方式进行处理 第一种方式 在查询语句中使用关键字 as 给列起别名 第二种方式 使用resultMap结果映射 第三种方式 开启驼峰命名自动映射 配置settings 实体类Car
  • Douglas-Peucker算法的Matlab实现

    代码 Douglas Peucker 道格拉斯 普克算法 function curve dpEdgeContour pnts clc clear A readmatrix EdgeContour1 xls Sheet 1 输入数据 x A
  • ES6系列教程第二篇--Iterator 详解

    一 什么是for of循环 对于如下一个数组 遍历其中的值方法有哪些 var arr a b c 首先想到的可能就是如下这种 这也是js最原始的遍历方法 和java的语法一样 var arr a b c for var i 0 i
  • 手写代码-Hudi-Demo

    import org apache hudi config HoodieIndexConfig import org apache hudi index HoodieIndex import org apache hudi DataSour
  • 托马斯推荐-android源码网站

    2019独角兽企业重金招聘Python工程师标准 gt gt gt http grepcode com 转载于 https my oschina net u 3407708 blog 1587258
  • js的简单数组去重

    介绍几种数组去重的方法 举例数组 const arr 1 1 2 2 null null undefined undefined new String 1 new String 1 a a NaN NaN set function uniq
  • 轮胎企业RFID生产线管理(MES系统)应用

    1 项目背景 在轮胎生产制造企业中 轮胎生产信息的正确采集和存储将对控制轮胎的生产过程 质量检验和质量跟踪等方面起着重要作用 目前 企业MES系统依靠手工记录和条码扫描的方式进行数据采集 由于轮胎生产工序多且复杂 半成品物料就有十几种工序
  • Spring整合Hibernate实现Spring Data JPA

    在上一篇文章 Spring整合Hibernate实现JPA持久化 中 我们已经介绍了怎样在Spring容器中合理地集成Hibernate来实现JPA的ORM机制 但是 细心的读者会发现 上一篇文章中使用了EntityManager来直接与数