We need to be able to get the associated java.sql.Connection of a hibernate session. No other connection will work, as this connection may be associated with a running transaction.
If session.connection() is now deprecated, how am I supposed to do that?
You now have to use the Work API:
session.doWork(connection -> doSomething(connection));
Or, in Java < 8 :
session.doWork(
new Work() {
public void execute(Connection connection) throws SQLException
{
doSomething(connection);
}
}
);
If session.connect() is now deprecated, how am I supposed to do that?
You have to use Session#doWork(Work) and the Work API, as mentioned in the Javadoc:
connection()
Deprecated. (scheduled for removal in 4.x). Replacement depends on need; for doing direct JDBC stuff use doWork(org.hibernate.jdbc.Work); for opening a 'temporary Session' use (TBD).
You have some time before Hibernate 4.x but, well, using a deprecated API somehow looks like this:
:)
Update: According to RE: [hibernate-dev] Connection proxying on the hibernate-dev list, it seems that the initial intention of the deprecation was to discourage the use of Session#connection() because it was/is considered as a "bad" API, but it was supposed to stay at that time. I guess they changed their mind...
Try This
((SessionImpl)getSession()).connection()
Actuly getSession returns Session Interface type, you should see what is the original class for the session, type cast to the original class then get the connection.
GOOD LUCK!
Here is a way to do it in Hibernate 4.3, and it is not deprecated:
Session session = entityManager.unwrap(Session.class);
SessionImplementor sessionImplementor = (SessionImplementor) session;
Connection conn = sessionImplementor.getJdbcConnectionAccess().obtainConnection();
This is what I use and works for me.
Downcast the Session object into a SessionImpl and get the connection object easily:
SessionImpl sessionImpl = (SessionImpl) session;
Connection conn = sessionImpl.connection();
where session is the name of your Hibernate session object.
There's another option with still a lot of casts involved, but at least it doesn't need reflection, which will give you back compile time checking:
public Connection getConnection(final EntityManager em) {
HibernateEntityManager hem = (HibernateEntityManager) em;
SessionImplementor sim = (SessionImplementor) hem.getSession();
return sim.connection();
}
You could of course make that even "prettier" with a few instanceof checks, but the version above works for me.
connection() was just deprecated on the interface. It is still available on SessionImpl. You can do what Spring does and just call that one.
Here is the code from HibernateJpaDialect in Spring 3.1.1
public Connection getConnection() {
try {
if (connectionMethod == null) {
// reflective lookup to bridge between Hibernate 3.x and 4.x
connectionMethod = this.session.getClass().getMethod("connection");
}
return (Connection) ReflectionUtils.invokeMethod(connectionMethod, this.session);
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException("Cannot find connection() method on Hibernate session", ex);
}
}
I found this article
package com.varasofttech.client;
import java.sql.Connection;
import java.sql.SQLException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.SessionImpl;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import com.varasofttech.util.HibernateUtil;
public class Application {
public static void main(String[] args) {
// Different ways to get the Connection object using Session
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
// Way1 - using doWork method
session.doWork(new Work() {
#Override
public void execute(Connection connection) throws SQLException {
// do your work using connection
}
});
// Way2 - using doReturningWork method
Connection connection = session.doReturningWork(new ReturningWork<Connection>() {
#Override
public Connection execute(Connection conn) throws SQLException {
return conn;
}
});
// Way3 - using Session Impl
SessionImpl sessionImpl = (SessionImpl) session;
connection = sessionImpl.connection();
// do your work using connection
// Way4 - using connection provider
SessionFactoryImplementor sessionFactoryImplementation = (SessionFactoryImplementor) session.getSessionFactory();
ConnectionProvider connectionProvider = sessionFactoryImplementation.getConnectionProvider();
try {
connection = connectionProvider.getConnection();
// do your work using connection
} catch (SQLException e) {
e.printStackTrace();
}
}
}
It helped me.
With Hibernate >= 5.0 you can get the Connection like this:
Connection c = sessionFactory.
getSessionFactoryOptions().getServiceRegistry().
getService(ConnectionProvider.class).getConnection();
For hibenate 4.3 try this:
public static Connection getConnection() {
EntityManager em = <code to create em>;
Session ses = (Session) em.getDelegate();
SessionFactoryImpl sessionFactory = (SessionFactoryImpl) ses.getSessionFactory();
try{
connection = sessionFactory.getConnectionProvider().getConnection();
}catch(SQLException e){
ErrorMsgDialog.getInstance().setException(e);
}
return connection;
}
Try this:
public Connection getJavaSqlConnectionFromHibernateSession() {
Session session = this.getSession();
SessionFactoryImplementor sessionFactoryImplementor = null;
ConnectionProvider connectionProvider = null;
java.sql.Connection connection = null;
try {
sessionFactoryImplementor = (SessionFactoryImplementor) session.getSessionFactory();
connectionProvider = (ConnectionProvider) sessionFactoryImplementor.getConnectionProvider().getConnection();
connection = connectionProvider.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
Connection conn = null;
PreparedStatement preparedStatement = null;
try {
Session session = (org.hibernate.Session) em.getDelegate();
SessionFactoryImplementor sfi = (SessionFactoryImplementor) session.getSessionFactory();
ConnectionProvider cp = sfi.getConnectionProvider();
conn = cp.getConnection();
preparedStatement = conn.prepareStatement("Select id, name from Custumer");
ResultSet rs = preparedStatement.executeQuery();
while (rs.next()) {
System.out.print(rs.getInt(1));
System.out.println(rs.getString(2));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (preparedStatement != null) {
preparedStatement.close();
}
if (conn != null) {
conn.close();
}
}
Here is a Java 8 method to return the Connection used by an EntityManager without actually doing anything with it yet:
private Connection getConnection(EntityManager em) throws SQLException {
AtomicReference<Connection> atomicReference = new AtomicReference<Connection>();
final Session session = em.unwrap(Session.class);
session.doWork(connection -> atomicReference.set(connection));
return atomicReference.get();
}
Related
I am building a basic java application to load some files into a mysql database. I am able to load the files up and populate my tables without any problems. However after speaking to someone who reviewed my code, I am apparently not correctly closing my connections and wasting resources. Where am I not closing up the connections? Have I done this incorrectly?
I am using the try-with-resources construct within my DbSinger class to execute prepared statements to my database, which should automatically close the connection so long as the AutoCloseable interface is implemented, which it is in the parent class of Db. The close() method however is never reached. The DbSinger is instantiated inside my main() and then runs it's single method populateSingers() with an ArrayList of Singer objects.
Connection Class
public class SQLConnection {
private static final String servername = "localhost";
private static final int port = 3306;
private static final String user = "ng_user";
private static final String pass = "ng";
private static final String db = "ng_music";
private static final String connectionString = "jdbc:mysql://" + servername + ":" + port + "/" + db;
public Connection provide() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
return DriverManager.getConnection(connectionString, user, pass);
}
catch (SQLException | ClassNotFoundException e) {
throw new SQLConnectionException(e);
}
}
public class SQLConnectionException extends RuntimeException {
SQLConnectionException(Exception e) {super(e);}
}
}
Abstract parent class
public abstract class Db implements AutoCloseable{
private Connection connection;
Db() {
SQLConnection sqlC = new SQLConnection();
this.connection = sqlC.provide();
}
#Override
public synchronized void close() throws SQLException {
if(connection != null) {
connection.close();
connection = null;
System.out.println("Connection closed");
}
}
Connection getConnection() {
return connection;
}
boolean checkIfPopulated(String query){
try {
PreparedStatement ps = getConnection().prepareStatement(query);
ResultSet rs = ps.executeQuery();
return !rs.next();
} catch (SQLException e) {
e.printStackTrace();
}
return true;
}
}
Concrete class to execute queries to database for singers table
public class DbSinger extends Db {
public DbSinger() {
super();
}
public void populateSingers(ArrayList<Singer> singers) {
String populateSingersQuery = "insert into ng_singers(name, dob, sex) values(?,?,?)";
if(!checkIfPopulated("select * from ng_singers")){
System.out.println("Singer Table is already populated");
return;
}
try (PreparedStatement ps = getConnection().prepareStatement(populateSingersQuery)) {
for (Singer s : singers) {
ps.setString(1, s.getName());
ps.setDate(2, java.sql.Date.valueOf(s.getDob()));
ps.setString(3, s.getSex());
ps.addBatch();
}
ps.executeBatch();
System.out.println("Singers added to table");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
My code is able to execute is able to run fine and does what it needs to, but I want to understand why and where I am not closing connections, and to understand how I can resolve this. Or at least understand if I am approaching this wrong.
In your case, you need to instantiate DBSinger class in try-with-resources statement to close the underlying connection.
Instead of doing:
DbSinger dbSinger = new DbSinger();
You need to do:
try (DbSinger dbSinger = new DbSinger()) {
// Your other code
}
This way the close() method you are overriding in your Db class will be called automatically.
Also, close the preparedStatement you created in your checkIfPopulated method by:
try (PreparedStatement ps = getConnection().prepareStatement(query)) {
// Other codes
}
Your code is old way. And you do need close manually. However, with Java 8, you can use try with resource like below,
try (Connection conn = ds.getConnection();
Statement stmt = conn.createStatement()) {
try {
stmt.execute(dropsql);
} catch (Exception ignore) {} // ignore if table not dropped
stmt.execute(createsql);
stmt.execute(insertsql);
try (ResultSet rs = stmt.executeQuery(selectsql)) {
rs.next();
} catch (Exception e2) {
e2.printStackTrace();
return("failed");
}
} catch(Exception e) {
e.printStackTrace();
return("failed");
}
I try to create a as-simple-as-possible connection manager för a small web-application that runs on a tomcat7 server. I have found many examples of how to implement this, but almost always those requires JNDI and JEE, or else there are no complete example.
I want my connection manager to deliver a connection from a connection-pool when I call getConnection(), and that it is thread-safe so that no other uses my connection until I close it.
In my application I call ConnectionManager.getConnection() from a REST-service and then use that connection for all database-calls throughout the request, and close it in a finally-clause.
Could someone please look at my code and see if it is good enough to fullfill my needs...? Is it any risk that two REST-calls could get the same connection or does the tomcat DataSource handle that for me?
Would it be more proper to make my class a DataSourceManager with a getDataSource() method that deliver the data source, and in my REST-service get a connection with DataSourceManager.getDataSource().getConnection()? Would it make any difference, technically?
Or do I have to do this in an other way to make it work properly...?
import java.sql.Connection;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import se.esvenska.util.Property;
public class ConnectionManager {
private static DataSource dataSource;
private static void initDataSource() throws DatabaseException {
try {
PoolProperties p = new PoolProperties();
p.setUrl("...url...");
p.setDriverClassName("org.postgresql.Driver");
p.setUsername("...user...");
p.setPassword("...password...");
p.setDefaultAutoCommit(false);
dataSource = new DataSource();
dataSource.setPoolProperties(p);
} catch (Exception e) {
e.printStackTrace();
throw new DatabaseException(e);
}
}
public static Connection getConnection() throws Exception {
if (dataSource == null) {
initDataSource();
}
return dataSource.getConnection();
}
}
It depends on what you mean by thread-safe. I don't think it'll blow up, but there's definitely the chance that you'll call initDataSource more than once.
public static Connection getConnection() throws Exception {
if (dataSource == null) { // Another thread can come in after this
initDataSource(); // and before this
}
return dataSource.getConnection();
}
You'll need to synchronize that method:
public static synchronized Connection getConnection() throws Exception {
// -----------^
if (dataSource == null) { // Now this region is
initDataSource(); // protected
}
return dataSource.getConnection();
}
I would recommend couple of changes:
Make constructor private
getConnection should be thread safe using double locking, detail here
package com.test;
import java.sql.Connection;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import se.esvenska.util.Property;
public class ConnectionManager {
private ConnectionManager() {
}
private static DataSource dataSource;
private static void initDataSource() throws DatabaseException {
try {
PoolProperties p = new PoolProperties();
p.setUrl("...url...");
p.setDriverClassName("org.postgresql.Driver");
p.setUsername("...user...");
p.setPassword("...password...");
p.setDefaultAutoCommit(false);
dataSource = new DataSource();
dataSource.setPoolProperties(p);
} catch (Exception e) {
e.printStackTrace();
throw new DatabaseException(e);
}
}
public static Connection getConnection() throws Exception {
if (dataSource == null) {
synchronized(ConnectionManager. class) {
if (dataSource == null) {
initDataSource();
}
}
}
return dataSource.getConnection();
}
}
have been a long-time reader here and now I`ve got a problem I can´t really get my head around.
For ease of access and to save object creation overhead I have a static class realizing database accesses. The used JVM implementation is Tomcat and for connection pooling org.apache.commons.dbcp is used.
I've read a lot about thread-safety, heap and stack here and elsewhere but I can`t get to a definitive conclusion if multiple method calls on my static class won't interfere with each other. Most topics I've read deal with instance methods while I use static ones which might have implications I overlooked.
If I understood everything correctly, as the variables connection, statement, resultset are on the method level, each function call should have a unique reference on the stack to a unique object in the heap and it should not be possible that multiple method calls interfere with each other.
Am I right or do I stand corrected? Any help would be appreciated.
The (shortened) code is :
public class DBQuery{
private static String pathToDataSource = "";
private static javax.naming.Context cxt = null;
private static javax.sql.DataSource ds = null;
private static void getDataSource() throws Exception {
if(pathToDataSource.equals("")){ pathToDataSource = Config.getParam("PathToDataSource"); }
cxt = new javax.naming.InitialContext();
ds = (javax.sql.DataSource) cxt.lookup(pathToDataSource);
}
private static Connection connect() throws Exception {
if(ds==null){ getDataSource(); }
return ds.getConnection();
}
public static Vector doDBquery(String querystring) throws Exception {
Vector retVec = new Vector();
Connection connection = null;
Statement statement = null;
ResultSet resultset = null;
try {
connection = getConnection();
statement = connection.createStatement();
resultset = statement.executeQuery(querystring);
...
} catch(Exception e) {
...
} finally {
myFinallyBlock(resultset, statement, connection);
}
return retVec;
}
// more methods like doDBInsert() following, hence closure in separate myFinallyBlock
private static void myFinallyBlock(ResultSet resultset, Statement statement, Connection connection) {
try {
if (resultset != null) resultset.close();
} catch (SQLException e) { resultset = null; }
try {
if (statement != null) statement.close();
} catch (SQLException e) { statement = null; }
try {
if (connection != null) connection.close();
} catch (SQLException e) { connection = null; }
}
} //close class
Yeah, you are right inside method there is no concurrency problems , until you are using shared variables inside it, in other words "Stateless objects are always thread-safe."
Servlet is quite good example of it ;)
edited.
For making your code safe I recommend you to do follow:
private static Connection connect() throws Exception {
if (ds == null) {
synchronized (Connection.class) {
if (ds == null) {
getDataSource();
}
}
}
return ds.getConnection();
}
In our web application we have a HibernateSessionFactory class, that is opening and closing connections. Everything is okay, but when we are updating data in the database, it doesn't change in our application. Unfortunately, we see old data from the database. How can I fix it?
public class HibernateSessionFactory {
private static final ThreadLocal threadLocal = new ThreadLocal();
private static org.hibernate.SessionFactory sessionFactory;
private static Configuration configuration = new Configuration();
private static ServiceRegistry serviceRegistry;
private static final Logger log = Logger.getLogger(HibernateSessionFactory.class);
static {
try {
configuration.configure();
serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties())
. buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (Exception e) {
log.error("Error Creating SessionFactory",e);
}
}
private HibernateSessionFactory() {
}
public static Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (session == null || !session.isOpen()) {
if (sessionFactory == null) {
rebuildSessionFactory();
}
session = (sessionFactory != null) ? sessionFactory.openSession()
: null;
threadLocal.set(session);
}
return session;
}
public static void rebuildSessionFactory() {
try {
configuration.configure();
serviceRegistry = new ServiceRegistryBuilder(). applySettings(configuration.getProperties())
.buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (Exception e) {
log.error("Error Creating SessionFactory",e);
}
}
public static void closeSession() throws HibernateException {
Session session = (Session) threadLocal.get();
threadLocal.set(null);
if (session != null) {
session.flush();
session.close();
}
}
public static org.hibernate.SessionFactory getSessionFactory() {
return sessionFactory;
}
public static Configuration getConfiguration() {
return configuration;
}
}
Hibernate is a sofisticated and complex framework for building a layer between your program and the database, providing an object oriented model to help object oriented programs in their job.
In order to do this, and to be more performat of course, it creates a cache which stores somehow some of the data coming from the database, or going to the database.
I think that this problem is NOT concerning the connection to the database but rather how you save and retrive data.
I'll try to explain my self better: when you query database to save data you do the following steps:
open session
open transaction
build object
flush and save object in session
commit transaction
close transaction
close session
eg
public Boolean saveNewCliente(Cliente c) {
Session s = getSession();
Transaction t = null;
try {
t = s.beginTransaction();
s.save(c);
s.flush();
t.commit();
s.close();
return true;
} catch (Exception e) {
if (t!=null) t.rollback();
System.out.println(e.getMessage());
return false;
}
finally{
s.close();
}
}
A common pitfall happens when you query database to fetch data, is to leave the part relating the transaction out of your "query" steps. As well for saving data you have to
open session
open transaction
build query / criteria
execute query over session
close transaction
close session
If you don't follow this steps is possible that you have stale data in your application, but not in your database. A check may be to execute your update/save-query and check manually in the database if the data has changed/created. If your application then loads stale data you know you do the fetch-query in the wrong way(without using transaction).
Here's a snipped of example
public Cliente get(Integer id) {
Session s = getSession();
Transaction tx = s.beginTransaction();
try {
System.out.println("get cliente by id");
Cliente res = new Cliente();
res = (Cliente) s.get(Cliente.class, id);
tx.commit();
return res;
} catch (Exception e) {
tx.rollback();
System.out.println(e.getMessage());
return null;
}finally{
s.close();
}
}
If you want to furthermore investigate you can suspend the usage of cache by hibernate, you can do it in the following way, but remember that if data is cached there is a reason ;)
This can be useful as a quick test in order to proceed in discovering if the error is due to wrong query usage.
You should add this to your hibernate config xml
<!-- to disable cache -->
<property name="hibernate.cache.use_second_level_cache">false</property>
<property name="hibernate.cache.use_query_cache">false</property>
I am using Hibernate + jersey Rest And Mysql as backend for database. In Hibernate used cp3 pool for connection but after some time it creates so many idle connections and gets stuck. my configurations are :
package com.appname.hibernate.util;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static SessionFactory sessionFactory;
static {
try {
Configuration configuration = new Configuration().configure();
sessionFactory = configuration.buildSessionFactory();
} catch (HibernateException he) {
System.err.println("Error creating Session: " + he);
he.printStackTrace();
throw new ExceptionInInitializerError(he);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
////////////////////// INSIDE DAO ///////////////////////////
private Session getSession() {
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session;
try {
session = sessionFactory.getCurrentSession();
} catch (org.hibernate.HibernateException he) {
session = sessionFactory.openSession();
}
return session;
}
public Users regsiterUser(Users users) throws Exception {
Session session = null;
try {
session = getSession();
Transaction transaction = session.beginTransaction();
session.saveOrUpdate(users);
transaction.commit();
return users;
//Using context session that is why I am not closing session
} catch (Exception e) { throw e; }
}
I am calling this DAO function form my controller where I am making transaction and session inside DAO layer. Please help me I am trying to solve this but not getting any solution please have a look what is wrong with the above configuration and code ......
Thanks in advance.....
I think this session = sessionFactory.openSession(); should be closed manually after use because it is not managed by an orchestrator that release resource after use.