JDBC步骤用的类和接口的简介

DriverManager类简介:
        概述:
            它是java.sql包下的类, 表示 驱动管理者.
        作用:
            1. 注册驱动.        实际开发不用这种方式.
            2. 获取连接对象.     掌握, 常用.
        涉及到的成员方法:
            public static void registerDriver(Driver driver);
                注册驱动, 但是这种方式会导致驱动注册两次, 所以实际开发中我们不用它, 而是通过反射的方式注册驱动.
                参数解释: Driver 表示具体的要连接的数据库的 驱动类.
                源码解读:
                                 MySQL厂商提供的驱动类               Sun公司的JDBC规范之 驱动规范
                    public class com.mysql.jdbc.Driver implements java.sql.Driver {
                    }
            public static Connection getConnection(String url, String username, String password);   获取连接对象.
                参数1: 数据库连接字符串.
                    格式: 协议:子协议://要连接的数据库的ip或者主机名:端口号/具体的要操作的数据库
                    例如:
                        jdbc:mysql://127.0.0.1:3306/day16
                        jdbc:mysql://localhost:3306/day16
                    细节:
                        如果操作的是本机数据库, 且端口号也是默认的3306: 则可以省略ip地址 和 端口号.

--------------------------------------------------------------------------
  Connection接口详解;
        概述:
            它是java.sql包下的接口, 表示: 连接对象.
        作用:
            1. 获取可以执行SQL语句的对象.
            2. 可以管理事务.
        涉及到的成员方法:
            public Statement createStatement();
                获取可以执行SQL语句的对象, 没有预编译功能, 不能解决SQL注入攻击问题.
            public PreparedStatement prepareStatement(String sql);
                获取可以执行SQL语句的对象, 有预编译功能, 能解决SQL注入攻击问题.

            和事务相关的方法:
                public void setTransactionIsolation(int level);     设置事务的隔离级别
                public void commit();   提交事务
                public void rollback(); 事务回滚
                public void setAutoCommit(boolean flag)  设置是否开启自动提交事务功能.

-----------------------------------------------------------------------------
  Statement接口详解;
        概述:
            它是java.sql包下的接口, 表示: 可以执行SQL语句的对象.
        作用:
            1. 执行SQL语句, 获取结果集.
            2. 可以进行批处理.
        涉及到的成员方法:
            public ResultSet executeQuery(sql);     执行查询语句, 获取结果集.
            public int executeUpdate(sql);          执行更新语句, 获取结果集(注意: 增删改语句统称为更新语句)

            和批处理相关的方法:  了解, 批处理只针对于更新语句有效.
                public void addBatch(String sql);     添加SQL语句到批处理队列中.
                public int[] executeBatch();          执行批处理.
                public void clearBatch();             清空批处理.
---------------------------------------------------------------------



    ResultSet接口详解;
        概述:
            它是java.sql包下的接口, 表示: 执行完查询语句后返回的结果集对象.
        涉及到的成员方法:
            public boolean next();      判断结果集中是否有下一个条数据, 类似于: Iterator#hasNext()
            public Xxx getXxx(int columnNumber);
                根据列的编号, 获取该列的值, 编号从1开始, Xxx表示数据类型, 例如: getInt(), getString()
            public Xxx getXxx(String columnName);   推荐掌握.
                根据列的名称, 获取该列的值. 




/*
    案例: 通过单元测试演示JDBC的CURD操作.

    JDBC的核心操作步骤:
        1. 注册驱动.
        2. 获取连接对象.
        3. 根据连接对象, 获取可以执行SQL语句的对象.
        4. 执行SQL语句, 获取结果集.
        5. 操作结果集.
        6. 释放资源.
 */

public class JDBCDemo03 {

    /**
     * 查询所有数据
     * @throws Exception
     */
    @Test
    public void select() throws Exception {
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取数据库连接对象
        Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1/afu01", "root", "root");
        //3.根据连接对象, 获取可以执行SQL语句的对象.
        Statement stat = conn.createStatement();
        //4执行sql语句获取结果集
        String sql = "select * from user;";
        //5.操作结果集
        ResultSet rs = stat.executeQuery(sql);     //executeQuery 只针对于查询操作
        while (rs.next()){
            System.out.println(rs.getInt("id") + ".." + rs.getString("username")+ "..." + rs.getString("password"));
        }

        //6.释放资源
        rs.close();
        stat.close();
        conn.close();
    }

