2020-06-08-JDBC操作數據庫

第一章 JDBC的簡介

1.1 JDBC是什么

JDBC (Java DataBase Connectivity) 是一組用于執行SQL語句的Java API,它由一組用Java語言編寫的類和接口組成,可以為多種關系型數據庫提供統一訪問。他是一種標準,是由oracle公司制定的一套使用Java語言訪問數據庫的標準.

1.2 JDBC原理圖

?

?

</center>

1.3 JDBC提供的常用接口和類

1. DriverManager: 管理數據庫驅動程序,用于建立數據庫連接

?

2. Driver: ? ? ?? 提供給各個數據庫廠商的接口,每一個數據庫廠商要想可以使用Java語言來與他們的數據庫進行通信,必須實現此接口.(主要處理與數據庫服務器之間的通信)


3. Connection: ?? 此接口具有用于聯系數據庫的所有方法

?

4. Statement: ? ? 從此接口創建的對象將SQL語句提交到數據庫

?

5. ResultSet: ? ? 在使用Statement對象執行SQL查詢后,這個對象保存從數據庫檢索的數據

?

6. SQLException: 處理數據庫應用程序中發生的異常

第二章 JDBC入門

以一個用戶表為例來使用JDBC進行增刪該查的操作

數據庫環境搭建

// 1)、創建數據庫

CREATE DATABASE jdbc DEFAULT CHARACTER SET UTF8;

// 2)、切換數據庫

USE jdbc;

// 3)、創建數據庫表

