使用SpringBoot-JPA进行自定义保存及批量保存功能

说明

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, String> {
}

一般的,我们只需要调用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的使用还不够了解,一些复杂的情况下没有找到最理想的实现方案。

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

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

以上所述是小编给大家介绍的使用SpringBoot-JPA进行自定义保存及批量保存功能,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

(0)

相关推荐

  • 详解spring boot jpa整合QueryDSL来简化复杂操作

    前言 使用过spring data jpa的同学,都很清楚,对于复杂的sql查询,处理起来还是比较复杂的,而本文中的QueryDSL就是用来简化JPA操作的. Querydsl定义了一种常用的静态类型语法,用于在持久域模型数据之上进行查询.JDO和JPA是Querydsl的主要集成技术.本文旨在介绍如何使用Querydsl与JPA组合使用.JPA的Querydsl是JPQL和Criteria查询的替代方法.QueryDSL仅仅是一个通用的查询框架,专注于通过Java API构建类型安全的SQL查

  • SpringBoot Data JPA 关联表查询的方法

    SpringBoot Data JPA实现 一对多.多对一关联表查询 开发环境 IDEA 2017.1 Java1.8 SpringBoot 2.0 MySQL 5.X 功能需求 通过关联关系查询商店Store中所有的商品Shop,商店对商品一对多,商品对商店多对一,外键 store_id存在于多的一方.使用数据库的内连接语句. 表结构 tb_shop tb_store 实体类,通过注解实现 1.商店类Store.java package com.gaolei.Entity; import ja

  • springboot使用JPA时间类型进行模糊查询的方法

    这个问题是我自己开发中遇到的问题  数据库使用的是mysql5.6  字段名称为checkingTime  类型为timestamp 显而易见 存到库中的是保留6位毫秒 即yyyy-MM-dd HH:mm:ss.ssssss  此时需求是精确到分钟的相同时间 不进行存储 这时候就需要进行模糊查询   搜了一圈百度 并没有什么好用的方法 我的bean类定义的是date类型 使用注解将类型更改为timestamp 存入库中 其实在做模糊查询的时候  只需要向持久层传入String类型参数即可 我的做

  • SpringBoot中logback日志保存到mongoDB的方法

    Springboot默认集成的就是logback,logback相对来说是优秀于log4j的,log4j2也是参考了logback的设计. 自定义Appender非常简单,继承一下AppenderBase类即可. 可以看到有个AppenderBase,有个UnsynchronizedAppenderBase,还有个AsyncAppenderBase继承了UnsynchronizedAppenderBase.从名字就能看出来区别,异步的.普通的.不加锁的. 我们定义一个MongoDBAppend

  • IDEA+maven+SpringBoot+JPA+Thymeleaf实现Crud及分页

    一.开发环境: 1.windows 7 企业版 2.IDEA 14 3.JDK 1.8 4.Maven 3.5.2 5.MariaDB 6.SQLYog 二.Maven设置: Maven目录下的conf目录下的settings.xml做如下内容的添加: 1.使用阿里云的仓库,比官网访问速度快很多 <mirror> <id>nexus-aliyun</id> <mirrorOf>central</mirrorOf> <name>Nexu

  • 使用SpringBoot-JPA进行自定义保存及批量保存功能

    说明 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 =

  • SpringBoot+JPA 分页查询指定列并返回指定实体方式

    目录 SpringBoot+JPA分页查询指定列并返回指定实体 SpringBoot JPA实现自定义语句分页查询 SpringBoot+JPA分页查询指定列并返回指定实体 用习惯Mybatis,没用过jpa 真是各种踩坑了 脑壳疼,一个分页弄老半天,原来就一句话的事情,唉 先来说说正常的JPA如何操作 实体类对应表来创建,举个例子 @Entity @Table(name = "td_user") public class TdUser extends BaseModel { priv

  • SpringBoot+JPA 分页查询指定列并返回指定实体方式

    目录 SpringBoot JPA分页查询指定列并返回指定实体 实体类对应表来创建,举个例子 SpringBoot JPA实现自定义语句分页查询 1.JPA持久层 InvoiceRepository.java 2.服务层 SpringBoot JPA分页查询指定列并返回指定实体 用习惯Mybatis,没用过jpa 真是各种踩坑了 脑壳疼,一个分页弄老半天,原来就一句话的事情,唉 先来说说正常的JPA如何操作 实体类对应表来创建,举个例子 @Entity @Table(name = "td_use

  • 解决spring data jpa 批量保存更新的问题

    spring data jpa 批量保存更新问题 使用jpa批量保存时,看日志发现是一条一条打印的,然后去看了下源码,果然是循环调用的单个保存(巨坑啊) 经查询jpa是可以实现批量保存更新的,具体设置如下: spring.jpa.properties.hibernate.jdbc.batch_size=500 spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true spring.jpa.properties.hibernate

  • SpringBoot Jpa 自定义查询实现代码详解

    这篇文章主要介绍了SpringBoot Jpa 自定义查询实现代码详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 持久层Domain public interface BaomingDao extends JpaRepository<BaomingBean,Integer> { @Query(value = "select distinct t.actid from BaomingBean t where t.belongs=?

  • Springboot JPA 枚举Enum类型存入到数据库的操作

    1.使用JPA 的@Enumerated 注解 ,可以直接将Enum映射到数据库中. 但是value的值只有两种方式选择,一种是使用枚举的序号映射,一种是枚举的名称来映射. public enum EnumType { /** Persist enumerated type property or field as an integer. */ ORDINAL, /** Persist enumerated type property or field as a string. */ STRIN

  • SpringBoot数据访问自定义使用Druid数据源的方法

    数据访问之Druid数据源的使用 说明:该数据源Druid,使用自定义方式实现,后面文章使用start启动器实现,学习思路为主. 为什么要使用数据源: ​数据源是提高数据库连接性能的常规手段,数据源会负责维持一个数据连接池,当程序创建数据源实例时,系统会一次性地创建多个数据库连接,并把这些数据库连接保存在连接池中. ​当程序需要进行数据库访问时,无须重新获得数据库连接,而是从连接池中取出一个空闲的数据库连接. ​当程序使用数据库连接访问数据库结束后,无须关闭数据库连接,而是将数据库连接归还给连接

  • Springboot整合PageOffice 实现word在线编辑保存功能

    目录 一.查看官网 二.查看下载包 三.测试 四.gitee地址 一.查看官网 http://www.zhuozhengsoft.com/ 点击首页下载,进入页面: 最新得5.2,我们就下载5.2版本进行测试. 二.查看下载包 Samples5 为示例文件.放入tomcat中得webapps可以直接访问. localhost:8080/Samples5/index.html集成文件 里面有我们需要jar包 新建springboot项目以及简单测试这里就不多说了. 1.springboot 引入

  • SpringBoot结合Neo4j自定义cypherSql的方法

    前言 SpringBoot引入neo4j <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-neo4j</artifactId> </dependency> <spring.boot.version>2.2.11.RELEASE</spring.boot.version> 大

  • springboot jpa之返回表中部分字段的处理详解

    目录 springboot jpa返回表中部分字段 JPA 自定义返回字段 springboot jpa返回表中部分字段 使用springboot jpa操作数据库可以加快我们的开发效率,对于简单的crud操作来说,使用jpa来开发不要太爽,但是说实话对于一些复杂的数据库操做jpa使用起来就不是这么爽了. 在开发中很多时候我们要返回的可能只是数据库表中或某个类中的一部分字段,这个要是用mybatis的话就很简单,直接在sql中select字段就好了,规范一点就数据传输类接一下,偷个懒的话直接用m

随机推荐