    /**
     * 添加数据
     * @throws Exception
     */
    @Test
    public void add() throws Exception {
        //1.注册数据库驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取数据库连接对象
        Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1/afu01", "root", "root");
        //3.根据连接对象,获取乐意操作的sql语句
        Statement stat = conn.createStatement();
        String sql = "insert into user values (null,'胡嘻嘻','123456');";    //字段包裹单引号
        //4.执行sql语句 获取结果集
        int num = stat.executeUpdate(sql);
        //5.操作结果集
        System.out.println(num > 0 ? "增加成功" : "增加失败");
        //6.释放资源
        stat.close();
        conn.close();
    }

    /**
     * 修改数据
     * @throws Exception
     */
    @Test
    public void update() throws Exception{
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.创建数据库连接对象
        Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1/afu01", "root", "root");
        //3.获取可以执行的sql语句
        Statement stat = conn.createStatement();
        String sql = "update user set username = '迪丽热巴',password = '88888888' where id = 2;";
        //4.执行sql语句 获取结果集
        int num = stat.executeUpdate(sql);
        //5. 打印输出
        System.out.println(num > 0 ? "修改成功": "修改失败" );
        //6. 释放资源
        stat.close();
        conn.close();
    }

    /**
     * 删除数据
     */
    @Test
    public void delete() throws Exception{
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取数据库连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1/afu01", "root", "root");
        //3.获取执行sql语句对象
        Statement stat = conn.createStatement();
        String sql = "delete from user where id = 2;";
        //4.操作sql语句
        int num = stat.executeUpdate(sql);
        //5.输出
        System.out.println(num > 0 ? "删除成功": "删除失败");
        //6.释放资源
        stat.close();
        conn.close();
    }
}

JDBC 工具类的使用

//自定义JDBCUtils工具类
public class JDBCUtils {
    //构造方法私有化
    private JDBCUtils() {
    }

