【问题标题】:Jersey + Hibernate Injecting SessionFactory to DAOJersey + Hibernate 将 SessionFactory 注入 DAO
【发布时间】:2017-05-04 01:51:53
【问题描述】:

我有一个使用 Hibernate 5.2 作为 orm 的 jax-rs/jersey rest 应用程序。还有一个使用令牌处理身份验证的弹簧过滤器。一切都很好,但有一个小问题。每个 dao 对象都在创建自己的会话工厂,就像这样。

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;
    }
}

这是一个问题,因为如果您创建多个 dao 对象,您很快就会用完连接,即使我在每次 dao 调用后调用 session.close() 也是如此。在对资源进行过多调用后,我的小型数据库实例抱怨连接过多。我的直接想法是会话工厂需要由 jersey 管理。所以我尝试了这样的工厂活页夹:

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");
        }       
    }
}

在资源配置中:

    register(new AbstractBinder() {
        @Override
        protected void configure() {
            bindFactory(HibernateSessionFactory.class).to(SessionFactory.class);
        }
    });

并将 BaseDAO 更改为此

@Inject
protected SessionFactory sessionFactory 

它似乎不起作用 - 会话工厂始终为空。另一个问题是弹簧过滤器不能使用球衣注入。过滤器是这样配置的。

@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;
    }
}

无论我在 BaseDao 中使用@Autowired 还是@Inject,sessionFactory 始终为空。我不能切换到纯弹簧。有没有办法让这个 sessionFactory 工作?这是在休息服务中处理会话工厂的最佳方法,还是有更好的方法不针对每个请求打开新会话?我发现的几乎所有答案都是仅限弹簧的解决方案。任何帮助是极大的赞赏。

【问题讨论】:

  • 您需要将 DAO 设置为 Spring bean,以便可以将其注入 Spring 组件中。然后对于 Jersey,您需要集成 Jersey 以使用 Spring 组件,以便您可以注入 Jersey。看看this post
  • 我通读了答案并查看了链接的 git 项目。我不确定我是否遵循。你能帮忙指出我在上面做错了什么和他们在做什么之间的差距吗?我已经尝试标记@@Component 并配置如上所示的bean 定义。
  • 我会发布一个答案,但这不是一个简单的设置。如果以后有时间,我会尝试为您整理一个示例项目。如果不是今天,也许明天的某个时间

标签: java spring hibernate rest jersey


【解决方案1】:

继续我上面的评论

您需要将 DAO 设为 Spring bean,以便可以将其注入 Spring 组件中。然后对于 Jersey,您需要集成 Jersey 以使用 Spring 组件,以便您可以注入 Jersey。看看this post。

所以你需要做的是让 DAO 成为一个 Spring Bean。你可以这样做

@Configuration
public class DataConfiguration {
    @Bean
    public MyDao myDao() {
        return new MyDao();
    }
}

然后在您的AbstractSecurityWebApplicationInitializer 中,您需要添加配置类,并覆盖contextConfigLocation 属性,以便Jersey 不会尝试创建ContextLoaderListener。这已经由 Spring Security 创建了

@Order(1)
public class DemoSecurityWebApplicationInitializer
        extends AbstractSecurityWebApplicationInitializer {

    public DemoSecurityWebApplicationInitializer() {
        super(DataConfiguration.class, SecurityConfig.class);
    }

    @Override
    public void afterSpringSecurityFilterChain(ServletContext servletContext) {
        // Set the Jersey used property to it won't load a ContextLoaderListener
        servletContext.setInitParameter("contextConfigLocation", "NOOP");
    }
}

您需要做的最后一件事是添加jersey-spring3 依赖项。这是 Jersey 依赖项,允许它与 Spring 组件集成(只有 Spring 到 Jersey,而不是 Jersey 到 Spring)。

<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-spring3</artifactId>
    <exclusions>
       <!-- exclude Spring 3 if you want to use Spring 4 -->
    </exclusions>
<dependency>

查看this GitHub project中的完整示例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-10
    • 1970-01-01
    • 2011-05-06
    • 2016-05-19
    • 1970-01-01
    • 1970-01-01
    • 2013-06-07
    • 1970-01-01
    相关资源
    最近更新 更多