论坛首页 Java企业应用论坛

反思Hibernate,可以有更简洁、更高效的ORM实现

浏览 34448 次
该帖已经被评为隐藏帖
作者 正文
   发表时间:2009-06-05   最后修改:2009-06-05
  我思考了良久才决定发这篇文章,各位老大手下留情。

  假设有一张表Student,有几个字段ID,Name,Gender,BirthDay,实际上这么一个从数据库设计中直接生成的类就可以很好地满足我们对于ORM的要求:

public class StudentSchema extends Schema {
	private int ID;

	private String Name;

	private String Gender;

	private Date BirthDay;

	public static final TableName = "Student";

	public static final PrimaryKeys PKs = new PrimaryKeys(new String[] { "ID" });

	public Date getBirthDay() {
		return BirthDay;
	}

	public void setBirthDay(Date birthDay) {
		BirthDay = birthDay;
	}

	public String getGender() {
		return Gender;
	}

	public void setGender(String gender) {
		Gender = gender;
	}

	public int getID() {
		return ID;
	}

	public void setID(int id) {
		ID = id;
	}

	public String getName() {
		return Name;
	}

	public void setName(String name) {
		Name = name;
	}
}


  其中Schema是一个公用类,里面主要是提供了fill(),update(),delete(),backup()等功能。其中fill()就是通过主键返回一条记录,其他几个方法就不用说了吧。

  但反观Hibernate呢?我们需要一个与StudentSchema类似的POJO类,一个DAO类,一个*.hbm.xml映射文件,才能达到类似的效果,机制繁琐,性能消耗明显要高。

  我不知道为什么这么简单的高下立见的对比没有引起大家的注意,我也承认我对Hibernate浅尝辄止,并且我估计会引来大片的攻击,但我确实想像不出Hibernate有可能会比这更简洁更高效。在我们的实际应用中,StudentSchema是由工具从PowerDesigner中直接生成的,开发人员根本不管ORM,直接使用即可。StudentSchema是硬编码的类,不需要进行字节码动态生成,不需反射,不需配置文件,十分简单。

  更重要的是,我们这个ORM实现总共才几千行代码。Hibernate功能很多,但我们的ORM实现虽然代码很少,但己能覆盖实际开发中的全部ORM需求。我将以回复的方式加以说明。
   发表时间:2009-06-05  
直接摘抄文档吧:

3.1 Schema的使用
Schema对应着数据表中的一行完整的记录,Schema类提供了对记录进行操作的众多方法,示例如下:

取数据:
	ZDUserSchema user = new ZDUserSchema();// new一个Schema实例
	user.setUserName("alex");// 设置字段UserName的值
	user.fill();// 根据主键值从数据库中取所有字段,如果主键值未set,则报错
	user.getMobile();// 取得Mobile字段的值
	user.getEmail();// 取得Email字段的值


插入数据:
	ZDUserSchema user = new ZDUserSchema();// new一个Schema实例
	user.setUserName("test");
	user.setPassword(StringUtil.md5Hex(password));
	user.setMobile("0086-13012340000");
	user.setEmail("test@hotmail.com");
	user.setAddUser("admin");
	user.setAddTime(new Date());
	user.insert();// 插入记录到数据库;


更新数据:
	ZDUserSchema user = new ZDUserSchema();// new一个Schema实例
	user.setUserName("alex");
	user.setMobile("0086-13012340000");
	user.setEmail("test@hotmail.com");
	boolean flag = user.update();// 更新用户alex的Mobile和Email两个字段


删除数据:
	ZDUserSchema user = new ZDUserSchema();// new一个Schema实例
	user.setUserName("alex");
	boolean flag = user. delete ();// 删除用户alex


其他操作:
	user.toDataRow();//转换成DataRow
	user.toMapx();//转换成Mapx
	user.setValue(dr);//将DataRow中的数据按字段名称匹配到Schema中
	user.setValue(map);//将Mapx中的数据按字段名称匹配到Schema中
	user.clone();//Cloneable
	user.getColumnCount();//返回列数
	user.getV(0);//取第1个字段的值


3.2 Set的使用
Set对应着数据表中的多条记录,Set类提供了对记录集进行操作的方法,示例如下:

取记录集:
	ZDUserSchema user = new ZDUserSchema();
	user.setAddUser("admin");
	ZDUserSet set = user.query();// 查询所有由admin添加的用户
	for (int i = 0; i < set.size(); i++) {
		ZDUserSchema u = set.get(i);
		System.out.println(u.getUserName());
	}


