How to get java.sql.Connection object from Hibernate Session - java

I have requirement where I need java.sql.Connection. But I am using Hibernate here. Somehow I researched and I found below alternative but that's not working.
import org.hibernate.connection.ProxoolConnectionProvider;
public class ConnectionDB{
//I have imported below class
ProxoolConnectionProvider proxoolProvider = new ProxoolConnectionProvider();
org.hibernate.cfg.Configuration cfg = HibernateUtil.getConfiguration();//this method will return configuration
java.util.Properties props = cfg.getProperties();//This will return Properties Object
//Using properties object I just tried to get The Connection Object by following method
proxoolConn.configure(props);// I just configured the Porperties object
proxoolConn.getConnection();
}
But no luck I just end-up with no exception in console .. I am using Struts 2, Hibernate and JasperReports.
Can any one help me to get the connection object from Hibernate?

The following code assumes you have an existing and open Hibernate org.hibernate.Session:
public void doWorkOnConnection(Session session) {
session.doWork(new Work() {
public void execute(Connection connection) throws SQLException {
//use the connection here...
}
});
}
Should you need information on how to use the above classes, please read the Hibernate 3.5 Javadoc. Specifically, read Session and then Work.

You can also downcast the Session method into a SessionImpl and get the connection object easily:
SessionImpl sessionImpl = (SessionImpl) session;
Connection conn = sessionImpl.connection();

Related

Injected JOOQ Context leaks connections

I use jooq to handle SQL Queries on a PostgreSQL database in an Wildfly web application. The DSLContext is injected via CDI into my beans following the example on http://awolski.com/integrating-jooq-easy/. A bean looks like this:
public class Foo {
private #Inject DSLContext jooq;
...
public boolean update....
{
...
try {
jooq.doStuff();
}
catch(Exception e) {
System.out.println(e.getCause().getMessage());
}
finally {
jooq.close();
}
...
}
When I run the application, every connection leaks. What am I missing ?
The constructor was the critical point.
Since the documentation on https://www.jooq.org/doc/3.10/manual/sql-building/dsl-context/connection-vs-datasource/ says that jooq will manage the connection, passing the datasource object instead of the connection object solves the issue:
public DSLContext getDSLContext() throws SQLException {
return DSL.using(ds, SQLDialect.MYSQL);
}
in http://awolski.com/integrating-jooq-easy/

Perform VACUUM FULL with JPA

I'm using a PostgreSQL DB and I would like to start VACUUM FULL using JPA EntityManager.
Version 1
public void doVacuum(){
entityManager.createNativeQuery("VACUUM FULL").executeUpdate()
}
throws TransactionRequiredException
Version 2
#Transactional
public void doVacuum(){
entityManager.createNativeQuery("VACUUM FULL").executeUpdate()
}
throws PersistenceException "VACUUM cannot run inside a transaction block"
Version 3
public void doVacuum(){
entityManager.createNativeQuery("VACUUM FULL").getResultList()
}
vacuum is performed but after that I get PersistenceException "No results"
What is the correct way to start this sql command?
As Alay Hay mentioned, using the underlying connection will work:
public void doVacuum(){
org.hibernate.Session session = entityManager.unwrap(org.hibernate.Session);
org.hibernate.internal.SessionImpl sessionImpl = (SessionImpl) session; // required because Session doesn't provide connection()
java.sql.Connection connection = sessionImpl.connection();
connection.prepareStatement("VACUUM FULL").execute();
}
Here is a solution that does not require cast to internal implementation of Hibernate Session. Please keep in mind that VACUUM cannot be run in transaction block, this is why you need to set autoCommit to true.
Session session = entityManager.unwrap(Session.class);
session.doWork(new Work() {
#Override
public void execute(Connection connection) throws SQLException {
connection.setAutoCommit(true);
connection.prepareStatement("VACUUM FULL").execute();
connection.setAutoCommit(false);
}
});

How to use managed Hibernate sessions with a web application

I'm trying to get my head around how Hibernate session management works. I'm trying to define a simple session-per-request model in a web application, but it just doesn't seem to be working. So far I have this:
#WebListener
public class HibernateDataAccess implements ServletRequestListener
{
Configuration configuration;
SessionFactory sessionFactory;
public HibernateDataAccess ()
{
configuration = new Configuration ().configure ();
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder ().applySettings (
configuration.getProperties ()).build ();
sessionFactory = configuration.buildSessionFactory (serviceRegistry);
}
public List<Customer> getAllCustomers () throws SQLException
{
Session session = sessionFactory.getCurrentSession ();
return (List<Customer>) session.createQuery ("select c from Customer c").list ();
}
#Override
public void requestDestroyed (ServletRequestEvent arg0)
{
ManagedSessionContext.unbind (sessionFactory).close ();
}
#Override
public void requestInitialized (ServletRequestEvent arg0)
{
System.out.println ("requestInitialized called!");
Session session = sessionFactory.openSession ();
ManagedSessionContext.bind (session);
session.beginTransaction ();
}
}
My problem is that despite the fact that I get the message "requestInitialized called!" on my server's console output, I get the following exception when I try to call getAllCustomers from within a servlet request (inside a doGet() method):
org.hibernate.HibernateException: No session currently bound to execution context
at org.hibernate.context.internal.ManagedSessionContext.currentSession(ManagedSessionContext.java:75)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1013)
at net.meridiandigital.binco.web.HibernateDataAccess.getAllCustomers(HibernateDataAccess.java:38)
at net.meridiandigital.binco.web.CustomerServlet.doGetList(CustomerServlet.java:80)
What am I doing wrong?
Have you set the hibernate.current_session_context_class configuration parameter to a Hibernate.Context.ICurrentSessionContext implementation?
The problem was quite obvious -- the instance of HibernateDataAccess I was using wasn't the same on the server was calling the listener methods on, so the session factory that was having the current session registered was a different one to the one my code was trying to use. The solutions was to separate the two concerns: have an entirely separate #WebListener class that called to my singleton HibernateDataAccess to start and close sessions.

