dbutils和反射实现ORM问题整理
来源:互联网 发布:怎样找淘宝客推广产品 编辑:程序博客网 时间:2024/06/06 04:26
背景
时间过的真快,用dbutils自定义ORM竟然已经是7月中旬的事情了,本周才真正用到那些代码,当初简单实现的MyBeanHandler类还有一些缺陷,整理修正流程如下。
实现流程
基本思路是这样的:需要将数据库的一条查询结果,转换成Java实体类的实例,自定义一个ResultHandler,接受一个类型Class。
由于select列可能不是Java全部的属性,所以不能遍历Java属性列表,逐个从ResultSet获取该列的值,一旦某一列没有被查出来,ResultSet的getObject(entityField)的时候直接报异常了。所以转换思路,以查询结果列,反着找到该列对应的Java类的属性,再用反射调用属性的setter方法,完成实例的组装。
此外,需要注意Java实体类属性的类型和数据库表列的类型,如果存在不一致的话,需要特殊处理。例如:Java中boolean类型的属性,对应到数据库中是tinyint。还有一个特别重要的问题就是ResultSet中获取的元数据信息中,所有的数值列都是Java的封装类型,即:tiny/int/bigint对应的Class是Integer.class的。
Java的封装类型和基本类型的Class是不同的,int/long/byte/char/float的Class是对应的类型名称.class的,如int.class,long.class。所以调用setter方法触发反射的类型一定要传递Java实体属性的类型。
反射获取类属性
开发过程中有些模块的实体有公共属性,所以有不少实体类是有基础父类的,这些实体类的表字段,除了有自己定义的属性外,还必须包括父类的属性。
反射获取实体字段的方法为:
/** * 判断某个属性是否是简单属性:所有的简单类型和对应的包装类型都是 * * @param fieldType * @return */ public static boolean isSimpleTypeField(Class<?> fieldType) { if (fieldType == Integer.class ||fieldType == int.class || fieldType == long.class ||fieldType == Long.class || fieldType == String.class || fieldType == Short.class || fieldType == Character.class) { return true; } return false; } /** * 判断某个属性是否是boolean类型,有两种包装类型和原始boolean类型 * * @param fieldType * @return */ public static boolean isBooleanTypeField(Class<?> fieldType) { if (fieldType == Boolean.class ||fieldType==boolean.class) { return true; } return false; }/** * 获取某一个类的fieleName对应的Field对象,如果当前类没有,从父类获取 * @comment 异常,不处理 * @param entityClass * @param fieldName * @return */ public static Field getFieldOfEntity(Class<?> entityClass,String fieldName){ if(fieldName==null){ return null; } try { return entityClass.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { try { return entityClass.getSuperclass().getDeclaredField(fieldName); } catch (NoSuchFieldException e1) { } catch (SecurityException e1) { } } catch (SecurityException e) { } return null; }
此外,数据库表列名称和Java属性setter直接的映射关系,可以自定义的,通用的方法是二者名称一致,对应的就是setXXX,getXXX,isXXX(boolean类型)。如果数据库列表是下划线,那么就是另一种映射关系,这些都可以自定义映射规则的。
那么获取列的getter/setter方法为:
/** * 获取某个属性的get方法名称 * * @param name * @return */ public static String obtainGetterName(String name,boolean isBooleanType) { //特殊处理boolean类型,统一为isXXX if(isBooleanType){ return "is" + name.substring(0, 1).toUpperCase()+name.substring(1); } return "get" + name.substring(0, 1).toUpperCase()+name.substring(1); } /** * 获取某个属性的set方法名称 * * @param name * @return */ public static String obtainSetterName(String name) { return "set" + name.substring(0, 1).toUpperCase()+name.substring(1); }
自定义BeanHandler
按上述思路修正原来实现的BeanHandler如下:
public class MyBeanHandler<T> implements ResultSetHandler<T> { private Logger logger = Logger.getLogger(MyBeanHandler.class); private Class<T> entityClass; public MyBeanHandler(Class<T> entityClass) { this.entityClass = entityClass; } @Override public T handle(ResultSet resultSet) { try { while (resultSet.next()) { T result = null; result = entityClass.newInstance(); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); for(int col = 1 ; col <= columnCount ; col++){ String name = metaData.getColumnName(col); Object oldValue = resultSet.getObject(col); Object columnValue = oldValue; if(columnValue==null){//空,直接过滤掉。。。。 continue; } //获取该字段名称对应的Java实体类字段声明:updated on 2017-09-05 Field field = IntrospectUtil.getFieldOfEntity(entityClass,name); if(field==null){ continue; } Class<?> columnType = field.getType(); if (ArrayList.class == columnType || List.class == columnType) { // 数据库存储的是JSON,回转成List对象 List<Object> list = JSONObject.parseArray(columnValue.toString(), Object.class); columnValue = list; } else if (IntrospectUtil.isSimpleTypeField(columnType)) { // non op 普通属性,直接设置值 } else if(IntrospectUtil.isBooleanTypeField(columnType)){ columnValue = (Integer)oldValue==1 ? Boolean.TRUE:Boolean.FALSE; }else { // 复杂对象,且非List,通过JSON转换为对应类型的对象 columnValue = JSONObject.parseObject(columnValue.toString(), columnType); } //找到对应简单字段的原始字段类型,作为setter的方法类型 String setter = IntrospectUtil.obtainSetterName(name); Method method = entityClass.getMethod(setter, columnType); method.invoke(result, columnValue); } return result; } } catch (Exception e) { logger.error("MyBeanHandler error", e); } return null; }}
启示录
反射应用时,Class类的getDeclaredField能获取当前类的所有属性,静态、非静态的都能获取到。如果插入操作需要根据实体属性集合类定义insert后面的字段,需要排除掉静态成员变量。
此外,父类的成员变量即使定义成protected的,也不能直接被子类用getDeclaredField方法获取到;但是getMethod方法不仅能获取到自己的方法,同时也能获取父类继承来的方法,所以Method不存在需要特别注意的地方。
代码不用就不知道有坑啊,demo编写过程中还是太简单,所以发现不了问题。编程路漫漫呐!
而且生活时不时还给你一点小打击,毕小宝不到三岁半,刚上幼儿园一个月,昨天惊闻幼儿园缴费上的课程跟其他小朋友单独报的课程不一样,突然就感受到了深深的伤害,一整天心情都很沉重。我该怎么跟这个社会对抗,才能全身而退呢?我的孩子又该怎么面对将来的社会环境呢。又是一个大命题!
- dbutils和反射实现ORM问题整理
- 反射实现Android Sqlite Orm
- 基于Java反射实现简易ORM
- C#根据反射实现ORM映射
- ORM底层封装( JDBC \ DBUtils)
- 自己写ORM框架 DBUtils
- 反射模拟DbUtils实现ResultSet转成Bean实例
- DButils实现clob和blob存储
- JAVA反射技术实现简单的JDBCUtil(ORM)
- 使用表达式树+反射实现简易ORM对象映射操作
- C#基础系列:实现自己的ORM(反射以及Attribute在ORM中的应用)
- 实现自己的ORM(反射以及Attribute在ORM中的应用)
- Java轻量级ORM工具--DbUtils介绍
- Java轻量级ORM工具--DbUtils使用
- 实施 ORM 的两项要旨:泛型和反射
- 反射(五)ORM
- Laravel Eloquent ORM--整理
- Laravel Eloquent ORM--整理
- golang匿名函数(闭包)
- 1212: [HNOI2004]L语言
- 2243: [SDOI2011]染色
- 今日头条|张一鸣:我遇到的优秀年轻人的5个特质
- js綁定快捷鍵
- dbutils和反射实现ORM问题整理
- C++文件流
- ios-swift基础
- 2764: [JLOI2011]基因补全
- composer方式安装thinkphp5
- 怎么将数字数组转为字符数组 (python)
- 嵌入式linux平台设备驱动(设备驱动模型)开发之linux内核中的设备
- 数据库一:深入理解数据库磁盘存储(Disk Storage)
- 【BZOJ1087】【SCOI2005】互不侵犯