做網(wǎng)站的時(shí)候?qū)挾榷荚趺磁W(wǎng)絡(luò)營(yíng)銷(xiāo)策劃活動(dòng)方案
1. 在 Wed 中應(yīng)用 MyBatis(同時(shí)使用MVC架構(gòu)模式,以及ThreadLocal 事務(wù)控制)
文章目錄
- 1. 在 Wed 中應(yīng)用 MyBatis(同時(shí)使用MVC架構(gòu)模式,以及ThreadLocal 事務(wù)控制)
- 2. 實(shí)現(xiàn)步驟:
- 1. 第一步:環(huán)境搭建
- 2. 第二步:前端頁(yè)面 index.html
- 3. 第三步:創(chuàng)建pojo包、service包、dao包、web包、utils包,exceptions包
- 4. 第四步:編寫(xiě) utils 包下的,獲取 MyBatis,SqlSesion 連接的工具類(lèi)
- 5. 第五步:定義pojo類(lèi):Account
- 6. 第六步:編寫(xiě)AccountDao接口,以及AccountDaoImpl實(shí)現(xiàn)類(lèi)
- 7. 第七步:AccountDaoImpl 中編寫(xiě)了mybatis 代碼,需要編寫(xiě)SQL映射文件了
- 8. 第八步:編寫(xiě)AccountService接口以及AccountServiceImpl
- 9. 第九步:編寫(xiě) 自定義 Exception 異常
- 10. 第十步:編寫(xiě)AccountController
- 11. 第十一步:運(yùn)行測(cè)試:
- 2.1 補(bǔ)充說(shuō)明:事務(wù)上的處理
- 3. MyBatis核心對(duì)象的作用域
- 3.1 SqlSessionFactoryBuilder
- 3.2 SqlSessionFactory
- 3.3 SqlSession
- 4. 總結(jié):
- 5. 最后:
在 Web 中應(yīng)用 MyBatis ,同時(shí)使用 MVC 架構(gòu)模式,以及對(duì)應(yīng)的 ThreadLocal 事務(wù)控制。
實(shí)現(xiàn)功能,銀行賬戶(hù)的轉(zhuǎn)賬功能,同時(shí)進(jìn)行事務(wù)上的處理。
需求描述:
實(shí)際簡(jiǎn)單的轉(zhuǎn)賬操作:
數(shù)據(jù)庫(kù)表的設(shè)計(jì)和準(zhǔn)備數(shù)據(jù):
2. 實(shí)現(xiàn)步驟:
這里說(shuō)明一下,開(kāi)發(fā)可以
- 從后往前
- 也可以,從前往后
二者沒(méi)有太大區(qū)別,你認(rèn)為哪個(gè)方向更好編寫(xiě),便按照哪個(gè)方向即可,我個(gè)人比較習(xí)慣從前往后,所以這里我就從前往后了。
1. 第一步:環(huán)境搭建
IDEA中創(chuàng)建Maven WEB應(yīng)用
注意:這里的 Archetype : 選擇org.apache.maven.archetypes:maven-archetype-webapp
不要選錯(cuò)了。
IDEA配置Tomcat,這里Tomcat使用10+版本。并部署應(yīng)用到tomcat。
默認(rèn)創(chuàng)建的maven web應(yīng)用沒(méi)有 java
和 resources
目錄。
一般會(huì)自動(dòng)添加上,如果沒(méi)有的話(huà),有兩種手動(dòng)添加上的方式:
- 第一種就是:直接在 IDEA 當(dāng)?shù)?main 目錄下,新建
- 第二種修改:修改maven-archetype-webapp-1.4.jar中的配置文件
這里自動(dòng)生成的:web.xml
文件的版本較低,內(nèi)容有點(diǎn)不太合適,我們可以從 tomcat10 的樣例文件中復(fù)制,然后修改
如下是:tomcat 10 當(dāng)中的樣例:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaeehttps://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"version="5.0"metadata-complete="true"></web-app>
-
刪除
index.jsp
文件,因?yàn)槲覀冞@個(gè)項(xiàng)目不使用JSP。只使用 html。 -
確定
pom.xml
文件中的打包方式是war
包。 -
引入相關(guān)依賴(lài)
-
- 編譯器版本修改為 17
- 引入的依賴(lài)包括:mybatis,mysql驅(qū)動(dòng),junit,logback,servlet。
<?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>com.rainbowsea</groupId><artifactId>mybatis-004-web</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><name>mybatis-004-web Maven Webapp</name><!-- FIXME change it to the project's website --><url>http://www.example.com</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.7</maven.compiler.source><maven.compiler.target>1.7</maven.compiler.target></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><!-- mybatis 依賴(lài)--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.10</version></dependency><!-- mysql驅(qū)動(dòng)依賴(lài)--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency><!-- logback依賴(lài)--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.11</version></dependency><!--servlet依賴(lài)--><dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId><version>5.0.0</version><scope>provided</scope></dependency></dependencies><build><finalName>mybatis-004-web</finalName><pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --><plugins><plugin><artifactId>maven-clean-plugin</artifactId><version>3.1.0</version></plugin><!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --><plugin><artifactId>maven-resources-plugin</artifactId><version>3.0.2</version></plugin><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.8.0</version></plugin><plugin><artifactId>maven-surefire-plugin</artifactId><version>2.22.1</version></plugin><plugin><artifactId>maven-war-plugin</artifactId><version>3.2.2</version></plugin><plugin><artifactId>maven-install-plugin</artifactId><version>2.5.2</version></plugin><plugin><artifactId>maven-deploy-plugin</artifactId><version>2.8.2</version></plugin></plugins></pluginManagement></build>
</project>
-
引入相關(guān)配置文件,放到resources目錄下(全部放到類(lèi)的根路徑下)
-
- mybatis-config.xml
- AccountMapper.xml
- logback.xml
- jdbc.properties
-
-
logback.xml logbak 日志框架信息
-
-
<?xml version="1.0" encoding="UTF-8"?><configuration debug="false"><!-- 控制臺(tái)輸出 --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化輸出:%d表示日期,%thread表示線(xiàn)程名,%-5level:級(jí)別從左顯示5個(gè)字符寬度%msg:日志消息,%n是換行符--><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern></encoder></appender><!--mybatis log configure--><logger name="com.apache.ibatis" level="TRACE"/><logger name="java.sql.Connection" level="DEBUG"/><logger name="java.sql.Statement" level="DEBUG"/><logger name="java.sql.PreparedStatement" level="DEBUG"/><!-- 日志輸出級(jí)別,logback日志級(jí)別包括五個(gè):TRACE < DEBUG < INFO < WARN < ERROR --><root level="DEBUG"><appender-ref ref="STDOUT"/><appender-ref ref="FILE"/></root></configuration>
-
AccountMapper.xml SQl語(yǔ)句的映射文件
-
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace先隨意寫(xiě)一個(gè)--> <mapper namespace="account"><select id="selectByActno" resultType="com.rianbowsea.bank.pojo.Account">select * from t_act where actno = #{actno}</select> <!--#{pojo的屬性名}--><update id="updateByActno">update t_act set balance = #{balance} where actno = #{actno}</update></mapper>
-
jdb.properties 數(shù)據(jù)庫(kù)連接信息的配置文件
-
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis jdbc.username=root jdbc.password=MySQL123
-
mybatis-config.xml MyBatis 的核心配置文件
-
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration><!--resource , 一定是從類(lèi)路徑下開(kāi)始查找資源--><properties resource="jdbc.properties"></properties><environments default="mybatis"><environment id="mybatis"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments><mappers><mapper resource="AccountMapper.xml"/></mappers> </configuration>
2. 第二步:前端頁(yè)面 index.html
在Tomcat當(dāng)中 ,index.html 默認(rèn)就是開(kāi)始頁(yè)面,主頁(yè)的。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>銀行賬戶(hù)轉(zhuǎn)賬</title>
</head>
<body>
<form action="/bank/transfer" method="post">轉(zhuǎn)出賬戶(hù):<input type="text" name="fromActno"> <br>轉(zhuǎn)入賬戶(hù):<input type="text" name="toActno"> <br>轉(zhuǎn)賬金額:<input type="text" name="money"> <br><input type="submit" value="轉(zhuǎn)賬"></form>
</body>
</html>
3. 第三步:創(chuàng)建pojo包、service包、dao包、web包、utils包,exceptions包
4. 第四步:編寫(xiě) utils 包下的,獲取 MyBatis,SqlSesion 連接的工具類(lèi)
package com.rianbowsea.bank.utils;import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;public class SqlSessionUtil {// 工具類(lèi)的構(gòu)造方法一般都是私有話(huà)化的// 工具類(lèi)中所有的方法都是靜態(tài)的,直接類(lèi)名即可調(diào)用,不需要 new 對(duì)象// 為了防止new對(duì)象,構(gòu)造方法私有化。private SqlSessionUtil() {}private static SqlSessionFactory sessionFactory = null;// 靜態(tài)代碼塊,類(lèi)加載時(shí)執(zhí)行// SqlSessionUtil 工具類(lèi)在進(jìn)行第一次加載的時(shí)候,解析mybatis-config.xml 文件,創(chuàng)建SqlSessionFactory對(duì)象。static {// 獲取到 SqlSessionFactoryBuilder 對(duì)象SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();// 獲取到SqlSessionFactory 對(duì)象// SQlsessionFactory對(duì)象,一個(gè)SqlSessionFactory對(duì)應(yīng)一個(gè) environment, 一個(gè)environment通常是一個(gè)數(shù)據(jù)庫(kù)try {sessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));} catch (IOException e) {throw new RuntimeException(e);}}// 全局的,服務(wù)器級(jí)別的,一個(gè)服務(wù)器當(dāng)中定義一個(gè)即可private static ThreadLocal<SqlSession> local = new ThreadLocal<>();/*** 獲取會(huì)話(huà)對(duì)象** @return SqlSession*/public static SqlSession openSession() {// 先從 ThreadLocal 當(dāng)中獲取,獲取到 SqlSession 對(duì)象SqlSession sqlSession = local.get();if (null == sqlSession) {// ThreadLocat 沒(méi)有就, 創(chuàng)建一個(gè)sqlSession = sessionFactory.openSession();// 同時(shí)將其設(shè)置到 ThreadLocal容器當(dāng)中,將SqlSession對(duì)象綁定到當(dāng)前線(xiàn)程上local.set(sqlSession);}return sqlSession;}/*** 關(guān)閉SqlSession 對(duì)象(從當(dāng)前線(xiàn)程中移除SqlSession 對(duì)象)* @param sqlSession*/public static void close(SqlSession sqlSession) {if(sqlSession != null) {// 1.先將其關(guān)閉sqlSession.close();// 2. 再將其當(dāng)前線(xiàn)程移除ThreadLocal 當(dāng)前線(xiàn)程外面,防止被其他線(xiàn)程拿到整個(gè)沒(méi)用的線(xiàn)程local.remove();/*注意:移除SqlSession 對(duì)象和當(dāng)前線(xiàn)程的綁定關(guān)系因?yàn)門(mén)omcat 服務(wù)器是支持線(xiàn)程池的,也就是說(shuō),用過(guò)的先吃對(duì)象t1,可能下一I此還會(huì)使用整個(gè)t1(已經(jīng)關(guān)閉,沒(méi)用的)線(xiàn)程。*/}}
}
5. 第五步:定義pojo類(lèi):Account
對(duì)于 pojo 當(dāng)中的類(lèi),一定要有 set 和 get 方法,以及無(wú)參數(shù)構(gòu)造方法,不然,大部分的框架是無(wú)法通過(guò)反射機(jī)制,進(jìn)行操作的,從而出現(xiàn)錯(cuò)誤的。
package com.rianbowsea.bank.pojo;/*** 賬戶(hù)類(lèi),封裝賬戶(hù)數(shù)據(jù)*/
public class Account {private Long id;private String actno;private Double balance;public Account() {}public Account(Long id, String actno, Double balance) {this.id = id;this.actno = actno;this.balance = balance;}@Overridepublic String toString() {return "Account{" +"id=" + id +", actno='" + actno + '\'' +", balance=" + balance +'}';}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getActno() {return actno;}public void setActno(String actno) {this.actno = actno;}public Double getBalance() {return balance;}public void setBalance(Double balance) {this.balance = balance;}
}
6. 第六步:編寫(xiě)AccountDao接口,以及AccountDaoImpl實(shí)現(xiàn)類(lèi)
分析dao中至少要提供幾個(gè)方法,才能完成轉(zhuǎn)賬:
- 轉(zhuǎn)賬前需要查詢(xún)余額是否充足:selectByActno
- 轉(zhuǎn)賬時(shí)要更新賬戶(hù):update
AccountDao
package com.rianbowsea.bank.dao;import com.rianbowsea.bank.pojo.Account;/*** 賬戶(hù)的DAO對(duì)象,負(fù)責(zé)t_act 表中數(shù)據(jù)的CRUD,一般一個(gè)表對(duì)應(yīng)一個(gè) DAO* 強(qiáng)調(diào)以下,DAO對(duì)象中的任何一個(gè)方法和業(yè)務(wù)不掛鉤,沒(méi)有任何業(yè)務(wù)邏輯在里頭* DAo中的方法就是CRUD的,所以方法名大部分是:insertXxx,deletexxx,updatexxx,selectxxx*/
public interface AccountDao {/*** 根據(jù)賬號(hào)查詢(xún)賬戶(hù)信息* @param actno 賬號(hào)* @return 賬戶(hù)信息*/Account selectActno(String actno);/*** 更新賬戶(hù)信息* @param account 被更新的賬戶(hù)信息* @return 1表示更新成功,其他表示更新失敗*/int updateByActno(Account account);}
AccountDaoImpl
package com.rianbowsea.bank.dao.impl;import com.rianbowsea.bank.dao.AccountDao;
import com.rianbowsea.bank.pojo.Account;
import com.rianbowsea.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;public class AccountDaoImpl implements AccountDao {private SqlSession sqlSession = SqlSessionUtil.openSession();@Overridepublic Account selectActno(String actno) {Account account = (Account) sqlSession.selectOne("account.selectByActno", actno);// 注意:事務(wù)的控制,都是放在業(yè)務(wù)層的,不是放在持久層DAo,更不放在utils工具層//sqlSession.close();return account;}@Overridepublic int updateByActno(Account account) {int count = sqlSession.update("account.updateByActno", account);// 注意:事務(wù)的控制,都是放在業(yè)務(wù)層的,不是放在持久層DAo,更不放在utils工具層//sqlSession.commit(); // 提交數(shù)據(jù)//sqlSession.close();return count;}
}
7. 第七步:AccountDaoImpl 中編寫(xiě)了mybatis 代碼,需要編寫(xiě)SQL映射文件了
AccountMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace先隨意寫(xiě)一個(gè)-->
<mapper namespace="account"><select id="selectByActno" resultType="com.rianbowsea.bank.pojo.Account">select * from t_act where actno = #{actno}</select>
<!--#{pojo的屬性名}--><update id="updateByActno">update t_act set balance = #{balance} where actno = #{actno}</update></mapper>
8. 第八步:編寫(xiě)AccountService接口以及AccountServiceImpl
AccountService
package com.rianbowsea.bank.service;import com.rianbowsea.bank.exceptions.MoneyNotEnoughException;
import com.rianbowsea.bank.exceptions.TransferException;/*** 注意: 業(yè)務(wù)類(lèi)當(dāng)中的業(yè)務(wù)方法的名字在起名字的時(shí)候,最好見(jiàn)名知意,能夠體現(xiàn)出具體的業(yè)務(wù)是做什么的* 賬戶(hù)業(yè)務(wù)類(lèi)*/
public interface AccountService {/*** 賬戶(hù)轉(zhuǎn)賬業(yè)務(wù)** @param fromActno 轉(zhuǎn)出賬戶(hù)* @param toActno 轉(zhuǎn)入賬戶(hù)* @param money 轉(zhuǎn)賬金額*/void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException;}
AccountServiceImpl
package com.rianbowsea.bank.service.impl;import com.rianbowsea.bank.dao.AccountDao;
import com.rianbowsea.bank.dao.impl.AccountDaoImpl;
import com.rianbowsea.bank.exceptions.MoneyNotEnoughException;
import com.rianbowsea.bank.exceptions.TransferException;
import com.rianbowsea.bank.pojo.Account;
import com.rianbowsea.bank.service.AccountService;
import com.rianbowsea.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;public class AccountServiceImpl implements AccountService {private AccountDao accountDao = new AccountDaoImpl();@Overridepublic void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException {// 添加事務(wù)控制代碼SqlSession sqlSession = SqlSessionUtil.openSession();// 1.判斷轉(zhuǎn)出賬戶(hù)的金額是否充足(select)Account fromAct = accountDao.selectActno(fromActno);if (fromAct.getBalance() < money) {// 2.如果轉(zhuǎn)出賬戶(hù)余額不足,提示用戶(hù)throw new MoneyNotEnoughException("對(duì)不起,余額不足");}// 3. 如果轉(zhuǎn)出賬戶(hù)余額充足,更新轉(zhuǎn)出賬戶(hù)的余額(update)// 先在內(nèi)存當(dāng)中修改Account toACt = accountDao.selectActno(toActno);toACt.setBalance(toACt.getBalance() + money);fromAct.setBalance(fromAct.getBalance() - money);// 4. 更新轉(zhuǎn)入賬戶(hù)的余額(update)int count = accountDao.updateByActno(toACt);count += accountDao.updateByActno(fromAct);// 模擬異常//String s = null;//s.toString();if(count !=2) {throw new TransferException("轉(zhuǎn)賬異常,未知原因");}// 注意:事務(wù)的控制,都是放在業(yè)務(wù)層的,不是放在持久層DAo,更不放在utils工具層sqlSession.commit(); // 提交數(shù)據(jù)sqlSession.close();}}
9. 第九步:編寫(xiě) 自定義 Exception 異常
MoneyNotEnoughException
package com.rianbowsea.bank.exceptions;/*** 余額不足異常*/
public class MoneyNotEnoughException extends Exception{public MoneyNotEnoughException() {}public MoneyNotEnoughException(String message) {super(message);}
}
TransferException
package com.rianbowsea.bank.exceptions;/*** 轉(zhuǎn)賬異常*/
public class TransferException extends Exception{public TransferException() {}public TransferException(String message) {super(message);}
}
10. 第十步:編寫(xiě)AccountController
AccountController
package com.rianbowsea.bank.web;import com.rianbowsea.bank.exceptions.MoneyNotEnoughException;
import com.rianbowsea.bank.exceptions.TransferException;
import com.rianbowsea.bank.service.AccountService;
import com.rianbowsea.bank.service.impl.AccountServiceImpl;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/transfer")
public class AccountServlet extends HttpServlet {// 為了讓整個(gè)對(duì)象在其他方法中可以用,聲明為實(shí)例變量private AccountService accountService = new AccountServiceImpl();@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {// 獲取表單數(shù)據(jù)String fromActno = request.getParameter("fromActno");String toActno = request.getParameter("toActno");double money = Double.parseDouble(request.getParameter("money"));// 轉(zhuǎn)賬業(yè)務(wù)try {// 調(diào)用service的轉(zhuǎn)賬方法完成轉(zhuǎn)賬,(調(diào)用業(yè)務(wù)層)accountService.transfer(fromActno,toActno,money);// 程序能夠走到這里,表示轉(zhuǎn)賬一定成功了// 調(diào)用View完成展示結(jié)果response.sendRedirect(request.getContextPath()+"/success.html");} catch (MoneyNotEnoughException e) {response.sendRedirect(request.getContextPath()+"/error.html");throw new RuntimeException(e);} catch (TransferException e) {response.sendRedirect(request.getContextPath()+"/error2.html");throw new RuntimeException(e);} catch (NullPointerException e) {response.sendRedirect(request.getContextPath()+"/error2.html");throw new RuntimeException(e);}}
}
11. 第十一步:運(yùn)行測(cè)試:
首先測(cè)試,沒(méi)有模擬異常,看是否可以轉(zhuǎn)賬成功。
模擬異常,看事務(wù)上的處理,是否成功,是否能成功回滾,是否會(huì)丟失數(shù)據(jù) 。
2.1 補(bǔ)充說(shuō)明:事務(wù)上的處理
在之前的轉(zhuǎn)賬業(yè)務(wù)中,更新了兩個(gè)賬戶(hù),我們需要保證它們的同時(shí)成功或同時(shí)失敗,這個(gè)時(shí)候就需要使用事務(wù)機(jī)制,在 transfer 方法開(kāi)始執(zhí)行時(shí)開(kāi)啟事務(wù),直到兩個(gè)更新都成功之后 ,為了保證service和dao中使用的SqlSession對(duì)象是同一個(gè),可以將SqlSession對(duì)象存放到ThreadLocal當(dāng)中。
注意:移除SqlSession 對(duì)象和當(dāng)前線(xiàn)程的綁定關(guān)系
因?yàn)門(mén)omcat 服務(wù)器是支持線(xiàn)程池的,也就是說(shuō),用過(guò)的先吃對(duì)象t1,可能下一I此還會(huì)使用整個(gè)t1(已經(jīng)關(guān)閉,沒(méi)用的)線(xiàn)程。
注意:事務(wù)的控制,都是放在業(yè)務(wù)層的,不是放在持久層DAo,更不放在utils工具層 .
3. MyBatis核心對(duì)象的作用域
3.1 SqlSessionFactoryBuilder
這個(gè)類(lèi)可以被實(shí)例化、使用和丟棄,一旦創(chuàng)建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 實(shí)例的最佳作用域是方法作用域(也就是局部方法變量)。 你可以重用 SqlSessionFactoryBuilder 來(lái)創(chuàng)建多個(gè) SqlSessionFactory 實(shí)例,但最好還是不要一直保留著它,以保證所有的 XML 解析資源可以被釋放給更重要的事情。
3.2 SqlSessionFactory
SqlSessionFactory 一旦被創(chuàng)建就應(yīng)該在應(yīng)用的運(yùn)行期間一直存在,沒(méi)有任何理由丟棄它或重新創(chuàng)建另一個(gè)實(shí)例。 使用 SqlSessionFactory 的最佳實(shí)踐是在應(yīng)用運(yùn)行期間不要重復(fù)創(chuàng)建多次,多次重建 SqlSessionFactory 被視為一種代碼“壞習(xí)慣”。因此 SqlSessionFactory 的最佳作用域是應(yīng)用作用域。 有很多方法可以做到,最簡(jiǎn)單的就是使用單例模式或者靜態(tài)單例模式。
3.3 SqlSession
每個(gè)線(xiàn)程都應(yīng)該有它自己的 SqlSession 實(shí)例。SqlSession 的實(shí)例不是線(xiàn)程安全的,因此是不能被共享的,所以它的最佳的作用域是請(qǐng)求或方法作用域。 絕對(duì)不能將 SqlSession 實(shí)例的引用放在一個(gè)類(lèi)的靜態(tài)域,甚至一個(gè)類(lèi)的實(shí)例變量也不行。 也絕不能將 SqlSession 實(shí)例的引用放在任何類(lèi)型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你現(xiàn)在正在使用一種 Web 框架,考慮將 SqlSession 放在一個(gè)和 HTTP 請(qǐng)求相似的作用域中。 換句話(huà)說(shuō),每次收到 HTTP 請(qǐng)求,就可以打開(kāi)一個(gè) SqlSession,返回一個(gè)響應(yīng)后,就關(guān)閉它。 這個(gè)關(guān)閉操作很重要,為了確保每次都能執(zhí)行關(guān)閉操作,你應(yīng)該把這個(gè)關(guān)閉操作放到 finally 塊中。 下面的示例就是一個(gè)確保 SqlSession 關(guān)閉的標(biāo)準(zhǔn)模式:
try (SqlSession session = sqlSessionFactory.openSession()) {// 你的應(yīng)用邏輯代碼
}
4. 總結(jié):
- 為了保證 service 和 dao 中使用的SqlSession對(duì)象是同一個(gè),可以將SqlSession對(duì)象存放到ThreadLocal當(dāng)中。
- 注意:移除SqlSession 對(duì)象和當(dāng)前線(xiàn)程的綁定關(guān)系
因?yàn)門(mén)omcat 服務(wù)器是支持線(xiàn)程池的,也就是說(shuō),用過(guò)的先吃對(duì)象t1,可能下一I此還會(huì)使用整個(gè)t1(已經(jīng)關(guān)閉,沒(méi)用的)線(xiàn)程。
注意:事務(wù)的控制,都是放在業(yè)務(wù)層的,不是放在持久層DAo,更不放在utils工具層 .
SqlSessionFactoryBuilder: 這個(gè)類(lèi)可以被實(shí)例化、使用和丟棄,一旦創(chuàng)建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 實(shí)例的最佳作用域是方法作用域(也就是局部方法變量)。
SqlSessionFactory 一旦被創(chuàng)建就應(yīng)該在應(yīng)用的運(yùn)行期間一直存在,沒(méi)有任何理由丟棄它或重新創(chuàng)建另一個(gè)實(shí)例。
SqlSession 它的最佳的作用域是請(qǐng)求或方法作用域**。 絕對(duì)不能將 SqlSession 實(shí)例的引用放在一個(gè)類(lèi)的靜態(tài)域,甚至一個(gè)類(lèi)的實(shí)例變量也不行。 **也絕不能將 SqlSession 實(shí)例的引用放在任何類(lèi)型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你現(xiàn)在正在使用一種 Web 框架,考慮將 SqlSession 放在一個(gè)和 HTTP 請(qǐng)求相似的作用域中。
5. 最后:
“在這個(gè)最后的篇章中,我要表達(dá)我對(duì)每一位讀者的感激之情。你們的關(guān)注和回復(fù)是我創(chuàng)作的動(dòng)力源泉,我從你們身上吸取了無(wú)盡的靈感與勇氣。我會(huì)將你們的鼓勵(lì)留在心底,繼續(xù)在其他的領(lǐng)域奮斗。感謝你們,我們總會(huì)在某個(gè)時(shí)刻再次相遇?!?/p>