【发布时间】:2011-02-15 15:30:14
【问题描述】:
所以在春季之前,我们使用了 HibernateUtil 版本,如果建立了成功的原始 JDBC 连接,则缓存 SessionFactory 实例,否则抛出 SQLException。这使我们能够从由于身份验证或服务器连接问题而“坏”的 SessionFactory 的初始设置中恢复。
我们迁移到 Spring 并以或多或少的经典方式连接了 LocalSessionFactoryBean、C3P0 数据源和注入 SessionFactory 的各种 dao 类。
现在,如果在 Web 应用程序运行时 SQL 服务器似乎未启动,则 Web 应用程序永远不会恢复。对 dao 方法的所有访问都会因为一个空 sessionfactory 被注入而崩溃。 (一旦 sessionfactory 设置好,连接池主要处理 sql server 的 up/down 状态,所以恢复是可能的)
现在,dao 方法默认连接为单例,我们可以将它们更改为原型。不过,我认为这不会解决问题 - 我相信 LocalSessionFactoryBean 现在“卡住”并缓存了空引用(不过,我还没有对此进行测试,我会羞愧地承认)。 这一定是人们关心的问题。
按照下面的建议尝试了代理 -- 失败了
首先,我不得不忽略调用LocalSessionFactory.buildSessionFactory 的建议(坦率地说,从反编译看来这是错误的)——它是不可见的。
相反,我尝试了如下修改版本:
覆盖 newSessionFactory。最后返回代理 SessionFactory 指向下面列出的调用处理程序
这也失败了。
org.hibernate.HibernateException:找不到用于配置的本地数据源 - 必须在 LocalSessionFactoryBean 上设置“数据源”属性
现在,如果 newSessionfactory() 改为简单
return config.buildSessionFactory()(而不是代理)它可以工作,但当然不再表现出所需的代理行为。
public static class HibernateInvocationHandler implements InvocationHandler {
final private Configuration config;
private SessionFactory realSessionFactory;
public HibernateInvocationHandler(Configuration config) {
this.config=config;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (false) proxy.hashCode();
System.out.println("Proxy for SessionFactory called");
synchronized(this) {
if (this.realSessionFactory == null){
SessionFactory sf =null;
try {
System.out.println("Gonna BUILD one or die trying");
sf=this.config.buildSessionFactory();
} catch (RuntimeException e) {
System.out.println(ErrorHandle.exceptionToString(e));
log.error("SessionFactoryProxy",e);
closeSessionFactory(sf);
System.out.println("FAILED to build");
sf=null;
}
if (sf==null) throw new RetainConfigDataAccessException("SessionFactory not available");
this.realSessionFactory=sf;
}
return method.invoke(this.realSessionFactory, args);
}
}
newSessionFactory 中的代理创建是这样的
SessionFactory sfProxy= (SessionFactory) Proxy.newProxyInstance(
SessionFactory.class.getClassLoader(),
new Class[] { SessionFactory.class },
new HibernateInvocationHandler(config));
并且可以返回此代理(失败)或 config.buildSessionFactory() 可以正常工作但不能解决最初的问题。
bozho 提出了另一种方法,使用 getObject()。注意 d) 中的致命缺陷,因为 buildSessionFactory 是不可见的。
a) 如果 this.sessionfactory 为非空,则不需要代理,只需返回 b)如果是,建立一个代理... c) 应该包含 sessionfactory 的私有引用,并且每次调用它时检查它是否为空。如果是这样,您将构建一个新工厂,如果成功分配给私有引用并从现在开始返回它。 d) 现在,说明您将如何从 getObject() 构建该工厂。您的答案应该涉及调用 buildSessionFactory ....但您不能。一个人可以自己创建工厂,但你最终会冒着破坏弹簧的风险(查看 buildSessionFactory 代码)
【问题讨论】:
-
你在启动时得到堆栈跟踪吗?
-
我做到了,如果你愿意,我可以发布它,但如果注入的会话工厂在 dao 中为空,那只不过是你所期望的......
-
我找到了奇怪的数据源错误的原因。Spring 在调用 newSessionFactory 之后非常故意将 buildSessionFactory 中的引用(它是线程本地的)清空。如果不重写课程,想不出办法解决这个问题
-
显示您的
getObject()方法。 -
getObject() 没有条目,因为您无法使用您的方法。简单证明:如果 this.sessionFactory 为空,如何在不重写 buildSessionFactory 的情况下从这里返回 sessionFactory?