nested EJB not rolling back - java

Simple application service throws exception:
#Stateless
public class AppService {
#Inject
private Repository repo;
#Inject
private AppService2 service2;
public void createFoo(String name) {
Foo foo = new Foo(repo.nextIdentity(), name);
repo.save(foo);
// service2.createBar(name);
throw new RuntimeException("asdf");
}
}
Repository is also #Stateless and AppService2 is similiar to AppService.
When I throw exception like above the foo is rolled back - everything is ok. But when I call another service (the commented), the bar is not persisted into DB but foo is persisted.
Why foo is not rolled back after calling another service?
EDIT
I found that AppService2 is making a checking query to DB before saving bar
public void createBar(String name) {
if (repo.find(name) != null)
...
Bar bar = new Bar(repo.nextIdentity(), name);
repo.save(bar);
}
and this query is interrupting transaction. When I set #TransactionAttribute on this method
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public Bar find(String name) {}
it resolves the problem.
Could you explain how it works?
Is this the right way or is there another?

Related

Service delegate throws an "LazyInitializationException: could not initialize proxy - no Session"

I know there are plenty of "could not initialize proxy - no Session" questions but I did not found any answer to my problem.
So the problem is, when I delegate fetchLazy method, it throws an above mentioned exception. Here is a simplified version of my service class:
Service
public abstract class Service<S extends Service<S,E>, E extends Entity> {
#PersistenceContext private EntityManager entityManager;
// person = personService.fetchLazy(person, Person::getCompany); OK
public E fetchLazy(E entity, Function<E,?> proxyMapper) {
E attachedEntity = entityManager.find(entity.getClass(), entity.getId());
Object proxy = proxyMapper.apply(attachedEntity);
if (!Hibernate.isInitialized(proxy)) { Hibernate.initialize(proxy); }
return attachedEntity;
}
// person = personService.fetch(person).lazy(Person::getCompany); EXCEPTION
public FetchBuilder fetch(E entity) { return new FetchBuilder((S) this, entity); }
public class FetchBuilder {
private final S service; private final E entity;
LazyFetchBuilder(E e, S s) { this.entity = e; this.service = s; }
public E lazy(E entity, Function<E,?> proxyMapper) {
return service.fetchLazy(entity, proxyMapper); // DELEGATE
}
}
}
PersonService
#Stateless
public class PersonService extends Service<PersonService,Person> { ... }
PersonBean
#Named #ViewScoped
public class PersonBean implements Serializable {
#EJB private PersonService personService;
#PostConstruct public void init() {
person = personService.getById(id);
person = personService.fetchLazy(person, Person::getCompany); // OK
person = personService.fetch(person).lazy(Person::getCompany); // EXCEPTION
}
}
I will assume this service is a Java EE or Spring transactional service. Declarative transactions are proxy-based. When you get an instance of a service using dependency injection and call a transactional method, you in fact call the method of a transactional proxy that wraps the service:
client ----------> transactional proxy -----------> service
- start the transaction
- call the service
- commit
-return the value returned by the service
When you call fetchLazy(), everything works fine:
a transaction is started,
then the entity is found using the entity manager and its company proxy is initialized,
then the transaction is committed
then you get the entity with its initialized company.
When you call fetch(), here's what happens
a transaction is started,
a FetchBuilder is constructed
then the transaction is committed
then you get the FetchBuilder
This transaction is actually useless, since you never use the entity manager.
Now what happens when you call fetch() on the returned builder? It calls fetchLazy on the service instance variable of the FetchBuilder. This service is an instance of the actual service instance, not an instance of the proxy that wraps the service instance, since you initialized it with this, from the service instance itself. You're thus bypassing the proxy, and these is thus no transaction wrapping the call to find() and the intialization of the company.

Log Exception into DB

