学习内容》:

    //========Java学习内容=========
    1.直播课—-JDBC封装第四天

    《代码内容》:
    /**设计一个方法 之前的QueryList方法如果查询的话在Dao层用户需要去实现一个策略,这样体验感不太好*
    我现在需要把他设计成 你只需要给我一个sql,给我sql中预处理的参数,再给我一个Class类型的类
    __
    我就帮你把查询到的数据,存储在你给我的这个类中去
    * 参数问题??——>Sql肯定需要给我,预处理sql的?值你需要给我,还肯定需要各位Class的类型
    /_
    //===================================================================================//

    SqlSession类

    1. package orm;
    2. import domain.Atm;
    3. import domain.KeyAndSql;
    4. import java.sql.Connection;
    5. import java.sql.DriverManager;
    6. import java.sql.PreparedStatement;
    7. import java.sql.ResultSet;
    8. import java.util.ArrayList;
    9. import java.util.List;
    10. import java.util.Map;
    11. public class SqlSession {
    12. /**
    13. *因为下面update方法的返回值是一个Atm对象 如果这里返回一个集合的话 整体用户体验不太好
    14. * 思考:我们需要把这个Query(查询) 让他的返回值也变成一个对象
    15. * 策略设计模式--->匿名内部类实现策略
    16. /
    17. /**
    18. * 由于新增一条记录 删除一条记录 修改一条记录 代码中除了sql语句和预处理?的值不同,其他的代码都一样
    19. * 如果进行优化的话 参数问题??--->因为只有sql不同,那么参数需要传一条sql 预处理用动态参数列表
    20. * 若这样封装可以省2次的代码
    21. * 返回值-->可有可无 可以是修改的行数 或者 无
    22. * */
    23. /**来一个Handler小弟 给我解析 拼接**/
    24. private Handler handler = new Handler();
    25. /**写一些连接数据库时需要用到的参数**/
    26. //Driver类加载地址
    27. private String driver="com.mysql.cj.jdbc.Driver";
    28. //连接需要的参数url user password
    29. private String url = "jdbc:mysql://localhost:3306/atm?serverTimezone=CST&characterEncoding=utf-8";
    30. private String user = "root";
    31. private String password = "at123123";
    32. /**查询单表格一条记录**/ /**但是需要三个参数,一条SQL一个策略-->策略设计模式,按顺序给我sql中预处理?的值**/
    33. /**方法重载 另一个是给sql,预处理参数,存储的对象类型**/
    34. public <T>T queryOne(String sql, RowMapper rm, Object... objs){
    35. return (T)this.queryList(sql,rm,objs).get(0);
    36. }
    37. public <T>T queryOne(String sql,Object obj,Class resultType){return (T)this.queryList(sql,obj,resultType).get(0);}
    38. /**查询单表格多条记录**/
    39. /**设计一个方法 之前的QueryList方法如果查询的话在Dao层用户需要去实现一个策略,这样体验感不太好*
    40. * 我现在需要把他设计成 你只需要给我一个sql,给我sql中预处理的参数,再给我一个Class类型的类
    41. * 我就帮你把查询到的数据,存储在你给我的这个类中去
    42. * 参数问题??---->Sql肯定需要给我,预处理sql的?值你需要给我,还肯定需要各位Class的类型
    43. * */
    44. /**======查询单表格的多条记录======**/
    45. public <T> List<T> queryList(String sql, RowMapper rm, Object... objs){
    46. ArrayList<T> lists = new ArrayList<>();
    47. try {
    48. Class.forName(driver);
    49. Connection conn = DriverManager.getConnection(url, user, password);
    50. PreparedStatement pstat = conn.prepareStatement(sql);
    51. for (int i = 0; i <objs.length ; i++) {
    52. pstat.setObject(i+1,objs[i]);
    53. }
    54. ResultSet rst = pstat.executeQuery();
    55. while(rst.next()){
    56. lists.add(rm.mapperRow(rst));
    57. }
    58. rst.close();
    59. pstat.close();
    60. conn.close();
    61. } catch (Exception e) {
    62. e.printStackTrace();
    63. }
    64. return lists;
    65. }
    66. public <T> List<T> queryList(String sql,Object obj,Class resultType){
    67. ArrayList<T> lists = new ArrayList<>();
    68. try {
    69. /**JDBC基本操作**/
    70. Class.forName(driver);
    71. Connection conn = DriverManager.getConnection(url, user, password);
    72. /**在给状态参数之前要把给的属性名提前的拿到,并存集合中字段名 然后再给他一条他认识的Sql**/
    73. KeyAndSql keyAndSql = handler.parseSql(sql);
    74. PreparedStatement pstat = conn.prepareStatement(keyAndSql.getNewSql());
    75. /**给赋值**/
    76. if (obj != null){handler.handlerParam(pstat,obj,keyAndSql.getColumnName());}
    77. ResultSet rst = pstat.executeQuery();
    78. while (rst.next()){
    79. /**调用handler中的方法来往class里面赋值**/
    80. lists.add((T)handler.handlerResult(rst,resultType));
    81. }
    82. rst.close();
    83. pstat.close();
    84. conn.close();
    85. } catch (Exception e) {
    86. e.printStackTrace();
    87. }
    88. return lists;
    89. }
    90. /**JDBC第三天的框架封装,之前的Update方法参数传的是一个对象
    91. * 但是现在是Object... 这个东西看起来不太好--->而且需要你给我顺序才可以
    92. * 我需要实现---->你给我一条按照我的格式来写的sql语句 和一个 =对象=,更新任意一条记录
    93. * ==========================================================================
    94. * 思考:--->是否需要返回值---->修改的行数 或 不需要
    95. * ---->参数需要怎么给 ----> Sql肯定得给,然后我们不需要数组, 直接给我一个对象
    96. * ==========================================================================
    97. * 思路--->
    98. * //1.按照 #{}这种格式给我一条sql
    99. * //2.我把这条sql给拆开,然后把这里面给的表的字段的顺序和字段名 存在--->集合中
    100. * //3.并且把他拼接回认识的?组成一条之前那种JDBC认识的SQL语句
    101. * //4.将拿到的字段名通过反射domain实体类或Map或基本类型,各种类型的判断,不同分支的赋值到sql中
    102. * **/
    103. /**例如SQL---- insert into user values(#{uname},#{upassword},#{ubal}) **/
    104. /**新增,删除,更新 各种表格一条记录**/
    105. /**构成方法重载,一个动态参数列表,一个是给对象**/
    106. public void update(String sql,Object... objs){
    107. try {
    108. Class.forName(driver);
    109. Connection conn = DriverManager.getConnection(url, user, password);
    110. PreparedStatement pstat = conn.prepareStatement(sql);
    111. for (int i = 0; i <objs.length ; i++) {
    112. pstat.setObject(i+1,objs[i]);
    113. }
    114. pstat.executeUpdate();
    115. pstat.close();
    116. conn.close();
    117. } catch (Exception e) {
    118. e.printStackTrace();
    119. }
    120. }
    121. public void insert(String sql,Object... objs){this.update(sql,objs);}
    122. public void delete(String sql,Object... objs){this.update(sql,objs);}
    123. public void update(String sql,Object obj){
    124. try {
    125. Class.forName(driver);
    126. Connection conn = DriverManager.getConnection(url, user, password);
    127. //在这里 提前把这条sql给解析好 存在 集合中字段名和顺序
    128. //并且 把用完的sql语句 再拼接回可以解析的带?的sql
    129. KeyAndSql keyAndSql = handler.parseSql(sql);
    130. PreparedStatement pstat = conn.prepareStatement(keyAndSql.getNewSql());
    131. //如果你给我的Obj对象不是空的
    132. if (obj != null){
    133. //将sql中的?分别赋值 在执行类Handler里面再写一个 把参数赋值的方法
    134. handler.handlerParam(pstat,obj,keyAndSql.getColumnName());
    135. }
    136. pstat.executeUpdate();
    137. pstat.close();
    138. conn.close();
    139. } catch (Exception e) {
    140. e.printStackTrace();
    141. }
    142. }
    143. public void insert(String sql,Object obj){this.update(sql,obj);}
    144. public void delete(String sql,Object obj){this.update(sql,obj);}
    145. }

    Handler类

    1. package orm;
    2. import domain.KeyAndSql;
    3. import java.lang.reflect.Field;
    4. import java.sql.PreparedStatement;
    5. import java.sql.ResultSet;
    6. import java.sql.ResultSetMetaData;
    7. import java.sql.SQLException;
    8. import java.util.ArrayList;
    9. import java.util.HashMap;
    10. import java.util.Map;
    11. public class Handler {
    12. /**如果给定的容器是一个Map, 那么通过查询的结果集 把值和字段名 存入Map集合中**/
    13. private Map resultMap(ResultSet rst) throws SQLException {
    14. Map map = new HashMap();
    15. ResultSetMetaData rstmd = rst.getMetaData();
    16. for (int i = 1; i <= rstmd.getColumnCount(); i++) {
    17. String columnName = rstmd.getColumnName(i);
    18. Object object = rst.getObject(columnName);
    19. map.put(columnName,object);
    20. }
    21. return map;
    22. }
    23. /**如果给定的是一个 domain实体类,那么需要通过反射来往里面注入值**/
    24. private Object resultObj(ResultSet rst,Class resultType) throws SQLException {
    25. /**通过结果集 获取数据库的字段名 去匹配class的属性**/
    26. Object obj = null;
    27. try {
    28. //创建一个给定类型的对象
    29. obj = resultType.newInstance();
    30. /**拿到数据集中每一个字段的值和字段名**/
    31. ResultSetMetaData rstmd = rst.getMetaData();
    32. for (int i = 1; i <=rstmd.getColumnCount(); i++) {
    33. String columnName = rstmd.getColumnName(i);
    34. Object fieldValue = rst.getObject(columnName);
    35. /**通过反射去注入到属性中对应的值**/
    36. System.out.println(columnName);
    37. Field field = resultType.getDeclaredField(columnName);
    38. field.setAccessible(true);
    39. field.set(obj,fieldValue);
    40. }
    41. } catch (InstantiationException e) {
    42. e.printStackTrace();
    43. } catch (IllegalAccessException e) {
    44. e.printStackTrace();
    45. } catch (NoSuchFieldException e) {
    46. e.printStackTrace();
    47. }
    48. return obj;
    49. }
    50. /**如果给定的是一个map类型 那么我需要给这个map值取出来,然后赋值到?里面**/
    51. private void parseMap(PreparedStatement pstat,Object obj,ArrayList<String> keys) throws SQLException {
    52. Map map = (Map)obj;
    53. for (int i = 0; i < keys.size(); i++) {
    54. //通过读取参数后的List集合中的字段名 进行获取map的值
    55. Object value = map.get(keys.get(i));
    56. pstat.setObject(i+1,value);
    57. }
    58. }
    59. /**最正常的情况,你给了我一个domain实体类对象 那么我需要把这个对象通过反射把属性值取出来,然后赋值到?里面**/
    60. private void parseObj(PreparedStatement pstat,Object obj,ArrayList<String> keys) throws SQLException {
    61. try {
    62. Class clazz = obj.getClass();
    63. for (int i = 0; i < keys.size(); i++) {
    64. System.out.println(keys.get(i));
    65. Field field = clazz.getDeclaredField(keys.get(i));
    66. field.setAccessible(true);
    67. pstat.setObject(i+1, field.get(obj));
    68. }
    69. } catch (IllegalAccessException e) {
    70. e.printStackTrace();
    71. }catch (NoSuchFieldException e){
    72. e.printStackTrace();
    73. }
    74. }
    75. /**这个方法是用来拆解传进来的SQL然后获取列名存到集合中,并且重新拼接带?的SQL**/
    76. KeyAndSql parseSql(String sql){
    77. /**先把两个容器给创建出来,最后把他俩装满后,返回到KeyAndSql实体类中**/
    78. ArrayList<String> keys = new ArrayList<>();
    79. StringBuilder newSql = new StringBuilder();
    80. /**先处理传进来的Sql**/
    81. /**例如SQL---- insert into user values(#{uname},#{upassword},#{ubal}) **/
    82. while (true) {
    83. int left = sql.indexOf("#{");
    84. int right = sql.indexOf("}");
    85. if (left != -1 && right != -1 && left<right ) {
    86. newSql.append(sql.substring(0, left));
    87. newSql.append("?");
    88. keys.add(sql.substring(left+2,right));
    89. }else {
    90. newSql.append(sql);
    91. break;
    92. }
    93. sql = sql.substring(right+1);
    94. }
    95. return new KeyAndSql(newSql.toString(),keys);
    96. }
    97. /**这个方法用来将给定的Object 给?赋值**/
    98. void handlerParam(PreparedStatement pstat,Object obj,ArrayList<String> keys) throws SQLException {
    99. Class<?> clazz = obj.getClass();
    100. /**先判断一些---- 基本类型 ---- 如果基本是基本类型 说明只查到了一条信息 只需要给赋值一次**/
    101. if (clazz == int.class || clazz == Integer.class){
    102. pstat.setInt(1,(Integer) obj);
    103. }else if(clazz == float.class || clazz == Float.class){
    104. pstat.setFloat(1,(Float)obj);
    105. }else if (clazz == double.class || clazz == Double.class){
    106. pstat.setDouble(1,(Double) obj);
    107. }else if(clazz == String.class){
    108. pstat.setString(1,(String)obj);
    109. }else {
    110. if (clazz == Map.class){
    111. /**如果给的对象是一个 Map类型----- 那么需要单独的给他进行赋值操作**/
    112. this.parseMap(pstat,obj,keys);
    113. }else {
    114. /**那么经过前几个判断如果可以走到这里 说明肯定是给的一个 domain 对象类型 单独出去反射操作赋值**/
    115. this.parseObj(pstat,obj,keys);
    116. }
    117. }
    118. }
    119. /**这个方法给一个ResultSet 给往obj里面赋值**/
    120. Object handlerResult(ResultSet rst,Class resultType) throws SQLException {
    121. Object obj = null;
    122. /**先判断 一些基本类型 Map集合 **/
    123. if (resultType == int.class || resultType == Integer.class){
    124. obj = rst.getInt(1);
    125. }else if (resultType == float.class || resultType == Float.class){
    126. obj = rst.getFloat(1);
    127. }else if (resultType == double.class || resultType == Double.class){
    128. obj = rst.getDouble(1);
    129. }else if (resultType == String.class){
    130. obj = rst.getString(1);
    131. }else {
    132. if (resultType == Map.class){
    133. obj = this.resultMap(rst);
    134. }else{
    135. obj = this.resultObj(rst,resultType);
    136. }
    137. }
    138. return obj;
    139. }
    140. }

    学习总结:
    学懂的:
    今天晚上的直播课程自己可以听懂,今天主要是把代码给写了一下,写完之后感觉哇~~小细节~
    我熟练学会了DeBug调试和插入输出语句进行辅助调试代码
    有问题的地方:
    那个查询方法,报了一下午的NoSuchFieldException,调了一下午,最后DeBug都没发现什么问题,
    结果是我数据库列名是大写,实体类是小写
    再最后我改了实体类后,报了一个无法实例化异常,**然后最后我发现我实体类改了之后没写无参构造方法


    以后记住上面今天发生的问题,下次绝对不会出错啦~纪点小细节