首页 > 其他分享 >jdbc学习笔记

jdbc学习笔记

时间:2023-02-20 22:55:36浏览次数:47  
标签:语句 jdbc String 笔记 学习 connection preparedStatement statement sql

JDBC

尚硅谷https://www.bilibili.com/video/BV1sK411B71e?share_source=copy_web

概述

  • JDBC是连接Java和数据库的必要纽带

  • MyBatis、HIBERNATE等都是封装了JDBC的应用层框架

 

JDBC的实现

  • JDBC规范和接口(Java)

    • java语言提供规范(接口),规定了数据库的操作方法。

    • 类库在java.sql,javax.sql包下

  • JDBC

    • java连接数据库技术的统称

  • 第三方数据库厂商(数据库软件)

    • 根据java的jdbc规范,完成集体的实现驱动(jar),实现代码可以不同,但方法都相同

总结

  • jdbc是Java连接数据库技术的统称

  • jdbc由两部分组成

    • Java提供的jdbc规范(接口)

    • 各个数据库厂商的实现驱动jar包

  • jdbc技术是一门面向接口编程

核心API

导入jar包

  • 创建lib文件

  • 导入依赖jar包

  • jar包右键-添加为项目依赖

jdbc使用基本步骤

  1. 注册驱动(依赖的jar包)

  2. 获取连接(connection)

  3. 创建发送sql语句对象(statement对象 )

  4. 发送sql语句,并获取返回结果(statement发送sql语句到数据库,并获取返回 结果)

  5. 结果集解析(resultset结果对象)

  6. 资源关闭(connection释放,statement释放,resultset释放)

基于statement查询演示

 public static void main(String[] args) throws SQLException {
         //1.注册驱动
         /**
          * 注册驱动
          * 依赖:驱动版本8.+ com.mysql.cj.jdbc.Driver
          *           5.+ com.mysql.jdbc.Driver
          */
         DriverManager.registerDriver(new Driver());
         //2.获取连接
         /**
          * java程序要与数据库连接
          * 要连接数据库,肯定需要调用某些方法,且需要填入数据库基本信息作为参数
          * 数据库ip地址:127.0.0.1
          * 数据库端口号:3306
          * 账号:root
          * 密码:123456
          * 连接数据库的名称:atguigu
          */
         /**
          * 参数1:url
          * jdbc:数据库厂商名://ip地址:port/数据库名
          */
         //java.sql 接口=实现类
         Connection connection=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb?serverTimezone=UTC","root","123456");
         //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();
    }

基于statement执行流程

驱动注册

  • 使用DriverManager.registerDriver()注册驱动,会注册两次

    • DriverManager.registerDriver() 方法本身会注册一次

    • Driver.static{DriverManager.registerDriver()}静态代码块,也会注册一次

  • 想只触发一次:只触发静态代码块

  • 静态代码块

    • 类加载时,触发

      • 加载[class文件->jvm虚拟机的class对象]

      • 连接[验证->准备(静态变量的默认值)->解析(触发静态代码块)]

      • 初始化

    • 触发类加载

      • new关键字

      • 调用静态方法

      • 调用静态属性

      • 接口 default默认实现

      • 反射

      • 子类触发弗雷

      • 程序的人口mian方法

连接

  • getConnection是一个重载方法,允许开发者以不同的形式传入数据库的核心参数

    • 核心属性

      • 数据库所在主机的ip地址:localhost/127.0.0.1

      • 数据库所在主机的端口号:3306

      • 具体要连接的库

      • 连接的账号

      • 连接的密码

    • 三个参数

      • String url

        • 数据库软件所在的信息,连接的库,及其他可选信息

        • 语法:jdbc:数据库管理软件名称://ip地址或主机名:port端口号/数据库名?key=value&key=value可选信息

      • String username

      • String password

    • 二个参数

      • String url 与三个参数的url作用相同

      • Properties info:存储账号和密码

    • 一个参数

      • String url数据库软件所在的信息,连接的库,及其他可选信息(账号、密码) jdbc:mysql://127.0.0.1:3306/atguigudb?user=root&password=123456

