LINUX.ORG.RU

Проброс транзакции из DAO или как заставить entity не детачится

 ,


0

1

Доброго времени суток, ЛОР. Проблема следующая: слой DAO используется в слое сервисов, а между ними над результатами селектов должны производиться некоторые преобразования. Для этого использую ещё один небольшой слой в виде session-бина, что как раз вызвано необходимостью проброса транзакции. Так вот, пытаюсь я пробросить транзакцию, но получаю:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session
Да-да, я говорил про openjpa, а здесь hibernate - просто обкатываю на JBoss. Немного кода для полноты картины:
@WebService
public class AccountServiceImpl implements AccountService {

    @Inject
    AccountDAO accountDao;

    @Inject
    BOExtractor boExtractor;

    ...

    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public Account getByEan(String ean) throws SOAPException {
        try {
            AccountEntity accountEntity = accountDao.getAccountByEan(ean);
            return boExtractor.extractAccount(accountEntity);
        } catch (AccountNotFoundException e) {
            SOAPFactory sf = SOAPFactory.newInstance();
            throw new SOAPFaultException(sf.createFault(e.getMessage(), new QName(FaultCode.ACCOUNT_NOT_FOUND.getFaultCode())));
        }
    }

    ...
}
@Stateless
public class AccountDAOImpl extends AbstractFiscalDAO<AccountEntity> implements AccountDAO {

    @PersistenceContext(unitName = "accounts")
    private EntityManager entityManager;
    
    ...

    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public AccountEntity getAccountByEan(String ean) throws AccountNotFoundException {
        AccountEntity accountEntity = (AccountEntity) entityManager.createNamedQuery("account.getByEAN")
                .setParameter("ean", ean).getSingleResult();

        ...

        return accountEntity;
    }
}
@Stateless
public class BOExtractorImpl implements BOExtractor {

    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public Account extractAccount(AccountEntity accountEntity) {
        Account acc = new Account();
        acc.setEan(accountEntity.getEan());
        acc.setActiveAmount(accountEntity.getActiveAmount());
        acc.setCardUID(accountEntity.getCard().getNumber());
        acc.setCurrencyCode(accountEntity.getCurrency().getCode());
        acc.setIsLocked(accountEntity.getLocked());
        acc.setLimit(accountEntity.getLimit());
        acc.setHolderUID(accountEntity.getHolder().getUid());
        return acc;
    }
    
    ...
}

Прошу обратить внимание на то, что в AccountServiceImpl#getByEan, после возвращения результата из DAO, из сущности счета (aka Account) уже невозможно получить Card и Holder. Ах да, собственно, сущность:

@Entity
@Table(name = "ACCOUNT")
@NamedQueries({
        @NamedQuery(name = "account.getByEAN", query = "select a from AccountEntity a where a.ean = :ean"),
        @NamedQuery(name = "account.listByHolderID", query = "select a from AccountEntity a where a.holder.uid = :uid")
})
public class AccountEntity implements Serializable {

    @Id
    @Column(name = "ID", nullable = false, insertable = true, updatable = true)
    ...

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "CARD_ID")
    private CardEntity card;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "HOLDER_UID")
    private HolderEntity holder;

    ...
}
Этот код здесь уже излишен, да и сванговать можно, но пусть будет. Так о чем это я? Ну да, точно:

в AccountServiceImpl#getByEan, после возвращения результата из DAO, из сущности счета (aka Account) уже невозможно получить Card и Holder

Вопрос: как правильно обойти?
Заранее спасибо.

@Id
updatable = true

полезно постить на ЛОР (:

bytecode ★★
() автор топика

Начнём с того что у вас ошибки в проектировании, если в сервисе нужны Card и Holder то почему они LAZY, если не нужны - почему они есть в сущности?

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

ya-betmen ★★★★★
()

Есть пара вариантов:

1) Объявить AccountEntity.card и AccountEntity.holder с FetchType.EAGER (достаточно просто убрать FetchType.LAZY)

2) Использовать FETCH JOIN: http://docs.oracle.com/cd/E17904_01/apirefs.1111/e13946/ejb3_langref.html#ejb...

3) Использовать SELECT NEW. В этом случае AccountDao.getAccountByEan() будет возвращать не сущность, а сразу Account. Для этого понадобится в Account добавить конструктор, который бы принимал AccountEntity и делал все то, что сейчас делает extractAccount(). Запрос придётся переписать в стиле

select new a.b.c.Account(a) from AccountEntity a where a.ean = :ean"

Пример есть здесь: http://vard-lokkur.blogspot.ru/2013/05/jpa-basic-projections.html

php-coder ★★★★★
()
Последнее исправление: php-coder (всего исправлений: 1)
Ответ на: комментарий от ya-betmen

руками залукапить сессию/фабрику сессий. Где-то в хибернейте такое было

Понятное дело, что это решит все проблемы. DAO'шка удобна и инкапсулирует всю работу с БД, а тут получится разброд и шатание. Вариант с EAGER, скорее всего, и заиспользую.

bytecode ★★
() автор топика
Ответ на: комментарий от php-coder

Как уже писал выше, скорее всего буду использовать EAGER стратегию для полей с Card и Holder, а за вариант с select new огромное спасибо. Каюсь, не знал о такой плюшке!

bytecode ★★
() автор топика
Ответ на: комментарий от bytecode

Ещё раз, я предлагаю не работу через ЖДБЦ, я имел в виду, что вероятно Жбосовский инжектор заточен под жпа и не осилил правильно инициализировать сессию для гибернейта. должен быть способ взять и руками воткнуть эту сессию на этапе инициализации гиберенйта или сразу после. Как именно это делать - гугл в помощь.

ya-betmen ★★★★★
()
Ответ на: комментарий от ya-betmen

Да понял я, о чем ты говоришь. (: SessionFactory инжектить, юзать currentSession. Там можно дойти до размышлений над плюшками хибернейта, да и до спринга недалеко. Дело в том, что в нашем случае только openjpa на WAS и никаких плюшек.

P.S: мои небольшие познания жбосса говорят об обратном, он скорее «pure» jpa не осилит, чем не сможет правильно его родной хибернейт запилить. И таки да, вообще, глупая затея проверять на жбоссе то, что будет крутится на WAS.

bytecode ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.