《学习内容》:

    //========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类

    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. *写一些连接数据库时需要用到的参数
    27. * */
    28. //Driver类加载地址
    29. private String driver="com.mysql.cj.jdbc.Driver";
    30. //连接需要的参数url user password
    31. private String url = "jdbc:mysql://localhost:3306/atm?serverTimezone=CST&characterEncoding=utf-8";
    32. private String user = "root";
    33. private String password = "at123123";
    34. /**查询单表格一条记录**/ /**但是需要三个参数,一条SQL一个策略-->策略设计模式,按顺序给我sql中预处理?的值**/
    35. public <T>T QueryOne(String sql, RowMapper rm, Object... objs){
    36. return (T)this.QueryList(sql,rm,objs).get(0);
    37. }
    38. /**查询单表格多条记录**/
    39. public <T> List<T> QueryList(String sql, RowMapper rm, Object... objs){
    40. ArrayList<T> lists = new ArrayList<>();
    41. try {
    42. Class.forName(driver);
    43. Connection conn = DriverManager.getConnection(url, user, password);
    44. PreparedStatement pstat = conn.prepareStatement(sql);
    45. for (int i = 0; i <objs.length ; i++) {
    46. pstat.setObject(i+1,objs[i]);
    47. }
    48. ResultSet rst = pstat.executeQuery();
    49. while(rst.next()){
    50. lists.add(rm.mapperRow(rst));
    51. }
    52. pstat.close();
    53. conn.close();
    54. } catch (Exception e) {
    55. e.printStackTrace();
    56. }
    57. return lists;
    58. }
    59. /**查询单表格的多条记录**/
    60. public void Update(String sql,Object... objs){
    61. try {
    62. Class.forName(driver);
    63. Connection conn = DriverManager.getConnection(url, user, password);
    64. PreparedStatement pstat = conn.prepareStatement(sql);
    65. for (int i = 0; i <objs.length ; i++) {
    66. pstat.setObject(i+1,objs[i]);
    67. }
    68. pstat.executeUpdate();
    69. pstat.close();
    70. conn.close();
    71. } catch (Exception e) {
    72. e.printStackTrace();
    73. }
    74. }
    75. /**JDBC第三天的框架封装,之前的Update方法参数传的是一个对象
    76. * 但是现在是Object... 这个东西看起来不太好--->而且需要你给我顺序才可以
    77. * 我需要实现---->你给我一条按照我的格式来写的sql语句 和一个 =对象=,更新任意一条记录
    78. * ==========================================================================
    79. * 思考:--->是否需要返回值---->修改的行数 或 不需要
    80. * ---->参数需要怎么给 ----> Sql肯定得给,然后我们不需要数组, 直接给我一个对象
    81. * ==========================================================================
    82. * 思路--->
    83. * //1.按照 #{}这种格式给我一条sql
    84. * //2.我把这条sql给拆开,然后把这里面给的表的字段的顺序和字段名 存在--->集合中
    85. * //3.并且把他拼接回认识的?组成一条之前那种JDBC认识的SQL语句
    86. * //4.将拿到的字段名通过反射domain实体类或Map或基本类型,各种类型的判断,不同分支的赋值到sql中
    87. * **/
    88. /**例如SQL---- insert into user values(#{uname},#{upassword},#{ubal}) **/
    89. /**新增,删除,更新 各种表格一条记录**/
    90. public void Update(String sql,Object obj){
    91. try {
    92. Class.forName(driver);
    93. Connection conn = DriverManager.getConnection(url, user, password);
    94. //在这里 提前把这条sql给解析好 存在 集合中字段名和顺序
    95. //并且 把用完的sql语句 再拼接回可以解析的带?的sql
    96. KeyAndSql keyAndSql = handler.paraseSql(sql);
    97. PreparedStatement pstat = conn.prepareStatement(keyAndSql.getNewSql());
    98. //如果你给我的Obj对象不是空的
    99. if (obj != null){
    100. //将sql中的?分别赋值 在执行类Handler里面再写一个 把参数赋值的方法
    101. handler.handlerParam(pstat,obj,keyAndSql.getColumnName());
    102. }
    103. pstat.executeUpdate();
    104. } catch (Exception e) {
    105. e.printStackTrace();
    106. }
    107. }
    108. }

    Handler类—->这个类主要是用来做底层的一些执行操作—->拆解SQL拼接SQL

    1. package orm;
    2. import domain.KeyAndSql;
    3. import java.lang.reflect.Field;
    4. import java.sql.PreparedStatement;
    5. import java.sql.SQLException;
    6. import java.util.ArrayList;
    7. import java.util.Map;
    8. public class Handler {
    9. /如果给定的是一个map类型 那么我需要给这个map值取出来,然后赋值到?里面/
    10. private void parseMap(PreparedStatement pstat,Object obj,ArrayList keys) throws SQLException {
    11. Map map = (Map)obj;
    12. for (int i = 0; i < keys.size(); i++) {
    13. //通过读取参数后的List集合中的字段名 进行获取map的值
    14. Object value = map.get(keys.get(i));
    15. pstat.setObject(i+1,value);
    16. }
    17. }
    18. /**最正常的情况,你给了我一个domain实体类对象 那么我需要把这个对象通过反射把属性值取出来,然后赋值到?里面**/
    19. private void parseObj(PreparedStatement pstat,Object obj,ArrayList<String> keys) throws SQLException {
    20. try {
    21. for (int i = 0; i < keys.size(); i++) {
    22. Class<?> clazz = obj.getClass();
    23. Field field = clazz.getDeclaredField(keys.get(i));
    24. field.setAccessible(true);
    25. pstat.setObject(i+1, field.get(obj));
    26. }
    27. } catch (IllegalAccessException e) {
    28. e.printStackTrace();
    29. }catch (NoSuchFieldException e){
    30. e.printStackTrace();
    31. }
    32. }
    33. /**这个方法是用来拆解传进来的SQL然后获取列名存到集合中,并且重新拼接带?的SQL**/
    34. KeyAndSql paraseSql(String sql){
    35. /**先把两个容器给创建出来,最后把他俩装满后,返回到KeyAndSql实体类中**/
    36. ArrayList<String> keys = new ArrayList<>();
    37. StringBuilder newSql = new StringBuilder();
    38. /**先处理传进来的Sql**/
    39. /**例如SQL---- insert into user values(#{uname},#{upassword},#{ubal}) **/
    40. while (true) {
    41. int left = sql.indexOf("#{");
    42. int right = sql.indexOf("}");
    43. if (left != -1 && right != -1 && left<right ) {
    44. newSql.append(sql.substring(0, left));
    45. newSql.append("?");
    46. keys.add(sql.substring(left+2,right));
    47. }else {
    48. newSql.append(sql);
    49. break;
    50. }
    51. sql = sql.substring(right+1);
    52. }
    53. return new KeyAndSql(newSql.toString(),keys);
    54. }
    55. /**这个方法用来将给定的Object 给?赋值**/
    56. void handlerParam(PreparedStatement pstat,Object obj,ArrayList<String> keys) throws SQLException {
    57. /**先判断一些---- 基本类型 ---- 如果基本是基本类型 说明只查到了一条信息 只需要给赋值一次**/
    58. if (obj == int.class || obj == Integer.class){
    59. pstat.setInt(1,(Integer) obj);
    60. }else if(obj == float.class || obj == Float.class){
    61. pstat.setFloat(1,(Float)obj);
    62. }else if (obj == double.class || obj == Double.class){
    63. pstat.setDouble(1,(Double) obj);
    64. }else if(obj == String.class){
    65. pstat.setString(1,(String)obj);
    66. }else {
    67. if (obj == Map.class){
    68. /**如果给的对象是一个 Map类型----- 那么需要单独的给他进行赋值操作**/
    69. this.parseMap(pstat,obj,keys);
    70. }else {
    71. /**那么经过前几个判断如果可以走到这里 说明肯定是给的一个 domain 对象类型 单独出去反射操作赋值**/
    72. this.parseObj(pstat,obj,keys);
    73. }
    74. }
    75. }
    76. }

    学习总结:
    学懂的:
    今天晚上的直播课封装Update只需要传一条Sql语句和一个对象,就可以实现更新记录,并且单独把他封装在了
    SqlSession中,以后我们Dao层只需要传一个sql和调用方法就可以了,完全看不到JDBC了,这次封装持久层框架,
    最重要的觉得是思想的养成,比如在Sql语句中给他加 标记 ,在给他sql之前读取,把值取出来,给他一个他要的
    这就是思想的养成,太秒了~~
    有问题的地方:
    泛型——-因为长时间没有用,在封装的时候给返回了一个(T)Atm 但是类型已经确定了,为啥还需要多态转成T类型
    老师直播的时候帮我解决了
    ———————泛型只有在调用的时候才可以确定类型!!!!**