??xml version="1.0" encoding="utf-8" standalone="yes"?>一区国严二区亚洲三区,九月丁香婷婷亚洲综合色,久久99亚洲网美利坚合众国http://www.tkk7.com/sgsoft/category/12.htmlJAVA?/description>zh-cnTue, 27 Feb 2007 23:56:48 GMTTue, 27 Feb 2007 23:56:48 GMT60Zstruts+spring+ibatis?J2EE 开?/title><link>http://www.tkk7.com/sgsoft/articles/2257.html</link><dc:creator>天一?/dc:creator><author>天一?/author><pubDate>Sun, 20 Mar 2005 08:46:00 GMT</pubDate><guid>http://www.tkk7.com/sgsoft/articles/2257.html</guid><wfw:comment>http://www.tkk7.com/sgsoft/comments/2257.html</wfw:comment><comments>http://www.tkk7.com/sgsoft/articles/2257.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.tkk7.com/sgsoft/comments/commentRss/2257.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/sgsoft/services/trackbacks/2257.html</trackback:ping><description><![CDATA[1. 前言<BR><BR>Struts 是目前Java Web MVC框架中不争的王者。经q长达五q的发展QStruts已经逐渐成长Z个稳定、成熟的框架Qƈ且占有了MVC框架中最大的市场份额。但是Struts某些技术特性上已经落后于新兴的MVC框架。面对Spring MVC、Webwork2 q些设计更精密,扩展性更强的框架QStruts受到了前所未有的挑战。但站在产品开发的角度而言QStruts仍然是最E_的选择。本文的原型例子JpetStore 4.0是ZStruts开发的Q但是不拘惔于Struts的传l固定用法,例如只用了一个自定义Actionc,q且在form beancȝ定义上也是开创性的Qo目一斎ͼE后具体剖析一下?BR><BR>Spring Framework 实际上是Expert One-on-One J2EE Design and Development 一书中所阐述的设计思想的具体实现。Spring Framework的功能非常多。包含AOP、ORM、DAO、Context、Web、MVC{几个部分组成。Web、MVC暂不用考虑QJpetStore 4.0用的是更成熟的Struts和JSPQDAO׃目前Hibernate、JDO、ibatis的流行,也不考虑QJpetStore 4.0用的是ibatis。因此最需要用的是AOP、ORM、Context。Context中,最重要的是BeanfactoryQ它能将接口与实现分开Q非常强大。目前AOP应用最成熟的还是在事务理上?BR><BR>ibatis 是一个功能强大实用的SQL Map工具Q不同于其他ORM工具Q如hibernateQ,它是SQL语句映射成Java对象Q而对于ORM工具Q它的SQL语句是根据映定义生成的。ibatis 以SQL开发的工作量和数据库移植性上的让步,为系l设计提供了更大的自q间。有ibatis代码生成的工P可以ҎDDL自动生成ibatis代码Q能减少很多工作量?BR><BR>2. JpetStoreq?BR><BR>2.1. 背景<BR><BR>最初是Sun公司的J2EE petstoreQ其最主要目的是用于学习J2EEQ但是其~点也很明显Q就是过度设计了。接着Oracle用J2EE petstore来比较各应用服务器的性能。微软推ZZ.Netq_?Pet shopQ用于竞争J2EE petstore。而JpetStore则是l过改良的基于struts的轻便框架J2EE web应用E序Q相比来_JpetStore设计和架构更优良Q各层定义清晎ͼ使用了很多最佛_践和模式Q避免了很多"反模?Q如使用存储q程Q在java代码中嵌入SQL语句Q把HTML存储在数据库中等{。最新版本是JpetStore 4.0?BR><BR>2.2. JpetStore开发运行环境的建立<BR><BR>1、开发环?BR><BR>Java SDK 1.4.2<BR>Apache Tomcat 4.1.31<BR>Eclipse-SDK-3.0.1-win32<BR>HSQLDB 1.7.2<BR><BR>2、Eclipse插g<BR><BR>EMF SDK 2.0.1QEclipse建模框架Qlomboz插g需要,可以使用runtime版本?BR>lomboz 3.0QJ2EE插gQ用来在Eclipse中开发J2EE应用E序<BR>Spring IDE 1.0.3QSpring Bean配置理插g<BR>xmlbuddy_2.0.10Q编辑XMLQ用免费版功能即?BR>tomcatPluginV3Qtomcat理插g<BR>Properties EditorQ编辑java的属性文?q可以预览以及自动存盘ؓUnicode格式。免M手工或者ANT调用native2ascii的麻烦?BR>2.3. 架构<BR><BR> <CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2005/2/397089.jpg"></CENTER><BR><BR> <CENTER>? JpetStore架构?/CENTER><BR><BR>? 是JPetStore架构图。参照这个架构图Q让我们E微剖析一下源代码Q得出JpetStore 4.0的具体实现图Q图2Q,思\一下子p然开朗了。前a中提到的非传l的struts开发模式,关键在struts Actioncdform beancM?BR><BR>struts Actioncd有一个:BeanAction。没错,实是一个!与传l的struts~程方式很不同。再仔细研究BeanActionc,发现它其实是一个通用c,利用反射原理Q根据URL来决定调用formbean的哪个方法。BeanAction大大化了struts的编E模式,降低了对struts的依赖(与struts以及WEB容器有关的几个类都放在com.ibatis.struts包下Q其它的c都可以直接复用Q。利用这U模式,我们会很Ҏ的把它移植到新的框架如JSFQspring?BR><BR>q样重心p{Udform bean上了Q它已经不是普通意义上的form bean了。查看源代码Q可以看到它不仅仅有数据和校?重置ҎQ而且已经h了行为,从这个意义上来说Q它更像一个BO(Business Object)。这是前文讲到的,BeanActioncd用反原理,ҎURL来决定调用form bean的哪个方法(行ؓQ。form bean的这些方法的{֐很简单,例如Q?BR><BR> <CENTER><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public String myActionMethod() { //..work return "success"; }</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>Ҏ的返回值直接就是字W串Q对应的是forward的名Uͼ而不再是ActionForward对象Q创建ActionForward对象的Q务已l由BeanActioncM劳了。另外,E序q提供了ActionContext工具c,该工L装了request 、response、form parameters、request attributes、session attributes?application attributes中的数据存取操作Q简单而线E安全,form beancM用该工具cd以进一步从表现层框架解耦。在q里需要特别指出的是,BeanActioncL对struts扩展的一个有益尝试,虽然提供了非常好的应用开发模式,但是它还非常斎ͼ一直在发展中?<BR><BR> <CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2005/2/397091.gif"></CENTER><BR><BR> <CENTER>? JpetStore 4.0具体实现<BR><BR>2.4. 代码剖析<BR><BR>下面p我们开始进一步分析JpetStore4.0的源代码Qؓ下面的改造铺路。BeanAction.java是唯一一个Struts actionc,位于com.ibatis.struts包下。正如上文所aQ它是一个通用的控制类Q利用反机Ӟ把控制{Udform bean的某个方法来处理。详l处理过E参考其源代码,单明晰?BR><BR>Form beancM于com.ibatis.jpetstore.presentation包下Q命名规则ؓ***Bean。Form beancd部承于BaseBeanc,而BaseBeancd际承于ActionFormQ因此,Form beancd是Struts?ActionFormQForm beancȝ属性数据就由struts框架自动填充。而实际上QJpetStore4.0扩展了struts中ActionForm的应用: Form beanc还h行ؓQ更像一个BO,其行为(ҎQ由BeanActionҎ配置Qstruts-config.xmlQ的URL来调用。虽然如此,我们q是把Form beancd位于表现层。Struts-config.xml的配|里?U映方式,来告诉BeanAction把控制{到哪个form bean对象的哪个方法来处理。以q个hq接Zhttp://localhost/jpetstore4/shop/viewOrder.do<BR><BR>1. URL Pattern<BR><BR> <CENTER><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE><action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction" name="orderBean" scope="session" validate="false"> <forward name="success" path="/order/ViewOrder.jsp"/> </action></CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>此种方式表示Q控制将被{发到"orderBean"q个form bean对象 ?viewOrder"ҎQ行为)来处理。方法名?path"参数的以"/"分隔的最后一部分?<BR><BR>2. Method Parameter <BR><BR> <CENTER><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE><action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction" name="orderBean" parameter="viewOrder" scope="session" validate="false"> <forward name="success" path="/order/ViewOrder.jsp"/> </action></CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>此种方式表示Q控制将被{发到"orderBean"q个form bean对象?viewOrder"ҎQ行为)来处理。配|中?parameter"参数表示form beancM的方法?parameter"参数优先?path"参数?<BR><BR>3. No Method call <BR><BR> <CENTER><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE><action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction" name="orderBean" parameter="*" scope="session" validate="false"> <forward name="success" path="/order/ViewOrder.jsp"/> </action></CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>此种方式表示Qform bean上没有Q何方法被调用。如果存?name"属性,则struts把表单参数等数据填充到form bean对象后,把控制{发到"success"。否则,如果name为空Q则直接转发控制?success"。这q当于struts内置的org.apache.struts.actions.ForwardAction的功?<BR><BR> <CENTER><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE><action path="/shop/viewOrder" type="org.apache.struts.actions.ForwardAction" parameter="/order/ViewOrder.jsp " scope="session" validate="false"> </action></CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>ServicecM于com.ibatis.jpetstore.service包下Q属于业务层。这些类装了业务以及相应的事务控制。Servicecȝform beancL调用?<BR><BR>com.ibatis.jpetstore.persistence.iface包下的类是DAO接口Q属于业务层Q其屏蔽了底层的数据库操作,供具体的ServicecL调用。DaoConfigcL工具c(DAO工厂c)QServicec通过DaoConfigcL获得相应的DAO接口Q而不用关心底层的具体数据库操作,实现了如?中{耦合2}的解耦?<BR><BR>com.ibatis.jpetstore.persistence.sqlmapdao包下的类是对应DAO接口的具体实玎ͼ在JpetStore4.0中采用了ibatis来实现ORM。这些实现类l承BaseSqlMapDaoc,而BaseSqlMapDaocdl承ibatis DAO 框架中的SqlMapDaoTemplatecRibatis的配|文件存攑֜com.ibatis.jpetstore.persistence.sqlmapdao.sql目录下。这些类和配|文件位于数据层 <BR><BR>DomaincM于com.ibatis.jpetstore.domain包下Q是普通的javabean。在q里用作数据传输对象QDTOQ,贯穿视图层、业务层和数据层Q用于在不同层之间传输数据。剩下的部分比较简单了Q请看具体的源代码,非常清晰?<BR><BR>2.5. 需要改造的地方<BR><BR>JpetStore4.0的关键就在struts Actioncdform beancMQ这也是其精华之一Q虽然该实现方式是试验性,待扩充和验证Q,在此ơ改造中我们要保留下来,x制层一点不变,表现层获取相应业务类的方式变了(要加载spring环境Q,其它保持不变。要特别x的改动是业务层和持久层,q运的是JpetStore4.0设计非常好,需要改动的地方非常,而且由模式可循,如下Q?BR><BR>1. 业务层和数据层用Spring BeanFactory机制理?BR>2. 业务层的事务由spring 的aop通过声明来完成?BR>3. 表现层(form beanQ获取业务类的方法改p定义工厂cL实现Q加载spring环境Q?BR><BR>3. JPetStore的改?BR><BR>3.1. 攚w后的架?BR><BR> <CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2005/2/397093.gif"></CENTER><BR><BR>其中U色部分是要增加的部分,蓝色部分是要修改的部分。下面就让我们逐一剖析?BR><BR>3.2. Spring Context的加?BR>Z在Struts中加载Spring ContextQ一般会在struts-config.xml的最后添加如下部分:<BR><BR> <CENTER><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE><plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"> <set-property property="contextConfigLocation" value="/WEB-INF/applicationContext.xml" /> </plug-in></CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>Spring在设计时充分考虑C与Struts的协同工作,通过内置的Struts Plug-in在两者之间提供了良好的结合点。但是,因ؓ在这里我们一点也不改动JPetStore的控制层(q是JpetStore4.0的精华之一)Q所以本文不准备采用此方式来加蝲ApplicationContext。我们利用的是spring framework 的BeanFactory机制,采用自定义的工具c(bean工厂c)来加载spring的配|文Ӟ从中可以看出Spring有多灉|Q它提供了各U不同的方式来用其不同的部?层次Q您只需要用你想用的Q不需要的部分可以不用?<BR><BR>具体的来_是在com.ibatis.spring包下创徏CustomBeanFactoryc,spring的配|文件applicationContext.xml也放在这个目录下。以下就是该cȝ全部代码Q很单: <BR><BR> <CENTER><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public final class CustomBeanFactory { static XmlBeanFactory factory = null; static { Resource is = new InputStreamResource( CustomBeanFactory.class.getResourceAsStream("applicationContext.xml")); factory = new XmlBeanFactory(is); } public static Object getBean(String beanName){ return factory.getBean(beanName); } }</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>实际上就是封装了Spring 的XMLBeanFactory而已Qƈ且Spring的配|文件只需要加载一ơ,以后可以直接用CustomBeanFactory.getBean("someBean")来获得需要的对象?例如someBean)Q而不需要知道具体的cRCustomBeanFactorycȝ于{耦合1}的解耦。CustomBeanFactorycd本文中只用于表现层的form bean对象获得servicecȝ对象Q因为我们没有把form bean对象配置在applicationContext.xml中。但是,Z么不把表现层的form beancM配置h呢,q样q不着qCustomBeanFactory个类了,Spring会帮助我们创建需要的一切?问题的答案就在于form beancLstruts的ActionFormc!如果大家熟悉strutsQ就会知道ActionFormcLstruts自动创徏的:在一ơ请求中Qstruts判断Q如果ActionForm实例不存在,创Z个ActionForm对象Q把客户提交的表单数据保存到ActionForm对象中。因此formbeancȝ对象׃能由spring来创建,但是servicecM及数据层的DAOcd以,所以只有他们在spring中配|。所以,很自然的Q我们就创徏了CustomBeanFactoryc,在表现层来衔接struts和spring。就q么单,实现了另一U方式的{耦合一}的解耦?<BR><BR>3.3. 表现?<BR><BR>面分析到Qstruts和spring是在表现层衔接v来的Q那么表现层p做稍微的更改Q即所需要的servicecȝ对象创徏上。以表现层的AccountBeancMؓ例:原来的源代码如下 <BR><BR> <CENTER><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>private static final AccountService accountService = AccountService.getInstance(); private static final CatalogService catalogService = CatalogService.getInstance();</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>攚w后的源代码如下 <BR><BR> <CENTER><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>private static final AccountService accountService = (AccountService)CustomBeanFactory.getBean("AccountService"); private static final CatalogService catalogService = (CatalogService)CustomBeanFactory.getBean("CatalogService");</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>其他的几个presentationcM同样方式攚w。这P表现层就完成了。关于表现层的其它部分如JSP{一概不动。也许您会说Q没有看Z么特别之处的好处啊?你还是额外实C一个工厂类。别着急,帷幕刚刚开启,spring是在表现层引入,但您发没发现Q?<BR><BR>presentationcM仅面向servicecȝ接口~程Q具?AccountService"是哪个实现类QpresentationcM知道Q是在spring的配|文仉配置。(本例中,Z最大限度的保持原来的代码不作变化,没有抽象出接口)。Spring鼓励面向接口~程Q因为是如此的方便和自然Q当然您也可以不q么做?<BR><BR>CustomBeanFactoryq个工厂cMؓ什么会如此单,因ؓ其直接用了Spring的BeanFactory。Spring从其核心而言Q是一个DI容器Q其设计哲学是提供一U无侵入式的高扩展性的框架。ؓ了实现这个目标,Spring 大量引入了Java 的Reflection机制Q通过动态调用的方式避免编码方式的U束Qƈ在此基础上徏立了其核心组件BeanFactoryQ以此作为其依赖注入机制的实现基。org.springframework.beans包中包括了这些核心组件的实现c,核心中的核心为BeanWrapper和BeanFactorycR?<BR>3.4. 持久层在讨论业务层之前,我们先看一下持久层Q如下图所C:<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR> <CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2005/2/397095.jpg"></CENTER><BR><BR>在上文中Q我们把iface包下的DAO接口归ؓ业务层,在这里不需要做修改。ibatis的sql配置文g也不需要改。要改的是DAO实现c,q在spring的配|文件中配置h?BR><BR>1、修改基c?BR><BR>所有的DAO实现c都l承于BaseSqlMapDaocR修改BaseSqlMapDaocd下:<BR><BR> <CENTER><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public class BaseSqlMapDao extends SqlMapClientDaoSupport { protected static final int PAGE_SIZE = 4; protected SqlMapClientTemplate smcTemplate = this.getSqlMapClientTemplate(); public BaseSqlMapDao() { } }</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>使BaseSqlMapDaocL为承于Spring提供的SqlMapClientDaoSupportc,q定义了一个保护属性smcTemplateQ其cd为SqlMapClientTemplate?<BR><BR>2、修改DAO实现c?<BR><BR>所有的DAO实现c还是承于BaseSqlMapDaoc,实现相应的DAO接口Q但其相应的DAO操作委托SqlMapClientTemplate来执行,以AccountSqlMapDaocMؓ例,部分代码如下Q?<BR><BR> <CENTER><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public List getUsernameList() { return smcTemplate.queryForList("getUsernameList", null); } public Account getAccount(String username, String password) { Account account = new Account(); account.setUsername(username); account.setPassword(password); return (Account) smcTemplate.queryForObject ("getAccountByUsernameAndPassword", account); } public void insertAccount(Account account) { smcTemplate.update("insertAccount", account); smcTemplate.update("insertProfile", account); smcTemplate.update("insertSignon", account); }</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>p么简单,所有函数的{֐都是一LQ只需要查找替换就可以了!<BR>、除d厂类以及相应的配|文?BR><BR>除去DaoConfig.javaq个DAO工厂cd相应的配|文件dao.xmlQ因为DAO的获取现在要用spring来管理?BR><BR>4、DAO在Spring中的配置QapplicationContext.xmlQ?BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR> <CENTER><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>org.hsqldb.jdbcDriver</value> </property> <property name="url"> <value>jdbc:hsqldb:hsql://localhost/xdb</value> </property> <property name="username"> <value>sa</value> </property> <property name="password"> <value></value> </property> </bean> <!-- ibatis sqlMapClient config --> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation"> <value> classpath:com\ibatis\jpetstore\persistence\sqlmapdao\sql\sql-map-config.xml </value> </property> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> <!-- Transactions --> <bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> <!-- persistence layer --> <bean id="AccountDao" class="com.ibatis.jpetstore.persistence.sqlmapdao.AccountSqlMapDao"> <property name="sqlMapClient"> <ref local="sqlMapClient"/> </property> </bean></CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>1. 我们首先创徏一个数据源dataSourceQ在q里配置的是hsqldb数据库。如果是ORACLE数据库,driverClassName的值是"oracle.jdbc.driver.OracleDriver"QURL的值类g"jdbc:oracle:thin:@wugfMobile:1521:cdcf"。数据源现在由spring来管理,那么现在我们可以去掉properties目录下database.propertiesq个配置文g了;q有不要忘记修改sql-map-config.xmlQ去?PROPERTIES resource="properties/database.properties" />对它的引用?<BR><BR>2. sqlMapClient节点。这个是针对ibatis SqlMap的SqlMapClientFactoryBean配置。实际上配置了一个sqlMapClient的创建工厂类。configLocation属性配|了ibatis映射文g的名U。dataSource属性指向了使用的数据源Q这h有用sqlMapClient的DAO都默认用了该数据源Q除非在DAO的配|中另外昑ּ指定?<BR><BR>3. TransactionManager节点。定义了事务Q用的是DataSourceTransactionManager?<BR><BR>4. 下面可以定义DAO节点了,如AccountDaoQ它的实现类是com.ibatis.jpetstore.persistence.sqlmapdao.AccountSqlMapDaoQ用的SQL配置从sqlMapClient中读取,数据库连接没有特别列出,那么是默认使用sqlMapClient配置的数据源datasource?<BR><BR>q样Q我们就把持久层攚w完了,其他的DAO配置cM于AccountDao。怎么P单吧。这ơ有接口了:Q?AccountDao接口Q?gt;AccountSqlMapDao实现?<BR><BR>3.5. 业务?<BR><BR>业务层的位置以及相关c,如下图所C:在这个例子中只有3个业务类Q我们以OrderServicecMؓ例来攚w,q个cL最复杂的,其中涉及了事务?<BR><BR>1、在ApplicationContext配置文g中增加bean的配|: <BR><BR> <CENTER><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE><bean id="OrderService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"> <ref local="TransactionManager"></ref> </property> <property name="target"> <bean class="com.ibatis.jpetstore.service.OrderService"> <property name="itemDao"> <ref bean="ItemDao"/> </property> <property name="orderDao"> <ref bean="OrderDao"/> </property> <property name="sequenceDao"> <ref bean="SequenceDao"/> </property> </bean> </property> <property name="transactionAttributes"> <props> <prop key="insert*">PROPAGATION_REQUIRED</prop> </props> </property> </bean></CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>定义了一个OrderServiceQ还是很Ҏ懂的。ؓ了简单v见,使用了嵌套beanQ其实现cLcom.ibatis.jpetstore.service.OrderServiceQ分别引用了ItemDaoQOrderDaoQSequenceDao。该bean的insert*实现了事务管?AOP方式)。TransactionProxyFactoryBean自动创徏一个事务advisorQ?该advisor包括一个基于事务属性的pointcut,因此只有事务性的Ҏ被拦截?<BR><BR>2、业务类的修改,以OrderServiceZQ?<BR><BR> <CENTER><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public class OrderService { /* Private Fields */ private ItemDao itemDao; private OrderDao orderDao; private SequenceDao sequenceDao; /* Constructors */ public OrderService() { } /** * @param itemDao 要设|的 itemDao? */ public final void setItemDao(ItemDao itemDao) { this.itemDao = itemDao; } /** * @param orderDao 要设|的 orderDao? */ public final void setOrderDao(OrderDao orderDao) { this.orderDao = orderDao; } /** * @param sequenceDao 要设|的 sequenceDao? */ public final void setSequenceDao(SequenceDao sequenceDao) { this.sequenceDao = sequenceDao; } //剩下的部? …? }</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>U色部分Z攚w分。Spring采用的是Type2的设|依赖注入,所以我们只需要定义属性和相应的设值函数就可以了,ItemDaoQOrderDaoQSequenceDao的值由spring在运行期间注入。构造函数就可以为空了,另外也不需要自q写代码处理事务了Q事务在配置中声明)QdaoManager.startTransaction();{与事务相关的语句也可以L了。和原来的代码比较一下,是不是处理精了很多!可以更关注业务的实现?<BR><BR>4. l束?<BR><BR>ibatis是一个功能强大实用的SQL Map工具Q可以直接控制SQL,为系l设计提供了更大的自q间。其提供的最新示例程序JpetStore 4.0,设计优雅Q应用了q今为止很多最佛_践和设计模式Q非帔R于学习以及在此基础上创量的J2EE WEB应用E序。JpetStore 4.0是基于struts的,本文在此基础上,最大程度保持了原有设计的精华以及最的代码改动量,在业务层和持久化层引入了Spring。在您阅M本文以及攚w后的源代码后,会深切的感受到Spring带来的种U好处:自然的面向接口的~程Q业务对象的依赖注入Q一致的数据存取框架和声明式的事务处理,l一的配|文件…更重要的是Spring既是全面的又是模块化的,Spring有分层的体系l构Q这意味着您能选择仅仅使用它Q何一个独立的部分Q就像本文,而它的架构又是内部一致?<BR><BR>(T117) <BR></CENTER><img src ="http://www.tkk7.com/sgsoft/aggbug/2257.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/sgsoft/" target="_blank">天一?/a> 2005-03-20 16:46 <a href="http://www.tkk7.com/sgsoft/articles/2257.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用高效的日志工具—Log4Jhttp://www.tkk7.com/sgsoft/articles/2256.html天一?/dc:creator>天一?/author>Sun, 20 Mar 2005 08:36:00 GMThttp://www.tkk7.com/sgsoft/articles/2256.htmlhttp://www.tkk7.com/sgsoft/comments/2256.htmlhttp://www.tkk7.com/sgsoft/articles/2256.html#Feedback0http://www.tkk7.com/sgsoft/comments/commentRss/2256.htmlhttp://www.tkk7.com/sgsoft/services/trackbacks/2256.html大家在编E时l常不可避免地要使用C些日志操作,比如开发阶D늚调试信息、运行时的日志记录及审计。调查显C,日志代码占代码总量?Q。通常大家可以单地使用System.out.println()语句输出日志信息Q但是往往会有一些判断,比如Q?

if (someCondition) {
 System.out.println("some information.");
}


q些判断造成正常的程序逻辑中؜杂了大量的输句。而在开发阶D写下的q些判断仅ؓ了调试的语句Q在开发完成时需要查扑ƈU除。部|运行后Q尤其是在一些企业应用系l中Q还l常需要进一步调试,q时遇C更大的麻烦。所以,我们需要一套完备的、灵zȝ、可配置的日志工兗Log4J是优秀的选择?

Log4J是Apache软g基金会Jakarta目下的一个子目Q是用Java~写的优U日志工具包。通过Log4J可以在不修改代码的情况下Q方ѝ灵zd控制L_度的日志信息的开启或关闭Q然后用定制的格式Q把日志信息输出C个或多个需要的地方。ƈ且,Log4Jq有一条^滑的学习曲线Q在三分钟内可学会它的单用。随着使用深入Q你会发现Log4J功能的强大,几乎可以满日志斚w的所有需要?

快速入?/STRONG>


先看一D代码,看看Log4J是多么易于上手,代码如下Q?

package org.javaresearch.log4j;
import org.apache.log4j.*;
public class TestLog4J {
 static Logger log = Logger.getLogger(TestLog4J.class.getName());
 public static void main(String args[]) {
  BasicConfigurator.configure();
  // logging的各U方?
  cat.debug("Start of main()");
  cat.info("Just testing a log message with priority set to INFO");
  cat.warn("Just testing a log message with priority set to WARN");
  cat.error("Just testing a log message with priority set to ERROR");
  cat.fatal("Just testing a log message with priority set to FATAL");
  // 另一U不方便的格?
  cat.log(Priority.DEBUG, "Testing a log message use a alternate form");
  log.debug("End of main().");
 }
}


把这D代码保存在一个目录下Q编译运行(注意要把log4j-1.2.7.jar包含入类路径中)Q程序输出如下:

0 [main] DEBUG TestLog4J  - Start of main()
10 [main] INFO TestLog4J  - Just testing a log message with priority set to INFO
20 [main] WARN TestLog4J  - Just testing a log message with priority set to WARN
30 [main] ERROR TestLog4J  - Just testing a log message with priority set to ERROR
30 [main] FATAL TestLog4J  - Just testing a log message with priority set to FATAL
40 [main] DEBUG TestLog4J  - Testing a log message use a alternate form
50 [main] DEBUG TestLog4J  - End of main().


首先解释一下上面输出结果的意义。第一个数字是指程序开始运行到q行该日志语句所l历的毫U数Q用来做一点运行效率分析也不错Q,“[main]”是日志事g发生的线E,随后的“DEBUG”、“INFO”等信息是相应日志信息的优先U别Q“TestLog4”是当前Logger的实例名Q最后是日志信息?

在这D늨序中Q用了Log4J提供的一个基本配|类BasicConfigurator对Log4Jq行初始化。但在实际用时通常不这么做Q因多少有点“硬”编码。今后如果要修改Log4J的配|,需要修攏V重新编译代码,q通常不是大家所希望的。通常Q我们都提供一个名为log4j.properties的文Ӟ在第一ơ调用到Log4JӞLog4J会在c\径中定位q个文gQƈdq个文g完成的配|。这个配|文件告诉Log4J以什么样的格式、把什么样的信息、输出到什么地斏V我们来看一个简单的log4j.properties配置文g的示例,代码如下Q?

log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern= "%-4r [%t] %-5p %c %x - %m%n


把上面的内容存储为log4j.propertiesQƈ攑ֈ和TestLog4J.class同一目录下(当然也可以放到其它Q何目录,只要该目录被包含到类路径中即可)。具体这些配|文件中每行的意义,在以后章节会有详l的说明Q现在可以先跌。现在你可以注释掉上面程序中的“BasicConfigurator. configure();”语句,然后使用log4j.properties属性文件完成Log4J的配|,重新~译、运行,得到和上面一Ll果?

q样做有什么好处呢Q现在就初步领略一些Log4J的灵zR强大功能。比如系l要上线了,希望输出一些警告和错误信息Q这时仅需要修改log4j.properties文g中的“log4j.rootCategory=DEBUG, A1”即可,然后讄日志输出的最低别是WARNQ设|ؓ“log4j.root Category=WARN, A1”。此时不需要修改Q何代码,重新q行pȝQ输出结果就变成了:

20 [main] WARN TestLog4J  - Just testing a log message with priority set to WARN
30 [main] ERROR TestLog4J  - Just testing a log message with priority set to ERROR
30 [main] FATAL TestLog4J  - Just testing a log message with priority set to FATAL


原理分析


Log4J有三个主要部Ӟ它们是记录器QLoggersQ、输出源QAppendersQ和布局QLogoutsQ。记录器按照布局中指定的格式把日志信息写入一个或多个输出源。输出源可以是控制台、文本文件、XML文g或SocketQ甚臌可以把信息写入到Windows事g日志或通过电子邮g发送,q都需要相应的cL处理Q这些相关的cLConsoleAppender、FileAppender、SocketAppender、NtEventLogAppender和JMSAppender?

记录器(LoggerQ?/B>

首先让我们看Loggerc,代码如下Q?

package org.apache.log4j;
public class Logger {
 //创徏和恢复方?
 public static Logger getRootLogger();
 public static Logger getLogger(String name);
 public static Logger getLogger(Class clazz);
 // 打印Ҏ
 public void debug(Object message);
 public void info(Object message);
 public void warn(Object message);
 public void error(Object message);
 public void fatal(Object message);
 // 常用打印Ҏ
 public void log(Level l, Object message);
}


从这D代码中可以看出Logger的基本用。首先需要获取一个Logger对象Q获取Logger对象的语句ؓQ?

Logger logger = Logger.getLogger(JavaLoggingExample.class.getName());


有了q个Logger对象Q就可以在需要的地方方便地输出日志信息。对于这些信息是否输出、输出的格式{,都可以通过配置文g方便地配|,而不需要修改代码,q就是Log4J带来的方便之处?

记录器的层次l构

使用Log4J的Logger.getLogger()Ҏ时会得到一个Logger的实例。如果一个应用中包含了上千个c,那么也几乎需要上千个Logger实例。如何对q上千个Logger实例q行方便地配|,是一个很重要的问题。Log4J采用了一U树状的l承层次巧妙地解决了q个问题。在Log4J中Logger是具有层ơ关pȝ。它有一个共同的根,位于最上层Q其它Logger遵@cM包的层次Q比如:

static Logger root = Logger.getRootLogger();
static Logger log1 = Logger.getLogger("org");
static Logger log2 = Logger.getLogger("org.javaresearch");
static Logger log3 = Logger.getLogger("org.javaresearch.log4j.TestLog4J");


上面代码中,log1是log2的父Ԍ是log3的祖先,而root是所有log1、log2、log3的祖先,它们都从root中ѝ所以,一般情况下Q仅需要配|好rootLoggerQ其它子记录器都会从中承rootLogger的配|。如果修改了rootLogger的配|,其它所有的子记录器也会l承q种变化。这样就大大地方便了配置。现在回头看看在“快速入门”中的配|文Ӟ我们仅配|了rootLoggerQ就可以控制所有的Logger的行为?

U别QLevelQ?/B>

Log4J中的一个核心概忉|日志U别是有序的。Log4J内置?U日志别ؓQ?

DEBUG  <  INFO  <  WARN  <  ERROR  <  FATAL


双的别比左边的高。每一个Logger实例都有一个日志别,上面?U输出方法就是对应于5U不同别的日志h。比如,如果c是一个Logger实例Qc.info("some information")是一个INFOU别的日志请求。一个日志请求会不会输出Q取决于该Logger实例的日志别和该日志请求别的比较。规则如下:

假如在一个别ؓp的Logger实例中发生一个别ؓq的日志请求,则当q >= p时请求才会启用?

我们先来看实?代码如下Q?

// 得到一个logger 实例 "com.foo"
Logger  logger = Logger.getLogger("com.foo")
// 现在讄logger的别,但正常情冉|不需要刻意设|lggerU别的,因ؓ它已l在配置文g中完成了
logger.setLevel(Level.INFO);
Logger barlogger = Logger.getLogger("com.foo.Bar");
//因ؓ WARN >= INFOQ这个请求是可以实现?
logger.warn("Low fuel level.");
// 因ؓDEBUG < INFOQ所以这个请求是无法实现?
logger.debug("Starting search for nearest gas station.");
// logger实例"com.foo.Bar"从"com.foo"l承U别Q这P因ؓINFO >= 
INFOQ所以可以实C面的h
barlogger.info("Located nearest gas station.");
//因ؓDEBUG < INFOQ这个请求是不能实现?
barlogger.debug("Exiting gas station search");


布局QLayoutQ?/B>

Log4J采用cMC语言中的printf函数的打印格式格式化日志信息Q打印参数见?如下Q?

%m 输出代码中指定的消息
%p 输出优先U,即DEBUGQINFOQWARNQERRORQFATAL
%r 输出自应用启动到输出该log信息耗费的毫U数
%c 输出所属的cȝQ通常是所在类的全?/TD>
%t 输出产生该日志事件的U程?/TD>
%n 输出一个回车换行符QWindowsq_为“\r\n”,Unixq_为“\n?/TD>
%d 输出日志旉点的日期或时_默认格式为ISO8601Q也可以在其后指定格式,比如Q?d{yyy MMM dd HH:mm:ss,SSS}Q输出类|2002q?0?8?22Q?0Q?8Q?21
%l 输出日志事g的发生位|,包括cȝ名、发生的U程Q以及在代码中的行数。D例:Testlog4.main(TestLog4.java:10)


 

基本应用



Log4J的配|?/B>

现在来看log4j.properties配置文g的意义。第一行指定了根Logger的别是DEBUGQƈ此指定输出到A1。A1是W二行定义的org.apache.log4j.ConsoleAppenderQ此行表C将A1输出到控制台。第三行规定了输出到A1的格式ؓorg.apache.log4j.PatternLayout。第四行规定了输出到A1格式的{换模式ؓorg.javaresearch.log4j.TestLog4J?

很多成熟的服务器cȝ软g日志信息会输出到控制収ͼ同时输出到日志文件备查。用Log4J可以在不改变M代码的情况下Q仅通过修改配置文g可以轻村֜完成q项功能。相关配|文件如下:

#### Use two appenders, one to log to console, another to log to a file
log4j.rootCategory=debug, stdout, R

# Print only messages of priority WARN or higher for your category
log4j.category.your.category.name=WARN

#### First appender writes to console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

#### Second appender writes to a file
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log

# Control the maximum log file size
log4j.appender.R.MaxFileSize=100KB
# Archive log files (one backup file here)
log4j.appender.R.MaxBackupIndex=1

log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n


q个配置文g指定了两个输出源stdout和R。前者把日志信息输出到控制台Q后者是一个轮转日志文件。最大的文g?00KBQ当一个日志文件达到最大尺寸时QLog4J会自动把example.log重命名ؓexample.log.1Q然后重Z个新的example.log文gQ依ơ轮转?

在Web应用中?/B>

在Web应用中,应该在哪儿对Log4Jq行配置呢?首先要明,Log4J必须在应用的其它代码执行前完成初始化。因为Servlet是在Web服务器启动时立即装入的,所以,在Web应用中一般用一个专门的Servlet来完成Log4J的配|,q保证在web.xml的配|中Q这个Servlet位于其它Servlet之前。下面是一个例子,代码如下Q?

package org.javaresearch.log4j;
import java.io.*;
import javax.servlet.*;
import org.apache.log4j.*;
public class Log4JInit extends HttpServlet {
 public void init() throws ServletException {
  String prefix = getServletContext().getRealPath("/");
  String file = getServletConfig().getInitParameter("log4j-config-file");
  // 从Servlet参数dlog4j的配|文?
  if (file != null) {
   PropertyConfigurator.configure(prefix + file);
  }
 }
 public void doGet(HttpServletRequest request,HttpServletResponse response)throws 
IOException, ServletException {}
 public void doPost(HttpServletRequest request,HttpServletResponse response)throws 
IOException, ServletException {}
}


<servlet>
  <servlet-name>log4jinit</servlet-name> 
   <servlet-class>org.javaresearch. log4j.Log4JInit</servlet-class>	  	
   <init-param>
   <param-name> log4j-config-file </param-name> 
	<param-value>/properties/log4j.properties</param-value> 
   </init-param>
   <load-on-startup>1</load-on-startup>
  </servlet>


注意Q上面的load-on-startup应设?Q以便在Web容器启动时即装入该Servlet。log4j.properties文g攑֜根的properties子目录中Q也可以把它攑֜其它目录中。应该把.properties文g集中存放Q这h便管理?

高话题



性能

在记录一些日志信息时Q会一定程度地影响pȝ的运行效率,q时日志工具是否高效是一个关键。Log4J的首要设计目标就是高效,一些关键组仉重写q很多次以不断提高性能。根据Log4J目组的报告,在AMD Duron 800MHz Q?JDK1.3.1的环境下QLog4J判断一条日志语句是否需要输Z需?U秒。实际的日志语句执行的也非常快速,从用SimpleLayout?1微秒Q几乎与System.out.println一样快Q,C用TTCCLayout?7微秒不等?

嵌套诊断环境NDC

在多用户q发的环境下Q通常是由不同的线E分别处理不同的客户端请求。此时要在日志信息中区分Z同的客户端,你可以ؓ每一个线E生成一个LoggerQ从而从一堆日志信息中区分出哪些信息是属于哪个U程的,但这U方式ƈ不高效。Log4J巧妙C用了Neil Harrison提出的“NDCQ嵌套诊断环境)”机制来解决q个问题。Log4J为同一cd的线E生成一个LoggerQ多个线E共享用,而它仅在日志信息中添加能够区分不同线E的信息。NDC是什么?举例来说Q如果一个Servlet接到q发hӞ为每一个客L创徏一个新的线E,然后分配一个用于保存该h上下文的NDC堆栈。该上下文可能是发出h的主机名、IP地址或其它Q何可以用于标识该h的信息。这P׃不同的客L处理U程h不同的NDC堆栈Q即使这个Servlet同时生成多个U程处理不同的请求,q些日志信息仍然可以区分出来Q就好像Log4J为每一个线E都单独生成了一个Logger实例一栗在Log4J中是通过org.apache.log4j.NDC实现q种机制的。用NDC的方法也很简单,步骤如下Q?

1. 在进入一个环境时调用NDC.push(String)Q然后创Z个NDCQ?

2. 所做的日志操作输出中包括了NDC的信息;

3. d该环境时调用NDC.popҎQ?

4. 当从一个线E中退出时调用NDC.removeҎQ以侉K放资源?

下面是一个模拟记录来自不同客Lh事g的例子,代码如下Q?

import org.apache.log4j.Logger;
import org.apache.log4j.NDC;
public class TestNDC {
 static Logger log = Logger.getLogger(TestNDC.class.getName());
 public static void main(String[] args) {
  log.info("Make sure %x is in your layout pattern!");
  // 从客L获得IP地址的例?
  String[] ips = {"192.168.0.10","192.168.0.27"};
  for (int i = 0; i<ips.length ; i++)  // 模拟一个运行方?
  {
   // IP放进 NDC?
   NDC.push(ips[i]);
   log.info("A NEW client connected, who's ip should appear in this log message.");
   NDC.pop();
  }
  NDC.remove();
  log.info("Finished.");
 }
}


注意配置文g中的布局格式中一定要加上%x。系l输出如下:

INFO   - Make sure %x is in your layout pattern!
INFO  192.168.0.10 - A NEW client connected, who's ip should appear in this log 
message.
INFO  192.168.0.27 - A NEW client connected, who's ip should appear in this log 
message.
INFO   - Finished.


使用Log4Jq是JDK logging API

从JDK 1.4.0开始,引入了java.util.logging包。虽然Log4J组曄力游说JCPQJava Community ProcessQ采用Log4J作ؓJDK 1.4的“标准”日志APIQ虽然最l因Sun的日志API规范的负责hGraham Hamilton的一句“Merlin的开发已l到了最后阶D,q时不允许再对主要API做出改变”而没有被采纳Q但Log4Jq是Ҏ的日志API产生了重要媄响。那么,我们到底应该采用Log4Jq是java.util.logging包呢Q下面仅对两者做一单的比较?

1. Log4J更加成熟Q从1999q?0月开始至今已l有3q的旉Qƈ且已l在许多目中有着成熟的应用。而JDK中的logging包是?.4之后才引入的Qƈ且不能运行于JDK 1.3之前的版本。Log4J则可以良好地q行于JDK 1.1之后的所有版本?

2. Log4J已经被移植到多种环境下,包括log4cQCQ、log4cppQC++Q、log4perlQPerlQ、log4netQ?netQ等。在q些环境下,可以感受到几乎一致的配置和用方式。这是JDK中的logging API所不能比拟的?

3. Log4Jq具有更加强力的格式化系l,可以使记录输出时实现单的模式。但是,它不会增加类而导致格式化工具的扩展。众多的附加E序和处理器使得Log4J数据包成Z个绝佳的选择Q所有你所需要的都可能加以实现?

4. Log4J在性能上做了最大的优化?

Logging API对于单的使用是够的Q但它缺了许多Log4J所h的功能。所以,如果你需要一个强力的logging机制Q就应坚持用Log4JQ而如果只是需要一些简单的控制或文件记录,那么可以使用已经内徏在JDK之中的logging API?

虽然Log4J和JDK logging API是一U竞争关p,但在logging APIq在JCP中讨论(JSR47Q时Q两者之间就已经开始相互媄响了?

FAQ

1. 如何让Log4J使用指定的配|文?/B>

在启动你的应用时植入pȝ属性。例如,可以把上面的log4j.properties文g攑ֈ\properties的相对\径下Qƈ改名为log.propertiesQ此时如果让Log4J能够扑ֈq个配置文gq正地初始化,需要这栯行程序:

D:\..\java -Dlog4j.configuration=. \properties\log.properties YourAppName


Z么一定要使用pȝ属性,而不在配|文件中指定呢?很显Ӟ如果把它写入配置文gQ那么,Log4Jd它时已经q了?

2. 如何查看到Log4J的配|过E?/B>

可以cM1中的那样Q设|系l属性log4j.debug=trueQ从而打开Log4J的Verbose模式Q此时会输出Log4J的初始化q程Q这样就会对Log4J的启动有一个更详细的了解。下面是Log4J启动信息的一个示例:

log4j: Trying to find [log4j.xml] using context classloader 
sun.misc.Launcher$AppClassLoader@92e78c.
log4j: Trying to find [log4j.xml] using sun.misc.Launcher$ExtClassLoader@9fbe93class 
loader.
log4j: Trying to find [log4j.xml] using ClassLoader.getSystemResource().
log4j: Trying to find [log4j.properties] using context classloader 
sun.misc.Launcher$AppClassLoader@92e78c.
log4j: Using URL [file:/D:/java/logging/src/log4j.properties] for automatic log4j 
configuration.
log4j: Reading configuration from URL file:/E:/java/logging/src/log4j.properties
log4j: Parsing for [root] with value=[DEBUG, A1].
log4j: Level token is [DEBUG].
log4j: Category root set to DEBUG
log4j: Parsing appender named "A1".
log4j: Parsing layout options for "A1".
log4j: Setting property [conversionPattern] to [%d %l %-5p %c [%t] - %m%n].
log4j: End of parsing for "A1".
log4j: Parsed "A1" options.
log4j: Finished configuring.
......  // 下面是应用的日志信息,省略?/CCID_CODE>



]]>
版本控制工具Q-CVShttp://www.tkk7.com/sgsoft/articles/737.html天一?/dc:creator>天一?/author>Wed, 26 Jan 2005 11:11:00 GMThttp://www.tkk7.com/sgsoft/articles/737.htmlhttp://www.tkk7.com/sgsoft/comments/737.htmlhttp://www.tkk7.com/sgsoft/articles/737.html#Feedback0http://www.tkk7.com/sgsoft/comments/commentRss/737.htmlhttp://www.tkk7.com/sgsoft/services/trackbacks/737.html版本控制工具
    版本控制是程序开发、管理必不可的工具Q特别是在多人协作的团队中,适宜的版本控制工具可以提高开发效率,消除很多有代码版本带来的问题。本文首先列举没有版本控制工h可能遇到的问题,再对L版本控制工具做概要介l,之后对作为Java开发者首选的版本控制工具CVS的历双Ӏ功能、概念做详细的介l;最后在EclipseQCVS环境中,以CVS使用的一个完整流Eؓ例,介绍如何正确的用CVS工具?BR>
Z么要使用版本控制工具Q?/STRONG>
如果没有版本控制工具的协助,在开发中我们l常会遇C面的一些问题:
一?nbsp;代码理混ؕ。如果是别hd或删除一个文Ӟ你很隑֏现。没有办法对文g代码的修改追查跟t。甚臛_现文件丢失,或新版本代码被同伴无意覆盖等现象?BR>二?nbsp;解决代码冲突困难。当大家同时修改一个公共文件时Q解决代码冲H是一件很头疼的事。最原始的办法是手工打开冲突文gQ逐行比较Q再手工_脓复制。更高的做法是使用文g比较工具Q但仍省不了J杂的手工操作,一不小心,甚至会引入新的bug?BR>三?nbsp;在代码整合期间引入深层BUG。例如开发者A写了一个公共函敎ͼB觉得正好可以复用Q后来A又对q个公共函数q行了修改,d了新的逻辑Q而这个改动的却是B不想要的。或者是A发现q个公共函数不够用,又新做了一个函敎ͼB却没有及时获得通知。这些,都ؓ深层BUG留下隐患?BR>四?nbsp;无法对代码的拥有者进行权限控制。代码完全暴露在所有的开发者面前,M人都可以随意q行增、删、改操作Q无法指定明的人对代码q行负责。特别是产品的开发,q是极其危险的?BR>五?nbsp;目不同版本发布困难。特别是对品的开发,你会频繁的进行版本发布,q时如果没有一个有效的理产品版本的工P一切将变得非常艰难?BR>    上面只是列D了一些没有版本控制系l可能带来的问题Q特别是对大型项目和异地协同开发有了一个合适的版本控制工具Q它可以有效解决因ؓ代码版本不同引v的各U问题,让我们的开发h员能更多的把_֊p在开发上面。而不是每ơ都p很多旉q行代码整合和解决版本不同带来的各种问题?/P>

L版本控制工具介绍
    现在Q有很多优秀的版本控制工具供我们选择Q下面就五种L的版本控制工具做单的介绍?BR>Starteam
    是一个集合了版本控制、构建管理(Build ManagementQ和~陷跟踪pȝZ体的软gQƈ且具有强大的囑Ş界面Q易学易用;但管理复杂、维护困难?002q底被Borland公司收购?BR>PVCS Version Manager
     是美国的MERANT公司软g配置理工具PVCS 家族中的一个组成部分,它能够实现源代码、可执行文g、应用文件、图形文件和文档的版本管理;它能安全地支持Y件ƈ行开发,对多个Y件版本的变更q行有效的控制管理?BR>ClearCase(CC)
    是ROSE构g的一部分Q目前最牛的配置理工具Q主要应用于复杂的品发放、分布式团队合作、ƈ行的开发和l护d。可以控制word, excel,powerpointQvisio{文件格式,对于不认识的格式可以自己定义一U类型来标识?BR>Visual SourceSafeQVSSQ?BR>    单易用、方侉K效、与Windows操作pȝ及微软开发工具高度集成?BR>CVSQConcurrent Versions SystemQ?BR>    是开发源码的q发版本pȝ,它是目前最行的面向Y件开发h员的源代码版本管理解x案。它可用于各U^収ͼ包括 Linux 、Unix?Windows NT/2000/XP{等?BR>    前面三种是重量的商业版本控制工P更适合庞大的团队和目Qƈ且hg菌ӀVisual SourceSafe是微软的产品Q当然只能用在windowsq_q与微Y的开发工h~集成。CVS免费开源,q且几乎所有开源项目都是用CVSq行版本理Q无疑,它是我们Java开发者最优选择?BR>
CVS的历双Ӏ功能、基本概늚介绍

历史

    CVS 诞生?1986 q_当时作ؓ一l?shell 脚本而出玎ͼ1989q?月,Brian Berlinor用C语言重新设计q编写了CVS的代码;1993q前后,Jim Kingdon最l将CVS设计成基于网l的q_Q开发者们能从InternetM地方获得E序源代码。截至目前最新版本是2004q?2?3日发布的1.12.11?BR>
功能介绍
一?nbsp;代码l一理Q保存所有代码文件更改的历史记录。对代码q行集中l一理Q可以方便查看新增或删除的文Ӟ能够跟踪所有代码改动痕qV可以随意恢复到以前L一个历史版本。ƈ避免了因为版本不同引入的深层BUG?BR>二?nbsp;完善的冲H解x案,可以方便的解x件冲H问题,而不需要借助其它的文件比较工具和手工的粘贴复制?BR>三?nbsp;代码权限的管理。可以ؓ不同的用戯|不同的权限。可以设|访问用L密码、只诅R修改等权限Q而且通过CVS ROOT目录下的脚本Q提供了相应功能扩充的接口,不但可以完成_的权限控Ӟq能完成更加个性化的功能?BR>四?nbsp;支持方便的版本发布和分支功能?/P>

基本概念
资源库(RepositoryQ?/STRONG>
CVS的资源库存储全部的版本控制下的文件copyQ通常不容许直接访问,只能通过cvs命oQ获得一份本地copyQ改动后再check inQcommitQ回资源库。而资源库通常Z工作目录分离的。CVS通过多种方式讉K资源库。每U方法有不同目录表示形式?BR>版本QRevisionQ?/STRONG>
每一个文件的各个版本都不相同QŞ?.1, 1.2.1,一?.1是该文g的第一个revisionQ后面的一个将自动增加最右面的一个整敎ͼ比如1.2, 1.3, 1.4...有时候会出现1.3.2.2Q原因见后。revisionL偶数个数字。一般情况下revision看作时CVS自己内部的一个编P而tag则可以标志用L特定信息?BR>标签QTagQ?/STRONG>
用符号化的表C方法标志文件特定revision的信息。通常不需要对某一个孤立的文g作tagQ而是Ҏ有文件同时作一个tagQ以后用户可以仅向特定tag的文件提交或者checkout。另外一个作用是在发布Y件的时候表C哪些文件及其哪个版本是可用的;各文件不同revision可以包括在一个tag中。如果命名一个已存在的tag默认不会覆盖原来的Q?BR>分支QBranchQ?BR>当用户修改一个branch时不会对另外的branch产生M影响。可以在适当的时候通过合ƈ的方法将两个版本合v来;branchL在当前revision后面加上一个偶数整敎ͼ?开始,?l束Q,所以branchL奇数个数字,比如1.2后面branch?.2.2Q该分支下revision可能?.2.2.1,1.2.2.2,...
冲突QConflctQ?BR>完全是纯文本的冲H,不包含逻辑上的矛盾。一般是一份文ӞA做了改动QB在A提交之前也做了改动,q样最后谁commit׃出现冲突Q需要手工解军_H再提交?BR>
CVS与eclipse集成开?/STRONG>
  前面对CVS的历双Ӏ功能、概论等理论知识做了介绍。下面我们将使用最行的Java IDE Eclipse中内|的CVS工具Q以一个完整开发流E,介绍实际环境中CVS的正用。关于CVSpȝ的安装,不是本文的内容,您可以从附录的链接中获取安装的介l资料?BR>
常用的CVS控制命o
Check OutQ检出)
把源文g从cvs源代码仓库中取出Q缺省的版本是最新的版本Q你也可以选择指定的版本。在每次更改源代码之前,需要Check Out最新的版本Q再起基之上Ҏ代码q行修改。将代码目录checkout到指定目录下Q所有文仉是read-write?BR>Check InQ检入)
把源代码加入到cvs源代码仓库中Q每一个添加进代码库中的文件的版本?1.1。以后每ơ修Ҏ仉新ci以后Q此文g的版本递增?.2 Q?.3.……。在每次Ҏ代码修改之后Q需要Check InQ提交最新版本的源代码?BR>Synchronize with Repository(与资源库同步Q简U同?
使本地更改与资源库同步,它会列出本地和资源库之间不同的所有文件?BR>Add to Version Control
新的文件加入到版本控制之中?BR>Add to .cvsIgnore
文件设|到版本控制之外Q这栯文g或目录中的文件的更改在CVS中不可见Q即使同步也无法发现?/P>

CVS正确使用步骤
一?nbsp;同步QSynchronizeQ?/STRONG>
是本地更改与服务器同步,同步之后可以清晰的看C一捡出QCheck OutQ版本之后本地、服务器上的最新改动。这是非常有用的Q特别是敏捷开发,集体拥有代码。有了同步功能,你可以全局把握目的代码,可以很方便的跟踪公共模块代码的Q何改动?BR>具体操作Q在Eclipse的资源视图(Resource PerspectiveQ或者Java视图QJava PerspectiveQ中Q选中要同步的目录Q点d键选择"Synchronize with Repository",之后它将昄同步的视图。如下图Q?BR> 
(图一、CVS同步视图)
同步之后Q它有四UMode可以选择Q见上图l色框框里按钮。从做到叛_别ؓQ?BR>Incoming ModeQ表CZҎ来自服务器,对应于更斎ͼupdateQ操作?BR>Outgoing ModeQ表CZҎ来自本地Q对应提交(commitQ操作?BR>Incoming/ Outgoing ModeQ本地和服务器修攚w在该模式QModeQ中昄?BR>Conflicts ModeQ显C本地和服务器修改的冲突文g?BR>二?nbsp;更新QupdateQ?BR>比较单,选择Incoming ModeQ再选中要更新的文gQ右键选择update操作?BR>三?nbsp;解决冲突q合q?solve conflct and merge)
如果有冲H文Ӟ冲突文g不能更新。你必须先解军_H再操作。选中冲突的文Ӟ再点右键选择"Open in Compare Editor"Q用比较工具打开该文件。如下图Q?

Q图二、CVS比较器视图)

比较器(CompareQ视图,左边版本底的是本地文ӞLocal FileQ,双是远E服务器文gQRemote FileQ。?Select Next Change"按钮Q绿框中的第一头向下按钮Q,逐一查看不同炏V如果不同点标识为黑色框框,则不用管它。如果是蓝色框框Q则需要手工调整。如上图Q不同点是蓝色框框,鼠标放C个不同点的中间小Ҏ中,则凸Z个向右的按钮Qƈ昄提示信息"Copy Current Change from Right to Left"Q意思是右Ҏ务器的不同点覆盖到左边的本地文g。点中此按钮。重复这L操作Q将所有服务器上的更改拯到本地?BR>如果有一行代码,本地和服务器都同时做了修攏V这Ӟ修改点则昄U色框框。这Ӟ你就必须手工做正的修改。全部修改完成,保存本地文g?BR>此时Q如果修改点没有了蓝色的框框Q就可以开始做合ƈQmergeQ操作了。操作也很简单,选择该文Ӟ点击右键Q选择"Mark as merged"?BR>注意Q必ȝ保没有蓝色框框,卛_全拷贝了服务器的修改才可以做合ƈQmergeQ操作,否则会覆盖服务器上的代码?BR>四?nbsp;提交QcommitQ?BR>更新服务器代码,解决冲突之后Q首先要查看本地文g修改之后是否有错误。如果有Q当焉先解决错误,再提交?BR>
附录Q?/STRONG>
http://www.8848software.com/scmchina/scmtools.htm 由很多版本控制工L文档链接?BR>http://www.perforce.com/perforce/reviews.html Infrastructure Group对Perforce 和Clearcase, CVS, PVCS,Visual SourceSafe (VSS)几种CM工具q行了定量和定性的比较. 对于定性的比较Q内Ҏ及工h持的Ҏ和环境;对于定量的比较,包括在不同的目规模上,执行不同的活动所需要的旉?/P>

---------------------------

讨论摘录Q?/P>

单就功能来说QSubversion 比v CVS q是要多了一些东西的Q有些还是不错的Q至于“市场”,q是要看需求,现在已经有一些开元Y件在使用 Subversion 了?

觉得 WinCVS 不好用的Q可以试?TortoiseCVSQ这是一个完全与资源理器集成的 CVS 工具Q比较符?Windows 使用者的思\Q很Ҏ上手Q主| http://tortoisecvs.sourceforge.net?

对于 SubversionQ也有相似的工具 TortoiseSVNQ用方法几乎完全相同,主页?A >http://tortoisesvn.tigris.org?

 

同样Q网站上也提供了其他关于软g配置理工具的部分学习资料下载:
参考:http://www.askguoyu.com

 


 


 

 

 



]]>
Java Web开发资?http://www.tkk7.com/sgsoft/articles/545.html天一?/dc:creator>天一?/author>Fri, 21 Jan 2005 07:49:00 GMThttp://www.tkk7.com/sgsoft/articles/545.htmlhttp://www.tkk7.com/sgsoft/comments/545.htmlhttp://www.tkk7.com/sgsoft/articles/545.html#Feedback0http://www.tkk7.com/sgsoft/comments/commentRss/545.htmlhttp://www.tkk7.com/sgsoft/services/trackbacks/545.html
      http://jakarta.apache.org tomcat的老家
      http://struts.apache.org Struts 一?MVC 的开发框?
      http://www.opensymphony.com/webwork/ WebWork官方|站Q又一个流行的MVC开发框?
      http://forum.javaeye.com Java视线论坛Q里面的Java牛h很多Q值得一看?
      http://www.springframework.com Spring 是一个非常好的AOP框架

      现在Java Web比较行的开发Ş式有Q?

      Freemarker + Webwork + Spring + Hibernate

      View层可参考的框架包括 Sitemesh/Freemarker/ECHO/Display Tag/JSTL/Velocity ...
      MVC框架包括QStruts/WebWork/Tapestry/Spring MVC/JSF ...
      持久层框架包括:Hibernate/iBatis ...

      到各个论坛找扄Q有不少资料Q特别是M面提到的Java视线论坛看看Q会有不收莗?

      一些开发好的应用,供参考:
      http://www.mvnforum.com  一个开源论?
      http://www.opencms.org 一个CMS
      http://www.magnolia.info 一个CMS
      http://www.laoer.com/ 开源社区,使用Hibernate+Spring+Struts
      http://www.fiyu.net 飞鱼论坛Q开源)
      http://www.chinaz.com/ 中国站长?里面有JSP源码下蝲

      希望上面的信息能l你一些帮助?

]]>
iBATIS SQL MapsQ二Q? 选择?rosen ?Blog http://www.tkk7.com/sgsoft/articles/424.html天一?/dc:creator>天一?/author>Tue, 18 Jan 2005 05:17:00 GMThttp://www.tkk7.com/sgsoft/articles/424.htmlhttp://www.tkk7.com/sgsoft/comments/424.htmlhttp://www.tkk7.com/sgsoft/articles/424.html#Feedback1http://www.tkk7.com/sgsoft/comments/commentRss/424.htmlhttp://www.tkk7.com/sgsoft/services/trackbacks/424.html  让我们重回到车辆理pȝ和张三的故事中?/SPAN>

   

    ?/SPAN> iBATIS SQL Maps 的世界里也存?/SPAN> one-to-many?/SPAN>many-to-one 的关p,惛_你已l对q些概念驾轻q了。好Q还是每?/SPAN> People 对应多条 AutoInfo 信息?/SPAN>

   

    本系列文章第一部分提到q?/SPAN> iBATIS SQL Maps 的映文件个数可以h定,但是Q把一l有共性的操作攑֜一h首选策略。下面我们看看ؓ张三首次买R所生成的映文件是怎样的:

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE sqlMap
    PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
    "
http://www.ibatis.com/dtd/sql-map-2.dtd">

<sqlMap namespace="AutoMag">

  <insert id="insertPeople" parameterClass="bo.People">
    <![CDATA[
      insert into people (name, address) values (#name#, #address#)
    ]]>
      <selectKey resultClass="java.lang.Integer" keyProperty="id">
    <![CDATA[
          select last_insert_id();
    ]]>
      </selectKey>
  </insert>
 
  <insert id="insertAutoInfo" parameterClass="bo.AutoInfo">
    <![CDATA[
      insert into auto_info (license_plate, owner_no) VALUES (#licensePlate#, #ownerNo.id#)
    ]]>
  </insert>
</sqlMap>

 

sqlMap

sqlMap 元素拥有属?/SPAN> namespace="?Q定义了?/SPAN> XML 文g命名I间。如果你在配|文?/SPAN> SqlMapConfig.xml 中指定了 settings 元素的属?/SPAN> useStatementNamespaces="true"Q那么就可以按照命名I间的方式访?/SPAN> Mapped statementQ比?/SPAN> namespace=" AutoMag"Q相?/SPAN> Java 代码Q?/SPAN>sqlMap.insert("AutoMag.insertPeople",people)。这样做是ؓ了防止不同映文件中出现同名 Mapped statement 而生冲H。什么是 Mapped statement Q?/SPAN>

 

Mapped statement

iBATIS SQL Maps 的核心概念就?/SPAN> Mapped statementQ?/SPAN>Mapped Statement 可以使用L?/SPAN> SQL 语句Q利?/SPAN> POJO?/SPAN>原始变量及其 Wrapper Class 作ؓ输入Q?/SPAN>parameter classQ和输出Q?/SPAN>result classQ?/SPAN>

Mapped Statement 包含以下几种cdQ?/SPAN>

 

insert 对应数据库的 insert 操作Q该操作q回本次操作插入记录的主键倹{?/SPAN>

select 对应数据库的 select 操作Q该操作q回特定?/SPAN> POJO ?/SPAN> 对象?/SPAN>

update 对应数据库的 update 操作Q该操作q回被更新的记录个数?/SPAN>

delete 对应数据库的 delete 操作Q该操作q回被删除的记录个数?/SPAN>

procedure 对应数据库存储过E?/SPAN>

statement cd最为通用Q可以代替以上所有的cd。但׃~Z操作直观性故不推荐?/SPAN>

 

insert id="insertPeople" parameterClass="bo.People"

定义?/SPAN> insert cd?/SPAN> Mapped Statement。属?/SPAN> id="insertPeople" 定义操作名称Q?/SPAN>parameterClass="bo.People" 定义传入参数?/SPAN> People 对象实例Q框架可保其属性持久化到数据库相应字段中。由?/SPAN> SQL 语句会包?/SPAN>?lt;>?/SPAN>q样的符PҎ?/SPAN> XML 产生冲突Q放q?/SPAN> <![CDATA[…?/SPAN>]]> 区域可避免?/SPAN>insert into people (name, address) values (#name#, #address#)Q?/SPAN>是一条普通的 SQL 语句Q?/SPAN>?name#?/SPAN>#address#?/SPAN>利用 Java 反射机制讉K People 对象实例的相应属性?/SPAN>

 

selectKey resultClass="java.lang.Integer" keyProperty="id"

iBATIS SQL Maps 通过 <insert> 元素的子元素 < selectKey> 来支持主键自动生成?/SPAN> resultClass="java.lang.Integer" 定义q回对象?/SPAN> int ?/SPAN> Wrapper Class?/SPAN>keyProperty="id" 定义了主键名U。本例是 MySQL 主键生成方式Q参考官Ҏ档,MySQL 的主键生成无需Zؓ来控Ӟ也就是说可不使用 <selectKey> 而由数据库自动处理。但我测试发玎ͼ在执?/SPAN> insert 操作以后Q程序没有返?/SPAN>本次操作插入记录的主键倹{在官方论坛上也有很多用hL疑惑Q作者的{复是:q和 JDBC Driver 有关pR不可能把驱动一一试吧?一x逸的办法是?/SPAN> <selectKey> 元素。以下是 Oracle ?/SPAN> SQL Server 主键生成ҎQ?/SPAN>

 

< !- Oracle ->
<insert id="insertProduct-ORACLE" parameterClass="com.domain.Product">
  <selectKey resultClass="int" keyProperty="id" >
    SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL
  </selectKey>
  insert into PRODUCT (PRD_ID,PRD_DESCRIPTION) values (#id#,#description#)
</insert>

<!- Microsoft SQL Server ->
<insert id="insertProduct-MS-SQL" parameterClass="com.domain.Product">
  insert into PRODUCT (PRD_DESCRIPTION) values (#description#)
  <selectKey resultClass="int" keyProperty="id" >
  SELECT @@IDENTITY AS ID
  </selectKey>
</insert>

 

insert into auto_info (license_plate, owner_no) VALUES (#licensePlate#, #ownerNo.id#)

在插入了 people 记录后,要ؓ auto_info 插入记录。基本原则和之前遇到q的一P只是?/SPAN>owner_no?/SPAN>q个字段值由 AutoInfo 对象属?/SPAN>?/SPAN>ownerNo?/SPAN>获得Q该属性类型ؓ People。这是由于我沿用?/SPAN> Hibernate 产生?/SPAN> POJOQ如果你愿意Q完全可以把?/SPAN>ownerNo?/SPAN>替换?/SPAN> Integer cd?/SPAN>

 

~程中几个关键对?/SPAN>

    com.ibatis.common.resources.Resources 对象负责?/SPAN> XML 得到 java.io.Reader 抽象cȝ实例Q供工厂Ҏ调用?/SPAN>

com.ibatis.sqlmap.client.SqlMapClientBuilder 构?/SPAN> SqlMapClient 实例?/SPAN>

    com.ibatis.sqlmap.client.SqlMapClient ?/SPAN> iBATIS SQL Maps 核心lgQ可以说我们的编E工作都是围l着它展开?/SPAN>

 

形成?/SPAN> one-to-many 保存如下Q?/SPAN>

 

package test;

import java.io.Reader;

import com.ibatis.sqlmap.client.*;
import com.ibatis.common.resources.*;

import bo.*;

public class AutoMag {

 private Reader reader;
 private SqlMapClient sqlMap;
 private String resource = "SqlMapConfig.xml";
 
 public void insertPeople() throws Exception{
  try{
   reader = Resources.getResourceAsReader(resource);
      sqlMap=SqlMapClientBuilder.buildSqlMapClient(reader);
      sqlMap.startTransaction();
     
      People people=new People();
      people.setName("张三");
      people.setAddress("中国");
            sqlMap.insert("insertPeople",people);

            AutoInfo autoInfo=new AutoInfo();
            autoInfo.setLicensePlate("A00001");
            autoInfo.setOwnerNo(people);           
      sqlMap.insert("insertAutoInfo",autoInfo);
     
      sqlMap.commitTransaction();
  }finally{
   sqlMap.endTransaction();
  }
 }
}

 

E序?/SPAN> Hibernate 写法差不多,我感觉甚x Hibernate 更简单。我可以昄的进?/SPAN> insert 操作Q符合传l?/SPAN> JDBC ~程习惯?/SPAN>iBATIS SQL Maps 支持自动事务处理Q可以不用写?/SPAN> startTransaction?/SPAN>commitTransaction。但如果 People insert 操作成功Q?/SPAN> AutoInfo insert 操作p|Q就破坏了两?/SPAN> insert 操作的原子性。最?/SPAN> endTransaction 包含异常情况下的回滚事务和关闭连接池q接两种功能?BR>

Q请注意Q引用、{贴本文应注明原作者:Rosen Jiang 以及出处Q?/FONT>http://blog.csdn.net/rosenQ?/STRONG>



]]>
iBATIS SQL MapsQ一Q? 选择?rosen ?Blog http://www.tkk7.com/sgsoft/articles/423.html天一?/dc:creator>天一?/author>Tue, 18 Jan 2005 05:15:00 GMThttp://www.tkk7.com/sgsoft/articles/423.htmlhttp://www.tkk7.com/sgsoft/comments/423.htmlhttp://www.tkk7.com/sgsoft/articles/423.html#Feedback2http://www.tkk7.com/sgsoft/comments/commentRss/423.htmlhttp://www.tkk7.com/sgsoft/services/trackbacks/423.html 

  

什么是 iBATIS Q?/SPAN>


    和众多的
SourceForge 开源项目一PiBATIS 曄也是其中的一员。在2004q?/SPAN>11?/SPAN>3日成功地成ؓ?/SPAN> Apache Incubator 下的子项目?/SPAN> iBATIS 包括 for Java ?/SPAN> for .NET 两个版本Q?/SPAN>for Java 版提供了SQL Maps ?/SPAN> DAO 框架Q?/SPAN>for .NET 只提供了 SQL Maps 框架。从现在开始我们只?/SPAN> for Java 版的SQL Maps 展开讨论?/SPAN>

 

你可以在 http://www.ibatis.com 下蝲 iBATIS for JavaQ目前最新版本是2.0.9Q压~包里已l包含了 SQL Maps(ibatis-sqlmap-2.jar) ?/SPAN> DAO 框架(ibatis-dao-2.jar)?/SPAN>


Z么选择 iBATIS Q?/SPAN>


    也许各位看官已在各种不同的技术资料上了解到它的优ѝ但是对于我来说Q选择它的理由只有一?/SPAN>——?/SPAN>利用原有资源?/SPAN>?/SPAN>Hibernate 的却优秀Q但是用来整合原有系l,它却很难胜Q。例如,以前在进行数据徏模时使用了复合主键、跨多表生的几十甚至上百行的查询语句、利用原有存储过E?/SPAN>??当面临这一pd问题?/SPAN> Hibernate 显得力不从心了Q要想?/SPAN> Hibernate只能改造原有系l!

 

当我面;pȝ整合问题Ӟ整合的要求很单:只需要保留原有系l查询部分)Q?/SPAN>iBATIS q入了我的视Uѝ原有系l中?/SPAN> SQL语句需要小的修改外,数据表、查询结果都不需要改变!也不用像 Hibernate 那样映射Z多的配置文g?/SPAN>POJOQ一下子清爽了很多?/SPAN>BTWQ?/SPAN>Hibernate q种做法没有错!只是我只需要查询功能,仅仅是取我所好而已Q避免了杀鸡用牛刀?/SPAN>

 

目前Q系l整合已l结束,׃一个月旉。如果?/SPAN> HibernateQ恐怕我现在q在为怎么设计数据表、怎样?/SPAN>HQL而和同事争论?/SPAN>

 

开始另一?/SPAN> O/R Mapping 之旅


    上次
O/R Mapping 之旅?/SPAN> BO、数据表q可以重用,只是把数据库q移C MySQL?SPAN style="COLOR: black">打开 EclipseQ新Z?/SPAN> Resin 目。把 ibatis-sqlmap-2.jar ?/SPAN> ibatis-common-2.jar 拯?/SPAN> lib 目录下,再导入项目。和 Hibernate 自动配置、自动映相比,iBATIS 的一切都是手工完成的?/SPAN>

   

    ?/SPAN> src 下徏立配|文?/SPAN> SqlMapConfig.xmlQ数据库链接、连接池?/SPAN>SqlMap 映射文g??q些都要靠它了。官方参考手册对怎样q行讄有很详细的描qͼ我只对要用到的地方进行粗略说明?BR>

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig
    PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"
    "
http://www.ibatis.com/dtd/sql-map-config-2.dtd">

<sqlMapConfig>

  <settings
    cacheModelsEnabled="true"
    enhancementEnabled="true"
    lazyLoadingEnabled="true"
    errorTracingEnabled="false"
    maxRequests="32"
    maxSessions="10"
    maxTransactions="5"
    useStatementNamespaces="false"
    />

  <transactionManager type="JDBC">
    <dataSource type="SIMPLE">
      <property name="JDBC.Driver" value="org.gjt.mm.mysql.Driver"/>
      <property name="JDBC.ConnectionURL" value="jdbc:mysql://localhost/new_db?useUnicode=true"/>
      <property name="JDBC.Username" value="root"/>
      <property name="JDBC.Password" value=""/>
      <property name="JDBC.DefaultAutoCommit" value="true"/>
      <property name="Pool.MaximumActiveConnections" value="10"/>
      <property name="Pool.MaximumIdleConnections" value="5"/>
      <property name="Pool.MaximumCheckoutTime" value="120000"/>
      <property name="Pool.TimeToWait" value="500"/>
      <property name="Pool.PingQuery" value="select 1 from ACCOUNT"/>
      <property name="Pool.PingEnabled" value="false"/>
      <property name="Pool.PingConnectionsOlderThan" value="1"/>
      <property name="Pool.PingConnectionsNotUsedFor" value="1"/>
      <property name="Pool.QuietMode" value="true"/>
    </dataSource>
  </transactionManager>

  <sqlMap resource="bo/mapping/AutoMag.xml"/>
</sqlMapConfig>


transactionManager
元素定义?/SPAN> iBATIS 事务理器?/SPAN>type 可选项包括Q?/SPAN> 

JDBCQ通过传统 JDBC 来管理事务?/SPAN>

JTAQ用一?/SPAN> JTA 全局事务Q iBATIS 的事务包括在更大的事务范围内Q跨数据?/SPAN>Session的)Q这个更大的事务范围可能包括了其他的数据库和事务资源。这个配|需要一?/SPAN> UserTransaction 属性,以便?/SPAN> JNDI 获得一?/SPAN> UserTransaction?/SPAN> 

EXTERNALQ调?/FONT> iBATIS 以外的其他事务管理器来管理事务?/SPAN>

 

dataSource 元素?/SPAN> transactionManager 的一部分?/SPAN>type 可选项包括Q?BR>
   
SIMPLEQ?/SPAN>SIMPLE ?/SPAN> iBATIS 内置?/SPAN> dataSource 实现Q其中实C一个简单的数据库连接池Q当无容器提?/SPAN> DataSource 服务时可以用该选项Q对?/SPAN> iBATIS 实现cMؓcom.ibatis.sqlmap.engine.datasource.SimpleDataSourceFactory?/SPAN>

?    DBCP:Z Apache DBCP q接?/SPAN> API 实现?/SPAN> DataSourceQ当无容器提?/SPAN> DataSource 服务Ӟ可以使用该选项Q对?/SPAN> iBATIS 实现cMؓ com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory?/SPAN>

?JNDIQ?/SPAN> J2EE 容器提供?/SPAN> DataSource 实现Q?/SPAN>DataSource 通过指定?/SPAN> JNDI Name 从容器中获取。对?/SPAN> iBATIS 实现cMؓ com.ibatis.sqlmap.engine.datasource.JndiDataSourceFactory?/SPAN>

    注意Q?/SPAN>每种 dataSource 元素?/FONT> property 都有不同的地方,不能光把 type 名字改了了事?/SPAN>

 

sqlMap 元素定义了映文件的存放位置Q配|文件中可包含多?/FONT> sqlMap 元素Q比如:

<sqlMap resource="mapping/AutoMag1.xml"/>
<sqlMap resource="bo/mapping/AutoMag2.xml"/>

<sqlMap url="file:///c:/eclipse/workspace/iBATISTest/src/bo/ AutoMag2.xml "/>
??/FONT>

你也许已发现Q我只定义了单个映射文g。不错,?/FONT> Hibernate 的一个表一个映文件不同,iBATIS 的映文件个数可以h为控Ӟ颗粒度自己掌握?/SPAN>

 

    光有 BO 和配|文件还不行Q还要ؓ本次试创徏试c?/FONT> AutoMag.java。完整的布局如下所C:

                                        


    以下?SPAN lang=EN-US> iBATIS SQL Maps 工作程Q对于理解概念很有帮助。大意是 1、你可以?JavaBean、Map cd、原始变量(或者它们的Wrapper ClassQ、XML 数据作ؓ传入对象Q?、通过配置文g载入映射文gQ?、利用框架翻译成 JDBC 来访问数据库Q?、执行结果可以是 JavaBean、Map cd、原始变量(或者它们的Wrapper ClassQ、XML 数据?BR>
                       
       

Q请注意Q引用、{贴本文应注明原作者:Rosen Jiang 以及出处Q?/FONT>http://blog.csdn.net/rosenQ?/STRONG>



]]>

前段旉写了?/SPAN> Hibernate 斚w?/SPAN>pd文章Q网友们反映q不错。在接下来的旉里,我将会引入另外一U?/SPAN> O/R Mapping 解决Ҏ—?B style="mso-bidi-font-weight: normal">iBATISQ本pd沿?/SPAN> Hibernate pd文章的风根{?/SPAN>

վ֩ģ壺 ޾ƷƷþ99һ| ؼëƬˬwwwѰ| ƷAò| ѹۿƬëƬ| 222wwwƵ| ߹ۿ| ˸һ91| һ߹ۿ| ҹҹAһ | ƷƵվ| ûɫվ| ҹ˾ѿƬ| ˳Ƶ߲Ų| vavava| AVһţţ| ɫͼ.com| ޹Ʒ߹ۿ | 99þþù| ޾Ʒ2021| ձ| ɫƷվ| Ӱ| ޾ƷĻ| Ļ޵һ| avպav߹ۿ| Ļպ| պ޶| ޾ƷƬ߹ۿ| ѹ˸Ƶվ| ѿŮͰ| ƷѾþþþþóӰԺ| aƬѹۿ| ߳ٸëˮˮ| A߹ۿվȫ| ѾþþƷ99reѾy| 99Ʒ| ѿ13| AƬһ| ۲ӰԺѹۿ| avһ| ޹Ʒ߲|