论坛首页 Java企业应用论坛

一个自制持久层的方法

浏览 14281 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2004-08-25  
我知道大家多有幸使用ejb或者hibernate来处理持久层。
所以,用这些东西武装到牙齿的同志们不用看本文。

只不过有时候发现还是偶尔有朋友象我一样郁闷地要自己做持久层。

鉴于经验的缺乏,我也不知道我这里介绍的方法是否属于常识还是异端。

如果是常识,麻烦就把它当成个屁。如果是异端,呵呵,我就兴奋了,欢迎向我开炮。

一般如果自己写持久层,底层用jdbc的话,往往有这样的一个封装:
1。ioc进来一个jdbc Connection对象。
2。生成Statement (如果这个对象封装的就是一个query,可以做成PreparedStatement)
3。查询数据库。
4。得到ResultSet.
5。遍历ResultSet, 处理结果。
5。关闭ResultSeet
6。关闭Statement。(如果是PreparedStatement就不用关闭了。不过那样这个Persistence对象多一个close()方法了)

其中1,2,3,4都没什么可说的。根据你要封装的持久层逻辑,做就是了。查询数据库可以通过command模式从外界接受Query对象,以避免hard code。
对5,有点问题。得到结果后怎么处理?是否返回一个array? List?如果返回array, List,那么你必须知道怎么从ResultSet的每一行数据构造出对象来。
对6,7,只要注意用finally就是了。

这样,封装出来的持久层对象不用担心资源泄漏,因为ResultSet和Statement这些临时对象都被保证关闭了。用户代码根本不用担心。
实际上,用户代码可以不知道这个持久层对象是基于jdbc的。

伪码如下:
public final class JdbcUtils{
	public static void close(Statement stmt);{
	  try{
	    stmt.close();;
	  }
	  catch(Exception e);{
	  	e.printStatckTrace(System.err);;    
	  }
	}
	public static void close(ResultSet stmt);{
	  try{
	    stmt.close();;
	  }
	  catch(Exception e);{
	  	e.printStatckTrace(System.err);;    
	  }
	}
}
final Statement stmt = conn.createStatement();;
try{
  final ResultSet rset = stmt.executeQuery(...);;
  try{
    //how do we deal with rset?
  }
  finally{
    JdbcUtils.close(rset);;
  }
}
finally{
  JdbcUtils.close(stmt);;
}

在进入正题之前,这里面有几个点我想要先说一下:
1。我们在close一个资源的时候,即使失败了,也不希望抛出异常,因为其实只要取得的数据得到了,就已经算是成功了,即使资源清理失败,程序也应该继续进行下去。
2。我看到许多人喜欢用下面的null的方法来统一处理错误:

Statement stmt = null;
ResultSet rset = null;
try{
  ....
}
finally{
  if(stmt!=null);stmt.close();;
  if(reset!=null);rset.close();;
}

