JDBC大总结

所需要的所有依赖

<dependencies>
	 <!-- mysql依赖 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
    <!-- 连接池依赖 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.8</version>
    </dependency>
    <!-- javabean类的依赖 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.22</version>
        <scope>provided</scope>
    </dependency>
    <!-- 添加日志的依赖 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.36</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.36</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
     <!-- 测试依赖 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

1. 概述

在 Java 中,用于数据库存取的技术可以有以下几种:

  • JDBC直接访问数据库
  • JDO(Java Data Object)技术
  • 第三方ORM库,如 Hibernate、MyBatis

JDBC 是 Java 访问数据库基础。

1.1 什么是 JDBC

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统,通用的 SQL数据库访问和存取的公共接口。定义了访问数据库的标准。

JDBC为访问不同的数据库提供了统一的途径和方法,为开发者屏蔽了一些细节问题。

对于数据库操作,实际上是数据库厂商自己提供 JDBC 的实现。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5jBaLR8L-1651410862692)(image/image-20220223093708060.png)]

1.2 JDBC的API

JDBC的API是一系列的接口,它统一和规范了应用程序与数据库连接、执行SQL的语句,并得到返回的结果。

在这里插入图片描述

1.3 开发步骤

1.加载驱动器类,把数据库厂商操作的程序加载到内存中。

2.通过DriverManager类来创建 Connnection 对象

3.通过Connection 对象来创建 PreparedStatement 对象

4.如果有参数,则设置参数

5.执行 SQL 语句

6.如果执行有返回结果,那么就对结果进行处理

7.关闭资源

2. 获取数据库连接

2.1 创建Java工程

在 IDEA 中创建一个空的工程,我们后续的内容都是在这个空工程中创建子模块的方式来进行。

打开 IDEA 工具,然后点击 New Object 按钮。

在这里插入图片描述

选择 Empty Project

在这里插入图片描述

然后输入工程名称和存放路径后,点击 Finish 完成空工程的创建。

2.2 创建 JDBC 模块

在刚创建的 工程中,点击 File -> new -> module 进入模块的创建界面。

在这个界面中在边选择 Maven,在右边的 Module SDK 处选择你的 JDK 安装版本,然后点击 Next。
在这里插入图片描述

输入完上面的信息后,点击 Finish

在这里插入图片描述

2.3 加入依赖

我们要使用 JDBC 来连接 MySQL 数据库,那么就需要把 MySQL 数据库厂商提供的 JDBC 实现的 jar 文件添加到工程中来。也就是它的依赖添加到 pom.xml 文件中。

打开浏览器,输入 https://mvnrepository.com 进入中央仓库地址,然后输入 mysql 来进行查询。

在这里插入图片描述

然后点击 MySQL Connector/J

在这里插入图片描述

我在此时选择 5.1.49 然后点击它。
在这里插入图片描述

然后复制上面的代码到项目的 pom.xml 文件中。

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>

2.4 编写程序来测试

在项目的 src 下的 main 下的 java 目录中创建包 com.xianopeng ,然后在这个包下创建 JdbcTest 类。

package com.xianopeng;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

/**
 * @Description TODO
 * @Author Jock
 * @Date 2022/2/23
 */
public class JdbcTest {
    public static void main(String[] args) throws Exception {
        // 1. 加载驱动器类
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 通过 DriverManager 来创建 Connection 对象
        /**
         * String url:用于指定连接数据库的字符串,这个字符串的格式说明:
         * jdbc:mysql://192.168.72.71:3306/mydb
         * 1、jdbc:mysql://  它是代表协议,是 jdbc 的协议,jdbc:是一级协议,而 mysql: 是二级协议
         * 2、192.168.72.71  它是代表数据库的主机名,如果是本机则可写为 localhost
         * 3、3306  它是代表数据库端口号
         * 4、mydb 它代表的是要连接的数据库名称
         *
         * String user: 用于连接数据库时的用户名
         * String password:用于连接数据库时的密码
         */
        String url = "jdbc:mysql://192.168.72.71:3306/mydb?useSSL=false&characterEncoding=utf8";
        String user = "root";
        String password = "123456";
        // 2. 创建连接对象
        Connection conn = DriverManager.getConnection(url, user, password);
        System.out.println(conn);

        // 3. 创建执行SQL的对象
        Statement stm = conn.createStatement();
        // 4. 执行SQL
        ResultSet rs = stm.executeQuery("select * from tb_user");
        // 5. 处理数据
        System.out.println("id\t\tname\t");
        while (rs.next()) {
            int id = rs.getInt("id");
            String name = rs.getString("name");
            System.out.println(id + "\t\t" + name);
        }

        // 6. 关闭资源
        rs.close();
        stm.close();
        conn.close();
    }
}

2.5 Statement

Statement 对象是通过 Connection 对象的 createStatement() 方法来创建的。这个对象中有两个方法需要大家关注:

  • ResultSet executeQuery(String sql) throws SQLException; 这个方法是执行 SQL 查询的方法,执行后会返回查询的结果,并存入 ResultSet 对象中。
  • int executeUpdate(String sql) throws SQLException; 这是执行增、删、改的方法,执行后会返回成功操作后影响的行数。

这两个方法都需要传入一 SQL 语句来作为执行的参数。

2.6 ResultSet

ResultSet 对象是查询的结果集对象,它是 Statement 执行完查询数据的 SQL 后产生的一个对象,它里面有以下几个方法:

  • boolean next() throws SQLException; 这是用于判断是否有下一条数据
  • XX getXX(int columnIndex) throws SQLException; 从结果集对象中获取需要的数据,是通过列的索引下标来获取,这个下标是从 1 开始。
  • XXX getXXX(String columnLabel) throws SQLException; 从结果集对象中获取需要的数据,是通过列的名称来获取
  • void close() throws SQLException; 关闭结果集资源方法

2.7 PreparedStatement

