把 jdbc返回的数据 装进对象里 (分析DBUtils源码)

这是原生的jdbc,只能一个一个取属性.

现在我有这样一个实体类我想要把我的jdbc返回值,直接包装进类里面

package cn.dbutils.analyze;

import java.util.Date;

public class Student {
	private Integer id;
	private String name;
	private Date time;
	
	
	public Student() {
		super();
	}
	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 Date getTime() {
		return time;
	}
	public void setTime(Date time) {
		this.time = time;
	}
	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", time=" + time + "]";
	}
	
	
}

这是查询的工具类(大部分源码都是参考的DBUtils)自己写的可能会有BUG有待大量测试

package cn.dbutils.analyze.piratemodel;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class QueryRunner {
	
	private Connection con;
	
	//这个属性存在的意义
	//因为数据库查出来的值很有可能那一列是空的那么返回的值就是null
	//我们的实体类有可能是int型 ,float型等等基本类型,是接收不了null,应该给他默认值才对
	private static final Map<Class<?>, Object> primitiveDefaults = new HashMap<Class<?>, Object>();

    static {
        primitiveDefaults.put(Integer.TYPE, Integer.valueOf(0));
        primitiveDefaults.put(Short.TYPE, Short.valueOf((short) 0));
        primitiveDefaults.put(Byte.TYPE, Byte.valueOf((byte) 0));
        primitiveDefaults.put(Float.TYPE, Float.valueOf(0f));
        primitiveDefaults.put(Double.TYPE, Double.valueOf(0d));
        primitiveDefaults.put(Long.TYPE, Long.valueOf(0L));
        primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
        primitiveDefaults.put(Character.TYPE, Character.valueOf((char) 0));
    }
	//把数据源放进来
	public QueryRunner(Connection con) {
		super();
		this.con = con;
	}
	
	public <T> T query(String sql,Class<T> type) throws SQLException, InstantiationException, IllegalAccessException, IntrospectionException{
		Statement stmt = con.createStatement();
		ResultSet rs = stmt.executeQuery(sql);
		if (rs.next()) {
			return toBean(rs,type);			
		}
		return null;
	}
	
	public <T> List<T> queryList(String sql,Class<T> type) throws SQLException, InstantiationException, IllegalAccessException, IntrospectionException{
		Statement stmt = con.createStatement();
		ResultSet rs = stmt.executeQuery(sql);
		//将要返回的list
		List<T> list = new ArrayList<T>();
		while(rs.next()) {
			list.add(toBean(rs,type));
		}
		return list;
	}
	
	
	//核心方法,根据rs当行数据返回想要的对象
	private <T> T toBean(ResultSet rs,Class<T> type) throws IntrospectionException, SQLException, InstantiationException, IllegalAccessException {
		//在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件。
		BeanInfo beanInfo = Introspector.getBeanInfo(type);
		//得 beans PropertyDescriptor
		//PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性。 
		PropertyDescriptor[] props = beanInfo.getPropertyDescriptors();
		
		//获取此 ResultSet 对象的列的编号、类型和属性。
		ResultSetMetaData rsmd = rs.getMetaData();
		
		//现在我已经获取了javaBean的所有属性和resultSet返回值的所有属性
		//计算出一个对应关系数组,使各个属性对应 
		int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
		
		
		
		return this.createBean(rs, type, props, columnToProperty);
	}
	
	
	
	private <T> T createBean(ResultSet rs, Class<T> type,
            PropertyDescriptor[] props, int[] columnToProperty)
            throws SQLException, InstantiationException, IllegalAccessException {
		//通过反射new一个对象
		T bean = type.newInstance();
		 for (int i = 1; i < columnToProperty.length; i++) {
			 //没有符合的属性字段
			 if (columnToProperty[i] == -1) {
	                continue;
	          }
			 //获得这一列匹配的属性
			 PropertyDescriptor prop = props[columnToProperty[i]];
			 
			 //获得属性的 Class 对象
			 Class<?> propType = prop.getPropertyType();
			 
			 //判断这个属性是什么数据类型,匹配并返回
			 Object value = this.processColumn(rs, i, propType);
			 
			 //如果属性是基本类型,但是值却未成功返回,那么应该给vale初始值 int:0 boolean:false
			 if (propType != null && value == null && propType.isPrimitive()) {
	                value = primitiveDefaults.get(propType);
	          }
			 
			 //把vale赋值给对象
			 this.callSetter(bean, prop, value);
		 }
		
		return bean;
		
	}
	
	
	//计算出一个对应关系数组,使各个属性对应 
	protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,
            PropertyDescriptor[] props) throws SQLException {
		//返回此 ResultSet 对象中的列数
        int cols = rsmd.getColumnCount();
        //将要返回的数组
        //因为ResultSet是从坐标1开始的所以坐标数组也要+1
        int[] columnToProperty = new int[cols + 1];
        //默认都是-1 如果没有找到对应的属性字段那么就是-1
        Arrays.fill(columnToProperty, -1);
        for(int col = 1; col <= cols; col++) {
        	//获取这一列的名字
        	String columnName = rsmd.getColumnName(col);
        	
        	//循环javaBean中的属性
        	for(int i=0;i<props.length;i++) {
        		//不区分大小写比较
        		if (columnName.equalsIgnoreCase(props[i].getName())) {
        			//ResultSet 第col列放置的是props[i]这个属性
        			columnToProperty[col] = i;
        			break;
				}
        	}
        }
        
        return columnToProperty;
	}
	
	
	private Object processColumn(ResultSet rs, int index, Class<?> propType) throws SQLException {
	
		//如果该属性不是一个基本类型并且该列的值为null
		//返回一个null
		if ( !propType.isPrimitive() && rs.getObject(index) == null ) {
        return null;
    }

    if (propType.equals(String.class)) {
        return rs.getString(index);

    } else if (
        propType.equals(Integer.TYPE) || propType.equals(Integer.class)) {
        return Integer.valueOf(rs.getInt(index));

    } else if (
        propType.equals(Boolean.TYPE) || propType.equals(Boolean.class)) {
        return Boolean.valueOf(rs.getBoolean(index));

    } else if (propType.equals(Long.TYPE) || propType.equals(Long.class)) {
        return Long.valueOf(rs.getLong(index));

    } else if (
        propType.equals(Double.TYPE) || propType.equals(Double.class)) {
        return Double.valueOf(rs.getDouble(index));

    } else if (
        propType.equals(Float.TYPE) || propType.equals(Float.class)) {
        return Float.valueOf(rs.getFloat(index));

    } else if (
        propType.equals(Short.TYPE) || propType.equals(Short.class)) {
        return Short.valueOf(rs.getShort(index));

    } else if (propType.equals(Byte.TYPE) || propType.equals(Byte.class)) {
        return Byte.valueOf(rs.getByte(index));

    } else if (propType.equals(Timestamp.class)) {
        return rs.getTimestamp(index);

    } else {
        return rs.getObject(index);
    }
	}
	 private void callSetter(Object target, PropertyDescriptor prop, Object value)
	            throws SQLException {
		 
		 //获得应该用于写入属性值的方法。
		 //知道为什么框架为什么要求实体类需要get,set方法了吧?
		 Method setter = prop.getWriteMethod();
		 if (setter == null) {
	            return;
	      }
		 // 返回 Class 对象的数组,这些对象描述了声明将此 Method 对象表示的底层方法抛出的异常类型。
		 Class<?>[] params = setter.getParameterTypes();
		 try {
	            // convert types for some popular ones
	            if (value != null) {
	                if (value instanceof java.util.Date) {
	                    if (params[0].getName().equals("java.sql.Date")) {
	                        value = new java.sql.Date(((java.util.Date) value).getTime());
	                    } else
	                    if (params[0].getName().equals("java.sql.Time")) {
	                        value = new java.sql.Time(((java.util.Date) value).getTime());
	                    } else
	                    if (params[0].getName().equals("java.sql.Timestamp")) {
	                        value = new java.sql.Timestamp(((java.util.Date) value).getTime());
	                    }
	                }
	            }

	            // Don't call setter if the value object isn't the right type
	            if (isCompatibleType(value, params[0])) {
	                setter.invoke(target, new Object[]{value});
	            } else {
	              throw new SQLException(
	                  "Cannot set " + prop.getName() + ": incompatible types.");
	            }

	        } catch (IllegalArgumentException e) {
	            throw new SQLException(
	                "Cannot set " + prop.getName() + ": " + e.getMessage());

	        } catch (IllegalAccessException e) {
	            throw new SQLException(
	                "Cannot set " + prop.getName() + ": " + e.getMessage());

	        } catch (InvocationTargetException e) {
	            throw new SQLException(
	                "Cannot set " + prop.getName() + ": " + e.getMessage());
	        }
	 }
	 
	 
	 //判断该值和该属性是非兼容
	 private boolean isCompatibleType(Object value, Class<?> type) {
	        // Do object check first, then primitives
	        if (value == null || type.isInstance(value)) {
	            return true;

	        } else if (type.equals(Integer.TYPE) && Integer.class.isInstance(value)) {
	            return true;

	        } else if (type.equals(Long.TYPE) && Long.class.isInstance(value)) {
	            return true;

	        } else if (type.equals(Double.TYPE) && Double.class.isInstance(value)) {
	            return true;

	        } else if (type.equals(Float.TYPE) && Float.class.isInstance(value)) {
	            return true;

	        } else if (type.equals(Short.TYPE) && Short.class.isInstance(value)) {
	            return true;

	        } else if (type.equals(Byte.TYPE) && Byte.class.isInstance(value)) {
	            return true;

	        } else if (type.equals(Character.TYPE) && Character.class.isInstance(value)) {
	            return true;

	        } else if (type.equals(Boolean.TYPE) && Boolean.class.isInstance(value)) {
	            return true;

	        }
	        return false;

	    }
}

