所需要的所有依赖
<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 的实现。
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());
}
}
}
}