PreparedStatement 是 Statement 的子类,它的一大好处是可以防止 SQL 注入。

Statement是在执行时才传入 SQL 语句,而 PreparedStatement 是在创建这个对象时就已经把 SQL 语句给它了。

如果 SQL 中需要条件,是通过 ? 号来进行占位,然后需要通过这个对象提供的一系列的 set 方法来设置这些参数,从而就避免了 SQL 注入。

3. 使用 JDBC来实现 CRUD

CRUD是增、删、改、查的简写。

C: create,就是增加数据,或叫插入数据

R:read,就是查询,也是 query

U:update,就是修改

D:delete, 就是删除

3.1 插入数据

我们还以表 tb_user 为例来进行演示

3.1.1 编写 User 实体类

package com.xianopeng.entity;

/**
 * @Description TODO
 * @Author Jock
 * @Date 2022/2/23
 */
public class User {
    private int id;
    private String name;
    private int age;

    public User() {
    }

    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    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;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

上面实体类中大部分的代码都是封装的代码,我们可以使用 Lombok 来简化这部分代码的编写。

在 IDEA 2020 版本后就已经集成了 Lombok 插件,我们只需要在项目中添加 lombok 的依赖就可以在项目中使用了。

在项目的 pom.xml 文件中添加 lombok 的依赖。

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
    <scope>provided</scope>
</dependency>

在项目中引入这个插件后,我们就可以把实体类简化为如下:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Accessors(chain = true) // 用于链式编程
public class User {
    private int id;
    private String name;
    private int age;
}

3.1.2 编写Dao 接口

UserDao.java

package com.xianopeng.entity;

public interface UserDao {
    // 插入数据
    void insert(User user) throws Exception;
}

3.1.3 编写Dao实现

UserDaoImpl.java

package com.xianopeng.entity;

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

public class UserDaoImpl implements UserDao {
    @Override
    public void insert(User user) throws Exception {
        // 1. 加载驱动器类
        Class.forName("com.mysql.jdbc.Driver");

        // 2. 创建 Connnection
        String url = "jdbc:mysql://192.168.72.71:3306/mydb?useSSL=false&characterEncoding=utf8";
        String username = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url, username, password);
        // 3. 创建 PreparedStatement 对象
        String sql = "INSERT INTO tb_user(name, age) VALUES(?, ?)";
        PreparedStatement pstm = conn.prepareStatement(sql);
        // 4. 设置参数
        pstm.setString(1, user.getName());
        pstm.setInt(2, user.getAge());
        // 5. 执行SQL
        int r = pstm.executeUpdate();
        // 输出
        System.out.println(r);
        // 6. 关闭资源,要返着关
        pstm.close();
        conn.close();
    }
}

3.1.4 编写测试

在 src 目录下的 test 目录中的 java 目录下,创建 com.xianopeng 包, 然后创建 UserDaoImplTest.java 类,代码如下:

package com.xianopeng;

import com.xianopeng.entity.User;
import com.xianopeng.entity.UserDao;
import com.xianopeng.entity.UserDaoImpl;
import org.junit.Test;

public class UserDaoImplTest {
    // 测试插入数据
    @Test
    public void testInsert() throws Exception {
        User user = new User()
                .setName("张飞")
                .setAge(19);

        UserDao userDao = new UserDaoImpl();
        userDao.insert(user);

    }
}

在上没的代码中,我们使用了 junit 来进行测试,因此我们需要把 junit 的依赖添加到 pom.xml 文件。

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

3.2 修改数据

3.2.1 修改接口

在 UserDao 接口中添加修改数据的方法。

public interface UserDao {
    // 插入数据
    void insert(User user) throws Exception;
    // 修改数据
    int update(User user) throws Exception;
}

3.2.2 修改实现

在 UserDaoImpl 类中添加 update 实现的方法,代码如下:

    // 修改数据
    @Override
    public int update(User user) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
        String sql = "UPDATE tb_user SET name=?,age=? WHERE id=?";
        PreparedStatement pstm = conn.prepareStatement(sql);
        pstm.setString(1, user.getName());
        pstm.setInt(2, user.getAge());
        pstm.setInt(3, user.getId());
        int r = pstm.executeUpdate();
        pstm.close();
        conn.close();
        return r;
    }

3.2.3 添加测试方法

编写测试修改数据的测试方式。

package com.xianopeng;

import com.xianopeng.entity.User;
import com.xianopeng.entity.UserDao;
import com.xianopeng.entity.UserDaoImpl;
import org.junit.Before;
import org.junit.Test;

/**
 * @Description TODO
 * @Author Jock
 * @Date 2022/2/23
 */
public class UserDaoImplTest {
    UserDao userDao;
    @Before
    public void init() {
        userDao = new UserDaoImpl();
    }

    // 测试修改
    @Test
    public void testUpdate() throws Exception {
        User user = new User()
                .setId(1)
                .setName("赵云")
                .setAge(21);
        userDao.update(user);
    }
    // 测试插入数据
    @Test
    public void testInsert() throws Exception {
        User user = new User()
                .setName("张飞")
                .setAge(19);

        //UserDao userDao = new UserDaoImpl();
        userDao.insert(user);

    }
}

3.3 删除数据

3.3.1 修改接口

public interface UserDao {
    // 插入数据
    void insert(User user) throws Exception;
    // 修改数据
    int update(User user) throws Exception;
    // 删除数据
    int delete(int id) throws Exception;
}

3.3.2 修改实现

    // 删除数据
    @Override
    public int delete(int id) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
        String sql = "DELETE FROM tb_user WHERE id=?";
        PreparedStatement pstm = conn.prepareStatement(sql);
        pstm.setInt(1, id);
        int r = pstm.executeUpdate();
        return r;
    }

3.3.3 编写测试方法

    // 删除数据
    @Test
    public void testDelete() throws Exception {
        int id = 4;
        userDao.delete(id);
    }

3.4 封装通用方法

