JDBC基础

文章目录

  • 创建依赖
  • 基本步骤
    • 案例
      • 1. statement案例(不常用):
      • 2.prepared 案例(常用)
      • 3.prepared curd 案例(获取列信息)
      • 5.事务一致性(转账)
  • 连接池Druid
  • jdbc工具类封装

创建依赖

各个版本有各个的依赖包,想MySQL8.0 和MySQL5.0 应该下载对应的依赖包
JDBC基础_第1张图片
在idea项目当中创建文件夹libs来放相关的依赖,将依赖放进去后,右击,增加到此项目当中(如图一),加载完成之后会有三角形的这个依赖包可以打开,如图二所示:
图一:
JDBC基础_第2张图片

图二:
JDBC基础_第3张图片

基本步骤

 * TODO: 步骤总结 (6)
 *    1. 注册驱动
 *    2. 获取连接
 *    3. 创建statement或者Prepared
 *    4. 发送SQL语句,并获取结果
 *    5. 结果集解析
 *    6. 关闭资源

1.注册驱动

// 方法1:
DriverManager.registerDriver(new Driver());// 这个驱动会创建两次
//方法2:利用反射机制-推荐
Class.forName("com.mysql.cj.jdbc.Driver");

2.获取链接

获取链接的有三个参数的,两个参数的,一个参数的。接下来一个一个介绍

// 三个参数
public static Connection getConnection(String url,
        String user, String password)
//两个参数      
public static Connection getConnection(String url,
        java.util.Properties info)
// 一个参数
public static Connection getConnection(String url)

三个参数的:

/**
这里有三个参数,具体在这里详细描述【】——》带这个的是你需要里面需要填的东西。
第一个参数:(String URL: 连接数据库地址) 连接数据库地址模仿下列的写法 jbdc:【数据库名】://【域名或ip地址】:【端口号】/【数据库名】
其实还可以有其他的参数:
第二个参数:(String user: 连接数据库用户名)
第三个参数:连接数据库用户对应的密码
*/
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1","root","root");

数据库URL语法:

(1)链接Oracle数据库 ip 127.0.0.1 端口号:3306 数据库名:db
jdbc:oracle :// 127.0.0.1  : 3306 / db



(2)链接Mysql数据库 ip 本机 端口号:3306 数据库名:day01
jdbc:mysql://localhost:3306/day01



(3) 当前电脑的省略写法! 注意:本机和端口3306

jdbc:mysql://localhost:3306/day01相当于jdbc:mysql:///day01

两个参数的:

String URL : 写法还是jdbc的三个参数的写法!
Properties : 就是一个参数封装容器!至少要包含 user / password key!存储连接账号信息!(就是properties配置文件需要自己写)

一个参数的:

String URL: URl可以携带目标地址,可以通过?分割,在后面key=value&key=value形式传递参数
jdbc:mysql:///day01?user=root&password=123456

  1. 创建statement/prepared

他们的区别就在于statement,只能给字符串,可以形成SQL注入,而prepared可以给一个方法传入参数,则可以防止注入。

1.statement:

Statement statement = connection.createStatement();

2.prepared

//创建preparedStatement
        //TODO 需要传入SQL语句结构
        //TODO 要的是SQL语句结构,动态值的部分使用 ? ,  占位符!
        //TODO ?  不能加 '?'  ? 只能替代值,不能替代关键字和容器名
String sql = "select * from t_user where account = ? and password = ? ;";
PreparedStatement preparedStatement = connection.prepareStatement(sql);

 //占位符赋值
 //给占位符赋值! 从左到右,从1开始!
 /**
*  int 占位符的下角标
*  object 占位符的值
*/
preparedStatement.setObject(2,password);
 preparedStatement.setObject(1,account);
  1. 发送SQL语句,并获取结果

1.statement 获取

/**
这里举例:写了一个sql 语句 
*/
String sql = "select id,account,password,nickname from t_user ;";
        ResultSet resultSet =  statement.executeQuery(sql);
  1. prepared的获取(方法一样)
ResultSet resultSet = preparedStatement.executeQuery();

resultSet的返回值:

ResultSet 结果集对象 = executeQuery(DQL语句)
int 响应行数 = executeUpdate(非DQL语句)

5.结果集解析

