I want to call different methods that interact with my database in one method.
something like this :
#Autowired
EnteteService es; // service for jpaRepository
#Autowired
SystemOracleServiceJpa so; // service using jdbcTemplate
#Autowired
DetailService ds; // service for jpaRepository
#Transactional
public void configure(EnteteAR entete) throws ConfigurationException {
try{
this.es.save(entete); // first methode
List<DetailAR> details = this.so.getStructure(entete.getTableName());
if(details.size()>0){
this.ds.saveAllDetails(details); // second
this.so.CreateTable(details, entete.getTableName(), "DEM");//third
this.so.createPkIdxDem(entete.getTableName()); // fourth
this.so.CreateTable(details, entete.getTableName(), "BACK"); // fifth
}
else{
throw new ConfigurationException("Configuration error");
}
}catch(Exception e){
throw new ConfigurationException(e.getMessage());
}
}
I want to commit only if no errors appears in all this methods inside my main method "configure".
I was thinking that #transactionnal annotation work for this, but that commit after each method inside.
Exemple :
if this.es.save work and this.ds.saveAllDetails dont, I find data of es.save on database :(
Someone can help my please ?
thank with advance for your reading and your potential help.
#Transactional will automatically invoke a rollback if an unchecked exception is thrown from the executed method.
ConfigurationException in your case is a checked exception and hence it does not work.
You can make it work by modifying your annotation to
#Transactional(rollbackOn = ConfigurationException.class)
public void configure(EnteteAR entete) throws ConfigurationException {
try{ ....
Related
Bank.java
#Stateless
#Local
public class Bank implements IBank {
#EJB
IConfigBean iConfigBean;
#EJB
IDbs iDBS;
#EJB
IPosb iPosb;
#Override
public void doTransaction() {
System.out.println("--Bank Transaction Started--");
try {
Config config1 = getConfig(1);
iConfigBean.create(config1);
iDBS.doDBSTransaction();
Config config3 = getConfig(3);
iConfigBean.create(config3);
iPosb.doPOSBTransaction();
Config config5 = getConfig(5);
iConfigBean.create(config5);
} catch (Exception e) {
e.printStackTrace();
System.out.println("---Bank Exception--");
}
System.out.println("--Bank Transaction End--");
}
#Override
public Config getConfig(int inserttionOrderNo) {
Config config = new Config();
config.setType("EJBTransactionTESTING - " + inserttionOrderNo);
return config;
}
}
DBS.java
#Stateless
#Local
public class DBS implements IDbs {
#EJB
IConfigBean iConfigBean;
#Override
public void doDBSTransaction() {
System.out.println("--DBS Transaction Started--");
try {
Config config2 = getConfig(2);
iConfigBean.create(config2);
} catch (Exception e) {
e.printStackTrace();
System.out.println("--DBS Exception--");
}
System.out.println("--DBS Transaction End--");
}
#Override
public Config getConfig(int inserttionOrderNo) {
Config config = new Config();
config.setType("EJBTransactionTESTING - " + inserttionOrderNo);
return config;
}
}
POSB.java
#Stateless
#Local
public class POSB implements IPosb {
#EJB
IConfigBean iConfigBean;
#Override
public void doPOSBTransaction() {
System.out.println("--POSB Transaction Started--");
try {
Config config4 = getConfig(4);
iConfigBean.create(config4);
if (true) {
//For Test 1
//throw new NullPointerException();
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("--POSB Exception--");
}
if (true) {
//For Test 2
// throw new NullPointerException();
}
System.out.println("--POSB Transaction End--");
}
#Override
public Config getConfig(int inserttionOrderNo) {
Config config = new Config();
config.setType("EJBTransactionTESTING - " + inserttionOrderNo);
return config;
}
}
I am new to Stack Overflow and Its my new question so correct me If I am wrong.
Environment is..
Windows 10
Java 1.8
Eclipse
Tomcat 8.5
EJB3
I have Three stateless bean, Please look at the Sequence Diagram of the Transaction flow.
I purposely making NullPointer Exception at two places during the transaction to know the difference and I have marked with Lightening Bold symbol in sequence diagram.
I am not using any #TransactionAttribute to any methods.
Test 1 - Null Pointer in Inside the try block (Lightening Bold symbol with Green)
When I start the testing, Got Null pointer exception and all the transaction are not marked for roll back and data also got inserted in db.
I can only see Null pointer exception in the console log.
Test 2 - Null Pointer in Outside the try - catch method (Lightening Bold symbol with Red)
When I start the testing, Got Null pointer exception plus EJBTransactionRolledbackException and all the transaction marked for roll back and no data inserted in db.
I can see NullPointer and EJBTransactionRolledback Exception in the console log.
Question here is,
Why EJB transaction is not marked for roll back If I made Null pointer inside try block
Why EJB transaction is roll back happens If I made null pointer outside try block
Thanks in advance.
Keep in mind EJB calls, all the "magic" made by container happens there, including transaction markup. It's possible due to the fact that EJB calls are not direct, but always go through proxy.
You have such calls in your code:
iPosb.doPOSBTransaction();
So, if unchecked exception (NPE for example) is thrown in this method and not caught - it's ultimately caught by container due to EJB proxy wrapping the call above. In this case transaction only could be rolled back.
Adding a call to a method of the same bean in your method (without using #EJB reference), does not change that:
#Override
public void doPOSBTransaction() {
try {
Config config4 = getConfig(4);
iConfigBean.create(config4);
if (true) {
newMethod();
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("--POSB Exception--");
}
if (true) {
newMethod();
}
}
private void newMethod(){
throw new RuntimeException();
}
You can easily check that commit/rollback behaviour is just the same in this case, no matter that a method is added to call stack.
So the important thing you have to remember is that all container tricks work only on #EJB calls. So, for example, it's pointless to place transactional annotation on a private method - it won't be used ever.
Another important point is about checked exceptions. By default these do not cause transaction rollback indeed. But it's still possible to annotate your checked exception like below to make it rollback ongoing transaction:
#ApplicationException(rollback = true)
I have the following code:
try {
em.persist(myObject);
em.flush();
}
catch (Exception e) {
System.out.println("An Exception occur when trying to persist and flush");
}
In test I have mock my EntityManager with Mockito:
#Mock
EntityManager mockEm;
but as persist and flush are void methods, I'm not able able to write something like:
when(mockEm.persist(anObject).then(doSomeThing);
How can I write unit testing (with JUnit)to mock em.persist and em.flush in order to test both cases with and without exception?
Thanks.
I guess we need more details. For now, you can do something like this: (it's kind of pseudo-code).
#Test // it passess when there's one object in repository (I assume u have clean memory db)
public void shouldPersistMyObject() throws Exception {
em.persist(myObject);
em.flush();
Assert.assertThat(dao.findAll(), Matchers.equalTo(1));
}
#Test(expected = <YourDesiredException>.class) // if <YourDesiredException> is thrown, your test passes
public void shouldThrowExceptionWhenSavingCorruptedData() {
//set NULL on some #NotNull value, then save, like:
myObject.setUserId(null);
em.save(myObject); //exception is thrown
}
I try to execute this code
#Transactional
#Controller
public class MyController{
....
#RequestMapping(..)
public String MyMethod(...)
{
....
try {
ao_history_repository.save(new AoHistory(..));
}
catch (DataIntegrityViolationException e) {
System.out.println("history already exist");
}
....
model.addAttribute("...", my_respository.findAoToDetail(id) );
return "...";
}
But when i got duplicate entry Exception i catch it but after i got a other Exception
org.hibernate.AssertionFailure: null id in persistence.AoHistory entry
(don't flush the Session after an exception occurs)
I know that When a ConstraintViolationException is thrown it invalidates the current session but how can i reopen a new session and a new transaction ?
As you write, you need a new transaction. From your code snippet it looks like the simplest thing would be to move #Transactional from the controller to the repository classes. As an alternative, you could add a service layer and move #Transactional there.
A different approach would be to pre-check the entity object before trying to save it in the entity manager, so that exception is never thrown.
Below is what I did, I need to implement rollback, using #transactional annotation, but not working as expected, what else need to be done for proper rollback to happen ?? I want that when the code is executed result in db should be "testingOne" , currently it is set to "notRollBacked". Can you please point my mistake.
public Response deleteUser(Request argVO)throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setUserName("testingOne");
UsersLocalServiceUtil.updateUsers(users);
try
{
testRollbackFunction();
}
catch (Exception ex)
{
}
return new Response();
}
#Transactional(isolation = Isolation.PORTAL, rollbackFor =
{PortalException.class, SystemException.class})
private void testRollbackFunction() throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setUserName("notRollbacked");
UsersLocalServiceUtil.updateUsers(users);
throw new PortalException();
}
****************Edit 1*************
I did what was mentioned in answers:
I did taken bean from context
and written a class/bean as
#Transactional(isolation = Isolation.PORTAL, rollbackFor =
{PortalException.class, SystemException.class})
public class RollBack
{
#Transactional(isolation = Isolation.PORTAL, rollbackFor =
{PortalException.class, SystemException.class})
public void thisWillRollBack() throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setBarringReason("notRollbacked");
UsersLocalServiceUtil.updateUsers(users);
throw new PortalException();
}
}
spring xml file bean refrence set as :
<bean id="rollBackBean" class="com.alepo.RollBack">
</bean>
public Response myMethod(Request argVO)throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setBarringReason("testingOne");
UsersLocalServiceUtil.updateUsers(users);
try
{
Test test = new Test();
Object obj = test.getBean();
RollBack rollBack = (RollBack)obj;
rollBack.thisWillRollBack();
}
catch (Exception ex)
{
ex.printStackTrace();
}
return new Response();
}
#################EDIT 4
now calling rollback function as :
RollBack rollBack = (RollBack)PortalBeanLocatorUtil.getBeanLocator().locate("rollBackBean");
rollBack.thisWillRollBack();
No Test class in picture now ...no new anywhere ...
still NOT WORKING .......
If you have a #Transactional annotation on method, Spring wraps the call to this method with aspect handling the transaction.
So:
1) Only public methodes can be wrapped in aspect
2) You call wrapped code only if you call the method on a bean taken from / injected by Spring container.
In your case:
1) The code isn't wrapped in transactional aspect because it is not public method
2) Event if it was, it is called directly from within the class, so you wouldn't call wrapped version anyway.
So the solution is to make separate bean with #Transactional method, inject it into and call it from Response class.
Of course you need <tx:annotation-driven/> in your spring-xml or instruct Spring otherwise to process #Transactional annotations (see the reference).
The issue is you are outside the application context. You are creating a new instance of a class, NEW is bad in Spring, very bad. Get an instance of Test from the application context, not by creating a new instance unless you start your application context in Test. Try to Autowire test in your class you mention above or inject it from Spring and then let me know, but the code you are showing above will never work with transaction management.
As shown below, I am accessing a Service layer method inside of another DAO.
(Every DAO in the system is implemented using HibernateDAOSupport class)
I wanted to rollback the transaction when #1 or #2 (commented in the following code) is failed.
But when #2 throws an exception, #1 does not get rolled back and I can see the entries in the database.
#Transactional(readOnly=false, rollbackFor={DuplicateEmailException.class,DuplicateLoginIdException.class,IdentityException.class},propagation=Propagation.REQUIRES_NEW)
public void createUserProfile(UserProfile profile)
throws DuplicateEmailException, DuplicateLoginIdException,
IdentityException {
// #1 create principal using Identity Service
identityService.createPrincipal(profile.getSecurityPrincipal());
try {
// #2 save user profile using Hibernate Template
getHibernateTemplate().save(profile);
} catch (RuntimeException e) {
throw new IdentityException("UseProfile create Error", e);
}
}
Here is the signature for createPrincipal() method of'IdentityService'.
public void createPrincipal(Principal principal) throws DuplicateEmailException,DuplicateLoginIdException,IdentityException ;
There's no Transaction management configured in 'IdentityService'
What I am doing wrong here ?
Try Propagation.REQUIRED, instead of Propagation.REQUIRES_NEW
During the calls identityService.createPrincipal(profile.getSecurityPrincipal()); aren't you flushing the session ? (executing a query for example, with FlushMode.AUTO)