Как бороться с одновременным добавлением и / или обновлением миллионов записей с помощью Hibernate? - PullRequest
0 голосов
/ 10 мая 2018

Я разрабатываю сервис, который получит несколько миллионов записей, которые нужно вставить в две таблицы.

Таблицы

Foo

| id | name  |  
| 1  |  foo  |  

Бар

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

Сущности

Foo

@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, и если объект Foo из этого объекта Bar отсутствует в базе данных, он должен быть вставлен в таблицу 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 Мне придется создавать новый сеанс для каждого бара, что не является хорошей производительностью, учитывая, что мне нужно вставить миллионы баров.

Если вам нужна дополнительная информация / объяснения по проблеме, пожалуйста, прокомментируйте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...