Liferay Service Builder is one of the great tool to auto generate code to interact with database. But have you ever thought of transaction in Liferay Service Layer? In simple I am just reminding you about database roll back. In this post I will focus on the Transactional aspect of Liferay Service Layer. Â
Note:Â A transaction is a set of tasks that you want to treat as a unit. The unit should be completed as a whole or not at all.
Real-time Example:
Say you want to transfer money on-line from your account to another account. The whole process can be considered as
i) Check the sufficient balance.
ii) Deduct specific amount from account.
iii) Deposit the same amount to the destination account.
In this case the whole process is a set of 3 tasks. If any one of these task fails then the whole process must be rolled back. For example consider the last task No. (iii) got failed. That means that amount is deducted from your account but its not deposited in the destination account. In this situation we must consider about transaction.
I am assuming that you know the Liferay Service Builder tool. This post does not explain how to use to Liferay Service builder.Â
service.xml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.2.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_2_0.dtd"> <service-builder package-path="com.proliferay.demo.sbuilder"> <author>Hamidul Islam</author> <namespace>transaction</namespace> <entity name="Customer" local-service="true" remote-service="true"> <column name="customerId" type="long" primary="true" /> <column name="firstName" type="String" /> <column name="middleName" type="String" /> <column name="lastName" type="String" /> <column name="age" type="int" /> </entity> <entity name="Address" local-service="true" remote-service="true"> <column name="addressId" type="long" primary="true" /> <column name="customerId" type="long" primary="true" /> <column name="address" type="String" /> <column name="contactNo" type="int" /> </entity> </service-builder>
 In the above service.xml there are two entities. First entity is Customer and second entity is Address.Â
Note: The services methods are executed in a transaction which means that if your database supports it, then if there is an error in the middle of the execution of a service method (or in any other method invoked by it), all the previous operations will be undone (rolled back) automatically for you. Liferay implements this under the hood using Spring’s transactions mechanisms.
Scenario 1:Â
We need add customer and their respective address. Both has to be inserted in different database tables. Consider every Customer should have valid address. Â That means that after adding customer there must be a valid address record in address table. Therefore if we fail to add address then we must also should not add any customer without address.Â
Solution:
Every liferay service methods are Transactional in nature. That means every local service impl methods are transnational. We can add records in both the tables from a single method by adding custom method in local service impl file.Â
public void addCustomerAddress(Customer customer, Address address) throws SystemException{ /** * Inserting data to Customer */ long customerId = counterLocalService.increment(Customer.class.getName()); customer.setCustomerId(customerId); customer = customerLocalService.addCustomer(customer); /** * Inserting data to Address */ long addressId = counterLocalService.increment(Customer.class.getName()); address.setAddressId(addressId); address.setCustomerId(customer.getCustomerId()); addressLocalService.addAddress(address); }
 According to the above code after insertion of customer object if insertion of address object fails then whole process will be rolled back. Liferay handle this  Transaction without writing any custom code. Is not it fantastic?Â
Scenario 2:Â
Sometimes we want to roll back in some certain conditions. Say you are adding records in five tables one after another in a single method as shown in the above.
Solution:
If you want to roll back on the basis of certain conditions then we can explicitly throw any one of the below two exceptions
i) com.liferay.portal.kernel.exception.PortalException
 Or
ii)Â com.liferay.portal.kernel.exception.SystemException
For example:
throw new PortalException();
The above code is sufficient to to roll back the previous insertions.Â
Good to know
Internally Liferay services are annotated by @Transactional(isolation = Isolation.PORTAL, rollbackFor = Â {PortalException.class, SystemException.class})Â
This will be mentioned in your ${EntityName}LocalService interface. That means that service layer methods are Transactional. Roll back will be happened for the exceptions PortalException as well as SystemException.Â
Really very nice article, explained in very simplified manner thanks for sharing 🙂
Great concept explained in simple way !
Hi,
I liked your way of explanation. If possible try provide some examples in service builder like mappings 1-1, 1-M, M-M etc.
I didn’t found any examples like that any place properly.
Thanks once again.
I am trying to implementing the Liferay Transaction RoleBack, I am not geting the deleted values(ie RollBack is not working).
Please help.
public void serveResource(ResourceRequest resourceRequest,
ResourceResponse resourceResponse) throws IOException
{
try {
deleteTheCheckedBoxVals(splitedVals);
} catch (PortalException e) {
_log.info(“PortalException Exceptions in Main Method “+e);
}
}
protected void deleteTheCheckedBoxVals(List splitedVals) throws PortalException{
try {
//Here I am deleting the each ID in DB, If Exection comes like NO ID FOUND IN DB, then I have to rollback the deleted ID
for (String curDeltngIdVal : splitedVals) {
XYZLocalServiceUtil.deleteAbc(Integer .parseInt(curDeltngIdVal));
_log.info(“deleted IDs are –>”+curDeltngIdVal);
}
} catch (NumberFormatException e) {
_log.info(“Number Formate Exceptions “+e);
} catch (PortalException e) {
_log.info(“PortalException Exceptions “+e);
throw new PortalException();
} catch (SystemException e) {
_log.info(“SystemException Exceptions “+e);
}
}
Pingback: Assicurarsi che la transazione sia finita | Marco Rosetti
Is it possible add @Transactional in Liferay MVCPortlet?
public class ProductPortlet extends MVCPortlet {
@Transactional
@ProcessAction(name = “addProduct”)
public void addProduct(ActionRequest actionRequest,
ActionResponse actionRresponse) {
try {
AAALocalServiceUtil.aaa();
BBBLocalServiceUtil.bbb();
CCCLocalServiceUtil.ccc();
} catch (Exception e) {
e.printStackTrace();
SessionMessages.add(actionRequest, “error”);
}
}
}