Why transaction can't commit in a self-invoked ejb method with #REQUIRES_NEW Annotation - jpa

First I want to explain my self-invoked ejb method in this situation. I have a stateful session bean with a method which starts a new transaction (Annotated by #REQUIRES_NEW). To invoke this method inside the bean itself and make the annotation effective, I use SessionContext#getBusinessObject() to achieve the effect of #EJB (#EJB here causes stackoverflow?!). My code is shown below:
#Stateful
#Local
public class TransactionTest implements ITransactionTest {
#PersistenceContext(unitName="Table",Type=PersistenceContextType.EXTENDED)
private EntityManager manager;
#Resource
SessionContext sc;
ITransactionTest me;
#PostConstruct
public void init(){
me = this.sc.getBusinessObject(ITransactionTest.class);
}
public void generateRecord(int i) throws RuntimeException{
Record record = new Record();
record.setId(i+"");
record.status(1);
manager.persist(record);
manager.flush(); //If not flush, result is correct. Why?
me.updateRecord(i);
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void updateRecord(int i) throws RuntimeException{
try {
Record record = manager.find(Record.class, i+"");
record.setStatus(2);
manager.flush();
} catch(Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
While,generateRecord() runs properly. The console shows it executes 'insert' and 'update' HQL without any exception (I use Hibernate as JPA provider). However, the 'update' result doesn't appear in the database. Why? Does updateRecord() commit correctly?
Also, I try it in two altenative ways: First is invoking generateRecord() (it will no longer invoke updateRecord()) and updateRecord() consecutively in another bean. It can give me the right result.
The second is removing the first flush(). Then both 'insert' and 'update' HQL will be executed at the second flush(). This method can also produce right result.
My program is running under JBOSS 6.1.0-Final and database is Oracle.
Best Regards,
Kajelas

Related

Persist object without try-catch block in method

I'm currently working on a project and I want to see what damage it can do if I don't embrace my code with try-catch block when persisting object into database. Here is my code down below that i use as test.
public class NewEventServiceBean implements NewEventService {
#PersistenceContext(name = "example")
EntityManager manager;
#Resource
private UserTransaction userTransaction;
#Override
public void createNewEvent(String title, String content) throws Exception
{
userTransaction.begin();
Event event = new Event();
Content cont = new Content();
cont.setContent(content);
event.setTitle(title);
event.setCont(cont);
manager.persist(event);
manager.persist(cont);
userTransaction.commit();
}
In the database i have this Event table that has a foreign key to Content table.
And my question is if It's possible that Event object is persisted in to the database even if I cause something wrong when persisting the content class. Or what is the disadvantages of not embracing the code with try catch and rollback?
I've tried to cause an error when persisting the content object, but the Event is not persisted into the datbase even if everything is correct in that class.

#Transactional and serializable level problem

I have a problem with isolation levels in JPA. For example I have following code:
#Transactional(isolation = Isolation.SERIALIZABLE)
public void first() {
Obj obj = new Obj();
obj.setName("t");
objDAO.save(obj);
second();
}
#Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
public void second(){
List<Obj> objs = objDAO.findAll();
}
In my opinion the second method should not see uncomitted changes from method first. So new object with name "t" should not be visible till commit (but it is).
If I am wrong, then please, give me example in JPA where it won't be visible. Many thanks for any advice.
If your methods are inside one class it will not work because container will treat this as a single transaction. The container doesn't know that you want to create new transaction.
From the Spring reference:
Note: In proxy mode (which is the default), only 'external' method calls coming in through the proxy will be intercepted. This means that 'self-invocation', i.e. a method within the target object calling some other method of the target object, won't lead to an actual transaction at runtime even if the invoked method is marked with #Transactional!
If you want to call method second() in new transaction you can try this:
#Autowired
private ApplicationContext applicationContext;
#Transactional(isolation = Isolation.SERIALIZABLE)
public void first() {
Obj obj = new Obj();
obj.setName("t");
objDAO.save(obj);
applicationContext.getBean(getClass()).second();
}
#Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
public void second(){
List<Obj> objs = objDAO.findAll();
}

Detecting when the database session gets refreshed on a Spring Boot 2 application

I'm trying to execute the following SQL statement every time the Database Session gets refreshed. I have a Spring Boot 2.0.1.RELEASE with JPA application and a PostgreSQL Database.
select set_config('SOME KEY', 'SOME VALUE', false);
As the PostgreSQL documentation states the is_local parameter is used to indicate that this configuration value will apply just for the current transaction -if true- or will be attached to the session (as I require) -if false-
The problem is that I'm not aware when Hibernate/Hikari are refreshing the db session, so, in practice, the application start failing when it has a couple of minutes running, as you can imagine...
My approach -that is not working yet- is to implement a EmptyInterceptor, for that I have added a DatabaseCustomizer class to inject my hibernate.session_factory.interceptor properly in a way that Spring can fill out all my #Autowires
DatabaseInterceptor.class
#Component
public class DatabaseInterceptor extends EmptyInterceptor {
#Autowired
private ApplicationContext context;
#Override
public void afterTransactionBegin(Transaction tx) {
PersistenceService pc = context.getBean(PersistenceService.class);
try {
pc.addPostgresConfig("SOME KEY", "SOME VALUE");
System.out.println("Config added...");
} catch (Exception e) {
e.printStackTrace();
}
}
}
DatabaseCustomizer.class
#Component
public class DatabaseCustomizer implements HibernatePropertiesCustomizer {
#Autowired
private DatabaseInterceptor databaseInterceptor;
#Override
public void customize(Map<String, Object> hibernateProperties) {
hibernateProperties.put("hibernate.session_factory.interceptor", databaseInterceptor);
}
}
Obviously, there is a problem with this approach because when I #Override the afterTransactionBegin method to start another transaction I get an Infinite loop.
I tried to look something inside that Transaction tx that could help to be sure that this transaction is not being generated by my own addPostgresConfig but there is not much on it.
Is there something else I could try to achieve this?
Thanks in advance,

Is it possible to annotate a method with #TransactionAttribute in BMT EJB?

I came across a piece of code where the bean implementation class has #TransactionManagement(TransactionManagementType.BEAN) annotation wherein methods are annotated with CMT #TransactionAttribute. Is is valid?
Can an EJB with BMT persistence use CMT transaction annotation? What will be the behavior at runtime?
Though javadoc http://docs.oracle.com/javaee/6/api/javax/ejb/TransactionAttribute.html says that "It can only be specified if container managed transaction demarcation is used.", specifying it doesn't throw any compilation error. Does it mean that jvm simply ignores it at runtime?
#Stateless( mappedName = "Abc")
#Remote("AbcRemote.class")
#Local("AbcLocal.class")
#TransactionManagement(TransactionManagementType.BEAN)
public class AbcBean implements AbcLocal, AbcRemote{
#Resource
private UserTransaction utx;
#PersistenceUnit
private EntityManagerFactory emf;
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public Abc getAlpbabets(String name) {
EntityManager em = null;
try {
em = emf.createEntityManager();
}
catch (RuntimeException re) {
throw re;
}
finally {
}
}
}
If you use CMT, then #TransactionAttribute(TransactionAttributeType.REQUIRED) would tell the container to check for an existing transaction and open one if there is none.
But if you use BMT, then it's your responsibility to do such a thing, so there's no one to observe the annotation above. Since it is still syntactically correct and the class is available, there is no need for the JVM to complain about.
Concerning ignoring annotations, there's a hint in the answer to this question.

Cannot remove a JPA entity using Spring Integration

When I try to remove an entity using Outbound Channel Adapter I always get removing a detached instance exception.
I know that an entity should be retrieved and deleted in the same transaction to avoid this exception, but how can I achieve it with Spring Integration?
To demonstrate the problem I modified the JPA sample:
PersonService.java
public interface PersonService {
...
void deletePerson(Person person);
}
Main.java
private static void deletePerson(final PersonService service) {
final List<Person> people = service.findPeople();
Person p1 = people.get(0);
service.deletePerson(p1);
}
spring-integration-context.xml
<int:gateway id="personService"
service-interface="org.springframework.integration.samples.jpa.service.PersonService"
default-request-timeout="5000" default-reply-timeout="5000">
<int:method name="createPerson" request-channel="createPersonRequestChannel"/>
<int:method name="findPeople" request-channel="listPeopleRequestChannel"/>
<int:method name="deletePerson" request-channel="deletePersonChannel"/>
</int:gateway>
<int:channel id="deletePersonChannel"/>
<int-jpa:outbound-channel-adapter entity-manager-factory="entityManagerFactory"
channel="deletePersonChannel" persist-mode="DELETE" >
<int-jpa:transactional transaction-manager="transactionManager" />
</int-jpa:outbound-channel-adapter>
When I call deletePerson I get the exception:
Exception in thread "main" java.lang.IllegalArgumentException:
Removing a detached instance
org.springframework.integration.samples.jpa.Person#1001
UPDATE:
Apparently I should've chosen a sample closer to my actual project, because here you can just create a new transaction programmatically and wrap both retrieve and delete function calls in it, as Artem did.
In my project I have a transformer connected to an outbound-channel-adapter. The transformer retrieves an entity and the outbound-channel-adapter removes it. How can I get the transformer and the outbound-channel-adapter to use the same transaction in this case?
To get it worked you should wrap all operations in the deletePerson to transaction, e.g.
private static void deletePerson(final PersonService service) {
new new TransactionTemplate(transactionManager)
.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
final List<Person> people = service.findPeople();
Person p1 = people.get(0);
service.deletePerson(p1);
}
});
}
In this case you should somehow provide to your method transactionManager bean too.
UPDATE:
I shown you a sample for use-case in the original question.
Now re. <transformer> -> <jpa:outbound-channel-adapter>.
In this you should understand where your message flow is started:
If it is <inbound-channel-adapter> with poller, so just make the <poller> <transactional>
If it <gateway>, who call <transformer>, so it's just enough to mark gateway's method with #Transactional
Here is one more transactional advice trick: Keep transaction within Spring Integration flow
In all cases you should get rid of <transactional> from your <jpa:outbound-channel-adapter>