反射乃框架的灵魂
接着昨天的文章,今天言何为反射?反射指的是这部分代码,只有在程序运行时,存在某一动作下,会触发执行。这就会带来,不同触发条件下,带来的结果会不同。
1、回顾JDBCUtils
package com.wnx.mall.tiny.fruit.utils;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class JDBCUtils {public static Connection getConnection() throws SQLException,ClassNotFoundException {Class.forName("com.mysql.cj.jdbc.Driver");String url = "jdbc:mysql://localhost:3306/javaweb";String username = "root"; String password = "root";Connection conn = DriverManager.getConnection(url, username,password);return conn;}public static void release(Statement stmt, Connection conn) {if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}stmt = null;}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}conn = null;}} public static void release(ResultSet rs, Statement stmt, Connection conn){if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}rs = null;}release(stmt, conn);}}
2、缺点弥补
采用上述方式操作数据库,问题一,数据库IO连接需要耗费大量资源,费时!问题二,该工具类出现的硬编码,没有做到代码和参数的解耦合。
聪明的程序员,想到了数据库连接池的概念来解决IO连接耗费资源的问题。池子中存在许多连接,当使用完这个连接的时候,该连接不会直接销毁,而是放回到池子中,便于程序反复复用之。而硬编码问题,程序员想到将这些参数统一放到XXX.properties文件中,集中管理他们。比如jdbc.properties,就存放了数据库连接的基本参数。
2.1、jdbc.properties
driverClassName=com.mysql.cj.jdbc.Driverurl=jdbc:mysql://127.0.0.1:3306/javaweb?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=trueusername=rootpassword=rootinitialSize=5maxActive=10maxWait=3000
2.2、整合数据库连接池
package com.wnx.mall.tiny.fruit.utils;import com.alibaba.druid.pool.DruidDataSource;import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Properties;public class JDBCUtils { // 加载驱动,并建立数据库连接 public static Connection getConnection() throws Exception { Properties properties = new Properties(); properties.load(JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties")); DataSource dataSource = DruidDataSourceFactory.createDataSource(properties); return dataSource.getConnection(); } // 关闭数据库连接,释放资源 public static void release(Statement stmt, Connection conn) { if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } } public static void release(ResultSet rs, Statement stmt, Connection conn){ if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } rs = null; } release(stmt, conn); }}
3、并不能真的优化我们开发体验
虽然上述操作对于程序底层操作做到了资源合理利用,解决了硬编码问题。但是我们业务类还是要写很多冗余的代码。特别是查询,还有遍历结果集,封装到model中,实在啰嗦。而反射便可以有效提高我们开发体验。
3.1、baseDao
定义baseDao,让反射帮我们执行吧!
package com.wnx.mall.tiny.fruit.dao;import com.wnx.mall.tiny.fruit.utils.JDBCUtils;import java.lang.reflect.Field;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.sql.*;import java.util.ArrayList;import java.util.List;public abstract class baseDAO { protected Connection conn ; protected PreparedStatement psmt ; protected ResultSet rs ; //T的Class对象 private Class entityClass ; public baseDAO(){ //getClass() 获取Class对象,当前我们执行的是new FruitDAOImpl() , 创建的是FruitDAOImpl的实例 //那么子类构造方法内部首先会调用父类(baseDAO)的无参构造方法 //因此此处的getClass()会被执行,但是getClass获取的是FruitDAOImpl的Class //所以getGenericSuperclass()获取到的是baseDAO的Class Type genericType = getClass().getGenericSuperclass(); //ParameterizedType 参数化类型 Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments(); //获取到的中的T的真实的类型 Type actualType = actualTypeArguments[0]; try { entityClass = Class.forName(actualType.getTypeName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } protected Connection getConn(){ try { return JDBCUtils.getConnection(); } catch (Exception e) { e.printStackTrace(); } return null ; } protected void close(ResultSet rs , PreparedStatement psmt , Connection conn){ try { if (rs != null) { rs.close(); } if(psmt!=null){ psmt.close(); } if(conn!=null && !conn.isClosed()){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } //给预处理命令对象设置参数 private void setParams(PreparedStatement psmt , Object..、params) throws SQLException { if(params!=null && params.length>0){ for (int i = 0; i < params.length; i++) { psmt.setObject(i+1,params[i]); } } } //执行更新,返回影响行数 protected int executeUpdate(String sql , Object..、params){ boolean insertFlag = false ; insertFlag = sql.trim().toUpperCase().startsWith("INSERT"); try { conn = getConn(); if(insertFlag){ psmt = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); }else { psmt = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); } setParams(psmt,params); int count = psmt.executeUpdate() ; rs = psmt.getGeneratedKeys(); if(rs.next()){ return ((Long)rs.getLong(1)).intValue(); } return count ; } catch (SQLException e) { e.printStackTrace(); }finally { close(rs,psmt,conn); } return 0; } //通过反射技术给obj对象的property属性赋propertyValue值 private void setValue(Object obj , String property , Object propertyValue){ Class clazz = obj.getClass(); try { //获取property这个字符串对应的属性名 , 比如 "fid" 去找 obj对象中的 fid 属性 Field field = clazz.getDeclaredField(property); if(field!=null){ field.setAccessible(true); field.set(obj,propertyValue); } } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } } //执行复杂查询,返回例如统计结果 protected Object[] executeComplexQuery(String sql , Object..、params){ try { conn = getConn() ; psmt = conn.prepareStatement(sql); setParams(psmt,params); rs = psmt.executeQuery(); //通过rs可以获取结果集的元数据 //元数据:描述结果集数据的数据 , 简单讲,就是这个结果集有哪些列,什么类型等等 ResultSetmetaData rsmd = rs.getmetaData(); //获取结果集的列数 int columnCount = rsmd.getColumnCount(); Object[] columnValueArr = new Object[columnCount]; //6.解析rs if(rs.next()){ for(int i = 0 ; i executeQuery(String sql , Object..、params){ List list = new ArrayList<>(); try { conn = getConn() ; psmt = conn.prepareStatement(sql); setParams(psmt,params); rs = psmt.executeQuery(); //通过rs可以获取结果集的元数据 //元数据:描述结果集数据的数据 , 简单讲,就是这个结果集有哪些列,什么类型等等 ResultSetmetaData rsmd = rs.getmetaData(); //获取结果集的列数 int columnCount = rsmd.getColumnCount(); //6.解析rs while(rs.next()){ T entity = (T)entityClass.newInstance(); for(int i = 0 ; i
3.2、FruitDAOImpl
再看实现的变化,可以看到,对比之前,我们要写的代码大大减少了!我们的关注点,只在那条最关键的SQL。
package com.wnx.mall.tiny.fruit.dao.impl;import com.wnx.mall.tiny.fruit.dao.baseDAO;import com.wnx.mall.tiny.fruit.dao.FruitDAO;import com.wnx.mall.tiny.fruit.domain.Fruit;import com.wnx.mall.tiny.fruit.utils.JDBCUtils;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.util.ArrayList;import java.util.List;public class FruitDAOImpl extends baseDAO implements FruitDAO { @Override public List getFruitList() { String sql = "select * from t_fruit"; return executeQuery(sql); } @Override public boolean addFruit(Fruit fruit) { String sql = "insert into t_fruit values(0,?,?,?,?)"; int count = super.executeUpdate(sql, fruit.getFname(), fruit.getPrice(), fruit.getFcount(), fruit.getRemark()); return count>0; } @Override public boolean updateFruit(Fruit fruit) { String sql = "update t_fruit set fcount = ? where fid = ? " ; int count = super.executeUpdate(sql,fruit.getFcount(),fruit.getFid()); return count>0; } @Override public Fruit getFruitByFname(String fname) { String sql = "select * from t_fruit where fname like ? "; return load(sql,fname); } @Override public boolean delFruit(String fname) { String sql = "delete from t_fruit where fname like ? " ; int count = executeUpdate(sql, fname); return count>0; } @Override public boolean delete(int fid) { String sql = "delete from t_fruit where fid = ? " ; int count = super.executeUpdate(sql,fid); return count >0; } @Override public Fruit findById(int fid) { String sql = "select * from t_fruit where fid = ? "; return load(sql,fid); }}
4、缺点
反射是框架的灵魂,合理的使用框架,能减少我们的代码量。让我们能快速开发。优秀的数据库框架有Apache DbUtils工具类,Spring的JdbcTemplate工具类,Mybatis框架, SpringData Jpa框架。 下一篇文章,我们使用框架进行开发。