JDBC(入门)

为什么要学jdbc

1.jdbc是java和数据库的必要纽带。
2.一些应用层的框架底层代码是jdbc。

jdbc是什么

JDBC:Java Database Connectivity | java连接数据库技术
在java代码中可以使用jdbc提供的方法,可以发送字符串类型的SQL语句到数据库管理软件(MySQL,Oracle等)并获取语句执行的结果,进而实现数据库数据CRUD操作的技术。
jdbc是java连接数据库技术的统称,jdbc技术是一种典型的面向接口编程。

jdbc如何连接java程序和数据库软件

image.png

jdbc的组成

java语言提供规范(接口),规定数据库发操作方法,位于java.sql.javax.sql包下。
第三方数据库(实现类):各个数据库厂商,根据java的jdbc接口,完成具体的实现代码,提供jar包(类)。
注意:实现接口的代码可能不同,但是方法必然相同。

jdbc基本步骤

1.注册驱动,依赖jar包,进行安装。
2.建立连接
3.java程序创建sql语句对象
4.statement对象,发送sql语句到数据库,并获取返回结果result对象
5.解析结果集,即拆对象。
6.销毁资源,关闭connection,释放statement,释放result。
image.png

import com.mysql.cj.jdbc.Driver;

import java.sql.*;

public class StatementQuery {
    public static void main(String[] args) throws SQLException {
        //1.注册驱动
        //依赖:注意mysql8+以上版本,导入com.mysql.cj.jdbc.Driver
        //              5+             导入com.mysql.jdbc.Driver
        DriverManager.registerDriver(new Driver());
        //2.获取连接
        //java程序连接数据库,需要调用某种方法,方法需要调用连入数据库的基本信息:
            //数据库ip地址:127.0.0.1
            //数据库端口号:3306
            //账号
            //密码
            //连接数据库的名称
        /*
        参数1:url
            语法:jdbc:数据库厂商名://ip地址:数据库端口号/数据库名
            jdbc:mysql://127.0.0.1:3306/test
        参数2:username 数据库软件的账号
        参数3:password 数据库软件的密码
         */
        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "gpy678615");
        //3.创建statement
        Statement statement = connection.createStatement();
        //4.发送sql语句并获取结果
        String sql = "select * from t_user;";
        ResultSet resultSet = statement.executeQuery(sql);
        //5.结果集解析
        //先看看有没有下一行数据
        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();

    }
}

静态SQL路线(没有动态值语句 - statement)

基于statement实现模拟用户登入
缺点:1.SQL语句的拼接麻烦。
2.只能拼接字符串类型,其他数据库无法处理。
3.可能发生注入攻击:动态值充当sql语句影响原有的查询条件。

package com.atguigu.api.statement;

import com.mysql.cj.jdbc.Driver;

import java.sql.*;
import java.util.Properties;
import java.util.Scanner;

/*
模拟用户登入
    输入账号,密码
    进行数据库查询
    反馈成功或失败
 */
public class StatementUserLogIn {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        //获取用户输入信息
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入账号");
        String account = scan.nextLine();
        System.out.println("请输入密码");
        String password = scan.nextLine();
        //注册驱动
        /*
        问题:注册俩次驱动(在Driver类的对象创建时,会自动调用类的静态方法,这个静态方法中会注册一次)
             即DriverManager.registerDriver(new Driver())本身就会注册一次。
             static静态代码块也会注册一次。
         解决:只想注册一次
            只触发静态代码块即可。

         */
        Class.forName("com.mysql.cj.jdbc.Driver");//要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段。
        //获取连接
        /*
        getConnection是一个重载函数
        允许开发者,用不同的形式传入数据库的参数
        核心属性:
            1.数据库软件所在的主机ip地址:127.0.0.1 | localhost
            2.数据库软件所在的主机的端口号:3306
            3.连接的具体库:test
            4.连接账号:root
            5.连接的密码:gpy
        三个参数:(一般选1和3)
            String url:数据库软件所在的信息,连接的具体库,以及其他的可选信息
                格式:jdbc:数据库软件名称(mysql)://ip地址|主机名:端口号/数据库名?key=value&key=value...
                jbdc:mysql://127.0.0.1:3306/test
                jbdc:mysql://localhost:3306/test
                本机的省略写法:如果数据的软件安装到本机可以进行一些省略,省略本机地址和默认端口号。
                jdbc:mysql:///test
            String username:root
            String password:gpy
        俩个参数:
            String url:url与三个参数相同
            Properties info:存储账号和密码
                Properties类似于Map key(user) = value(password) 只不过都是字符串形式
        一个参数:
            数据库软件所在的信息,连接的具体库,以及其他的可选信息
            格式:jdbc:数据库软件名称(mysql)://ip地址|主机名:端口号/数据库名?key=value&key=value...
            jdbc:mysql:localhost:3306/test?user=root&password=gpy
            user和password就算账号和密码,这是规定

        url可选信息:
            url?user=账号&password=密码
             serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true
                serverTimezone=Asia/Shanghai:时区
                useUnicode=true:是否使用Unicode编码格式
                useSSL=true:是否忽略ssl的格式验证
             驱动在8之后,不用写了
         */
        Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
        //俩个参数