使用效果

使用到的DBUtil工具类

package com.zzzy.book.manage.util;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
 
public final class DBUtil {
 
	private DBUtil() {
 
	}
 
	/**
	 * 获取数据库连接对象
	 * 
	 * @return
	 */
	public static Connection getConn() {
 
		String url = "jdbc:mysql://localhost:3306/test?characterEncoding=utf-8";
		String user = "root";
		String password = "1913599913";
 
		Connection connection = null;
 
		try {
			// 加载驱动程序
			Class.forName("com.mysql.jdbc.Driver");
 
			// 获取连接对象
			connection = DriverManager.getConnection(url, user, password);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
 
		return connection;
 
	}
 
	// 关闭数据库连接,静态方法
	public static void closeConn(Connection conn) {
		try {
			// 如果数据库连接不为空且没有被关闭,则执行关闭操作
			if (conn != null && !conn.isClosed()) {
				conn.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
 
	public static void closeResource(Connection conn, PreparedStatement ps) {
		try {
			if (ps != null && !ps.isClosed()) {
				ps.close();
			}
 
			if (conn != null && !conn.isClosed()) {
				conn.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
 
	public static void closeResource(Connection conn, PreparedStatement ps,
			ResultSet rs) {
		try {
			if (ps != null && !ps.isClosed()) {
				ps.close();
			}
 
			if (rs != null && !rs.isClosed()) {
				rs.close();
			}
 
			if (conn != null && !conn.isClosed()) {
				conn.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
 
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SUNbrightness

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值