JDBC

JDBC总结

JDBC概述

  1. 数据的持久化

    持久化(persistence):将数据保存到可掉电式存储设备中以供之后使用

  2. JDBC的理解

    JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API)

    优点:

    从开发者的角度:不需要关注具体的数据库的细节

    从数据库厂商的角度:只需要提供标准的具体实现--数据库的驱动

数据库的连接

获取连接:

/*获取连接的方式
将数据库需要的四个基本信息声明在配置文件中,通过读取配置文件的方式获取连接
1. 实现了数据与代码分离--解耦
2. 如果需要修改配置文件信息,可以避免程序重新打包
 */
@Test
public void getConnection() throws Exception {
    //1. 读取配置文件中的4个基本信息
    InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
    Properties pros = new Properties();
    pros.load(is);

    String user = pros.getProperty("user");
    String password = pros.getProperty("password");
    String url = pros.getProperty("url");
    String driverClass = pros.getProperty("driverClass");

    //2. 加载驱动
    Class.forName(driverClass);

    //3. 获取链接
    Connection connection = DriverManager.getConnection(url, user, password);
    System.out.println(connection);

}

配置文件:jdbc.properties(声明在工程的src下)

user=root
password=****
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=truejava
driverClass=com.mysql.jdbc.Driver

通用操作:JDBCUtils

package com.th1024.utils;

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

/**
 * 操作数据库的工具类
 *
 * @author TuHong
 * @create 2021-03-09 10:32
 */
public class JDBCUtils {

    /*
    获取数据库的连接
     */
    public static Connection getConnection() throws Exception {
        //1. 读取配置文件中的4个基本信息
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");

        Properties properties = new Properties();
        properties.load(is);

        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driverClass = properties.getProperty("driverClass");

        //2. 加载驱动
        Class.forName(driverClass);

        //3. 获取连接
        Connection connection = DriverManager.getConnection(url, user, password);
        return connection;
    }

