更多精彩博文,欢迎访问我的个人博客


说明

SpringBoot版本:2.1.4.RELEASE

java版本:1.8

文中所说JPA皆指spring-boot-starter-data-jpa

使用JPA保存一个Student对象

在JPA中保存一个对象,仅需要该对象,一个仓储即可。

StudentDO实体类:

@Getter
@Setter
@Entity
@Table(name = "t_student")
public class StudentDO {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private Long id;
@Column
private String seq;
@Column
private String name;
@Column
private int sex;
}

JPA仓储:

@Repository
public interface StudentRepo extends JpaRepository<StudentDO, Long> {
}

一般的,我们只需要调用StudentRepo.save()方法即可完成对实体对象的保存操作。

    @Test
public void testSave() {
StudentDO student = new StudentDO();
student.setName("张三");
student.setSex(1);
student.setSeq("123456");
studentRepo.save(student);
Assert.assertNotNull(student.getId());
}

在插入过程中使用mysql函数

如果我们希望student的seq值由系统自动生成,且生成规则为“yyMMdd + 8位自增序列”(例如19060310000000)又该如何实现呢?

首先想到的是该如何生成这一串序列,mysql不像oracle自身支持sequence,因此在这里可以借用函数以及额外的sequence表来实现这一操作,网上有很多实现方式,这里就不再赘述。

现在已经有了函数getseq('student_seq')可以获取到该序列,该如何将其应用到保存对象的方法中?显然的一个问题是,像上面那样再直接调用save方法已经行不通了,应该得需要自定义插入的sql实现。

一个容易想到的办法是,在StudentDO类上使用注解@SQLInsert来定义insert的实现,它写起来应该会像这个样子:

@SQLInsert(sql = "INSERT INTO t_student(seq, name, sex) VALUES (getseq('student_seq'), ?, ?")

这条sql语句本身并没有什么问题,再次调用save()方法也确实能够执行。但是很可惜,它确会抛出一个sql异常:

java.sql.SQLException: Parameter index out of range (3 > number of parameters, which is 2).

显然是程序认为有多少个参数,就得有多少个“?”与之匹配,目前我并没有找到解决这个问题的方案,所以这种方法宣告失败。

既然@SQLInsert行不通,或许可以考虑使用@Query注解来自定义一个实现。我们可以在StudentRepo中定义这样一个方法:

    @Transactional
@Modifying
@Query(value = "INSERT INTO t_student(seq, name, sex) VALUES (getseq('student_seq'), :#{#student.name}, :#{#student.sex})", nativeQuery = true)
int insert(@Param("student") StudentDO student);

试着运行一下,结果很成功,对象被正常的存储到数据库中,并且seq的取值也正常。看上去我们的问题已经得到了解决,但事实真的如此么?

自定义的批量存储

上面的例子中,我们成功通过JPA调用了mysql函数将对象存储到数据库中。但上面的例子只提供了单个保存的方法,如果我们想批量保存呢?@Query里面的sql能够进行改造么?我并没有找到@Query中使用List作为参数的insert方法,但是这并不代表这一操作不能执行。JPA仍旧提供给了使用者原始的使用方式:利用EntityManager来构造sql并执行。

    @PersistenceContext
private EntityManager entityManager; private int batchInsert(List<StudentDO> students) {
StringBuilder sb = new StringBuilder();
sb.append("INSERT INTO t_student(seq, name, sex) VALUES ");
for(StudentDO student : students) {
sb.append("(getseq('student_seq'), ?, ?),");
}
String sql = sb.toString().substring(0, sb.length() - 1);
Query query = entityManager.createNativeQuery(sql);
int paramIndex = 1;
for(StudentDO student : students) {
query.setParameter(paramIndex++, student.getName());
query.setParameter(paramIndex++, student.getSex());
}
return query.executeUpdate();
}

就像MyBatis一样,使用者也可以自定义SQL来执行,试试看,同样没有问题,再多的数据也可以被保存到数据库中!批量保存的效果达到了。

再仔细想一想,通过上面的过程,还有什么问题么?对比JPA自带的save()方法,似乎我们的自定义保存返回的都是int结果,也就是操作影响的数据库行数。使用过JPA的人都应该了解,JPA的save()方法(或者其他JPA方法)返回的对象是经过持久化的,得益于这一特性,使用者可以在调用save()方法之后获取到对象的id等必须先插入到数据库之后才会有的值。显然这里的操作已经失去了这一特性,那如果我们把返回值对应的改为Object或者List可以做到么?答案是并不能,我们会得到如下异常:

org.springframework.dao.InvalidDataAccessApiUsageException: Modifying queries can only use void or int/Integer as return type!; nested exception is java.lang.IllegalArgumentException: Modifying queries can only use void or int/Integer as return type!

insert方法必须使用@Modifying进行注解,而@Modifying注解的方法又只能返回int类型的结果。这种情况下或许只能先利用查询得到seq的值再进行操作。

总结

对于JPA的使用还不够了解,一些复杂的情况下没有找到最理想的实现方案。

  1. @Query注解中是否能够使用List以及实现动态拼接参数的效果没有得到解决
  2. 自定义的sql语句返回持久化对象的问题没有方案

在以后的使用了解中希望能够找到解决办法,将问题记录在这里,以便后续查看。


更多精彩博文,欢迎访问我的个人博客

