I want to ask how i connect second database in my program . First i tell you this is a inventry system . I want to get functionality that when i update the record in existing data base record also update into other databases. please checkout the code . I use this code for single database connection and for the update record .
I create another button in my webpage . i need that when i click the second button record save in second database .
jdbc.driver=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
jdbc.driver2=com.mysql.jdbc.Driver
jdbc.dialect=org.hibernate.dialect.MySQLDialect
jdbc.databaseurl=jdbc:mysql://localhost:3306/bvasdb?zeroDateTimeBehavior=convertToNull&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
jdbc.username=admin
jdbc.password=admin
<beans:property name="dataSource">
<beans:bean class="${jdbc.driver}">
<beans:property name="url" value="${jdbc.databaseurl}" />
<beans:property name="user" value="${jdbc.username}" />
<beans:property name="password" value="${jdbc.password}" />
</beans:bean>
#RequestMapping("partmaintenanceupdate")
public ModelAndView partmaintenanceUpdate(#RequestParam("partmaintenancemode") String partmaintenancemode,
#RequestParam("searchpartno") String searchpartno,
#RequestParam("makemodelcodeselected") String makemodelcodeselected,
#RequestParam("subcategoryselected") String subcategoryselected,
#RequestParam("ordertypeselected") String ordertypeselected, Model map, HttpSession session,
ModelAndView mav) {
AppUser user = (AppUser) session.getAttribute("user");
if (user == null) {
throw new OrderNotFoundException();
} else {
LOGGER.info("#partmaintenanceupdate");
if (subcategorylistdd.size() == 0) {
subcategorylistdd = mainService.getAllSubCategory();
}
if (makemodellistdd.size() == 0) {
makemodellistdd = mainService.getAllMakeModelMap();
}
if (ordertypelistdd.size() == 0) {
ordertypelistdd = mainService.getAllOrderType();
}
partmaintenancemode = "update";
parts.setPartno(request.getParameter("partno").trim().toUpperCase());
parts.setInterchangeno(request.getParameter("interchangeno").trim().toUpperCase());
parts.setYear(request.getParameter("year").trim());
parts.setYearfrom(Integer.parseInt(request.getParameter("yearfrom").trim()));
parts.setYearto(Integer.parseInt(request.getParameter("yearto").trim()));
parts.setPartdescription(request.getParameter("partdescription").trim());
parts.setActualprice(new BigDecimal(request.getParameter("actualprice").trim()));
parts.setCostprice(new BigDecimal(request.getParameter("costprice").trim()));
parts.setListprice(new BigDecimal(request.getParameter("listprice").trim()));
parts.setWholesaleprice(new BigDecimal(request.getParameter("wholesaleprice").trim()));
parts.setUnitsinstock(Integer.parseInt(request.getParameter("unitsinstock").trim()));
parts.setUnitsonorder(Integer.parseInt(request.getParameter("unitsonorder").trim()));
parts.setReorderlevel(Integer.parseInt(request.getParameter("reorderlevel").trim()));
parts.setSafetyquantity(Integer.parseInt(request.getParameter("safetyquantity").trim()));
parts.setKeystonenumber(request.getParameter("keystonenumber").trim());
parts.setOemnumber(request.getParameter("oemnumber").trim());
parts.setDpinumber(request.getParameter("dpinumber").trim());
parts.setLocation(request.getParameter("location").trim());
parts.setCapa(request.getParameter("capa").trim());
parts.setMakemodelcode(makemodellistdd.get(request.getParameter("makemodelcodeselected").trim()));
parts.setSubcategory(subcategorylistdd.get(request.getParameter("subcategoryselected").trim()));
parts.setOrdertype(request.getParameter("ordertypeselected").trim());
partsService.updatePartsMaintenance(parts);
mav.clear();
mav.setViewName("partmaintenancepage");
mav.addObject("user", user);
mav.addObject("branch", branch);
mav.addObject("appcss", appcss);
mav.addObject("sysdate", InsightUtils.getNewUSDate());
mav.addObject("partmaintenancemode", partmaintenancemode);
mav.addObject("subcategorylistdd", subcategorylistdd);
mav.addObject("makemodellistdd", makemodellistdd);
mav.addObject("ordertypelistdd", ordertypelistdd);
mav.addObject("makemodelcodeselected", makemodelcodeselected);
mav.addObject("subcategoryselected", subcategoryselected);
mav.addObject("ordertypeselected", ordertypeselected);
mav.addObject("searchpartno", searchpartno);
mav.addObject("parts", parts);
return mav;
}
}
#Transactional
public void updatePartsMaintenance(Parts parts) {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(parts);
// updateSubcategoryOnParts();
// updateMakemodelOnParts();
session.flush();
session.clear();
}
I create a button in my webpage . i need to update the data in second database only when i click the button .
If you need more then one DB you need more then one HibernateSessionFactory.
You just create an hibernate factory for each DB with the relevant table mapping in each configuration.
Create Multiple Datasource beans and use #Qualifier while autowiring to select your desired connection.
Related
Preamble - using Spring
I am confused as to the purpose of the spring #Transactional annotation. I thought from a few blog posts I've read that it would allow me to simplify transaction management and just write this, and it would handle connection/commit/rollback automagically:
public class DaoImpl implements Dao {
#Autowired
private SessionFactory sessionFactory;
#Transactional
public void saveOrUpdateOne(final AdditionalDataItem item) {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(p_item);
}
}
However this gives me an exception: " Calling method 'saveOrUpdate' is not valid without an active transaction"
If I instead change the save method to this, it all works - so my question is, what is #Transactional doing?
#Override
#Transactional
public void saveOrUpdateOne(final AdditionalDataItem p_item) {
Session session = null;
Transaction trans = null;
try {
session = sessionFactory.getCurrentSession();
trans = session.beginTransaction();
TransactionStatus status = trans.getStatus();
session.saveOrUpdate(p_item);
trans.commit();
} catch (Exception e) {
LOGGER.error("Exception saving data: {}", e.getMessage());
if (trans != null) {
try {
trans.rollback();
} catch (RuntimeException rbe) {
LOGGER.error("Couldn’t roll back transaction", rbe);
}
}
} finally {
if (session != null && session.isOpen()) {
try {
session.close();
} catch (HibernateException ne) {
LOGGER.error("Couldn’t close session", ne);
}
}
}
}
For reference, I'm using Java 11 with Spring Framework 5.3.7 and hibernate 5.5.7 and have appropriate dao, session factory and tx manager beans:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="${sessionFactory.datasource}" />
<property name="configLocation" value="${sessionFactory.configLocation}" />
</bean>
<bean id="txManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="Dao" class="com.xxxxx.dao.DaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
It is because you do not enable #Transactional yet and also not begin the transaction when calling session.saveOrUpdate(), so it gives you the error 'not valid without an active transaction' .
To enable #Transactional, you have to use #EnableTransactionManagement or add <tx:annotation-driven/> in case you are using XML configuration . It basically does the following for you (source) :
#EnableTransactionManagement and <tx:annotation-driven/> are
responsible for registering the necessary Spring components that power
annotation-driven transaction management, such as the
TransactionInterceptor and the proxy- or AspectJ-based advice that
weaves the interceptor into the call stack when JdbcFooRepository's
#Transactional methods are invoked.
Your working example works because you manually manage the transaction by yourself .It is nothing to do with #Transactional since you never enable it.
Take the working codes as an example , what #Transactional does for you is that you no longer need to manually write the following transaction codes as all of them will be encapsulated in the TransactionInterceptor and execute around your #Transactional method based on AOP :
public Object invoke() {
Session session = null;
Transaction trans = null;
try {
session = sessionFactory.getCurrentSession();
trans = session.beginTransaction();
TransactionStatus status = trans.getStatus();
/***********************************************/
Here it will invoke your #Transactional Method
/************************************************/
trans.commit();
} catch (Exception e) {
LOGGER.error("Exception saving data: {}", e.getMessage());
if (trans != null) {
try {
trans.rollback();
} catch (RuntimeException rbe) {
LOGGER.error("Couldn’t roll back transaction", rbe);
}
}
} finally {
if (session != null && session.isOpen()) {
try {
session.close();
} catch (HibernateException ne) {
LOGGER.error("Couldn’t close session", ne);
}
}
}
}
So you can see that your #Transactional method will become very clean after removing these "ceremony" codes.
#Transactional is used if you want to update 2 tables if one of them failed the other one will rollback automatic
you can use it above method
and you can call save or update using bean of a Repository class
I have recently started working on a project with SOAP webservices, Spring and Hibernate.
I am facing the following issue:
We use SOAP UI to send requests to test our code. I have written a service which processes bills. Basically there are 2 services, one creates a bill and the other processes that bill.
We have a table called BillTb. Before processing a bill, we check the status of the bill. If the bill status is 3(pending), we process it. If it is not equal to 3, we do not process it. Once the bill is processed, we change the status to 4(processed).
Now if the bill status is 3, we do a number of entries in other tables and at last, status is changed to 4.
If in between processing, if the processing fails, we need to revert all those entries. So we call these entries within a transaction.
The DAO layer with hibernate code is as follows:
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.Query;
#PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
public class BillDAOImpl implements BillDao {
...
...
...
int pendingStatus = 3;
int processedStatus = 4;
Session session = null;
for(int id: ids){
Bill bill = null;
try{
session = entityManager.unwrap(Session.class);
bill= entityManager.find(Bill.class, id);
session.getTransaction().begin();
if(bill.status() != pendingStatus ){
System.out.println("The bill is already processed");
continue;
}
...
...
bill.setStatus(processedStatus);
entityManager.persist(bill);
session.getTransaction().commit();
} catch(Exception e){
}
}
}
Now the problem is, once a bill status is changed from 3 to 4, if I change the status again to 3 by firing an update query in database, it should again work, but somehow, it reads the status as 4 only.
If I bring down the server, then execute the request again then it works for same entry.
The other transaction related parameters are set as :
<property name="hibernate.cache.use_query_cache" value="false" />
Also,
<bean id="projectEntityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:persistenceXmlLocation="classpath*:META-INF/persistence.xml"
p:persistenceUnitName="persistenceUnit" p:loadTimeWeaver-ref="loadTimeWeaver"
p:jpaVendorAdapter-ref="jpaVendorAdapter" p:jpaDialect-ref="jpaDialect"
p:dataSource-ref="datasourceBean">
<property name="jpaProperties">
<props>
<prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.BTMTransactionManagerLookup
</prop>
<prop key="hibernate.transaction.flush_before_completion">false</prop>
...
...
<prop key="hibernate.connection.isolation">3</prop>
<prop key="hibernate.connection.release_mode">auto</prop>
</props>
</property>
</bean>
So here it seems that session is somehow storing the bill object and when I update the bill object directly in database, it stores stale data. So what should be done to in this case. Should I clear the session at end of method?
You should perform the query inside of the transaction and also remember to commit the transaction everytime (if you trigger continue, that is ommited).
Actually i would write it like this:
for(int id: ids){
Bill bill = null;
Transaction tx = session.getTransaction();
tx.begin();
try{
bill= entityManager.find(Bill.class, id);
if(bill.status() != pendingStatus ){
System.out.println("The bill is already processed");
tx.commit();
continue;
}
bill.setStatus(processedStatus);
entityManager.persist(bill);
session.flush();
tx.commit();
}catch(Exception e){
tx.rollback();
throw e;
}
}
Currently I have this code duplicated in each one of my Controller methods:
Transaction transaction = HibernateUtil.getSessionFactory().getCurrentSession().getTransaction();
if (!HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().isActive()) {
transaction.begin();
}
Is this the correct way or is there a better way of doing this, perhaps in a separate class that I can reference? If so, how? Every time I've tried to put it in a separate class and reference it from other classes, it failed.
edit: I'm trying to use as few external libraries as possible. I wouldn't use Hibernate if Java had an ORM/JPA implementation built into the JDK
I've run into this myself many times. Ordinarily my first recommendation would be Spring transaction management, however I understand you are trying to limit the number of third party libraries you are using.
Since you're using a static API in your HibernateUtil class, you may find it helpful to consolidate your logic in a method, and putting the 'what you want to do in a transaction' code (which varies controller to controller) in a callback.
First, define an interface to describe each controller's inTransaction behavior:
public interface TransactionCallback {
void doInTransaction();
}
Now, create a static method in your HibernateUtil class to handle beginning, committing, and if necessary rolling back your transactions:
public class HibernateUtil {
public static void inTransaction(TransactionCallback tc) {
Transaction transaction = HibernateUtil.getSessionFactory().getCurrentSession().getTransaction();
if (!HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().isActive()) {
transaction.begin();
try {
tc.doInTransaction();
transaction.commit();
} catch (Exception e) {
transaction.rollback();
}
}
}
}
In your controller, you'd use your new method with an anonymous inner class:
....
HibernateUtil.inTransaction(new TransactionCallback() {
void doInTransaction() {
// do stuff for this controller
}
});
....
This approach should at least take care of the duplication you'd like to eliminate, and there's plenty of room for extending it to handle particular exceptions, etc.
You have to close hibernate transaction after each transaction (e.g. Controller request).
In this case you will not need
if (!HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().isActive())
and you WILL need to call .close() each time after request.
It is better to use code like:
class Controller {
//...
commonActionMethod() {
begin transaction
specificActionMethod();
close transaction
}
And childs of this Controller class should implement specificActionMethod().
Code is clean. Transactions are safe. No third-party libs required.
You can Very well use JDK Proxies to implement your own AOP .
Ref : Link1 Link2
Have Service Layer to intract with DAO framework such as Hibernate and so. So that your controller is just controll the flow and your service can implement business.
Have SeviceLocator / FactoryPattern to get hold of your Service instances ( In other words return proxies instead of Actual instance).
Define your own Annotations and identify your methods required transaction or not. if required handle transaction around your method call in your proxy handler.
In this way you don't need to depend on any library other than JDK. and you can turn off or on transaction just by having Annotations.
If you start manage the instances ( services) you can lot of magics with combination of FactoryPattern + JDK Proxies ( Actual Interfaces) + AOP Concepts.
you can create separate class for connection.
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
#SuppressWarnings("deprecation")
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from Annotation
return new AnnotationConfiguration().configure().buildSessionFactory();
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
On Server Side you can write :-
Session session=null;
Transaction tx=null;
try {
session =HibernateUtil.getSessionFactory().openSession();
tx=session.beginTransaction();
} catch (HibernateException e) {
e.printStackTrace();
}finally
{
session.close();
}
Avoiding the use of additional, external libraries, you may wish to supply an interceptor that implements that standard J2EE servlet Filter interface. Such implementation is sometimes referred to as the Open Session in View pattern. I cite the following from this page:
When an HTTP request has to be handled, a new Session and database transaction will begin. Right before the response is send to the client, and after all the work has been done, the transaction will be committed, and the Session will be closed.
If you are using spring in your project. I will suggest to use the TX using spring AOP, In that you just have to specify the pointcuts for your transactions. The Spring AOP TX will taken care of begin and commit the transaction on basis of your point cut and could also roll-back the TX in case of exception occurred. Please go through the link of example - here
package com.project.stackoverflow;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
public class HibernateUtil {
private static final ThreadLocal threadSession = new ThreadLocal();
private static SessionFactory sessionFactory;
/**
* A public method to get the Session.
*
* #return Session
*/
public static Session getSession() {
Session session = (Session) threadSession.get();
// Open a Session, if this thread has none yet
if ((null == session) || !session.isOpen()) {
logger.info("Null Session");
session = sessionFactory.openSession();
logger.info("Session Opened");
threadSession.set(session);
}
return session;
}
public static void closeSession() {
Session session = (Session) threadSession.get();
// Open a Session, if this thread has none yet
if (null != session) {
session.close();
session = null;
threadSession.set(null);
}
}
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
logger.info("Inside set session Factory");
this.sessionFactory = sessionFactory;
logger.info("After set session Factory");
}
public static void save(Object obj) {
getSession().save(obj);
getSession().flush();
}
public static void saveOrUpdate(Object obj) {
getSession().saveOrUpdate(obj);
getSession().flush();
}
public static void batchUpdate(Object obj) {
getSession().saveOrUpdate(obj);
getSession().flush();
}
public static void update(Object obj) {
getSession().update(obj);
getSession().flush();
}
public static void delete(Object obj) {
getSession().delete(obj);
getSession().flush();
}
}
You can probably go for this solution. I have made a separate JavaClass for Hibernate Instantiation and use. You can get the session from here itself which may suffice your need. Hope it helps :)
I used this technique.
My Servlet context is like this:
<beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.databaseurl}" p:username="${jdbc.username}" p:password="${jdbc.password}" />
<beans:bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="configLocation">
<beans:value>classpath:hibernate.cfg.xml</beans:value>
</beans:property>
<beans:property name="configurationClass">
<beans:value>org.hibernate.cfg.AnnotationConfiguration</beans:value>
</beans:property>
<beans:property name="hibernateProperties">
<beans:props>
<beans:prop key="hibernate.dialect">${jdbc.dialect}</beans:prop>
<beans:prop key="hibernate.show_sql">true</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
<beans:bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<beans:property name="sessionFactory" ref="sessionFactory" />
</beans:bean>
<tx:annotation-driven transaction-manager="transactionManager" />
Then you can simply use
#Autowired
private SessionFactory sessionFactory;
Whenever I want to use a session or do any operations I simply do it like this:
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
session.save(userAccount);
transaction.commit();
session.close();
I think it will help.
If you have application server like glassfish, it has imbended eclipselink JPA/ORM implementation and you can manage transaction using standart JEE annotations.
I've been reading about transactions & jooq but I struggle to see how to implement it in practice.
Let's say I provide JOOQ with a custom ConnectionProvider which happens to use a connection pool with autocommit set to false.
The implementation is roughly:
#Override public Connection acquire() throws DataAccessException {
return pool.getConnection();
}
#Override public void release(Connection connection) throws DataAccessException {
connection.commit();
connection.close();
}
How would I go about wrapping two jooq queries into a single transaction?
It is easy with the DefaultConnectionProvider because there's only one connection - but with a pool I'm not sure how to go about it.
jOOQ 3.4 Transaction API
With jOOQ 3.4, a transaction API has been added to abstract over JDBC, Spring, or JTA transaction managers. This API can be used with Java 8 as such:
DSL.using(configuration)
.transaction(ctx -> {
DSL.using(ctx)
.update(TABLE)
.set(TABLE.COL, newValue)
.where(...)
.execute();
});
Or with pre-Java 8 syntax
DSL.using(configuration)
.transaction(new TransactionRunnable() {
#Override
public void run(Configuration ctx) {
DSL.using(ctx)
.update(TABLE)
.set(TABLE.COL, newValue)
.where(...)
.execute();
}
});
The idea is that the lambda expression (or anonymous class) form the transactional code, which:
Commits upon normal completion
Rolls back upon exception
The org.jooq.TransactionProvider SPI can be used to override the default behaviour, which implements nestable transactions via JDBC using Savepoints.
A Spring example
The current documentation shows an example when using Spring for transaction handling:
http://www.jooq.org/doc/latest/manual/getting-started/tutorials/jooq-with-spring/
This example essentially boils down to using a Spring TransactionAwareDataSourceProxy
<!-- Using Apache DBCP as a connection pooling library.
Replace this with your preferred DataSource implementation -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
init-method="createDataSource" destroy-method="close">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:~/maven-test" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<!-- Using Spring JDBC for transaction management -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="transactionAwareDataSource"
class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg ref="dataSource" />
</bean>
<!-- Bridging Spring JDBC data sources to jOOQ's ConnectionProvider -->
<bean class="org.jooq.impl.DataSourceConnectionProvider"
name="connectionProvider">
<constructor-arg ref="transactionAwareDataSource" />
</bean>
A running example is available from GitHub here:
https://github.com/jOOQ/jOOQ/tree/master/jOOQ-examples/jOOQ-spring-example
A Spring and Guice example
Although I personally wouldn't recommend it, some users have had success replacing a part of Spring's DI by Guice and handle transactions with Guice. There is also an integration-tested running example on GitHub for this use-case:
https://github.com/jOOQ/jOOQ/tree/master/jOOQ-examples/jOOQ-spring-guice-example
This is probably not the best way but it seems to work. The caveat is that it is not the release but the commit method which closes the connection and returns it to the pool, which is quite confusing and could lead to issues if some code "forgets" to commit...
So the client code looks like:
final PostgresConnectionProvider postgres =
new PostgresConnectionProvider("localhost", 5432, params.getDbName(), params.getUser(), params.getPass())
private static DSLContext sql = DSL.using(postgres, SQLDialect.POSTGRES, settings);
//execute some statements here
sql.execute(...);
//and don't forget to commit or the connection will not be returned to the pool
PostgresConnectionProvider p = (PostgresConnectionProvider) sql.configuration().connectionProvider();
p.commit();
And the ConnectionProvider:
public class PostgresConnectionProvider implements ConnectionProvider {
private static final Logger LOG = LoggerFactory.getLogger(PostgresConnectionProvider.class);
private final ThreadLocal<Connection> connections = new ThreadLocal<>();
private final BoneCP pool;
public PostgresConnectionProvider(String serverName, int port, String schema, String user, String password) throws SQLException {
this.pool = new ConnectionPool(getConnectionString(serverName, port, schema), user, password).pool;
}
private String getConnectionString(String serverName, int port, String schema) {
return "jdbc:postgresql://" + serverName + ":" + port + "/" + schema;
}
public void close() {
pool.shutdown();
}
public void commit() {
LOG.debug("Committing transaction in {}", Thread.currentThread());
try {
Connection connection = connections.get();
if (connection != null) {
connection.commit();
connection.close();
connections.set(null);
}
} catch (SQLException ex) {
throw new DataAccessException("Could not commit transaction in postgres pool", ex);
}
}
#Override
public Connection acquire() throws DataAccessException {
LOG.debug("Acquiring connection in {}", Thread.currentThread());
try {
Connection connection = connections.get();
if (connection == null) {
connection = pool.getConnection();
connection.setAutoCommit(false);
connections.set(connection);
}
return connection;
} catch (SQLException ex) {
throw new DataAccessException("Can't acquire connection from postgres pool", ex);
}
}
#Override
//no-op => the connection won't be released until it is commited
public void release(Connection connection) throws DataAccessException {
LOG.debug("Releasing connection in {}", Thread.currentThread());
}
}
Easiest way,(I have found) to use Spring Transactions with jOOQ, is given here: http://blog.liftoffllc.in/2014/06/jooq-and-transactions.html
Basically we implement a ConnectionProvider that uses org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(ds) method to find and return the DB connection that holds transaction created by Spring.
Create a TransactionManager bean for your DataSource, example shown below:
<bean
id="dataSource"
class="org.apache.tomcat.jdbc.pool.DataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="mysql://locahost:3306/db_name"
p:username="root"
p:password="root"
p:initialSize="2"
p:maxActive="10"
p:maxIdle="5"
p:minIdle="2"
p:testOnBorrow="true"
p:validationQuery="/* ping */ SELECT 1"
/>
<!-- Configure the PlatformTransactionManager bean -->
<bean
id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"
/>
<!-- Scan for the Transactional annotation -->
<tx:annotation-driven/>
Now you can annotate all the classes or methods which uses jOOQ's DSLContext with
#Transactional(rollbackFor = Exception.class)
And while creating the DSLContext object jOOQ will make use of the transaction created by Spring.
Though its an old question, Please look at this link to help configure JOOQ to use spring provided transaction manager. Your datasource and DSLContext have to be aware of Transacation.
https://www.baeldung.com/jooq-with-spring
You may have to change
#Bean
public DefaultDSLContext dsl() {
return new DefaultDSLContext(configuration());
}
to
#Bean
public DSLContext dsl() {
return new DefaultDSLContext(configuration());
}
I'm adding envers to an existing hibernate entities. Everything is working smoothly so far as far as auditing, however querying is a different issue because the revision tables aren’t populated with the existing data. Has anyone else already solved this issue? Maybe you’ve found some way to populate the revision tables with the existing table? Just thought I’d ask, I'm sure others would find it useful.
We populated the initial data by running a series of raw SQL queries to simulate "inserting" all the existing entities as if they had just been created at the same time. For example:
insert into REVINFO(REV,REVTSTMP) values (1,1322687394907);
-- this is the initial revision, with an arbitrary timestamp
insert into item_AUD(REV,REVTYPE,id,col1,col1) select 1,0,id,col1,col2 from item;
-- this copies the relevant row data from the entity table to the audit table
Note that the REVTYPE value is 0 to indicate an insert (as opposed to a modification).
You'll have a problem in this category if you are using Envers ValidityAuditStrategy and have data which has been created other than with Envers enabled.
In our case (Hibernate 4.2.8.Final) a basic object update throws "Cannot update previous revision for entity and " (logged as [org.hibernate.AssertionFailure] HHH000099).
Took me a while to find this discussion/explanation so cross-posting:
ValidityAuditStrategy with no audit record
You don't need to.
AuditQuery allows you to get both RevisionEntity and data revision by :
AuditQuery query = getAuditReader().createQuery()
.forRevisionsOfEntity(YourAuditedEntity.class, false, false);
This will construct a query which returns a list of Object [3]. Fisrt element is your data, the second is the revision entity and the third is the type of revision.
We have solved the issue of populating the audit logs with the existing data as follows:
SessionFactory defaultSessionFactory;
// special configured sessionfactory with envers audit listener + an interceptor
// which flags all properties as dirty, even if they are not.
SessionFactory replicationSessionFactory;
// Entities must be retrieved with a different session factory, otherwise the
// auditing tables are not updated. ( this might be because I did something
// wrong, I don't know, but I know it works if you do it as described above. Feel
// free to improve )
FooDao fooDao = new FooDao();
fooDao.setSessionFactory( defaultSessionFactory );
List<Foo> all = fooDao.findAll();
// cleanup and close connection for fooDao here.
..
// Obtain a session from the replicationSessionFactory here eg.
Session session = replicationSessionFactory.getCurrentSession();
// replicate all data, overwrite data if en entry for that id already exists
// the trick is to let both session factories point to the SAME database.
// By updating the data in the existing db, the audit listener gets triggered,
// and inserts your "initial" data in the audit tables.
for( Foo foo: all ) {
session.replicate( foo, ReplicationMode.OVERWRITE );
}
The configuration of my data sources (via Spring):
<bean id="replicationDataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="org.postgresql.Driver"/>
<property name="url" value=".."/>
<property name="username" value=".."/>
<property name="password" value=".."/>
<aop:scoped-proxy proxy-target-class="true"/>
</bean>
<bean id="auditEventListener"
class="org.hibernate.envers.event.AuditEventListener"/>
<bean id="replicationSessionFactory"
class="o.s.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="entityInterceptor">
<bean class="com.foo.DirtyCheckByPassInterceptor"/>
</property>
<property name="dataSource" ref="replicationDataSource"/>
<property name="packagesToScan">
<list>
<value>com.foo.**</value>
</list>
</property>
<property name="hibernateProperties">
<props>
..
<prop key="org.hibernate.envers.audit_table_prefix">AUDIT_</prop>
<prop key="org.hibernate.envers.audit_table_suffix"></prop>
</props>
</property>
<property name="eventListeners">
<map>
<entry key="post-insert" value-ref="auditEventListener"/>
<entry key="post-update" value-ref="auditEventListener"/>
<entry key="post-delete" value-ref="auditEventListener"/>
<entry key="pre-collection-update" value-ref="auditEventListener"/>
<entry key="pre-collection-remove" value-ref="auditEventListener"/>
<entry key="post-collection-recreate" value-ref="auditEventListener"/>
</map>
</property>
</bean>
The interceptor:
import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
..
public class DirtyCheckByPassInterceptor extends EmptyInterceptor {
public DirtyCheckByPassInterceptor() {
super();
}
/**
* Flags ALL properties as dirty, even if nothing has changed.
*/
#Override
public int[] findDirty( Object entity,
Serializable id,
Object[] currentState,
Object[] previousState,
String[] propertyNames,
Type[] types ) {
int[] result = new int[ propertyNames.length ];
for ( int i = 0; i < propertyNames.length; i++ ) {
result[ i ] = i;
}
return result;
}
}
ps: keep in mind that this is a simplified example. It will not work out of the box but it will guide you towards a working solution.
Take a look at http://www.jboss.org/files/envers/docs/index.html#revisionlog
Basically you can define your own 'revision type' using #RevisionEntity annotation,
and then implement a RevisionListener interface to insert your additional audit data,
like current user and high level operation. Usually those are pulled from ThreadLocal context.
You could extend the AuditReaderImpl with a fallback option for the find method, like:
public class AuditReaderWithFallback extends AuditReaderImpl {
public AuditReaderWithFallback(
EnversService enversService,
Session session,
SessionImplementor sessionImplementor) {
super(enversService, session, sessionImplementor);
}
#Override
#SuppressWarnings({"unchecked"})
public <T> T find(
Class<T> cls,
String entityName,
Object primaryKey,
Number revision,
boolean includeDeletions) throws IllegalArgumentException, NotAuditedException, IllegalStateException {
T result = super.find(cls, entityName, primaryKey, revision, includeDeletions);
if (result == null)
result = (T) super.getSession().get(entityName, (Serializable) primaryKey);
return result;
}
}
You could add a few more checks in terms of returning null in some cases.
You might want to use your own factory as well:
public class AuditReaderFactoryWithFallback {
/**
* Create an audit reader associated with an open session.
*
* #param session An open session.
* #return An audit reader associated with the given sesison. It shouldn't be used
* after the session is closed.
* #throws AuditException When the given required listeners aren't installed.
*/
public static AuditReader get(Session session) throws AuditException {
SessionImplementor sessionImpl;
if (!(session instanceof SessionImplementor)) {
sessionImpl = (SessionImplementor) session.getSessionFactory().getCurrentSession();
} else {
sessionImpl = (SessionImplementor) session;
}
final ServiceRegistry serviceRegistry = sessionImpl.getFactory().getServiceRegistry();
final EnversService enversService = serviceRegistry.getService(EnversService.class);
return new AuditReaderWithFallback(enversService, session, sessionImpl);
}
}
I've checked many ways, but the best way for me is to write a PL/SQL script as below.
The below script is written for PostgreSQL. Didn't check other vendors, but they must have the same feature.
CREATE SEQUENCE hibernate_sequence START 1;
DO
$$
DECLARE
u RECORD;
next_id BIGINT;
BEGIN
FOR u IN SELECT * FROM user
LOOP
SELECT NEXTVAL('hibernate_sequence')
INTO next_id;
INSERT INTO revision (rev, user_id, timestamp)
VALUES (next_id,
'00000000-0000-0000-0000-000000000000',
(SELECT EXTRACT(EPOCH FROM NOW() AT TIME ZONE 'utc')) * 1000);
INSERT INTO user_aud(rev,
revend,
revtype,
id,
created_at,
created_by,
last_modified_at,
last_modified_by,
name)
VALUES (next_id,
NULL,
0,
f.id,
f.created_at,
f.created_by,
f.last_modified_at,
f.last_modified_by,
f.name);
END LOOP;
END;
$$;