CREATE TABLE user(

?? `user_id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '主鍵',

?? `user_name` VARCHAR(20) NOT NULL COMMENT '用戶名',

?? `price` double(10,2) DEFAULT 0.0 COMMENT '價格',

?? `create_time` DATETIME DEFAULT NULL COMMENT '創建時間'

)ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=UTF8 COMMENT="用戶表";

添加用戶

//①加載數據庫驅動

Class.forName("com.mysql.jdbc.Driver");

//②創建數據庫連接

Connection conn = DriverManager.getConnection("jdbc:mysql:///jdbc?characterEncoding=utf-8","rooe","root");

//③創建Statement對象

Statement statement = conn.createStatement();

//④創建添加SQL語句

String sql="INSERT INTO user(user_name,price,create_time) VALUES('admin',100.12,now())";

//⑤執行sql語句,返回影響行數,如果需要獲取主鍵,要配置Statement.RETURN_GENERATED_KEYS參數

int row = statement.executeUpdate(sql,Statement.RETURN_GENERATED_KEYS);

//⑥打印影響行數

System.out.println("row:"+row);

?

//如果想獲取插入用戶的主鍵可以使用statement.getGeneratedKeys()獲取

?

ResultSet resultSet=statement.getGeneratedKeys();

if(resultSet.next()){

?? int id = resultSet.getInt(1);

}

?

更新用戶

//①加載數據庫驅動

Class.forName("com.mysql.jdbc.Driver");

//②創建數據庫連接

Connection conn = DriverManager.getConnection("jdbc:mysql:///jdbc?characterEncoding=utf-8","rooe","root");

//③創建Statement對象

Statement statement = conn.createStatement();

//④創建更新SQL語句

String sql="UPDATE user SET user_name='admin1',price=12.12,create_time=now() WHERE user_id=100";

//⑤執行sql語句,返回影響行數

int row = statement.executeUpdate(sql);

//⑥打印影響行數

System.out.println("row:"+row);

刪除用戶

//①加載數據庫驅動

Class.forName("com.mysql.jdbc.Driver");

//②創建數據庫連接

Connection conn = DriverManager.getConnection("jdbc:mysql:///jdbc?characterEncoding=utf-8","rooe","root");

//③創建Statement對象

Statement statement = conn.createStatement();

//④創建更新SQL語句

String sql="DELETE FROM user WHERE user_id=100";

//⑤執行sql語句,返回影響行數

int row = statement.executeUpdate(sql);

//⑥打印影響行數

System.out.println("row:"+row);

查詢用戶列表

//創建一個集合對象,將從數據庫查詢出來的數據保存到集合中

List<User> users = new ArrayList<>();

//①加載數據庫驅動

Class.forName("com.mysql.jdbc.Driver");

//②創建數據庫連接

Connection conn = DriverManager.getConnection("jdbc:mysql:///jdbc?characterEncoding=utf-8","rooe","root");

//③創建Statement對象

Statement statement = conn.createStatement();

//④創建SQL語句

String sql="SELECT * FROM user";

//⑤執行sql語句,返回結果集對象

ResultSet result = statement.executeQuery(sql);

while(result.next()){

? ? User user = new User();

? ? int userId = result.getInt("user_id");

? ? user.setUserId(userId);

? ? String userName = result.getString("user_name");

? ? user.setUserName(userName);

? ? double price = result.getDouble("price");

? ? user.setPrice(price);

? ? Date createTime = result.getDate("create_time");

? ? user.setCreateTime(createTime);

? ? users.add(user);

}

//打印從數據庫查詢出來的對象

System.out.println(users);

查詢一個用戶(因為JDBC沒有提供一個查詢單個對象的結果集,所以查詢單個對象時也是使用ResultSet對象,取集合中的一個對象即可)

//創建一個集合對象,將從數據庫查詢出來的數據保存到集合中

List<User> users = new ArrayList<>();

//①加載數據庫驅動

Class.forName("com.mysql.jdbc.Driver");

//②創建數據庫連接

Connection conn = DriverManager.getConnection("jdbc:mysql:///jdbc?characterEncoding=utf-8","root","root");

//③創建Statement對象

Statement statement = conn.createStatement();

//④創建SQL語句

String sql="SELECT * FROM user";

//⑤執行sql語句,返回結果集對象

ResultSet result = statement.executeQuery(sql);

while(result.next()){

? ? User user = new User();

? ? int userId = result.getInt("user_id");

? ? user.setUserId(userId);

? ? String userName = result.getString("user_name");

? ? user.setUserName(userName);

? ? double price = result.getDouble("price");

? ? user.setPrice(price);

? ? Date createTime = result.getDate("create_time");

? ? user.setCreateTime(createTime);

? ? users.add(user);

}

//打印從數據庫查詢出來的對象

System.out.println(users);

查詢數據庫總記錄數

//①加載數據庫驅動

Class.forName("com.mysql.jdbc.Driver");

//②創建數據庫連接

Connection conn = DriverManager.getConnection("jdbc:mysql:///jdbc?characterEncoding=utf-8","root","root");

//③創建Statement對象

Statement statement = conn.createStatement();

//④創建SQL語句

String sql="SELECT COUNT(*) AS count FROM user";

//⑤執行sql語句,返回結果集對象

ResultSet result = statement.executeQuery(sql);

if(result.next()){

? ? int count = result.getInt("count");

? ? System.out.println(count);

}

分頁查詢

MYSQL數據庫分頁的語法

SELECT * FROM user LIMIT [offset],[max];

MYSQL的SQL語句的語法格式帶有兩個參數分別是offset和max,而我們前端或者是客戶端給后臺發送過來的數據為pageNo(當前頁)和pageSize(每頁顯示多少條數),而SQL語句里面的max和我們前端傳送過來的pageSize是相同的都是為每頁顯示的條數。但是offset和pageNo不是相同的所以需要轉換。

轉換的公式:

offset=(pageNo-1)*pageSize

中文亂碼

url:jdbc:mysql:///jdbc?characterEncoding=utf-8&useUnicode=true

第三章 PreparedStatement接口

1. 它是擴展了Statement的接口(PreparedStatement本身也是接口)

2. 功能比Statement更強大

3. 可以動態地提供/接受參數

4. 創建PreparedStatement對象 --> conn.prepareStatement(通過Connection對象獲取)

5. 可以使用占位符(?)來進行數據綁定

6. 通過stmt.setInt(1, 35);綁定參數

添加用戶

//①加載數據庫驅動

Class.forName("com.mysql.jdbc.Driver");

//②創建數據庫連接

Connection conn = DriverManager.getConnection("jdbc:mysql:///jdbc?characterEncoding=utf-8","rooe","root");

//③準備sql,帶有(?)占位符

String sql="INSERT INTO user(user_name,price,create_time) VALUES(?,?,?)";

//④預編譯

PreparedStatement prepareStatement = conn.prepareStatement(sql);

//⑤綁定參數(參數的下標從1開始而不是像數組或者集合從0開始)

prepareStatement.setString(1, "admin");

prepareStatement.setDouble(2, 100.12);

prepareStatement.setDate(3, new Date(new java.util.Date().getTime()));

//⑥執行進行綁定參數之后的sql語句

int row = prepareStatement.executeUpdate();

System.out.println("row:"+row);

其余(刪改查)操作自己完成

第四章 JDBC的事務處理

jdbc默認情況下每執行完一條SQL就會提交到數據庫一次,因為jdbc默認提交事務的方式是自動提交,但是在正常的業務邏輯下,一個業務正常要執行多條sql語句,如果每執行完一條sql都進行事務提交就很容易出現前后數據不一致的問題,所以要保證這一組的sql語句操作都在一個事務管理中,所以我們要取消JDBC的默認事務提交

1. 使用Connection對象的setAutoCommit()方法關閉他的自動提交

2. 手動提交事務調用connection的commit()方法

3. 事務回滾調用connection的rollback()方法

第五章 JDBC批量處理

使用JDBC進行批處理的對象有兩個:

Statement?

PreparedStatement

Statement

介紹

①使用Statement對象批量添加要執行的SQL語句,事例如下:

?

? statement.addBatch(sql1);

? statement.addBatch(sql2);

? statement.addBatch(sql3);

? statement.addBatch(sql4);

②執行批處理SQL語句:statement.executeBatch();

③清除批處理命令:statement.clearBatch();

代碼

Statement statement = connection.createStatement();

//批量準備SQL

String sql1="INSERT INTO user(user_name,price,create_time) VALUES('AA',12.12,NOW())";

String sql2="INSERT INTO user(user_name,price,create_time) VALUES('BB',13.12,NOW())";

String sql3="INSERT INTO user(user_name,price,create_time) VALUES('CC',14.12,NOW())";

String sql4="DELETE FROM user WHERE user_id=100";

//批量添加SQL

statement.addBatch(sql1);

statement.addBatch(sql2);

statement.addBatch(sql3);

statement.addBatch(sql4);

//批量執行

statement.executeBatch();

PreparedStatement

代碼

String sql="INSERT INTO user(user_name,price,create_time) VALUES(?,?,?)";

PreparedStatement prepareStatement = connection.prepareStatement(sql);

//綁定參數1

prepareStatement.setString(1, "AA");

prepareStatement.setDouble(2, 12.12);

prepareStatement.setDate(2, new Date(new java.util.Date().getTime()));

prepareStatement.addBatch();

//綁定參數2

prepareStatement.setString(1, "BB");

prepareStatement.setDouble(2, 12.12);

prepareStatement.setDate(2, new Date(new java.util.Date().getTime()));

prepareStatement.addBatch();

//執行

prepareStatement.executeBatch();

Statement和PrepareStatement的優缺點:

1.Statement可以添加不同的SQL語句INSERT UPDATE DELETE可以同時進行批處理,但是效率相對較差

?

2.PrepareStatement執行同一條SQL不同參數的SQL語句,但是效率相對較高

第六章 JDBC存儲大字段到MYSQL(了解)

6.1 大字段介紹

對于字段長度要求超過 255 個的情況下,MySQL 提供了 TEXT 和 BLOB 兩種類型。根據存儲數據的大小,它們都有不同的子類型。這些大型的數據用于存儲文本塊或圖像、聲音文件等二進制數據類型

6.2 大字段類型

4種文本: TINYTEXT(0-255字節)、TEXT(0-65535字節)、MEDIUMTEXT(0-16777215字節) 和 LONGTEXT(0-4294967295字節)

4種二進制: TINYBLOB(0-255字節)、BLOB(0-65535字節)、MEDIUMBLOB(0-16777215字節) 和 LONGBLOB(0-4294967295字節)

6.3 具體使用圖示

?

?

</center>

6.4 大字段的操作

創建保存大字段的表

CREATE TABLE user(

? ? `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '主鍵',

? ? `image` MEDIUMBLOB? COMMENT '圖片',

? ? `article` TEXT NOT NULL COMMENT '大字段文本'

)ENGINE=InnoDB DEFAULT CHARSET=UTF8;

