论坛首页 Java企业应用论坛

用反射注解实现的ORM(一看就明白的那种)

浏览 9277 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (18) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-05-24   最后修改:2010-05-24

之所以会写这个程序完全是因为偷懒^^,那时在公司实习要我们用jdbc来写练习,想想已经很久没用过纯的jdbc来写操作数据库了,用Hibernate多好啊,最少也可以用ibatis,想到那么SQL语句就烦,写来写去都是那几句。那时刚好学过反射和注解所以就决定自己写一个ORM框架,当然只是简单的那种,刚开始用到了apache下的开源项目dbutils,不过后来就慢慢的改进用反射来代替,虽然现在那用到这个包,关键部分还是用反射实现的.

 

核心类:

 

package com.permission.utils.db;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import com.permission.utils.log.LogUtil;




/**
 * @Title: OperateDB.java
 * @Package com.cai.order.db
 * @Description: TODO(ORM)
 * @author awfhome@163.com
 * @date 2010-3-6 下午01:46:05
 * @version V1.2
 */
public class OperateDB {
	
	private Connection conn = null;
	private String sql ="";
	private PreparedStatement pst;
	private Hashtable map = null;
	
	
	
	public void setConnection(Connection conn) {
		this.conn = conn;
	}


	/**
	 * 创建sql语句
	 */
	public void createQuery(String sql){
		map = new Hashtable(); //初始化
		this.sql = sql;	
	}
	
	
	/**设置参数*/
	@Deprecated
	public void setParameter(String name,String val){
		map.put(name, val);
	}
		
	/**设置String类型参数*/
	public void setParameterString(String name,String val){
		map.put(name, val);
	}
	/**设置Integer类型参数*/
	public void setParameterString(String name,Integer val){
		map.put(name, Integer.toString(val));
	}
	/**设置Double类型参数*/
	public void setParameterString(String name,Double val){
		map.put(name, Double.toString(val));
	}
	
	
	
	

	/**
	 * 处理sql语句(数据库标准的sql)
	 */
	private String doSql(){
		
		if(sql!=null && !"".equals(sql)){
			String tem = "";
			Enumeration e = map.keys();
			while(e.hasMoreElements()){
				tem = (String)e.nextElement();
				this.sql = sql.replace(":" + tem, "'" +(String) map.get(tem)+"'");
			}
		}else {
			System.out.println("没有sql语句");
		}
		LogUtil.info("这次调用的sql语名: " + sql);
		return sql;
	}
	
	

	
	
	/**
	 * 返回类数组,这部分用的是dbutils
	 */
	public List list(Class type) throws SQLException{
		doSql(); //调用处理sql成标准
		List list = null;
		QueryRunner queryR = new QueryRunner();
		list = (List)queryR.query(conn, sql,  new BeanListHandler(type));
		return list;
	}
	
	
	/**
	 * 返回单个数据
	 */
	public Object uniqueResult(Class type) throws SQLException{
		doSql();
		Object object = null;
		List list = null;
		QueryRunner queryR = new QueryRunner();
		list = (List)queryR.query(conn, sql,  new BeanListHandler(type));
		if(list != null && list.size()!=0) object = (Object)list.get(0);
		return object;
	}
	
	
	/**
	 * 返回影响行数
	 */
	public int executeUpdate() throws SQLException{
		doSql();
		QueryRunner queryR = new QueryRunner();
		int a = queryR.update(conn,sql);
		return a;
	}
	
	
	
	/** 添加数据
	 * @throws NoSuchMethodException 
	 * @throws SecurityException 
	 * @throws InvocationTargetException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 * @throws SQLException */
	public int save(Object object) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SQLException {
		
		List<String> list = new ArrayList();
		StringBuffer str = new StringBuffer();
		String _tableName = "";
		
		Class c = object.getClass();
		
		//解析类获取表名
		FieldAnnotation _file = (FieldAnnotation) c.getAnnotation(FieldAnnotation.class);
	
		
		if(_file!=null){
			
			 _tableName = _file.name();//得到表名,如果没有写,将用和类名一样的表名
			if("_null".equals(_tableName)){
				_tableName = c.getSimpleName();
				LogUtil.info("警告:你这个使用的是同类名相同的表名,请在对应的类注解表名! " );
			}
			
		}else {//没有在类上注解表名
			_tableName = c.getSimpleName();
			LogUtil.info("警告:你这个使用的是同类名相同的表名,请在对应的类注解表名! " );
			
		}
		
		str.append("insert into " + _tableName +"(");
		
		
		
		
		
		//解析属性
		Field[] fs = c.getDeclaredFields();			
		for(int i = 0; i < fs.length; i++){
			Field f = fs[i];
			PrimaryAnnotation pa = f.getAnnotation(PrimaryAnnotation.class);
			
			if(pa!=null){
				LogUtil.debug("检测到主键标识,SQL语句忽略主键,对应的字段为:" + f.getName());
			}else { //非主键,忽略主键字键来创建SQL语句
				str.append(f.getName());
				if(i!=fs.length-1) str.append(",");
				list.add(f.getName());
			}
		
		}
		
		
		str.append(") values(");
	//	System.out.println(str);
		
		//解析方法,调用方法get()
		for(int i = 0; i < list.size(); i++){
			String s = list.get(i);
			String tem = s.substring(0, 1).toUpperCase();
			String tem2 = s.substring(1);
			String tem3 = tem + tem2;
			Method m = c.getMethod("get"+ tem3);		
			//System.out.println(m.toString());
			Object result = m.invoke(object);
			if(result==null){
				str.append("null");
			}else{
				str.append("'" +result.toString() + "'");
			}
			
			if(i!=list.size()-1){
				str.append(",");
				String ss = str.toString();
				//System.out.println(ss);
			}	
		}
		
		str.append(")");
		
		sql = str.toString();
		LogUtil.info("此次调用的SQL语名: " + sql);
		QueryRunner queryR = new QueryRunner();
		int a = queryR.update(conn,sql);
		LogUtil.info("添加数据完成!");		
		return a;
	}
	
	
	
