zoukankan      html  css  js  c++  java
  • 让我们来谈谈JDBC

    1.JDBC
        1)JDBC简介
            - JDBC就是Java中连接数据库方式
            - 我们可以通过JDBC来执行SQL语句。
     
        2)获取数据库连接

     

      - java.sql.Connection 数据库连接
            - 我们对数据库的一切操作都是从获取Connection开始
            - 获取数据库连接的四个参数:
                1.数据库的地址 url
                    语法:jdbc:子协议:厂商内容
                    MySQl的格式:jdbc:mysql://主机名:端口号/数据库名字
                    例子:jdbc:mysql://localhost:3306/test
     
                2.用户名 user 连接数据库使用的用户名
                3.密码 password 数据库的密码
                4.数据库驱动全类名 driverClass
     
            - 基本步骤:
                1.导入数据库驱动的jar包
                    mysql-connector-java-5.1.37-bin.jar
                2.准备四个参数
                    - url
                    - user
                    - password
                    - driverClass
                3.加载数据库驱动
                    Class.forName(driverClass)
                4.通过DriverManager来获取数据库连接   
                    static Connection getConnection(String url, String user, String password)
     
            - 问题:
                - 在JDBC的代码中有如下一行代码:
                    Class.forName("com.mysql.jdbc.Driver");
                    - 我们发现这行代码和其他的代码没有上下文关系,就是不写他编译也不会出错。
                        而且,这行代码不写也好使,这是为什么呢?
                        - Class.forName() 主要是将一个类加载进的虚拟机。
                        - 通过观察mysql的Driver的源码,发现类中有一个静态代码块:
                            static {
                                try {
                                    java.sql.DriverManager.registerDriver(new Driver());
                                } catch (SQLException E) {
                                    throw new RuntimeException("Can't register driver!");
                                }
                            }
                        - 静态代码块会在类被加载进虚拟机时就会执行,也就是当我们调用          Class.forName("com.mysql.jdbc.Driver");
                            以后,该静态代码块会立即执行。
                            而静态代码块中,有如下代码:
                                java.sql.DriverManager.registerDriver(new Driver());
                            这个代码会将数据库驱动注册进DriverManager中。
     
                        - 但是我们这个东西不写也好使,因为在JDBC4以后,程序会自动加载数据库驱动。
                        - 虽然是这样,但是我们要求这行代码必须写。尤其是在web工程,更是必须写。   
     
     
            - 核心类:
                - java.sql.DriverManager
                    - 数据库驱动的管理器,负责加载数据库的驱动获取数据库连接
                    - static Connection getConnection(String url, String user, String password) 
                        - getConnection方法用来通过url地址,用户名,密码等参数来获取数据库连接的
     
                - java.sql.Connection
                    - 数据库连接
                    - Statement createStatement() 
                        - 创建一个Statement对象,通过Statement对象来执行SQL语句
     
                - java.sql.Statement
                    - SQL语句的执行器
                        - boolean execute(String sql) 
                            - 执行一条SQL语句,并返回一个布尔值,执行成功返回true,执行失败返回false。用的不多
                        - ResultSet executeQuery(String sql) 
                            - 执行查询的SQL语句,并返回一个结果集
                        - int executeUpdate(String sql) 
                            - 执行修改数据的SQL语句(增删改),并返回受影响的行数
     
                - java.sql.ResultSet
                    - 查询到的数据的结果集,我们通过JDBC查询数据库获得的数据,都封装在ResultSet中
                    - boolean next() 
                        - 控制光标向下移动一行,如果光标当前位置是afterLast则返回false,告诉你没数据了,就别读了。
                            如果光标移动以后,没有在afterLast则返回true,可以读取数据。
     
                    - 在ResultSet有很多getXxx(int),比如getString(),getInt(),getByte()。
                        通过这些方法可以读取当前行的数据,它们需要一个int值作为参数,
                            int指的是读取数据的列数。
                        列数是从1开始的。
     
                    - 在ResultSet中还有很多getXxx(String),它和上边的方法的作用一致,
                        只不过它们需要的都是String类型的参数,参数代表的是当前的列名,
                            比如:我们要获取id的值
                                getInt("id")
                            要获取name的值
                                getString("name")
                        注意:如果查询的SQL使用了别名,则列名以别名为准。
     
        3)数据的增删改
            //创建一个SQL执行器
            Statement stat = conn.createStatement();
     
            //创建一个SQL语句
            String sql = "INSERT INTO t_stu(`name` , age) VALUES('沙僧',28)";
     
            //执行SQL语句
            //executeUpdate用来执行一条修改SQL的语句
     
            //它需要一个String类型sql作为参数,并会返回一个int型的值,该值表示SQL语句执行以后影响到的行数
            int count = stat.executeUpdate(sql);
     
        4)数据的查询
            //创建Statement对象
            Statement stmt = conn.createStatement();
            //创建一个SQL语句
            String sql = "SELECT id, name sname, age FROM t_stu WHERE id=2";
            //执行查询
            ResultSet rs = stmt.executeQuery(sql);
            //控制光标下移一行
            //如果当前行有数据,则读取
            if(rs.next()){
     
                //获取id,name,age
                int id = rs.getInt("id");
                String name = rs.getString("sname");
                int age = rs.getInt("age");
     
                System.out.println(id+"--"+name+"--"+age);
            }
     
            > 查询操作和修改的主要不同的是,查询使用executeQuery(),
                它会返回ResultSet结果集,我们需要对结果集进行读取。
     
            > 当我们只需要读取一个数据时,用if。
                当需要读取全部数据时,用while
     
            > 代码的规范:
                - Connection、Statement、ResultSet,这些资源都是需要和数据建立连接的
                    这些资源我们并不是总需要使用,当我们不适用这些资源,需要将这些资源关闭。
                - 关闭资源顺序:
                    从后往前关:
                        先关 ResultSet
                        在关 Statement
                        最后关 Connection
                - 示例代码:
                    //定义三个变量
                    Connection conn = null;
                    Statement stmt = null;
                    ResultSet rs = null;
     
                    try{
     
                    }catch(Exception e){
                        e.printStackTrace();
                    }finally{
     
                        if(rs!=null){
                            //关闭ResulSet
                            try {
                                rs.close();
                            } catch (SQLException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
     
                        if(stmt != null){
                            try {
                                stmt.close();
                            } catch (SQLException e) {
                                e.printStackTrace();
                            }
                        }
     
                        if(conn != null){
                            try {
                                conn.close();
                            } catch (SQLException e) {
                                e.printStackTrace();
                            }
                        }
     
                    }
     

     

     

    2.JDBC
        1) SQL注入
            > 目前我们的使用的是Statement来执行SQL语句
                而我们传递的参数时通过拼接字符串的形式添加进SQL语句
            > "SELECT * FROM t_user WHERE username='"+username+"' AND password='"+password+"'"   
            > 这种形式如果正常用户的访问,问题不大,但是一旦出现恶意的用户,
                他如果传递了这样一组参数 用户名 : a' OR 'a'='a  密码:a' OR 'a'='a
            > 这两个参数如果拼接进SQL语句,SQL语句会变成如下的状态:
                "SELECT * FROM t_user WHERE username='a' OR 'a'='a' AND password='a' OR 'a'='a'"   
            > 通过这种特殊的参数,导致我们SQL语句的语义完全改变,这样即使用户名和密码不正确也可以登录
                这样就对我们的网站带来很大的安全隐患
            > 解决:
                1.在用户输入时进行验证,验证用户名中是否包含特殊字符 ' " 等等
                2.如果我们一直使用Statement则永远都会有SQL注入的隐患,所以最佳解决方案是不使用Statement
                    而是使用PreparedStatement
     
        2) PreparedStatement
            > java.sql.PreparedStatement --> 预编译的Statement
            > PreparedStatement是Statement的子接口,使用方式和Statement类似,但是它在Statement做了一些扩展。
            > 获取PreparedStatement是通过Connection的prepareStatement()的方法获取的,这个方法需要传一个SQL语句,
                当我们通过SQL语句获取一个PreparedStatement时,JDBC会将SQL语句先发送给MySQL,
                    让我们的数据库对SQL语句进行预编译操作。
            > 使用PreparedStatement的好处:
                1.PreparedStatement使用占位符来替换SQL语句中的变量,更方便编写和阅读,维护起来更加的简单。
                2.MySQL在执行一条SQL语句时,要分两个步骤:
                    1) 编译SQL语句,检查SQL语句是否有语法错误
                    2) 执行SQL语句
                    使用Statement时,每一次都需要进行这两个步骤,而MySQL可以对已经编译过的语句进行缓存,
                        使用PreparedStatement,执行一次以后,会对SQL语句进行缓存,下次再次执行时效率更高。
                    这个优点体现的不明显。
                3.使用PreparedStatement时SQL语句会交给MySQL进行预编译,预编译的过程MySQL会将SQL语句,
                    转换为一个类似于Java中方法的东西,而我们的那些填充的占位符,会以方法的参数的形式发送给MySQL
                  这样即使传递在奇怪的参数,也不能改变SQL语义,所以它有效的避免了SQL注入的问题。
     
            > 所以我们实际的使用中绝对不能使用Statement而是使用PreparedStatement
     
        3) 大数据
            - 比较大的数据
            - 大数据主要分两种:
                1.大文本数据
                2.大的字节数据
            - mysql对文件的大小有限制,也就是超过一定的大小以后,文件将不能插入进数据库,插入时会抛出异常
            - 我们可以修改mysql对文件的大小的限制,修改my.ini文件
                在my.ini文件中添加如下配置
                    max_allowed_packet = 10M
     
        4) 批处理(batch)
            - 批处理指的是一次操作中执行多条SQL语句
            - 批处理相比于一次一次执行效率会提高很多
            - 批处理主要是分两步:
                1.将要执行的SQL语句保存
                2.执行SQL语句
            - Statement和PreparedStatement都支持批处理操作,这里我们只需要掌握PreparedStatement的批处理方式:
                - 方法:
                    void addBatch()
                        - 将要执行的SQL先保存起来,先不执行
                        - 这个方法在设置完所有的占位符之后调用
                    int[] executeBatch()
                        - 这个方法用来执行SQL语句,这个方法会将批处理中所有SQL语句执行
     
            - mysql默认批处理是关闭的,所以我们还需要去打开mysql的批处理:
                    rewriteBatchedStatements=true
                我们需要将以上的参数添加到mysql的url地址中
     
            - 注意:低版本的mysql-jdbc驱动也不支持批处理
     
        5) 事务(Transaction)
            - 在开发中我们的一个业务往往需要同时操作多个表,这些操作往往是不可分割,业务中的对数据库的多次操作,
                要么同时成功,要么全都失败。
            - 注意:我们在同一个事务中使用的数据库连接(Connection)必须是同一个。   
     
            - 事务的特性(ACID):
                 原子性(atomicity)
                    一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
     
                一致性(consistency)
                    事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
     
                隔离性(isolation)
                    一个事务的执行不能被其他事务干扰。
                        即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
     
                持久性(durability)
                    持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
                        接下来的其他操作或故障不应该对其有任何影响。
     
            - 操作事务的基本步骤:
                1.开启事务
                    - 开启事务以后,我们只后的所有操作将都会在同一个事务当中
                2.操作数据库
                    - 开启事务以后再去操作数据库,所有操作将不会直接提交到数据库中
                3.提交事务
                    - 将修改应用到数据库
                4.回滚事务
                    - 数据库操作过程中出现异常了,回滚事务,回滚事务以后,数据库变成开启事务之前的状态
     
            - mysql中的事务控制
                #开启事务
                START TRANSACTION
     
                #回滚事务
                ROLLBACK
     
                #提交事务
                COMMIT
     
            - JDBC中的事务主要通过Connection对象来控制的
                1.开启事务
                    void setAutoCommit(boolean autoCommit) throws SQLException;
                    - 设置事务是否自动提交,默认是自动提交
                    - 设置事务手动提交
                        conn.setAutoCommit(false);
     
                2.提交事务
                    void commit() throws SQLException;
                    - 提交事务
                    conn.commit()
     
                3.回滚事务
                    void rollback() throws SQLException;
                    - 回滚事务
                    conn.rollback()
     
            - 事务控制的格式:
                //创建一个Connection
                Connection conn = null;
     
                try{
     
                    //获取Connection
                    conn = JDBCUtils.getConnection();
     
                    //开启事务
                    conn.setAutoCommit(false);
     
                    //对数据库进行操作
     
                    //操作成功,提交事务
                    conn.commit();
     
                }catch(Exception e){
                    e.printStackTrace();
     
                    //回滚事务
                    try {
                        conn.rollback();
                    } catch (SQLException e1) {
                        e1.printStackTrace();
                    }
     
                }finally{
                    JDBCUtils.close(conn, null, null);
                }
     
        6) 数据库连接池
            > 数据库连接池就是存放数据库连接(Connection)的集合
            > 我们获取一个数据库连接是一个相对很麻烦的过程,
                如果我们获取一个数据库连接,使用一次以后就给它关闭了
                    下一次再去使用的时候就要重新创建一个新的数据库连接。
            > 所以我们提出了一个数据库连接池的概念,数据库连接池放的都是数据库连接(Connection)
                我们在去使用数据库连接时候,不用再去重新创建数据库连接,而是直接从池中获取,
                    使用完的数据库连接,也不是直接销毁,而是要放回到连接池。
            > 数据库连接池的常见的属性:
     
                    初始连接数量:数据连接池创建以后,保存数据库连接的数量
     
                    最小空闲连接数:数据库连接池最少得未使用的数据库连接的数量
     
                    最大空闲连接数:数据库连接池最大闲置连接数,当闲置连接数满了以后,将不会有其他连接进入池
     
                    每次增加连接数:当数据库连接都被占用以后,一次性增加的数据库连接的个数
     
                    最大连接数:数据库连接池的最大容量,当最大连接数饱和了,则不再创建新的数据库连接
     
                    最大等待时间:当数据库连接池饱和以后,等待获取数据库连接的时间
     
            > 常见的数据库连接池
                - 所有的数据库连接池都需要实现DataSource,当使用数据库连接池时,我们便不再需要使用DriverManger获取数据库连接
                    而是使用DataSource。
                     - Connection getConnection()
                        - 从数据库连接池中获取数据库连接对象
     
                1.DBCP
                    - DBCP是Apache出品的一款数据库连接
                    - DBCP依赖于commons-pool
                    - 使用DBCP需要导入两个jar包:
                        commons-dbcp-1.4.jar
                        commons-pool-1.5.5.jar
                    - 当我们通过数据库连接池获取数据库连接以后,我们所获取到数据库连接已经不是我们熟悉的那个Connection
                        数据库连接池对Connection对象进行了包装,它修改Connection的close()方法,
                            再去调用close()数据库连接将不会真的关闭,而是要放回到数据库连接池中,供其他线程使用。
                    - 核心类:
                        BasicDataSourceFactory
     
                2.C3P0(重点)
                    - C3P0使用的是XML作为配置文件
                    - 使用c3p0需要导入一个jar包:
                        c3p0-0.9.1.2.jar
                    - 导入c3p0的配置文件:
                        1.配置文件的名字:c3p0-cofig.xml
                        2.配置文件要求放到类路径下(src)
                    - 核心类:   
                        ComboPooledDataSource
                    - 注意:
                        DataSource就相当于池子,我们的数据库连接都是从DataSource中获取的,
                            如果程序中有多个DataSource的实例,那么我们说你还不如不用数据库连接池。
                        所以我们的DataSource在项目中应该只有一个实例。   
     
     
     

    转载请注明出处!

    http://www.cnblogs.com/libingbin/

    感谢您的阅读。如果文章对您有用,那么请轻轻点个赞,以资鼓励。

     
     
     
  • 相关阅读:
    MySQL主从复制原理
    MySQL调优
    apache禁止php解析--安全
    apache禁止指定的user_agent访问
    python---日常练习
    字符、字节的概念和区别;编码概念
    Django模型初识
    git安装
    Django--Hello
    fillder---断言/打断点,更改提交数据
  • 原文地址:https://www.cnblogs.com/libingbin/p/5977388.html
Copyright © 2011-2022 走看看