Dropwizard&Hibernate - 当前没有会话绑定到执行上下文

Mic*_*eds 4 rest hibernate dropwizard

我想我对这里的问题有一个很好的了解,但我绝对不知道如何解决它...

以下是我在dropwizard中启动应用程序的方法:

@Override
public void run(ServerConfiguration configuration, Environment environment)
{

    // Setting up the database.
    final DBIFactory factory = new DBIFactory();
    final DBI jdbi = factory.build(environment, configuration.getDataSourceFactory(), "mysql");

    //Hibernate
    final UserDAO dao = new UserDAO(hibernate.getSessionFactory());
    environment.jersey().register(new UserResource(dao));

    final TemplateHealthCheck healthCheck = new TemplateHealthCheck(configuration.getTemplate());

    environment.healthChecks().register("template", healthCheck);

    // security
    //****** Dropwizard security - custom classes ***********/
    environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<User>()
            .setAuthenticator(new BasicAuth(dao))
            .setAuthorizer(new BasicAuthorizer())
            .setRealm("BASIC-AUTH-REALM")
            .buildAuthFilter()));
    environment.jersey().register(RolesAllowedDynamicFeature.class);
    environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}
Run Code Online (Sandbox Code Playgroud)

现在,正如你在这里看到的那样,我将我的用户dao传递给我的身份验证器......我在网上看到的没有教程就是这样,这是因为每个在线教程都使用硬编码值而不是显示如何查询数据库.

也就是说,这就是我试图验证的方式......

public class BasicAuth implements Authenticator<BasicCredentials, User> {

UserDAO _userDAO;
final Encryption enc = new Encryption();

public BasicAuth(UserDAO dao)
{
    this._userDAO = dao;
}

@Override
public Optional<User> authenticate(BasicCredentials credentials)
        throws AuthenticationException {

    // Get the user record.
    User requestedUser = _userDAO.findOneByUsername(credentials.getUsername());

    if (requestedUser != null)
    {
        // check pw.
        if(enc.compare(credentials.getPassword(), requestedUser.getPassword())) {
            return Optional.of(requestedUser);
        }
        else {
            return Optional.empty();
        }
    }
    return Optional.empty();
}
}
Run Code Online (Sandbox Code Playgroud)

请原谅上面那个可怕的缩进,我把我的代码从intelliJ粘贴到这里并且表现不佳 - 无论如何,当我尝试运行这个应用程序时,验证者告诉我:

No session currently bound to execution context
Run Code Online (Sandbox Code Playgroud)

这是踢球者,我知道这只是为了安全方面我得到了这个错误,因为如果我从Application类中删除安全线并运行它,我仍然可以点击我的创建用户端点(也使用这个DAO),效果很好.

所以我的问题真的是 - 我是不是想在验证器中使用那个dao?如果没有,我该怎么想查询数据库?

如果我,那我在哪里错了?

提前致谢.

pan*_*adb 5

首先,你的问题:

从DW文档:

目前,使用@UnitOfWork注释创建事务仅对Jersey管理的资源开箱即用.如果您想在Jersey资源之外使用它,例如在身份验证器中,您应该使用UnitOfWorkAwareProxyFactory实例化您的类.

使用您的代码,您可以创建一个Authenticator但是您永远不会将其挂钩回Hibernate会话.如何Authenticator知道何时开启新会话以DAO进行操作?这是由UnitOfWork机制完成的.但是,这仅适用于Jersey资源,需要为任何其他想要参与此类的人启用.

所以,幸运的是Docs Authenticator在这里给出了一个确切的例子:http: //www.dropwizard.io/1.0.6/docs/manual/hibernate.html

