[英]Injecting hibernate session to Jersey using HK2
我正在開發小應用程序並且在DI方面存在一些問題。
我有一個存儲庫類用於保存我注入到我的服務中的實體。 我想使用H2K向它注入Session對象。 為此我嘗試做以下SO帖子中描述的類似內容:
所以我創建了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
一種方法是:
使用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();
在請求范圍中配置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);
}
所以除了陰影之外,另一個主要問題是在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.