创建发送sql语句的statement对象

  • 可以发送sql语句到数据库,并且获取返回结果

发送sql语句

  • 编写sql语句

  • 发送sql语句

  • SQL分类:DDL(创建,修改,删除) DML(插入,修改,删除) DQL(查询) DCL(权限控制) TPL(事务控制)

    • 参数:sql 非DQL

      • 返回:int

      • 情况1:DML 返回影响的行数

      • 情况2:非DML 返回0

      • row=executeUpdate(sql)

    • 参数:sql DQL

      • 返回:resultSet 结果封装对象

      • ResultSet resultSet=executeQuery(sql);

查询结果集解析

  • Java是面向对象的思维,所以将查询结果封装成了ResultSet对象,内部一定是有行和列的

    • resultSet->逐行获取数据,行->行内的列的信息

    • 有一指针指向数据行前,next()会使指针向后移动一位,搭配while使用,指向第一行,以此类推,直至指向最后一行

    • 获取列的数据(获取指针指向的行的列的数据)

      • resultSet.get类型(String columnLabel | int columnIndex)

        • columnLabel:列名 如果 有别名 写别名

        • columnIndex:列的下标 从1开始

关闭资源

 public static void main(String[] args) throws SQLException, ClassNotFoundException {
         //获取输入信息
         Scanner scanner=new Scanner(System.in);
         System.out.println("请输入账号:");
         String accout=scanner.nextLine();
         System.out.println("请输入密码:");
         String password=scanner.nextLine();
         //方案一
         //DriverManager.registerDriver(new Driver());
         //方案二 new关键字
         //new Driver();
         //方案三 反射 触发类加载,触发静态代码块
         Class.forName("com.mysql.cj.jdbc.Driver");//可以提取作为参数的字符串到外部的配置文件,可以在不改变代码的情况下,完成数据库驱动的切换
         Connection connection=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb?serverTimezone=UTC","root","123456");
         //创建发送sql语句的statement对象
         //可以发送sql语句到数据库,并且获取返回结果
         Statement statement=connection.createStatement();
         //发送sql语句
         String sql="select * from t_user where account='"+accout+"' and password='"+password+"';";
         ResultSet resultSet=statement.executeQuery(sql);
         //查询结果集解析 resultSet
         if (resultSet.next()){
             System.out.println("cg");
        }else
             System.out.println("sb");
         //6.关闭资源
         resultSet.close();
         statement.close();
         connection.close();
    }

基于preparedStatement优化

statement存在的问题

  • 可能发生注入攻击

  • SQL语句需要字符串拼接,比较麻烦

  • 只能拼接字符类型,其他数据库类型无法处理

statement和preparedStatement

  • statement

    • 创建statement

    • 拼接sql语句

    • 发送sql语句,且获取返回结果集

    • statement.executeQuery(sql)/executeUpdate(sql);

  • preparedStatement

    • 编写sql语句结果 不包含动态值的部分,动态值部分用占位符?替代(注意:?只能替代动态值部分)

    • 创建preparestatement,并且传入动态值

    • 动态值给占位符?赋值,单独赋值即可

    • 发送sql语句,且获取返回结果集

    • preparedStatement.executeQuery()/executeUpdate();

      • 不需要sql,因为它已经知道sql语句,语句动态值

 public static void main(String[] args) throws ClassNotFoundException, SQLException {
         //获取输入信息
         Scanner scanner=new Scanner(System.in);
         System.out.println("请输入账号:");
         String accout=scanner.nextLine();
         System.out.println("请输入密码:");
         String password=scanner.nextLine();
 ​
         //ps的数据库流程
         //1.注册驱动
         Class.forName("com.mysql.cj.jdbc.Driver");
         //2.获取连接
         Connection connection= DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb?serverTimezone=UTC","root","123456");
         //3.编写sql语句
         String sql="select * from t_user where account = ? and password = ? ;";
         //4.创建预编译statement并且设置sql语句
         PreparedStatement preparedStatement=connection.prepareStatement(sql);
         //5.单独的占位符赋值
     /**
          * 参数1:index 占位符的位置,从1开始
          * 参数2:Object 占位符的值 可以设置任何类型的值
          */
         preparedStatement.setObject(1,accout);
         preparedStatement.setObject(2,password);
         //6.发送sql语句,并获取返回结果
         ResultSet resultSet=preparedStatement.executeQuery();
         //7.结果集解析
         if (resultSet.next()){
             System.out.println("登录成功");
        }else
             System.out.println("登录失败");
         //8.关闭资源
         resultSet.close();
         preparedStatement.close();
         connection.close();
    }

