Spring #Transactional does not succeed or hangs up - java

I need help with my project since I am not familiar with the spring #transactional annotation. The question is why my application hungs up using the #transactional method but will not hung up without it. Also how do I solve this so that the transaction will suceed.
The scenario is that my application is set-up like this:
Uses declarative transaction Management
One method has the #transactional(rollbackFor=Exception.class) annotation and accesses the database multiple times.
The said method calls another method that returns a String and accesses the database multiple times.
Transaction does not suceed finishing causes a deadlock on my application. It does not return any exception.
The Code below is sample snippet
#Autowired
JdbcTemplate template;
#Transactional(rollbackFor = Exception.class)
public final void upsertData(){
insertTable1(); // insert query to insert in table 1
addReferencingData(); // was just called to update table but returns something which is not used;
//Hangs up before getting to next statement
somePreparedStatmentSQLmergeTable1(mergesql,template); // query to merge in table 1
}
public final String addReferencingData(){
updateTableA(); // update query to update values in table A
updateTableB(); // update query to update values in table B
mergeTable1(); // merge query to update or insert in table 1
return someString;
}
public static void somePreparedStatmentSQLmergeTable1(sql,template){
template.batchUpdate(sql, new BatchPreparedStatementSetter() {
public void setValues(final PreparedStatement ps, final int i){
// setting parameters to be used
}
public int getBatchSize(){
// returns size of data's
}
}
}
Also added the default Transaction manager on my application-context.xml file.Transaction already works based on logs. Only in this specific method it does not work.
Updated some information to be clearer.

Related

Is it possible to use "TRUNCATE" in Spring Data JPA using JpaRepository? Or a more effective method than the standard deleteAll()?

Im currently using the standard jparepository method repository.deleteAll() to clean my table before adding new info. The table consists of 8300 rows with 5 columns each. It's currently taking about 80 sec to get them all removed, while it takes 1-3 sec to put them in using the standard repository.saveAll(list). Is there a more effecient way to do this? Deleting the data manually in sql with DELETE FROM table takes 0,1 sec. Using MySQL database. log from putting in data
log from deletion.
Example, in your service interface:
public interface MyTableInterface {
//...
void truncateMyTable();
}
In your service implementation (with #Autowired myTableRepository):
public class MyTableImpl implements MyTableService {
// other methods, #Autowiring, etc
#Override
#Transactional
public void truncateMyTable() {
myTableRepository.truncateMyTable();
}
}
In your repository;
public interface MyTableRepository extends JpaRepository<MyTable, Long> {
//....
#Modifying
#Query(
value = "truncate table myTable",
nativeQuery = true
)
void truncateMyTable();
}
EDIT: Also notice the #Transactional on service implemntation layer, instead of placing it on DAO/Repository layer
The reason that deleteAll is not fast, is because it fetches all entities and then removes them one by one (normally sending one SQL to the database per row):
// Code from SimpleJpaRepository
#Transactional
public void deleteAll() {
for (T element : findAll()) {
delete(element);
}
}
I would suggest that you create your own delete method in the repo like:
#Modifying
#Transactional
#Query("delete from MyEntity m")
void deleteAllWithQuery();
This will create only one SQL DELETE statement.

How to force commit entity in #Transactional mode

I am using Spring data jpa and jdbc (using entityManager.unwrap(Session.class) ) connection.
My request flows through 3 method. 1st -> 2nd ->3rd.
1st and 3rd are annotated with #Transactional.
In 1st and 2nd method saving entity through JPA CRUD repository.
In 3rd method using basic jdbc query to retrieve values saved in 2nd method.
As we know in #Transaction entities are not committed to database until JPA commit the transaction.
I used saveAndFlush also in 2nd method but can not see retrieve updated values in method 3 using jdbc query.
1st Method - update()
#Transactional
#Override
public ApiResponse update(RequestInput input) {
ApiResponse response = doRestOfWork(input); // calling 2nd method
// .... some other entity save....
}
2nd Method - doRestOfWork() : setting status=true and calling saveAndFlush method
#Override
public ApiResponse doRestOfWork(Request request) {
Insight insight = insightRepository.findOne(request.getTypeId());
insight.setStatus(true);
insightRepository.saveAndFlush(insight);
processOperation(); // calling 3rd method
}
3rd method - processOperation() : retrieving updated status value through jdbc connection.
#Transactional
public void processOperation() {
Connection conn = null;
SessionImplementor sessionImpl = (SessionImplementor) entityManager.unwrap(Session.class);
try {
conn = sessionImpl.getJdbcConnectionAccess().obtainConnection();
Connection conn = null;
String stmt = "SELECT status from insight";
PreparedStatement ps = conn.prepareStatement(stmt);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
boolean value = rs.getBoolean(status); // returning false, i want this to return true as already set true in 2nd method and called saveAndFlush
}
} catch (SQLException ex) {
} finally {
JdbcUtility.closeResources(conn, ps, rs);
}
}
InsightRepository is extending JpaRepository
#Repository
public interface InsightRepository extends JpaRepository<Insight, Long> {}
I want updated value of status (which is boolean true - updated in method 2) in method 3.
How to achieve this ?
Update
I searched a lot and do not think that if a method is annotated with #Transactional then you can commit changes before completing JPA transaction. So the solution is to remove #Transactional annotation and use entityManager.getTransaction() if want to control over JPA transaction.
Create a method (4th method) for update with #Transactional(propagation = Propagation.REQUIRES_NEW) annotation in a bean different from the 2nd Method and change second method as calling 4th method like this :
#Component //or Service etc..
public class AnotherBean{
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void doUpdateWork(Request request){
Insight insight = insightRepository.findOne(request.getTypeId());
insight.setStatus(true);
insightRepository.save(insight);
}
}
and
#Override
public ApiResponse doRestOfWork(Request request) {
anotherBean.doUpdateWork(request);
processOperation(); // calling 3rd method
}
If you want to controll transaction, dont use #Transactional
em; //entityManager
Transaction tx=em.getTransaction();
try{
tx.begin();
//do work
tx.commit(); // comminted transaction
// start another transaction if you need to, doo more work etc.
...
}
This is most basic and fundamental block of code for using JPA for atomic operations.
Also there is no need to unwrap entity manager, use pure JPA. By unwrapping you are binding current implementation to JPA provider which is in contrary to JPA idea of independency from underlying JPA implementation provider. In other words, it will stop working if for some reasons Spring would change its JPA implementation from Hibrnate to eg. EclipseLink.

