简体   繁体   English

使用HK2向Jersey返回hibernate会话

[英]Injecting hibernate session to Jersey using HK2

I'm developing little app and have some issues with DI. 我正在开发小应用程序并且在DI方面存在一些问题。

I have a repository class for persisting my entities which I inject to my service. 我有一个存储库类用于保存我注入到我的服务中的实体。 And I'd like to inject Session object to it using H2K. 我想使用H2K向它注入Session对象。 For this purpose I try do something similar described in following SO posts: 为此我尝试做以下SO帖子中描述的类似内容:

  1. Jersey + HK2 + Grizzly: Proper way to inject EntityManager? Jersey + HK2 + Grizzly:注入EntityManager的正确方法是什么?
  2. Using Jersey 2.0, how do you register a bindable instance per request? 使用Jersey 2.0,如何为每个请求注册一个可绑定实例?
  3. How do I properly configure an EntityManager in a jersey / hk2 application? 如何在jersey / hk2应用程序中正确配置EntityManager?

So I created SFFactory class and register it in ApplicationConfig. 所以我创建了SFFactory类并在ApplicationConfig中注册它。

SFFactory.java SFFactory.java

import org.glassfish.hk2.api.Factory;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;

public class SFFactory implements Factory<Session> {

private SessionFactory factory;

public SFFactory() {
    Configuration configuration = new Configuration();
    configuration.configure("hibernate.cfg.xml");
    StandardServiceRegistryBuilder srBuilder = new StandardServiceRegistryBuilder();
    srBuilder.applySettings(configuration.getProperties());
    factory = configuration.buildSessionFactory(srBuilder.build());
    }

@Override
public Session provide() {
    return factory.openSession();
    }

@Override
public void dispose(Session session) {
    if (session.isOpen()) {
        session.close();
        }
    }
}

ApplicationConfig.java ApplicationConfig.java

import org.alexdzot.phonettesttask.repository.MessageRepository;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.hibernate.Session;

import javax.inject.Singleton;
import javax.ws.rs.ApplicationPath;

@ApplicationPath("/rest/*")
public class ApplicationConfig extends ResourceConfig {

public ApplicationConfig() {
    packages("org.alexdzot.phonettesttask");
    register(new AbstractBinder() {
        @Override
        protected void configure() {
            bindFactory(SFFactory.class).to(Session.class);
            bindFactory(MessageRepositoryFactory.class).to(MessageRepository.class).in(Singleton.class);
            }
        });
    }
}

DefaultMessageRepository DefaultMessageRepository

import org.alexdzot.phonettesttask.model.Message;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.jvnet.hk2.annotations.Service;

import javax.inject.Inject;
import java.util.List;

@Service
public class DefaultMessageRepository implements MessageRepository {

@Inject
private Session session;

public void saveMessage(Message message) {
    Session session = null;
    Transaction tx = null;
    try {
        tx = session.beginTransaction();
        session.save(message);
        tx.commit();
        } catch (Exception e) {
        tx.rollback();
        } finally {
        session.close();
        }
    }

public List<Message> getAllMessages() {
    Session session = null;
    Transaction tx = null;
    List<Message> messages = null;
    try {
        tx = session.beginTransaction();
        messages = session.createCriteria(Message.class).list();

        } catch (Exception e) {
        tx.rollback();
        } finally {
        session.close();
        return messages;
        }
    }
}

But when I run the app and trying to call repository methods I get NullPoinerException. 但是当我运行应用程序并尝试调用存储库方法时,我得到NullPoinerException。 I can't understand what am I doing wrong. 我无法理解我做错了什么。 This is what Tomcat log is saying: 这是Tomcat日志所说的:

*08-Aug-2015 02:42:18.232 SEVERE [http-nio-8080-exec-10] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [org.alexdzot.phonettesttask.config.ApplicationConfig] in context with path [] threw exception [java.lang.NullPointerException] with root cause
 java.lang.NullPointerException
    at org.alexdzot.phonettesttask.repository.DefaultMessageRepository.getAllMessages(DefaultMessageRepository.java:42)
    at org.alexdzot.phonettesttask.service.MessageResource.viewSentMessages(MessageResource.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:205)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
    at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:308)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:291)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1140)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:403)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:386)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:334)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:221)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:617)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1521)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1478)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)*

Any ideas what wrong with my solution and what causes that problem? 任何想法我的解决方案有什么问题以及导致该问题的原因?

Main problem: 主要问题:

[java.lang.NullPointerException]

Look at your repository class 查看您的存储库类

@Service
public class DefaultMessageRepository implements MessageRepository {

    @Inject
    private Session session; <-------------------+
                                                 | // You shadow session
    public void saveMessage(Message message) {   |
        Session session = null;   <--------------+
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
        ...
    }
    ...
}

