JDBC技术

1 JDBC基础

1.1 jdbc入门

以前我们操作数据库都是通过远程连接mysql的客户端工具来登录数据库,登录成功之后,我们在客户端工具上(sqlYog)编写好sql语句,然后再将sql语句发送给数据库服务器执行,执行完的结果保存在数据库服务器上。

但是在我们实际开发中,我们往往需要将数据库里面的数据拿到程序中来,此时我们会在java程序中编写好sql语句,然后再通过这段java程序将sql语句发送给数据库服务器,数据库服务器执行完sql语句之后,再将执行的结果返回给我们的程序。这种技术就是jdbc技术。

一句话总结jdbc:使用java程序发送sql语句的技术。

接下来我们就使用jdbc技术去连接数据库。

使用jdbc连接数据库的前提是:

  • 数据库的ip地址
  • 数据库的端口号
  • 数据库名称
  • 数据库用户名和密码

连接数据库,需要用到数据库驱动包。所以我们必须将数据库驱动包自行引入到程序中。

1.1.1 使用jdbc技术连接数据库

在这里插入图片描述

此时这个依赖还没有真正的引入到项目中去,我们还需要手动的将这个依赖导入到项目中。

右键jar包–>add as Libary

连接数据库的方式有三种:

第一种:

public class JdbcDemo1 {

    //定义连接数据库的url  http:// sftp:// jdbc:mysql://
    String url = "jdbc:mysql://192.168.10.140:3306/day01";
    //定义连接数据库的用户名 用户名必须叫user
    String user = "root";
    //定义连接数据库的密码
    String password = "Admin2022!";

    /**
     * 连接数据库的第一种方式
     * @throws Exception
     */
    @Test
    public void test01() throws Exception{
        //创建数据库驱动
        Driver driver = new com.mysql.jdbc.Driver();
        Properties properties = new Properties();
        properties.setProperty("user",user);
        properties.setProperty("password",password);
        //connect:通过驱动类来连接数据库服务器的方法
        Connection conn = driver.connect(url, properties);
        System.out.println(conn);
    }
}

第二种:

/**
  * 连接数据库的第二种方式
  * @throws Exception
  */
@Test
public void test02() throws Exception{
    Driver driver = new com.mysql.jdbc.Driver();
    //注册驱动
    DriverManager.registerDriver(driver);
    //连接数据库
    Connection connection = DriverManager.getConnection(url, user, password);
    System.out.println(connection);
}

第三种(推荐使用这种):

/**
  * 连接数据库的第三种方式 -- 推荐使用这种
  * 通过反射的机制动态加载数据库驱动程序
  * @throws Exception
  */
@Test
public void test03() throws Exception{
    //通过反射的机制加载数据库驱动
    Class.forName("com.mysql.jdbc.Driver");
    //连接数据库
    Connection connection = DriverManager.getConnection(url, user, password);
    System.out.println(connection);
}
1.1.2 jdbc中的核心API
  • Driver接口 数据库驱动程序的接口,所有的数据库厂商都需要实现这个接口。

    • connect(String url, java.util.Properties info):连接数据库的方法。

      url: 连接数据库的地址。

      user:连接数据库需要用到的用户名。

      password:连接数据库需要用到的密码。

  • DriverManager类 数据库驱动管理器类,用于管理所有注册的驱动程序。

    • registerDriver(Driver driver):注册驱动的方法。
    • getConnection(String url,String username,String password) :获取数据库连接对象的方法。
  • Connection接口:表示java程序连接数据库的连接对象。如果我们可以打印输出这个连接对象的内存地址,说明我们成功的连接上了数据库。

    • createStatement(): 创建Statement对象,这个对象帮助我们执行sql语句。
    • prepareStatement(String sql):创建PreparedStatement对象,用于预编译sql语句。
    • prepareCall(String sql): 创建CallableStatement对象,用来执行存储过程语句。
  • Statement接口: 用于执行静态的sql语句。

    • int executeUpdate(String sql): 主要用来执行更新的语句(DML DDL)
    • ResultSet executeQuery(String sql): 主要用来执行DQL语句,返回ResultSet对象。
  • PreparedStatement接口:用于预编译sql语句

    • executeUpdate() 预编译DDL DML语句
    • executeQuery() 预编译DQL语句
  • CallableStatement接口:主要用来执行存储过程的语句

    • executeQuery() 调用存储过程的方法
  • ResultSet 接口:用来处理(封装)查询结果集

    • boolean next() 判断是否存在下一行数据
    • getXX() 取出对应列的值

1.2 使用Statement对象执行sql语句

1.2.1 执行DDL语句
public class JdbcDemo2 {

