在 Wed 中应用 MyBatis(同时使用MVC架构模式,以及ThreadLocal 事务控制)

1. 在 Wed 中应用 MyBatis(同时使用MVC架构模式,以及ThreadLocal 事务控制)

文章目录

  • 1. 在 Wed 中应用 MyBatis(同时使用MVC架构模式,以及ThreadLocal 事务控制)
  • 2. 实现步骤:
    • 1. 第一步:环境搭建
    • 2. 第二步:前端页面 index.html
    • 3. 第三步:创建pojo包、service包、dao包、web包、utils包,exceptions包
    • 4. 第四步:编写 utils 包下的,获取 MyBatis,SqlSesion 连接的工具类
    • 5. 第五步:定义pojo类:Account
    • 6. 第六步:编写AccountDao接口,以及AccountDaoImpl实现类
    • 7. 第七步:AccountDaoImpl 中编写了mybatis 代码,需要编写SQL映射文件了
    • 8. 第八步:编写AccountService接口以及AccountServiceImpl
    • 9. 第九步:编写 自定义 Exception 异常
    • 10. 第十步:编写AccountController
    • 11. 第十一步:运行测试:
    • 2.1 补充说明:事务上的处理
  • 3. MyBatis核心对象的作用域
    • 3.1 SqlSessionFactoryBuilder
    • 3.2 SqlSessionFactory
    • 3.3 SqlSession
  • 4. 总结:
  • 5. 最后:


在 Web 中应用 MyBatis ,同时使用 MVC 架构模式,以及对应的 ThreadLocal 事务控制。

实现功能,银行账户的转账功能,同时进行事务上的处理。

需求描述:

实际简单的转账操作:

在这里插入图片描述

数据库表的设计和准备数据

在这里插入图片描述

在这里插入图片描述

2. 实现步骤:

这里说明一下,开发可以

  • 从后往前
  • 也可以,从前往后

二者没有太大区别,你认为哪个方向更好编写,便按照哪个方向即可,我个人比较习惯从前往后,所以这里我就从前往后了。

1. 第一步:环境搭建

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 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文件,因为我们这个项目不使用JSP。只使用 html。

  • 确定 pom.xml 文件中的打包方式是 war 包。

  • 引入相关依赖

    • 编译器版本修改为 17
    • 引入的依赖包括:mybatis,mysql驱动,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 依赖--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.10</version></dependency><!--    mysql驱动依赖--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency><!--    logback依赖--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.11</version></dependency><!--servlet依赖--><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>
  • 引入相关配置文件,放到resources目录下(全部放到类的根路径下)

    • mybatis-config.xml
    • AccountMapper.xml
    • logback.xml
    • jdbc.properties
  • 在这里插入图片描述

  • logback.xml logbak 日志框架信息

  • 在这里插入图片描述

  • <?xml version="1.0" encoding="UTF-8"?><configuration debug="false"><!-- 控制台输出 --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.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 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"/><!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR --><root level="DEBUG"><appender-ref ref="STDOUT"/><appender-ref ref="FILE"/></root></configuration>
    
  • AccountMapper.xml SQl语句的映射文件

  • <?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先随意写一个-->
    <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 数据库连接信息的配置文件

  • 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 , 一定是从类路径下开始查找资源--><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. 第二步:前端页面 index.html

在Tomcat当中 ,index.html 默认就是开始页面,主页的。

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>银行账户转账</title>
</head>
<body>
<form action="/bank/transfer" method="post">转出账户:<input type="text" name="fromActno"> <br>转入账户:<input type="text" name="toActno"> <br>转账金额:<input type="text" name="money"> <br><input type="submit" value="转账"></form>
</body>
</html>

3. 第三步:创建pojo包、service包、dao包、web包、utils包,exceptions包

在这里插入图片描述

4. 第四步:编写 utils 包下的,获取 MyBatis,SqlSesion 连接的工具类

在这里插入图片描述

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 ThreadLocal<SqlSession> 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(已经关闭,没用的)线程。*/}}
}

5. 第五步:定义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;}
}

6. 第六步:编写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;}
}

7. 第七步:AccountDaoImpl 中编写了mybatis 代码,需要编写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先随意写一个-->
<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. 第八步:编写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();}}

9. 第九步:编写 自定义 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);}
}

10. 第十步:编写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);}}
}

11. 第十一步:运行测试:

首先测试,没有模拟异常,看是否可以转账成功。

在这里插入图片描述

模拟异常,看事务上的处理,是否成功,是否能成功回滚,是否会丢失数据

在这里插入图片描述

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. 总结:

  1. 为了保证 service 和 dao 中使用的SqlSession对象是同一个,可以将SqlSession对象存放到ThreadLocal当中。

在这里插入图片描述

  1. 注意:移除SqlSession 对象和当前线程的绑定关系
    因为Tomcat 服务器是支持线程池的,也就是说,用过的先吃对象t1,可能下一I此还会使用整个t1(已经关闭,没用的)线程。

在这里插入图片描述

  1. 注意:事务的控制,都是放在业务层的,不是放在持久层DAo,更不放在utils工具层 .

  2. SqlSessionFactoryBuilder: 这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。

  3. SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。

  4. SqlSession 它的最佳的作用域是请求或方法作用域**。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 **也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。

5. 最后:

“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/26869.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vulnhub-DC-1,7

靶机IP:192.168.20.141 kaliIP:192.168.20.128 网络有问题的可以看下搭建Vulnhub靶机网络问题(获取不到IP) 前言 1和7都是Drupal的网站&#xff0c;只写了7&#xff0c;包含1的知识点 信息收集 用nmap扫描端口及版本号 进入主页查看作者给的提示&#xff0c;不是暴力破解的…

nodejs湖北省智慧乡村旅游平台-计算机毕业设计源码00232

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;旅游行业当然也不能排除在外。智慧乡村旅游平台是以实际运用为开发背景&#xff0c;运用软件工程开发方法&#xff0c;采…

Weighted A* 改进型(1):XDP

本文的主要内容来自于文献[1]&#xff0c;总的来说这篇文献给我的感觉就是理论证明非常精妙&#xff0c;最后的实际效果也是提升的非常明显。 在Introduction中作者给出了一般Best first search&#xff08;BFS&#xff0c;常用的包括A *&#xff0c;weighted A * &#xff0c…

TK防关联引流系统:全球多账号运营,一“键”掌控!

在TikTok的生态系统中&#xff0c;高效管理多个账号对于品牌推广的成功起着决定性的作用。TK防关联引流系统&#xff0c;作为一款专门为TikTok用户打造的强大工具&#xff0c;为全球范围内的多账号运营提供了坚实的支持。 TK防关联引流系统的核心优势体现在以下几个方面&#x…

anaconda安装pytorch-快速上手99%可以(可以虚拟环境OR不进行虚拟环境)

一、预备工作 先检查自己是否有anaconda 在cmd里面输入conda --version查看 二、在anaconda中创建虚拟环境 1.1 打开Anaconda Prompt 1.2 进行自定义安装python 将其中的自定义地址和版本换成自己想安装的地址和版本 我这里安装的地址是E:\Anaconda\DL,python版本是3.8.3…

uniapp地图自定义文字和图标

这是我的结构&#xff1a; <map classmap id"map" :latitude"latitude" :longitude"longitude" markertap"handleMarkerClick" :show-location"true" :markers"covers" /> 记住别忘了在data中定义变量…

Sqoop学习详细介绍!!

一、Sqoop介绍 Sqoop是一款开源的工具&#xff0c;主要用于在Hadoop(HDFS/Hive/HBase)与传统的数据库(mysql、postgresql...)间进行数据的传递&#xff0c;可以将一个关系型数据库&#xff08;例如 &#xff1a; MySQL ,Oracle ,Postgres等&#xff09;中的数据导进到Hadoop的H…

虚拟声卡实现音频回环

虚拟声卡实现音频回环 一、电脑扬声器播放声音路由到麦克风1. Voicemeeters安装设置2. 音频设备选择 二、回声模拟 一、电脑扬声器播放声音路由到麦克风 1. Voicemeeters安装设置 2. 音频设备选择 以腾讯会议为例 二、回声模拟 选中物理输入设备“Stereo Input 1”和物理输出设…

GlusterFS企业分布式存储

GlusterFS 分布式文件系统代表-nfs常见分布式存储Gluster存储基础梳理GlusterFS 适合大文件还是小文件存储&#xff1f; 应用场景术语Trusted Storage PoolBrickVolumes Glusterfs整体工作流程-数据访问流程GlusterFS客户端访问流程 GlusterFS常用命令部署 GlusterFS 群集准备环…

