首    页 建模架构 设计模式 培训咨询 jdon框架 论坛

上一级 首页 下一级


Jdon框架高级使用

从前面章节已经清楚:Jdon框架快速开发主要体现在CRUD和批量查询这两个基本功能上,大量数据库系统基本都由这两个基本功能组成。

Jdon框架的这两个功能横跨J2EE多个层次,主要简化工作体现在表现层,这一层主要是简化了Struts一些烦琐的配置和代码过程。

在这一章,主要介绍在深入使用Jdon框架中碰到的一些问题和解决方案,从而达到更加灵活地使用Jdon框架。

Struts+Jdon表现层组件编程

通常,一个Jsp页面可能显示很多各种内容,涉及到肯定不只一个数据库,也就是多个组件的结合,但是Struts配置文件中一般又是一个Action一个ActionForm一个Jsp,他们之间的关系是1:1:1

如何在这样一个体系下实现灵活的一个页面多个组件编程模式呢?

首先,要区分两种情况:一个Action一个ActionForm,一个ActionForm中装载多个组件;一个Action一个ActionForm一个组件;

一个ActionForm多个组件

第一种情况:如果同一个Action就可以对付这些组件,那么在这种情况下又有两个办法:

1.将这多个组件装入一个ActionForm中,如使用MapForm等机制;

一般多个组件在同一个Jsp页面显示情况比较经常发生在批量查询中,对于批量查询,因为Jdon框架规定必须有一个ModelListForm,在ModelListForm中最多只能装载一个Model,如果你有多个组件,也就是多个Model,如何装入ModelListForm呢?

可以使用Jdon框架的com.jdon.controller.model.DynamicModel装载多个Model,其实DynamicModel中有一个Map,然后将DynamicModel装入ModelListFormoneModel中。

Struts对于Map可以直接访问,以DynamicModel为例子:

Action中代码:

    DynamicModel dynamicModel = new DynamicModel();

    Test test = new Test();

    test.setName("ooooooooooooooooooooo");

    //装入Test组件,可以装入更多其他组件。”test”Test实例的key

    dynamicModel.put("test", test);

    modelListForm.setOneModel(dynamicModel);

Jsp中显示代码:

<bean:define id="dynamicModel" name=" modelListForm " property="oneModel"/>

<bean:define id="test" name="dynamicModel" property="value(test)"/>

<bean:write name="test" property="name"/>

其中value(test)是这样解释:valuedynamicModel方法getValue方法的简写,test是之间保存Test实例的key值,然后从test对象中取出其属性name的值。

 

2.手工将多个组件装入request/sessionscope中,然后根据其名称在jsp中获得。这种方式比较传统和原始,类似Jsp编程方式,学习难度小,方便。

一个ActionForm一个组件

前面情况,多个组件装载在一个ActionForm中,隐含的意思实际是:所有功能都可以在一个Action中实现,在这个Action中,我们将多个组件装入当前对应的ActionForm,但是,如果这些组件必须有预先由不同的Action来处理,每个组件必须经过Action -->ActionForm流程,那么在这种情况下也有两种办法:

1. 使用Tiles, 不同流程输出到同一个页面的不同区域。是一种并行处理方式。

2. 对多个流程首尾相连,第一Action forward结果是第二个Action,最后输出一个Jsp,在这个jsp中就可以使用前面多个流程的多个ActionForm了,这属于串行方式。

例如,当前页面是使用Jdon框架的批量查询实现,Jdon框架的批量查询一定是一个ModelListAction对应一个ModelListForm,这是一个比较固定的流程了。如果在当前这个批量查询页面中还要显示其他Model的批量查询,也就是两个或多个Model的批量查询在同一个Jsp页面中显示,在这种情况有上述两个方案选择。

第一个方案前提是你必须使用Struts+Tiles架构,使用Tiles可以将页面划分成任意块,这样多个批量查询在页面任何位置以任何方式组合显示,完全灵活,缺点是多加入Tiles概念。

