JDBC详解

一、JDBC简介

  • JDBC(Java数据基础连接,Java Database Connectivity)
        是标准的Java访问数据库的API。
        JDBC定义了数据库的连接,SQL语句的执行以及查询结果集的遍历等。
        JDBC把这些操作定义为接口,位于包java.sql下面。
        如java.sql.Connection、java.sql.Statement、java.sql.ResultSet等。
        各个数据库提供商在自己的JDBC驱动中实现了这些接口。
  • 查询实例:列出人员信息
        Connection conne = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            DriverManager.registerDriver(new com.mysql.jdbc.Driver());
            conn = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/databaseWeb", "root", "admin");
            stmt = conn.createStatement();
            rs = stmt.executeQuery("select * from tb");
            while(rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
            }
        } catch(SQLException e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                rs.close();
            }
            if (stmt != null) {
                stmt.close();
            }
            if (conn != null) {
                conn.close();
            }
        }
  • 各种数据库的连接
        MySQL            jdbc:mysql://localhost:3306/db
        Oracle            jdbc:oracle:thin:@localhost:1521:db
        DB2                jdbc:db2://localhost:6789/db
        PostgreSQL        jdbc:postgresql:/localhost:5432/db
        Sysbase            jdbc:jtds:sysbase://localhost:2638/db
        SQLServer        jdbc:microsoft:sqlserver://localhost:1433;databaseName=db
        SQLServer 2005    jdbc:sqlserver://localhost:1433;databaseName=db

二、MySQL的乱码解决

  • MySQL的乱码解决
        常用的中文编码方式有GB2312、GBK、GB18030、UTF-8等。
            GB2312是针对中文的编码方式,每个汉字均占两个字节,解析比较简单,
                但是仅能编码中文字符。中文网站往往采用GB2312编码,例如BAIDU。
            GBK类似于GB2312,但是比GB2312编码更多的中文以及中文字符。
            GB18030又比GBK广泛,不仅可以编码中文,还可以编码少数民族的语言。
            UTF-8能够编码目前为止的任意语言,国际化的网站往往采用UTF-8编码,
                例如Google。但是UTF-8编码比较浪费空间,
                有的汉字编码后能占到三个字符,解析也比较复杂。
  • 从控制台修改编码
        从控制台输入命令:
            ALTER DATABASE database_name CHARACTER SET utf8;
        查看当前数据库编码方式的命令为:
            show variables like 'character_set_database';
        注意修改数据库编码不会影响已经存在的表的编码方式。
        如果表的编码方式原来为latin1,还需要单独修改表的编码方式。
        修改某个数据表的编码方式的SQL为:
            ALTER TABLE table_name CHARACTER SET utf8;
        也可以在创建数据库的时候指定编码方式,例如:
            CREATE DATABASE databse_name CHARACTER SET utf8;
  • 从配置文件修改编码
        修改配置文件。用记事本打开MySQL目录下的my.ini文件
            (C:\Program Files\MySQL\MySQL Server 5.0\my.ini),
            找到下面的一句话:
            default-chcharacter-set=latin1
            应该有两行这样的代码。将编码方式latin1都修改为utf8即可。
            注意修改该参数只会影响以后创建的数据库、表,
            但不会影响已经存在的数据库,表。
  • 利用图形界面工具修改
  • URL中指定编码方式
        指定JDBC连接的编码方式,方法是修改连接URL,例如:
            Connection conn = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/database_name?
                    unicode=true&characterEncoding=utf-8",
                "root", "admin")

