商丘市做网站的公司烟台元和网络科技有限公司
- 作者: 五速梦信息网
- 时间: 2026年04月20日 09:25
当前位置: 首页 > news >正文
商丘市做网站的公司,烟台元和网络科技有限公司,网站开发的英文文献,桂林亿星网络科技公司1. 在 Wed 中应用 MyBatis#xff08;同时使用MVC架构模式#xff0c;以及ThreadLocal 事务控制#xff09; 文章目录 1. 在 Wed 中应用 MyBatis#xff08;同时使用MVC架构模式#xff0c;以及ThreadLocal 事务控制#xff09;2. 实现步骤#xff1a;1. 第一步#xf…1. 在 Wed 中应用 MyBatis同时使用MVC架构模式以及ThreadLocal 事务控制 文章目录 1. 在 Wed 中应用 MyBatis同时使用MVC架构模式以及ThreadLocal 事务控制2. 实现步骤1. 第一步环境搭建2. 第二步前端页面 index.html3. 第三步创建pojo包、service包、dao包、web包、utils包exceptions包4. 第四步编写 utils 包下的获取 MyBatisSqlSesion 连接的工具类5. 第五步定义pojo类Account6. 第六步编写AccountDao接口以及AccountDaoImpl实现类7. 第七步AccountDaoImpl 中编写了mybatis 代码需要编写SQL映射文件了8. 第八步编写AccountService接口以及AccountServiceImpl9. 第九步编写 自定义 Exception 异常10. 第十步编写AccountController11. 第十一步运行测试2.1 补充说明事务上的处理 3. MyBatis核心对象的作用域3.1 SqlSessionFactoryBuilder3.2 SqlSessionFactory3.3 SqlSession 4. 总结5. 最后 在 Web 中应用 MyBatis 同时使用 MVC 架构模式以及对应的 ThreadLocal 事务控制。 实现功能银行账户的转账功能同时进行事务上的处理。 需求描述 实际简单的转账操作 数据库表的设计和准备数据 2. 实现步骤 这里说明一下开发可以 从后往前也可以从前往后 二者没有太大区别你认为哪个方向更好编写便按照哪个方向即可我个人比较习惯从前往后所以这里我就从前往后了。
- 第一步环境搭建
IDEA中创建Maven WEB应用
注意这里的 Archetype : 选择org.apache.maven.archetypes:maven-archetype-webapp 不要选错了。 IDEA配置Tomcat这里Tomcat使用10版本。并部署应用到tomcat。 默认创建的maven web应用没有 java和 resources目录。 一般会自动添加上如果没有的话有两种手动添加上的方式
第一种就是直接在 IDEA 当的 main 目录下新建 第二种修改修改maven-archetype-webapp-1.4.jar中的配置文件 这里自动生成的web.xml 文件的版本较低内容有点不太合适我们可以从 tomcat10 的样例文件中复制然后修改
如下是tomcat 10 当中的样例 ?xml version1.0 encodingUTF-8? web-app xmlnshttps://jakarta.ee/xml/ns/jakartaeexmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttps://jakarta.ee/xml/ns/jakartaeehttps://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsdversion5.0metadata-completetrue/web-app删除 index.jsp文件因为我们这个项目不使用JSP。只使用 html。 确定 pom.xml 文件中的打包方式是 war 包。 引入相关依赖 编译器版本修改为 17引入的依赖包括mybatismysql驱动junitlogbackservlet。 ?xml version1.0 encodingUTF-8?project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.rainbowsea/groupIdartifactIdmybatis-004-web/artifactIdversion1.0-SNAPSHOT/versionpackagingwar/packagingnamemybatis-004-web Maven Webapp/name!– FIXME change it to the projects website –urlhttp://www.example.com/urlpropertiesproject.build.sourceEncodingUTF-8/project.build.sourceEncodingmaven.compiler.source1.7/maven.compiler.sourcemaven.compiler.target1.7/maven.compiler.target/propertiesdependenciesdependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.11/versionscopetest/scope/dependency!– mybatis 依赖–dependencygroupIdorg.mybatis/groupIdartifactIdmybatis/artifactIdversion3.5.10/version/dependency!– mysql驱动依赖–dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion8.0.30/version/dependency!– logback依赖–dependencygroupIdch.qos.logback/groupIdartifactIdlogback-classic/artifactIdversion1.2.11/version/dependency!–servlet依赖–dependencygroupIdjakarta.servlet/groupIdartifactIdjakarta.servlet-api/artifactIdversion5.0.0/versionscopeprovided/scope/dependency/dependenciesbuildfinalNamemybatis-004-web/finalNamepluginManagement!– lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) –pluginspluginartifactIdmaven-clean-plugin/artifactIdversion3.1.0/version/plugin!– see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging –pluginartifactIdmaven-resources-plugin/artifactIdversion3.0.2/version/pluginpluginartifactIdmaven-compiler-plugin/artifactIdversion3.8.0/version/pluginpluginartifactIdmaven-surefire-plugin/artifactIdversion2.22.1/version/pluginpluginartifactIdmaven-war-plugin/artifactIdversion3.2.2/version/pluginpluginartifactIdmaven-install-plugin/artifactIdversion2.5.2/version/pluginpluginartifactIdmaven-deploy-plugin/artifactIdversion2.8.2/version/plugin/plugins/pluginManagement/build /project 引入相关配置文件放到resources目录下全部放到类的根路径下 mybatis-config.xmlAccountMapper.xmllogback.xmljdbc.properties logback.xml logbak 日志框架信息 ?xml version1.0 encodingUTF-8?configuration debugfalse!– 控制台输出 –appender nameSTDOUT classch.qos.logback.core.ConsoleAppenderencoder classch.qos.logback.classic.encoder.PatternLayoutEncoder!–格式化输出%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符–pattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n/pattern/encoder/appender!–mybatis log configure–logger namecom.apache.ibatis levelTRACE/logger namejava.sql.Connection levelDEBUG/logger namejava.sql.Statement levelDEBUG/logger namejava.sql.PreparedStatement levelDEBUG/!– 日志输出级别,logback日志级别包括五个TRACE DEBUG INFO WARN ERROR –root levelDEBUGappender-ref refSTDOUT/appender-ref refFILE//root/configurationAccountMapper.xml SQl语句的映射文件 ?xml version1.0 encodingUTF-8 ? !DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd !–namespace先随意写一个– mapper namespaceaccountselect idselectByActno resultTypecom.rianbowsea.bank.pojo.Accountselect * from t_act where actno #{actno}/select !–#{pojo的属性名}–update idupdateByActnoupdate t_act set balance #{balance} where actno #{actno}/update/mapperjdb.properties 数据库连接信息的配置文件 jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/mybatis jdbc.usernameroot jdbc.passwordMySQL123mybatis-config.xml MyBatis 的核心配置文件 ?xml version1.0 encodingUTF-8 ? !DOCTYPE configurationPUBLIC -//mybatis.org//DTD Config 3.0//ENhttp://mybatis.org/dtd/mybatis-3-config.dtd configuration!–resource , 一定是从类路径下开始查找资源–properties resourcejdbc.properties/propertiesenvironments defaultmybatisenvironment idmybatistransactionManager typeJDBC/dataSource typePOOLEDproperty namedriver value\({jdbc.driver}/property nameurl value\){jdbc.url}/property nameusername value\({jdbc.username}/property namepassword value\){jdbc.password}//dataSource/environment/environmentsmappersmapper resourceAccountMapper.xml//mappers /configuration2. 第二步前端页面 index.html 在Tomcat当中 index.html 默认就是开始页面主页的。 !DOCTYPE html html langen headmeta charsetUTF-8title银行账户转账/title /head body form action/bank/transfer methodpost转出账户:input typetext namefromActno br转入账户:input typetext nametoActno br转账金额:input typetext namemoney brinput typesubmit value转账/form /body /html3. 第三步创建pojo包、service包、dao包、web包、utils包exceptions包 4. 第四步编写 utils 包下的获取 MyBatisSqlSesion 连接的工具类 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 {// 工具类的构造方法一般都是私有话化的// 工具类中所有的方法都是静态的直接类名即可调用不需要 new 对象// 为了防止new对象构造方法私有化。private SqlSessionUtil() {}private static SqlSessionFactory sessionFactory null;// 静态代码块类加载时执行// SqlSessionUtil 工具类在进行第一次加载的时候解析mybatis-config.xml 文件创建SqlSessionFactory对象。static {// 获取到 SqlSessionFactoryBuilder 对象SqlSessionFactoryBuilder sqlSessionFactoryBuilder new SqlSessionFactoryBuilder();// 获取到SqlSessionFactory 对象// SQlsessionFactory对象一个SqlSessionFactory对应一个 environment, 一个environment通常是一个数据库try {sessionFactory sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(mybatis-config.xml));} catch (IOException e) {throw new RuntimeException(e);}}// 全局的服务器级别的一个服务器当中定义一个即可private static ThreadLocalSqlSession local new ThreadLocal();/*** 获取会话对象** return SqlSession/public static SqlSession openSession() {// 先从 ThreadLocal 当中获取获取到 SqlSession 对象SqlSession sqlSession local.get();if (null sqlSession) {// ThreadLocat 没有就 创建一个sqlSession sessionFactory.openSession();// 同时将其设置到 ThreadLocal容器当中,将SqlSession对象绑定到当前线程上local.set(sqlSession);}return sqlSession;}/** 关闭SqlSession 对象从当前线程中移除SqlSession 对象* param sqlSession*/public static void close(SqlSession sqlSession) {if(sqlSession ! null) {// 1.先将其关闭sqlSession.close();// 2. 再将其当前线程移除ThreadLocal 当前线程外面防止被其他线程拿到整个没用的线程local.remove();/注意移除SqlSession 对象和当前线程的绑定关系因为Tomcat 服务器是支持线程池的也就是说用过的先吃对象t1可能下一I此还会使用整个t1(已经关闭没用的)线程。/}} } - 第五步定义pojo类Account 对于 pojo 当中的类一定要有 set 和 get 方法以及无参数构造方法不然大部分的框架是无法通过反射机制进行操作的从而出现错误的。 package com.rianbowsea.bank.pojo;/*** 账户类封装账户数据*/ 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;} }
- 第六步编写AccountDao接口以及AccountDaoImpl实现类 分析dao中至少要提供几个方法才能完成转账 转账前需要查询余额是否充足selectByActno转账时要更新账户update AccountDao package com.rianbowsea.bank.dao;import com.rianbowsea.bank.pojo.Account;/*** 账户的DAO对象负责t_act 表中数据的CRUD一般一个表对应一个 DAO* 强调以下DAO对象中的任何一个方法和业务不挂钩没有任何业务逻辑在里头* DAo中的方法就是CRUD的所以方法名大部分是insertXxx,deletexxx,updatexxx,selectxxx/ public interface AccountDao {/** 根据账号查询账户信息* param actno 账号* return 账户信息/Account selectActno(String actno);/** 更新账户信息* param account 被更新的账户信息* 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);// 注意事务的控制都是放在业务层的不是放在持久层DAo更不放在utils工具层//sqlSession.close();return account;}Overridepublic int updateByActno(Account account) {int count sqlSession.update(account.updateByActno, account);// 注意事务的控制都是放在业务层的不是放在持久层DAo更不放在utils工具层//sqlSession.commit(); // 提交数据//sqlSession.close();return count;} }
- 第七步AccountDaoImpl 中编写了mybatis 代码需要编写SQL映射文件了 AccountMapper.xml ?xml version1.0 encodingUTF-8 ? !DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd !–namespace先随意写一个– mapper namespaceaccountselect idselectByActno resultTypecom.rianbowsea.bank.pojo.Accountselect * from t_act where actno #{actno}/select !–#{pojo的属性名}–update idupdateByActnoupdate t_act set balance #{balance} where actno #{actno}/update/mapper8. 第八步编写AccountService接口以及AccountServiceImpl AccountService package com.rianbowsea.bank.service;import com.rianbowsea.bank.exceptions.MoneyNotEnoughException; import com.rianbowsea.bank.exceptions.TransferException;/*** 注意: 业务类当中的业务方法的名字在起名字的时候最好见名知意能够体现出具体的业务是做什么的* 账户业务类/ public interface AccountService {/** 账户转账业务** param fromActno 转出账户* param toActno 转入账户* param money 转账金额*/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 {// 添加事务控制代码SqlSession sqlSession SqlSessionUtil.openSession();// 1.判断转出账户的金额是否充足(select)Account fromAct accountDao.selectActno(fromActno);if (fromAct.getBalance() money) {// 2.如果转出账户余额不足提示用户throw new MoneyNotEnoughException(对不起余额不足);}// 3. 如果转出账户余额充足更新转出账户的余额update// 先在内存当中修改Account toACt accountDao.selectActno(toActno);toACt.setBalance(toACt.getBalance() money);fromAct.setBalance(fromAct.getBalance() - money);// 4. 更新转入账户的余额(update)int count accountDao.updateByActno(toACt);count accountDao.updateByActno(fromAct);// 模拟异常//String s null;//s.toString();if(count !2) {throw new TransferException(转账异常未知原因);}// 注意事务的控制都是放在业务层的不是放在持久层DAo更不放在utils工具层sqlSession.commit(); // 提交数据sqlSession.close();}}
- 第九步编写 自定义 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;/** 转账异常*/ public class TransferException extends Exception{public TransferException() {}public TransferException(String message) {super(message);} }
- 第十步编写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 {// 为了让整个对象在其他方法中可以用声明为实例变量private AccountService accountService new AccountServiceImpl();Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {// 获取表单数据String fromActno request.getParameter(fromActno);String toActno request.getParameter(toActno);double money Double.parseDouble(request.getParameter(money));// 转账业务try {// 调用service的转账方法完成转账调用业务层accountService.transfer(fromActno,toActno,money);// 程序能够走到这里表示转账一定成功了// 调用View完成展示结果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);}} }
- 第十一步运行测试 首先测试没有模拟异常看是否可以转账成功。 模拟异常看事务上的处理是否成功是否能成功回滚是否会丢失数据 。 2.1 补充说明事务上的处理 在之前的转账业务中更新了两个账户我们需要保证它们的同时成功或同时失败这个时候就需要使用事务机制在 transfer 方法开始执行时开启事务直到两个更新都成功之后 为了保证service和dao中使用的SqlSession对象是同一个可以将SqlSession对象存放到ThreadLocal当中。 注意移除SqlSession 对象和当前线程的绑定关系 因为Tomcat 服务器是支持线程池的也就是说用过的先吃对象t1可能下一I此还会使用整个t1(已经关闭没用的)线程。 注意事务的控制都是放在业务层的不是放在持久层DAo更不放在utils工具层 . 3. MyBatis核心对象的作用域 3.1 SqlSessionFactoryBuilder 这个类可以被实例化、使用和丢弃一旦创建了 SqlSessionFactory就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域也就是局部方法变量。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例但最好还是不要一直保留着它以保证所有的 XML 解析资源可以被释放给更重要的事情。 3.2 SqlSessionFactory SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到最简单的就是使用单例模式或者静态单例模式。 3.3 SqlSession 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的因此是不能被共享的所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说每次收到 HTTP 请求就可以打开一个 SqlSession返回一个响应后就关闭它。 这个关闭操作很重要为了确保每次都能执行关闭操作你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式 try (SqlSession session sqlSessionFactory.openSession()) {// 你的应用逻辑代码 }4. 总结 为了保证 service 和 dao 中使用的SqlSession对象是同一个可以将SqlSession对象存放到ThreadLocal当中。 注意移除SqlSession 对象和当前线程的绑定关系 因为Tomcat 服务器是支持线程池的也就是说用过的先吃对象t1可能下一I此还会使用整个t1(已经关闭没用的)线程。 注意事务的控制都是放在业务层的不是放在持久层DAo更不放在utils工具层 . SqlSessionFactoryBuilder: 这个类可以被实例化、使用和丢弃一旦创建了 SqlSessionFactory就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域也就是局部方法变量。 SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在没有任何理由丢弃它或重新创建另一个实例。 SqlSession 它的最佳的作用域是请求或方法作用域**。 绝对不能将 SqlSession 实例的引用放在一个类的静态域甚至一个类的实例变量也不行。 **也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 5. 最后 “在这个最后的篇章中我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底继续在其他的领域奋斗。感谢你们我们总会在某个时刻再次相遇。”
- 上一篇: 商丘市做网站的公司龙岩网红
- 下一篇: 商丘手机网站建设给客户做一个网站ppt怎么做
相关文章
-
商丘市做网站的公司龙岩网红
商丘市做网站的公司龙岩网红
- 技术栈
- 2026年04月20日
-
商丘市网站建设设计制作费用计入什么会计科目
商丘市网站建设设计制作费用计入什么会计科目
- 技术栈
- 2026年04月20日
-
商丘加盟小吃网站免费咨询服务合同模板下载
商丘加盟小吃网站免费咨询服务合同模板下载
- 技术栈
- 2026年04月20日
-
商丘手机网站建设给客户做一个网站ppt怎么做
商丘手机网站建设给客户做一个网站ppt怎么做
- 技术栈
- 2026年04月20日
-
商丘网站建设aliapp开发直播软件需要多少钱
商丘网站建设aliapp开发直播软件需要多少钱
- 技术栈
- 2026年04月20日
-
商丘网站建设价格公众号开发算软件开发吗
商丘网站建设价格公众号开发算软件开发吗
- 技术栈
- 2026年04月20日
