教程:使用PreparedStatement訪問DLA

xumingmingv發表於2018-11-30

大家都知道PreparedStatement相比手動拼寫SQL有很多好處,比如:

  • 它會自動做敏感字元的轉義,防止SQL Injection攻擊。
  • 它可以幫助我們動態執行SQL,Prepare一次之後,後續執行只需要替換引數就可以了。
  • 它可以幫助以OOP的方式來寫SQL相關相關程式碼,因為我們是通過 PrepareSteatement.setXxx()的方式而不是字串拼接的方式來設定引數。

等等,PreparedStatement的好處還有很多,更多可以參考這篇《JDBC Statement vs PreparedStatement – SQL Injection Example》, 上面說的很詳細。

今天我們Data Lake Analytics也引入了對PreparedStatement的支援, 今天給大家演示一下,如何用 PreparedStatement 來訪問 DLA.

準備工作

為了準備演示的環境,大家可能要先熟悉一下怎麼在DLA上面建立資料庫,建立表,在雲棲社群上面通過關鍵字 DLA 可以搜尋到很多文章,比如建立訪問RDS的DLA庫可以看這篇文章: 教程:使用Data Lake Analytics讀/寫RDS資料 .

我們在這篇教程裡面要演示的表的結構是這樣的:

CREATE EXTERNAL TABLE `type_test` (
    `id` bigint(20) NULL DEFAULT NULL COMMENT ``,
    `tinyint_col` tinyint(4) NULL DEFAULT NULL COMMENT ``,
    `int_col` int(11) NULL DEFAULT NULL COMMENT ``,
    `char_col` char(10) NULL DEFAULT `NULL` COMMENT ``,
    `varchar_col` varchar(10) NULL DEFAULT `NULL` COMMENT ``,
    `float_col` double NULL DEFAULT NULL COMMENT ``,
    `double_col` double NULL DEFAULT NULL COMMENT ``,
    `decimal_col` decimal(20, 4) NULL DEFAULT NULL COMMENT ``,
    `time_col` time(3) NULL DEFAULT `NULL` COMMENT ``,
    `datetime_col` datetime(6) NULL DEFAULT NULL COMMENT ``,
    `timestamp_col` timestamp(6) NOT NULL COMMENT ``,
    `string_col` varchar(100) NULL DEFAULT `NULL` COMMENT ``,
    `date_col` date NULL DEFAULT `NULL` COMMENT ``,
    `smallint_col` smallint(6) NULL DEFAULT NULL COMMENT ``,
    `mediumint_col` int NULL DEFAULT NULL COMMENT ``,
    `bigint_col` bigint(20) NULL DEFAULT NULL COMMENT ``
)
COMMENT ``

裡面的資料是這樣的:

> select * from type_testG;
*************************** 1. row ***************************
           id: 1
  tinyint_col: 2
      int_col: 3
     char_col: hello1
  varchar_col: 5
    float_col: 6.01
   double_col: 7.02
  decimal_col: 8.0300
     time_col: 01:02:01.000
 datetime_col: 1986-10-01 01:02:03.000000
timestamp_col: 2018-11-29 14:04:28.305523
   string_col: hello
     date_col: 2018-09-07
 smallint_col: NULL
mediumint_col: NULL
   bigint_col: 2
*************************** 2. row ***************************
           id: 1111111
  tinyint_col: 127
      int_col: 4
     char_col: hello2
  varchar_col: 5555555555
    float_col: 9996.01
   double_col: 7777777.02
  decimal_col: 888888888.0300
     time_col: 01:02:02.000
 datetime_col: 1986-10-01 01:02:03.000000
timestamp_col: 2018-11-29 14:36:05.486738
   string_col: hello
     date_col: 2018-09-08
 smallint_col: NULL
mediumint_col: NULL
   bigint_col: 1111112
*************************** 3. row ***************************
           id: 3
  tinyint_col: 127
      int_col: 5
     char_col: hello3
  varchar_col: 5555555555
    float_col: 9997.01
   double_col: 7777777.02
  decimal_col: 888888888.0300
     time_col: 01:02:03.000
 datetime_col: 1986-10-01 01:02:03.000000
timestamp_col: 2018-11-20 10:31:40.112000
   string_col: hello
     date_col: 2018-09-09
 smallint_col: 3
