首页 > 其他分享 >一篇学会JDBC的使用。

一篇学会JDBC的使用。

时间:2024-07-28 18:54:54浏览次数:10  
标签:JDBC 一篇 学会 连接 connection id public 连接池 String

目录

一、基础篇

1.1 概念

1.2 快速入门

1.3 核心API讲解

1.4 基于PreparedStatement实现CRUD(创建,读取,更新,删除四种操作)

1.4.1 查询

1.4.1 增,删,改

二、进阶篇

2.1 jdbc扩展

2.2 主键回显

2.3 连接池(Druid为例)

2.4 properties集合

三、高级篇

3.1 jdbc工具类封装

3.2 Dao层和BaseDao工具类

3.3 jdbc中事务实现


一、基础篇

1.1 概念

  • JDBC: Java Database Connectivity,意为Java数据库连接,用来操作数据库的。

  • JDBC是Java提供的一组独立于任何数据库管理系统的API。

  • Java提供接口规范,由各个数据库厂商提供接口的实现,厂商提供的实现类封装成jar文件,也就是我们俗称的数据库驱动jar包。

  • 学习JDBC,充分体现了面向接口编程的好处,程序员只关心标准和规范,而无需关注实现过程。

1.2 快速入门

【前期准备】

  1. 准备数据库。

  2. 官网下载数据库连接驱动jar包。MySQL :: Download MySQL Connector/J (Archived Versions)

  3. 创建Java项目,在项目下创建lib文件夹,将下载的驱动jar包复制到文件夹里。

  4. 选中lib文件夹右键->Add as Library,与项目集成。

  5. 编写代码

【代码如下】主要有6步。

