Hibernate connection problems - java

I'm working on a webapp and I have connection errors after Hibernate throws exceptions :
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
It gave me this exception each time I try to access my db after an exception occured.
I now Hibernate's not supposed to throw errors if my application is well coded but if something happens with the connection to the db, I don't want my application to be stuck with this error.
Here's my HibernateUtil class :
public class HibernateUtil {
private static Logger log = Logger.getLogger(HibernateUtil.class);
private static org.hibernate.SessionFactory sessionFactory;
private static String confFile = "hibernate-test.properties";
private static final ThreadLocal<Session> threadSession = new ThreadLocal<Session>();
private HibernateUtil() {
}
public static void buildSessionFactory(){
Configuration configuration = new Configuration();
synchronized(HibernateUtil.class){
if(sessionFactory == null){
try {
Properties properties = new Properties();
properties.load(HibernateUtil.class.getClassLoader().getResourceAsStream(confFile));
configuration.setProperties(properties);
} catch (Exception e) {
log.fatal("cannot load the specified hibernate properties file: " + confFile);
throw new RuntimeException("cannot load the specified hibernate properties file : " + confFile, e);
}
sessionFactory = configuration.configure().buildSessionFactory();
}
HibernatePBEEncryptorRegistry registry = HibernatePBEEncryptorRegistry.getInstance();
if(registry.getPBEStringEncryptor("strongHibernateStringEncryptor") == null) {
StandardPBEStringEncryptor strongEncryptor = new StandardPBEStringEncryptor();
strongEncryptor.setAlgorithm("PBEWithMD5AndDES"); // not really needed as it is the default
strongEncryptor.setPassword("aStrongPassword");
registry.registerPBEStringEncryptor("strongHibernateStringEncryptor", strongEncryptor);
}
}
}
public static SessionFactory getSessionFactory() {
if(sessionFactory == null){
buildSessionFactory();
}
return sessionFactory;
}
public static Session getCurrentSession(){
if(!getSessionFactory().getCurrentSession().isOpen())
getSessionFactory().openSession();
return getSessionFactory().getCurrentSession();
}
}
Here's my BaseAction class where initialization and closing of sessions is set :
public class BaseAction extends ActionSupport {
public Session hib_session;
public void initHibSession() {
hib_session = HibernateUtil.getCurrentSession();
hib_session.beginTransaction();
hib_session.clear();
}
public void closeHibSession() {
hib_session.getTransaction().commit();
}
}
Here's an example of an action:
Transaction transaction = new Transaction(user, Transaction.Type.REGISTRATION, new HashSet(domains));
initHibSession();
hib_session.save(transaction);
closeHibSession();
transaction_id = transaction.getId();
Is there a way to avoid the exception above ?

It gave me this exception each time I try to access my db after an exception occurred.
I'm not sure to understand the exact condition. Anyway, after an exception, you should rollback the transaction, close the session and start over. That being said, I have some remarks about your code.
About your HibernateUtil:
why do you have a ThreadLocal, the Session#getCurrentSession() method handle that for you (you don't seem to use the thread local though).
in HibernateUtil.getCurrentSession(), why do you mess with getCurrentSession() and openSession()? Firstly, there is no need to do what you do, getCurrentSession() will return a new session if no session is associated to the current thread. Secondly, both approaches are different and have different semantics (you need to close the session yourself when using openSession()), you should use one or the other.
About your BaseAction:
I wonder why you clear() the session after Session#beginTransaction(). In case you didn't committed an ongoing transaction, you'll loose all the pending changes. Is this really what you want?
PS: I would consider using the Open Session in View pattern to remove all this burden from your code.
Resources
Sessions and transactions
Open Session in View

Related

JPA: How to set MySQL session variables?