经过上面的插入数据、修改数据和删除数据的方法的实现,我们发现这三个方法的代码都基本相同,不同的地方在于:SQL语句不同,设置的参数个数不同。

3.4.1 封装方法

为了让方法具有通用性,我们对其进行封装,封装的代码如下:

    /**
     * 封装的通用增删改方法
     * @param sql 需要操作的SQL语句
     * @param params 执行SQL语句时需要的参数
     */
    public void update(String sql, Object... params) {
        Connection conn = null;
        PreparedStatement pstm = null;
        try {
            // 1. 加载驱动器类
            Class.forName("com.mysql.jdbc.Driver");
            // 2. 创建连接对象
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            // 3. 创建执行SQL语句对象
            pstm = conn.prepareStatement(sql);
            // 4. 设置参数
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
            // 5. 执行SQL语句
            pstm.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6. 释放资源
            if (pstm != null) {
                try {
                    pstm.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

3.4.2 修改Dao实现

UserDaoImpl.java

package com.xianopeng.entity;

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

/**
 * @Description TODO
 * @Author Jock
 * @Date 2022/2/23
 */
public class UserDaoImpl implements UserDao {
    private final String URL = "jdbc:mysql://192.168.72.71:3306/mydb?useSSL=false&characterEncoding=utf8";
    private final String USERNAME = "root";
    private final String PASSWORD = "123456";
    @Override
    public void insert(User user) throws Exception {
        update("INSERT INTO tb_user(name, age) VALUES(?, ?)", user.getName(), user.getAge());
    }

    // 修改数据
    @Override
    public int update(User user) throws Exception {
        return update("UPDATE tb_user SET name=?,age=? WHERE id=?",
                user.getName(),user.getAge(),user.getId());
    }

    // 删除数据
    @Override
    public int delete(int id) throws Exception {
        return update("DELETE FROM tb_user WHERE id=?", id);
    }

    /**
     * 封装的通用增删改方法
     * @param sql 需要操作的SQL语句
     * @param params 执行SQL语句时需要的参数
     */
    public int update(String sql, Object... params) {
        Connection conn = null;
        PreparedStatement pstm = null;
        try {
            // 1. 加载驱动器类
            Class.forName("com.mysql.jdbc.Driver");
            // 2. 创建连接对象
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            // 3. 创建执行SQL语句对象
            pstm = conn.prepareStatement(sql);
            // 4. 设置参数
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
            // 5. 执行SQL语句
            return pstm.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        } finally {
            // 6. 释放资源
            if (pstm != null) {
                try {
                    pstm.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }


}

3.5 编写工具类

为了代码重用,我们可以编写一个连接数据库的工具类,这样的话,一方面它的职责更加的明确,另一方面它的代码重用性更好。

3.5.1 编写工具类

JdbcUtil01.java

package com.xianopeng.utils;

import lombok.extern.slf4j.Slf4j;

import java.sql.*;

/**
 * @Description 连接数据库的工具类
 * @Author Jock
 * @Date 2022/2/23
 */
@Slf4j
public final class JdbcUtil01 {
    // 定义连接数据库的几个参数
    private static final String URL = "jdbc:mysql://192.168.72.71:3306/mydb?useSSL=false&characterEncoding=utf8";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "123456";

    private JdbcUtil01() {
    }

    // 加载驱动类,这个加载操作只在项目中执行一次即可,无须返回加载
    static {
        try {
            // 1. 加载驱动器类
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            log.info("加载驱动器类时出错,错误信息为:" + e.getMessage());
            throw new RuntimeException("加载驱动器类时出错,错误信息为:" + e.getMessage());
        }
    }

    /**
     * 封装的通用增删改方法
     * @param sql 需要操作的SQL语句
     * @param params 执行SQL语句时需要的参数
     */
    public static int update(String sql, Object... params) {
        Connection conn = null;
        PreparedStatement pstm = null;
        try {
            // 2. 创建连接对象
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            // 3. 创建执行SQL语句对象
            pstm = conn.prepareStatement(sql);
            // 4. 设置参数
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
            // 5. 执行SQL语句
            return pstm.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        } finally {
            // 6. 释放资源
            destroy(conn, pstm, null);
        }
    }

    /**
     * 通用的释放资源的方法
     * @param conn 连接对象
     * @param pstm 执行语句对象
     * @param rs 结果集对象
     */
    public static void destroy(Connection conn, PreparedStatement pstm, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                log.info("释放 ResultSet 对象时出错,错误信息为:" + e.getMessage());
            }
        }
        if (pstm != null) {
            try {
                pstm.close();
            } catch (SQLException e) {
                log.info("释放 PreparedStatement 对象时出错,错误信息为:" + e.getMessage());
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                log.info("释放 Connection 对象时出错,错误信息为:" + e.getMessage());
            }
        }
    }

}

在这个工具类中,包括加载驱动器类,包含通知增删改方法,也包括释放资源的方法。

3.5.2 修改实现类

UserDaoImpl.java

package com.xianopeng.dao.impl;

import com.xianopeng.dao.UserDao;
import com.xianopeng.entity.User;
import com.xianopeng.utils.JdbcUtil01;

/**
 * @Description TODO
 * @Author Jock
 * @Date 2022/2/23
 */
public class UserDaoImpl implements UserDao {
    @Override
    public void insert(User user) throws Exception {
        JdbcUtil01.update("INSERT INTO tb_user(name, age) VALUES(?, ?)", user.getName(), user.getAge());
    }

    // 修改数据
    @Override
    public int update(User user) throws Exception {
        return JdbcUtil01.update("UPDATE tb_user SET name=?,age=? WHERE id=?",
                user.getName(),user.getAge(),user.getId());
    }

    // 删除数据
    @Override
    public int delete(int id) throws Exception {
        return JdbcUtil01.update("DELETE FROM tb_user WHERE id=?", id);
    }




}

4. 查询

4.1 查询多条数据

我们希望查询出 tb_user表中所有的数据

4.1.1 修改Dao接口

在 UserDao 接口类中添加如下的方法。

public interface UserDao {
    // 插入数据
    void insert(User user) throws Exception;
    // 修改数据
    int update(User user) throws Exception;
    // 删除数据
    int delete(int id) throws Exception;
    // 查询全部数据
    List<User> query();
}

4.1.2 修改Dao实现类

在 UserDaoImpl 类中实现查询全部数据的方法。

    @Override
    public List<User> query() {
        Connection conn = null;
        PreparedStatement pstm = null;
        ResultSet rs = null;
        List<User> users = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            Properties info;
            conn = DriverManager.getConnection("jdbc:mysql://192.168.72.71:3306/mydb?useSSL=false&characterEncoding=utf8", "root", "123456");
            pstm = conn.prepareStatement("SELECT id, name, age FROM tb_user");
            rs = pstm.executeQuery();
            users = new ArrayList<>();
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                User user = new User()
                        .setId(id)
                        .setName(name)
                        .setAge(age);
                users.add(user);
            }
            return users;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil01.destroy(conn, pstm, rs);
        }
        return users;
    }

4.1.3 编写测试

    @Test
    public void testQuery() {
        List<User> users = userDao.query();
        for (User user : users) {
            System.out.println(user);
        }
    }

4.2 分页查询

4.2.1 修改Dao接口

public interface UserDao {
    // 插入数据
    void insert(User user) throws Exception;
    // 修改数据
    int update(User user) throws Exception;
    // 删除数据
    int delete(int id) throws Exception;
    // 查询全部数据
    List<User> query();
    // 分页查询
    List<User> query(int offset, int pageSize);
}

4.2.2 修改Dao实现

    /**
     * 分页查询
     * @param page 查询的页码
     * @param pageSize 每页显示记录数
     * @return 返回的结果
     */
    @Override
    public List<User> query(int page, int pageSize) {
        Connection conn = null;
        PreparedStatement pstm = null;
        ResultSet rs = null;
        List<User> users = null;

        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://192.168.72.71:3306/mydb?useSSL=false&characterEncoding=utf8", "root", "123456");
            pstm = conn.prepareStatement("SELECT id,name,age FROM tb_user LIMIT ?, ?");
            pstm.setInt(1, (page - 1) * pageSize);
            pstm.setInt(2, pageSize);
            rs = pstm.executeQuery();
            users = new ArrayList<>();
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                User user = new User()
                        .setId(id)
                        .setName(name)
                        .setAge(age);
                users.add(user);
            }
            return users;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil01.destroy(conn, pstm, rs);
        }
        return users;
    }

4.2.3 编写测试

    // 分页查询
    @Test
    public void testQueryLimit() {
        List<User> users = userDao.query(2, 2);
        for (User user : users) {
            System.out.println(user);
        }
    }

4.3 单条数据查询

4.3.1 修改Dao接口

public interface UserDao {
    // 插入数据
    void insert(User user) throws Exception;
    // 修改数据
    int update(User user) throws Exception;
    // 删除数据
    int delete(int id) throws Exception;
    // 查询全部数据
    List<User> query();
    // 分页查询
    List<User> query(int page, int pageSize);
    // 查询单数据
    User select(int id);
}

4.3.2 修改Dao实现

   /**
     * 查询单条数据
     * @param id 主键
     * @return 返回对象
     */
    @Override
    public User select(int id) {
        Connection conn = null;
        PreparedStatement pstm = null;
        ResultSet rs = null;
        User user = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://192.168.72.71:3306/mydb?useSSL=false&characterEncoding=utf8", "root", "123456");
            pstm = conn.prepareStatement("SELECT id,name,age FROM tb_user WHERE id=?");
            pstm.setInt(1, id);
            rs = pstm.executeQuery();
            if (rs.next()) {
                int _id = rs.getInt("id");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                
                user = new User()
                        .setId(id)
                        .setName(name)
                        .setAge(age);
            }
            return user;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil01.destroy(conn, pstm, rs);
        }
        return user;
    }