        Properties info = new Properties();
        info.put("user","root");
        info.put("password","gpy678615");
        Connection connection2 = DriverManager.getConnection("jdbc:mysql:///test", info);

        //三个参数
        Connection connection3 = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?user=root&password=gpy678615");

        //创建发送sql语句的statement对象
        String sql = "select * from t_user where account = '"+account+"' and password = '"+password+"' ;";
        Statement statement = connection.createStatement();
        /*
        SQL分类
            DDL:容器(库/表)的创建,修改,删除
            DML:插入,修改,删除
            DQL:查询语句
            DCL:权限控制
            TPL:事务控制语句
        int row = statement.executeUpdate(sql)  ---->非DQL
            情况1:DML 返回影响的行数   eg删除3条return3  修改0条return0
            情况2:非DML语句,return 0;
        ResultSet resultSet = statement.executeQuery(sql);   ---->DQL
            resultSet:结果封装对象
         */
        //int i = statement.executeUpdate(sql);
        ResultSet resultSet = statement.executeQuery(sql);
        //结果集解析
        /*
        想要进行数据解析,需要进行俩件事情:
            1.移动游标,指向获取数据行
                resultSet内部包含一个游标指向当前行数据
                默认指向第0行,第一行之前
                我们可以使用next方法移动游标,返回只是一个boolean类型
                可以使用while loop获得每一行的数据
            2.获取指定行的列数据
            resultSet.get类型(String columnLabel | int columnIndex)
                columnLabel:列名 | 别名
                columnIndex:根据列的下角标从左到右,从1到结束
          */
//        while(resultSet.next()){
//            int id = resultSet.getInt(1);
//            String account1 = resultSet.getString("account");
//            String password1 = resultSet.getString(3);
//            String nickname = resultSet.getString("nickname");
//            System.out.println(id + " " + account  + " " + password1 + " " + nickname);
//        }
//        移动一次即成功
        if(resultSet.next()){
            System.out.println("成功");
        }else{
            System.out.println("失败");
        }
        //关闭资源
        resultSet.close();
        statement.close();
        connection.close();
        scan.close();
    }
}

动态SQL路线(动态值语句 - PreparedStatement)

package preparedstatement;

import java.sql.*;
import java.util.Scanner;
/*
使用预编译的statement完成用户登入
    防止注入攻击 | 演示ps的使用流程

 */
public class PSUserLogIn {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //1.获取用户输入信息
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入账号");
        String account = scan.nextLine();
        System.out.println("请输入密码");
        String password = scan.nextLine();
        //ps的数据库的流程
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
        /*3.创建对象发送sql语句
         preparedStatement
            1.编写SQL语句结构,不包含动态值的语句。动态值部分使用?替代 ?只能替代动态值。
            2.创建preparedStatement,并传入动态值。
            3.占位符 ? 赋值
            4.发送sql语句并获取结果。
         */
        String sql = "select * from t_user where account = ? and password = ? ;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //参数1:占位符的位置,从左向右,容1开始 account:1 password:2
        //参数2:占位符的值 可以设置任意类型的数据,拼接和类型更加丰富
        preparedStatement.setObject(1,account);
        preparedStatement.setObject(2,password);
        //发送sql语句,并获取返回结果
        //因为他已经知道语句,知道动态值,就不需要填入sql了
        ResultSet resultSet = preparedStatement.executeQuery();
        //5.结果集解析
        if(resultSet.next()){
            System.out.println("操作成功");
        }else {
            System.out.println("操作失败");
        }
        //6.关闭资源
        resultSet.close();
        preparedStatement.close();
        connection.close();


    }
}