public class Main {
    public static void main(String[] args) throws Exception {
//        1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
//        2.获取连接对象
        String url="jdbc:mysql://localhost:3306/bd01";
        String username="root";
        String password="root";
        Connection connection = DriverManager.getConnection(url, username, password);
//        3.获取执行SQl语句的对象
        Statement statement = connection.createStatement();
//        4.编写SQL语句并执行,接收返回的结果集
        String sql="select * from emp";
        ResultSet resultSet = statement.executeQuery(sql);
//        5.处理结果集
        while(resultSet.next()){//next()方法判断查询的下一行是否为空
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            int age = resultSet.getInt("age");
            String job = resultSet.getString("job");
            System.out.println(id+"\t"+name+"\t"+age+"\t"+job+"\t\n");
        }
//        6.关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }
}

【注意】statement有sql注入的风险所以要用prepareStatement,代码如下:

public class Main {
    public static void main(String[] args) throws Exception {
//        1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
//        2.获取连接对象
        String url="jdbc:mysql://localhost:3306/bd01";
        String username="root";
        String password="root";
        Connection connection = DriverManager.getConnection(url, username, password);
//        3.预编译SQL语句得到preparedStatement对象
        System.out.println("输入你要查询的id:");
        Scanner sc = new Scanner(System.in);
        int id1=sc.nextInt();
        PreparedStatement preparedStatement = connection.prepareStatement("select * from emp where id=?");
//        4. 为?赋值,执行sql,接收返回的结果集。?为占位符防止sql注入,提升效率。         
        preparedStatement.setInt(1,id1);
        ResultSet resultSet = preparedStatement.executeQuery();
//        5.处理结果集
        while(resultSet.next()){//next()方法判断查询的下一行是否为空
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            int age = resultSet.getInt("age");
            String job = resultSet.getString("job");
            System.out.println(id+"\t"+name+"\t"+age+"\t"+job+"\t");
        }
//        6.关闭资源
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

1.3 核心API讲解

注册驱动

  • 注册驱动的意思就是将一个特定的JDBC驱动程序加载到Java虚拟机(JVM)中,使其能够与指定的数据库进行通信。

  • 注册驱动的过程,实际上就是通过调用Class.forName()方法,加载并初始化驱动程序中的Driver类。这个Driver类通常会包含一个static块,该static块会向DriverManager注册自身。一旦驱动程序被注册,Java程序就可以通过DriverManager获取与数据库的连接,从而执行SQL语句和获取结果。

  • 简单来说,注册驱动就是让JVM知道我们要使用哪个JDBC驱动程序来与数据库进行通信。

1.Connection

  • 通过DriverManager对象调用getConnection()获取的

  • Connection接口是JDBCAPI的重要接口,用于建立与数据库的通信通道。换而言之,Connection对象不为空,则代表一次数据库连接。

  • 在建立连接时,需要指定数据库URL、用户名、密码参数。

    URL: jdbc:mysq!://localhost:3306/使用的数据库名 jdbc:mysql:/IP地址:端口号/数据库名称?参数键值对1&参数键值对2

  • Connection接口还负责管理事务,Connection接口提供了commit和rollback方法,用于提交事务和回滚事务。

  • 可以创建Statement对象,用于执行sQL语句并与数据库进行交互。

  • 在使用JDBC技术时,必须要先获取Connection对象,在使用完毕后,要释放资源,避免资源占用浪费及泄漏

2.Statement

  • Statement接口用于执行SQL语句并与数据库进行交互。它是JDBC API中的一个重要接口。通过Statement对象,可以向数据库发送SQL语句并获取执行结果。

  • 结果可以是一个或多个结果。 增删改︰受影响行数单个结果。 查询:单行单列、多行多列、单行移列等结果。

  • 但是Statement接口在执行SQL语句时,会产生SQL注入攻击问题: 当使用statement 执行动态构建的SQL查询时,往往需要将查询条件与SQL语句拼接在一起,直接将参数和SQL语句一并生成,让SQL的查询条件始终为true得到结果。

3.PreparedStatement

PreparedStatement是 Statement接口的子接口,用于执行预编译的SQL查询,作用如下:

  • 预编译SQL语句:在创建PreparedStatement时,就会预编译SQL语句,也就是SQL语句已经固定。

  • 防止SQL注入: PreparedStatement支持参数化查询,将数据作为参数传递到SQL语句中,采用?占位符的方式,将传入的参数用一对单引号包裹起来",无论传递什么都作为值。有效防止传入关键字或值导致SQL注入问题。

  • 性能提升:PreparedStatement是预编译SQL语句,同一SQL语句多次执行的情况下,可以复用,不必每次重新编译和解析。

4.ResultSet

  • Resultset是JDBCAPI中的一个接口,用于表示从数据库中执行查询语句所返回的结果集。它提供了一种用于遍历和访问查询结果的方式。

  • 遍历结果: ResultSet可以使用next()方法将游标移动到结果集的下一行,逐行遍历数据库查询的结果,返回值为boolean类型,true代表有下一行结果,false则代表没有。

  • 获取单列结果:可以通过getXxx的方法获取单列的数据,该方法为重载方法,支持索引和列名进行获取。

1.4 基于PreparedStatement实现CRUD(创建,读取,更新,删除四种操作)

1.4.1 查询

  • 以多行多列为例,代码如下

    /       1. 注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
    //      2. 通过DriverManager对象获取连接对象
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bd01","root","root");
    //      3.预编译SQL语句,获取PreparedStatement对象
            PreparedStatement preparedStatement = connection.prepareStatement("select * from emp where id < ?");
    //       4.为?赋值,执行SQL语句,返回得到的结果集
            preparedStatement.setInt(1,5);
            ResultSet resultSet = preparedStatement.executeQuery();
    //        5.处理结果集
            while (resultSet.next()){
                int count = resultSet.getInt(1);
                System.out.println(count);
            }
    //      6.释放资源
            resultSet.close();
            preparedStatement.close();
            connection.close();
        }
    }   

1.4.1 增,删,改

用法都差不多,都是调用executeUpdate()方法,返回受影响的行数。

  • 以新增部门操作为例,代码如下

    public class Test02 {
        public static void main(String[] args) throws Exception {
    //        1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
    //        2.获取连接
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bd01","root","root");
    //        3.预编译SQL语句,获取preparedStatement对象
            PreparedStatement preparedStatement = connection.prepareStatement("insert into dept(id,name) value(?,?) ");
    //        4.为?赋值,执行SQL语句,得到受影响的行数
            preparedStatement.setInt(1,0);//id是自增的,不需要赋值也可以
            preparedStatement.setString(2,"吹牛部");
            int result = preparedStatement.executeUpdate();
    //        5.根据受影响的行数,判断是否执行成功
            if(result > 0){
                System.out.println("成功!");
            }else {
                System.out.println("失败!");
            }
    //        6.释放资源
            preparedStatement.close();
            connection.close();
    ​
        }
    }

二、进阶篇

2.1 jdbc扩展

实体类和ORM思想

