9、Spring_事务管理

六、Spring 事务管理

1.Spring 事务简介

  • 事务概述:保证数据库操作同时成功或者同时失败

  • Spring 事务的概述:在数据层保证数据库操作同时成功或者同时失败

2.转账案例分析

  • 转账肯定有一个业务方法:给转出用户减钱,给转入用户加钱
  • 要求:
    • 要么同时成功要么同时失败

2.1Spring 平台事务管理器

  • 提供规范接口

    public interface PlatformTransactionManager {
        TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException;
    
        void commit(TransactionStatus var1) throws TransactionException;
    
        void rollback(TransactionStatus var1) throws TransactionException;
    }
    
  • 方法

    • void commit(TransactionStatus var1):用于提交事务
    • void rollback(TransactionStatus var1):用户事务回滚
  • 具体实现:DataSourceTransactionManager 来实现的,通过DataSource dataSource 以 JDBC 事务的方式来控制事务在这里插入图片描述

2.2转账案例分析

  • 业务分析

    • 在业务层需要保证事务的同时成功或者同时失败
    • 结果
      • 出现异常:张三转账给李四,比如中途出现问题,张三的钱和李四的钱应该不出现误差
      • 没有出现异常:张三转账给李四,没有出现问题,张三的钱正常减少,李四的钱正常增多
  • 提供service 方法

    public interface IAccountService {
        /**
         * 实现转账操作
         * @param srcId 转账人
         * @param deskId 接收人
         * @param money 转账金额
         */
        public void transfer(Long srcId,Long deskId,int money);
    }
    
  • 下载插件的官方地址 https://plugins.jetbrains.com/

  • 提供service 实现方法

    @Service
    public class AccountServiceImpl implements IAccountService {
    
        //会使用到 mapper
        @Autowired
        private AccountMapper mapper;
    
        public void transfer(Long srcId, Long deskId, int money) {
            mapper.outAccount(srcId,money);//转账扣钱
    
            mapper.inAccount(deskId,money);//接收转账钱
        }
    }
    
  • 提供 mapper 接口

    public interface AccountMapper {
        void outAccount(@Param("id") Long srcId, @Param("money") int money);
    
        void inAccount(@Param("id")Long deskId,@Param("money") int money);
    }
    
  • 提供 mapper.xml

    </update>
        <update id="outAccount">
        update account
        set money = money-#{money,jdbcType=INTEGER}
        where id = #{id,jdbcType=BIGINT}
        </update>
      <update id="inAccount">
        update account
        set money = money+#{money,jdbcType=INTEGER}
        where id = #{id,jdbcType=BIGINT}
      </update>
    
  • 测试

     @Test
        public void testMybatis(){
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
            IAccountService bean = context.getBean(IAccountService.class);
            bean.transfer(1L,2L,500);
    
        }
    

3.基于注解的方式实现

3.1@EnableTransactionManagement

  • @EnableTransactionManagement:用于开启事务支持的,直接添加到spring 配置类

  • 说明

    名称@EnableTransactionManagement
    位置配置类上方
    作用设置当前spring环境支持事务
  • 修改配置类

    @Configuration
    @ComponentScan("cn.sycoder")
    @PropertySource("db.properties")
    @Import({JdbcConfig.class,MyBatisConfig.class})
    //开启事务支持
    @EnableTransactionManagement
    public class SpringConfig {
    }
    

3.2 配置事务管理,配置数据源

  • 如果不配置的话会报:找不到数据源错误
    在这里插入图片描述

  • PlatformTransactionManager

  • 代码

    public class JdbcConfig {
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
        @Value("${jdbc.driverClassName}")
        private String driverClassName;
        @Value("${jdbc.url}")
        private String url;
        //配置连接池
        @Bean
        public DataSource dataSource(){
            DruidDataSource source = new DruidDataSource();
            source.setUsername(username);
            source.setPassword(password);
            source.setDriverClassName(driverClassName);
            source.setUrl(url);
            return source;
        }
    
        @Bean
        public PlatformTransactionManager transactionManager(DataSource dataSource){
            DataSourceTransactionManager manager = new DataSourceTransactionManager();
            manager.setDataSource(dataSource);
            return manager;
        }
    }
    