    /*
    关闭连接和statement的操作
     */
    public static void closeResource(Connection connection, Statement statement){
        try {
            if(statement != null)
                statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if(connection != null)
                connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /*
    关闭资源的操作
     */
    public static void closeResource(Connection connection, Statement statement, ResultSet resultSet){
        try {
            if(statement != null)
                statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if(connection != null)
                connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if(resultSet != null)
                resultSet.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Statement接口实现CRUD操作(了解)

代码演示:

	// 使用Statement的弊端:需要拼写sql语句,并且存在SQL注入的问题
	@Test
	public void testLogin() {
		Scanner scan = new Scanner(System.in);
	System.out.print("用户名:");
	String userName = scan.nextLine();
	System.out.print("密   码:");
	String password = scan.nextLine();

	// SELECT user,password FROM user_table WHERE USER = '1' or ' AND PASSWORD = '='1' or '1' = '1';
	String sql = "SELECT user,password FROM user_table WHERE USER = '" + userName + "' AND PASSWORD = '" + password
			+ "'";
	User user = get(sql, User.class);
	if (user != null) {
		System.out.println("登陆成功!");
	} else {
		System.out.println("用户名或密码错误!");
	}
}

弊端:

  1. 存在拼串操作,繁琐

  2. 存在SQL注入问题

    SQL注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句段或命令(如:SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1'),从而利用系统的SQL引擎完成恶意行为的做法

  3. Statement无法操作Blob类型变量,批量操作效率低

PreparedStatement替换Statement实现CRUD操作

  1. PreparedStatement的理解

    ① PreparedStatement是Statement的子接口

    ② 预编译SQL语句

  2. 使用PreparedStatement完成通用的增、删、改操作

    //通用的增删改操作
    public void update(String sql,Object... args) {
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            //1. 获取数据库的连接
            connection = JDBCUtils.getConnection();
            //2. 预编译sql语句,返回PreparedStatement实例
            ps = connection.prepareStatement(sql);
            //3. 填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1,args[i]);
            }
            //4. 执行
            ps.execute();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //5. 关闭资源
            try {
                if(ps != null)
                    ps.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            try {
                if(connection != null)
                    connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    
  3. 使用PreparedStatement完成通用的查询操作

    /*
    针对不同的表的通用的查询操作,返回一条记录
     */
    public <T>T getInstance(Class<T> tClass,String sql,Object... args){
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();
            ps = conn.prepareStatement(sql);
    
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1,args[i]);
            }
            rs = ps.executeQuery();
            //获取结果集的元数据:ResultSetMetaData
            ResultSetMetaData rsmd = rs.getMetaData();
            //通过ResultSetMetaData获取结果集中的列数
            int columnCount = rsmd.getColumnCount();
            if(rs.next()){
                T t = tClass.newInstance();
                //处理结果集一行数据中的每一个列
                for (int i = 0; i < columnCount; i++) {
                    //获取列值
                    Object columnValue = rs.getObject(i + 1);
                    //获取列名或列的别名
                    String columnLabel = rsmd.getColumnLabel(i + 1);
                    //给t对象指定的columnLabel属性,赋值为columnValue:通过反射
                    Field field = tClass.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }
                return t;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(conn,ps,rs);
        }
        return null;
    }
    
    /*
    针对不同的表的通用的查询操作,返回多条记录
     */
    public <T> List<T> getInstances(Class<T> tClass, String sql, Object... args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();
            ps = conn.prepareStatement(sql);
    
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1,args[i]);
            }
            rs = ps.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            //创建集合对象
            ArrayList<T> list = new ArrayList<>();
            while(rs.next()){
                T t = tClass.newInstance();
                //处理结果集一行数据中的每一个列,给t对象指定的属性赋值
                for (int i = 0; i < columnCount; i++) {
                    Object columnValue = rs.getObject(i + 1);
                    String columnLabel = rsmd.getColumnLabel(i + 1);
    
                    Field field = tClass.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(conn,ps,rs);
        }
        return null;
    }
    
  4. 总结

    两种思想:面向接口编程思想、

    ORM编程思想(Object Relational Mapping)

    • 一个数据表对应一个java类
    • 表中的一条记录对应java类的一个对象
    • 表中的一个字段对应java类的一个属性

    两种技术:

    使用结果集的元数据:ResultSetMetaData、

    • getColumnCount():获取列数
    • getColumnLabel():获取列的别名或列名

    反射:

    • 创建对应的运行时类的对象
    • 在运行时,动态地调用指定的运行时类的属性、方法

PreparedStatement操作Blob类型的变量

  1. 写入操作的方法:setBlob(InputStream is)

  2. 读取操作的方法:

    Blob blob = getBlob(int index);

    InputStream is = blob.getBinaryStream();

  3. 具体的insert操作

    /*
    测试使用PreparedStatement操作Blob类型的数据
     */
    @Test
    public void testInsert() throws Exception {
        Connection conn = JDBCUtils.getConnection();
        String sql = "insert into customers(name,email,birth,photo) values(?,?,?,?)";
        PreparedStatement ps = conn.prepareStatement(sql);
    
        ps.setObject(1,"枫花恋");
        ps.setObject(2,"123@qq.com");
        ps.setObject(3,"1999-01-01");
        FileInputStream fis = new FileInputStream(new File("fenghualian.jpg"));
        ps.setBlob(4,fis);
        ps.execute();
    
        JDBCUtils.closeResource(conn,ps);
    
    }
    
  4. 具体的query操作

    /*
    查询数据表customers中Blob类型的字段
     */
    @Test
    public void testQuery() {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        InputStream is = null;
        FileOutputStream fos = null;
        try {
            conn = JDBCUtils.getConnection();
            String sql = "select id,name,email,birth,photo from customers where id = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1,23);
            rs = ps.executeQuery();
            if(rs.next()){
                //方式一
    //            int id = rs.getInt(1);
    //            String name = rs.getString(2);
    //            String email = rs.getString(3);
    //            Date birth = rs.getDate(4);
                //方式二
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String email = rs.getString("email");
                Date birth = rs.getDate("birth");
    
                Customer customer = new Customer(id, name, email, birth);
                System.out.println(customer);
    
                //将Blob类型的字段下载下来,以文件的形式保存在本地
                Blob photo = rs.getBlob("photo");
                is = photo.getBinaryStream();
                fos = new FileOutputStream("fenghualian1.jpg");
                byte[] buffer = new byte[1024];
                int len;
                while((len = is.read(buffer)) != -1){
                    fos.write(buffer,0,len);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(fos != null)
                    fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(is != null)
                    is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            JDBCUtils.closeResource(conn,ps,rs);
        }
    }
    

PreparedStatement实现高效的批量操作

代码实现:

/*
    批量插入的方式二:使用PreparedStatement
    优化1:
    1. addBatch()、executeBatch()、clearBatch()
    2. MySQL服务器默认是关闭批处理的,需要一个参数,让MySQL开启批处理的支持
        ?rewriteBatchedStatements=true 写在配置文件的url后面
    3. 使用更新的MySQL驱动:mysql-connector-java-5.1.37-bin.jar
    优化2:设置连接不允许自动提交数据
     */
    @Test
    public void testInsert() {
        Connection conn = null;
        PreparedStatement ps = null;
        try {

            long start = System.currentTimeMillis();

            conn = JDBCUtils.getConnection();
            conn.setAutoCommit(false);

            String sql = "insert into goods(name) values(?)";
            ps = conn.prepareStatement(sql);
            for (int i = 1; i <= 20000; i++) {
                ps.setObject(1,"name_" + i);

                //1. “攒”sql
                ps.addBatch();
                if(i % 500 == 0){
                    //2. 执行Batch
                    ps.executeBatch();
                    //3. 清空Batch
                    ps.clearBatch();
                }
//                ps.execute();
            }
            //提交数据
            conn.commit();

            long end = System.currentTimeMillis();
            System.out.println("花费的时间为" + (end - start));//未优化:12427--优化:1334
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(conn,ps);
        }
    }

总结:

PreparedStatement与Statement的异同

  • PreparedStatement是Statement的子接口
  • 开发中使用PreparedStatement代替Statement
  • PreparedStatement可以解决Statement存在的效率低下以及SQL注入问题

数据库事务

  1. 事务

    一组逻辑操作单元,是数据从一种状态变换到另外一种状态

    四大属性:ACID

    原子性(Atomicity):原子性是指事务是一个不可分割的工作单元,事务中的操作要么都发生,要么都不发生

    一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态

    隔离性(Isolation):事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能相互干扰

    持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响

  2. 事务处理的原则

    保证所有事物都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式;一个事务中执行多个操作时,要么所有事务都被提交(commit),修改将永久地保存,要么放弃所有修改,整个事务回滚(rollback)到最初状态

    说明:

    ① 数据一旦提交,就不可回滚

    ② 哪些操作会导致数据的自动提交

    • DDL操作一旦执行,都会自动提交

    • DML操作默认情况下,一旦执行就会自动提交

      通过set autocommit = false的方式取消DML操作的自动提交

    • 默认在关闭连接时,会自动提交数据

  3. 代码的体现

    //考虑数据库事务之后的转账操作
        @Test
        public void testUpdateWithTx() {
            Connection conn = null;
            try {
                conn = JDBCUtils.getConnection();
                //取消数据的自动提交
                conn.setAutoCommit(false);
    
                String sql1 = "update user_table set balance = balance - 100 where user = ?";
                update(conn,sql1,"AA");
    
    //            //模拟网络阻塞
    //            System.out.println(10/0);
    
                String sql2 = "update user_table set balance = balance + 100 where user = ?";
                update(conn,sql2,"BB");
    
                System.out.println("转账成功");
                //2. 提交数据
                conn.commit();
            } catch (Exception e) {
                e.printStackTrace();
                //3. 回滚数据
                try {
                    if(conn != null)
                        conn.rollback();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            } finally {
                try {
                    if(conn != null)
                        conn.setAutoCommit(true);
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
                JDBCUtils.closeResource(conn,null);
            }
        }
    
  4. 考虑事务的通用增删改操作

    public int update(Connection conn,String sql,Object... args) {
    
        PreparedStatement ps = null;
        try {
            //1. 预编译sql语句,返回PreparedStatement实例
            ps = conn.prepareStatement(sql);
            //2. 填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1,args[i]);
            }
            //3. 执行
            return ps.executeUpdate();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            //4. 资源的关闭
            JDBCUtils.closeResource(null,ps);
        }
        return 0;
    }
    
  5. 考虑事务的通用查询操作

    //通用的查询操作(考虑事务),返回数据表中的一条记录
    public <T> T getInstance(Connection conn,Class<T> tClass,String sql,Object... args) {
    
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1,args[i]);
            }
    
            rs = ps.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            if(rs.next()){
                T t = tClass.newInstance();
                //处理结果集的一行数据的每一列
                for (int i = 0; i < columnCount; i++) {
                    Object columnValue = rs.getObject(i + 1);
                    String columnLabel = rsmd.getColumnLabel(i + 1);
    
                    Field field = tClass.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }
                return t;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(null,ps,rs);
        }
        return null;
    }
    

DAO及其子类

BaseDAO

package com.th1024.DAO;

import com.th1024.utils.JDBCUtils;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * DAO:data(base) access object
 * 封装了针对数据表的通用的操作
 *
 * @author TuHong
 * @create 2021-03-11 21:29
 */
public abstract class BaseDAO {
    //通用的增删改操作
    public int update(Connection conn, String sql, Object... args) {

        PreparedStatement ps = null;
        try {
            //1. 预编译sql语句,返回PreparedStatement实例
            ps = conn.prepareStatement(sql);
            //2. 填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1,args[i]);
            }
            //3. 执行
            return ps.executeUpdate();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            //4. 资源的关闭
            JDBCUtils.closeResource(null,ps);
        }
        return 0;
    }

    //通用的查询操作,用于返回数据表中的一条记录
    public <T> T getInstance(Connection conn,Class<T> tClass,String sql,Object... args) {

        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1,args[i]);
            }

            rs = ps.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            if(rs.next()){
                T t = tClass.newInstance();
                //处理结果集的一行数据的每一列
                for (int i = 0; i < columnCount; i++) {
                    Object columnValue = rs.getObject(i + 1);
                    String columnLabel = rsmd.getColumnLabel(i + 1);

                    Field field = tClass.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }
                return t;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(null,ps,rs);
        }
        return null;
    }

    //通用的查询操作,用于返回数据表中的多条记录构成的集合
    public <T> List<T> getInstances(Connection conn,Class<T> tClass,String sql,Object... args) {
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = conn.prepareStatement(sql);

            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1,args[i]);
            }

            rs = ps.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            //创建集合对象
            ArrayList<T> list = new ArrayList<>();
            while(rs.next()){
                T t = tClass.newInstance();
                for (int i = 0; i < columnCount; i++) {
                    Object columnValue = rs.getObject(i + 1);
                    String columnLabel = rsmd.getColumnLabel(i + 1);

                    Field field = tClass.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(null,ps,rs);
        }
        return null;
    }

    //用于查询特殊值的通用方法
    public <E> E getValue(Connection conn,String sql,Object... args) {
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1,args[i]);
            }

            rs = ps.executeQuery();
            if(rs.next()){
                return (E) rs.getObject(1);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JDBCUtils.closeResource(null,ps,rs);
        }
        return null;
    }
}