    //定义连接数据库的url
    String url = "jdbc:mysql://192.168.10.140:3306/jdbc";
    //定义连接数据库的用户名
    String username = "root";
    //定义连接数据库的密码
    String password = "Admin2022!";

    //执行DDL语句
    @Test
    public void test01() throws Exception{
        //首先需要获取数据库连接
        Class.forName("com.mysql.jdbc.Driver");
        //通过驱动管理类获取数据库连接
        Connection connection = DriverManager.getConnection(url, username, password);
        //创建Statement对象,准备执行sql语句
        Statement statement = connection.createStatement();
        String sql = "create table student(id int primary key auto_increment,name varchar(20) not null,gender varchar(2))";
        //执行sql语句 返回值是int 描述的影响的行数
        int count = statement.executeUpdate(sql);
        System.out.println(count);
        //关闭资源
        statement.close();
        connection.close();
    }
}

以上代码我们可以抽取出工具类。

public class JdbcUtils {

    //定义连接数据库的url
    static String url = "jdbc:mysql://192.168.10.140:3306/jdbc";
    //定义连接数据库的用户名
    static String username = "root";
    //定义连接数据库的密码
    static String password = "Admin2022!";

    //获取数据库连接的方法
    public static Connection getConnection() throws Exception{
        Class.forName("com.mysql.jdbc.Driver");
        Connection connection = DriverManager.getConnection(url, username, password);
        return connection;
    }
}
1.2.2 执行DML语句
  • 执行新增的语句
//执行DML语句-- insert
@Test
public void test02() throws Exception{
    Connection connection = JdbcUtils.getConnection();
    //创建Statement对象
    Statement statement = connection.createStatement();
    String sql = "insert into student(name,gender) values('eric','男')";
    //执行sql语句
    int result = statement.executeUpdate(sql);
    System.out.println("影响的行数是:" + result);
    //关闭资源
    statement.close();
    connection.close();

}
  • 执行修改的语句
//执行DML语句-- update
 @Test
 public void test03() throws Exception{
     Connection connection = JdbcUtils.getConnection();
     Statement statement = connection.createStatement();
     String name = "kobe";
     int id = 1;
     String sql = "update student set name='"+name+"' where id = "+id+"";
     int count = statement.executeUpdate(sql);
     System.out.println("修改成功,影响记录的行数是:" + count);
     //关闭资源
     statement.close();
     connection.close();
 }
  • 执行删除掉 语句
//执行DML语句--删除
@Test
public void test04() throws Exception{
     Connection connection = JdbcUtils.getConnection();
     Statement statement = connection.createStatement();
     int id = 2;
     String sql = "delete from student where id = "+id+"";
     //执行sql语句
     int count = statement.executeUpdate(sql);
     System.out.println("删除成功,影响记录的行数是:" + count);
     //关闭资源
     statement.close();
     connection.close();
}
1.2.3 执行DQL语句
//执行DQL语句--查询
@Test
public void test05() throws Exception{
    Connection connection = JdbcUtils.getConnection();
    Statement statement = connection.createStatement();
    String sql = "select * from student";
    //执行sql语句
    ResultSet rs = statement.executeQuery(sql);
    // rs封装了查询结果集 next() 获取一个指针,指针默认指向第一条记录的上一个单位,
    // 每次判断当前指针的下一个单位是否存在记录,如果存在就进入while循环,否则跳出循环。
     while(rs.next()){
        //getXX 根据字段的数据类型取对应字段的值。 可以根据字段的编号去获取值。编号从左到右 依次从1开始编号。
         /*int id = rs.getInt(1);
         String name = rs.getString(2);
         String gender = rs.getString(3);*/
         //按照字段的名称去取值
         int id = rs.getInt("id");
         String name = rs.getString("name");
         String gender = rs.getString("gender");
         System.out.println("id:" + id + "  姓名:" + name + "  性别:" + gender);
     }
     //关闭资源
     rs.close();
     statement.close();
     connection.close();
}

使用Statement对象执行sql语句的问题:没有预编译sql语句,容易引起sql注入的风险。

什么是sql注入?

不检测sql语句的语法格式,通过sql语句语法的一些漏洞,直接请求数据库。

我们举个例子:

我们要进行登录,通过输入用户名和密码就可以登录,sql语句定义如下:

select * from user where username = 'admin' and password = 'admin';

但是我们可以定义如下的sql语句,一样可以成功访问数据库

select * from user where username = 'aa' and password = ' ' or 1=1 -- '
-- ': 由于--在mysql中是注释,可以判定1=1后面的内容不会执行。由此sql语句会等价于
select * from user where username = 'aa' and password = '' or 1=1
由于1=1 条件是恒成立的。所以这条sql语句就是绕过密码直接登录,只要用户名正确就可以。