4.3.3 编写测试

    // 单条数据查询
    @Test
    public void testSelect() {
        User user = userDao.select(2);
        System.out.println(user);
    }

4.4 查询总记录数

4.4.1 修改Dao接口

public interface UserDao {
    // 插入数据
    void insert(User user) throws Exception;
    // 修改数据
    int update(User user) throws Exception;
    // 删除数据
    int delete(int id) throws Exception;
    // 查询全部数据
    List<User> query();
    // 分页查询
    List<User> query(int page, int pageSize);
    // 查询单数据
    User select(int id);
    // 查询总记录数
    int count();
}

4.4.2 修改Dao实现

    /**
     * 查询总记录数
     * @return
     */
    @Override
    public int count() {
        Connection conn = null;
        PreparedStatement pstm = null;
        ResultSet rs = null;
        int count = 0;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://192.168.72.71:3306/mydb?useSSL=false&characterEncoding=utf8", "root", "123456");
            //pstm = conn.prepareStatement("SELECT count(*) as num FROM tb_user");
            pstm = conn.prepareStatement("SELECT count(*) FROM tb_user");
            rs = pstm.executeQuery();
            if (rs.next()) {
                //count = rs.getInt("num");
                count = rs.getInt(1);
            }
            return count;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil01.destroy(conn, pstm, rs);
        }
        return count;
    }

4.4.3 编写测试

    // 查询总记录数
    @Test
    public void testCount() {
        int count = userDao.count();
        System.out.println(count);
    }

4.5 查询封装

4.5.1 封装方法

