使⽤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("张三");jpa mybatis
student.setSex(1);
student.setSeq("123456");
studentRepo.save(student);
Assert.Id());
}
在插⼊过程中使⽤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 = ateNativeQuery(sql);
int paramIndex = 1;
for(StudentDO student : students) {
query.setParameter(paramIndex++, Name());
query.setParameter(paramIndex++, Sex());
}
uteUpdate();
}
就像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进⾏⾃定义保存及批量保存功能,希望对⼤家有所帮助,如果⼤家有任何疑问欢迎给我留⾔,⼩编会及时回复⼤家的!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论