我不打算复制他们的代码,因为我有一个独立的例子供你玩:

 public class HibernateTest extends io.dropwizard.Application<DBConfiguration> {

    private final HibernateBundle<DBConfiguration> hibernate = new HibernateBundle<DBConfiguration>(Object.class) {
        @Override
        public DataSourceFactory getDataSourceFactory(DBConfiguration configuration) {
            return configuration.getDataSourceFactory();
        }
    };

    @Override
    public void initialize(Bootstrap<DBConfiguration> bootstrap) {
        super.initialize(bootstrap);
        bootstrap.addBundle(hibernate);
    }

    @Override
    public void run(DBConfiguration configuration, Environment environment) throws Exception {
        MyDao dao = new MyDao(hibernate.getSessionFactory());
        environment.jersey().register(new MyHelloResource(dao));


        // THIS IS ABSOLUTELY CRITICAL
        MyAuthenticator proxyAuth = new UnitOfWorkAwareProxyFactory(hibernate).create(MyAuthenticator.class, MyDao.class, dao);

        AuthDynamicFeature authDynamicFeature = new AuthDynamicFeature(
                new BasicCredentialAuthFilter.Builder<Principal>()
                    .setAuthenticator(proxyAuth)
                    .setRealm("SUPER SECRET STUFF")
                    .buildAuthFilter());

        environment.jersey().register(authDynamicFeature);
    }

    public static void main(String[] args) throws Exception {
        new HibernateTest().run("server", "/home/artur/dev/repo/sandbox/src/main/resources/config/db.yaml");
    }

    @Path("test")
    @Produces(MediaType.APPLICATION_JSON)
    public static class MyHelloResource {

        private MyDao dao;

        public MyHelloResource(MyDao dao) {
            this.dao = dao;
        }

        @GET
        @Path("/test")
        @UnitOfWork
        @PermitAll
        public Response downloadFile() throws Exception {
            dao.get();
            return Response.ok().build();
        }

    }

    public static class MyAuthenticator implements Authenticator<BasicCredentials, Principal> {
        private MyDao dao;

        MyAuthenticator(MyDao dao) {
            this.dao = dao;
        }

        @Override
        @UnitOfWork
        public Optional<Principal> authenticate(BasicCredentials credentials) throws AuthenticationException {
            dao.get(); 
            return Optional.empty();
        }
    }

    public static class MyDao extends AbstractDAO<Object> {

        public MyDao(SessionFactory sessionFactory) {
            super(sessionFactory);
        }

        public Object get() {
            // if not bridged to Jersey this will throw an exception due to session
            currentSession().createSQLQuery("SELECT 1;").uniqueResult();
            return new Object();
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

上面的代码运行一个最小的DW应用程序,在内存中设置了h2 db.您必须应用配置更改才能启动(并更改服务器配置文件)

这样做是:

  1. 创建提供的hibernate包 DataSourceFactory
  2. 创建 DAO
  3. 创建Authenticator代理
  4. 将所有这些连接在一起

重要的是:

MyAuthenticator proxyAuth = new UnitOfWorkAwareProxyFactory(hibernate).create(MyAuthenticator.class, MyDao.class, dao);
Run Code Online (Sandbox Code Playgroud)

这为您创建了一个知道UnitOfWork注释的代理.它允许Authenticator挂钩(我相信)事件系统,它将根据请求打开和关闭会话.

然后在您的代理中使用该代理 AuthDynamicFeature

最后,在您Authenticator执行身份验证时必须告诉它打开一个新的Session,例如:

@Override
        @UnitOfWork
        public Optional<Principal> authenticate(BasicCredentials credentials) throws AuthenticationException {
            dao.get(); 
            return Optional.empty();
        }
Run Code Online (Sandbox Code Playgroud)

现在所有这些都可以正常运行:

curl user:pass@localhost:9085/api/test/test 
Credentials are required to access this resource.
Run Code Online (Sandbox Code Playgroud)

至于你的上一个问题:

我实际上对春天更流利你认为转换而不是经常处理它是个好主意吗?

我认为这主要是基于意见的,但是:Spring DI!= Jersey DI.你基本上是做春天同样的事情,你弥合Jersey DISpring DI这样jersey可以访问这些豆子Spring.但是,所有session逻辑仍然以相同的方式处理.Spring简单地将显式抽象带走了 - 它在创建bean时已经为你创建了代理.所以我认为你不会有很多优势,而且从个人经验来看,连接Spring和Jersey并不是那么容易.dependency(spring-jersey-bridge)jersey广告不适用于嵌入式Jetties(例如DW设置),而是通过在启动时挂钩到Servlet(您没有).这仍然可以工作,但需要一些黑客才能获得该设置.根据我的经验,guice(例如https://github.com/xvik/dropwizard-guicey)在DW中更容易和更好地集成,并将为您提供相同的优势.Guice显然没有完成Spring所做的所有事情(也没有Spring完成所有的事情Guice)所以你可能需要做自己的研究.

我希望这可以解决问题并让你开始:)

问候,

阿图尔