学习内容》:
//========Java学习内容=========
1.直播课—-JDBC封装第四天
《代码内容》:
/**设计一个方法 之前的QueryList方法如果查询的话在Dao层用户需要去实现一个策略,这样体验感不太好*
我现在需要把他设计成 你只需要给我一个sql,给我sql中预处理的参数,再给我一个Class类型的类
__ 我就帮你把查询到的数据,存储在你给我的这个类中去
* 参数问题??——>Sql肯定需要给我,预处理sql的?值你需要给我,还肯定需要各位Class的类型
/_
//===================================================================================//
SqlSession类
package orm;
import domain.Atm;
import domain.KeyAndSql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class SqlSession {
/**
*因为下面update方法的返回值是一个Atm对象 如果这里返回一个集合的话 整体用户体验不太好
* 思考:我们需要把这个Query(查询) 让他的返回值也变成一个对象
* 策略设计模式--->匿名内部类实现策略
/
/**
* 由于新增一条记录 删除一条记录 修改一条记录 代码中除了sql语句和预处理?的值不同,其他的代码都一样
* 如果进行优化的话 参数问题??--->因为只有sql不同,那么参数需要传一条sql 预处理用动态参数列表
* 若这样封装可以省2次的代码
* 返回值-->可有可无 可以是修改的行数 或者 无
* */
/**来一个Handler小弟 给我解析 拼接**/
private Handler handler = new Handler();
/**写一些连接数据库时需要用到的参数**/
//Driver类加载地址
private String driver="com.mysql.cj.jdbc.Driver";
//连接需要的参数url user password
private String url = "jdbc:mysql://localhost:3306/atm?serverTimezone=CST&characterEncoding=utf-8";
private String user = "root";
private String password = "at123123";
/**查询单表格一条记录**/ /**但是需要三个参数,一条SQL一个策略-->策略设计模式,按顺序给我sql中预处理?的值**/
/**方法重载 另一个是给sql,预处理参数,存储的对象类型**/
public <T>T queryOne(String sql, RowMapper rm, Object... objs){
return (T)this.queryList(sql,rm,objs).get(0);
}
public <T>T queryOne(String sql,Object obj,Class resultType){return (T)this.queryList(sql,obj,resultType).get(0);}
/**查询单表格多条记录**/
/**设计一个方法 之前的QueryList方法如果查询的话在Dao层用户需要去实现一个策略,这样体验感不太好*
* 我现在需要把他设计成 你只需要给我一个sql,给我sql中预处理的参数,再给我一个Class类型的类
* 我就帮你把查询到的数据,存储在你给我的这个类中去
* 参数问题??---->Sql肯定需要给我,预处理sql的?值你需要给我,还肯定需要各位Class的类型
* */
/**======查询单表格的多条记录======**/
public <T> List<T> queryList(String sql, RowMapper rm, Object... objs){
ArrayList<T> lists = new ArrayList<>();
try {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, user, password);
PreparedStatement pstat = conn.prepareStatement(sql);
for (int i = 0; i <objs.length ; i++) {
pstat.setObject(i+1,objs[i]);
}
ResultSet rst = pstat.executeQuery();
while(rst.next()){
lists.add(rm.mapperRow(rst));
}
rst.close();
pstat.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
return lists;
}
public <T> List<T> queryList(String sql,Object obj,Class resultType){
ArrayList<T> lists = new ArrayList<>();
try {
/**JDBC基本操作**/
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, user, password);
/**在给状态参数之前要把给的属性名提前的拿到,并存集合中字段名 然后再给他一条他认识的Sql**/
KeyAndSql keyAndSql = handler.parseSql(sql);
PreparedStatement pstat = conn.prepareStatement(keyAndSql.getNewSql());
/**给赋值**/
if (obj != null){handler.handlerParam(pstat,obj,keyAndSql.getColumnName());}
ResultSet rst = pstat.executeQuery();
while (rst.next()){
/**调用handler中的方法来往class里面赋值**/
lists.add((T)handler.handlerResult(rst,resultType));
}
rst.close();
pstat.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
return lists;
}
/**JDBC第三天的框架封装,之前的Update方法参数传的是一个对象
* 但是现在是Object... 这个东西看起来不太好--->而且需要你给我顺序才可以
* 我需要实现---->你给我一条按照我的格式来写的sql语句 和一个 =对象=,更新任意一条记录
* ==========================================================================
* 思考:--->是否需要返回值---->修改的行数 或 不需要
* ---->参数需要怎么给 ----> Sql肯定得给,然后我们不需要数组, 直接给我一个对象
* ==========================================================================
* 思路--->
* //1.按照 #{}这种格式给我一条sql
* //2.我把这条sql给拆开,然后把这里面给的表的字段的顺序和字段名 存在--->集合中
* //3.并且把他拼接回认识的?组成一条之前那种JDBC认识的SQL语句
* //4.将拿到的字段名通过反射domain实体类或Map或基本类型,各种类型的判断,不同分支的赋值到sql中
* **/
/**例如SQL---- insert into user values(#{uname},#{upassword},#{ubal}) **/
/**新增,删除,更新 各种表格一条记录**/
/**构成方法重载,一个动态参数列表,一个是给对象**/
public void update(String sql,Object... objs){
try {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, user, password);
PreparedStatement pstat = conn.prepareStatement(sql);
for (int i = 0; i <objs.length ; i++) {
pstat.setObject(i+1,objs[i]);
}
pstat.executeUpdate();
pstat.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void insert(String sql,Object... objs){this.update(sql,objs);}
public void delete(String sql,Object... objs){this.update(sql,objs);}
public void update(String sql,Object obj){
try {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, user, password);
//在这里 提前把这条sql给解析好 存在 集合中字段名和顺序
//并且 把用完的sql语句 再拼接回可以解析的带?的sql
KeyAndSql keyAndSql = handler.parseSql(sql);
PreparedStatement pstat = conn.prepareStatement(keyAndSql.getNewSql());
//如果你给我的Obj对象不是空的
if (obj != null){
//将sql中的?分别赋值 在执行类Handler里面再写一个 把参数赋值的方法
handler.handlerParam(pstat,obj,keyAndSql.getColumnName());
}
pstat.executeUpdate();
pstat.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void insert(String sql,Object obj){this.update(sql,obj);}
public void delete(String sql,Object obj){this.update(sql,obj);}
}
Handler类
package orm;
import domain.KeyAndSql;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class Handler {
/**如果给定的容器是一个Map, 那么通过查询的结果集 把值和字段名 存入Map集合中**/
private Map resultMap(ResultSet rst) throws SQLException {
Map map = new HashMap();
ResultSetMetaData rstmd = rst.getMetaData();
for (int i = 1; i <= rstmd.getColumnCount(); i++) {
String columnName = rstmd.getColumnName(i);
Object object = rst.getObject(columnName);
map.put(columnName,object);
}
return map;
}
/**如果给定的是一个 domain实体类,那么需要通过反射来往里面注入值**/
private Object resultObj(ResultSet rst,Class resultType) throws SQLException {
/**通过结果集 获取数据库的字段名 去匹配class的属性**/
Object obj = null;
try {
//创建一个给定类型的对象
obj = resultType.newInstance();
/**拿到数据集中每一个字段的值和字段名**/
ResultSetMetaData rstmd = rst.getMetaData();
for (int i = 1; i <=rstmd.getColumnCount(); i++) {
String columnName = rstmd.getColumnName(i);
Object fieldValue = rst.getObject(columnName);
/**通过反射去注入到属性中对应的值**/
System.out.println(columnName);
Field field = resultType.getDeclaredField(columnName);
field.setAccessible(true);
field.set(obj,fieldValue);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return obj;
}
/**如果给定的是一个map类型 那么我需要给这个map值取出来,然后赋值到?里面**/
private void parseMap(PreparedStatement pstat,Object obj,ArrayList<String> keys) throws SQLException {
Map map = (Map)obj;
for (int i = 0; i < keys.size(); i++) {
//通过读取参数后的List集合中的字段名 进行获取map的值
Object value = map.get(keys.get(i));
pstat.setObject(i+1,value);
}
}
/**最正常的情况,你给了我一个domain实体类对象 那么我需要把这个对象通过反射把属性值取出来,然后赋值到?里面**/
private void parseObj(PreparedStatement pstat,Object obj,ArrayList<String> keys) throws SQLException {
try {
Class clazz = obj.getClass();
for (int i = 0; i < keys.size(); i++) {
System.out.println(keys.get(i));
Field field = clazz.getDeclaredField(keys.get(i));
field.setAccessible(true);
pstat.setObject(i+1, field.get(obj));
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}catch (NoSuchFieldException e){
e.printStackTrace();
}
}
/**这个方法是用来拆解传进来的SQL然后获取列名存到集合中,并且重新拼接带?的SQL**/
KeyAndSql parseSql(String sql){
/**先把两个容器给创建出来,最后把他俩装满后,返回到KeyAndSql实体类中**/
ArrayList<String> keys = new ArrayList<>();
StringBuilder newSql = new StringBuilder();
/**先处理传进来的Sql**/
/**例如SQL---- insert into user values(#{uname},#{upassword},#{ubal}) **/
while (true) {
int left = sql.indexOf("#{");
int right = sql.indexOf("}");
if (left != -1 && right != -1 && left<right ) {
newSql.append(sql.substring(0, left));
newSql.append("?");
keys.add(sql.substring(left+2,right));
}else {
newSql.append(sql);
break;
}
sql = sql.substring(right+1);
}
return new KeyAndSql(newSql.toString(),keys);
}
/**这个方法用来将给定的Object 给?赋值**/
void handlerParam(PreparedStatement pstat,Object obj,ArrayList<String> keys) throws SQLException {
Class<?> clazz = obj.getClass();
/**先判断一些---- 基本类型 ---- 如果基本是基本类型 说明只查到了一条信息 只需要给赋值一次**/
if (clazz == int.class || clazz == Integer.class){
pstat.setInt(1,(Integer) obj);
}else if(clazz == float.class || clazz == Float.class){
pstat.setFloat(1,(Float)obj);
}else if (clazz == double.class || clazz == Double.class){
pstat.setDouble(1,(Double) obj);
}else if(clazz == String.class){
pstat.setString(1,(String)obj);
}else {
if (clazz == Map.class){
/**如果给的对象是一个 Map类型----- 那么需要单独的给他进行赋值操作**/
this.parseMap(pstat,obj,keys);
}else {
/**那么经过前几个判断如果可以走到这里 说明肯定是给的一个 domain 对象类型 单独出去反射操作赋值**/
this.parseObj(pstat,obj,keys);
}
}
}
/**这个方法给一个ResultSet 给往obj里面赋值**/
Object handlerResult(ResultSet rst,Class resultType) throws SQLException {
Object obj = null;
/**先判断 一些基本类型 Map集合 **/
if (resultType == int.class || resultType == Integer.class){
obj = rst.getInt(1);
}else if (resultType == float.class || resultType == Float.class){
obj = rst.getFloat(1);
}else if (resultType == double.class || resultType == Double.class){
obj = rst.getDouble(1);
}else if (resultType == String.class){
obj = rst.getString(1);
}else {
if (resultType == Map.class){
obj = this.resultMap(rst);
}else{
obj = this.resultObj(rst,resultType);
}
}
return obj;
}
}
学习总结:
学懂的:
今天晚上的直播课程自己可以听懂,今天主要是把代码给写了一下,写完之后感觉哇~~小细节~
我熟练学会了DeBug调试和插入输出语句进行辅助调试代码
有问题的地方:
那个查询方法,报了一下午的NoSuchFieldException,调了一下午,最后DeBug都没发现什么问题,
结果是我数据库列名是大写,实体类是小写
再最后我改了实体类后,报了一个无法实例化异常,**然后最后我发现我实体类改了之后没写无参构造方法
以后记住上面今天发生的问题,下次绝对不会出错啦~纪点小细节