三、JDBC基本操作:CRUD

  • 数据库程序常被称为CRUD程序,因为它包括数据的创建Create、
        读取查询Read、更新Update、删除Delete等操作,取首字母缩写便是CRUD。
  • 查询数据库遵循固定的流程:
        注册MySQL驱动、获取Connection、创建Statement、
            查询数据库返回ResultSet对象、遍历ResultSet输出数据、
            关闭ResultSet、关闭Statement、关闭Connection。
  • 插入人员信息
        Java程序也可以执行INSERT语句往数据库插入数据,方法仍然是使用Statement对象,
            不过执行INSERT语句时要使用executeUpdate(String sql)方法
                而不是executeQuery(String sql)方法。
            executeUpdate()方法用于执行INSERT、UPDATE、DELETE等,
                返回数据库中影响的行数,返回int类型。
            而executeQuery()方法用于执行SELECT,
                返回查询到的结果集,返回ResultSet类型。
  • 注册数据库驱动
        代码中注册驱动使用的是显示的注册,直接调用DriverManager的方法注册:
            DriverManager.registerDriver(new com.mysql.jdbc.Driver());
        对于MySQL来说,还可以使用下面的人一种方式:
            new com.mysql.jdbc.Driver();
            Class.forName("com.mysql.jdbc.Driver").newInstance();
        MySQL驱动的构造函数中会调用DriverManager的注册方法。
  • 获取自动插入的ID
        Statement提供的getGenerateKeys()方法以ResultSet方法
            返回所有自动生成的键值,遍历该ResultSet对象便可得到插入行的id。
            rs = stmt.getGeneratedKeys();
            rs.next();
            rs.getInt(1);
  • 删除人员信息
        删除数据使用Statement的executeUpdate(String sql)方法执行DELETE语句。
            与INSERT不同的是,DELETE必须使用WHERE条件指定删除哪一行数据,
            否则将删除所有数据。
  • 修改人员数据
        修改数据需要先把原来的数据呈现出来,客户做出修改后再保存数据。
        需要注意的是修改信息的使用一定要保存id信息与action信息,
            否则保存信息时不知道是修改的哪条数据,是保存还是添加。
  • 使用PreparedStatement
        除了使用Statement,还可以使用PreparedStatement。
            PreparedStatement接口继承自Statement接口,是Statement的子类,
            因此拥有Statement接口的所有方法。
        PreparedStatement与Statement的最大区别是PreparedStatement可以使用参数,
            也就是(?)号。PreparedStatement允许使用不完整的SQL语句,
            空缺的部分使用问号(?)代替,直到执行前的时候设置进去。
            PreparedStatement preStmt = null;
            preStmt = conn.prepareStatement(sql);
            preStmt.setInt(1, id);
            preStmt.setString(2, name);
            int result = preStmt.executeUpdate(sql);
        由于PreparedStatement使用问号代替了SQL中所有可变的部分,
            剩下了不变的部分,因此JDBC将PreparedStatement的SQL中
            不变的部分进行预编译,下次执行时直接高效率执行。
        Statement与PreparedStatement的区别:
            1、Statement不预编译,执行效率相对较低。
                PreparedStatement预编译,执行效率相对较高。
            2、Statement不支持复杂类型。需要调用数据库函数转化。
                PreparedStatement支持。通过响应的set方法可以设置。例如setDate。
            3、Statement不支持大文本(Clob)类型字段。
                    有些数据库中SQL语句有长度限制。
                PreparedStatement支持。大文本字段要通过setClob方法设置。
            4、Statement不支持二进制(Blob)数据类型。
                PreparedStatement支持。二进制数据要通过setBlob方法设置。
            5、Statement支持批处理。不支持设置参数。
                PreparedStatement支持。可以设置参数。
  • Statement批处理SQL
        Statement与PreparedStatement都能够批处理SQL语句,
            也就是同时执行多条SQL语句。这些SQL语句必须是INSERT、UPDATE、
            DELETE等SQL语句,它们执行后都返回一个int类型数,表示影响的行数。
            Statement与PreparedStatement通过addBatch()方法添加一条SQL语句,
            通过executeBatch()方法批量执行SQL语句,返回一个int数组,
            代表各句SQL的返回值。
            stmt = conn.createStatement();
            stmt.addBatch(sql);
            int[] result = stmt.executeBatch();
  • PreparedStatement批处理SQL
        Statement的批处理SQL必须为完整的SQL语句。而PreparedStatement就灵活得多,
            既可以用完整的SQL(因为它继承自Statement),
            又可以用带参数的不完整的SQL。
            preStmt = conn.prepareStatement("");
            preStmt.setInt(1, id);
            preStmt.setString(2, name);
            preStmt.addBatch();
            int[] result = preStmt.executeBatch();

