JDBC詳解學習筆記

JDBC詳解學習筆記

JDBC簡介

架構時——沒有什麼是加一層解決不了的,若是有,就再加一層。html

如tomcat集羣上面的Nginx,Nginx集羣上面的LVS.java

JDBC是數據庫驅動的接口規範,是SUN公司將來簡化開發人員對數據庫的統一操做而提供的一個規範。即Java數據庫鏈接,(Java Database Connectivity,簡稱JDBC)。mysql

這些規範有具體的數據庫廠商去實現,而開發人員只須要掌握JDBC接口操做便可。sql

沒有JDBC時:數據庫

數據庫驅動.png

經過JDBC時:apache

jdbc位置.png

驅動包.png

JDBC操做步驟——賈璉欲執事

口訣:賈璉欲執事——加連預執釋tomcat

  • 加載驅動
  • 鏈接數據庫
  • 建立預編譯語句
  • 執行sql
  • 釋放資源

jdbc.png

url鍵值對 描述
useUnicode=true 支持中文編碼
characterEncoding=utf8 設置字符集編碼爲utf8
useSSL=true 使用安全鏈接

JDBC對象解析

oracle沒有數據庫概念,一個模式就是一個庫安全

driver.png

url.png

statement.png

resultset.png

statement對象

statement1.png

statement2.png

statement3.png

JDBC工具類示例

public class JDBCTest {
    public static void main(String[] args) {
        //查詢、插入展現
        select();
        insert();
    }

    public static void select() {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtil.getConnection();
            st = conn.createStatement();
            String sql = "select * from user where 1=1";
            rs = st.executeQuery(sql);
            while (rs.next()) {
                //參數爲字段名
                System.out.println(rs.getString("name"));
            }
        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            JDBCUtil.release(rs, st, conn);
        }
    }

    public static void insert() {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtil.getConnection();
            st = conn.createStatement();
            String sql = "insert into user(`name`,age)values ('安安',1)";
            int i = st.executeUpdate(sql);
            if (i > 0) {
                System.out.println("插入成功");
            }
        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            JDBCUtil.release(rs, st, conn);
        }

    }
}

//JDBC工具類,返回的Connection至關於一個數據庫操做對象,能夠用於事務等處理
public class JDBCUtil {

    private static String URL = null;
    private static String USERNAME = null;
    private static String PASSWORD = null;
    private static String DIRVIER = null;