3.3 @Transactional

  • @Transactional:为业务添加事务的

  • 说明

    名称@Transactional
    位置业务层接口上方,或者实现类上方,或者具体业务方法上方
    作用为当前的业务方法添加事务支持
  • 修改业务层

    • 业务方法上添加

      @Transactional
      public void transfer(Long srcId, Long deskId, int money) {
          mapper.outAccount(srcId,money);//转账扣钱
          System.out.println(1/0);
          mapper.inAccount(deskId,money);//接收转账钱
      }
      
    • 业务类上添加

      @Service
      @Transactional
      public class AccountServiceImpl implements IAccountService {
      
          //会使用到 mapper
          @Autowired
          private AccountMapper mapper;
      
      
          public void transfer(Long srcId, Long deskId, int money) {
              mapper.outAccount(srcId,money);//转账扣钱
              System.out.println(1/0);
              mapper.inAccount(deskId,money);//接收转账钱
          }
      }
      
    • 接口层添加

      @Transactional
      public interface IAccountService {
          /**
           * 实现转账操作
           * @param srcId 转账人
           * @param deskId 接收人
           * @param money 转账金额
           */
          public void transfer(Long srcId,Long deskId,int money);
      }
      

    4.事务角色

  • 在没有开启Spring事务之前:两条语句分别开启两个事务 T1 和 T2

    • 如果同时成功,T1和T2都会正常提交
    • 如果T1正常,T2之前抛出异常,就会出现T1能够正常转账,但是T2收不到钱,因为不是同一个事务导致金钱异常
    public void transfer(Long srcId, Long deskId, int money) {
        mapper.outAccount(srcId,money);//转账扣钱
        System.out.println(1/0);
        mapper.inAccount(deskId,money);//接收转账钱
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KE12TXbZ-1693295276398)(picture/image-20221105210048391.png)]

  • 开启Spring 事务管理之后

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZQEeOh60-1693295276399)(picture/image-20221105210911281.png)]

    • 在转账 transfer 方法上加入 @Transactional 注解之后,该方法会新建一个事务T
    • 把 mapper 中 outAccount 事务 T1 加入到 事务T中,把 mapper 中 inAccount 事务 T2 也加入到事务T中
    • 通过 @Transactional 注解统一了 transfer 方法的事务保证转账和入账方法变成同一事务操作

4.1事务管理员&事务协调员

  • 事务管理员:发起新事务,使用 @Transactional 注解开启事务
  • 事务协调员:加入新事务,保证多个事务变成同一事务下的操作

5.@Transactional 属性

5.1 readOnly

  • 概述:表示只读,没有写操作。可以通过这个属性告诉数据库我们没有写操作,从而数据库可以针对只读sql做优化操作

  • 使用

    @Transactional(readOnly = true)
    public Account selectById(Long id){
        return mapper.selectByPrimaryKey(id);
    }
    
  • 如果对于有写操作的使用这个属性,会报如下错误

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lhqysmIG-1693295276402)(picture/image-20221105212707549.png)]

5.2 timeout

  • 超时概述:事务再执行的时候,由于某些原因卡住,长时间占用数据库资源。此时很可能程序sql有问题,希望撤销事务,能够让事务结束,释放资源,即超时回滚。

  • 默认值是-1.-1表示用不回滚,单位是秒

  • int timeout() default -1;
    
  • 使用

    @Transactional(readOnly = true,timeout = 1)
        public Account selectById(Long id){
            try {
                Thread.sleep(10000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return mapper.selectByPrimaryKey(id);
        }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hamXB1vD-1693295276402)(picture/image-20221105213517098.png)]

5.3 rollbackFor&rollbackForClassName

  • 回滚概述:回滚策略,希望对于什么样的异常回顾

  • 注意:并不是所有的异常 Spring 都会回滚,Spring 只对 Error 异常和 RuntimeException 异常回滚

  • 使用

    @Transactional(rollbackFor = IOException.class)
        public void transfer(Long srcId, Long deskId, int money) throws IOException {
            mapper.outAccount(srcId,money);//转账扣钱
            if(true){
                throw new IOException("");
            }
            mapper.inAccount(deskId,money);//接收转账钱
        }
    
    @Transactional(rollbackForClassName = "IOException")
        public void transfer(Long srcId, Long deskId, int money) throws IOException {
            mapper.outAccount(srcId,money);//转账扣钱
            if(true){
                throw new IOException("");
            }
            mapper.inAccount(deskId,money);//接收转账钱
        }
    

