JDBC(2024)

以前的JDBC笔记:点此跳转

基础实现

查询

// 1.注册驱动(可以省略)
//    Class.forName("com.mysql.cj.jdbc.Driver");
//    DriverManager.registerDriver(new Driver());

// 2.获取连接对象
String url = "jdbc:mysql:///atguigu";
String username = "root";
String password = "atguigu";
Connection connection = DriverManager.getConnection(url, username, password);

// 3.预编译SQL语句获得PreparedStatement对象
PreparedStatement preparedStatement = connection.prepareStatement("SELECT emp_id,emp_name,emp_salary,emp_age FROM t_emp WHERE emp_id = ?");

//4.为占位符赋值,然后执行,并接收结果
preparedStatement.setInt(1,5);
ResultSet resultSet = preparedStatement.executeQuery();

// 5.处理结果
while(resultSet.next()){
    int empId = resultSet.getInt("emp_id");
    String empName = resultSet.getString("emp_name");
    double empSalary = resultSet.getDouble("emp_salary");
    int empAge = resultSet.getInt("emp_age");

    System.out.println(empId+"\t"+empName+"\t"+empSalary+"\t"+empAge);
}

// 6.资源释放
resultSet.close();
preparedStatement.close();
connection.close();

增删改

主键回显:返回新增行的主键,无需再次查询获得。

// 获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql:///atguigu", "root", "atguigu");

// 预编译SQL语句,告知preparedStatement,返回新增数据的主键列的值
String sql = "INSERT INTO t_emp(emp_name,emp_salary,emp_age) VALUES (?,?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

// 创建对象,将对象的属性值,填充在?占位符上 (ORM)
Employee employee = new Employee(null, "jack", 123.45, 29);
preparedStatement.setString(1, employee.getEmpName());
preparedStatement.setDouble(2, employee.getEmpSalary());
preparedStatement.setInt(3, employee.getEmpAge());

// 执行SQL,并获取返回的结果
int result = preparedStatement.executeUpdate();
ResultSet resultSet = null;

// 处理结果
if(result > 0){
    System.out.println("成功!");

    //获取当前新增数据的主键列,回显到Java中employee对象的empId属性上。
    //返回的主键值,是一个单行单列的结果存储在ResultSet里
    resultSet = preparedStatement.getGeneratedKeys();
    if(resultSet.next()){
        int empId = resultSet.getInt(1);
        employee.setEmpId(empId);
    }

    System.out.println(employee);
}else{
    System.out.println("失败!");
}

// 释放资源
if(resultSet!=null){
    resultSet.close();
}
preparedStatement.close();
connection.close();

连接池

Druid

需要导入druid-x.x.x.jar包。

# properties文件
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/atguigu
username=root
password=atguigu
initialSize=10
maxActive=20
//1.创建Properties集合,用于存储外部配置文件的key和value值。
Properties properties = new Properties();

//2.读取外部配置文件,获取输入流,加载到Properties集合里。
InputStream inputStream = DruidTest.class.getClassLoader().getResourceAsStream("db.properties");
properties.load(inputStream);

//3.基于Properties集合构建DruidDataSource连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);

//4.通过连接池获取连接对象
Connection connection = dataSource.getConnection();
System.out.println(connection);

//5.开发CRUD

//6.回收连接
connection.close();

Hikari

需要导入HikariCP-x.x.x.jarslf4j-api-x.x.x.jar包。

# properties文件
driverClassName=com.mysql.cj.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/atguigu
username=root
password=atguigu
minimumIdle=10
maximumPoolSize=20
//1.创建Properties集合,用于存储外部配置文件的key和value值。
Properties properties = new Properties();

//2.读取外部配置文件,获取输入流,加载到Properties集合里。
InputStream inputStream = HikariTest.class.getClassLoader().getResourceAsStream("hikari.properties");
properties.load(inputStream);

//3.创建HikariConfig连接池配置对象,将Properties集合传进去。
HikariConfig hikariConfig = new HikariConfig(properties);