在一般小型应用中,我们只需采取第二方案,将多个批量查询的Actionstruts-config.xml配置进行首尾连接既可,这种方式类似是按照Jsp页面先上后下的顺序串联的。

例如:threadPrevNexListFormmessageListForm是两个批量查询的ModelListForm,那么struts-config.xml配置如下:

    <action path="/message/messageList"

type="com.jdon.jivejdon.presentation.action.ThreadPrevNexListAction"

      name="threadPrevNexListForm" scope="request"

      validate="false">

      <forward name="success" path="/message/messageListBody.shtml"/>

    </action>

 

          <action path="/message/messageListBody"

type="com.jdon.jivejdon.presentation.action.MessageListAction"

      name="messageListForm" scope="request"

      validate="false">

      <forward name="success" path="/message/messageList.jsp"/>

    </action>

这样,当url调用/message/messageList.shtml时,最后导出/message/messageList.jsp这个页面,在messageList.jsp中可以访问threadPrevNexListFormmessageListForm两个ActionForm了。

上述组件构造方法可应用在复杂的Master-Details编程中,将Master看成一个对象ModelDetails是一系列子Model的集合。

 

内嵌对象(Embedded Object)缓存设计

请看下面这段代码:

 

public class Category extends Model{

String Id;

Product product;   //内嵌包含了一个Product对象

}

Category这个Model内嵌了Product这个Model,属于一种关联关系。

目前Jdon框架提供的缺省缓存是扁平式,不是嵌入式或树形的(当然也可以使用JbossCache等树形缓存替代),因此,一个ModelB对象(如Product)被嵌入到另外一个ModelA对象(如Category)中,那么这个ModelB对象随着ModelA对象被Jdon框架一起缓存。

假设实现ModelA已经在缓存中,如果客户端从缓存直接获取ModelB,缓存由于不知道ModelB被缓存到ModelA中(EJB3实体Bean中是通过Annotation标注字段),那么缓存可能告知客户端没有ModelB缓存。那么有可能客户端会再向缓存中加一个新的ModelB,这样同一个ID可能有两份ModelB,当客户端直接调用的ModelB进行其中字段更新,那么包含在ModelA中的ModelB可能未被更新(因为ModelA没有字段更新)。

有两种解决方案:

第一.最直接方式,通过手工清除ModelA缓存方式来更新,或者耐心等待ModelA缓存自动更新。手工清除缓存见下章。

注意:下面这种做法将也会导致不一致现象发生:

DAO层读取数据库。生成ModelA时,直接读取数据库将ModelB充填。

 

第二.在进行ModelAModelB的相关操作服务设计时,就要注意保证这两种情况下ModelB指向的都是同一个。如下图:

为达到这个目的,只要在Service层和Dao层之间加一个缓存Decorator,服务层向Dao层调用的任何Model对象都首先经过缓存检查,缓存中保存的ModelA中的ModelB是一个只有ModelB主键的空对象,在服务层getModelA方法中,再对ModelAModelB进行充实,填满,根据ModelA中的ModelB的主键,首先再到缓存查询,如果有,则将缓存中ModelB充填到ModelAModelB中,这样上图目的就可以实现了。

相关实现可参考JiveJdon 3.0的代码,Forum/ForumMessage都属于这种情况。

 

 

 

Model缓存使用

Jdon框架通过两种方式使用Model缓存:

CRUD框架内部使用,如果你使用Jdon框架提供的CRUD功能,那么其已经内置Model缓存,而且会即时清除缓存。

通过CacheInterceptor缓存拦截器,如果你不使用Jdon框架的CRUD功能,缓存拦截器功能将激活,在向Service获取Model之前,首先查询当前缓存器中是否存在该Model,如果有从缓存中获取。当你的Model中数值更改后,必须注意自己需要手工清除该Model缓存,清除方法如下介绍。

 

