Seasar提供了S2Hibernate来封装hibernate。但是在公司的CIM系统中,我们又自定义了一套自己的S2hibernate规范,封装原有的S2hibernate类库。研究了一下它的实现,做点笔记以防忘记。
(注:关于Seasar框架,请查阅
http://dhongwu.iteye.com/admin/blogs/1949725; 关于S2hibernate,请查阅官方文档
http://s2hibernate.seasar.org/ja/s2hibernate.html。很遗憾暂时只有日文版本。)
CIM中S2hibernate的注入声明是在s2hibernate3.dicon中。
...
<component name="s2SessionFactory" class="jp.co.worksap.common.s2.S2SessionFactoryImpl" >
...
<component class="jp.co.worksap.common.s2.CwfDaoMetaDataFactoryImpl"/>
<component class="jp.co.worksap.common.s2.HistoricalSaveInterceptor"/>
<component name="readOnly" class="org.seasar.hibernate3.interceptor.ReadOnlySessionInterceptor"/>
<component name="interceptor" class="jp.co.worksap.common.s2.S2HibernateDaoInterceptor"/>
<component name="s2H3Customizer" class="org.seasar.framework.container.customizer.AspectCustomizer">
<initMethod name="addInterceptorName">
<arg>"s2hibernate.interceptor"</arg>
</initMethod>
</component>
...
可以看到,最后得到一个s2H3Customizer,再把这个customizer添加到customize.dicon的DaoCustomizer中,随着Seasar框架的启动就把定义的AOP加载到Dao类上。(更多关于Seasar框架启动的细节,请查阅
http://dhongwu.iteye.com/admin/blogs/1949725)。
直接的AOP就是类S2HibernateDaoInterceptor。在S2HibernateDaoInterceptor的invoke函数中,有:
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
if (!MethodUtil.isAbstract(method)) {
return invocation.proceed();
}
Class daoClass = getTargetClass(invocation);
CwfDaoMetaDataImpl dmd = (CwfDaoMetaDataImpl) hibernateDaoMetaDataFactory_.getDaoMetaData(daoClass);
Date historyDate = dmd.getFilterBaseDate(invocation);
if (historyDate != null) {
holder.set(historyDate);
} else {
holder.set(S2SessionFactoryImpl.NULLDATE);
}
try{
HibernateCommand cmd = dmd.getHibernateCommand(method.getName());
Object ret = cmd.execute(invocation.getArguments());
if(!dmd.isSelectMethod(invocation.getMethod().getName())){
if(!s2fac.getSessionNoHist().getFlushMode().equals(FlushMode.MANUAL)){
s2fac.getSessionNoHist().flush();
}
}
return ret;
}finally{
holder.remove();
}
}
首先判断当前调用的函数是否为抽象函数(即没有函数实现的body)。S2hibernate原本的理念就是将Dao中的各种增删查改(以下简称CRUD)函数声明成抽象函数,具体内容通过AOP来实现。 CIM这里也沿用这一理念。接下来获得当前Dao的相关metadata。hibernateDaoMetaDataFactory_通过S2hibernate3.dicon入获得,实际上为CwfDaoMetaDataFactoryImpl类。该getDaoMetaData函数为:
public synchronized HibernateDaoMetaData getDaoMetaData(Class daoClass) {
String key = daoClass.getName();
HibernateDaoMetaData dmd = (HibernateDaoMetaData) daoMetaDataCache_
.get(key);
if (dmd != null) {
return dmd;
}
dmd = new CwfDaoMetaDataImpl(sessionFac, daoClass);
daoMetaDataCache_.put(key, dmd);
return dmd;
}
先查看是否已经定义过该类的metadata,没有的话就去创建。在CwfDaoMetaDataImpl的构造函数中,有:
public CwfDaoMetaDataImpl(S2SessionFactory arg0, Class daoClass) {
super(arg0, daoClass);
sessionFactory=arg0;
daoClass_ = daoClass;
daoBeanDesc_ = BeanDescFactory.getBeanDesc(daoClass_);
Field beanField = daoBeanDesc_.getField(BEAN_KEY);
beanClass_ = (Class) FieldUtil.get(beanField, null);
commandMap.put("listCommand", new CwfCommand(sessionFactory,beanClass_));
commandMap.put(METHOD_SIMPLE_SEARCH, new SimpleSearchCommand(sessionFactory,beanClass_));
commandMap.put(METHOD_GET_COUNT, new GetCountCommand(sessionFactory,beanClass_));
setupHibernateCommand();
}
super(arg0, daoClass)做的就是原先S2Hibernate的本职工作,包括初始化Dao的各种CRUD函数,并添加到commandMap中。然后这里人工添加了3个函数listCommand, simpleSearch, getCount,这个是为了和cim中的自定义组件sstable兼容(关于sstable日后再详细讨论),提供为sstable服务的接口函数。最后setupHibernateCommand()是查看Dao中声明的select函数,查看他们是否声明了一个"baseDate"参数。如果声明了就把相应的sql查询定义为HistoricalHqlQueryCommand。这也是我们要自定义S2hibernate的最根本目的,就是为了添加这一层封装。
多说一句,HistoricalHqlQueryCommand继承自HibernateCommand,这个是S2Hibernate提供的封装SQL行为的函数,通过调用它的execute就可以执行相应的SQL指令。
重新回到S2HibernateDaoInterceptor的invoke函数,在获得Dao的相关metadata后,就可以通过metadata中的commandMap获得当前调用函数名对应的HibernateCommand,调用execute来执行SQL指令。如果当前HibernateCommand是我们上文提到的HistoricalHqlQueryCommand,则它的execute函数如下:
public Object execute(Object[] args) {
Session session = getSession();
Query query = session.createQuery(hqlQuery_);
ArgsMetaData argsMeta = getArgsMeta();
for (int i = 0; i < argsMeta.getArgsCount(); ++i) {
Object value = argsMeta.getValue( args, i);
String name = argsMeta.getArgument(i).getFieldName();
if (value != null) {
if (name.equals("firstResult") == true) {
query.setFirstResult(((Integer) value).intValue());
} else if (name.equals("maxResults") == true) {
query.setMaxResults(((Integer) value).intValue());
} else if (name.equals(CwfDaoMetaDataImpl.HIST_DATE) == true) {
session.enableFilter("history").setParameter("date", (Date)value);
} else {
if( value instanceof List){
query.setParameterList(name, (List)value);
}else{
query.setParameter(name, value);
}
}
}
}
List ret = query.list();
return getReturnObject(getMethod(),ret );
}
重点是这一句
else if (name.equals(CwfDaoMetaDataImpl.HIST_DATE) == true) {
session.enableFilter("history").setParameter("date", (Date)value);
}
HIST_DATE就是我们前文提及的字符串"baseDate",如果Dao的函数ARGS中声明了"baseDate",则此时Hibernate开启名为history的过滤器,并把过滤器condition中的date值设为该Dao函数传入的Date。
多说一句,关于S2HibernateDaoInterceptor的invoke函数中的holder变量,是在s2hibernate3.dicon中注入的一个singleton的HistoryDateHolder对象。虽然是singleton变量,但是它实际上是一个ThreadLocal,所以每个线程有自己独立的Date对象。它的作用主要就是在session管理上。在HibernateCommand中的execute函数中,getSession()调用s2hibernate3.dicon自定义的S2SessionFactoryImpl中的getSession()。该工厂维护了一个双层SessionMap,第一层键值是线程当前的JTA transaction(关于seasar的JTA transaction AOP,请阅读官方文档
http://s2container.seasar.org/2.4/en/tx.html),第二层键值就是每个线程自己维护的Date变量转成形如yyyy/MM/dd后的字符串。
那么实际上对于每一个JTA Transaction, 内部都维护两个session。一个是Date为当天值的session,一个是Date为NULL的session(以下简称NoHist-session)。前者用于针对带有baseDate的查询命令的执行,后者则是针对不含baseDate的查询命令以及其它的增删改命令。再回顾S2HibernateDaoInterceptor的invoke函数,在每一条增删改命令完成以后,都会让相应的NoHist-session flush一遍(似乎这样的效率不高,可以有改进的空间)
分享到:
相关推荐
这份文档可能会介绍CMPI的结构、用法以及如何使用它来编写自定义管理提供者。 5. **WBEM Client**:WBEM客户端是与CIM服务器交互的工具。这部分内容可能涵盖如何使用各种语言(如Python、Java等)编写或使用现有...
电力系统通用信息模型(CIM,Common Information Model)是一种国际标准,主要由国际电工委员会(IEC)制定,包括了IEC61968和IEC61970两个部分。CIM模型旨在为电力行业的信息化提供一个统一的数据交换和共享框架,...
1. **cim_provider_development_guide.pdf**:这份文档应该是VMware CIM Provider的开发指南,详细阐述了如何设计、实现和测试自定义的CIM Provider。它可能涵盖了CIM模型的创建,如何使用CIM Programming ...
CIM模型,全称为Common Information Model,是一种广泛用于电力系统和其他工业领域的标准化数据模型。它定义了一套统一的、抽象的、独立于具体系统的概念,用于描述和交换各种设备、网络和系统的运行状态、配置信息...
城市信息模型(CIM)基础平台技术导则.pdf城市信息模型(CIM)基础平台技术导则.pdf城市信息模型(CIM)基础平台技术导则.pdf城市信息模型(CIM)基础平台技术导则.pdf城市信息模型(CIM)基础平台技术导则.pdf城市信息模型...
此外,CIM还可能提供了诸如标签分类、过滤器、自定义视图等功能,帮助用户更好地组织和筛选任务。 在“cim-master”这个压缩包文件中,我们通常会发现项目的源代码、文档、示例以及安装和运行指南等资源。源代码是...
**IEC61970/61968 CIM模型CIM14V13详解** IEC61970与IEC61968是国际电工委员会(International Electrotechnical Commission,简称IEC)制定的一系列标准,它们在电力系统信息化领域扮演着至关重要的角色。这两个...
CIM(Computer-aided Information Modeling)是一种用于构建和管理复杂系统信息模型的技术,它在IT行业中被广泛应用,尤其是在系统集成、仿真分析、建筑信息模型(BIM)等领域。CIM开发环境是专为开发者设计的工作...
**CIM 模型详解** CIM,全称为Common Information Model,中文译为公共信息模型,是国际电工委员会(IEC)制定的一套标准,旨在为电力系统及其他相关的能源领域提供一个统一的数据交换和信息共享模型。CIM 模型分为...
QAD CIM Load 工具是专门设计用于将Excel电子表格中的数据高效地导入到QAD(Quality Application Dynamics)企业级管理系统的工具。QAD是一种先进的制造业解决方案,它提供了全面的企业资源规划(ERP)功能,帮助...
**CIM 模型概述** CIM,全称为Common Information Model,中文名为公共信息模型,是由国际电工委员会(IEC)制定的一种标准,用于描述和交换电力系统及相关系统的数据。CIM模型的主要目的是促进不同系统之间的信息...
对于CIM模型中自定义的枚举类型,如FuelType或BusbarConfiguration,它们会被映射为整型(IntegCIM),将枚举值转换为整数值。对于CIM自定义的其他数据类型,如Voltage或TapStep,它们通常包含值和单位,值可以用基础...
ITRI CIM Emulator模拟器是一款专门用于SECS(半导体设备通信标准)通信的服务端工具,旨在帮助开发者在没有实际硬件设备的情况下进行测试和调试。SECS是半导体制造领域中的一个关键通信协议,它允许设备控制软件与...
《IEC61970/61968 CIM模型详解及应用》 IEC61970和IEC61968是国际电工委员会(IEC)制定的两个重要标准,它们共同构成了电力系统信息化模型的基石。这两个标准在电力行业的信息化建设中扮演着至关重要的角色,为...
"数字孪生城市信息模型CIM平台建设技术方案" CIM(City Information Modeling,城市信息模型)是一种将城市空间信息和城市动态信息有机结合的技术,通过微观建筑信息模型(BIM)、宏观地理空间数据(GSD)和物联网...
基于三菱Q系列PLC的CIM系统的设计及实现主要涵盖了工业自动化领域中,如何利用三菱Q系列可编程逻辑控制器(PLC)来搭建计算机集成制造(CIM)系统。这一系统的设计和实现能够让生产流程更加统一化管理,提高设备效率...
iec61970cim17v23_iec61968cim13v11_iec62325cim03v14.zip属于调度和配网的较新标准,解压后用Enterprise Architect打开,方便电力系统建模,了解老外的一首资料。
"CIM的本体表达"这一主题深入探讨了在信息集成领域中,如何利用本体(Ontology)来构建和表示通用信息模型(Common Information Model,简称CIM)。CIM是一种标准化的方法,用于描述和共享不同系统、平台或应用中的...
**CIM模型全集——揭示电力系统的标准化建模** 标题中的"CIM模型全集.rar"是一个包含多个CIM(Common Information Model,公共信息模型)模型的压缩文件,它基于国际电工委员会(IEC)发布的标准IEC61970。CIM是一...
CIM是一种广泛使用的标准,它定义了一种通用的方式来描述和交换有关IT资源的信息,如硬件、软件、网络设备等。POST是HTTP协议中的一个方法,用于向服务器发送数据,常用于提交表单数据或更新资源。 1. **什么是CIM...