在 JdbcUtil01.java 类中封装查询多条数据的方法,代码如下:

    /**
     * 查询多条数据的封装方法
     * @param clazz 结果的封装对象
     * @param sql 要执行的SQL语句
     * @param params 执行SQL语句时需要的参数
     * @param <T> 消费金融泛型类型
     * @return 返回查询的结果集
     */
    public static <T> List<T> query(Class<T> clazz, String sql, Object...params) {
        Connection conn = null;
        PreparedStatement pstm = null;
        ResultSet rs = null;
        List<T> entities = null;

        try {
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            pstm = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
            rs = pstm.executeQuery();
            entities = new ArrayList<>();
            // 获取 ResultSet 对象来获取元数据信息
            ResultSetMetaData metaData = rs.getMetaData();
            // 获取SQL语名中字段的个数
            int columnCount = metaData.getColumnCount();
            // 处理结果集
            while (rs.next()) {
                // 通过反射来实例化对象
                //T entity = clazz.newInstance();
                T entity = clazz.getDeclaredConstructor().newInstance();

                for (int i = 1; i <= columnCount; i++) {
                    // getColumnName() 是获取表中的字段名称 SELECT id,name,age FROM xxx
                    //metaData.getColumnName();
                    // getColumnLabel() 是获取SQL中的字段名称 SELECT id,name as n,age FROM xxx
                    // 获取字段的名称
                    String columnLabel = metaData.getColumnLabel(i);
                    // 通过反射获取对象的字段
                    Field field = clazz.getDeclaredField(columnLabel);
                    // 给这个字段设置值
                    field.setAccessible(true);
                    field.set(entity, rs.getObject(columnLabel));
                }
                entities.add(entity);
            }
            return entities;
        } catch (Exception e) {
            throw new RuntimeException("查询多条数据时出错,错误信息为:" + e.getMessage());
        } finally {
            destroy(conn, pstm, rs);
        }
    }

4.5.2 修改Dao实现类

    // 查询全部
    @Override
    public List<User> query() {
        return JdbcUtil01.query(User.class, "SELECT * FROM tb_user");
    }
    /**
     * 分页查询
     * @param page 查询的页码
     * @param pageSize 每页显示记录数
     * @return 返回的结果
     */
    @Override
    public List<User> query(int page, int pageSize) {
        return JdbcUtil01.query(User.class, "SELECT * FROM tb_user LIMIT ?,?",(page - 1) * pageSize, pageSize);
    }
    /**
     * 查询单条数据
     * @param id 主键
     * @return 返回对象
     */
    @Override
    public User select(int id) {
        return JdbcUtil01.query(User.class, "SELECT id,name,age FROM tb_user WHERE id=?", id).get(0);
    }

4.5.2 封装查询统计的方法

    /**
     * 查询多条数据的封装方法
     * @param clazz 结果的封装对象
     * @param sql 要执行的SQL语句
     * @param params 执行SQL语句时需要的参数
     * @param <T> 消费金融泛型类型
     * @return 返回查询的结果
     */
    public static <T> T queryForObject(Class<T> clazz, String sql, Object...params) {
        Connection conn = null;
        PreparedStatement pstm = null;
        ResultSet rs = null;
        T entity = null;

        try {
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            pstm = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
            rs = pstm.executeQuery();
            ResultSetMetaData metaData = rs.getMetaData();
            while (rs.next()) {
                if (clazz == Integer.class) {
                    Constructor<T> constructor = clazz.getDeclaredConstructor(int.class);
                    entity = constructor.newInstance(rs.getInt(1));
                } else if (clazz == Long.class) {
                    Constructor<T> constructor = clazz.getDeclaredConstructor(long.class);
                    entity = constructor.newInstance(rs.getLong(1));
                } else {
                    throw new RuntimeException("参数中能传 int 类型或 long 类型。");
                }
            }

            return entity;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("统计查询时出错,错误信息为:" + e.getMessage());
        } finally {
            destroy(conn, pstm, rs);
        }
    }

4.5.4 修改Dao实现类

    /**
     * 查询总记录数
     * @return
     */
    @Override
    public int count() {
        return JdbcUtil01.queryForObject(Integer.class, "SELECT count(*) FROM tb_user");
    }

5. 调整工具类

在工具类中,我们把连接数据库的信息以硬编码的方式书写在了 Java 类中,这种方式是不推荐,在实际开发的项目中,我们通过都是把连接数据库的信息写在一个单独的文件中,例如 db.properties 文件中。

5.1 创建配置文件

在项目的 src 下的 main 下的 resources 目录中创建 db.properties 文件,内容如下:

jdbc.url=jdbc:mysql://192.168.72.71:3306/mydb?useSSL=false&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
jdbc.driver=com.mysql.jdbc.Driver

5.2. 使用这个文件

在连接数据库的工具类中(JdbcUtil.java)类中我们去使用这个配置文件。来读取文件中的内容。

package com.xianopeng.utils;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @Description 连接数据库的工具类
 * @Author Jock
 * @Date 2022/2/23
 */
@Slf4j
public final class JdbcUtil {
    // 定义连接数据库的几个参数
    private static final String DRIVER;
    private static final String URL;
    private static final String USERNAME;
    private static final String PASSWORD;

    private JdbcUtil() {
    }

    // 加载驱动类,这个加载操作只在项目中执行一次即可,无须返回加载
    static {
        try {
            // 读取db.properties配置文件
            InputStream is = JdbcUtil.class.getClassLoader().getResourceAsStream("db.properties");
            Properties prop = new Properties();
            prop.load(is);
            DRIVER = prop.getProperty("jdbc.driver");
            URL = prop.getProperty("jdbc.url");
            USERNAME = prop.getProperty("jdbc.username");
            PASSWORD = prop.getProperty("jdbc.password");
        } catch (IOException e) {
            log.info("加载配置文件出错,错误信息为:" + e.getMessage());
            throw new RuntimeException("加载配置文件出错,错误信息为:" + e.getMessage());
        }

        try {
            // 1. 加载驱动器类
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            log.info("加载驱动器类时出错,错误信息为:" + e.getMessage());
            throw new RuntimeException("加载驱动器类时出错,错误信息为:" + e.getMessage());
        }
    }