存儲圖片/文本的例子

/*

* 保存大字段數據

*/

@Test

public void test01() throws ClassNotFoundException, SQLException, IOException {

? ? //加載數據庫驅動

? ? Class.forName("com.mysql.jdbc.Driver");

? ? //獲取數據庫連接

? ? Connection conn = DriverManager.getConnection("jdbc:mysql:///bigdata?characterEncoding=utf8", "root", "root");

? ? //準備sql語句

? ? String sql="INSERT INTO user(image,article) VALUES(?,?)";

? ? //獲取preparedStatement對象

? ? PreparedStatement ps = conn.prepareStatement(sql);

? ? //綁定參數

? ? //獲取二進制流

? ? InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mysql.jpg");

? ? ps.setBinaryStream(1, inputStream, inputStream.available());

? ? //獲取文本流

? ? File file = new File("mysql.sql");

? ? FileReader reader = new FileReader(file);

? ? ps.setCharacterStream(2, reader, file.length());

? ? ps.executeUpdate();

? ? ps.close();

? ? conn.close();

}

讀取圖片/文本的例子

/*

* 查詢大字段數據

*/

@Test

public void test02() throws SQLException, ClassNotFoundException, IOException {

? ? //加載數據庫驅動

? ? Class.forName("com.mysql.jdbc.Driver");

? ? //獲取數據庫連接

? ? Connection conn = DriverManager.getConnection("jdbc:mysql:///bigdata?characterEncoding=utf8", "root", "root");

? ? //準備sql語句

? ? String sql="SELECT id,image,article FROM user WHERE id=?";

? ? //獲取preparedStatement對象

? ? PreparedStatement ps = conn.prepareStatement(sql);

? ? ps.setInt(1, 1);

? ? ResultSet resultSet = ps.executeQuery();

? ? if(resultSet.next()) {

? ? ? ? InputStream binaryStream = resultSet.getBinaryStream("image");

? ? ? ? Reader characterStream = resultSet.getCharacterStream("article");

? ? ? ? FileOutputStream outputStream = new FileOutputStream(new File("1.jpg"));

? ? ? ? byte[] b = new byte[1024];

? ? ? ? int len_=0;

? ? ? ? while((len_=binaryStream.read(b))!=-1) {

? ? ? ? ? ? outputStream.write(b, 0, len_);

? ? ? ? }

? ? ? ? System.out.println("=============");

? ? ? ? char[] c = new char[512];

? ? ? ? int len=0;

? ? ? ? while((len=characterStream.read(c))!=-1) {

? ? ? ? ? ? System.out.println(new String(c, 0,len));

? ? ? ? }

? ? }

}