基于PreparedStatement进行CRUD

package preparedstatement;

import org.junit.Test;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/*
使用PreparedStatement对t_user进行CRUD操作


 */
public class PSCURD {
    @Test
    public void testInsert() throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
        String sql = "insert into t_user(account,password,nickname) values(?,?,?)";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setObject(1,"test");
        preparedStatement.setObject(2,"test");
        preparedStatement.setObject(3,"xzj");
        int i = preparedStatement.executeUpdate();
        if(i > 0){
            System.out.println("插入成功");
        }else{
            System.out.println("数据插入失败");
        }
        preparedStatement.close();
        connection.close();
    }
    @Test
    public void testUpdate() throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
        String sql = "update t_user set nickname = ? where id = ?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setObject(1,"gpy");
        preparedStatement.setObject(2,3);
        int i = preparedStatement.executeUpdate();
        if(i > 0){
            System.out.println("删除成功");
        }else{
            System.out.println("删除失败");
        }
        preparedStatement.close();
        connection.close();
    }
    @Test
    public void testDelete() throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
        String sql = "delete from t_user where id = ?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setObject(1,3);
        int i = preparedStatement.executeUpdate();
        if(i > 0){
            System.out.println("删除成功");
        }else{
            System.out.println("删除失败");
        }
        preparedStatement.close();
        connection.close();
    }
    /*
    目标:查询所有数据,并封装到List<Map> list集合中
     */
    @Test
    public void testSelect() throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
        String sql = "select id,account,password,nickname from t_user";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        ResultSet resultSet = preparedStatement.executeQuery();
        //ResultSetMetaData 对象装的就算当前结果集列的信息
        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();
        List<Map> list = new ArrayList<Map>();
        while(resultSet.next()){
            Map map = new HashMap<>();
            //纯手动
//            map.put("id",resultSet.getObject(1));
//            map.put("account",resultSet.getObject(2));
//            map.put("password",resultSet.getObject(3));
//            map.put("nickname",resultSet.getObject(5));
            //自动遍历列,注意从1开始
            for (int i = 1; i <= columnCount; i++) {
                //获取指定类的下角标的值
                Object value = resultSet.getObject(i);
                //获取指定列的列名
                //getColumnLabel获取别名和名称
                String columnLabel = metaData.getColumnLabel(i);
                map.put(columnLabel,value);
            }
            list.add(map);
        }
        System.out.println("list = " + list);
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

扩展提升1

主键回显:获取主键
批量插入数据优化

package preparedstatement;

import org.junit.Test;

import java.sql.*;

/*
PS的特殊使用情况


 */