    public static Connection getConnection() {
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            return conn;
        } catch (SQLException e) {
            throw new RuntimeException("创建连接对象时出错,错误信息为:" + e.getMessage());
        }
    }

    /**
     * 封装的通用增删改方法
     * @param sql 需要操作的SQL语句
     * @param params 执行SQL语句时需要的参数
     */
    public static int update(String sql, Object... params) {
        Connection conn = null;
        PreparedStatement pstm = null;
        try {
            // 2. 创建连接对象
            conn = getConnection();
            // 3. 创建执行SQL语句对象
            pstm = conn.prepareStatement(sql);
            // 4. 设置参数
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
            // 5. 执行SQL语句
            return pstm.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        } finally {
            // 6. 释放资源
            destroy(conn, pstm, null);
        }
    }

    /**
     * 查询多条数据的封装方法
     * @param clazz 结果的封装对象
     * @param sql 要执行的SQL语句
     * @param params 执行SQL语句时需要的参数
     * @param <T> 消费金融泛型类型
     * @return 返回查询的结果集
     */
    public static <T> List<T> query(Class<T> clazz, String sql, Object...params) {
        Connection conn = null;
        PreparedStatement pstm = null;
        ResultSet rs = null;
        List<T> entities = null;

        try {
            conn = getConnection();
            pstm = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
            rs = pstm.executeQuery();
            entities = new ArrayList<>();
            // 获取 ResultSet 对象来获取元数据信息
            ResultSetMetaData metaData = rs.getMetaData();
            // 获取SQL语名中字段的个数
            int columnCount = metaData.getColumnCount();
            // 处理结果集
            while (rs.next()) {
                // 通过反射来实例化对象
                //T entity = clazz.newInstance();
                T entity = clazz.getDeclaredConstructor().newInstance();

                for (int i = 1; i <= columnCount; i++) {
                    // getColumnName() 是获取表中的字段名称 SELECT id,name,age FROM xxx
                    //metaData.getColumnName();
                    // getColumnLabel() 是获取SQL中的字段名称 SELECT id,name as n,age FROM xxx
                    // 获取字段的名称
                    String columnLabel = metaData.getColumnLabel(i);
                    // 通过反射获取对象的字段
                    Field field = clazz.getDeclaredField(columnLabel);
                    // 给这个字段设置值
                    field.setAccessible(true);
                    field.set(entity, rs.getObject(columnLabel));
                }
                entities.add(entity);
            }
            return entities;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("查询多条数据时出错,错误信息为:" + e.getMessage());
        } finally {
            destroy(conn, pstm, rs);
        }
    }

    /**
     * 查询多条数据的封装方法
     * @param clazz 结果的封装对象
     * @param sql 要执行的SQL语句
     * @param params 执行SQL语句时需要的参数
     * @param <T> 消费金融泛型类型
     * @return 返回查询的结果
     */
    public static <T> T queryForObject(Class<T> clazz, String sql, Object...params) {
        Connection conn = null;
        PreparedStatement pstm = null;
        ResultSet rs = null;
        T entity = null;

        try {
            conn = getConnection();
            pstm = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
            rs = pstm.executeQuery();
            ResultSetMetaData metaData = rs.getMetaData();
            while (rs.next()) {
                if (clazz == Integer.class) {
                    Constructor<T> constructor = clazz.getDeclaredConstructor(int.class);
                    entity = constructor.newInstance(rs.getInt(1));
                } else if (clazz == Long.class) {
                    Constructor<T> constructor = clazz.getDeclaredConstructor(long.class);
                    entity = constructor.newInstance(rs.getLong(1));
                } else {
                    throw new RuntimeException("参数中能传 int 类型或 long 类型。");
                }
            }

            return entity;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("统计查询时出错,错误信息为:" + e.getMessage());
        } finally {
            destroy(conn, pstm, rs);
        }
    }

    /**
     * 通用的释放资源的方法
     * @param conn 连接对象
     * @param pstm 执行语句对象
     * @param rs 结果集对象
     */
    public static void destroy(Connection conn, PreparedStatement pstm, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                log.info("释放 ResultSet 对象时出错,错误信息为:" + e.getMessage());
            }
        }
        if (pstm != null) {
            try {
                pstm.close();
            } catch (SQLException e) {
                log.info("释放 PreparedStatement 对象时出错,错误信息为:" + e.getMessage());
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                log.info("释放 Connection 对象时出错,错误信息为:" + e.getMessage());
            }
        }
    }

}

6. 使用连接池

在 Java 中常用的连接池有 C3P0、DBCP、Druid 等。它们都免费开源的产品。

我们在开发中,使用最多的就是 Druid ,因为它是阿里开源的产品。

好处:

  • 它的帮助文档是中文,便于查看
  • 它是经过阿里大量数据的使用后的产品,必能好

6.1 添加依赖

把 druid 依赖添加到项目的 pom.xml 文件中

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version>
</dependency>

6.2 修改连接数据库工具类

package com.xianopeng.utils;

import com.alibaba.druid.pool.DruidDataSource;
import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @Description 连接数据库的工具类
 * @Author Jock
 * @Date 2022/2/23
 */
@Slf4j
public final class JdbcUtil {
    // 定义连接数据库的几个参数
    private static final String DRIVER;
    private static final String URL;
    private static final String USERNAME;
    private static final String PASSWORD;

    private JdbcUtil() {
    }

    // 加载驱动类,这个加载操作只在项目中执行一次即可,无须返回加载
    static {
        try {
            // 读取db.properties配置文件
            InputStream is = JdbcUtil.class.getClassLoader().getResourceAsStream("db.properties");
            Properties prop = new Properties();
            prop.load(is);
            DRIVER = prop.getProperty("jdbc.driver");
            URL = prop.getProperty("jdbc.url");
            USERNAME = prop.getProperty("jdbc.username");
            PASSWORD = prop.getProperty("jdbc.password");
        } catch (IOException e) {
            log.info("加载配置文件出错,错误信息为:" + e.getMessage());
            throw new RuntimeException("加载配置文件出错,错误信息为:" + e.getMessage());
        }
    }