CustomerDAO

package com.th1024.DAO;

import com.th1024.bean.Customer;

import java.sql.Connection;
import java.sql.Date;
import java.util.List;

/**
 * 此接口用于规范针对customers表的常用操作
 *
 * @author TuHong
 * @create 2021-03-11 21:49
 */
public interface CustomerDAO {
    /*
    将customer对象添加到数组中
     */
    void insert(Connection conn, Customer customer);
    /*
    针对指定的id,删除表中的一条记录
     */
    void deleteById(Connection conn,int id);
    /*
    使用内存中的customer对象,修改数据表中指定的记录
     */
    void update(Connection conn,Customer customer);
    /*
    根据指定的id查询得到对应的Customer对象
     */
    Customer getCustomerById(Connection conn,int id);
    /*
    查询表中所有记录构成的集合
     */
    List<Customer> getAll(Connection conn);
    /*
    返回数据表中的数据的条目数
     */
    Long getCount(Connection conn);
    /*
    返回数据表中的最大的生日
     */
    Date getMaxBirth(Connection conn);

}

CustomerDAOImpl

package com.th1024.DAO;

import com.th1024.bean.Customer;

import java.sql.Connection;
import java.sql.Date;
import java.util.List;

/**
 * @author TuHong
 * @create 2021-03-11 21:58
 */