第七章 封裝JDBC工具類

封裝獲取和關閉數據庫連接的工具類

①獲取數據庫連接

?? --加載數據庫驅動

?? --獲取數據庫連接

②關閉數據庫連接

?? --關閉ResultSet

?? --關閉PreparedStatement

?? --關閉Connection

代碼

public class JdbcUtils {

?

? ? private static final String URL="jdbc:mysql:///test";

? ? private static final String USER="root";

? ? private static final String PASSWORD="root";


? ? private static Connection connection=null;


? ? static {


? ? ? ? try {

? ? ? ? ? ? connection = DriverManager.getConnection(URL, USER, PASSWORD);

? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }


? ? }


? ? /**

? ? * 獲取數據庫連接

? ? */

? ? public static Connection getConnection() {

? ? ? ? return connection;

? ? }



? ? /**

? ? * 關閉數據庫連接

? ? */

? ? public static void close(Connection conn) {

? ? ? ? if(conn!=null) {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? conn.close();

? ? ? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? /**

? ? * 關閉連接

? ? * @param conn

? ? * @param ps

? ? */

? ? public static void close(Connection conn,PreparedStatement ps) {

? ? ? ? if(ps!=null) {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ps.close();

? ? ? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? if(conn!=null) {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? conn.close();

? ? ? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? /**

? ? * 關閉連接

? ? * @param conn

? ? * @param ps

? ? * @param rs

? ? */

? ? public static void close(Connection conn,PreparedStatement ps,ResultSet rs) {

? ? ? ? if(rs!=null) {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? rs.close();

? ? ? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? if(ps!=null) {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ps.close();

? ? ? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? if(conn!=null) {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? conn.close();

? ? ? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }

? ? }

}

?

封裝操作數據庫Dao層的通用類

查詢單個對象

/*

* 查詢單個對象(采用JDK原生的API方式調用反射接口進行返回對象的封裝)

*/

public T get(Connection conn,String sql,Class<T> clazz,Object ...args) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {

? ? //聲明返回值

? ? T t=null;

? ? //將SQL進行預編譯

? ? PreparedStatement ps = conn.prepareStatement(sql);

? ? ResultSet rs = null;


? ? if(args!=null && args.length>0) {

? ? ? ? //進行參數綁定

? ? ? ? for(int i=0;i

? ? ? ? ? ? ps.setObject(i+1, args[i]);

? ? ? ? }

? ? ? ? rs=ps.executeQuery();

? ? }else {

? ? ? ? //執行綁定參數之后的SQL語句返回結果集對象

? ? ? ? rs=ps.executeQuery();

? ? }

? ? //獲取描述結果集對象的元數據信息

? ? ResultSetMetaData rsmd = rs.getMetaData();

? ? //獲取總列數

? ? int count = rsmd.getColumnCount();

? ? //如果存在結果集那么開始進行封裝

? ? if(rs.next()) {

? ? ? ? //通過反射創建對象

? ? ? ? t = clazz.newInstance();

? ? ? ? //循環獲取列對象

? ? ? ? for(int i=0;i

? ? ? ? ? ? //獲取列名(如果有別名獲取別名,沒有別名獲取列名)

? ? ? ? ? ? String columnName = rsmd.getColumnLabel(i+1);

? ? ? ? ? ? //獲取列值

? ? ? ? ? ? Object columnValue = rs.getObject(columnName);

? ? ? ? ? ? //通過反射獲取clazz對象里描述屬性的Field對象

? ? ? ? ? ? Field field = clazz.getDeclaredField(columnName);

? ? ? ? ? ? //開啟權限控制

? ? ? ? ? ? field.setAccessible(true);

? ? ? ? ? ? //給T對象屬性賦值

? ? ? ? ? ? field.set(t, columnValue);

? ? ? ? }

? ? }

? ? return t;

}

查詢對象列表

/*

* 查詢對象列表

* 不采用原生反射API進行對象封裝而是采用第三方提供的工具類進行封裝

* commons-beanutils-1.8.0.jar

* commons-logging-1.1.1.jar

* 需要這兩個jar包,進行對象封裝

*/

public List<T> getList(Connection conn,String sql,Class<T> clazz,Object ...args) throws SQLException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {

? ? //聲明返回值

? ? List list = new ArrayList<>();

? ? //將SQL進行預編譯

? ? PreparedStatement ps = conn.prepareStatement(sql);

? ? ResultSet rs = null;


? ? if(args!=null && args.length>0) {

? ? ? ? //進行參數綁定

? ? ? ? for(int i=0;i

? ? ? ? ? ? ps.setObject(i+1, args[i]);

? ? ? ? }

? ? ? ? //執行綁定參數之后的SQL語句返回結果集對象

? ? ? ? rs=ps.executeQuery();

? ? }else {

? ? ? ? rs=ps.executeQuery();

? ? }

? ? //獲取描述結果集對象的元數據信息

? ? ResultSetMetaData rsmd = rs.getMetaData();

? ? //獲取總列數

? ? int count = rsmd.getColumnCount();

? ? //如果存在結果集那么開始進行封裝

? ? while(rs.next()) {

? ? ? ? T t = clazz.newInstance();

? ? ? ? for(int i=0;i

? ? ? ? ? ? //獲取列名

? ? ? ? ? ? String columnName = rsmd.getColumnLabel(i+1);

? ? ? ? ? ? //獲取列值

? ? ? ? ? ? Object columnValue = rs.getObject(columnName);

? ? ? ? ? ? //封裝數據到對象中

? ? ? ? ? ? PropertyUtils.setProperty(t, columnName, columnValue);

? ? ? ? }

? ? ? ? list.add(t);

? ? }

? ? return list;

}

第八章 數據庫連接池

8.1 連接池介紹

數據庫連接池的概念:負責分配、管理和釋放數據庫連接,它允許應用程序重復使用一個現有的數據庫連接,而不是再重新建立一個連接來操作數據庫.

8.2 常見連接池

C3P0:比較古老的數據庫連接池

官網地址: https://www.mchange.com/projects/c3p0/

?

C3P0提供了多種創建數據庫連接的方式,根據官網任選其一即可

Druid:alibaba的基于Java的高效的數據庫連接池

官網地址: https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98

8.3 C3P0連接池使用

ComboPooledDataSource pool = new ComboPooledDataSource();

pool.setDriverClass("com.mysql.jdbc.Driver");

pool.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc");

pool.setUser("root");

pool.setPassword("root");

第九章 DBUtils工具類

9.1 DBUtils介紹

DBUtils是apache的一個組件,算是一個輕量級的JDBC框架,官網地址

?

https://commons.apache.org/proper/commons-dbutils/

?

DbUtils是一個非常小的類庫,不需要花很長的時間去看他的API類或者是接口,只需要知道QueryRunner和ReulthSthand兩個/接口類即可

9.2 DBUtils使用

通過BeanHandler查詢一個ResultSet返回一個JavaBean對象(查詢單個對象)

QueryRunner run = new QueryRunner(dataSource);

?

ResultSetHandler<Person> h = new BeanHandler<Person>(Person.class);

?

Person p = run.query("SELECT * FROM Person WHERE name=?", h, "John Doe");

通過BeanListHandler查詢所有的ResultSet返回一組JavaBean列表

QueryRunner run = new QueryRunner(dataSource);

?

ResultSetHandler<List<Person>> h = new BeanListHandler<Person>(Person.class);

?

List<Person> persons = run.query("SELECT * FROM Person", h);

第十章 多線程安全問題

當前端多個用戶發送多個請求的時候,每一個請求都會建立一個數據庫連接,而一個用戶請求可能執行多條SQL語句,多次操作數據庫,在執行多條SQL語句時怎么保證一個用戶請求在一個事務管理內,如果不能保證在一個事務管理中就會出現線程安全問題,造成前后數據的不一致。保證線程安全的主要任務就是保證一個用戶請求中執行的多條SQL語句在一個數據庫連接中即可.還有一種情況實在使用數據庫連接池時候,由于數據庫連接池的鏈接是可以共用的,在一個用戶請求過來的時候由于有異常產生而需要事物回滾操作,但是在事務還沒有來得及回滾的時候其他的請求使用了這個連接池里的連接,進行了提交,這個時候在回滾就會沒有效果,造成的數據出錯

解決方案

public class JdbcUtil {

? ? private static ThreadLocal pool = new ThreadLocal<>();

? ? /**

? ? * 獲取當前請求線程上的Connection

? ? * @return

? ? */

? ? public static Connection getConnection() {

? ? ? ? Connection conn = pool.get();

? ? ? ? if(conn==null) {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? Class.forName("com.mysql.jdbc.Driver");

? ? ? ? ? ? ? ? conn = DriverManager.getConnection("", "", "");

? ? ? ? ? ? ? ? pool.set(conn);

? ? ? ? ? ? } catch (ClassNotFoundException e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? return conn;

? ? }

? ? /**

? ? * 關閉當前請求線程上的Connection

? ? */

? ? public static void close(){

? ? Connection conn = pool.get();

? ? conn.close();//關閉連接

? ? pool.remove();//移除連接

? ? }

}

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380