public class PSOtherPart {
    //主键回显
    /*
    t_user插入一条数据,并获取数据库自增长的主键
        26行:创建PreparedStatement对象的时候,告知,携带会数据库增长的主键(sql,Statement.RETURN_GENERATED_KEYS);
        36行:获取主键的结果集对象,一行 一列 id = 值
     */
    @Test
    public void returnPrimaryKey() throws SQLException, ClassNotFoundException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
        String sql = "insert into t_user(account,password,nickname) values(?,?,?)";
        //
        PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        preparedStatement.setObject(1,"test1");
        preparedStatement.setObject(2,"12345");
        preparedStatement.setObject(3,"wxy");
        int i = preparedStatement.executeUpdate();
        if(i > 0){
            System.out.println("插入成功");
            //取主键值
            //获取主键的结果集对象,一行 一列 id = 值
            ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
            generatedKeys.next();
            int anInt = generatedKeys.getInt(1);
            System.out.println(anInt);
        }else{
            System.out.println("插入失败");
        }
        preparedStatement.close();
        connection.close();
    }
    /*
    使用普通的方式插入1000条信息
     */
    @Test
    public void testInsert() throws SQLException, ClassNotFoundException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
        String sql = "insert into t_user(account,password,nickname) values(?,?,?);";
        //
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        long l = System.currentTimeMillis();
        for (int i = 0; i < 100; i++) {
            preparedStatement.setObject(1, "test" + i);
            preparedStatement.setObject(2, "12345" + i);
            preparedStatement.setObject(3, "wxy" + i);
        }
        long l1 = System.currentTimeMillis();
        System.out.println(l1-l);
        int i = preparedStatement.executeUpdate();

        preparedStatement.close();
        connection.close();
    }

    /*
    优化方式插入1000条信息
    1.路径后添加:?rewriteBatchedStatements=true表示允许批量插入
    2.insert into values 语句不能添加分号;
    3.不是执行语句每条,是批量添加addBatch(),添加到values后面
    4。同意批量执行
     */
    @Test
    public void testBatchInsert() throws SQLException, ClassNotFoundException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        //路径后加一个值:?rewriteBatchedStatements=true表示允许批量操作
        Connection connection = DriverManager.getConnection("jdbc:mysql:///test?rewriteBatchedStatements=true", "root", "gpy678615");
        String sql = "insert into t_user(account,password,nickname) values(?,?,?)";
        //
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        long l = System.currentTimeMillis();
        for (int i = 0; i < 100; i++) {
            preparedStatement.setObject(1, "test" + i);
            preparedStatement.setObject(2, "12345" + i);
            preparedStatement.setObject(3, "wxy" + i);
            //不执行,追加到values后面
            preparedStatement.addBatch();
        }
        //批量执行,批量操作
        preparedStatement.executeBatch();
        long l1 = System.currentTimeMillis();
        System.out.println(l1-l);

        preparedStatement.close();
        connection.close();
    }
}

扩展提升2

事务:银行卡业务方法

package com.atguigu.api.transation;

import org.junit.Test;

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

/**
 * @program: jdbc
 * @description: 银行卡业务方法,调用dao方法
 * @author: Mr.Guan
 * @create: 2023-02-05 18:27
 *
 **/
public class BankService {

    public void transfer(String addAccount,String subAccount,int money) throws SQLException, ClassNotFoundException {
        BankDao bankDao = new BankDao();
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "gpy678615");
        try {
            //开启事务
            //关闭MySQL的自动提交事务
            connection.setAutoCommit(false);
            //执行数据库动作
            bankDao.add(addAccount, money,connection);
            System.out.println("------------------");
            bankDao.sub(subAccount, money,connection);
            //提交事务
            connection.commit();
        }catch (Exception e){
            //事务回滚
            connection.rollback();
            //抛出异常信息
            throw e;
        }finally {
            connection.close();
        }
    }

    /**
     * TODO:
     *      1.事务添加是在业务方法中
     *      2.利用try catch代码块开启事务、提交事务和事务回滚
     *      3.将connetion传入到dao层即可。dao只负责使用,不需要close
     * @throws SQLException
     * @throws ClassNotFoundException
     */
    @Test
    public void transferTest() throws SQLException, ClassNotFoundException {
        transfer("lvdandan","ergouzi",500);

    }
}

package com.atguigu.api.transation;

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

/*
bank表的数据库方法存储类
 */
public class BankDao {
    /**
     * 加钱的数据库操作方法
     * @param account 加钱的账号
     * @param money 加钱的金额
     */
    public void add(String account,int money,Connection connection) throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        String sql = "update t_bank set money = money +? where account =?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setObject(1,money);
        preparedStatement.setObject(2,account);
        preparedStatement.executeUpdate();
        preparedStatement.close();
        System.out.println("加钱成功");
    }

    /**
     * 减钱的数据库操作方法
     * @param account 减钱的账号
     * @param money 减钱的金额
     */
    public void sub(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.setObject(2,account);
        preparedStatement.executeUpdate();
        preparedStatement.close();
        System.out.println("减钱成功");
    }
}

Druid连接池

image.png

  • 我们可以建立一个连接池,这个池中可以容纳一定数量的连接对象,一开始,
    我们可以先替用户先创建好一些连接对象,等用户要拿连接对象时,就直接从池中拿,
    不用新建了,这样也可以节省时间。然后用户用完后,放回去,别人可以接着用。
  • 可以提高连接的使用率。当池中的现有的连接都用完了,那么连接池可以向服务器申
    请新的连接放到池中。
  • 直到池中的连接达到“最大连接数”,就不能在申请新的连接了,如果没有拿到连接的用户只能等待。