I got a method call from a stateless ejb which looks like the following
#Stateless
#Local(MyCrudService.class)
#TransactionAttribute(TransactionAttributeType.MANDATORY)
public class MyCrudServiceBean implements MyCrudService {
#EJB
private CrudService crudService;
#Override
public void writeLogEntry(StatementLog statementLog) {
try {
crudService.execute(statement.getSql());
} catch (Exception e) {
crudService.writeLogEntry(statementLog);
throw new MyApplicationException(e.getLocalizedMessage());
}
}
// ...
}
CrudSerivce:
#Stateless
#Local(CrudService.class)
#TransactionAttribute(TransactionAttributeType.MANDATORY)
#Interceptors(GenericFrepDataBaseUserInterceptor.class)
public class CrudServiceBean implements CrudService {
public static final String PERSISTENCE_UNIT_NAME = "name";
private EntityManager entityManager;
#PersistenceContext(unitName = PERSISTENCE_UNIT_NAME)
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
public void execute(String sqlString) {
Query query = entityManager.createNativeQuery(sqlString);
query.executeUpdate();
}
#Override
public void writeLogEntry(StatementLog statementLog) {
entityManager.persist(entity);
}
// ....
}
Statement is an entity which got an sql which is invalid (like 'invalid sql'). On execution I get the following error
javax.ejb.EJBTransactionRolledbackException: JBAS011469
If I debug this, I can see that this happens in the line with the logging.
I think the problem is, that because I am getting an exception the transaction gets rolled back. Because of that it is not possible to write into the db, because there is no open session anymore. Is this the case? What's best practice then? To manually open a session by myself seems quite ugly to me.
Your method log.writeErrorInDb needs to start its own transaction, so that it can still operate when the main transaction is rolled back. Yes, if your Hibernate session is already closed, then your log class would need to be able to open its own session. However it would probably be better to have a transaction boundary covering this entire block of code, and bind the Hibernate session to that, then set your log method to require a new transaction, to ensure it can operate once the first transaction is marked for rollback. i.e. two transactions, but one session
Based on your code, you should be able to annotate your log method:
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void writeLogEntry(StatementLog statementLog) {
entityManager.persist(entity);
}

What is the scope of a Transaction used in shared EntityManager contexts?

In JPA:
consider the following example, which uses a Container managed transaction scoped Entity manager.
public class ItemDAOImpl implements ItemDAO {
#PersistenceContext(unitName="ItemService")
EntityManager em;
LoggingService ls;
public void createItem(Item item) {
em.persist(item);
ls.log(item.getId(), "created item");
}
// ...
}
public class LoggingService implements AuditService {
#PersistenceContext(unitName="ItemService")
EntityManager em;
public void log(int itemId, String action) {
// verify item id is valid
if (em.find(Item.class, itemId) == null) {
throw new IllegalArgumentException("Unknown item id");
}
LogRecord lr = new LogRecord(itemId, action);
em.persist(lr);
}
}
Am I right in supposing that ls.log() method
will use the transaction of the calling method.
I'm pretty confused about these things right now, can you help?
If you are in EJBs, then very probably those methods will use the same transaction, because of the default transaction propagation method. Just check how they are configured, as it seems they are configured in an XML file.

HibernateTemplate not getting the object when called through TaskExecutor

