9-分析事物问题并编写 Utils 文件
目錄
- 接下來安排
- 一、創建新工程
- 1. pom.xml
- 2.結構
- 3.接口與實現類添加的操作
- ⅠIAccountDao
- Ⅱ AccountDaoImpl
- Ⅲ IAccountService
- 四 IAccountService
- Ⅴ Test
- Ⅵ 問題
- 二、編寫 ConnectionUtils
- 1. ConnectionUtils 類
- 2. TransactionManager類
- 三、編寫業務層和持久層事務控制代碼并配置 spring 的 ioc
- 1.AccountServiceImpl
- 2.AccountDaoImpl
- 3.Test
- 4.結果
接下來安排
一、創建新工程
創建新的 maven 工程,命名為 spring07
做一個轉賬的小操作
1. pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>spring07</groupId><artifactId>spring07</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><dependencies><!-- https://mvnrepository.com/artifact/org.springframework/spring-context --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.9.RELEASE</version></dependency><!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils --><dependency><groupId>commons-dbutils</groupId><artifactId>commons-dbutils</artifactId><version>1.7</version></dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>6.0.6</version></dependency><!-- https://mvnrepository.com/artifact/com.mchange/c3p0 --><dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.4</version></dependency><!-- https://mvnrepository.com/artifact/junit/junit --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-test --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.1.9.RELEASE</version><scope>test</scope></dependency></dependencies> </project>2.結構
3.接口與實現類添加的操作
ⅠIAccountDao
/*** 根據名稱查詢賬戶* @param accountName 賬戶名稱* @return 如果有唯一結果就返回,沒有就返回 Null, 如果結果有多個,這返回錯誤*/Account findAccountByName(String accountName);Ⅱ AccountDaoImpl
public void updateAccount(Account account) {try {runner.update("update account02 set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());}catch (SQLException e){e.printStackTrace();}}public Account findAccountByName(String accountName) {List<Account> accounts=null;try {accounts= runner.query("select *from account02 where name=?",new BeanListHandler<Account>(Account.class),accountName);if (accounts==null || accounts.size()==0){return null;}if(accounts.size()>1){throw new RuntimeException("結果集不唯一,數據有問題");}} catch (SQLException e) {e.printStackTrace();}return accounts.get(0);}Ⅲ IAccountService
/*** 轉賬* @param sourceName 轉出賬戶名稱* @param targetName 轉入賬戶名稱* @param money 轉賬金額*/void transfer(String sourceName,String targetName,Float money);/*** 更新* @param account*/void updateAccount(Account account);四 IAccountService
public void transfer(String sourceName, String targetName, Float money) {//1.根據名稱查詢轉出賬戶Account source=accountDao.findAccountByName(sourceName);//2.根據名稱查詢轉入賬戶Account target=accountDao.findAccountByName(targetName);//3.轉出賬戶減錢source.setMoney(source.getMoney()-money);//4.轉入賬戶加錢target.setMoney(target.getMoney()+money);//5.更新轉出賬戶accountDao.updateAccount(source);//6.更新轉入賬戶accountDao.updateAccount(target);}Ⅴ Test
@Testpublic void testTransfer(){as.transfer("aaa","bbb",10f);}測試結果沒有問題
Ⅵ 問題
假如 我們在AccountServiceImpl 這樣操作一波,多加了個 int i=1/0;
public void transfer(String sourceName, String targetName, Float money) {//1.根據名稱查詢轉出賬戶Account source=accountDao.findAccountByName(sourceName);//2.根據名稱查詢轉入賬戶Account target=accountDao.findAccountByName(targetName);//3.轉出賬戶減錢source.setMoney(source.getMoney()-money);//4.轉入賬戶加錢target.setMoney(target.getMoney()+money);//5.更新轉出賬戶accountDao.updateAccount(source);int i=1/0;//6.更新轉入賬戶accountDao.updateAccount(target);}運行,你會看到 source 的賬戶減錢了,而 target 的賬戶卻沒有價錢,這怎么辦?
原因:
這是一個多例對象
解決:
要讓四個操作全為同一個 connection 來實現
需要使用 ThreadLocal 對象把 Connection 和當前線程綁定,從而使一個線程中只有一能控制事務的對象
二、編寫 ConnectionUtils
創建 utiles 文件夾
創建 ConnectionUtils 類
創建 TransactionManager類
1. ConnectionUtils 類
package com.utils;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;import javax.sql.DataSource; import java.sql.Connection;/*** 描述:* 〈連接的工具類,它用于從數據源中獲取一個連接,并且實現和線程的綁定〉** @author zuiren* @create 2019/8/30* @since 1.0.0*/ @Component("connectionUtils") public class ConnectionUtils {private ThreadLocal<Connection> tl=new ThreadLocal<Connection>();@Autowiredprivate DataSource dataSource;public Connection getThreadConnection(){//1.先從 ThreadLocal 上獲取Connection conn=tl.get();try {//2.判斷當前線程是否有連接if (conn==null){//3.從數據源中獲取一個鏈接,并且存入 ThreadLocal 中conn=dataSource.getConnection();tl.set(conn);}//4.返回當前線程上的連接return conn;}catch (Exception e){throw new RuntimeException(e);}}/*** 把連接和線程解綁*/public void removeConnection(){tl.remove();} }2. TransactionManager類
package com.utils;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;import java.sql.SQLException;/*** 描述:* 〈和事務相關的工具類,它包含了,開啟事務,提交事務,回滾事務和釋放連接〉** @author zuiren* @create 2019/8/30* @since 1.0.0*/ @Component(value = "txManager") public class TransactionManager {@Autowiredprivate ConnectionUtils connectionUtils;/*** 開啟事務*/public void beginTransaction() {try {connectionUtils.getThreadConnection().setAutoCommit(false);} catch (SQLException e) {e.printStackTrace();}}/*** 開啟事務*/public void commit(){try {connectionUtils.getThreadConnection().commit();} catch (SQLException e) {e.printStackTrace();}}/*** 回滾事務*/public void rollback(){try {connectionUtils.getThreadConnection().rollback();} catch (SQLException e) {e.printStackTrace();}}/*** 釋放連接*/public void release(){try {//還回池中connectionUtils.getThreadConnection().close();connectionUtils.removeConnection();} catch (SQLException e) {e.printStackTrace();}} }細節:
連接使用了連接池,
連接池的好處:把消耗時間連接獲取連接的這部分放到應用加載一開始。
在 web 工程中,當我們啟動tomcat加載應用時,我們創建一些連接,從而在后續項目運行階段不在跟數據庫獲取鏈接保證了我們使用 connection 時的使用效率。
我們使用服務器,服務器也會有一個池的技術叫做線程池,它的特點是當 connection 啟動時,會啟動一大堆的線程放到一個容器中,接下來我們每次訪問,它都是從線程池中拿出一個線程給我們使用。
這樣的話線程池中的線程也跟我們連接池中的一樣,所以我們最后調用
connectionUtils.getThreadConnection().close();
的方法并不是將其關閉,而是將他放回線程池中。
造理推斷,線程用完了,也不是真正的關了,而是把線程還回線程池中。
所以這個線程中是綁著一個連接的,當我們把連接關閉,線程還回池中時,線程上是有連接的,只不過這個連接已經被關閉了,當我們下次再獲取這個線程判斷上面有沒有連接時,你得到的結果一定是有,但是這個連接已經不能用了,因為它已經被 close 過了,被加載過池子里去了。
所以從這點上來說:我們應該在整個這個線程用完了之后,把這個線程和這個連接進行解綁。
在 ConnectionUtils 類中添加一方法
/*** 把連接和線程解綁*/public void removeConnection(){tl.remove();}在 TransactionManager 類
/*** 釋放連接*/public void release(){try {//還回池中connectionUtils.getThreadConnection().close();connectionUtils.removeConnection();} catch (SQLException e) {e.printStackTrace();}}三、編寫業務層和持久層事務控制代碼并配置 spring 的 ioc
1.AccountServiceImpl
package com.service.Impl;import com.dao.IAccountDao; import com.domain.Account; import com.service.IAccountService; import com.utils.TransactionManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;import java.util.List; import java.util.TreeMap;/*** 描述:* 〈〉** @author zuiren* @create 2019/8/29* @since 1.0.0*/ @Service("accountService") public class AccountServiceImpl implements IAccountService {@Autowiredprivate IAccountDao accountDao;@Autowiredprivate TransactionManager txManager;public List<Account> findAllAccount() {List<Account> accounts=null;try {//1.開啟事務txManager.beginTransaction();//2.執行操作accounts=accountDao.findAllAccount();//3.提交事務txManager.commit();//4.返回結果return accounts;}catch (Exception e){//5.回滾操作txManager.rollback();}finally {//6.釋放資源txManager.release();}return accounts;}public Account findAccountById(Integer accountId) {Account account=null;try {//1.開啟事務txManager.beginTransaction();//2.執行操作account=accountDao.findAccountById(accountId);//3.提交事務txManager.commit();//4.返回結果return account;}catch (Exception e){//5.回滾操作txManager.rollback();}finally {//6.釋放資源txManager.release();}return account;}public void saveAccount(Account account) {try {//1.開啟事務txManager.beginTransaction();//2.執行操作accountDao.saveAccount(account);//3.提交事務txManager.commit();//4.返回結果}catch (Exception e){//5.回滾操作txManager.rollback();}finally {//6.釋放資源txManager.release();}}public void updateAccount(Account account) {try {//1.開啟事務txManager.beginTransaction();//2.執行操作accountDao.updateAccount(account);//3.提交事務txManager.commit();//4.返回結果}catch (Exception e){//5.回滾操作txManager.rollback();}finally {//6.釋放資源txManager.release();}}public void deleteAccount(Integer accountId) {try {//1.開啟事務txManager.beginTransaction();//2.執行操作accountDao.deleteAccount(accountId);//3.提交事務txManager.commit();//4.返回結果}catch (Exception e){//5.回滾操作txManager.rollback();}finally {//6.釋放資源txManager.release();}}public void transfer(String sourceName, String targetName, Float money) {try {txManager.beginTransaction();//1.根據名稱查詢轉出賬戶Account source=accountDao.findAccountByName(sourceName);//2.根據名稱查詢轉入賬戶Account target=accountDao.findAccountByName(targetName);//3.轉出賬戶減錢source.setMoney(source.getMoney()-money);//4.轉入賬戶加錢target.setMoney(target.getMoney()+money);//5.更新轉出賬戶accountDao.updateAccount(source);int i=1/0;//6.更新轉入賬戶accountDao.updateAccount(target);txManager.commit();}catch (Exception e){txManager.rollback();}finally {txManager.release();}} }2.AccountDaoImpl
這里只是在 runner 執行操作時,給它配置連接
package com.dao.Impl;import com.dao.IAccountDao; import com.domain.Account; import com.utils.ConnectionUtils; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service;import java.sql.SQLException; import java.util.List;/*** 描述:* 〈〉** @author zuiren* @create 2019/8/29* @since 1.0.0*/ @Repository("accountDao") public class AccountDaoImpl implements IAccountDao {@Autowiredprivate QueryRunner runner;@Autowiredprivate ConnectionUtils connectionUtils;public List<Account> findAllAccount() {try {return runner.query(connectionUtils.getThreadConnection(),"select *from account02",new BeanListHandler<Account>(Account.class));}catch (Exception e){throw new RuntimeException(e);}}public Account findAccountById(Integer accountId) {try {return runner.query(connectionUtils.getThreadConnection(),"select *from account02 where id = ?",new BeanHandler<Account>(Account.class),accountId);}catch (Exception e){throw new RuntimeException(e);}}public void saveAccount(Account account) {try {runner.update(connectionUtils.getThreadConnection(),"insert into account02(name,money) values(?,?)",account.getName(),account.getMoney());}catch (SQLException e){e.printStackTrace();}}public void updateAccount(Account account) {try {runner.update(connectionUtils.getThreadConnection(),"update account02 set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());}catch (SQLException e){e.printStackTrace();}}public void deleteAccount(Integer accountId) {try {runner.update(connectionUtils.getThreadConnection(),"delete from account02 where id=?",accountId);}catch (SQLException e){e.printStackTrace();}}public Account findAccountByName(String accountName) {List<Account> accounts=null;try {accounts= runner.query(connectionUtils.getThreadConnection(),"select *from account02 where name=?",new BeanListHandler<Account>(Account.class),accountName);if (accounts==null || accounts.size()==0){return null;}if(accounts.size()>1){throw new RuntimeException("結果集不唯一,數據有問題");}} catch (SQLException e) {e.printStackTrace();}return accounts.get(0);} }3.Test
@Testpublic void testTransfer(){as.transfer("aaa","bbb",10f);}4.結果
你會發現報錯,事務回滾,aaa 的 money 沒有變化,轉賬正常執行。
問題是現在依賴特別混亂,以后會解決
轉載于:https://www.cnblogs.com/zuiren/p/11437528.html
總結
以上是生活随笔為你收集整理的9-分析事物问题并编写 Utils 文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 08-spring整合 junit
- 下一篇: ubuntu系统安装gedit