    /**
     * 获取数据源
     * @return
     */
    public static DataSource getDataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(DRIVER);
        ds.setUrl(URL);
        ds.setUsername(USERNAME);
        ds.setPassword(PASSWORD);
        return ds;
    }

    public static Connection getConnection() {
        try {
            return getDataSource().getConnection();
        } catch (SQLException e) {
            throw new RuntimeException("创建连接对象时出错,错误信息为:" + e.getMessage());
        }
    }

    /**
     * 封装的通用增删改方法
     * @param sql 需要操作的SQL语句
     * @param params 执行SQL语句时需要的参数
     */
    public static int update(String sql, Object... params) {
        Connection conn = null;
        PreparedStatement pstm = null;
        try {
            // 2. 创建连接对象
            conn = getConnection();
            // 3. 创建执行SQL语句对象
            pstm = conn.prepareStatement(sql);
            // 4. 设置参数
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
            // 5. 执行SQL语句
            return pstm.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        } finally {
            // 6. 释放资源
            destroy(conn, pstm, null);
        }
    }

    /**
     * 查询多条数据的封装方法
     * @param clazz 结果的封装对象
     * @param sql 要执行的SQL语句
     * @param params 执行SQL语句时需要的参数
     * @param <T> 消费金融泛型类型
     * @return 返回查询的结果集
     */
    public static <T> List<T> query(Class<T> clazz, String sql, Object...params) {
        Connection conn = null;
        PreparedStatement pstm = null;
        ResultSet rs = null;
        List<T> entities = null;

        try {
            conn = getConnection();
            pstm = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
            rs = pstm.executeQuery();
            entities = new ArrayList<>();
            // 获取 ResultSet 对象来获取元数据信息
            ResultSetMetaData metaData = rs.getMetaData();
            // 获取SQL语名中字段的个数
            int columnCount = metaData.getColumnCount();
            // 处理结果集
            while (rs.next()) {
                // 通过反射来实例化对象
                //T entity = clazz.newInstance();
                T entity = clazz.getDeclaredConstructor().newInstance();

                for (int i = 1; i <= columnCount; i++) {
                    // getColumnName() 是获取表中的字段名称 SELECT id,name,age FROM xxx
                    //metaData.getColumnName();
                    // getColumnLabel() 是获取SQL中的字段名称 SELECT id,name as n,age FROM xxx
                    // 获取字段的名称
                    String columnLabel = metaData.getColumnLabel(i);
                    // 通过反射获取对象的字段
                    Field field = clazz.getDeclaredField(columnLabel);
                    // 给这个字段设置值
                    field.setAccessible(true);
                    field.set(entity, rs.getObject(columnLabel));
                }
                entities.add(entity);
            }
            return entities;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("查询多条数据时出错,错误信息为:" + e.getMessage());
        } finally {
            destroy(conn, pstm, rs);
        }
    }

    /**
     * 查询多条数据的封装方法
     * @param clazz 结果的封装对象
     * @param sql 要执行的SQL语句
     * @param params 执行SQL语句时需要的参数
     * @param <T> 消费金融泛型类型
     * @return 返回查询的结果
     */
    public static <T> T queryForObject(Class<T> clazz, String sql, Object...params) {
        Connection conn = null;
        PreparedStatement pstm = null;
        ResultSet rs = null;
        T entity = null;

        try {
            conn = getConnection();
            pstm = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
            rs = pstm.executeQuery();
            ResultSetMetaData metaData = rs.getMetaData();
            while (rs.next()) {
                if (clazz == Integer.class) {
                    Constructor<T> constructor = clazz.getDeclaredConstructor(int.class);
                    entity = constructor.newInstance(rs.getInt(1));
                } else if (clazz == Long.class) {
                    Constructor<T> constructor = clazz.getDeclaredConstructor(long.class);
                    entity = constructor.newInstance(rs.getLong(1));
                } else {
                    throw new RuntimeException("参数中能传 int 类型或 long 类型。");
                }
            }

            return entity;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("统计查询时出错,错误信息为:" + e.getMessage());
        } finally {
            destroy(conn, pstm, rs);
        }
    }

    /**
     * 通用的释放资源的方法
     * @param conn 连接对象
     * @param pstm 执行语句对象
     * @param rs 结果集对象
     */
    public static void destroy(Connection conn, PreparedStatement pstm, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                log.info("释放 ResultSet 对象时出错,错误信息为:" + e.getMessage());
            }
        }
        if (pstm != null) {
            try {
                pstm.close();
            } catch (SQLException e) {
                log.info("释放 PreparedStatement 对象时出错,错误信息为:" + e.getMessage());
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                log.info("释放 Connection 对象时出错,错误信息为:" + e.getMessage());
            }
        }
    }

}

在这个类中,我们增加了 getDataSource() 方法用于创建连接池对象。然后在 geConection() 方法中从连接池中获取连接对象。而静态代码块中的 加载驱动器类的代码就不再需要,因为这部分的功能已经放到的连接池对象中了。

其它地方的代码无须做任何的修改即可使用。

7. ThreadLocal

在 JDK1.2 的版本中已经提供了 ThreadLocal对象了,它是为了解决多线程程序并发问题而产生。使用这个类非常的方便。

这个类提供了以下几个静态方法:

  • get() :用于获取ThreadLocal 中当前线程共享变量的值。
  • set():设置ThreadLocal中当前线程共享的变量的值。
  • remove():移除 ThreadLocal中当前线程共享的变量的值。
  • initialValue:ThreadLocal没有被当前线程赋值时调用当前线程的remove方法后再调用 get 方法时就会返回这个值。

我们来修改 JdbcUtil 这个工具类,让它支持 ThreadLocal

package com.xianopeng.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @Description 连接数据库的工具类
 * @Author Jock
 * @Date 2022/2/23
 */
