背景:项目中需要对业务中的某些HQL语句做一些预处理,因此必须在分析HQL语句的过程中能够根据Java的反射机制动态获知某些实体类的信息。例如:from Person where name like '%张%',我们必须设法根据Person能够加载它对应的实体类。
想来想去还是在将*.hbm.xml这些配置文件交给spring的LocalSessionFactoryBean处理之前最好我们自己的系统能先处理一下,以便缓存某些配置信息以备需要的时候能够方便的取到。
首先要解决的一点就是spring中配置SessionFactory的Bean不能像下面这样定义了:
<bean id="unieapSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource"> <ref bean="unieapDatasource" /> </property> <property name="mappingResources"> <list> <value>com/neuqsoft/czgs/common/entity/Demo.hbm.xml</value> <value>com/neuqsoft/czgs/common/entity/PERSON.hbm.xml</value> …… </list> </property> </bean>
原因在于如果这样定义的话我们的系统无法截取实体配置文件这些资源文件,为了解决这个问题我很自然地想到了spring中的FactoryBean这个接口,于是下面的类应运而生了。
public class HibernateMappingResourceFactoryBean implements FactoryBean {
private List _list;
public Object getObject() throws Exception {
return _list;
}
public Class getObjectType() {
return List.class;
}
public boolean isSingleton() {
return true;
}
public void set_list(List _list) {
this._list = _list;
EntityManager.getInstance().registerAll(_list);
}
}
它的作用有两个:一是用于配置一个List的实例bean,二是将List中的所有资源文件注册到EntityManager中,有关EntityManager我们稍后会提到它的作用。
需要指出的是,使用spring可以很方便地向某个Bean中注入List类型的属性(通过list元素),但却不能简单地配置一个类型为List的Bean,所以我们这里利用FactoryBean接口以及list元素来达到可以配置一个List类型的Bean的目的。
HibernateMappingResourceFactoryBean以及LocalSessionFactoryBean在spring中的配置片段如下。
<bean id="bizHibernateMappingResource" class="HibernateMappingResourceFactoryBean"> <property name="_list"> <list> <value>com/neuqsoft/czgs/common/entity/Demo.hbm.xml</value> <value>com/neuqsoft/czgs/common/entity/PERSON.hbm.xml</value> …… </list> </property> </bean> <bean id="unieapSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource"> <ref bean="unieapDatasource" /> </property> <property name="mappingResources" ref="bizHibernateMappingResource"/> </bean>
这样以来既不妨碍spring中的LocalSessionFactoryBean加载Hibernate的配置文件又不妨碍我们自己的系统加载这些配置文件,而且配置也较为简单,可谓一举两得。
但是接下来在解析XML的过程中我却又遇到了麻烦,总是报告java.net.ConnectException: Connection timed out: connect 错误,可以肯定这是由于网络连接引起的,上班时间我们公司不能上外网,问题是解析XML文件究竟和网络连接有什么关系呢?
要找出错误的原因还得从源代码查起,下面是EntityManager类中registerAll方法的源代码(修改前)。
public void registerAll(List resourceList){
if(resourceList!=null){
try{
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(false);
factory.setValidating(false);
DocumentBuilder builder=factory.newDocumentBuilder();
for(int i=0;i<resourceList.size();i++){
String resourcePath=(String)resourceList.get(i);
try{
URL url=getResource(resourcePath);
File is=new File(url.getFile());
org.w3c.dom.Document w3cDocument=builder.parse(is);
DOMReader reader=new DOMReader();
Document document=reader.read(w3cDocument);
parseDocument(document);
}catch(Exception e){
throw new RuntimeException("Error:"+resourcePath,e);
}
}
}catch(ParserConfigurationException e){
e.printStackTrace();
} catch (FactoryConfigurationError e) {
e.printStackTrace();
}
}
}
错误是从org.w3c.dom.Document w3cDocument=builder.parse(is);这一行开始的,无疑是解析XML的错误了,为了彻底弄清楚错误的原因,我打开一个实体配置文件(Demo.hbm.xml)仔细研究了一下,发现在文档类型定义中确实有URL:
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
于是我猜想会不会是XML解析器在解析XML的时候发现文档类型定义中的systemId是URL,那么就会尝试建立连接以便通过连接读取DTD的内容呢,如果是这样的话,那么等下班后能够上网了,这个问题就应该消失,巧的是正在这时下班的时间到了,于是我开始重新启动服务器,一切尽在意料之中,错误消失了。
现在的问题是上班的时间怎么办,有没有一种机制可以改变XML解析器分析文档类型定义时默认的行为呢,答案是肯定的,那就是通过定制实体解析器来改变其默认的行为。我首先把Hibernate的文档类型定义hibernate-mapping-3.0.dtd这个文件拷贝到我们的工程的类路径中,接下来将EntityManager中的registerAll方法改成下面这样,上班的时候启动服务器也不会报网络连接异常这样可恶的错误了。
public void registerAll(List resourceList){
if(resourceList!=null){
try{
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(false);
factory.setValidating(false);
DocumentBuilder builder=factory.newDocumentBuilder();
builder.setEntityResolver(new EntityResolver(){
public InputSource resolveEntity(String publicID, String systemID)
throws SAXException, IOException {
InputStream is=getClass().getResourceAsStream("hibernate-mapping-3.0.dtd");
return new InputSource(is);
}
});
for(int i=0;i<resourceList.size();i++){
String resourcePath=(String)resourceList.get(i);
try{
URL url=getResource(resourcePath);
File is=new File(url.getFile());
org.w3c.dom.Document w3cDocument=builder.parse(is);
DOMReader reader=new DOMReader();
Document document=reader.read(w3cDocument);
parseDocument(document);
}catch(Exception e){
throw new RuntimeException("Error:"+resourcePath,e);
}
}
}catch(ParserConfigurationException e){
e.printStackTrace();
} catch (FactoryConfigurationError e) {
e.printStackTrace();
}
}
}
The article is end.