Hi I have tested this code with junit 4:
public class UserDaoTest {
/**
* Runs before all test methods
*/
#BeforeClass
public static void createDB() {
sessionFactory = HibernateUtil.getSessionFactory();
userDao = new UserDaoImpl();
languageDao = new LanguageDaoImpl();
requestDao = new RequestDaoImpl();
feedbackDao = new FeedbackDaoImpl();
hostelDao = new HostelDaoImpl();
imageDao = new ImageDaoImpl();
}
/**
* Runs after all test methods
*/
#AfterClass
public static void closeSessionFactory() {
HibernateUtil.shutdown();
}
/**
* Runs before each test method
*
* #return
*/
#Before
public void beginTransaction() {
session = sessionFactory.getCurrentSession();
transaction = session.beginTransaction();
}
/**
* Runs after each test method
*/
#After
public void rollbackTransaction() {
if (transaction != null && transaction.isActive()) {
System.out.println("Rolling back trasnaction after #Test method");
// rollback transaction so that tests don't modify database
transaction.rollback();
}
}
#Test
public void testSaveUser() {
User user1 = new User("Ostap", "Stashyshyn", Gender.MALE);
// save user. It delegates to `session.save(user1)` method.
userDao.create(user1);
Integer userId = user1.getUserId();
Assert.assertNotNull(userId);
// causes persistent entities to be saved into persistent context
session.flush();
// read languages from db
List<User> users = userDao.readAll();
Assert.assertThat(users.size(), CoreMatchers.is(1));
// next method annotated with #After is running.
// It rollbacks transaction causing hibernate not to store data into database. Thus database state is the same as when entering this test method
}
It tests successfully, but when I set breakpoint after session.flush() and go to db from command line I see that there is no row for user.
Calling userDao.readAll(); causes hibernate to issue select statement and return users?
I see insert and select statements on console but nothing in db. But when I call transaction.commit() instead of session.flush() then appropriate data is in db.
Is above code correct way to test saving user? Or I should call transaction.commit() instead of session.flush()?
EDIT: I shall adhere to this way of testing org.hibernate.Session?
Thank you!
The session used in this test is a different session to the db session you got from command. Until transaction.commit(), you will not see any data changes by your test.
Your test is valid! You should not use transaction.commit() instead of session.flush() as it will persist your test data and make your next run harder.
You only do transaction.commit() instead of session.flush() if you want to debug your test and verify the test data manually/
session.flush() doesn't mean that persistent object will be written to database. flush will only responsible for synchronizing the underlying persistent store with persistant state held in memory. in simple word it will update or insert into your tables in the running transaction, but it may not commit those changes all this is depends on FlushMode setting.
As the code given by you is not complete so its difficult to tell wther its correct or not. Because what you have written in userDao.create() is hiiden? Where you beginning Transaction is also not clear.
Related
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.
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 have bellow code stuffs and I'm using spring and hibernate
//main method in main class
public static void main(String[] args) {
String[] path = new String[]{"applicationContext.xml"};
ApplicationContext context = new ClassPathXmlApplicationContext(path);
serviceObj = (ServiceClassType)context.getBean("serviceBean");
serviceObj.doTask();
}
//service method in service class
doTask(){
Obj obj=new Obj();
obj.setValue1("value1");
obj.setValue2("value2");
myDao.saveObject(obj);
}
//in dao class
//scenario #1
saveObject(Obj obj){
gethibernatetemplate().save(obj);
}
//scenario #2
saveObject(Obj obj){
session = getHibernateTemplate().getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
session.save(obj);
tx.commit();
}
***scenario #1 does not persists data but scenario #2 working fine. Can someone explain why?
Hibernate Session doesn't work without Transaction in standart configuration. If you add this property<property name="connection.autocommit">true</property> scenario #1 will work .
Because Hibernate doesn't commit the transaction by default. I would recommend to use Spring's Transaction Manager to handle this logic, instead of manual commit/rollback. It automatically commits transaction if everything went fine, and rollbacks the transaction in case of any error. With correct Spring configuration your code will look like:
class ServiceClassType {
#Transactional
public doTask(){
// update entities with your DAO classes
}
}
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.
I know multiple causes can trigger OptimistickLockException, but current test case demands a two transactions write into same data at same time to trigger the exception. More like to create read/write violation issue by parallel transactions. This is what I did:
In the testing class, Create a private field to hold the transactions and some transactional methods:
private List<Transaction> transactions = new ArrayList<Transaction>();
// This method only generate transactions and assign
// them to the member field
#Transactional
public void retrieveTransactions(){
transactions = generateTransactions(); // generateTransactions() is transactional
}
// Following two methods just write those
// transactions into same summery, only the first one sleeps
// during the process to make sure the second transaction
// can occur in a parallel way
#Transactional
public void processTransactionsOne(){
for(Transaction transaction:transactions){
logger.info("====Transaction One=====");
transaction.writeToSameSummary(); // writeToSameSummary() is transactional
try {Thread.sleep(10000); } // sleep for 10 seconds before commit
catch(InterruptedException ex) {}
}
}
#Transactional
public void processTransactionsTwo(){
for(Transaction transaction:transactions){
logger.info("====Transaction Two=====");
transaction.writeToSameSummary(); // writeToSameSummary() is transactional
}
}
in applicationContext.xml, I have made above three methods as scheduler, retrieveTransactions run every 60 seconds, processTransactionsOne and processTransactionsTwo run every 10 seconds
<task:scheduled ref="testBeanName" method="retrieveTransactions" cron="*/60 * * * * *"/>
<task:scheduled ref="testBeanName" method="processTransactionsOne" cron="*/10 * * * * *"/>
<task:scheduled ref="testBeanName" method="processTransactionsTwo" cron="*/10 * * * * *"/>
However, I can never reproduce OptimistickLockException and the log showing up
====Transaction One=====
====Transaction One=====
====Transaction One=====
......
====Transaction Two=====
====Transaction Two=====
====Transaction Two=====
So the transaction are running as synchronized and never in parallel. So how can I (1) making parallel transactions and (2)reproduce OptimistickLockException
To reproduce the optimistic locking exception, try to run code from a method that is not itself transactional. For example call a method that reads and modifies an entity, and returns a detached copy.
Then call the same method to modify the same entity, and return a new detached copy. The first copy now is stale compared to the database. Try to merge the first stale copy into a session, it will throw the optimistic locking exception.
This is some sample code for a versioned entity:
#Entity
public class MyEntity {
...
#Version
private Long version;
}
and this an example of a service that reads and returns an entity detached:
#Repository
public class SomeRepository {
#PersistenceContext
EntityManager em;
#Transactional
public MyEntity readAndModify(Long key) {
MyEntity myEntity = em.find( key, MyEntity.class);
myEntity.setSomeField(...); // this will trigger update via dirty checking
return myEntity;
}
}
This code in a test can then trigger the optimistic locking exception:
public someNonTransactionalTest() {
//read and modify entity, get detached copy
MyEntity detachedCopy1 = someRepository.readAndModify(1L);
//read and modify entity, get another detached copy - copy 1 is stale now
MyEntity detachedCopy2 = someRepository.readAndModify(1L);
// try to merge the stale copy to a session, the optimistic locking exception is thrown
detachedCopy1 = entityManager.merge(detachedCopy1);
... assertions ...
}