@Slf4j
public final class JdbcUtil {
    // 数据源连接池对象
    private static DataSource dataSource;
    // ThreadLocal 对象
    private static ThreadLocal<Connection> threadLocal;

    private JdbcUtil() {
    }

    // 加载驱动类,这个加载操作只在项目中执行一次即可,无须返回加载
    static {
        try {
            // 读取db.properties配置文件
            InputStream is = JdbcUtil.class.getClassLoader().getResourceAsStream("db.properties");
            Properties prop = new Properties();
            prop.load(is);

            // 使用Druid连接池工厂方式
            dataSource = DruidDataSourceFactory.createDataSource(prop);
            threadLocal = new ThreadLocal<>();
        } catch (Exception e) {
            log.info("加载配置文件出错,错误信息为:" + e.getMessage());
            throw new RuntimeException("加载配置文件出错,错误信息为:" + e.getMessage());
        }
    }

    /**
     * 获取连接对象
     * @return 返回连接对象
     */
    public static Connection getConnection() {
        // 从当前线程中获取连接
        Connection conn = threadLocal.get();
        if (conn == null) {
            try {
                // 如果没有就从连接池中去获取
                conn = dataSource.getConnection();
                // 放到ThreadLocal中
                threadLocal.set(conn);
            } catch (SQLException e) {
                throw new RuntimeException("创建连接对象时出错,错误信息为:" + e.getMessage());
            }
        }
        return conn;
    }

    /**
     * 封装的通用增删改方法
     * @param sql 需要操作的SQL语句
     * @param params 执行SQL语句时需要的参数
     */
    public static int update(String sql, Object... params) {
        Connection conn = null;
        PreparedStatement pstm = null;
        try {
            // 2. 创建连接对象
            conn = getConnection();
            // 3. 创建执行SQL语句对象
            pstm = conn.prepareStatement(sql);
            // 4. 设置参数
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
            // 5. 执行SQL语句
            return pstm.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        } finally {
            // 6. 释放资源
            destroy(conn, pstm, null);
        }
    }

    /**
     * 查询多条数据的封装方法
     * @param clazz 结果的封装对象
     * @param sql 要执行的SQL语句
     * @param params 执行SQL语句时需要的参数
     * @param <T> 消费金融泛型类型
     * @return 返回查询的结果集
     */
    public static <T> List<T> query(Class<T> clazz, String sql, Object...params) {
        Connection conn = null;
        PreparedStatement pstm = null;
        ResultSet rs = null;
        List<T> entities = null;

        try {
            conn = getConnection();
            pstm = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
            rs = pstm.executeQuery();
            entities = new ArrayList<>();
            // 获取 ResultSet 对象来获取元数据信息
            ResultSetMetaData metaData = rs.getMetaData();
            // 获取SQL语名中字段的个数
            int columnCount = metaData.getColumnCount();
            // 处理结果集
            while (rs.next()) {
                // 通过反射来实例化对象
                //T entity = clazz.newInstance();
                T entity = clazz.getDeclaredConstructor().newInstance();

                for (int i = 1; i <= columnCount; i++) {
                    // getColumnName() 是获取表中的字段名称 SELECT id,name,age FROM xxx
                    //metaData.getColumnName();
                    // getColumnLabel() 是获取SQL中的字段名称 SELECT id,name as n,age FROM xxx
                    // 获取字段的名称
                    String columnLabel = metaData.getColumnLabel(i);
                    // 通过反射获取对象的字段
                    Field field = clazz.getDeclaredField(columnLabel);
                    // 给这个字段设置值
                    field.setAccessible(true);
                    field.set(entity, rs.getObject(columnLabel));
                }
                entities.add(entity);
            }
            return entities;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("查询多条数据时出错,错误信息为:" + e.getMessage());
        } finally {
            destroy(conn, pstm, rs);
        }
    }

    /**
     * 查询多条数据的封装方法
     * @param clazz 结果的封装对象
     * @param sql 要执行的SQL语句
     * @param params 执行SQL语句时需要的参数
     * @param <T> 消费金融泛型类型
     * @return 返回查询的结果
     */
    public static <T> T queryForObject(Class<T> clazz, String sql, Object...params) {
        Connection conn = null;
        PreparedStatement pstm = null;
        ResultSet rs = null;
        T entity = null;

        try {
            conn = getConnection();
            pstm = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
            rs = pstm.executeQuery();
            ResultSetMetaData metaData = rs.getMetaData();
            while (rs.next()) {
                if (clazz == Integer.class) {
                    Constructor<T> constructor = clazz.getDeclaredConstructor(int.class);
                    entity = constructor.newInstance(rs.getInt(1));
                } else if (clazz == Long.class) {
                    Constructor<T> constructor = clazz.getDeclaredConstructor(long.class);
                    entity = constructor.newInstance(rs.getLong(1));
                } else {
                    throw new RuntimeException("参数中能传 int 类型或 long 类型。");
                }
            }

            return entity;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("统计查询时出错,错误信息为:" + e.getMessage());
        } finally {
            destroy(conn, pstm, rs);
        }
    }

    /**
     * 通用的释放资源的方法
     * @param conn 连接对象
     * @param pstm 执行语句对象
     * @param rs 结果集对象
     */
    public static void destroy(Connection conn, PreparedStatement pstm, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                log.info("释放 ResultSet 对象时出错,错误信息为:" + e.getMessage());
            }
        }
        if (pstm != null) {
            try {
                pstm.close();
            } catch (SQLException e) {
                log.info("释放 PreparedStatement 对象时出错,错误信息为:" + e.getMessage());
            }
        }
        if (conn != null) {
            try {
                conn.close();
                // 从 ThreadLocal中删除当前连接对象
                threadLocal.remove();
           } catch (SQLException e) {
                log.info("释放 Connection 对象时出错,错误信息为:" + e.getMessage());
            }
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值