添加新的Schema到Set中:
	ZDUserSchema alex = new ZDUserSchema();
	alex.setUserName("alex");
	alex.fill();// 查询出alex的所有字段
	set.add(alex);// 将alex加入到set中


记录集反馈回数据库:
	set.insert();//将记录集插入数据库
	set.update();//将记录集更新到数据库
	set.delete();//从数据库中删除记录集
	set.backup();//备份记录集到B表
	set.deleteAndBackup();//删除记录集同时备份到B表
	set.deleteAndInsert();//插入记录,插入之前先删除己有的记录,以避免可能的主键冲突


其他操作:
	set.clear();// 清空Set中的所有Schema
	set.clone();// Cloneable
	set.toDataTable();// 转换成DataTable;
	set.filter(new Filter() {// 只要Email是hotmail邮箱的用户
		public boolean filter(Object obj) {
			ZDUserSchema user = (ZDUserSchema) obj;
			if (user.getEmail().endsWith("@hotmail.com")) {
				return true;
			}
			return false;
		}
	});
	set.sort(“UserName”);//按用户名排序
	set.sort(new Comparator() {// 按添加时间排序
		public int compare(Object obj1, Object obj2) {
			ZDUserSchema user1 = (ZDUserSchema) obj1;
			ZDUserSchema user2 = (ZDUserSchema) obj2;
			return user1.getAddTime().compareTo(user2.getAddTime());
		}
	});
0 请登录后投票
   发表时间:2009-06-05  
4 事务支持
Zving Framework支持传统型事务和非阻塞事务两种事务模式。
传统型事务即JDBC本身提供的事务支持,在这种事务模式下,一开始便打开数据连接,然后执行Java业务逻辑,在Java逻辑中不时执行数据库操作,但这些操作并不立刻改变数据库中的数据,而是等最后执行commit动作时才一次性写入数据库。Zving Framework针对这种类型的事务提供了DataAccess类,代码示例如下:
		DataAccess da = new DataAccess();
		try {
			da.setAutoCommit(false); 
			da.executeNoQuery(new QueryBuilder("delete from zduser where username='test'"));
			da.insert(user);
			da.delete(alex);
			da.commit();
			da.setAutoCommit(true); 
		} catch (SQLException e) {
			try {
				da.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		} finally {
			try {
				da.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

注意:使用DataAccess需要手动调用close()方法,以实现连接的关闭。

非阻塞事务是Zving Framework中提供的事务模式,在这种事务模式下,一开始并不占用连接,执行Java业务逻辑过程不时声明需要执行的操作,在最后执行commit动作时才申请连接并依次执行先前声明的操作,并且开发人员不需要手工管理连接。代码示例如下:
		Transaction tran = new Transaction();
		tran.add(new QueryBuilder("delete from zduser where username='test'"));
		tran.add(user, Transaction.INSERT);
		tran.add(alex, Transaction.DELETE);
		tran.add(set, Transaction.DELETE_AND_BACKUP);
		tran.commit();


在传统型事务中下一个数据库操作可以依赖于上一个数据库操作产生的结果,但无阻塞事务则不能。而传统型事务数据库一开始就占用连接,在两个数据库操作之间的业务逻辑处理过程中本不需要连接,但也一直被占用;而无阻塞型事务则只有最后才使用连接,连接占用的时间被压缩到了极致。如果Web应用同时在线用户数较大且业务较复杂,则无阻塞型事务比传统型事务具有相当大的性能优势。另一方面,传统型事务必须手工管理资源释放,而无阻塞型事务则不需要。
实践开发中几乎全部事务都可以改造成无阻塞事务,建议开发人员只使用无阻塞型事务。
0 请登录后投票
   发表时间:2009-06-05  
2.1执行SQL语句
通过使用QueryBuilder类,可以方便地对在数据库中执行SQL语句。QueryBuilder需要给定一个参数化的SQL语句,并设置它的参数,然后指定的相应的操作方法即可操作数据库,而不需要手工管理Connection、Statement、ResultSet等对象。之所以使用参数化的SQL语句,而不是直接用字符串拼接SQL,是基于以下几点原因:
1、 用字符串拼接SQL,会引入外部参数中不安全的SQL逻辑,从而受到SQL注入攻击。(请参考SQL注入相关资料)。
2、 QueryBuilder内部调用PreparedStatement处理参数化SQL,能够有效地避免SQL注入攻击。
3、 参数化SQL使得代码具有良好的可读性和可维护性。

QueryBuilder有两类构造函数:
1、 带一个String参数,即指定的SQL语句。
2、 带二或者三个参数,第一个参数为指定的参数化SQL语句,第二、第三个参数可以是int、long、String、或者Object。

通常的参数化SQL语句参数个数小于2个,所以通常使用第二类构造函数,直接指定参数。
有的SQL语句参数个数大于2个,则需要使用第一类构造函数,先指定SQL语句,然后通过add方法设置参数。
参数设置后,可以通过set方法进行修改。
QueryBuilder拥有executePagedDataTable方法,以提供分页查询的能力。QueryBuilder自动针对当前数据库类型形成分页SQL语句,使得只有当前页的数据载入内存,从而获得良好的性能。

以下是代码示例:

简单SQL查询:
	DataTable dt = new QueryBuilder("select * from user where userid=?", userid).executeDataTable();

多参数SQL查询:
	QueryBuilder qb = new QueryBuilder("select * from user where userid=? and addtime=? and lastlogintime=?");
	qb.add(userid);
	qb.add(DateUtil.parse("2006-01-01"));
	qb.add(new Date());
	DataTable dt = qb.executeDataTable();


分页查询:
	DataTable dt = new QueryBuilder("select * from user where userid=?", userid).executePagedDataTable(15,0);


删除、修改操作:
	new QueryBuilder("delete from user where userid=?",userid).executeNoQuery();


批量操作:
	QueryBuilder qb = new QueryBuilder("update user set mobile=?,email=? Where username=?");
	qb.add("0086-13988877001");
	qb.add("user1@hotmail.com");
	qb.addBatch();
	qb.add("0086-13988877001");
	qb.add("user1@hotmail.com");
	qb.addBatch();
	qb.add("0086-13988877001");
	qb.add("user1@hotmail.com");
	qb.addBatch();
	qb. executeBatch ();//批量更新3条数据,这比分别更新3条数据性能要好


返回单个值:
	int count = new QueryBuilder("select count(*) from user").executeInt();


2.1 DataTable类
通过QueryBuilder类的executeDataTable或者executePagedDataTable方法,可以得到一个DataTable实例。DataTable是对ResultSet的封装,提供了众多ResultSet不具备的功能:
1、 不需要移动当前位置,直接存取任意一行数据。
2、 直接存取任意一行的任意一列(通过列索引或列名)。
3、 直接修改任意一行的任意一列(通过列索引或列名)的值。
4、 可以对DataTable的行进行增、删、改等操作(只在内存中操作,以便于利用数据,不反馈到数据库)。
5、 可以对DataTable的列进行增、删、改等操作(只在内存中操作,以便于利用数据,不反馈到数据库)。
6、 可以合并两个DataTable,如果这两个DataTable列相同的话。
7、 可以筛选掉不符合条件的列,得到一个新的DataTable。
8、 支持按Comparable排序。
9、 可以将所有行的某一列转成数组。
10、 可以将某一行转成以字段名为key的HashMap。

以下是代码示例:

获取值:
	Object obj = dt.get(3, 4);//取第4行,第5列的值,下标从0开始
	String str = dt.getString(0, "username");//取第1行username列的值

设置值(设置的值不反馈回数据库,但可以用get方法取到,以便于数据利用):
	dt.set(0, 0, "TRUE");//将第1行第1列的值设为字符串TRUE
	dt.set(0, "username", "mike");//北朝鲜第1行username列的值设为mike

行操作:
	Object[] rowValue = new Object[] { "john", "password", "2008-01-11", "220.194.110.74" };
	dt.insertRow(rowValue);// 在DataTable最后追加一行,各列数据按rowValue中的值依次填充
	dt.insertRow(rowValue, 0);// 在DataTable最前面插入一行
	dt.deleteRow(9);// 删除第10行
	DataRow dr = dt.getDataRow(0);// 得到第1行的DataRow实例,DataRow是对一行数据的封装
	String username = dr.getString("username");// 从dr中取username的值;
dr.fill(map);//将map中的值按字段名匹配(不区分大小写)到行的字段中
	dt.getDataRow(0).toMapx();// 将第1行转成Mapx









列操作:
	dt.insertColumn("FirstName");// 增加FirstName列
	dt.insertColumn(new DataColumn("Age", DataColumn.INTEGER));// 增加Age列,类型为int
	dt.deleteColumn(4);// 删除第5列
	dt.deleteColumn("FristName");// 删除FirstName列
	Object[] columnValues = dt.getColumnValues(0);// 得到全部行的第一列的值组成的数组
	dt.getDataColumn(0).setColumnName("UserName");// 将第一列的列名修改为UserName

其他操作:
	dt.clone();// Cloneable
	dt.union(anotherDataTable);// 两个DataTable合并
	dt.getColCount();// 返回列数
	dt.getRowCount();// 返回行数
	dt.filter(new Filter() {// 过滤掉Age<18的记录
		public boolean filter(Object obj) {
			DataRow dr = (DataRow) obj;
			if (Integer.parseInt(dr.getString("Age")) < 18) {
				return false;
			}
			return true;
		}
	});
	dt.sort(new Comparator() {// 按Age列排序
		public int compare(Object obj1, Object obj2) {
			DataRow dr1 = (DataRow) obj1;
			DataRow dr2 = (DataRow) obj2;
			return Integer.parseInt(dr1.getString("Age")) - Integer.parseInt(dr2.getString("Age"));
		}
	});
0 请登录后投票
   发表时间:2009-06-05  
学那个Hibernate就快把人搞死了,
现在用这个又要学新东西.
现在的技术啊,老是与时惧进,东西越来越多.

用这个东西也许能提高些效率,可是以后维护怎么办?
来的人还得从头开始学习,理解,消化...
不看好.
0 请登录后投票
   发表时间:2009-06-06  
liujunsong 写道
学那个Hibernate就快把人搞死了,
现在用这个又要学新东西.
现在的技术啊,老是与时惧进,东西越来越多.

用这个东西也许能提高些效率,可是以后维护怎么办?
来的人还得从头开始学习,理解,消化...
不看好.


不用的呀,其实写在纸面上一大堆,实际上这个很简单,开发人员都不管,Schema类都是自动生成的。
我们的程序员入职花在ORM上的时间不超过一小时。
0 请登录后投票
   发表时间:2009-06-06  
这是构想中的东西还是已经实现了?
我的框架里也实现了类似操作的ORM工具。
不过下面示例中的table构建比较复杂。
可以说成字段对象集,是个中间层。
可以从类似你上面的StudentSchema类转换过来。
有些操作不是很河蟹,还在重构中。


        SampleArticleTableEntry table = SampleArticleTableEntry.newInstance();
        table.getId().turnOff();
        table.setPrimeKey("id", "NEXT VALUE FOR ARTICLE_SEQ_ID ");
        table.setTitle("标题灰常党");
        table.setContent("内容灰常虚~");
        table.setEditTime(time);
        effectRowCount = zdo.contactTable(table).insert();
        assertEquals(effectRowCount, 1);



你上面提到的Zving Framework是你们自己的框架吧?是否准备开源?
我从Php进入Java快半年了,一直在为开发一套亲近Php开发人员使用的框架而努力。
这里的大牛比较多,提问很容被莫名鄙视。所以希望有机会能私下交流。
QQ:5943590 Email:SysTem128@GMail.Com
0 请登录后投票
   发表时间:2009-06-06  
Bernard 写道
这是构想中的东西还是已经实现了?
我的框架里也实现了类似操作的ORM工具。
不过下面示例中的table构建比较复杂。
可以说成字段对象集,是个中间层。
可以从类似你上面的StudentSchema类转换过来。
有些操作不是很河蟹,还在重构中。


        SampleArticleTableEntry table = SampleArticleTableEntry.newInstance();
        table.getId().turnOff();
        table.setPrimeKey("id", "NEXT VALUE FOR ARTICLE_SEQ_ID ");
        table.setTitle("标题灰常党");
        table.setContent("内容灰常虚~");
        table.setEditTime(time);
        effectRowCount = zdo.contactTable(table).insert();
        assertEquals(effectRowCount, 1);



你上面提到的Zving Framework是你们自己的框架吧?是否准备开源?
我从Php进入Java快半年了,一直在为开发一套亲近Php开发人员使用的框架而努力。
这里的大牛比较多,提问很容被莫名鄙视。所以希望有机会能私下交流。
QQ:5943590 Email:SysTem128@GMail.Com


准备开源,还在整理,很快了
1 请登录后投票
   发表时间:2009-06-06   最后修改:2009-06-06
很好奇关联关系是如何实现的?
LZ为什么不举个例子?
0 请登录后投票
   发表时间:2009-06-06  
55555 楼主的想法不错的呢
呵呵 只不过我们那的都是分模块的 不是集中设计的 所以我们很少用继承

对  了楼主留个qq什么的吧 以便交流
0 请登录后投票
论坛首页 Java企业应用版

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