业务场景:保存业务数据表的时候,同时记录下日志。
import java.sql.Connection;
import java.sql.DriverManager;
public class DBUtil {
// 数据库配置
private static final String driver = "com.mysql.jdbc.Driver";
private static final String url = "jdbc:mysql://localhost:3306/test";
private static final String username = "root";
private static final String password = "root";
private static Connection conn = null;
public static Connection getDbConnection() {
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
// 关闭数据库连接
public static void closeDbConnection() {
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
import com.example.springboottest.util.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ThreadLocalTest3 {
private static final String UPDATE_SQL = "update t_test set num = ? where id = ?";
private static final String INSERT_SQL = "insert into log (id, desc) values (?, ?)";
public static void main(String[] args) {
ThreadLocalTest3 service = new ThreadLocalTest3();
service.updateBusiness(1, 3000);
}
public void updateBusiness(int id, int num) {
try {
// 获取连接
Connection conn = DBUtil.getDbConnection();
conn.setAutoCommit(false); // 关闭自动提交事务(开启事务)
// 执行操作
updateBizData(conn, UPDATE_SQL, id, num);
// 插入日志
insertLog(conn, INSERT_SQL, "描述");
// 提交事务
conn.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭连接
DBUtil.closeDbConnection();
}
}
private void updateBizData(Connection conn, String updateSQL, int id, int num) throws Exception {
PreparedStatement pstmt = conn.prepareStatement(updateSQL);
pstmt.setInt(1, id);
pstmt.setLong(2, num);
int rows = pstmt.executeUpdate();
if (rows != 0) {
System.out.println("业务数据更新成功!");
}
}
private void insertLog(Connection conn, String insertSQL, String desc) throws Exception {
PreparedStatement pstmt = conn.prepareStatement(insertSQL);
pstmt.setString(1, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()));
pstmt.setString(2, desc);
int rows = pstmt.executeUpdate();
if (rows != 0) {
System.out.println("日志插入成功!");
}
}
}
上面的代码直接运行肯定是没有问题的,但是如果在多线程下面去跑,就会报数据库连接关闭的错误。原因很简单,多线程下执行,可能一个线程刚获取到连接另一个线程就已经把连接关闭了,所以就会导致这样的问题,要解决这个问题,ThreaLocal就是一个很好的选择,只需要改调整下DBUtil里面获取连接的代码即可。如下代码所示:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBUtil {
// 数据库配置
private static final String driver = "com.mysql.jdbc.Driver";
private static final String url = "jdbc:mysql://localhost:3306/test";
private static final String username = "root";
private static final String password = "root";
private static ThreadLocal<Connection> connContainer = new ThreadLocal<Connection>(){
//初始化connContainer里面的值,如果不重写则为null;
@Override
protected Connection initialValue() {
try {
Class.forName(driver);
return DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
};
// 获取连接
public static Connection getDbConnection() {
Connection conn = connContainer.get();
try {
if (conn == null) {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
connContainer.set(conn);
}
return conn;
}
// 关闭连接
public static void closeDbConnection() {
Connection conn = connContainer.get();
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
connContainer.remove();
}
}
}