当前位置: 首页 > news >正文

发现Hibernate的bug与对Properties的深入认识

下午接着检查王泽佑完成的作业情况,王泽佑的任务是使用Hibernate中的DriverManagerConnectionProvider来获得数据库连接,由于DriverManagerConnectionProvider没有提供构造方法或setter方法来接收配置信息,而是只能调用configure(Properties)方法来设置其配置信息,王泽佑为了能够利用起Spring,他将confiure方法接受的参数Properties对象作为Spring的一个bean对象(这有点过度使用Spring了,他们的理由是好玩和加深对spring的应用),没想到这一用却用出了一个很怪的问题:
下面是spring配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
<bean id="providerProps" class="java.util.Properties">
<constructor-arg>
<props>
<prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop>
<prop key="hibernate.connection.url">jdbc:mysql:///itcast</prop>
<prop key="hibernate.connection.username">root</prop>
<prop key="hibernate.connection.password">1234</prop>
</props>
</constructor-arg>
</bean>
</beans>
上面的配置相当于如下的一段源程序代码:
Properties providerProps = new Properties();
providerProps.setProperty("hibernate.connection.driver_class","com.mysql.jdbc.Driver");
providerProps.setProperty("hibernate.connection.url","jdbc:mysql:///itcast");
providerProps.setProperty("hibernate.connection.username","root");
providerProps.setProperty("hibernate.connection.password","1234");
Properties props = new Properties(providerProps);
即先创建出一个Properties对象,然后将这个Properties对象作为另外一个Properties对象的构造方法的参数去初试化另外那个Properties对象。
源程序中的调用代码片段如下:
DriverManagerConnectionProvider dmcp = new DriverManagerConnectionProvider();
ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
props = (Properties)context.getBean("providerProps");
dmcp.configure(props);
Connection cn = dmcp.getConnection();
....
程序运行时,总是报告连接数据库失败,MySQl报告的失败原因是登陆帐户的密码为空。他们将上面的程序代码改成如下形式:
DriverManagerConnectionProvider dmcp = new DriverManagerConnectionProvider();
ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
props = (Properties)context.getBean("providerProps");
dmcp.configure(props);
//增加了下面这条语句
props.setProperty("hibernate.connection.password",props.getProperty("hibernate.connection.password"));
Connection cn = dmcp.getConnection();
....
再次运行,结果就正常了。修改后的代码仅仅是再次设置了password属性,并且设置值就是从原来的props对象中提取出来的,加不加这条语句,似乎没有什么不同啊?在新增的这条语句前后分别打印出props对象中的内容,看到的结果也完全一样。
DriverManagerConnectionProvider dmcp = new DriverManagerConnectionProvider();
ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
props = (Properties)context.getBean("providerProps");
dmcp.configure(props);
props.list(System.out); //与下面的list显示结果完全一样
//增加了下面这条语句
props.setProperty("hibernate.connection.password",props.getProperty("hibernate.connection.password"));
props.list(System.out); //与上面的list显示结果完全一样
Connection cn = dmcp.getConnection();
....
这个问题实在太怪异了!!!我怎么也想不明白!
于是,我打开JDK帮助文档,仔细阅读Properties类的帮助信息,注意到了如下一段描述:
A property list can contain another property list as its "defaults"; this second property list is searched if the property key is not found in the original property list.
其大概意思是说:一个Properties对象内部可以包含另外一个Properties对象作为其缺省Properties对象,当我们从Properties对象中检索一个属性时,先从这个Properties对象自身中查找,当找不着时,再从它内部包含的另外那个缺省Properties对象中查找。这是我以前不曾注意的一个细节,而我们上面的应用看起来正好与这个细节有些关系,我们也是先构造出一个Properties对象,然后将它设置成了另外一个Properties对象的内部缺省Properties对象,尽管内部的缺省Properties对象中包含了一个名为“hibernate.connection.password”的属性,但它毕竟不是外面的Properties对象自己的属性,我们对外面的Properties对象调用setProperty("hibernate.connection.password",...)方法,导致外面的Properties对象与它内部的缺省Properties对象中都有了一个名为“hibernate.connection.password”的属性,显然,我们调用了setProperty方法后确实改变了外面的Properties对象的内部数据,而不是我们以前认为的没有变化,看来问题可能就出现在这个细节上了。于是,我在eclipse下开始跟踪调试这个程序,跟踪到ConnectionProviderFacatory.getConnectionProperties方法时,看到该方法的代码如下:
public static Properties getConnectionProperties(Properties properties) {

Iterator iter = properties.keySet().iterator();
Properties result = new Properties();
while ( iter.hasNext() ) {
String prop = (String) iter.next();
if ( prop.indexOf(Environment.CONNECTION_PREFIX) > -1 && !SPECIAL_PROPERTIES.contains(prop) ) {
result.setProperty(
prop.substring( Environment.CONNECTION_PREFIX.length()+1 ),
properties.getProperty(prop)
);
}
}
String userName = properties.getProperty(Environment.USER);
if (userName!=null) result.setProperty( "user", userName );
return result;
}
该方法的作用是从一个Properties对象中提取与数据库连接信息相关的属性,并将这些连接属性生成一个新的Properties对象返回,这个新生成的properties对象中确实没有包含“password”属性,问题就出现在这个方法上。结合前面的Properties对象的重新认识,分析上面的代码,猜想是Properties.keySet()方法返回的关键字集合中只包含了它自己的关键字,而没有包含它内部的缺省Properties对象的关键字,为此,我写了如下一段代码来证实自己的猜想:
Properties props1 = new Properties();
props1.setProperty("hibernate.connection.username","root");
props1.setProperty("hibernate.connection.password","1234");
Properties props = new Properties(props1);
props.setProperty("hibernate.connection.username","root");
Enumeration e = props.keys();
while(e.hasMoreElements())
{
System.out.println((String)e.nextElement());
}
运行的结果,果然是只打印出了props.setProperty方法设置的关键字,而没有打印出props1中包含的关键字。至此,我完全确定了问题的原因,看来,Hibernate的开发者可能也没有注意Properties类的这个细节,因而给Hibernate留下了这样的一个Bug。