mediumint_col: NULL
   bigint_col: 4
3 rows in set (0.00 sec)

Java

因為我們相容MySQL協議,我們使用MySQL的JDBC驅動來訪問DLA。值得注意的是 MySQL JDBC驅動支援客戶端 PrepareStatement (也是很厲害啊), 要使用服務端的PreparedStatement的功能,需要在JDBC連線串的末尾加上 useServerPrepStmts=true 的引數,如下:

import java.sql.*;

public class DLAPrepStmtMain {
    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");

        String sql = "select * from type_test where `key` = ?";
        try (Connection dlaConn = DriverManager.getConnection(
                "jdbc:mysql://1013022312866336-fake.cn-hangzhou.datalakeanalytics.aliyuncs.com:10000/yourdb?useServerPrepStmts=true",
                "your-username",
                "your-password");
             PreparedStatement stmt = dlaConn.prepareStatement(sql)) {
            stmt.setString(1, "key01");
            ResultSet rs = stmt.executeQuery();
            while (rs.next()) {
                for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
                    System.out.print(rs.getString(i + 1) + ", ");
                }
                System.out.println();
            }
        }
    }
}

執行會發現,控制檯會正確列印出結果:

1, 2, 3, hello1, 5, 6.01, 7.02, 8.03, 01:02:01, 1986-10-01 01:02:03.0, 2018-11-29 14:04:28.305, hello, 2018-09-07, null, null, 2,

Php

PHP不愧是世界上最好的語言,寫起PreparedStatement起來也是非常的簡潔:

<?php
$mysqli = new mysqli("fakee.cn-hangzhou.datalakeanalytics.aliyuncs.com:10000",
                     "democ", "demo", "demo");
$stmt = $mysqli->stmt_init();
// 開始prepare
$stmt->prepare("select * from type_test where id = ?");

$id = 1;
// 繫結引數
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
var_dump($result->fetch_all());
?>

執行結果:

array(1) {
  [0]=>
  array(16) {
    [0]=>
    int(1)
    [1]=>
    int(2)
    [2]=>
    int(3)
    [3]=>
    string(6) "hello1"
    [4]=>
    string(1) "5"
    [5]=>
    float(6.01)
    [6]=>
    float(7.02)
    [7]=>
    float(8.03)
    [8]=>
    string(8) "01:02:01"
    [9]=>
    string(19) "1986-10-01 01:02:03"
    [10]=>
    string(19) "2018-11-29 14:04:28"
    [11]=>
    string(5) "hello"
    [12]=>
    string(10) "2018-09-07"
    [13]=>
    NULL
    [14]=>
    NULL
    [15]=>
    int(2)
  }
}

CSharp

C#不愧是比Java更好的語言,寫起來也是虎虎生風:

        public static void Main()
        {
            string connStr = "server=your-endpoint.cn-hangzhou.datalakeanalytics.aliyuncs.com;UID=your-username;database=yourdb;port=10000;password=your-password;SslMode=none";

            MySqlConnection conn = new MySqlConnection(connStr);
            try
            {
                Console.WriteLine("Connecting to MySQL...");
                conn.Open();
                string sql = "select * from type_test where id = @var1";
                MySqlCommand cmd = new MySqlCommand(sql, conn);
                // 開始prepare
                cmd.Prepare();
                // 繫結引數
                cmd.Parameters.AddWithValue("@var1", 1);

                MySqlDataReader res = cmd.ExecuteReader();
                while (res.Read())
                {
                    for (int i = 0; i < res.FieldCount; i++)
                    {
                        Console.Write(res[i] + ",");
                    }
                }
                while (res.NextResult())
                {

                }
                res.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            conn.Close();
            Console.WriteLine("Done.");
        }
    }

結果如下:

Connecting to MySQL...
1,2,3,hello1,5,6.01,7.02,8.03,01:02:01,10/01/1986 01:02:03,11/29/2018 14:04:28,hello,09/07/2018 00:00:00,,,2,Done.

總結

隨著DLA對於各種語言PreparedStatement的支援,大家可以用PreparedStatement替換原先手動拼SQL的程式碼,讓你的程式碼更OOP,更安全!


相關文章