I have a jax-rs/jersey rest application that uses Hibernate 5.2 as the orm. There is also a spring filter that handles the authentication using tokens. Everything works pretty well but there is a small problem. Each dao object is creating its own session factory on construction like so.
public abstract class BaseDAO<T> {
protected SessionFactory sessionFactory = getSessionFactory();
protected final Validator validator = getValidator();
protected SessionFactory getSessionFactory() {
try {
return (SessionFactory) new Configuration().configure().buildSessionFactory();
} catch (Exception e) {
throw new IllegalStateException("Could not locate SessionFactory in JNDI");
}
}
protected Validator getValidator() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
return factory.getValidator();
}
@SuppressWarnings({"hiding", "unchecked"})
public <T> T save(final T o) {
Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction();
T savedObj = (T) session.save(o);
tx.commit();
session.close();
return savedObj;
}
public void delete(final Object object) {
Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction();
session.delete(object);
tx.commit();
session.close();
}
@SuppressWarnings("hiding")
public <T> T get(final Class<T> type, final int id) {
Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction();
T obj = session.get(type, id);
tx.commit();
session.close();
return obj;
}
@SuppressWarnings({"hiding", "unchecked"})
public <T> T merge(final T o) {
Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction();
T obj = (T) session.merge(o);
tx.commit();
session.close();
return obj;
}
@SuppressWarnings("hiding")
public <T> void saveOrUpdate(final T o) {
Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction();
session.saveOrUpdate(o);
tx.commit();
session.close();
}
@SuppressWarnings({ "hiding", "deprecation", "unchecked" })
public <T> List<T> getAll(final Class<T> type) {
final Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction();
final Criteria crit = session.createCriteria(type);
List<T> results = crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
tx.commit();
session.close();
return results;
}
@SuppressWarnings({ "hiding", "deprecation", "unchecked" })
public <T> List<T> findByExample(final Class<T> type, T instance) {
try {
Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction();
List<T> results = session
.createCriteria(type)
.add(Example.create(instance))
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.list();
tx.commit();
session.close();
return results;
} catch (RuntimeException re) {
//log.error("find by example failed", re);
throw re;
}
}
This is a problem because if you create multiple dao objects, you quickly run out of connections and even though I call session.close() after each dao call. After making too many calls to the resources, my tiny db instance is complaining about too many connections. My immediate thoughts were that the session factory needed to be managed by jersey. So I attempted to a factory binder like so:
public class HibernateSessionFactory implements Factory<SessionFactory> {
protected SessionFactory sessionFactory;
@Override
public void dispose(SessionFactory arg0) {
sessionFactory.close();
}
@Override
public SessionFactory provide() {
try {
sessionFactory = (SessionFactory) new Configuration().configure().buildSessionFactory();
return sessionFactory;
} catch (Exception e) {
throw new IllegalStateException("Could not locate SessionFactory in JNDI");
}
}
}
In the resource config:
register(new AbstractBinder() {
@Override
protected void configure() {
bindFactory(HibernateSessionFactory.class).to(SessionFactory.class);
}
});
And changing BaseDAO to this
@Inject
protected SessionFactory sessionFactory
It doesn't seem to work - session factory is always null. The other problem is that the spring filter can't use the jersey inject. The filter is configured like this.
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Order(2)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
private final com.renewedvoices.security.UserService userService;
private final TokenAuthenticationService tokenAuthenticationService;
private final SessionFactory sessionFactory;
public SpringSecurityConfig() {
super(true);
this.userService = new UserService();
this.tokenAuthenticationService = new TokenAuthenticationService("tooManySecrets", userService);
this.sessionFactory = createSessionFactory();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// Custom Token based authentication based on the header previously given to the client
http
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService),
UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/rest/auth/**").permitAll()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().and()
.anonymous().and()
.servletApi().and()
.headers().cacheControl();
}
private SessionFactory createSessionFactory() {
try {
return (SessionFactory) new org.hibernate.cfg.Configuration().configure().buildSessionFactory();
} catch (Exception e) {
throw new IllegalStateException("Could not locate SessionFactory in JNDI");
}
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Override
public UserService userDetailsService() {
return userService;
}
@Bean
public TokenAuthenticationService tokenAuthenticationService() {
return tokenAuthenticationService;
}
@Bean
public SerssionFactory sessionFactory() {
return sessionFactory;
}
}
Whether I use @Autowired or @Inject in BaseDao, sessionFactory is always null. I cannot switch over to purely spring. Is there any way to get this sessionFactory working? Is this the best way to handle the session factory in a rest service or is there a better way to not open a new session on each request? Almost all of the answers I have found have been spring-only solutions. Any help is greatly appreciated.