四、处理结果集

  • 查询多个结果集
        进行第一次查询时,stmt会返回一个ResultSet对象。
            进行第二次查询时,stmt会返回一个全新的对象。
            中间不需要执行rs.close()关闭第一个对象。
            这是因为Statement与PreparedStatement有一个特性,
            在返回ResultSet结果集时,会自动关闭上一次查询的结果集。
  • 可以滚动的结果集
        如果后面还有记录,next()会返回true,
                同时自动向下滚动一条记录,否则返回false。
            previous()方法刚好相反,如果前面还有记录,previous()会返回true,
                同时自动向上滚动一条记录,否则返回false。
            first()是滚动到第一条记录,如果第一条记录存在的话。
            last()是滚动到最后一条记录。
        为了效率,Statement默认返回的ResultSet是只可往后滚动的,
            因此只有next()、last()方法时刻用的。
        要想使用previous()、first()方法,可以这样创建Statement:
            stmt = conn.createStatement(
                ResultSet.TYPE_SCROLL_INSENSITIVE,
                ResultSet.CONCUR_UPDATABLE);
            rs = stmt.executeQuery("");
            第一个参数指定Statement创建的ResultSet可以自由滚动,
            第二个参数指定Statement创建的ResultSet可以直接修改。
  • Pagination分页显示
        1、代码1
            String url = pageUrl.contains("?") ? pageUrl : pageUrl + "?";
            if (!url.endsWith("?") && !url.endsWith("&")) {
                url += "&";
            }
            StringBuffer buffer = new StringBuffer();
            buffer.append("第 " + pageNum + "/" + pageCount + " 页 " + 
                "共 " + recordCount + " 记录 ");
            buffer.append(pageNum == 1 ? "第一页 " : 
                "<a href='" + url + "pageNum=1'>第一页</a> ");
            buffer.append(pageNum == 1 ? "上一页 " : 
                "<a href='" + url + "pageNum=" + (pageNum-1) + "'>上一页</a> ");
            buffer.append(pageNum == pageCount ? "下一页 " : 
                "<a href='" + url + "pageNum=" + (pageNum + 1) + "'>下一页</a> ");
            buffer.append(pageNum == pageCount ? "最后一页 " : 
                "<a href='" + url + "pageNum=" + pageCount + "'>下一页</a> ");
            return buffer.toString();
        2、代码2
            final int pageSize = 10;
            int pageNum = Integer.parseInt(request.getParameter("pageNum"));
            int recordCount = DbManager.getCount(sql);
            int pageCount = (recordCount + pageSize - 1) / pageSize;
            int startRecord = (pageNum - 1) * pageSize;
            sql = "SELECT * FROM table_name LIMIT ?, ? ";
            DbManager.setParams(preStmt, startRecord, pageSize);
            rs = preStmt.executeQuery();
  • 带条件的查询
        实用的查询页面能够设置复杂的查询条件。其基本原理是把查询条件
            转化为对应的WHERE子句,然后使用包含该WHERE子句的SQL查询数据库。
  • ResultSetMetaData元数据
        JDBC可以直接获取Result对象的列名。
            根据获得到的列名,可以动态的显示查询的各列内容。
        ResultSet对象的列名可由ResultSetMetaData元数据获得。
            ResultSet.getMetaData可以返回元数据,
            遍历元数据即可获知ResultSet中有哪些列,每一列是什么类型。
            ResultSetMetaData meta = rs.getMetaData();
            int columnCount = meta.getColumnCount();
            String[] columns = new String[columnCount];
            for (int i = 1; i <= columnCount; i++) {
                columns[i-1] = meta.getCountName(i);
            }
            for (String column : columns) {
                rs.getString(column);
            }
        ResultSetMetaData元数据中还包括其他信息,
            例如列名称、列类型、列长度等,可根据需要获取相应的信息。
  • 直接显示中文列名
        MySQL的驱动在处理中文列名时会有乱码。这时候可能需要对列名进行编码转化。
            String columnName = rs.getMetaData().getColumnName(1);
            columnName = new String(columnName.getBytes("latin1"), "utf-8");

