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/

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

     
     
     
  • 相关阅读:
    uva 408 Uniform Generator
    Java实现 蓝桥杯VIP 算法提高 栅格打印问题
    Java实现 蓝桥杯VIP 算法提高 栅格打印问题
    Java实现 蓝桥杯VIP 算法提高 栅格打印问题
    Java实现 蓝桥杯VIP 算法提高 打水问题
    Java实现 蓝桥杯VIP 算法提高 打水问题
    Java实现 蓝桥杯VIP 算法提高 打水问题
    Java实现 蓝桥杯VIP 算法提高 打水问题
    Java实现 蓝桥杯VIP 算法提高 不同单词个数统计
    Java实现 蓝桥杯VIP 算法提高 不同单词个数统计
  • 原文地址:https://www.cnblogs.com/libingbin/p/5977388.html
Copyright © 2011-2022 走看看