Jdon框架除了提供单个Model缓存外,还在持久层Dao层提供了查询条件的缓存,例如如果你是根据某个字段按照如何排列等条件进行查询,这个查询条件将被缓存,这样,下次如果有相同查询条件,该查询条件将被提出,与其相关的满足查询条件一些结果(如符合条件总数等)也将被从缓存中提出,节省翻阅数据库的性能开销。

手工访问缓存

在一般情况下,前台表现层通过getService方法访问服务层一个服务,然后通过该服务Service获得一个Model,这种情况Jdon框架的缓存拦截器将自动首先从缓存读取。

但是,有时我们在服务层编码时,需要获得一个Model,在这种情况下,Jdon框架的缓存拦截器就不起作用,这时可能我们需要手工访问缓存。

因为所有服务类POJO都属于Jdon框架的容器内部组件,这实际是在容器内访问容器组件的问题。

使用com.jdon.container.finder. ContainerCallback,同时,该服务POJO类以ContainerCallback作为构造参数,当该POJO服务类注册到容器中时,容器的Ioc特性将会找到事先以及注册的ContainerCallback类。

通过ContainerCallback获得ContainerWrapper容器实例,然后通过ContainerWrapper下面方法:

public Object lookup(String name);

从容器中获得container.xml中注册的组件实例,这种方法每次调用获得的是同一个实例,相当于单例方式获得。

ModelManager modelManager =

(ModelManager)containerWrapper. lookup (“modelManager”);

其中“modelManager”字符串名称是从Jdon框架的jdonFramework.jar包中META-INFcontainer.xml中查询获知的。

获得ModelManager后,我们基本可以访问Model有关的功能安排,如ModelManagergetCache方法。

下节的手工清除缓存中,我们是通过WebAppUtil.getComponentInstance获得ModelManager实例,这是一种从容器外获得容器组件的方式,本节介绍从容器内获得容器组件的方式,这两种方式可根据我们实际需要灵活使用,关键是弄清除你需要在哪里触发组件调用?

 

手工清除缓存

注意:手工清除缓存不是必要的,因为缓存中对象是有存在周期的,这在Cache.xml中设置的,过一段时间缓存将自动清除那些超过配置时间不用的对象,这样你修改的数据将被从数据库重新加载。如果你等不及这些内在变化,可以手工处理:

有两种情况需要手工清除缓存,首先,在持久层的Dao类中,总是需要手工清除查询条件的缓存,只要在相应的增删改方法中调用PageIteratorSolverclearCache方法既可。

如果你不实行这种缓存清除,那么你更改一个Model数据或新增一个新的Model数据,你在批量查询时,将看不到任何变化:Model数据没有被修改;新的Model没有出现在查询页面中。

其次,单个Model缓存在不使用Jdon框架的CRUD功能下也必须手工清除,如果你使用1.2.3以后版本,可以调用com.jdon.strutsutil.util. ModelUtil类的clearModelCache方法,该方法一般是Action中调用后台增删改服务之前被激活调用:

注意,手工清除Model缓存代码关键是:

modelManager.removeCache(keyValue);

keyValueModel的主键值,例如User的主键userId值是”2356”,那么keyValue就是”2356”。简化代码如下:

//获得ModelManager实例

ModelManager modelManager = (ModelManager)
WebAppUtil.getComponentInstance(ComponentKeys.MODEL_MANAGER, request);

modelManager.removeCache(keyValue);

 

上面代码是在容器外访问获得ModelManager,使用上节容器内访问组件方式也可以获得ModelManager;前者适合在表现层使用;后者适合在服务层使用。

最后,介绍一下清除全部缓存的方式,调用ModelManagerclearCache方法(Jdon框架1.3版本),这样实际上将整个Jdon框架缓存全部清零。