I need to set a mysql session variable for my application to work with a MariaDB Galera Cluster as expected. The SQL call is: SET SESSION wsrep_sync_wait = 1. It shall be set at all times when the application uses the database. I am using EclipseLink as the JPA provider.
My question is: What is the best way to achieve this?
Option 1: EclipseLink Session Customizer
Register a session customizer in persistence.xml:
public class SessionCustomizerImpl implements org.eclipse.persistence.config.SessionCustomizer {
private final static String WSREP_SYNC_WAIT_CHECK_SQL = "SHOW SESSION VARIABLES LIKE 'wsrep_sync_wait'";
private final static String WSREP_SYNC_WAIT_SET_SQL = "SET SESSION wsrep_sync_wait = 1";
#Override
public void customize(Session session) throws Exception {
Vector result = session.executeSQL(WSREP_SYNC_WAIT_CHECK_SQL);
if ((result != null) && !result.isEmpty()) {
session.executeNonSelectingSQL(WSREP_SYNC_WAIT_SET_SQL);
// Galera connection detected; wsrep_sync_wait set to 1
} else {
// No Galera connection detected; wsrep_sync_wait not set
}
}
}
This does not work for me. Querying the session variable from an EntityManager returns a value of 0.
Option 2: EntityManager factory
Every time a new EntityManager is created, the SQL is executed.
public class SyncWaitEntityManagerFactory implements Factory<EntityManager> {
private final EntityManagerFactory emf;
#Inject
public SyncWaitEntityManagerFactory(EntityManagerFactory emf) {
this.emf = emf;
}
#Override
public EntityManager provide() {
final EntityManager em = emf.createEntityManager();
// set it
em.getTransaction().begin();
em.createNativeQuery("SET SESSION wsrep_sync_wait = 1").executeUpdate();
em.getTransaction().commit();
return em;
}
#Override
public void dispose(EntityManager instance) {
if (instance.isOpen()) {
instance.close();
}
}
}
This works, but I'm not sure if it is overkill. Also, I am worried about the cost of the transaction, which is only required by Query#executeUpdate(), but not by the actual SQL call.
Option 3: Via JDBC URL
Appending the variable and value to the JDBC URL (see here for details):
String jdbcUrl = "jdbc:mysql://db.example.test:3306/"+ JDBC_DB
+"?sessionVariables=wsrep_sync_wait=1";
Properties p = new Properties();
p.put("javax.persistence.jdbc.url", jdbcUrl);
p.put("javax.persistence.jdbc.user", JDBC_USER);
p.put("javax.persistence.jdbc.password", JDBC_PASSWORD);
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPU", p);
EntityManager entityManager = emf.createEntityManager();
Nice solution. Works for me; no effort, no transaction necessary. Downside: I can't catch exceptions (example: check first if the variable exists, then set it -- allows deployment of the code on systems that don't support/use this specific variable).
You could also use an aspect to execute a query every time a getConnection() is called which is for every transaction basically (the aspect is set after the call so that we have a valid connection object):
#Component
#Aspect
public class CustomConnectionPreparer implements ConnectionPreparer
{
#AfterReturning(pointcut = "execution(* *.getConnection(..))", returning = "connection")
public Connection prepare(Connection connection) throws SQLException {
// execute the query (also exception handling)
try (Statement statement = connection.createStatement()) {
statement.execute("SET SESSION wsrep_sync_wait = 1");
} catch (SQLException e) {
throw e;
}
return connection;
}
}
And before you return the connection to the caller you execute your query and you should always have that value set.

Dropwizard #UnitOfWork with asynchronous database call

I imagine that this is a common problem, but after some searching I wasn't able to find anything relevant.
The problem I'm having is that I'm getting a No Hibernate Session bound to thread exception when annotating my resource method with #UnitOfWork and inside my resource method, making an asynchronous DAO call. The idea behind this design is to make the database call on a separate I/O thread so that it frees up the Jersey resource thread.
Unfortunately, as the exception says, this RxIoScheduler-2 thread doesn't have a hibernate session bound to it.
Any suggestions?
Hibernate Session is not thread safe, so we need a strategy how to get the current session for the current thread. Such strategy is called CurrentSessionContext.
The current session is a session which we get by this call:
sessionFactory.getCurrentSession()
Hibernate can be configured with various current session strategies. #UnitOfWork uses this strategy:
hibernate.current_session_context_class = managed
For this strategy you should put a session to the context by an explicit call of the
ManagedSessionContext.bind(session)
So, as we know a Session is not thread safe, you should create a new session for a separate thread and put that session in the ManagedSessionContext. After that you can call your DAO by the same way as in the endpoint methods with #UnitOfWork.
Keep in mind that you should unbind the session before closing it with
ManagedSessionContext.unbind(factory)
You can use this utility class to create a session for a separate thread:
public final class HibernateSessionUtils {
private HibernateSessionUtils() {
}
public static void request(SessionFactory factory, Runnable request) {
request(factory, () -> {
request.run();
return null;
});
}
public static <T> T request(SessionFactory factory, Supplier<T> request) {
Transaction txn = null;
Session session = factory.openSession();
try {
ManagedSessionContext.bind(session);
txn = session.beginTransaction();
T result = request.get();
commit(txn);
return result;
} catch (Throwable th) {
rollback(txn);
throw Throwables.propagate(th);
} finally {
session.close();
ManagedSessionContext.unbind(factory);
}
}
private static void rollback(Transaction txn) {
if (txn != null && txn.isActive()) {
txn.rollback();
}
}
private static void commit(Transaction txn) {
if (txn != null && txn.isActive()) {
txn.commit();
}
}
}
Throwables from guava.
It can be used by this way
List<Campaign> getCampaigns(SessionFactory factory, CampaignDao dao) {
return HibernateSessionUtils.request(
factory,
dao::getCampaigns
);
}
In the dao.getCampaigns() method you can get the session
sessionFactory.getCurrentSession()
You can inject the factory everywhere using Guice.
Other option is to use UnitOfWorkAwareProxyFactory.

