论坛首页 Java企业应用论坛

一个ActiveRecord的简单实现(没有配置,没有注解,只有约定)

浏览 7982 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (17) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-05-25   最后修改:2010-05-26
一个类extends ActiveRecordBase后,将自动获得对指定表的insert,update,delete操作能力,仅仅需要指定表名。如果表名规范,约定也可以。

ActiveRecordBase通过反射和命名约定,自动匹配子类属性和子类指定表字段。
例User.userId <-> t_user.user_id等等

ActiveRecordBase里还内置了Spring JdbcTemplate(非常好用的东西,推荐还在使用Jdbc的同学使用).

public class User extends ActiveRecordBase {

	public User() {
		this.setTableName_("t_user");
	}

	public BigDecimal userId;
	public String userName;
	public String password;
	public Timestamp insertTime;

public String toString(){
		return "user_id="+this.userId+"    userName="+this.getUserName()+"    password="+this.getPassword()+"    insert_time"+this.getInsertTime();
	}

	public static void main(String[] args) throws Exception {
		User user = new User();
		user.setUserId(new BigDecimal(1));
        user.setUserName("user");
        user.setPassword("pwd");
        user.setInsertTime(new Timestamp(new Date().getTime()));
        //save object to db
        user.insert();
        
        System.out.println("t_user info after insert:"+(User)user.getJdbcTemplate_().queryForObject("select * from t_user where user_id="+user.userId,new BeanPropertyRowMapper(User.class)));
        
        user.setPassword("pwdChanged");
        //update
        user.update();
        System.out.println("t_user info after update:"+(User)user.getJdbcTemplate_().queryForObject("select * from t_user where user_id="+user.userId,new BeanPropertyRowMapper(User.class)));
        
        user.delete();
        System.out.println("t_user count after delete:"+user.getJdbcTemplate_().queryForInt("select count(1) from t_user where user_id="+user.userId));
        
	}

}


测试结果:
--一条记录生成了
Activerecord sql=INSERT INTO T_USER(USER_ID,USER_NAME,PASSWORD,INSERT_TIME) VALUES (?,?,?,?)
t_user info after insert:user_id=1    userName=user    password=pwd    insert_time2010-05-24 00:00:00.0

--password被修改了
Activerecord sql=UPDATE T_USER SET USER_ID=?,USER_NAME=?,PASSWORD=?,INSERT_TIME=? WHERE USER_ID=?
t_user info after update:user_id=1    userName=user    password=pwdChanged    insert_time2010-05-24 00:00:00.0

--记录被删除了
Activerecord sql=DELETE FROM T_USER WHERE USER_ID=?
t_user count after delete:0

注意:User类main方法里的sql语句仅仅是为了使用JdbcTemplate来验证ActiveRecord的操作是成功的,本身并不需要sql语句。但我们同时也可以看到JdbcTemplate的强大,使用BeanPropertyRowMapper后,具有一条语句将表直接映射成对象的能力。
   发表时间:2010-05-25  
ActiveRecordBase中动态组合sql。以insert和delete为例:

public class ActiveRecordBase {
        public String tableName_;
	
        public void insert() {
		SqlParameters sqlParams = this.getInsertSqlParams();
		this.getJdbcTemplate_().update(sqlParams.getSql(),
				sqlParams.getParameters(), sqlParams.getTypes());
	}

public SqlParameters getInsertSqlParams() {
		SqlParameters sqlParams = new SqlParameters();
		List tableColumnsList = this.getTableColumns();
		Iterator iter = tableColumnsList.iterator();
		StringBuffer placeholder = new StringBuffer("");
		StringBuffer insertStr = new StringBuffer("INSERT INTO ").append(
				this.getTableName_()).append("(");
		Object[] params = new Object[tableColumnsList.size()];
		int[] types = new int[tableColumnsList.size()];
		int index = 0;
		for (int i = 0; i < tableColumnsList.size(); i++) {
			Column c = (Column) iter.next();
			params[index] = getClassProperties().get(c.getColumnName());
			types[index] = c.getColumnType();
			index++;
			insertStr.append(c.getColumnName());
			placeholder.append("?");
			if (index != tableColumnsList.size()) {
				insertStr.append(",");
				placeholder.append(",");
			}
		}
		insertStr.append(") VALUES (").append(placeholder).append(")");
		sqlParams.setSql(insertStr.toString());
		sqlParams.setParameters(params);
		sqlParams.setTypes(types);
		return sqlParams;
	}
	public void delete() {
		StringBuffer sql = new StringBuffer("DELETE FROM ").append(
				this.getTableName_()).append(" WHERE ").append(
				this.getPK().getColumnName()).append("=?");
		Object[] params = new Object[] { this.getClassProperties().get(
				this.getPK().getColumnName()) };
		this.getJdbcTemplate_().update(sql.toString(), params);
	}
}