5.4 noRollbackFor&noRollbackForClassName

  • 不会滚概述:出现这个异常不回滚

  • 使用

    @Transactional(noRollbackFor = ArithmeticException.class)
        public void transfer(Long srcId, Long deskId, int money) throws IOException {
            mapper.outAccount(srcId,money);//转账扣钱
            System.out.println(1/0);
            mapper.inAccount(deskId,money);//接收转账钱
        }
    
    @Transactional(noRollbackForClassName = "ArithmeticException")
        public void transfer(Long srcId, Long deskId, int money) throws IOException {
            mapper.outAccount(srcId,money);//转账扣钱
            System.out.println(1/0);
            mapper.inAccount(deskId,money);//接收转账钱
        }
    

5.5 isolation

  • 概述:设置事务隔离级别;

  • 如果不记得事务隔离级别,回去复习一下我讲的MySql

    • DEFAULT :默认隔离级别, 会采用数据库的隔离级别
    • READ_UNCOMMITTED : 读未提交
    • READ_COMMITTED : 读已提交
    • REPEATABLE_READ : 重复读取
    • SERIALIZABLE: 串行化
  • 使用

    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public Account selectById(Long id) throws IOException {
        return mapper.selectByPrimaryKey(id);
    }
    

5.6propagation

  • 事务传播行为:事务协调员对事务管理员所携带的事务的处理态度

  • 说明

    传播属性说明
    REQUIRED外围方法会开启新事务,内部方法会加入到外部方法的事务中
    SUPPORTS外围方法没有事务,则内部方法不执行事务
    MANDATORY使用当前事务,如果当前没有事务就抛异常
    REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起
    NOT_SUPPORTED不支持事务
    NEVER不支持事务,如果存在事务还会抛异常
    NESTED如果当前存在事务,则在嵌套事务内执行,如果不存在,执行REQUIRED类似操作
  • 实操

    • REQUIRED:T1和T2会加入T中

      @Transactional(propagation = Propagation.REQUIRED)//事务T
      public void transfer(Long srcId, Long deskId, int money) throws IOException {
          mapper.outAccount(srcId,money);//转账扣钱 //事务T1
          System.out.println(1/0);
          mapper.inAccount(deskId,money);//接收转账钱 //事务T2
      }
      
    • SUPPORTS:外围没事务,所以内部只执行自己的事务,T1 和 T2 单独执行

      public void transfer(Long srcId, Long deskId, int money) throws IOException {
              mapper.outAccount(srcId,money);//转账扣钱//事务T1
              System.out.println(1/0);
              mapper.inAccount(deskId,money);//接收转账钱//事务T2
          }
      
    • REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起

       @Transactional(propagation=Propagation.REQUIRES_NEW)
          public void outAccount(Long id,  int money){
              mapper.outAccount(id,money);//转账扣钱
          }
      
          @Transactional(propagation=Propagation.REQUIRES_NEW)
          public void inAccount(Long id,  int money){
              mapper.inAccount(id,money);//转账扣钱
          }
      
      public void transfer(Long srcId, Long deskId, int money) throws IOException {
          outAccount(srcId,money);
          inAccount(deskId,money);
          throw new RuntimeException();
      
      }
      
      • 这种情况上面一条语句能够正常执行
      @Transactional(propagation = Propagation.REQUIRES_NEW)
          public void outAccount(Long id, int money) {
              mapper.outAccount(id, money);//转账扣钱
          }
      
          @Transactional(propagation = Propagation.REQUIRES_NEW)
          public void inAccount(Long id, int money) {
              if (true)
                  throw new RuntimeException();
              mapper.inAccount(id, money);//转账扣钱
          }
      

