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