??xml version="1.0" encoding="utf-8" standalone="yes"?>一区国严二区亚洲三区,九月丁香婷婷亚洲综合色,久久99亚洲网美利坚合众国 http://www.tkk7.com/sgsoft/category/12.htmlJAVA?/description>zh-cn Tue, 27 Feb 2007 23:56:48 GMT Tue, 27 Feb 2007 23:56:48 GMT 60 Zstruts+spring+ibatis?J2EE 开?/title> http://www.tkk7.com/sgsoft/articles/2257.html天一?/dc:creator>天一?/author>Sun, 20 Mar 2005 08:46:00 GMT http://www.tkk7.com/sgsoft/articles/2257.html http://www.tkk7.com/sgsoft/comments/2257.html http://www.tkk7.com/sgsoft/articles/2257.html#Feedback 1 http://www.tkk7.com/sgsoft/comments/commentRss/2257.html http://www.tkk7.com/sgsoft/services/trackbacks/2257.html 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> 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> ibatis 是一个功能强大实用的SQL Map工具Q不同于其他ORM工具Q如hibernateQ,它是SQL语句映射成Java对象Q而对于ORM工具Q它的SQL语句是根据映定义生成的。ibatis 以SQL开发的工作量和数据库移植性上的让步,为系l设计提供了更大的自q间。有ibatis代码生成的工P可以ҎDDL自动生成ibatis代码Q能减少很多工作量?BR> 2. JpetStoreq?BR> 2.1. 背景 最初是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> 2.2. JpetStore开发运行环境的建立 1、开发环?BR> Java SDK 1.4.2 Apache Tomcat 4.1.31 Eclipse-SDK-3.0.1-win32 HSQLDB 1.7.2 2、Eclipse插g EMF SDK 2.0.1QEclipse建模框架Qlomboz插g需要,可以使用runtime版本?BR>lomboz 3.0QJ2EE插gQ用来在Eclipse中开发J2EE应用E序 Spring IDE 1.0.3QSpring Bean配置理插g xmlbuddy_2.0.10Q编辑XMLQ用免费版功能即?BR>tomcatPluginV3Qtomcat理插g Properties EditorQ编辑java的属性文?q可以预览以及自动存盘ؓUnicode格式。免M手工或者ANT调用native2ascii的麻烦?BR>2.3. 架构
? JpetStore架构?/CENTER> ? 是JPetStore架构图。参照这个架构图Q让我们E微剖析一下源代码Q得出JpetStore 4.0的具体实现图Q图2Q,思\一下子p然开朗了。前a中提到的非传l的struts开发模式,关键在struts Actioncdform beancM?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> q样重心p{Udform bean上了Q它已经不是普通意义上的form bean了。查看源代码Q可以看到它不仅仅有数据和校?重置ҎQ而且已经h了行为,从这个意义上来说Q它更像一个BO(Business Object)。这是前文讲到的,BeanActioncd用反原理,ҎURL来决定调用form bean的哪个方法(行ؓQ。form bean的这些方法的{很简单,例如Q?BR>
public String myActionMethod()
{
//..work
return "success";
}
Ҏ的返回值直接就是字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扩展的一个有益尝试,虽然提供了非常好的应用开发模式,但是它还非常斎ͼ一直在发展中?
? JpetStore 4.0具体实现 2.4. 代码剖析 下面p我们开始进一步分析JpetStore4.0的源代码Qؓ下面的改造铺路。BeanAction.java是唯一一个Struts actionc,位于com.ibatis.struts包下。正如上文所aQ它是一个通用的控制类Q利用反机Ӟ把控制{Udform bean的某个方法来处理。详l处理过E参考其源代码,单明晰?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 1. URL Pattern
<action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction"
name="orderBean" scope="session"
validate="false">
<forward name="success" path="/order/ViewOrder.jsp"/>
</action>
此种方式表示Q控制将被{发到"orderBean"q个form bean对象 ?viewOrder"ҎQ行为)来处理。方法名?path"参数的以"/"分隔的最后一部分? 2. Method Parameter
<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>
此种方式表示Q控制将被{发到"orderBean"q个form bean对象?viewOrder"ҎQ行为)来处理。配|中?parameter"参数表示form beancM的方法?parameter"参数优先?path"参数? 3. No Method call
<action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction"
name="orderBean" parameter="*" scope="session"
validate="false">
<forward name="success" path="/order/ViewOrder.jsp"/>
</action>
此种方式表示Qform bean上没有Q何方法被调用。如果存?name"属性,则struts把表单参数等数据填充到form bean对象后,把控制{发到"success"。否则,如果name为空Q则直接转发控制?success"。这q当于struts内置的org.apache.struts.actions.ForwardAction的功?
<action path="/shop/viewOrder" type="org.apache.struts.actions.ForwardAction"
parameter="/order/ViewOrder.jsp " scope="session" validate="false">
</action>
ServicecM于com.ibatis.jpetstore.service包下Q属于业务层。这些类装了业务以及相应的事务控制。Servicecȝform beancL调用? com.ibatis.jpetstore.persistence.iface包下的类是DAO接口Q属于业务层Q其屏蔽了底层的数据库操作,供具体的ServicecL调用。DaoConfigcL工具c(DAO工厂c)QServicec通过DaoConfigcL获得相应的DAO接口Q而不用关心底层的具体数据库操作,实现了如?中{耦合2}的解耦? com.ibatis.jpetstore.persistence.sqlmapdao包下的类是对应DAO接口的具体实玎ͼ在JpetStore4.0中采用了ibatis来实现ORM。这些实现类l承BaseSqlMapDaoc,而BaseSqlMapDaocdl承ibatis DAO 框架中的SqlMapDaoTemplatecRibatis的配|文件存攑֜com.ibatis.jpetstore.persistence.sqlmapdao.sql目录下。这些类和配|文件位于数据层 DomaincM于com.ibatis.jpetstore.domain包下Q是普通的javabean。在q里用作数据传输对象QDTOQ,贯穿视图层、业务层和数据层Q用于在不同层之间传输数据。剩下的部分比较简单了Q请看具体的源代码,非常清晰? 2.5. 需要改造的地方 JpetStore4.0的关键就在struts Actioncdform beancMQ这也是其精华之一Q虽然该实现方式是试验性,待扩充和验证Q,在此ơ改造中我们要保留下来,x制层一点不变,表现层获取相应业务类的方式变了(要加载spring环境Q,其它保持不变。要特别x的改动是业务层和持久层,q运的是JpetStore4.0设计非常好,需要改动的地方非常,而且由模式可循,如下Q?BR> 1. 业务层和数据层用Spring BeanFactory机制理?BR>2. 业务层的事务由spring 的aop通过声明来完成?BR>3. 表现层(form beanQ获取业务类的方法改p定义工厂cL实现Q加载spring环境Q?BR> 3. JPetStore的改?BR> 3.1. 攚w后的架?BR>
其中U色部分是要增加的部分,蓝色部分是要修改的部分。下面就让我们逐一剖析?BR> 3.2. Spring Context的加?BR>Z在Struts中加载Spring ContextQ一般会在struts-config.xml的最后添加如下部分:
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/applicationContext.xml" />
</plug-in>
Spring在设计时充分考虑C与Struts的协同工作,通过内置的Struts Plug-in在两者之间提供了良好的结合点。但是,因ؓ在这里我们一点也不改动JPetStore的控制层(q是JpetStore4.0的精华之一)Q所以本文不准备采用此方式来加蝲ApplicationContext。我们利用的是spring framework 的BeanFactory机制,采用自定义的工具c(bean工厂c)来加载spring的配|文Ӟ从中可以看出Spring有多灉|Q它提供了各U不同的方式来用其不同的部?层次Q您只需要用你想用的Q不需要的部分可以不用? 具体的来_是在com.ibatis.spring包下创徏CustomBeanFactoryc,spring的配|文件applicationContext.xml也放在这个目录下。以下就是该cȝ全部代码Q很单:
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);
}
}
实际上就是封装了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方式的{耦合一}的解耦? 3.3. 表现? 面分析到Qstruts和spring是在表现层衔接v来的Q那么表现层p做稍微的更改Q即所需要的servicecȝ对象创徏上。以表现层的AccountBeancMؓ例:原来的源代码如下
private static final AccountService accountService
= AccountService.getInstance();
private static final CatalogService catalogService
= CatalogService.getInstance();
攚w后的源代码如下
private static final AccountService accountService
= (AccountService)CustomBeanFactory.getBean("AccountService");
private static final CatalogService catalogService
= (CatalogService)CustomBeanFactory.getBean("CatalogService");
其他的几个presentationcM同样方式攚w。这P表现层就完成了。关于表现层的其它部分如JSP{一概不动。也许您会说Q没有看Z么特别之处的好处啊?你还是额外实C一个工厂类。别着急,帷幕刚刚开启,spring是在表现层引入,但您发没发现Q? presentationcM仅面向servicecȝ接口~程Q具?AccountService"是哪个实现类QpresentationcM知道Q是在spring的配|文仉配置。(本例中,Z最大限度的保持原来的代码不作变化,没有抽象出接口)。Spring鼓励面向接口~程Q因为是如此的方便和自然Q当然您也可以不q么做? CustomBeanFactoryq个工厂cMؓ什么会如此单,因ؓ其直接用了Spring的BeanFactory。Spring从其核心而言Q是一个DI容器Q其设计哲学是提供一U无侵入式的高扩展性的框架。ؓ了实现这个目标,Spring 大量引入了Java 的Reflection机制Q通过动态调用的方式避免编码方式的U束Qƈ在此基础上徏立了其核心组件BeanFactoryQ以此作为其依赖注入机制的实现基。org.springframework.beans包中包括了这些核心组件的实现c,核心中的核心为BeanWrapper和BeanFactorycR? 3.4. 持久层在讨论业务层之前,我们先看一下持久层Q如下图所C:
在上文中Q我们把iface包下的DAO接口归ؓ业务层,在这里不需要做修改。ibatis的sql配置文g也不需要改。要改的是DAO实现c,q在spring的配|文件中配置h?BR> 1、修改基c?BR> 所有的DAO实现c都l承于BaseSqlMapDaocR修改BaseSqlMapDaocd下:
public class BaseSqlMapDao extends SqlMapClientDaoSupport
{
protected static final int PAGE_SIZE = 4;
protected SqlMapClientTemplate smcTemplate
= this.getSqlMapClientTemplate();
public BaseSqlMapDao()
{
}
}
使BaseSqlMapDaocL为承于Spring提供的SqlMapClientDaoSupportc,q定义了一个保护属性smcTemplateQ其cd为SqlMapClientTemplate? 2、修改DAO实现c? 所有的DAO实现c还是承于BaseSqlMapDaoc,实现相应的DAO接口Q但其相应的DAO操作委托SqlMapClientTemplate来执行,以AccountSqlMapDaocMؓ例,部分代码如下Q?
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);
}
p么简单,所有函数的{都是一LQ只需要查找替换就可以了! 、除d厂类以及相应的配|文?BR> 除去DaoConfig.javaq个DAO工厂cd相应的配|文件dao.xmlQ因为DAO的获取现在要用spring来管理?BR> 4、DAO在Spring中的配置QapplicationContext.xmlQ?BR>
<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>
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" />对它的引用? 2. sqlMapClient节点。这个是针对ibatis SqlMap的SqlMapClientFactoryBean配置。实际上配置了一个sqlMapClient的创建工厂类。configLocation属性配|了ibatis映射文g的名U。dataSource属性指向了使用的数据源Q这h有用sqlMapClient的DAO都默认用了该数据源Q除非在DAO的配|中另外昑ּ指定? 3. TransactionManager节点。定义了事务Q用的是DataSourceTransactionManager? 4. 下面可以定义DAO节点了,如AccountDaoQ它的实现类是com.ibatis.jpetstore.persistence.sqlmapdao.AccountSqlMapDaoQ用的SQL配置从sqlMapClient中读取,数据库连接没有特别列出,那么是默认使用sqlMapClient配置的数据源datasource? q样Q我们就把持久层攚w完了,其他的DAO配置cM于AccountDao。怎么P单吧。这ơ有接口了:Q?AccountDao接口Q?gt;AccountSqlMapDao实现? 3.5. 业务? 业务层的位置以及相关c,如下图所C:在这个例子中只有3个业务类Q我们以OrderServicecMؓ例来攚w,q个cL最复杂的,其中涉及了事务? 1、在ApplicationContext配置文g中增加bean的配|:
<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>
定义了一个OrderServiceQ还是很Ҏ懂的。ؓ了简单v见,使用了嵌套beanQ其实现cLcom.ibatis.jpetstore.service.OrderServiceQ分别引用了ItemDaoQOrderDaoQSequenceDao。该bean的insert*实现了事务管?AOP方式)。TransactionProxyFactoryBean自动创徏一个事务advisorQ?该advisor包括一个基于事务属性的pointcut,因此只有事务性的Ҏ被拦截? 2、业务类的修改,以OrderServiceZQ?
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;
}
//剩下的部?
…?
}
U色部分Z攚w分。Spring采用的是Type2的设|依赖注入,所以我们只需要定义属性和相应的设值函数就可以了,ItemDaoQOrderDaoQSequenceDao的值由spring在运行期间注入。构造函数就可以为空了,另外也不需要自q写代码处理事务了Q事务在配置中声明)QdaoManager.startTransaction();{与事务相关的语句也可以L了。和原来的代码比较一下,是不是处理精了很多!可以更关注业务的实现? 4. l束? 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就像本文,而它的架构又是内部一致? (T117) ]]> 使用高效的日志工具—Log4J http://www.tkk7.com/sgsoft/articles/2256.html天一?/dc:creator>天一?/author>Sun, 20 Mar 2005 08:36:00 GMT http://www.tkk7.com/sgsoft/articles/2256.html http://www.tkk7.com/sgsoft/comments/2256.html http://www.tkk7.com/sgsoft/articles/2256.html#Feedback 0 http://www.tkk7.com/sgsoft/comments/commentRss/2256.html http://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-CVS http://www.tkk7.com/sgsoft/articles/737.html天一?/dc:creator>天一?/author>Wed, 26 Jan 2005 11:11:00 GMT http://www.tkk7.com/sgsoft/articles/737.html http://www.tkk7.com/sgsoft/comments/737.html http://www.tkk7.com/sgsoft/articles/737.html#Feedback 0 http://www.tkk7.com/sgsoft/comments/commentRss/737.html http://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 GMT http://www.tkk7.com/sgsoft/articles/545.html http://www.tkk7.com/sgsoft/comments/545.html http://www.tkk7.com/sgsoft/articles/545.html#Feedback 0 http://www.tkk7.com/sgsoft/comments/commentRss/545.html http://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 GMT http://www.tkk7.com/sgsoft/articles/424.html http://www.tkk7.com/sgsoft/comments/424.html http://www.tkk7.com/sgsoft/articles/424.html#Feedback 1 http://www.tkk7.com/sgsoft/comments/commentRss/424.html http://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 statement Q比?/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 statement Q?/SPAN>Mapped Statement 可以使用L?/SPAN> SQL 语句Q利?/SPAN> POJO ?/SPAN>原始变量 及其 Wrapper Class 作ؓ输入Q?/SPAN>parameter class Q和输出Q?/SPAN>result class Q?/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> POJO Q如果你愿意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/rosen Q?/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 GMT http://www.tkk7.com/sgsoft/articles/423.html http://www.tkk7.com/sgsoft/comments/423.html http://www.tkk7.com/sgsoft/articles/423.html#Feedback 2 http://www.tkk7.com/sgsoft/comments/commentRss/423.html http://www.tkk7.com/sgsoft/services/trackbacks/423.html
前段旉写了?/SPAN> Hibernate 斚w?/SPAN>pd文章 Q网友们反映q不错。在接下来的旉里,我将会引入另外一U?/SPAN> O/R Mapping 解决Ҏ —?B style="mso-bidi-font-weight: normal">iBATIS Q本pd沿?/SPAN> Hibernate pd文章的风根{?/SPAN>
什么是 iBATIS Q?/SPAN>
和众多的 SourceForge 开源项目一P iBATIS 曄也是其中的一员。在 2004 q?/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 Java Q目前最新版本是 2.0.9 Q压~包里已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>POJO Q一下子清爽了很多?/SPAN>BTW Q?/SPAN>Hibernate q种做法没有错!只是我只需要查询功能,仅仅是取我所好而已Q避免了杀鸡用牛刀?/SPAN>
目前Q系l整合已l结束,׃一个月旉。如果?/SPAN> Hibernate Q恐怕我现在q在为怎么设计数据表、怎样?/SPAN>HQL 而和同事争论?/SPAN>
开始另一?/SPAN> O/R Mapping 之旅
上次 O/R Mapping 之旅?/SPAN> BO 、数据表q可以重用,只是把数据库q移C MySQL ?SPAN style="COLOR: black">打开 Eclipse Q新Z?/SPAN> Resin 目。把 ibatis-sqlmap-2.jar ?/SPAN> ibatis-common-2.jar 拯?/SPAN> lib 目录下,再导入项目。和 Hibernate 自动配置、自动映相比, iBATIS 的一切都是手工完成的?/SPAN>
?/SPAN> src 下徏立配|文?/SPAN> SqlMapConfig.xml Q数据库链接、连接池?/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>
JDBC Q通过传统 JDBC 来管理事务?/SPAN>
JTA Q用一?/SPAN> JTA 全局事务Q iBATIS 的事务包括在更大的事务范围内Q跨数据?/SPAN>Session 的)Q这个更大的事务范围可能包括了其他的数据库和事务资源。这个配|需要一?/SPAN> UserTransaction 属性,以便?/SPAN> JNDI 获得一?/SPAN> UserTransaction ?/SPAN>
EXTERNAL Q调?/FONT> iBATIS 以外的其他事务管理器来管理事务?/SPAN>
dataSource 元素?/SPAN> transactionManager 的一部分?/SPAN>type 可选项包括Q?BR> SIMPLE Q?/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> DataSource Q当无容器提?/SPAN> DataSource 服务Ӟ可以使用该选项Q对?/SPAN> iBATIS 实现cMؓ com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory ?/SPAN>
?JNDI Q?/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比如:
你也许已发现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/rosen Q?/STRONG>
]]>
վ֩ģ壺
ƷƷþ99һ |
ؼëƬˬwwwѰ |
ƷAò |
ѹۿƬëƬ |
222wwwƵ |
߹ۿ |
˸һ91 |
һ߹ۿ |
ҹҹAһ
|
ƷƵվ |
ûɫվ |
ҹ˾ѿƬ |
˳Ƶ߲Ų |
vavava |
AVһţţ |
ɫͼ.com |
Ʒ߹ۿ
|
99þþù |
Ʒ2021 |
ձ |
ɫƷվ |
Ӱ |
ƷĻ |
Ļһ |
avպav߹ۿ |
Ļպ |
պ |
ƷƬ߹ۿ |
ѹ˸Ƶվ |
ѿŮͰ |
ƷѾþþþþóӰԺ |
aƬѹۿ |
߳ٸëˮˮ |
A߹ۿվȫ |
ѾþþƷ99reѾy |
99Ʒ |
ѿ13 |
AƬһ |
۲ӰԺѹۿ |
avһ |
Ʒ߲ |