使用注解优化通用Dao
待解决问题
1. 当数据库表名与类名不一致
2. 字段与属性不一样
3. 主键不叫id
使用注解
好处:简化XML配置,程序处理方便。
缺点:不便于维护,例如修改字段名,就要修改注解,需要重新编译。服务器发布成本大。
使用XML
好处:便于维护
缺点:读取麻烦
优化
使用的工具
BeanUtils组件( 引入BeanUtils包commons-beanutils-1.8.3.jar点击打开链接 引入日志支持包commons-logging-1.1.3.jar点击打开链接) DbUtils组件( 引入jar文件 : commons-dbutils-1.6.jar点击打开链接)数据库表如下
实体类
package com.cn.generic_reflect;
/**
* 实体类:对应数据库中的accout表
* 表名 account
* 字段 id(主键) accountName money
*
* @author liuzhiyong
*
*/
@Table(tableName = "account")
public class Entity_Account {
@Id
@Column(columnName="id")
private int aId;
@Column(columnName="accountName")
private String aName;
@Column(columnName="money")
private double aMoney;
public int getaId() {
return aId;
}
public void setaId(int aId) {
this.aId = aId;
}
public String getaName() {
return aName;
}
public void setaName(String aName) {
this.aName = aName;
}
public double getaMoney() {
return aMoney;
}
public void setaMoney(double aMoney) {
this.aMoney = aMoney;
}
@Override
public String toString() {
return "Entity_Account [aId=" + aId + ", aName=" + aName + ", aMoney="
+ aMoney + "]";
}
}
描述表名的注解Table
package com.cn.generic_reflect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.persistence.EnumType;
/**
* 注解:描述表的名称
* @author liuzhiyong
*
*/
@Target(ElementType.TYPE)//指定注解的作用域,只能作用在类上
@Retention(RetentionPolicy.RUNTIME)//指定注解在运行时有效,反射时必须这么做
public @interface Table {
/**
* 表名
* @return
*/
public abstract String tableName();
}
描述表字段的注解
package com.cn.generic_reflect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 注解:描述表的字段
* @author liuzhiyong
*
*/
@Target(ElementType.FIELD)//指定注解的作用域,只能作用在属性Field上
@Retention(RetentionPolicy.RUNTIME)//指定注解运行时有效
public @interface Column {
public abstract String columnName();
}
描述主键的注解
package com.cn.generic_reflect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.persistence.EnumType;
/**
* 注解:描述表的主键id
* @author liuzhiyong
*
*/
@Target(ElementType.FIELD)//指定注解的作用域,只能作用在属性Field上
@Retention(RetentionPolicy.RUNTIME)//指定注解运行时有效
public @interface Id {
}
通用BaseDao
package com.cn.generic_reflect;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import com.cn.Util.JdbcUtils;
/**
* 通用Dao设计
*
* 解决优化的问题:
* 1. 当数据库表名与类名不一致
* 2. 字段与属性不一样
* 3. 主键不叫id
* @author liuzhiyong
*
* @param <T>
*/
public class BaseDao<T> {
//保存当前运行类要操作实体对象的字节码对象
private Class<T> clazz = null;
//保存当前操作实体类对象对应的表的表名
private String tableName;
//保存当前操作实体类对象对应的表的主键id
private String idName;
public BaseDao() {
//获取当前运行类的字节码对象(例如AccountDao.class)
Class currentClazz = this.getClass();
//获取此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type。 (例如:BaseDao<Entity_Account>)
ParameterizedType pt = (ParameterizedType)currentClazz.getGenericSuperclass();//参数化的类型
Type[] types = pt.getActualTypeArguments();//获取参数化类型的参数类型数组
clazz = (Class<T>) types[0];//获取参数化类型的实参类型(例如:Entity_Account.class)
/**
* 1. 获取表名
*/
Table table = clazz.getAnnotation(Table.class);//获取类上的Table注解
tableName = table.tableName();
/**
* 2. 获取表的主键id
*/
//获取所有的属性Filed
Field[] fs = clazz.getDeclaredFields();
//遍历Filed数组,获取每一个属性Filed上的Id注解,判断是否存在Id的注解
for(Field f : fs){
Id id = f.getAnnotation(Id.class);//获取Id注解
if(id != null){//如果注解Id不为空,说明该属性Field为主键id属性
Column column = f.getAnnotation(Column.class);//获取该属性Field上的Column注解
idName = column.columnName();//获取该注解中的columnName属性值,即为表的主键id字段名
//跳出循环
break;
}
}
}
/**
* 根据主键id查询
* @param id
* @return
*/
public T findById(int id){
try{
String sql = "select * from " + tableName + " where " + idName + " = ?";
return JdbcUtils.getQueryRunner().query(sql, new MyBeanHandler<T>(clazz), id);
}catch(Exception e){
throw new RuntimeException(e);
}
}
/**
* 查询全部
* @return
*/
public List<T> getAll(){
try{
String sql = "select * from " + tableName;
return JdbcUtils.getQueryRunner().query(sql, new MyBeanListHandler<T>(clazz));
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
自定义结果集MyBeanHandler
package com.cn.generic_reflect;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.dbutils.ResultSetHandler;
public class MyBeanHandler<T> implements ResultSetHandler<T> {
//保存需要封装成的对象的Class字节码
private Class<T> clazz;
public MyBeanHandler(Class<T> clazz1) {
this.clazz = clazz1;
}
@Override
public T handle(ResultSet rs) throws SQLException {
try {
//创建要封装的对象
T t = (T) clazz.newInstance();
//向下读取一行
if(rs.next()){
//获取所有的Field属性数组
Field[] fs = clazz.getDeclaredFields();
//遍历Field属性数组,得到每一个字段类型Field
for(Field f : fs){
//得到属性名,即对象属性名
String fieldName = f.getName();
//得到该Field属性的Column注解
Column column = f.getAnnotation(Column.class);
//获取Column注解的columnName属性值,即字段名
String columnName = column.columnName();
//获取该字段对应的值
Object objValue = rs.getObject(columnName);
//封装属性值到对象属性中
BeanUtils.copyProperty(t, fieldName, objValue);
}
}
return t;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
自定义结果集MyBeanListHandler
package com.cn.generic_reflect;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.dbutils.ResultSetHandler;
/**
* 自定义结果集封装数据
* @author liuzhiyong
*
* @param <T>
*/
public class MyBeanListHandler<T> implements ResultSetHandler<List<T>> {
//保存需要封装对象的字节码
private Class<T> clazz;
public MyBeanListHandler(Class<T> clazz1) {
this.clazz = clazz1;
}
@Override
public List<T> handle(ResultSet rs) throws SQLException {
//创建集合,封装结果集
List<T> list = new ArrayList<T>();
try{
//遍历结果集
while(rs.next()){
//获取所有的Field属性数组
Field[] fs = clazz.getDeclaredFields();
//创建要封装的对象
T t = clazz.newInstance();
//遍历Field属性数组,得到每一个字段类型Field
for(Field f : fs){
//得到属性名,即对象属性名
String fieldName = f.getName();
//得到该Field属性的Column注解
Column column = f.getAnnotation(Column.class);
//获取Column注解的columnName属性值,即字段名
String columnName = column.columnName();
//获取该字段对应的值
Object objValue = rs.getObject(columnName);
//封装属性值到对象属性中
BeanUtils.copyProperty(t, fieldName, objValue);
}
list.add(t);
}
return list;
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
AccountDao调用通用BaseDao
package com.cn.generic_reflect;
/**
* dao实现,继承BaseDao
* @author liuzhiyong
*
*/
public class AccountDao extends BaseDao<Entity_Account>{
}
测试
package com.cn.generic_reflect;
import java.util.List;
public class Test {
@org.junit.Test
public void testDao() throws Exception {
//调用dao
AccountDao accountDao = new AccountDao();
Entity_Account account = accountDao.findById(2);
System.out.println(account.toString());
List<Entity_Account> accountList = accountDao.getAll();
System.out.println(accountList);
}
}