  • 在使用JDBC操作数据库时,我们会发现数据都是零散的,明明在数据库中是一行完整的数据,到了Java中变成了一个一个的变量,不利于维护和管理。而我们Java是面向对象的,一个表对应的是一个类,一行数据就对应的是Java中的一个对象,一个列对应的是对象的属性,所以我们要把数据存储在一个载体里,这个载体就是实体类。

  • ORM (Object Relational Mapping)思想,对象到关系数据库的映射,作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来,以面向对象的角度操作数据库中的数据,即一张表对应一个类,一行数据对应一个对象,一个列对应一个属性!

  • 当下JDBC中这种过程我们称其为手动ORM。后续我们也会学习ORM框架,比如MyBatis、JPA等。

以查询多个部门为例,实现ORM,代码如下:

1.首先定义实体类
public class Department {
    private int id;
    private String name;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Department{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
2.代码编写
public class Test02 {
    public static void main(String[] args) throws Exception {
//        1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
//        2.获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bd01","root","root");
//        3.预编译SQL语句,获取preparedStatement对象
        PreparedStatement preparedStatement = connection.prepareStatement("select * from dept where id < ?");
//        4.为?复制,执行SQL语句,得到结果集
        preparedStatement.setInt(1,4);
        ResultSet resultSet = preparedStatement.executeQuery();
//        5.处理结果集,这里用实体类映射不同的字段,用集合去存放多个实体类。
        Department department = null;
        ArrayList<Department> departments = new ArrayList<>();
        while (resultSet.next()){
            department = new Department();
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            department.setId(id);
            department.setName(name);
            departments.add(department);
        }
        System.out.println(departments);
//        6.释放资源
        resultSet.close();
        preparedStatement.close();
        connection.close();
​
    }
}

2.2 主键回显

概念

  • 在数据中,执行新增操作时,主键列为自动增长,可以在表中直观的看到,但是在Java程序中,我们执行完新增后,只能得到受影响行数,无法得知当前新增数据的主键值。在Java程序中获取数据库中插入新数据后的主键值,并赋值给Java对象,此操作为主键回显。

  • 代码演示如下:

public class Test03 {
    public static void main(String[] args) throws Exception {
//        1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
//        2.获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bd01", "root", "root");
//        3.1预编译SQL语句得到PreparedStatement对象
//        3.2后面那个参数表示回显执行成功的主键值。
        PreparedStatement preparedStatement =
                connection.prepareStatement("insert into dept(name) value (?)", Statement.RETURN_GENERATED_KEYS);
//        4.给?赋值,执行sql语句,得到受影响的行数
        Department department = new Department(0,"春牛逼");
        preparedStatement.setString(1,department.getName());
        int result = preparedStatement.executeUpdate();
//        5.处理结果
        ResultSet generatedKeys=null;
        if(result > 0){
            System.out.println("成功!");
//            得到插入成功后的主键值。(单行单列的结果)
            generatedKeys = preparedStatement.getGeneratedKeys();
            if(generatedKeys.next()){
//                得到主键值
                int id = generatedKeys.getInt(1);
                System.out.println(id);
//                赋值给插入成功的那条数据
                department.setId(id);
            }
        }else {
            System.out.println("失败!");
        }
//        6.1 释放资源
//        6.2 如果没有成功执行generatedKeys有为空,这里要判断一下。
        if(generatedKeys!=null){
            generatedKeys.close();
        }
        preparedStatement.close();
        connection.close();
//
    }
}

2.3 连接池(Druid为例)

存在的问题

  • 每次操作数据库都要获取新连接,使用完毕后就close释放,频繁的创建和销毁造成资源浪费。

  • 连接的数量无法把控,对服务器来说压力巨大。

概念

  • 连接池就是数据库连接对象的缓冲区,通过配置,由连接池负责创建连接、管理连接、释放连接等操作。预先创建数据库连接放入连接池,用户在请求时,通过池直接获取连接,使用完毕后,将连接放回池中,避免了频繁的创建和销毁,同时解决了创建的效率。 当池中无连接可用,且未达到上限时,连接池会新建连接。池中连接达到上限,用户请求会等待,可以设置超时时间。

常见的连接池

  • JDBC的数据库连接池使用javax.sql.DataSource接口进行规范,所有的第三方连接池都实现此接口,自行添加具体实现!也就是说,所有连接池获取连接的和回收连接方法都一样,不同的只有性能和扩展功能!

  • DBCP是Apache提供的数据库连接池,速度相对c3Po较快,但自身存在一些BUG。

  • C3Po是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以。

  • Proxool是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点.

  • Druid是阿里提供的数据库连接池,是集DBCP、C3P0、Proxool优点于一身的数据库连接池,性能、扩展性、易用性都更好,功能丰富。

  • Hikari (U小b)[shi ga li])取自日语,是光的意思,是SpringBoot2.x之后内置的一款连接池,基于BoneCP (已经放弃维护,推荐该连接池)做了不少的改进和优化,口号是快速、简单、可靠。

使用

1.准备,导入需要的jar包

2.硬编码方式实现

public class Test05 {
    public static void main(String[] args) throws SQLException {
    /*
     连接池的使用,硬编码方式
     分为4步
     1.创建DruidDataSource连接对象
     2.设置连接池的配置信息,必须和非必须
     3.通过连接池获取连接对象
     4.回收连接(将连接归还给连接池,给其他线程复用)
    * */
//        1.创建连接池对象
        DruidDataSource druidDataSource = new DruidDataSource();
//        2.设置配置信息,必须
        druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql:///bd01");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("root");
//        非必须
        druidDataSource.setInitialSize(10);//设置连接数量
        druidDataSource.setMaxActive(20);//最大连接数,当超过10个时,再创建,最大20个
//        3.通过连接池获取连接对象
        DruidPooledConnection connection = druidDataSource.getConnection();
//        获取连接之后,其他操作一样。。。。。。。。
        System.out.println(connection);
​
​
//        4.回收连接
        connection.close();
​
    }
}

3.软编码方式实现(推荐)

首先建一个目录resources,将其标记为资源目录,然后创建文件pro.properties,配置内容如下:

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///bd01
username=root
password=root

其次就是读取配置文件信息,建立连接的过程,代码如下:

public class Test06 {
    public static void main(String[] args) throws Exception {
//        1.创建properties集合,用于存储外部配置文件的key和value值
        Properties properties = new Properties();
//        2.读取外部配置文件,获取输入流,加载到properties集合中去
//        只要加载Test06这个类的时候就得到读取pro.properties文件的流
        InputStream inputStream = Test06.class.getClassLoader().getResourceAsStream("pro.properties");
//        将读取的信息加载到集合中
        properties.load(inputStream);
//        3.基于properties集合构建DruidDataSource连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//        4.获取连接
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
//        进行连接后的操作
​
//        5.回收连接
        connection.close();
    }
}

2.4 properties集合

概念

