其他分享
首页 > 其他分享> > JDBC应用

JDBC应用

作者:互联网

JDBC应用

记录JDBC的学习笔记,为后期使用JDBC建立一个简单的速查页面,省去中间的推导等

池: Druid

DBUtil: Apache Commons DbUtils

也作为八股文的速查

数据库连接的基本步骤

以 Druid 连接池包为例,读取 /src/druid.properties 并建立 DataSource

  1. 准备配置文件

    与硬编码解耦合

    1. 配置文件(properties为例) -- 通常包含四个基本信息
      url, username, password, driverClassName

      (不同连接池工具的命名方式不一致,以Druid为例)

      url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&rewriteBatchedStatements=true
      driverClassName=com.mysql.cj.jdbc.Driver
      username=root
      password=root
      initialSize=10
      maxActive=10
      
  2. 读取配置文件

    1. 通常使用 Properties + InputStream
      (以 /src/druid.properties为例)

      private static DataSource source = null;
      /* 工具类的静态代码块,在类加载时读取配置并生成 DataSource */
      static {
          try {
              Properties pros = new Properties();
              /* 这里是一种读取方式 */
              InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
              pros.load(is);
              // DruidDataSourceFactory 读取配置并返回 DataSource对象
              source = DruidDataSourceFactory.createDataSource(pros);
          } catch (Exception e) {
              throw new RuntimeException(e);
          }
      }
      /* 工具类提供获取 DataSource 的方法 */
      public static DataSource getSource() {
          return source;
      }
      
  3. 获取连接 Connection

    public static Connection getConnection() throws SQLException {
        return source.getConnection();
    }
    
  4. 关闭资源
    Connection, Statement, ResultSet

    /* 重载closeResource */
    public static void closeResource(Connection conn, Statement ps) {
        closeResource(conn, ps, null);
    }
    /* 重载closeResource */
    public static void closeResource(Connection conn, Statement ps, ResultSet rs) {
        // 关闭资源
        try {
            if (ps != null)  ps.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (conn != null) conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (rs != null) rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    

ORM 编写 对象类和 DAO

ORM

Object Relational Mapping,简称ORM,以将数据库表中查询记录封装为类对象(称为Bean),便于编程语言操作。

例如表如下

Field Type Null Key Default Extra
id int NO PRI NULL auto_increment
name varchar(15) YES NULL
email varchar(20) YES NULL
birth date YES NULL
photo mediumblob YES NULL

Bean

Bean中变量命的命名与SQL中有区别,并且表中请不要使用SQL已有的字段如 order 命名,例如(命名不一致的应使用别名)

select first_name as firstName from yourtable;

SQL Col Name Bean Field Name
first_name firstName
last_find_time lastFindTime
close_time closeTime
import java.sql.Date;

public class Customer {
    private int id;
    private String name;
    private String email;
    private Date birth;

    /* constructor */
    public Customer() {super();}
    public Customer(int id, String name, String email, Date birth) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.birth = birth;
    }

    /* getter setter */
    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 getEmail() {return email;}
    public void setEmail(String email) {this.email = email;}
    public Date getBirth() {return birth;}
    public void setBirth(Date birth) {this.birth = birth;}

    @Override
    public String toString() {
        return "Customer{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", birth=" + birth +
                '}';
    }
    /* 省略 equals & hashCode */
}

DAO

data access object

DAO是和数据库的某一张表一一对应的,其中封装了增删改查基本等原子操作

通常建立如下的继承关系结构

(抽象类, 无抽象方法, 定义通用的增删改查操作, 多数工具包已经提供) BaseDAO
(接口, 定义对应表的原子操作) CustomerDAO
(实现类) CustomerDAOImpl extends BaseDAO implements CustomerDAO

BaseDAO

/**
 * DAO: data(base) access object
 * java代码操作数据库表的基本方法
 */
public abstract class BaseDAO {
    /**
     * SQL中占位符的数量应当与args形参列表的数目一致
     * @param conn 传入的SQL 连接, 本方法不会关闭 conn连接
     * @param sql SQL
     * @param args 形参列表
     * @return 表中改动的条目数
     */
    public int update(Connection conn, String sql, Object ... args) {
        int query = 0;
        QueryRunner queryRunner = null;
        
        try {
            queryRunner = new QueryRunner();
            query = queryRunner.update(conn, sql, args);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtils.closeResource(null, null);
        }
        return query;
    }

    /**
     * 针对不同表的通用查询操作,返回一条查询记录
     * @param conn sql连接
     * @param clazz 要返回的对象 T.class
     * @param sql sql语句
     * @param args sql语句的补充参数
     * @return 返回一个 T 对象
     * @param <T> 返回对象的类型
     */
    public <T> T getInstance(Connection conn, Class<T> clazz, String sql, Object...args)  {
        T query = null;
        QueryRunner queryRunner = null;
        BeanHandler<T> beanHandler = null;
        try {
            queryRunner = new QueryRunner();
            beanHandler = new BeanHandler<>(clazz);
            query = queryRunner.query(conn, sql, beanHandler, args);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // 迷惑操作, 步骤得有
            JDBCUtils.closeResource(null, null, null);
        }
        return query;
    }

    /**
     * 针对不同表的通用查询操作,返回查询集合
     * @param conn sql连接
     * @param clazz 要返回的对象 T.class
     * @param sql sql语句
     * @param args sql语句的补充参数
     * @return 返回一个 List<T> 对象
     * @param <T> 返回对象的类型
     */
    public <T> List<T> getForList(Connection conn, Class<T> clazz, String sql, Object...args) {

        BeanListHandler<T> beanListHandler = null;
        QueryRunner queryRunner = null;
        List<T> query = null;

        try {
            beanListHandler = new BeanListHandler<>(clazz);
            queryRunner = new QueryRunner();
            query = queryRunner.query(conn, sql, beanListHandler, args);
        } catch (SQLException  | SecurityException |
                 IllegalArgumentException  e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtils.closeResource(null, null, null);
        }
        return query;

    }


    /**
     * 允许 SQL 中使用 count(*)\ max(col) 等组函数
     * @param conn  sql连接
     * @param sql  sql语句
     * @param args  sql语句的补充参数
     * @return 返回查询值
     * @param <E> 返回值的类型
     */
    public <E> E getValue(Connection conn, String sql, Object...args) {
        E query = null;
        QueryRunner queryRunner = null;
        ScalarHandler scalarHandler = new ScalarHandler();

        try {
            queryRunner = new QueryRunner();
            query = (E)queryRunner.query(conn, sql, scalarHandler);
        } catch (SQLException ex) {
            throw new RuntimeException(ex);
        } finally {
            JDBCUtils.closeResource(null, null, null);
        }

        return query;
    }
}

CustomerDAO

public interface CustomerDAO {
    /**
     * 将 customer 添加到数据表中
     * @param conn sql连接
     * @param customer 待添加的 Customer 对象
     */
    void inset(Connection conn, Customer customer);

    /**
     * 根据指定的id删除一条记录
     * @param conn sql连接
     * @param id 待删除的id
     */
    void deleteById(Connection conn, int id);

    /**
     * 针对于内存中的 Customer 对象, 去修改 id=customer.id 的记录
     * @param conn sql连接
     * @param customer 内存中的 Customer 对象
     */
    void updateById(Connection conn, Customer customer);

    /**
     * 根据指定的id, 查询对应的 Customer
     * @param conn sql连接
     * @param id 待查找的id
     * @return Customer对象
     */
    Customer getCustomerById(Connection conn, int id);

    /**
     * 查询表中所有记录
     * @param conn sql连接
     * @return List<Customer>对象
     */
    List<Customer> getAll(Connection conn);

    /**
     *
     * 返回表的条目数
     * @param conn sql连接
     * @return 条目数
     */
    Long getCount(Connection conn);

    /**
     * 返回数据表中的最大生日
     * @param conn sql连接
     * @return java.sql.Date 最大生日
     */
    Date getMaxBirth(Connection conn);
}

CustomerDAOImpl

public class CustomerDAOImpl extends BaseDAO implements CustomerDAO {
    @Override
    public void inset(Connection conn, Customer customer) {
        String sql = "insert into customers(name, email, birth) values (?,?,?)";
        update(conn, sql, customer.getName(), customer.getEmail(), customer.getBirth());
    }

    @Override
    public void deleteById(Connection conn, int id) {
        String sql = "delete from customers where id = ?";
        update(conn, sql, id);
    }

    @Override
    public void updateById(Connection conn, Customer customer) {
        String sql = "update customers set name=?, email=?, birth=? where id=?";
        update(conn, sql, customer.getName(), customer.getEmail(), customer.getBirth(), customer.getId());
    }

    @Override
    public Customer getCustomerById(Connection conn, int id) {
        String sql = "select id, name, email, birth from customers where id = ?";
        return getInstance(conn, Customer.class, sql, id);
    }

    @Override
    public List<Customer> getAll(Connection conn) {
        String sql = "select id, name, email, birth from customers";
        return getForList(conn, Customer.class, sql);
    }

    @Override
    public Long getCount(Connection conn) {
        String sql = "select count(*) from customers";
        return getValue(conn, sql);
    }

    @Override
    public Date getMaxBirth(Connection conn) {
        String sql = "select max(birth) from customers";
        return getValue(conn, sql);
    }
}

测试用例

import org.junit.jupiter.api.Test;

class CustomerDAOImplTest {
    private CustomerDAOImpl dao = new CustomerDAOImpl();

    @Test
    void inset() {
        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            java.util.Date date = simpleDateFormat.parse("2022-08-26");
            Customer customer = new Customer(1, "猪鼻二号", "mewmew@cat.com", new Date(date.getTime()));

            dao.inset(conn, customer);

            System.out.println("添加成功");
        } catch (SQLException | ParseException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtils.closeResource(conn, null);
        }
    }

    @Test
    void deleteById() {
    }

    @Test
    void updateById() {
        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            java.util.Date date = simpleDateFormat.parse("1914-01-17");
            Customer customer = new Customer(28, "猪鼻二号", "mewmewmew@cat.com", new Date(date.getTime()));

            dao.updateById(conn, customer);

            System.out.println("修改成功");
        } catch (SQLException | ParseException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtils.closeResource(conn, null);
        }
    }

    @Test
    void getCustomerById() {
        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();

            Customer customer = dao.getCustomerById(conn, 28);

            System.out.println(customer);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtils.closeResource(conn, null);
        }
    }

    @Test
    void getAll() {
        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();

            List<Customer> all = dao.getAll(conn);

            all.forEach(System.out::println);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtils.closeResource(conn, null);
        }
    }

    @Test
    void getCount() {

        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();

            Long count = dao.getCount(conn);

            System.out.println("表中的记录数为:" + count);
        } catch (SQLException  e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtils.closeResource(conn, null);
        }
    }

    @Test
    void getMaxBirth() {
        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();

            Date maxBirth = dao.getMaxBirth(conn);

            System.out.println("表中最大的生日是:" + maxBirth);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtils.closeResource(conn, null);
        }
    }
}

