jdbc-2-基本數據庫操作

簡介:

  • 數據庫連接:用於向數據庫發送請求,一個數據庫連接就是一個socket連接
  • CRUD:crud是指在做計算處理時的增加(Create)、讀取(Retrieve)、更新(Update)和刪除(Delete)幾個單詞的首字母簡寫。crud主要被用在描述軟件系統中數據庫或者持久層的基本操作功能。

java

  • 三種操作方式

    • Statement:用於執行不帶參數的簡單sql語句—現在很少用:
      • sql注入:因爲它的語句,只是用string作了簡單的拼接
      • 性能差等等問題
    • PreparedStatement:用於執行預編譯sql語句
    • CallableStatement:用於執行對存儲過程的調用
  • 類圖關係:
    在這裏插入圖片描述
    mysql實現了jdbc的三個Statment操作接口

  • 重點關注PrepareStatement

代碼

  • 前置:封裝一個獲取連接的工具類:
package utils;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class ConnGetUtil {
    /**
     * 加載配置文件獲取一個連接
     */
    public static Connection getConnection() throws Exception {
        InputStream resourceAsStream = ConnGetUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
        Properties properties = new Properties();
        properties.load(resourceAsStream);

        Class.forName(properties.getProperty("driverClass"));
        Connection connection = DriverManager.getConnection(properties.getProperty("url"),
                properties.getProperty("user"),
                properties.getProperty("password"));
        return connection;
    }

    /**
     * 關閉數據庫連接和PreparedStatment
     */
    public static void closeConnection(Connection connection, PreparedStatement ps, ResultSet resultSet) {
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

Statement代碼示例:
  • 由於Statement存在不安全的場景:故這裏只做簡單介紹:
package StatementTest;

import org.junit.Test;
import utils.ConnGetUtil;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

public class Test1 {
    @Test
    public void test01() throws Exception {
        Connection connection = ConnGetUtil.getConnection();
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery("select * from user_table;");
        statement.execute("insert into user_table(user , password, balance) values ('qianliu', '333', 200)");
//        System.out.println(resultSet.toString());
//        System.out.println(resultSet.getArray(1));
    }
}

  • 輸出略
PrepareStatement代碼示例:
  • 通用操作:
package PreparedStatementTest;

import domain.Order;
import domain.UserTable;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import utils.ConnGetUtil;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

/**
 * 將Test1中的CRUD操作,封裝爲通用的,不再限制死
 */
public class Test2Common {

    private Connection connection;
    private PreparedStatement preparedStatement;
    private ResultSet resultSet;

    @Before
    public void setConnection() {
        try {
            connection = ConnGetUtil.getConnection();
        } catch (Exception e) {
            System.out.println("獲取連接失敗");
            e.printStackTrace();
        }
    }

    @After
    public void close() {
        ConnGetUtil.closeConnection(connection, preparedStatement, resultSet);
    }

    /**
     * 1. 開始封裝通用的查詢操作
     */
    public List<Object> queryUserTable(String sql, Object... args) throws Exception{
        // 1.先獲取連接
        connection = ConnGetUtil.getConnection();
        // 2.獲取PreparedStatement,並進行預編譯
        preparedStatement = connection.prepareStatement(sql);
        for (int i = 0; i < args.length; i++) {
            preparedStatement.setObject(i+1, args[i]);
        }
        // 3.執行查詢
        ResultSet resultSet = preparedStatement.executeQuery();
        // 4.通過反射賦值
        List<Object> result = new ArrayList<>();
        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();
        while (resultSet.next()) {
            UserTable userTable = new UserTable();
            for (int i = 0; i < columnCount; i++) {
                String columnName = metaData.getColumnName(i + 1);
                Object object = resultSet.getObject(i + 1);
                // 反射獲取UserTable中的字段
                Field declaredField = UserTable.class.getDeclaredField(columnName);
                if (declaredField != null) {
                    declaredField.setAccessible(true);
                    declaredField.set(userTable, object);
                }
            }
            System.out.println(userTable);
            result.add(userTable);
        }
        return result;
    }

    @Test
    public void testQuery() throws Exception {
        System.out.println("============== 查詢一條 ==============");
        String sql = "select id, user, password, balance from user_table where id = ?;";
        queryUserTable(sql, 1);

        System.out.println("============== 查詢所有 ==============");
        sql = "select id, user, password, balance from user_table;";
        queryUserTable(sql);

        System.out.println("============== 只查一個字段 ==============");
        sql = "select user from user_table;";
        queryUserTable(sql);
    }


    /**
     * 2. 使用泛型操作
     */
    public <T> List<T> queryWithGeneric(String sql, Class<T> tClass, Object... args) throws Exception{
        // 1.先獲取連接
        connection = ConnGetUtil.getConnection();
        // 2.獲取PreparedStatement,並進行預編譯
        preparedStatement = connection.prepareStatement(sql);
        for (int i = 0; i < args.length; i++) {
            preparedStatement.setObject(i+1, args[i]);
        }
        // 3.執行查詢
        ResultSet resultSet = preparedStatement.executeQuery();
        // 4.通過反射賦值
        List<T> result = new ArrayList<>();
        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();
        while (resultSet.next()) {
            T t = tClass.newInstance();
            for (int i = 0; i < columnCount; i++) {
                // 這裏需要使用getColumnLable獲取label標誌(別名),不能用getColumnName(只能獲取表的字段名)
//                String columnName = metaData.getColumnName(i + 1);
                String columnLabel = metaData.getColumnLabel(i + 1);
                Object object = resultSet.getObject(i + 1);
                // 反射獲取UserTable中的字段
                Field declaredField = tClass.getDeclaredField(columnLabel);
                if (declaredField != null) {
                    declaredField.setAccessible(true);
                    declaredField.set(t, object);
                }
            }
            System.out.println(t);
            result.add(t);
        }
        return result;
    }

    /**
     * 用泛型測試最通用的
     * @throws Exception
     */
    @Test
    public void testQueryGeneric() throws Exception {
        System.out.println("============== 使用泛型, 查詢一條 ==============");
        String sql = "select id, user, password, balance from user_table where id = ?;";
        queryWithGeneric(sql, UserTable.class, 1);

        System.out.println("============== 使用泛型, 查詢所有 ==============");
        sql = "select id, user, password, balance from user_table;";
        queryWithGeneric(sql, UserTable.class);
    }

    /**
     * 測試數據庫字段和類字段不匹配的情況:
     *  1. select的時候,需要對字段設置別名
     *  2. 通用裏面,需要用getColumnLabel()獲取別名,不能使用getColumnName
     */
    @Test
    public void testOrder() throws Exception {
        System.out.println("============== 使用泛型, 查詢所有 ==============");
        String sql = "select order_id orderId, order_name orderName, order_date orderDate from `order`;";
        queryWithGeneric(sql, Order.class);
    }
}

輸出:

Wed Jun 10 22:19:53 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
============== 使用泛型, 查詢一條 ==============
Wed Jun 10 22:19:53 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
UserTable{id=1, user='lisi', password='456', balance=9999}
============== 使用泛型, 查詢所有 ==============
Wed Jun 10 22:19:53 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
UserTable{id=1, user='lisi', password='456', balance=9999}
UserTable{id=2, user='qianliu', password='333', balance=200}
UserTable{id=3, user='wangwu', password='123_new', balance=1000}
UserTable{id=4, user='zhagnsan', password='123', balance=1000}
UserTable{id=12, user='lisi', password='777', balance=9999}
UserTable{id=13, user='lisi', password='777', balance=1500}
UserTable{id=22, user='qianliu', password='333', balance=200}
Wed Jun 10 22:19:53 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
============== 使用泛型, 查詢所有 ==============
Wed Jun 10 22:19:53 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Order{orderId=1, orderName='pens', orderDate=2000-01-01 00:00:00.0}
Order{orderId=2, orderName='food', orderDate=1995-03-12 00:00:00.0}
Wed Jun 10 22:19:53 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
============== 查詢一條 ==============
Wed Jun 10 22:19:53 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
UserTable{id=1, user='lisi', password='456', balance=9999}
============== 查詢所有 ==============
Wed Jun 10 22:19:53 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
UserTable{id=1, user='lisi', password='456', balance=9999}
UserTable{id=2, user='qianliu', password='333', balance=200}
UserTable{id=3, user='wangwu', password='123_new', balance=1000}
UserTable{id=4, user='zhagnsan', password='123', balance=1000}
UserTable{id=12, user='lisi', password='777', balance=9999}
UserTable{id=20, user='lisi', password='777', balance=1500}
UserTable{id=22, user='qianliu', password='333', balance=200}
============== 只查一個字段 ==============
Wed Jun 10 22:19:53 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
UserTable{id=0, user='lisi', password='null', balance=0}
UserTable{id=0, user='qianliu', password='null', balance=0}
UserTable{id=0, user='wangwu', password='null', balance=0}
UserTable{id=0, user='zhagnsan', password='null', balance=0}
UserTable{id=0, user='lisi', password='null', balance=0}
UserTable{id=0, user='qianliu', password='null', balance=0}
  • 批量操作效率:
package PreparedStatementTest;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import sun.lwawt.macosx.CSystemTray;
import utils.ConnGetUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

/**
 * 測試批量提交
 */
public class TestBatch {


    private Connection connection;
    private PreparedStatement preparedStatement;
    private ResultSet resultSet;

    @Before
    public void setConnection() {
        try {
            connection = ConnGetUtil.getConnection();
        } catch (Exception e) {
            System.out.println("獲取連接失敗");
            e.printStackTrace();
        }
    }

    @After
    public void close() {
        ConnGetUtil.closeConnection(connection, preparedStatement, resultSet);
    }

    /**
     * 測試批量提交
     *  方式1:使用Statement
     *  方式2:使用PreparedStatement直接提交,簡化sql翻譯
     *          因爲PreparedStatement只翻譯一次,故下面四項,都只做一次
     *              語法檢查:
     *              語義檢查:
     *              翻譯成二進制指令:
     *              緩存
     *          後續佔位符傳入的時候,只需要傳參過去即可
     *  方式3:使用PreparedStatement批量提交
     *  方式4:使用PreparedStatement批量提交+一次性提交(利用AutoCommit方式)
     */
    @Test
    public void testBatch() throws Exception{
        int batchSize = 20000;

        System.out.println("================ 方式1:使用Statement ================");
        long start = System.currentTimeMillis();
        Statement statement = connection.createStatement();
        for (int i = 0; i < batchSize; i++) {
            String sql = "insert into batch_test(name) values('statement" + i + "');";
            statement.execute(sql);
        }
        statement.close();
        System.out.println("時間花費爲:" + (System.currentTimeMillis() - start) + " ms");

        System.out.println("================ 方式2:使用PreparedStatement直接提交,簡化sql翻譯 ================");
        start = System.currentTimeMillis();
        preparedStatement = connection.prepareStatement("insert into batch_test(name) values(?);");

        for (int i = 0; i < batchSize; i++) {
            preparedStatement.setString(1, "preparedStatement" + i);
            preparedStatement.execute();
        }
        preparedStatement.close();
        System.out.println("時間花費爲:" + (System.currentTimeMillis() - start) + " ms");

        System.out.println("================ 方式3:使用PreparedStatement批量提交 ================");
        start = System.currentTimeMillis();
        preparedStatement = connection.prepareStatement("insert into batch_test(name) values(?);");

        for (int i = 0; i < batchSize; i++) {
            preparedStatement.setString(1, "preparedStatement" + i);
            preparedStatement.addBatch();
            if (i % 500 == 0 || i >= batchSize - 1) {
                // 批量執行、批量清空
                preparedStatement.executeBatch();
                preparedStatement.clearBatch();
            }
        }
        preparedStatement.close();
        System.out.println("時間花費爲:" + (System.currentTimeMillis() - start) + " ms");

        System.out.println("================ 方式4:使用PreparedStatement批量提交+一次性提交(利用AutoCommit方式) ================");
        start = System.currentTimeMillis();
        preparedStatement = connection.prepareStatement("insert into batch_test(name) values(?);");
        connection.setAutoCommit(false);

        for (int i = 0; i < batchSize; i++) {
            preparedStatement.setString(1, "preparedStatement" + i);
            preparedStatement.addBatch();
            if (i % 500 == 0 || i >= batchSize - 1) {
                // 批量執行、批量清空
                preparedStatement.executeBatch();
                preparedStatement.clearBatch();
            }
        }
        connection.commit();
        preparedStatement.close();
        System.out.println("時間花費爲:" + (System.currentTimeMillis() - start) + " ms");

    }

    @Test
    public void testBestBatchInsert() throws Exception{
        int batchSize = 20000;
        System.out.println("================ 方式4:使用PreparedStatement批量提交+一次性提交(利用AutoCommit方式) ================");
        long start = System.currentTimeMillis();
        preparedStatement = connection.prepareStatement("insert into batch_test(name) values(?);");
        connection.setAutoCommit(false);

        for (int i = 0; i < batchSize; i++) {
            preparedStatement.setString(1, "preparedStatement" + i);
            preparedStatement.addBatch();
            if (i % 500 == 0 || i >= batchSize - 1) {
                // 批量執行、批量清空
                preparedStatement.executeBatch();
                preparedStatement.clearBatch();
            }
        }
        connection.commit();
        preparedStatement.close();
        System.out.println("時間花費爲:" + (System.currentTimeMillis() - start) + " ms");

    }
}

輸出:

================ 方式1:使用Statement ================
時間花費爲:6082 ms
================ 方式2:使用PreparedStatement直接提交,簡化sql翻譯 ================
時間花費爲:5706 ms
================ 方式3:使用PreparedStatement批量提交 ================
時間花費爲:4940 ms
================ 方式4:使用PreparedStatement批量提交+一次性提交(利用AutoCommit方式) ================
時間花費爲:1693 ms
  • 操作二進制大文件:
package PreparedStatementTest;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import utils.ConnGetUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.sql.*;
import java.util.Date;

/**
 * 測試Bloc的插入、查詢
 * (刪除和更新類似,這裏不做描述了)
 */
public class TestBlob {

    private Connection connection;
    private PreparedStatement preparedStatement;
    private ResultSet resultSet;

    @Before
    public void setConnection() {
        try {
            connection = ConnGetUtil.getConnection();
        } catch (Exception e) {
            System.out.println("獲取連接失敗");
            e.printStackTrace();
        }
    }

    @After
    public void close() {
        ConnGetUtil.closeConnection(connection, preparedStatement, resultSet);
    }

    /**
     * 測試插入blob
     */
    @Test
    public void testInsertBlob() throws Exception {
        String sql = "insert into customers(name, email, birth, photo) values(?, ?, ?, ?);";
        preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setObject(1, "zhangsan");
        preparedStatement.setObject(2, "zhangsan.qq.com");
        preparedStatement.setObject(3, new Date());
        FileInputStream fis = new FileInputStream(
                new File("src/main/resources/tianfuzhen.jpg"));
        preparedStatement.setBlob(4, fis);
        preparedStatement.execute();
        fis.close();
    }

    /**
     * 測試查詢blob
     */
    @Test
    public void testGetBlob() throws Exception {
        String sql = "select * from customers;";
        preparedStatement = connection.prepareStatement(sql);
        ResultSet resultSet = preparedStatement.executeQuery();
        if (resultSet != null) {
            System.out.println("查詢到了結果...");
            if (resultSet.next()) {
                // 聲明一個輸入流,讀取db中的照片
                Blob photo1 = resultSet.getBlob("photo");
                InputStream binaryStream = photo1.getBinaryStream();
                // 聲明一個輸出流,用於保存照片
                FileOutputStream fileOutputStream = new FileOutputStream("src/main/resources/tianfuzhen_read.jpg");
                byte[] buf = new byte[1024];
                int len = 0;
                while ((len = binaryStream.read(buf)) != -1) {
                    fileOutputStream.write(buf, 0, len);
                }
                binaryStream.close();
                fileOutputStream.close();
            }
        }
    }


}

CallableStatement代碼示例:
  • 存儲過程略,待後續分析mysql的call過程再看
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章