gethibernatetemplate().save(object) does not persist data - java

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
}
}

Related

Why transaction not getting rollbacked in Spring JPA for REQUIRED propagation level?

I have two methods in the JPA repository. Both the methods have propagation level as REQUIRED
The methods are used to persist entity objects using Hibernate to Postgresql
#Transactional(propagation = Propagation.REQUIRED)
public void persistEmployee() {
Employee employee = new Employee("Peter", "Washington DC");
entityManager.persist(employee);
try {
persistLineManager();
}
catch( Exception e ) {
// some task
}
}
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = RuntimeException.class)
public void persistLineManager() {
Employee lineManager = new Employee("John", "NYC");
entityManager.persist(lineManager);
if(lineManager != null) // intentionally! To trigger rollback
throw new RuntimeException("Rollback!");
}
As per Spring docs when propagation level is REQUIRED both methods will run in the same transaction. In my code, I am intentionally throwing the Exception to trigger the rollback but still, both the entities are getting persisted. But I believe both the operations should be rollbacked. Please correct if my understanding is incorrect and let me know the correct way to
rollback both the operations.
PROPAGATION_REQUIRES_NEW: [ from spring Docs ]
PROPAGATION_REQUIRES_NEW, in contrast to PROPAGATION_REQUIRED, uses a completely independent transaction for each affected transaction scope. In that case, the underlying physical transactions are different and hence can commit or roll back independently, with an outer transaction not affected by an inner transaction’s rollback status.
PROXYFICATION
In your service, you created 2 methods, both #Transactional. When you create your bean, spring will create a proxy to add for you at runtime the behavior for a Transactional method. Let's dive deeper:
This proxyfication is illustrated by the image. Any caller form the outside world will not directly talk to you, but to your proxy. And then, the proxy will call you to execute the code of your service.
Now, this "Any caller form the outside world will not directly talk to you" is very important. If you make an inner call, like you do in persistEmployee which is calling persistLineManager, then you do not pass through the proxy. You call directly your method, NO PROXY. Therefor, the annotations at the top of your persistLineManager method are not read.
So, when persistLineManager is throwing a RuntimeException, the exception is catched directly by your caller persistEmployee, you go directly in your catch. As there is no proxy, there is no rollback because the transactional proxy did not catch the exception.
If you do only this, you will have a rollback occurring:
#Transactional(propagation = Propagation.REQUIRED)
public void persistEmployee() {
Employee employee = new Employee("Peter", "Washington DC");
entityManager.persist(employee);
persistLineManager();
// Don't catch and the exception will be catched by the transaction proxy, which will rollback
}
public void persistLineManager() {
Employee lineManager = new Employee("John", "NYC");
entityManager.persist(lineManager);
if(lineManager != null) // intentionally! To trigger rollback
throw new RuntimeException("Rollback!");
}
By default, #Transactional rollback for a RuntimeException
TRANSACTION TEMPLATE
Suppose you still want both method to be transactional independently, what you can do is using the TransactionTemplate. Here is an example:
class MyService {
// Have a field of type TransactionTemplate
private TransactionTemplate template;
// In the constructor, Spring will inject the correct bean
public MyService(PlatformTransactionManager transactionManager) {
template = new TransactionTemplate(transactionManager);
// Set this here if you always want this behaviour for your programmatic transaction
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
}
// Here you start your first transaction when arriving from the outside
#Transactional(propagation = Propagation.REQUIRED)
public void persistEmployee() {
Employee employee = new Employee("Peter", "Washington DC");
entityManager.persist(employee);
// Inner call
try {
persistLineManager();
} catch (RuntimeException e) {
// Do what you want
}
}
public void persistLineManager() {
// Here, ask to your transactionTemplate to execute your code.
template.execute(status -> {
Employee lineManager = new Employee("John", "NYC");
entityManager.persist(lineManager);
if(lineManager != null) // intentionally! To trigger rollback
throw new RuntimeException("Rollback!");
return null;
});
}
}
I haven't tested everything, you might face some errors, but I hope you get the idea.
PROPAGATION
Let me add a last part about the difference between PROPAGATION_REQUIRED and PROPAGATION_REQUIRES_NEW:
PROPAGATION_REQUIRED:
Either I have no transaction, then I create one
Or There is a running transaction, and I join it.
PROPAGATION_REQUIRES:
In any situations, whether a transaction is running or not, I create a new one.
Example:
A client is entering in my transactional method, with PROPAGATION_REQUIRED. It creates a transaction name "TA".
This transactional method calls a method, which is also transactional, but PROPAGATION_REQUIRES_NEW. It creates a second transaction named "TB"
With have now: "client" -> "TA" -> "TB"
But the second method triggers a rollback. In that case, only "TB" will be rolledback, as "TA" and "TB" are 2 differents transactions.
So, in DB, I will persist every operation that has been made in "TA", but not in "TB".
Hope it helps

Is there any reasons to call entityManager.flush() right after the manager was created (JPA, Hibernate)

I am following Hibernate video lesson, and there were shown this code:
public class Main {
private static EntityManagerFactory entityManagerFactory;
public static void main(String[] args)
{
entityManagerFactory = Persistence.createEntityManagerFactory("org.hibernate.tutorial.jpa");
addEntities("Client1","Bank1");
entityManagerFactory.close();
}
private static void addEntities(String clientName, String BankName)
{
Client client = new Client();
client.setName(clientName);
Bank bank = new Bank();
bank.setName(BankName);
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
entityManager.flush();
entityManager.persist(client);
entityManager.persist(bank);
entityManager.getTransaction().commit();
}
}
And I am concerned about this part of code:
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
entityManager.flush();
We generated new EntityManager. As I understand, it has empty Persistence context, as it was just created, isn't it ?
In that case,why do we call flush() method. What is the purpose ?
EntityManager#flush actually pushes the changes to the database immediately.
In the above code, a transaction has just started entityManager.getTransaction().begin() and there is no change that needs to be pushed to the database so I would say it is not needed there. You may remove it.
Anyways, it is a good practice to let entitymanager take care of when to push the data changes to the database instead of manually taking control over it. There could be use case when different applications or threads are trying to access the same data at same time.

is it possible to use #transactional annotation with a transaction opened by hand (spring/hibernate)

I have a spring application which works with hibernate and annotations.
I open a session by hand which is done like in this How do I manually open a hibernate session? stack overflow answer.
Now i want to use a method of a service in the method that opens the session itself. This service however is annotated with the transactional statement. Is it possible to tell the method to use the transaction i just opened by hand?
e.g.
#Transactional("TransactionManager")
class Service1 {
public LazyObject someMethod();
}
class MetaService {
#Autowired
SessionFactory sf;
Service1 s1;
public someMethod() {
Session s = sf.openSession();
s.beginTransaction();
// tell this method to use s's transaction
// without annotating someMethod() with #transactional
LazyObject lo = s1.someMethod();
for ( LazyAtt la : lo.getLazyAtt() ) {
la.doSomething();
}
s.flush();
s.getTransaction().commit();
s.close();
}
}
For those wondering why I want to do it, check this question:
https://stackoverflow.com/questions/29363634/how-to-open-two-sessions-in-one-function-in-hibernate-spring

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.

How to test org.hibernate.Session?

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.

Categories

Resources