Druid的使用

硬编码(不推荐)

/**
 * 创建druid连接池对象,使用硬编码进行核心参数设置!
 *   必须参数: 账号
 *             密码
 *             url
 *             driverClass
 *   非必须参数:
 *           初始化个数
 *           最大数量等等  不推荐设置
 */
@Test
public void druidHard() throws SQLException {

   DruidDataSource dataSource = new DruidDataSource();

   //设置四个必须参数
   dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
   dataSource.setUsername("root");
   dataSource.setPassword("root");
   dataSource.setUrl("jdbc:mysql:///day01");

   //获取连接
   Connection connection = dataSource.getConnection();
   // JDBC的步骤
   //回收连接
   connection.close();
}

软编码

public class DruidTest {
    /**
     * 通过读取外部配置文件的方法,实例化druid的连接池对象
     */
    @Test
    public void testSoft() throws Exception {
        //1.读取外部配置文件
        Properties properties = new Properties();
        //2.src下的文件可以使用类加载器提供的方法
        InputStream resourceAsStream = DruidTest.class.getClassLoader().getResourceAsStream("durid.properties");
        properties.load(resourceAsStream);
        //3.使用连接池工具类的工程模式,创建连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        //4.获取连接
        Connection connection = dataSource.getConnection();
        //之后进行数据库的crud
        connection.close();
    }
}

工具类的封装

1.0版本

封装一个工具类,内部包含连接池对象,同时对外部提供连接的方法和回收方法。
缺点:无法同一个线程不同的方法得到同一个连接

package com.atguigu.api.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.sun.jdi.connect.spi.Connection;

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

/**
 * 内部包含连接池,并且对外提供获取连接和回收连接的方法
 * 建议:工具类的方法,推荐写成静态
 * @program: jdbc
 * @description:
 * @author: Mr.Guan
 * @create: 2023-02-06 16:39
 *
 **/
public class JDBCUtils {
    private static DataSource dataSource = null;//连接池对象

    static {
        Properties properties = new Properties();
        InputStream resourceAsStream = JDBCUtils.class.getClassLoader().getResourceAsStream("durid.properties");
        try {
            properties.load(resourceAsStream);
        } 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 (Connection) dataSource.getConnection();
    }
    //回收连接的方法
    //连接池的连接调用close是回收
    public static void freeConnection(Connection connection) throws IOException {
        connection.close();
    }
}
	
package com.atguigu.api.utils;

import com.sun.jdi.connect.spi.Connection;

import java.io.IOException;
import java.sql.SQLException;

/**
 * @program: jdbc
 * @description:
 * @author: Mr.Guan
 * @create: 2023-02-06 16:52
 **/
public class JDBCUtilCRUD {
    public static void main(String[] args) throws SQLException, IOException {
        Connection connection = JDBCUtils.getConnection();
        JDBCUtils.freeConnection(connection);
    }
}

2.0版本

考虑事务的情况下。同一个线程不同的方法获得同一个连接

package com.atguigu.api.utils;

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;

/**
 * @program: jdbc
 * @description:
 * @author: Mr.Guan
 * @create: 2023-02-06 16:39
 *TODO:
 *      利用线程本地变量,存储连接信息!确保一个线程的多个方法获取同一个连接。
 *      优势:事务操作的时候,service和dao方法属于同一个线程,不用再传递参数了。
 *      大家都可以调用getConnection,获取相同的连接。
 **/
public class JDBCUtilsV2 {
    private static DataSource  dataSource = null;//连接池对象

    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    static {
        Properties properties = new Properties();
        InputStream resourceAsStream = JDBCUtilsV2.class.getClassLoader().getResourceAsStream("durid.properties");
        try {
            properties.load(resourceAsStream);
        } 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;
    }
    //回收连接的方法
    //连接池的连接调用close是回收
    public static void freeConnection() throws IOException, SQLException {
        Connection connection = tl.get();
        if (connection == null) {
            tl.remove();//清空线程本地数据
            connection.setAutoCommit(true);//事务回归
            connection.close();//回收到连接池
        }
    }
}

package com.atguigu.api.transation;

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

