JDBC 学习笔记

作者: 降落在地球上的那美克星人 | 来源:发表于2017-07-17 16:56 被阅读51次

    这篇文章是我对李刚老师的《疯狂Java讲义》第三版第十三章:MySQL 数据库与JDBC编程的学习总结。适合有一定SQL基础的同学,如果你还不是很了解SQL,建议去: w3school 熟悉一下SQL的概念和基本语法。本文更专注于数据库在Java程序中的实践。最近刚学会用Github和Hexo来搭建自己的个人博客,有兴趣的朋友可以关注一下,一起探讨技术。

    SQL语句基础

    语句 内容
    查询语句 select
    DML(Data Manipulation Language) insert, update, delete
    DDL(Data Definition Language) create, alter, drop, truncate
    DCL(Data Control Language) grant, revoke

    JDBC 常用类

    接口/类 解释
    Driver Manager 用于管理JDBC驱动
    Connection 数据库连接对象
    Statement 用于执行SQL语句的工具接口
    PreparedStatement 预编译的Statement对象
    CallableStatement 用于调用储存过程
    ResultSet 结果集对象

    JDBC 编程步骤

    1. 加载数据库驱动: <code>Class.forName("com.mysql.jdbc.Driver");</code>

    2. 通过<code>DriverManager</code>获取数据库连接,获得<code>Connection</code>对象:
      <code>jdbc:mysql://hostname:port/databasename</code>

    3. 通过<code>Connection</code>对象创建<code>Statement</code>对象

    4. 使用Statement对象执行SQL语句

    • <code>execute()</code> 可执行任何SQL语句
    • <code>executeUpdate()</code> 用于执行DML和DDL语句。在执行DML语句返回受影响行数,执行DDL语句返回0。
    • <code>executeQuery()</code> 只执行查询语句,返回<code>ResultSet</code>对象
    1. 操作结果集
    • 程序可通过 <code>next()</code>, <code>previous()</code>, <code>first()</code>, <code>last()</code>, <code>beforeFirst()</code>, <code>beforeLast()</code>, <code>afterLast()</code>, <code>absolute()</code>等方法移动记录指针
    • 通过<code>getXXX(column index)</code>获取记录指针指向行、列的值。
    1. 回收数据库资源:关闭<code>ResultSet</code>、<code>Statement</code>、<code>Connection</code>等资源。

    1. 用Properties类来加载属性文件

    1.1 属性文件格式

    drv = com.mysql.jdbc.Driver
    url = jdbc:mysql://hostname:port/databasename
    usr = username
    pwd = password 
    

    例如下面的配置建立的连接会占用电脑的3306端口,生成一个people_list数据库的<code>Connection</code>对象,我们把这个配置文件命名为<code>mysql.ini</code>并将其放置在项目的根目录中:

    drv = com.mysql.jdbc.Driver
    url = jdbc:mydql://127.0.0.1:3306/people_list
    usr = enzeM
    pwd = 1234567
    

    需要注意的是 mysql 在计算机中的默认端口是3306,除非更改mysql的默认端口值,否则把配置文件端口设为其他端口会出现mysql连接异常

    1.2 在Java程序中加载属性文件

    private String drv;
    private String url;
    private String usr;
    private String pwd;
    
    void initConnFileds(String fileName) throws Exception {
        Properties p = new Properties();
        p.load(new FileInputStream(fileName));
        drv = p.getProperties("drv");
        url = p.getProperties("url");
        usr = p.getProperties("usr");
        pwd = p.getProperties("pwd");
        //step1: load the database
        Class.forName("mysql.ini")  
    }
    

    2. 通过JDBC实现数据库的基本操作

    2.1 用Statement和execute()写一个可以执行任何SQL语句的函数

    使用情境:

    • 不清楚SQL语句类型的情况下可以使用。
    • 使用getString()方法可以取得除Blob之外的任意类型列的值。
    void executeSQL(String query) throws Exception {
        //step2: get Connection object
        try(Connection conn = DriverManager.getConnection(url, usr, pwd); 
            Statement stmt = conn.createStatement()) {//step3: get Statement object
            //step4: get ResutSet object
            boolean hasResultSet = stmt.execute(query);
            if(hasResultSet) {
                try(ResultSet rs = stmt.getResutSet()) {
                    //use metadata object to visit all possible info from tables
                    ResultSetMetaData rsmd = rs.getMetaData();
                    int colunmCount = rsmd.getColumnCount();
    
                    //print result after execute the query
                    while(rs.next()) {
                        for(int i=0; i<colunmCount; i++) {
                            System.out.print(rs.getString(i+1) + "\t");
                        }
                        System.out.println();
                    }
                }
            } else {
                System.out.println("this query effect "
                        + stmt.getUpdateCount() + " record(s) in the database");
            }
        }
    }
    

    上面的代码使用<code>execute()</code>来执行SQL语句返回了一个<code>boolean</code>值,它表明SQL是否返回了<code>ResultSet</code>对象。然后程序用<code>getResutSet()</code> 获取<code>Statement</code>执行查询语句后的<code>RsultSet</code>。使用<code>getUpdateCount()</code>获取<code>Statement</code> DML语句所影响的记录行数。

    2.2 用PreparedStatement执行SQL语句

    使用情境:

    • 适用于经常需要反复执行的一条语句
    • 使用占位符<code>?</code>代替需要输入的参数
    • 预编译SQL语句,性能更好
    • 防止SQL注入攻击,安全性高

    <code>PreparedStatement</code>对象储存了预编译SQL语句,因此可以高效的反复执行该语句。
    若清楚表格的参数类型,我们可以使用</code>setXXX(int index, XXX value)</code>的方法传入参若不清楚表格的参数类型,我们可以使用<code>setObject()</code>的方法传入参数。接下来,我们在数据库里建一个Employees,然后完成:1)加入新的employee record2)根据ID找出Employee在Table里记录的讯息

    CREATE TABLE Employees (
        ID INT AUTO_INCREMENT PRIMARY KEY,
        Name VARCHAR(255) NOT NULL,
        Occupation VARCHAR(255),
        Salary VARCHAR(255)
    )
    
    • 加入新的 employee record:
    void insertNewEmployee(String name, String occupation, int salary) throws Exception {
        try(Connection conn = DriverManager.getConnection(url, usr, pwd); 
            PreparedStatement pstmt = conn.preparedStatement("INSERT INTO Employee VALUES(null, ?, ?, ?)")) {
            //pstmt.setXXX(index, value)
            pstmt.setString(1, "Tom");
            pstmt.setString(2, "Java Enginner");
            pstmt.setInt(3, 20000);
            pstmt.executeUpdate();
        }
    }
    

    这里的index指的是占位符<code>?</code>的位置,跟数组不同的是,SQL中的index相关的起始位置为1而不是0,比如column也是从0开始的。上面的代码用<code>PreparedStatement</code>的<code>setXXX()</code>方法添加了新的employee record并且在最后使用<code>executeUpdate()</code>更新了Table, 下面的代码实现了根据ID找出Employee在数据库里面的讯息

    • 根据ID找出Employee在Table里记录的讯息:
    void findEmployeeBy(int id) throws Exception {
        try(Connection conn = DriverManager.getConnection(url, usr, pwd);
                PreparedStatement pstmt = conn.preparedStatement("SELECT * FROM Employee WHERE ID=?")) {
            pstmt.setInt(1, id); //replace ? to id parameter
            ResultSet rs = pstmt.executeQuery(); //get result set
            //if we know number of column in the table 
            /*
               if(rs.next()) {
               for(int i=0; i<4; i++) {
               System.out.print(rs.getString(i+1)+"\t");
               }
               System.out.println();
               }*/
            //if we do not know abut the table, we use metadata
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            if(rs.next()) {
                for(int i=0; i<columnCount; i++) {
                    System.out.print(rs.getString(i+1)+"\t");
                }
                System.out.println();
            }
        }
    }
    

    2.3 用CallableStatement调用储存过程(Procedure)

    使用场景:

    • 需要调用数据库中定义的procedure来处理数据

    例子:建立一个储存过程:

    delimiter //
    create procedure diff_salary(a int, b int, out diff int)
    
    begin
    set diff = abs(a - b)
    end;
    

    以上例子在数据库里定义了一个procedure diff_salary 来计算两个input的差值。我们要通过程序调用这个procedure,需要使用<code>CallableStatement</code>,以下是具体步骤:

    • 使用<code>CallableStatement</code>调用 procedure:
    1. 用<code>Connection</code>对象的<code>prepareCall()</code>方法生成<code>CallableStatement</code>对象
    2. 用<code>CallableStatment</code>的<code>setXXX()</code>为procedure传入参数
    3. 用<code>rigisterOutParameter()</code>的方法来注册获取储存过程的值
    4. 用<code>CallStatement</code>的<code>execute()</code>方法来执行储存过程
    5. 用<code>getXXX(int index)</code>获取指定参数的值
    void callDiffSalaryProc(int a, int b) throws Exception {
        try(Connection conn = DriverManager.getConnection(url, usr, pwd); 
                CallableStatment cstmt = conn.prepareCall("{call diff_salary(?, ?, ?)}")) { //注意这里使用的格式
            cstmt.setInt(1, a);
            cstmt.setInt(2, b);
            cstmt.registerOutParameter(3, TYPE.INTEGER);
            cstmt.execute();
            System.out.println("result is:"+cstmt.getInt(3));
        }
    }
    
    

    管理结果集(to be continue)

    • 文章还会继续更新,有不足的地方请指教,或者在我的博客留言。

    相关文章

      网友评论

        本文标题:JDBC 学习笔记

        本文链接:https://www.haomeiwen.com/subject/zrcqkxtx.html