前面两种清除缓存方式前提是首先获得ModelManager,特别是服务层需要清除缓存时,需要以容器内访问组件方式获得ModelManager,这只适合POJOService构成的服务层,如果我们使用EJBSession Bean作为服务层实现,这时当前版本的Jdon框架容器不会在EJB容器中加载,因此,在Session Bean中无法访问到容器,无法获得ModelManager了,在这种情况下,可以通过设置ModelsetModified属性为True,表示该Model已经修改更新,这样当表现层获取该Model时,Jdon框架缓存拦截器拦截时,发现该Model已经被修改,也就不会从缓存中获取。

Model还有另外一个方法setCacheble,当设置为false时,该Model将不会被保存到缓存中。如果你不希望某个Model被框架自动存入缓存,那么使用此功能。

 

常用API说明

Jdon框架提供了丰富的案例源码,一般简单情况下可以通过参考案例源码的用法明白一些用法,但是在深入使用过程中,需要熟悉一些API的用法,上节缓存的使用实际已经涉及了Jdon框架的API使用。

JdbcTemp API

com.jdon.model.query.JdbcTemp提供了一个简单的持久层解决方案,通过JdbcTemp(JDBC模板)可以完成增///查等SQL语句操作。

一般我们会选择Hibernate/Ibatis等作为持久层架构,但是这些框架需要另外学习和复杂的配置,对非常轻量的简单应用,直接使用JdbcTemp可节省大量时间,随着项目复杂和成熟,还是可以使用这些专门框架来替代JdbTemp的。

JdbcTemp主要帮助开发者省却复杂的数据库JDBC操作语句(如打开/关闭数据库连接等),只要告诉它你要操作的SQL语句和参数数值,通过如下一句:

jdbcTemp. operate (queryParams, sql);

其中queryParams参数是一个List集合类型,里面装载的是参数集合,是有一定顺序的;sql是一个字符串类型的SQL语句,如:

       String sql = "INSERT INTO testuser (userId , name)  VALUES (?, ?)";

       List queryParams = new ArrayList();

       queryParams.add(userTest.getUserId());

       queryParams.add(userTest.getName());

       jdbcTemp.operate(queryParams,sql);

queryParams中装载的是sql语句中两个问号参数,顺序也是和问号的顺序对应的,第一个问号是对应testuseruserId字段;第二个问号对应testusername字段。

数据库的INSERT UPDATE DELETE操作都是可以通过operate方法实现。

JdbcTemp也提供多种查询操作语句:queryMultiObjectquerySingleObject方法,例如:

jdbcTemp. queryMultiObject (queryParams, sql);

jdbcTemp. querySingleObject (queryParams, sql);

querySingleObject是查询返回一个对象,适合如下SQL语句:

select name from testuser where userId = ?

select count(1) from testuser where userId = ?

返回的是数据表一个字段,querySingleObject则返回的相应类型,如字符串等,整数型则是整数型对象,如int则返回的是Integerlong则返回的是Long

queryMultiObject是最常用的查询方法,与querySingleObject返回单个字段相反,它返回的是多个字段,当然也可能是多行多个字段。类如SQL语句:

select userId, name from testuser where userId = ?

这个返回的两个字段,而且结果一般是一行,就是一条记录,但是下面SQL语句:

select userId, name from testuser where name = ?

可能返回多行符合查询条件的记录,每行记录包含两个字段。

这两种形式都是可以采取queryMultiObject调用,queryMultiObject返回的是一个List结果,在这个List结果中按照查询顺序装载着查询结果,这个List中每个元素是一个Map对象,Mapkey是数据表字段名称,Mapvalue是数据表字段名对应的查询出来的值。

因此,从queryMultiObject结果中取值的写法如下:

List list = pageIteratorSolverOfUser.queryMultiObject(queryParams, GET_FIELD);

Iterator iter = list.iterator();

if (iter.hasNext()) { //如果有多行记录,这里是while

       Map map = (Map) iter.next();

       ret = new UserTest();

       ret.setName((String) map.get("name"));

       ret.setUserId((String) map.get("userId"));

}