注意事项

本文由于直接使用 Druid + Apache Commons DbUtils缩减代码量,java.sql 中两个重要的类

  1. 其中 Statement 由于无法解决SQL 注入,并且每一次调用他都要验证一次 SQL 语句,不利于实际开发使用,因此多采用 PreparedStatement,获取方式为 ps = conn.prepareStatement(sql);

  2. PreparedStatement预编译了SQL语句,如

    String sql =  "select name, email from customers where name = ?";
    ps.setObject(1, "张学友");
    

    也即预编译一次,可以设置多次执行多次

    ResultSet rs = ps.executeQuery(); // 以查询为例, 也可以调用  ps.execute()
    
  3. ResultSet的获取方式见2,是一个集合,可通过获取每一行的数据

    // 处理结果集
    while (rs.next()) {
        int id = rs.getInt(1);         // id
        String name = rs.getString(2); // name
        String email = rs.getString(3);// email
        Date date = rs.getDate(4);     // birth
        Customer customer = new Customer(id, name, email, date);
        System.out.println(customer);
    }
    

总之,原生JDBC使用非常不便,前文中的DAO仅是列出了一张表的原子操作,如果多张表联查,复杂操作等,使用JDBC会非常麻烦

END~!

标签:JDBC,public,Connection,应用,sql,null,id,conn
来源: https://www.cnblogs.com/jentreywang/p/16632587.html