锁定老帖子 主题:用反射注解实现的ORM(一看就明白的那种)
精华帖 (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模板就是用这两种模式来设计的.
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-05-25
有那么多开源的成熟的架构,除非非常有必要,否则还是拿来主义比较好。
只是研究的话也不错~ 顺便感概下,java的反射效率真的不咋地~ |
|
返回顶楼 | |
发表时间:2010-05-25
虽然反射效率不好,但目前我用过的几个开源ORM框架底层都是用反射来实现的.
|
|
返回顶楼 | |
发表时间:2010-05-25
哈哈,不怎么的
|
|
返回顶楼 | |
发表时间:2010-05-25
性能的瓶颈不在反射上
|
|
返回顶楼 | |
发表时间:2010-05-25
J-catTeam 写道 性能的瓶颈不在反射上
仁兄说的对. 对于Java的持久层访问来说,真正瓶颈在于数据库连接的开销,反射虽然不快却并不是短板所在. |
|
返回顶楼 | |
发表时间:2010-05-25
不知道为什么,相当反感注解这种东西
|
|
返回顶楼 | |
发表时间:2010-05-26
反射还可以,不是很占时间
|
|
返回顶楼 | |
发表时间:2010-05-26
另外反射其实也可以把method缓存起来吧
|
|
返回顶楼 | |
发表时间:2010-05-26
aws 写道 另外反射其实也可以把method缓存起来吧
这没用,每个实体对象的属性值还得通过反射来得到。 我这个http://www.iteye.com/topic/675119也用反射实现了一个ActiveRecord,不需要注解,纯约定。 里边有一个地方可以缓存的是数据库结构。只需取一次。 |
|
返回顶楼 | |