五、JDBC高级应用

  • DAO模式与Java Bean
        DAO(数据库操作对象,Database Access Object)是JDBC下常用的模式,
            保存数据时将Java Bean的属性拆分成正确的SQL语句,并保存到数据库中;
            读取数据时将数据从数据库中读取出来,并通过setter方法设置到Java Bean中。
  • 事务实例:转账
        数据库是具有事务性的,这是数据库不同于其他存储方式的区别之一。一个事务内
            可以执行多个操作,这些操作要么全部执行成功,要么全部执行失败。
        事务有两个结果,提交(commit)与回滚(Rollback)。
            如果两步转账都没有错误,则可以提交,转账结果保存进数据库。
            如果其中任何一步出错了,则必须回滚,数据库不会有任何的改动。
            conn.setAutoCommit(false);
            conn.commit();
            conn.rollback();
  • 抛出异常自动回滚
        设置自动提交为false后,事务的提交必须执行conn.commit(),而事务的回滚
            不一定要显示的执行conn.rollback()。如果程序最后没有执行conn.commit(),
            事务也会回滚。一般是直接抛出异常,终止本段程序的正常运行。
  • 存储二进制数据
        MySQL提供了BLOB类型字段用于存储二进制数据。用户上传后,
            可以将文件保存到硬盘上,只把文件名、路径等保存进数据库,
            也可以把文件内容直接存储到数据库中。
            preStmt.setBinaryStream(
                4, fileItem.getInputStream(), (int)fileItem.getSize());
            preStmt.executeUpdate();
  • 读取二进制数据
        文件写入数据库中,还需要专门的程序将数据读出来。
            读取文件内容同样要使用Stream。
            InputStream ins = rs.getBinaryStream("content");
            OutputStream ous = response.getOutputStream();
            byte[] b = new byte[1024];
            int len = 0;
            while((len = ins.read(b)) != -1) {
                out.write(b, 0, len);
  • 数据源(连接池)
        先前的JDBC编程中,每操作一次数据库,都会经过下面的过程:创建Connection、
            创建Statement对象、获取ResultSet、销毁ResultSet、销毁Statement、
            断开Connection。即每操作一次数据库,都会创建连接、断开连接。
        在实际的使用中,创建与断开Connection都会销毁一定的时间、IO资源,
            在大量的并发访问时尤其明显。为了避免频繁地创建、断开数据库连接,
            工程师们提出了数据源技术(Data Source),
            也称为连接池(DBCP,Database Connection Pool)。
        要操作数据库时,程序并不是直接创建Connection,
            而是向连接池“申请”一个Connection。如果连接池中有空闲的Connection,
            则返回该Connection,否则创建新的Connection。
            使用完毕,然后会“释放”该Connection。连接池会将该Connection回收,
            并交付其他的线程使用,达到减少创建、断开连接次数的目的。
        Tomcat使用Jakarta-Commons Database Connection Pool作为数据源实现,
            使用时只需按照Tomcat文档配置即可。
        数据源可以配置在tomcat/conf/server.xml中,
            也可以配置在tomcat/conf/context.xml中。
            注意此时要把MySQL驱动放在Tomcat全局lib里下面,
            而不能放在本web应用下面。
            <Context cookies="true">
                <Resource name="jdbc/databaseWeb"
                    auth="Container"
                    type="javax.sql.DataSource"
                    maxActive="100" maxIdle="30" maxWait="100000"
                    username="root" password="admin"
                    driverClassName="com.mysql.jdbc.Driver"
                    url="jdbc:mysql://localhost:3306/databaseWeb?
                        characterEncoding=utf-8"/>
            </Context>
        然后要在Web程序的Web.xml中配置数据源的引用。
            <web-app>
                <resource-ref>
                    <description>DB Connection</description>
                    <res-ref-name>jdbc/databaseWeb</res-ref-name>
                    <res-type>javax.sql.DataSource</res-type>
                    <res-auth>Container</res-auth>
                </resource-ref>
            </web-app>
        使用Java代码查找JNDI:
            Context initContext = new InitialContext();
            Context envContext = 
                (Context) initContext.loopup("java:/comp/env");
            DataSource ds = 
                (DataSource)envContext.loopup("jdbc/databaseWeb");
            Connection conn = ds.getConnection();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值