修改版的VectorDBBench更好用

原版本VectorDBBench的几个问题 在这里就不介绍VectorDBBench是干什么的了&#xff0c;上官网即可。 1.并发数设置的太少 2.测试时长30秒太长 3.连接milvus无用户和密码框&#xff0c;这个是最大的问题 4.修改了一下其它参数 由于很多网友发私信问一些milvus的相关技术问…

【机器学习】支持向量机(个人笔记)

文章目录 SVM 分类器的误差函数分类误差函数距离误差函数C 参数 非线性边界的 SVM 分类器&#xff08;内核方法&#xff09;多项式内核径向基函数&#xff08;RBF&#xff09;内核 源代码文件请点击此处&#xff01; SVM 分类器的误差函数 SVM 使用两条平行线&#xff0c;使用…

基于flask的网站如何使用https加密通信

文章目录 内容简介网站目录示例生成SSL证书单独使用Flask使用WSGI服务器Nginx反向代理参考资料 内容简介 HTTPS 是一种至关重要的网络安全协议&#xff0c;它通过在 HTTP 协议之上添加 SSL/TLS 层来确保数据传输的安全性和完整性。这有助于防止数据在客户端和服务器之间传输时…

高考志愿专业选择:计算机人才需求激增,人工智能领域成热门

随着2024年高考的落幕&#xff0c;数百万高三学生站在了人生新的十字路口&#xff0c;面临着一个重要的抉择&#xff1a;选择大学专业。这一选择不仅关乎未来四年的学习生涯&#xff0c;更可能决定一个人一生的职业方向和人生轨迹。在众多专业中&#xff0c;计算机相关专业因其…

如何用Xinstall CPS结算系统打破传统营销桎梏,实现用户增长?

在互联网流量红利逐渐衰退的今天&#xff0c;App推广和运营面临着前所未有的挑战。如何快速搭建起满足用户需求的运营体系&#xff0c;成为了众多企业急待解决的问题。而在这个关键时刻&#xff0c;Xinstall CPS结算系统应运而生&#xff0c;以其独特的优势帮助企业解决了一系列…

深度学习500问——Chapter11:迁移学习(2)

文章目录 11.2 迁移学习的基本思路有哪些 11.2.1 基于样本迁移 11.2.2 基于特征迁移 11.2.3 基于模型迁移 11.2.4 基于关系迁移 11.2 迁移学习的基本思路有哪些 迁移学习的基本方法可以分为四种。这四种基本方法分别是&#xff1a;基于样本的迁移&#xff0c;基于模型的迁移&a…

Sa-Token鉴权与网关服务实现

纠错&#xff1a; 在上一部分里我完成了微服务框架的初步实现&#xff0c;但是先说一下之前有一个错误&#xff0c;就是依赖部分 上次的学习中我在总的父模块下引入了spring-boot-dependencies&#xff08;版本控制&#xff09;我以为在子模块下就不需要再引用了&#xff0c;…

Opencv图像梯度计算

Opencv图像梯度计算 Sobel算子 可以理解为是做边缘检测的一种方法。 首先说明自己对图像梯度的简单理解&#xff1a;简单理解就是图像的颜色发生变化的边界区域在X方向和Y方向上的梯度值 Gx Gy 而Gx和Gy处的梯度的计算—使用下面的公式来进行计算。 G x [ − 1 0 1 − 2 0 …

【计算机毕业设计】258基于微信小程序的课堂点名系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

总结【GetHub的WebAPI,ASSET_ID】,【Linux的jq命令】(草稿版+实际操作)

目录 1.介绍一下github中的 asset_id 2. GitHub 的 asset_id相关操作 2.1.获取特定 repository 的 release 列表&#xff1a; 2.2.获取特定 release 中的 asset 列表&#xff0c;并找到 asset_id&#xff1a; 2.3.使用ASSET_ID获取资材 3.返回的 assets 的信息 是什么样样…

【STM32CubeIDE -使用-调试过程中-遇到的“弹窗”报错问题-部分总结-处理方式】

【STM32CubeIDE -使用-调试过程中-遇到的“弹窗”报错问题-部分总结-处理方式】 1、前言2、软件环境问题1&#xff1a;Confirm Perspective Switch&#xff08;确认视角切换&#xff09;&#xff08;1&#xff09;问题描述:This kind of launch is configured to open the Debu…