You are shadowing the injected field session . 您正在注入注入的字段session So you are just using the local session , which is null. 所以你只是使用本地session ,它是null。 So in your method, just get rid of Session session = null; 所以在你的方法中,只需要摆脱Session session = null; . The injection should work fine, and you should be able to just use the session field. 注入应该可以正常工作,您应该只能使用session字段。

Another Problem: 另一个问题:

The same Session is being used for all request. 所有请求都使用相同的Session Since DefaultMessagFactory is a singleton, HK2 asserts that Session should also be a singleton, since it is contained within a singleton. 由于DefaultMessagFactory是一个单例,因此HK2断言Session也应该是一个单例,因为它包含在一个单例中。 So instead of making the SFFactory the default @PerLookup scope, you get a factory that is a singleton, and so only one Session will be created. 因此,不是将SFFactory作为默认的@PerLookup范围,而是获得一个单例的工厂,因此只会创建一个Session This is not the behavior you should want. 这不是您应该要的行为。 You can test this by putting some print statements inside your factory class. 您可以通过在工厂类中放置一些print语句来测试它。

One way to make sure that a new Session is created for each request is to: 确保为每个请求创建新Session一种方法是:

  1. Use javax.inject.Provider<Session> , to lazily load the session, which will allow use to keep it in the scope we configured. 使用javax.inject.Provider<Session>来懒惰地加载会话,这将允许使用它来保持我们配置的范围。

     @Inject private javax.inject.Provider<Session> session; @Override public void saveEvent(Event event) { Session s = session.get(); Transaction tx = s.beginTransaction(); 
  2. Configure the SFFactory in a request scope 在请求范围中配置SFFactory

     @Override protected void configure() { bindFactory(SFFactory.class) .to(Session.class) .in(RequestScoped.class); } 

The other thing to fix is that SessionFactory should be a single instance through the application. 要解决的另一件事是SessionFactory应该是应用程序的单个实例。 For that we can create a single factory for it, and inject it into the SFFactory . 为此,我们可以为它创建一个工厂,并将其注入SFFactory For example 例如

public class SessionFactoryFactory implements Factory<SessionFactory> {

    private final SessionFactory factory;

    public SessionFactoryFactory() {
        Configuration configuration = new Configuration();
        configuration.configure("hibernate.cfg.xml");
        StandardServiceRegistryBuilder srBuilder = new StandardServiceRegistryBuilder();
        srBuilder.applySettings(configuration.getProperties());
        factory = configuration.buildSessionFactory(srBuilder.build());
        System.out.println("--- SessionFactory Created ---");
    }

    @Override
    public SessionFactory provide() {
        System.out.println("--- SessionFactory Provided ---");
        return factory;
    }

    @Override
    public void dispose(SessionFactory factory) {
        factory.close();
    }
}

The in your SFFactory 你的SFFactory

public class SFFactory implements Factory<Session> {

    private final SessionFactory factory;

    @Inject
    public SFFactory(SessionFactory factory) {
        this.factory = factory;
    }

    @Override
    public Session provide() {
        System.out.println("--- Session Created ---");
        return factory.openSession();
    }

    @Override
    public void dispose(Session session) {
        if (session.isOpen()) {
            session.close();
        }
    }
}

You configuration would change to look like 您的配置将更改为外观

@Override
protected void configure() {
    bindFactory(SessionFactoryFactory.class)
            .to(SessionFactory.class)
            .in(Singleton.class);
    bindFactory(SFFactory.class)
            .to(Session.class)
            .in(RequestScoped.class);
}

UPDATE UPDATE

So it seems aside from the shadowing, another main problem was in the MessageRepositoryFactory that the OP didn't show (in github project from comments). 所以除了阴影之外,另一个主要问题是在MessageRepositoryFactory中OP没有显示(在注释的github项目中)。

public class MessageRepositoryFactory implements Factory<MessageRepository> {
    @Override
    public MessageRepository provider() {
        return new DefaultMessageRepository();
    }
    ...
}

When you instantiate the object your self, by default, the framework doesn't handle the injection. 当您实例化自己的对象时,默认情况下,框架不处理注入。 So that's why the injection of the Session didn't happen. 这就是为什么没有发生Session注入的原因。

Instead of using the a Factory like this, you could simply bind like this 你可以像这样绑定,而不是像这样使用Factory

bind(DefaultMessageRepository.class).to(MessageRepository.class).in(..);

This way the framework creates the instance. 这样框架就会创建实例。 The Factory should really only be used where it is not possible to bind the above way, for instance you have some initializations to do. Factory真的应该只使用它不可能按照上述方式进行绑定,比如你有一些初始化的事情。 We could have even not created the Factory for the SessionFactoryFactory and instead did all the session factory configuration elsewhere and just binded the instance. 我们甚至可能没有为SessionFactoryFactory创建Factory ,而是在其他地方完成了所有会话工厂配置,并将实例绑定。 For example 例如

SessionFactory sessionFactory =...
...
bind(sessionFactory).to(SessionFactory.class);

We just bind a SessionFactory singleton. 我们只绑定一个SessionFactory单例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM