------------------------------
 Loonframework-DAO-Alpha-0.1.0 - 2008年2月24日
------------------------------

开发JDK: JDK1.4(所以此版本不支持Annotation)

文件名称:Loonframework-DAO-Alpha-0.1.0.jar
                    Loonframework-DAO-Alpha-0.1.0-src.zip

版本声明:此为测试用版本,是一个供测试与调整用的系统原型,不保证现有接口或函数实现在可见的未来无变更,不保证运行效率及错误会得到有效处理,强烈反对任何个人或组织将此版本投入到实际或自认为有用的项目中……

版权声明:Loonframework下属所有源码遵循Apache License 2.0协议,保留对其下代码的版权,任何对源代码的改进和变动,请告知原作者;不能将Loonframework全部或部分编译后再以其他项目名义发布, 不得经简单修改单独商品化销售.所有对Loonframework源码与文档的引用和转载请注明出处.

1.什么是Loonframework

Loonframework由三个子项目构成,分别对应DAO框架实现,WEB框架实现,以及一个2D的GAME框架实现,全部完成后将作为一个快速开发用综合框架而存在;目前三部分都尚处于开发阶段。

2.关于Loonframework-DAO

Loonframework- DAO是loonframework项目下的DAO实现,是一个轻量级、低外部依赖度的实现;为实现小规模项目的快速开发而诞生,目前提供了jdbc数据 接口的封装及简单的pojo应用处理能力(但并不是彻底的orm)。loonframework-DAO本身是一个试验性质的项目,内部提供了事务、日 志、Cache、异常处理等方面的简单实现,能够不依赖于任何第三方项目而单独运行,当然也可以通过接口选择和其它项目并用,将会陆续提供支持第三方的 template以供调用。

3.关于Loonframework-DAO的文件构成


本次公开测试的代码部分如上图所示。

4.关于Loonframework-DAO的初始化设置

Loonframework-DAO通过读取cfg.lf.xml文件初始化设置,该文件基本配置如下:
<? xml version="1.0" encoding="UTF-8" ?>
< loon >
  
<!--  在loonframework中,配置会映射到一个对象实体进行操作,DAOFruit为数据驱动设定  -->  
  
< pojo  id ="dao"  class ="org.loon.framework.pojo.sys.DAOFruit" >
  
<!--  驱动别名,选填  -->
     
< set  name ="alias" >< value  type ="string" > mysql </ value ></ set >
  
<!--  jdbc驱动类,可使用全写也支持'mysql'、'oralce'这样的简写方式  -->    
     
< set  name ="jdbcClass" >< value  type ="string" > org.gjt.mm.mysql.Driver </ value ></ set >
  
<!--  url地址  -->             < set  name ="url" >< value  type ="string" > jdbc:mysql://localhost:3306/test?useUnicode=true </ value ></ set >
  
<!--  用户名  -->     
     
< set  name ="use" >< value  type ="string" > ltest </ value ></ set >
  
<!--  密码  -->
     
< set  name ="pwd" >< value  type ="string" ></ value ></ set >
  
<!--  最小连接数,选填。loonframework-dao内置有一个小型的连接池,可通过DAOManager类启动 -->      
     
< set  name ="minconnection" >< value  type ="int" > 100 </ value ></ set >
  
<!--  最大连接数,选填 -->      
     
< set  name ="maxconnection" >< value  type ="int" > 100 </ value ></ set >
  
<!--  超时设定  -->
     
< set  name ="timeout" >< value  type ="int" > 5 </ value ></ set >
   
</ pojo >
   
< pojo  id ="jtds"  class ="org.loon.framework.pojo.sys.DAOFruit" >
     
< set  name ="alias" >< value  type ="string" > jtds </ value ></ set >
     
< set  name ="jdbcClass" >< value  type ="string" > net.sourceforge.jtds.jdbc.Driver </ value ></ set >
     
< set  name ="url" >< value  type ="string" > jdbc:jtds:sqlserver://localhost:1433/test </ value ></ set >
     
< set  name ="use" >< value  type ="string" > sa </ value ></ set >
     
< set  name ="pwd" >< value  type ="string" ></ value ></ set >
     
< set  name ="minconnection" >< value  type ="int" > 100 </ value ></ set >
     
< set  name ="maxconnection" >< value  type ="int" > 100 </ value ></ set >
     
< set  name ="timeout" >< value  type ="int" > 5 </ value ></ set >
   
</ pojo >
   
<!--  LOGFruit为日志设定  -->
   
< pojo  id ="log"  class ="org.loon.framework.pojo.sys.LOGFruit" >
     
<!--  此项为生成的文件名,可以设定相关路径,无设置以默认路径保存  -->
     
< set  name ="file" >< value  type ="string" > log.txt </ value ></ set >
     
<!--  此项为log服务名,启动服务后有效  -->
     
< set  name ="servername" >< value  type ="string" > logserver </ value ></ set >
     
<!--  服务器所在地址  -->
     
< set  name ="host" >< value  type ="string" > 127.0.0.1 </ value ></ set >
     
<!--  服务器端口  -->
     
< set  name ="port" >< value  type ="string" > 8071 </ value ></ set >
     
<!--  是否在控制台显示log中信息  -->
     
< set  name ="filesys" >< value  type ="boolean" > true </ value ></ set >
     
<!--  是否保存log文件  -->
     
< set  name ="filemark" >< value  type ="boolean" > false </ value ></ set >
     
<!--  是否允许log服务启动  -->
     
< set  name ="netmark" >< value  type ="boolean" > false </ value ></ set >
   
</ pojo >
   
<!--  此项为基础配置,在0.1.0版本中未实装  -->
   
< pojo  id ="cfg"  class ="org.loon.framework.pojo.sys.CFGFruit" >
     
< set  name ="encoding" >< value  type ="string" > utf-8 </ value ></ set >
     
< set  name ="cache" >< value  type ="boolean" > true </ value ></ set >
     
< set  name ="timeout" >< value  type ="long" > 60 </ value ></ set >
     
< set  name ="sleep" >< value  type ="long" > 10 </ value ></ set >
   
</ pojo >
</ loon >

5.关于Loonframework-DAO的事务管理

package  test;

import  java.sql.SQLException;

import  org.loon.framework.dao.DAOManager;
import  org.loon.framework.dao.DAOTransaction;
import  org.loon.framework.log.Log;
import  org.loon.framework.log.LogFactory;

public   class  DAOTransactionTest  ... {
    
    
final static private Log log=LogFactory.getInstance(DAOTransactionTest.class);
    
    
public static void main(String[]args)...{    
        
//获得默认事务
        DAOTransaction transaction= DAOManager.getDefaultTransaction();
        
try ...{
            System.out.println(transaction.getTransactionConnection().isClosed());
        }
 catch (SQLException e) ...{
            log.debug(e.getMessage());
        }

        
    }

}

通过 DAOManager,我们得到了一个通过连接池获取的,简化了的事务操作。

DAOManager提供的部分方法如下:



利用 DAOTransaction接口,我们可以构建一个简单的事务管理实现。

除了DAOManager外,本项目还提供了一个
javax.transaction.Transaction 的实现,位于org.loon.framework.dao.jta下,需要用户提供UserTransaction实现。


6.关于Loonframework-DAO中的Engine

在Loonframework-DAO中,原则上所有的数据操作都要通过Engine类派生。此类位于org.loon.framework.dao下。

其中静态方法如下:



我们可以通过读取默认配置,设置connection、datasource、daofruit或者选择配置文件中指定的pojo创建Engine。

Engine中,目前提供了两种数据处理模式可供选择。

1.通过Execute实现进行操作
// 选择名为dao的数据源配置
Engine engine = Engine.begin( " dao " );
// 获得一个Execute操作
Execute execute = engine.makeExecute();

Execute接口如下:


Execute接口是一个基于JDBC的常用CRUD操作封装,他将所有的SQLException封装为精简的LException异常进行处理,并提供了一个复刻ResultSet的Query结果集。

Query接口如下(部分,基本为ResultSet复刻):


Query结果集没有数据库依赖,是一个存在于内存中的ResultSet缓存,我们完全可以关闭数据库后如常使用Query接口,但这也意味着Query中的数据量始终不能保存过大,否则过多的Query将导致内存耗尽。在下一版本会提出解决方案。

Query接口基本用法如下:
// 选择名为dao的数据源配置
Engine engine = Engine.begin( " dao " );
// 获得一个Execute操作
Execute execute = engine.makeExecute();
Query query
= execute.sqlQuery( " select * from ltest " );

 
while (query.isNext()) ... {
            
//输出数据
            System.out.println(query.getString(0));
            System.out.println(query.getString(
"name"));
  }

我们可以看出,Query接口缓存ResultSet后滚动结果集的方式几乎与ResultSet接口相同;区别在于,Query接口内部是通过数组及Map实现的,所以索引由0开始。