我们需要避免这个问题。对应的解决方案就是在sql语句执行之前,要对sql语句进行预编译(执行sql语句之前,检查sql语句的语法格式是否符合规范,如果符合规范就编译通过,可以执行。如果编译不通过,就不会执行这条sql语句)。

1.3 使用PreparedStatement对象执行sql语句

public class JdbcDemo3 {

    //执行新增的方法
    @Test
    public void test01() throws Exception{
        //获取数据库连接
        Connection connection = JdbcUtils.getConnection();
        String sql = "insert into student(name,gender) values(?,?)";// ?指代的是参数的占位符
        PreparedStatement pst = connection.prepareStatement(sql);//预编译sql语句,检查sql语句的格式是否符合规范
        //设置参数
        pst.setString(1,"curry");
        pst.setString(2,"男");
        //执行sql语句
        int count = pst.executeUpdate();
        System.out.println("影响记录的行数是:" + count);
        //关闭资源
        pst.close();
        connection.close();
    }

    //执行修改的方法
    @Test
    public void test02() throws Exception{
        //获取数据库连接
        Connection connection = JdbcUtils.getConnection();
        //准备sql语句
        String sql = "update student set name = ? where id = ?";
        //预编译sql语句
        PreparedStatement pst = connection.prepareStatement(sql);
        //设置参数
        pst.setString(1,"库里");
        pst.setInt(2,5);
        //执行sql语句
        int count = pst.executeUpdate();
        System.out.println("修改成功,影响记录的行数是:" + count);
        //关闭资源
        pst.close();
        connection.close();
    }

    //执行删除的方法
    @Test
    public void test03() throws Exception{
        Connection connection = JdbcUtils.getConnection();
        String sql = "delete from student where id = ?";
        PreparedStatement pst = connection.prepareStatement(sql);
        pst.setInt(1,5);
        int count = pst.executeUpdate();
        System.out.println("删除成功,影响记录的行数是:" + count);
        pst.close();
        connection.close();
    }

    //执行查询的方法
    @Test
    public void test04() throws Exception{
        Connection connection = JdbcUtils.getConnection();
        String sql = "select * from student where id = ?";
        PreparedStatement pst = connection.prepareStatement(sql);
        pst.setInt(1,1);
        ResultSet rs = pst.executeQuery();
        while(rs.next()){
            int id = rs.getInt("id");
            String name = rs.getString("name");
            String gender = rs.getString("gender");
            System.out.println("编号:" + id + "  姓名:" + name + "  性别:" + gender);
        }
        rs.close();
        pst.close();
        connection.close();
    }
}

1.4 使用CallableStatement执行存储过程

1.4.1 准备存储过程语句
-- 只带有输入参数的存储过程语句
DELIMITER $
   
   CREATE PROCEDURE proc_findByGender(IN sex VARCHAR(2))
   BEGIN
        SELECT * FROM student WHERE gender = sex;
   END $

DELIMITER; 

-- 调用存储过程
CALL proc_findByGender('男');

-- 带有输入 输出参数的存储过程语句
DELIMITER $
   
   CREATE PROCEDURE proc_findCount(IN sex VARCHAR(2),OUT total INT)
   BEGIN
        SELECT COUNT(*) INTO total FROM student WHERE gender = sex;
   END $

DELIMITER; 

-- 调用存储过程
CALL proc_findCount('男',@total);

-- 输出输出参数的结果
SELECT @total;
1.4.2 执行存储过程语句
//执行只有输入参数的存储过程语句
@Test
public void test05() throws Exception{
    Connection connection = JdbcUtils.getConnection();
    //准备存储过程语句
    String sql = "CALL proc_findByGender(?)";
    //预编译存储过程语句
    CallableStatement cst = connection.prepareCall(sql);
    //设置输入参数
    cst.setString(1,"女");
    //执行存储过程语句
    ResultSet resultSet = cst.executeQuery();
    while(resultSet.next()){
         int id = resultSet.getInt("id");
         String name = resultSet.getString("name");
         String gender = resultSet.getString("gender");
         System.out.println("编号:" + id + "  姓名:" + name + "  性别:" + gender);
     }
     cst.close();
     connection.close();
}
 //执行存储过程 -- 带有输入 输出参数的存储过程
 @Test
 public void test06() throws Exception{
     Connection connection = JdbcUtils.getConnection();
     //准备存储过程语句
     String sql = "CALL proc_findCount(?,?)";
     //预编译存储过程,prepareCall 不是 prepareStatement语句
     CallableStatement cst = connection.prepareCall(sql);
     //设置输入参数
     cst.setString(1,"男");
     //设置输出参数 输出参数的类型是jdbc类型
     cst.registerOutParameter(2, Types.INTEGER);
     //执行存储过程语句
     cst.executeQuery();
     //获取输出参数
     int count = cst.getInt(2);
     System.out.println("对应性别数量是:" + count);
     //关闭资源
     cst.close();
     connection.close();
}