0 请登录后投票
   发表时间:2010-05-25  
第一次执行时,取表结构。取到后cache住。

public List getTableColumns() {
		if (Cache.tableColumnsCache.get(this.getTableName_()) == null) {
			List list = (List) jdbcTemplate_.execute(new ConnectionCallback() {
				public Object doInConnection(Connection con)
						throws SQLException, DataAccessException {
					DatabaseMetaData dbMetaData = con.getMetaData();
					ResultSet rsColumns = dbMetaData.getColumns(null, null,
							getTableName_(), null);
					List columnsList = new ArrayList();
					while (rsColumns.next()) {
						Column c = new Column();
						c.setColumnName(rsColumns.getObject(4).toString()
								.toUpperCase());
						c.setColumnType(((BigDecimal) rsColumns.getObject(5))
								.intValue());
						columnsList.add(c);
					}
					ResultSet rsPK = dbMetaData.getPrimaryKeys(null, null,
							getTableName_());
					if (rsPK.next()) {
						Column c = new Column();
						c.setColumnName(rsPK.getObject(4).toString());
						c.setColumnType(((BigDecimal) rsPK.getObject(5))
								.intValue());
						Cache.tablePKCache.put(getTableName_(), c);
					}
					return columnsList;
				}
			});
			Cache.tableColumnsCache.put(this.getTableName_(), list);
		}
		return (List) Cache.tableColumnsCache.get(this.getTableName_());
	}
0 请登录后投票
   发表时间:2010-05-27  
这种方案似乎没考虑多表的情况,比如父子关系。
0 请登录后投票
   发表时间:2010-05-27  
我觉得找表结构不好,要通过bean反射
0 请登录后投票
   发表时间:2010-05-27  
joehe 写道
我觉得找表结构不好,要通过bean反射


1. 值就是通过反射得到的。
2. 如果user表中有一些不属于表本身的属性时,我需要取bean的属性和表结构的共同部分,所以表结构还是要取的。
0 请登录后投票
   发表时间:2010-05-27  
可以看看http://www.scooterframework.com的activerecord实现,目前发现这个框架挺强的,相对于playframework来说,controller全都是静态方法来说好多了~~
0 请登录后投票
   发表时间:2010-05-27  
Arden 写道
可以看看http://www.scooterframework.com的activerecord实现,目前发现这个框架挺强的,相对于playframework来说,controller全都是静态方法来说好多了~~


谢谢,我之前看过他们。但我不太能接受他这样的写法,它不是直接操作属性,而是全部用setData(key, value)来做的。

http://www.scooterframework.com/docs/activerecord_basics.html

Create a record in database:

    ActiveRecord post = new Post();
    post.setData("title", "Happy Java Programming");
    post.setData("body", "Java programming is fun.");
    post.create();

The last line above is equivalent to the following sql statement:

    insert into posts (title, body) values ('Happy Java Programming', 'Java programming is fun.');

0 请登录后投票
   发表时间:2010-06-19  
最近花时间完善了一下,现在稳定下来的api如下:
特别是findByWhere,支持任何查找条件,就是支持写where条件的sql。对Jdbc熟悉的人来说,用起来几乎没有任何学习曲线。


--按主键查找,pk为1的
User user = (User)User.find(User.Class,1);

--查找user_name="sean.zhou"的
User user = (User)User.findByWhere(User.Class,"user_name = ?", new Object[]{"sean.zhou"});

--查找所有sean打头的
List<User> users = User.findList(User.class,"user_name like ?", new Object[]{"sean%"});

--插入一条新记录
user.insert();

--更新记录
user.update();

--删除记录
user.delete();

0 请登录后投票
   发表时间:2010-06-19   最后修改:2010-06-19
建议楼主考虑下一对多和多对多的情况,还有就是联合主键的情况,还有查询的时候如果是单表的最好把查询条件封装对象
0 请登录后投票
论坛首页 Java企业应用版

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