/*
bank表的数据库方法存储类
 */
public class BankDao {
    /**
     * 加钱的数据库操作方法
     * @param account 加钱的账号
     * @param money 加钱的金额
     */
    public void add(String account,int money,Connection connection) throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        String sql = "update t_bank set money = money +? where account =?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setObject(1,money);
        preparedStatement.setObject(2,account);
        preparedStatement.executeUpdate();
        preparedStatement.close();
        System.out.println("加钱成功");
    }

    /**
     * 减钱的数据库操作方法
     * @param account 减钱的账号
     * @param money 减钱的金额
     */
    public void sub(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.setObject(2,account);
        preparedStatement.executeUpdate();
        preparedStatement.close();
        System.out.println("减钱成功");
    }
}

package com.atguigu.api.transation;

import org.junit.Test;

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

/**
 * @program: jdbc
 * @description: 银行卡业务方法,调用dao方法
 * @author: Mr.Guan
 * @create: 2023-02-05 18:27
 *
 **/
public class BankService {

    public void transfer(String addAccount,String subAccount,int money) throws SQLException, ClassNotFoundException {
        BankDao bankDao = new BankDao();
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "gpy678615");
        try {
            //开启事务
            //关闭MySQL的自动提交事务
            connection.setAutoCommit(false);
            //执行数据库动作
            bankDao.add(addAccount, money,connection);
            System.out.println("------------------");
            bankDao.sub(subAccount, money,connection);
            //提交事务
            connection.commit();
        }catch (Exception e){
            //事务回滚
            connection.rollback();
            //抛出异常信息
            throw e;
        }finally {
            connection.close();
        }
    }

    /**
     * TODO:
     *      1.事务添加是在业务方法中
     *      2.利用try catch代码块开启事务、提交事务和事务回滚
     *      3.将connetion传入到dao层即可。dao只负责使用,不需要close
     * @throws SQLException
     * @throws ClassNotFoundException
     */
    @Test
    public void transferTest() throws SQLException, ClassNotFoundException {
        transfer("lvdandan","ergouzi",500);

    }
}

BaseDao

简化非DQL语句

 public int executeUpdate(String sql,Object... params) throws SQLException, IOException {
        Connection connection = JDBCUtilsV2.getConnection();
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        for (int i = 1; i <= params.length; i++) {
            preparedStatement.setObject(i, params[i]);
        }
        int rows = preparedStatement.executeUpdate();
        preparedStatement.close();
        //是否回收连接,需要考虑是不是事务
        if (connection.getAutoCommit()) {
            //为真则没有开启事务
            JDBCUtilsV2.freeConnection();
        }
        return rows;
    }
public void testInsert() throws ClassNotFoundException, SQLException, IOException {
        String sql = "insert into t_user(account,password,nickname) values(?,?,?)";
        int i = executeUpdate(sql,"测试333", "333", "ergoiuzi");

    }

简化DQL语句

/**
     * 非DQL语句封装
     * 数据库数据--》java的实体类
     *
     */
public <T> List<T> executeQuery(Class<T> clazz,String sql,Object... params) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException, IOException {
        Connection connection = JDBCUtilsV2.getConnection();
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        if(params != null && params.length != 0){
            for (int i = 1; i <= params.length; i++) {
                preparedStatement.setObject(i, params[i-1]);
            }
        }
        ResultSet resultSet = preparedStatement.executeQuery();
        List<T> list = new ArrayList<>();
        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();
        while(resultSet.next()){
            T t = clazz.newInstance();//调用类的无参构造函数
            //自动遍历列,注意从1开始
            for (int i = 1; i <= columnCount; i++) {
                //获取指定类的下角标的值
                Object value = resultSet.getObject(i);
                //获取指定列的列名
                //getColumnLabel获取别名和名称
                String columnLabel = metaData.getColumnLabel(i);
                //反射给对象的属性值
                Field filed = clazz.getDeclaredField(columnLabel);
                filed.setAccessible(true);//属性可以设置,打破私有的修饰
                filed.set(t, value);
            }
            list.add(t);
        }
        resultSet.close();
        preparedStatement.close();
        if(connection.getAutoCommit()){
            JDBCUtilsV2.freeConnection();
        }
        return list;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JLAU Veritas

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值