/**
         *
         * TODO:1.需要理解ResultSet的数据结构和mysql可视化工具查询出来的是一样,需要在脑子里构建结果表!
         * TODO:2.有一个光标指向的操作数据行,默认指向第一行的上边!我们需要移动光标,指向行,在获取列即可!
         *        boolean = next()
         *              false: 没有数据,也不移动了!
         *              true:  有更多行,并且移动到下一行!
         *       推荐:推荐使用if 或者 while循环,嵌套next方法,循环和判断体内获取数据!
         *       if(next()){获取列的数据!} ||  while(next()){获取列的数据!}
         *
         *TODO:3.获取当前行列的数据!
         *         get类型(int columnIndex | String columnLabel)
         *        列名获取  //lable 如果没有别名,等于列名, 有别名label就是别名,他就是查询结果的标识!
         *        列的角标  //从左到右 从1开始! 数据库全是从1开始!
         */
while (resultSet.next()){
            int id = resultSet.getInt("id");
            String account = resultSet.getString("account");
            String password = resultSet.getString("password");
            String nickname = resultSet.getString("nickname");
            System.out.println(id+"::"+account+"::"+password+"::"+nickname);
        }

6.关闭资源

resultSet.close();
statement.close();
connection.close();

案例

1. statement案例(不常用):

public class test {

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        
        
        //1.注册一次驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

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

        //3.创建statement
        Statement statement = connection.createStatement();

        //4.执行SQL语句 [动态SQL语句,需要字符串拼接]
//        String sql = "select * from t_user where account = '"+account+"' and password = '"+password+"' ;";
        String sql = "SELECT * FROM my_work ;";

        ResultSet resultSet = statement.executeQuery(sql);
        // 5.使用数据

        while (resultSet.next()){
            System.out.println(resultSet.getString(1)); // 打印第一竖的数据
        }
        //关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }
    
}

2.prepared 案例(常用)

public class testPrepared {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {


        //1.注册一次驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

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

        //3.创建prepared
        String sql = "SELECT * FROM my_work WHERE password = ?;"; //sql 语句
        PreparedStatement preparedStatement = connection.prepareStatement(sql); // 准备prepared解析SQL语句
        preparedStatement.setObject(1,"ccc"); // 第1个参数传入ccc
        //4.执行SQL语句 [动态SQL语句,需要字符串拼接]


        ResultSet resultSet = preparedStatement.executeQuery();
        // 5.使用数据

        while (resultSet.next()){
            System.out.println(resultSet.getString(1)); // 打印第一竖的数据
            System.out.println("name:" + resultSet.getString("name")); // 通过 名称查找 获取当前行的数据
            System.out.println("password:" + resultSet.getString("password")); 

        }
        //关闭资源
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }

}

3.prepared curd 案例(获取列信息)

public class JdbcPreparedStatementCrudPart {


    /**
     * 插入一条用户数据!
     * 账号: test
     * 密码: test
     * 昵称: 测试
     */
    @Test
    public void testInsert() throws Exception{

        //注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

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

        //TODO: 切记, ? 只能代替 值!!!!!  不能代替关键字 特殊符号 容器名
        String sql = "insert into t_user(account,password,nickname) values (?,?,?);";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //占位符赋值
        preparedStatement.setString(1, "test");
        preparedStatement.setString(2, "test");
        preparedStatement.setString(3, "测试");

        //发送SQL语句
        int rows = preparedStatement.executeUpdate();

        //输出结果
        System.out.println(rows);

        //关闭资源close
        preparedStatement.close();
        connection.close();
    }



    /**
     * 修改一条用户数据!
     * 修改账号: test的用户,将nickname改为tomcat
     */
    @Test
    public void testUpdate() throws Exception{

        //注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

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

        //TODO: 切记, ? 只能代替 值!!!!!  不能代替关键字 特殊符号 容器名
        String sql = "update t_user set nickname = ? where account = ? ;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //占位符赋值
        preparedStatement.setString(1, "tomcat");
        preparedStatement.setString(2, "test");

        //发送SQL语句
        int rows = preparedStatement.executeUpdate();

        //输出结果
        System.out.println(rows);

        //关闭资源close
        preparedStatement.close();
        connection.close();
    }


    /**
     * 删除一条用户数据!
     * 根据账号: test
     */
    @Test
    public void testDelete() throws Exception{

        //注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

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

        //TODO: 切记, ? 只能代替 值!!!!!  不能代替关键字 特殊符号 容器名
        String sql = "delete from t_user where account = ? ;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //占位符赋值
        preparedStatement.setString(1, "test");

        //发送SQL语句
        int rows = preparedStatement.executeUpdate();

        //输出结果
        System.out.println(rows);

        //关闭资源close
        preparedStatement.close();
        connection.close();
    }