I have a web service DocGenerationServiceImpl that inserts (for every format) a record in the table using DocRepository and object representing the record as DocFileDO. In the for-loop, I can get the id of the record that was created in the table. For each record, I will call the executor's execute method where DocGenTask will search for the record given the id. However, for example, there are 3 formats, the DocGenTask is able to get only the last record. The first 2 it cannot find. Although it's using hibernateTemplate. Can please advise?
#RestfulService
#Controller
#RequestMapping("/docs")
public class DocGenerationServiceImpl {
#Autowired
private TaskExecutor taskExecutor;
#Autowired
private DocRepository docRepository;
#RequestMapping(value = "/generate", method = RequestMethod.POST)
#ResponseBody
public String generatedDocFile(DOCParam param) {
for(String format : param.getFormatList()) {
DocFileDO docFileDO = new DocFileDO();
...
docRepository.saveDocFile(docFileDO);
log.debug("docFileDO id = " + docFileDO.getId());
DocGenTask task = new DocGenTask(docFileDO.getId());
task.setDocRepository(docRepository);
taskExecutor.execute(task);
}
}
}
#Repository
public class DocRepository {
#Autowired
private HibernateTemplate hibernateTemplate;
public DocFileDO saveDocFile(DocFileDO docFile) {
hibernateTemplate.save(docFile);
hibernateTemplate.flush();
return docFile;
}
public DocFileDO getDocFile(Long docFileId) {
return hibernateTemplate.get(DocFileDO.class, docFileId);
}
}
public class DocGenTask implements Runnable {
public void run() {
generate();
}
private void generate() {
DocFileDO docFileObj = docRepository.getDocFile(docFileId);
}
}
A couple of things
Don't use HibernateTemplate it should be considered deprecated as of Hibernate 3.0.1 (which was released somewhere in 2006). Use the SessionFactory directly and use the getCurrentSession() method to get a hibernate Session to operate on.
You don't have transactions setup (judging from the snippets), to work with a databse you need proper transaction setup.
Your controller is doing much, all of this should be inside a service.
The first refactor your repository
#Repository
public class DocRepository {
#Autowired
private SessionFactory sf;
public DocFileDO saveDocFile(DocFileDO docFile) {
Session session = sf.getCurrentSession();
session.save(docFile);
return docFile;
}
public DocFileDO getDocFile(Long docFileId) {
return sf.getCurrentSession().get(DocFileDO.class, docFileId);
}
}
Now your code will probably fail due to improper transaction setup. Add #Transactional to all the methods (or class) that need a transaction (like the saveDocFile method).
As mentioned you probably should move the code found in the controller to a service. The controller should be nothing more then a thin integration layer converting from the web to an internal representation of something and then kick off a service/business method somewhere. This service-/business-method is also your transactional unit-of-work it either all succeeds or all fails.

SessionContext getBusinessObject creates new bean

I am using jboss 5.1.0.GA (build: SVNTag=JBoss_5_1_0_GA date=200905221634). And need to get business interface of the bean. That is necessary for transaction management.
So I have:
#Local
public interface MyBeanInterface {
void transactionalMethod();
}
#Stateless
public class MyBean implements MyBeanInterface {
#Resource
private SessionContext context;
private int aState;
public void someMethod() {
aState = 42;
context.getBusinessObject(MyBeanInterface.class).transactionalMethod();
}
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void transactionalMethod() {
System.out.println(aState); // 0!!!!!
}
}
For some reason I do not get the same bean, but new bean is created. That is disastrous as transactionalMethod needs the state variable value to execute correctly.
What am I doing wrong, or that is a bug of jboss? By the way there is a bug which affects ability to get business object via bean's class: https://issues.jboss.org/browse/EJBTHREE-2126. Not sure however if it relates to my issue.
The best solution is this:
#Stateless
public class MyBean implements MyBeanInterface {
#Resource private TransactionManager tm;
private int aState;
public void someMethod() {
aState = 42;
Transaction transaction = tm.suspend();
transactionalMethod();
tm.resume(transaction);
}
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void transactionalMethod() {
System.out.println(aState); // 0!!!!!
}
}
When you call a published interface method from the same istance, passing by ejb context, the resource is:
If it is #Stateless, a new instance is created.
If it is #Stateful, a new session is created for the first call, then other call are same as #Singleton.
If it is #Singleton, the caller waits for the resource to be freed, in case it calls itself, a deadlock is created. If the method is annotated with #Read, calling yourself does not create any deadlocks.
I don't have time to see if the syntax is perfect but you could try:
InitialContext jndiContext = new InitialContext();
Object ref = jndiContext.lookup("projname/MyBeanInterface/local");
MyBeanInterfaceLocal m = (MyBeanInterfaceLocal) ref;
However I saw that you have a SessionContext field, so maybe for you the code should be a little bit different. Maybe it would be:
Object ref = SessionContext.lookup("projname/MyBeanInterface/local");
MyBeanInterfaceLocal m = (MyBeanInterfaceLocal) ref;
Let me know if this helps!

Categories

Resources