JavaEEDay34數據庫

JavaEEDay34數據庫

@toc

筆記總結:2019-8-1

首先是用戶管理,即新建用戶並授予權限以及刪除用戶;
然後介紹事務;
接着介紹數據庫數據導入和導出的方法;
最後介紹使用 Java 連接數據庫;

  • 第一個代碼是連接數據庫的方式一;
  • 第二個代碼是連接數據庫和釋放資源的方式二:
  • 下面的代碼是基於第二種連接數據庫的方式進行增刪改查操作;
    最最後介紹了一種防止 SQL 注入的方式:PreparedStatement

一、DCL 操作:

一般是項目經理或者 DBA 進行管理;
即管理用戶:創建用戶,給予用戶操作數據的權限;

二、用戶管理:

  • 創建一個用戶,用戶名爲 zhangsan 所在數據庫的主機地址爲 localhost,初始化密碼:12345:
    create user "zhangsan"@"localhost" identified by "12345";
  • 授予新用戶權限,上面的 localhost 也可以是一個 IP 地址;
    grant all on hello.* to "zhangsan" @"localhost"; //將 hello 數據庫賦值權限給 zhangsan;
  • 刷新權限:flush privileges; 至此完成新用戶管理;
  • 取消授權:revoke all on hello.* from "zhangsan" @ "localhost";
  • 刪除用戶:drop user "zhangsan" @ "localhost";

三、 DTL 事務處理

當出現操作錯誤的時候,給予回退的機會;

  • 以銀行轉賬爲例,首先創建數據表:
create table bank(
  userID tinyint not null primary key auto_increment,
  money double(15,2),
  name varchar(30)
);
  • 插入部分數據,用於測試:
insert into bank(money,name) values(66,"zhangsan");
insert into bank(money,name) values(11,"lisi");
  • 開啓事務:一共兩種方式:開始事務是將這個表加載在內存中進行處理,沒有改變原來結構
    • 方式一:start transaction;
    • 方式二:set autocommit = 0;
start transaction;
  • 然後開始進行操作:
update bank set money = money - 55 where userID = 1;
update bank set money = money + 55 where userID = 2;
  • 操作完成之後,兩種確認方式:
    • 回滾,即操作中出現了異常,可以回滾到開始事務之前;命令爲:rollback;
    • 提交,即確認操作無誤,提交開始事務之後的所有操作;命令爲:commit;

四、數據庫導入導出

使用命令行導入導出數據庫,必須是沒有登錄數據庫的狀態下;

(一)導出數據庫:

導出的是一個.sql 文件,裏面包含了所有的數據庫操作信息,包括創建數據庫和插入數據;
在未登錄數據庫的狀態下操作以下語句:
mysqldump -uroot -p hello > hello.sql; 將 hello 數據庫導出保存爲 hello.sql 文件;
注意: 導出文件的保存位置爲當前操作的命令的文件目錄下,可以通過先修改操作命令的目錄進行保存位置的修改;

(二)導入數據庫:

前提: 必須在數據庫中創建一個數據庫用於接收導入的數據庫,然後退出數據庫,操作以下語句:
mysql -uroot -p receiveHello < hello.sql; 將 hello.sql 數據庫導入到 receiveHello 數據庫中;

五、Java 連接數據庫方式

JDBC:Java Database Connectivity 操作數據庫的規範;

JDBC 主要通過接口實現,組成 JDBC 中兩個包:java.sql 和 javax.sql,以上兩個包是 JavaSE 中包含的,但是需要導入 JDBC 的實現類纔可以使用,該實現類是由第三方數據庫提供商完成;

JDBC 主要的接口和類:

  • Driver 接口:連接數據庫的驅動 API;
  • DriverManager 類:驅動管理類,負責驅動的註冊(加載),獲取數據庫連接;
  • Statement 接口:負責 SQL 語句的執行;
    • PreparedStatement 接口:負責 SQL 語句的預處理;
  • ResultSet 接口:處理查詢數據庫的結果集;