6.基于XML事务

  • 导入依赖

    <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.2.17.RELEASE</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>5.2.17.RELEASE</version>
            </dependency>
    
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <!--            <scope>test</scope>-->
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.16</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.29</version>
            </dependency>
            <!--        spring 整合 mybatis 的包-->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>1.3.0</version>
            </dependency>
            <!--        mybatis 包-->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.6</version>
            </dependency>
            <!--        spring 操作 jdbc 包-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>4.3.29.RELEASE</version>
            </dependency>
        </dependencies>
    
  • 配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/tx
     http://www.springframework.org/schema/tx/spring-tx.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
        <bean id="accountService" class="cn.sycoder.service.impl.AccountServiceImpl">
    <!--        <property name="mapper" ref="mapper"/>-->
        </bean>
        <!--    <bean id="mapper" class="cn.sycoder.mapper.AccountMapper"></bean>-->
    
        <aop:config>
            <aop:advisor advice-ref="tx" pointcut="execution(* cn.sycoder.service.impl.*.*(..))"></aop:advisor>
        </aop:config>
    
        <tx:advice id="tx" transaction-manager="txManager">
            <tx:attributes>
                <tx:method name="get*" read-only="true"/>
            </tx:attributes>
        </tx:advice>
    
        <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!-- (this dependency is defined somewhere else) -->
            <property name="dataSource" ref="dataSource"/>
        </bean>
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="${jdbc.driverClassName}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
        <context:property-placeholder location="db.properties"/>
    
    
    </beans>
    
  • 配置详解

    • 引入db.properties

      <context:property-placeholder location="db.properties"/>
      
    • 配置连接池

      <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
          <property name="driverClassName" value="${jdbc.driverClassName}"/>
          <property name="url" value="${jdbc.url}"/>
          <property name="username" value="${jdbc.username}"/>
          <property name="password" value="${jdbc.password}"/>
      </bean>
      
    • 配置事务管理器

      <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
          <property name="dataSource" ref="dataSource"/>
      </bean>
      
    • 配置 aop 事务增强

      <aop:config>
              <aop:advisor advice-ref="tx" pointcut="execution(* cn.sycoder.service.impl.*.*(..))"/>
      </aop:config>
              
      <tx:advice id="tx" transaction-manager="txManager">
          <tx:attributes>
              <tx:method name="get*" read-only="true"/>
          </tx:attributes>
      </tx:advice>    
      
  • 注意:如果你还想通过 xml 配置 mybatis ,那么你还需要把 mybatis 配置文件搞一份过来,通过xml 配置好 mybatis 之后,然后再获取 sqlSessionFactory 去获取 mapper 文件

  • 注意:spring 是面试重头戏,所以,你需要花时间认真巩固和复习,ioc 和 di 特别是对于常用注解,以及事务机制,aop 等都很爱问。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux枚举文件目录、获取文件属性

目录 1.枚举指定路径下的文件目录2.获取文件属性stat其他方式&#xff1a;Linux获取文件属性stat()、fstat()、lstat()函数实现stat属性代码 1.枚举指定路径下的文件目录 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <dirent.…

Python基础学习第六天:Python 数据类型

内置数据类型 在编程中&#xff0c;数据类型是一个重要的概念。 变量可以存储不同类型的数据&#xff0c;并且不同类型可以执行不同的操作。 在这些类别中&#xff0c;Python 默认拥有以下内置数据类型&#xff1a; 获取数据类型 您可以使用 type() 函数获取任何对象的数据…

IDEA使用git

文章目录 给所有文件配置git初始化本地仓库创建.gitignore文件添加远程仓库分支操作 给所有文件配置git 初始化本地仓库 创建.gitignore文件 添加远程仓库 分支操作 新建分支 newbranch 切换分支 checkout 推送分支 push 合并分支 merge

Linux中的进程、fork、进程状态、环境变量

1、进程 1.1 PCB 进程信息被放在一个叫做进程控制块的数据结构中&#xff0c;可以理解为进程属性的集合。课本上称之为PCB&#xff08;process control block&#xff09;&#xff0c;Linux操作系统下的PCB是: task_struct 在Linux中描述进程的结构体叫做task_struct。task_s…

pxe网络装机

目录 PXE是什么&#xff1f; PXE的组件&#xff1a; 配置vsftpd关闭防火墙与selinux ​编辑配置tftp 准备pxelinx.0文件、引导文件、内核文件 ​编辑配置dhcp 创建default文件 创建新虚拟机等待安装&#xff08;交互式安装完毕&#xff09; 创建客户端验证&#xff08;…

Error encountered when performing Introspect database postgres schema

我在使用postgresql数据库的时候遇到了这两个异常: 并且查看不到数据库里的表信息 Error encountered when performing Introspect database postgres schema public (details): 错误: 字段 t.relhasoids 不存在 Error encountered when performing Introspect database pos…

沉浸式VR虚拟实景样板间降低了看房购房的难度

720 全景是一种以全景视角为特点的虚拟现实展示方式&#xff0c;它通过全景图像和虚拟现实技术&#xff0c;将用户带入一个仿佛置身其中的沉浸式体验中。720 全景可以应用于旅游、房地产、展览等多个领域&#xff0c;为用户提供更为直观、真实的体验。 在房地产领域&#xff0c…

reactantd(12)动态表单的默认值问题

最近遇到一个需求是有一个表单可以输入各种信息&#xff0c;然后还需要有一个编辑功能&#xff0c;点击编辑的时候需要把当前数据填入到表单里面。在网上查了很多种方法&#xff0c;然后我的思路是使用initialValues搭配setState()使用。默认值都为空&#xff0c;然后点击单条数…

