I'm trying to upgrade hibernate from 3 to 4 and running into multiple issues. Here is a configuration we had for v3.
#Bean
public LocalSessionFactoryBean sessionFactory(DataSource datasource) {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(datasource);
sessionFactory.setPackagesToScan("com.company.hs.service");
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory);
return transactionManager;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "com.company.hs.service.hibernate.MySQL5InnoDBIndexDialect");
properties.put("hibernate.show_sql", Boolean.TRUE.toString());
properties.put("hibernate.generate_statistics", Boolean.FALSE.toString());
properties.put("transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory");
properties.put("transaction.manager_lookup_class", "org.hibernate.transaction.JBossTransactionManagerLookup");
properties.put("hibernate.cache.use_query_cache", Boolean.TRUE.toString());
properties.put("hibernate.cache.use_second_level_cache", Boolean.TRUE.toString());
return properties;
}
After upgrading dependency version and class packages itself, I was able to compile and start application.
But then, after trying to execute any write operation against DB I'm getting following error:
org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
After researching ton of information, there seems to be multiple options to go about it.
Option 1:
Override OpenSessionInViewFilter like suggested in How can I globally set FlushMode for Hibernate 4.3.5.Final with Spring 4.0.6?
Though it seems to help, there are multiple edge cases when app behavior changed (i.e. HIbernateTemplate's method persist does not update entity id in place while using #GeneratedValue(strategy = GenerationType.IDENTITY), so instead we have to use save method). And in general, concerned about other side effects since it seems that transaction management is not properly engaged here.
Option 2:
As it was suggested in https://crunchtech.io/blog/migrating-from-hibernate-3-to-4-with/ instead of using JTATransactionFactory we can switch to CMTTransactionFactory. Which is seems like something we want to proceed since we want Spring container to manage transactions.
Corresponding spring javadocs - https://docs.spring.io/spring-framework/docs/3.2.0.M1_to_3.2.0.M2/changes/docdiffs_org.springframework.orm.hibernate4.html
While trying to execute SQL query it fails with
org.hibernate.TransactionException: Could not register synchronization for container transaction.
For reference, only this piece changed from original configuration:
properties.put("hibernate.transaction.factory_class", "org.hibernate.transaction.CMTTransactionFactory");
properties.put("hibernate.transaction.manager_lookup_class", "org.hibernate.transaction.JBossTransactionManagerLookup");
properties.put("hibernate.transaction.jta.platform", "org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform");
Controversially, Spring's bug tracker suggests completelly opposite approach - https://github.com/spring-projects/spring-framework/issues/15230
Option 3 would be using AOP aspect that will be executed around #Transactional for flushing data into DB.
Option 4 Use JPA
IMHO likellyhood of 3 and 4 is very low.
Multiple examples in internet suggest that migration of Hibernate from 3 -> 4 should work as a charm for Tomcat and most of the issues come when running in Jboss/GlassFish server.
Unfortunately, we run our application inside WildFly.
At this point, I'd appreciate any input on this. Starting with the question what is a general usage paradigm, maybe options mentioned here are completely off and we need to use different mechanism. Or we are missing some crucial piece of configuration.
Corresponding dependency versions
Spring - 4.0.5.RELEASE
Hibernate - 4.2.12.Final
WildFly - 18.0.1
I did manage to get it working with Spring 4 Hibernate 5 running within WildFly w/o customizing OpenSessionInViewFilter or specifying container-specific properies (like hibernate.transaction.factory_class or hibernate.transaction.manager_lookup_class).
Key to success was proper usage of #Transactional annotation and little bit of tweeking queries itself.
Even more to that, in my test app enabling JTA transactions properties (like prescribed here) caused side effects like incorrect rollback on runtime exceptions.
These are the properties I was using to enable it:
properties.put("hibernate.transaction.jta.platform", "JBossAS");
properties.put("hibernate.transaction.coordinator_class", "jta");
Same code w/o these being specified rollbacks all intermediate DB entries as expected. Didn't known why is that yet, but there is no good reason for us to use JTA transactions in the first place.
Related
I want to create a microservice with Spring Boot. For persistence i use a mariadb database. To wait for the database which is running in a docker container, i implemented the following code like shown here:
#Bean
public DatabaseStartupValidator databaseStartupValidator(DataSource dataSource) {
var dsv = new DatabaseStartupValidator();
dsv.setDataSource(dataSource);
dsv.setTimeout(60);
dsv.setInterval(7);
dsv.setValidationQuery(DatabaseDriver.MYSQL.getValidationQuery());
return dsv;
}
The code is working very well, my application is now waiting for the database connection. But i get an exception at startup of the application:
java.sql.SQLNonTransientConnectionException: Could not connect to Host ....
...
...
...
In the next line i get an information, that it will wait for the database:
021-04-07 21:29:40.816 INFO 16569 --- [ main] o.s.j.support.DatabaseStartupValidator : Database has not started up yet - retrying in 7 seconds (timeout in 57.65 seconds)
After that the application is starting as expected. So i think everything is working fine, but what i have to do to suppress the Exception? In the linked article it should work without an exception. Do i have to implement the "dependsOnPostProcessor" function? Which dependency i have to use? Sorry, possible a dumb question, i am new to spring boot.
to get rid of that exception you can state the below directive in your application.properties file:
logging.level.com.zaxxer.hikari=OFF
Keep in mind that if the application will not be able to get in contact with the db your spring crashes after a while due to that exception. In addition the above directive prevent you to see any logging activity related to Hikari.
In summary you hide the appearance of the exception until it is possible before the application dies due to timeout.
hoping I clarified a bit the case
Yes indeed you need to add the "depends-on" for the beans that rely on the data source. Note the following part of the documentation:
To be referenced via "depends-on" from beans that depend on database startup, like a Hibernate SessionFactory or custom data access objects that access a DataSource directly.
If I understand it well, this means that beans such as an EntityManagerFactory which rely on the database will now have to go through the DatabaseStartupValidator bean and wait for the DB startup. I don't know what caused your exception, but usually there is an EntityManagerFactory involved, so try adding the DependsOn on this object at least.
This is how the linked article is doing it:
#Bean
public static BeanFactoryPostProcessor dependsOnPostProcessor() {
return bf -> {
// Let beans that need the database depend on the DatabaseStartupValidator
// like the JPA EntityManagerFactory or Flyway
String[] flyway = bf.getBeanNamesForType(Flyway.class);
Stream.of(flyway)
.map(bf::getBeanDefinition)
.forEach(it -> it.setDependsOn("databaseStartupValidator"));
String[] jpa = bf.getBeanNamesForType(EntityManagerFactory.class);
Stream.of(jpa)
.map(bf::getBeanDefinition)
.forEach(it -> it.setDependsOn("databaseStartupValidator"));
};
}
You may not necessarily have Flyway configured, but the main thing to note is the dependency itself is referenced by the bean name databaseStartupValidator which is the name of the method that creates the bean.
I am having spring webservice application with oracle as a database. Right now i have datasource created using weblogic server. Also using eclipse linkg JPA to do both read and write transactions(insert,Read and update). Now we want to separate dataSources for read(read) and wrtie(insert or update) transactions.
My current dataSource is as followed:
JNDI NAME : jdbc/POI_DS
URL : jdbc:oracle:thin:#localhost:1521:XE
using this, I am doing both read and write transactions.
What if i do the following:
JNDI NAME : jdbc/POI_DS_READ
URL : jdbc:oracle:thin:#localhost:1521:XE
JNDI NAME : jdbc/POI_DS_WRITE
URL : jdbc:oracle:thin:#localhost:1521:XE
I knew that using XA datasource we can define multiple dataSources. Can I do same thing without XA dataSource. Does any one tried this kind of approach.
::UPDATE::
Thank you all for your responses I have implemented following solution.
I have taken the multiple database approach. where you will define multiple transactionManagers and managerFactory. I have taken only single non xa dataSource(JNDI) that is refereed in EntityManagerFactory Bean.
you can reefer following links here which are for multiple dataSources
Multiple DataSource Approach
defining #transactional value
Also explored on transaction managers org.springframework.transaction.jta.WebLogicJtaTransactionManager and org.springframework.orm.jpa.JpaTransactionManager as well.
There is an interesting article about this in Spring docs - Dynamic DataSource Routing. There is an example there, that allows you to basically switch data sources at runtime. It should help you. I'd gladly help you more, if you have any more specific questions.
EDIT: It tells, that the actual use is to have connection to multiple databases via one configuration, but you could manage to create different configs to one database with different params, as you'd need to.
I would suggest using Database "services". Each workload, read-only and read-write, would be using its own service to access the database. That way you can use AWR reports to get statistics for each service. You can also turn off read-write when you keep read-only up and running.
Here is a pointer to the Oracle Database documentation that talks about Services:
https://docs.oracle.com/database/121/ADMIN/create.htm#CIABBCAI
If you're using spring, you should be able to accomplish this without using 2 Datasources via spring #Transactional with the readonly property set to true. The reason why I suggest this is that you seem to be concerned about the transactionality only and this seems to be catered for in the spring framework?
I'd suggest something like this for your case:
#Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}
Using this style, you should be able to split read only services from their write counterparts, or even have read and write service methods combined. But both of these do not use 2 datasources.
Code is from the Spring Reference
I am pretty sure that you need to address the problem on the database / connection url + properties layer.
I would google around for something like read write replication.
Related to your question with JPA and transaction. You are doomed when you are using multiple Datasources. Also XA datasources are not really a solution for that. The only thing they do for you is to ensure consistency over multi data source operations. XA Transaction do only span some sort of logical transaction over two transactions (one for each datasource). From the transaction isolation point of view (as long as your not using READ_UNCOMMITED) both datasources use their own transaction. This means the read data source would not see the changes made by the write transaction.
I am working on a setup with multiple databases, technology stack's spring with hibernate running on tomcat 6. Transactions across databases was not a requirement, and each database has its own dataSource, sessionFactory and transactionManager (org.springframework.orm.hibernate3.HibernateTransactionManager) with a declarative use of transaction management (#Transactional annotation). Recently there have been a requirement to have a one-off case of making insertions in two of those DBs (say db1 and db2) transactional.
I am aware that there are third party libraries like JOTM and atomikos, which can add JTA support to tomcat. But I would like to know if it's at all possible to manage transactions manually.
For example, can there be something like following?
Transaction transactionDb1 = sessionFactoryDb1.getCurrentSession().beginTransaction();
Transaction transactionDb2 = sessionFactoryDb2.getCurrentSession().beginTransaction();
try
{
// DAO layer call to DB1
// DAO layer call to DB2
transactionDb1.commit();
transactionDb2.commit();
}
catch (Exception e) {
transactionDb1.rollback();
transactionDb2.rollback();
}
It probably wouldn't be as simplistic. But is something like that possible? As far as I know Programmatic transactional handling can be used. But how do I go about it combining with the declarative approach? Would I still be able to use #Transactional for other cases? Any help would be really appreciated.
You can use programmatic transaction against multiple non-JTA DataSources, but there won't be any global transaction. Each DataSource will use its own isolated transaction, so if the first one commits and the second one rollbacks, you won't have a chance to roll back the already committed first transactions.
The Spring #Transactional annotation can only target one TransactionManager only, and since you don's use JTA, you can either pick one SessionFactory or DataSource. That's why you can only rely on JtaTransactionManager, if you want automatic transaction management. If you don't want JTA, you will have to write your own transaction management code.
Is there any way to remove/suspend a current spring managed hibernate session from a thread so a new one can be used, to then place the original session back onto the thread? Both are working on the same datasource.
To describe the problem in more detail. I'm trying to create a plugin for a tool who has it's own spring hibernate transaction management. In this plugin I would like to do some of my own database stuff which is done on our own spring transaction manager. When I currently try to perform the database actions our transaction manager starts complaining about an incompatibly transactionmanager already being used
org.springframework.transaction.IllegalTransactionStateException: Pre-bound JDBC Connection found! HibernateTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single HibernateTransactionManager for all transactions on a single DataSource, no matter whether Hibernate or JDBC access.
A workaround that seems to do the trick is running my own code in a different thread and waiting for it to complete before I continue with the rest of the code.
Is there a better way then that, seems a bit stupid/overkill? Some way to suspend the current hibernate session, then open a new one and afterworths restoring the original session.
Is there any reason you can't have the current transaction manager injected into your plugin code? Two tx managers sounds like too many cooks in the kitchen. If you have it injected, then you should be able to require a new session before doing your work using the #transactional annotation's propagation REQUIRES_NEW attribute see the documentation for an example set-up
e.g.
#transactional(propogation = Propogation.REQUIRES_NEW)
public void addXXX(Some class) {
...
}
But this would use spring's PlatformTransactionManager rather than leaving it up to hibernate to manage the session / transaction.
I'm playing around with Spring + Hibernate and some "manual" transaction management with PostgreSQL
I'd like to try this out and understand how this works before moving to aop based transaction management.
#Repository
public class UserDAOImpl extends HibernateDaoSupport implements UserDAO {
#Override
public void saveUser(User u) {
Transaction tx = getSession().beginTransaction();
getHibernateTemplate().saveOrUpdate(u);
tx.rollback();
}
}
Calling saveUser here, I'd assume that saving a new User will be rolled back.
However, moving to a psql command line, the user is saved in the table.
Why isn't this rolled back, What do I have to configure to do transactions this way ?
Edit; a bit more debugging seems to indicate getHibernateTemplate() uses a different session than what getSession() returns (?)
Changing the code to
Transaction tx = getSession().beginTransaction();
getSession().persist(u);
tx.rollback();
and the transaction does get rolled back. But I still don't get why the hibernateTemplate would use/create a new session..
A couple of possibilities spring to mind (no pun intended):
a) Your JDBC driver defaults to autocommit=true and is somehow ignoring the beginTransaction() and rollback() calls;
b) If you're using Spring 3, I believe that SessionFactory.getSession() returns the Hibernate Session object wrapped by a Spring proxy. The Spring proxy is set up on the Session in part to handle transaction management, and maybe it's possible that it is interfering with your manual transaction calls?
While you can certainly use AOP-scoped proxies for transaction management, why not use the #Transactional(readOnly=false|true) annotation on your service layer methods? In your Spring config file for your service layer methods, all you need to do to make this work is to add
<tx:annotation-driven />
See chapters 10 and 13 of the Spring Reference Documentation on Transaction Management and ORM Data Access, respectively:
http://static.springsource.org/spring/docs/3.0.x/reference/index.html
Finally, if you're using Spring 3, you can eliminate references to the Spring Framework in your code by injecting the Spring-proxied SessionFactory bean into your DAO code - no more need to use HibernateDaoSupport. Just inject the SessionFactory, get the current Session, and use Hibernate according to the Hibernate examples. (You can combine both HibernateDaoSupport and plain SessionFactory-based Hibernate code in the same application, if required.)
If you see the JavaDoc for HibernateDaoSupport.getSession() it says it will obtain a new session or give you the one that is used by the existing transaction. In your case there isn't a transaction listed with HibernateDaoSupport already.
So if you use getHibernateTemplate().getSession() instead of just getSession(), you should get the session that is used by HibernateTemplate and then the above should work.
Please let me know how it goes.
EDIT:
I agree its protected...my bad. So the other option then is to keep the session thread bound which is usually the best practice in a web application. If HibernateDaoSupport is going to find a thread bound session then it will not create a new one and use the same one. That should let you do rollbacks.