• Java开发工程师(Web方向)


    第1章--JDBC

    JDBC基础

    通过Java Database Connectivity可以实现Java程序对后端数据库的访问

    一个完整的数据库部署架构,通常是由客户端和服务器端两部分组成

    客户端封装数据库请求,并发送给服务器端,服务器端执行完毕后将结果返回给客户端

    常见客户端工具:

    MySQL Workbench(图形化工具操作简单,但只能实现简单的查询)

    JDBC(驱动程序jar包)

    命令行工具(直接敲入SQL语句进行查询)

    市面上多种数据库不尽相同,学习成本高,数据库迁移的移植性

    -- JDBC(普通的Java类库): 应用程序通过统一的接口,即可实现对任意数据库的访问。

    对于数据库厂商来说,JDBC就是一套接口规范,每一个数据库都需要实现JDBC定义的接口,用户通过接口访问数据库即可。

    JDBC优势:对数据库的访问简单,开发快捷、省时间,面向不同数据库时的移植性强,在JDBC上可以定制功能强大的框架(如MyBatis)

    体系架构:

    上层API层负责与Java Web程序之间的通信

    JDBC API:

    Driver & DriverManager:

    Driver是驱动程序的抽象,通过操作Driver接口,可以实现对各个驱动程序的操作

    DriverManager是驱动程序的管理类,用户通过Class.forname(DriverName)向DriverManager注册一个驱动程序,

    之后通过DriverManager的getConnection方法调用该驱动程序建立到后端数据库的物理连接。

    DriverManager.getConnection(DB_URL, USER, PASS));

    // USER和PASS在部署数据库时获得

    // DB_URL是后端数据库实例的唯一标识符

    i.e. jdbc : mysql : //10.164.172.20:3306/cloud_study

         协议  子协议      子名称(主机  端口    数据库)

    (子协议不同,子名称的格式也略有不同。)

    Connection:通过DriverManager的getConnection方法获得的到后端数据库的物理连接

    Java应用程序对后端数据库的一条物理连接

    通过这些连接,可以执行一些SQL语句:

    Statement stmt = conn.createStatement(); // sql statement

    Statement:sql语句的容器,用于承载sql语句,在该容器中,可以进行增删改查等操作

    通过executeQuery方法,执行数据库查询并得到返回结果的集合,以ResultSet类的对象来表示:

    ResultSet rs = stmt.executeQuery("select userName from user");

    通过executeUpdate方法,执行数据库更新、删除语句,返回的是int值的对象,代表被影响的数据库记录数量

    ResultSet对象表示一个sql语句查询的结果。

    关系型数据库:二元表 -- ResultSet对象也是由行和列组成的

    ResultSet对象内部有一个指针,指向当前对应的行记录(默认指向第一行记录)

    .next():将指针移动到下一行

    .previous():将指针移动到下一行

    .absolute():将指针定位在某一行

    .beforeFirst():将指针移到第一行的之前(通过.next()才能到第一行)

    .afterLast():将指针移到最后一行之后

    .getString(ColumnName/Index):(index从0开始)获取对应列的值

    .getInt(ColumnName/Index):

    .getObject(ColumnName/Index):

    SQLException:在执行过程中MySQL可能会抛出一些异常

    通过捕获SQLException对象来进行异常的处理

    下层Driver API层负责与具体的数据库建立连接,一般而言下层的driver都是由数据库厂商提供的。

    安装JDBC:

    JDBC已经集成在JDK中,可以直接引用,无需安装。

    需要安装的是数据库的驱动程序

    对于MySQL数据库:

    登陆Oracle账号,下载jar包,添加到java web project中。

    https://dev.mysql.com/downloads/connector/ 下载Connector/J 5.1.43

    构建完整的Java Web程序:

    NB. 使用JDBC之前,需要准备一个数据库的后端实例,创建一个user表

    构建步骤:

    装载驱动程序:向DriverManager注册一个驱动程序Driver

    建立数据库连接:DriverManager.getConnection()

    执行SQL语句:Statement.execute...()

    获取执行结果:ResultSet对象

    清理环境:关闭Connection, Statement, ResultSet对象

    public class HelloJDBC {
    
        static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
        static final String DB_URL = "jdbc:mysql://localhost/helloworld";
        static final String USER = "matt";
        static final String PASSWORD = "matt";
        
        public static void helloworld() throws ClassNotFoundException {
            Connection conn = null;
            Statement stmt = null;
            ResultSet rs = null;
            // 1. 装载驱动程序
            Class.forName(JDBC_DRIVER);
            // 2. 建立数据库连接
            try {
                conn = DriverManager.getConnection(DB_URL, USER, PASSWORD);
                // 3. 执行SQL语句
           stmt = conn.createStatement(); rs = stmt.executeQuery("select userName from user"); // 4. 获取执行结果 while(rs.next()) { System.out.println("Hello" + rs.getString("userName")); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { // 5. 清理环境 try { if (conn != null) { // 若conn创建成功 conn.close();// conn.close()也可能有异常 } if (stmt != null) { stmt.close(); } if (rs != null) { rs.close(); } } catch (SQLException e) { // ignore e.printStackTrace(); } } } public static void main(String[] args) throws ClassNotFoundException { helloworld(); } }

      

    代码找茬: 

    public static void test() throws ClassNotFoundException, SQLException {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        ResultSet rs1 = null;
        // 1. 装载驱动程序
        Class.forName(JDBC_DRIVER);
        // 2. 建立数据库连接
        conn = DriverManager.getConnection(DB_URL, USER, PASSWORD);
        // 3. 创建statement
            stmt = conn.createStatement();
            // 4. 执行SQL语句
        rs = stmt.executeQuery("select userName from user");
        // 5. 获取执行结果
        while(rs.next()) {
            System.out.println("Hello" + rs.getString("userName"));
        }
    }    

     

    1. try-catch block

    2. close the connection to the database in finally block with another try-catch block

      

    MySQL安装与初始化:

    MySQL安装:http://www.jianshu.com/p/fd3aae701db9

    MySQL之终端(Terminal)管理MySQL: http://www.cnblogs.com/GarveyCalvin/p/4297221.html

    MySQL之终端(Terminal)管理数据库、数据表、数据的基本操作: http://www.cnblogs.com/GarveyCalvin/p/4297282.html

    Maven+MySQL入门: https://www.kancloud.cn/digest/javaframe/125574

    JDBC MYSQL CONNECTION TUTORIAL:http://theopentutorials.com/tutorials/java/jdbc/jdbc-mysql-connection-tutorial/#Java_MySQL_Connector

      

    安装与初始化:

    在https://dev.mysql.com/downloads/mysql/下载安装

    (安装过程中会告诉一个临时密码)安装路径为/usr/local/mysql/bin/mysql

    配置环境变量PATH="$PATH":/usr/local/mysql/bin

    启动MySQL server的服务,需要到system preference中查看MySQL选项,点击start MySQL server

    之后在终端输入 mysql -uroot -p和密码a(UhqWd;t4rU即可

    试着输入命令,返回:

    ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.    

    solution:https://dev.mysql.com/doc/refman/5.6/en/alter-user.html

    输入SET PASSWORD = PASSWORD('root'); // 设密码为root

    返回Query OK, 0 rows affected, 1 warning (0.00 sec)

    创建用户:

    insert into mysql.user(Host,User,Password) values ("localhost","matthew",password("1234"));

    返回错误:ERROR 1054 (42S22): Unknown column 'Password' in 'field list'

    原因:https://stackoverflow.com/questions/30692812/mysql-user-db-does-not-have-password-columns-installing-mysql-on-osx

    尝试解决:

    use mysql;

    show tables;  // 返回所有的table名,里面有一个为user table

    describe user;  // 查询user table,发现里面有Host,有User,但是没有Password field。

    // the password field is named ' authentication_string'. 

    解决方案:(documentation)https://dev.mysql.com/doc/refman/5.6/en/create-user.html

    CREATE USER 'matt'@'localhost' IDENTIFIED BY 'matt';

    以root身份登陆后创建数据库:

    create database helloworld;

    查看所有的数据库:show databases;  // 看是否创建成功

    授权matt拥有该数据库的所有权限:GRANT privileges ON dbname.tablename TO 'username'@'host'

    grant all privileges on helloworld.* to matt@localhost identified by 'matt';

    刷新系统权限表:

    flush privileges;

    切换用户:

    exit;

    mysql -u matt -p;

    使用helloworld数据库:use helloworld;

    查看该数据库中的所有表:show tables;

     

    给数据库helloworld初始化数据:

    CREATE TABLE User ( ID int AUTO_INCREMENT PRIMARY KEY, userName varchar(20) not null);

    INSERT INTO User VALUES (null, 'ZhangSi');

    INSERT INTO User VALUES (null, 'LiSan');

    INSERT INTO User VALUES (null, 'GuoYi');

    在Eclipse中创建Java project:

    HelloJDBC, 新建类文件。

    Then, you need to add the downloaded Java MySQL Connector JAR in client project’s classpath . To do this, right click on your Java Project (JDBCMySQLSample) -> Properties -> Buildpath -> Libraries -> Add External JAR and select “mysql-connector-java-5.1.14-bin.jar” JAR file.

    Run the project:

    JDBC进阶

    业务场景一:过滤条件弱,一次可能读出较多记录

    业务场景二:需要海量数据读取

    产生结果:Java内存的溢出异常

    原因分析:Java程序是运行在JVM中的,而JVM有内存大小限制,当我们把数据库中的记录一次性全部读入到内存中,必须考虑内存中是否放得下这些数据。

    解决方法:将数据分批读入内存并处理。

    游标:提供一种客户端读取部分服务器端结果集的机制

    游标的使用:

    1. 开启游标:在DB_URL中加入参数 useCursorFetch=true

    DB_URL: jdbc:mysql://<IP>:<Port>/<database> ?useCursorFetch=true

    2. PreparedStatement接口(继承自Statement)

    使用PreparedStatement对象替换原来的Statement对象

    需要在创建时传入sql语句(sql语句是参数格式化的,即用?表示参数)

    后续通过setString()等来设置这些参数

    SetFetchSize()接口可以帮助实现游标的功能:设置客户端每次从服务器端取回的记录数量

    PreparedStatement ptmt = null;
    String sql = "select * from user where sex = ?";
    ptmt = conn.preparedStatement(sql);
    ptmt.setFetchSize(10);
    ptmt.setString(1, "男");
    rs = ptmt.executeQuery();

    业务场景三:某一条记录的数据为大字段

    产生结果:和多条记录读取相同,出现Java内存你的溢出异常

    解决方法:

    流方式(与游标相似):将大字段的数据按照二进制流的方式进行划分

    流方式的使用:ResultSet.getBinaryStream();

    while (rs.next()){
        // 获取对象流
        InputStream in = rs.getBinaryStream("blog");
        File f = new File(FILE_URL);
        OutputStream out = null;
        out = new FileOutputStream(f);
        int temp = 0;
        while((temp = in.read()) != -1) {
            // 边读边写
            out.write(temp);
        }
        in.close();
        out.close();
    }

    业务场景四:数据录入:大量数据的插入操作

    产生结果:插入数据的速度太慢

    原因分析:每次进行executeUpdate()操作时,都是一次客户端到服务端发送sql的过程,发送和接受sql浪费了很多时间,降低了效率

    解决方法:一次发送多条sql语句

    批处理:一次提交多条SQL语句,节省网络开销

    批处理的使用:

    Statement:.addBatch();  .executeBatch();  .clearBatch();

    addBatch():batch指执行sql的一个单元,addBatch即将一条条sql加入到batch这个单元中

    executeBatch():执行一批sql语句

    clearBatch():执行完毕,即可清空batch中的sql语句

    private static void insertUsers(Set<String> users) throws ClassNotFoundException {
        Connection conn = null;
        Statement stmt = null;
        
        // 1. 装载驱动程序
        Class.forName(JDBC_DRIVER);
        // 2. 建立数据库连接
        try {
            conn = DriverManager.getConnection(DB_URL, USER, PASSWORD);
            // 3. 执行SQL语句
            stmt = conn.createStatement();
            
            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            // add Batch
            for (String user: users) {
                stmt.addBatch("insert into User values (null,'" + user +"');");
            }
            // execute Batch
            stmt.executeBatch();
            // clear Batch
            stmt.clearBatch();
            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 4. 清理环境
            try {
                if (conn != null) {
                    // 若conn创建成功
                    conn.close();// conn.close()也可能有异常
                }
                if (stmt != null) {
                    stmt.close();
                }
            } catch (SQLException e) {
                // ignore
                e.printStackTrace();
            }    
            
        }
        
    }
    
    public static void main(String[] args) throws ClassNotFoundException {
        Set<String> users = new HashSet<String>();
        users.add("GuoYi");
        users.add("ZhangSi");
        users.add("LiSan");
        insertUsers(users);
    }

    业务场景五:中文字符集

    JDBC字符集需要和数据库的字符集相同

    数据库的内部编码:

    mysql> show variables like '%character%';

    +--------------------------+-----------------------------------------------------------+

    | Variable_name            | Value                                                     |

    +--------------------------+-----------------------------------------------------------+

    | character_set_client     | utf8                                                      |

    | character_set_connection | utf8                                                      |

    | character_set_database   | latin1                                                    |

    | character_set_filesystem | binary                                                    |

    | character_set_results    | utf8                                                      |

    | character_set_server     | latin1                                                    |

    | character_set_system     | utf8                                                      |

    | character_sets_dir       | /usr/local/mysql-5.7.19-macos10.12-x86_64/share/charsets/ |

    +--------------------------+-----------------------------------------------------------+

    8 rows in set (0.02 sec)

    里面有个character_set_server:server级别的编码

           character_set_database:database级别的编码

    (还可以设置表级别Table、字段级别column等级别的编码)

    优先级:Server -> Database -> Table -> column

    设置JDBC的编码:

    DB_URL = DB_URL + "characterEncoding=urt8";

     

    JDBC 单元测试

    本次得分为:70.00/70.00, 本次测试的提交时间为:2017-08-21, 如果你认为本次测试成绩不理想,你可以选择再做一次。
    1单选(5分)

    以下哪项不是使用JDBC为我们编写应用程序访问数据库带来的好处?

    • A.JDBC提供的仅仅是基础接口,基于这些接口,我们可以定制更加灵活方便的数据库框架。
    • B.JDBC屏蔽了数据库客户端与服务器端繁琐的交互协议,降低了开发者使用数据库的门槛。
    • C.JDBC 限定了数据库的使用方式,对于一些特殊的数据库协议,JDBC无法支持。�5.00/5.00
    • D.应用程序使用标准的JDBC API接口,可以访问多个数据库,使得应用程序访问数据库的接口具有跨数据库特性。
    2单选(5分)

    有关JDBC的描述,哪项是不正确的?

    • A.JDBC只是一个抽象的调用规范,底层程序实际上依赖各个数据库的驱动程序。
    • B.JDBC的体系架构分为API层和Driver层:API层负责提供统一的接口给应用程序调用;Driver层负责实现到具体数据库的通信协议。
    • C.JDBC提供了图形化的操作页面,程序员通过页面操作即可完成对数据库的管理。�5.00/5.00
    • D.JDBC仅能在Java程序中使用,不能在C++程序中使用。
    3单选(5分)

    一个正确的JDBC操作步骤包括:

    1. 释放资源

    2. 获得数据库物理连接

    3. 执行SQL命令、

    4. 注册JDBC Driver

    5. 创建Statement

    6. 如果有结果集,处理结果集

    • A.423561
    • B.42356
    • C.243561 
    • D.425361�5.00/5.00
    4单选(5分)

    下列哪个方法用于获取关系数据库二元表中某一列的值?

    • A..getString()�5.00/5.00
    • B..absolute()
    • C..previous()
    • D..next()
    5单选(5分)

    当一次向数据库插入多条数据时,应该使用JDBC的哪项高级功能?

    • A.游标
    • B.设置字符集
    • C.流方式
    • D.批处理�5.00/5.00
    6单选(5分)

    当需要读取某个大字段内容时,应该采用哪种JDBC的高级功能实现?

    • A.游标
    • B.批处理
    • C.设置字符集
    • D.流方式�5.00/5.00
    7多选(40分)

    使用游标时,需要设置?

    • A.在JDBC的URL参数中,增加useCursorFetch=true�13.33/40.00
    • B.使用PreparedStatment接口的ExecuteQuery方法�13.33/40.00
    • C.使用Statement接口的ExecuteQuery方法
    • D.需要设置setFetchSize接口�13.33/40.00

    JDBC 单元作业

    1(100分)现有一个在线交易系统,有一张Product商品表,表中共有3个字段:
      • Id: auto_increment,自增主键,商品唯一标识;

      • ProductName:varchar(100),商品名称;

      • Inventory: int 商品库存;

    表中已经插入了一些商品,请编写一段Java程序,尝试读取商品ID为1的商品记录,输出商品名称和库存数量。

    注意:程序中已经定义了一些常量,DRIVER_NAME、DB_URL、DB_USER_NAME、DB_PASSWORD,大家在编写程序时,可以直接引用。

    已有商品
    Id ProductName Inventory
    1 bread 11
    2 milk 8

     

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.Statement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
     
    public class ProductProcessing {
         
        // static final String DRIVER_NAME = "";
        // static final String DB_URL = "";
        // static final String DB_USER_NAME = "";
        // static final String DB_PASSWORD = "";
         
        public static void processing() throws ClassNotFoundException {
            Connection conn = null;
            Statement stmt = null;
            ResultSet rs = null; 
            try {
                Class.forName(DRIVER_NAME);
                conn = DriverManager.getConnection(DB_URL, DB_USER_NAME, DB_PASSWORD);
                stmt = conn.createStatement();
                rs = stmt.executeQuery("select ProductName, Inventory from Product where Id=1");
                 
                while (rs.next()) {
                    System.out.println(rs.getString("ProductName") + ": " + rs.getString("Inventory"));
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    if(conn != null) {
                        conn.close();                   
                    }
                    if(stmt != null) {
                        stmt.close();
                    }
                    if(rs != null) {
                        rs.close();
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
             
        }
        public static void main(String[] args) throws ClassNotFoundException {
            processing();
        }
    }
  • 相关阅读:
    C#下对象与JSON串互相转换
    靠纯技术是否能渡过中年危机
    个人小结
    Qt:Drag-Drop操作在QGraphicsView及Model/View框架下的实现
    Lex&Yacc Parser错误发生后再次parser之前恢复初始状态
    lex中yyrestart()的使用
    go特性-数组与切片
    go特性-defer
    golang实现mysql udf
    go创建动态库
  • 原文地址:https://www.cnblogs.com/FudgeBear/p/7398291.html
Copyright © 2020-2023  润新知