Hibernate Session factory shared by system database users

I want to create session factory which is shared by all system database users. As session factory is heavy component it is not right to create it every time user login to application.
My scenario is that, I have an application which is login by system database users i.e. Mysql database users not by application users. So I need a session factory which can be created only once and used by all system database users.
Is it possible to do that?
Yes, it is possible. you can create a class which have static object of SessionFactory. suppose this class have an static block so that it will execute only once at the time of when the class is loaded into JVM. In that static block you have to initialize SessionFactory and when you want to use sessionFactory you can call getter of sessionFactory object.
public final class SessionFactoryHelper {
private static SessionFactory sysDBUserSessionFactory;
static {
try {
// initialize the sysDBUserSessionFactory object.
} catch (Exception e) {
//exception handling
}
}
public static SessionFactory getsysDBUserSessionFactory() {
return sysDBUserSessionFactory;
}
}
Whenever you want sessionFactory for system database users use :
SessionFactory sessionFactory = SessionFactoryHelper.getsysDBUserSessionFactory();

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.

Hibernate LazyInitializationException - How do I resolve this?

I have a Database where user and address data is stored in separate tables. When a user logs in to my page I want to show him a form with which he can change his user/account data. However, I end up getting an Exception: org.hibernate.LazyInitializationException: could not initialize proxy - no Session. The address attributes can't be loaded. The user loads just fine and that's what I don't understand. My AddressDAOImpl looks like this:
#Autowired
private SessionFactory sessionFactory;
public void addAddress(Address address)
{
sessionFactory.getCurrentSession().save(address);
}
public void updateAddress(Address address)
{
sessionFactory.getCurrentSession().update(address);
}
public List<Address> listAddress()
{
return sessionFactory.getCurrentSession().createQuery("from address").list();
}
public void deleteAddress(int id)
{
Address addr = (Address) sessionFactory.getCurrentSession().load(Address.class, id);
if(addr != null)
{
sessionFactory.getCurrentSession().delete(addr);
}
}
public Address getAddress(int id)
{
return (Address) sessionFactory.getCurrentSession().load(Address.class, id);
}
My Controller does this:
#RequestMapping("/library/home")
public ModelAndView showHome()
{
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User user = userService.getUser(username);
Address address = addressService.getAddress(user.getId());
ModelMap mmap = new ModelMap();
mmap.addAttribute("user", user);
mmap.addAttribute("addresse", address);
return new ModelAndView("/library/home", mmap);
}
Why doesn't this work? And why does it work for the user data?
I'm assuming that your AddressService created the transaction used to load the Address proxy (it's a proxy because you used load() instead of get()). The session associated with this transaction is closed when you return the Address from the AddressService.
By the time your view resolver tries to access the attributes of the Address proxy, there is no session available through which a database query can be run to fetch the attributes. Hence the exception.
Either use get() instead of load(), or access the attributes while you're still in the scope of the transaction.
The load method doesn't fetch an address from the database. It returns a proxy to an uninitialized address, assuming the address exists in the database. Use Session.get to get the address.
See http://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Session.html#load%28java.lang.Class,%20java.io.Serializable%29
I think its possibly because you did not initialize your sessionfactory configuration. Try this class to help you create a session factory.
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
sessionFactory = new Configuration().configure()
.buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
hope this helps
You can consider using an OpenSessionInView pattern even if it's not always the best solution

Categories