Get hold of a JDBC Connection object from a Stateless Bean

In a Stateless Session Bean an EntityManager is injected but I would like to get hold of a Connection object in order to invoke a DB Procedure.
Is there any solution to this ?
This is going to be JPA provider specific code. Typically this is done by invoking unwrap() on the EntityManager class.
If you are using EclipseLink, the following code (from the EclipseLink wiki) will be useful (in the case you are using an application-managed EntityManager) :
JPA 2.0
entityManager.getTransaction().begin();
java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class); // unwraps the Connection class.
...
entityManager.getTransaction().commit();
JPA 1.0
entityManager.getTransaction().begin();
UnitOfWork unitOfWork = (UnitOfWork)((JpaEntityManager)entityManager.getDelegate()).getActiveSession();
unitOfWork.beginEarlyTransaction();
Accessor accessor = unitOfWork.getAccessor();
accessor.incrementCallCount(unitOfWork.getParent());
accessor.decrementCallCount();
java.sql.Connection connection = accessor.getConnection();
...
entityManager.getTransaction().commit();
Note, that the solution provided for JPA 2.0 will fail for Hibernate 3.6.5 with a PersistenceException containing the message
Hibernate cannot unwrap interface java.sql.Connection
Use the code provided by Skaffman to get it to work against Hibernate (verified to work under 3.6.5 even for container managed persistence contexts).
However, the EclipseLink wiki points out one useful bit of info - if you are using JTA managed datasources, you should be injecting it using the #Resource annotation or retrieving it using a JNDI lookup. As long as you need to perform transactional work against the database, it is immaterial as to whether you are obtaining a new connection from the data source or an existing one; most connection pools will anyway provide the same connection that is associated with the current thread (i.e. the one already used by the entity manager). You would therefore avoiding unwrapping the entity manager this way, and also perform transactional activity against the database; do remember that the persistence context cache, and a second-level cache may not be synchronized if you do this.
In Hibernate, the solution posted by skaffman resulted in the following error message:
Hibernate cannot unwrap class org.hsqldb.Session
I did get it to work using SessionImpl rather than Session:
Connection connection = entityManager().unwrap(SessionImpl.class).connection();
An example of solving the problem using Session.doWork() is as follows:
private void executeNative(final String query) {
Session session = entityManager.unwrap(Session.class);
session.doWork(new Work() {
#Override
public void execute(Connection connection) throws SQLException {
Statement s = null;
try {
s = connection.createStatement();
s.executeUpdate(query);
}
finally {
if (s != null) {
s.close();
}
}
}
});
}
The JPA API itself doesn't seem to offer this, not surprisingly, but if you're willing to couple your code to a specific implementation, then you can use something like this (Hibernate):
Session hibernateSession = entityManager.unwrap(Session.class);
Connection jdbcConnection = hibernateSession.connection();
Note that Session.connection() is deprecated for removal in Hibernate 4. Consider using Session.doWork() instead.
You must take the underlying delegate using entitymanager.getDelegate() or entitymanager.unwrap(which is the better way), cast it to the specific implementation(in Hibernate it is called Session). Then you can call the connection() method. Be aware this is deprecated, use the Work class instead. Read more here.
In JPA2.0, if need JDBC is por DTO nomodel or entity for query more
complex. Sometimes JPA is not all...
I hope this will help you:
Statement statement = null;
EntityManager em = null;
em = emf.createEntityManager();
EntityTransaction et = em.getTransaction();
if(!et.isActive()) {
et.begin();
}
java.sql.Connection connection = em.unwrap(java.sql.Connection.class);
String qquerry="SELE ...
try {
statement = connection.createStatement();
ResultSet rs = statement.executeQuery(qquerry);
if (!rs.next()) {
return null;
}
else{
wwwwas=rs.getString(4);
}
statement.close();
}
catch (SQLException e) {
System.out.println("\n b-03:"+e);
throw new RuntimeException(e.getMessage(), e);
}
finally {
try {
// em.getTransaction().commit();
if(connection != null )
connection.close();
}
catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
This works awesomely and you can use the connection object elsewhere if needed
SessionImpl sessionImpl = (SessionImpl) session;
Connection conn = sessionImpl.connection();
Where session is the name of the Hibernate Session object
Below is the code that worked for me. We use jpa 1.0, Apache openjpa implementation.
import java.sql.Connection;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAPersistence;
public final class MsSqlDaoFactory {
public static final Connection getConnection(final EntityManager entityManager) {
OpenJPAEntityManager openJPAEntityManager = OpenJPAPersistence.cast(entityManager);
Connection connection = (Connection) openJPAEntityManager.getConnection();
return connection;
}
}

DbUnit - Warning: AbstractTableMetaData

I am using DbUnit in the latest version 2.4.8 and I get many warnings in my Unit tests with this message:
WARN : org.dbunit.dataset.AbstractTableMetaData -
Potential problem found: The configured data type factory
'class org.dbunit.dataset.datatype.DefaultDataTypeFactory'
might cause problems with the current database 'MySQL' (e.g. some datatypes may
not be supported properly). In rare cases you might see this message because the
list of supported database products is incomplete (list=[derby]). If so please
request a java-class update via the forums.If you are using your own
IDataTypeFactory extending DefaultDataTypeFactory, ensure that you override
getValidDbProducts() to specify the supported database products.
So I thought I add this (I use a MySQL database):
protected void setUpDatabaseConfig(DatabaseConfig config) {
config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MySqlDataTypeFactory());
}
But this does not help to avoid these warnings. What's wrong here?
Thank you in advance & Best Regards Tim.
I solved this with info from the dbunit faq. Just setting the data type factory property made the warning go away.
Connection dbConn = template.getDataSource().getConnection();
IDatabaseConnection connection = new DatabaseConnection(dbConn, "UTEST", false);
DatabaseConfig dbConfig = connection.getConfig();
// added this line to get rid of the warning
dbConfig.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new OracleDataTypeFactory());
With Spring-Boot you can use such configuration bean
#Configuration
public class DbUnitConfiguration {
#Autowired
private DataSource dataSource;
#Bean
public DatabaseDataSourceConnectionFactoryBean dbUnitDatabaseConnection() {
DatabaseConfigBean bean = new DatabaseConfigBean();
bean.setDatatypeFactory(new MySqlDataTypeFactory());
DatabaseDataSourceConnectionFactoryBean dbConnectionFactory = new DatabaseDataSourceConnectionFactoryBean(dataSource);
dbConnectionFactory.setDatabaseConfig(bean);
return dbConnectionFactory;
}
}
I know this is an old thread but all the answers here are more complicated than they need to be.
The simplest way to accomplish setting the factory on every connection acquisition is to supply an OperationListener and implement its connectionRetrieved method to do what you want. No overriding needed; the listener will be invoked every time an IDatabaseConnection is acquired.
I was using JTDS driver and MS SQL 2008. In my DBUntiTest class override the following method. The waring message disappeared.
#Override
protected void setUpDatabaseConfig(DatabaseConfig config) {
config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MsSqlDataTypeFactory());
}
#reassembler's answer is spot on. Just to add that I am testing against different database products, so I now set the DataType Factory according to the current connection:
private IDatabaseConnection getConnection(Connection jdbcConnection) throws Exception {
String databaseProductName = jdbcConnection.getMetaData().getDatabaseProductName();
DatabaseConnection databaseConnection = new DatabaseConnection(jdbcConnection);
DatabaseConfig dbConfig = databaseConnection.getConfig();
switch (databaseProductName) {
case "HSQL Database Engine":
dbConfig.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new HsqldbDataTypeFactory());
break;
case "MySQL":
dbConfig.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MySqlDataTypeFactory());
break;
default:
log.warn("No matching database product found when setting DBUnit DATATYPE_FACTORY");
}
return databaseConnection;
}
You can obviously add any additionaly databases to this list.
I am using Dbunit version 2.7.0. In my case, just setting the data type factory property in the #Test doesn't suffice. The warning continues when calling dbunit JdbcDatabaseTester.onsetup() method.
I solved the problem implementing a MyJdbcDatabaseTester that extends JdbdDatabaseTester, and overriding the method getConnection(), configuring the datatype factory property:
public class MyJdbcDatabaseTester extends JdbcDatabaseTester {
public MyJdbcDatabaseTester(String driverClass, String connectionUrl, String username,
String password )
throws ClassNotFoundException {
super( driverClass, connectionUrl, username, password );
}
#Override
public IDatabaseConnection getConnection() throws Exception {
IDatabaseConnection result = super.getConnection();
DatabaseConfig dbConfig = result.getConfig();
//to supress warnings when accesing to database
dbConfig.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MySqlDataTypeFactory());
return result;
}
}
Then I use MyJcbdDatabaseTester instead of JdbcDatabaseTester in my tests

Categories