首页 > 解决方案 > 有什么方法可以让 JPA 更快地持久化对象?

问题描述

使用 JPA、Hibernate/MVC/Servlets,我正在尝试在我的数据库中上传一个 .csv 文件。该文件有 90k+ 行长。我设法开始工作的是使用 Servlet 上传 .csv,读取每一行并将行拆分为列插入。在我的 while 循环中,我创建了 EntityManager,将数据插入到一个新对象中,持久化并提交。我工作,但通过所有数据大约需要 5 小时。有什么我可以改变的吗?我对编程比较陌生。这是我的代码:

Scanner s = new Scanner(myCSVasInputStream).useDelimiter("\\n");
while(s.hasNext()) {
try{
EntityManager em = JPAUtils.createEntityManager();
em.getTransaction().begin();
String row = s.next();
String[] column = row.split(;);
myEntity m = new myEntity();
m.setValue(column[0]);
em.persist(m);
em.getTransaction().commit();
} catch (Excepltion e) {
system.out.print("I think i need to catch exception for duplicate rows");
}
}

标签: javahibernatejpa

解决方案


即使是带有 HDD 的旧 2010 计算机也可以轻松插入 1000 行/秒。但是您必须避免使用不良软件/算法。

清单:

a) 仔细检查您的数据库调整。例如,开发模式 mysql 是对真实服务器的糟糕引用。它具有微小的缓冲区、池等……Linux VM 导入 mysqldump 的速度比 Windows 快 10 倍……举个例子……让服务器为艰苦的工作做好准备。

b)如果您要将@Transactional 放在所有内容上,那么您可能会为每一行经历很多Java堆栈帧。检查您是否受 CPU 限制。对于这样的插入,您应该是 io 有界的。在这里,您对每一行都进行 TXN...

c)也不要尝试进行 90000 行的单个事务,这很愚蠢。我知道你不是,保持这种状态。也许完全避免使用 TXN,因为我看不到您的案例的好处。

如果你可以避免休眠,那么你有几个选择:

d) 显然,服务器端准备好的语句更可取。

e)在准备好的语句之上,使用批处理语句(一次可以做 20-100 个,找到最佳位置)。

f)如果你真的不是胆小,并且没有人以你的方式抱怨他们不理解的事情并在该死的代码审查中阻止你的拉取请求(!),你实际上可以让 mysql 直接读取 CSV,带有可加载文件(LOAD DATA INFLE https://dev.mysql.com/doc/refman/8.0/en/load-data.html)。Postgress 有类似的东西。大多数供应商都应该这样做。通常,您必须将整理后的 CSV 文件(适当的分隔符和引号)存储在 DB 服务器本身上,因此这在远程 DB 上甚至是不可能的。但它的速度惊人地快。为了实现这一点,几乎值得在数据库实例上放置一个文件上传微服务。

希望这可以帮助。


推荐阅读