个人不推荐这种写法。
第一,它在stmt.close()的时候可能会有一场抛出,导致rset.close()无法运行。
第二,当资源个数很多的时候,这种写法很不清晰。而每个资源有自己一个finally块结构上清楚得多。不容易犯错误。(哎,多么爱慕c#的using啊。java的finally太丑陋了!)
第三,变量不是final的,如果在try块里面对rset赋值了两个不同的ResultSet,就会造成资源泄漏。
第四,所有资源都是最后清理,这在一些场合不合适。资源还是用完就情理比较好。比如,如果try块里有两个ResultSet要处理,如果按照我上面的方法,在每个ResultSet用完后用finally块清理,就会更即时地释放资源。
我们项目组就因为一些人喜欢这么写造成了一些很难调试的问题。

好,闲话说完,进入正题。
我们怎么处理这个ResultSet呢?
为了简单起见,我假设这个ResultSet只是一个forward-only的结果集,而且只有一个结果集。(这应该是大多数普通应用的情况吧?)
一般的思路都是返回array, List或者Iterator。
这三者都有一个如何从ResultSet创建对象的问题。
如果你的这个持久类只对付一种业务对象,你直接就从ResultSet里面读取数据,创建对象就是。

但是,这样对不同的业务对象的持久逻辑就会有代码重复。

更灵活一点的是从外界接受一个abstract factory,这个工厂负责从ResultSet构建一个业务对象。
然后,我们把它放在List,数组里,最后返回数组,List或者Iterator。

返回List, 数组的一个缺点是:我们要预先把数据全部populate到一块空间中去。对大量的数据的话,不太合算。
返回Iterator的方案一般背后的思想是lazy。我们不一次把结果从ResultSet中取出,而是用户在调用it.next()的时候才一条一条记录向下走。

但是这样就隐含着我们无法马上关闭ResultSet对象,要等待用户it.next()到记录末尾。万一用户没有那么老实,走了一半就不走了怎么办? 资源不就泄漏了?
等待finalize()吗?


我这里介绍另外一个可以选择的比较灵活的方案:回调。

我要求用户给我一个回调函数,我对每一个记录调用用户的回调函数。这样,用户如果直接要在web页面上打印的话,根本就不需要一个中间的临时List或者数组。也没有了资源泄漏的担心。

具体方法如下:

首先,用户的回调要接受一行数据, 为了避免用户有机会影响我的ResultSet的当前记录(比如用户不小心把我的ResultSet关掉了之类的),
我们引入一个接口叫做Record:

interface Record{
  Object get(int i);;
  Object get(String name);;
  int getInt(int i);;
  int getInt(String name);;
  ......
}


这个接口包括了ResultSet里的所有读取数据的方法。这里,把SqlException给去掉了,因为用户根本不应该知道是在跟jdbc打交道。

然后,实现一个从ResultSet到Record的adapter。
public final class ResultSet2Record implements Record{
  ......
  public static Record adapt(ResultSet rset);{
    return new ResultSet2Record(rset);;
  }
}

呵呵,又是一个“该死”的静态工厂。
这个实现里面会catch SqlException,并把它封装成一个自定义的runtime exception。如RecordException。

用户的回调接口如下:

interface RecordListener{
  bool receive(Record rec);;
}
这个返回的bool告诉持久层:我要/不要继续继续下去了。

然后,最初的持久层代码就会是这样:

final Statement stmt = conn.createStatement();;
try{
  final ResultSet rset = stmt.executeQuery(...);;
  try{
    final Record rec = ResultSet2Record.adapt(rset);;
    while(rset.moveNext(););{
      if(!listener.receive(rec););break;
    }
  }
  finally{
    JdbcUtils.close(rset);;
  }
}
finally{
  JdbcUtils.close(stmt);;
}

这样,如果客户代码只是要在web页面上打印html,可以写一个简单的Listener:
new RecordListener();{
  public bool receive(Record rec);{
    HtmlTableRow tr = ...;
    tr.addColumn(rec.get("customer id"););;
    tr.addColumn(rec.get("account number"););;
    ...
    HtmlUtils.outputTableRow(tr, response);;
    returnt true;
  }
};

而如果客户需要的是一个数组,或者List, 也可以这样:
new RecordListener();{
  public bool receive(Record rec);{
    list.add(new Customer(rec.get("customer id");, rec.get("account number");););;
    return true;
  }
};


如果用户只想最多显示20条记录,(万一数据库不支持top也不支持rownum怎么办?)也可以在Listener里面自己计数。


当然,持久层可以缺省提供几个RecordListner的实现,比如把结果populate成List之类的。

这其实仍然是一个比较底层的持久层。目的是为了得到灵活性。用户使用起来因为要自制RecordListener,并不是十分方便。

所以,在这个层上面,还可以制作一个可选的对用户更友好的层,这一层,使用那几个缺省提供的RecordListener实现,可以给用户提供更加传统的类似下面的接口:

List query(Query qry);;


这个Query对象是一个独立于具体物理层和dbms的直接描述查询逻辑的对象。针对xml, jdbc, 甚至不同的dbms,我们可以在内部把Query对象翻译成合适的sql语句。
另外,Query对象内部应该也保存了一个抽象工厂,用来把Record翻译成业务对象。
这两者都放在Query对象中是因为要哪个column和用哪个column来创建对象是有一定耦合的。


这样的增值服务层可以做很多,直到得到一个非常傻瓜的用户界面。用户根据需求,可以选择傻瓜界面,也可以选择专家界面。
   发表时间:2004-08-25  
这个做法是偶上个月刚刚学会的常识:
Template Method + Callback Interface

用Spring吧, 它封装的JDBC代码比偶们Home Build的优雅多了......
0 请登录后投票
   发表时间:2004-08-25  
Readonly 写道
这个做法是偶上个月刚刚学会的常识:
Template Method + Callback Interface

用Spring吧, 它封装的JDBC代码比偶们Home Build的优雅多了......

确实如此,Spring的HibernateTempalte就是一个模板!
相当于把一些相同的而又频繁操作的打开关闭session,connection,事务开始,提交,会滚,关闭等等操作做成模板!!!
不过,AOP作这方面优势太多了!!
很佩服ajoo思考问题的深入!!呵呵,不过建议在思考阿同时多看看国外优秀的一些框架的源代码,研究研究,受益更大!!
0 请登录后投票
   发表时间:2004-08-25  
Readonly 写道
这个做法是偶上个月刚刚学会的常识:
Template Method + Callback Interface

用Spring吧, 它封装的JDBC代码比偶们Home Build的优雅多了......

gof上的template method?依赖继承和override的template method?别恶心我了。
老早就想写这么个东西介绍一下,以前曾经在jdon上介绍了一下,没人响应。大概jdon上的高手都是用ejb的,不会这么土自己砍柴烧火。
不过要是我在上个月之前贴出来,你是否又要骂偶异端了?
0 请登录后投票
   发表时间:2004-08-25  
firebody 写道
Readonly 写道
这个做法是偶上个月刚刚学会的常识:
Template Method + Callback Interface

用Spring吧, 它封装的JDBC代码比偶们Home Build的优雅多了......

确实如此,Spring的HibernateTempalte就是一个模板!
相当于把一些相同的而又频繁操作的打开关闭session,connection,事务开始,提交,会滚,关闭等等操作做成模板!!!
不过,AOP作这方面优势太多了!!
很佩服ajoo思考问题的深入!!呵呵,不过建议在思考阿同时多看看国外优秀的一些框架的源代码,研究研究,受益更大!!

也没啥思考的。这东西我是觉得是常识。不过那个静态厂的事儿让我发现一个人的常识不见得是所有人的常识。
本来也没打算写的。只是想看看是不是我认为是常识的东西在这都是异端。
0 请登录后投票
   发表时间:2004-08-25  
有点象spring的jdbc封装,不过在有的方面走得远一点
0 请登录后投票
   发表时间:2004-08-25  
没有看得太懂楼主的代码,总感觉思路有些差别(也许我对AOP不熟悉吧)。
   上个礼拜我写一个简单的ORM,属于楼主列出的那种无聊的人,说来很简单,因为hibernate还不熟悉,Spring也没有怎么看,所以不敢给项目中使用(当然未来还是要使用)。用了一段ibatis,发现它其实并不怎么好用(比如不知道怎么表示Oracle中的左右链接以及基本的CURD操作都需要自己写SQL),再加上分页、事务管理这些在用它之前已经有了,所以不想重新将以前版本的东西大量改动(否则工作量太大)。
    我把我写的ORM的思路说一下吧。
    首先,是实体,
   
public interface IPersistent extends Comparable {
    String defaultId = "Not-Setting";

    /**
     * 设置对象的识别ID
     * @param id String 唯一ID
     */
    public void setId(String id);;

    /**
     * 获得持久性对象的识别ID
     * @return String
     */
    public String getId();;

    /**
     * 获得持久性对象的版本号
     * @return long
     */
    public long getLogVersion();;

    /**
     * 设置持久性对象的版本号
     * @param logVersion long 版本号
     */
    public void setLogVersion(long logVersion);;

    /**
     * 是否打印持久性对象所有属性
     * @param printAll boolean 为true,则打印对象的属性值
     * @return String 以字符串形式表现
     */
    public String toString(boolean printAll);;

    /**
     * 返回主键对应的字段名
     * @return String
     */
    public String getPkName(); ;

    public boolean isUpdate(); ;
}
,当然有一个基本实现EntityObject,我就不列出来了,所有的匹配对象都继承了它,呵呵,很多人要骂我,为什么不用POJO。
   另外就是事务接口TransactionContext
   
public interface TransactionContext extends Cloneable {
    /**
     * 开始事务
     */
    public void beginTransaction(); ;

    /**
     * 提交事务
     */
    public void commitTransaction(); ;

    /**
     * 回滚事务
     */
    public void rollbackTransaction(); ;

    /**
     * 关闭连接
     */
    public void closeConnection(); ;

    /**
     * 释放连接
     */
    public void releaseConnection(); ;

    /**
     * 获得连接类型
     * @return String
     */
    public String getContextType(); ;

    /**
     * @deprecated 已经作废,由getContextType代替
     * @return String
     */
    public String getConnectionType(); ;

    /**
     * 获得连接
     * @return Connection
     */
    public Connection getConnection(); ;

    /**
     * 是否是容器管理
     * @return boolean
     */
    public boolean isContainerManagement(); ;

    /**
     * 初始化事务环境
     * @param ctxCfg ContextConfig
     */
    public void initContext(ContextConfigure ctxCfg);;

    /**
     * 获得事务环境配置对象
     * @return ContextConfigure 事务环境配置对象
     */
    public ContextConfigure getContextConfigure();;

    /**
     * 克隆事务环境
     * 注意:事务环境经过克隆后,必须重新初始化事务环境
     * @throws CloneNotSupportedException
     * @return Object
     */
    public Object clone(); throws CloneNotSupportedException ;

}
不要骂我事务也去clone(我不是克隆链接的)
    接下来就是数据分页接口Page,Page的实现支持各种数据库过滤分页和内存对象分页:
   
public interface Page extends java.io.Serializable {
    /**
	 * 获得Page中包含的元素数量
	 * @return int 一个Page中元素的数量
     */
	public int size(); ;

    /**
     * 获得全部的数据页数
     * @return int
     */
    public int getPageCount(); ;

    /**
     * 获得全部的数据记录数
     * @return int
     */
    public int getElementCount(); ;

	/**
	 * 是否还有下一页
	 * @return boolean
	 */
    public boolean hasNext(); ;

    /**
     * 是否有上一页
     * @return boolean
     */
    public boolean hasPrevious();;

    /**
     * 是否是最后一页
     * @return boolean
     */
    public boolean isLast();;

    /**
     * 是否是第一页
     * @return boolean
     */
    public boolean isFirst();;

    /**
     * 返回当前页的索引
     * @return int
     */
    public int getPageIndex(); ;

    /**
     * 返回页的标识
     * @return String
     */
    public String getPageID(); ;

    /**
     * 以迭代器的形式返回页内的数据
     * @return Iterator
     */
    public Iterator elements(); ;

    /**
     * 返回页内元素所对应的类
     * @return Class
     */
    public Class getElementClass();;

    /**
     * 设置页的索引
     * @param pageIndex int 页的索引号
     */
    public void setPageIndex(int pageIndex);;

    /**
     * 获得指定的页
     * @param pageIndex int
     * @return Page
     */
    public Page getSpecialPage(int pageIndex); ;

	/**
	 * 重新刷新页面数据
	 */
    public void refresh();;

    /**
     * 重新更新页内的数据
     * @param iter Iterator 数据迭代器
     */
    public void update(Iterator iter);;

}

    接下来就是数据库操作接口Processor,和hibernate的session,以及ibatis的SqlMapClient有些相似的
  
public interface Processor extends java.lang.Cloneable {
    /**
     * 设置事务环境
     * @param transactionContext TransactionContext
     */
    public void setTransactionContext(TransactionContext transactionContext);;

    /**
     * 将持久化对象创建生成数据库记录
     * @param persistent IPersistent
     * @throws PersistentAccessException
     */
    public void create(IPersistent persistent);
            throws PersistentAccessException ;

    /**
     * 成批保存一批实体对象
     * @param poList List IPersistent对象集合
     * @throws PersistentAccessException
     */
    public void create(List poList);
            throws PersistentAccessException ;
    /**
     * 将持久化对象更新数据库记录
     * @param persistent IPersistent
     * @throws PersistentAccessException
     */
    public void update(IPersistent persistent);
            throws PersistentAccessException ;

    /**
     * 根据条件将持久化对象更新数据库记录
     * @param persistent IPersistent
     * @param cond SQLCondition
     * @throws PersistentAccessException
     */
    public void update(IPersistent persistent , SQLCondition cond);
            throws PersistentAccessException ;

    /**
     * 根据条件将持久化对象更新数据库记录
     * @param persistent IPersistent
     * @param cond SQLCondition
     * @throws PersistentAccessException
     */
    public void update(List poList , SQLCondition cond);
            throws PersistentAccessException ;


    /**
     * 删除持久化对象所映射的数据库记录
     * @param persistent IPersistent
     * @throws PersistentAccessException
     */
    public void delete(IPersistent persistent);
            throws PersistentAccessException ;

    /**
     * 删除一批持久化对象所映射的数据库记录
     * @param poList List
     * @throws PersistentAccessException
     */
    public void delete(List poList);
            throws PersistentAccessException ;

    /**
     * 执行SQL
     * @param sql String
     * @throws PersistentAccessException
     */
    public void execute(String sql);
            throws PersistentAccessException ;

    /**
     * 执行配置文件定义的SQL
     * @param id String 配置文件中定义的SQL的唯一识别号
     * @param param Object[] 参数对象(无参数为null);
     * @throws PersistentAccessException
     */
    public void execute(String id , Object[] param);
            throws PersistentAccessException;

    /**
     * 批处理执行SQL语句
     * @param sqlList List SQL语句集合
     * @throws PersistentAccessException
     */
    public void executeBatch(List sqlList);
            throws PersistentAccessException ;


    /**
     * 执行存储过程
     * @param procedureName String 存储过程名
     * @param param Object[] 参数对象
     * @param output int[] 输出参数的索引号
     * @param outputType int[] 输出参数类型
     * @throws PersistentAccessException
     * @return List 返回对象集合
     */
    public List executeProcedure(String procedureName , Object[] param ,
                                 int[] output , int[] outputType);
            throws PersistentAccessException ;

    /**
     * 克隆处理器
     * @throws CloneNotSupportedException
     * @return Object
     */
    public Object clone(); throws CloneNotSupportedException ;

    /**
     * 通过指定的SQL,查询VO对象
     * @param sql String SQL语句
     * @param voClass Class VO对象(POJO对象);类
     * @throws PersistentAccessException
     * @return Object VO对象实例
     */
    public Object query(String sql , Class voClass);
            throws PersistentAccessException ;

    /**
     * 通过匹配文件查询VO对象
     * @param voClass Class VO对象类
     * @param param Object[] 查询参数
     * @throws PersistentAccessException
     * @return Object VO对象实例
     */
    public Object query(Class voClass , Object[] param);
            throws PersistentAccessException ;

    /**
     * 通过实体(只传入实体的ID);查询一个实体
     * @param persistent IPersistent 实体对象(只包含id的值);
     * @throws PersistentAccessException
     * @return IPersistent 实体对象(所有实体值);
     */
    public IPersistent query(IPersistent persistent);
            throws PersistentAccessException ;

    /**
     * 通过指定SQL语句查询数据,并以分页的形式显示
     * @param pageIndex int 页索引号,从1开始
     * @param rowsOfPage int 每页显示的数据
     * @param sql String 指定的SQL语句
     * @param voClass Class 页内存放的VO对象类
     * @throws PersistentAccessException
     * @return Page 数据页
     */
    public Page queryForPage(int pageIndex , int rowsOfPage ,
                             String sql , Class voClass);
            throws PersistentAccessException;

    /**
     * 通过配置文件中VO映射的SQL语句查询数据,并以分页的形式显示
     * @param pageIndex int 页索引号,从1开始
     * @param rowsOfPage int 每页显示的数据
     * @param voClass Class 页内存放的VO对象类
     * @param param Object[] 查询参数
     * @throws PersistentAccessException
     * @return Page 数据页
     */
    public Page queryForPage(int pageIndex , int rowsOfPage ,
                             Class voClass , Object[] param);
            throws PersistentAccessException;

    /**
     * 通过配置文件中定义的指定SQL语句查询数据,并以分页的形式显示
     * @param pageIndex int 页索引号,从1开始
     * @param rowsOfPage int 每页显示的数据
     * @param id String 配置文件中SQL的索引号
     * @param voClass Class 页内存放的VO对象类
     * @param param Object[] 查询参数
     * @throws PersistentAccessException
     * @return Page 数据页
     */
    public Page queryForPage(int pageIndex , int rowsOfPage , String id ,
                             Class voClass , Object[] param );
            throws PersistentAccessException ;

    /**
     * 通过配置文件VO映射的SQL ,查询数据以集合的形式显示
     * @param voClass Class VO对象类
     * @param param Object[] 参数数组
     * @throws PersistentAccessException
     * @return List 数据集合
     */
    public List queryForList(Class voClass , Object[] param);
            throws PersistentAccessException;

    /**
     * 通过指定的SQL查询VO对象列表
     * @param sql String SQL语句
     * @param voClass Class VO对象类
     * @param param Object[] 参数数组
     * @throws PersistentAccessException
     * @return List 数据集合
     */
    public List queryForList(String sql , Class voClass , Object[] param);
            throws PersistentAccessException;

    /**
     * 设置SQL的映射实现器(映射基本CURD的SQL语句);
     * @param map SQLMap SQLMap的实现类
     */
    public void setSQLMap(SQLMap map); ;

    /**
     * 设置实体关系映射配置类
     * @param config ORMappingConfigure 实体关系映射类
     */
    public void setORMappingConfigure(ORMappingConfigure config); ;
}

    最后就是简单的SQLMap类,它用来构造基本的CURD
   
public interface SQLMap extends SQLOperatorPolicy {

    public String SQLMAP_CREATE_ATTRIBUTE = "create";

    public String SQLMAP_UPDATE_ATTRIBUTE = "update";

    public String SQLMAP_READ_ATTRIBUTE = "read";

    public String SQLMAP_DELETE_ATTRIBUTE = "delete";

    public Map getCURDSqlMap(IPersistent persistent); ;

    public void setORMappingConfigure(ORMappingConfigure config); ;

}

    另外,我对于对象的生命期进行了判断,也就是采用logVersion,时间戳的方式,每创建一个实体对象,就会产生一个记录戳号,每从数据库读取一个对象,就会把最新的记录戳读取。所以当插入数据库的时候,就会和数据库的记录戳(也可以不采用数据库的方式存放记录戳)进行对比。当大于,如果数据库记录戳为0,则是新增,小于,则是脏数据(别人更新了)。
   下面是DAO框架IBaseDAO的代码
   
public interface IBaseDAO {
    /**
     * 检查当前持久性对象的版本记录是否为脏数据。
     * @throws PersistentAccessException
     * @return int 如果为0,则版本相同,
     *       如果>0,则数据为新数据,如果<0 , 则数据为脏数据。
     */
    public int checkLogVersion(); throws PersistentAccessException ;

    /**
     * 设置当前DAO访问的持久性对象
     * @param obj IPersistent
     * @throws PersistentAccessException
     */
    public void setPersistent(IPersistent obj); throws PersistentAccessException ;

    /**
     * 将持久性对象保存至稳定的存储介质中
     * @throws PersistentAccessException
     */
    public void save(); throws PersistentAccessException ;

    /**
     * 将持久性对象从稳定的存储介质中删除
     * @throws PersistentAccessException
     */
    public void remove(); throws PersistentAccessException ;

    /**
     * 重新从数据库从读取数据到实体对象中
     * @throws PersistentAccessException
     */
    public void refresh(); throws PersistentAccessException ;

    /**
     * 验证持久性对象是否能够被存储
     * @throws PersistentAccessException
     * @return boolean 验证通过,返回true,否则返回false
     */
    public boolean validate(); throws PersistentAccessException ;

    /**
     * 通过持久性对象的ID,获得指定的持久性对象
     * @param poClass Class 持久性对象所在的类
     * @param id String 持久性对象id
     * @throws PersistentAccessException
     * @return IPersistent 持久性对象
     */
    public IPersistent searchPOById(Class poClass , String id);
            throws PersistentAccessException ;

    /**
     * 查询VO ,并以分页显示
     * @param pageIndex int
     * @param rowsOfPage int
     * @param voClass Class
     * @param cond SQLCondition
     * @throws PersistentAccessException
     * @return Page
     */
    public Page searchByCondition(int pageIndex , int rowsOfPage ,
                                  Class voClass , SQLCondition cond);
            throws PersistentAccessException ;

    /**
     * 设置DAO的事务环境
     * @param tc TransactionContext
     */
    public void setTransactionContext(TransactionContext tc); ;

}

     并采用了AbstractDAO对IBaseDAO进行基本实现。采用Template模式,封装了save的判断实现,下面是一段代码:
   
    public void save(); throws PersistentAccessException {
        RecordLogVersion version = new RecordLogVersion(); ;
        int check = checkLogVersion(); ;
        switch (check); {
            case  0 :
                if (validate();); {
                    update();;
                    //更新完后重新设置版本号
                    persistent.setLogVersion(Tools.buildNewestVersion(););;
                } else {
                    throw new PersistentAccessException("" , -1);;
                }
                version.setNewVersion(true);;
                break;
            case  1 :
                if (validate();); {
                    create();;
                } else {
                    throw new PersistentAccessException("" , -1);;
                }
                version.setNewVersion(true);;
                break;
            case  -1 :
                throw new PersistentAccessException("" , -1);;
        }
      //......调用方法更新logVersion
}

     最后,给大家看看配置文件(在附件中)
   事务的管理在delegate层的实现进行封装。
   实现中用了oscache,对性能进行了简单的测试,还凑合。
   另外做了一个封装简单条件逻辑的(支持并、或、以及基本的逻辑门),可以让web端的条件输入与数据库的实现相脱离(至少不必在web层次低级的传入SQL条件了)。
   DAO框架和事务我看了一下,对现今的ORM都可以进行封装。
   给程序员们用了,反正感觉比ibatis好,呵呵。
   不过也要不少地方无法处理,第一就是关系映射(没有实现它,只能算OM,不能算ORM的),想做一对多,但是总没有找到好思路(其实也完全没有必要,这毕竟只是一个过渡的组件)。第二就是复杂查询(包括函数以及其他),第三就是处理的性能(像将字符串转换为对象并赋值全部是自己通过反射做的,居然没有用到java.beans的PropertyEditor,实在是失败),第四就是并发问题,对于并发的编程,经验不算很够,虽然用optimeziter进行了几次测试,不过感觉还远远不够。
    本来很想作为一个开源的东东,和大家交流交流,不过,呵呵,怕拿不出来,丢丑就不好意思了。今天借ajoo的话题,顺便贴出了点,大家聊聊。
0 请登录后投票
   发表时间:2004-08-25  
怎么贴的附件没有看到了??
   
<?xml version="1.0" encoding="UTF-8"?>
<!-- edited with XMLSPY v5 rel. 4 U (http://www.xmlspy.com); by phenix (goldenchance); -->
<ORMapping>
  <SQLMapClass ref="" needLog="false">com.goldenchance.common.sql.SQLMapOracleImpl</SQLMapClass>
  <SQL-Definition>
  	<SQL id="1" type="common" text="SELECT * FROM T_OrganInfo WHERE organId = ?">
  	  <Parameter index="1" type="java.lang.String" defaultValue=""/>
    </SQL>
  	<SQL id="3" type="common" text="SELECT * FROM T_OrganInfo WHERE organName like ? AND compCode like ?">
  	  <Parameter index="1" type="java.lang.String"/>
  	  <Parameter index="2" type="java.lang.String"/>
    </SQL>
    <SQL id="2" text="SELECT * FROM T_OrganType"/>
  </SQL-Definition>
  <Persistent-Mapping persistent="com.goldenchance.common.test.Organ" table="T_OrganInfo" dbSchema="amis" matching="true">
	<Field-Mapping property="OrganId" type="java.lang.String" boolChange="false" mappingField="OrganId" defaultValue=""></Field-Mapping>
	<Field-Mapping property="OrganNo" type="java.lang.String" boolChange="false" mappingField="organNo" defaultValue=""></Field-Mapping>
	<Field-Mapping property="OrganName" type="java.lang.String" boolChange="false" mappingField="organName" defaultValue=""></Field-Mapping>
	<Field-Mapping property="OrganTypeName" type="java.lang.String" boolChange="false" mappingField="organTypeName" defaultValue=""></Field-Mapping>
	<Field-Mapping property="Internal" type="boolean" boolChange="true" mappingField="internal" defaultValue=""></Field-Mapping>
	<Field-Mapping property="parentOrgan" type="java.lang.String" boolChange="false" mappingField="parentOrganId" defaultValue=""></Field-Mapping>
	<Field-Mapping property="organLevel" type="int" boolChange="false" mappingField="organLevelId" defaultValue=""></Field-Mapping>
	<Ref-Persistent persistent="com.goldenchance.common.test.OrganTypeInfo" relation="many" load="delay"></Ref-Persistent>
  </Persistent-Mapping>

  <Persistent-Mapping persistent="com.goldenchance.common.test.OrganTypeInfo" table="T_OrganType" dbSchema="amis" matching="true">
	<Field-Mapping property="organTypeId" type="int" boolChange="false" mappingField="organTypeId" defaultValue=""></Field-Mapping>
	<Field-Mapping property="organTypeName" type="java.lang.String" boolChange="false" mappingField="organTypeName" defaultValue=""></Field-Mapping>
	<Field-Mapping property="organTypeDesc" type="java.lang.String" boolChange="false" mappingField="organTypeDesc" defaultValue=""></Field-Mapping>
  </Persistent-Mapping>

  <ValueObject-Mapping vo="com.goldenchance.common.test.OrganVO" matching="false" ref="1">
	<Field-Mapping property="OrganId" type="java.lang.String" boolChange="false" mappingField="OrganId" defaultValue=""></Field-Mapping>
	<Field-Mapping property="OrganNo" type="java.lang.String" boolChange="false" mappingField="organNo" defaultValue=""></Field-Mapping>
	<Field-Mapping property="OrganName" type="java.lang.String" boolChange="false" mappingField="organName" defaultValue=""></Field-Mapping>
  </ValueObject-Mapping>
  <ValueObject-Mapping vo="com.goldenchance.common.test.OrganTypeVO" matching="true" ref="2" >
	<Field-Mapping property="organTypeId" type="int" boolChange="false" mappingField="organTypeId" defaultValue=""></Field-Mapping>
	<Field-Mapping property="typeName" type="java.lang.String" boolChange="false" mappingField="organTypeName" defaultValue=""></Field-Mapping>
	<Field-Mapping property="organTypeDesc" type="java.lang.String" boolChange="false" mappingField="organTypeDesc" defaultValue=""></Field-Mapping>
  </ValueObject-Mapping>  
</ORMapping>
0 请登录后投票
   发表时间:2004-08-25  
补充一点,字段的映射采用了反射和配置文件映射两种方式,通过配置文件中的matching来判断,如果是true,则通过反射赋值,字段的匹配就不需要了
0 请登录后投票
   发表时间:2004-08-25  
引用
没有看得太懂楼主的代码,总感觉思路有些差别(也许我对AOP不熟悉吧)。

是有差别。根本就说的不是一个东西。
我的就是一个怎样处理result set,你的可关联到事务处理,实体类设计,or映射,这些东西复杂得多。
可以把这些东西都加入home-made的持久层,只不过很难做到完美。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics