九、JDBC(重點)

十四2001發表於2024-08-01

JDBC(重點)

資料庫驅動

JDBC

SUN公司為了簡化 開發人員的(對資料庫的同一)操作,提供了一個(Java運算元據庫的)規範,俗稱JDBC

這些規範的實現由具體的廠商去做

對於開發人員來說,我們只需要掌握JDBC介面的操作即可

java.sql

javax.sql

還需要匯入一個資料庫驅動包

第一個JDBC程式

  • 建立普通專案

  • 匯入資料庫驅動

  • 編寫程式碼

    package com.gs.lesson;
    
    import com.mysql.jdbc.Driver;
    
    import java.sql.*;
    
    /**
     * @version: java version 1.8
     * @author: 14
     * @description:
     */
    public class JdbcFisrstDemo {
        public static void main(String[] args) throws ClassNotFoundException, SQLException {
            // 1、載入驅動
            Class.forName("com.mysql.jdbc.Driver");
            // 2、使用者資訊和url
            String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false";
            String username = "root";
            String password = "123456";
            // 3、連線成功,返回資料庫物件
            Connection connection = DriverManager.getConnection(url, username, password);
    
            // 4、執行sql的物件
            Statement statement = connection.createStatement();
            // 5、執行sql的物件 去執行 sql,可能存在結果,檢視返回結果
            String sql = "select * from users";
            ResultSet resultSet = statement.executeQuery(sql);// 返回的結果集,封裝了全部的查詢結果
            while (resultSet.next()) {
                System.out.println("id:" + resultSet.getObject("id"));
                System.out.println("name:" + resultSet.getObject("name"));
                System.out.println("pwd:" + resultSet.getObject("password"));
                System.out.println("email:" + resultSet.getObject("email"));
                System.out.println("birth:" + resultSet.getObject("birthday"));
            }
            // 6、釋放連線
            resultSet.close();
            statement.close();
            connection.close();
        }
    }
    

步驟總結:

  • 載入驅動
  • 連線資料庫 DriverManager
  • 獲得執行sql的物件 Statement
  • 獲得返回的結果集
  • 釋放連線

DriverManager

//DriverManager.registerDriver(new Driver());
Class.forName("com.mysql.jdbc.Driver");//固定寫法,載入驅動

Connection connection = DriverManager.getConnection(url, username, password);
//connection 代表資料庫
//資料庫設定自動提交
//事務提交
//事務回滾
connection.setAutoCommit();
connection.commint();
connection.rollback();

URL

String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false";

//mysql --- 3306
//jdbc:mysql://主機地址:埠號/資料庫名?引數1&引數2&引數3
//oralce --- 1521
//jdbc:oracle:thin:@localhost:1521:sid

Statement | PrepareStatement 執行sql的物件

String sql = "select * from users" //編寫sql

statement.executeQuery();//查詢操作 返回ResultSet
statement.execute();//執行任何SQL
statement.executeUpdate();//更新、插入、刪除 都是用這個, 返回一個受影響的行數

ResultSet 查詢的結果集:封裝了所有的查詢結果

獲得指定的資料型別

resultSet.getObject();// 在不指定列型別的情況下使用
 // 如果知道列型別就使用指定型別
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();

遍歷,指標

resultSet.beforeFirst();//移動到最前面
resultSet.afterLast();//移動到最後面
resultSet.next();//移動到下一個
resultSet.previous();//移動到前一行
resultSet.absolute();//移動到指定行

釋放資源

// 6、釋放連線
resultSet.close();
statement.close();
connection.close();// 消耗資源,用完關掉

statement物件

程式碼實現

1、提取工具類

public class JdbcUtils {
    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;
    static {
        try {
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(in);

            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            // 1、驅動只用載入一次
            Class.forName(driver);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    //獲取連線資源
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,username,password);
    }

    //釋放連線資源
    public static void release(Connection conn, Statement stmt, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

2、編寫增刪改的方法 executeUpdate

public class TestInsert {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection(); //獲取資料庫連線
            stmt = conn.createStatement(); //獲得sql的執行物件
            String sql = "INSERT INTO users(ID,NAME,PASSWORD,EMAIL,BIRTHDAY)"+
                    "VALUES(4,'KUANGSHEN','123456','1235563@QQ.COM','2025-1-1')";
            int i  = stmt.executeUpdate(sql);
            if (i > 0){
                System.out.println("插入成功");
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.release(conn, stmt, rs);
        }
    }
}
public class TestDelete {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;

        try {
            conn = JdbcUtils.getConnection();
            stmt = conn.createStatement();
            String sql = "delete from users where id=4";
            int i = stmt.executeUpdate(sql);
            if ( i > 0){
                System.out.println("刪除成功");
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.release(conn,stmt,null);
        }
    }
}
public class TestUpdate {
    public static void main(String[] args) throws SQLException {
        Connection conn = null;
        Statement stmt = null;

        try {
            conn = JdbcUtils.getConnection();
            stmt = conn.createStatement();
            String sql = "UPDATE users SET name= 'kuangshen' ,email = '858728@qq.com' where id = 1";
            int i = stmt.executeUpdate(sql);
            if (i > 0) {
                System.out.println("修改成功");
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.release(conn, stmt, null);
        }
    }
}

3、查詢execuQuery

public class TestSelect {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();
            stmt = conn.createStatement();
            //SQL
            String sql = "select * from users where id = 1";
            rs = stmt.executeQuery(sql);
            while (rs.next()) {
                System.out.println(rs.getString("name"));
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.release(conn, stmt, rs);
        }
    }
}

SQL隱碼攻擊的問題

sql存在漏洞,會被攻擊導致資料洩露 SQL會被拼接 or

public class SQL隱碼攻擊 {
    public static void main(String[] args) {

        login("' or'1=1","' or'1=1");
    }

    public static void login(String username, String password){
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();
            stmt = conn.createStatement();
            String sql = "select * from users where name = '"+username+"' and password = '"+password+"'";
            rs = stmt.executeQuery(sql);
            while(rs.next()){
                System.out.println(rs.getString("name"));
                System.out.println(rs.getString("password"));
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JdbcUtils.release(conn, stmt, rs);
        }
    }
}

PreparedStatement

PreparedStatement 可以防止SQL隱碼攻擊,並且效率更高

1、新增

public class TestInsert {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection(); //獲取資料庫連線

            //區別
            //使用?佔位符 代替引數

            String sql = "INSERT INTO users(ID,NAME,PASSWORD,EMAIL,BIRTHDAY)"+
                    "VALUES(?,?,?,?,?)";
            ps = conn.prepareStatement(sql);//預編譯SQL,先寫sql,然後不執行

            //手動給引數賦值
            ps.setInt(1,6);
            ps.setString(2,"shisi");
            ps.setString(3,"123456");
            ps.setString(4,"8578482@qq.com");
            //注意點:sql.Date   資料庫   java.sql.Date
            //       util.Date   java   new Date().getTime() 獲得時間戳
            ps.setDate(5, new java.sql.Date(new Date().getTime()));

            //執行
            int i = ps.executeUpdate();
            if (i > 0) {
                System.out.println("插入成功");
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.release(conn, ps, rs);
        }
    }
}

2、刪除

public class TestDelete {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            conn = JdbcUtils.getConnection();

            String sql = "delete from users where id=?";

            ps = conn.prepareStatement(sql);
            ps.setInt(1, 5);
            int i = ps.executeUpdate();
            if ( i > 0){
                System.out.println("刪除成功");
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.release(conn,ps,null);
        }
    }
}

3、更新

public class TestUpdate {
    public static void main(String[] args) throws SQLException {
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            conn = JdbcUtils.getConnection();
            String sql = "UPDATE users SET name=?  where id =?";

            ps = conn.prepareStatement(sql);

            ps.setString(1, "kuangshen");
            ps.setInt(2, 5);

            int i = ps.executeUpdate();
            if (i > 0) {
                System.out.println("修改成功");
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.release(conn, ps, null);
        }
    }
}

4、查詢

public class TestSelect {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();
            //SQL
            String sql = "select * from users where id =?";

            ps = conn.prepareStatement(sql);
            ps.setInt(1, 6);//傳遞引數
            rs = ps.executeQuery();
            while (rs.next()) {
                System.out.println(rs.getString("name"));
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.release(conn, ps, rs);
        }
    }
}

5、防止SQL隱碼攻擊

public class SQL隱碼攻擊 {
    public static void main(String[] args) {

        login("' or'1=1","' or'1=1");
    }

    public static void login(String username, String password){
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();
            //PreparedStatement 防止SQL隱碼攻擊的本質,把傳遞進來的引數當做字串
            // 假設其中存在跳脫字元,直接忽略,比如說'
            String sql = "select * from users where name = ? and password = ?";
            stmt = conn.prepareStatement(sql);
            stmt.setString(1, username);
            stmt.setString(2, password);
            rs = stmt.executeQuery();
            while(rs.next()){
                System.out.println(rs.getString("name"));
                System.out.println(rs.getString("password"));
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JdbcUtils.release(conn, stmt, rs);
        }
    }
}

使用IDEA連線資料庫

事務

程式碼實現

1、關閉自動提交,開啟事務conn.setAutoCommit(false);

2、一組事務執行完畢,提交事務

3、可以在catch語句中顯式的定義 回滾語句,但預設失敗就會回滾

public class 事務 {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            con = JdbcUtils.getConnection();

            con.setAutoCommit(false);// 關閉自動提交,開啟事務

            String sql = "UPDATE account SET money = money - 100 where name = 'A'";
            ps = con.prepareStatement(sql);
            ps.executeUpdate();

            //int i = 1/0; // 讓 事務 不能完成

            sql = "UPDATE account SET money = money + 100 where name = 'B'";
            ps = con.prepareStatement(sql);
            ps.executeUpdate();

            con.commit();// 業務完成 提交
        }catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.release(con, ps, rs);
        }
    }
}

資料庫連線池

資料庫連線 ---- 執行完畢 ---- 釋放

連線 ---- 釋放 十分浪費系統資源

池化技術:準備一些預先的資源,過來就連線預先準備好的

----- 開門 ------ 業務員:等待 ------ 服務 ------

若 常用連線數:10個

​ 最小連線數:10個

​ 最大連線數:15個 業務最高承載上限

​ 等待超時:100ms

編寫連線池,實現一個介面 DataSource

開源資料來源實現

DBCP

C3P0

Druid:阿里巴巴

使用了這些資料庫連線池後,在專案開發中就不需要編寫連線資料庫的程式碼了

DBCP

需要的jar包

commons-dbcp-1,4.jar commons-pool-1.6.jar

工具類

public class JdbcUtils_DBCP {

    private static DataSource dataSource = null;

    static {
        try {
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
            Properties properties = new Properties();
            properties.load(in);

            //建立資料來源 工廠模式  --->建立
            dataSource = BasicDataSourceFactory.createDataSource(properties);

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //獲取連線資源
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();//從資料來源中獲取連線
    }

    //釋放連線資源
    public static void release(Connection conn, Statement stmt, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

配置檔案

#連線設定
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false
username=root
password=123456

#<!-- 初始化連線 -->
initialSize=10

#最大連線數量
maxActive=50

#<!-- 最大空閒連線 -->
maxIdle=20

#<!-- 最小空閒連線 -->
minIdle=5

#<!-- 超時等待時間以毫秒為單位 6000毫秒/1000等於60秒 -->
maxWait=60000
#JDBC驅動建立連線時附帶的連線屬性屬性的格式必須為這樣:【屬性名=property;】
#注意:"user" 與 "password" 兩個屬性會被明確地傳遞,因此這裡不需要包含他們。
connectionProperties=useUnicode=true;characterEncoding=UTF8

#指定由連線池所建立的連線的自動提交(auto-commit)狀態。
defaultAutoCommit=true

#driver default 指定由連線池所建立的連線的只讀(read-only)狀態。
#如果沒有設定該值,則“setReadOnly”方法將不被呼叫。(某些驅動並不支援只讀模式,如:Informix)
defaultReadOnly=

#driver default 指定由連線池所建立的連線的事務級別(TransactionIsolation)。
#可用值為下列之一:(詳情可見javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED

C3P0

需要的jar包

c3p0-0.9.5.5.jar 、mchange-commons-java-0.2.19.jar

工具類

public class JdbcUtils_C3P0 {

    private static DataSource dataSource = null;

    static {
        try {
            //建立資料來源 工廠模式  --->建立
            dataSource = new ComboPooledDataSource("MySQL");//配置檔案寫法

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //獲取連線資源
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();//從資料來源中獲取連線
    }

    //釋放連線資源
    public static void release(Connection conn, Statement stmt, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!--
c3p0的預設(預設)配置
如果在程式碼中"ComboPooledDataSource ds=new ComboPooledDataSource();"這樣寫就表示使用的是c3p0的預設(預設)-->
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&amp;characterEncoding=utf8&amp;uesSSL=true&amp;serverTimezone=UTC</property>
        <property name="user">root</property>
        <property name="password">123456</property>

        <property name="acquiredIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </default-config>

    <!--如果在程式碼中"ComboPooledDataSource ds=new ComboPooledDataSource("MySQL");"這樣寫就表示使用的是name是MySQL的配置-->
    <named-config name="MySQL">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&amp;characterEncoding=utf8&amp;uesSSL=true&amp;serverTimezone=UTC</property>
        <property name="user">root</property>
        <property name="password">123456</property>

        <property name="acquiredIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>
</c3p0-config>

結論

無論使用什麼資料來源,本質還是一樣的,DataSource介面不會變,方法也不會變

相關文章