1.5 使用properties配置文件优化jdbc程序

  • 定义properties配置文件
# 连接数据库的用户名
user=root
# 连接数据库的密码
password=Admin2022!
# 连接数据库的url
url=jdbc:mysql://192.168.10.140:3306/jdbc
# 数据库的驱动类
driverClass=com.mysql.jdbc.Driver
  • 优化工具类
public class JdbcUtils {

    private static String user = null;
    private static String password = null;
    private static String url = null;
    private static String driverClass = null;


    //读取properties配置文件,加载数据库驱动
    static{
        try {
            Properties properties = new Properties();
            InputStream in = JdbcUtils.class.getResourceAsStream("/db.properties");
            properties.load(in);
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            url = properties.getProperty("url");
            driverClass = properties.getProperty("driverClass");
            Class.forName(driverClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取数据库连接
    public static Connection getConnection() throws Exception{
        Connection connection = DriverManager.getConnection(url, user, password);
        return connection;
    }

    //释放资源
    public static void close(Connection conn, PreparedStatement pst){
        if(pst != null){
            try {
                pst.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void close(Connection conn, PreparedStatement pst, ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(pst != null){
            try {
                pst.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }


    public static void close(Connection conn, CallableStatement pst, ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(pst != null){
            try {
                pst.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void close(Connection conn, CallableStatement pst){
        if(pst != null){
            try {
                pst.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 测试
public class TestUser {

    @Test
    public void test01() throws Exception{
        Connection connection = JdbcUtils.getConnection();
        String sql = "select * from tb_user";
        PreparedStatement pst = connection.prepareStatement(sql);
        ResultSet rs = pst.executeQuery();
        while(rs.next()){
            int id = rs.getInt("id");
            String username = rs.getString("username");
            String password = rs.getString("password");
            System.out.println("用户编号:" + id + " 用户名:" + username + " 密码:" + password);
        }
        //关闭资源
        JdbcUtils.close(connection,pst,rs);
    }
}

1.6 连接池

在系统初始化的时候,将数据库连接作为对象存储在内存中(集合),当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已经建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连 接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数 以及每个连接的最大使用次数、最大空闲时间等等,也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。

目前连接池可分为:自定义连接池、DBCP连接池、C3P0连接池、Druid德鲁伊连接池

连接池有很多属性:

  • 最小连接数: 是数据库一直保持的数据库连接数,所以如果应用程序对数据库连接的使用量不大,将有大量的数据库资源被浪费。

  • 初始化连接数: 连接池启动时创建的初始化数据库连接数量。

  • 最大连接数: 是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求被加入到等待队列中。

  • 最大等待时间: 当没有可用连接时,连接池等待连接被归还的最大时间,超过时间则抛出异常,可设置参数为0或者负 数使得无限等待(根据不同连接池配置)。

数据库连接池又可以分为空闲连接池和活动连接池。

  1. 如果空闲连接池有连接对象,jdbc程序会直接找空闲连接池去要连接。

在这里插入图片描述

  1. 如果空闲连接池的连接全部被要走了,jdbc程序要找活动连接池获取连接对象,先看看活动连接池是否达到上限。

如果没有,就new一个连接对象,然后给活动连接池,再返回给jdbc程序,用完之后再归还空闲的连接对象。

如果达到了活动连接池的上限,那么就将最早创建的连接对象返回给用户使用。

1.6.1 自定义连接池
  • 自定义连接池
/**
 * 自定义连接池
 */
public class PoolCustomize {

    //创建一个集合,专门存放连接对象
    private static LinkedList<Connection> pools = new LinkedList<>();

    //在静态代码块里面初始化一些连接对象
    static{
        try {
            //加载数据库驱动
            Class.forName("com.mysql.jdbc.Driver");
            //初始化10个连接对象
            for(int i = 0;i<10;i++){
                Connection connection = JdbcUtils.getConnection();
                pools.add(connection);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取连接,从连接池里获取连接给用户使用
    public static Connection getConnection(){
        Connection connection = pools.removeFirst();
        return connection;
    }

    //连接用完之后,需要将连接对象返回给连接池
    public static void backToPool(Connection connection){
        pools.addLast(connection);
    }
}
  • 测试连接池是否可用
//从连接池里面获取连接使用
@Test
public void test02() throws Exception{
    Connection connection = PoolCustomize.getConnection();
    //System.out.println(connection);
    String sql = "select * from tb_user where id = ?";
    PreparedStatement pst = connection.prepareStatement(sql);
    pst.setInt(1,1);
    ResultSet rs = pst.executeQuery();
      if(rs.next()){
         int id = rs.getInt("id");
         String username = rs.getString("username");
         String password = rs.getString("password");
         System.out.println("用户ID:" + id + " 用户名:" + username + " 密码:" + password);
     }
     //归还连接给连接池
     PoolCustomize.backToPool(connection);
}

2 jdbc加强

2.1 Dbutils组件的基本概述

2.2.1 什么是dbutils

Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。

一句话,dbutils可以简化jdbc的代码。如果我们使用dbutils,需要引入对应依赖:commons-dbutils-1.6.jar

2.1.2 DbUtils组件中的核心API
  • QueryRunner: 是一个核心的工具类,通过这个工具类,我们实现数据的增删改查。
    • int update(Connection conn,String sql,Object… params) 执行更新的操作
    • int update(String sql,Object… params)
    • T query(Connection conn,String sql,ResultSetHander hander,Object… params) 执行查询的操作
  • BeanHandler 处理查询结果集,返回单个的查询对象。
  • BeanListHandler 处理查询结果集,返回List集合。
  • ArrayHandler 返回查询结果集的第一行数据,并将数据封装成对象数组。Object[]
  • ScalarHandler 返回聚合函数的结果。
  • MapHandler 将查询结果集封装成Map集合。

2.2 dbutils的使用

  • 引入依赖
c3p0-0.9.1.2.jar
commons-dbutils-1.6.jar
mysql-connector-java-5.1.6.jar
  • 引入c3p0连接池的配置文件,放在src目录下面。配置文件的名称必须叫c3p0-config.xml。
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://192.168.10.140:3306/jdbc</property>
        <property name="user">root</property>
        <property name="password">Admin2022!</property>
        <!--
            初始化的连接数量 在连接池里面初始化10个连接对象
        -->
        <property name="initialPoolSize">10</property>
        <!--
           最大空闲时间
           某一个连接对象空闲时长最多是30s,超过了30s,该连接对象会被自动回收
        -->
        <property name="maxIdleTime">30</property>
        <!--
            最大连接数量
            在连接池里面存在最多的连接数量
        -->
        <property name="maxPoolSize">100</property>
        <!--
           最小连接数量
        -->
        <property name="minPoolSize">10</property>
    </default-config>
</c3p0-config>
  • 编写工具类
public class JdbcUtils {

    //初始化一个数据源对象
    static DataSource dataSource = new ComboPooledDataSource();

    //获取数据源的方法
    public static DataSource getDataSource() {
        return dataSource;
    }

    //获取连接对象的方法
    public static Connection getConnection() throws Exception{
        return dataSource.getConnection();
    }
}
  • 编写实体类(编写一个类和数据表形成映射关系)
public class Student {

    private int id;
    private String name;
    private String gender;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                '}';
    }
}
  • 编写测试类
public class TestDbUtils {

    @Test
    public void test01() throws Exception{
        Connection connection = JdbcUtils.getConnection();
        System.out.println(connection);
    }

    //执行更新的操作 -- insert
    @Test
    public void test02() throws Exception{
        QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
        //QueryRunner runner = new QueryRunner();
        String sql = "insert into student(name,gender) values(?,?)";
        int result = runner.update(sql, "铁蛋", "男");
        System.out.println("影响的行数是:" + result);
    }

    //执行更新操作 -- update
    @Test
    public void test03() throws Exception{
        QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "update student set name  = ?,gender = ? where id = ?";
        int count = runner.update(sql,"小丽","女",6);
        System.out.println("修改成功,影响的行数是:" + count);
    }

    //执行更新操作 -- delete
    @Test
    public void test04() throws Exception{
        QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "delete from student where id = ?";
        int count = runner.update(sql, 6);
        System.out.println("删除成功,影响的行数是:" + count);
    }

    //查询操作 -- select
    @Test
    public void test05() throws Exception{
        QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "select * from student";
        /**
         * BeanListHandler 专门用来处理数据表结果集的。负责将结果集转换成List集合。
         */
        List<Student> list = runner.query(sql, new BeanListHandler<Student>(Student.class));
        /*for(Student student : list){
            System.out.println(student);
        }*/
        list.forEach(student -> {
            System.out.println(student);
        });
    }

    @Test
    public void test06() throws Exception{
        QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "select * from student where id = ?";
        Student student = runner.query(sql, new BeanHandler<>(Student.class),1);
        System.out.println(student);
    }
    
}

2.3 BeanUtils组件

BeanUtils组价是apache提供的一套开源的api。用来简化javabean的操作。我们要使用BeanUtils的话,需要引入以下几个jar包:

commons-beanutils-1.8.3.jar
commons-logging-1.1.3.jar  --这个包一定要引入,否则会报异常
2.3.1 beanutils的基本用法
  • 前期准备
public class Admin {

    private String username;
    private String password;
    private Integer age;

    public Admin(){}

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Admin{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Student {

    private String name;
    private Integer age;
    private Date birthday;

    public Student(){}

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                '}';
    }
}
  • 使用BeanUtils组件进行基本操作
public class BeanUtilsDemo1 {

    @Test
    public void test01() throws Exception{
        Admin admin = new Admin();
        //以前的做法
        //admin.setUsername("eric");
        //admin.setPassword("eric");

        /**
         * BeanUtils组件可以实现对象属性的拷贝
         * copyProperty 实现对象属性的拷贝
         * 参数1:给哪个对象拷贝属性
         * 参数2:属性名称
         * 参数3: 属性的值
         * 如果涉及到基本数据类型的值的拷贝,BeanUtils还可以对数据类型进行自动转换。
         */
        BeanUtils.copyProperty(admin,"username","sunny");
        BeanUtils.copyProperty(admin,"password","sunny");
        //BeanUtils.copyProperty(admin,"age",12);
        BeanUtils.copyProperty(admin,"age","12");
        System.out.println(admin);
        /**
         * 实现对象的拷贝
         * copyProperties
         * 参数1:需要拷贝的新对象
         * 参数2:被拷贝的对象
         */
        Admin newAdmin = new Admin();
        BeanUtils.copyProperties(newAdmin,admin);
        System.out.println(newAdmin);

        /**
         * 将Map集合中的数据进行拷贝
         */
        Admin adminMap = new Admin();
        Map<String,Object> map = new HashMap<>();
        map.put("username","kobe");
        map.put("password","kobe");
        BeanUtils.populate(adminMap,map);
        System.out.println(adminMap);
    }

    @Test
    public void test02() throws Exception{
        Student student = new Student();
        ConvertUtils.register(new Converter() {
            @Override
            public Object convert(Class type, Object value) {
                if(type != Date.class){
                    return null;
                }
                if(value == null || value.toString().trim().equals("")){
                    return null;
                }
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                try {
                    return sdf.parse(value.toString());
                } catch (ParseException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
        }, Date.class);
        BeanUtils.copyProperty(student,"name","eric");
        BeanUtils.copyProperty(student,"age",18);
        BeanUtils.copyProperty(student,"birthday","1999-12-03");
        System.out.println(student);
    }

    @Test
    public void test03() throws Exception{
        Student student = new Student();
        ConvertUtils.register(new DateLocaleConverter(),Date.class);
        BeanUtils.copyProperty(student,"name","eric");
        BeanUtils.copyProperty(student,"age",18);
        BeanUtils.copyProperty(student,"birthday","1999-12-03");
        System.out.println(student);
    }
}

2.4 Jdbc进行事务处理

我们也可以在jdbc中实现事务的控制。jdbc提供了相关事务控制的api,具体如下:

connection.setAutoCommit(false);  关闭事务的自动提交,手动提交事务。
connection.commit(); 提交事务
connection.rollback(); 回滚事务

案例:使用jdbc进行转账事务的控制

/**
 * 实现转账功能
 * eric 向 james 转账500
 * 如果不出现异常,转账成功(进行事务的提交)
 * 如果出现异常,转账失败(进行事务的回滚)
 */
public class TestAccount {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement pst1 = null;
        PreparedStatement pst2 = null;
        try {
            connection = JdbcUtils.getConnection();
            //手动提交事务  默认为true 就是自动提交 false 手动提交事务
            connection.setAutoCommit(false);
            //准备sql语句--扣钱的语句
            String sql1 = "update account set money = money - ? where id = ?";
            //准备sql语句--加钱的语句
            String sql2 = "update account set money = money + ? where id = ?";
            //预编译sql
            pst1 = connection.prepareStatement(sql1);
            pst2 = connection.prepareStatement(sql2);
            //设置参数
            pst1.setInt(1,500);
            pst1.setInt(2,1);
            pst2.setInt(1,500);
            pst2.setInt(2,2);
            //执行sql
            pst1.executeUpdate();
            //手动模拟异常,表示转账失败
            int i = 10 / 0;
            pst2.executeUpdate();
            //提交事务
            connection.commit();
        } catch (Exception e) {
            if(connection != null){
                try {
                    //回滚事务
                    connection.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }
        }finally {
            //关闭资源
            JdbcUtils.close(connection,pst1);
            JdbcUtils.close(connection,pst2);
        }
    }
}

2.5 分层开发

  • 准备工具类
package com.qf.utils;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {

    private static String user = null;
    private static String password = null;
    private static String url = null;
    private static String driverClass = null;


    //读取properties配置文件,加载数据库驱动
    static{
        try {
            Properties properties = new Properties();
            InputStream in = JdbcUtils.class.getResourceAsStream("/db.properties");
            properties.load(in);
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            url = properties.getProperty("url");
            driverClass = properties.getProperty("driverClass");
            Class.forName(driverClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取数据库连接
    public static Connection getConnection() throws Exception{
        Connection connection = DriverManager.getConnection(url, user, password);
        return connection;
    }

    //释放资源
    public static void close(Connection conn, PreparedStatement pst){
        if(pst != null){
            try {
                pst.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void close(Connection conn, PreparedStatement pst, ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(pst != null){
            try {
                pst.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }


    public static void close(Connection conn, CallableStatement pst, ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(pst != null){
            try {
                pst.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void close(Connection conn, CallableStatement pst){
        if(pst != null){
            try {
                pst.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 准备实体类
package com.qf.pojo;

public class Account {

    private Integer id;
    private String name;
    private Integer money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getMoney() {
        return money;
    }

    public void setMoney(Integer money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

  • 准备dao及其实现类
package com.qf.dao;

import com.qf.pojo.Account;

import java.util.List;

public interface AccountDao {

    //根据id查询对应的账户信息
    public Account findAccountById(Integer id);

    //查询所有的账户信息
    public List<Account> findAll();

    //新增账户信息
    public int addAccount(Account account);

    //修改账户信息
    public int updateAccount(Account account);

    //删除账户信息
    public int deleteAccount(Integer id);

    //根据姓名查询对应的账户信息
    Account findAccountByName(String name);

    //更新账户余额
    void updateMoney(String source, Integer money);
}
package com.qf.dao.impl;

import com.qf.dao.AccountDao;
import com.qf.pojo.Account;
import com.qf.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class AccountDaoImpl implements AccountDao {

    //根据id查询对应的账户信息
    @Override
    public Account findAccountById(Integer id) {
        Connection connection = null;
        PreparedStatement pst = null;
        ResultSet rs = null;
        try {
            connection = JdbcUtils.getConnection();
            String sql = "select * from account where id = ? ";
            pst = connection.prepareStatement(sql);
            pst.setInt(1,id);
            rs = pst.executeQuery();
            Account account = new Account();
            if(rs.next()){
                account.setId(rs.getInt("id"));
                account.setName(rs.getString("name"));
                account.setMoney(rs.getInt("money"));
            }
            return account;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.close(connection,pst,rs);
        }
    }

    //查询所有账户信息
    @Override
    public List<Account> findAll() {
        Connection connection = null;
        PreparedStatement pst = null;
        ResultSet rs = null;
        try {
            connection = JdbcUtils.getConnection();
            String sql = "select * from account";
            pst = connection.prepareStatement(sql);
            rs = pst.executeQuery();
            List<Account> accountList = new ArrayList<>();
            while(rs.next()){
                Account account = new Account();
                account.setId(rs.getInt("id"));
                account.setName(rs.getString("name"));
                account.setMoney(rs.getInt("money"));
                accountList.add(account);
            }
            return accountList;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.close(connection,pst,rs);
        }

    }

    //新增账户信息
    @Override
    public int addAccount(Account account) {
        Connection connection = null;
        PreparedStatement pst = null;
        try {
            connection = JdbcUtils.getConnection();
            String sql = "insert into account(name,money) values(?,?)";
            pst = connection.prepareStatement(sql);
            pst.setString(1,account.getName());
            pst.setInt(2,account.getMoney());
            int count = pst.executeUpdate();
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.close(connection,pst);
        }

    }

    //修改账户信息
    @Override
    public int updateAccount(Account account) {
        Connection connection = null;
        PreparedStatement pst = null;
        try {
            connection = JdbcUtils.getConnection();
            String sql = "update account set name = ?,money = ? where id = ?";
            pst = connection.prepareStatement(sql);
            pst.setString(1,account.getName());
            pst.setInt(2,account.getMoney());
            pst.setInt(3,account.getId());
            int count = pst.executeUpdate();
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.close(connection,pst);
        }
    }

    //删除账户信息
    @Override
    public int deleteAccount(Integer id) {
        Connection connection = null;
        PreparedStatement pst = null;
        try {
            connection = JdbcUtils.getConnection();
            String sql = "delete from account  where id = ?";
            pst = connection.prepareStatement(sql);
            pst.setInt(1,id);
            int count = pst.executeUpdate();
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.close(connection,pst);
        }
    }

    @Override
    public Account findAccountByName(String name) {
        Connection connection = null;
        PreparedStatement pst = null;
        ResultSet rs = null;
        try {
            connection = JdbcUtils.getConnection();
            String sql = "select * from account where name = ? ";
            pst = connection.prepareStatement(sql);
            pst.setString(1,name);
            rs = pst.executeQuery();
            Account account = new Account();
            if(rs.next()){
                account.setId(rs.getInt("id"));
                account.setName(rs.getString("name"));
                account.setMoney(rs.getInt("money"));
            }
            return account;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.close(connection,pst,rs);
        }
    }

    //更新账户信息
    @Override
    public void updateMoney(String source, Integer money) {
        Connection connection = null;
        PreparedStatement pst = null;
        try {
            connection = JdbcUtils.getConnection();
            String sql = "update account set money = money -? where name = ?";
            pst = connection.prepareStatement(sql);
            pst.setInt(1,money);
            pst.setString(2,source);
            //执行sql语句
            pst.executeUpdate();
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.close(connection,pst);
        }
    }
}

  • 准备service及其实现类
package com.qf.service;

import com.qf.pojo.Account;

import java.util.List;

public interface AccountService {

    //根据id查询对应的账户信息
    public Account findAccountById(Integer id);

    //查询所有的账户信息
    public List<Account> findAll();

    //新增账户信息
    public int addAccount(Account account);

    //修改账户信息
    public int updateAccount(Account account);

    //删除账户信息
    public int deleteAccount(Integer id);

    //定义一个转账的业务
    public void transfer(String source,String target,Integer money);
}
package com.qf.service.impl;

import com.qf.dao.AccountDao;
import com.qf.dao.impl.AccountDaoImpl;
import com.qf.pojo.Account;
import com.qf.service.AccountService;
import com.qf.utils.JdbcUtils;

import java.util.List;

public class AccountServiceImpl implements AccountService {

    AccountDao accountDao = new AccountDaoImpl();

    @Override
    public Account findAccountById(Integer id) {
        return accountDao.findAccountById(id);
    }

    @Override
    public List<Account> findAll() {
        return accountDao.findAll();
    }

    @Override
    public int addAccount(Account account) {
        return accountDao.addAccount(account);
    }

    @Override
    public int updateAccount(Account account) {
        return accountDao.updateAccount(account);
    }

    @Override
    public int deleteAccount(Integer id) {
        return accountDao.deleteAccount(id);
    }

    //转账业务实现的方法
    @Override
    public void transfer(String source, String target, Integer money) {
        //查询扣款人的信息
        Account sourceAccount = accountDao.findAccountByName(source);
        //查询入账人的信息
        Account targetAccount = accountDao.findAccountByName(target);
        //如果两个账户信息都存在这些转账操作
        if(sourceAccount != null && targetAccount != null){
            //继续判断账户余额是否足够
            if(sourceAccount.getMoney() >= money){
                //执行转账操作 -- 转出
                accountDao.updateMoney(source,money);
                //执行转账操作 -- 转入
                accountDao.updateMoney(target,-money);
            }else{
                System.out.println("余额不足....");
            }
        }
    }
}

  • 测试类
package com.qf.test;

import com.qf.pojo.Account;
import com.qf.service.AccountService;
import com.qf.service.impl.AccountServiceImpl;
import org.junit.jupiter.api.Test;

import java.util.List;

public class TestAccount {

    AccountService accountService =  new AccountServiceImpl();

    @Test
    public void test01(){
        Account account = accountService.findAccountById(1);
        System.out.println(account);
    }

    @Test
    public void test02(){
        List<Account> accountList = accountService.findAll();
        for(Account account : accountList){
            System.out.println(account);
        }
    }

    @Test
    public void test03(){
        Account account = new Account();
        account.setName("kobe");
        account.setMoney(2000);
        int count = accountService.addAccount(account);
        System.out.println("新增成功,影响的记录行数是:" + count);
    }

    @Test
    public void test04(){
        Account account = new Account();
        account.setName("curry");
        account.setMoney(2000);
        account.setId(2);
        int count = accountService.updateAccount(account);
        System.out.println("修改成功,影响记录的行数是:" + count);
    }

    @Test
    public void test05(){
        int count = accountService.deleteAccount(3);
        System.out.println("删除成功,影响记录的行数是:" + count);
    }

    //执行转账业务操作
    @Test
    public void test06(){
        accountService.transfer("eric","curry",500);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值