java入门笔记一:浅谈反射(reflect)&泛型(genericity)在通用数据访问库中的应用...

        As we know,在eclipse中,我们可以通过连接数据库,对其中的数据进行一系列操作。大概步骤为:加载数据库的jdbc驱动--->数据库与eclipse建立连接--->建立一个连接声明--->写出要操作数据库的SQL语言字符串--->通过声明对SQL语言进行操作。可是对于程序来说,每天都可能有数以万计的数据要查询,只要程序中一需要查数据,就得讲上面的这些步骤照搬么?NO!这并不符合程序员深入简出的尿性!How could we do?创建通用库!

        如果每次访问数据都要将上述代码复制粘贴的话,代码重复率就太高了,而且也增加了代码的行数,十分不可取,而通用库则可以完美地解决这个方法,其实这里的通用库指的是通用的一个类。看到这可能会有人想到:每个数据库的表都各不相同,怎么可能用一个通用库就可以解决问题?很好,这个时候泛型就可以派上用场了。泛型是程序设计语言的一种特性,允许程序员在强类型程序设计语言中编写代码时定义一些可变部分(摘自百度百科),而java中的泛型可以并且只能代表类,如果我们把每个表看作一个类,就可以利用泛型将方法变成通用。

public class BaseDao<T>{//T代表泛型

}

        当引用这个方法的时候,T已经有一个具体的类,接下来就是根据传入的T类型来获取该类中的信息,如何获取?利用反射(reflect)可以很容易做到!

    一、获取类:

ParameterizedType c=(ParameterizedType) this.getClass().getGenericSuperclass();
cas=(Class<T>) c.getActualTypeArguments()[0];//cas被赋予T的类

    二、创建对象:

Object o=cas.new Instance();

    三、获取属性:

Object o=cas.new Instance();//创建类的对象
Field[] fields=cas.getDeclaredFields();//得到所有成员
fields[i].getName();//得到成员名字
fields[i].getModifiers();//得到修饰符
fields[i].getType().getSimpleName();//得到属性类型名字
fields[i].set(o, value);//将对象o中的fields[i]成员设置值为value
fields[i].get(o);//取出对象o中的fields[i]值

值得一提的是,当使用cas的getFields()方法时得到的只是类中的公有成员,而getDeclaredFields()得到的是包括公有、私有和子类共有成员。另外因为反射涉及到成员值的更改,所以对于私有成员,须在更改前将该成员的setAccessible()方法设置为true,因此可以通过反射直接访问私有成员。

    四、获取方法:

Method[] method=cas.getDeclaredMethods();//获取所有方法
method[i].getParameterTypes();//获取方法的传入参数类型
method[i].getReturnType();//获取方法的返回值类型
method[i].getDeclaredConstructors();//获取所有的构造方法
cas.getSuperClass();//获取父类
cas.getInerFace();//获取实现的接口

        好了,说了这么多,终于该进入正题了,接下来我们看一下如何实现通用数据访问库。实现步骤如下(本次操作以mysql为数据库)(如何将mysql与eclipse连接这里就不过多详述,此位大神已给出最详细的解答:http://www.cnblogs.com/fnng/archive/2011/07/18/2110023.html):

       首先,先创建一个带有泛型的类(文章开头以提过),利用上述的反射方法,写数据库的基本操作方法:

//待改善:让系统自动找出主键名字
public class BaseDao<T> {//利用泛型打造通用数据库访问方法:增、删、查、改。
	private Class<T> cas;
	private String tableName;
	private Field[] fields,f;
	private Field pkField;
	private Connection con;
	@SuppressWarnings("unchecked")
	public BaseDao() {
		ParameterizedType c=(ParameterizedType) this.getClass().getGenericSuperclass();
		cas=(Class<T>) c.getActualTypeArguments()[0];//cas被赋予T的类
		tableName=cas.getSimpleName();
		fields=cas.getDeclaredFields();
		f=new Field[fields.length-1];
		con=DBUtil.getConnection();
	}
	
	public void insert(T o){
		StringBuffer insert=new StringBuffer("insert into ");//append连接比传统+号连接效率高
		insert.append(tableName).append(" values(");
		for(int i=0;i<fields.length;i++){
			insert.append("?");
			if(i==fields.length-1){
				insert.append(")");
			}
			else insert.append(",");
		}
		try {
			PreparedStatement pstm=con.prepareStatement(insert.toString());
			for(int i=0;i<fields.length;i++){
				fields[i].setAccessible(true);//将访问权限设置为true,即使是private成员也可以访问
				Object v=fields[i].get(o);//得到o对象中fields[i]的值
				pstm.setObject(i+1, v);//如此设计需注意一个问题,即表类中的成员定义顺序必须与数据库表中列的顺序相同
			}
			pstm.executeUpdate();
			System.out.println("insert success!");
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}
	
	public void delete(String pkName,Object pkValue){//传入参数:主键名字和主键值
		try {
			StringBuffer delete=new StringBuffer("delete from ");
			delete.append(tableName).append(" where ").append(pkName).append("=?");
			PreparedStatement pstm=con.prepareStatement(delete.toString());
			pstm.setObject(1, pkValue);
			pstm.executeUpdate();
			System.out.println("delete success!");
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	public void update(T o,String pkName){
		int a=0;
		for(int i=0;i<fields.length;i++){//将非主键的属性存入另外一个Field[]中
			if(fields[i].getName()!=pkName){
				f[a]=fields[i];
				a++;
			}
			else pkField=fields[i];//将主键存入pkField中
		}
		StringBuffer update=new StringBuffer("update ");
		update.append(tableName).append(" set ");
		for(int i=0;i<f.length;i++){//利用for循环将where之前的字符写好
			update.append(f[i].getName()).append("=?");
			if(i==f.length-1) update.append(" where ");
			else update.append(",");
		}
		update.append(pkName).append("=?");//增加where之后的字符
		try {
			PreparedStatement pstm=con.prepareStatement(update.toString());
			for(int i=0;i<fields.length;i++){
				if(i==fields.length-1){//主键部分
					pkField.setAccessible(true);
					Object v=pkField.get(o);
					pstm.setObject(i+1, v);
					break;
				}
				//非主键部分
				f[i].setAccessible(true);
				Object v=f[i].get(o);
				pstm.setObject(i+1, v);
			}
			pstm.executeUpdate();
			System.out.println("update success!");
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<T> findAll(){
		List<T> list=new ArrayList<>();
		StringBuffer select=new StringBuffer("select * from ");
		select.append(tableName);
		try {
			Statement sta=con.createStatement();
			ResultSet rs=sta.executeQuery(select.toString());
			list=resultToList(rs);
		} catch (SQLException e) {
			e.printStackTrace();
		} 
		System.out.println("select success!");
		return list;
	}
	
	@SuppressWarnings("unchecked")
	public T findOne(String pkName,Object pkValue){
		int a=0;
		for(int i=0;i<fields.length;i++){//将非主键的属性存入另外一个Field[]中
			if(fields[i].getName()!=pkName){
				f[a]=fields[i];
				a++;
			}
			else pkField=fields[i];//将主键存入pkField中
		}
		try {
			StringBuffer select=new StringBuffer("select * from ");
			select.append(tableName).append(" where ").append(pkName).append("=?");
			PreparedStatement pstm= con.prepareStatement(select.toString());
			pstm.setObject(1, pkValue);
			ResultSet rs=pstm.executeQuery();
			Object o=cas.newInstance();//利用cas类创建一个对象
			while(rs.next()){			
				for(int i=0;i<fields.length;i++){
					fields[i].setAccessible(true);
					Object v=rs.getObject(i+1);
					if(v instanceof BigDecimal){//数据库number类型转换为java中基本类型
						BigDecimal bd=(BigDecimal) v;
						switch(fields[i].getType().getSimpleName()){
						case "int":
						case "Integer":
							v=bd.intValue();//类型转换方法(number-->int)
							break;
						case "float":
						case "Float":
							v=bd.floatValue();//类型转换方法(number-->float)
							break;
						}
					}
					fields[i].set(o, v);//设置o对象中fields[i]属性的值
				}
			}
			return (T)o;
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;

	}
	
	public List<T> resultToList(ResultSet rs){
		List<T> list=new ArrayList<>();
		try {
			while(rs.next()){
				Object o=cas.newInstance();//利用cas类创建一个对象
				for(int i=0;i<fields.length;i++){
					fields[i].setAccessible(true);
					Object v=rs.getObject(i+1);
					if(v instanceof BigDecimal){//数据库number类型转换为java中基本类型
						BigDecimal bd=(BigDecimal) v;
						switch(fields[i].getType().getSimpleName()){
						case "int":
						case "Integer":
							v=bd.intValue();//类型转换方法(number-->int)
							break;
						case "float":
						case "Float":
							v=bd.floatValue();//类型转换方法(number-->float)
							break;
						}
					}
					if(v instanceof Timestamp){
						Timestamp t=(Timestamp) v;//当值为时间戳类型的时候,直接将其转换为String类型
						v=t.toString();
					}
					fields[i].set(o, v);//设置o对象中fields[i]属性的值
				}
				list.add((T)o);
			}
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return list;
	}
	
	public String[] getFieldNames(){
		String[] names=new String[fields.length];
		for(int i=0;i<names.length;i++){
			names[i]=fields[i].getName();
		}
		return names;
	}

	protected List<T> findBySql(String sql){
		List<T> list = null;
		try {
			Statement sta=con.createStatement();
			ResultSet rs=sta.executeQuery(sql);
			list=resultToList(rs);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return list;
	}
}

        其次,建立一个针对某个表的数据访问类作为上述类的子类,代码如下:

public class CityDao extends BaseDao<City>{

}

        继承主要是为了能直接访问BaseDao中的方法,当然,如果有专门针对City这个表的方法,可以直接写在CityDao中,方便且独立。

        然后,建立一个与City表对应的类,将表中的所有列作为成员,并创建getter&setter方法,代码如下:

public class City {
	private Integer id;
	private String name;
	private String countryCode;
	private String district;
	private Integer population;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getCountryCode() {
		return countryCode;
	}
	public void setCountryCode(String countryCode) {
		this.countryCode = countryCode;
	}
	public String getDistrict() {
		return district;
	}
	public void setDistrict(String district) {
		this.district = district;
	}
	public Integer getPopulation() {
		return population;
	}
	public void setPopulation(Integer population) {
		this.population = population;
	}
}

        最后一步,建一个Test类:

public class Test1 {
	public static void main(String[] args) {
		CityDao cd=new CityDao();
		List<City> list=cd.findAll();
		for(City c:list)
			System.out.println(c.getId()+"  "+c.getCountryCode()+"  "+c.getDistrict()
			+"  "+c.getName()+"  "+c.getPopulation());
	}
}

        Last but not least,说明几个注意事项:

        1.目前确定主键的方法是在调用方法的时候直接输入其名字,有点蠢,之后若有更好的方法,会及时更新上来,如果看此篇文章的你有好方法,希望能告诉我?。

        2.为了方便日后调取数据或者进行更复杂的方法设计,表格类中的列表成员最好按照表中的顺序来定义

        3、第一次发文,请多指教?

转载于:https://my.oschina.net/marina1216/blog/735834

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值