尚学堂JAVA高级学习笔记_2/2

尚学堂JAVA高级学习笔记

文章目录

  • 尚学堂JAVA高级学习笔记
    • 写在前面
    • 第4章 正则表达式
      • 1. 正则表达式语法
      • 2. 正则表达式的练习
      • 3. 常用正则表达式列表
      • 4. JAVA程序中使用正则表达式
      • 5. 手写网络爬虫
    • 第5章 JDBC
      • 1. Mysql引入
      • 2. JDBC简述
      • 3. JDBC常用接口
      • 4. 事务
      • 5. 时间处理
      • 6. 大对象操作
        • 6.1 CLOB文本大对象操作
        • 6.2 BLOB二进制大对象操作
      • 7. 经典JDBC代码总结
      • 8. ORM原理
      • 9. 手写SORM框架
        • 9.1 第一版SORM
        • 9.2 通过模板方法模式对Query进行优化
        • 9.3 工厂模式+单例模式+克隆模式构建QueryFactory
        • 9.4 增加连接池提高效率
        • 9.5 jar包和API文档的生成

写在前面

学习链接:Java 视频教程全集

课件链接:Java课件

声明:全是本人边学习边手打的,希望对大家有帮助。

第4章 正则表达式

  • 文本的复杂处理
  • regular expression
  • 开发中使用正则表达式的流程:
    • 分析所有匹配的数据,写出测试用的典型数据
    • 在工具软件中进行匹配测试
    • 在程序中调用通过测试的正则表达式
  • regexbuddy工具

1. 正则表达式语法

  • 普通字符:匹配与之相同的一个字符

  • 简单的转义字符:

    尚学堂JAVA高级学习笔记_2/2_第1张图片

  • 标准字符集合

    • 能够与“多种字符”匹配的表达式
    • 注意区分带小写,大写是相反的意思(比如\d是匹配一个数字,\D就是匹配非数字)
    • 小数点不能匹配换行符\n
    • [\s\S]用来匹配任意字符,包括换行符

    尚学堂JAVA高级学习笔记_2/2_第2张图片

  • 自定义字符集合

    • []方括号匹配方式,能够匹配方括号中任意一个字符

    尚学堂JAVA高级学习笔记_2/2_第3张图片

    • 正则表达式的特殊符号,被包含到中括号中,则失去特殊意义,除了^,-之外
    • 标准字符集合,除小数点外,如果被包含于中括号,自定义字符集合将包含该集合。比如:[\d.\-+]将匹配:数字、小数点、+、-
  • 量词 | Quantifier

    • 修饰匹配次数的特殊符号

      尚学堂JAVA高级学习笔记_2/2_第4张图片

      \d\d{6} != {\d\d}{6}

    • 匹配次数中的贪婪模式(匹配字符越多越好,默认!)

    • 匹配次数中的非贪婪模式(匹配字符越少越好,修饰匹配次数的特殊符号后再加上一个“?”号)例:\d{2,3}?

  • 字符边界(零宽)

    • 本组标记匹配的不是字符而是位置,符合某 种条件的位置

    • \b匹配这样一个位置:前面的字符和后面的字符不全是\w

      尚学堂JAVA高级学习笔记_2/2_第5张图片

    • gaoqi\b测试:

      尚学堂JAVA高级学习笔记_2/2_第6张图片

    • \bgaoqi\b测试:

      尚学堂JAVA高级学习笔记_2/2_第7张图片

  • IGNORECASE:忽略大小写模式

    • 匹配时忽略大小写
    • 默认情况下,正则表达式是要区分大小写的
  • SINGLELINE:单行模式

    • 整个文本看作一个字符串,只有一个开头,一个结尾。
    • 使小数点“.”可以匹配包含换行符(\n)在内的任意字符。
  • MULTILINE:多行模式

    • 每行都是一个字符串,都有开头和结尾
    • 在制定了MULTILINE之后,如果需要仅匹配字符串开始和结束位置,可以使用**\A和\Z**
  • 选择符和分组

    尚学堂JAVA高级学习笔记_2/2_第8张图片

  • 反向引用(\nnm)

    • 每一对()会分配一个编号,使用()的捕获根据左括号的顺序从1开始自动编号。
    • 通过反向应用,可以对分组已捕获的字符串进行应用。
  • 预搜索(零宽断言)

    • 只进行子表达式的匹配,匹配内容不计入最终的匹配结果,是零宽度

    • 这个位置应该符合某个条件。判断当前位置的前后字符,是否符合指定的条件,但不匹配前后的字符。是对位置的匹配。

    • 正则表达式匹配过程中,如果子表达式匹配到的是字符的内容,而非位置,并被保存到最终的匹配结果中,那么就认为这个子表达式是占有字符的;如果子表达式匹配的仅仅是位置,或者匹配的内容并不保存到最终的匹配结果中,那么就认为这个子表达式是零宽度的。占有字符还是零宽度,是针对匹配的内容是否保存到最终的匹配结果中而言的。

      尚学堂JAVA高级学习笔记_2/2_第9张图片

2. 正则表达式的练习

  • 练习1

    尚学堂JAVA高级学习笔记_2/2_第10张图片

  • 解答1:(0\d{2,3}-\d{7,8})|(1[35789]\d{9})

  • 练习2

    尚学堂JAVA高级学习笔记_2/2_第11张图片

  • 解答2:[\w\-]+@[a-z0-9A-Z]+(\.[A-Za-z]{2,4}){1,2}

3. 常用正则表达式列表

尚学堂JAVA高级学习笔记_2/2_第12张图片

4. JAVA程序中使用正则表达式

  • 开发环境

    • 开发环境和文本编译器中使用正则
    • 数据库中也可以使用正则
  • JAVA相关类位于java.util.regex包下面

  • 类Pattern:

    • 正则表达式的编译表现形式
    • 建立正则表达式,并启用相应模式
    • Pattern p = Pattern.compile(r, int);
  • 类Matcher:

    • 通过解释Pattern对character sequence执行匹配操作的引擎
    • 匹配str字符串
    • Matcher m = p.matcher(str);
  • 测试

    • 匹配整个正则表达式

      package com.sxt.regex;
      
      import java.util.regex.Matcher;
      import java.util.regex.Pattern;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: Demo01.java
       * @time: 2020/3/3 14:10
       * @desc: |
       */
      
      public class Demo01 {
          public static void main(String[] args){
              // 在这个字符串:asdfsadf2323,是否符合制定的正则表达式:\w+
              Pattern p = Pattern.compile("\\w+");
              // 创建Matcher对象
              Matcher m = p.matcher("asdfsadf@@2323");
              // 尝试将整个字符序列与该模式匹配
              // boolean yo = m.matches();
              // 该方法扫描输入的序列,查找与该模式匹配的下一个子序列
              while(m.find()){
                  // group()和group(0)都是匹配整个表达式的子字符串
                  System.out.println(m.group());
                  System.out.println(m.group(0));
              }
          }
      }
      
    • 测试正则表达式分组的处理

      package com.sxt.regex;
      
      import java.util.regex.Matcher;
      import java.util.regex.Pattern;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @desc: | 测试正则表达式分组的处理
       */
      
      public class Demo02 {
          public static void main(String[] args) {
              // 在这个字符串:asdfsadf2323,是否符合制定的正则表达式:\w+
              Pattern p = Pattern.compile("([a-z]+)([0-9]+)");
              // 创建Matcher对象
              Matcher m = p.matcher("asdfsa12**asd233**dsd11");
              // 尝试将整个字符序列与该模式匹配
              while (m.find()) {
                  // group()和group(0)都是匹配整个表达式的子字符串
                  System.out.println("start---");
                  System.out.println("满足整个表达式的子字符串:");
                  System.out.println(m.group());
                  System.out.println("满足第1个括号中表达式的字符串:");
                  System.out.println(m.group(1));
                  System.out.println("满足第2个括号中表达式的字符串:");
                  System.out.println(m.group(2));
              }
          }
      }
      
    • 测试正则表达对象替换操作

      package com.sxt.regex;
      
      import java.util.regex.Matcher;
      import java.util.regex.Pattern;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @desc: | 测试正则表达对象替换操作
       */
      
      public class Demo03 {
          public static void main(String[] args) {
              // 在这个字符串:asdfsadf2323,是否符合制定的正则表达式:\w+
              Pattern p = Pattern.compile("[0-9]");
              // 创建Matcher对象
              Matcher m = p.matcher("asdfsa12**asd233**dsd11");
      
              // 替换
              String newStr = m.replaceAll("#");
              System.out.println(newStr);
          }
      }
      
    • 测试正则表达对象分割字符串的操作

      package com.sxt.regex;
      
      import java.util.Arrays;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @desc: | 测试正则表达对象分割字符串的操作
       */
      
      public class Demo04 {
          public static void main(String[] args) {
              String str = "asdfsa12asd233dsd11";
      
              // 切割
              String[] arrs = str.split("\\d+");
              System.out.println(Arrays.toString(arrs));
          }
      }
      

5. 手写网络爬虫

package com.sxt.regex;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author: Li Tian
 * @contact: [email protected]
 * @software: IntelliJ IDEA
 * @file: WebSpider.java
 * @time: 2020/3/4 17:29
 * @desc: |网络爬虫取数据
 */

public class WebSpider {
    public static void main(String[] args) {
        String url = "http://www.163.com";
        String destStr = getURLContent(url);

        // 取到的超链接的整个内容
        // Pattern p = Pattern.compile("");
        // 取到的超链接的地址
        // Pattern p = Pattern.compile("href=\"(.+?)\"");
        // 注意:上述?是非贪婪模式
        // Matcher m = p.matcher(destStr);
        // while (m.find()) {
        //     System.out.println(m.group());
        //     System.out.println("-----");
        //     System.out.println(m.group(1));
        // }

        List<String> result = getMatherSubstrs(destStr, "href=\"(http://[\\w\\s./]+?)\"");
        for (String temp : result) {
            System.out.println(temp);
        }
    }

    public static String getURLContent(String loc) {
        /*获得url对应的网页源码内容*/
        StringBuilder sb = new StringBuilder();
        try {
            URL url = new URL(loc);
            BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), Charset.forName("gbk")));
            String temp = "";
            while ((temp = reader.readLine()) != null) {
                sb.append(temp);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    public static List<String> getMatherSubstrs(String destStr, String regexStr) {
        // 取到的超链接地址
        Pattern p = Pattern.compile(regexStr);
        Matcher m = p.matcher(destStr);
        List<String> result = new ArrayList<>();
        while (m.find()) {
            result.add(m.group(1));
        }
        return result;
    }
}

第5章 JDBC

1. Mysql引入

  • 常用命令行操作

    尚学堂JAVA高级学习笔记_2/2_第13张图片

2. JDBC简述

  • JDBC(Java Database Connection)为java开发者使用数据库提供了统一的编程接口,它由一组java类和接口组成。是java程序与数据库系统通信的标准api。JDBC API使得开发人员可以使用纯java的方式来连接数据库,并执行操作。

  • 访问数据库流程

    尚学堂JAVA高级学习笔记_2/2_第14张图片

  • Driver接口

    • Driver接口由数据库厂家提供,对于java开发者而言,只需要使用Driver接口就可以了。
    • 在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序。不同的数据库有不同的装载方法。
    • 驱动:就是各个数据库厂商实现的Sun公司提出的JDBC接口。即对Connection等接口的实现类的jar文件。
    • 装载MySQL驱动
      • Class.forName("com.mysql.jdbc.Driver");
    • 装载Oracle驱动
      • Class.forName("oracle.jdbc.driver.OracleDriver")
  • DriverManager接口

    • DriverManager是JDBC的管理层,作用于用户和驱动程序之间。
    • DriverManager跟踪可用的驱动程序,并在数据库和相应的驱动程序之间建立连接。
  • Connection接口

    • Connection与特定数据库的连接(会话),在连接上下文中执行SQL语句并返回结果。
    • DriverManager的getConnection()方法建立在JDBC URL中定义的数据库Connection连接上。
    • 连接Mysql数据:
      • Connection con = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");
    • 连接Oracle数据库:
      • Connection con = DriverManager.gtConnection("jdbc:oracle:thin:@host:port:database", "user", "password");
  • JDBC详细操作

    • 灵活指定SQL语句中的变量:PreparedStatement
    • 对存储过程进行调用:CallableStatement
    • 运用事务处理:Transaction
    • 批处理:Batch,对于大量的批处理,建议使用Statement,因为PreparedStatement的预编译空间有限,当数据量特别大时,会发生异常。
  • 连接测试

  • 报错参考连接:解决方案

    package com.sxt.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @file: Demo01.java
     * @time: 2020/3/5 12:48
     * @desc: | 测试跟数据库建立连接
     * 如果报错:参考连接:https://www.cnblogs.com/cn-chy-com/p/10145690.html
     */
    
    public class Demo01 {
        public static void main(String[] args) {
            try {
                // 加载驱动类
                Class.forName("com.mysql.cj.jdbc.Driver");
                long start = System.currentTimeMillis();
                // 建立连接(连接对象内部其实包含了Socket对象,是一个远程的连接。比较耗时!这是Connection对象管理的一个要点!)
                // 真正开发中,为了提高效率,都会使用连接池来管理连接对象!
                Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
                long end = System.currentTimeMillis();
                System.out.println(conn);
                System.out.println("建立连接耗时:" + (end - start) + "ms毫秒");
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            }
        }
    }
    