  • Properties 集合是 Java 提供的一个类,用于存储可以通过 String 键来访问的 String 值。它继承自 Hashtable<Object,Object> 类,但它有一个特性,即它的键和值都是字符串。由于这个特性,Properties 类经常用于处理配置文件,如 .properties 文件。

主要特点

  • 键和值都是字符串:Properties 集合只能存储字符串类型的键和值。

  • 可持久化:Properties 集合提供了将键值对保存到文件(如 .properties 文件)或从文件中加载键值对的方法。

  • 继承自 Hashtable:虽然 Properties 类扩展了 Hashtable,但不建议使用 Hashtable 的特定方法(如 put() 和 get()),因为 Properties 类已经提供了自己的 setProperty() 和 getProperty() 方法。

核心方法介绍

  • getProperty(String key)

    根据给定键去查找对应的值,找到了返回该值,否则返回空。

  • setProperty(String key, String value)

    将一个键值对添加到集合中,如果已经存在该键就替换掉其值。

  • load(InputStrean inStream)

    这个方法可以从输入流中加载属性列表。比如,我们有一个.properties文件,里面存储了一些键值对,我们就可以使用这个方法把这个文件的内容加载到Properties集合中。

  • store(OutputStream out, String comments)

    这个方法可以把Properties集合中的键值对存储到输出流中。通常,我们会把它用来把Properties集合的内容保存到.properties文件中。comments参数是一个注释,它会被存储到文件的开头。