基于preparedStatement的CURD

Insert

 @Test
     public void testInsert() throws ClassNotFoundException, SQLException {
         //1.注册驱动
         Class.forName("com.mysql.cj.jdbc.Driver");
         //2.获取连接
         Connection connection= DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb?serverTimezone=UTC","root","123456");
         //3.编写sql语句
         String sql="Insert into t_user(account,password,nickname) values(?,?,?);";
         //4.创建preparedStatement,并且传入sql语句
         PreparedStatement preparedStatement=connection.prepareStatement(sql);
         //5.占位符赋值
         preparedStatement.setObject(1,"test_a");
         preparedStatement.setObject(2,"test_p");
         preparedStatement.setObject(3,"test_n");
         //6.发送sql语句
         int rows=preparedStatement.executeUpdate();
         //7.输出结果
         if (rows>0){
             System.out.println("数据插入成功");
        }else
             System.out.println("数据插入失败");
         //8.关闭资源
         preparedStatement.close();
         connection.close();
    }

Update

 @Test
     public void testUpdate() throws ClassNotFoundException, SQLException {
         //1.注册驱动
         Class.forName("com.mysql.cj.jdbc.Driver");
         //2.创建连接
         Connection connection=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb?serverTimezone=UTC","root","123456");
         //3.编写sql语句
         String sql="Update t_user set nickname=? where id=?";
         //4.创建preparedStatement,并传入sql语句
         PreparedStatement preparedStatement=connection.prepareStatement(sql);
         //5.占位符赋值
         preparedStatement.setObject(1,"员工");
         preparedStatement.setObject(2,"3");
         //6.发送sql语句
         int rows=preparedStatement.executeUpdate();
         //7.输出结果
         if (rows>0){
             System.out.println("数据修改成功");
        }else
             System.out.println("数据修改失败");
         //8.关闭资源
         preparedStatement.close();
         connection.close();
    }

Delete

 @Test
     public void testDelete() throws ClassNotFoundException, SQLException {
         //1.注册驱动
         Class.forName("com.mysql.cj.jdbc.Driver");
         //2.创建连接
         Connection connection=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb?serverTimezone=UTC","root","123456");
         //3.编写sql语句
         String sql="delete from t_user where id=?";
         //4.创建preparedStatement,且传入sql语句
         PreparedStatement preparedStatement=connection.prepareStatement(sql);
         //5.占位符赋值
         preparedStatement.setObject(1,3);
         //6.发送sql语句
         int rows=preparedStatement.executeUpdate();
         //7.输出结果
         if (rows>0){
             System.out.println("数据删除成功");
        }else
             System.out.println("数据删除失败");
         //8.关闭资源
         preparedStatement.close();
         connection.close();
    }

