博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JPA进行insert操作时会首先select吗
阅读量:5312 次
发布时间:2019-06-14

本文共 4209 字,大约阅读时间需要 14 分钟。

在某个项目中,使用JPA的saveAll方法去批量写入数据时,通过打印sql,发现每次insert前都会先select一次,极大的浪费了写入性能。

分析一下代码,saveAll()

@Transactionalpublic  List saveAll(Iterable entities) {     Assert.notNull(entities, "The given Iterable of entities not be null!");     List result = new ArrayList();     for (S entity : entities) {        result.add(save(entity));  //在此处进行保存操作    }     return result;}

save()

@Transactionalpublic  S save(S entity) {     if (entityInformation.isNew(entity)) {        em.persist(entity);        return entity;    } else {        return em.merge(entity);    }}

通过断点调试,可以发现是在判断isNew时候,进入了merge方法,总而造成先select,再写入,我个人理解其实是进行了update操作。

查看isNew方法的父类方法,在AbstractEntityInformation类中

public boolean isNew(T entity) {     ID id = getId(entity);    Class
idType = getIdType(); if (!idType.isPrimitive()) { return id == null; } if (id instanceof Number) { return ((Number) id).longValue() == 0L; } throw new IllegalArgumentException(String.format("Unsupported primitive id type %s!", idType));}

可以看出,程序会判断实体的id是否为空,如果为空则是新数据,非空一般就是旧数据,进行update。

不过实际情况下,我的ID是UUID,并且也人为确认这个UUID在数据库中并不存在。那为何会出现这个问题呢?

看看我目前使用的实体类

package com.haramasu.simple_jpa.test.entity; import javax.persistence.Entity;import javax.persistence.Id;import javax.persistence.Version; /*** @author: Ding, Shuo* @description:* @create: 2019-03-14 11:04**/@Entitypublic class School {    @Id    String id;    String name;     public School() {    }     public School(String id, String name) {        this.id = id;        this.name = name;    }     public String getId() {        return id;    }     public void setId(String id) {        this.id = id;    }     public String getName() {        return name;    }     public void setName(String name) {        this.name = name;    }}

对于ID,使用的@Id注解,在构建实体对象的时候,ID需要我手动通过set方法或构造函数赋值。

尝试将ID的注解改为

@Id@GenericGenerator(name = "id-generator", strategy = "uuid")@GeneratedValue(generator = "id-generator")

再次尝试saveAll,断点处,isNew方法会判定id为null,本条数据是新数据,便不会再先进行select操作再insert。

猜想(暂不具体研究),是@GeneratedValue注解会在isNew方法执行后才对id进行赋值,而常规手动赋值ID,则会在isNew方法之前完成。

所以这就是第一种解决方法

但是有时候并没有合适的@GenericGenerator给我们使用,必须需要手动赋值ID,该如何实现?

通过StackOverflow搜索,发现第二种解决方法,可以通过在实体类中加入一个注解为@Version的属性,所以我的实体类现在长这样

@IdString id;String name;@Versionprivate Long version;

使用版本号进行锁控制,此时判断isNew的时候就会比对version值,如果不为新,则不需要在select+insert了。

不过这种方法会使数据库表中多出一个字段,如果不希望出现多余字段的话。那么接下来就是第三种方法

参考方法一,进行自定义ID生成器

package com.haramasu.simple_jpa.test.generator; import org.hibernate.HibernateException;import org.hibernate.MappingException;import org.hibernate.engine.spi.SharedSessionContractImplementor;import org.hibernate.id.Configurable;import org.hibernate.id.IdentifierGenerator;import org.hibernate.service.ServiceRegistry;import org.hibernate.type.Type; import java.io.Serializable;import java.util.Properties;import java.util.UUID; /*** @author: Ding, Shuo* @description:* @create: 2019-03-14 13:34**/public class MyGenerator implements Configurable, IdentifierGenerator {     @Override    public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {     }     @Override    public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {        return UUID.randomUUID().toString();    }}

修改实体类

package com.haramasu.simple_jpa.test.entity; import org.hibernate.annotations.GenericGenerator; import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;import javax.persistence.Version; /*** @author: Ding, Shuo* @description:* @create: 2019-03-14 11:04**/@Entitypublic class School {    @Id    @GeneratedValue(generator = "id_generator")    @GenericGenerator(name = "id_generator",strategy = "com.haramasu.simple_jpa.test.generator.MyGenerator")    String id;    String name;     public School(String name) {        this.name = name;    }     public String getId() {        return id;    }     public void setId(String id) {        this.id = id;    }     public String getName() {        return name;    }     public void setName(String name) {        this.name = name;    }}

再次尝试,不会再出现select了。

如上就是三种解决新数据写入时候实际执行的时update操作的方法了。

转载于:https://www.cnblogs.com/tilv37/p/10529853.html

你可能感兴趣的文章
eclipse_中的注释_快捷键
查看>>
ADB server didn't ACK
查看>>
IIS 7.0的集成模式和经典模式
查看>>
Discrete Log Algorithms :Baby-step giant-step
查看>>
HDOJ1860 ( 统计字符 ) 【水题】
查看>>
hdu 1863(最小生成树kruskal)
查看>>
史上最全的Angular.js 的学习资源
查看>>
IOS原生地图与高德地图
查看>>
团队项目开发篇章8
查看>>
Java SpringMvc+hibernate架构中,调用Oracle中的sp,传递数组参数
查看>>
经验总结03-dwr
查看>>
[BZOJ 1072] 排列perm
查看>>
带有控制按钮的图片滚动
查看>>
11月16日站立会议
查看>>
Web前端笔试面试题汇总(转自github)
查看>>
Comparison of video container formats
查看>>
网络编程资源
查看>>
Android SDK 目录说明
查看>>
嵌入式第11次实验
查看>>
管道模式 pipe
查看>>