I have a Java Spring application with an Oracle DB and Hibernate. In my controller, I'm calling a DAO to retrieve some data. The DAO method proceeds until it reaches the return statement and then it fails to return to the controller. No exception is thrown. Instead, it times out. It's something like this
Controller:
#Autowired
DAO dao;
public #ResponseBody int controller(){
//stuff
System.out.println(1);
Map<Long, DBObj> objs = dao.getObjMap(ids);
System.out.println(3);
//other stuff
}
DAO:
#Transactional
public Map<Long, DBObj> getObjMap(List<Long> ids){
//stuff
System.out.println(2)
return objs;
}
Output:
1
2
As far as I can tell, it is retrieving from the DB correctly, so it doesn't seem to be a DB issue. Other database calls work fine.
From the debugger, it seems to be hanging somewhere inside the return statement. Specifically, it seems to be hung on SocketInputStream.java while trying to call socketRead0
EDIT: The problem was to do with sorting. I sorted the child objects of the retrieved object. On return, Hibernate was attempting to make additional database calls and hanging as a result. I resolved this by passing the parent object to the calling method and then sorting in the calling method instead of the DAO.
Try this :
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#Documented
#Transactional(
timeout = 3600,
rollbackFor = { RuntimeException.class, ApplicationCheckedException.class },
noRollbackFor = { ApplicationCheckedNoRollbackException.class, InternalNoRollbackException.class })
public #interface LongTx {
// Empty.
}
Annotate your method with #LongTx
#LongTx
public Map<Long, DBObj> getObjMap(List<Long> ids){
//stuff
System.out.println(2)
return objs;
}
Just see to it there is no lock on the Database from say some other transaction, meaning see to it that if there is some lock mechanism used in the code.
There may be some other transaction holding a lock on the records in the table.
Related
I have a weird behaviour in my Spring Boot App.
The app have the OpenSessionInView to false.
I have a Controller and a Service exposing 2 methods with annotation #Transactionnal.
Application.properties :
spring.jpa.open-in-view=false
My service :
#Service
public class MyService {
#Transactional(transactionManager = "myTx")
public void doA(Integer objectId) {
Object o = repo.findMyObject(objectId);
updateMyObject(o);
repo.save(o);
}
#Transactional(transactionManager = "myTx")
public void doB(Integer objectId) {
Object o = repo.findMyObject(objectId);
updateMyObjectDifferently(o);
repo.save(o);
}
}
My Controller (case 1) :
#RequestMapping("/do/{myId}")
public String do(Model model, HttpServletRequest request) {
service.doA(myId);
service.doB(myId);
return "page";
}
With SQL in debug, I see that the SELECT queries are performed during the call to the service.
But I see only 1 flush (several UPDATES), and it's done when service.doB() is finished and the TransactionInterceptor around the method launch the method invokeWithinTransaction which is weird.
As both method have #Transactional, I was hoping to see 2 flush : the first flush just at the end of service.doA() and a second flush at the end of service.doB().
What is more weird is that if I comment the second call, so
My Controller (case 2) :
#RequestMapping("/do/{myId}")
public String do(Model model, HttpServletRequest request) {
service.doA(myId);
//service.doB(myId);
return "page";
}
In case 1, it's like service.doA() knows that a second call will arrive just after on the same object, so it does not commit/flush the transaction and wait for the end of service.doB().
Why do I see only 1 flush ?
Is it because both calls are on the some DB object ?
I thought my knowledge of #Transactional were ok.
But now, I am lost.
Hibernate will detect if an object really is dirty and avoid sending an UPDATE statement if it's not necessary. I assume that your updateMyObject is simply not changing the state of the entity with respect to the state it had initially when it was loaded.
My question is given below. Pseudocode Code is given below:
public Object rollBackTestMainMethod(List<Object> list) {
List<Object> responseList = new ArrayList<>();
for(Object item:list){
try{
Boolean isOperationSuccess = rollBackTestSubMethod(item);
if (isOperationSuccess==null || !isOperationSuccess){
item.addError("Operation failed");
item.addSuccess(false);
} else {
item.addError(null);
item.addSuccess(true);
}
} catch(Exception exception) {
item.addError(exception.getMessage());
item.addSuccess(false);
}
responseList.add(item);
}
return responseList;
}
#Transactional(rollbackFor = {Exception.class, SQLException.class})
private Boolean rollBackTestSubMethod(Object listItem){
Long value1=save(listItem.getValue1());
if(value1==null){
throw new Exception("Error during save 1");
}
Long value2=save(listItem.getValue2());
if(value2==null){
throw new Exception("Error during save 2");
}
Long value3=save(listItem.getValue3());
if(value3==null){
throw new Exception("Error during save 3");
}
return Boolean.TRUE;
}
What I am doing here:
Iterate a list in rollBackTestMainMethod(). Sending one list item in rollBackTestSubMethod() and performing a 3 save operation.
If all save complete then returning true response, otherwise throwing an exception.
In rollBackTestMainMethod(), after getting response or exception, it is adding error or successful value on each item.
It is adding this item in new list named responseList. After all operations it is sending this back as response.
My questions:
After throwing from rollBackTestSubMethod() it will not be rolled back because it is calling from a try catch block.
If I want to forcefully roll back via TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); then it will be rolled back all item for any throw/exception.
Here I want rollback only for throw item not all item.
This method's are in a spring bean
I am saving data into my relational database via spring data jpa
My imports:
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
It's because you're invoking #Transactional method from within same bean.
#Transactional only works on methods invoked on proxies created by spring. It means, that when you create a #Service or other bean, method called from the outside will be transactional. If invoked from within bean, nothing will happen, as it doesn't pass through proxy object.
The easiest solution would be to move the method to another #Service or bean. If you really want to keep it within same component, then you need to invoke it, so that it gets wrapped in proxy by spring AOP. You can do this like that:
private YourClass self;
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void postContruct(){
self = applicationContext.getBean(YourClass.class);
}
Then invoking method on self would result in opening a transaction.
Marking a non-public method #Transactional is both useless and misleading because Spring doesn't "see" non-public methods, and so makes no provision for their proper invocation. Nor does Spring make provision for the methods invoked by the method it called.
Therefore marking a private method, for instance, #Transactional can only result in a runtime error or exception if the method is actually written to be #Transactional.
I have the following code:
public void method1(String id){
Object object = repository.findOne(id);
object.setState("running");
repository.save(object);
try{
object2.method2(object); //This method takes 2 hours to complete
object.setState("complete");
}catch(Exception e){
object.setState("failed");
}
repository.save(object);
}
So, I change the state to "running" before calling a method that takes hours to execute. My object is a JPA Entity(with lazily loded collections) and method2() tries to load all the linked entities.
Now, in method2, I am getting
could not initialize proxy - no Session
error because it is outside of transaction (expected behavior). To prevent this, there are two solutions:
Annotate method1 with #Transactional. This would solve it, but then, the state won't be reflected to other transactions until the method execution finishes.
Change the fetch mode in Entity config and make it Eager. This would also solve it but I don't want eager fetching every time.
Is there any other way by which I can make it work?
How about this:
Option 1
1) Create a service method for status changing like following:
#Transactional( propagation = Propagation.REQUIRES_NEW)
public void changeStatusInNewTransaction(String id, String status){
Object object = repository.findOne(id);
object.setState(status);
repository.save(object);
}
2) Change the original method as follows:
#Autowired
Service service;
#Transactional
public void method1(String id){
service.changeStatusInNewTransaction(id, "running");
Object object = repository.findOne(id);
try{
object2.method2(object); //This method takes 2 hours to complete
object.setState("complete");
}catch(Exception e){
object.setState("failed");
}
repository.save(object);
}
Thanks to this set-up, everything can be run under one #Transactional method, but when the state is to be changed to 'running' then :
The current transaction would be suspended
New one would be created
State would be changed and transaction commited
Parent transaction would continue and you can process with your big operation not having a problem that other users will wont see the status change for 2 hours..
Option 2
1) Create a service method for status changing like following:
#Transactional
public void changeStatusInNewTransaction(String id, String status){
Object object = repository.findOne(id);
object.setState(status);
repository.save(object);
}
2) Create transactional method just for long processing
#Transactional
public void performLongProcessing(String id){
Object object = repository.findOne(id);
object2.method2(object); //This method takes 2 hours to complete
object.setState("complete");
repository.save(objects;
}
3) Mark the main method to run without transaction:
#Transactional(propagation = Propagation.NOT_SUPPORTED)
public void method1(String id){
service.changeStatusInNewTransaction(id, "running");
try{
service.performLongProcessing(id);
}catch(Exception e){
service.changeStatusInNewTransaction(id, "failed");
}
}
Having a transaction around a method that executes for several hours, seems like a design mistake, so method1() should not have #Transactional! When you start a transaction, you need a connection and this connection will be allocated from you connection pool for the entire duration, which greatly limits scalability (and pisses of your DBA).
could not initialize proxy - no Session
You get this error because (without #Transactional on method1) your entity is detached after repository.save() has been called, and you can't load the lazy collections. A quick solution for this is to inject an EntityManager into object2 and call EntityManager.refresh() inside method2() this does not require a transaction, as you are only reading data.
There is no reason to use any sort of Transaction propagation to solve this issue.
I am inserting the data in one method(has #Transactional(propagation = Propagation.Required)) but in the other method(has #Transactional(propagation = Propagation.Required)) if I try to get the same data it is giving null.
Both methods are wrote in service layer with #Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
How to get the data which is inserted in the same transaction.
Something like :-
#Service
public class Service{
#Transactional
public void method(){
mapper.insert(); //insert to DB(Using Mapper interface)
ServiceLayer.method2()
}
}
#Service
public void ServiceLayer{
#Transactional
public static void method2(){
result = mapper.select() //Select inserted data - returning null
}
}
In order to persist the changes made to the current session you can invoke entityManager.flush();
It may be worked, but it's not a solution.
In your case, your Transaction from Service.method() created a transaction that is not committed yet. That's why you can't fetch it.
I found the answer...after removing #transactional from ServiceLayer.method2() it's worked fine.
Due to lack of key words to capture this scenario, let me just proceed to describe it. The classes have been simplified.
Given this:
public ItemController {
#Autowired
ItemDtoService ItemDtoService;
#Autowired
DiscountService discountService;
#RequestMapping(value = "/viewItems", method = RequestMethod.POST)
public void process() {
List<ItemDto> ItemDtos = ItemDtoService.getItemDtos();
for(ItemDto i: ItemDtos) {
boolean isDiscounted = discountService.hasDiscount(i); //throws exception here on iteration 2 and the last iteration, ItemDto was discounted
if (isDiscounted) {
i.setPrice(discountService.getDiscountedPrice(i));
//do some other i.setter, basically modify the pojo
}
}
}
}
An exception is thrown at the discountService.hasDiscount when:
on subsequent iteration
and the previous iteration, the ItemDto was discounted.
Exception is:
Caused by: org.hibernate.exception.SQLGrammarException: could not update: [somepackage.ItemDto#364]
And somewhere in the stacktrace you will see this:
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:456)"
The problem is that method call uses a dao method underneath that is #Transactional (and maybe for a good reason even though it's only a query, complicated query). When the JPA Tx manager does its job upon method call end, it sees the pojo as modified and tries to synch it. The ItemDto pojo does have #Entity because inside ItemDtoService.getItemDtos uses the getEntityManager().createNativeQuery(nativeSql, ItemDto.class). The 5 other class details are here:
#Entity
public class ItemDto{
//body
}
#Service
public class ItemService {
#Autowired
ItemDao itemDao;
public List<ItemDto> getItems() {
return itemDao.getItems(); //for sake of simplicity
}
}
#Repository
#Transactional
public class ItemDaoImpl {
public List<ItemDto> getItems() {
String nativeSql = "select...."
return getEntityManager().createNativeQuery(nativeSql, ItemDto.class);
}
}
#Service
public class DiscountService {
#Autowired
DiscountDao discountDao;
public boolean hasDiscount(ItemDto i) {
boolean hasDiscount = discountDao.hasDiscount(i);
//do other service stuff that might influence the hasDiscount flag
return hasDiscount;
}
}
#Repository
#Transactional
public class DiscountDaoImpl {
public boolean hasDiscount(ItemDto i) {
String nativeSql = "select...."
boolean hasDiscount;
//in reality the query is a complicated joins, executes and returns if has discount or not
return hasDiscount;
}
}
What am I doing wrong?
Some of the options I tried and worked include:
add to the #Transactional the (readonly=true) on the Dao methods
since they are only queries (negative effect though is those might
be intentionally transactional due to complex queries, and may need
locking to prevent dirty reads)
in the Controller, create a separate loop for modification, it
then have 2 loops, 1 for looping through items and seeing which is
discounted, store those info somewhere to be referenced later on 2nd
loop, which does the modification of said pojos
I am looking at other options, and please comment if you see something wrong with the way it was coded.
Another option I just found is inside the Dao that returns the list of ItemDto, before returning the list, I would execute this:
getEntityManager().clear();
It works fine because the list is Dto anyways and one would expect that these require no DB synching, at the same time the #Transactional is retained for necessary locking for consistent reads.
That's one more alternative, but what is the most appropriate way really?