Select

 /**
      * 查询所有用户,且封装到List<Map> list集合中
      * 实现思路
      * 遍历数据,一行对应一个map,获取一行的列名和对应属性
      * 将map装到一个集合中
      */
     @Test
     public void testSelect() throws ClassNotFoundException, SQLException {
         //1.注册驱动
         Class.forName("com.mysql.cj.jdbc.Driver");
         //2.创建连接
         Connection connection=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb?serverTimezone=UTC","root","123456");
         //3.编写sql语句
         String sql="select * from t_user;";
         //4.创建preparedStatement,且传入sql语句
         PreparedStatement preparedStatement=connection.prepareStatement(sql);
         //5.占位符赋值
         //6.发送sql语句
         ResultSet resultSet=preparedStatement.executeQuery();
         //7.结果集解析
         List<Map> list=new ArrayList<>();
         //获取列的信息对象
         //TODO:装的当前结果集列的信息对象(可以获取列的名称,列的数量)
         ResultSetMetaData metaData=resultSet.getMetaData();
         //有了它,就可以水平遍历列了
         int columnCount=metaData.getColumnCount();
         while (resultSet.next()){
             Map map=new HashMap();
             //一行数据对应一个map
             // map.put("id",resultSet.getInt("id"));
             // map.put("account",resultSet.getString("account"));
             // map.put("password",resultSet.getString("password"));
             // map.put("nickname",resultSet.getString("nickname"));
             //自动遍历列
             for (int i=1;i<=columnCount;i++){
                 Object value=resultSet.getObject(i);
                 //获取列名
                 //getColumnLabel有别名获取别名,没有就获取名称
                 //getColumnName只会获取名称
                 String columnLabel=metaData.getColumnLabel(i);
                 map.put(columnLabel,value);
            }
             //将map存到集合中
             list.add(map);
        }
         System.out.println("list="+list);
         //8.关闭资源
         resultSet.close();
         preparedStatement.close();
         connection.close();
    }

获取结果集列的信息:

装的当前结果集列的信息对象(可以获取列的名称,列的数量) ResultSetMetaData metaData=resultSet.getMetaData();

int columnCount=metaData.getColumnCount();

//获取列名 //getColumnLabel有别名获取别名,没有就获取名称 //getColumnName只会获取名称 String columnLabel=metaData.getColumnLabel(i);

JDBC拓展提升

自增长主键回显

  1. 创建preparedStatement时,告知携带回数据库自增长的主键

  2. 获取装主键的结果集对象,一行一列

 @Test
     public void returnPrimaryKey() throws ClassNotFoundException, SQLException {
         Class.forName("com.mysql.cj.jdbc.Driver");
         Connection connection= DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb?serverTimezone=UTC","root","123456");
         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,"123456");
         preparedStatement.setObject(3,"ldd");
         int row=preparedStatement.executeUpdate();
         if (row>0){
             System.out.println("数据插入成功");
             //可以获取回显的主键
             //获取装主键的结果集对象,一行一列 id=value
             ResultSet generatedKeys=preparedStatement.getGeneratedKeys();
             generatedKeys.next();//移动指针
             int i=generatedKeys.getInt(1);
             System.out.println("id="+i);
        }else
             System.out.println("数据插入失败");
         preparedStatement.close();
         connection.close();
    }

批量插入

  1. 路径后添加rewriteBatchedStatements=true 允许批量插入

  2. insert into values【必须带s】,语句不能添加;结束

  3. 不是执行,是添加addBatch()

  4. 遍历添加结束后,统一批量执行executeBatch()

 @Test
     public void testInsert() throws ClassNotFoundException, SQLException {
         Class.forName("com.mysql.cj.jdbc.Driver");
         Connection connection= DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb?rewriteBatchedStatements=true&serverTimezone=UTC","root","123456");
         String sql="Insert into t_user(account,password,nickname) values(?,?,?)";
         PreparedStatement preparedStatement=connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
 ​
         long start=System.currentTimeMillis();
         for (int i=0;i<10000;i++) {
             preparedStatement.setObject(1,"test_"+i);
             preparedStatement.setObject(2,"123456");
             preparedStatement.setObject(3,"ldd_"+i);
             // preparedStatement.executeUpdate();//普通插入
             preparedStatement.addBatch();//不执行,追加到values后面
        }
         preparedStatement.executeBatch();//执行批量操作
         long end=System.currentTimeMillis();
         //118932 | 212
         System.out.println("执行1w次插入消耗的时间:"+(end-start));
         preparedStatement.close();
         connection.close();
    }

数据库事务实现

事务的概念

  • 数据库事务就是SQL语句执行的缓存机制,不会单条执行完就更新数据库数据,而是最终根据缓存内的多条语句执行结果统一判定

  • 一个事务内所有sql语句都成功代表事务成功,可以触发commit提交事务来结束事务以及更新数据

  • 一个事务内任意一条sql语句失败代表事务失败,触发rollback回滚事务