  • stringPropertyNames()

    这个方法返回一个Set集合,其中包含Properties集合中所有的键。这些键及其对应的值都是字符串类型。

三、高级篇

3.1 jdbc工具类封装

JDBCUtil(v1.0版本)

  • 维护一个连接池对象

  • 对外提供在连接池中获取连接的方法

  • 对外提供回收连接的方法

  • 工具类对外仅仅提供共性的功能代码,方法均为静态方法!

properties配置文件

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///bd01
username=root
password=root

【注】也可以用资源目录对象的方法读取文件信息

static {
        //通过资源目录读取配置文件
        ResourceBundle jdbc = Resources.getBundle("jdbc");
        String driverClassName = jdbc.getString("driverClassName");
        String url = jdbc.getString("url");
        String username = jdbc.getString("username");
        String password = jdbc.getString("password");
        try {
            Class.forName(driverClassName);
            connection = DriverManager.getConnection(url, username, password);
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

工具类代码

public class JDBCUtil1 {
//   创建连接池引用,因为要提供给当前项目的全局使用,创建为静态
   private static DataSource dataSource;
​
   //   在项目启动时,即创建连接池对象,赋值给dataSource;
   static {
      try {
         Properties properties = new Properties();
         InputStream inputStream = JDBCUtil1.class.getClassLoader().getResourceAsStream("pro.properties");
         properties.load(inputStream);
         dataSource = DruidDataSourceFactory.createDataSource(properties);
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }
//   得到连接对象
   public static Connection getConnection(){
      try {
         return dataSource.getConnection();
      } catch (SQLException e) {
         throw new RuntimeException(e);
      }
   }
//   回收连接
   public static void release(Connection connection){
      try {
         connection.close();
      } catch (SQLException e) {
         throw new RuntimeException(e);
      }
   }
​
}

测试代码

public class Test07 {
    public static void main(String[] args) {
        Connection connection = JDBCUtil1.getConnection();
        System.out.println(connection);
        JDBCUtil1.release(connection);
    }
}

JDBCUtil(v2.0版本)

ThreadLocal介绍

  • ThreadLocal,给每个线程提供了一个独立的存储空间,这样每个线程都可以读取、修改自己存储空间内的值,而不会影响其他线程。它就像一个为每个线程定制的“小盒子”,每个线程都有自己的“小盒子”,互不干扰。

  • 主要有下面几个方法:

    set(T value):往当前线程的“小盒子”里放个值。 get():从当前线程的“小盒子”里取值。 initialValue():获取当前线程的“小盒子”里的初始值,这个方法在第一次调用get()方法时才会被调用。 remove():删除当前线程的“小盒子”里的值,这个操作通常是为了减少内存占用。 ThreadLocal的优点很明显,就是线程安全、高效、简单易用。但也要注意,如果使用不当,也可能会导致内存泄漏等问题。

编写工具类

  • 维护一个连接池对象、维护了一个线程绑定变量的ThreadLocal对象

  • 对外提供在ThreadLocal中获取连接的方法

  • 对外提供回收连接的方法,回收过程中,将要回收的连接从ThreadLocal中移除!

  • 注意:使用ThreadLocal就是为了一个线程在多次数据库操作过程中,使用的是同一个连接!

工具类代码

public class JDBCUtil2 {
    //创建连接池引用,因为要提供给当前项目的全局使用,创建为静态
    private static DataSource dataSource;
//用ThreadLocal去存放连接,用的时候直接在这里面取,保证拿到的是一个连接,避免了一个用户从连接池中拿多个连接,防止资源的浪费。
    private static ThreadLocal<Connection> threadLocal=new ThreadLocal<>();

    static {
        try {
            Properties properties = new Properties();
            InputStream inputStream = JDBCUtil1.class.getClassLoader().getResourceAsStream("pro.properties");
            properties.load(inputStream);
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    //   得到连接对象
    public static Connection getConnection(){
        try {
//            从threadLocal中获取连接
            Connection connection = threadLocal.get();
//            如果连接等于空,说明是第一次连接
            if (connection == null) {
//                从连接池中拿出一个连接放到threadLocal中
                connection= dataSource.getConnection();
                threadLocal.set(connection);
            }
            return connection;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    //   回收连接
    public static void release(){
        try {
            Connection connection = threadLocal.get();
//            1.如果不等于null说明已经threadLocal中存储了一个连接,将其清除
//            2.然后再将从中拿出的连接关闭
            if (connection!=null){
                threadLocal.remove();
                connection.close();
           }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

3.2 Dao层和BaseDao工具类

Dao概念

  • DAO: Data Access Object,数据访问对象。

  • Java是面向对象语言,数据在Java中通常以对象的形式存在。一张表对应一个实体类,一张表的操作对应一个DAo对象!

  • 在Java操作数据库时,我们会将对同一张表的增删改查操作统一维护起来,维护的这个类就是DAO层。

  • DAO层只关注对数据库的操作,供业务层service调用,将职责划分清楚!

BaseDao概念

  • 基本上每一个数据表都应该有一个对应的DAo接口及其实现类,发现对所有表的操作(增、删、改、查)代码重复度很高,所以可以抽取公共代码,给这些DAo的实现类可以抽取一个公共的父类,复用增删改查的基本操作,我们称为BaseDAO。

jdbc操作数据库有6个步骤:

  1. 注册驱动

  2. 获取连接

  3. 预编译SQL语句,得到PreparedStatement对象

  4. 为?赋值,执行SQL语句,返回结果集

  5. 处理结果集

  6. 释放资源

1,2,6三个步骤已经被封装到JDBCUtil工具类中,我们需要把3,4,6的共性代码,也封装到一个BaseDao工具类中,从而简化代码的书写。对于BaseDao工具类

  • 返回值可能结果:整数,单个对象,或者是一个对象的集合。

  • 参数:一个sql语句,一个可变参数列表,还有一个类对象(不确定是什么对象,需要用反射)

编写BaseDAO工具类

public class BaseDAO {
    /*
    * 一、通用的增,删,改的方法
    *    返回值:受影响的行数
    *    参数:
    *       1.String类型的sql语句
    *       2.为占位符赋值的object类型的可变参数
    * */
    public static int executeUpdate(String sql,Object...params) throws Exception {
//        1.通过JDBCUtil2获取连接
        Connection connection = JDBCUtil2.getConnection();
//        2.预编译SQL语句,获取PreparedStatement对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
//        3.为占位符?赋值,执行sql语句
        if(params!=null && params.length > 0){
            for (int i = 0; i < params.length; i++) {
                preparedStatement.setObject(i+1,params[i]);
            }
        }
        int row = preparedStatement.executeUpdate();
//        释放资源
        preparedStatement.close();
       if(connection.getAutoCommit()){
            JDBCUtil2.release();
        }
//        返回受影响的行数
        return row;
    }

    /*
     * 二、通用的查询方法:多行多列,单行多列,单行单列
     *    返回值:多行多列:List<Department>
                单行多列:Department
     *          单行单列:一个结果:Double,Integer
     * 封装过程:
     *      1.返回类型:不确定,用泛型。
     *      2.返回结果:通用,用List集合,存储多个结果,单个结果为get(0)
     *      3.结果的封装:反射,要求调用者告知BaseDAO要封装对象的类对象,Class
     * */
    public static <T> List<T> executeQuery(Class<T> tClass,String sql,Object...params) throws Exception {
//        1.用工具类建立连接
        Connection connection = JDBCUtil2.getConnection();
//        2.预编译SQL
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
//       3. 为占位符赋值,得到结果集
        if(params!=null && params.length > 0){
            for (int i = 0; i < params.length; i++) {
                preparedStatement.setObject(i+1,params[i]);
            }
        }
//        4.获取结果集
        ResultSet resultSet = preparedStatement.executeQuery();
//        5.处理结果集
//              5.1 获取结果集中的元数据对象(将查询的结果当成一个对象)
//              5.2 这个对象中包含获取列的数量和列名等方法。
        ResultSetMetaData metaData = resultSet.getMetaData();
        List<T> list = new ArrayList<>();
        while (resultSet.next()){
//            循环一次,有一行数据,创建一个对象去接收,因为不知道是什么对象,所以用反射去创建未知的对象
            T t=tClass.newInstance();
//            得到当前行的每一列,metaData.getColumnCount()获取列数
            for (int i = 0; i < metaData.getColumnCount(); i++) {
//                获取每列的值
                Object value = resultSet.getObject(i + 1);
//                得到列名,也就是对象的属性名
                String columnName = metaData.getColumnName(i + 1);
//                用反射去获取对象的属性,然后为其赋值
                Field field = tClass.getDeclaredField(columnName);
//                暴力破解
                field.setAccessible(true);
//                为该对象属性赋值
                field.set(t,value);
            }
//            将查询的结果封装到对象里后,在存储到集合中去。
            list.add(t);
        }
        resultSet.close();
        preparedStatement.close();
//        如果开启了自动提交就释放
        if(connection.getAutoCommit()){
            JDBCUtil2.release();
        }
        return list;
    }
    /*
    * 获取单行单列的结果
    * */
    public static <T> T executeQueryBean(Class<T> tClass,String sql,Object...params){
        List<T> query = null;
        try {
            query = executeQuery(tClass, sql, params);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return query.get(0);

    }
}

编写Dao层

1.编写接口

public interface Depar {
//    查询所有
    List<Department> selectAll();
//    根据id查询
    Department selectById(Integer id);
//    新增部门
    int insertDepart(Department d);
//    修改部门
    int update(Department d);
//    删除部门
    int delete(int d);
}

2.编写实现类(用工具类,简化了很多重复代码的编写)

public class DepartmentImp implements Depar {
    @Override
    public List<Department> selectAll() {
        List<Department> departments=null;
        try {
            departments = BaseDAO.executeQuery(Department.class, "select * from dept");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return departments;
    }
    @Override
    public Department selectById(Integer id) {
      Department department =
                BaseDAO.executeQueryBean(Department.class,"select * from dept where id=?",id);

        return department;
    }

    @Override
    public int insertDepart(Department d) {
        int row=0;
        try {
            row= BaseDAO.executeUpdate("insert into dept(name) value (?)",d.getName() );
            if(row > 0){
                System.out.println("新增成功!");
            }else {
                System.out.println("新增失败!");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return row;
    }
    @Override
    public int update(Department d) {
        try {
            int row = BaseDAO.executeUpdate("update dept set name=? where id=?", d.getName(),d.getId());
            if(row > 0){
                System.out.println("修改成功!");
            }else {
                System.out.println("修改失败!");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return 0;
    }
    @Override
    public int delete(int d) {
        int row=0;
        try {
            row=BaseDAO.executeUpdate("delete from dept where id=?",d);
            if(row > 0){
                System.out.println("删除成功!");
            }else {
                System.out.println("删除失败!");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return row;

3.3 jdbc中事务实现

以转账为例

public class Test03 {
    public static void main(String[] args) {
        Connection connection = JDBCUtil2.getConnection();
        try {
//            1.将事务设置为手动提交
            connection.setAutoCommit(false);
//            id为1的加100
            AccountImp.addMoney(100,1);
            //出现异常
            int a=10/0;
//            id为2的减100
            AccountImp.subMoney(100,2);
//            2.提交事务
            connection.commit();
        } catch (Exception e) {
            try {
//                3.当出现异常时就回滚事务
                connection.rollback();
            } catch (SQLException ex) {
                throw new RuntimeException(ex);
            }
            throw new RuntimeException(e);
        }finally {
            JDBCUtil2.release();
        }
    }
}

标签:JDBC,一篇,学会,连接,connection,id,public,连接池,String
From: https://blog.csdn.net/qq_62555748/article/details/140671190

相关文章

  • 学会VS调试
    引言:你是否曾为程序运行结果与预期不符而感到抓狂?是否在代码的海洋中迷失,苦苦寻找那个隐藏的错误?别担心,VS调试就是你的救星,让我们一起揭开它神秘的面纱!1、什么是bug?在介绍调试前,我们需要了解一个东西,bug。相信大家对这个词应该不陌生吧!想象一下你正在建造一座房子,你按照自......
  • 学习【线程池】原理知识这一篇就够了
    线程池线程池原理知识1.线程池基础线程池是什么线程池解决了什么问题2.线程池核心设计与实现总体设计生命周期管理任务执行机制Worker线程管理3.线程池在业务中的实践业务背景实际问题及方案思考4.动态化线程池设计方案动态修改配置线程池信息监控线程池告警通知......
  • 旧文一篇《然并卵》
    你在论坛上和人针砭时弊,痛斥腐败,向往民主,然而这些并没有什么卵用,你无力改变什么。你和人交流新上市那部电影好看,哪部电影该得烂番茄奖,然而这些并没有什么卵用,每个人都有他喜欢和不喜欢的东西。为了喜欢的球队,球员,比赛和人争的面红耳赤,然而这些并没有什么卵用,他们打他们的比赛......
  • 小白的第一篇blog
    Markdown学习1.标题要写标题可用#加空格,再下字,然后再用回车键。2.字体1.粗体打法:在文字两侧加两个*如:helloworld!2.斜体打法:在文字两侧加一个*如:helloworld!3.既要斜体又要粗体打法:在文字两侧加三个*如helloworld!4.划线打法:在文字两侧加两个~~如:helloworld!3.引......
  • CTF入门,看这一篇文章就够了
    CTF从入门到提升一、Web基础HTTP请求包HTTP请求方法定义了HTTP请求时所要告诉服务器执行的动作。常见的HTTP请求方法有以下几种:•GET:通常用于直接获取服务器上的资源•POST:一般用于向服务器发送数据,常用于更新资源信息•PUT:一般用于新增一个数据记录•PATCH:一般用于......
  • Servlet 超详细快速入门(详解 看这一篇就够了)
    1.Servlet介绍1.1 什么是Servlet  Servlet是ServerApplet的简称,是用Java编写的是运行在Web服务器上的程序,它是作为来自Web浏览器或其他HTTP客户端的请求和HTTP服务器上的数据库或应用程序之间的中间层。使用Servlet,可以收集来自网页表单的用户输入,呈现来自......
  • 五分钟学会 调整 Gradio 运行界面 WEB UI 的背景图 和 添加 Html 标签
    场景我在调整gradio运行界面的组件的CSS样式时,我又再想,能不能给这个运行界面添个背景图或者其他HTML元素上去呢?如果可以那就真的太棒了吧~问题如何给gradio运行界面添加其他HTML元素和背景图?解决✨通过Markdown()方法:添加HTML标签通过g......
  • 一篇文章带你入门爬虫并编写自己的第一个爬虫程序
    一、引言        目前我们处在一个信息快速迭代更新的时代,海量的数据以大爆炸的形式出现在网络之中,相比起过去那个通过广播无线电、书籍报刊等传统媒介获取信息的方式,我们现在通过网络使用搜索引擎几乎可以获得任何我们需要的信息资源。        但与此同时信......
  • 你觉得很好用的东西和道理,也许别人并不喜欢,所以还不如学会等价交换
    在日常生活中,我们常常怀着一颗助人的心,希望能够帮助他人改善生活或解决问题。然而,有时候过度的介入可能并不是最好的帮助方式。本文将探讨如何平衡助人的愿望与尊重他人命运的重要性。助人情节的反思助人情节可能源自于善良和同情,但有时也可能带来反效果:过度介入:过度的帮......
  • 一键退出苹果手机恢复模式?你学会了吗?
    iPhone的恢复模式是一个用于修复或重新安装iOS系统的特殊状态。然而,如果不小心进入了恢复模式,而你又不知道如何退出,你可能回感到不知所措。下面,小编将给大家分享如何一键退出iPhone的恢复模式,一起来看看吧!一、强制重启强制重启iPhone是最简单也是最常用的退出恢复模式的方......