[猫爪--综合] 一个自制简陋的持久层方案
Joard
2007-09-01
今天确实郁闷,打开电脑本想继续完善代码,
结果却发现代码尽然忘记放进u盘,着实郁闷啊! 今天代码就不贴代码了,过两天在补上。 在这里和大家探讨一下我对这个持久层的思路,想法和遇到的问题。 但是再开始之前,先向大家推荐两篇文章 《你擦了吗?确定擦了?真的确定擦了?》 http://www.iteye.com/article/13649 《一个自制持久层的方法》 http://www.iteye.com/topic/7068 毫无例外,这两篇文章都是ajoo牛以前的文章 其中《你擦了吗》是以前就拜读过好几次了,虽然文中比喻不雅,但也正好符合我等粗人。后一篇文章是写搜索到的,里面的思想对我这样的小菜鸟也挺受用,各位要是有时间也可以看看。 下面,我就直接说正题了 PersistentRecord 这就是我这个简陋持久层方案的名字 它的主要要求是 第一,简单,不需要任何xml文件, 唯一的配置文件就是一个数据库相关的properties文件 第二,支持部分功能的ORM 只支持单表的ORM,象多表查询,表间关联(对象之间的关系)这些都不支持。 但是对单表操作是支持ORM的。 第三,侵入性不太大,只要求你的数据库表是单一主键或使用代理主键。 第四,代码少(功能少,必然代码也多不到那里去) 第五,代码生成(后面就会提到) 第六,可测试性 说它简陋是因为持久层里面很重要的两个东西--缓存以及表间关联,我完全没考虑实现。虽然缓存这东西java里不错的现成解决方案可以选择,但是到现在还没打算是否为PersistentRecord加上缓存。至于表间关联,则完全是复杂度的考虑,不是说自己实现这个有多难,也不是担心实现的代码多么丑陋,而是没有太大必要,如果要用到表间关联,我看直接使用hibernate3+annotation就可以了,这个玩意上手也挺快地。少了这两个东西,复杂度下降不少啊。 写PersistentRecord的时候,主要是借鉴了rails和spring的daotemplate,hibernate3的annotation。整体上来说PersistentRecord是一个表记录封装框架,是一个以数据库表记录为中心的框架。第五条和第六条都是初步打算,通过代码生成来生成对应的类以及dao。可测试性也就是也一个junit测试类,让它在单元测试的时候自动导入数据和测试完后自动删除数据。 来一个简单的例子 加入数据库里有这样一张表 create table product ( id int not null, title varchar(100) not null, price decimal(10,2) not null, primary key (id) ) 通过generate生成这样一个类 @PersistentRecord public class Product { private long id; private String title; private BigDecimal price; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getTitle() { return titles; } public void setTitle(String title) { this.titles = title; } public BigDecimal getPrice() { return price; } public void setPrice(BigDecimal price) { this.price = price; } } 这个Product类就体现出PersistentRecord的思路一:一个类对应一张表,类的成员变量对应表的列,每一个类的实例对应表中的一条记录。但在这里要求该表是一个单一主键的表(或者使用了代理主键)就象Product.id一样。 annotation上场了 如果Product对应的表或者表中的字段发生了变化,怎么办呢! 哈哈祭出annotation,让我们摆脱配置文件的束缚。PersistentRecord里提供了至少3种annotation。 PersistentRecord:就如上面代码里面所示,PersistentRecord这个annotation是一个标注型annotation,它负责告诉持久层的核心PersistentRecordManager“你现在处理的这个类是一个可以被持久化的PersistentRecord”,当然,这也表明如果你个bean不是一个用@PersistentRecord标注的类的话,你会在运行期得到一个异常提示你该类无法被持久化。 Table:如果对应的表发生了变换,那么这个Table就可以派上用场了 @Table(name="Product") public class Product { 通过这样就可以改变类与之对应的表。 Column:同样数据库表的列发生了变化,这样些就可以了 @Column(name="title") private String titles; 最后变更后的代码会是这样 @PersistentRecord @Table(name="Product") public class Product { @Column(name="id",isPrimaryKey=true) private long key; @Column(name="title") private String titles; private BigDecimal price; public long getId() { return key; } public void setId(long id) { this.key = id; } public String getTitle() { return titles; } public void setTitle(String title) { this.titles = title; } public BigDecimal getPrice() { return price; } public void setPrice(BigDecimal price) { this.price = price; } } 晕,不对呀,怎么多了一个“@Column(name="id",isPrimaryKey=true)”,其实我打算是这样地,PersistentRecord会默认id为主键,但如果你的表不是这样的时候,就可以如上述代码那样做,明确告诉PersistentRecord你的表的主键是哪一个,但同时isPrimaryKey=true 只能设置一次,否则你会得到一个异常。 好了,思路一大体上是这个样子了。接下来说说说说这个框架的核心类PersistentRecordManager 思路二,使用回调来处理数据库连接和事务 try{ template.manageRecord(new ManageRecordCallback(){ public Object doInPersistentRecordManager(PersistentRecordManager manager) throws SQLException { Product newp = new Product(); newp.setId(4); newp.setPrice(new BigDecimal(85)); newp.setTitle("Unix Shell"); manager.insert(newp); Product p = manager.get(Product.class,new Long(4)); printProduct(p); manager.locked(p); p.setPrice(new BigDecimal(128.5)); manager.update(p); p = manager.reload(p); printProduct(p); int count = manager.countByConditions(Product.class,"@key < ?", 5); System.out.println("Count_Product : " + count + "\n"); manager.delete(p); List<Product> list = manager.findAllByConditions(Product.class, "@key < ? and @price>?", 2, 80); if(list != null) for(Product tp : list){ printProduct(tp); } Product tp = manager.find(Product.class, "find_by_titles_and_price", "Programming Ruby 2nd", 99); printProduct(tp); return null; } }); } catch (SQLException se) { se.printStackTrace(); } 基本上提供的操作就是如此了,基本的CRUD就不说了,这这里就简单地说说三个函数: countByConditions和findAllByConditions这两个函数的第一个参数是你要处理的类,第二个参数是conditions,第三个及以后的参数是实际查询的条件的值。关于第二个参数,多说两句。"@key < ? and @price>?",这里第一个参数所指定的类里的成员成员变量名前加上一个@符号,就可以生成正确的sql了,当然,你也可以直接些表里面的列名,但这样不太好,最好还是写类的成员变量名。 find方法和前两者类似,这里不太一样的是第二参数"find_by_titles_and_price",它强制要求传入的参数必须以find_by_开头,每个单词之间用and分开。(略了解Rails的大大们笑了,“这不就是ActiveRecord里面的计量吗?人家用method_missing人家用来实现,你就用字符串阿”),当然,这里的每个单词也是类的成员变量名。 要是觉得上面的代码太丑陋了,不用担心,以后用dao在封装一下,就能好看点了。 现在有一个问题征询一下大家的意见,现在有这样块代码 final Connection conn = getConnection(); final Transaction trans = new Transaction(conn); try{ trans.beginTransaction(); final PersistentRecordManager manager = new PersistentRecordManager(trans); Object result = action.doInPersistentRecordManager(manager); trans.commit(); return result; } catch (SQLException se) { trans.rollback(); throw se; } finally { DaoUtils.closeConnection(conn); } 这段代码里事务是由框架负责提交地,我就在想到底该不该下放事务的更多自主权,而不是由框架来负责提交或回滚。例如根据业务需要来回滚事务,也需需要在事务完成后继续做一点事情?如果不该下放的话,那么我这里就会像spring那样实现一个TransactionStatus;如果该下放的话,怎么实现才能更好呢? |
|
apolloty
2007-09-03
框架负责事务,然后rollback时给业务层返回状态即可
|
相关讨论
相关资源推荐
- extjs2.2+dwr2.0+struts1.3实现的WebQQ 即时聊天
- 你必须知道的495个C语言问题电子书pdf下载
- sql语句常见问题集锦
- ExtJS+DWR+Spring+Hibernate开发HRMS(1)
- ExtJS+DWR+Spring+Hibernate开发HRMS(5)
- ExtJS+DWR+Spring+Hibernate开发HRMS(2)
- Extjs实战(Extjs+Spring+Hibernate+dwr)章一:各框架的整合
- ExtJS+DWR+Spring+Hibernate开发HRMS(4)
- ExtJS+DWR+Spring+Hibernate开发HRMS(3)
- 基于Qt开发的截图工具- 支持全屏截图, 支持自定义截图,支持捕获窗口截图,支持固定大小窗口截图,颜色拾取,图片编辑