2014-02-21 2 views
1

У меня есть довольно сложные методы, которые создают разные объекты во время его выполнения и используют их. Например, я создаю несколько изображений, а затем добавить их к статье:Hibernate: доступ к созданному объекту из разных транзакций

@Transactional 
public void createArticle() { 
    List<Image> images = ... 
    for (int i = 0; i < 10; i++) { 
     // creating some new images, method annotated @Transactional 
     images.add(repository.createImage(...)); 
    } 

    Article article = getArticle(); 
    article.addImages(images); 
    em.merge(article); 
} 

Это работает правильно - изображения имеют свои идентификаторы, а затем они добавляются к статье. Проблема в том, что во время этого выполнения база данных заблокирована, и ничто не может быть изменено. Это очень неудобно, потому что изображения могут обрабатываться некоторым графическим процессором, и это может занять некоторое время.

Таким образом, мы можем попытаться удалить @Transactional из основного метода. Это может быть хорошо. Что происходит, так это то, что изображения правильно созданы и имеют свой идентификатор. Но как только я попытаюсь добавить их к article и вызову merge, я получаю javax.persistence.EntityNotFoundException для изображения с ID XXXX. Диспетчер объектов не может видеть, что изображение было создано и имеет свой идентификатор. Поэтому база данных не заблокирована, но мы ничего не можем сделать.

Так что я могу сделать? Я не хочу, чтобы база данных блокировалась в течение всего выполнения, и я хочу иметь доступ к созданным объектам!

Я использую текущую версию Spring и Hibernate, все, что определено Annotations. Я не использую сессионную фабрику, я обращаюсь ко всему через javax.persistence.EntityManager.

ответ

2

Рассмотрим усиливая Hibernate каскадные функциональность для сохраняющихся деревья объектов в один присест с минимальной блокировкой базы данных:

@Entity 
public class Article { 
    @OneToMany(cascade=CascadeType.MERGE) 
    private List<Images> images; 
} 

@Transactional 
public void createArticle() { 
    //images created as Java objects in memory, no DAOs called yet 
    List<Image> images = ... 
    Article article = getArticle(); 
    article.addImages(images); 
    // cascading will save the article AND the images 
    em.merge(article); 
} 

Как это изделие, и это изображение будет получить сохранялось в конце сделки в одной транзакции с минимальное время жизни. До этого момента в базе данных не было блокировки.

Alternativelly расколоть createArticle в двух @Transactional методов ведения бизнеса, один createImages и другой addImagesToArticle и называть их один за другим в третьем способе в другом компоненте:

@Service 
public class OtherBean { 
    @Autowired 
    private YourService yourService;  

    // note that no transactional annotation is used, this is intentional 
    public otherMethod() { 
     yourService.createImages(); // first transaction - images are committed 

     yourService.addImagesToArticle(); // second transaction - images are added to article 
    } 
} 
+0

Объявление 1: Это может быть хорошим решением, но я забыл упомянуть, что при создании изображений мне нужно знать их идентификатор, чтобы их можно было сохранить на диск. Я полагаю, что если я не начну сохранять их своими хэшами, вызов БД будет неизбежным. Однако я все еще думаю, что это сработает, если все вызовы БД будут раздельными. –

+0

Объявление 2: Я действительно пробовал это, но это приводит к 'javax.persistence.EntityNotFoundException', как будто две транзакции не знали друг о друге. –

+0

попробуйте создать идентификатор изображения без блокировки с помощью @SequenceGenerator. Это создаст идентификатор, вызвав последовательность базы данных, но не будет инициировать вставку объекта непосредственно непосредственно. Http: //www.java2s.com/Tutorial/Java/0355__JPA/UseSequenceGenerator.htm. Как и у вас есть идентификатор и можете использовать его, прежде чем вставлять в себя изображение самого –

0

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

+0

Я попытался использовать это, но исключение говорит, что это запрещено в спящем режиме. В любом случае - мне бы не хотелось READ_UNCOMMITTED - я предпочел бы внести изменения и работать с ними. –

0

Мое предположение, что вашей изоляции транзакций уровень SERIALIZABLE. Именно поэтому БД блокирует затронутые таблицы на весь период транзакции.
Если это так, измените уровень на READ_COMMITTED. Hibernate (или любой поставщик JPA) прекрасно работает с этим. Он ничего не закроет, если вы явно не назовете entityManager.lock(someEntity, LockModeType.SomeLockType))
Также, когда вы выбираете границы транзакций, сначала думайте об атомарности. Если createArticle() является атомной единицей работы, ее просто нужно сделать транзакционным, а нарушение транзакций в целях оптимизации - ошибочно.

Смежные вопросы