如何去除代码味道:getSessionFactory,只在必要时创建SessionFactory
How to remove code smell: getSessionFactory, create SessionFactory only when necessary
我使用休眠来处理与数据库的连接。我有几个连接到不同模式的会话工厂。
在启动时构建所有 SessionFactory 至少需要 60 秒。所以我只在必要时才构建它们:
public class HibernateUtil {
private static SessionFactory factory_db1;
private static SessionFactory factory_db2;
//...
public enum DbSchema {
db1, db2 //...
}
private createSessionFactory(Configuration conf){
//...
}
public static SessionFactory getFactory(DbSchema dbSchema) {
try {
switch (dbSchema) {
case db1:
if (factory_db1== null){
Configuration conf = new Configuration().configure(HIBERNATE_CFG_DB1);
factory_db1= createSessionFactory(conf);
}
return factory_db1;
case db2:
if (factory_db2 == null){
Configuration conf = new Configuration().configure(HIBERNATE_CFGXML_DB2);
factory_ooarchive = createSessionFactory(conf);
}
return factory_ooarchive;
//... more factories created
default:
assert false : "Switch default should not be reachable.";
return null;
}
} catch (Throwable ex) {
log.error("Failed to initialize SessionFactory." + ex);
throw new ExceptionInInitializerError(ex);
}
}
现在当我访问这个工厂时:
Session session = HibernateUtil.getFactory(db1).openSession();
// **Compiler warning: method invocation may produce java.lang.nullpointerexception**
只能通过 getFactory() 方法获取工厂,因此我认为 NPE 永远不可能。我知道问题出在工厂实例变量的静态关键字上,并且构造函数中没有初始化。我不想要这个 "always-on" 初始化!它应该只在需要时至少初始化一次。
我阅读了一些设计模式和代码质量方面的书籍,但我很难将所学付诸实践。我想我创造了一种代码味道。我该如何修复这个设计?请解释我的错误以及为什么我的选择有问题。
我不确定编译器警告(可能由 IDE 而不是 javac
发出)与 getFactory()
的 static
修饰符有关。
实际上 getFactory()
实现声明了一个 switch
语句,该语句有一个 default
案例 returns null
:
default:
assert false : "Switch default should not be reachable.";
return null;
所以 getFactory()
可能确实 return null
如果传递的参数不允许在前面的情况之一中输入。
但我认为 getFactory()
的主要问题是它缺乏线程安全性。实际上,如果多个线程同时访问它,您可能会创建多个会话并可能生成不一致的状态。
作为替代方案,要按需创建会话,您可以使用特定风格的单例模式:the initialization-on-demand holder idiom :
In software engineering, the initialization-on-demand holder (design
pattern) idiom is a lazy-loaded singleton. In all versions of Java,
the idiom enables a safe, highly concurrent lazy initialization with
good performance.
我使用休眠来处理与数据库的连接。我有几个连接到不同模式的会话工厂。
在启动时构建所有 SessionFactory 至少需要 60 秒。所以我只在必要时才构建它们:
public class HibernateUtil {
private static SessionFactory factory_db1;
private static SessionFactory factory_db2;
//...
public enum DbSchema {
db1, db2 //...
}
private createSessionFactory(Configuration conf){
//...
}
public static SessionFactory getFactory(DbSchema dbSchema) {
try {
switch (dbSchema) {
case db1:
if (factory_db1== null){
Configuration conf = new Configuration().configure(HIBERNATE_CFG_DB1);
factory_db1= createSessionFactory(conf);
}
return factory_db1;
case db2:
if (factory_db2 == null){
Configuration conf = new Configuration().configure(HIBERNATE_CFGXML_DB2);
factory_ooarchive = createSessionFactory(conf);
}
return factory_ooarchive;
//... more factories created
default:
assert false : "Switch default should not be reachable.";
return null;
}
} catch (Throwable ex) {
log.error("Failed to initialize SessionFactory." + ex);
throw new ExceptionInInitializerError(ex);
}
}
现在当我访问这个工厂时:
Session session = HibernateUtil.getFactory(db1).openSession();
// **Compiler warning: method invocation may produce java.lang.nullpointerexception**
只能通过 getFactory() 方法获取工厂,因此我认为 NPE 永远不可能。我知道问题出在工厂实例变量的静态关键字上,并且构造函数中没有初始化。我不想要这个 "always-on" 初始化!它应该只在需要时至少初始化一次。
我阅读了一些设计模式和代码质量方面的书籍,但我很难将所学付诸实践。我想我创造了一种代码味道。我该如何修复这个设计?请解释我的错误以及为什么我的选择有问题。
我不确定编译器警告(可能由 IDE 而不是 javac
发出)与 getFactory()
的 static
修饰符有关。
实际上 getFactory()
实现声明了一个 switch
语句,该语句有一个 default
案例 returns null
:
default:
assert false : "Switch default should not be reachable.";
return null;
所以 getFactory()
可能确实 return null
如果传递的参数不允许在前面的情况之一中输入。
但我认为 getFactory()
的主要问题是它缺乏线程安全性。实际上,如果多个线程同时访问它,您可能会创建多个会话并可能生成不一致的状态。
作为替代方案,要按需创建会话,您可以使用特定风格的单例模式:the initialization-on-demand holder idiom :
In software engineering, the initialization-on-demand holder (design pattern) idiom is a lazy-loaded singleton. In all versions of Java, the idiom enables a safe, highly concurrent lazy initialization with good performance.