如何使用Hibernate

时间:2016-01-12 08:26:00

标签: java database performance hibernate jpa

我读取文件并从中创建一个Object并存储到postgresql数据库。我的文件有100,000个文件,我从一个文件中读取并拆分并最终存储到数据库。 我无法创建List<>并将所有文档存储在List<>中,因为我的内存很少。我读写数据库的代码如下。但是我的JVM堆填充并且无法继续存储更多文档。如何有效地读取文件和存储到数据库。

public void readFile() {
    StringBuilder wholeDocument = new StringBuilder();
    try {
        bufferedReader = new BufferedReader(new FileReader(files));
        String line;
        int count = 0;
        while ((line = bufferedReader.readLine()) != null) {
            if (line.contains("<page>")) {
                wholeDocument.append(line);
                while ((line = bufferedReader.readLine()) != null) {
                    wholeDocument = wholeDocument.append("\n" + line);
                    if (line.contains("</page>")) {
                        System.out.println(count++);
                        addBodyToDatabase(wholeDocument.toString());

                        wholeDocument.setLength(0);
                        break;
                    }
                }
            }
        }
        wikiParser.commit();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            bufferedReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public void addBodyToDatabase(String wholeContent) {
    Page page = new Page(new Timestamp(System.currentTimeMillis()),
            wholeContent);
    database.addPageToDatabase(page);
}

public static int counter = 1;

public void addPageToDatabase(Page page) {
    session.save(page);
    if (counter % 3000 == 0) {
        commit();
    }
    counter++;
}

5 个答案:

答案 0 :(得分:10)

首先,您应该在此处应用fork-join方法。

主要任务解析文件并将最多100个项目的批次发送到ExecutorServiceExecutorService应该有许多工作线程,它们等于可用数据库连接的数量。如果你有4个CPU内核,那么假设数据库可以采用8个并发连接,而​​无需进行太多的上下文切换。

然后,您应配置connection pooling DataSource并使minSize等于maxSize且等于8.尝试使用HikariCP或ViburDBCP进行连接池。

然后您需要配置JDBC batching。如果你使用MySQL,IDENTITY生成器将禁用沐浴。如果您使用的是支持序列的数据库,请确保您还使用增强的标识符生成器(它们是Hibernate 5.x中的默认选项)。

这样,实体插入过程被并行化并解耦为主解析线程。主线程应该等待ExecutorService在关闭之前完成所有任务的处理。

答案 1 :(得分:2)

实际上,如果不进行真正的分析并找出使代码变慢或效率低的原因,很难向您建议。

但是,我们可以从您的代码中看到一些内容

  1. 您使用StringBuilder效率低下

    wholeDocument.append("\n" + line);应该写成wholeDocument.append("\n").append(line);而不是

    因为您原来写的内容将由编译器翻译成 whileDocument.append(new StringBuilder("\n").append(line).toString())。您可以看到您创建了多少不必要的StringBuilder:)

  2. 使用Hibernate时的注意事项

    我不确定您是如何管理session或如何实施commit()的,我认为您做得对,还有更多事情需要考虑:

    • 您是否在Hibernate中正确设置了批量大小? (hibernate.jdbc.batch_size)默认情况下,JDBC批处理大小约为5.您可能希望确保将其设置为更大的大小(以便内部Hibernate将以更大的批量发送插入)。

    • 鉴于您不需要第一级缓存中的实体供以后使用,您可能希望间歇性会话flush() + clear()

      1. 触发上一点中提到的批量插入
      2. 清除第一级缓存
  3. 远离Hibernate以获取此功能。

    Hibernate很酷但它并不是万能的灵丹妙药。鉴于此功能,您只需根据文本文件内容将记录保存到DB中。你既不需要任何实体行为,也不需要使用第一级缓存进行后续处理,在这里没有太多理由使用Hibernate给出额外的处理和空间开销。简单地使用手动批量处理来完成JDBC将为您节省很多麻烦。

答案 2 :(得分:1)

我使用@RookieGuy回答。 stackoverflow.com/questions/14581865/hibernate-commit-and-flush

我用

session.flush();
session.clear();

最后阅读完所有文件并将其存储到数据库中

tx.commit();
session.close();

并更改

wholeDocument = wholeDocument.append("\n" + line);

wholeDocument.append("\n" + line);

答案 3 :(得分:0)

我不太确定你的数据文件的结构。如果你能提供你的文件样本,那将很容易理解。

内存消耗的根本原因是读取/迭代文件的方式。一旦读完了某些内容,就会留在内存中。您应该使用java.io.FileInputStreamorg.apache.commons.io.FileUtils

以下是使用java.io.FileInputStream

进行迭代的示例代码
try (
        FileInputStream inputStream = new FileInputStream("/tmp/sample.txt");
        Scanner sc = new Scanner(inputStream, "UTF-8")
) {
    while (sc.hasNextLine()) {
        String line = sc.nextLine();
        addBodyToDatabase(line);
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

以下是使用org.apache.commons.io.FileUtils

进行迭代的示例代码
File file = new File("/tmp/sample.txt");
LineIterator it = FileUtils.lineIterator(file, "UTF-8");
try {
    while (it.hasNext()) {
        String line = it.nextLine();
        addBodyToDatabase(line);
    }
} finally {
    LineIterator.closeQuietly(it);
}

答案 4 :(得分:0)

您应该开始一个事务,执行保存操作并提交事务。 (保存后不要开始交易!)。您可以尝试使用StatelessSession来排除缓存的内存消耗。

在此代码

中使用更少的值,例如20
if (counter % 20 == 0)

您可以尝试尽可能地将StringBuilder作为方法的参数传递。