9-分析事物问题并编写 Utils 文件
目錄
- 接下來安排
- 一、創(chuàng)建新工程
- 1. pom.xml
- 2.結(jié)構(gòu)
- 3.接口與實(shí)現(xiàn)類添加的操作
- ⅠIAccountDao
- Ⅱ AccountDaoImpl
- Ⅲ IAccountService
- 四 IAccountService
- Ⅴ Test
- Ⅵ 問題
- 二、編寫 ConnectionUtils
- 1. ConnectionUtils 類
- 2. TransactionManager類
- 三、編寫業(yè)務(wù)層和持久層事務(wù)控制代碼并配置 spring 的 ioc
- 1.AccountServiceImpl
- 2.AccountDaoImpl
- 3.Test
- 4.結(jié)果
接下來安排
一、創(chuàng)建新工程
創(chuàng)建新的 maven 工程,命名為 spring07
做一個(gè)轉(zhuǎn)賬的小操作
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.結(jié)構(gòu)
3.接口與實(shí)現(xiàn)類添加的操作
ⅠIAccountDao
/*** 根據(jù)名稱查詢賬戶* @param accountName 賬戶名稱* @return 如果有唯一結(jié)果就返回,沒有就返回 Null, 如果結(jié)果有多個(gè),這返回錯(cuò)誤*/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("結(jié)果集不唯一,數(shù)據(jù)有問題");}} catch (SQLException e) {e.printStackTrace();}return accounts.get(0);}Ⅲ IAccountService
/*** 轉(zhuǎn)賬* @param sourceName 轉(zhuǎn)出賬戶名稱* @param targetName 轉(zhuǎn)入賬戶名稱* @param money 轉(zhuǎn)賬金額*/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.根據(jù)名稱查詢轉(zhuǎn)出賬戶Account source=accountDao.findAccountByName(sourceName);//2.根據(jù)名稱查詢轉(zhuǎn)入賬戶Account target=accountDao.findAccountByName(targetName);//3.轉(zhuǎn)出賬戶減錢source.setMoney(source.getMoney()-money);//4.轉(zhuǎn)入賬戶加錢target.setMoney(target.getMoney()+money);//5.更新轉(zhuǎn)出賬戶accountDao.updateAccount(source);//6.更新轉(zhuǎn)入賬戶accountDao.updateAccount(target);}Ⅴ Test
@Testpublic void testTransfer(){as.transfer("aaa","bbb",10f);}測試結(jié)果沒有問題
Ⅵ 問題
假如 我們在AccountServiceImpl 這樣操作一波,多加了個(gè) int i=1/0;
public void transfer(String sourceName, String targetName, Float money) {//1.根據(jù)名稱查詢轉(zhuǎn)出賬戶Account source=accountDao.findAccountByName(sourceName);//2.根據(jù)名稱查詢轉(zhuǎn)入賬戶Account target=accountDao.findAccountByName(targetName);//3.轉(zhuǎn)出賬戶減錢source.setMoney(source.getMoney()-money);//4.轉(zhuǎn)入賬戶加錢target.setMoney(target.getMoney()+money);//5.更新轉(zhuǎn)出賬戶accountDao.updateAccount(source);int i=1/0;//6.更新轉(zhuǎn)入賬戶accountDao.updateAccount(target);}運(yùn)行,你會看到 source 的賬戶減錢了,而 target 的賬戶卻沒有價(jià)錢,這怎么辦?
原因:
這是一個(gè)多例對象
解決:
要讓四個(gè)操作全為同一個(gè) connection 來實(shí)現(xiàn)
需要使用 ThreadLocal 對象把 Connection 和當(dāng)前線程綁定,從而使一個(gè)線程中只有一能控制事務(wù)的對象
二、編寫 ConnectionUtils
創(chuàng)建 utiles 文件夾
創(chuàng)建 ConnectionUtils 類
創(chuàng)建 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;/*** 描述:* 〈連接的工具類,它用于從數(shù)據(jù)源中獲取一個(gè)連接,并且實(shí)現(xiàn)和線程的綁定〉** @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.判斷當(dāng)前線程是否有連接if (conn==null){//3.從數(shù)據(jù)源中獲取一個(gè)鏈接,并且存入 ThreadLocal 中conn=dataSource.getConnection();tl.set(conn);}//4.返回當(dāng)前線程上的連接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;/*** 描述:* 〈和事務(wù)相關(guān)的工具類,它包含了,開啟事務(wù),提交事務(wù),回滾事務(wù)和釋放連接〉** @author zuiren* @create 2019/8/30* @since 1.0.0*/ @Component(value = "txManager") public class TransactionManager {@Autowiredprivate ConnectionUtils connectionUtils;/*** 開啟事務(wù)*/public void beginTransaction() {try {connectionUtils.getThreadConnection().setAutoCommit(false);} catch (SQLException e) {e.printStackTrace();}}/*** 開啟事務(wù)*/public void commit(){try {connectionUtils.getThreadConnection().commit();} catch (SQLException e) {e.printStackTrace();}}/*** 回滾事務(wù)*/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();}} }細(xì)節(jié):
連接使用了連接池,
連接池的好處:把消耗時(shí)間連接獲取連接的這部分放到應(yīng)用加載一開始。
在 web 工程中,當(dāng)我們啟動tomcat加載應(yīng)用時(shí),我們創(chuàng)建一些連接,從而在后續(xù)項(xiàng)目運(yùn)行階段不在跟數(shù)據(jù)庫獲取鏈接保證了我們使用 connection 時(shí)的使用效率。
我們使用服務(wù)器,服務(wù)器也會有一個(gè)池的技術(shù)叫做線程池,它的特點(diǎn)是當(dāng) connection 啟動時(shí),會啟動一大堆的線程放到一個(gè)容器中,接下來我們每次訪問,它都是從線程池中拿出一個(gè)線程給我們使用。
這樣的話線程池中的線程也跟我們連接池中的一樣,所以我們最后調(diào)用
connectionUtils.getThreadConnection().close();
的方法并不是將其關(guān)閉,而是將他放回線程池中。
造理推斷,線程用完了,也不是真正的關(guān)了,而是把線程還回線程池中。
所以這個(gè)線程中是綁著一個(gè)連接的,當(dāng)我們把連接關(guān)閉,線程還回池中時(shí),線程上是有連接的,只不過這個(gè)連接已經(jīng)被關(guān)閉了,當(dāng)我們下次再獲取這個(gè)線程判斷上面有沒有連接時(shí),你得到的結(jié)果一定是有,但是這個(gè)連接已經(jīng)不能用了,因?yàn)樗呀?jīng)被 close 過了,被加載過池子里去了。
所以從這點(diǎn)上來說:我們應(yīng)該在整個(gè)這個(gè)線程用完了之后,把這個(gè)線程和這個(gè)連接進(jìn)行解綁。
在 ConnectionUtils 類中添加一方法
/*** 把連接和線程解綁*/public void removeConnection(){tl.remove();}在 TransactionManager 類
/*** 釋放連接*/public void release(){try {//還回池中connectionUtils.getThreadConnection().close();connectionUtils.removeConnection();} catch (SQLException e) {e.printStackTrace();}}三、編寫業(yè)務(wù)層和持久層事務(wù)控制代碼并配置 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.開啟事務(wù)txManager.beginTransaction();//2.執(zhí)行操作accounts=accountDao.findAllAccount();//3.提交事務(wù)txManager.commit();//4.返回結(jié)果return accounts;}catch (Exception e){//5.回滾操作txManager.rollback();}finally {//6.釋放資源txManager.release();}return accounts;}public Account findAccountById(Integer accountId) {Account account=null;try {//1.開啟事務(wù)txManager.beginTransaction();//2.執(zhí)行操作account=accountDao.findAccountById(accountId);//3.提交事務(wù)txManager.commit();//4.返回結(jié)果return account;}catch (Exception e){//5.回滾操作txManager.rollback();}finally {//6.釋放資源txManager.release();}return account;}public void saveAccount(Account account) {try {//1.開啟事務(wù)txManager.beginTransaction();//2.執(zhí)行操作accountDao.saveAccount(account);//3.提交事務(wù)txManager.commit();//4.返回結(jié)果}catch (Exception e){//5.回滾操作txManager.rollback();}finally {//6.釋放資源txManager.release();}}public void updateAccount(Account account) {try {//1.開啟事務(wù)txManager.beginTransaction();//2.執(zhí)行操作accountDao.updateAccount(account);//3.提交事務(wù)txManager.commit();//4.返回結(jié)果}catch (Exception e){//5.回滾操作txManager.rollback();}finally {//6.釋放資源txManager.release();}}public void deleteAccount(Integer accountId) {try {//1.開啟事務(wù)txManager.beginTransaction();//2.執(zhí)行操作accountDao.deleteAccount(accountId);//3.提交事務(wù)txManager.commit();//4.返回結(jié)果}catch (Exception e){//5.回滾操作txManager.rollback();}finally {//6.釋放資源txManager.release();}}public void transfer(String sourceName, String targetName, Float money) {try {txManager.beginTransaction();//1.根據(jù)名稱查詢轉(zhuǎn)出賬戶Account source=accountDao.findAccountByName(sourceName);//2.根據(jù)名稱查詢轉(zhuǎn)入賬戶Account target=accountDao.findAccountByName(targetName);//3.轉(zhuǎn)出賬戶減錢source.setMoney(source.getMoney()-money);//4.轉(zhuǎn)入賬戶加錢target.setMoney(target.getMoney()+money);//5.更新轉(zhuǎn)出賬戶accountDao.updateAccount(source);int i=1/0;//6.更新轉(zhuǎn)入賬戶accountDao.updateAccount(target);txManager.commit();}catch (Exception e){txManager.rollback();}finally {txManager.release();}} }2.AccountDaoImpl
這里只是在 runner 執(zhí)行操作時(shí),給它配置連接
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("結(jié)果集不唯一,數(shù)據(jù)有問題");}} catch (SQLException e) {e.printStackTrace();}return accounts.get(0);} }3.Test
@Testpublic void testTransfer(){as.transfer("aaa","bbb",10f);}4.結(jié)果
你會發(fā)現(xiàn)報(bào)錯(cuò),事務(wù)回滾,aaa 的 money 沒有變化,轉(zhuǎn)賬正常執(zhí)行。
問題是現(xiàn)在依賴特別混亂,以后會解決
轉(zhuǎn)載于:https://www.cnblogs.com/zuiren/p/11437528.html
總結(jié)
以上是生活随笔為你收集整理的9-分析事物问题并编写 Utils 文件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 08-spring整合 junit
- 下一篇: Common Attention Poi