List结果中装载内容结构如下:

服务接口

目前在Jdon框架1.3版本中,提供三种接口让你的应用服务Service类继承:

com.jdon.controller.pool.Poolable   //推荐使用,使用对象池提高你的服务实例运行性能

com.jdon.controller.service.Stateful  //有状态,当你需要处理有状态数据使用,如购物车

com.jdon.container.visitor.data.SessionContextAcceptable //需要获得容器的登陆信息,见下节。

上述三种接口选择根据你自己要求,也可以都不选择,这样,你的服务实例将每次调用是一个新的实例。SessionContextAcceptable可以和前面两种混合使用,Poolable   Stateful  不能并列使用,只能二选其一。

 

 

用户注册登陆

用户注册登陆系统是每个系统必须模块,主要包括两个功能块:用户资料注册和修改部分;用户登陆和访问权限ACL部分。

前一个部分主要是用户资料的增删改查CRUD,可以使用Jdon框架轻易完成;如JPestore中的SignAction类。

第二个部分有两种实现方式:基于容器和手工定制(Container-based vs. custom security),这两种实现方式都可由用户自己选择,这里主要说明一下,如何将用户登陆后的信息如用户ID共享给业务模型,例如:论坛发言者登陆后,发表一个帖子,这个帖子应该有一个用户ID,如何将发言者的用户ID赋值给帖子里?依据不同的实现方式有不同的捷径:

手工定制就是开发者自己编制代码完成,为需要保护的资源设立filter,或者在每个Jsp页面include一个安全权限检查Jsp模块,在这个方式下,用户登陆信息一般是保存在HttpSession中,那么在表现层继承ModelHandler实现一个子类,在这个Handler中能够访问HttpSession,并取出事先保存在HttpSession中的用户信息如ID

当然,基于容器的实现方式也可以采取上述办法,例如JdonNewsNewsHandler类中的serviceAction方法代码,如下:

User user = (User) ContainerUtil.getUserModelAfterLogin(request);

if (user != null) {

  News news = (News) em.getModel();

  news.setUser(user);

}

else

  Debug.logVerbose(" not found user in any scope ", module);

这样,将用户登陆信息user直接赋值到news实例中。

这种方式是通用方式,缺点是需要专门实现一个ModelHandler类,需要编写代码,还有一个不需要编写代码的方式:

获得基于容器登陆用户Principle

下面介绍在服务层能够直接获得基于容器的登陆用户UserPrinciple名称的方式,这种方式只适合POJO Service架构,而且必须是基于容器实现方式。

使用本方式目的在于简化表现层代码,在Jdon框架中,表现层基本都是通过jdonframework.xml配置实现,一般情况不必编码,如果不使用本方式,那么就必须自己实现一个ModelHandler子类,现在则不必,可以在业务服务层服务Service对象中直接获得request.getPrinciple()方法结果。

直接简单的使用方式:

1.Service对象继承com.jdon.container.visitor.data.SessionContextAcceptable,该接口目前有两个方法:

  void setSessionContext(SessionContext sessionContext);

  SessionContext getSessionContext ();

这样这个Service类必须完成接口上述两个方法,实际是Jdon容器在运行时,通过com.jdon.aop.interceptor. SessionContextInterceptor这个拦截器在运行时注射到被调用的这个Service对象中。

目前com.jdon.container.visitor.data.SessionContext中已经包含String类型的principleName,这个值来源于request.getUserPrincipal()获得的PrincipalName值,如下:

Principal principal = request.getUserPrincipal();

String principleName= principal.getName();

上述这段代码是在Jdon框架中执行, 这样通过这种方式,在服务层服务对象中我们可以容易获得保存在SessionContext中用户登陆信息Principle Name,使用这个方式虽然方便,但是必须有下列注意点:

1. 继承com.jdon.container.visitor.data.SessionContextAcceptable的服务层服务Service对象必须被客户端直接调用,调用方式是代码或配置方式都可以,客户端代码方式如下:

FrameworkTestServiceIF frameworkTestService = (FrameworkTestServiceIF) WebAppUtil.getEJBService(

            "frameworkTestService",  request);

frameworkTestService.getName()

或在jdonframework.xml中配置model部分指向frameworkTestService调用也可以,这是一种配置方式调用。

只有这个服务Service对象直接面对客户端调用,拦截器的注射功能才能将SessionData注射进入它的“体内”。

2.访问服务层服务Service对象的客户端必须在容器安全体系保护下,一般这样客户端都是JspServlet/Action 都会有Url,例如http://localhost:8080/MyWeb/Admin/XXXX.do,在web.xml以及配置/Admin/*都是必须被特地角色才能访问。

因为只有这样,Web容器的Request对象中getUserPrinciple才会有值,这是J2EE规范规定的。载有角色值的Request访问/Admin/XXXX.doXXXXX.doStruts的一个Action,再通过Action访问特定的服务Service对象。

在上述两种情况同时满足情况下,服务层服务Service对象中sessionContext中的principleName才会有该用户的登陆信息,注意这个登陆信息令牌可以是用户ID或用户名,取决于你容器配置如JBosslogin-config.xml,只有密码验证通过过Web容器才会有值。

一旦你在Service中获得principleName,你就可以再通过查询数据库获得该用户的完整资料,从而将用户信息共享给所有业务服务层。

具体代码见Jivejdon 3.0com.jdon.jivejdon.service.imp. ForumMessageServiceImp

 

SessionContext用处

上述容器登陆信息Principle Name通过保存在SessionContext中使得服务层服务对象能够获得用户的登陆信息。

SessionContext是保存在Web容器的HttpSession中一个载体,当在服务层服务对象中需要缓存一些数据,这些数据的ScopeSession性质,这样可以通过SessionContext实现数据共享。Jdon框架还提供Stateful提供另外一种与Session有关状态载体选择。

如果希望缓存或共享的数据的scopeRequest,可以使用JDK 新语言特性ThreadLocal,服务层的服务对象就能够和Jdon容器以外的Request请求相关发生联系,例如我们可以做一个ServletFilter,将某些数据保存在ThreadLocal中,然后在服务层服务对象中从ThreadLocal中获取,这属于一般应用技巧,这里不详细描述,只是做一个应用提示。

如果缓存或共享的数据scopeApplication,就将其注册到Jdon容器中,因为Jdon容器的scopeApplication的,这样整个服务层也可以访问。

 


上一级 首页 下一级


更多Jdon框架专题讨论

JdonFramework作为一个免费开源软件开发平台,可以商用开发大多数数据库应用软件和管理软件: 电子商务软件 在线教育软件 税务软件 Web快速开发软件 财务软件 购物车软件 医院帐务软件 crm software medical software 人事薪资软件payroll software 在线购物软件 销售软件 项目管理软件 房产不动产管理软件 生产软件 PDM软件 制造业软件 仓库软件 采购软件 进销存软件 危险源监控软件 物流软件 超市软件 银行软件 保险软件 汽车软件 医疗软件 电子软件 自动化软件 服装软件 烟草软件 分销管理软件 供应商管理软件

下载源码

框架文档

框架应用系统

演示运行

JiveJdon3

性能测试

Q&A 问答

技术支持

 

 

更多标签...



Jdon框架演示

JiveJdon
源码下载

VIP收费区

历史热点讨论排行榜




google yahoo 新浪ViVi 365Key网摘 天极网摘 CSDN网摘 添加到百度搜藏 POCO网摘





手机 add to google add to yahoo
联系我们 | 关于我们 | 广告联系 | 网站地图 | 设为首页

沪ICP证08026060 如有意见请与我们联系 Powered by JdonFramework
_×
您有新消息