加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

使用注解反射实现BaseDao(代替XML配置)

发布时间:2020-12-16 08:29:54 所属栏目:百科 来源:网络整理
导读:问题 表名与实体类名称不一致, 表中字段与实体类中的属性名称不一致, 主键不叫id时, 上面的BaseDao不能用! 解决方案1:通过配置文件(XML) 解决 XML:便于维护!但需要写读取代码! 解决方案2:通过注解的方式 优点: 无需XML配置,需要的信息在java源代

问题

表名与实体类名称不一致,

表中字段与实体类中的属性名称不一致,

主键不叫id时,

上面的BaseDao不能用!

解决方案1:通过配置文件(XML) 解决

XML:便于维护!但需要写读取代码!

解决方案2:通过注解的方式

优点:

无需XML配置,需要的信息在java源代码级别

缺点:

不便于维护:例如修改字段名,要重新编译。

需要自己来处理表和实体类之间的映射关系。
package dao;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.dbutils.ResultSetHandler;

import anno.Column;
import anno.PrimaryKey;
import anno.Table;

import util.*;

/**
 * 所有dao的公用的方法,都在这里实现
 * 使用注解来保存信息
 * 
 */
public class BaseDao<T> {

	// 保存当前运行类的参数化类型中的实际的类型
	private Class<T> clazz;
	// 表名 这里就不需要约定表名就是实体类名了 -- 在C#的BaseDAL中,是通过Lamda表达式传入表名的
	private String tableName;
	
	private String primaryKey;

	// 构造函数: 1. 获取当前运行类的参数化类型;
	// 2. 获取参数化类型中实际类型的定义(class)
	public BaseDao() {
		// this 表示当前运行类 (AccountDao/AdminDao)
		// this.getClass() 当前运行类的字节码对象(AccountDao.class/AdminDao.class)
		
		// this.getClass().getGenericSuperclass(); 当前运行类的父类,即为BaseDao<Account>
		// 其实就是“参数化类型”, ParameterizedType
		
		//返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type
		Type type = this.getClass().getGenericSuperclass();
		// 强制转换为“参数化类型” 【BaseDao<Account>】
		ParameterizedType pt = (ParameterizedType) type;
		
		// Class类是Type接口的实现类
		// 获取参数化类型中,实际类型的定义 【new Type[]{Account.class}】
		Type types[] = pt.getActualTypeArguments();
		// 获取数据的第一个元素:Accout.class
		clazz = (Class<T>) types[0];
		
		//现在 拿到了Account.class
		
		//获取表名
		Table table = clazz.getAnnotation(Table.class);
		tableName = table.tableName();
		
		//获取主键字段
		Field[] declaredFields = clazz.getDeclaredFields();
		for (Field field : declaredFields) {
			field.setAccessible(true);
			
			PrimaryKey pKey = field.getAnnotation(PrimaryKey.class);
			if(pKey!=null){
				Column column = field.getAnnotation(Column.class);
				primaryKey = column.columnName();
				break;
			}
		}
	}

	/**
	 * 主键查询
	 * 
	 * @param id
	 *            主键值
	 * @return 返回封装后的对象
	 */
	@SuppressWarnings({"unchecked"})
	public T findById(int id) {
		/*
		 * 1. 知道封装的对象的类型 2. 表名【表名与对象名称一样, 且主键都为id】
		 * 
		 * 即,得到当前运行类继承的父类 BaseDao<Account> ----》 得到Account.class
		 */

		String sql = "select * from " + tableName + " where id=? ";
		try {
			//这个时候由于数据库表和实体不是一一对应的,
			//import org.apache.commons.dbutils.handlers.BeanHandler;就不能使用了
			//需要自己处理映射关系
			return JdbcUtils.getQuerrRunner().query(sql,new MyBeanHandler<T>(clazz),id);
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
}

/**
 * DbUtils组件的ResultSetHandler接口作用
 * convert resultSets into Objects 表到对象的映射
 * @author bellychang
 *
 * @param <T>
 */
class MyBeanHandler<T> implements ResultSetHandler<T>{
	
	private Class<T> clazz;
	
	public MyBeanHandler(Class<T> clazz){
		this.clazz = clazz;
	}
	
	public T handle(ResultSet rSet) throws SQLException {
		try {
			T t = clazz.newInstance();
			if (rSet.next()) {
				Field[] fields = clazz.getDeclaredFields();
				//遍历 需要获得属性名 属性值 和 对象
				for (Field field : fields) {
					//获得属性名
					String fieldName = field.getName();
					
					//获得属性值 需要先获得字段值
					//获得Field上的注解
					Column column = field.getAnnotation(Column.class);
					//获得字段名
					String columnName = column.columnName();
					//获得字段值 即属性值
					Object columnValue = rSet.getObject(columnName);
					
					//对象属性的拷贝 使用BeanUtil组件的copyProperty()方法
					//Copy the specified property value 
					//to the specified destination bean,//performing any type conversion that is required.
					BeanUtils.copyProperty(t,fieldName,columnValue);
				}
			}
			return t;
		} catch (Exception e) {
			throw new RuntimeException(e);
		} 
		
	}
	
	
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读