如何使用hibernate处理并发插入和/或更新数百万条记录?

时间:2018-05-10 16:44:08

标签: java mysql database hibernate concurrency

我正在开发一种服务,它将接收数百万条必须插入两个表的记录。

表格

Foo

| id | name  |  
| 1  |  foo  |  

酒吧

| id | name | updated | foo_id |  
| 1  |  bar |    0    |   1    |  

实体

@Entity
public class Foo implements java.io.Serializable {

    @Id
    @GeneratedValue(strategy=IDENTITY)
    private Long Id;

    @Column(unique=true, nullable=false)
    private String name;

    @OneToMany(mappedBy="foo")
    private Set<Bar> bars = new HashSet<Bar>();

    // ... Constructors, gets and sets
}

酒吧

@Entity
public class Bar implements java.io.Serializable {

    @Id
    @GeneratedValue(strategy=IDENTITY)
    private Long Id;

    @Column(unique=true, nullable=false)
    private String name;

    @Column
    private boolean updated;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="foo_id")
    private Foo foo;

    // ... Constructors, gets and sets
}

这是我在的地方:

我收到一个必须插入数据库的Bar对象列表。每个Bar对象都有一个Foo关联,如果该Bar对象的Foo不在数据库中,那么它必须插入到Foo表中。此外,如果该Bar对象已经在数据库中,则必须更新它。

我的问题是我可以遇到几个并发问题,如下所示:

private session;
private transaction;

// The ModelBar and ModelFoo class have the same fields of Bar and Foo entities except it doesn't have the annotations
public void saveBars(List<ModelBar> modelBars)
{
    try 
    {
        transaction = session.beginTransaction();

        session.doWork(new Work()
        {
            @Override
            public void execute(Connection connection) throws SQLException
            {
                 connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
            }
        });

        for(ModelBar modelBar : modelBars)
        {
            Foo foo = getFoo(modelBar.getModelFoo().getName());

            insertOrUpdateBar(modelBar, foo);
        }

        transaction.commit();
    }
    catch(Exception e)
    {
        // ...
    }
}

private Foo getFoo(String name)
{
    Foo foo = fooDAO.getFooByName(name);

    if(foo == null) 
    {
        foo = new Foo(name);
        session.save(foo);
    }

    return foo;
}

private void insertOrUpdateBar(ModelBar modelBar, Foo foo)
{
    // insert the Bar and if ConstraintViolationException is thrown then get the object from the database and update it
}

从方法 getFoo 开始。虽然起初,当我试图获得它时,我可能没有提供名称的foo,具有相同名称的foo可能被另一个服务插入。因此,当我尝试使用相同的名称保存新的Foo对象时,将抛出ConstraintViolationException,这会导致我丢失会话。

我尝试过的一个“解决方案”是:

private Foo getFoo(String name)
{
    Foo foo = fooDAO.getFooByName(name);

    if(foo == null) 
    {
        Session newSession = null;
        Transaction newTransaction = null;
        try
        {
          Session newSession = getSessionFactory().openSession();
          newTransaction = newSession.beginTransaction();

          foo = new Foo(name);
          session.save(foo);

          newTransaction.commit();
        }
        catch (ConstraintViolationException e)
        {
          // rollback
          foo = fooDAO.getFooByName(name);
        }
        finally
        {
          // close new session
        }
      }

    return foo;
}

我遇到的问题是,如果在插入条形图时发生了某些事情(例如丢失的连接等),这些foo将保留在数据库中,我将无法回滚这些更改。

我遇到的另一个问题是,如果我在方法中插入条形码时使用此方法 insertOrUpdateBar 我将不得不为每个条形图创建一个新的会话,考虑到我有插入数百万的酒吧。

如果您需要有关此问题的更多详情/解释,请发表评论。

0 个答案:

没有答案