    /**
     * 查询全部数据!
     *   将数据存到List中
     *   map -> 对应一行数据
     *      map key -> 数据库列名或者别名
     *      map value -> 数据库列的值
     * TODO: 思路分析
     *    1.先创建一个List集合
     *    2.遍历resultSet对象的行数据
     *    3.将每一行数据存储到一个map对象中!
     *    4.将对象存到List中
     *    5.最终返回
     *
     * TODO:
     *    初体验,结果存储!
     *    学习获取结果表头信息(列名和数量等信息)
     */
    @Test
    public void testQueryMap() throws Exception{

        //注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

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

        //TODO: 切记, ? 只能代替 值!!!!!  不能代替关键字 特殊符号 容器名
        String sql = "select id,account,password,nickname from t_user ;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //占位符赋值 本次没有占位符,省略

        //发送查询语句
        ResultSet resultSet = preparedStatement.executeQuery();

        //创建一个集合
        List<Map> mapList = new ArrayList<>();

        //获取列信息对象
        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();
        while (resultSet.next()) {
            Map map = new HashMap();
            for (int i = 1; i <= columnCount; i++) {
                map.put(metaData.getColumnLabel(i), resultSet.getObject(i));
            }
            mapList.add(map);
        }

        System.out.println(mapList);

        //关闭资源close
        preparedStatement.close();
        connection.close();
        resultSet.close();
    }

}

5.事务一致性(转账)

利用try ...catch..finally
开启事务(关闭事务自动提交)
connection.setAutoCommit(false);
提交事务
connection.commit();
回滚事务
connection.rollback();
等操作,实现事务的一致性

BankDao 类(是用传入参数 用来加钱减钱的操作)

public class BankDao {

    /**
     * 加钱方法
     * @param account
     * @param money
     * @param connection 业务传递的connection和减钱是同一个! 才可以在一个事务中!
     * @return 影响行数
     */
    public int addMoney(String account, int money,Connection connection) throws ClassNotFoundException, SQLException {


        String sql = "update t_bank set money = money + ? where account = ? ;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //占位符赋值
        preparedStatement.setObject(1, money);
        preparedStatement.setString(2, account);

        //发送SQL语句
        int rows = preparedStatement.executeUpdate();

        //输出结果
        System.out.println("加钱执行完毕!");

        //关闭资源close
        preparedStatement.close();

        return rows;
    }

    /**
     * 减钱方法
     * @param account
     * @param money
     * @param connection 业务传递的connection和加钱是同一个! 才可以在一个事务中!
     * @return 影响行数
     */
    public int subMoney(String account, int money,Connection connection) throws ClassNotFoundException, SQLException {

        String sql = "update t_bank set money = money - ? where account = ? ;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //占位符赋值
        preparedStatement.setObject(1, money);
        preparedStatement.setString(2, account);

        //发送SQL语句
        int rows = preparedStatement.executeUpdate();

        //输出结果
        System.out.println("减钱执行完毕!");

        //关闭资源close
        preparedStatement.close();

        return rows;
    }
}

BankService类(这里注册了驱动,建立了数据库的链接,然后用了try catch 保持事务的一致性,在关闭了资源)

public class BankService {


    /**
     * 转账业务方法
     * @param addAccount  加钱账号
     * @param subAccount  减钱账号
     * @param money  金额
     */
    public void transfer(String addAccount,String subAccount, int money) throws ClassNotFoundException, SQLException {

        System.out.println("addAccount = " + addAccount + ", subAccount = " + subAccount + ", money = " + money);

        //注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

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

        int flag = 0;

        //利用try代码块,调用dao
        try {
            //开启事务(关闭事务自动提交)
            connection.setAutoCommit(false);

            BankDao bankDao = new BankDao();
            //调用加钱 和 减钱
            bankDao.addMoney(addAccount,money,connection);
            System.out.println("--------------");
            bankDao.subMoney(subAccount,money,connection);
            flag = 1;
            //不报错,提交事务
            connection.commit();
        }catch (Exception e){

            //报错回滚事务
            connection.rollback();
            throw e;
        }finally {
            connection.close();
        }

        if (flag == 1){
            System.out.println("转账成功!");
        }else{
            System.out.println("转账失败!");
        }
    }

}

