《学习内容》:
//========Java学习内容=========
1.直播课—-JDBC封装第三天
《代码内容》:
/JDBC第三天的框架封装,之前的Update方法参数传的是一个对象
但是现在是Object… 这个东西看起来不太好—->而且需要你给我顺序才可以
我需要实现——>你给我一条按照我的格式来写的sql语句 和一个 =对象=,更新任意一条记录
==========================================================================
思考:—->是否需要返回值——>修改的行数 或 不需要
——>参数需要怎么给 ——> Sql肯定得给,然后我们不需要数组, 直接给我一个对象
==========================================================================
思路—->
//1.按照 #{}这种格式给我一条sql
//2.我把这条sql给拆开,然后把这里面给的表的字段的顺序和字段名 存在—->集合中
//3.并且把他拼接回认识的?组成一条之前那种JDBC认识的SQL语句
//4.将拿到的字段名通过反射domain实体类或Map或基本类型,各种类型的判断,不同分支的赋值到sql中
/
/例如SQL—— insert into user values(#{uname},#{upassword},#{ubal}) /
经过上面的思路分析后,我们的持久层框架主要在SqlSession中封装,封装后的代码如下
//===================================================================================//
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中预处理?的值**/
public <T>T QueryOne(String sql, RowMapper rm, Object... objs){
return (T)this.QueryList(sql,rm,objs).get(0);
}
/**查询单表格多条记录**/
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));
}
pstat.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
return lists;
}
/**查询单表格的多条记录**/
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();
}
}
/**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 obj){
try {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, user, password);
//在这里 提前把这条sql给解析好 存在 集合中字段名和顺序
//并且 把用完的sql语句 再拼接回可以解析的带?的sql
KeyAndSql keyAndSql = handler.paraseSql(sql);
PreparedStatement pstat = conn.prepareStatement(keyAndSql.getNewSql());
//如果你给我的Obj对象不是空的
if (obj != null){
//将sql中的?分别赋值 在执行类Handler里面再写一个 把参数赋值的方法
handler.handlerParam(pstat,obj,keyAndSql.getColumnName());
}
pstat.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Handler类—->这个类主要是用来做底层的一些执行操作—->拆解SQL拼接SQL
package orm;
import domain.KeyAndSql;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Map;
public class Handler {
/如果给定的是一个map类型 那么我需要给这个map值取出来,然后赋值到?里面/
private void parseMap(PreparedStatement pstat,Object obj,ArrayList 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 {
for (int i = 0; i < keys.size(); i++) {
Class<?> clazz = obj.getClass();
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 paraseSql(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 {
/**先判断一些---- 基本类型 ---- 如果基本是基本类型 说明只查到了一条信息 只需要给赋值一次**/
if (obj == int.class || obj == Integer.class){
pstat.setInt(1,(Integer) obj);
}else if(obj == float.class || obj == Float.class){
pstat.setFloat(1,(Float)obj);
}else if (obj == double.class || obj == Double.class){
pstat.setDouble(1,(Double) obj);
}else if(obj == String.class){
pstat.setString(1,(String)obj);
}else {
if (obj == Map.class){
/**如果给的对象是一个 Map类型----- 那么需要单独的给他进行赋值操作**/
this.parseMap(pstat,obj,keys);
}else {
/**那么经过前几个判断如果可以走到这里 说明肯定是给的一个 domain 对象类型 单独出去反射操作赋值**/
this.parseObj(pstat,obj,keys);
}
}
}
}
学习总结:
学懂的:
今天晚上的直播课封装Update只需要传一条Sql语句和一个对象,就可以实现更新记录,并且单独把他封装在了
SqlSession中,以后我们Dao层只需要传一个sql和调用方法就可以了,完全看不到JDBC了,这次封装持久层框架,
最重要的觉得是思想的养成,比如在Sql语句中给他加 标记 ,在给他sql之前读取,把值取出来,给他一个他要的
这就是思想的养成,太秒了~~
有问题的地方:
泛型——-因为长时间没有用,在封装的时候给返回了一个(T)Atm 但是类型已经确定了,为啥还需要多态转成T类型
老师直播的时候帮我解决了
———————泛型只有在调用的时候才可以确定类型!!!!**