相关文章:

  • NSNumber、NSData、NSValue的使用场景
  • Blog小技巧之二-让朋友在Blog上也能QQ到自己
  • 高尔夫实际和弹玻璃珠是一回事
  • 兰德酷路泽原来就是陆地巡洋舰啊
  • 市场上咋没有好的关于嵌入式项目管理的书呢?
  • 比较日期精确到日
  • 一个感人的真事
  • 为什么《Visual Basic 2005程序开发与界面设计秘诀》和《Visual C# 2005程序开发与界面设计秘诀》值得买?...
  • 父亲的一个同学
  • appfuse配置文件解读
  • 使CBIntrospect支持在设备上使用
  • Windows Mobile BLOG 问题集锦 2006-9-25
  • 拨一下就断的电话是否是在测电话号码是否可用
  • JBoss JTA的使用心得
  • 确保测试代码不会在发布版上运行
  • 345-反转字符串中的元音字母
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • Babel配置的不完全指南
  • canvas 五子棋游戏
  • CSS选择器——伪元素选择器之处理父元素高度及外边距溢出
  • DOM的那些事
  • iOS | NSProxy
  • JavaScript学习总结——原型
  • Java深入 - 深入理解Java集合
  • JSDuck 与 AngularJS 融合技巧
  • JS实现简单的MVC模式开发小游戏
  • Kibana配置logstash,报表一体化
  • React-redux的原理以及使用
  • 翻译--Thinking in React
  • 关于使用markdown的方法(引自CSDN教程)
  • 计算机常识 - 收藏集 - 掘金
  • 码农张的Bug人生 - 见面之礼
  • 那些被忽略的 JavaScript 数组方法细节
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 【运维趟坑回忆录】vpc迁移 - 吃螃蟹之路
  • Java总结 - String - 这篇请使劲喷我
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • ​虚拟化系列介绍(十)
  • #162 (Div. 2)
  • (1)常见O(n^2)排序算法解析
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (3)llvm ir转换过程
  • (MATLAB)第五章-矩阵运算
  • (poj1.3.2)1791(构造法模拟)
  • (八)Flask之app.route装饰器函数的参数
  • (超详细)语音信号处理之特征提取
  • (附源码)计算机毕业设计ssm本地美食推荐平台
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • (译)计算距离、方位和更多经纬度之间的点
  • (转)编辑寄语:因为爱心,所以美丽
  • ... 是什么 ?... 有什么用处?
  • ./include/caffe/util/cudnn.hpp: In function ‘const char* cudnnGetErrorString(cudnnStatus_t)’: ./incl
  • .NET Core 中的路径问题
  • .NET Standard 的管理策略
  • .NET 指南:抽象化实现的基类