Spring Boot JPA 批量插入
Posted
技术标签:
【中文标题】Spring Boot JPA 批量插入【英文标题】:Spring Boot JPA Bulk insert 【发布时间】:2018-05-17 20:10:37 【问题描述】:我有 3 个实体父、子、子子。 Parent 是 Child 的 parent, Child 是 SubChild 的 parent。我需要插入大约 700 个 Parent 对象。父级可以拥有 50 个子级对象。 Child 可以拥有 50 个 SubChild 对象。
我尝试了正常的repository.save(ListOfObjects)
大约需要 4 分钟。
然后我尝试根据批量大小(500)使用实体管理器的persist
、flush
和clear
。这也花了大约 4 分钟。
性能上没有太大区别。请提出一种有效插入如此大量数据的最佳方法。
父母
@Entity
public class Parent
@Id @GeneratedValue(strategy= GenerationType.AUTO)
private Long parentId;
private String aaa;
private String bbb;
private String ccc;
@Version
private Long version;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "parent", fetch = FetchType.LAZY)
@JoinColumnsOrFormulas(
@JoinColumnOrFormula(column=@JoinColumn(name="parentId",referencedColumnName="parentId",nullable=false)))
private List<Child> childs = new ArrayList<>();
public Long getParentId()
return parentId;
public void setParentId(Long parentId)
this.parentId = parentId;
public String getAaa()
return aaa;
public void setAaa(String aaa)
this.aaa = aaa;
public String getBbb()
return bbb;
public void setBbb(String bbb)
this.bbb = bbb;
public String getCcc()
return ccc;
public void setCcc(String ccc)
this.ccc = ccc;
public Long getVersion()
return version;
public void setVersion(Long version)
this.version = version;
public List<Child> getChilds()
return childs;
public void setChilds(List<Child> childs)
this.childs = childs;
孩子
@Entity
public class Child
@Id @GeneratedValue(strategy= GenerationType.AUTO)
private Long childId;
private String ddd;
private String ccc;
private Integer eee;
@OneToMany(cascade = CascadeType.ALL,orphanRemoval = true, mappedBy = "child", fetch = FetchType.LAZY)
@JoinColumnsOrFormulas(
@JoinColumnOrFormula(column = @JoinColumn(name = "childId", referencedColumnName = "childId", nullable = false)) )
private List<SubChild> subChilds = new ArrayList<>();
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumnsOrFormulas(
@JoinColumnOrFormula(column= @JoinColumn( name="parentId",referencedColumnName="parentId",nullable=false))
)
private Parent parent;
public Long getChildId()
return childId;
public void setChildId(Long childId)
this.childId = childId;
public String getDdd()
return ddd;
public void setDdd(String ddd)
this.ddd = ddd;
public String getCcc()
return ccc;
public void setCcc(String ccc)
this.ccc = ccc;
public Integer getEee()
return eee;
public void setEee(Integer eee)
this.eee = eee;
public List<SubChild> getSubChilds()
return subChilds;
public void setSubChilds(List<SubChild> subChilds)
this.subChilds = subChilds;
public Parent getParent()
return parent;
public void setParent(Parent parent)
this.parent = parent;
子子
@Entity
public class SubChild
@Id @GeneratedValue(strategy= GenerationType.AUTO)
private Long subChildId;
private String fff;
private String ggg;
private Integer hhh;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumnsOrFormulas(
@JoinColumnOrFormula(column= @JoinColumn( name="childId",referencedColumnName="childId",nullable=false))
)
private Child child;
public Long getSubChildId()
return subChildId;
public void setSubChildId(Long subChildId)
this.subChildId = subChildId;
public String getFff()
return fff;
public void setFff(String fff)
this.fff = fff;
public String getGgg()
return ggg;
public void setGgg(String ggg)
this.ggg = ggg;
public Integer getHhh()
return hhh;
public void setHhh(Integer hhh)
this.hhh = hhh;
public Child getChild()
return child;
public void setChild(Child child)
this.child = child;
用于持久化父实体列表的存储库方法
@Value("$spring.jpa.hibernate.jdbc.batch_size")
private int batchSize;
public <T extends Parent> Collection<T> bulkSave(Collection<T> entities)
final List<T> savedEntities = new ArrayList<T>(entities.size());
int i = 0;
for (T t : entities)
savedEntities.add(persistOrMerge(t));
i++;
if (i % batchSize == 0)
// Flush a batch of inserts and release memory.
entityManager.flush();
entityManager.clear();
return savedEntities;
private <T extends Parent> T persistOrMerge(T t)
if (t.getTimeSlotId() == null)
entityManager.persist(t);
return t;
else
return entityManager.merge(t);
application.yml
spring:
application:
name: sample-service
jpa:
database: mysql
show-sql: true
hibernate:
ddl-auto: update
dialect: org.hibernate.dialect.MySQL5Dialect
naming_strategy: org.hibernate.cfg.ImprovedNamingStrategy
jdbc:
batch_size: 100
jackson:
date-format: dd/MM/yyyy
thymeleaf:
cache: false
spring.datasource.url : jdbc:mysql://$dbhost/sample?createDatabaseIfNotExist=true
spring.datasource.username : root
spring.datasource.password : root
spring.datasource.driver-class-name : com.mysql.cj.jdbc.Driver
【问题讨论】:
刷新后你会得到 50 个插入还是一个? 如果我错了,请纠正我,因为事务是由 @transactional 注释处理的,我认为提交发生在最后阶段。所以我认为我们刷新时它不会插入任何记录。 对不起,以为你在每批之后都提交了。所以在提交时.. 日志显示每个对象插入一个?.. 所以 700? ***.com/questions/45670583/… @MaciejKowalski 它在提交时插入 700 个对象 【参考方案1】:要启用批量插入,您需要配置中的 batch_size 属性。
此外,由于 jdbc 批处理只能针对一个表,因此您需要 spring.jpa.hibernate.order_inserts=true
属性来对父子之间的插入进行排序,否则语句是无序的,您将看到部分批处理(新批处理随时插入不同的表被调用)
【讨论】:
以上是关于Spring Boot JPA 批量插入的主要内容,如果未能解决你的问题,请参考以下文章