连接池Druid

作用:节约了创建和销毁连接性能消耗,提升了时间效率!

总结确定:
(1)不使用数据库连接池,每次都通过DriverManager获取新连接,用完直接抛弃断开,连接的利用率太低。
(2)对于数据库服务器来说,压力太大了。我们数据库服务器和Java程序对连接数也无法控制,很容易导致数据库服务崩溃。
所以我们就希望能够管理连接:

  • 我们可以建立一个连接池,这个池中可以容纳一定数量的连接对象,一开始我们就可以先替用户先创建好一些连接对象,等用户要拿连接对象时,就直接从池中拿,不用新建了,这样也可节约时间。然后用户用完之后,那么连接池可以向服务器申请新的连接放到池中。
    直到池中的连接达到“最大连接数量”,就不能在申请新的连接了,如果没有拿到连接的用户只能等待。
  • 连接池有很多,这里我讲的是国货之光Druid。
javax.sql.DataSource 接口 :(1)规范了连接池获取的方法
						   (2)规范了连接池回收连接的方法

硬链接模式:

import com.alibaba.druid.pool.DruidDataSource;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * 创建druid连接池对象,使用硬编码进行核心参数设置
 * 必须要参数: 账号,密码,url,driverClass
 * 非必要参数:初始化个数,最大数量等等 不推荐设置
 */

// TODO: 这里写一个硬链接模式
public class DruidTest {

    public static void main(String[] args) throws SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();

        //设置四个必要的参数
        druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/my_project?useUnicode=true&characterEncoding=utf-8");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("Thm123456!");
        druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        druidDataSource.setInitialSize(5); // 初始化数量
        druidDataSource.setMaxActive(20); // 最大数量
        //获取链接
        Connection connection = druidDataSource.getConnection();
        // curd
        System.out.println(connection);

        //回收链接
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }


    }
}

软连接模式:
先在项目的src下面创建一个druid.properties配置文件
JDBC基础_第4张图片
文件里写入

# druid 连接池需要配置参数,key固定名字

driverClassName=com.mysql.cj.jdbc.Driver
username=root
password=Thm123456!
url=jdbc:mysql://127.0.0.1:3306/my_project
import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;

/**
 *这里写一个软连接的模式:
 * 不直接在Java代码编写配置文件!
 * 利用工厂模式,传入配置文件对象,创建连接池!
 */
public class DruidTest2 {
    public static void main(String[] args) throws Exception {
        //1.读取外部配置文件Properties
        Properties properties = new Properties();

        // 2.使用了连接池的工具类工厂模式,创建连接池
        // 这里使用了类加载器 druid.properties 这个文件在src 下面
        InputStream ips = DruidTest2.class.getClassLoader().getResourceAsStream("druid.properties");
        properties.load(ips);
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        Connection connection = dataSource.getConnection();

        // 3.数据crud


        // 4.回收连接
        connection.close();
    }
}

jdbc工具类封装

  • 但是重要的是,不是每次我们要用的时候就要去写一些代码来创建连接池吧,所以我们需要封装它到一个工具类里面。