    static {
        //經過classloader去獲取配置文件的輸入流
        InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");
        //再建立properties對象去load輸入流
        Properties properties = new Properties();
        try {
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        URL = properties.getProperty("roy.mysql.url");
        USERNAME = properties.getProperty("roy.mysql.username");
        PASSWORD = properties.getProperty("roy.mysql.password");
        DIRVIER = properties.getProperty("roy.mysql.driver");
        try {
            Class.forName(DIRVIER);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(URL, USERNAME, PASSWORD);
    }

    public static void release(ResultSet rs, Statement st, Connection conn) {
        try {
            if (rs != null) {
                rs.close();
            }
            if (st != null) {
                st.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
roy.mysql.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
roy.mysql.username=root
roy.mysql.password=123456
roy.mysql.driver=com.mysql.cj.jdbc.Driver
#注意properties文件就是String類型的鍵值對,所以不須要再加""雙引號

sql注入

所謂SQL注入,就是經過把SQL命令插入到Web表單遞交或輸入域名或頁面請求的查詢字符串,最終達到欺騙服務器執行惡意的SQL命令。服務器

咱們永遠不要信任用戶的輸入,咱們必須認定用戶輸入的數據都是不安全的,咱們都須要對用戶輸入的數據進行過濾處理。mybatis

ssi.png
ssi2.png

PreparedStatement——防止sql注入

PreparedStatement能夠防止sql注入,其防止sql注入的本質是PreparedStatement會把傳進來的參數當作字符,假如其中存在轉義符如''引號這種,會被直接轉義處理;且由於預編譯因此效率更高。

DB中執行的SQL爲

select file from file where name = '\'test\' or 1=1'

把整個參數用引號包起來,並把參數中的引號做爲轉義字符,從而避免了參數也做爲條件的一部分

PreparedStatement是如何防止SQL注入的?

示例

public class PreparedStatementTest {
    public static void main(String[] args) {
        select();
        insert("白衣阿風",28);
    }

    public static void select() {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtil.getConnection();
            String sql = "select * from user where 1=1 and `name` =?";
            st = conn.prepareStatement(sql);
            st.setString(1,"艾米");
            rs = st.executeQuery();
            while (rs.next()) {
                //參數爲字段名
                System.out.println(rs.getString("email"));
            }
        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            JDBCUtil.release(rs, st, conn);
        }
    }

    public static void insert(String name,Integer age) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtil.getConnection();
            String sql = "insert into user(`name`,age)values (?,?)";
            st = conn.prepareStatement(sql);
            st.setString(1,name);
            st.setInt(2,age);
            int i = st.executeUpdate();
            if (i > 0) {
                System.out.println("插入成功");
            }
        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            JDBCUtil.release(rs, st, conn);
        }

    }
}

Idea鏈接數據庫

idea鏈接數據庫.png
idea鏈接數據庫2.png
idea鏈接數據庫3.png
idea鏈接數據庫4.png
idea鏈接數據庫5.png

JDBC事務

事務.png

代碼示例

public class TranscationTest {
    public static void main(String[] args) {
        transcation("艾米哈珀","哈米國王",new BigDecimal("1500000"));
    }

    public static void transcation(String seller,String buyer, BigDecimal money) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtil.getConnection();
            //關閉事務自動提交,這時候表示自動開啓事務
            conn.setAutoCommit(false);
            String sql1 = "update t_bank_account set money=money+? where `name`=?";
            st = conn.prepareStatement(sql1);
            st.setBigDecimal(1,money);
            st.setString(2,seller);
            int i = st.executeUpdate();
            //測試事務異常
//            System.out.println(1/0);
            String sql2 = "update t_bank_account set money=money -? where `name`=?";
            st = conn.prepareStatement(sql2);
            st.setBigDecimal(1,money);
            st.setString(2,buyer);
            int j = st.executeUpdate();
            if (j > 0 && i > 0) {
                System.out.println("交易成功");
            }
            conn.commit();
        } catch (Exception throwables) {
            try {
                conn.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            throwables.printStackTrace();
        } finally {
            JDBCUtil.release(rs, st, conn);
        }
    }
}

數據庫鏈接池——池化技術

池化技術的出如今與每次建立鏈接和釋放鏈接都很是的耗費資源,現實場景中,咱們更好的處理方法是經過池化技術來優化和提升這種每次都要建立釋放資源的事情,就像咱們的工廠生產產品或者銀行對外提供服務,工廠不能有產品要生產才招一個臨時工,生產完就解僱掉;銀行不能每次要處理業務就開個門或者說開個窗口,處理好了就把處理窗口關閉了,二者在現實中咱們都能觀察到:工廠是有必定數量的穩定工人的,而銀行的對外業務窗口也是穩定的,這就是池化技術。

數據庫鏈接池的本質是實現一個DataSource接口。

數據庫鏈接池.png

開源數據源實現

  • DBCP
  • C3P0
  • Druid(德魯伊)阿里巴巴開源

使用了這些數據庫鏈接池後,咱們在項目開發中就不須要編寫鏈接數據庫的代碼了。

數據庫鏈接池須要的jar包依賴.png

DBCP及C3P0示例

maven依賴

<!--		dbcp鏈接池依賴,commons-dbcp內部已經依賴了commons-pool-->
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
		</dependency>
		<dependency>
			<groupId>commons-pool</groupId>
			<artifactId>commons-pool</artifactId>
			<version>1.5.4</version>
		</dependency>
<!--		c3p0鏈接依賴,c3p0內部已經依賴了mchange-commons-java-->
		<dependency>
			<groupId>com.mchange</groupId>
			<artifactId>c3p0</artifactId>
			<version>0.9.5.2</version>
		</dependency>

dbcp配置文件——dbcp.properties

driverClassName=com.mysql.cj.jdbc.Driver
#url
url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
#用戶名
username=root
#密碼
password=123456
#初試鏈接數
initialSize=30
#最大活躍數
maxTotal=30
#最大idle數
maxIdle=10
#最小idle數
minIdle=5
#最長等待時間(毫秒)
maxWaitMillis=1000
#程序中的鏈接不使用後是否被鏈接池回收(該版本要使用removeAbandonedOnMaintenance和removeAbandonedOnBorrow)
#removeAbandoned=true
removeAbandonedOnMaintenance=true
removeAbandonedOnBorrow=true
#鏈接在所指定的秒數內未使用纔會被刪除(秒)(爲配合測試程序才配置爲1秒)
removeAbandonedTimeout=1

c3p0配置文件——c3p0-config.xml

<c3p0-config>
    <!-- 使用默認的配置讀取鏈接池對象 -->
    <default-config>
        <!--  鏈接參數 -->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/mybatis_plus?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=GMT%2B8</property>
        <property name="user">root</property>
        <property name="password">123456</property>

        <!-- 鏈接池參數 -->
        <!--初始化申請的鏈接數量-->
        <property name="initialPoolSize">5</property>
        <!--最大的鏈接數量-->
        <property name="maxPoolSize">10</property>
        <!--超時時間-->
        <property name="checkoutTimeout">3000</property>
    </default-config>

<!--    其餘如oracle、mysql等的配置,要使用時只須要指定要加載的配置名問參數,不傳則使用默認配置-->
    <named-config name="mysql">
        <!--  鏈接參數 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <!-- 鏈接池參數 -->
        <property name="initialPoolSize">5</property>
        <property name="maxPoolSize">8</property>
        <property name="checkoutTimeout">1000</property>
    </named-config>
</c3p0-config>
public class C3P0Util {
    private static DataSource dataSource = null;

    static {
        //經過classloader去獲取配置文件的輸入流

        try {
             dataSource = new ComboPooledDataSource();
             //不指定配置名稱則讀取默認的c3p0配置
            //xml文件不須要讀取成流便能讀取文件
//            dataSource = new ComboPooledDataSource("mysql");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }

    public static void release(ResultSet rs, Statement st, Connection conn) {
        try {
            if (rs != null) {
                rs.close();
            }
            if (st != null) {
                st.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class DBCPUtil {
    private static DataSource dataSource = null;

    static {
        //經過classloader去獲取配置文件的輸入流
        InputStream is = DBCPUtil.class.getClassLoader().getResourceAsStream("dbcp.properties");
        //再建立properties對象去load輸入流
        Properties properties = new Properties();
        //Properties文件注意要加載流,不然讀不到文件會報錯以下
        //org.apache.commons.dbcp.SQLNestedException: Cannot create JDBC driver of class '' for connect URL 'null'
        try {
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
             dataSource = BasicDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }

    public static void release(ResultSet rs, Statement st, Connection conn) {
        try {
            if (rs != null) {
                rs.close();
            }
            if (st != null) {
                st.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class ConnectionPoolTest {
    public static void main(String[] args) {
        c3p0();
        dbcp();
    }

    public static void c3p0() {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = C3P0Util.getConnection();
            st = conn.createStatement();
            String sql = "select * from user where 1=1";
            rs = st.executeQuery(sql);
            while (rs.next()) {
                //參數爲字段名
                System.out.println(rs.getString("name"));
            }
        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            JDBCUtil.release(rs, st, conn);
        }
    }

    public static void dbcp() {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = DBCPUtil.getConnection();
            st = conn.createStatement();
            String sql = "insert into user(`name`,age)values ('安安啊',1)";
            int i = st.executeUpdate(sql);
            if (i > 0) {
                System.out.println("插入成功");
            }
        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            JDBCUtil.release(rs, st, conn);
        }

    }
}

參考文獻

[狂神MySQL基礎](

相關文章
相關標籤/搜索