(一)通過 JDBC 連接 MySQL

  • 需要一:確定數據庫的 URL;

    • 例如:jdbc:mysql://localhost:3306/hello 協議:子協議://IP:端口號/數據庫名?參數
      • 協議:JDBC 總協議;
      • 子協議:目前使用的是連接 MySQL 數據庫的協議;
      • IP:是數據庫服務器的 IP 地址,localhost 表示本機的 IP 地址;
      • 端口號:3306 MySQL 默認的端口號,可以修改;
      • 數據庫名:目前連接的操作的數據庫是哪一個;
      • 參數:通常爲:useUnicode = true characterEncoding = utf-8;
  • 需要二:連接數據庫需要用戶名和密碼:

具體的連接操作方式:

  • 方式一:直接在代碼中寫入要操作的數據庫以及用戶名和密碼信息,但是代碼不可複用;

    • 步一:通過 Class.forName(“com.mysql.jdbc.cj.Driver”); 註冊驅動;
    • 步二:準備 URL:jdbc:mysql://數據庫IP:3306/數據庫名 ? serverTimezone = GMT%2B8;
    • 步三:通過 DriverManager 連接對象:即調用:DriverManager.getConnection(url, 用戶名, 密碼)
    • 步四:關閉數據庫:連接對象.close();
  • 方式二:使用.properties 文件

    • 步一:建立.properties 文件,內容爲:
driver = com.mysql.jdbc.cj.Driver
user = 用戶名
password = 密碼
url = jdbc:mysql://IP:3306/數據庫名 ? serverTimezone = GMT%2B8
  • 步二:建立 JdbcUtil.java 類,裏面包含數據庫連接和關閉的方式

    • 準備工作:
    • 步一:讀取配置文件,即.properties 文件:Properties p = new Properties(); 並將其加入到內存之中:InputStream is = new FileInputStream(文件路徑)
    • 步二:利用 Properties 裏面的 load 方法:p.load(is)
    • 步三:通過 properties 類對象獲取想要的數據:p.getProperty("url");等等
    • 步四:加載類文件:Class.forName(driver)
    • 步五:關閉資源:在 finally 裏面加入:is.close();
    • 進行連接:
      • 步一:建立新的連接方法:getConnection(),返回值爲Connection類型
      • 步二:建立連接:DriveManager.getConnection(url, user, password)
      • 步三:返回建立的連接:return connection;
    • 關閉連接:
      • 類一:沒有結果集的關閉(針對:增、刪、改操作)
        • 步一:新建的關閉方法中參數爲 Connection 和 Statement 類型;
        • 步二:逐個關閉資源:先關閉 statement,後關閉 Connection;
      • 類二:含有結果集的關閉(針對:查操作)
        • 步一:方法中的參數爲:Connection、Statement、ResultSet 類型;
        • 步二:逐個關閉資源:順序爲:Statement、Connection、ResultSet
  • 具體實現類:(以創建數據表和查詢數據表爲例)

    • 創建表格:同刪除、修改操作,僅僅是 SQL 語句不同
      • 步一:通過已經創建好的工具類創建數據庫連接對象:Connection connection = jdbcUtil.getConnection()
      • 步二:獲取 Statement(SQL 語句運輸者):Statement statement = connection.createStatement();
      • 步三:準備 SQL 語句,語句的最後不需要分號:String sql = "";
      • 步四: 通過 statement 執行 SQL 語句:statement.executeUpdate(sql);
      • 步五:關閉資源:JdbcUtil.closeConnection(conn.., sta);
    • 查詢信息:即查操作
      • 步一:連接數據庫,同上;
      • 步二:獲取 Statement;
      • 步三:準備 SQL 語句;
      • 步四:通過 statement 執行 SQL 語句:ResultSet set = statement.executeQuery(sql);
      • 步五:關閉資源:JdbcUtil.closeConnectionWithResult(con.., state.., set)