接下来讲一个工程来说明: example 包是之前的转钱的项目 然后用了 工具类之后然后用UtilsV2的封装的连接池工具
UtilsV1 是封装类的工具(可以用来连接和销毁,这个有一个缺陷,每次都要传connection,不推荐使用
UtilsV2是第二个封装的工具类(也是用来连接池,用了使用了线程本地变量存储连接信息,确保一个线程的多个方法可以获取同一个connection,就不用传connection)
BaseDao是一个封装的dao 就是操作数据库里面的数据 要使用需要继承BaseDao,里面封装了非DQL 和DQL 的两个方法(里面的连接还是用了UtilsV2)

JDBC基础_第5张图片

bankDao.java

package example;
import jbdcUtils.UtilsV2;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;


public class BankDao {

    /**
     * 加钱方法
     * @param account
     * @param money
     * @return 影响行数
     */
    public int addMoney(String account, int money) throws ClassNotFoundException, SQLException {

        Connection connection = UtilsV2.getConnection();
        String sql = "update t_bank set money = money + ? where account = ? ;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //占位符赋值
        preparedStatement.setObject(1, money);
        preparedStatement.setString(2, account);

        //发送SQL语句
        int rows = preparedStatement.executeUpdate();

        //输出结果
        System.out.println("加钱执行完毕!");

        //关闭资源close
        preparedStatement.close();

        return rows;
    }

    /**
     * 减钱方法
     * @param account
     * @param money
     * @return 影响行数
     */
    public int subMoney(String account, int money) throws ClassNotFoundException, SQLException {
        Connection connection = UtilsV2.getConnection();
        String sql = "update t_bank set money = money - ? where account = ? ;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //占位符赋值
        preparedStatement.setObject(1, money);
        preparedStatement.setString(2, account);

        //发送SQL语句
        int rows = preparedStatement.executeUpdate();

        //输出结果
        System.out.println("减钱执行完毕!");

        //关闭资源close
        preparedStatement.close();

        return rows;
    }
}

Bankservice.java

package example;

import jbdcUtils.UtilsV2;
import java.sql.Connection;
import java.sql.SQLException;


public class BankService {


    /**
     * 转账业务方法
     * @param addAccount  加钱账号
     * @param subAccount  减钱账号
     * @param money  金额
     */
    public void transfer(String addAccount,String subAccount, int money) throws ClassNotFoundException, SQLException {

        System.out.println("addAccount = " + addAccount + ", subAccount = " + subAccount + ", money = " + money);

        Connection connection = UtilsV2.getConnection();


        int flag = 0;

        //利用try代码块,调用dao
        try {
            //开启事务(关闭事务自动提交)
            connection.setAutoCommit(false);

            BankDao bankDao = new BankDao();
            //调用加钱 和 减钱
            bankDao.addMoney(addAccount,money);
            System.out.println("--------------");
            bankDao.subMoney(subAccount,money);
            flag = 1;
            //不报错,提交事务
            connection.commit();
        }catch (Exception e){

            //报错回滚事务
            connection.rollback();
            throw e;
        }finally {
            UtilsV2.releaseConnection();
        }

        if (flag == 1){
            System.out.println("转账成功!");
        }else{
            System.out.println("转账失败!");
        }
    }

}

UtilsV1.java

package jbdcUtils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * v1.0 版本工具类
 * 内部包含一个连接池对象,并且对外提供获取连接和回收的方法!
 *
 * 小建议:工具类的方法,推荐写成静态,外部调用会更加方便!
 *
 * 实现:
 *      属性 连接池对象 [指实例化一次] 1.单例模式, 2.用静态代码块
 *      static {
 *          全局调用一次
 *      }
 *      方法
 *          对外提供连接方法
 *          回收外部传入连接方法
 */
public class UtilsV1 {
    private static DataSource dataSource = null;
    static {
        // 初试化连接池对象
        Properties properties = new Properties();
        InputStream ips = UtilsV1.class.getClassLoader().getResourceAsStream("druid.properties");
        try {
            properties.load(ips);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try {
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 对外提供连接方法
     */
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    /**
     * 回收外部传入连接方法
     */
    public static void releaseConnection(Connection connnection) throws SQLException {
        connnection.close();// 连接池的连接,调用close就回收
    }


}

UtlisV2.java

package jbdcUtils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * 之前v 1.0的工具需要每次都要传入参数比较麻烦,
 * v 2.0 使用同一个线程实现的连接是同一个,就不用那么麻烦的去传参数了
 *
 * 这里使用了线程本地变量存储连接信息,确保一个线程的多个方法可以获取同一个connection
 * 优势:在做事务操作的时候 service 和dao 方法  获取都是同一个连接池
 */
public class UtilsV2 {
    private static DataSource dataSource = null;

    /**
     * 线程本地变量
     */
    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();


    static {
        // 初试化连接池对象
        Properties properties = new Properties();
        InputStream ips = UtilsV2.class.getClassLoader().getResourceAsStream("druid.properties");
        try {
            properties.load(ips);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try {
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 对外提供连接方法
     */
    public static Connection getConnection() throws SQLException {
        // 线程本地变量是否存在?
        Connection connection = tl.get();
        //第一次没有
        if (connection == null ) {
            connection = dataSource.getConnection();
            tl.set(connection);
        }
        return connection;
    }
    /**
     * 回收外部传入连接方法
     */
    public static void releaseConnection() throws SQLException {
        Connection connection = tl.get();
        if (connection != null){
            tl.remove(); // 清空线程本地变量
            connection.setAutoCommit(true); // 事务状态回顾false,要回归到默认状态
            connection.close();
        }

    }


}

BaseDao.java

package jbdcUtils;

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

/**
 * ClassName: BaseDap
 * Package: jbdcUtils
 * Description: 封装dao数据库重复代码!
 * todo:
 *      封装两个方法 一个简化非DQL
 *                  一个简化DQL
 *

 * @Author Thmiao

 */
public class BaseDao {

    /**
     * 封装简化DQL语句
     * @param sql 带占位符的sql 语句
     * @param params 占位符的值
     * @return 执行影响的行数
     */
    public static int executeUpdate(String sql, Object... params) throws SQLException {
        Connection connection = UtilsV2.getConnection();
        PreparedStatement ps = connection.prepareStatement(sql);
        for (int i = 0; i < params.length; i++) { //循环赋值
            ps.setObject(i + 1, params[i]);
        }

        int result = ps.executeUpdate();
        ps.close();
        // 是否回收连接,需要考虑是不是事务
        if(connection.getAutoCommit()){
            // 没有开启事务,正常回收
            UtilsV2.releaseConnection();

        }
        return result;
    }

    /**
     * 封装简化非DQL语句
     * @param sql 带占位符的sql 语句
     * @param clazz 要接值得实体集合的模板对象
     * @param params 占位符的值
     * @return list
     *     数据库数据-->Java实体类
     *     数据库表有 t_uer :id,account,password,nickname,
     *     java 层面:
     *     User : id ,account,password,nickname,
     *
     *     将查询结果封装到一个实体集合
     */
    public static <T> List<T> executeQuery(String sql, Class<T> clazz, Object... params) throws Exception {
        // 获取连接
        Connection connection = UtilsV2.getConnection();
        PreparedStatement ps = connection.prepareStatement(sql);
        if(params !=null){
            for (int i = 0; i < params.length; i++) { //循环赋值
                ps.setObject(i + 1, params[i]);
            }
        }


        // 获取列信息的对象
        ResultSet rs = ps.executeQuery();
        ResultSetMetaData metaData = rs.getMetaData();
        int columnCount = metaData.getColumnCount();
        // 创建数组
        List<T> list = new ArrayList<T>();

        while (rs.next()) {
            T t = clazz.newInstance(); // 调用类的无参构造函数实例化对象
            // 自动遍历列,注意要从1开始,并且小于等于总列数
            for (int i = 0; i <= columnCount; i++) {
                // 对象的属性值
                Object value = rs.getObject(i);
                // 获取指定列下标的列名称!
                String propertyName = metaData.getColumnLabel(i);
                // 反射给对象的属性值赋值
                Field field = clazz.getDeclaredField(propertyName);
                //  打破private的修饰限制 属性可以设置
                field.setAccessible(true);
                /**
                 * @param t 要复制的对象 如果属性是静态的第一个参数可以为null
                 * @param value 要赋值的值
                 */
                field.set(t, value);


            }
            // 一行数据所有列全部存到map中
            list.add(t);

        }

        //关闭资源
        rs.close();
        ps.close();

        // 判断是否有事务
        if(connection.getAutoCommit()){
            // 没有开启事务,正常回收
            UtilsV2.releaseConnection();
        }
        return list;
    }

}

这里有一个测试CudrTest.java

package jbdcUtils;

import org.junit.Test;
import java.sql.SQLException;
import java.util.List;

/**
 * ClassName: CurdTest
 * Package: jbdcUtils
 * Description:
 *
 * @Author Thmiao
 * @Create 2023/8/25 17:37
 * @Version 1.0
 */
public class CurdTest extends BaseDao {
    /**
     * 这个DQl的测试
     * @throws SQLException
     * return 是修改的行数
     */
    @Test
    public void test() throws SQLException {
        String sql = "DELETE * FROM test WHERE name = '?'";
        executeUpdate(sql,"小狗");
    }

    
    /**
     * 这个非DQl的测试,会在数据库获取一个List
     * @throws Exception
     */
    @Test
    public void test1() throws Exception {
        String sql = "SELECT * FROM test WHERE name = '?'";
        List<User> userList = executeQuery(sql, User.class, "小狗");
    }

}

注意:User类在上述没有写出来,这个就行相应的数据库表 ,数据库表与对象一一对应。

你可能感兴趣的:(Java,java)