ZooKeeper集群环境搭建

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 个人主页&#xff1a;beixi 本文章收录于专栏&#xff08;点击传送&#xff09;&#xff1a;【大数据学习】 &#x1f493;&#x1f493;持续更新中&#xff0c;感谢各位前辈朋友们支持…

苍穹外卖01-项目概述、环境搭建

项目概述、环境搭建 课程内容 软件开发整体介绍苍穹外卖项目介绍开发环境搭建导入接口文档Swagger 项目整体效果展示&#xff1a; 管理端-外卖商家使用用户端-点餐用户使用当我们完成该项目的学习&#xff0c;可以培养以下能力&#xff1a; 1. 软件开发整体介绍 作为一名软…

Go Map

学习了GO语言中数组&#xff0c;切片类型&#xff0c;但是我们发现使用数组或者是切片存储的数据量如果比较大&#xff0c;那么通过下标来取出某个具体的数据的时候相对来说&#xff0c;比较麻烦。例如&#xff1a; names : []string{"张三","李四","…

如何从ChatGPT中获得最佳聊天对话效果

从了解ChatGPT工作原理开始&#xff0c;然后从互动中学习&#xff0c;这是一位AI研究员的建议。 人们利用ChatGPT来撰写文章、论文、生成文案和计算机代码&#xff0c;或者仅仅作为学习或研究工具。然而&#xff0c;大多数人不了解它的工作原理或它能做什么&#xff0c;所以他…

云计算和Docker分别适用场景

在大规模网络爬虫系统中&#xff0c;通过使用云计算和Docker技术&#xff0c;可以实现大规模网络爬虫系统的高效架构设计和部署。这种架构能够提供可扩展性、高可用性和灵活性&#xff0c;为爬虫系统的运行和管理带来便利。 云计算和Docker在大规模网络爬虫系统中有不同的业务…

华为OD机试 - 字符串分割(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路1、根据题意&#xff1a;2、例如&#xff1a;3、解题思路&#xff1a; 五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《…

算法通关村14关 | 堆在数组中找第k大的元素应用

1. 在数组中找第k大元素 题目 LeetCode215&#xff1a;给定整数数组nums和整数k&#xff0c;请返回数组中第k个最大的元素&#xff0c; 思路 解题思路用三个&#xff0c;选择法&#xff0c;堆查找和快速排序。 我们选择用大堆小堆解决问题&#xff0c;“找最大用小堆&#xff…

Python飞机大战小游戏

游戏规则&#xff1a;键盘上下左右键控制飞机移动 游戏展示图片&#xff1a; 源码&#xff1a; 第一个py命名为&#xff1a;plane_main.py import pygamefrom plane_sprites import *class PlaneGame(object):# """飞机大战主游戏"""def __in…

由于cpu cache line机制在共享原子数据操作上带来的硬件干扰对多线程机制的性能影响

由于cpu cache line机制在共享原子数据操作上带来的硬件干扰会对对多线程性能造成影响。例如不同的原子数据&#xff0c;位于同一个cpu cache line&#xff0c;这时候一个处理器读取这个cpu cache line这段数据的时候&#xff0c;就会控制这段数据的所有权&#xff0c;其他想要…

ASUS华硕VivoBook15笔记本V5200EA_X515EA原装出厂Win11预装OEM系统

华硕11代酷睿笔记本电脑VivoBook_ASUSLaptop X515EA_V5200EA原厂Windows11系统 自带显卡、声卡、网卡、蓝牙等所有驱动、出厂主题壁纸、Office办公软件、华硕电脑管家MyASUS、迈克菲等预装程序 链接&#xff1a;https://pan.baidu.com/s/1yAEdA7aiuHK4CTdGLlSOKw?pwdo45a …

ShardingJDBC——分库分表实践

摘要 本文主要介绍分表分库&#xff0c;以及SpringBoot集成基于ShardingJDBC的单库分表实践。 一、Sharding-JDBC Sharding-JDBC是ShardingSphere的第一个产品&#xff0c;也是ShardingSphere的前身。 它定位为轻量级Java框架&#xff0c;在Java的JDBC层提供的额外服务。它使…

汽车服务门店小程序模板制作指南

在数字化时代&#xff0c;一个小程序的力量不可忽视。它不仅是展示品牌形象和提供用户服务的重要工具&#xff0c;更是扩大客户群体和提高营收的关键手段。对于汽车服务门店来说&#xff0c;拥有一个精美且功能齐全的小程序&#xff0c;更将成为你在竞争激烈的市场中的重要武器…