事务的特性

  • 原子性:事务是一个不可分割的单位,内部的操作要么都成功,要么都失败

  • 一致性:必须使数据库从一个一致性状态变换到另一个一致性状态

  • 隔离性:一个事务执行不能被其他事务干扰

  • 持久性:事务一旦提交,它对数据库的数据的改变就是永久性的

事务类型

  • 自动提交:每条sql语句都是一个事务,执行成功自动提交,失败自动回滚

  • 手动提交:手动开启事务,执行成功手动提交,失败手动回滚

操作流程

  • 事务添加是在业务方法中

  • 利用try catch开启事务、提交事务、事务回滚

  • 将connection传入dao层。dao只负责使用,不要close

DAO层

 /**
      * 加钱的数据库操作
      * @param account
      * @param money
      */
     public void add(String account,int money,Connection connection) throws Exception {
         //1.注册驱动
         //Class.forName("com.mysql.cj.jdbc.Driver");
         //2.创建连接
         //Connection connection =DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb?serverTimezone=UTC","root","123456");
         //3.编写sql语句
         String sql="update t_bank set money = money+? where account=?;";
         //4.创建preparedStatement,传入sql
         PreparedStatement statement=connection.prepareStatement(sql);
         //5.占位符赋值
         statement.setObject(1,money);
         statement.setObject(2,account);
         //6.发送sql语句
         statement.executeUpdate();
         //7.关闭资源
         statement.close();
         //connection.close();
         System.out.println("加钱成功");
    }
 ​
     /**
      * 减钱的数据库操作
      * @param account
      * @param money
      */
     public void sub(String account,int money,Connection connection) throws Exception{
         //1.注册驱动
         Class.forName("com.mysql.cj.jdbc.Driver");
         //2.创建连接
         //Connection connection =DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb?serverTimezone=UTC","root","123456");
         //3.编写sql语句
         String sql="update t_bank set money = money - ? where account=?;";
         //4.创建preparedStatement,传入sql
         PreparedStatement statement=connection.prepareStatement(sql);
         //5.占位符赋值
         statement.setObject(1,money);
         statement.setObject(2,account);
         //6.发送sql语句
         statement.executeUpdate();
         //7.关闭资源
         statement.close();
         //connection.close();
         System.out.println("减钱成功");
    }

Service层

 @Test
     public void start() throws Exception {
         transfer("lvdandan","ergouzi",500);
    }
 ​
     public void transfer(String addAccount,String subAccount,int money) throws Exception {
         BankDao bankDao=new BankDao();
         //一个事务最基本要求 必须是同一个connection对象
         //一个转账方法属于一个事务
         //1.注册驱动
         Class.forName("com.mysql.cj.jdbc.Driver");
         //2.创建连接
         Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb?serverTimezone=UTC","root","123456");
         try {
             //开启事务
             //关闭事务自动提交
             connection.setAutoCommit(false);
             //执行数据库操作
             bankDao.add(addAccount,money,connection);
             bankDao.sub(subAccount,money,connection);
             //数据库提交
             connection.commit();
        }catch (Exception e){
             //事务回滚
             connection.rollback();
             //抛出异常
             throw e;
        }finally {
             connection.close();
        }
 ​
    }

一次connection,相当于一个事务

连接池

节约了创建和销毁连接的性能消耗,提升了响应时间

连接池的作用

  • 不使用连接池,每次通过DriverManager获取新连接,使用完后,直接断开,利用率太低,极其浪费。

  • 每次通过DriverManager获取新连接,对于数据库服务器来说,压力太大。服务器和Java程序对连接数也无法控制,容易导致服务器崩溃。

管理连接

  • 建立连接池,池中可以容纳一定数量的连接对象,用户要连接时,可以从池中获取连接对象,不需要新建。使用完毕后,将连接对象放回池中,可以让其他用户使用。

  • 可以提高连接使用率。当池中现有连接都用完后,连接池可以向服务器申请新的连接放到池中

  • 当池中连接到达“最大连接数”,则不能申请新连接,如有没有拿到连接的用户,则只能等待

javax.sql.DataSource接口

  • 规范了连接池获取连接的方法

  • 规范了连接池回收连接的方法

  • 市面上的连接池都是对接口的实现

硬编码方式

  • 直接使用代码设置连接池连接参数方式

  • 创建druid对象

  • 设置连接池参数【必须|非必须】

  • 获取连接(通用)

  • 回收连接(通用)

 /**
      * 硬编码
      */
     public void testHard() throws SQLException {
         //连接池对象
         DruidDataSource dataSource=new DruidDataSource();
         //设置参数
         //必须 连接数据库驱动类,url,username/password)
         dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/atguigudb?serverTimezone=UTC");
         dataSource.setUsername("root");
         dataSource.setPassword("123456");
         dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
         //非必须 初始化连接数量,最大连接数量...
         dataSource.setInitialSize(5);//初始化连接数量
         dataSource.setMaxActive(10);//最大连接数量
         //获取连接
         Connection connection=dataSource.getConnection();
 ​
         //回收连接
         connection.close();//连接池提供的连接,close就是回收连接
    }

软编码

  • 配置文件

 driverClassName=com.mysql.cj.jdbc.Driver
 username=root
 password=123456
 url=jdbc:mysql://127.0.0.1:3306/atguigudb?serverTimezone=UTC
  • 实例化连接池(Java)

 /**
      * 通过读取外部配置文件的方式,实例化druid连接池
      */
     @Test
     public void testSoft() throws Exception {
         //1.读取外部配置文件
         Properties properties=new Properties();
         //scr下的配置文件可以使用类加载器提供的方法
         InputStream ips =DruidUserPart.class.getClassLoader().getResourceAsStream("druid.properties");
         properties.load(ips);
         //2.使用连接池的工具类的工厂模式,创建连接池
         DataSource dataSource=DruidDataSourceFactory.createDataSource(properties);
         Connection connection=dataSource.getConnection();
 ​
         ips.close();
         connection.close();
    }

封装工具类

