Spring & Hibernate SessionFactory - recovering from server down

So, pre spring, we used a version of HibernateUtil that would cache the SessionFactory instance if there was a successful connection to the original JDBC, and threw SQLException otherwise. This allowed us to restore the original SessionFactory setting, which was "bad" due to authentication or server connectivity issues.

We moved to Spring and wired things up more or less classically with LocalSessionFactoryBean, C3P0 datasource, and various dao classes that were introduced to SessionFactory.

Now, if the SQL server doesn't seem to be running when the web app is started, the web app is never restored. All access to dao methods is blown up because a null sessionfactory is introduced. (once sessionfactory is correct the connection pool basically handles the up / down state of the SQL Server, so recovery is possible)

Now the dao methods are connected to singlons by default and we can change them to prototype. I don't think this will fix the question, though, I believe that the LocalSessionFactoryBean is now "stuck" and caching a null reference (I haven't tested this yet, though I'm ashamed to admit). This must be a problem that concerns people.

Tried proxy as suggested below - it failed

First of all, I had to ignore the proposal (which, frankly, was wrong with decompilation), to name LocalSessionFactory.buildSessionFactory

it - it is not visible.

Instead, I tried the modified version like this:

override newSessionFactory. At the end, enter the proxy address SessionFactory

pointing to the call handler below

This also failed.

org.hibernate.HibernateException: local data source for configuration - dataSource property must be set to LocalSessionFactoryBean

Now if newSessionfactory()

changed to simple return config.buildSessionFactory()

(instead of proxy) works, but of course no longer exhibits the desired proxy behavior.

public static class HibernateInvocationHandler implements InvocationHandler {
    final private Configuration config;
    private SessionFactory realSessionFactory;
    public HibernateInvocationHandler(Configuration config) {
        this.config=config;
        }

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        if (false) proxy.hashCode();
        System.out.println("Proxy for SessionFactory called");
            synchronized(this) {
                if (this.realSessionFactory == null){
                    SessionFactory sf =null;
                    try {
                        System.out.println("Gonna BUILD one or die trying");

                        sf=this.config.buildSessionFactory();
                    } catch (RuntimeException e) {
                        System.out.println(ErrorHandle.exceptionToString(e));
                        log.error("SessionFactoryProxy",e);
                        closeSessionFactory(sf);
                        System.out.println("FAILED to build");
                        sf=null;
                    }
                    if (sf==null) throw new RetainConfigDataAccessException("SessionFactory not available");
                    this.realSessionFactory=sf;                     
                }
                return method.invoke(this.realSessionFactory, args);    
        }

    }

      

Proxy creation in newSessionFactory looks like this

    SessionFactory sfProxy= (SessionFactory) Proxy.newProxyInstance(
            SessionFactory.class.getClassLoader(),
            new Class[] { SessionFactory.class },
            new HibernateInvocationHandler(config));

      

and it is possible to return this proxy (which does not work) or config.buildSessionFactory () which works but does not solve the initial problem.

An alternative approach was suggested by bozho using getObject (). Note the fatal error in d) because buildSessionFactory is not showing.

a) if this.sessionfactory is unnecessary, no need for a proxy, just return b) if so, create a proxy that ... c) should contain a private reference to sessionfactory and every time it is called check whether it is null. If so, you create a new factory and on successful assignment to the private reference and return it from now on. d) Now specify how you built this factory from getObject (). Your answer should include a call to buildSessionFactory .... but you CAN'T. You can create a factory yourself, but you run the risk of breaking Spring this way (look at the buildSessionFactory code)

+2


a source to share


1 answer


You don't have to worry about this. Running an application is something you rarely do in production, and in development - well, you need a DB server anyway.

You have to worry about the application not recovering if the db server stops while the application is running.



What you can do is extend LocalSessionFactoryBean

and override the method getObject()

and force it to return the proxy (via java.lang.reflect.Proxy

or CGLIB / javassist ) if sessionFactory is null. Thus will be introduced SessionFactory

. The proxy must contain a link to the bare one SessionFactory

, which would initially be null. Whenever the proxy is requesting a connection, if sessionFacotry

it is still null, you call buildSessionFactory()

(from LocalSessionFactoryBean

) and delegate it. Otherwise, throw an exception. (Then, of course, map the new factory bean instead of the current one)

This way your application will be available even if the db is not available at startup. But I wouldn't worry about that myself.

+1


a source







All Articles