手写Mybatis源码01
前言
这篇介绍怎么动态获取结果集中的数据并通过反射
一一对应赋值给实体类,如果为空,则赋值null
一、定义一个Sql数据类型转换为java数据类型的工具类
首先我们先定义一个sql数据类型转换成java数据类型的工具类,后面我们会用到。
package com.darkForest.uitl;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.sql.*;
/*
* sql数据类型转换工具类
* */
@Slf4j(topic = "e")
public class SqlTypeUtil {
public Class<?> sqlTypeToJavaType(String sqlType) throws SQLException {
//我们使用反射来获得实体类的方法对象,通过方法对象调用invoke()方法来给实体类赋值,
// 所以我们在工具类定义并返回数据类型的Class对象
Class<?> javaType=null;
switch (sqlType){
case "VARCHAR":
case "CHAR":
case "TEXT":
//返回String类型的Class对象
javaType=String.class;
break;
case "INT":
case "SMALLINT":
case "TINYINT":
//返回int类型的Class对象
javaType=int.class;
break;
case "DECIMAL":
//返回BigDecimal类型的Class对象
javaType= BigDecimal.class;
break;
case "DATE":
//返回Date类型的Class对象
javaType= Date.class;
break;
case "TIME":
//返回Time类型的Class对象
javaType= Time.class;
break;
case "TIMESTAMP":
//返回Timestamp类型的Class对象
javaType= Timestamp.class;
break;
case "BOOLEAN":
//返回boolean类型的Class对象
javaType= Boolean.class;
break;
default:
log.debug("result:{}","未知类型");
break;
}
return javaType;
}
}
二、动态获取列对应的值并通过方法赋值给实体类
package com.darkForest.uitl;
import com.darkForest.entity.StudentUser;
import lombok.val;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.List;
//处理结果集工具类
public class ResultSetUtil {
ResultSet resultSet;
public ResultSetUtil(ResultSet resultSet){
this.resultSet=resultSet;
}
public <T>T getEntity(Class clazz) throws InstantiationException, IllegalAccessException, SQLException, NoSuchMethodException, InvocationTargetException {
//定义list集合存放实体类对象(避免数据库有多条记录的情况)
List<Object> list=null;
while (resultSet.next()) {
//使用反射来通过无参构造函数来new一个实体类对象
Constructor<?> constructor=clazz.getDeclaredConstructor();
Object object=constructor.newInstance();
//获取了结果集的元数据信息。结果集中列的信息,比如列的名称、数据类型、列的数量 getMetaData:得到(元数据对象)
ResultSetMetaData md = resultSet.getMetaData();
//得到结果集数据记录(列)数
int columnCount = md.getColumnCount();
for (int i = 1; i <=columnCount; i++) {
//得到当前(i)对应的一条记录对象
Object val=resultSet.getObject(i);
//获取与查询结果集中指定列(对应的i)关联的目录名称
String catalogName = md.getCatalogName(i);
//得到当前列对应的值
Object value=resultSet.getObject(i);
//得到当前列的列名
String name= md.getColumnName(i);
//转换成方法名,对列名第一个字符做大写处理,与set拼接
String firstStr=name.substring(0,1);
String entityName="set"+firstStr.toUpperCase()+name.substring(1);
//得到当前列的数据类型(这里要用到我们刚刚定义的工具类)
String typeName=md.getColumnTypeName(i);
Class<?> typeObject= new SqlTypeUtil().sqlTypeToJavaType(typeName);
//得到实体类中对应的方法对象,并执行invoke方法
Method method=clazz.getDeclaredMethod(entityName,typeObject);
method.invoke(object,value);
}
list.add(object);
}
return (T)list;
}
}
这种方式固然可以解决了一些问题,但是还是有一些弊端,比如,查询的不是数据库表中所有列的数据,而是其中的一些,那么在数据类型为String的情况下,没有查询的列对应的实体类中的属性的值是一个空串,而不是null,int的情况下,是默认值0,也不是null,这对于我们后面通过该实体类或集合做处理时很不方便,如何解决这个问题,下次我们继续讨论。