Spring data : #Transactional and propogation

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.

Java Spring DAO hangs on return statement

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.

Spring #transaction not working as expected in junit in non debug mode

All MyService methods are transactional. The junit test below, gets count of items, saves a new item, and gets count of items to make sure that counts has been incremented by 1.
public class MyTest extends ServiceTest{
1. int countBefore = myService.getCount(); //return n
2. myService.add(item); //item is really added to DB
3. int countAfter = myService.getCount(); //return n (sometimes n+1)
}
#Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED)
getCount(){…}
#Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.SERIALIZABLE)
add(){…}
#Ignore
#ContextConfiguration(locations = { "file:src/main/resources/xxx-context.xml",
"file:src/main/resources/xxx-data.xml",
"file:src/main/resources/xxx-services.xml" })
#TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)
#TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
TestListener.class})
public class ServiceTest extends AbstractUT{
#Ignore
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners( {TestListener.class})
public class AbstractUT{
When debugging (3.) returns n+1 which is what I want. But when running the test without debug I get n.
Even sometimes when running the test I get n+1 and next time I get n and when comparing the std output between the two execution, it looks exactly the same. I have enabled log4j.logger.org.springframework.transaction=TRACE and I can see:
Initializing transaction synchronization
Getting transaction for MyService.getCount
...
Completing transaction for MyService.getCount
Clearing transaction synchronization
...
Initializing transaction synchronization
Getting transaction for MyService.add
...
Completing transaction for MyService.add
Clearing transaction synchronization
...
Initializing transaction synchronization
Getting transaction for MyService.getCount
...
Completing transaction for MyService.getCount
Clearing transaction synchronization
So transactions are being executed one after the other, but how is possible that (3.) don't see the saved item?
Transaction managment is setup in my test class as per: https://stackoverflow.com/a/28657650/353985
How can I find what is going wrong?
Thanks!
Had similar issue, but in my case it did not rollback. It seems that you forgot to add #Transactional. From documentation (link)
Transaction management
In the TestContext framework, transactions are managed by the
TransactionalTestExecutionListener which is configured by default,
even if you do not explicitly declare #TestExecutionListeners on your
test class. To enable support for transactions, however, you must
configure a PlatformTransactionManager bean in the ApplicationContext
that is loaded via #ContextConfiguration semantics (further details
are provided below). In addition, you must declare Spring’s
#Transactional annotation either at the class or method level for your
tests.
Here is example form the link above.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
#Transactional
public class FictitiousTransactionalTest {
#BeforeTransaction
public void verifyInitialDatabaseState() {
// logic to verify the initial state before a transaction is started
}
#Before
public void setUpTestDataWithinTransaction() {
// set up test data within the transaction
}
#Test
// overrides the class-level defaultRollback setting
#Rollback(true)
public void modifyDatabaseWithinTransaction() {
// logic which uses the test data and modifies database state
}
#After
public void tearDownWithinTransaction() {
// execute "tear down" logic within the transaction
}
#AfterTransaction
public void verifyFinalDatabaseState() {
// logic to verify the final state after transaction has rolled back
}
}
A solution I found till now to pass test is to put assert in afterTransaction method
public class MyTest extends ServiceTest{
#Test
public void test(){
1. int countBefore = myService.getCount(); //return n
2. myService.add(item); //item is really added to DB
}
#AfterTransaction
public void verifyFinalDatabaseState() {
3. int countAfter = myService.getCount(); //return n (sometimes n+1)
//Now always return n+1
}
I would have asked this in a comment but since my reputation does not allow it, I would just try to provide an answer.
It is possible that your are using and ORM that caches the results of count query. Depending how your add/getCount methods are implemented and the configurations of the ORM and datasource, on your second invocation of getCount, you might get a cached value obtained during first invocation of getCount.
This does not explain however why in debug mode you always get the correct result.
Because the current running transaction is set at the test method level, you have two options:
You either remove the #Transactional from the test method and rely on your service method #Transactional boundaries. This way when you call:
int countBefore = myService.getCount();
myService.add(item);
int countAfter = myService.getCount();
Each service call will run in an isolated transaction, just like it happens in the run-time production call.
You flush the Hibernate Session, just after adding the item:
int countBefore = myService.getCount();
myService.add(item);
transactionTemplate.execute(new TransactionCallback<Void>() {
#Override
public Company doInTransaction(TransactionStatus transactionStatus) {
entityManager.flush();
return null;
}
});
int countAfter = myService.getCount();
A HQL/JPQL count query should trigger a flush in AUTO flush mode, but an SQL native query doesn't flush the Session.

Categories

Resources