Транзакция клиента TransactionRolledbackLocalException прервана при обращении к @Singleton
Примечание: добавление этого вопроса в краткой форме с ответом, потратив гораздо больше времени, чем я хотел бы признать, на поиск причины. Надеюсь, я избавлю кого-нибудь еще от боли.
При делегировании вызова метода EJB
с аннотацией @Singleton
контейнер создает исключение в виде:
TransactionRolledbackLocalException Client's transaction aborted
Одноэлементный Боб не имеет доступа к данным происходящий.
ServiceBeanImpl.java
@Stateless
@Local
public class ServiceBean extends BaseBean{
@EJB private CacheService cacheService;
public FooObj getFooFromCache(int id) {
FooObj fooObj = (FooObj) cacheService.get(id);
if (fooObj == null) {
fooObj = getEntityById(FooObj.class, id);
cacheService.put(id, fooObj); //This throws exception
}
return cacheService.get(id);
}
}
CacheServiceImpl.java
@Singleton
@Startup
public class CacheServiceImpl implements CacheService {
private Cache cache;
@PostConstruct
public void init() {
CacheManager instance = CacheManager.getInstance();
cache = instance.getCache("cache");
}
@PreDestroy
public void destroy() {
CacheManager.getInstance().shutdown();
}
public Object get(Object id) {
return cache.get(id);
}
public void put(Object id, Object obj) {
return cache.put(id, obj);
}
}
Вопрос Почему вызов Одноэлементного компонента, который не имеет доступа к данным, вызывает исключение транзакции?
3 ответа:
Краткий ответ: проверьте стек кода, который выполнялся ранее (предполагая, что в StackTrace нет подсказок) для исключения, которое захвачено и не распространяется (таким образом, никаких подсказок в StackTrace).
В данном конкретном случае было
try/catch
вокруг создания именованного запроса. Если этот именованный запрос не удался, в блоке catch выполнялся вторичный запрос. Запросы не требовали транзакции, поэтому резервный запрос выполнялся правильно и возвращал ожидаемые сущности.Однако...
Это также (я думаю) означает, что транзакция нуждается в откате. Фасоль
@Singleton
является чем-то вроде красной селедки. Что-то в передаче управления этому Бобу заставило контейнер проверить транзакцию, что, в свою очередь, вызвало исключение в этой точке, но исключение было уместным, так как транзакция больше не действительна.Мораль рассказа:
- пройдите свой путь назад к последнему взаимодействию с базой данных, потому что это, вероятно, где твоя проблема.
- только потому, что последнее взаимодействие с базой данных вернуло данные, не означает, что у него не было проблемы (это было убийцей для меня).
Я столкнулся с подобной проблемой и обнаружил интересную вещь:
Из @Startup @Singleton у меня есть следующие вызовы
@PostConstruct public void initHardcodedUsers() { for (User u : getHardcodedUsers()) { if (!userRepository.userExists(u.getName())) { userRepository.add(u); } } }
UserRepository - это EJB @без состояния с EntityManager. В методе
userExists()
я в основном делаю запрос "findUserByName" с помощьюgetSingleResult()
. Если результат недоступен, то JPA выдаетNoResultException
. Я ловлю его внутриuserExists()
и возвращаю false ("пользователь не существует").Поскольку NoResultException является RuntimeException, откат здесь принудительный, так как я только что узнал!
Самый простой подход для этого состоит в том, чтобы избежать вызова
getSingleResult()
. Вместо этого я используюgetResultList()
иsetMaxResults(1)
. Это просто вернет пустой список в случае отсутствия результатов, следовательно, никаких исключений, следовательно, никакого отката tx.Удачи:)
Крис
Аналогичная проблема с
TransactionRolledbackLocalException
в GlassFish / Payara Server случилась и со мной. Но это было вызвано ошибкой в GlassFish / Payara, ничто не откатило транзакцию в моем приложении. Перезапуск домена исправил его, как описано здесь: javax.EJB-компонента.TransactionRolledbackLocalException (Glassfish 3 + JPA + EclipseLink)