说明
类似 Mybatis,支持将查询结果封装到 Bean 中
继承
需要继承 某个 DbUtils 类 或 子类即可
注意
MySQL 中的 decimal 类型,JDBC 查询是
BigDecimal类型,需要做判断,转换成double、float类型bean中有某成员变量,但查询表时,没有查询该列,就不能取该数据
代码
定义内省工具类
package top.malaoshi.jdbc;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class IntrospectorUtils {
/**
* 获取setter方法
* @param bean
* @return 返回Map,属性名作为key,setter方法作为value
*/
public static Map<String, Method> getSetMethods(Class bean) throws IntrospectionException {
// 使用内省,目的:是反射的简化写法
BeanInfo bi= Introspector.getBeanInfo(bean); // 返回 BeanInfo,bean的信息
// 将信息取出,放入到数组里
PropertyDescriptor[] pds=bi.getPropertyDescriptors();
// 将属性 和 相关的set方法,放入到map中
Map<String,Method> propertyAndSetterMap = new HashMap();
for( PropertyDescriptor pd : pds){
//System.out.println("属性名:"+pd.getName());
String propertyName = pd.getName();
// 获取 set方法 的信息
Method method = pd.getWriteMethod();
// 如果有 set 方法,就放入到map中
if(method!=null) {
propertyAndSetterMap.put(propertyName,method);
}
}
return propertyAndSetterMap;
}
}
定义 DbUtilsBean 类
/**
* 指定封装类,将查询的结果封装到该类中
* 注意:列名要与类成员变量名相同,才能封装
*
* @param sql
* @param params
* @param clazz 指定封装类
* @return
* @throws SQLException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws InvocationTargetException
*/
public <E> List<E> query(Class clazz,String sql,Object ... params) throws SQLException, IntrospectionException, IllegalAccessException, InstantiationException, InvocationTargetException {
Map<String,Method> propertyAndSetterMap = IntrospectorUtils.getSetMethods(clazz);
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 先循环绑定参数
for (int i=0 ;i< params.length ;i++ ){
// ? 的索引,从1开始的
// 数组的索引,从0开始的
preparedStatement.setObject(i+1 , params[i] );
}
List retList = new ArrayList();
// 然后执行查询
ResultSet resultSet = preparedStatement.executeQuery();
ResultSetMetaData rsmd = resultSet.getMetaData();
// 返回查询结果列的总数
int columnNum = rsmd.getColumnCount();
while (resultSet.next()){
// 通过反射方式实例化
Object bean = clazz.newInstance();
// 将查询列的值,放入到 javabean(模型类)的 属性中
for( int i = 1 ;i<=columnNum ;i++){
// 取出查询的列名
String columnLabel = rsmd.getColumnLabel(i);
// 取出与该列名相应的值
Object value = resultSet.getObject(i);
// 根据列名,从map中取出与列名相同的属性的set方法
Method setMethod = propertyAndSetterMap.get(columnLabel);
// 如果方法为null,表示没有对应的方法,就不再往下执行
if(setMethod == null) {
continue;
}
invokeSetMethod(bean,setMethod,value);
}
retList.add(bean);
}
resultSet.close();
preparedStatement.close();
return retList;
}
/**
* 调用 setter 方法
* @param bean
* @param setMethod
* @param value
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
private void invokeSetMethod(Object bean, Method setMethod, Object value ) throws InvocationTargetException, IllegalAccessException {
// set方法一定有一个参数
Class paraType = setMethod.getParameterTypes()[0];
Object dataTemp=null;
if ( value instanceof BigDecimal){ // mysql数据库的 decimal 类型,jdbc查询出来的类型是 BigDecimal,需要做额外处理
BigDecimal temp=(BigDecimal)value;
if (paraType == double.class){
dataTemp = temp.doubleValue();
}else if(paraType == float.class){
dataTemp = temp.floatValue();
}
}else{
dataTemp=value;
}
// 动态调用 setXXX 方法,并且传值
try {
setMethod.invoke(bean,dataTemp);
}catch (java.lang.IllegalArgumentException e){
String dataType = "null";
if(dataTemp!=null){
dataType = dataTemp.getClass().getName();
}
String msg = String.format("%s() 形参类型是:%s,从数据库查询出来的数据:%s,类型是:%s",
setMethod.getName(),
setMethod.getParameterTypes()[0],
dataTemp,
dataType);
throw new IllegalArgumentException(msg);
}
}
测试
依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>top.malaoshi</groupId>
<artifactId>util</artifactId>
<version>1.4.5</version>
<scope>test</scope>
</dependency>
定义封装类 Emp
public class Emp {
private int empno;
private String ename;
private String job;
private Integer mgr;
public int getEmpno() {
return empno;
}
public void setEmpno(int empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Integer getMgr() {
return mgr;
}
public void setMgr(Integer mgr) {
this.mgr = mgr;
}
@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", mgr=" + mgr +
'}';
}
}
定义测试类
import top.malaoshi.jdbc.ConnectionFactory;
import top.malaoshi.jdbc.DB;
import top.malaoshi.util.Out;
import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
public class Test {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
//东8时区
String url="jdbc:mysql://localhost:3308/scott?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
Connection conn = ConnectionFactory.connect("com.mysql.cj.jdbc.Driver"
, url
,"root"
,"");
DB db = new DB(conn);
String sql = "select * from emp";
try {
List<Emp> list = db.query(Emp.class, sql);
Out.elementSep(",\n").printList(list);
} catch (IntrospectionException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}