3. JDBC常用接口

  • Statement接口

    • 用于执行静态SQL语句并返回它所生成结果的对象。
    • 三种Statement类:
      • Statement:由createStatement创建,用于发送简单的SQL语句。(不带参数的)
      • PreparedStatement:继承自Statement接口,由prepareStatement创建,用于发送含有一个或多个输入参数的sql语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入。我们一般都用PreparedStatement
      • CallableStatement:集成自PreparedStatement。由方法prePareCall创建,用于调用存储过程。
    • 常用的Statement方法:
      • execute():运行语句,返回是否有结果集。
      • executeQuery():运行select语句,返回ResultSet结果集。
      • executeUpadate():运行insert/update/delete操作,返回更新的行数。
  • Statement测试执行sql语句以及sql注入问题

    package com.sxt.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @file: Demo02.java
     * @time: 2020/3/5 12:48
     * @desc: | 测试执行sql语句以及sql注入问题
     */
    
    public class Demo02 {
        public static void main(String[] args) {
            try {
                // 加载驱动类
                Class.forName("com.mysql.cj.jdbc.Driver");
                // 建立连接(连接对象内部其实包含了Socket对象,是一个远程的连接。比较耗时!这是Connection对象管理的一个要点!)
                // 真正开发中,为了提高效率,都会使用连接池来管理连接对象!
                Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
                Statement stmt = conn.createStatement();
                String sql = "insert into t_user (username, pwd, regTime) values ('赵六', 6666, now())";
                stmt.execute(sql);
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
  • sql注入问题:若要根据id删除一行记录,很容易出现数据库危险,比如要删除id=5的记录,传入的时候为id = 5 or 1 = 1,最终导致数据库都被删除。

  • 测试PreparedStatement的用法

    package com.sxt.jdbc;
    
    import java.sql.*;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @file: Demo01.java
     * @time: 2020/3/5 12:48
     * @desc: | 测试PreparedStatement的基本用法
     */
    
    public class Demo03 {
        public static void main(String[] args) {
            try {
                // 加载驱动类
                Class.forName("com.mysql.cj.jdbc.Driver");
                Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
    
                // ?是占位符
                String sql = "insert into t_user (username, pwd, regTime) values (?, ?, ?)";
                PreparedStatement ps = conn.prepareStatement(sql);
                // 参数索引是从1开始计算,而不是0
                // ps.setString(1, "傻瓜");
                // ps.setString(2, "12345");
    
                // 还可以不管类型直接setObject
                // ps.setObject(1, "傻瓜2");
                // ps.setObject(2, "12344");
    
                // 设置时间:注意该时间的格式应该是java.sql.Date
                ps.setObject(1, "傻瓜3");
                ps.setObject(2, "12343");
                ps.setObject(3, new java.sql.Date(System.currentTimeMillis()));
    
                System.out.println("插入一行记录");
                // 返回是否有结果集
                // ps.execute();
                // 返回更新的行数
                int count = ps.executeUpdate();
                System.out.println(count);
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
  • 关闭顺序:resultset–>statement–>connection

  • 测试ResultSet结果集的用法

    package com.sxt.jdbc;
    
    import java.sql.*;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @desc: | 测试ResultSet结果集的用法
     * 记得要关闭打开的接口
     */
    
    public class Demo04 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                // 加载驱动类
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
    
                // ?是占位符
                String sql = "select id, username, pwd from t_user where id>?";
                ps = conn.prepareStatement(sql);
                // 把id>2的记录都取出来
                ps.setObject(1, 2);
                rs = ps.executeQuery();
    
                while (rs.next()) {
                    // 数字代表哪一列
                    System.out.println(rs.getInt(1) + "-->" + rs.getString(2) + "-->" + rs.getString(3));
                }
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            } finally {
                // 一定要将三个try catch分开写
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (ps != null) {
                    try {
                        ps.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
  • Batch批处理,尽量使用Statement而不是PreparedStatement

    package com.sxt.jdbc;
    
    import java.sql.*;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @desc: | 批处理
     */
    
    public class Demo05 {
        public static void main(String[] args) {
            Connection conn = null;
            Statement stmt = null;
            ResultSet rs = null;
            try {
                // 加载驱动类
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
                // 设为手动提交
                conn.setAutoCommit(false);
                long start = System.currentTimeMillis();
                stmt = conn.createStatement();
                for (int i = 0; i < 20000; i++) {
                    stmt.addBatch("insert into t_user (username, pwd, regTime) values ('li'" + ", 666666, now())");
                    stmt.executeBatch();
                }
                // 提交事务
                conn.commit();
                long  end = System.currentTimeMillis();
                System.out.println("插入20000条数据,耗时(毫秒):" + (end - start));
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            } finally {
                // 一定要将三个try catch分开写
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (stmt != null) {
                    try {
                        stmt.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

4. 事务

  • 事务的基本概念:一组要么同时执行成功,要么同时执行失败的SQL语句。是数据库操作的一个执行单元。

  • 事务开始于:

    • 连接到数据库上,并执行一条DML语句(INSERT、UPDATE或DELETE)
    • 前一个事务结束后,又输入了另外一条DML语句
  • 事务结束于:

    • 执行COMMIT或ROLLBACK语句
    • 执行一条DDL语句,如CREATE TABLE语句;这种情况下,会自动执行COMMIT语句
    • 执行一条DCL语句,例如GRANT语句;这种情况下,会自动执行COMMIT语句
    • 断开与数据库的连接
    • 执行了一条DML语句,该语句却失败了;在这种情况下,会为这个无效的DML语句执行ROLLBACK语句
  • 事务的四大特性(ACID)

    • atomicity(原子性):表示一个事务内的所有操作是一个整体,要么全部成功,要么全部失败
    • consistency(一致性):表示一个事务内有一个操作失败时,所有的更改过的数据都必须回滚到修改前的状态
    • isolation(隔离性):事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。
      • 事务隔离级别从低到高:
        • 读取未提交(Read Uncommitted)
        • 读取已提交(Read Committed)
        • 可重复读(Repeatable Read)
        • 序列化(Serializable)
    • durability(持久性):持久性事务完成之后,它对系统的影响是永久的
  • 测试事务的基本用法

    package com.sxt.jdbc;
    
    import java.sql.*;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @desc: | 测试事务的基本用法
     */
    
    public class Demo06 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps1 = null;
            PreparedStatement ps2 = null;
            try {
                // 加载驱动类
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
    
                // JDBC默认是自动提交
                conn.setAutoCommit(false);
    
                ps1 = conn.prepareStatement("insert into t_user (username, pwd) values (?, ?)");
                ps1.setObject(1, "狗子");
                ps1.setObject(2, "111");
                ps1.execute();
                System.out.println("插入一个用户1");
                Thread.sleep(6000);
    
                ps2 = conn.prepareStatement("insert into t_user (username, pwd) values (?, ?, ?)");
                ps2.setObject(1, "狗子2");
                ps2.setObject(2, "111");
                ps2.execute();
                System.out.println("插入一个用户2");
    
                conn.commit();
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 一定要将三个try catch分开写
                if (ps1 != null) {
                    try {
                        ps1.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (ps2 != null) {
                    try {
                        ps2.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

5. 时间处理

  • 时间类型

    • java.util.Date
      • 子类:java.sql.Date:表示年月日
      • 子类:java.sql.Time:表示时分秒
      • 子类:java.sql.Timestamp:表示年月日时分秒
    • 日期比较处理
      • 插入随机日期
      • 取出指定日期范围的记录
  • Date、Timestamp比较和插入随机日期

    package com.sxt.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    import java.util.Random;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @desc: | 测试时间处理(java.sql.Date, java.sql.Time, java.sql.Timestamp)
     */
    
    public class Demo07 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps1 = null;
            try {
                // 加载驱动类
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
    
                for (int i = 0; i < 1000; i++) {
                    ps1 = conn.prepareStatement("insert into t_user (username, pwd, regTime, lastLoginTime) values (?, ?, ?, ?)");
                    ps1.setObject(1, "狗子" + i);
                    ps1.setObject(2, "111");
    
                    // 定义随机数
                    int rand = 10000000 + new Random().nextInt(1000000000);
    
                    java.sql.Date date = new java.sql.Date(System.currentTimeMillis() - rand);
                    ps1.setDate(3, date);
    
                    // 如果需要插入制定日期,可以使用Calendar或DateFormat
                    java.sql.Timestamp stamp = new java.sql.Timestamp(System.currentTimeMillis());
                    ps1.setTimestamp(4, stamp);
    
                    ps1.execute();
                }
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            } finally {
                // 一定要将三个try catch分开写
                if (ps1 != null) {
                    try {
                        ps1.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
  • 取出指定日期范围的记录

    package com.sxt.jdbc;
    
    import java.sql.*;
    import java.text.DateFormat;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @desc: | 测试时间处理,取出指定时间段的数据
     */
    
    public class Demo08 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                // 加载驱动类
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
    
                // 选择满足regTime条件的记录,Timestamp格式同理,把java.sql.Date改成java.sql.Timestamp即可getDate改成getTimestamp
                ps = conn.prepareStatement("select * from t_user where regTime>? and regTime);
                java.sql.Date start = new java.sql.Date(str2Date("2020-3-1 10:23:45"));
                java.sql.Date end = new java.sql.Date(str2Date("2020-3-3 10:23:45"));
                ps.setObject(1, start);
                ps.setObject(2, end);
                rs = ps.executeQuery();
                while(rs.next()){
                    System.out.println(rs.getInt("id") + "-->" + rs.getString("username") + "-->" + rs.getDate("regTime"));
                }
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            } finally {
                // 一定要将三个try catch分开写
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (ps != null) {
                    try {
                        ps.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public static long str2Date(String dateStr){
            /*将字符串代表的日期转为long数字(格式:yyyy-MM-dd hh:mm:ss)*/
            DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            try {
                return format.parse(dateStr).getTime();
            } catch (ParseException e) {
                e.printStackTrace();
                return 0;
            }
        }
    }
    

6. 大对象操作

6.1 CLOB文本大对象操作

  • Character Large Object

  • 用于存储大量的文本数据

  • 大字段有些特殊,不同数据库处理的方式不一样,大字段的操作常常是以流的方式来处理的。而非一般的字段,一次即可读出数据。

  • Mysql中相关类型:

    • TINYTEXT最大长度为255(21-1)字符的TEXT列。
    • TEXT[(M)]最大长度为65535(22-1)字符的TEXT列。
    • MEDIUMTEXT最大长度为16777215(23-1)字符的TEXT列。
    • LONGTEXT最大长度为4294967295或4GB(24-1)字符的TEXT列。
  • 测试CLOB文本大对象的使用

  • 包含:将字符串、文件内容插入数据库中的CLOB字段,将CLOB字段值取出来操作

    package com.sxt.jdbc;
    
    import java.io.*;
    import java.sql.*;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @desc: | 测试CLOB文本大对象的使用
     * 包含:将字符串、文件内容插入数据库中的CLOB字段,将CLOB字段值取出来操作
     */
    
    public class Demo09 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                // 加载驱动类
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
    
                ps = conn.prepareStatement("insert into t_user2 (username, myInfo) values (?, ?)");
                ps.setString(1, "狗子");
                // 将文本文件的内容直接输入到数据库中
                // ps.setClob(2, new FileReader(new File("a1.txt")));
                // 通过流的操作写入字符串内容
                // ps.setClob(2, new BufferedReader(new InputStreamReader(new ByteArrayInputStream("aaaabbbb".getBytes()))));
                // ps.executeUpdate();
    
                // 读取Clob字段
                ps = conn.prepareStatement("select * from t_user2 where username=?");
                ps.setObject(1, "狗子");
                rs = ps.executeQuery();
                while (rs.next()) {
                    Clob c = rs.getClob("myInfo");
                    Reader r = c.getCharacterStream();
                    int temp = 0;
                    while((temp = r.read()) != -1){
                        System.out.print((char)temp);
                    }
                    System.out.println();
                }
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 一定要将三个try catch分开写
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (ps != null) {
                    try {
                        ps.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

6.2 BLOB二进制大对象操作

  • Binary Large Object

  • 用于存储大量的二进制数据

  • 大字段有些特殊,不同数据库处理的方式不一样,大字段的操作常常是以流的方式来处理的。而非一般的字段,一次即可独处数据。

  • Mysql中相关类型与CLOB类似,只是将CLOB改为BLOB

  • 测试BLOB二进制大对象的使用

    package com.sxt.jdbc;
    
    import java.io.*;
    import java.sql.*;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @desc: | 测试BLOB二进制大对象的使用
     */
    
    public class Demo10 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                // 加载驱动类
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
    
                // ps = conn.prepareStatement("insert into t_user2 (username, headImg) values (?, ?)");
                // ps.setString(1, "狗子2");
                // 将图片文件的内容直接输入到数据库中
                // ps.setBlob(2, new FileInputStream("test.png"));
                // ps.executeUpdate();
    
                // 读取Blob字段
                ps = conn.prepareStatement("select * from t_user2 where username=?");
                ps.setObject(1, "狗子2");
                rs = ps.executeQuery();
                while (rs.next()) {
                    Blob b = rs.getBlob("headImg");
                    InputStream is = b.getBinaryStream();
                    OutputStream os = new FileOutputStream("a1_input.png");
                    int temp = 0;
                    while((temp = is.read()) != -1){
                        os.write(temp);
                    }
                }
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 一定要将三个try catch分开写
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (ps != null) {
                    try {
                        ps.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

7. 经典JDBC代码总结

  • JDBC工具类

  • 包括返回数据库驱动连接,关闭各个接口

    package com.sxt.jdbc;
    
    import java.io.IOException;
    import java.sql.*;
    import java.util.Properties;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @file: JDBCUTIL.java
     * @time: 2020/3/8 19:43
     * @desc: |JDBC工具类
     */
    
    public class JDBCUtil {
        // 可以帮助我们读取和处理资源文件中的信息
        private static Properties pros = null;
        static {
            /*静态代码块:只有在加载JDBCUtil类的时候调用一次*/
            pros = new Properties();
            try {
                pros.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/sxt/jdbc/db.properties"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static Connection getMysqlConn() {
            /*获取数据库(mysql)驱动连接*/
            // 加载驱动类
            try {
                Class.forName(pros.getProperty("mysqlDriver"));
                return DriverManager.getConnection(
                        pros.getProperty("mysqlURL"),
                        pros.getProperty("mysqlUser"),
                        pros.getProperty("mysqlPwd"));
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        public static Connection getOracleConn() {
            /*获取数据库(oracle)驱动连接*/
            // 加载驱动类
            try {
                Class.forName(pros.getProperty("oracleDriver"));
                return DriverManager.getConnection(
                        pros.getProperty("oracleURL"),
                        pros.getProperty("oracleUser"),
                        pros.getProperty("oraclePwd"));
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        public static void close(ResultSet rs, Statement ps, Connection conn){
            /*关闭接口方法*/
            try {
                if (rs != null){
                    rs.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                if (ps != null){
                    ps.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                if (conn != null){
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public static void close(Statement ps, Connection conn){
            /*关闭接口方法,重载*/
            try {
                if (ps != null){
                    ps.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                if (conn != null){
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public static void close(Connection conn){
            /*关闭接口方法,重载*/
            try {
                if (conn != null){
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
  • 配置文件封装

    mysqlURL=jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC
    #mysqlURL=jdbc:mysql://localhost:3306/sorm?serverTimezone=UTC
    mysqlDriver=com.mysql.cj.jdbc.Driver
    mysqlUser=root
    mysqlPwd=123456
    
    oracleDriver=oracle.jdbc.driver.OracleDriver
    oracleURL=jdbc:oracle:thin:@localhost:1521:database
    oracleUser=scott
    oraclePwd=tiger
    
  • 测试使用JDBCUtil工具类来简化JDBC开发

    package com.sxt.jdbc;
    
    import java.sql.*;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @desc: | 测试使用JDBCUtil工具类来简化JDBC开发
     */
    
    public class Demo11 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
    
            try {
                conn = JDBCUtil.getMysqlConn();
                ps = conn.prepareStatement("insert into t_user (username) values (?)");
                ps.setString(1, "hehe");
                ps.execute();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(rs, ps, conn);
            }
        }
    }
    

8. ORM原理

  • ORM的基本思想

    • Object Relationship Mapping:对象关系映射
    • 表结构跟类对应;表中字段和类的属性对应;表中记录和对象对应。
    • 让javabean的属性名和类型尽量和数据库保持一致!
    • 一条记录对应一个对象。将这些查询到的对象放到容器中(List,Set,Map)
  • 将表中的一条记录封装到Object数组中

  • 将表中的一条记录封装到map中

  • 将表中的一条记录封装到javabean对象中

  • 测试使用Object数组来封装一条记录,使用List存储多条记录

    package com.sxt.testORM;
    
    import com.sxt.jdbc.JDBCUtil;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @file: Demo01.java
     * @time: 2020/3/10 13:29
     * @desc: |测试使用Object数组来封装一条记录
     * 使用List存储多条记录
     */
    
    public class Demo01 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            List<Object[]> list = new ArrayList<>();
            try {
                conn = JDBCUtil.getMysqlConn();
                ps = conn.prepareStatement("select empname, salary, age from emp where id > ?");
                ps.setObject(1, 0);
                rs = ps.executeQuery();
                while (rs.next()) {
                    // System.out.println(rs.getString(1) + "-->" + rs.getDouble(2) + "-->" + rs.getInt(3));
                    Object[] objs = new Object[3];
                    objs[0] = rs.getObject(1);
                    objs[1] = rs.getObject(2);
                    objs[2] = rs.getObject(3);
    
                    list.add(objs);
                }
    
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(rs, ps, conn);
            }
    
            for (Object[] objs : list) {
                System.out.println(objs[0] + "-->" + objs[1] + "-->" + objs[2]);
            }
        }
    }
    
  • 测试使用Map来封装一条记录,使用List存储多条记录(也可用Map

    package com.sxt.testORM;
    
    import com.sxt.jdbc.JDBCUtil;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @desc: |测试使用Map来封装一条记录
     * 使用List存储多条记录(也可用Map)
     */
    
    public class Demo02 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            // 使用一个Map封装一条记录
            List<Map<String, Object>> list = new ArrayList<>();
            try {
                conn = JDBCUtil.getMysqlConn();
                ps = conn.prepareStatement("select empname, salary, age from emp where id > ?");
                ps.setObject(1, 0);
                rs = ps.executeQuery();
                while (rs.next()) {
                    // System.out.println(rs.getString(1) + "-->" + rs.getDouble(2) + "-->" + rs.getInt(3));
                    Map<String, Object> row = new HashMap<>();
                    row.put("empname", rs.getString(1));
                    row.put("salary", rs.getString(2));
                    row.put("age", rs.getString(3));
                    list.add(row);
                }
    
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(rs, ps, conn);
            }
    
            // 遍历List和Map
            for (Map<String, Object> row : list) {
                for (String key : row.keySet()) {
                    System.out.print(key + "-->" + row.get(key) + "\t\t");
                }
                System.out.println();
            }
        }
    }
    
  • 使用Javabean对象来封装一条记录,使用List存储多条记录

    package com.sxt.testORM;
    
    import com.sxt.jdbc.JDBCUtil;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @desc: |使用Javabean对象来封装一条记录
     * 使用List存储多条记录
     */
    
    public class Demo03 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            List<Emp> list = new ArrayList<>();
            try {
                conn = JDBCUtil.getMysqlConn();
                ps = conn.prepareStatement("select empname, salary, age from emp where id > ?");
                ps.setObject(1, 0);
                rs = ps.executeQuery();
                while (rs.next()) {
                    // System.out.println(rs.getString(1) + "-->" + rs.getDouble(2) + "-->" + rs.getInt(3));
                    Emp emp = new Emp(rs.getString(1), rs.getInt(2), rs.getDouble(3));
                    list.add(emp);
                }
    
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(rs, ps, conn);
            }
    
            for (Emp e: list) {
                System.out.println(e);
            }
        }
    }
    
  • 其中需要为每一个表定义相同结构的类

    • Emp

      package com.sxt.testORM;
      
      import java.sql.Date;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: Emp.java
       * @time: 2020/3/10 14:35
       * @desc: |表结构和类对应
       */
      
      public class Emp {
          private Integer id;
          private String empname;
          private Integer age;
          private Double salary;
          private Date birthday;
          private Integer deptId;
      
          public Emp(String empname, Integer age, Double salary) {
              this.empname = empname;
              this.age = age;
              this.salary = salary;
          }
      
          public Integer getId() {
              return id;
          }
      
          public void setId(Integer id) {
              this.id = id;
          }
      
          public String getEmpname() {
              return empname;
          }
      
          public void setEmpname(String empname) {
              this.empname = empname;
          }
      
          public Integer getAge() {
              return age;
          }
      
          public void setAge(Integer age) {
              this.age = age;
          }
      
          public Double getSalary() {
              return salary;
          }
      
          public void setSalary(Double salary) {
              this.salary = salary;
          }
      
          public Date getBirthday() {
              return birthday;
          }
      
          public void setBirthday(Date birthday) {
              this.birthday = birthday;
          }
      
          public Integer getDeptId() {
              return deptId;
          }
      
          public void setDeptId(Integer deptId) {
              this.deptId = deptId;
          }
      
          public Emp(String empname, Integer age, Double salary, Date birthday, Integer deptId) {
              this.empname = empname;
              this.age = age;
              this.salary = salary;
              this.birthday = birthday;
              this.deptId = deptId;
          }
      
          public Emp(Integer id, String empname, Integer age, Double salary, Date birthday, Integer deptId) {
              this.id = id;
              this.empname = empname;
              this.age = age;
              this.salary = salary;
              this.birthday = birthday;
              this.deptId = deptId;
          }
      
          public Emp() {
          }
      
          @Override
          public String toString() {
              return empname + "-->" + age + "-->" + salary;
          }
      }
      
    • Dept

      package com.sxt.testORM;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: Dept.java
       * @time: 2020/3/10 14:38
       * @desc: |
       */
      
      public class Dept {
          private Integer id;
          private String dname;
          private String address;
      
          public Dept() {
          }
      
          public Dept(String dname, String address) {
              this.dname = dname;
              this.address = address;
          }
      
          public Dept(Integer id, String dname, String address) {
              this.id = id;
              this.dname = dname;
              this.address = address;
          }
      
          public Integer getId() {
              return id;
          }
      
          public void setId(Integer id) {
              this.id = id;
          }
      
          public String getDname() {
              return dname;
          }
      
          public void setDname(String dname) {
              this.dname = dname;
          }
      
          public String getAddress() {
              return address;
          }
      
          public void setAddress(String address) {
              this.address = address;
          }
      }
      

9. 手写SORM框架

  • Simple Object Relationship Mapping

  • 我们希望设计一个可以实现对象和SQL自动映射的框架,但是整体用法和设计比Hibernate简单。砍掉不必要的功能。

  • 会穿插使用设计模式

  • 从对象到sql

    • 增加:将对象对应成sql语句,执行sql,插入数据库中
    • 删除:根据对象主键的值,生成sql,执行,从库中删除
    • 修改:根据对象需要修改属性的值,生成sql,执行
  • 从sql到对象

    • 查询:根据结果分类
      • 多行多列:List
      • 一行多列:Javabean
      • 一行一列:普通对象:Object,数字:Number
  • 核心架构:

    • Query接口:负责查询(对外提供服务的核心类)
    • QueryFactory类:负责根据配置信息创建query对象
    • TypeConvertor类:负责类型转换
    • TableContext类:负责获取管理数据库所有表结构和类结构的关系,并可以根据表结构生成类结结构。
    • DBManager类:根据配置信息,维持连接对象的管理(增加连接池功能)
    • 工具类:
      • JDBCUtils封装常用JDBC操作
      • StringUtils封装常用字符串操作
      • JavaFileUtils封装Java文件操作
      • ReflectUtils封装常用反射操作
  • 架构图

    尚学堂JAVA高级学习笔记_2/2_第15张图片

  • 核心bean,封装相关数据

    • ColumnInfo:封装表中一个字段的信息(字段类型、字段名、键类型)
    • Configuration:封装配置文件信息
    • TableInfo:封装一张表的信息
  • 针对SORM框架的说明:

    • 核心思想:使用简单、性能高、极易上手!
    • 配置文件:目前使用资源文件、后期项目复杂后可以增加XML文件配置和注解
    • 类名由表名生成,只有首字母大写有区别,其他无区别
    • Java对象的属性由表中字段生成,完全对应
    • 目前,只支持表中只有一个主键,联合主键不支持

9.1 第一版SORM

  • bean

    • ColumnInfo

      package com.sxt.SORM.bean;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: ColumnInfo.java
       * @time: 2020/3/11 13:46
       * @desc: |封装表中一个字段的信息
       */
      
      public class ColumnInfo {
          // 字段名称
          private String name;
          // 字段数据类型
          private String dataType;
          // 字段的键类型(0普通键;1主键;2外键)
          private int keyType;
      
          public ColumnInfo() {
          }
      
          public ColumnInfo(String name, String dataType, int keyType) {
              this.name = name;
              this.dataType = dataType;
              this.keyType = keyType;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public String getDataType() {
              return dataType;
          }
      
          public void setDataType(String dataType) {
              this.dataType = dataType;
          }
      
          public int getKeyType() {
              return keyType;
          }
      
          public void setKeyType(int keyType) {
              this.keyType = keyType;
          }
      }
      
    • Configuration

      package com.sxt.SORM.bean;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: Configuration.java
       * @time: 2020/3/11 13:55
       * @desc: |管理配置信息
       */
      
      public class Configuration {
          // 正在使用哪个数据库
          private String usingDb;
          // jdbc的url
          private String URL;
          // 驱动类
          private String driver;
          // 数据库的用户名
          private String user;
          // 数据库的密码
          private String pwd;
          // 项目的源码路径
          private String srcPath;
          // 扫描生成java类的包(po的意思是Persistence Object持久化对象)
          private String poPackage;
      
          public Configuration() {
          }
      
          public Configuration(String usingDb, String URL, String driver, String user, String pwd, String srcPath, String poPackage) {
              this.usingDb = usingDb;
              this.URL = URL;
              this.driver = driver;
              this.user = user;
              this.pwd = pwd;
              this.srcPath = srcPath;
              this.poPackage = poPackage;
          }
      
          public String getUsingDb() {
              return usingDb;
          }
      
          public void setUsingDb(String usingDb) {
              this.usingDb = usingDb;
          }
      
          public String getURL() {
              return URL;
          }
      
          public void setURL(String URL) {
              this.URL = URL;
          }
      
          public String getDriver() {
              return driver;
          }
      
          public void setDriver(String driver) {
              this.driver = driver;
          }
      
          public String getUser() {
              return user;
          }
      
          public void setUser(String user) {
              this.user = user;
          }
      
          public String getPwd() {
              return pwd;
          }
      
          public void setPwd(String pwd) {
              this.pwd = pwd;
          }
      
          public String getSrcPath() {
              return srcPath;
          }
      
          public void setSrcPath(String srcPath) {
              this.srcPath = srcPath;
          }
      
          public String getPoPackage() {
              return poPackage;
          }
      
          public void setPoPackage(String poPackage) {
              this.poPackage = poPackage;
          }
      }
      
    • JavaFieldGetSet

      package com.sxt.SORM.bean;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: JavaFieldGetSet.java
       * @time: 2020/3/11 18:24
       * @desc: |封装了java属性和get、set方法的源代码
       */
      
      public class JavaFieldGetSet {
          // 属性源码信息。如:private int userId;
          private String fieldInfo;
          // get方法的源码信息。如:public int getUserId;
          private String getInfo;
          // set方法的源码信息。如:public void setUserId(int id){this.id = id;}
          private String setInfo;
      
          public JavaFieldGetSet() {
          }
      
          public JavaFieldGetSet(String fieldInfo, String getInfo, String setInfo) {
              this.fieldInfo = fieldInfo;
              this.getInfo = getInfo;
              this.setInfo = setInfo;
          }
      
          public String getFieldInfo() {
              return fieldInfo;
          }
      
          public void setFieldInfo(String fieldInfo) {
              this.fieldInfo = fieldInfo;
          }
      
          public String getGetInfo() {
              return getInfo;
          }
      
          public void setGetInfo(String getInfo) {
              this.getInfo = getInfo;
          }
      
          public String getSetInfo() {
              return setInfo;
          }
      
          public void setSetInfo(String setInfo) {
              this.setInfo = setInfo;
          }
      
          @Override
          public String toString() {
              // System.out.println(fieldInfo);
              // System.out.println(getInfo);
              // System.out.println(setInfo);
              return super.toString();
          }
      }
      
    • TableInfo

      package com.sxt.SORM.bean;
      
      import java.util.List;
      import java.util.Map;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: TableInfo.java
       * @time: 2020/3/11 13:56
       * @desc: |存储表结构信息
       */
      
      public class TableInfo {
          // 表名
          private String tname;
          // 所有字段的信息
          private Map<String, ColumnInfo> columns;
          // 唯一主键(目前只能处理表中有且只有一个的情况)
          private ColumnInfo onlyPriKey;
          // 如果联合主键,则在这里存储
          private List<ColumnInfo> priKeys;
      
          public TableInfo() {
          }
      
          public TableInfo(String tname, List<ColumnInfo> priKeys, Map<String, ColumnInfo> columns) {
              this.tname = tname;
              this.columns = columns;
              this.priKeys = priKeys;
          }
      
          public String getTname() {
              return tname;
          }
      
          public void setTname(String tname) {
              this.tname = tname;
          }
      
          public Map<String, ColumnInfo> getColumns() {
              return columns;
          }
      
          public void setColumns(Map<String, ColumnInfo> columns) {
              this.columns = columns;
          }
      
          public ColumnInfo getOnlyPriKey() {
              return onlyPriKey;
          }
      
          public void setOnlyPriKey(ColumnInfo onlyPriKey) {
              this.onlyPriKey = onlyPriKey;
          }
      
          public List<ColumnInfo> getPriKeys() {
              return priKeys;
          }
      
          public void setPriKeys(List<ColumnInfo> priKeys) {
              this.priKeys = priKeys;
          }
      }
      
  • core

    • Query

      package com.sxt.SORM.core;
      
      import java.util.List;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: Query.java
       * @time: 2020/3/10 17:31
       * @desc: |负责查询(对外提供服务的核心类)
       */
      
      public interface Query {
      
          /**
           * 直接执行一个DML语句
           *
           * @param sql    sql语句
           * @param params 参数
           * @return 执行sql语句后影响记录的行数
           */
          public int executeDML(String sql, Object[] params);
      
          /**
           * 将一个对象存储到数据库中
           *
           * @param obj 要存储的对象
           */
          public void insert(Object obj);
      
          /**
           * 删除clazz表示类对应的表中的记录(指定主键id的记录)
           * 把对象中不为null的属性往数据库中存储!如果数字为null则放0
           * @param clazz 跟表对应的类的Class对象
           * @param id    主键的值
           */
          // delete from User where id = 2;
          public void delete(Class clazz, Object id);
      
          /**
           * 删除对象在数据库中对应的记录(对象所在类对应到表,对象的主键对应到的记录)
           *
           * @param obj
           */
          public void delete(Object obj);
      
          /**
           * 更新对象对应的记录,并且只更新指定的字段的值
           *
           * @param obj        索要更新的对象
           * @param fieldNames 更新的属性列表
           * @return 执行sql语句后影响记录的行数
           */
          // update user set uname=?, pwe=?
          public int update(Object obj, String[] fieldNames);
      
          /**
           * 查询返回多行记录,并将每行记录封装到clazz指定的类的对象中
           *
           * @param sql    查询语句
           * @param clazz  封装数据的javabean类的Class对象
           * @param params sql的参数
           * @return 返回查询到的结果
           */
          public List queryRows(String sql, Class clazz, Object[] params);
      
          /**
           * 查询返回一行记录,并将该记录封装到clazz指定的类的对象中
           *
           * @param sql    查询语句
           * @param clazz  封装数据的javabean类的Class对象
           * @param params sql的参数
           * @return 返回查询到的结果
           */
          public Object queryUniqueRows(String sql, Class clazz, Object[] params);
      
          /**
           * 查询返回一个值(一行一列),并将该值返回
           *
           * @param sql    查询语句
           * @param params sql的参数
           * @return 返回查询到的结果
           */
          public Object queryValue(String sql, Object[] params);
      
          /**
           * 查询返回一个数字(一行一列),并将该值返回
           *
           * @param sql    查询语句
           * @param params sql的参数
           * @return 返回查询到的数字
           */
          public Number queryNumber(String sql, Object[] params);
      }
      
    • MysqlQuery

      package com.sxt.SORM.core;
      
      import com.sxt.SORM.bean.ColumnInfo;
      import com.sxt.SORM.bean.TableInfo;
      import com.sxt.SORM.po.Emp;
      import com.sxt.SORM.utils.JDBCUtils;
      import com.sxt.SORM.utils.ReflectUtils;
      import com.sxt.SORM.vo.EmpVO;
      
      import java.lang.reflect.Field;
      import java.sql.*;
      import java.util.ArrayList;
      import java.util.List;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: MysqlQuery.java
       * @time: 2020/3/13 16:54
       * @desc: |负责针对mysql数据库的查询
       */
      
      public class MysqlQuery implements Query {
      
          public static void main(String[] args) {
              Object obj = new MysqlQuery().queryValue("select count(*) from emp where salary>?", new Object[]{1000});
              System.out.println(obj);
          }
      
          /**
           * 复杂多行查询测试
           */
          public static void testQueryRows() {
              List<Emp> list = new MysqlQuery().queryRows("select id,empname,age from emp where age>? and salary, Emp.class,
                      new Object[]{1, 9000});
              for (Emp e : list) {
                  System.out.println(e.getEmpname());
              }
      
              String sql2 = "select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e"
                      + " " + "join dept d on e.deptId=d.id;";
              List<EmpVO> list2 = new MysqlQuery().queryRows(sql2, EmpVO.class, null);
              for (EmpVO e : list2) {
                  System.out.println(e.getEmpname() + "-" + e.getDeptAddr() + "-" + e.getXinshui());
              }
          }
      
          /**
           * 增删改操作测试
           */
          public static void testDML() {
              Emp e = new Emp();
              e.setEmpname("Tom");
              e.setBirthday(new java.sql.Date(System.currentTimeMillis()));
              e.setAge(30);
              e.setSalary(8888.0);
              e.setId(1);
      
              // new MysqlQuery().delete(e);
              // new MysqlQuery().insert(e);
              new MysqlQuery().update(e, new String[]{"empname", "age", "salary"});
          }
      
          @Override
          public int executeDML(String sql, Object[] params) {
              Connection conn = DBManager.getConn();
              int count = 0;
              PreparedStatement ps = null;
              try {
                  ps = conn.prepareStatement(sql);
                  // 给sql设置参数,就是?位置的参数
                  JDBCUtils.handleParams(ps, params);
                  count = ps.executeUpdate();
              } catch (SQLException e) {
                  e.printStackTrace();
              } finally {
                  DBManager.close(ps, conn);
              }
              return count;
          }
      
          @Override
          public void insert(Object obj) {
              // obj --> 表中。 insert into 表名(id, name, pwd) values (?, ?, ?)
              Class c = obj.getClass();
              // 存储sql的参数对象
              List<Object> params = new ArrayList<>();
              TableInfo tableInfo = TableContext.poClassTableMap.get(c);
              StringBuilder sql = new StringBuilder("insert into " + tableInfo.getTname() + " (");
      
              // 计算不为空的属性值
              int countNotNullField = 0;
      
              // 目前只能处理数据库来维护自增的方式
              Field[] fs = c.getDeclaredFields();
              for (Field f : fs) {
                  String fieldName = f.getName();
                  Object fieldValue = ReflectUtils.invokeGet(fieldName, obj);
                  if (fieldValue != null) {
                      // 如果该属性值不为空
                      countNotNullField++;
                      sql.append(fieldName + ",");
                      params.add(fieldValue);
                  }
              }
      
              // 把最后一个属性后面的,换成)
              sql.setCharAt(sql.length() - 1, ')');
              sql.append(" values (");
      
              for (int i = 0; i < countNotNullField; i++) {
                  sql.append("?,");
              }
              sql.setCharAt(sql.length() - 1, ')');
      
              executeDML(sql.toString(), params.toArray());
          }
      
          @Override
          public void delete(Class clazz, Object id) {
              // Emp.class, 2 --> delete from emp where id=2
              // 通过Class对象找TableInfo
              TableInfo tableInfo = TableContext.poClassTableMap.get(clazz);
              // 获得主键
              ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
      
              String sql = "delete from " + tableInfo.getTname() + " where " + onlyPriKey.getName() + "=?;";
              executeDML(sql, new Object[]{id});
          }
      
          @Override
          public void delete(Object obj) {
              Class c = obj.getClass();
              TableInfo tableInfo = TableContext.poClassTableMap.get(c);
              // 获得主键
              ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
              // 通过反射机制,调用属性对应的get方法或set方法
              Object priKeyValue = ReflectUtils.invokeGet(onlyPriKey.getName(), obj);
              delete(obj.getClass(), priKeyValue);
          }
      
          @Override
          public int update(Object obj, String[] fieldNames) {
              // obj{"uname", "pwd} --> update 表名 set uname=?, pwd=? where id=?
              Class c = obj.getClass();
              List<Object> params = new ArrayList<>();
              TableInfo tableInfo = TableContext.poClassTableMap.get(c);
              ColumnInfo priKey = tableInfo.getOnlyPriKey();
              StringBuilder sql = new StringBuilder("update " + tableInfo.getTname() + " set ");
      
              for (String fname : fieldNames) {
                  Object fvalue = ReflectUtils.invokeGet(fname, obj);
                  params.add(fvalue);
                  sql.append(fname + "=?,");
              }
              sql.setCharAt(sql.length() - 1, ' ');
              sql.append(" where ");
              sql.append(priKey.getName() + "=?");
              params.add(ReflectUtils.invokeGet(priKey.getName(), obj));
      
              return executeDML(sql.toString(), params.toArray());
          }
      
          @Override
          public List queryRows(String sql, Class clazz, Object[] params) {
              Connection conn = DBManager.getConn();
              // 存放查询结果的容器
              List list = null;
              PreparedStatement ps = null;
              ResultSet rs = null;
              try {
                  ps = conn.prepareStatement(sql);
                  // 给sql设置参数,就是?位置的参数
                  JDBCUtils.handleParams(ps, params);
                  rs = ps.executeQuery();
                  ResultSetMetaData metaData = rs.getMetaData();
                  // 多行
                  while (rs.next()) {
                      if (list == null) {
                          list = new ArrayList();
                      }
                      // 调用javabean的无参构造器
                      Object rowObj = clazz.newInstance();
                      // 多列 select username, pwd, age from user where id>? and age>?
                      for (int i = 0; i < metaData.getColumnCount(); i++) {
                          // username
                          String columnName = metaData.getColumnLabel(i + 1);
                          Object columnValue = rs.getObject(i + 1);
      
                          // 调用rowObj对象的setUsername(String uname)方法,将columnValue的值设置进去
                          ReflectUtils.invokeSet(rowObj, columnName, columnValue);
                      }
                      list.add(rowObj);
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  DBManager.close(ps, conn);
              }
              return list;
          }
      
          @Override
          public Object queryUniqueRows(String sql, Class clazz, Object[] params) {
              List list = queryRows(sql, clazz, params);
              return (list == null && list.size() > 0) ? null : list.get(0);
          }
      
          @Override
          public Object queryValue(String sql, Object[] params) {
              Connection conn = DBManager.getConn();
              // 存储查询结果的对象
              Object value = null;
              PreparedStatement ps = null;
              ResultSet rs = null;
              try {
                  ps = conn.prepareStatement(sql);
                  // 给sql设置参数,就是?位置的参数
                  JDBCUtils.handleParams(ps, params);
                  rs = ps.executeQuery();
                  // 多行
                  while (rs.next()) {
                      // select count(*) from user
                      value = rs.getObject(1);
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  DBManager.close(ps, conn);
              }
              return value;
          }
      
          @Override
          public Number queryNumber(String sql, Object[] params) {
              return (Number) queryValue(sql, params);
          }
      }
      
    • TypeConvertor

      package com.sxt.SORM.core;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: TypeConvertor.java
       * @time: 2020/3/11 13:39
       * @desc: |负责java数据类型和数据库数据类型的互相转换
       */
      
      public interface TypeConvertor {
          /**
           * 将数据库数据类型转化成java的数据类型
           * @param columnType 数据库字段的数据类型
           * @return java的数据类型
           */
          public String databaseType2JavaType(String columnType);
      
          /**
           * 将java数据类型转化为数据库数据类型
           * @param javaDataType java数据类型
           * @return 数据库数据类型
           */
          public String javaType2DatabaseType(String javaDataType);
      }
      
    • MySqlTypeConvertor

      package com.sxt.SORM.core;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: MySqlTypeConvertor.java
       * @time: 2020/3/11 18:16
       * @desc: |mysql数据类型和java数据类型的转换
       */
      
      public class MySqlTypeConvertor implements TypeConvertor {
          @Override
          public String databaseType2JavaType(String columnType) {
              // varchar --> String
              if ("varchar".equalsIgnoreCase(columnType) || "char".equalsIgnoreCase(columnType)) {
                  return "String";
              } else if ("int".equalsIgnoreCase(columnType)
                      || "tinyint".equalsIgnoreCase(columnType)
                      || "smallint".equalsIgnoreCase(columnType)
                      || "integer".equalsIgnoreCase(columnType)) {
                  return "Integer";
              } else if ("bigint".equalsIgnoreCase(columnType)) {
                  return "long";
              } else if ("double".equalsIgnoreCase(columnType) || "float".equalsIgnoreCase(columnType)) {
                  return "Double";
              } else if ("clob".equalsIgnoreCase(columnType)) {
                  return "java.sql.Clob";
              } else if ("blob".equalsIgnoreCase(columnType)) {
                  return "java.sql.Blob";
              }else if("date".equalsIgnoreCase(columnType)){
                  return "java.sql.Date";
              }else if("time".equalsIgnoreCase(columnType)){
                  return "java.sql.Time";
              }else if("timestamp".equalsIgnoreCase(columnType)){
                  return "java.sql.Timestamp";
              }
              return null;
          }
      
          @Override
          public String javaType2DatabaseType(String javaDataType) {
              return null;
          }
      }
      
    • DBManager

      package com.sxt.SORM.core;
      
      import com.sxt.SORM.bean.Configuration;
      
      import java.io.IOException;
      import java.sql.Connection;
      import java.sql.DriverManager;
      import java.sql.ResultSet;
      import java.sql.Statement;
      import java.util.Properties;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: DBManager.java
       * @time: 2020/3/11 13:43
       * @desc: |根据配置信息,维持连接对象的管理(增加连接池功能)
       */
      
      public class DBManager {
          private static Configuration conf;
      
          static {
              // 静态代码块
              Properties pros = new Properties();
              try {
                  pros.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/sxt/SORM/db.properties"));
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
              conf = new Configuration();
              conf.setDriver(pros.getProperty("driver"));
              conf.setPoPackage(pros.getProperty("poPackage"));
              conf.setPwd(pros.getProperty("pwd"));
              conf.setSrcPath(pros.getProperty("srcPath"));
              conf.setURL(pros.getProperty("URL"));
              conf.setUser(pros.getProperty("user"));
              conf.setUsingDb(pros.getProperty("usingDB"));
          }
      
          public static Connection getConn() {
              /*获取数据库(mysql)驱动连接*/
              // 加载驱动类
              try {
                  Class.forName(conf.getDriver());
                  // 直接建立连接,后期增加连接池处理,提高效率!
                  return DriverManager.getConnection(conf.getURL(), conf.getUser(), conf.getPwd());
              } catch (Exception e) {
                  e.printStackTrace();
                  return null;
              }
          }
      
          public static void close(ResultSet rs, Statement ps, Connection conn) {
              /*关闭接口方法*/
              try {
                  if (rs != null) {
                      rs.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
              try {
                  if (ps != null) {
                      ps.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
              try {
                  if (conn != null) {
                      conn.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          public static void close(Statement ps, Connection conn) {
              /*关闭接口方法,重载*/
              try {
                  if (ps != null) {
                      ps.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
              try {
                  if (conn != null) {
                      conn.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          public static void close(Connection conn) {
              /*关闭接口方法,重载*/
              try {
                  if (conn != null) {
                      conn.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          public static Configuration getConf(){
              return conf;
          }
      }
      
    • TableContext

      package com.sxt.SORM.core;
      
      import com.sxt.SORM.bean.ColumnInfo;
      import com.sxt.SORM.bean.TableInfo;
      import com.sxt.SORM.utils.JavaFileUtils;
      import com.sxt.SORM.utils.StringUtils;
      
      import java.sql.Connection;
      import java.sql.DatabaseMetaData;
      import java.sql.ResultSet;
      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.Map;
      
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: TableContext.java
       * @time: 2020/3/11 13:42
       * @desc: |负责获取管理数据库所有表结构和类结构的关系,并可以根据表结构生成类结结构
       */
      
      public class TableContext {
          // 表名为key,表信息对象为value
          public static Map<String, TableInfo> tables = new HashMap<>();
          // 将po的calss对象和表信息对象关联起来,便于重用。
          public static Map<Class, TableInfo> poClassTableMap = new HashMap<>();
      
          private TableContext() {
          }
      
          static {
              try {
                  // 初始化获得的表信息
                  Connection conn = DBManager.getConn();
                  DatabaseMetaData dbmd = conn.getMetaData();
                  ResultSet tableSet = dbmd.getTables("", "%", "%", new String[]{"TABLE"});
      
                  while (tableSet.next()) {
                      // 循环每个表名
                      String tableName = (String) tableSet.getObject("TABLE_NAME");
                      TableInfo ti = new TableInfo(tableName, new ArrayList<ColumnInfo>(), new HashMap<String, ColumnInfo>());
                      tables.put(tableName, ti);
                      // 查询表中的所有字段
                      ResultSet set = dbmd.getColumns("", "%", tableName, "%");
                      while (set.next()) {
                          // 循环每个列名
                          ColumnInfo ci = new ColumnInfo(set.getString("COLUMN_NAME"), set.getString("TYPE_NAME"), 0);
                          ti.getColumns().put(set.getString("COLUMN_NAME"), ci);
                      }
      
                      // 查询表中的主键
                      // System.out.println(tableName);
                      ResultSet set2 = dbmd.getPrimaryKeys("", "%", tableName);
                      while (set2.next()) {
                          ColumnInfo ci2 = (ColumnInfo) ti.getColumns().get(set2.getObject("COLUMN_NAME"));
                          // 设置为主键类型
                          ci2.setKeyType(1);
                          ti.getPriKeys().add(ci2);
                      }
                      if (ti.getPriKeys().size() > 0) {
                          // 取唯一主键。方便使用。如果是联合主键。则为空!
                          ti.setOnlyPriKey(ti.getPriKeys().get(0));
                      }
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
      
              // 更新类结构
              updateJavaPOFile();
              // 加载po包下面的所有类,便于重用,提高效率!
              loadPOTables();
          }
      
          /**
           * 根据表结构,更新配置的po包下面的java类
           */
          public static void updateJavaPOFile() {
              Map<String, TableInfo> map = TableContext.tables;
              for (TableInfo t : map.values()) {
                  JavaFileUtils.createJavaPOFile(t, new MySqlTypeConvertor());
              }
          }
      
          /**
           * 加载po包下面的类
           */
          public static void loadPOTables() {
              for (TableInfo tableInfo : tables.values()) {
                  try {
                      Class c = Class.forName(DBManager.getConf().getPoPackage() + "." + StringUtils.firstChar2UpperCase(tableInfo.getTname()));
                      poClassTableMap.put(c, tableInfo);
                  } catch (ClassNotFoundException e) {
                      e.printStackTrace();
                  }
              }
          }
      
          public static void main(String[] args) {
              Map<String, TableInfo> tables = TableContext.tables;
              System.out.println(tables);
          }
      
      }
      
  • utils

    • JavaFileUtils

      package com.sxt.SORM.utils;
      
      import com.sxt.SORM.bean.ColumnInfo;
      import com.sxt.SORM.bean.JavaFieldGetSet;
      import com.sxt.SORM.bean.TableInfo;
      import com.sxt.SORM.core.DBManager;
      import com.sxt.SORM.core.MySqlTypeConvertor;
      import com.sxt.SORM.core.TableContext;
      import com.sxt.SORM.core.TypeConvertor;
      
      import java.io.*;
      import java.util.ArrayList;
      import java.util.List;
      import java.util.Map;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: JavaFileUtils.java
       * @time: 2020/3/11 13:44
       * @desc: | 封装了生成java文件(源代码)常用操作
       */
      
      public class JavaFileUtils {
          /**
           * 根据字段信息生成java属性信息。如varchar username --> private String username;以及相应的set和get方法源码
           *
           * @param column    字段信息
           * @param convertor 类型转化器
           * @return java属性和set/get方法源码
           */
          public static JavaFieldGetSet createFieldGetSetSRC(ColumnInfo column, TypeConvertor convertor) {
              JavaFieldGetSet jfgs = new JavaFieldGetSet();
              String javaFieldType = convertor.databaseType2JavaType(column.getDataType());
              jfgs.setFieldInfo("\tprivate " + javaFieldType + " " + column.getName() + ";\n");
      
              // public String getUsername(){return username;}
              StringBuilder getSrc = new StringBuilder();
              getSrc.append("\tpublic " + javaFieldType + " get" + StringUtils.firstChar2UpperCase(column.getName()) + "(){\n");
              getSrc.append("\t\treturn " + column.getName() + ";\n");
              getSrc.append("\t}\n");
              jfgs.setGetInfo(getSrc.toString());
      
              // public void setUsername(String username){this.username = username;}
              StringBuilder setSrc = new StringBuilder();
              setSrc.append("\tpublic void set" + StringUtils.firstChar2UpperCase(column.getName()) + "(");
              setSrc.append(javaFieldType + " " + column.getName() + "){\n");
              setSrc.append("\t\tthis." + column.getName() + " = " + column.getName() + ";\n");
              setSrc.append("\t}\n");
              jfgs.setSetInfo(setSrc.toString());
      
              return jfgs;
          }
      
          /**
           * 根据表信息生成java类的源代码
           *
           * @param tableInfo 表信息
           * @param convertor 数据类型转化器
           * @return java类的源代码
           */
          public static String createJavaSrc(TableInfo tableInfo, TypeConvertor convertor) {
              Map<String, ColumnInfo> columns = tableInfo.getColumns();
              List<JavaFieldGetSet> javaFields = new ArrayList<>();
      
              for (ColumnInfo c : columns.values()) {
                  javaFields.add(createFieldGetSetSRC(c, convertor));
              }
              StringBuilder src = new StringBuilder();
      
              // 生成package语句
              src.append("package " + DBManager.getConf().getPoPackage() + ";\n\n");
              // 生成import语句
              src.append("import java.sql.*;\n");
              src.append("import java.util.*;\n\n");
              // 生成类声明语句
              src.append("public class " + StringUtils.firstChar2UpperCase(tableInfo.getTname()) + " {\n\n");
              // 生成属性列表
              for (JavaFieldGetSet f : javaFields) {
                  src.append(f.getFieldInfo());
              }
              src.append("\n\n");
              // 生成set方法列表
              for (JavaFieldGetSet f : javaFields) {
                  src.append(f.getSetInfo());
              }
              // 生成get方法列表
              for (JavaFieldGetSet f : javaFields) {
                  src.append(f.getGetInfo());
              }
              // 生成类结束
              src.append("}\n");
              // System.out.println(src);
              return src.toString();
          }
      
          public static void createJavaPOFile(TableInfo tableInfo, TypeConvertor convertor) {
              String src = createJavaSrc(tableInfo, convertor);
              String srcPath = DBManager.getConf().getSrcPath() + "\\";
              String packagePath = DBManager.getConf().getPoPackage().replaceAll("\\.", "/");
              // 修正poPackage路径,因为没有重新创建项目
              String[] packagePath_list = packagePath.split("/");
              packagePath = packagePath_list[packagePath_list.length - 1];
      
              File f = new File(srcPath + packagePath);
              // System.out.println(f.getAbsolutePath());
      
              if (!f.exists()) {
                  // 指定目录不存在则帮助用户建立该目录
                  f.mkdirs();
              }
      
              BufferedWriter bw = null;
              try {
                  bw = new BufferedWriter(new FileWriter(f.getAbsolutePath() + "/" + StringUtils.firstChar2UpperCase(tableInfo.getTname()) + ".java"));
                  bw.write(src);
                  System.out.println("建立表" + tableInfo.getTname() + "对应的java类");
                  bw.flush();
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  try {
                      if (bw != null) {
                          bw.close();
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
      
          }
      
          public static void main(String[] args) {
              // 测试每一个表的field,set、get方法源码生成
              // ColumnInfo ci = new ColumnInfo("username", "int", 0);
              // JavaFieldGetSet f = createFieldGetSetSRC(ci, new MySqlTypeConvertor());
              // System.out.println(f);
              // System.out.println("\n--------------------" + "分割线" + "--------------------\n");
      
              // 测试每一个表的从头到尾完全源码生成
              // Map map = TableContext.tables;
              // TableInfo t = map.get("emp");
              // createJavaSrc(t, new MySqlTypeConvertor());
      
              Map<String, TableInfo> map = TableContext.tables;
              // TableInfo t = map.get("emp");
              for(TableInfo t: map.values()) {
                  createJavaPOFile(t, new MySqlTypeConvertor());
              }
          }
      }
      
    • JDBCUtils

      package com.sxt.SORM.utils;
      
      import java.sql.PreparedStatement;
      import java.sql.SQLException;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: JDBCUtils.java
       * @time: 2020/3/11 13:43
       * @desc: | 封装了JDBC查询常用的操作
       */
      
      public class JDBCUtils {
          /**
           * 给sql设置参数,就是?位置的参数
           * @param ps 预编译sql语句对象
           * @param params 参数
           */
          public static void handleParams(PreparedStatement ps, Object[] params) {
              if (params != null) {
                  for (int i = 0; i < params.length; i++) {
                      try {
                          ps.setObject(1 + i, params[i]);
                      } catch (SQLException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
      }
      
    • StringUtils

      package com.sxt.SORM.utils;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: StringUtils.java
       * @time: 2020/3/11 13:44
       * @desc: | 封装了字符串常用的操作
       */
      
      public class StringUtils {
          /**
           * 将目标字符串首字母变为大写
           * @param str 目标字符串
           * @return 首字母变为大写的字符串
           */
          public static String firstChar2UpperCase(String str){
              // abcd-->Abcd
              return str.toUpperCase().substring(0, 1) + str.substring(1);
          }
      }
      
    • ReflectUtils

      package com.sxt.SORM.utils;
      
      import java.lang.reflect.Method;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: ReflectUtils.java
       * @time: 2020/3/11 13:44
       * @desc: | 封装了反射的常用操作
       */
      
      public class ReflectUtils {
          /**
           * 调用obj对象对应属性fieldName的get方法
           *
           * @param fieldName 属性名
           * @param obj       Object对象
           * @return
           */
          public static Object invokeGet(String fieldName, Object obj) {
              // 通过反射机制,调用属性对应的get方法或set方法
              try {
                  Class c = obj.getClass();
                  Method m = c.getDeclaredMethod("get" + StringUtils.firstChar2UpperCase(fieldName), null);
                  return m.invoke(obj, null);
              } catch (Exception e) {
                  e.printStackTrace();
                  return null;
              }
          }
      
          public static void invokeSet(Object obj, String columnName, Object columnValue) {
              try {
                  if (columnValue != null) {
                      Method m = obj.getClass().getDeclaredMethod("set" + StringUtils.firstChar2UpperCase(columnName),
                              columnValue.getClass());
                      m.invoke(obj, columnValue);
                  }
              } catch (Exception e) {
                  e.printStackTrace();
      
              }
          }
      }
      
  • po(自动生成的代码保存位置)

    • Dept

      package com.sxt.SORM.po;
      
      public class Dept {
      
      	private String address;
      	private Integer id;
      	private String dname;
      
      
      	public void setAddress(String address){
      		this.address = address;
      	}
      	public void setId(Integer id){
      		this.id = id;
      	}
      	public void setDname(String dname){
      		this.dname = dname;
      	}
      	public String getAddress(){
      		return address;
      	}
      	public Integer getId(){
      		return id;
      	}
      	public String getDname(){
      		return dname;
      	}
      }
      
    • Emp

      package com.sxt.SORM.po;
      
      public class Emp {
      
          private String empname;
          private java.sql.Date birthday;
          private Double bonus;
          private Integer deptId;
          private Integer id;
          private Double salary;
          private Integer age;
      
          public String getEmpname() {
              return empname;
          }
      
          public void setEmpname(String empname) {
              this.empname = empname;
          }
      
          public java.sql.Date getBirthday() {
              return birthday;
          }
      
          public void setBirthday(java.sql.Date birthday) {
              this.birthday = birthday;
          }
      
          public Double getBonus() {
              return bonus;
          }
      
          public void setBonus(Double bonus) {
              this.bonus = bonus;
          }
      
          public Integer getDeptId() {
              return deptId;
          }
      
          public void setDeptId(Integer deptId) {
              this.deptId = deptId;
          }
      
          public Integer getId() {
              return id;
          }
      
          public void setId(Integer id) {
              this.id = id;
          }
      
          public Double getSalary() {
              return salary;
          }
      
          public void setSalary(Double salary) {
              this.salary = salary;
          }
      
          public Integer getAge() {
              return age;
          }
      
          public void setAge(Integer age) {
              this.age = age;
          }
      }
      
  • vo(复杂查询生成的类所保存的位置)

    • EmpVO

      package com.sxt.SORM.vo;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: EmpVO.java
       * @time: 2020/3/15 12:44
       * @desc: |
       */
      
      public class EmpVO {
          // select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e
          // join dept d on e.deptId=d.id;
          private Integer id;
          private String empname;
          private Double xinshui;
          private Integer age;
          private String deptName;
          private String deptAddr;
      
          public EmpVO() {
          }
      
          public EmpVO(Integer id, String empname, Double xinshui, Integer age, String deptName, String deptAddr) {
              this.id = id;
              this.empname = empname;
              this.xinshui = xinshui;
              this.age = age;
              this.deptName = deptName;
              this.deptAddr = deptAddr;
          }
      
          public Integer getId() {
              return id;
          }
      
          public void setId(Integer id) {
              this.id = id;
          }
      
          public String getEmpname() {
              return empname;
          }
      
          public void setEmpname(String empname) {
              this.empname = empname;
          }
      
          public Double getXinshui() {
              return xinshui;
          }
      
          public void setXinshui(Double xinshui) {
              this.xinshui = xinshui;
          }
      
          public Integer getAge() {
              return age;
          }
      
          public void setAge(Integer age) {
              this.age = age;
          }
      
          public String getDeptName() {
              return deptName;
          }
      
          public void setDeptName(String deptName) {
              this.deptName = deptName;
          }
      
          public String getDeptAddr() {
              return deptAddr;
          }
      
          public void setDeptAddr(String deptAddr) {
              this.deptAddr = deptAddr;
          }
      }
      
  • 配置文件(db.properties)

    usingDB=mysql
    URL=jdbc:mysql://localhost:3306/sorm?serverTimezone=UTC
    driver=com.mysql.cj.jdbc.Driver
    user=root
    pwd=123456
    srcPath=F:/BookStudy/else/JAVAPro/src/com/sxt/SORM
    poPackage=com.sxt.SORM.po
    
  • README(说明文件)

    1. 在src下建立db.properties
    2. 每张表只有一个主键,不能处理多个主键的情况
    3. po尽量使用包装类,不要使用基本数据类型
    4. 目前只能处理数据库来维护自增的方式
    

9.2 通过模板方法模式对Query进行优化

  • Query

    package com.sxt.SORM.core;
    
    import com.sxt.SORM.bean.ColumnInfo;
    import com.sxt.SORM.bean.TableInfo;
    import com.sxt.SORM.utils.JDBCUtils;
    import com.sxt.SORM.utils.ReflectUtils;
    
    import java.lang.reflect.Field;
    import java.sql.*;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @file: Query.java
     * @time: 2020/3/10 17:31
     * @desc: |负责查询(对外提供服务的核心类)
     */
    
    public abstract class Query implements Cloneable {
    
        /**
         * 采用模板方法模式将JDBC操作封装成模板,变于重用
         *
         * @param sql    sql语句
         * @param params sql的参数
         * @param clazz  记录要封装到的java类
         * @param back   CallBack的实现类,实现回调
         * @return 返回查询结果
         */
        public Object executeQueryTemplate(String sql, Object[] params, Class clazz, CallBack back) {
            Connection conn = DBManager.getConn();
            // 存放查询结果的容器
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                ps = conn.prepareStatement(sql);
                // 给sql设置参数,就是?位置的参数
                JDBCUtils.handleParams(ps, params);
                rs = ps.executeQuery();
                ResultSetMetaData metaData = rs.getMetaData();
    
                return back.doExecute(conn, ps, rs);
    
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            } finally {
                DBManager.close(ps, conn);
            }
        }
    
        /**
         * 直接执行一个DML语句
         *
         * @param sql    sql语句
         * @param params 参数
         * @return 执行sql语句后影响记录的行数
         */
        public int executeDML(String sql, Object[] params) {
            Connection conn = DBManager.getConn();
            int count = 0;
            PreparedStatement ps = null;
            try {
                ps = conn.prepareStatement(sql);
                // 给sql设置参数,就是?位置的参数
                JDBCUtils.handleParams(ps, params);
                count = ps.executeUpdate();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                DBManager.close(ps, conn);
            }
            return count;
        }
    
        /**
         * 将一个对象存储到数据库中
         *
         * @param obj 要存储的对象
         */
        public void insert(Object obj) {
            // obj --> 表中。 insert into 表名(id, name, pwd) values (?, ?, ?)
            Class c = obj.getClass();
            // 存储sql的参数对象
            List<Object> params = new ArrayList<>();
            TableInfo tableInfo = TableContext.poClassTableMap.get(c);
            StringBuilder sql = new StringBuilder("insert into " + tableInfo.getTname() + " (");
    
            // 计算不为空的属性值
            int countNotNullField = 0;
    
            // 目前只能处理数据库来维护自增的方式
            Field[] fs = c.getDeclaredFields();
            for (Field f : fs) {
                String fieldName = f.getName();
                Object fieldValue = ReflectUtils.invokeGet(fieldName, obj);
                if (fieldValue != null) {
                    // 如果该属性值不为空
                    countNotNullField++;
                    sql.append(fieldName + ",");
                    params.add(fieldValue);
                }
            }
    
            // 把最后一个属性后面的,换成)
            sql.setCharAt(sql.length() - 1, ')');
            sql.append(" values (");
    
            for (int i = 0; i < countNotNullField; i++) {
                sql.append("?,");
            }
            sql.setCharAt(sql.length() - 1, ')');
    
            executeDML(sql.toString(), params.toArray());
        }
    
        /**
         * 删除clazz表示类对应的表中的记录(指定主键id的记录)
         * 把对象中不为null的属性往数据库中存储!如果数字为null则放0
         *
         * @param clazz 跟表对应的类的Class对象
         * @param id    主键的值
         */
        // delete from User where id = 2;
        public void delete(Class clazz, Object id) {
            // Emp.class, 2 --> delete from emp where id=2
            // 通过Class对象找TableInfo
            TableInfo tableInfo = TableContext.poClassTableMap.get(clazz);
            // 获得主键
            ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
    
            String sql = "delete from " + tableInfo.getTname() + " where " + onlyPriKey.getName() + "=?;";
            executeDML(sql, new Object[]{id});
        }
    
        /**
         * 删除对象在数据库中对应的记录(对象所在类对应到表,对象的主键对应到的记录)
         *
         * @param obj
         */
        public void delete(Object obj) {
            Class c = obj.getClass();
            TableInfo tableInfo = TableContext.poClassTableMap.get(c);
            // 获得主键
            ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
            // 通过反射机制,调用属性对应的get方法或set方法
            Object priKeyValue = ReflectUtils.invokeGet(onlyPriKey.getName(), obj);
            delete(obj.getClass(), priKeyValue);
        }
    
        /**
         * 更新对象对应的记录,并且只更新指定的字段的值
         *
         * @param obj        索要更新的对象
         * @param fieldNames 更新的属性列表
         * @return 执行sql语句后影响记录的行数
         */
        // update user set uname=?, pwe=?
        public int update(Object obj, String[] fieldNames) {
            // obj{"uname", "pwd} --> update 表名 set uname=?, pwd=? where id=?
            Class c = obj.getClass();
            List<Object> params = new ArrayList<>();
            TableInfo tableInfo = TableContext.poClassTableMap.get(c);
            ColumnInfo priKey = tableInfo.getOnlyPriKey();
            StringBuilder sql = new StringBuilder("update " + tableInfo.getTname() + " set ");
    
            for (String fname : fieldNames) {
                Object fvalue = ReflectUtils.invokeGet(fname, obj);
                params.add(fvalue);
                sql.append(fname + "=?,");
            }
            sql.setCharAt(sql.length() - 1, ' ');
            sql.append(" where ");
            sql.append(priKey.getName() + "=?");
            params.add(ReflectUtils.invokeGet(priKey.getName(), obj));
    
            return executeDML(sql.toString(), params.toArray());
        }
    
        /**
         * 查询返回多行记录,并将每行记录封装到clazz指定的类的对象中
         *
         * @param sql    查询语句
         * @param clazz  封装数据的javabean类的Class对象
         * @param params sql的参数
         * @return 返回查询到的结果
         */
        public List queryRows(final String sql, final Class clazz, final Object[] params) {
            // 存放查询结果的容器
            return (List) executeQueryTemplate(sql, params, clazz, new CallBack() {
                @Override
                public Object doExecute(Connection conn, PreparedStatement ps, ResultSet rs) {
                    List list = null;
                    try {
                        ResultSetMetaData metaData = rs.getMetaData();
                        // 多行
                        while (rs.next()) {
                            if (list == null) {
                                list = new ArrayList();
                            }
                            // 调用javabean的无参构造器
                            Object rowObj = clazz.newInstance();
                            // 多列 select username, pwd, age from user where id>? and age>?
                            for (int i = 0; i < metaData.getColumnCount(); i++) {
                                // username
                                String columnName = metaData.getColumnLabel(i + 1);
                                Object columnValue = rs.getObject(i + 1);
    
                                // 调用rowObj对象的setUsername(String uname)方法,将columnValue的值设置进去
                                ReflectUtils.invokeSet(rowObj, columnName, columnValue);
                            }
                            list.add(rowObj);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return list;
                }
            });
        }
    
        /**
         * 查询返回一行记录,并将该记录封装到clazz指定的类的对象中
         *
         * @param sql    查询语句
         * @param clazz  封装数据的javabean类的Class对象
         * @param params sql的参数
         * @return 返回查询到的结果
         */
        public Object queryUniqueRows(String sql, Class clazz, Object[] params) {
            List list = queryRows(sql, clazz, params);
            return (list != null || list.size() > 0) ? list.get(0) : null;
        }
    
        /**
         * 根据主键的值直接查找对应的对象
         * @param clazz
         * @param id
         * @return
         */
        public Object queryById(Class clazz, Object id){
            // select * from emp where id=?
            TableInfo tableInfo = TableContext.poClassTableMap.get(clazz);
            ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
            String sql = "select * from " + tableInfo.getTname() +  " where " + onlyPriKey.getName() + "=?";
            return queryUniqueRows(sql, clazz, new Object[]{id});
        }
    
        /**
         * 查询返回一个值(一行一列),并将该值返回
         *
         * @param sql    查询语句
         * @param params sql的参数
         * @return 返回查询到的结果
         */
        public Object queryValue(String sql, Object[] params) {
            return executeQueryTemplate(sql, params, null, new CallBack() {
                @Override
                public Object doExecute(Connection conn, PreparedStatement ps, ResultSet rs) {
                    Object value = null;
                    try {
                        // 多行
                        while (rs.next()) {
                            // select count(*) from user
                            value = rs.getObject(1);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return value;
                }
            });
        }
    
        /**
         * 查询返回一个数字(一行一列),并将该值返回
         *
         * @param sql    查询语句
         * @param params sql的参数
         * @return 返回查询到的数字
         */
        public Number queryNumber(String sql, Object[] params) {
            return (Number) queryValue(sql, params);
        }
    
        /**
         * 分页查询
         *
         * @param pageNum 第几页数据
         * @param size    每页显示多少记录
         * @return
         */
        public abstract Object queryPagenate(int pageNum, int size);
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    
  • MysqlQuery

    package com.sxt.SORM.core;
    
    import com.sxt.SORM.po.Emp;
    import com.sxt.SORM.vo.EmpVO;
    
    import java.util.List;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @file: MysqlQuery.java
     * @time: 2020/3/13 16:54
     * @desc: |负责针对mysql数据库的查询
     */
    
    public class MysqlQuery extends Query {
    
        public static void main(String[] args) {
            Object obj = new MysqlQuery().queryValue("select count(*) from emp where salary>?", new Object[]{1000});
            System.out.println(obj);
        }
    
        /**
         * 复杂多行查询测试
         */
        public static void testQueryRows() {
            List<Emp> list = new MysqlQuery().queryRows("select id,empname,age from emp where age>? and salary, Emp.class,
                    new Object[]{1, 9000});
            for (Emp e : list) {
                System.out.println(e.getEmpname());
            }
    
            String sql2 = "select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e"
                    + " " + "join dept d on e.deptId=d.id;";
            List<EmpVO> list2 = new MysqlQuery().queryRows(sql2, EmpVO.class, null);
            for (EmpVO e : list2) {
                System.out.println(e.getEmpname() + "-" + e.getDeptAddr() + "-" + e.getXinshui());
            }
        }
    
        /**
         * 增删改操作测试
         */
        public static void testDML() {
            Emp e = new Emp();
            e.setEmpname("Tom");
            e.setBirthday(new java.sql.Date(System.currentTimeMillis()));
            e.setAge(30);
            e.setSalary(8888.0);
            e.setId(1);
    
            // new MysqlQuery().delete(e);
            // new MysqlQuery().insert(e);
            new MysqlQuery().update(e, new String[]{"empname", "age", "salary"});
        }
    
        @Override
        public Object queryPagenate(int pageNum, int size) {
            return null;
        }
    }
    

9.3 工厂模式+单例模式+克隆模式构建QueryFactory

  • 修改db.properties,增加queryClass的路径

    queryClass=com.sxt.SORM.core.MysqlQuery
    
  • 修改Configuration,增加queryClass的属性,getset方法

    // 项目使用的查询类的路径
    private String queryClass;
    
    public String getQueryClass() {
    	return queryClass;
    }
    
    public void setQueryClass(String queryClass) {
    	this.queryClass = queryClass;
    }
    
  • Query实现implements Cloneable接口

    public abstract class Query implements Cloneable
    
    protected Object clone() throws CloneNotSupportedException {
    	return super.clone();
    }
    
  • QueryFactory

    package com.sxt.SORM.core;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @file: QueryFactory.java
     * @time: 2020/3/11 13:33
     * @desc: |创建Query对象的工厂类:单例+克隆+工厂
     */
    
    public class QueryFactory {
        private static QueryFactory factory = new QueryFactory();
        // 原型对象
        private static Query prototypeObj;
    
        static{
            try {
                // 加载指定的Query类
                Class c = Class.forName(DBManager.getConf().getQueryClass());
                prototypeObj = (Query) c.newInstance();
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        // 私有构造器
        private QueryFactory(){}
    
        public static Query createQuery(){
            try {
                return (Query) prototypeObj.clone();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    }
    
  • 客户端调用测试类

    package com.sxt.SORM.test;
    
    import com.sxt.SORM.core.MysqlQuery;
    import com.sxt.SORM.core.Query;
    import com.sxt.SORM.core.QueryFactory;
    import com.sxt.SORM.vo.EmpVO;
    
    import java.util.List;
    
    /**
     * @author: Li Tian
     * @contact: [email protected]
     * @software: IntelliJ IDEA
     * @file: Test.java
     * @time: 2020/3/17 12:55
     * @desc: |客户端调用测试类
     */
    
    public class Test {
        public static void main(String[] args){
            Query q = QueryFactory.createQuery();
            String sql2 = "select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e"
                    + " " + "join dept d on e.deptId=d.id;";
            List<EmpVO> list2 = q.queryRows(sql2, EmpVO.class, null);
            for (EmpVO e : list2) {
                System.out.println(e.getEmpname() + "-" + e.getDeptAddr() + "-" + e.getXinshui());
            }
        }
    }
    

9.4 增加连接池提高效率

  • Connection Pool

  • 就是将Connection对象放入List中,反复重用!

  • 连接池的初始化:事先放入多个连接对象

  • 从连接池中取连接对象

    • 如果池中有可用连接,则将池中最后一个返回。同时,讲该连接从池中remove,表示正在使用。
    • 如果池中无可用连接,则创建一个新的。
  • 关闭连接

  • 不是真正关闭连接,而是将用完的连接放入连接池中。

  • 市面上的连接池产品:

    • DBCP
    • c3p0
    • proxool
  • 代码

    • 配置文件新增连接池参数

      poolMinSize=10
      poolMaxSize=100
      
    • Configuration增加上述参数的属性和getset方法

      // 连接池最小限制
      private int poolMinSize;
      // 连接池最大限制
      private int poolMaxSize;
      
      public int getPoolMinSize() {
      	return poolMinSize;
      }
      
      public void setPoolMinSize(int poolMinSize) {
      	this.poolMinSize = poolMinSize;
      }
      
      public int getPoolMaxSize() {
      	return poolMaxSize;
      }
      
      public void setPoolMaxSize(int poolMaxSize) {
      	this.poolMaxSize = poolMaxSize;
      }
      
    • 新增连接池类:DBConnPool

      package com.sxt.SORM.pool;
      
      import com.sxt.SORM.core.DBManager;
      
      import java.sql.Connection;
      import java.util.ArrayList;
      import java.util.List;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: DBConnPool.java
       * @time: 2020/3/17 14:03
       * @desc: |连接池的类
       */
      
      public class DBConnPool {
          // 最大连接数
          private static final int POOL_MAX_SIZE = DBManager.getConf().getPoolMaxSize();
          // 最小连接数
          private static final int POOL_MIN_SIZE = DBManager.getConf().getPoolMinSize();
          // 连接池对象
          private List<Connection> pool;
      
          public DBConnPool() {
              initPool();
          }
      
          /**
           * 初始化连接池,使池中的连接数达到最小值
           */
          public void initPool() {
              if (pool == null) {
                  pool = new ArrayList<Connection>();
              }
      
              while (pool.size() < DBConnPool.POOL_MIN_SIZE) {
                  pool.add(DBManager.createConn());
                  System.out.println("初始化池,池中连接数:" + pool.size());
              }
          }
      
          /**
           * 从连接池中取出一个连接
           */
          public synchronized Connection getConnection() {
              int last_index = pool.size() - 1;
              // 获得一个连接
              Connection conn = pool.get(last_index);
              pool.remove(last_index);
              return conn;
          }
      
          /**
           * 将连接放回池中
           *
           * @param conn
           */
          public synchronized void close(Connection conn) {
              if (pool.size() >= POOL_MAX_SIZE) {
                  try {
                      if (conn != null) {
                          conn.close();
                      }
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              } else {
                  pool.add(conn);
              }
          }
      }
      
    • 修改DBManager方法,把原来获取连接的方式,改为使用连接池,关闭连接的方式也做同样的修改

      package com.sxt.SORM.core;
      
      import com.sxt.SORM.bean.Configuration;
      import com.sxt.SORM.pool.DBConnPool;
      
      import java.io.IOException;
      import java.sql.Connection;
      import java.sql.DriverManager;
      import java.sql.ResultSet;
      import java.sql.Statement;
      import java.util.Properties;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: DBManager.java
       * @time: 2020/3/11 13:43
       * @desc: |根据配置信息,维持连接对象的管理(增加连接池功能)
       */
      
      public class DBManager {
          // 配置信息
          private static Configuration conf;
          // 连接池对象
          private static DBConnPool pool;
      
          static {
              // 静态代码块
              Properties pros = new Properties();
              try {
                  pros.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/sxt/SORM/db.properties"));
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
              conf = new Configuration();
              conf.setDriver(pros.getProperty("driver"));
              conf.setPoPackage(pros.getProperty("poPackage"));
              conf.setPwd(pros.getProperty("pwd"));
              conf.setSrcPath(pros.getProperty("srcPath"));
              conf.setURL(pros.getProperty("URL"));
              conf.setUser(pros.getProperty("user"));
              conf.setUsingDb(pros.getProperty("usingDB"));
              conf.setQueryClass(pros.getProperty("queryClass"));
              conf.setPoolMaxSize(Integer.parseInt(pros.getProperty("poolMaxSize")));
              conf.setPoolMinSize(Integer.parseInt(pros.getProperty("poolMinSize")));
      
              // 加载TableContext
              System.out.println(TableContext.class);
          }
      
          public static Connection getConn() {
              /*获取数据库(mysql)驱动连接*/
              // 加载驱动类
      
              /* 第一种方法:直接取
              try {
                  Class.forName(conf.getDriver());
                  // 直接建立连接,后期增加连接池处理,提高效率!
                  return DriverManager.getConnection(conf.getURL(), conf.getUser(), conf.getPwd());
              } catch (Exception e) {
                  e.printStackTrace();
                  return null;
              }
               */
      
              // 第二种方法,通过连接池
              if(pool == null){
                   pool = new DBConnPool();
              }
              return pool.getConnection();
          }
      
          /**
           * 创建新的Connection连接
           *
           * @return
           */
          public static Connection createConn() {
              /*获取数据库(mysql)驱动连接*/
              // 加载驱动类
              try {
                  Class.forName(conf.getDriver());
                  // 直接建立连接,后期增加连接池处理,提高效率!
                  return DriverManager.getConnection(conf.getURL(), conf.getUser(), conf.getPwd());
              } catch (Exception e) {
                  e.printStackTrace();
                  return null;
              }
          }
      
          public static void close(ResultSet rs, Statement ps, Connection conn) {
              /**关闭接口方法*/
              try {
                  if (rs != null) {
                      rs.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
              try {
                  if (ps != null) {
                      ps.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
              try {
                  //     if (conn != null) {
                  //         conn.close();
                  //     }
                  pool.close(conn);
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          public static void close(Statement ps, Connection conn) {
              /**关闭接口方法,重载*/
              try {
                  if (ps != null) {
                      ps.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
              try {
                  //     if (conn != null) {
                  //         conn.close();
                  //     }
                  pool.close(conn);
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          public static void close(Connection conn) {
              /*关闭接口方法,重载*/
              try {
                  //     if (conn != null) {
                  //         conn.close();
                  //     }
                  pool.close(conn);
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          public static Configuration getConf() {
              return conf;
          }
      }
      
    • 客户端测试

      package com.sxt.SORM.test;
      
      import com.sxt.SORM.core.Query;
      import com.sxt.SORM.core.QueryFactory;
      import com.sxt.SORM.vo.EmpVO;
      
      import java.util.List;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: Test2.java
       * @time: 2020/3/17 16:40
       * @desc: |测试连接池的调用效率
       */
      
      public class Test2 {
          public static void test1(){
              Query q = QueryFactory.createQuery();
              String sql2 = "select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e"
                      + " " + "join dept d on e.deptId=d.id;";
              List<EmpVO> list2 = q.queryRows(sql2, EmpVO.class, null);
              for (EmpVO e : list2) {
                  System.out.println(e.getEmpname() + "-" + e.getDeptAddr() + "-" + e.getXinshui());
              }
          }
      
          public static void main(String[] args){
              long a = System.currentTimeMillis();
              for (int i = 0; i < 3000; i++) {
                  test1();
              }
              long b = System.currentTimeMillis();
              // 不加连接池的耗时:13077ms,增加连接池之后,耗时为2478
              System.out.println(b-a);
          }
      }
      

9.5 jar包和API文档的生成

  • idea导出参考连接:https://blog.csdn.net/rico_rico/article/details/84936785

  • javadoc导出参考连接:https://blog.csdn.net/qq_29347295/article/details/78635861

  • 其中编码需要改为:-encoding utf-8 -charset utf-8

  • 测试使用

    • 配置配置文件

      usingDB=mysql
      URL=jdbc:mysql://localhost:3306/sorm?serverTimezone=UTC
      driver=com.mysql.cj.jdbc.Driver
      user=root
      pwd=123456
      srcPath=F:/BookStudy/else/SORMDemo/src
      poPackage=po
      queryClass=com.sxt.SORM.core.MysqlQuery
      poolMinSize=10
      poolMaxSize=100
      
    • 测试po类的生成,增删改查

      package test;
      
      import com.sxt.SORM.core.Query;
      import com.sxt.SORM.core.QueryFactory;
      import po.Emp;
      
      /**
       * @author: Li Tian
       * @contact: [email protected]
       * @software: IntelliJ IDEA
       * @file: Test.java
       * @time: 2020/3/17 17:38
       * @desc: |
       */
      
      public class Test {
          public static void main(String[] args) {
              // 通过这个方法可以生成po类
              // TableContext.updateJavaPOFile();
      
              // add();
              // select();
              // delete();
              update();
      
          }
      
          public static void add() {
              // 测试插入对象
              Emp e = new Emp();
              e.setAge(18);
              e.setEmpname("我");
              e.setSalary(2000.0);
      
              Query q = QueryFactory.createQuery();
              q.insert(e);
          }
      
          public static void delete(){
              // 测试删除对象
              Emp e = new Emp();
              e.setId(12);
              Query q = QueryFactory.createQuery();
              q.delete(e);
          }
      
          public static void update(){
              // 测试删除对象
              Emp e = new Emp();
              e.setId(1);
              e.setAge(1);
              Query q = QueryFactory.createQuery();
              q.update(e, new String[]{"age"});
          }
      
          public static void select(){
              // 测试查询
              Query q = QueryFactory.createQuery();
              Number n = q.queryNumber("select count(*) from emp where salary>?", new Object[]{100});
              System.out.println(n);
          }
      }
      
  • 关于连表查询,我看操作实在是过于复杂,还要自己建立EmpVO,想想就算了。


我的CSDN:https://blog.csdn.net/qq_21579045

我的博客园:https://www.cnblogs.com/lyjun/

我的Github:https://github.com/TinyHandsome

纸上得来终觉浅,绝知此事要躬行~

欢迎大家过来OB~

by 李英俊小朋友


  1. 8 ↩︎

  2. 16 ↩︎

  3. 24 ↩︎

  4. 32 ↩︎

你可能感兴趣的:(学习笔记)