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.propertiesdriverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/javaweb?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true username=root password=root initialSize=5 maxActive=10 maxWait=30002.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、并不能真的优化我们开发体验
3.1、baseDao虽然上述操作对于程序底层操作做到了资源合理利用,解决了硬编码问题。但是我们业务类还是要写很多冗余的代码。特别是查询,还有遍历结果集,封装到model中,实在啰嗦。而反射便可以有效提高我们开发体验。
定义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 baseDAO4、缺点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); } } 反射是框架的灵魂,合理的使用框架,能减少我们的代码量。让我们能快速开发。优秀的数据库框架有Apache DbUtils工具类,Spring的JdbcTemplate工具类,Mybatis框架, SpringData Jpa框架。 下一篇文章,我们使用框架进行开发。