    //工具类的成员都是静态的  //静态代码块在访问该类的时候回优先执行  随着类的加载而存在
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    //对外提供成员方法, 用来获取连接对象
    public static Connection getConnection() {
        //具体的 返回操作连接对象
        try {
            return DriverManager.getConnection("jdbc:mysql://127.0.0.1/afu01", "root", "root");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        //走到这里说明有问题
        return null;
    }

    //释放资源
    //复杂版
    public static void release(Connection conn, Statement stat, ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
                rs = null;   //做一个非空判断 GC会优先回收Null对象  这个代码值500块  哈哈哈........
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (stat != null) {
                    stat.close();
                    stat = null;    //GC优先回收null
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (conn != null) {
                        conn.close();
                        conn = null;
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //简单版
/*    public static void release(Connection conn, Statement stat, ResultSet rs) {
        try {
            rs.close();
            stat.close();
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }*/
}



public class TestJDBCUtils {
    public static void main(String[] args) throws  Exception{
        //需求: 抽取JDBCUtils工具类, 并完成JDBC的核心操作步骤.
        //1. 注册驱动.
        //2. 获取连接对象.
        Connection conn = JDBCUtils.getConnection();
        //3. 根据连接对象, 获取可以执行SQL语句的对象
        Statement stat = conn.createStatement();
        String sql = "select * from user;";
        //操作sql语句
        ResultSet rs = stat.executeQuery(sql);
        while (rs.next()){
            //获取ID
        /*    rs.getInt("ID");
            //获取用户名
            rs.getString("username");
            //获取密码
            rs.getString("password");*/
            //输出
            System.out.println( rs.getInt("ID") + "..." + rs.getString("username") + rs.getString("password"));
        }
        //释放资源
        JDBCUtils.release(conn,stat,rs);
    }
}

关于Sql的注入攻击问题

public class JDBCMysql {
    public static void main(String[] args)  throws Exception{
       // method01();

        //需求2: 通过Java代码 解决SQL注入攻击
        //1. 提示用户录入账号和密码, 并接收.
        System.out.println("请输入账号:");
        String username = new Scanner(System.in).nextLine();
        System.out.println("请输入密码:");
        String password = new Scanner(System.in).nextLine();

        //2.获取数据库连接对象
        Connection conn = JDBCUtils.getConnection();
        //3.获取执行sql语句的对象
        String sql = "select * from user where username = ? and password = ?;"; //需要用户录入内容的地方, 都用占位符?表示.
        PreparedStatement ps = conn.prepareStatement(sql);  //这里已经对SQL格式预编译了, 即: 之后不管传入什么内容, 都只是当做普通字符处理.

        //细节  给占位符设置值
        ps.setString(1,username);
        ps.setString(2,password);

        //4.执行sql 获取结果集.
        ResultSet rs =ps.executeQuery();   //因为已经预编译过SQL语句了, 所以这里不需要传入.
        System.out.println(rs.next()? "登录成功" : "登录失败");
        //5.释放资源
        JDBCUtils.release(conn,ps,rs);

//PreparedStatement 继承了  Statement
}



    public static void method01() throws SQLException {
        //演示sql注入问题
        //1. 提示用户录入账号和密码, 并接收.
        System.out.println("请输入账号信息:");
        String username = new Scanner(System.in).nextLine();
        System.out.println("请输入密码信息:");
        String password = new Scanner(System.in).nextLine();

        //2. 通过JDBC代码, 判断是否登录成功, 并提示.
        //1. 注册驱动.
        //2. 获取连接对象.
        Connection conn = JDBCUtils.getConnection();
        //3. 根据连接对象, 获取可以执行SQL语句的对象.
        Statement stat = conn.createStatement();
        //4.执行sql的语句
        String sql = "select * from user where username = '"+ username + " 'and password= ' "  + password+";'";
        //5.操作sql语句
        ResultSet rs = stat.executeQuery(sql);
        System.out.println(rs.next()? "登录成功" : " 登录失败");
        //6.释放资源
        JDBCUtils.release(conn,stat,rs);
    }
}

演示PreparedStatement接口的 CURD操作

/*
    案例: 演示PreparedStatement接口的 CURD操作.      掌握  JDBC工具类操作     实际开发用的比较多 因为防止可sql注入攻击问题
 */
public class PrepareStatmentCRUD  {

    /**
     * 查询
     * @throws Exception
     */
    @Test
    public void select() throws Exception{
        //1.获取数据库连接对象
        Connection conn = JDBCUtils.getConnection();
        //2.获取可以执行的sql语句对象
        String sql = "select * from user where id = ?;";
        PreparedStatement ps = conn.prepareStatement(sql);
        //3. 执行SQL语句, 获取结果集.
        //细节: 给占位符填充值.
        ps.setInt(1,3);  //表示第一个 ?  给ID为 3 的人
        ResultSet rs = ps.executeQuery();   //查询用executeQuery()
;        //4.操作结果集
        while (rs.next()){
            System.out.println(rs.getInt("id") +  ".." + rs.getString("username") + ".." + rs.getString("password"));
        }
        //5.释放资源
        JDBCUtils.release(conn,ps,rs);
    }

    /**
     * 增加
     * @throws Exception
     */
    @Test
    public void add() throws Exception{
        //1.获取数据库连接对象
        Connection conn = JDBCUtils.getConnection();
        //2.获取可以执行sql语句的对象
        String sql = "insert into user values(null,?,?);"; //这一步已经确定了SQL语句的格式, 之后不管传入什么, 都当做普通字符处理.
        PreparedStatement ps = conn.prepareStatement(sql);
        //3.操作结果集的sql语句
        ps.setString(1,"张学友");
        ps.setString(2,"123456");
        int num = ps.executeUpdate();
        //4. 输出
        System.out.println( num>0? "操作成功":"操作失败");

        //5.释放资源
        JDBCUtils.release(conn,ps,null);
    }

    /**
     * 修改
     */
    @Test
    public void update() throws Exception{
        //1.获取数据库连接对象
        Connection conn = JDBCUtils.getConnection();
        //2.创建执行操作sql语句的对象
        String sql = "update user set username = ?  where id = ?";
        PreparedStatement ps = conn.prepareStatement(sql);
        //3.获取操作的结果集
        //细节  给占位符设置值
        ps.setString(1,"阿福哥哥啊");
        ps.setInt(2,1);
        int num = ps.executeUpdate();
        //4.输出
        System.out.println(num > 0? "操作成功":"操作失败");

        //释放资源
        JDBCUtils.release(conn,ps,null);
    }

    /**
     * 删除
     */
    @Test
    public void delete() throws Exception{
        //1.获取连接对象
        Connection conn = JDBCUtils.getConnection();
        //2.创建执行sql语句的对象
        String sql = "delete from user where id = ?";
        PreparedStatement ps = conn.prepareStatement(sql);
        //3.操作执行sql
        //细节  给 ? 赋值
        ps.setInt(1,1);
        int num = ps.executeUpdate();
        //4.输出
        System.out.println(num > 0?"操作成功":"操作失败");
        //5.释放资源
        JDBCUtils.release(conn,ps,null);
    }
}

数据库连接池

/*
    案例: 演示数据库连接池入门.

    数据库连接池简介:
        概述:
            实际开发中, 当我们需要使用大量生命周期短的连接对象时, 频繁的创建和销毁连接对象是非常消耗资源的, 针对于这种情况,
            我们可以创建一个池子出来, 里边放一些连接对象, 用的时候从里边拿, 用完之后再放回去. 这样做的好处是: 节约资源, 提高效率.
            这个池子就叫: 数据库连接池.   Data Base Connection Pool
        分类:
            DBCP: 属于Apache软件基金组织的, 不能自动回收空闲连接.
            C3P0: 属于Apache软件基金组织的, 能自动回收空闲连接.
                Java的框架中, 底层但凡是涉及到数据库连接池部分了, 用的都是 C3P0.
            Druid: 属于阿里巴巴的产品, 采用分布式事务的方式实现的, 能够更好的兼容大数据框架.
        步骤:
            1. 导入连接池的jar包.
            2. 从连接池中获取连接对象.
            3. 正常的JDBC的操作.
            4. 正常的释放资源即可.
                因为如果Connection对象是我们自己创建的, 当调用 conn.close()的时候, 就是销毁资源.
                如果Connection对象是我们从数据库连接池中获取的, 当调用 conn.close()的时候, 就是 归还连接对象到连接池中.
                为什么同样的代码, 效果却不同呢?
                    因为数据库连接池的底层已经 重构了Connection#close()方法.

    遇到的问题:
        我们这样写代码, 效率还没有以前不用数据库连接池的代码效率高.
    产生原因:
        1. 此时的数据库连接池并不是共享的, 而是每个代码都有自己的数据库连接池对象.
        2. 此时我们创建的数据库连接池对象, 默认的连接对象的个数是5个, 但是我们就用了1个, 其他4个处于闲置状态.
    解决方案:
        抽取C3P0Utils工具类, 设置 数据库连接池对象 为大家共享的.
 */

//自定义的C3P0Utils工具类, 用来操作 C3P0 数据库连接池的.
public class C3P0Utils {
    //1. 构造方法私有化.
    private C3P0Utils() {
    }

    //2. 定义个公共的静态的常量, 表示数据库连接池对象.
    public static final ComboPooledDataSource cpds = new ComboPooledDataSource();

    //3. 对外提供一个公共的成员方法, 用来获取: 数据库连接池对象.
    public static ComboPooledDataSource getDataSource() {
        return cpds;
    }

    //4. 对外提供一个公共的成员方法, 用来从 连接池中 获取连接对象.
    public static Connection getConnection() {
        try {
            return cpds.getConnection();    //从 连接池中 获取连接对象, 并返回.
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        //如果有问题, 则返回null
        return null;
    }

    //5. 释放资源.
    public static void release(Connection conn, Statement stat, ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
                rs = null;
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            try {
                if (stat != null) {
                    stat.close();
                    stat = null;
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            } finally {
                try {
                    if (conn != null) {
                        conn.close();
                        conn = null;
                    }
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }

}



/*
    案例: 演示 C3P0Utils工具类的定义和使用.
 */
public class CP30Test {
    public static void main(String[] args)  throws Exception{
        //1.从池子中获取连接对象
        Connection conn = C3P0Utils.getConnection();
        //2. 根据连接对象, 获取可以执行SQL语句的对象.
        String sql = "select * from user;";
        PreparedStatement ps = conn.prepareStatement(sql);
        //3. 执行SQL语句, 获取结果集.
        //细节: 给占位符填充值.
        ResultSet rs = ps.executeQuery();
        while (rs.next()){
            System.out.println(rs.getInt("id") + ".." + rs.getString("username") + ".." +rs.getString("password"));

        }
        //4.释放资源
        C3P0Utils.release(conn,ps,rs);
    }
}

mysql事物

事务简介:
        概述:
            专业版: 事务指的是逻辑上的一组操作, 组成该逻辑操作的各个逻辑单元, 要么全部执行成功, 要么全部执行失败.
            大白话版: 同生同死.
        案例:
            逻辑操作: 阿福 给 凤姐 转1000
            逻辑单元:
                动作1: 阿福的账号 - 1000
                动作2: 凤姐的账号 + 1000
                
        特点: ACID
            原子性(Atomicity)   原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
            一致性(Consistency)事务前后数据的完整性必须保持一致。
            隔离性(Isolation)  事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
            持久性(Durability) 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
            
        如果不考虑事务的隔离性, 可能会出现问题:
            关于读:
                脏读:  也叫: 读-未提交
                    指的是一个事务读取到了另一个事务还没有来得及提交的数据, 导致多次查询结果不一致.
                不可重复读: 也叫: 读-已提交(更新)
                    指的是一个事务读取到了另一个事务已经提交的(更新, 修改)数据, 导致多次查询结果不一致.
                虚读(幻读): 也叫: 读-已提交(插入)
                    指的是一个事务读取到了另一个事务已经提交的(插入)数据, 导致多次查询结果不一致.
                      //事物只针对更新语句有效


            关于写:
                丢失更新(了解).
        其实所谓的不考虑隔离性, 就是不考虑事务的隔离级别, 那么事务的隔离级别有哪些呢? 具体如下:
            隔离级别从小到大分别是:
                read uncommitted <  read committed < repeatable read < serializable
            隔离级别安全从低到高分别是:
                read uncommitted <  read committed < repeatable read < serializable
            隔离级别效率从高到第分别是:
                read uncommitted >  read committed > repeatable read > serializable
                
        总结:
            1. 四大隔离级别可能发生以及已经解决的问题.
               read uncommitted:
                会发生: 3, 脏读, 不可重复读, 虚读.
                已解决: 0
               read committed:
                会发生: 2, 不可重复读, 虚读.
                已解决: 1, 脏读.
               repeatable read:
                会发生: 1, 虚读.
                已解决: 2, 脏读, 不可重复读
               serializable:
                会发生: 0
                已解决: 3, 脏读, 不可重复读, 虚读.
                
        
            2. MySQL数据库默认的隔离级别是: repeatable read   Oracle数据库默认的隔离级别是:read committed
            3. MySQL数据库默认开启了事务的自动提交功能, 每一个SQL语句都是一个事务.
            4. Oracle数据库默认没有开启事务的自动提交功能, 需要我们手动开启.
            
            5. 和事务相关的SQL语句.
                select @@tx_isolation;              查看事务的隔离级别
                show variables like '%commit%';     查看是否开启事务的自动提交功能
                start transaction; 或者 begin;       开启事务
                commit;                             提交事务
                rollback;                           事务回滚, 即: 把数据还原到事务还没有执行之前的状态.
                set session transaction isolation level 隔离级别;   临时的设置事务的隔离级别为指定的级别.

案例 模拟转账演示事物

/*
    案例: 模拟转账, 来演示事务,  掌握

    涉及到的成员方法:  Connection接口中的成员方法.
        概述:
            它是java.sql包下的接口, 表示: 连接对象.
        作用:
            1. 获取可以执行SQL语句的对象.
            2. 可以管理事务.
        涉及到的成员方法:
            public Statement createStatement();
                获取可以执行SQL语句的对象, 没有预编译功能, 不能解决SQL注入攻击问题.
            public PreparedStatement prepareStatement(String sql);
                获取可以执行SQL语句的对象, 有预编译功能, 能解决SQL注入攻击问题.

            和事务相关的方法:
                public void setTransactionIsolation(int level);     设置事务的隔离级别
                public void commit();   提交事务
                public void rollback(); 事务回滚
                public void setAutoCommit(boolean flag)  设置是否开启自动提交事务功能.
 */
public class Demo02 {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stat = null;
        try {
            //1.获取数据库连接对象
            conn = C3P0Utils.getConnection();
            //2.执行sql语句的对象
            stat = conn.createStatement();
            //3.执行sql语句
            String sql2 = "update account set money = money - 100 where id = 1;";
            String sql1 = "update account set money = money + 100 where id = 2;";
            //开启事务,   关闭事务的自动提交功能, 相当于开启了我们自己的事务, 只要不提交或者回滚, 这下述的SQL语句都是同一个事务的.
            conn.setAutoCommit(false);

            //4. 操作sql语句
            int rs1 = stat.executeUpdate(sql1);
           // System.out.println(1 / 0);
            int rs2 = stat.executeUpdate(sql2);
            //5.操作结果集
            if (rs1 == 1 && rs2 == 1) {

                //转账成功
                conn.commit();      //提交事务
                System.out.println("转账成功");
            }

        } catch (SQLException e) {
            try {
                //事物回滚
                conn.rollback();
                System.out.println("转账失败");
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
            //6.释放资源
            C3P0Utils.release(conn, stat, null);
        }

    }
}