由于Query本质上是由一个数组内部封装Map实现,所以也提供了很多集合操作功能,如叠代器操作。
for (Iterator it = query.iterator();it.hasNext();) ... {
            System.out.println(((Map)it.next()).get(
"id"));
}

Query甚至可以通过makeTo方法直接自身匹配为目标类型
// 将query中数据映射到指定类中自动匹配,并返回对象数组
        Object[] ltests = query.makeTo(Ltest. class );
        
// ArrayIterator为loonframework提供的数组叠代器
        ArrayIterator it = new  ArrayIterator(ltests);
        
for (;it.hasNext();) ... {
            System.out.println(
"name="+((Ltest)it.next()).getName());
        }


在Execute接口的辅助工具中,还有如Select类这样的存在。

Select是一个简单的xml文件配置查询器,他用于查询及返回存在于xml中的sql语句。

比如,我有已配置好的sql语句如下:
< loon >
< sql >
      
< list  id ="list1" > <![CDATA[ select * from ltest ]]> </ list >
      
< list  id ="list2" > <![CDATA[ select * from ltest where id>#id# and name like '%#name#%' ]]> </ list >
      
< list  id ="list3" > <![CDATA[ select count(id) from ltest ]]> </ list >
</ sql >
</ loon >

借助Select类可以这样获得动态的sql语句:
        Select select  = new  Select();
        
// 或者Select select=Select("/sql.lf.xml");
        
// 初始一个xml文档
        select.initializeConfigure( " /sql.lf.xml " );
        
// 以getSelect方式获得指定名称的节点数据
        List list = execute.sqlQueryToList(select.getSelect( " list1 " ));
        
for (Iterator it = list.iterator();it.hasNext();) ... {
            System.out.println(((Map)it.next()).get(
"id"));
        }

        

Select还可以根据不同的设置读取任意xml文档,比如有ibatis演示用例的Account.xml
<? xml version="1.0" encoding="UTF-8"  ?>

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

< sqlMap  namespace ="Account" >

  
<!--  Use type aliases to avoid typing the full classname every time.  -->
  
< typeAlias  alias ="Account"  type ="com.mydomain.domain.Account" />

  
<!--  Result maps describe the mapping between the columns returned
       from a query, and the class properties.  A result map isn't
       necessary if the columns (or aliases) match to the properties 
       exactly. 
-->
  
< resultMap  id ="AccountResult"  class ="Account" >
    
< result  property ="id"  column ="ACC_ID" />
    
< result  property ="firstName"  column ="ACC_FIRST_NAME" />
    
< result  property ="lastName"  column ="ACC_LAST_NAME" />
    
< result  property ="emailAddress"  column ="ACC_EMAIL" />
  
</ resultMap >

  
<!--  Select with no parameters using the result map for Account class.  -->
  
< select  id ="selectAllAccounts"  resultMap ="AccountResult" >
    select * from ACCOUNT
  
</ select >

  
<!--  A simpler select example without the result map.  Note the 
       aliases to match the properties of the target result class. 
-->
  
< select  id ="selectAccountById"  parameterClass ="int"  resultClass ="Account" >
    select
      ACC_ID as id,
      ACC_FIRST_NAME as firstName,
      ACC_LAST_NAME as lastName,
      ACC_EMAIL as emailAddress
    from ACCOUNT
    where ACC_ID = #id#
  
</ select >
   
  
<!--  Insert example, using the Account parameter class  -->
  
< insert  id ="insertAccount"  parameterClass ="Account" >
    insert into ACCOUNT (
      ACC_ID,
      ACC_FIRST_NAME,
      ACC_LAST_NAME,
      ACC_EMAIL
    values (
      #id#, #firstName#, #lastName#, #emailAddress#
    )
  
</ insert >

  
<!--  Update example, using the Account parameter class  -->
  
< update  id ="updateAccount"  parameterClass ="Account" >
    update ACCOUNT set
      ACC_FIRST_NAME = #firstName#,
      ACC_LAST_NAME = #lastName#,
      ACC_EMAIL = #emailAddress#
    where
      ACC_ID = #id#
  
</ update >

  
<!--  Delete example, using an integer as the parameter class  -->
  
< delete  id ="deleteAccountById"  parameterClass ="int" >
    delete from ACCOUNT where ACC_ID = #id#
  
</ delete >

</ sqlMap >

     //  也可用于读取其他框架中通过xml进行的sql设置
        select.initializeConfigure( " /Account.xml " , " sqlMap " , " select " , " id " );
        System.out.println(select.getSelect(
" account " ));

此时我们只要知道目标节点,下属子节点名以及属性就可以检索其中的sql语句

Select类也允许将object中数据自动匹配到获得的sql的#?#标记中

Ltest test = new  Ltest();
System.out.println(select.matchObject(test, 
" list2 " ));

此时,会自动匹配所有符合条件的数据到sql中,完成sql语句。



2.通过Handle实现进行操作

该接口目前设置如下:


Handle是Execute的二次封装,他将Execute抽象为针对pojo映射进行操作。

Engine engine = Engine.begin();
// 与Execute相同,Handle也需要通过Engine获得
Handle handle = engine.makeHandle();

获得Handle后,假设我有如下pojo对象。

public   class  Role ... {

    
int caseid;

    String name;

    
long sex;

    Date day;

    
boolean die;

    String death;

    
public int getCaseid() ...{
        
return caseid;
    }


    
public void setCaseid(int caseid) ...{
        
this.caseid = caseid;
    }


    
public Date getDay() ...{
        
return day;
    }


    
public void setDay(Date day) ...{
        
this.day = day;
    }


    
public String getDeath() ...{
        
return death;
    }


    
public void setDeath(String death) ...{
        
this.death = death;
    }


    
public boolean isDie() ...{
        
return die;
    }


    
public void setDie(boolean die) ...{
        
this.die = die;
    }


    
public String getName() ...{
        
return name;
    }


    
public void setName(String name) ...{
        
this.name = name;
    }


    
public long getSex() ...{
        
return sex;
    }


    
public void setSex(long sex) ...{
        
this.sex = sex;
    }


}

这时要求我针对此pojo进行一次insert操作到数据库中。

对于这种easy的需求,我们只需要设定如下语句即可轻松完成。

        Engine engine=Engine.begin();
        
//与Execute相同,Handle也需要通过Engine获得
        Handle handle=engine.makeHandle();
        Role role=
new  Role();
        role.setName("鹏凌三千");
        role.setSex(1);
        role.setDay(
new  Date());
        role.setDeath("ABC");
        role.setDie(
false );
        
try  ... ... {
            
//插入role到数据库中,主键自动增长
            handle.doInsert(role,true);
            
//提交数据
            handle.commit();
        }
  catch  (LExecuteException e) ... ... {
            
//异常则回滚
            handle.rollback();
        }


此时查询mysql,得知数据增加结果如下:



可以看到,我们没有对pojo进行任何描述,也没有在外部设置任何的对应文档,就成功地将一条数据插入到数据库中。

原理大家都非常清楚,就在于我们在数据库创建表格时,都会对table的主键等参数进行描述,利用jdbc我们可以得到这些设置,只要将这些设置反作用于pojo对象,就可以实现基本操作零配置进行.

通过loonframework-log,我们可以得知操作流程如下:


其它操作同样原理:



但这时就有一个问题,对于简单的CRUD我们虽然可以采取如上操作,但对于略为复杂的操作,比如删除pojo在某一范围内的区域,或者要限定删除或变更对象怎么办呢?

这时,loonframework-DAO提供了一个基于VO接口的解决方案。

VO接口内容如下:

package  org.loon.framework.dao.object;

import  org.loon.framework.core.LSerializable;

/** */ /**
 * <p>
 * Title: LoonFramework
 * </p>
 * <p>
 * Description:VO接口,需要在其中规定调用sql的依据条件。当实现的返回结果为null或""时,
 * 将自动引用table为pojo作自动实现。
 * </p>
 * <p>
 * Copyright: Copyright (c) 2007
 * </p>
 * <p>
 * Company: LoonFramework
 * </p>
 * 
 * 
@author chenpeng
 * @email:[email]ceponline@yahoo.com.cn[/email]
 * 
@version 0.1
 
*/

public   interface  VO  extends  LSerializable  ... {

    
/** *//**
     * 表名
     * 
     * 
@return
     
*/

    
public String getTableName();

    
/** *//**
     * 主键
     * 
     * 
@return
     
*/

    
public Object[] getPrimaryKeys();

    
/** *//**
     * 连接语句
     * 
     * 
@param sequence
     
*/

    
public Object getJoin();

    
/** *//**
     * 允许的条件
     * 
     * 
@return
     
*/

    
public Object getCondition();

    
/** *//**
     * 允许的列名
     * 
     * 
@return
     
*/

    
public Object[] getColumn();

}


我们令刚才的Role类实现VO接口(红字处将为生效区域)。


public   class  Role  implements  VO ... {

    
/** *//**
     * 
     
*/

    
private static final long serialVersionUID = 1L;

    
int caseid;

    String name;

    
long sex;

    Date day;

    
boolean die;

    String death;

    
public int getCaseid() ...{
        
return caseid;
    }


    
public void setCaseid(int caseid) ...{
        
this.caseid = caseid;
    }


    
public Date getDay() ...{
        
return day;
    }


    
public void setDay(Date day) ...{
        
this.day = day;
    }


    
public String getDeath() ...{
        
return death;
    }


    
public void setDeath(String death) ...{
        
this.death = death;
    }


    
public boolean isDie() ...{
        
return die;
    }


    
public void setDie(boolean die) ...{
        
this.die = die;
    }


    
public String getName() ...{
        
return name;
    }


    
public void setName(String name) ...{
        
this.name = name;
    }


    
public long getSex() ...{
        
return sex;
    }


    
public void setSex(long sex) ...{
        
this.sex = sex;
    }


    
public Object[] getColumn() ...{
        
return new Object[]...{"name","caseid"};
    }


    
public Object getCondition() ...{
        
return "name<>'鹏凌三千'";
    }


    
public Object getJoin() ...{
        
return null;
    }


    
public Object[] getPrimaryKeys() ...{
        
return null;
    }


    
public String getTableName() ...{
        
return null;
    }


}


此时我们无需更改任何操作代码,执行如下操作:
        Engine engine=Engine.begin();
        
//与Execute相同,Handle也需要通过Engine获得
        Handle handle=engine.makeHandle();
        Role role=
new  Role();
        role.setName("正常人");
        role.setSex(1);
        role.setDay(
new  Date());
        role.setDeath("ABC");
        role.setDie(
false );
        
try   ... {
            
//变更
            handle.doUpdate(role);
            
//提交数据
            handle.commit();
        }
  catch  (LExecuteException e)  ... {
            
//异常则回滚
            handle.rollback();
}

此时再查看控制台输出的日志,我们会发现自动生成的sql语句已经有了变化。


此时将自动以VO实现中的设置为优先,令较为复杂的查询得以实现。

如果我们提供相应接口的set方法,就可以动态的设定操作,只需要变更pojo对象,而无需改变操作代码本身。

当然,现在只是一个简单的实现模型,更复杂的操作,将会在后续版本中提供。

7.关于Loonframework-DAO中的其它相关包

在此发布的,除了DAO相关部分之外,还有一些有依赖关系的相关包.

比如自制的Log,Xml,Cache,Collection等部分,但与Loonframework-DAO一样,这些部分并非第三方工具提供,而是个人杜撰而成,并没有经过任何项目的实际运作,也没有经过必要的测试,无法保证其稳定可靠,建议在正式项目中不要使用。

——————————————————————————————————

目 前Loonframework三个子项目中DAO及GAME原型系统已经建立,会陆续的发布出来,而WEB方面还在构建中,希望有闲的,想令自己智商上的 优越感油然而生的(一般人看到这样的代码都敢发布,都会有这种感觉的^^)先生们女士们能自觉自愿的×××到此项目中来,以期共同进步。

个人认为做开源项目,除了有想要“提升技术”、“出名”等因素外,更主要的还是为了开阔眼界,了解不同的思路,跟更多的人接触,打破原有意识形态束缚。

世人常说“求同存异”,殊不知天下之兴在“有异”而不在“有同”,“求异存同”才是开源的真正核心价值所在,我常自诩“史上最差程序员”,这么弱的人都敢发开源项目,别人还有什么不敢干,不能干的?

重复发明轮子,当然不是想要替代轮子。假如我能够作的比Hibernate、Spring、Struts等已近乎标准的框架优秀,那不如直接加入这些团队,直接优化原项目,也让他们知道中国人有多牛……

就是因为技术及思想上达不到,才会想自己做开源,从而发现及改进自身的漏洞与不足,所谓授人以鱼,只供一餐,授人以渔,可享一生。 此之谓也。


有兴趣×××项目的[有爱人士]们……
可以通过: ceponline@yahoo.com.cn与我联系。
或者 hotmail:rsuncp@hotmail.com(此msn通常只有晚6:30以后可能在线)

项目地址:

[url]https://sourceforge.net/projects/looframework[/url]

目前未分子项目,只创建了DAO一个项目的Alpha版本。