ThreadLocal可以为一个线程存储共享变量

  • 工具类

 /**
  * 内部包含一个连接池对象
  * 对外提供获取连接和回收连接的方法
  *
  * 工具类的方法推荐静态的,方便外部调用
  * 实现
  * 属性:连接池对象【实例化一次】
  * 单例模式
  *
  * static{
  *     全局调用一次
  * }
  *
  * TODO:
  * 利用线程本地变量,存储连接信息。确保一个线程的多个方法可以获取同一个连接connection
  * 优势:事务操作时,service和dao属于同一线程,不用再传递参数了
  * 都可以调用getConnection自动获取相同的连接
  */
 public class JdbcUtilsV2 {
     private static DataSource dataSource=null;//连接池对象
     private static ThreadLocal<Connection> tl=new ThreadLocal<>();
     static {
         //初始化连接池对象
         Properties properties=new Properties();
         InputStream ips =JdbcUtils.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 freeConnection() throws SQLException {
         Connection connection=tl.get();
         if (connection!=null){
             tl.remove();//清空线程本地变量
             connection.setAutoCommit(true);//事务状态回到false
             connection.close();//回收到连接池
        }
    }
 }
  • DAO层

 public class BankDao {
     /**
      * 加钱的数据库操作
      * @param account
      * @param money
      */
     public void add(String account,int money) throws Exception {
         Connection connection=JdbcUtilsV2.getConnection();
         //3.编写sql语句
         String sql="update t_bank set money = money+? where account=?;";
         //4.创建preparedStatement,传入sql
         PreparedStatement statement=connection.prepareStatement(sql);
         //5.占位符赋值
         statement.setObject(1,money);
         statement.setObject(2,account);
         //6.发送sql语句
         statement.executeUpdate();
         //7.关闭资源
         statement.close();
         //connection.close();
         System.out.println("加钱成功");
    }
 ​
     /**
      * 减钱的数据库操作
      * @param account
      * @param money
      */
     public void sub(String account,int money) throws Exception{
         Connection connection =JdbcUtilsV2.getConnection();
         //3.编写sql语句
         String sql="update t_bank set money = money - ? where account=?;";
         //4.创建preparedStatement,传入sql
         PreparedStatement statement=connection.prepareStatement(sql);
         //5.占位符赋值
         statement.setObject(1,money);
         statement.setObject(2,account);
         //6.发送sql语句
         statement.executeUpdate();
         //7.关闭资源
         statement.close();
         //connection.close();
         System.out.println("减钱成功");
    }
 }
  • Service

 /**
  * 银行卡业务方法,调用dao方法
  */
 public class BankService {
     @Test
     public void start() throws Exception {
         transfer("lvdandan","ergouzi",500);
    }
 ​
     public void transfer(String addAccount,String subAccount,int money) throws Exception {
         BankDao bankDao=new BankDao();
         //一个事务最基本要求 必须是同一个connection对象
         //一个转账方法属于一个事务
         Connection connection=JdbcUtilsV2.getConnection();
         try {
             //开启事务
             //关闭事务自动提交
             connection.setAutoCommit(false);
             //执行数据库操作
             bankDao.add(addAccount,money);
             bankDao.sub(subAccount,money);
             //数据库提交
             connection.commit();
        }catch (Exception e){
             //事务回滚
             connection.rollback();
             //抛出异常
             throw e;
        }finally {
             JdbcUtilsV2.freeConnection();
        }
 ​
    }
 }

BaseDao

 /**
  * 封装数据库重复代码
  * TODO:
  * 封装两个方法
  * 一个简化非DQL
  * 一个简化DQL
  */
 public abstract class BaseDao {
 ​
     /**
      * 封装简化非DQL语句
      * @param sql 带占位符的SQL语句
      * @param param 占位符的值
      * @return 影响的行数
      */
     public int executeUpdate(String sql,Object... param) throws SQLException {
         Connection connection=JdbcUtilsV2.getConnection();
         //4.创建preparedStatement,传入sql
         PreparedStatement statement=connection.prepareStatement(sql);
         //5.占位符赋值
         //可变参数可以当作数组使用
         for (int i = 1; i <= param.length; i++) {
             statement.setObject(i,param[i-1]);
        }
         //6.发送sql语句
         int rows=statement.executeUpdate();
         //7.关闭资源
         statement.close();
         //是否回收连接,需考虑是不是事务
         if (connection.getAutoCommit()){
             //没有开启事务
             JdbcUtilsV2.freeConnection();
        }//connection.getAutoCommit(false)//开启了事务,不需要管连接,业务层来处理
         return rows;
    }
 ​
     /**
      * DQL语句
      * 返回值无法使用List<Map>
      * map 没有数据校验机制
      * map不支持反射
      *
      * 数据库数据->java的实体类
      * 表中一行数据->Java实体类一个对象->多行->List<Java实体类>
      * public <T> List<T> executeQuery(Class<T> clazz,String sql,Object... params);
      * <T>:声明一个不确定类型的泛型
      * Class<T>好处
      * 1.确定泛型
      * 2.要使用发射激素属性赋值
      */
     /**
      * 将查询结果封装到实体类集合
      * @param clazz 要接收值的实体类集合的模板对象
      * @param sql   查询语句,要求列名或别名对于实体类对象
      * @param params   占位符的值
      * @param <T>   声明的结果的类型
      * @return 查询的实体类集合
      * @throws SQLException
      * @throws InstantiationException
      * @throws IllegalAccessException
      * @throws NoSuchFieldException
      */
     public <T> List<T> executeQuery(Class<T> clazz, String sql, Object... params) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException {
         Connection connection=JdbcUtilsV2.getConnection();
         //4.创建preparedStatement,且传入sql语句
         PreparedStatement preparedStatement=connection.prepareStatement(sql);
         //5.占位符赋值
         //6.发送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();
         //7.结果集解析
         List<T> list=new ArrayList<>();
         //获取列的信息对象
         //TODO:装的当前结果集列的信息对象(可以获取列的名称,列的数量)
         ResultSetMetaData metaData=resultSet.getMetaData();
         //有了它,就可以水平遍历列了
         int columnCount=metaData.getColumnCount();
         while (resultSet.next()){
             T t=clazz.newInstance();//调用类的无参构造函数实例化对象
             //一行数据对应一个T类型的对象
             //自动遍历列
             for (int i=1;i<=columnCount;i++){
                 //对象的属性值
                 Object value=resultSet.getObject(i);
                 //对象的属性名
                 //getColumnLabel有别名获取别名,没有就获取名称
                 //getColumnName只会获取名称
                 String columnLabel=metaData.getColumnLabel(i);
                 //反射,给对象的属性值赋值
                 Field field=clazz.getDeclaredField(columnLabel);
                 field.setAccessible(true);//打破私有限制
                 /*
                 参数1:要赋值的对象 如果属性是静态的,第一个参数可以为null
                 参数2:集体的属性值
                  */
                 field.set(t,value);
            }
             //将map存到集合中
             list.add(t);
        }
         //8.关闭资源
         resultSet.close();
         preparedStatement.close();
 //是否回收连接,需考虑是不是事务
         if (connection.getAutoCommit()){
             //没有开启事务
             JdbcUtilsV2.freeConnection();
        }//connection.getAutoCommit(false)//开启了事务,不需要管连接,业务层来处理
         return list;
    }
 }
 

标签:语句,jdbc,String,笔记,学习,connection,preparedStatement,statement,sql
From: https://www.cnblogs.com/H-MADAO/p/17139316.html

相关文章

  • 今日学习总结-01
    今天创建了软件工程日报分类,从今天开始,每天写一篇博客,记录自己每天的学习历程。今天是第二周的周一,下午王老师为我们上了这半学期第二节软工课。在这节课上,王老师用很长......
  • jenkins学习笔记之五:Maven、Ant、Gradl、Node构建工具集成
    一、jienkins集成Maven1.安装Maven下载地址:https://maven.apache.org/download.cgiwget--no-check-certificatehttps://dlcdn.apache.org/maven/maven-3/3.9.0/bin......
  • rust学习笔记
    目录rust学习笔记基础类型FundamentalTypes定宽数字类型rust学习笔记这学期选了一门rust课,今年上半年呢,由PKU精英团队打造的rust内核zroj就要正式上线了,请大家多多支持......
  • 嵌入式开发学习之--抢答器
    前言  前一篇大致了解了什么是中断,中断需要配置的参数,再结合之前的按键输入,这一篇实战一下。一、项目概况1.1、项目需求  两个按键,按下后蜂鸣器都会响;  1号按键......
  • 软件工程学习第一天
    今天学习所花时间为140分钟,今天老师花了2个小时为我们介绍了如何去认识面对软件工程这门专业课和这门课程的未来,我们与老师的关系如同健身教练和学员,老师可以给我们布置任......
  • [转]《基于图像点特征的多视图三维重建》——相关概念汇总笔记
    1.   基于图像的图像3D重建传统上首先使用Structure-from-Motion恢复场景的稀疏表示和输入图像的相机姿势。然后,此输出用作Multi-ViewStereo(多视图立体)的输入,以恢......
  • pytorch中学习率的调整方法
    一、手动法二、利用lr_scheduler()提供的集中衰减函数2.1利用lr_lambda函数具体使用:fromtorch.optimimportSGD,lr_schedulerimportmatplotlib.pyplotaspltfro......
  • javaweb学习
    EL语言${}<c:iftest="{brand.status==1}"><td>启用</td></c:if>//判断<cforEach:items="${brands}" var="brand"varStatus="status"><td>${status.count}</td>......
  • 构造题学习笔记
    抽屉原理在构造题中,若我们遇到了\(n/k\)这样的操作次数的时候,可以考虑将所有数划分为\(k\)个集合。这样,最小的那个集合的大小就一定小于等于\(n/k\)了。CF1198C给......
  • 华为eNSP学习笔记
    严正声明:全篇内容为原创内容,版权归属博客园用户Hmi1234所有,仅供学习和参考,未经允许严禁转载!网络组建与应用第1章华为VRP系统基本操作1.0.1用户视图(查看命令)......