使用SpringBoot-JPA进行自定义的保存及批量保存的更多相关文章

  1. SpringBoot Jpa 自定义查询

    SpringBoot Jpa 自定义查询 持久层Domain public interface BaomingDao extends JpaRepository<BaomingBean,Inte ...

  2. springboot jpa 批量保存数据--EntityManager和 JpaRepository

    1: 项目里面使用springboo-boot-start-data-jpa操作数据库,通过源码,在repository上继承JpaRepository 可以实现保存操作,其中源码接口为: <S ...

  3. SpringBoot JPA查询映射到自定义实体类

    和 SegmentFault上的文章(https://segmentfault.com/a/1190000021869465)一样, 都是俺账号 场景 举一个简单的栗子: 比如有一个User实体类 @ ...

  4. Springboot Jpa: [mysql] java.sql.SQLException: Duplicate entry 'XXX' for key 'PRIMARY'

    前言 1.问题背景 偶尔会出现登录请求出错的情况,一旦失败就会短时间内再也登录不上,更换浏览器或者刷新可能会暂时解决这个问题. 项目运行日志如下: 2022-07-21 09:43:40.946 DE ...

  5. JPA中自定义的插入、更新、删除方法为什么要添加@Modifying注解和@Transactional注解?

    前几天,有个同事在使用JPA的自定义SQL方法时,程序一直报异常,捣鼓了半天也没能解决,咨询我的时候,我看了一眼他的程序,差不多是这个样子的: @Repository public interface ...

  6. 补习系列(19)-springboot JPA + PostGreSQL

    目录 SpringBoot 整合 PostGreSQL 一.PostGreSQL简介 二.关于 SpringDataJPA 三.整合 PostGreSQL A. 依赖包 B. 配置文件 C. 模型定义 ...

  7. 【原】无脑操作:IDEA + maven + Shiro + SpringBoot + JPA + Thymeleaf实现基础授权权限

    上一篇<[原]无脑操作:IDEA + maven + Shiro + SpringBoot + JPA + Thymeleaf实现基础认证权限>介绍了实现Shiro的基础认证.本篇谈谈实现 ...

  8. 【原】无脑操作:IDEA + maven + Shiro + SpringBoot + JPA + Thymeleaf实现基础认证权限

    开发环境搭建参见<[原]无脑操作:IDEA + maven + SpringBoot + JPA + Thymeleaf实现CRUD及分页> 需求: ① 除了登录页面,在地址栏直接访问其他 ...

  9. 带着新人学springboot的应用08(springboot+jpa的整合)

    这一节的内容比较简单,是springboot和jpa的简单整合,jpa默认使用hibernate,所以本质就是springboot和hibernate的整合. 说实话,听别人都说spring data ...

随机推荐

  1. HBase之四--(1):Java操作Hbase进行建表、删表以及对数据进行增删改查,条件查询

    1.搭建环境 新建JAVA项目,添加的包有: 有关Hadoop的hadoop-core-0.20.204.0.jar 有关Hbase的hbase-0.90.4.jar.hbase-0.90.4-tes ...

  2. bzoj3198

    容斥原理+哈希表 恰好k个,那么上容斥原理,我们先2^6枚举相同的位置,用哈希表判断有多少个对应位置相同的元素,然后用容斥原理计算,似乎这里的容斥没有那么简单,详见这里 http://www.cnbl ...

  3. hdoj1495简单BFS

    #include <stdio.h> #include <string.h> #include <math.h> #include <algorithm> ...

  4. thinkphp整合swoole

    cli模式下执行thinkphp1.cd 项目根目录2.php index.php admin/index/index --执行 模块/控制器/方法名 异步消息队列1.服务器端核心代码 /** * 脚 ...

  5. poj2893 M*N puzzle 【n*m数码问题小结】By cellur925

    题目传送门 这个问题是来源于lydrainbowcat老师书上讲排序的一个扩展.当时讲的是奇数码问题,其实这种问题有两种问法:一种局面能否到另一种局面.到达目标局面的最小步数. 本文部分内容引用于ly ...

  6. Vant入门

    Vant是什么 简介 Vant是有赞开源的一套基于Vue2.0的Mobile组件库.通过Vant,可以快速搭建出风格统一的页面,提升开发效率.目前已有近50个组件,这些组件被广泛使用于有赞的各个移动端 ...

  7. iOS 将navigationItem.titleView设置为自定义UISearchBar (Ficow实例讲解)

    这篇文章可以解决以下问题: 1.将searchBar设置为titleView后,无法调整位置的问题 : 2.searchBar的背景色无法设置为透明色的问题: 3.searchBar输入框内用户输入的 ...

  8. 二分搜索 Codeforces Round #299 (Div. 2) C. Tavas and Karafs

    题目传送门 /* 题意:给定一个数列,求最大的r使得[l,r]的数字能在t次全变为0,每一次可以在m的长度内减1 二分搜索:搜索r,求出sum <= t * m的最大的r 详细解释:http:/ ...

  9. java 反向工具类

    1.ReflectUtil.java package com.example.scansell; import android.util.Log; import java.lang.reflect.C ...

  10. RHEL 6.5---SVN服务实现过程

    主机名 IP地址  master 192.168.30.130 slave 192.168.30.131 安装 [root@master ~]# yum install -y subversion h ...