下面代碼中: Class.forName("com.mysql.jdbc.Driver");
這句話首先會加載com.mysql.jdbc.Driver類文件到內存當中,而在這個類文件中有一下這段代碼
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException(“Can’t register driver!”);
}
}
這個代碼塊是一個靜態代碼塊,會在類文件加載到內存時,直接運行
而在這個靜態代碼塊中,完成了以下這些事情:
1. 創建的MySQL連接的Java程序的JDBC.Driver對象
2. 將這個創建的Driver對象,註冊到java.sql.DriverManager裏面

這樣做到好處:
簡化代碼的邏輯,提高效率

package jdbc.connection;

import org.junit.jupiter.api.Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * @author GJXAIOU
 * @create 2019-08-01-14:14
 */
public class JdbcConnection {
    @Test
    public void connection()  {
        // 1.註冊驅動 JDBC 連接MySQL
        try {
         Class.forName("com.mysql.cj.jdbc.Driver");

        // 2.準備url,是JDBC所要連接MySQL數據庫的URL,後面參數爲設置時區
        String url = "jdbc:mysql://localhost:3306/day34jdbc?serverTimezone = GMT%2B8" ;

        // 3.通過DriverManager連接對象
        Connection con = null;
        con = DriverManager.getConnection(url, "root", "GJXAIOU");
        System.out.println(con);

        // 4.關閉數據庫連接,釋放資源
        con.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

程序運行結果:com.mysql.cj.jdbc.ConnectionImpl@15761df8

  • 方式二:將數據庫的具體信息寫入:.properties 文件中,然後使用 Java 代碼讀取文件中信息,更改數據庫信息只要在.properties 文件中修改就行;
    db.properties
driver = com.mysql.cj.jdbc.Driver 
url = jdbc:mysql://localhost:3306/day34jdbc?serverTimezone = GMT%2B8 
user = root 
password = GJXAIOU

這裏 URL 後面對應的重要參數見下面:不同參數之間用 &進行分割

參數名稱 參數說明 缺省值 最低版本要求
user 數據庫用戶名(用於連接數據庫) 所有版本
password 用戶密碼(用於連接數據庫) 所有版本
useUnicode 是否使用Unicode字符集,如果參數characterEncoding設置爲gb2312或gbk,本參數值必須設置爲true false 1.1g
characterEncoding 當useUnicode設置爲true時,指定字符編碼。比如可設置爲gb2312或gbk false 1.1g
autoReconnect 當數據庫連接異常中斷時,是否自動重新連接? false 1.1
autoReconnectForPools 是否使用針對數據庫連接池的重連策略 false 3.1.3
failOverReadOnly 自動重連成功後,連接是否設置爲只讀? true 3.0.12
maxReconnects autoReconnect設置爲true時,重試連接的次數 3 1.1
initialTimeout autoReconnect設置爲true時,兩次重連之間的時間間隔,單位:秒 2 1.1
connectTimeout 和數據庫服務器建立socket連接時的超時,單位:毫秒。 0表示永不超時,適用於JDK 1.4及更高版本 0 3.0.1
socketTimeout socket操作(讀寫)超時,單位:毫秒。 0表示永不超時 0 3.0.1

JdbcUtil.java 數據庫連接自定義工具類

  • 自定義JDBC工具類
  1. 加載驅動
  2. 獲取連接對象
  3. 關閉連接

把連接數據庫需要的信息,都保存在一個文件中,這個文件是一個properties文件

package jdbc.connection;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**實現數據庫連接和資源關閉操作
 * @author GJXAIOU
 * @create 2019-08-01-15:16
 */
public class JdbcUtil {
   private static String url = null;
   private static String user = null;
   private static String password = null;
   private static String driver = null;
   private static InputStream inputStream = null;

   // 這裏利用靜態代碼塊的特徵,在類文件加載到內存的時候,
   // 就會執行在靜態代碼塊中的代碼
    static {
       // 1.讀取配置文件信息,即讀取properties文件
       Properties properties = new Properties();
       // 如果一個properties文件加載到內存中,需要藉助IO流;
       try {
           inputStream = new FileInputStream(
                   "E:\\Program\\Java\\Project\\VideoClass\\JavaEEDay34" +
                           "\\src\\jdbc\\connection\\db.properties");
       } catch (FileNotFoundException e) {
           e.printStackTrace();
       }

       // 2.利用properties裏面的load方法
       try {
           properties.load(inputStream);
       } catch (IOException e) {
           e.printStackTrace();
       }

       // 3.可以通過properties類對象,獲取想要的數據
       url = properties.getProperty("url");
       user = properties.getProperty("user");
       password = properties.getProperty("password");
       driver = properties.getProperty("driver");

       // 4.加載類文件
       try {
           Class.forName(driver);
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
           System.out.println("驅動加載失敗");
       }finally {
           // 關閉文件連接
           if (inputStream != null){
               try {
                   inputStream.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
       }
   }


    /**
     * 獲取數據庫連接對象
     * @return Connection對象
     */
   public static Connection getConnection(){
       Connection connection = null;
       try {
           connection = DriverManager.getConnection(url, user, password);
       } catch (SQLException e) {
           e.printStackTrace();
       }

       return  connection;
   }

    /**
     * 關閉數據庫連接,釋放statement
     * @param connection 數據庫連接對象
     * @param statement statement對象
     */
    public static void closeConnection(Connection connection, Statement statement){

        try {
            if (statement != null){
                statement.close();
            }

            if (connection != null){
            connection.close();
            }
        }catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 關閉帶有結果集的查詢語句資源
     * @param connection 數據庫連接對象
     * @param statement statement 對象
     * @param set 結果集
     */
    public static void closeConnectionWithResult(Connection connection, Statement statement, ResultSet set){
        if (statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (connection != null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (set != null){
            try {
                set.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    }


}


jdbcRealizationTest.java
實現常見的增刪改查操作:

package jdbc.connection;

import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


/**使用JdbcUtil工具類創建並操作數據表
 * @author GJXAIOU
 * @create 2019-08-01-16:07
 */
public class JdbcRealizationTest {

    /**
     * 通過JDBC創建數據表
     */
    @Test
    public void createTable()  {
        // 1.通過已經封裝好的JdbcUtil工具類,獲取數據庫的連接對象
        Connection connection = JdbcUtil.getConnection();

        try {
            // 2.獲取Statement,即SQL語句的運輸者,作用是將SQL語句運輸到MySQL中,讓MySQL運行
            Statement statement = connection.createStatement();

            // 3.準備好SQL語句,語句最後不用分號
            String sql = "create table person(" +
                    "  id   tinyint  not null  primary key  auto_increment," +
                    "  name  char(5) not null," +
                    "  gender  char(1)," +
                    "  score  decimal(4, 2)," +
                    "  home  enum(\"江蘇\", \"上海\", \"杭州\", \"蘇州\")," +
                    "  hobby  set(\"游泳\", \"打球\", \"跑步\"))";

            // 4.通過statement執行SQL語句
            int count = statement.executeUpdate(sql);

            // 5.查看創建的結果
            System.out.println("影響的行數" + count);

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

    }

    /**
     * 使用statement執行DML語句-- insert into
     */
    @Test
    public void testInsert(){
        // 1.建立數據庫連接
        Connection connection = JdbcUtil.getConnection();

        Statement statement = null;
        try {
            // 2.獲取到Statement
            statement = connection.createStatement();

            // 3.準備SQL語句
            String sql1 = "insert into person(name, gender, score, home, hobby) values(\"張三\", \"男\", 98.23, 2, 3)";
            String sql2 = "insert into person(name, gender, score, home, hobby) values(\"李四五\", \"女\",99.00,\"江蘇\", \"游泳,打球\")";

            // 4.通過statement執行SQL語句
            int count1 = statement.executeUpdate(sql1);
            int count2 = statement.executeUpdate(sql2);

            // 5.打印影響的行數
            System.out.println("改變的行數:" + (count1 + count2));
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            // 6.關閉所有資源:connection 是連接數據庫的資源,statement是引用程序到MySQL之間的SQL語句運輸者,都是資源
            JdbcUtil.closeConnection(connection, statement);
        }
    }

    @Test
    /**
     * 使用statement刪除數據庫中一條數據
     */
    public void testDelete() {
        // 1.建立數據庫連接
        Connection connection = JdbcUtil.getConnection();

        Statement statement = null;
        try {
            // 2.獲取到statement
            statement = connection.createStatement();

            // 3.準備SQL語句
            String sql = "delete from person where id = 1";

            // 4.使用statement執行SQL語句
            int count = statement.executeUpdate(sql);

            // 5.輸出影響的行數
            System.out.println("改變的行數:" + count);
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            // 6.關閉所有資源
            JdbcUtil.closeConnection(connection, statement);
        }
    }

    /**
     * 使用statement修改數據庫中一條數據
     */
    public void testUpdate(){
        // 1.建立數據庫連接
        Connection connection = JdbcUtil.getConnection();

        Statement statement = null;
        try {
            // 2.獲取到statement
            statement = connection.createStatement();

            // 3.準備SQL語句
            String sql = "update person set name = '李四' where id = 2";

            // 4.使用statement執行SQL語句
            int count = statement.executeUpdate(sql);

            // 5.輸出影響的行數
            System.out.println("改變的行數" + count);
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            // 6.關閉所有的資源
            JdbcUtil.closeConnection(connection, statement);
        }
    }

    /**
     * 使用statement查詢數據庫中數據並返回結果集set
     */
    @Test
    public void testSelect(){
        // 1.連接數據庫
        Connection connection = JdbcUtil.getConnection();

        Statement statement = null;
        // 查詢語句返回的結果集對象
        ResultSet set = null;
        try {
            // 2.獲取到statement
            statement = connection.createStatement();

            // 3.準備SQL語句
            String sql = "select * from person";

            // 4.通過statement執行SQL語句, 獲得查詢結果集
            set = statement.executeQuery(sql);

            // 5.輸出影響的行數
            while(set.next()){
                int id = set.getInt("id");
                String name = set.getString("name");
                String gender = set.getString("gender");
                BigDecimal score = set.getBigDecimal("score");
                String home = set.getString("home");
                String hobby = set.getString("hobby");

                System.out.println("id :" + id + " name :" + name + " gender :" + gender +
                        " socore :" + score + " home :" + home + " hobby :" + hobby);


            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtil.closeConnectionWithResult(connection, statement, set);
        }
    }


}

第一個 Test 結果:影響的行數0
第二個 Test 結果:改變的行數:2
第三個 Test 結果:改變的行數:1
第四個 Test 結果:id :2 name :李四五 gender :女 socore :99.00 home :江蘇 hobby :游泳,打球

上面的 set.next()方法使用說明: 該 next()方法屬於 RestltSet 中

boolean next()
      throws [SQLException](../../java/sql/SQLException.html "class in java.sql")

將光標從當前位置向前移動一行。 `ResultSet`光標最初位於第一行之前; 第一次調用方法`next`使第一行成爲當前行; 第二個調用使第二行成爲當前行,依此類推。

當調用`next`方法返回`false`時,光標位於最後一行之後。 任何調用需要當前行的`ResultSet`方法將導致拋出`SQLException` 。 如果結果集類型爲`TYPE_FORWARD_ONLY` ,這是他們指定的JDBC驅動程序實現是否會返回供應商`false`或拋出一個`SQLException`上的後續調用`next` 。

如果當前行的輸入流已打開,則對方法`next`的調用將隱式關閉它。 當讀取新行時, `ResultSet`對象的警告鏈將被清除。

結果

`true`如果新的當前行有效; `false`如果沒有更多的行

異常

`[SQLException](../../java/sql/SQLException.html "class in java.sql")` - 如果發生數據庫訪問錯誤,或者在關閉的結果集上調用此方法



(二)上面總結:JDBC 核心 API

  • Driver接口:

    • connect(url, propertie);
      • url: JDBC連接數據庫(目前爲 MySQL)URL
        標準格式爲:jdbc:mysql://localhost:3306/javaee1707?useSSL=true
      • propertie:
        連接數據庫的屬性,主要包含的是數據庫的用戶名和密碼
  • DriverManager類:是驅動管理類,用於管理【加載/註冊】過的驅動程序

    • registerDriver(driver); 註冊驅動程序
    • Connection getConnection(url, user, password);返回值是獲取一個數據庫的連接對象,需要的參數是存在JDBC協議的URL, 數據庫用戶名 和 密碼
  • Connection接口:

    • Statement createStament(); 創建一個Statement的實現類對象(因爲 Statement 是接口)
    • PreparedStatement preparedStatement(String sql); 獲取到一個PreparedStatement SQL語句預處理對象
    • CallableStatmenet preparedCall(String sql); 瞭解
  • Statement接口:

    • int executeUpdate(String sql); 執行給定的SQL語句,通常用來執行DDL,DML,返回影響數據的行數
    • ResultSet executeQuery(String sql); 執行給定的SQL語句 DQL 查詢語句,返回數據結果集
  • PreparedStatement接口:

    • int executeUpdate(); 執行預處理的SQL語句,通常用來執行DDL,DML,返回影響數據的行數
    • ResultSet executeQuery(); 執行預處理的SQL語句 DQL 查詢語句,返回數據結果集
      預處理有利於防止 SQL注入
  • ResultSet接口: 查詢語句的數據結果集:

    • boolean next(); 得到當前數據行,並且光標指向下一個數據行,如果沒有數據行,返回false
    • getXXX(String “字段名”); 獲取指定數據類型的字段數據

使用 PreparedStatement 防止 SQL 注入代碼:

package jdbc.connection;

import org.junit.jupiter.api.Test;

import java.sql.*;

/**使用preparedStatement防止SQL注入,因爲在獲取preparedStatement時候,已經對SQL語句進行了預處理
 * @author GJXAIOU
 * @create 2019-08-01-19:19
 */
public class PreparedStatementTest {
    @Test
    public void loginInTest(){
        // 1.建立連接
        Connection connection = null;
        connection= JdbcUtil.getConnection();

        // 2.準備預處理SQL語句,其中?爲佔位符,且順序從1開始
        String sql = "select * from person where id = ? and name = ?";

        // 3.獲取preparedStatement對象
        PreparedStatement preparedStatement = null;
        try {
            preparedStatement = connection.prepareStatement(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        // 4.準備參數
        try {
            preparedStatement.setInt(1,2);
            preparedStatement.setString(2,"李四五");
        } catch (SQLException e) {
            e.printStackTrace();
        }

        // 5.準備接收查詢結果
        ResultSet set = null;
        try {
            set = preparedStatement.executeQuery();
            System.out.println(set);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        // 6.判斷輸入的參數在數據表中有沒有
        try {
            if (set.next()){
                System.out.println("登錄成功");
            }else{
                System.out.println("登錄失敗");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtil.closeConnectionWithResult(connection, preparedStatement, set);
        }
    }
}

程序輸出的結果:
com.mysql.cj.jdbc.result.ResultSetImpl@23529fee
登錄成功
此時對應的數據庫中數據爲:

mysql> select * from person;
+----+-----------+--------+-------+--------+---------------+
| id | name      | gender | score | home   | hobby         |
+----+-----------+--------+-------+--------+---------------+
|  2 | 李四五    || 99.00 | 江蘇   | 游泳,打球     |
+----+-----------+--------+-------+--------+---------------+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章