簡體   English   中英

使用HK2向Jersey返回hibernate會話

[英]Injecting hibernate session to Jersey using HK2

我正在開發小應用程序並且在DI方面存在一些問題。

我有一個存儲庫類用於保存我注入到我的服務中的實體。 我想使用H2K向它注入Session對象。 為此我嘗試做以下SO帖子中描述的類似內容:

  1. Jersey + HK2 + Grizzly:注入EntityManager的正確方法是什么?
  2. 使用Jersey 2.0,如何為每個請求注冊一個可綁定實例?
  3. 如何在jersey / hk2應用程序中正確配置EntityManager?

所以我創建了SFFactory類並在ApplicationConfig中注冊它。

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

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

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

但是當我運行應用程序並嘗試調用存儲庫方法時,我得到NullPoinerException。 我無法理解我做錯了什么。 這是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)*

任何想法我的解決方案有什么問題以及導致該問題的原因?

主要問題:

[java.lang.NullPointerException]

查看您的存儲庫類

@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();
        ...
    }
    ...
}

您正在注入注入的字段session 所以你只是使用本地session ,它是null。 所以在你的方法中,只需要擺脫Session session = null; 注入應該可以正常工作,您應該只能使用session字段。

另一個問題:

所有請求都使用相同的Session 由於DefaultMessagFactory是一個單例,因此HK2斷言Session也應該是一個單例,因為它包含在一個單例中。 因此,不是將SFFactory作為默認的@PerLookup范圍,而是獲得一個單例的工廠,因此只會創建一個Session 這不是您應該要的行為。 您可以通過在工廠類中放置一些print語句來測試它。

確保為每個請求創建新Session一種方法是:

  1. 使用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. 在請求范圍中配置SFFactory

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

要解決的另一件事是SessionFactory應該是應用程序的單個實例。 為此,我們可以為它創建一個工廠,並將其注入SFFactory 例如

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

你的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();
        }
    }
}

您的配置將更改為外觀

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

UPDATE

所以除了陰影之外,另一個主要問題是在MessageRepositoryFactory中OP沒有顯示(在注釋的github項目中)。

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

當您實例化自己的對象時,默認情況下,框架不處理注入。 這就是為什么沒有發生Session注入的原因。

你可以像這樣綁定,而不是像這樣使用Factory

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

這樣框架就會創建實例。 Factory真的應該只使用它不可能按照上述方式進行綁定,比如你有一些初始化的事情。 我們甚至可能沒有為SessionFactoryFactory創建Factory ,而是在其他地方完成了所有會話工廠配置,並將實例綁定。 例如

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

我們只綁定一個SessionFactory單例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM