问题
表名与实体类名称不一致,
表中字段与实体类中的属性名称不一致,
主键不叫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);
}
}
}