//4.基于HikariConfig连接池配置对象,构建HikariDataSource
HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);

//5.获取连接
Connection connection = hikariDataSource.getConnection();
System.out.println(connection);

//6.回收连接
connection.close();

封装工具类

/**
 *  JDBC工具类(V2.0):
 *      1、维护一个连接池对象、维护了一个线程绑定变量的ThreadLocal对象
 *      2、对外提供在ThreadLocal中获取连接的方法
 *      3、对外提供回收连接的方法,回收过程中,将要回收的连接从ThreadLocal中移除!
 *  注意:工具类仅对外提供共性的功能代码,所以方法均为静态方法!
 *  注意:使用ThreadLocal就是为了一个线程在多次数据库操作过程中,使用的是同一个连接!
 */

public class JDBCUtil {
    // 创建连接池引用,因为要提供给当前项目的全局使用,所以创建为静态的。
    private static DataSource dataSource;
    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();

    // 在项目启动时,即创建连接池对象,赋值给dataSource
    static {
        try {
            Properties properties = new Properties();
            InputStream inputStream = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");
            properties.load(inputStream);

            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    // 对外提供在连接池中获取连接的方法
    public static Connection getConnection(){
        try {
            // 在ThreadLocal中获取Connection、
            Connection connection = threadLocal.get();
            // threadLocal里没有存储Connection,也就是第一次获取
            if (connection == null) {
                // 在连接池中获取一个连接,存储在threadLocal里。
                connection = dataSource.getConnection();
                threadLocal.set(connection);
            }
            return connection;

        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    
    // 对外提供回收连接的方法
    public static void release(){
        try {
            Connection connection = threadLocal.get();
            if(connection != null){
                // 从threadLocal中移除当前已经存储的Connection对象
                threadLocal.remove();
                // 如果开启了事务的手动提交,操作完毕后,归还给连接池之前,要将事务的自动提交改为true
                connection.setAutoCommit(true);
                // 将Connection对象归还给连接池
                connection.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

}

DAO

DAO:Data Access Object,数据访问对象。一张表的操作对应一个DAO对象。

BaseDAO

/**
 * 将共性的数据库的操作代码封装在BaseDAO里。
 */
public class BaseDAO {
    /**
     * 通用的增删改的方法。
     *
     * @param sql    调用者要执行的SQL语句
     * @param params SQL语句中的占位符要赋值的参数
     * @return 受影响的行数
     */
    public int executeUpdate(String sql, Object... params) throws Exception {
        // 1.通过JDBCUtil获取数据库连接
        Connection connection = JDBCUtil.getConnection();

        // 2.预编译SQL语句
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        // 4.为占位符赋值,执行SQL,接受返回结果
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                // 占位符是从1开始的。参数的数组是从0开始的
                preparedStatement.setObject(i + 1, params[i]);
            }
        }
        int row = preparedStatement.executeUpdate();

        // 5.释放资源
        preparedStatement.close();
        // 如果开启了事务(autocommit = false),则不能释放
        if (connection.getAutoCommit()) {
            JDBCUtil.release();
        }

        //6.返回结果
        return row;
    }

    /**
     * 通用的查询:多行多列、单行多列、单行单列
     * 多行多列:List<Employee>
     * 单行多列:Employee
     * 单行单列:封装的是一个结果。Double、Integer、。。。。。
     * 封装过程:
     * 1、返回的类型:泛型:类型不确定,调用者知道,调用时,将此次查询的结果类型告知BaseDAO就可以了。
     * 2、 :通用,List  可以存储多个结果,也可以存储一个结果 get(0)
     * 3、结果的封装:反射,要求调用者告知BaseDAO要封装对象的类对象。 Class
     */
    public <T> List<T> executeQuery(Class<T> clazz, String sql, Object... params) throws Exception {
        // 获取连接
        Connection connection = JDBCUtil.getConnection();

        // 预编译SQL语句
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        // 设置占位符的值
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                preparedStatement.setObject(i + 1, params[i]);
            }
        }
        // 执行SQL,并接受返回的结果集
        ResultSet resultSet = preparedStatement.executeQuery();

        // 获取结果集中的元数据对象
        // 包含了:列的数量、每个列的名称
        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();

        List<T> list = new ArrayList<>();
        //处理结果
        while (resultSet.next()) {
            //循环一次,代表有一行数据,通过反射创建一个对象
            T t = clazz.newInstance();
            //循环遍历当前行的列,循环几次,看有多少列
            for (int i = 1; i <= columnCount; i++) {
                //通过下表获取列的值
                Object value = resultSet.getObject(i);

                // 获取到的列的value值,这个值就是t这个对象中的某一个属性
                // 获取当前拿到的列的名字 = 对象的属性名
                String fieldName = metaData.getColumnLabel(i);
                // 通过类对象和fieldName获取要封装的对象的属性
                Field field = clazz.getDeclaredField(fieldName);
                // 突破封装的private
                field.setAccessible(true);
                field.set(t, value);
            }
            list.add(t);
        }
        resultSet.close();
        preparedStatement.close();
        if (connection.getAutoCommit()) {
            JDBCUtil.release();
        }

        return list;
    }

    /**
     * 通用查询:在上面查询的集合结果中获取第一个结果。 简化了获取单行单列的获取、单行多列的获取
     */
    public <T> T executeQueryBean(Class<T> clazz, String sql, Object... params) throws Exception {
        List<T> list = this.executeQuery(clazz, sql, params);
        if (list == null || list.size() == 0) {
            return null;
        }
        return list.get(0);
    }
}

具体表格的DAO

/**
 * EmployeeDao这个类对应的是t_emp这张表的增删改查的操作
 */
public interface EmployeeDao {

    List<Employee> selectAll();

    Employee selectByEmpId(Integer empId);

    int insert(Employee employee);

    int update(Employee employee);

    int delete(Integer empId);
}
public class EmployeeDaoImpl extends BaseDAO implements EmployeeDao {
    @Override
    public List<Employee> selectAll() {
        try {
            String sql = "SELECT emp_id empId,emp_name empName,emp_salary empSalary,emp_age empAge FROM t_emp";
            return executeQuery(Employee.class,sql,null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Employee selectByEmpId(Integer empId) {
        try {
            String sql = "SELECT emp_id empId,emp_name empName,emp_salary empSalary,emp_age empAge FROM t_emp where emp_id = ?";
            return executeQueryBean(Employee.class,sql,empId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int insert(Employee employee) {
        try {
            String sql = "INSERT INTO t_emp(emp_name,emp_salary,emp_age) VALUES (?,?,?)";
            return executeUpdate(sql,employee.getEmpName(),employee.getEmpSalary(),employee.getEmpAge());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int update(Employee employee) {
        try {
            String sql = "UPDATE t_emp SET emp_salary = ? WHERE emp_id = ?";
            return executeUpdate(sql,employee.getEmpSalary(),employee.getEmpId());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int delete(Integer empId) {
        try {
            String sql = "delete from t_emp where emp_id = ?";
            return executeUpdate(sql,empId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

事务

BankDao bankDao = new BankDaoImpl();
Connection connection=null;
try {
    // 1.获取连接,将连接的事务提交改为手动提交
    connection = JDBCUtilV2.getConnection();
    connection.setAutoCommit(false);//开启事务,当前连接的自动提交关闭。改为手动提交!

    // 2.操作减钱
    bankDao.subMoney(1,100);

    // 3.操作加钱
    bankDao.addMoney(2,100);

    // 4.前置的多次dao操作,没有异常,提交事务!
    connection.commit();
} catch (Exception e) {
    try {
        connection.rollback();
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}finally {
    JDBCUtilV2.release();
}

JDBC(2024)
https://shuusui.site/blog/2024/09/20/jdbc-2/
作者
Shuusui
发布于
2024年9月20日
更新于
2024年9月20日
许可协议