public class CustomerDAOImpl extends BaseDAO implements CustomerDAO{
    @Override
    public void insert(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 update(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 = ?";
        Customer customer = getInstance(conn, Customer.class, sql, id);
        return customer;
    }

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

    @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);
    }
}

数据库连接池(Druid数据库连接池)

导入jar包:druid-1.1.10.jar

具体实现:

//使用Druid数据库连接池获取连接
private static DataSource source;
static{
    try {
        Properties pros = new Properties();

        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");

        pros.load(is);

        source = DruidDataSourceFactory.createDataSource(pros);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
public static Connection getConnection1() throws Exception {

    Connection conn = source.getConnection();
    return conn;
}

配置文件:druid.properties(定义在src下)

url=jdbc:mysql://localhost:3306/test
username=root
password=****
driverClassName=com.mysql.jdbc.Driver

initialSize=10
maxActive=10

DBUtils提供的jar包实现CRUD操作

导入jar包:commons-dbutils-1.3.jar

使用QueryRunner测试增、删、改操作:

//测试插入
@Test
public void testInsert() {
    Connection conn = null;
    try {
        QueryRunner runner = new QueryRunner();
        conn = JDBCUtils.getConnection1();
        String sql = "insert into customers(name,email,birth) values(?,?,?)";
        int insertCount = runner.update(conn, sql, "枫花恋", "fhl@qq.com", "1999-01-01");
        System.out.println("添加了" + insertCount + "行数据");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(conn,null);
    }
}
//测试修改
    @Test
    public void testUpdate() {
        Connection conn = null;
        try {
            QueryRunner runner = new QueryRunner();
            conn = JDBCUtils.getConnection1();
            String sql = "update customers set birth = ? where id = ?";
            int count = runner.update(conn, sql, "1999-05-06", 24);
            System.out.println("共" + count + "行受到影响");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(conn,null);
        }
    }

使用QueryRunner测试查询操作:

//测试查询
/*
BeanHandler:是ResultSetHandler接口的实现类,用于封装表中的一条记录
 */
@Test
public void testQuery1() {
    Connection conn = null;
    try {
        QueryRunner runner = new QueryRunner();
        conn = JDBCUtils.getConnection1();
        String sql = "select id,name,email,birth from customers where id = ?";
        BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);
        Customer customer = runner.query(conn, sql, handler, 23);
        System.out.println(customer);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(conn,null);
    }
}

/*
BeanListHandler:是ResultSetHandler接口的实现类,用于封装表中的多条记录
 */
@Test
public void testQuery2() {
    Connection conn = null;
    try {
        QueryRunner runner = new QueryRunner();
        conn = JDBCUtils.getConnection1();
        String sql = "select id,name,email,birth from customers where id < ?";
        BeanListHandler<Customer> handler = new BeanListHandler<>(Customer.class);
        List<Customer> customerList = runner.query(conn, sql, handler, 23);
        customerList.forEach(System.out::println);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(conn,null);
    }
}

/*
MapHandler:是ResultSetHandler接口的实现类,对应表中的一条记录
将字段及相应字段的值作为map中的key和value
 */
@Test
public void testQuery3() {
    Connection conn = null;
    try {
        QueryRunner runner = new QueryRunner();
        conn = JDBCUtils.getConnection1();
        String sql = "select id,name,email,birth from customers where id = ?";
        MapHandler handler = new MapHandler();
        Map<String, Object> map = runner.query(conn, sql, handler, 23);
        System.out.println(map);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(conn,null);
    }
}

/*
MapListHandler:是ResultSetHandler接口的实现类,对应表中的多条记录
将字段及相应字段的值作为map中的key和value,并将这些map添加到list中
 */
@Test
public void testQuery4() {
    Connection conn = null;
    try {
        QueryRunner runner = new QueryRunner();
        conn = JDBCUtils.getConnection1();
        String sql = "select id,name,email,birth from customers where id < ?";
        MapListHandler handler = new MapListHandler();
        List<Map<String, Object>> mapList = runner.query(conn, sql, handler, 23);
        mapList.forEach(System.out::println);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(conn,null);
    }
}

/*
ScalarHandler:用于查询特殊值
 */
@Test
public void testQuery5() {

    Connection conn = null;
    try {
        QueryRunner runner = new QueryRunner();
        conn = JDBCUtils.getConnection1();
        String sql = "select count(*) from customers";
        ScalarHandler handler = new ScalarHandler();
        Long count = (Long) runner.query(conn, sql, handler);
        System.out.println(count);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(conn,null);
    }
}
@Test
public void testQuery6() {

    Connection conn = null;
    try {
        QueryRunner runner = new QueryRunner();
        conn = JDBCUtils.getConnection1();
        String sql = "select max(birth) from customers";
        ScalarHandler handler = new ScalarHandler();
        Date maxBirth = (Date) runner.query(conn, sql, handler);
        System.out.println(maxBirth);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(conn,null);
    }
}

/*
自定义ResultSetHandler实现类
 */
@Test
public void testQuery7() {
    Connection conn = null;
    try {
        QueryRunner runner = new QueryRunner();
        conn = JDBCUtils.getConnection1();
        String sql = "select id,name,email,birth from customers where id = ?";
        ResultSetHandler<Customer> handler = resultSet -> {
            if (resultSet.next()){
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                String email = resultSet.getString("email");
                Date birth = resultSet.getDate("birth");

                Customer customer = new Customer(id,name,email,birth);
                return customer;
            }
            return null;
        };
        Customer customer = runner.query(conn, sql, handler, 24);
        System.out.println(customer);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(conn,null);
    }
}

关闭资源:

/*
    关闭资源的操作
    使用dbutils.jar中提供的DbUtils工具类,实现资源的关闭
     */
    public static void closeResource1(Connection connection, Statement statement, ResultSet resultSet){
//        try {
//            DbUtils.close(connection);
//        } catch (SQLException throwables) {
//            throwables.printStackTrace();
//        }
//        try {
//            DbUtils.close(statement);
//        } catch (SQLException throwables) {
//            throwables.printStackTrace();
//        }
//        try {
//            DbUtils.close(resultSet);
//        } catch (SQLException throwables) {
//            throwables.printStackTrace();
//        }

        DbUtils.closeQuietly(connection,statement,resultSet);
    }
posted on 2021-03-13 16:47  来点番茄酱  阅读(69)  评论(0)    收藏  举报