1. 程式人生 > 其它 >面向物件系列講解—面向物件的含義&工廠模式

面向物件系列講解—面向物件的含義&工廠模式

JDBC介紹

  • 資料庫訪問技術簡介:

  • JDBC(Java DataBase Connectivity)

    • 是由Sun Microsystem公司提供的API(Application Programming Interface應用程式程式設計介面);

    • 它為Java應用程式提供了一系列的類,使其能夠快速高效地訪問資料庫;

    • 這些功能是由一系列的類和物件來完成的,我們只需使用相關的物件,即可完成對資料庫的操作

JDBC的工作原理

JDBC訪問資料庫步驟

步驟1:載入驅動

  • 使用Class類的forName方法,將驅動程式類載入到JVM(Java虛擬機器)中;

    private static String driverName= "com.mysql.cj.jdbc.Driver";//mysql  8.0
    Class.forName(driver);//載入驅動

步驟2:獲取資料庫連線

  • 成功載入驅動後,必須使用DriverManager類的靜態方法getConnection來獲得連線物件;

     

  • jdbc:mysql://localhost:3306/資料庫名稱?引數名稱=引數值&引數名稱=引數值
  • String url = "jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";`

  • URL引數

    • useUnicode:是否使用Unicode字符集,如果引數characterEncoding設定為gb2312或gbk,本引數值必須設定為true。

    • characterEncoding:當useUnicode設定為true時,指定字元編碼。比如可設定為gb2312或gbk。

    • useSSL:MySQL在高版本需要指明是否進行SSL連線,在mysql連線字串url中加入ssl=true或者false。

    • serverTimezone:設定時區 例如 serverTimezone=UTC(統一標準世界時間)或serverTimezone=Asia/Shanghai(中國時區)

步驟3:建立Statement執行SQL語句

  • 通過Connection物件建立

  • 用於執行SQL語句

步驟4:處理ResultSet結果集

  • 用於儲存查詢結果

    • 只在執行select語句時返回

步驟5:釋放資源 close();

案例

User定義

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'jim', '11111');
INSERT INTO `user` VALUES ('2', 'tom', '22222');
INSERT INTO `user` VALUES ('3', 'lucy', '3333');
INSERT INTO `user` VALUES ('4', 'laowang', '4444');
INSERT INTO `user` VALUES ('5', 'laoli', '5555');

使用Statement查詢User

引入MySql驅動jar

 

user表的實體類

public class User {
    private Long id;//主鍵
    private String username;//使用者名稱
    private String password;//密碼
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    public Long getId() {return id;}
    public void setId(Long id) {this.id = id; }
    public String getUsername() {return username; }
    public void setUsername(String username) {this.username = username; }
    public String getPassword() {return password; }
    public void setPassword(String password) {this.password = password;
    }
    @Override
    public String toString() {
        return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' + '}';
    }
}

使用Statement查詢User

import java.sql.*;
import java.util.Scanner;
public class JdbcTest {
    public static void main(String[] args) {
        login();
    }
    //模擬登陸
    private static void login() {
        Scanner sc = new Scanner(System.in);
        System.out.println("請輸入使用者名稱:");
        String username = sc.nextLine();
        System.out.println("請輸入密碼:");
        String password = sc.nextLine();
        User user = query(username, password);
        if (user != null) {
            System.out.println("歡迎" + username + "登陸成功");
        } else {
            System.out.println("使用者名稱或密碼不正確!");
        }
    }

    private static User query(String username, String password) {
        Connection conn = null;
        Statement statement = null;
        ResultSet rs = null;
        try {
            //1、通過反射,載入與註冊驅動類
            Class.forName("com.mysql.cj.jdbc.Driver");/*載入mysql8.0+的驅動*/
            /*mysql5.0+的驅動 : Class.forName("com.mysql.jdbc.Driver");*/
            //2、獲取連線
            String url = "jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";
            conn = DriverManager.getConnection(url, "root", "123456");
            /*DriverManager.getConnection();*/
            //3、建立Statement物件並執行 執行sql返回ResultSet
            statement = conn.createStatement();
            String sql = "select * from `user` where username='" + username + "' and `password`='" + password + "'";
            rs = statement.executeQuery(sql);
            //4、處理結果集
            User user = null;
            /*boolean next()
                將游標從當前位置向前移動一行。 ResultSet游標最初位於第一行之前; 第一次呼叫方法next使第一行成為當前行; 第二個呼叫使第二行成為當前行,依此類推。
                當呼叫next方法返回false時,游標位於最後一行之後。
                如果當前行的輸入流已開啟,則對方法next的呼叫將隱式關閉它。 當讀取新行時, ResultSet物件的警告鏈將被清除。
                結果  true如果新的當前行有效; false如果沒有更多的行
            */
            if (rs.next()) {
                /*Object getObject(int columnIndex)
                    獲取此的當前行中指定列的值 ResultSet作為物件
                    引數 columnIndex - 第一列是1,第二列是2,...
                    結果 一個 java.lang.Object持有列值
                */
                Long id = (Long) rs.getObject(1);
                String username1 = (String) rs.getObject(2);
                String password1 = (String) rs.getObject(3);
                user = new User(id, username1, password1);
            }
            return user;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        //5、關閉資源
        finally {
            try {
                if (rs != null) {
                    rs.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            try {
                if (statement != null) {
                    statement.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        return null;
    }
}

     

SQL 注入(SQL Injection)是發生在 Web 程式中資料庫層的安全漏洞。主要原因是程式對使用者輸入資料的合法性沒有判斷和處理,導致攻擊者可以在 Web 應用程式中事先定義好的 SQL 語句中新增額外的 SQL 語句,在管理員不知情的情況下實現非法操作。
簡而言之,SQL 注入就是在使用者輸入的字串中加入 SQL 語句,如果在設計不良的程式中忽略了檢查,那麼這些注入進去的 SQL 語句就會被資料庫伺服器誤認為是正常的 SQL 語句而執行,攻擊者就可以執行計劃外的命令或訪問未被授權的資料。

statement 只能拼接sql語句,可能會出現sql注入的風險
例如:sql注入 使用者名稱輸入   ' or 1=1 #  密碼隨便輸入,就會查出資料),不推薦用。

       

JDBC的PreparedStatement

  • PreparedStatement的概述:

    • 繼承自Statement介面
    • 能夠對SQL語句進行預編譯
  • PreparedStatement的優點 
    • 提高SQL語句執行效率
    • 提高安全性
  • 注意: 
    • SQL語句使用“”作為資料佔位符
    • 在建立時對SQL語句進行預編譯
    • 使用setXxx()方法設定資料  

使用PreparedStatement查詢

private static User query(String username, String password) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
        try {
            //1、通過反射,載入與註冊驅動類
            Class.forName("com.mysql.cj.jdbc.Driver");/*載入mysql8.0+的驅動*/
            //2、獲取連線
            String url = "jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";
            conn = DriverManager.getConnection(url, "root", "123456");
            //3、獲取PreparedStatement物件 並執行sql返回ResultSet
            String sql = "select * from `user` where username= ? and `password` = ?";
            ps = conn.prepareStatement(sql);
            ps.setObject(1, username);
            ps.setObject(2, password);
            rs = ps.executeQuery();
            //4、處理結果集
            User user = null;
            if (rs.next()) {
                Long id = (Long) rs.getObject(1);
                String username1 = (String) rs.getObject(2);
                String password1 = (String) rs.getObject(3);
                user = new User(id, username1, password1);
            }
            return user;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        //5、關閉資源
        finally {
            try {
                if (rs != null) {
                    rs.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            try {
                if (ps != null) {
                    ps.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    return null;
   }

 

解決SQL注入問題 

新增

/**
 * 新增
 *
 * @throws ClassNotFoundException
 * @throws SQLException
 */
private static void insert() throws ClassNotFoundException, SQLException {
    //1、載入jdbc驅動
    Class.forName("com.mysql.cj.jdbc.Driver");
    //2、獲取連線
    //在mysql8.0的資料庫 serverTimezone=Asia/shanghai是必須的引數
    String url = "jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";
    Connection conn = DriverManager.getConnection(url, "root", "123456");
    //3、獲取PreparedStatement物件
    //注意點:sql語句中使用?作為引數的佔位符
    String sql = "insert into `user`(username,password) values(?,?)";
    PreparedStatement ps = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
    //把問號替換成引數
    ps.setObject(1, "jim");
    ps.setObject(2, 30);
    //4、執行sql,新增 要使用executeUpdate()
    ps.executeUpdate();
    // 執行此 Statement 物件而建立的所有自動生成的鍵
    ResultSet rs = ps.getGeneratedKeys();
    if (rs.next()) {
        // 指定返回生成的主鍵
        Object id = rs.getObject(1);
        System.out.println("新增的主鍵為:"+id);
    }
    //5、關閉資源
    ps.close();
    conn.close();
}

修改和刪除

/**
 * 更新
 *
 * @throws ClassNotFoundException
 * @throws SQLException
 */
private static void update() throws ClassNotFoundException, SQLException {
    //1、載入jdbc驅動
    Class.forName("com.mysql.cj.jdbc.Driver");
    //2、獲取連線
    //在mysql8.0的資料庫 serverTimezone=Asia/shanghai是必須的引數
    String url = "jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";
    Connection conn = DriverManager.getConnection(url, "root", "123456");
    //3、獲取PreparedStatement物件
    //注意點:sql語句中使用?作為引數的佔位符
    String sql = "update user set password=? where id = ?";
    PreparedStatement ps = conn.prepareStatement(sql);
    //把問號替換成引數
    ps.setObject(1, "888");
    ps.setObject(2, 1);
    //4、執行sql,更新或者刪除 要使用executeUpdate(),處理返回值,返回值代表的含義:影響的行數
    int result = ps.executeUpdate();
    if (result > 0) {
        System.out.println("更新密碼成功");
    } else {
        System.out.println("更新密碼失敗");
    }
    //5、關閉資源
    ps.close();
    conn.close();
}

模糊查詢

    private static List<User> queryForLike(String usernameLike) {
        //假如:usernameLike 為a
        //sql語句
        String sql = "select * from `user` where username like ?";
        //引數
        Object[] params = {"%" + usernameLike + "%"};
        List<User> users = new ArrayList<>();
        try {
            List<Map<String, Object>> datas = DbUtils.selectList(sql, params);
            for (Map<String, Object> data : datas) {
                Long id = (Long) data.get("id");
                String username = (String) data.get("username");
                String password = (String) data.get("password");
                User user = new User(id, username, password);
                users.add(user);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return users;
    }

分頁查詢

/**
     * 分頁查詢
     *
     * @param pageNum  頁碼
     * @param pageSize 每頁數量
     * @return
     */
    private static List<User> queryPage1(int pageNum, int pageSize) {
        List<User> users = new ArrayList<>();
        String sql = "select * from `user` order by id asc limit ?,?";
        Object[] params = {(pageNum - 1) * pageSize, pageSize};
        try {
            List<Map<String, Object>> datas = DbUtils.selectList(sql, params);
            for (Map<String, Object> data : datas) {
                Long id = (Long) data.get("id");
                String username = (String) data.get("username");
                String password = (String) data.get("password");
                User user = new User(id, username, password);
                users.add(user);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return users;
    }

封裝DbUtils工具類

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.*;
public class DbUtils {
    private static String driverName;
    private static String url;
    private static String username;
    private static String password;
    static {
        InputStream inputStream = null;
        try {
            //建立db.properties的流
            inputStream = DbUtils.class.getClassLoader().getResourceAsStream("db.properties");
            //建立Properties物件
            Properties p = new Properties();
            //把資料流讀入Properties物件中
            p.load(inputStream);
            //從Properties物件中獲取配置資料
            url = p.getProperty("url");
            username = p.getProperty("username");
            password = p.getProperty("password");
            driverName = p.getProperty("driverName");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * insert語句使用,返回新增資料的自增主鍵。
     *
     * @param sql
     * @return
     */
    public static Object insert(Connection connection, String sql, Object[] params) throws SQLException {
        PreparedStatement ps = null;
        Object id = null;
        try {
            //建立PreparedStatement
            ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            //設定引數
            setPreparedStatementParam(ps, params);
            //執行sql
            ps.executeUpdate();
            // 執行此 Statement 物件而建立的所有自動生成的鍵
            ResultSet rs = ps.getGeneratedKeys();
            if (rs.next()) {
                // 指定返回生成的主鍵
                id = rs.getObject(1);
            }
        } finally {
            close(ps);
        }
        return id;
    }
    /**
     * insert語句使用,返回新增資料的自增主鍵。
     *
     * @param sql
     * @return
     */
    public static Object insert(String sql, Object[] params) throws SQLException, ClassNotFoundException {
        Connection conn = null;
        Object id;
        try {
            //建立連線
            conn = getConnection();
            id = insert(conn, sql, params);
        } finally {
            close(conn);
        }
        return id;
    }
    /**
     * 更新、刪除
     *
     * @param sql
     * @param params
     * @return
     * @throws SQLException
     */
    public static boolean update(Connection connection, String sql, Object[] params) throws SQLException {
        PreparedStatement ps = null;
        try {
            //步驟2:設定SQL語句以及對應的引數
            ps = connection.prepareStatement(sql);
            setPreparedStatementParam(ps, params);
            //步驟3:執行update
            int result = ps.executeUpdate();
            //返回執行的結果
            return result > 0 ? true : false;
        } finally {
            //步驟4:關閉資源
            close(ps);
        }
    }
    /**
     * 更新、刪除
     *
     * @param sql
     * @param params
     * @return
     * @throws SQLException
     */
    public static boolean update(String sql, Object[] params) throws SQLException, ClassNotFoundException {
        Connection connection = null;
        try {
            //步驟1:獲取連結
            connection = getConnection();
            return update(connection, sql, params);
        } finally {
            //步驟2:關閉連線資源
            close(connection);
        }
    }
    /**
     * 查詢一個
     *
     * @param sql
     * @param params
     * @return
     * @throws SQLException
     */
    public static Map<String, Object> selectOne(Connection connection, String sql, Object[] params) throws SQLException {
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //步驟2:設定SQL語句以及對應的引數
            ps = connection.prepareStatement(sql);
            setPreparedStatementParam(ps, params);
            //步驟3:執行查詢,把查詢結果的列作為key,列對應的值作為value,儲存到Map中
            rs = ps.executeQuery();
            if (rs.next()) {
                return getResultMap(rs);
            }
        } finally {
            //步驟4:關閉資源
            close(rs, ps);
        }
        return null;
    }
    /**
     * 獲取ResultMap
     *
     * @param rs
     * @return
     * @throws SQLException
     */
    private static Map<String, Object> getResultMap(ResultSet rs) throws SQLException {
        //獲取到result的元資料,包含了列的資訊
        ResultSetMetaData metaData = rs.getMetaData();
        //獲取到當前表的所有的列的列數
        int columnCount = metaData.getColumnCount();
        //儲存資料庫列與值的map
        Map<String, Object> map = new HashMap<>();
        //根據列的數量,獲取到每一個列的列名以及對應的值
        for (int i = 0; i < columnCount; i++) {
            //能夠獲取到每一個列的名稱,引數是每個列的序號值
            String columnLabel = metaData.getColumnLabel(i + 1);
            Object columnValue = rs.getObject(columnLabel);
            map.put(columnLabel, columnValue);
        }
        return map;
    }
    /**
     * 查詢一個
     *
     * @param sql
     * @param params
     * @return
     * @throws SQLException
     */
    public static Map<String, Object> selectOne(String sql, Object[] params) throws SQLException, ClassNotFoundException {
        Connection connection = null;
        try {
            //步驟1:獲取連結
            connection = getConnection();
            return selectOne(connection, sql, params);
        } finally {
            //步驟4:關閉資源
            close(connection);
        }
    }
    /**
     * 查詢集合
     *
     * @param sql
     * @param params
     * @return
     */
    public static List<Map<String, Object>> selectList(Connection connection, String sql, Object[] params) throws SQLException {
        PreparedStatement ps = null;
        ResultSet rs = null;
        List<Map<String, Object>> list;
        try {
            //步驟2:設定SQL語句以及對應的引數
            ps = connection.prepareStatement(sql);
            setPreparedStatementParam(ps, params);
            //步驟3:執行查詢,把查詢結果的列作為key,列對應的值作為value,儲存到Map中
            rs = ps.executeQuery();
            list = new ArrayList<>();
            while (rs.next()) {
                list.add(getResultMap(rs));
            }
        } finally {
            //步驟4:關閉資源
            close(rs, ps);
        }
        return list;
    }
    /**
     * 查詢集合
     *
     * @param sql
     * @param params
     * @return
     */
    public static List<Map<String, Object>> selectList(String sql, Object[] params) throws SQLException, ClassNotFoundException {
        Connection connection = null;
        try {
            connection = getConnection();
            return selectList(connection, sql, params);
        } finally {
            close(connection);
        }
    }
    /**
     * 設定引數
     *
     * @param ps
     * @param params
     * @throws SQLException
     */
    private static void setPreparedStatementParam(PreparedStatement ps, Object[] params) throws SQLException {
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
        }
    }
    /**
     * 獲取連線
     *
     * @return
     * @throws SQLException
     */
    public static Connection getConnection() throws ClassNotFoundException, SQLException {
        //載入資料庫驅動
        Class.forName(driverName);
        return DriverManager.getConnection(url, username, password);
    }
    //開啟事務
    public static void beginTransaction(Connection conn) throws SQLException {
        conn.setAutoCommit(false);
    }
    //提交事務
    public static void commit(Connection conn) throws SQLException {
        conn.commit();
    }
    //回滾事務
    public static void rollback(Connection conn) throws SQLException {
        conn.rollback();
    }
    public static void close(Connection connection) {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 釋放PreparedStatement,Connection
     */
    private static void close(PreparedStatement ps, Connection connection) {
        close(ps);
        close(connection);
    }
    /**
     * 釋放ResultSet,PreparedStatement
     */
    private static void close(ResultSet rs, PreparedStatement ps) {
        close(rs);
        close(ps);
    }
    /**
     * 釋放ResultSet,PreparedStatement,Connection
     */
    private static void close(ResultSet rs, PreparedStatement ps, Connection conn) {
        close(rs);
        close(ps);
        close(conn);
    }

    private static void close(Statement statement) {
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private static void close(PreparedStatement statement) {
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private static void close(ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

 呼叫儲存過程

  • 呼叫儲存過程的步驟

    (1)prepareCall(sql):建立執行儲存過程的callableStatement物件。

    (2)CallableStatement:繼承自PreparedStatement介面,由方法prepareCall建立,用於呼叫儲存過程。

    (3)執行:和其它物件使用的方法一樣 eg:cs.executeUpdate()

    (4)呼叫儲存過程的sql: String sql = “{call 儲存過程名字(引數1,引數2....)}”

  • 案例

    • 定義儲存過程

      /*
        -- 儲存過程:分頁查詢使用者列表
        -- 輸入引數:page_index:當前頁碼
        -- 輸入引數:page_size:分頁大小
        -- 輸出引數:total_count:資料總數
        -- 輸出引數:total_page:總頁數
      */
      DROP PROCEDURE IF EXISTS proc_search_user;
      CREATE PROCEDURE proc_search_user(IN page_index INT,IN page_size INT, OUT total_count INT, OUT total_page INT)
      BEGIN
              DECLARE begin_no INT;
              SET begin_no = (page_index-1)*page_size;
       
              -- 分頁查詢列表
              SELECT * FROM `user`
              ORDER BY id ASC
              LIMIT begin_no,page_size;
       
              -- 計算資料總數
              SELECT COUNT(1) INTO total_count FROM `user`;
       
              -- 計算總頁數
              SET total_page = FLOOR((total_count + page_size - 1) / page_size);
      END;
    • Navicat工具呼叫儲存過程

      CALL proc_search_user(1,2,@total_count,@total_page);
      select @total_count,@total_page;
    • JDBC呼叫儲存過程

      /**
       * 呼叫儲存過程
       *
       * @throws ClassNotFoundException
       * @throws SQLException
       */
      private static void selectPro() throws ClassNotFoundException, SQLException {
          //1、載入jdbc驅動
          Class.forName("com.mysql.cj.jdbc.Driver");
          //2、獲取連線
          //在mysql8.0的資料庫 serverTimezone=Asia/shanghai是必須的引數
          String url = "jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";
          Connection conn = DriverManager.getConnection(url, "root", "123456");
          //3、建立CallableStatement物件
          CallableStatement callStatement = conn.prepareCall("{CALL proc_search_user(?,?,?,?)}");
      
          // 設定輸入引數
          callStatement.setInt(1, 1); // 查詢第1頁資料
          callStatement.setInt(2, 2); // 每頁2條資料
      
          // 註冊輸出引數
          callStatement.registerOutParameter(3, Types.INTEGER);
          callStatement.registerOutParameter(4, Types.INTEGER);
      
          // 執行呼叫儲存過程,並獲取結果集
          ResultSet rs  = callStatement.executeQuery();
          //4、執行sql返回ResultSet
          ResultSet rs = ps.executeQuery();
          while (rs.next()) {
              int id = (Integer) rs.getObject(1);
              String username1 = (String) rs.getObject(2);
              String password1 = (String) rs.getObject(3);
      
              User user = new User();
              user.setId(id);
              user.setUsername(username1);
              user.setPassword(password1);
              System.out.println(user);
          }
      
          //5、關閉資源
          rs.close();
          ps.close();
          conn.close();
      }