	/**根据对象主键修改数据
	 * @throws NoSuchMethodException 
	 * @throws SecurityException 
	 * @throws InvocationTargetException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 * @throws SQLException */
	public boolean update(Object object) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SQLException{
		List<String> list = new ArrayList();
		StringBuffer str = new StringBuffer();
		String _tableName = "";//表名
		String pk_name = "主键名"; //主键 
		String pk_id = "主键值"; //主键 
		
		Class c = object.getClass();
		//解析类获取表名
		FieldAnnotation _file = (FieldAnnotation) c.getAnnotation(FieldAnnotation.class);
		if(_file!=null){	
			 _tableName = _file.name();//得到表名,如果没有写,将用和类名一样的表名
			if("_null".equals(_tableName)){
				_tableName = c.getSimpleName();
				LogUtil.info("警告:你这个使用的是同类名相同的表名,请在对应的类注解表名! " );
			}
			
		}else {//没有在类上注解表名
			_tableName = c.getSimpleName();
			LogUtil.info("警告:你这个使用的是同类名相同的表名,请在对应的类注解表名! " );
			
		}
		
		//update student set sex='xx',dsf='' where xh=001;
		
		
		str.append("update " + _tableName +" set ");
		//解析属性+方法
		Field[] fs = c.getDeclaredFields();			
		for(int i = 0; i < fs.length; i++){
			Field f = fs[i];
			PrimaryAnnotation pa = f.getAnnotation(PrimaryAnnotation.class);
			
			if(pa!=null){	
				LogUtil.info("检测到主键标识,SQL语句主键,进行主键条件查询");
				pk_name = f.getName();
				
				String tem = pk_name.substring(0, 1).toUpperCase();
				String tem2 = pk_name.substring(1);
				String tem3 = tem + tem2;
				Method m = c.getMethod("get"+ tem3);		
				Object result = m.invoke(object);
				pk_id = result.toString();
				//LogUtil.info("检测到主键标识,SQL语句主键进行条件查询  " + pk_id);
			}else { //非主键,忽略主键字键来创建SQL语句
				String s = f.getName();
				str.append(s);
				str.append("=");
				//调用对应的get方法
				String tem = s.substring(0, 1).toUpperCase();
				String tem2 = s.substring(1);
				String tem3 = tem + tem2;
				Method m = c.getMethod("get"+ tem3);		
				//System.out.println(m.toString());
				Object result = m.invoke(object);
				if(result==null){
					str.append("null");
				}else{
					str.append("'" +result.toString() + "'");
				}
				
				if(i!=fs.length-1) str.append(", ");
			}
		}
		
		str.append(" where " + pk_name + " = " + pk_id );
		
		
		
		sql = str.toString();
		LogUtil.info("此次调用的SQL语名: " + sql);
		QueryRunner queryR = new QueryRunner();
		int a = queryR.update(conn,sql);
		LogUtil.info("添加数据完成!");		
		
		if(a!=0){
			return true;
		}
		return false;
	}
	
	
	
	/**关闭连接*/
	public void close(){
		
		try{
			if (conn != null) {
				conn.close();
			}
		}catch(Exception e){
			LogUtil.info("关闭数据库时出错了...");
			e.printStackTrace();
		}
	}
	

}

 

改进了处理sql语句:

 

public class StringToHql {
	
	private String hql ="";
	private Hashtable map = null;
	
	
	

	//初始化1
	public StringToHql(){
		
	}
	
	//初始化2
	public StringToHql(String hql){
		map = new Hashtable(); //初始化
		this.hql = hql;	
	}
	
	
	/**
	 * 创建hql语句
	 */
	public StringToHql createQuery(String hql){
		map = new Hashtable(); //初始化
		this.hql = hql;	
		return this;
	}
	
	
	/**设置String类型参数*/
	public StringToHql setParameterString(String name,String val){
		map.put(name, val);
		return this;
	}
	/**设置Integer类型参数*/
	public StringToHql setParameterString(String name,Integer val){
		map.put(name, val);
		return this;
	}
	/**设置Double类型参数*/
	public StringToHql setParameterString(String name,Double val){
		map.put(name,val);
		return this;
	}
	
	
	
	/**
	 * 处理hql语句(数据库标准的hql)
	 */
	public String doHql(){
		
		if(hql!=null && !"".equals(hql)){
			String tem = "";
			Enumeration e = map.keys();
			while(e.hasMoreElements()){
				tem = (String)e.nextElement(); //key ~ String
				if(map.get(tem)  instanceof Integer) {
					this.hql = hql.replace(":" + tem, "" +(Integer) map.get(tem)+" ");
				}else if(map.get(tem)  instanceof String){
					this.hql = hql.replace(":" + tem, "'" +(String) map.get(tem)+"'");
				}else if(map.get(tem) instanceof Double){
					this.hql = hql.replace(":" + tem, "" +(Double) map.get(tem)+" ");
				}
				
				
			}
		}else {
			System.out.println("没有sql语句");
		}
		LogUtil.info("这次调用的sql语名[StringToHql]: " + hql);
		return hql;
	}
	
	
	
	
	/**
	 * 测试,使用方法
	 */
	@Test
	public void test1(){
		StringToHql st = new StringToHql();
		st.createQuery("from Manager where anutoh=:anutoh");
		st.setParameterString("anutoh", "小明");
		String hql =  st.doHql();
		
		System.out.println(hql);
		
	}
	
	@Test
	public void test2(){
		String hql =  new StringToHql("from Manager where anutoh=:anutoh and  mone=:mone")
							.setParameterString("anutoh", "小明2")
							.setParameterString("mone", 4500.5)
							.doHql();
		
		
		
		System.out.println(hql);
		
	}
	
	
}

 

 

 

 

 

 

 

注解:

 

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
 * @Title: PrimaryAnnotation.java
 * @Package com.cai.order.db
 * @Description: TODO(用于标识主键的注解)
 * @author awfhome@163.com
 * @date 2010-3-6 下午02:26:13
 * @version V1.x
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PrimaryAnnotation {
	
}

 

 

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * @Title: FieldAnnotation.java
 * @Package com.cai.order.db
 * @Description: TODO(用于注解数据库表名,字段;对应类的类,属性)
 * @author  awfhome@163.com
 * @date 2010-3-6 下午03:13:51
 * @version V1.x
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldAnnotation {
	String name() default "_null";

}

 

 

model:

 

/**角色*/
@FieldAnnotation(name = "role")
public class Role {
	@PrimaryAnnotation
	private int roleId;
	private String roleName;
	private String description;
	public int getRoleId() {
		return roleId;
	}
	public void setRoleId(int roleId) {
		this.roleId = roleId;
	}
	public String getRoleName() {
		return roleName;
	}
	public void setRoleName(String roleName) {
		this.roleName = roleName;
	}
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}
	
	

}

 

 

 

还有很多没有实现的功能,例如只能标识一个主键和表名,但是数据库字段现在只能和model属性一样,不过实现过程和主键差不多,后来看到了设计模式之模板模式和策略模式如果能结合他们,那就程序就更灵活了,spring的hibernate模板就是用这两种模式来设计的.

 

   发表时间:2010-05-25  
有那么多开源的成熟的架构,除非非常有必要,否则还是拿来主义比较好。

只是研究的话也不错~

顺便感概下,java的反射效率真的不咋地~
0 请登录后投票
   发表时间:2010-05-25  
虽然反射效率不好,但目前我用过的几个开源ORM框架底层都是用反射来实现的.
0 请登录后投票
   发表时间:2010-05-25  
哈哈,不怎么的
0 请登录后投票
   发表时间:2010-05-25  
性能的瓶颈不在反射上
0 请登录后投票
   发表时间:2010-05-25  
J-catTeam 写道
性能的瓶颈不在反射上

仁兄说的对.

对于Java的持久层访问来说,真正瓶颈在于数据库连接的开销,反射虽然不快却并不是短板所在.
1 请登录后投票
   发表时间:2010-05-25  
不知道为什么,相当反感注解这种东西
0 请登录后投票
   发表时间:2010-05-26  
反射还可以,不是很占时间
0 请登录后投票
   发表时间:2010-05-26  
另外反射其实也可以把method缓存起来吧
0 请登录后投票
   发表时间:2010-05-26  
aws 写道
另外反射其实也可以把method缓存起来吧


这没用,每个实体对象的属性值还得通过反射来得到。

我这个http://www.iteye.com/topic/675119也用反射实现了一个ActiveRecord,不需要注解,纯约定。

里边有一个地方可以缓存的是数据库结构。只需取一次。
0 请登录后投票
论坛首页 Java企业应用版

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