[{"data":1,"prerenderedAt":6064},["ShallowReactive",2],{"search-docs":3,"doc-\u002Fother\u002Fspring-series\u002Fspring\u002Fspring-transaction":886},[4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,140,144,148,152,156,159,162,165,169,172,175,178,182,186,190,194,198,202,206,210,214,218,222,226,230,234,238,242,246,250,254,258,262,266,269,273,277,281,285,288,291,294,298,301,304,307,310,313,316,319,322,325,329,332,336,340,344,348,352,356,359,362,365,368,371,374,377,380,383,386,389,393,396,399,402,405,408,411,414,417,420,424,428,432,435,438,442,446,450,454,458,462,466,470,474,477,480,483,487,491,494,497,500,504,507,511,515,518,521,524,527,530,533,536,539,542,545,548,551,554,557,560,563,566,569,572,575,579,583,587,591,595,599,603,606,610,614,617,620,623,626,629,633,637,640,643,646,649,652,655,658,661,664,667,670,673,676,679,682,685,688,691,694,697,700,703,706,709,712,716,720,724,728,732,736,740,744,748,752,756,760,764,768,772,775,779,783,787,790,793,796,799,802,805,808,811,814,818,822,825,829,832,835,838,841,844,848,851,854,858,862,865,869,873,876,879,882],{"path":5,"title":6,"description":7},"\u002Fabout\u002Fauthor","作者相关","只想纯粹的做一个程序员...",{"path":9,"title":10,"description":11},"\u002Fabout\u002Fjourney","心路历程","",{"path":13,"title":14,"description":15},"\u002Fai\u002Fagent\u002Fframeworks","Agent 框架","主流 Agent 框架：LangChain、LlamaIndex、AutoGen、CrewAI",{"path":17,"title":18,"description":19},"\u002Fai\u002Fagent\u002Fhooks","Agent Hooks 与自动化","Claude Agent 的 Hooks 生命周期、事件类型、典型自动化场景",{"path":21,"title":22,"description":23},"\u002Fai\u002Fagent\u002Fintroduction","AI Agent 概述","AI Agent 核心概念：感知、规划、执行、记忆",{"path":25,"title":26,"description":27},"\u002Fai\u002Fagent\u002Fpractice","Agent 实战","AI Agent 实战：构建自主任务执行系统",{"path":29,"title":30,"description":31},"\u002Fai\u002Fagent\u002Fsdk","Claude Agent SDK 开发","使用 Claude Agent SDK 构建自定义 AI Agent：架构、API、生命周期",{"path":33,"title":34,"description":35},"\u002Fai\u002Fagent\u002Fsubagents","Subagents 子代理","用 Subagents 分解复杂任务、并发执行、隔离上下文",{"path":37,"title":38,"description":39},"\u002Fai\u002Fagent\u002Ftool-use","工具调用","AI Agent 工具调用：Function Calling、Tool Use 原理与实践",{"path":41,"title":42,"description":43},"\u002Fai\u002Ffundamentals\u002Fdeep-learning","深度学习入门","深度学习基础知识：前向传播、反向传播、损失函数、优化器",{"path":45,"title":46,"description":47},"\u002Fai\u002Ffundamentals\u002Fml-basics","机器学习基础","机器学习核心概念：监督学习、无监督学习、强化学习",{"path":49,"title":50,"description":51},"\u002Fai\u002Ffundamentals\u002Fneural-networks","神经网络原理","神经网络架构：CNN、RNN、注意力机制",{"path":53,"title":54,"description":55},"\u002Fai\u002Fgetting-started","AI 学习路线","AI 技术学习路线图，从基础到实战的完整指南",{"path":57,"title":58,"description":59},"\u002Fai\u002Fllm\u002Ffine-tuning","模型微调","大模型微调技术：LoRA、QLoRA、全量微调、RLHF",{"path":61,"title":62,"description":63},"\u002Fai\u002Fllm\u002Fintroduction","大模型概述","大语言模型发展历程、核心能力与主流模型对比",{"path":65,"title":66,"description":67},"\u002Fai\u002Fllm\u002Flocal-deploy","本地部署","大模型本地部署：Ollama、vLLM、llama.cpp",{"path":69,"title":70,"description":71},"\u002Fai\u002Fllm\u002Ftransformer","Transformer 架构","Transformer 架构详解：自注意力机制、位置编码、多头注意力",{"path":73,"title":74,"description":75},"\u002Fai\u002Fmcp\u002Fclient","MCP Client 开发","MCP Client 开发指南：连接、调用、集成",{"path":77,"title":78,"description":79},"\u002Fai\u002Fmcp\u002Fdebugging","MCP 调试与排错","MCP Server 开发与集成过程中的常见问题、日志分析、诊断工具",{"path":81,"title":82,"description":83},"\u002Fai\u002Fmcp\u002Fintroduction","MCP 概述","Model Context Protocol 协议概述：架构、核心概念、应用场景",{"path":85,"title":86,"description":87},"\u002Fai\u002Fmcp\u002Fserver","MCP Server 开发","MCP Server 开发指南：资源、工具、提示词的实现",{"path":89,"title":90,"description":91},"\u002Fai\u002Fmcp\u002Ftools","MCP Tools 深入","深入理解 MCP Tools：与 Resources\u002FPrompts 的差异、Schema 设计、Annotations 与权限控制",{"path":93,"title":94,"description":95},"\u002Fai\u002Fprompt\u002Fadvanced","高级 Prompt 模式","高级 Prompt 设计模式：Tree-of-Thought、自我反思、多轮对话策略",{"path":97,"title":98,"description":99},"\u002Fai\u002Fprompt\u002Fbasics","Prompt 基础","Prompt Engineering 入门：基本概念、角色设定、输出格式控制",{"path":101,"title":102,"description":103},"\u002Fai\u002Fprompt\u002Ftechniques","提示词技巧","常用提示词技巧：Few-shot、Chain-of-Thought、ReAct",{"path":105,"title":106,"description":107},"\u002Fai\u002Frag\u002Fembedding","文本嵌入","文本嵌入模型：Embedding 原理、模型选择、相似度计算",{"path":109,"title":110,"description":111},"\u002Fai\u002Frag\u002Fintroduction","RAG 概述","检索增强生成（RAG）架构原理、优势与应用场景",{"path":113,"title":114,"description":115},"\u002Fai\u002Frag\u002Fpractice","RAG 实战","RAG 应用实战：文档问答系统、知识库搭建",{"path":117,"title":118,"description":119},"\u002Fai\u002Frag\u002Fvector-database","向量数据库","主流向量数据库对比：Milvus、Pinecone、Chroma、Weaviate",{"path":121,"title":122,"description":123},"\u002Fai\u002Fskills\u002Fbest-practices","Skill 最佳实践","编写高质量 Skill 的设计原则、常见陷阱与优化技巧",{"path":125,"title":126,"description":127},"\u002Fai\u002Fskills\u002Fcreating","创建自定义 Skill","从零编写一个可被 Agent 自动发现和调用的 Skill",{"path":129,"title":130,"description":131},"\u002Fai\u002Fskills\u002Fintroduction","Agent Skills 概述","Claude Agent Skills 概念、工作原理、与 Tools\u002FMCP 的区别",{"path":133,"title":134,"description":135},"\u002Fgolang\u002Fadvanced\u002Fconcurrency","Go - 并发深入","深入理解 Go 并发编程的核心机制。",{"path":137,"title":138,"description":139},"\u002Fgolang\u002Fadvanced\u002Fgc","Go - 垃圾回收","理解 Go 的垃圾回收机制，掌握 GC 调优方法。",{"path":141,"title":142,"description":143},"\u002Fgolang\u002Fadvanced\u002Fgmp","Go - GMP 调度模型","GMP 是 Go 运行时调度器的核心模型，理解它对于编写高性能 Go 程序至关重要。",{"path":145,"title":146,"description":147},"\u002Fgolang\u002Fadvanced\u002Fgo-concurrency","Go - 并发编程","Go 的并发是其核心特性之一，通过 Goroutine 和 Channel 实现。",{"path":149,"title":150,"description":151},"\u002Fgolang\u002Fadvanced\u002Fmemory","Go - 内存模型","理解 Go 的内存分配机制和内存模型。",{"path":153,"title":154,"description":155},"\u002Fgolang\u002Fadvanced\u002Fprofiling","Go - 性能分析","掌握 Go 的性能分析工具：pprof、trace、benchmark。",{"path":157,"title":158,"description":11},"\u002Fgolang\u002Fcore\u002Fgo-basic","Go - 基础语法",{"path":160,"title":161,"description":11},"\u002Fgolang\u002Fcore\u002Fgo-composite","Go - 复合类型",{"path":163,"title":164,"description":11},"\u002Fgolang\u002Fcore\u002Fgo-control","Go - 流程控制",{"path":166,"title":167,"description":168},"\u002Fgolang\u002Fcore\u002Fgo-error","Go - 错误处理","Go 使用显式的错误返回值来处理错误，而不是异常机制。",{"path":170,"title":171,"description":11},"\u002Fgolang\u002Fcore\u002Fgo-function","Go - 函数",{"path":173,"title":174,"description":11},"\u002Fgolang\u002Fcore\u002Fgo-install","Go - 环境搭建",{"path":176,"title":177,"description":11},"\u002Fgolang\u002Fcore\u002Fgo-interface","Go - 接口",{"path":179,"title":180,"description":181},"\u002Fgolang\u002Fcore\u002Fgo-module","Go - 包管理","Go Modules 是 Go 1.11 引入的官方依赖管理方案，Go 1.16 后成为默认模式。",{"path":183,"title":184,"description":185},"\u002Fgolang\u002Fdistributed\u002Fgrpc","Go - gRPC","gRPC 是 Google 开发的高性能 RPC 框架，使用 Protocol Buffers 作为序列化协议。",{"path":187,"title":188,"description":189},"\u002Fgolang\u002Fdistributed\u002Fmicroservice","Go - 微服务","微服务架构的核心组件：服务发现、负载均衡、熔断降级。",{"path":191,"title":192,"description":193},"\u002Fgolang\u002Fdistributed\u002Fmq","Go - 消息队列","使用 Go 操作 Kafka 和 RabbitMQ。",{"path":195,"title":196,"description":197},"\u002Fgolang\u002Fdistributed\u002Fredis","Go - Redis","使用 go-redis 操作 Redis，实现缓存、分布式锁等功能。",{"path":199,"title":200,"description":201},"\u002Fgolang\u002Fengineering\u002Fconfig","Go - 配置管理","使用 viper 进行配置管理，支持多种配置格式和配置中心。",{"path":203,"title":204,"description":205},"\u002Fgolang\u002Fengineering\u002Fdocker","Go - Docker 部署","使用 Docker 容器化部署 Go 应用。",{"path":207,"title":208,"description":209},"\u002Fgolang\u002Fengineering\u002Fkubernetes","Go - Kubernetes 部署","在 Kubernetes 上部署和管理 Go 应用。",{"path":211,"title":212,"description":213},"\u002Fgolang\u002Fengineering\u002Flogging","Go - 日志系统","使用 zap 和 logrus 构建高性能结构化日志系统。",{"path":215,"title":216,"description":217},"\u002Fgolang\u002Fengineering\u002Ftesting","Go - 单元测试","Go 内置了强大的测试框架，掌握测试是编写高质量代码的基础。",{"path":219,"title":220,"description":221},"\u002Fgolang\u002Fstdlib\u002Fbufio","bufio","在 Go 语言中，bufio 包提供了带缓冲的 I\u002FO 操作，能够提高读写性能。以下是一些常用的 bufio 包 API 及其详细说明：",{"path":223,"title":224,"description":225},"\u002Fgolang\u002Fstdlib\u002Fcontainer","container","在Go语言标准库中，container 包提供了几种常用的数据结构实现，这些数据结构对于高效地管理和操作数据非常有用。以下是 container 包中主要的数据结构：",{"path":227,"title":228,"description":229},"\u002Fgolang\u002Fstdlib\u002Fcrypto","crypto","在 Go 语言中，crypto 包提供了一组用于加密和解密的功能。以下是一些常用的 crypto 包及其子包的 API 及其详细说明：",{"path":231,"title":232,"description":233},"\u002Fgolang\u002Fstdlib\u002Fencoding-csv","encoding\u002Fcsv","在 Go 语言中，encoding\u002Fcsv 包提供了对 CSV（逗号分隔值）文件进行读写的功能。以下是一些常用的 encoding\u002Fcsv 包的 API 及其详细说明：",{"path":235,"title":236,"description":237},"\u002Fgolang\u002Fstdlib\u002Fencoding-json","encoding\u002Fjson","在 Go 语言中，encoding\u002Fjson 包提供了对 JSON 数据进行编码和解码的功能。以下是一些常用的 encoding\u002Fjson 包的 API 及其详细说明：",{"path":239,"title":240,"description":241},"\u002Fgolang\u002Fstdlib\u002Fencoding-xml","encoding\u002Fxml","在 Go 语言中，encoding\u002Fxml 包提供了对 XML 数据进行编码和解码的功能。以下是一些常用的 encoding\u002Fxml 包的 API 及其详细说明：",{"path":243,"title":244,"description":245},"\u002Fgolang\u002Fstdlib\u002Fflag","flag","在Go语言中，flag 包是用于处理命令行参数的标准库，它提供了一种简单而直接的方式来解析和使用命令行参数。下面是关于 flag 包的一些基本介绍和常用功能：",{"path":247,"title":248,"description":249},"\u002Fgolang\u002Fstdlib\u002Ffmt","fmt","在 Go 语言的标准库中，fmt 包是非常重要的，它提供了处理格式化输入和输出的基本工具。以下是一些 fmt 包内常用的API：",{"path":251,"title":252,"description":253},"\u002Fgolang\u002Fstdlib\u002Fhttp","net\u002Fhttp","在 Go 语言中，net\u002Fhttp 包提供了用于构建 HTTP 客户端和服务器的强大工具。以下是一些常用的 net\u002Fhttp 包的 API 及其详细说明：",{"path":255,"title":256,"description":257},"\u002Fgolang\u002Fstdlib\u002Fio","io","在 Go 语言中，io 包提供了基本的输入输出功能。以下是一些常用的 io 包的 API 及其详细说明：",{"path":259,"title":260,"description":261},"\u002Fgolang\u002Fstdlib\u002Flog","log","在 Go 语言中，log 包提供了简单的日志记录功能。以下是一些常用的 log 包的 API 及其详细说明：",{"path":263,"title":264,"description":265},"\u002Fgolang\u002Fstdlib\u002Fmath","math","在 Go 语言中，math 包提供了基本的数学函数和常量。以下是一些常用的 math 包的 API 及其详细说明：",{"path":267,"title":268,"description":11},"\u002Fgolang\u002Fstdlib\u002Fnet","net",{"path":270,"title":271,"description":272},"\u002Fgolang\u002Fstdlib\u002Fos","os","在Go语言中，os 包是一个非常重要且常用的标准库，它提供了与操作系统交互的功能，包括文件操作、环境变量管理、进程管理等。下面是一些 os 包中常用的功能和API：",{"path":274,"title":275,"description":276},"\u002Fgolang\u002Fstdlib\u002Fsort","order","在 Go 语言中，sort 包提供了对切片和用户定义的集合进行排序的函数。它实现了常见的排序算法，如快速排序（Quicksort）和堆排序（Heapsort），并且为自定义集合提供了接口，使得用户可以根据特定的需求进行排序。",{"path":278,"title":279,"description":280},"\u002Fgolang\u002Fstdlib\u002Fstrconv","strconv","在 Go 语言中，strconv 包提供了字符串和基本数据类型之间的转换函数，例如将整数转换为字符串、字符串转换为整数，以及其他类型之间的转换。这些功能非常有用，特别是在处理用户输入或从外部数据源读取数据时。",{"path":282,"title":283,"description":284},"\u002Fgolang\u002Fstdlib\u002Ftime","time","在 Go 语言中，time 包提供了处理时间和日期的功能。以下是一些常用的 time 包的 API 及其详细说明：",{"path":286,"title":287,"description":11},"\u002Fgolang\u002Fweb\u002Fgin\u002Ferror","Gin - 错误处理",{"path":289,"title":290,"description":11},"\u002Fgolang\u002Fweb\u002Fgin\u002Ffile","Gin - 文件处理",{"path":292,"title":293,"description":11},"\u002Fgolang\u002Fweb\u002Fgin\u002Fmiddleware","Gin - 中间件",{"path":295,"title":296,"description":297},"\u002Fgolang\u002Fweb\u002Fgin\u002Fquickstart","Gin - 快速开始","Gin 是目前最流行的 Go Web 框架，以高性能和简洁 API 著称。",{"path":299,"title":300,"description":11},"\u002Fgolang\u002Fweb\u002Fgin\u002Frequest","Gin - 请求处理",{"path":302,"title":303,"description":11},"\u002Fgolang\u002Fweb\u002Fgin\u002Fresponse","Gin - 响应处理",{"path":305,"title":306,"description":11},"\u002Fgolang\u002Fweb\u002Fgin\u002Frouter","Gin - 路由",{"path":308,"title":309,"description":11},"\u002Fgolang\u002Fweb\u002Fgin\u002Fvalidation","Gin - 参数校验",{"path":311,"title":312,"description":11},"\u002Fgolang\u002Fweb\u002Fgorm\u002Fassociation","GORM - 关联关系",{"path":314,"title":315,"description":11},"\u002Fgolang\u002Fweb\u002Fgorm\u002Fcrud","GORM - CRUD 操作",{"path":317,"title":318,"description":11},"\u002Fgolang\u002Fweb\u002Fgorm\u002Fmodel","GORM - 模型定义",{"path":320,"title":321,"description":11},"\u002Fgolang\u002Fweb\u002Fgorm\u002Fperformance","GORM - 日志与性能",{"path":323,"title":324,"description":11},"\u002Fgolang\u002Fweb\u002Fgorm\u002Fquery","GORM - 高级查询",{"path":326,"title":327,"description":328},"\u002Fgolang\u002Fweb\u002Fgorm\u002Fquickstart","GORM - 快速开始","GORM 是 Go 语言最流行的 ORM 库，功能强大，使用简单。",{"path":330,"title":331,"description":11},"\u002Fgolang\u002Fweb\u002Fgorm\u002Ftransaction","GORM - 事务与 Hook",{"path":333,"title":334,"description":335},"\u002Finterview\u002Fbasic","计算机基础面经","本章节汇总了面试中常见的通用技术概念，不局限于特定语言或数据库，是考察技术内功的关键考点。",{"path":337,"title":338,"description":339},"\u002Finterview\u002Fgolang","Golang 面试题","Go 语言面试高频考点，覆盖基础语法、数据结构、并发编程、内存管理、GC、调度器等核心知识。",{"path":341,"title":342,"description":343},"\u002Finterview\u002Fk8s","Kubernetes 面试题","Kubernetes（K8s）面试高频考点，覆盖架构原理、核心资源、网络存储、调度策略、运维监控等核心知识。",{"path":345,"title":346,"description":347},"\u002Finterview\u002Fmysql","MySQL 面试题","MySQL 数据库面试高频考点，覆盖索引、事务、锁、优化、主从复制等核心知识。",{"path":349,"title":350,"description":351},"\u002Finterview\u002Fredis","Redis 面试题","Redis 面试高频考点，覆盖数据结构、持久化、集群、缓存一致性、性能优化等核心知识。",{"path":353,"title":354,"description":355},"\u002Finterview\u002Frocketmq","RocketMQ 面试题","RocketMQ 面试高频考点，覆盖消息模型、可靠性、顺序消息、事务消息、存储与高可用等核心知识。",{"path":357,"title":358,"description":11},"\u002Fother\u002Fjava\u002Fcollection\u002Flist-arraylist","List - ArrayList 源码解析",{"path":360,"title":361,"description":11},"\u002Fother\u002Fjava\u002Fcollection\u002Flist-linkedlist","List - LinkedList 源码解析",{"path":363,"title":364,"description":11},"\u002Fother\u002Fjava\u002Fcollection\u002Flist-stack","List - Satck源码解析",{"path":366,"title":367,"description":11},"\u002Fother\u002Fjava\u002Fcollection\u002Flist-vectore","List - Vector 源码解析",{"path":369,"title":370,"description":11},"\u002Fother\u002Fjava\u002Fcollection\u002Fmap-hashmap","Map - HashMap 源码解析",{"path":372,"title":373,"description":11},"\u002Fother\u002Fjava\u002Fcollection\u002Fmap-linkedhashmap","Map - LinkedHashMap 源码解析",{"path":375,"title":376,"description":11},"\u002Fother\u002Fjava\u002Fcollection\u002Fmap-treemap","Map - TreeMap 源码解析",{"path":378,"title":379,"description":11},"\u002Fother\u002Fjava\u002Fcollection\u002Fqueue-deque","Queue - Deque 接口解析",{"path":381,"title":382,"description":11},"\u002Fother\u002Fjava\u002Fcollection\u002Fqueue-queue","Queue - Queue 接口解析",{"path":384,"title":385,"description":11},"\u002Fother\u002Fjava\u002Fcollection\u002Fset-hashset","Set - HashSet源码解析",{"path":387,"title":388,"description":11},"\u002Fother\u002Fjava\u002Fcollection\u002Fset-linkedhashset","Set - LinkedHashSet 源码解析",{"path":390,"title":391,"description":392},"\u002Fother\u002Fjava\u002Fcollection\u002Fset-treeset","Set - TreeSet源码解析","TreeSet 是一个 Set 集合接口的实现类，与 HashSet 类似，其底层也是通过维护了一个 TreeMap 对象来封装了一些实现方法，故本篇不再对 TreeSet 的底层原理进行详细说明，仅对常用 API 做简单介绍，如需了解 TreeMap 的底层实现原理，请移步 Map - HashMap 源码解析",{"path":394,"title":395,"description":11},"\u002Fother\u002Fjava\u002Fcore\u002Fannotation","Java核心 - 注解",{"path":397,"title":398,"description":11},"\u002Fother\u002Fjava\u002Fcore\u002Fbasic-grammar","Java核心 - 基础语法",{"path":400,"title":401,"description":11},"\u002Fother\u002Fjava\u002Fcore\u002Fclass-and-object","Java核心 - 面向对象",{"path":403,"title":404,"description":11},"\u002Fother\u002Fjava\u002Fcore\u002Fcommon-classes","Java核心 - 常用类",{"path":406,"title":407,"description":11},"\u002Fother\u002Fjava\u002Fcore\u002Fexception","Java核心 - 异常处理",{"path":409,"title":410,"description":11},"\u002Fother\u002Fjava\u002Fcore\u002Fgenerics","Java核心 - 泛型",{"path":412,"title":413,"description":11},"\u002Fother\u002Fjava\u002Fcore\u002Fjdk-env-path","Java核心 - 环境搭建",{"path":415,"title":416,"description":11},"\u002Fother\u002Fjava\u002Fcore\u002Freflection","Java核心 - 反射",{"path":418,"title":419,"description":11},"\u002Fother\u002Fjava\u002Fcore\u002Fstring","Java核心 - String 字符串",{"path":421,"title":422,"description":423},"\u002Fother\u002Fjava\u002Fio\u002Fbuffer-stream","Java IO - 缓冲流","缓冲流是对基本流的包装，通过内置缓冲区减少系统调用次数，大幅提升读写效率。",{"path":425,"title":426,"description":427},"\u002Fother\u002Fjava\u002Fio\u002Fbyte-stream","Java IO - 字节流","字节流是 Java IO 中最基本的流类型，以字节（byte）为单位进行数据读写，可以处理任意类型的文件。",{"path":429,"title":430,"description":431},"\u002Fother\u002Fjava\u002Fio\u002Fchar-stream","Java IO - 字符流","字符流以字符为单位进行读写，专门用于处理文本文件。相比字节流，字符流能够正确处理字符编码，避免中文乱码问题。",{"path":433,"title":434,"description":11},"\u002Fother\u002Fjava\u002Fio\u002Ffile","Java IO - File 类",{"path":436,"title":437,"description":11},"\u002Fother\u002Fjava\u002Fio\u002Fio-stream-system","Java IO - IO流概述",{"path":439,"title":440,"description":441},"\u002Fother\u002Fjava\u002Fio\u002Fnio","Java IO - NIO","NIO（New IO）是 JDK 1.4 引入的新 IO 模型，提供了更高效的 IO 操作方式，支持非阻塞 IO 和多路复用。",{"path":443,"title":444,"description":445},"\u002Fother\u002Fjava\u002Fjvm\u002Fclass-loading","类加载机制","类加载机制是 JVM 将 .class 文件加载到内存，并对数据进行校验、转换解析和初始化，最终形成可被 JVM 直接使用的 Java 类型的过程。",{"path":447,"title":448,"description":449},"\u002Fother\u002Fjava\u002Fjvm\u002Fgarbage-collection","垃圾回收","垃圾回收（Garbage Collection，GC）是 JVM 自动管理内存的机制，负责回收不再使用的对象所占用的内存。",{"path":451,"title":452,"description":453},"\u002Fother\u002Fjava\u002Fjvm\u002Fjvm-memory","JVM 内存结构","JVM 在执行 Java 程序时，会把它管理的内存划分为若干个不同的数据区域。这些区域有各自的用途、创建和销毁时间。",{"path":455,"title":456,"description":457},"\u002Fother\u002Fjava\u002Fjvm\u002Fjvm-tuning","JVM 调优","JVM 调优是优化 Java 应用性能的重要手段，主要包括参数配置、性能监控和问题排查。",{"path":459,"title":460,"description":461},"\u002Fother\u002Fjava\u002Fthread\u002Fatomic","原子类","Java 原子类（Atomic Classes）提供了一种无锁的线程安全方式，基于 CAS（Compare-And-Swap）操作实现。",{"path":463,"title":464,"description":465},"\u002Fother\u002Fjava\u002Fthread\u002Fcompletable-future","CompletableFuture","CompletableFuture 是 JDK 8 引入的异步编程工具，实现了 Future 和 CompletionStage 接口，支持函数式编程和链式调用。",{"path":467,"title":468,"description":469},"\u002Fother\u002Fjava\u002Fthread\u002Fconcurrent-collections","并发集合","Java 并发包提供了多种线程安全的集合类，用于替代传统的同步集合（如 Collections.synchronizedList）。",{"path":471,"title":472,"description":473},"\u002Fother\u002Fjava\u002Fthread\u002Fconcurrent-utils","并发工具类","Java 并发包提供了多种实用的并发工具类，用于控制线程之间的协调与同步。",{"path":475,"title":476,"description":11},"\u002Fother\u002Fjava\u002Fthread\u002Fsynchronized-lock","同步机制",{"path":478,"title":479,"description":11},"\u002Fother\u002Fjava\u002Fthread\u002Fthread-basic","线程基础",{"path":481,"title":482,"description":11},"\u002Fother\u002Fjava\u002Fthread\u002Fthread-pool","线程池",{"path":484,"title":485,"description":486},"\u002Fother\u002Fspring-series\u002Fspring\u002Fannotations-beans","Spring - 基于注解管理Bean","从 Java 5 开始，Java 增加了对注解（Annotation）的支持，它是代码中的一种特殊标记，可以在编译、类加载和运行时被读取，执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下，在源代码中嵌入补充信息。",{"path":488,"title":489,"description":490},"\u002Fother\u002Fspring-series\u002Fspring\u002Fimplement-ioc","Spring - 原理手写IoC","Spring 框架的 IOC 是基于 Java 反射机制实现的，在学习手写 IoC 之前，你需要具备一定的 Java 反射相关的知识，参考本站内的 Java 教程。",{"path":492,"title":493,"description":11},"\u002Fother\u002Fspring-series\u002Fspring\u002Fintroduction-case","Spring - 入门案例",{"path":495,"title":496,"description":11},"\u002Fother\u002Fspring-series\u002Fspring\u002Fspring-aop","Spring - 面向切面AOP",{"path":498,"title":499,"description":11},"\u002Fother\u002Fspring-series\u002Fspring\u002Fspring-aot","Spring - AOT提前编译",{"path":501,"title":502,"description":503},"\u002Fother\u002Fspring-series\u002Fspring\u002Fspring-data-validation","Spring - 数据校验","在开发中，我们经常遇到参数校验的需求，比如用户注册的时候，要校验用户名不能为空、用户名长度不超过20个字符、手机号是合法的手机号格式等等。如果使用普通方式，我们会把校验的代码和真正的业务处理逻辑耦合在一起，而且如果未来要新增一种校验逻辑也需要在修改多个地方。而spring validation允许通过注解的方式来定义对象校验规则，把校验和业务逻辑分离开，让代码编写更加方便。Spring Validation其实就是对Hibernate Validator进一步的封装，方便在Spring中使用。",{"path":505,"title":506,"description":11},"\u002Fother\u002Fspring-series\u002Fspring\u002Fspring-i18n","Spring - 国际化i18n",{"path":508,"title":509,"description":510},"\u002Fother\u002Fspring-series\u002Fspring\u002Fspring-ioc","Spring - IOC容器","IoC 是 Inversion of Control 的简写，译为“控制反转”，它不是一门技术，而是一种设计思想，是一个重要的面向对象编程法则，能够指导我们如何设计出松耦合、更优良的程序。",{"path":512,"title":513,"description":514},"\u002Fother\u002Fspring-series\u002Fspring\u002Fspring-junit","Spring - 单元测试JUnit","在之前的测试方法中，几乎都能看到以下的两行代码：",{"path":516,"title":517,"description":11},"\u002Fother\u002Fspring-series\u002Fspring\u002Fspring-resources","Spring - 资源操作",{"path":519,"title":520,"description":11},"\u002Fother\u002Fspring-series\u002Fspring\u002Fspring-summarize","Spring - Spring概述",{"path":522,"title":523,"description":11},"\u002Fother\u002Fspring-series\u002Fspring\u002Fspring-transaction","Spring - 事务",{"path":525,"title":526,"description":11},"\u002Fother\u002Fspring-series\u002Fspring\u002Fxml-beans","Spring - 基于XML管理Bean",{"path":528,"title":529,"description":11},"\u002Fother\u002Fspring-series\u002Fspringboot\u002Fspringboot-config","SpringBoot - 配置详解",{"path":531,"title":532,"description":11},"\u002Fother\u002Fspring-series\u002Fspringboot\u002Fspringboot-data","SpringBoot - 数据访问",{"path":534,"title":535,"description":11},"\u002Fother\u002Fspring-series\u002Fspringboot\u002Fspringboot-quickstart","SpringBoot - 快速入门",{"path":537,"title":538,"description":11},"\u002Fother\u002Fspring-series\u002Fspringboot\u002Fspringboot-web","SpringBoot - Web 开发",{"path":540,"title":541,"description":11},"\u002Fother\u002Fspring-series\u002Fspringcloud\u002Fspringcloud-config","SpringCloud - 配置中心",{"path":543,"title":544,"description":11},"\u002Fother\u002Fspring-series\u002Fspringcloud\u002Fspringcloud-discovery","SpringCloud - 服务注册与发现",{"path":546,"title":547,"description":11},"\u002Fother\u002Fspring-series\u002Fspringcloud\u002Fspringcloud-feign","SpringCloud - 服务调用",{"path":549,"title":550,"description":11},"\u002Fother\u002Fspring-series\u002Fspringcloud\u002Fspringcloud-gateway","SpringCloud - 服务网关",{"path":552,"title":553,"description":11},"\u002Fother\u002Fspring-series\u002Fspringcloud\u002Fspringcloud-introduction","SpringCloud - 微服务概述",{"path":555,"title":556,"description":11},"\u002Fother\u002Fspring-series\u002Fspringcloud\u002Fspringcloud-sentinel","SpringCloud - 服务保护",{"path":558,"title":559,"description":11},"\u002Fother\u002Fspring-series\u002Fspringmvc\u002Fspringmvc-databind","SpringMVC - 数据绑定与转换",{"path":561,"title":562,"description":11},"\u002Fother\u002Fspring-series\u002Fspringmvc\u002Fspringmvc-exception","SpringMVC - 异常处理",{"path":564,"title":565,"description":11},"\u002Fother\u002Fspring-series\u002Fspringmvc\u002Fspringmvc-interceptor","SpringMVC - 拦截器",{"path":567,"title":568,"description":11},"\u002Fother\u002Fspring-series\u002Fspringmvc\u002Fspringmvc-introduction","SpringMVC - 简介与环境搭建",{"path":570,"title":571,"description":11},"\u002Fother\u002Fspring-series\u002Fspringmvc\u002Fspringmvc-request","SpringMVC - 请求处理",{"path":573,"title":574,"description":11},"\u002Fother\u002Fspring-series\u002Fspringmvc\u002Fspringmvc-response","SpringMVC - 响应处理",{"path":576,"title":577,"description":578},"\u002Fproject\u002Frocket-leaf\u002Farchitecture","项目架构","Rocket-Leaf 的目录结构、模块划分、数据流向，以及各层之间的依赖关系。",{"path":580,"title":581,"description":582},"\u002Fproject\u002Frocket-leaf\u002Fbackend-layers","后端分层设计","Rocket-Leaf 的 model \u002F rocketmq \u002F service 三层结构，以及服务之间的依赖关系与设计取舍。",{"path":584,"title":585,"description":586},"\u002Fproject\u002Frocket-leaf\u002Fclient-manager","RocketMQ 客户端管理器","AdminClientManager 的多客户端池、默认连接懒加载、自动重连重试的设计与实现。",{"path":588,"title":589,"description":590},"\u002Fproject\u002Frocket-leaf\u002Fencryption","连接信息加密存储","AES-256-GCM + SHA-256 字段级派生密钥的实现，以及如何在不破坏兼容性的前提下为历史明文数据做透明迁移。",{"path":592,"title":593,"description":594},"\u002Fproject\u002Frocket-leaf\u002Ffrontend","前端结构与类型绑定","React + Vite 目录组织、自动生成的 Wails 绑定、api 薄封装与自定义 hooks 的职责划分。",{"path":596,"title":597,"description":598},"\u002Fproject\u002Frocket-leaf","项目简介","Rocket-Leaf 是一款基于 Wails v3 构建的跨平台 RocketMQ 桌面管理客户端，Go 后端 + React 前端。本文档系列拆解它的架构与关键实现。",{"path":600,"title":601,"description":602},"\u002Fproject\u002Frocket-leaf\u002Fwails-v3","Wails v3 入门","Wails v3 的核心概念、Service 绑定机制，以及 Rocket-Leaf 是如何用它把 Go 后端和 React 前端打通的。",{"path":604,"title":605,"description":11},"\u002Ftutorials\u002Fcloud\u002Fdocker\u002Fdocker-basic","Docker - 入门基础",{"path":607,"title":608,"description":609},"\u002Ftutorials\u002Fcloud\u002Fdocker\u002Fdocker-compose","Docker - Compose","在部署应用时，常常使用到不止一个容器，那么在部署容器的时候就需要一个一个进行部署，这样的部署过程也相对来说比较繁琐复杂，也容易出问题，那么有没有一种更为简单的方法呢？",{"path":611,"title":612,"description":613},"\u002Ftutorials\u002Fcloud\u002Fdocker\u002Fdocker-container-connection","Docker - 容器互联","在上一个章节中我们学习了 Docker 容器的端口映射，可以将 Docker 容器和本地以及网络中的端口进行连接起来。",{"path":615,"title":616,"description":11},"\u002Ftutorials\u002Fcloud\u002Fdocker\u002Fdocker-dockerfile","Docker - Dockerfile",{"path":618,"title":619,"description":11},"\u002Ftutorials\u002Fcloud\u002Fdocker\u002Fdocker-helloworld","Docker - HelloWorld",{"path":621,"title":622,"description":11},"\u002Ftutorials\u002Fcloud\u002Fdocker\u002Fdocker-install","Docker - 安装",{"path":624,"title":625,"description":11},"\u002Ftutorials\u002Fcloud\u002Fdocker\u002Fdocker-introduce","Docker - 简介",{"path":627,"title":628,"description":11},"\u002Ftutorials\u002Fcloud\u002Fdocker\u002Fdocker-object","Docker - 镜像、容器、仓库",{"path":630,"title":631,"description":632},"\u002Ftutorials\u002Fcloud\u002Fdocker\u002Fdocker-warehouse","Docker - 仓库管理","仓库是集中存放资源的地方，代码仓库是存放代码的，那么Docker 中的仓库就是存放 Docker 镜像的。",{"path":634,"title":635,"description":636},"\u002Ftutorials\u002Fcloud\u002Fdocker\u002Fdocker-web-containers","Docker - WEB应用实例","在之前的章节中，仅对普通容器进行了演示，但在实际中常常使用到 Docker 容器中的 WEB 应用程序。",{"path":638,"title":639,"description":11},"\u002Ftutorials\u002Fcloud\u002Fkubernetes\u002Fk8s-config","Kubernetes - ConfigMap 与 Secret",{"path":641,"title":642,"description":11},"\u002Ftutorials\u002Fcloud\u002Fkubernetes\u002Fk8s-helm","Kubernetes - Helm 包管理",{"path":644,"title":645,"description":11},"\u002Ftutorials\u002Fcloud\u002Fkubernetes\u002Fk8s-install","Kubernetes - 集群安装",{"path":647,"title":648,"description":11},"\u002Ftutorials\u002Fcloud\u002Fkubernetes\u002Fk8s-introduction","Kubernetes - 简介与架构",{"path":650,"title":651,"description":11},"\u002Ftutorials\u002Fcloud\u002Fkubernetes\u002Fk8s-kubectl","Kubernetes - kubectl 命令行工具",{"path":653,"title":654,"description":11},"\u002Ftutorials\u002Fcloud\u002Fkubernetes\u002Fk8s-monitoring","Kubernetes - 监控与日志",{"path":656,"title":657,"description":11},"\u002Ftutorials\u002Fcloud\u002Fkubernetes\u002Fk8s-network-security","Kubernetes - 网络与安全",{"path":659,"title":660,"description":11},"\u002Ftutorials\u002Fcloud\u002Fkubernetes\u002Fk8s-service","Kubernetes - Service 与 Ingress",{"path":662,"title":663,"description":11},"\u002Ftutorials\u002Fcloud\u002Fkubernetes\u002Fk8s-storage","Kubernetes - 持久化存储",{"path":665,"title":666,"description":11},"\u002Ftutorials\u002Fcloud\u002Fkubernetes\u002Fk8s-workload","Kubernetes - 工作负载资源",{"path":668,"title":669,"description":11},"\u002Ftutorials\u002Fcloud\u002Flinux\u002Flinux-bash","Linux - Bash 基础语法",{"path":671,"title":672,"description":11},"\u002Ftutorials\u002Fcloud\u002Flinux\u002Flinux-file-directory","Linux - 文件与目录操作",{"path":674,"title":675,"description":11},"\u002Ftutorials\u002Fcloud\u002Flinux\u002Flinux-network","Linux - 网络配置",{"path":677,"title":678,"description":11},"\u002Ftutorials\u002Fcloud\u002Flinux\u002Flinux-package","Linux - 软件包管理",{"path":680,"title":681,"description":11},"\u002Ftutorials\u002Fcloud\u002Flinux\u002Flinux-process","Linux - 进程管理",{"path":683,"title":684,"description":11},"\u002Ftutorials\u002Fcloud\u002Flinux\u002Flinux-scripts","Linux - 常用脚本示例",{"path":686,"title":687,"description":11},"\u002Ftutorials\u002Fcloud\u002Flinux\u002Flinux-service","Linux - 服务管理",{"path":689,"title":690,"description":11},"\u002Ftutorials\u002Fcloud\u002Flinux\u002Flinux-user-permission","Linux - 用户与权限管理",{"path":692,"title":693,"description":11},"\u002Ftutorials\u002Fcloud\u002Fnginx\u002Fnginx-https","Nginx - HTTPS 配置",{"path":695,"title":696,"description":11},"\u002Ftutorials\u002Fcloud\u002Fnginx\u002Fnginx-install","Nginx - 安装与配置",{"path":698,"title":699,"description":11},"\u002Ftutorials\u002Fcloud\u002Fnginx\u002Fnginx-loadbalance","Nginx - 负载均衡",{"path":701,"title":702,"description":11},"\u002Ftutorials\u002Fcloud\u002Fnginx\u002Fnginx-optimization","Nginx - 性能优化",{"path":704,"title":705,"description":11},"\u002Ftutorials\u002Fcloud\u002Fnginx\u002Fnginx-proxy","Nginx - 反向代理",{"path":707,"title":708,"description":11},"\u002Ftutorials\u002Fcloud\u002Fnginx\u002Fnginx-static","Nginx - 静态资源服务",{"path":710,"title":711,"description":11},"\u002Ftutorials\u002Fcloud\u002Fnginx\u002Fnginx-vhost","Nginx - 虚拟主机配置",{"path":713,"title":714,"description":715},"\u002Ftutorials\u002Fdatabase\u002Fmysql\u002Fmysql-architecture","MySQL 高可用架构","主从复制、读写分离、分库分表。",{"path":717,"title":718,"description":719},"\u002Ftutorials\u002Fdatabase\u002Fmysql\u002Fmysql-index","MySQL 索引","索引是帮助 MySQL 高效获取数据的有序数据结构。",{"path":721,"title":722,"description":723},"\u002Ftutorials\u002Fdatabase\u002Fmysql\u002Fmysql-lock","MySQL 锁","锁用于解决并发访问时的数据一致性问题。",{"path":725,"title":726,"description":727},"\u002Ftutorials\u002Fdatabase\u002Fmysql\u002Fmysql-optimize","MySQL 性能优化","SQL 优化是后端开发必备技能。",{"path":729,"title":730,"description":731},"\u002Ftutorials\u002Fdatabase\u002Fmysql\u002Fmysql-transaction","MySQL 事务","事务是一组不可分割的操作，要么全部成功，要么全部失败。",{"path":733,"title":734,"description":735},"\u002Ftutorials\u002Fdatabase\u002Fmysql\u002Fsql-advanced","SQL 进阶","多表查询、子查询、函数、视图、存储过程。",{"path":737,"title":738,"description":739},"\u002Ftutorials\u002Fdatabase\u002Fmysql\u002Fsql-basic","SQL 基础","SQL（Structured Query Language）是操作关系型数据库的标准语言。",{"path":741,"title":742,"description":743},"\u002Ftutorials\u002Fdatabase\u002Fredis\u002Fredis-advanced","Redis 进阶功能","事务、发布订阅、Lua 脚本、Pipeline。",{"path":745,"title":746,"description":747},"\u002Ftutorials\u002Fdatabase\u002Fredis\u002Fredis-basic","Redis 基础","Redis 安装配置与基本命令。",{"path":749,"title":750,"description":751},"\u002Ftutorials\u002Fdatabase\u002Fredis\u002Fredis-cluster","Redis 高可用","主从复制、哨兵、Cluster 集群。",{"path":753,"title":754,"description":755},"\u002Ftutorials\u002Fdatabase\u002Fredis\u002Fredis-datatype","Redis 数据类型","Redis 5 种基本数据类型 + 4 种特殊类型。",{"path":757,"title":758,"description":759},"\u002Ftutorials\u002Fdatabase\u002Fredis\u002Fredis-optimize","Redis 性能优化","内存优化、缓存问题、最佳实践。",{"path":761,"title":762,"description":763},"\u002Ftutorials\u002Fdatabase\u002Fredis\u002Fredis-persistence","Redis 持久化","Redis 提供 RDB 和 AOF 两种持久化方式。",{"path":765,"title":766,"description":767},"\u002Ftutorials\u002Fdatabase\u002Fredis\u002Fredis-principle","Redis 底层原理","数据结构、线程模型、网络模型。",{"path":769,"title":770,"description":771},"\u002Ftutorials\u002Fdev-idea\u002Fdesign-patterns\u002Fbehaiver-patterns\u002Fobserver-pattern","观察者模式","观察者模式属于行为型模式，定义了对象之间的一对多的依赖关系，在这种模式中，当一个对象的状态发生变化时，所有依赖于它的对象都会得到通知，并且执行相关操作。观察者模式又被成为“发布—订阅模式”，即发布者发生改变后，会通知所有订阅者。",{"path":773,"title":774,"description":11},"\u002Ftutorials\u002Fdev-idea\u002Fdesign-patterns\u002Fcreate-patterns\u002Ffactory-pattern","工厂模式",{"path":776,"title":777,"description":778},"\u002Ftutorials\u002Fdev-idea\u002Fdesign-patterns\u002Fcreate-patterns\u002Fsingleton-pattern","单例模式","单例模式是最常用的设计模式之一，他可以保证在整个应用中，某个类只存在一个实例化对象，即全局使用到该类的只有一个对象，这种模式在需要限制某些类的实例数量时非常有用，通常全局只需要一个该对象即可，如一些配置文件映射对象、数据库连接对象等。",{"path":780,"title":781,"description":782},"\u002Ftutorials\u002Fdev-idea\u002Fdesign-patterns\u002Fstructural-patterns\u002Fadapter-pattern","适配器模式","适配器模式是一种结构型模式，可以将一个类的接口转换成客户端所期望的另一种接口，适配器模式可以帮助开发人员在不修改现有代码的情况下，将不兼容的类组合在一起。",{"path":784,"title":785,"description":786},"\u002Ftutorials\u002Fdev-tools\u002Fgit\u002Fgit-basic-operations","Git 创建版本库","在 Git 上创建版本库有两种方式，一种是直接拷贝远程 Git 仓库到本地，另外一种是我们自己创建本地的版本库。",{"path":788,"title":789,"description":11},"\u002Ftutorials\u002Fdev-tools\u002Fgit\u002Fgit-branch-manage","Git 分支管理",{"path":791,"title":792,"description":11},"\u002Ftutorials\u002Fdev-tools\u002Fgit\u002Fgit-content-operations","Git 仓库内容操作",{"path":794,"title":795,"description":11},"\u002Ftutorials\u002Fdev-tools\u002Fgit\u002Fgit-introduce-install","Git 介绍和安装",{"path":797,"title":798,"description":11},"\u002Ftutorials\u002Fdev-tools\u002Fgit\u002Fgit-remote-manage","Git 远程管理",{"path":800,"title":801,"description":11},"\u002Ftutorials\u002Fdev-tools\u002Fgit\u002Fgit-workspace-index-repo","Git 工作原理",{"path":803,"title":804,"description":11},"\u002Ftutorials\u002Fdev-tools\u002Fhomebrew","HomeBrew 教程",{"path":806,"title":807,"description":11},"\u002Ftutorials\u002Fdev-tools\u002Fidea\u002Fshortcuts","快捷键",{"path":809,"title":810,"description":11},"\u002Ftutorials\u002Fdev-tools\u002Fmaven\u002Fintroduce-install-config","Maven - 介绍、安装、配置",{"path":812,"title":813,"description":11},"\u002Ftutorials\u002Ffront-end\u002Fvue3\u002Fbasic-knowledge","2. 基础知识",{"path":815,"title":816,"description":817},"\u002Ftutorials\u002Ffront-end\u002Fvue3\u002Fcomponent-communication","9. 组件通信","在前面的章节内，介绍了 Vue 中最核心的内容——组件的介绍和使用，和 Java 等编程语言相反，组件并不近似于这些变成语言中的类，类可以通过类或者其实例化的对象来相互交互，但 Vue 组件之间的作用域是相互独立的，这就意味着不同组件之间的数据无法相互引用。",{"path":819,"title":820,"description":821},"\u002Ftutorials\u002Ffront-end\u002Fvue3\u002Fcomputed","4. 计算属性","虽然直接在模板中使用表达式方便，但是如果在模板中添加很多逻辑，会让模板变的臃肿且难维护，耦合度较高。有没有一种简单的方式来实现呢？答案是有的。",{"path":823,"title":824,"description":11},"\u002Ftutorials\u002Ffront-end\u002Fvue3\u002Fcreate-vue-project","1. 环境搭建及安装",{"path":826,"title":827,"description":828},"\u002Ftutorials\u002Ffront-end\u002Fvue3\u002Flife-cycle","6. 生命周期","生命周期是指组件从创建、挂载、更新到销毁的整个过程中所经历的一系列阶段。在 Vue 中，每个组件都有自己的生命周期，可以通过生命周期钩子函数来监听和处理组件在不同阶段的行为和状态。",{"path":830,"title":831,"description":11},"\u002Ftutorials\u002Ffront-end\u002Fvue3\u002Fother-api","10. 其他 API",{"path":833,"title":834,"description":11},"\u002Ftutorials\u002Ffront-end\u002Fvue3\u002Fpinia","8. Pinia",{"path":836,"title":837,"description":11},"\u002Ftutorials\u002Ffront-end\u002Fvue3\u002Frouter","7. 路由",{"path":839,"title":840,"description":11},"\u002Ftutorials\u002Ffront-end\u002Fvue3\u002Ftemplate-grammar","3. 指令及模板语法",{"path":842,"title":843,"description":11},"\u002Ftutorials\u002Ffront-end\u002Fvue3\u002Fvue3-new-component","11. Vue3 新组件",{"path":845,"title":846,"description":847},"\u002Ftutorials\u002Ffront-end\u002Fvue3\u002Fwatch","5. 监视","Watch 是 Vue 提供的一个用于监视响应式数据变化并执行相应操作的 API，能够对响应式数据的变化做出一些操作的功能。Vue3 中的 Watch 支持多种用法，包括监视响应式对象、ref 对象、数组、函数等。",{"path":849,"title":850,"description":11},"\u002Ftutorials\u002Fmq\u002Fkafka\u002Fkafka-introduction","Kafka 简介与安装",{"path":852,"title":853,"description":11},"\u002Ftutorials\u002Fmq\u002Fkafka\u002Fkafka-producer-consumer","Kafka 生产者与消费者",{"path":855,"title":856,"description":857},"\u002Ftutorials\u002Fmq\u002Fkafka\u002Fkafka-springboot","Spring Boot 整合 Kafka","Spring Kafka 提供了对 Apache Kafka 的便捷集成。",{"path":859,"title":860,"description":861},"\u002Ftutorials\u002Fmq\u002Frabbitmq\u002Frabbitmq-exchange","RabbitMQ Exchange 详解","Exchange（交换机）是 RabbitMQ 的核心组件，负责接收生产者发送的消息，并根据规则将消息路由到一个或多个队列。",{"path":863,"title":864,"description":11},"\u002Ftutorials\u002Fmq\u002Frabbitmq\u002Frabbitmq-introduction","RabbitMQ 简介与安装",{"path":866,"title":867,"description":868},"\u002Ftutorials\u002Fmq\u002Frabbitmq\u002Frabbitmq-reliability","RabbitMQ 消息可靠性","消息可靠性是消息队列的核心要求，RabbitMQ 提供了多种机制来保证消息不丢失。",{"path":870,"title":871,"description":872},"\u002Ftutorials\u002Fmq\u002Frabbitmq\u002Frabbitmq-springboot","Spring Boot 整合 RabbitMQ","Spring AMQP 提供了对 RabbitMQ 的便捷集成，大大简化了开发工作。",{"path":874,"title":875,"description":11},"\u002Ftutorials\u002Fmq\u002Frocketmq\u002Frocketmq-client","RocketMQ 客户端使用",{"path":877,"title":878,"description":11},"\u002Ftutorials\u002Fmq\u002Frocketmq\u002Frocketmq-concepts","RocketMQ 核心概念",{"path":880,"title":881,"description":11},"\u002Ftutorials\u002Fmq\u002Frocketmq\u002Frocketmq-installation","RocketMQ 安装部署",{"path":883,"title":884,"description":885},"\u002Ftutorials\u002Fmq\u002Frocketmq\u002Frocketmq-message-type","RocketMQ 消息类型","RocketMQ 支持多种消息类型，满足不同业务场景需求。",{"id":887,"title":523,"body":888,"description":11,"extension":6059,"meta":6060,"navigation":1327,"path":522,"seo":6061,"stem":6062,"__hash__":6063},"docs\u002Fother\u002Fspring-series\u002Fspring\u002Fspring-transaction.md",{"type":889,"value":890,"toc":6032},"minimark",[891,896,900,904,910,913,918,1159,1164,1216,1219,1224,1227,1571,1576,1771,1775,1778,1783,1892,1895,2020,2023,2207,2210,2355,2358,2435,2438,2503,2510,2513,2516,2519,2522,2525,2530,2533,2538,2541,2544,2547,2552,2555,2560,2563,2566,2569,2727,2730,2740,2743,2746,2749,2760,2763,2788,2791,2794,2799,2805,2832,2837,3213,3218,3221,3307,3310,3348,3351,3484,3487,3559,3562,3762,3765,3770,3865,3870,3873,3876,3879,3882,3885,3890,3893,3896,3902,3908,3911,3914,3917,4013,4016,4121,4124,4127,4132,4134,4137,4141,4144,4147,4151,4156,4159,4164,4253,4258,4261,4264,4269,4273,4277,4280,4283,4287,4423,4427,4430,4437,4443,4447,4451,4454,4457,4471,4475,4573,4577,4580,4586,4590,4594,4597,4600,4627,4630,4702,4705,4756,4760,4856,4860,4864,4867,4870,4873,4902,4907,4910,4954,4957,5079,5082,5135,5138,5142,5145,5148,5151,5154,5157,5162,5521,5525,5674,5678,5681,5684,5688,5691,6022,6028],[892,893,895],"h2",{"id":894},"jdbctemplate","JdbcTemplate",[897,898,899],"p",{},"Spring 框架对 JDBC 进行封装，使用 JdbcTemplate 方便实现对数据库操作",[901,902,903],"h3",{"id":903},"准备工作",[897,905,906],{},[907,908,909],"strong",{},"①搭建子模块",[897,911,912],{},"搭建子模块：spring6-transaction",[897,914,915],{},[907,916,917],{},"②加入依赖",[919,920,924],"pre",{"className":921,"code":922,"language":923,"meta":11,"style":11},"language-xml shiki shiki-themes github-light github-light github-dark","\u003Cdependencies>\n    \u003C!--spring jdbc  Spring 持久化层支持jar包-->\n    \u003Cdependency>\n        \u003CgroupId>org.springframework\u003C\u002FgroupId>\n        \u003CartifactId>spring-jdbc\u003C\u002FartifactId>\n        \u003Cversion>6.0.2\u003C\u002Fversion>\n    \u003C\u002Fdependency>\n    \u003C!-- MySQL驱动 -->\n    \u003Cdependency>\n        \u003CgroupId>mysql\u003C\u002FgroupId>\n        \u003CartifactId>mysql-connector-java\u003C\u002FartifactId>\n        \u003Cversion>8.0.33\u003C\u002Fversion>\n    \u003C\u002Fdependency>\n    \u003C!-- 数据源 -->\n    \u003Cdependency>\n        \u003CgroupId>com.alibaba\u003C\u002FgroupId>\n        \u003CartifactId>druid\u003C\u002FartifactId>\n        \u003Cversion>1.2.15\u003C\u002Fversion>\n    \u003C\u002Fdependency>\n\u003C\u002Fdependencies>\n","xml",[925,926,927,943,950,961,977,992,1007,1017,1023,1032,1046,1060,1074,1083,1089,1098,1112,1126,1140,1149],"code",{"__ignoreMap":11},[928,929,932,936,940],"span",{"class":930,"line":931},"line",1,[928,933,935],{"class":934},"sxrX7","\u003C",[928,937,939],{"class":938},"sovSZ","dependencies",[928,941,942],{"class":934},">\n",[928,944,946],{"class":930,"line":945},2,[928,947,949],{"class":948},"sCsY4","    \u003C!--spring jdbc  Spring 持久化层支持jar包-->\n",[928,951,953,956,959],{"class":930,"line":952},3,[928,954,955],{"class":934},"    \u003C",[928,957,958],{"class":938},"dependency",[928,960,942],{"class":934},[928,962,964,967,970,973,975],{"class":930,"line":963},4,[928,965,966],{"class":934},"        \u003C",[928,968,969],{"class":938},"groupId",[928,971,972],{"class":934},">org.springframework\u003C\u002F",[928,974,969],{"class":938},[928,976,942],{"class":934},[928,978,980,982,985,988,990],{"class":930,"line":979},5,[928,981,966],{"class":934},[928,983,984],{"class":938},"artifactId",[928,986,987],{"class":934},">spring-jdbc\u003C\u002F",[928,989,984],{"class":938},[928,991,942],{"class":934},[928,993,995,997,1000,1003,1005],{"class":930,"line":994},6,[928,996,966],{"class":934},[928,998,999],{"class":938},"version",[928,1001,1002],{"class":934},">6.0.2\u003C\u002F",[928,1004,999],{"class":938},[928,1006,942],{"class":934},[928,1008,1010,1013,1015],{"class":930,"line":1009},7,[928,1011,1012],{"class":934},"    \u003C\u002F",[928,1014,958],{"class":938},[928,1016,942],{"class":934},[928,1018,1020],{"class":930,"line":1019},8,[928,1021,1022],{"class":948},"    \u003C!-- MySQL驱动 -->\n",[928,1024,1026,1028,1030],{"class":930,"line":1025},9,[928,1027,955],{"class":934},[928,1029,958],{"class":938},[928,1031,942],{"class":934},[928,1033,1035,1037,1039,1042,1044],{"class":930,"line":1034},10,[928,1036,966],{"class":934},[928,1038,969],{"class":938},[928,1040,1041],{"class":934},">mysql\u003C\u002F",[928,1043,969],{"class":938},[928,1045,942],{"class":934},[928,1047,1049,1051,1053,1056,1058],{"class":930,"line":1048},11,[928,1050,966],{"class":934},[928,1052,984],{"class":938},[928,1054,1055],{"class":934},">mysql-connector-java\u003C\u002F",[928,1057,984],{"class":938},[928,1059,942],{"class":934},[928,1061,1063,1065,1067,1070,1072],{"class":930,"line":1062},12,[928,1064,966],{"class":934},[928,1066,999],{"class":938},[928,1068,1069],{"class":934},">8.0.33\u003C\u002F",[928,1071,999],{"class":938},[928,1073,942],{"class":934},[928,1075,1077,1079,1081],{"class":930,"line":1076},13,[928,1078,1012],{"class":934},[928,1080,958],{"class":938},[928,1082,942],{"class":934},[928,1084,1086],{"class":930,"line":1085},14,[928,1087,1088],{"class":948},"    \u003C!-- 数据源 -->\n",[928,1090,1092,1094,1096],{"class":930,"line":1091},15,[928,1093,955],{"class":934},[928,1095,958],{"class":938},[928,1097,942],{"class":934},[928,1099,1101,1103,1105,1108,1110],{"class":930,"line":1100},16,[928,1102,966],{"class":934},[928,1104,969],{"class":938},[928,1106,1107],{"class":934},">com.alibaba\u003C\u002F",[928,1109,969],{"class":938},[928,1111,942],{"class":934},[928,1113,1115,1117,1119,1122,1124],{"class":930,"line":1114},17,[928,1116,966],{"class":934},[928,1118,984],{"class":938},[928,1120,1121],{"class":934},">druid\u003C\u002F",[928,1123,984],{"class":938},[928,1125,942],{"class":934},[928,1127,1129,1131,1133,1136,1138],{"class":930,"line":1128},18,[928,1130,966],{"class":934},[928,1132,999],{"class":938},[928,1134,1135],{"class":934},">1.2.15\u003C\u002F",[928,1137,999],{"class":938},[928,1139,942],{"class":934},[928,1141,1143,1145,1147],{"class":930,"line":1142},19,[928,1144,1012],{"class":934},[928,1146,958],{"class":938},[928,1148,942],{"class":934},[928,1150,1152,1155,1157],{"class":930,"line":1151},20,[928,1153,1154],{"class":934},"\u003C\u002F",[928,1156,939],{"class":938},[928,1158,942],{"class":934},[897,1160,1161],{},[907,1162,1163],{},"③创建jdbc.properties",[919,1165,1169],{"className":1166,"code":1167,"language":1168,"meta":11,"style":11},"language-properties shiki shiki-themes github-light github-light github-dark","jdbc.user=codermast\njdbc.password=123456\njdbc.url=jdbc:mysql:\u002F\u002Flocalhost:3306\u002Fspring?characterEncoding=utf8&useSSL=false\njdbc.driver=com.mysql.cj.jdbc.Driver\n","properties",[925,1170,1171,1180,1188,1208],{"__ignoreMap":11},[928,1172,1173,1177],{"class":930,"line":931},[928,1174,1176],{"class":1175},"s8jYJ","jdbc.user",[928,1178,1179],{"class":934},"=codermast\n",[928,1181,1182,1185],{"class":930,"line":945},[928,1183,1184],{"class":1175},"jdbc.password",[928,1186,1187],{"class":934},"=123456\n",[928,1189,1190,1193,1196,1199,1202,1205],{"class":930,"line":952},[928,1191,1192],{"class":1175},"jdbc.url",[928,1194,1195],{"class":934},"=jdbc:mysql:\u002F\u002Flocalhost:3306\u002Fspring?",[928,1197,1198],{"class":1175},"characterEncoding",[928,1200,1201],{"class":934},"=utf8&",[928,1203,1204],{"class":1175},"useSSL",[928,1206,1207],{"class":934},"=false\n",[928,1209,1210,1213],{"class":930,"line":963},[928,1211,1212],{"class":1175},"jdbc.driver",[928,1214,1215],{"class":934},"=com.mysql.cj.jdbc.Driver\n",[897,1217,1218],{},"这里 user 就是数据库账号，password 就是数据库密码，spring 就是数据库名。根据自己实际的信息修改即可。",[897,1220,1221],{},[907,1222,1223],{},"④配置Spring的配置文件",[897,1225,1226],{},"beans.xml",[919,1228,1230],{"className":921,"code":1229,"language":923,"meta":11,"style":11},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003Cbeans xmlns=\"http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fbeans\"\n       xmlns:xsi=\"http:\u002F\u002Fwww.w3.org\u002F2001\u002FXMLSchema-instance\"\n       xmlns:context=\"http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fcontext\"\n       xsi:schemaLocation=\"http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fbeans\n       http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fbeans\u002Fspring-beans.xsd\n       http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fcontext\n       http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fcontext\u002Fspring-context.xsd\">\n\n    \u003C!-- 导入外部属性文件 -->\n    \u003Ccontext:property-placeholder location=\"classpath:jdbc.properties\" \u002F>\n\n    \u003C!-- 配置数据源 -->\n    \u003Cbean id=\"druidDataSource\" class=\"com.alibaba.druid.pool.DruidDataSource\">\n        \u003Cproperty name=\"url\" value=\"${jdbc.url}\"\u002F>\n        \u003Cproperty name=\"driverClassName\" value=\"${jdbc.driver}\"\u002F>\n        \u003Cproperty name=\"username\" value=\"${jdbc.user}\"\u002F>\n        \u003Cproperty name=\"password\" value=\"${jdbc.password}\"\u002F>\n    \u003C\u002Fbean>\n\n    \u003C!-- 配置 JdbcTemplate -->\n    \u003Cbean id=\"jdbcTemplate\" class=\"org.springframework.jdbc.core.JdbcTemplate\">\n        \u003C!-- 装配数据源 -->\n        \u003Cproperty name=\"dataSource\" ref=\"druidDataSource\"\u002F>\n    \u003C\u002Fbean>\n\n\u003C\u002Fbeans>\n",[925,1231,1232,1261,1276,1286,1296,1306,1311,1316,1323,1329,1334,1352,1356,1361,1386,1412,1434,1456,1478,1486,1490,1496,1519,1525,1548,1557,1562],{"__ignoreMap":11},[928,1233,1234,1237,1239,1243,1246,1250,1253,1255,1258],{"class":930,"line":931},[928,1235,1236],{"class":934},"\u003C?",[928,1238,923],{"class":938},[928,1240,1242],{"class":1241},"snPdu"," version",[928,1244,1245],{"class":934},"=",[928,1247,1249],{"class":1248},"sIIMD","\"1.0\"",[928,1251,1252],{"class":1241}," encoding",[928,1254,1245],{"class":934},[928,1256,1257],{"class":1248},"\"UTF-8\"",[928,1259,1260],{"class":934},"?>\n",[928,1262,1263,1265,1268,1271,1273],{"class":930,"line":945},[928,1264,935],{"class":934},[928,1266,1267],{"class":938},"beans",[928,1269,1270],{"class":1241}," xmlns",[928,1272,1245],{"class":934},[928,1274,1275],{"class":1248},"\"http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fbeans\"\n",[928,1277,1278,1281,1283],{"class":930,"line":952},[928,1279,1280],{"class":1241},"       xmlns:xsi",[928,1282,1245],{"class":934},[928,1284,1285],{"class":1248},"\"http:\u002F\u002Fwww.w3.org\u002F2001\u002FXMLSchema-instance\"\n",[928,1287,1288,1291,1293],{"class":930,"line":963},[928,1289,1290],{"class":1241},"       xmlns:context",[928,1292,1245],{"class":934},[928,1294,1295],{"class":1248},"\"http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fcontext\"\n",[928,1297,1298,1301,1303],{"class":930,"line":979},[928,1299,1300],{"class":1241},"       xsi:schemaLocation",[928,1302,1245],{"class":934},[928,1304,1305],{"class":1248},"\"http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fbeans\n",[928,1307,1308],{"class":930,"line":994},[928,1309,1310],{"class":1248},"       http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fbeans\u002Fspring-beans.xsd\n",[928,1312,1313],{"class":930,"line":1009},[928,1314,1315],{"class":1248},"       http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fcontext\n",[928,1317,1318,1321],{"class":930,"line":1019},[928,1319,1320],{"class":1248},"       http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fcontext\u002Fspring-context.xsd\"",[928,1322,942],{"class":934},[928,1324,1325],{"class":930,"line":1025},[928,1326,1328],{"emptyLinePlaceholder":1327},true,"\n",[928,1330,1331],{"class":930,"line":1034},[928,1332,1333],{"class":948},"    \u003C!-- 导入外部属性文件 -->\n",[928,1335,1336,1338,1341,1344,1346,1349],{"class":930,"line":1048},[928,1337,955],{"class":934},[928,1339,1340],{"class":938},"context:property-placeholder",[928,1342,1343],{"class":1241}," location",[928,1345,1245],{"class":934},[928,1347,1348],{"class":1248},"\"classpath:jdbc.properties\"",[928,1350,1351],{"class":934}," \u002F>\n",[928,1353,1354],{"class":930,"line":1062},[928,1355,1328],{"emptyLinePlaceholder":1327},[928,1357,1358],{"class":930,"line":1076},[928,1359,1360],{"class":948},"    \u003C!-- 配置数据源 -->\n",[928,1362,1363,1365,1368,1371,1373,1376,1379,1381,1384],{"class":930,"line":1085},[928,1364,955],{"class":934},[928,1366,1367],{"class":938},"bean",[928,1369,1370],{"class":1241}," id",[928,1372,1245],{"class":934},[928,1374,1375],{"class":1248},"\"druidDataSource\"",[928,1377,1378],{"class":1241}," class",[928,1380,1245],{"class":934},[928,1382,1383],{"class":1248},"\"com.alibaba.druid.pool.DruidDataSource\"",[928,1385,942],{"class":934},[928,1387,1388,1390,1393,1396,1398,1401,1404,1406,1409],{"class":930,"line":1091},[928,1389,966],{"class":934},[928,1391,1392],{"class":938},"property",[928,1394,1395],{"class":1241}," name",[928,1397,1245],{"class":934},[928,1399,1400],{"class":1248},"\"url\"",[928,1402,1403],{"class":1241}," value",[928,1405,1245],{"class":934},[928,1407,1408],{"class":1248},"\"${jdbc.url}\"",[928,1410,1411],{"class":934},"\u002F>\n",[928,1413,1414,1416,1418,1420,1422,1425,1427,1429,1432],{"class":930,"line":1100},[928,1415,966],{"class":934},[928,1417,1392],{"class":938},[928,1419,1395],{"class":1241},[928,1421,1245],{"class":934},[928,1423,1424],{"class":1248},"\"driverClassName\"",[928,1426,1403],{"class":1241},[928,1428,1245],{"class":934},[928,1430,1431],{"class":1248},"\"${jdbc.driver}\"",[928,1433,1411],{"class":934},[928,1435,1436,1438,1440,1442,1444,1447,1449,1451,1454],{"class":930,"line":1114},[928,1437,966],{"class":934},[928,1439,1392],{"class":938},[928,1441,1395],{"class":1241},[928,1443,1245],{"class":934},[928,1445,1446],{"class":1248},"\"username\"",[928,1448,1403],{"class":1241},[928,1450,1245],{"class":934},[928,1452,1453],{"class":1248},"\"${jdbc.user}\"",[928,1455,1411],{"class":934},[928,1457,1458,1460,1462,1464,1466,1469,1471,1473,1476],{"class":930,"line":1128},[928,1459,966],{"class":934},[928,1461,1392],{"class":938},[928,1463,1395],{"class":1241},[928,1465,1245],{"class":934},[928,1467,1468],{"class":1248},"\"password\"",[928,1470,1403],{"class":1241},[928,1472,1245],{"class":934},[928,1474,1475],{"class":1248},"\"${jdbc.password}\"",[928,1477,1411],{"class":934},[928,1479,1480,1482,1484],{"class":930,"line":1142},[928,1481,1012],{"class":934},[928,1483,1367],{"class":938},[928,1485,942],{"class":934},[928,1487,1488],{"class":930,"line":1151},[928,1489,1328],{"emptyLinePlaceholder":1327},[928,1491,1493],{"class":930,"line":1492},21,[928,1494,1495],{"class":948},"    \u003C!-- 配置 JdbcTemplate -->\n",[928,1497,1499,1501,1503,1505,1507,1510,1512,1514,1517],{"class":930,"line":1498},22,[928,1500,955],{"class":934},[928,1502,1367],{"class":938},[928,1504,1370],{"class":1241},[928,1506,1245],{"class":934},[928,1508,1509],{"class":1248},"\"jdbcTemplate\"",[928,1511,1378],{"class":1241},[928,1513,1245],{"class":934},[928,1515,1516],{"class":1248},"\"org.springframework.jdbc.core.JdbcTemplate\"",[928,1518,942],{"class":934},[928,1520,1522],{"class":930,"line":1521},23,[928,1523,1524],{"class":948},"        \u003C!-- 装配数据源 -->\n",[928,1526,1528,1530,1532,1534,1536,1539,1542,1544,1546],{"class":930,"line":1527},24,[928,1529,966],{"class":934},[928,1531,1392],{"class":938},[928,1533,1395],{"class":1241},[928,1535,1245],{"class":934},[928,1537,1538],{"class":1248},"\"dataSource\"",[928,1540,1541],{"class":1241}," ref",[928,1543,1245],{"class":934},[928,1545,1375],{"class":1248},[928,1547,1411],{"class":934},[928,1549,1551,1553,1555],{"class":930,"line":1550},25,[928,1552,1012],{"class":934},[928,1554,1367],{"class":938},[928,1556,942],{"class":934},[928,1558,1560],{"class":930,"line":1559},26,[928,1561,1328],{"emptyLinePlaceholder":1327},[928,1563,1565,1567,1569],{"class":930,"line":1564},27,[928,1566,1154],{"class":934},[928,1568,1267],{"class":938},[928,1570,942],{"class":934},[897,1572,1573],{},[907,1574,1575],{},"⑤准备数据库与测试表",[919,1577,1581],{"className":1578,"code":1579,"language":1580,"meta":11,"style":11},"language-sql shiki shiki-themes github-light github-light github-dark","CREATE DATABASE `spring`;\n\nuse `spring`;\n\nCREATE TABLE `t_emp` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `name` varchar(20) DEFAULT NULL COMMENT '姓名',\n  `age` int(11) DEFAULT NULL COMMENT '年龄',\n  `sex` varchar(2) DEFAULT NULL COMMENT '性别',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n","sql",[925,1582,1583,1600,1604,1615,1619,1634,1658,1688,1712,1737,1751],{"__ignoreMap":11},[928,1584,1585,1588,1591,1594,1597],{"class":930,"line":931},[928,1586,1587],{"class":1175},"CREATE",[928,1589,1590],{"class":1175}," DATABASE",[928,1592,1593],{"class":934}," `",[928,1595,1596],{"class":1241},"spring",[928,1598,1599],{"class":934},"`;\n",[928,1601,1602],{"class":930,"line":945},[928,1603,1328],{"emptyLinePlaceholder":1327},[928,1605,1606,1609,1612],{"class":930,"line":952},[928,1607,1608],{"class":1175},"use",[928,1610,1611],{"class":1248}," `spring`",[928,1613,1614],{"class":934},";\n",[928,1616,1617],{"class":930,"line":963},[928,1618,1328],{"emptyLinePlaceholder":1327},[928,1620,1621,1623,1626,1628,1631],{"class":930,"line":979},[928,1622,1587],{"class":1175},[928,1624,1625],{"class":1175}," TABLE",[928,1627,1593],{"class":934},[928,1629,1630],{"class":1241},"t_emp",[928,1632,1633],{"class":934},"` (\n",[928,1635,1636,1639,1642,1645,1649,1652,1655],{"class":930,"line":994},[928,1637,1638],{"class":1248},"  `id`",[928,1640,1641],{"class":1175}," int",[928,1643,1644],{"class":934},"(",[928,1646,1648],{"class":1647},"sBjJW","11",[928,1650,1651],{"class":934},") ",[928,1653,1654],{"class":1175},"NOT NULL",[928,1656,1657],{"class":934}," AUTO_INCREMENT,\n",[928,1659,1660,1663,1666,1668,1671,1673,1676,1679,1682,1685],{"class":930,"line":1009},[928,1661,1662],{"class":1248},"  `name`",[928,1664,1665],{"class":1175}," varchar",[928,1667,1644],{"class":934},[928,1669,1670],{"class":1647},"20",[928,1672,1651],{"class":934},[928,1674,1675],{"class":1175},"DEFAULT",[928,1677,1678],{"class":1175}," NULL",[928,1680,1681],{"class":934}," COMMENT ",[928,1683,1684],{"class":1248},"'姓名'",[928,1686,1687],{"class":934},",\n",[928,1689,1690,1693,1695,1697,1699,1701,1703,1705,1707,1710],{"class":930,"line":1019},[928,1691,1692],{"class":1248},"  `age`",[928,1694,1641],{"class":1175},[928,1696,1644],{"class":934},[928,1698,1648],{"class":1647},[928,1700,1651],{"class":934},[928,1702,1675],{"class":1175},[928,1704,1678],{"class":1175},[928,1706,1681],{"class":934},[928,1708,1709],{"class":1248},"'年龄'",[928,1711,1687],{"class":934},[928,1713,1714,1717,1719,1721,1724,1726,1728,1730,1732,1735],{"class":930,"line":1025},[928,1715,1716],{"class":1248},"  `sex`",[928,1718,1665],{"class":1175},[928,1720,1644],{"class":934},[928,1722,1723],{"class":1647},"2",[928,1725,1651],{"class":934},[928,1727,1675],{"class":1175},[928,1729,1678],{"class":1175},[928,1731,1681],{"class":934},[928,1733,1734],{"class":1248},"'性别'",[928,1736,1687],{"class":934},[928,1738,1739,1742,1745,1748],{"class":930,"line":1034},[928,1740,1741],{"class":1175},"  PRIMARY KEY",[928,1743,1744],{"class":934}," (",[928,1746,1747],{"class":1248},"`id`",[928,1749,1750],{"class":934},")\n",[928,1752,1753,1756,1758,1761,1763,1766,1768],{"class":930,"line":1048},[928,1754,1755],{"class":934},") ENGINE",[928,1757,1245],{"class":1175},[928,1759,1760],{"class":934},"InnoDB ",[928,1762,1675],{"class":1175},[928,1764,1765],{"class":934}," CHARSET",[928,1767,1245],{"class":1175},[928,1769,1770],{"class":934},"utf8mb4;\n",[901,1772,1774],{"id":1773},"实现curd","实现CURD",[897,1776,1777],{},"①装配 JdbcTemplate",[897,1779,1780],{},[907,1781,1782],{},"创建测试类，整合JUnit，注入JdbcTemplate",[919,1784,1788],{"className":1785,"code":1786,"language":1787,"meta":11,"style":11},"language-java shiki shiki-themes github-light github-light github-dark","package com.codermast.spring6;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.junit.jupiter.SpringJUnitConfig;\n\n@SpringJUnitConfig(locations = \"classpath:beans.xml\")\npublic class JDBCTemplateTest {\n\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n    \n}\n","java",[925,1789,1790,1798,1802,1810,1817,1824,1828,1849,1862,1866,1874,1882,1887],{"__ignoreMap":11},[928,1791,1792,1795],{"class":930,"line":931},[928,1793,1794],{"class":1175},"package",[928,1796,1797],{"class":934}," com.codermast.spring6;\n",[928,1799,1800],{"class":930,"line":945},[928,1801,1328],{"emptyLinePlaceholder":1327},[928,1803,1804,1807],{"class":930,"line":952},[928,1805,1806],{"class":1175},"import",[928,1808,1809],{"class":934}," org.springframework.beans.factory.annotation.Autowired;\n",[928,1811,1812,1814],{"class":930,"line":963},[928,1813,1806],{"class":1175},[928,1815,1816],{"class":934}," org.springframework.jdbc.core.JdbcTemplate;\n",[928,1818,1819,1821],{"class":930,"line":979},[928,1820,1806],{"class":1175},[928,1822,1823],{"class":934}," org.springframework.test.context.junit.jupiter.SpringJUnitConfig;\n",[928,1825,1826],{"class":930,"line":994},[928,1827,1328],{"emptyLinePlaceholder":1327},[928,1829,1830,1833,1836,1838,1841,1844,1847],{"class":930,"line":1009},[928,1831,1832],{"class":934},"@",[928,1834,1835],{"class":1175},"SpringJUnitConfig",[928,1837,1644],{"class":934},[928,1839,1840],{"class":1647},"locations",[928,1842,1843],{"class":1175}," =",[928,1845,1846],{"class":1248}," \"classpath:beans.xml\"",[928,1848,1750],{"class":934},[928,1850,1851,1854,1856,1859],{"class":930,"line":1019},[928,1852,1853],{"class":1175},"public",[928,1855,1378],{"class":1175},[928,1857,1858],{"class":1241}," JDBCTemplateTest",[928,1860,1861],{"class":934}," {\n",[928,1863,1864],{"class":930,"line":1025},[928,1865,1328],{"emptyLinePlaceholder":1327},[928,1867,1868,1871],{"class":930,"line":1034},[928,1869,1870],{"class":934},"    @",[928,1872,1873],{"class":1175},"Autowired\n",[928,1875,1876,1879],{"class":930,"line":1048},[928,1877,1878],{"class":1175},"    private",[928,1880,1881],{"class":934}," JdbcTemplate jdbcTemplate;\n",[928,1883,1884],{"class":930,"line":1062},[928,1885,1886],{"class":934},"    \n",[928,1888,1889],{"class":930,"line":1076},[928,1890,1891],{"class":934},"}\n",[897,1893,1894],{},"②测试增删改功能",[919,1896,1898],{"className":1785,"code":1897,"language":1787,"meta":11,"style":11},"@Test\n\u002F\u002F测试增删改功能\npublic void testUpdate(){\n    \u002F\u002F添加功能\n    String sql = \"insert into t_emp values(null,?,?,?)\";\n    int result = jdbcTemplate.update(sql, \"张三\", 23, \"男\");\n    \n    \u002F\u002F修改功能\n    \u002F\u002FString sql = \"update t_emp set name=? where id=?\";\n    \u002F\u002Fint result = jdbcTemplate.update(sql, \"codermast\", 1);\n\n    \u002F\u002F删除功能\n    \u002F\u002FString sql = \"delete from t_emp where id=?\";\n    \u002F\u002Fint result = jdbcTemplate.update(sql, 1);\n}\n",[925,1899,1900,1907,1912,1925,1930,1942,1978,1982,1987,1992,1997,2001,2006,2011,2016],{"__ignoreMap":11},[928,1901,1902,1904],{"class":930,"line":931},[928,1903,1832],{"class":934},[928,1905,1906],{"class":1175},"Test\n",[928,1908,1909],{"class":930,"line":945},[928,1910,1911],{"class":948},"\u002F\u002F测试增删改功能\n",[928,1913,1914,1916,1919,1922],{"class":930,"line":952},[928,1915,1853],{"class":1175},[928,1917,1918],{"class":1175}," void",[928,1920,1921],{"class":1241}," testUpdate",[928,1923,1924],{"class":934},"(){\n",[928,1926,1927],{"class":930,"line":963},[928,1928,1929],{"class":948},"    \u002F\u002F添加功能\n",[928,1931,1932,1935,1937,1940],{"class":930,"line":979},[928,1933,1934],{"class":934},"    String sql ",[928,1936,1245],{"class":1175},[928,1938,1939],{"class":1248}," \"insert into t_emp values(null,?,?,?)\"",[928,1941,1614],{"class":934},[928,1943,1944,1947,1950,1952,1955,1958,1961,1964,1967,1970,1972,1975],{"class":930,"line":994},[928,1945,1946],{"class":1175},"    int",[928,1948,1949],{"class":934}," result ",[928,1951,1245],{"class":1175},[928,1953,1954],{"class":934}," jdbcTemplate.",[928,1956,1957],{"class":1241},"update",[928,1959,1960],{"class":934},"(sql, ",[928,1962,1963],{"class":1248},"\"张三\"",[928,1965,1966],{"class":934},", ",[928,1968,1969],{"class":1647},"23",[928,1971,1966],{"class":934},[928,1973,1974],{"class":1248},"\"男\"",[928,1976,1977],{"class":934},");\n",[928,1979,1980],{"class":930,"line":1009},[928,1981,1886],{"class":934},[928,1983,1984],{"class":930,"line":1019},[928,1985,1986],{"class":948},"    \u002F\u002F修改功能\n",[928,1988,1989],{"class":930,"line":1025},[928,1990,1991],{"class":948},"    \u002F\u002FString sql = \"update t_emp set name=? where id=?\";\n",[928,1993,1994],{"class":930,"line":1034},[928,1995,1996],{"class":948},"    \u002F\u002Fint result = jdbcTemplate.update(sql, \"codermast\", 1);\n",[928,1998,1999],{"class":930,"line":1048},[928,2000,1328],{"emptyLinePlaceholder":1327},[928,2002,2003],{"class":930,"line":1062},[928,2004,2005],{"class":948},"    \u002F\u002F删除功能\n",[928,2007,2008],{"class":930,"line":1076},[928,2009,2010],{"class":948},"    \u002F\u002FString sql = \"delete from t_emp where id=?\";\n",[928,2012,2013],{"class":930,"line":1085},[928,2014,2015],{"class":948},"    \u002F\u002Fint result = jdbcTemplate.update(sql, 1);\n",[928,2017,2018],{"class":930,"line":1091},[928,2019,1891],{"class":934},[897,2021,2022],{},"③查询数据返回对象",[919,2024,2026],{"className":1785,"code":2025,"language":1787,"meta":11,"style":11},"public class Emp {\n\n    private Integer id;\n    private String name;\n    private Integer age;\n    private String sex;\n\n    \u002F\u002F生成get和set方法\n    \u002F\u002F......\n\n    @Override\n    public String toString() {\n        return \"Emp{\" +\n                \"id=\" + id +\n                \", name='\" + name + '\\'' +\n                \", age=\" + age +\n                \", sex='\" + sex + '\\'' +\n                '}';\n    }\n}\n",[925,2027,2028,2039,2043,2050,2057,2064,2071,2075,2080,2085,2089,2096,2110,2121,2135,2159,2171,2191,2198,2203],{"__ignoreMap":11},[928,2029,2030,2032,2034,2037],{"class":930,"line":931},[928,2031,1853],{"class":1175},[928,2033,1378],{"class":1175},[928,2035,2036],{"class":1241}," Emp",[928,2038,1861],{"class":934},[928,2040,2041],{"class":930,"line":945},[928,2042,1328],{"emptyLinePlaceholder":1327},[928,2044,2045,2047],{"class":930,"line":952},[928,2046,1878],{"class":1175},[928,2048,2049],{"class":934}," Integer id;\n",[928,2051,2052,2054],{"class":930,"line":963},[928,2053,1878],{"class":1175},[928,2055,2056],{"class":934}," String name;\n",[928,2058,2059,2061],{"class":930,"line":979},[928,2060,1878],{"class":1175},[928,2062,2063],{"class":934}," Integer age;\n",[928,2065,2066,2068],{"class":930,"line":994},[928,2067,1878],{"class":1175},[928,2069,2070],{"class":934}," String sex;\n",[928,2072,2073],{"class":930,"line":1009},[928,2074,1328],{"emptyLinePlaceholder":1327},[928,2076,2077],{"class":930,"line":1019},[928,2078,2079],{"class":948},"    \u002F\u002F生成get和set方法\n",[928,2081,2082],{"class":930,"line":1025},[928,2083,2084],{"class":948},"    \u002F\u002F......\n",[928,2086,2087],{"class":930,"line":1034},[928,2088,1328],{"emptyLinePlaceholder":1327},[928,2090,2091,2093],{"class":930,"line":1048},[928,2092,1870],{"class":934},[928,2094,2095],{"class":1175},"Override\n",[928,2097,2098,2101,2104,2107],{"class":930,"line":1062},[928,2099,2100],{"class":1175},"    public",[928,2102,2103],{"class":934}," String ",[928,2105,2106],{"class":1241},"toString",[928,2108,2109],{"class":934},"() {\n",[928,2111,2112,2115,2118],{"class":930,"line":1076},[928,2113,2114],{"class":1175},"        return",[928,2116,2117],{"class":1248}," \"Emp{\"",[928,2119,2120],{"class":1175}," +\n",[928,2122,2123,2126,2129,2132],{"class":930,"line":1085},[928,2124,2125],{"class":1248},"                \"id=\"",[928,2127,2128],{"class":1175}," +",[928,2130,2131],{"class":934}," id ",[928,2133,2134],{"class":1175},"+\n",[928,2136,2137,2140,2142,2145,2148,2151,2154,2157],{"class":930,"line":1091},[928,2138,2139],{"class":1248},"                \", name='\"",[928,2141,2128],{"class":1175},[928,2143,2144],{"class":934}," name ",[928,2146,2147],{"class":1175},"+",[928,2149,2150],{"class":1248}," '",[928,2152,2153],{"class":1647},"\\'",[928,2155,2156],{"class":1248},"'",[928,2158,2120],{"class":1175},[928,2160,2161,2164,2166,2169],{"class":930,"line":1100},[928,2162,2163],{"class":1248},"                \", age=\"",[928,2165,2128],{"class":1175},[928,2167,2168],{"class":934}," age ",[928,2170,2134],{"class":1175},[928,2172,2173,2176,2178,2181,2183,2185,2187,2189],{"class":930,"line":1114},[928,2174,2175],{"class":1248},"                \", sex='\"",[928,2177,2128],{"class":1175},[928,2179,2180],{"class":934}," sex ",[928,2182,2147],{"class":1175},[928,2184,2150],{"class":1248},[928,2186,2153],{"class":1647},[928,2188,2156],{"class":1248},[928,2190,2120],{"class":1175},[928,2192,2193,2196],{"class":930,"line":1128},[928,2194,2195],{"class":1248},"                '}'",[928,2197,1614],{"class":934},[928,2199,2200],{"class":930,"line":1142},[928,2201,2202],{"class":934},"    }\n",[928,2204,2205],{"class":930,"line":1151},[928,2206,1891],{"class":934},[897,2208,2209],{},"④查询返回对象",[919,2211,2213],{"className":1785,"code":2212,"language":1787,"meta":11,"style":11},"@Test\npublic void testSelectObject() {\n    \u002F\u002F写法一\n\u002F\u002F        String sql = \"select * from t_emp where id=?\";\n\u002F\u002F        Emp empResult = jdbcTemplate.queryForObject(sql,\n\u002F\u002F                (rs, rowNum) -> {\n\u002F\u002F                    Emp emp = new Emp();\n\u002F\u002F                    emp.setId(rs.getInt(\"id\"));\n\u002F\u002F                    emp.setName(rs.getString(\"name\"));\n\u002F\u002F                    emp.setAge(rs.getInt(\"age\"));\n\u002F\u002F                    emp.setSex(rs.getString(\"sex\"));\n\u002F\u002F                    return emp;\n\u002F\u002F                }, 1);\n\u002F\u002F        System.out.println(empResult);\n\n    \u002F\u002F写法二\n    String sql = \"select * from t_emp where id=?\";\n    Emp emp = jdbcTemplate.queryForObject(sql,\n                  new BeanPropertyRowMapper\u003C>(Emp.class),1);\n    System.out.println(emp);\n}\n",[925,2214,2215,2221,2232,2237,2242,2247,2252,2257,2262,2267,2272,2277,2282,2287,2292,2296,2301,2312,2327,2340,2351],{"__ignoreMap":11},[928,2216,2217,2219],{"class":930,"line":931},[928,2218,1832],{"class":934},[928,2220,1906],{"class":1175},[928,2222,2223,2225,2227,2230],{"class":930,"line":945},[928,2224,1853],{"class":1175},[928,2226,1918],{"class":1175},[928,2228,2229],{"class":1241}," testSelectObject",[928,2231,2109],{"class":934},[928,2233,2234],{"class":930,"line":952},[928,2235,2236],{"class":948},"    \u002F\u002F写法一\n",[928,2238,2239],{"class":930,"line":963},[928,2240,2241],{"class":948},"\u002F\u002F        String sql = \"select * from t_emp where id=?\";\n",[928,2243,2244],{"class":930,"line":979},[928,2245,2246],{"class":948},"\u002F\u002F        Emp empResult = jdbcTemplate.queryForObject(sql,\n",[928,2248,2249],{"class":930,"line":994},[928,2250,2251],{"class":948},"\u002F\u002F                (rs, rowNum) -> {\n",[928,2253,2254],{"class":930,"line":1009},[928,2255,2256],{"class":948},"\u002F\u002F                    Emp emp = new Emp();\n",[928,2258,2259],{"class":930,"line":1019},[928,2260,2261],{"class":948},"\u002F\u002F                    emp.setId(rs.getInt(\"id\"));\n",[928,2263,2264],{"class":930,"line":1025},[928,2265,2266],{"class":948},"\u002F\u002F                    emp.setName(rs.getString(\"name\"));\n",[928,2268,2269],{"class":930,"line":1034},[928,2270,2271],{"class":948},"\u002F\u002F                    emp.setAge(rs.getInt(\"age\"));\n",[928,2273,2274],{"class":930,"line":1048},[928,2275,2276],{"class":948},"\u002F\u002F                    emp.setSex(rs.getString(\"sex\"));\n",[928,2278,2279],{"class":930,"line":1062},[928,2280,2281],{"class":948},"\u002F\u002F                    return emp;\n",[928,2283,2284],{"class":930,"line":1076},[928,2285,2286],{"class":948},"\u002F\u002F                }, 1);\n",[928,2288,2289],{"class":930,"line":1085},[928,2290,2291],{"class":948},"\u002F\u002F        System.out.println(empResult);\n",[928,2293,2294],{"class":930,"line":1091},[928,2295,1328],{"emptyLinePlaceholder":1327},[928,2297,2298],{"class":930,"line":1100},[928,2299,2300],{"class":948},"    \u002F\u002F写法二\n",[928,2302,2303,2305,2307,2310],{"class":930,"line":1114},[928,2304,1934],{"class":934},[928,2306,1245],{"class":1175},[928,2308,2309],{"class":1248}," \"select * from t_emp where id=?\"",[928,2311,1614],{"class":934},[928,2313,2314,2317,2319,2321,2324],{"class":930,"line":1128},[928,2315,2316],{"class":934},"    Emp emp ",[928,2318,1245],{"class":1175},[928,2320,1954],{"class":934},[928,2322,2323],{"class":1241},"queryForObject",[928,2325,2326],{"class":934},"(sql,\n",[928,2328,2329,2332,2335,2338],{"class":930,"line":1142},[928,2330,2331],{"class":1175},"                  new",[928,2333,2334],{"class":934}," BeanPropertyRowMapper\u003C>(Emp.class),",[928,2336,2337],{"class":1647},"1",[928,2339,1977],{"class":934},[928,2341,2342,2345,2348],{"class":930,"line":1151},[928,2343,2344],{"class":934},"    System.out.",[928,2346,2347],{"class":1241},"println",[928,2349,2350],{"class":934},"(emp);\n",[928,2352,2353],{"class":930,"line":1492},[928,2354,1891],{"class":934},[897,2356,2357],{},"⑤查询数据返回list集合",[919,2359,2361],{"className":1785,"code":2360,"language":1787,"meta":11,"style":11},"@Test\n\u002F\u002F查询多条数据为一个list集合\npublic void testSelectList(){\n    String sql = \"select * from t_emp\";\n    List\u003CEmp> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper\u003C>(Emp.class));\n    System.out.println(list);\n}\n",[925,2362,2363,2369,2374,2385,2396,2422,2431],{"__ignoreMap":11},[928,2364,2365,2367],{"class":930,"line":931},[928,2366,1832],{"class":934},[928,2368,1906],{"class":1175},[928,2370,2371],{"class":930,"line":945},[928,2372,2373],{"class":948},"\u002F\u002F查询多条数据为一个list集合\n",[928,2375,2376,2378,2380,2383],{"class":930,"line":952},[928,2377,1853],{"class":1175},[928,2379,1918],{"class":1175},[928,2381,2382],{"class":1241}," testSelectList",[928,2384,1924],{"class":934},[928,2386,2387,2389,2391,2394],{"class":930,"line":963},[928,2388,1934],{"class":934},[928,2390,1245],{"class":1175},[928,2392,2393],{"class":1248}," \"select * from t_emp\"",[928,2395,1614],{"class":934},[928,2397,2398,2401,2404,2407,2409,2411,2414,2416,2419],{"class":930,"line":979},[928,2399,2400],{"class":934},"    List\u003C",[928,2402,2403],{"class":1175},"Emp",[928,2405,2406],{"class":934},"> list ",[928,2408,1245],{"class":1175},[928,2410,1954],{"class":934},[928,2412,2413],{"class":1241},"query",[928,2415,1960],{"class":934},[928,2417,2418],{"class":1175},"new",[928,2420,2421],{"class":934}," BeanPropertyRowMapper\u003C>(Emp.class));\n",[928,2423,2424,2426,2428],{"class":930,"line":994},[928,2425,2344],{"class":934},[928,2427,2347],{"class":1241},[928,2429,2430],{"class":934},"(list);\n",[928,2432,2433],{"class":930,"line":1009},[928,2434,1891],{"class":934},[897,2436,2437],{},"⑥查询返回单个的值",[919,2439,2441],{"className":1785,"code":2440,"language":1787,"meta":11,"style":11},"@Test\n\u002F\u002F查询单行单列的值\npublic void selectCount(){\n    String sql = \"select count(id) from t_emp\";\n    Integer count = jdbcTemplate.queryForObject(sql, Integer.class);\n    System.out.println(count);\n}\n",[925,2442,2443,2449,2454,2465,2476,2490,2499],{"__ignoreMap":11},[928,2444,2445,2447],{"class":930,"line":931},[928,2446,1832],{"class":934},[928,2448,1906],{"class":1175},[928,2450,2451],{"class":930,"line":945},[928,2452,2453],{"class":948},"\u002F\u002F查询单行单列的值\n",[928,2455,2456,2458,2460,2463],{"class":930,"line":952},[928,2457,1853],{"class":1175},[928,2459,1918],{"class":1175},[928,2461,2462],{"class":1241}," selectCount",[928,2464,1924],{"class":934},[928,2466,2467,2469,2471,2474],{"class":930,"line":963},[928,2468,1934],{"class":934},[928,2470,1245],{"class":1175},[928,2472,2473],{"class":1248}," \"select count(id) from t_emp\"",[928,2475,1614],{"class":934},[928,2477,2478,2481,2483,2485,2487],{"class":930,"line":979},[928,2479,2480],{"class":934},"    Integer count ",[928,2482,1245],{"class":1175},[928,2484,1954],{"class":934},[928,2486,2323],{"class":1241},[928,2488,2489],{"class":934},"(sql, Integer.class);\n",[928,2491,2492,2494,2496],{"class":930,"line":994},[928,2493,2344],{"class":934},[928,2495,2347],{"class":1241},[928,2497,2498],{"class":934},"(count);\n",[928,2500,2501],{"class":930,"line":1009},[928,2502,1891],{"class":934},[897,2504,2505],{},[2506,2507],"img",{"alt":2508,"src":2509},"测试结果","\u002Fassets\u002Fjava\u002Fspring\u002Fspring-transaction\u002F2024-01-02-09-12-47.png",[892,2511,2512],{"id":2512},"事务概念",[901,2514,2515],{"id":2515},"事务基本概念",[897,2517,2518],{},"①什么是事务",[897,2520,2521],{},"数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列，这些操作要么全部执行,要么全部不执行，是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。",[897,2523,2524],{},"②事务的特性",[897,2526,2527],{},[907,2528,2529],{},"A：原子性(Atomicity)",[897,2531,2532],{},"一个事务(transaction)中的所有操作，要么全部完成，要么全部不完成，不会结束在中间某个环节。事务在执行过程中发生错误，会被回滚（Rollback）到事务开始前的状态，就像这个事务从来没有执行过一样。",[897,2534,2535],{},[907,2536,2537],{},"C：一致性(Consistency)",[897,2539,2540],{},"事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。",[897,2542,2543],{},"如果事务成功地完成，那么系统中所有变化将正确地应用，系统处于有效状态。",[897,2545,2546],{},"如果在事务中出现错误，那么系统中的所有变化将自动地回滚，系统返回到原始状态。",[897,2548,2549],{},[907,2550,2551],{},"I：隔离性(Isolation)",[897,2553,2554],{},"指的是在并发环境中，当不同的事务同时操纵相同的数据时，每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时，数据所处的状态要么是另一事务修改它之前的状态，要么是另一事务修改它之后的状态，事务不会查看到中间状态的数据。",[897,2556,2557],{},[907,2558,2559],{},"D：持久性(Durability)",[897,2561,2562],{},"指的是只要事务成功结束，它对数据库所做的更新就必须保存下来。即使发生系统崩溃，重新启动数据库系统后，数据库还能恢复到事务成功结束时的状态。",[901,2564,2565],{"id":2565},"编程式事务",[897,2567,2568],{},"事务功能的相关操作全部通过自己编写代码来实现：",[919,2570,2572],{"className":1785,"code":2571,"language":1787,"meta":11,"style":11},"Connection conn = ...;\n    \ntry {\n    \n    \u002F\u002F 开启事务：关闭事务的自动提交\n    conn.setAutoCommit(false);\n    \n    \u002F\u002F 核心操作\n    \n    \u002F\u002F 提交事务\n    conn.commit();\n    \n}catch(Exception e){\n    \n    \u002F\u002F 回滚事务\n    conn.rollBack();\n    \n}finally{\n    \n    \u002F\u002F 释放数据库连接\n    conn.close();\n    \n}\n",[925,2573,2574,2584,2588,2595,2599,2604,2619,2623,2628,2632,2637,2647,2651,2669,2673,2678,2687,2691,2701,2705,2710,2719,2723],{"__ignoreMap":11},[928,2575,2576,2579,2581],{"class":930,"line":931},[928,2577,2578],{"class":934},"Connection conn ",[928,2580,1245],{"class":1175},[928,2582,2583],{"class":934}," ...;\n",[928,2585,2586],{"class":930,"line":945},[928,2587,1886],{"class":934},[928,2589,2590,2593],{"class":930,"line":952},[928,2591,2592],{"class":1175},"try",[928,2594,1861],{"class":934},[928,2596,2597],{"class":930,"line":963},[928,2598,1886],{"class":934},[928,2600,2601],{"class":930,"line":979},[928,2602,2603],{"class":948},"    \u002F\u002F 开启事务：关闭事务的自动提交\n",[928,2605,2606,2609,2612,2614,2617],{"class":930,"line":994},[928,2607,2608],{"class":934},"    conn.",[928,2610,2611],{"class":1241},"setAutoCommit",[928,2613,1644],{"class":934},[928,2615,2616],{"class":1647},"false",[928,2618,1977],{"class":934},[928,2620,2621],{"class":930,"line":1009},[928,2622,1886],{"class":934},[928,2624,2625],{"class":930,"line":1019},[928,2626,2627],{"class":948},"    \u002F\u002F 核心操作\n",[928,2629,2630],{"class":930,"line":1025},[928,2631,1886],{"class":934},[928,2633,2634],{"class":930,"line":1034},[928,2635,2636],{"class":948},"    \u002F\u002F 提交事务\n",[928,2638,2639,2641,2644],{"class":930,"line":1048},[928,2640,2608],{"class":934},[928,2642,2643],{"class":1241},"commit",[928,2645,2646],{"class":934},"();\n",[928,2648,2649],{"class":930,"line":1062},[928,2650,1886],{"class":934},[928,2652,2653,2656,2659,2662,2666],{"class":930,"line":1076},[928,2654,2655],{"class":934},"}",[928,2657,2658],{"class":1175},"catch",[928,2660,2661],{"class":934},"(Exception ",[928,2663,2665],{"class":2664},"sP4rz","e",[928,2667,2668],{"class":934},"){\n",[928,2670,2671],{"class":930,"line":1085},[928,2672,1886],{"class":934},[928,2674,2675],{"class":930,"line":1091},[928,2676,2677],{"class":948},"    \u002F\u002F 回滚事务\n",[928,2679,2680,2682,2685],{"class":930,"line":1100},[928,2681,2608],{"class":934},[928,2683,2684],{"class":1241},"rollBack",[928,2686,2646],{"class":934},[928,2688,2689],{"class":930,"line":1114},[928,2690,1886],{"class":934},[928,2692,2693,2695,2698],{"class":930,"line":1128},[928,2694,2655],{"class":934},[928,2696,2697],{"class":1175},"finally",[928,2699,2700],{"class":934},"{\n",[928,2702,2703],{"class":930,"line":1142},[928,2704,1886],{"class":934},[928,2706,2707],{"class":930,"line":1151},[928,2708,2709],{"class":948},"    \u002F\u002F 释放数据库连接\n",[928,2711,2712,2714,2717],{"class":930,"line":1492},[928,2713,2608],{"class":934},[928,2715,2716],{"class":1241},"close",[928,2718,2646],{"class":934},[928,2720,2721],{"class":930,"line":1498},[928,2722,1886],{"class":934},[928,2724,2725],{"class":930,"line":1521},[928,2726,1891],{"class":934},[897,2728,2729],{},"编程式的实现方式存在缺陷：",[2731,2732,2733,2737],"ul",{},[2734,2735,2736],"li",{},"细节没有被屏蔽：具体操作过程中，所有细节都需要程序员自己来完成，比较繁琐。",[2734,2738,2739],{},"代码复用性不高：如果没有有效抽取出来，每次实现功能都需要自己编写代码，代码就没有得到复用。",[901,2741,2742],{"id":2742},"声明式事务",[897,2744,2745],{},"既然事务控制的代码有规律可循，代码的结构基本是确定的，所以框架就可以将固定模式的代码抽取出来，进行相关的封装。",[897,2747,2748],{},"封装起来后，我们只需要在配置文件中进行简单的配置即可完成操作。",[2731,2750,2751,2754,2757],{},[2734,2752,2753],{},"好处1：提高开发效率",[2734,2755,2756],{},"好处2：消除了冗余的代码",[2734,2758,2759],{},"好处3：框架会综合考虑相关领域中在实际开发环境下有可能遇到的各种问题，进行了健壮性、性能等各个方面的优化",[897,2761,2762],{},"所以，我们可以总结下面两个概念：",[2731,2764,2765,2775],{},[2734,2766,2767,2770,2771,2774],{},[907,2768,2769],{},"编程式","：",[907,2772,2773],{},"自己写代码","实现功能",[2734,2776,2777,2780,2781,2784,2785,2774],{},[907,2778,2779],{},"声明式","：通过",[907,2782,2783],{},"配置","让",[907,2786,2787],{},"框架",[892,2789,2790],{"id":2790},"基于注解的声明式事务",[901,2792,903],{"id":2793},"准备工作-1",[897,2795,2796],{},[907,2797,2798],{},"①添加配置",[897,2800,2801,2802,2804],{},"在",[925,2803,1226],{},"添加配置",[919,2806,2808],{"className":921,"code":2807,"language":923,"meta":11,"style":11},"\u003C!--扫描组件-->\n\u003Ccontext:component-scan base-package=\"com.codermast.spring6\"\u002F>\n",[925,2809,2810,2815],{"__ignoreMap":11},[928,2811,2812],{"class":930,"line":931},[928,2813,2814],{"class":948},"\u003C!--扫描组件-->\n",[928,2816,2817,2819,2822,2825,2827,2830],{"class":930,"line":945},[928,2818,935],{"class":934},[928,2820,2821],{"class":938},"context:component-scan",[928,2823,2824],{"class":1241}," base-package",[928,2826,1245],{"class":934},[928,2828,2829],{"class":1248},"\"com.codermast.spring6\"",[928,2831,1411],{"class":934},[897,2833,2834],{},[907,2835,2836],{},"②创建表",[919,2838,2840],{"className":1578,"code":2839,"language":1580,"meta":11,"style":11},"CREATE TABLE `t_book` (\n  `book_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',\n  `book_name` varchar(20) DEFAULT NULL COMMENT '图书名称',\n  `price` int(11) DEFAULT NULL COMMENT '价格',\n  `stock` int(10) unsigned DEFAULT NULL COMMENT '库存（无符号）',\n  PRIMARY KEY (`book_id`)\n) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;\ninsert  into `t_book`(`book_id`,`book_name`,`price`,`stock`) values (1,'斗破苍穹',80,100),(2,'斗罗大陆',50,100);\nCREATE TABLE `t_user` (\n  `user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',\n  `username` varchar(20) DEFAULT NULL COMMENT '用户名',\n  `balance` int(10) unsigned DEFAULT NULL COMMENT '余额（无符号）',\n  PRIMARY KEY (`user_id`)\n) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;\ninsert  into `t_user`(`user_id`,`username`,`balance`) values (1,'admin',50);\n",[925,2841,2842,2855,2878,2902,2926,2952,2963,2987,3060,3073,3094,3118,3142,3153,3173],{"__ignoreMap":11},[928,2843,2844,2846,2848,2850,2853],{"class":930,"line":931},[928,2845,1587],{"class":1175},[928,2847,1625],{"class":1175},[928,2849,1593],{"class":934},[928,2851,2852],{"class":1241},"t_book",[928,2854,1633],{"class":934},[928,2856,2857,2860,2862,2864,2866,2868,2870,2873,2876],{"class":930,"line":945},[928,2858,2859],{"class":1248},"  `book_id`",[928,2861,1641],{"class":1175},[928,2863,1644],{"class":934},[928,2865,1648],{"class":1647},[928,2867,1651],{"class":934},[928,2869,1654],{"class":1175},[928,2871,2872],{"class":934}," AUTO_INCREMENT COMMENT ",[928,2874,2875],{"class":1248},"'主键'",[928,2877,1687],{"class":934},[928,2879,2880,2883,2885,2887,2889,2891,2893,2895,2897,2900],{"class":930,"line":952},[928,2881,2882],{"class":1248},"  `book_name`",[928,2884,1665],{"class":1175},[928,2886,1644],{"class":934},[928,2888,1670],{"class":1647},[928,2890,1651],{"class":934},[928,2892,1675],{"class":1175},[928,2894,1678],{"class":1175},[928,2896,1681],{"class":934},[928,2898,2899],{"class":1248},"'图书名称'",[928,2901,1687],{"class":934},[928,2903,2904,2907,2909,2911,2913,2915,2917,2919,2921,2924],{"class":930,"line":963},[928,2905,2906],{"class":1248},"  `price`",[928,2908,1641],{"class":1175},[928,2910,1644],{"class":934},[928,2912,1648],{"class":1647},[928,2914,1651],{"class":934},[928,2916,1675],{"class":1175},[928,2918,1678],{"class":1175},[928,2920,1681],{"class":934},[928,2922,2923],{"class":1248},"'价格'",[928,2925,1687],{"class":934},[928,2927,2928,2931,2933,2935,2938,2941,2943,2945,2947,2950],{"class":930,"line":979},[928,2929,2930],{"class":1248},"  `stock`",[928,2932,1641],{"class":1175},[928,2934,1644],{"class":934},[928,2936,2937],{"class":1647},"10",[928,2939,2940],{"class":934},") unsigned ",[928,2942,1675],{"class":1175},[928,2944,1678],{"class":1175},[928,2946,1681],{"class":934},[928,2948,2949],{"class":1248},"'库存（无符号）'",[928,2951,1687],{"class":934},[928,2953,2954,2956,2958,2961],{"class":930,"line":994},[928,2955,1741],{"class":1175},[928,2957,1744],{"class":934},[928,2959,2960],{"class":1248},"`book_id`",[928,2962,1750],{"class":934},[928,2964,2965,2967,2969,2972,2974,2977,2980,2982,2984],{"class":930,"line":1009},[928,2966,1755],{"class":934},[928,2968,1245],{"class":1175},[928,2970,2971],{"class":934},"InnoDB AUTO_INCREMENT",[928,2973,1245],{"class":1175},[928,2975,2976],{"class":1647},"3",[928,2978,2979],{"class":1175}," DEFAULT",[928,2981,1765],{"class":934},[928,2983,1245],{"class":1175},[928,2985,2986],{"class":934},"utf8;\n",[928,2988,2989,2992,2995,2997,2999,3002,3005,3007,3010,3012,3015,3017,3020,3022,3024,3026,3029,3031,3034,3036,3039,3042,3044,3046,3049,3051,3054,3056,3058],{"class":930,"line":1019},[928,2990,2991],{"class":1175},"insert  into",[928,2993,2994],{"class":1248}," `t_book`",[928,2996,1644],{"class":934},[928,2998,2960],{"class":1248},[928,3000,3001],{"class":934},",",[928,3003,3004],{"class":1248},"`book_name`",[928,3006,3001],{"class":934},[928,3008,3009],{"class":1248},"`price`",[928,3011,3001],{"class":934},[928,3013,3014],{"class":1248},"`stock`",[928,3016,1651],{"class":934},[928,3018,3019],{"class":1175},"values",[928,3021,1744],{"class":934},[928,3023,2337],{"class":1647},[928,3025,3001],{"class":934},[928,3027,3028],{"class":1248},"'斗破苍穹'",[928,3030,3001],{"class":934},[928,3032,3033],{"class":1647},"80",[928,3035,3001],{"class":934},[928,3037,3038],{"class":1647},"100",[928,3040,3041],{"class":934},"),(",[928,3043,1723],{"class":1647},[928,3045,3001],{"class":934},[928,3047,3048],{"class":1248},"'斗罗大陆'",[928,3050,3001],{"class":934},[928,3052,3053],{"class":1647},"50",[928,3055,3001],{"class":934},[928,3057,3038],{"class":1647},[928,3059,1977],{"class":934},[928,3061,3062,3064,3066,3068,3071],{"class":930,"line":1025},[928,3063,1587],{"class":1175},[928,3065,1625],{"class":1175},[928,3067,1593],{"class":934},[928,3069,3070],{"class":1241},"t_user",[928,3072,1633],{"class":934},[928,3074,3075,3078,3080,3082,3084,3086,3088,3090,3092],{"class":930,"line":1034},[928,3076,3077],{"class":1248},"  `user_id`",[928,3079,1641],{"class":1175},[928,3081,1644],{"class":934},[928,3083,1648],{"class":1647},[928,3085,1651],{"class":934},[928,3087,1654],{"class":1175},[928,3089,2872],{"class":934},[928,3091,2875],{"class":1248},[928,3093,1687],{"class":934},[928,3095,3096,3099,3101,3103,3105,3107,3109,3111,3113,3116],{"class":930,"line":1048},[928,3097,3098],{"class":1248},"  `username`",[928,3100,1665],{"class":1175},[928,3102,1644],{"class":934},[928,3104,1670],{"class":1647},[928,3106,1651],{"class":934},[928,3108,1675],{"class":1175},[928,3110,1678],{"class":1175},[928,3112,1681],{"class":934},[928,3114,3115],{"class":1248},"'用户名'",[928,3117,1687],{"class":934},[928,3119,3120,3123,3125,3127,3129,3131,3133,3135,3137,3140],{"class":930,"line":1062},[928,3121,3122],{"class":1248},"  `balance`",[928,3124,1641],{"class":1175},[928,3126,1644],{"class":934},[928,3128,2937],{"class":1647},[928,3130,2940],{"class":934},[928,3132,1675],{"class":1175},[928,3134,1678],{"class":1175},[928,3136,1681],{"class":934},[928,3138,3139],{"class":1248},"'余额（无符号）'",[928,3141,1687],{"class":934},[928,3143,3144,3146,3148,3151],{"class":930,"line":1076},[928,3145,1741],{"class":1175},[928,3147,1744],{"class":934},[928,3149,3150],{"class":1248},"`user_id`",[928,3152,1750],{"class":934},[928,3154,3155,3157,3159,3161,3163,3165,3167,3169,3171],{"class":930,"line":1085},[928,3156,1755],{"class":934},[928,3158,1245],{"class":1175},[928,3160,2971],{"class":934},[928,3162,1245],{"class":1175},[928,3164,1723],{"class":1647},[928,3166,2979],{"class":1175},[928,3168,1765],{"class":934},[928,3170,1245],{"class":1175},[928,3172,2986],{"class":934},[928,3174,3175,3177,3180,3182,3184,3186,3189,3191,3194,3196,3198,3200,3202,3204,3207,3209,3211],{"class":930,"line":1091},[928,3176,2991],{"class":1175},[928,3178,3179],{"class":1248}," `t_user`",[928,3181,1644],{"class":934},[928,3183,3150],{"class":1248},[928,3185,3001],{"class":934},[928,3187,3188],{"class":1248},"`username`",[928,3190,3001],{"class":934},[928,3192,3193],{"class":1248},"`balance`",[928,3195,1651],{"class":934},[928,3197,3019],{"class":1175},[928,3199,1744],{"class":934},[928,3201,2337],{"class":1647},[928,3203,3001],{"class":934},[928,3205,3206],{"class":1248},"'admin'",[928,3208,3001],{"class":934},[928,3210,3053],{"class":1647},[928,3212,1977],{"class":934},[897,3214,3215],{},[907,3216,3217],{},"③创建组件",[897,3219,3220],{},"创建BookController",[919,3222,3224],{"className":1785,"code":3223,"language":1787,"meta":11,"style":11},"@Controller\npublic class BookController {\n\n    @Autowired\n    private BookService bookService;\n\n    public void buyBook(Integer bookId, Integer userId){\n        bookService.buyBook(bookId, userId);\n    }\n}\n",[925,3225,3226,3233,3244,3248,3254,3261,3265,3288,3299,3303],{"__ignoreMap":11},[928,3227,3228,3230],{"class":930,"line":931},[928,3229,1832],{"class":934},[928,3231,3232],{"class":1175},"Controller\n",[928,3234,3235,3237,3239,3242],{"class":930,"line":945},[928,3236,1853],{"class":1175},[928,3238,1378],{"class":1175},[928,3240,3241],{"class":1241}," BookController",[928,3243,1861],{"class":934},[928,3245,3246],{"class":930,"line":952},[928,3247,1328],{"emptyLinePlaceholder":1327},[928,3249,3250,3252],{"class":930,"line":963},[928,3251,1870],{"class":934},[928,3253,1873],{"class":1175},[928,3255,3256,3258],{"class":930,"line":979},[928,3257,1878],{"class":1175},[928,3259,3260],{"class":934}," BookService bookService;\n",[928,3262,3263],{"class":930,"line":994},[928,3264,1328],{"emptyLinePlaceholder":1327},[928,3266,3267,3269,3271,3274,3277,3280,3283,3286],{"class":930,"line":1009},[928,3268,2100],{"class":1175},[928,3270,1918],{"class":1175},[928,3272,3273],{"class":1241}," buyBook",[928,3275,3276],{"class":934},"(Integer ",[928,3278,3279],{"class":2664},"bookId",[928,3281,3282],{"class":934},", Integer ",[928,3284,3285],{"class":2664},"userId",[928,3287,2668],{"class":934},[928,3289,3290,3293,3296],{"class":930,"line":1019},[928,3291,3292],{"class":934},"        bookService.",[928,3294,3295],{"class":1241},"buyBook",[928,3297,3298],{"class":934},"(bookId, userId);\n",[928,3300,3301],{"class":930,"line":1025},[928,3302,2202],{"class":934},[928,3304,3305],{"class":930,"line":1034},[928,3306,1891],{"class":934},[897,3308,3309],{},"创建接口BookService：",[919,3311,3313],{"className":1785,"code":3312,"language":1787,"meta":11,"style":11},"public interface BookService {\n    void buyBook(Integer bookId, Integer userId);\n}\n",[925,3314,3315,3327,3344],{"__ignoreMap":11},[928,3316,3317,3319,3322,3325],{"class":930,"line":931},[928,3318,1853],{"class":1175},[928,3320,3321],{"class":1175}," interface",[928,3323,3324],{"class":1241}," BookService",[928,3326,1861],{"class":934},[928,3328,3329,3332,3334,3336,3338,3340,3342],{"class":930,"line":945},[928,3330,3331],{"class":1175},"    void",[928,3333,3273],{"class":1241},[928,3335,3276],{"class":934},[928,3337,3279],{"class":2664},[928,3339,3282],{"class":934},[928,3341,3285],{"class":2664},[928,3343,1977],{"class":934},[928,3345,3346],{"class":930,"line":952},[928,3347,1891],{"class":934},[897,3349,3350],{},"创建实现类BookServiceImpl：",[919,3352,3354],{"className":1785,"code":3353,"language":1787,"meta":11,"style":11},"@Service\npublic class BookServiceImpl implements BookService {\n\n    @Autowired\n    private BookDao bookDao;\n\n    @Override\n    public void buyBook(Integer bookId, Integer userId) {\n        \u002F\u002F查询图书的价格\n        Integer price = bookDao.getPriceByBookId(bookId);\n        \u002F\u002F更新图书的库存\n        bookDao.updateStock(bookId);\n        \u002F\u002F更新用户的余额\n        bookDao.updateBalance(userId, price);\n    }\n}\n",[925,3355,3356,3363,3379,3383,3389,3396,3400,3406,3425,3430,3446,3451,3461,3466,3476,3480],{"__ignoreMap":11},[928,3357,3358,3360],{"class":930,"line":931},[928,3359,1832],{"class":934},[928,3361,3362],{"class":1175},"Service\n",[928,3364,3365,3367,3369,3372,3375,3377],{"class":930,"line":945},[928,3366,1853],{"class":1175},[928,3368,1378],{"class":1175},[928,3370,3371],{"class":1241}," BookServiceImpl",[928,3373,3374],{"class":1175}," implements",[928,3376,3324],{"class":1241},[928,3378,1861],{"class":934},[928,3380,3381],{"class":930,"line":952},[928,3382,1328],{"emptyLinePlaceholder":1327},[928,3384,3385,3387],{"class":930,"line":963},[928,3386,1870],{"class":934},[928,3388,1873],{"class":1175},[928,3390,3391,3393],{"class":930,"line":979},[928,3392,1878],{"class":1175},[928,3394,3395],{"class":934}," BookDao bookDao;\n",[928,3397,3398],{"class":930,"line":994},[928,3399,1328],{"emptyLinePlaceholder":1327},[928,3401,3402,3404],{"class":930,"line":1009},[928,3403,1870],{"class":934},[928,3405,2095],{"class":1175},[928,3407,3408,3410,3412,3414,3416,3418,3420,3422],{"class":930,"line":1019},[928,3409,2100],{"class":1175},[928,3411,1918],{"class":1175},[928,3413,3273],{"class":1241},[928,3415,3276],{"class":934},[928,3417,3279],{"class":2664},[928,3419,3282],{"class":934},[928,3421,3285],{"class":2664},[928,3423,3424],{"class":934},") {\n",[928,3426,3427],{"class":930,"line":1025},[928,3428,3429],{"class":948},"        \u002F\u002F查询图书的价格\n",[928,3431,3432,3435,3437,3440,3443],{"class":930,"line":1034},[928,3433,3434],{"class":934},"        Integer price ",[928,3436,1245],{"class":1175},[928,3438,3439],{"class":934}," bookDao.",[928,3441,3442],{"class":1241},"getPriceByBookId",[928,3444,3445],{"class":934},"(bookId);\n",[928,3447,3448],{"class":930,"line":1048},[928,3449,3450],{"class":948},"        \u002F\u002F更新图书的库存\n",[928,3452,3453,3456,3459],{"class":930,"line":1062},[928,3454,3455],{"class":934},"        bookDao.",[928,3457,3458],{"class":1241},"updateStock",[928,3460,3445],{"class":934},[928,3462,3463],{"class":930,"line":1076},[928,3464,3465],{"class":948},"        \u002F\u002F更新用户的余额\n",[928,3467,3468,3470,3473],{"class":930,"line":1085},[928,3469,3455],{"class":934},[928,3471,3472],{"class":1241},"updateBalance",[928,3474,3475],{"class":934},"(userId, price);\n",[928,3477,3478],{"class":930,"line":1091},[928,3479,2202],{"class":934},[928,3481,3482],{"class":930,"line":1100},[928,3483,1891],{"class":934},[897,3485,3486],{},"创建接口BookDao：",[919,3488,3490],{"className":1785,"code":3489,"language":1787,"meta":11,"style":11},"public interface BookDao {\n    Integer getPriceByBookId(Integer bookId);\n\n    void updateStock(Integer bookId);\n\n    void updateBalance(Integer userId, Integer price);\n}\n",[925,3491,3492,3503,3516,3520,3533,3537,3555],{"__ignoreMap":11},[928,3493,3494,3496,3498,3501],{"class":930,"line":931},[928,3495,1853],{"class":1175},[928,3497,3321],{"class":1175},[928,3499,3500],{"class":1241}," BookDao",[928,3502,1861],{"class":934},[928,3504,3505,3508,3510,3512,3514],{"class":930,"line":945},[928,3506,3507],{"class":934},"    Integer ",[928,3509,3442],{"class":1241},[928,3511,3276],{"class":934},[928,3513,3279],{"class":2664},[928,3515,1977],{"class":934},[928,3517,3518],{"class":930,"line":952},[928,3519,1328],{"emptyLinePlaceholder":1327},[928,3521,3522,3524,3527,3529,3531],{"class":930,"line":963},[928,3523,3331],{"class":1175},[928,3525,3526],{"class":1241}," updateStock",[928,3528,3276],{"class":934},[928,3530,3279],{"class":2664},[928,3532,1977],{"class":934},[928,3534,3535],{"class":930,"line":979},[928,3536,1328],{"emptyLinePlaceholder":1327},[928,3538,3539,3541,3544,3546,3548,3550,3553],{"class":930,"line":994},[928,3540,3331],{"class":1175},[928,3542,3543],{"class":1241}," updateBalance",[928,3545,3276],{"class":934},[928,3547,3285],{"class":2664},[928,3549,3282],{"class":934},[928,3551,3552],{"class":2664},"price",[928,3554,1977],{"class":934},[928,3556,3557],{"class":930,"line":1009},[928,3558,1891],{"class":934},[897,3560,3561],{},"创建实现类BookDaoImpl：",[919,3563,3565],{"className":1785,"code":3564,"language":1787,"meta":11,"style":11},"@Repository\npublic class BookDaoImpl implements BookDao {\n\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Override\n    public Integer getPriceByBookId(Integer bookId) {\n        String sql = \"select price from t_book where book_id = ?\";\n        return jdbcTemplate.queryForObject(sql, Integer.class, bookId);\n    }\n\n    @Override\n    public void updateStock(Integer bookId) {\n        String sql = \"update t_book set stock = stock - 1 where book_id = ?\";\n        jdbcTemplate.update(sql, bookId);\n    }\n\n    @Override\n    public void updateBalance(Integer userId, Integer price) {\n        String sql = \"update t_user set balance = balance - ? where user_id = ?\";\n        jdbcTemplate.update(sql, price, userId);\n    }\n}\n",[925,3566,3567,3574,3589,3593,3599,3605,3609,3615,3630,3642,3653,3657,3661,3667,3681,3692,3702,3706,3710,3716,3734,3745,3754,3758],{"__ignoreMap":11},[928,3568,3569,3571],{"class":930,"line":931},[928,3570,1832],{"class":934},[928,3572,3573],{"class":1175},"Repository\n",[928,3575,3576,3578,3580,3583,3585,3587],{"class":930,"line":945},[928,3577,1853],{"class":1175},[928,3579,1378],{"class":1175},[928,3581,3582],{"class":1241}," BookDaoImpl",[928,3584,3374],{"class":1175},[928,3586,3500],{"class":1241},[928,3588,1861],{"class":934},[928,3590,3591],{"class":930,"line":952},[928,3592,1328],{"emptyLinePlaceholder":1327},[928,3594,3595,3597],{"class":930,"line":963},[928,3596,1870],{"class":934},[928,3598,1873],{"class":1175},[928,3600,3601,3603],{"class":930,"line":979},[928,3602,1878],{"class":1175},[928,3604,1881],{"class":934},[928,3606,3607],{"class":930,"line":994},[928,3608,1328],{"emptyLinePlaceholder":1327},[928,3610,3611,3613],{"class":930,"line":1009},[928,3612,1870],{"class":934},[928,3614,2095],{"class":1175},[928,3616,3617,3619,3622,3624,3626,3628],{"class":930,"line":1019},[928,3618,2100],{"class":1175},[928,3620,3621],{"class":934}," Integer ",[928,3623,3442],{"class":1241},[928,3625,3276],{"class":934},[928,3627,3279],{"class":2664},[928,3629,3424],{"class":934},[928,3631,3632,3635,3637,3640],{"class":930,"line":1025},[928,3633,3634],{"class":934},"        String sql ",[928,3636,1245],{"class":1175},[928,3638,3639],{"class":1248}," \"select price from t_book where book_id = ?\"",[928,3641,1614],{"class":934},[928,3643,3644,3646,3648,3650],{"class":930,"line":1034},[928,3645,2114],{"class":1175},[928,3647,1954],{"class":934},[928,3649,2323],{"class":1241},[928,3651,3652],{"class":934},"(sql, Integer.class, bookId);\n",[928,3654,3655],{"class":930,"line":1048},[928,3656,2202],{"class":934},[928,3658,3659],{"class":930,"line":1062},[928,3660,1328],{"emptyLinePlaceholder":1327},[928,3662,3663,3665],{"class":930,"line":1076},[928,3664,1870],{"class":934},[928,3666,2095],{"class":1175},[928,3668,3669,3671,3673,3675,3677,3679],{"class":930,"line":1085},[928,3670,2100],{"class":1175},[928,3672,1918],{"class":1175},[928,3674,3526],{"class":1241},[928,3676,3276],{"class":934},[928,3678,3279],{"class":2664},[928,3680,3424],{"class":934},[928,3682,3683,3685,3687,3690],{"class":930,"line":1091},[928,3684,3634],{"class":934},[928,3686,1245],{"class":1175},[928,3688,3689],{"class":1248}," \"update t_book set stock = stock - 1 where book_id = ?\"",[928,3691,1614],{"class":934},[928,3693,3694,3697,3699],{"class":930,"line":1100},[928,3695,3696],{"class":934},"        jdbcTemplate.",[928,3698,1957],{"class":1241},[928,3700,3701],{"class":934},"(sql, bookId);\n",[928,3703,3704],{"class":930,"line":1114},[928,3705,2202],{"class":934},[928,3707,3708],{"class":930,"line":1128},[928,3709,1328],{"emptyLinePlaceholder":1327},[928,3711,3712,3714],{"class":930,"line":1142},[928,3713,1870],{"class":934},[928,3715,2095],{"class":1175},[928,3717,3718,3720,3722,3724,3726,3728,3730,3732],{"class":930,"line":1151},[928,3719,2100],{"class":1175},[928,3721,1918],{"class":1175},[928,3723,3543],{"class":1241},[928,3725,3276],{"class":934},[928,3727,3285],{"class":2664},[928,3729,3282],{"class":934},[928,3731,3552],{"class":2664},[928,3733,3424],{"class":934},[928,3735,3736,3738,3740,3743],{"class":930,"line":1492},[928,3737,3634],{"class":934},[928,3739,1245],{"class":1175},[928,3741,3742],{"class":1248}," \"update t_user set balance = balance - ? where user_id = ?\"",[928,3744,1614],{"class":934},[928,3746,3747,3749,3751],{"class":930,"line":1498},[928,3748,3696],{"class":934},[928,3750,1957],{"class":1241},[928,3752,3753],{"class":934},"(sql, price, userId);\n",[928,3755,3756],{"class":930,"line":1521},[928,3757,2202],{"class":934},[928,3759,3760],{"class":930,"line":1527},[928,3761,1891],{"class":934},[901,3763,3764],{"id":3764},"测试无事务情况",[897,3766,3767],{},[907,3768,3769],{},"①创建测试类",[919,3771,3773],{"className":1785,"code":3772,"language":1787,"meta":11,"style":11},"@SpringJUnitConfig(locations = \"classpath:beans.xml\")\npublic class TxByAnnotationTest {\n\n    @Autowired\n    private BookController bookController;\n\n    @Test\n    public void testBuyBook(){\n        bookController.buyBook(1, 1);\n    }\n}\n",[925,3774,3775,3791,3802,3806,3812,3819,3823,3829,3840,3857,3861],{"__ignoreMap":11},[928,3776,3777,3779,3781,3783,3785,3787,3789],{"class":930,"line":931},[928,3778,1832],{"class":934},[928,3780,1835],{"class":1175},[928,3782,1644],{"class":934},[928,3784,1840],{"class":1647},[928,3786,1843],{"class":1175},[928,3788,1846],{"class":1248},[928,3790,1750],{"class":934},[928,3792,3793,3795,3797,3800],{"class":930,"line":945},[928,3794,1853],{"class":1175},[928,3796,1378],{"class":1175},[928,3798,3799],{"class":1241}," TxByAnnotationTest",[928,3801,1861],{"class":934},[928,3803,3804],{"class":930,"line":952},[928,3805,1328],{"emptyLinePlaceholder":1327},[928,3807,3808,3810],{"class":930,"line":963},[928,3809,1870],{"class":934},[928,3811,1873],{"class":1175},[928,3813,3814,3816],{"class":930,"line":979},[928,3815,1878],{"class":1175},[928,3817,3818],{"class":934}," BookController bookController;\n",[928,3820,3821],{"class":930,"line":994},[928,3822,1328],{"emptyLinePlaceholder":1327},[928,3824,3825,3827],{"class":930,"line":1009},[928,3826,1870],{"class":934},[928,3828,1906],{"class":1175},[928,3830,3831,3833,3835,3838],{"class":930,"line":1019},[928,3832,2100],{"class":1175},[928,3834,1918],{"class":1175},[928,3836,3837],{"class":1241}," testBuyBook",[928,3839,1924],{"class":934},[928,3841,3842,3845,3847,3849,3851,3853,3855],{"class":930,"line":1025},[928,3843,3844],{"class":934},"        bookController.",[928,3846,3295],{"class":1241},[928,3848,1644],{"class":934},[928,3850,2337],{"class":1647},[928,3852,1966],{"class":934},[928,3854,2337],{"class":1647},[928,3856,1977],{"class":934},[928,3858,3859],{"class":930,"line":1034},[928,3860,2202],{"class":934},[928,3862,3863],{"class":930,"line":1048},[928,3864,1891],{"class":934},[897,3866,3867],{},[907,3868,3869],{},"②模拟场景",[897,3871,3872],{},"用户购买图书，先查询图书的价格，再更新图书的库存和用户的余额",[897,3874,3875],{},"假设用户id为1的用户，购买id为1的图书",[897,3877,3878],{},"用户余额为50，而图书价格为80",[897,3880,3881],{},"购买图书之后，用户的余额为-30，数据库中余额字段设置了无符号，因此无法将-30插入到余额字段",[897,3883,3884],{},"此时执行sql语句会抛出SQLException",[897,3886,3887],{},[907,3888,3889],{},"③观察结果",[897,3891,3892],{},"因为没有添加事务，图书的库存更新了，但是用户的余额没有更新",[897,3894,3895],{},"显然这样的结果是错误的，购买图书是一个完整的功能，更新库存和更新余额要么都成功要么都失败",[897,3897,3898],{},[2506,3899],{"alt":3900,"src":3901},"余额不足","\u002Fassets\u002Fjava\u002Fspring\u002Fspring-transaction\u002F2024-01-02-09-17-11.png",[897,3903,3904],{},[2506,3905],{"alt":3906,"src":3907},"余额充足","\u002Fassets\u002Fjava\u002Fspring\u002Fspring-transaction\u002F2024-01-02-09-17-39.png",[901,3909,3910],{"id":3910},"加入事务",[897,3912,3913],{},"①添加事务配置",[897,3915,3916],{},"在spring配置文件中引入tx命名空间",[919,3918,3920],{"className":921,"code":3919,"language":923,"meta":11,"style":11},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003Cbeans xmlns=\"http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fbeans\"\n       xmlns:xsi=\"http:\u002F\u002Fwww.w3.org\u002F2001\u002FXMLSchema-instance\"\n       xmlns:context=\"http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fcontext\"\n       xmlns:tx=\"http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Ftx\"\n       xsi:schemaLocation=\"http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fbeans\n       http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fbeans\u002Fspring-beans.xsd\n       http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fcontext\n       http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fcontext\u002Fspring-context.xsd\n       http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Ftx\n       http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Ftx\u002Fspring-tx.xsd\">\n\n",[925,3921,3922,3942,3954,3962,3970,3980,3988,3992,3996,4001,4006],{"__ignoreMap":11},[928,3923,3924,3926,3928,3930,3932,3934,3936,3938,3940],{"class":930,"line":931},[928,3925,1236],{"class":934},[928,3927,923],{"class":938},[928,3929,1242],{"class":1241},[928,3931,1245],{"class":934},[928,3933,1249],{"class":1248},[928,3935,1252],{"class":1241},[928,3937,1245],{"class":934},[928,3939,1257],{"class":1248},[928,3941,1260],{"class":934},[928,3943,3944,3946,3948,3950,3952],{"class":930,"line":945},[928,3945,935],{"class":934},[928,3947,1267],{"class":938},[928,3949,1270],{"class":1241},[928,3951,1245],{"class":934},[928,3953,1275],{"class":1248},[928,3955,3956,3958,3960],{"class":930,"line":952},[928,3957,1280],{"class":1241},[928,3959,1245],{"class":934},[928,3961,1285],{"class":1248},[928,3963,3964,3966,3968],{"class":930,"line":963},[928,3965,1290],{"class":1241},[928,3967,1245],{"class":934},[928,3969,1295],{"class":1248},[928,3971,3972,3975,3977],{"class":930,"line":979},[928,3973,3974],{"class":1241},"       xmlns:tx",[928,3976,1245],{"class":934},[928,3978,3979],{"class":1248},"\"http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Ftx\"\n",[928,3981,3982,3984,3986],{"class":930,"line":994},[928,3983,1300],{"class":1241},[928,3985,1245],{"class":934},[928,3987,1305],{"class":1248},[928,3989,3990],{"class":930,"line":1009},[928,3991,1310],{"class":1248},[928,3993,3994],{"class":930,"line":1019},[928,3995,1315],{"class":1248},[928,3997,3998],{"class":930,"line":1025},[928,3999,4000],{"class":1248},"       http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Fcontext\u002Fspring-context.xsd\n",[928,4002,4003],{"class":930,"line":1034},[928,4004,4005],{"class":1248},"       http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Ftx\n",[928,4007,4008,4011],{"class":930,"line":1048},[928,4009,4010],{"class":1248},"       http:\u002F\u002Fwww.springframework.org\u002Fschema\u002Ftx\u002Fspring-tx.xsd\"",[928,4012,942],{"class":934},[897,4014,4015],{},"在Spring的配置文件中添加配置：",[919,4017,4019],{"className":921,"code":4018,"language":923,"meta":11,"style":11},"\u003Cbean id=\"transactionManager\" class=\"org.springframework.jdbc.datasource.DataSourceTransactionManager\">\n    \u003Cproperty name=\"dataSource\" ref=\"druidDataSource\">\u003C\u002Fproperty>\n\u003C\u002Fbean>\n\n\u003C!--\n    开启事务的注解驱动\n    通过注解@Transactional所标识的方法或标识的类中所有的方法，都会被事务管理器管理事务\n-->\n\u003C!-- transaction-manager属性的默认值是transactionManager，如果事务管理器bean的id正好就是这个默认值，则可以省略这个属性 -->\n\u003Ctx:annotation-driven transaction-manager=\"transactionManager\" \u002F>\n",[925,4020,4021,4043,4068,4076,4080,4085,4090,4095,4100,4105],{"__ignoreMap":11},[928,4022,4023,4025,4027,4029,4031,4034,4036,4038,4041],{"class":930,"line":931},[928,4024,935],{"class":934},[928,4026,1367],{"class":938},[928,4028,1370],{"class":1241},[928,4030,1245],{"class":934},[928,4032,4033],{"class":1248},"\"transactionManager\"",[928,4035,1378],{"class":1241},[928,4037,1245],{"class":934},[928,4039,4040],{"class":1248},"\"org.springframework.jdbc.datasource.DataSourceTransactionManager\"",[928,4042,942],{"class":934},[928,4044,4045,4047,4049,4051,4053,4055,4057,4059,4061,4064,4066],{"class":930,"line":945},[928,4046,955],{"class":934},[928,4048,1392],{"class":938},[928,4050,1395],{"class":1241},[928,4052,1245],{"class":934},[928,4054,1538],{"class":1248},[928,4056,1541],{"class":1241},[928,4058,1245],{"class":934},[928,4060,1375],{"class":1248},[928,4062,4063],{"class":934},">\u003C\u002F",[928,4065,1392],{"class":938},[928,4067,942],{"class":934},[928,4069,4070,4072,4074],{"class":930,"line":952},[928,4071,1154],{"class":934},[928,4073,1367],{"class":938},[928,4075,942],{"class":934},[928,4077,4078],{"class":930,"line":963},[928,4079,1328],{"emptyLinePlaceholder":1327},[928,4081,4082],{"class":930,"line":979},[928,4083,4084],{"class":948},"\u003C!--\n",[928,4086,4087],{"class":930,"line":994},[928,4088,4089],{"class":948},"    开启事务的注解驱动\n",[928,4091,4092],{"class":930,"line":1009},[928,4093,4094],{"class":948},"    通过注解@Transactional所标识的方法或标识的类中所有的方法，都会被事务管理器管理事务\n",[928,4096,4097],{"class":930,"line":1019},[928,4098,4099],{"class":948},"-->\n",[928,4101,4102],{"class":930,"line":1025},[928,4103,4104],{"class":948},"\u003C!-- transaction-manager属性的默认值是transactionManager，如果事务管理器bean的id正好就是这个默认值，则可以省略这个属性 -->\n",[928,4106,4107,4109,4112,4115,4117,4119],{"class":930,"line":1034},[928,4108,935],{"class":934},[928,4110,4111],{"class":938},"tx:annotation-driven",[928,4113,4114],{"class":1241}," transaction-manager",[928,4116,1245],{"class":934},[928,4118,4033],{"class":1248},[928,4120,1351],{"class":934},[897,4122,4123],{},"②添加事务注解",[897,4125,4126],{},"因为service层表示业务逻辑层，一个方法表示一个完成的功能，因此处理事务一般在service层处理",[897,4128,4129],{},[907,4130,4131],{},"在BookServiceImpl的buybook()添加注解@Transactional",[897,4133,3889],{},[897,4135,4136],{},"由于使用了Spring的声明式事务，更新库存和更新余额都没有执行",[901,4138,4140],{"id":4139},"transactional注解标识的位置","@Transactional注解标识的位置",[897,4142,4143],{},"@Transactional标识在方法上，则只会影响该方法",[897,4145,4146],{},"@Transactional标识的类上，则会影响类中所有的方法",[901,4148,4150],{"id":4149},"事务属性只读","事务属性：只读",[897,4152,4153],{},[907,4154,4155],{},"①介绍",[897,4157,4158],{},"对一个查询操作来说，如果我们把它设置成只读，就能够明确告诉数据库，这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化。",[897,4160,4161],{},[907,4162,4163],{},"②使用方式",[919,4165,4167],{"className":1785,"code":4166,"language":1787,"meta":11,"style":11},"@Transactional(readOnly = true)\npublic void buyBook(Integer bookId, Integer userId) {\n    \u002F\u002F查询图书的价格\n    Integer price = bookDao.getPriceByBookId(bookId);\n    \u002F\u002F更新图书的库存\n    bookDao.updateStock(bookId);\n    \u002F\u002F更新用户的余额\n    bookDao.updateBalance(userId, price);\n    \u002F\u002FSystem.out.println(1\u002F0);\n}\n",[925,4168,4169,4188,4199,4204,4217,4222,4231,4236,4244,4249],{"__ignoreMap":11},[928,4170,4171,4173,4176,4178,4181,4183,4186],{"class":930,"line":931},[928,4172,1832],{"class":934},[928,4174,4175],{"class":1175},"Transactional",[928,4177,1644],{"class":934},[928,4179,4180],{"class":1647},"readOnly",[928,4182,1843],{"class":1175},[928,4184,4185],{"class":1647}," true",[928,4187,1750],{"class":934},[928,4189,4190,4192,4194,4196],{"class":930,"line":945},[928,4191,1853],{"class":1175},[928,4193,1918],{"class":1175},[928,4195,3273],{"class":1241},[928,4197,4198],{"class":934},"(Integer bookId, Integer userId) {\n",[928,4200,4201],{"class":930,"line":952},[928,4202,4203],{"class":948},"    \u002F\u002F查询图书的价格\n",[928,4205,4206,4209,4211,4213,4215],{"class":930,"line":963},[928,4207,4208],{"class":934},"    Integer price ",[928,4210,1245],{"class":1175},[928,4212,3439],{"class":934},[928,4214,3442],{"class":1241},[928,4216,3445],{"class":934},[928,4218,4219],{"class":930,"line":979},[928,4220,4221],{"class":948},"    \u002F\u002F更新图书的库存\n",[928,4223,4224,4227,4229],{"class":930,"line":994},[928,4225,4226],{"class":934},"    bookDao.",[928,4228,3458],{"class":1241},[928,4230,3445],{"class":934},[928,4232,4233],{"class":930,"line":1009},[928,4234,4235],{"class":948},"    \u002F\u002F更新用户的余额\n",[928,4237,4238,4240,4242],{"class":930,"line":1019},[928,4239,4226],{"class":934},[928,4241,3472],{"class":1241},[928,4243,3475],{"class":934},[928,4245,4246],{"class":930,"line":1025},[928,4247,4248],{"class":948},"    \u002F\u002FSystem.out.println(1\u002F0);\n",[928,4250,4251],{"class":930,"line":1034},[928,4252,1891],{"class":934},[897,4254,4255],{},[907,4256,4257],{},"③注意",[897,4259,4260],{},"对增删改操作设置只读会抛出下面异常：",[897,4262,4263],{},"Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed",[897,4265,4266],{},[2506,4267],{"alt":11,"src":4268},"\u002Fassets\u002Fjava\u002Fspring\u002Fspring-transaction\u002F2024-01-02-09-23-05.png",[901,4270,4272],{"id":4271},"事务属性超时","事务属性：超时",[897,4274,4275],{},[907,4276,4155],{},[897,4278,4279],{},"事务在执行过程中，有可能因为遇到某些问题，导致程序卡住，从而长时间占用数据库资源。而长时间占用资源，大概率是因为程序运行出现了问题（可能是Java程序或MySQL数据库或网络连接等等）。此时这个很可能出问题的程序应该被回滚，撤销它已做的操作，事务结束，把资源让出来，让其他正常程序可以执行。",[897,4281,4282],{},"概括来说就是一句话：超时回滚，释放资源。",[897,4284,4285],{},[907,4286,4163],{},[919,4288,4290],{"className":1785,"code":4289,"language":1787,"meta":11,"style":11},"\u002F\u002F超时时间单位秒\n@Transactional(timeout = 3)\npublic void buyBook(Integer bookId, Integer userId) {\n    try {\n        TimeUnit.SECONDS.sleep(5);\n    } catch (InterruptedException e) {\n        e.printStackTrace();\n    }\n    \u002F\u002F查询图书的价格\n    Integer price = bookDao.getPriceByBookId(bookId);\n    \u002F\u002F更新图书的库存\n    bookDao.updateStock(bookId);\n    \u002F\u002F更新用户的余额\n    bookDao.updateBalance(userId, price);\n    \u002F\u002FSystem.out.println(1\u002F0);\n}\n",[925,4291,4292,4297,4315,4325,4332,4347,4361,4371,4375,4379,4391,4395,4403,4407,4415,4419],{"__ignoreMap":11},[928,4293,4294],{"class":930,"line":931},[928,4295,4296],{"class":948},"\u002F\u002F超时时间单位秒\n",[928,4298,4299,4301,4303,4305,4308,4310,4313],{"class":930,"line":945},[928,4300,1832],{"class":934},[928,4302,4175],{"class":1175},[928,4304,1644],{"class":934},[928,4306,4307],{"class":1647},"timeout",[928,4309,1843],{"class":1175},[928,4311,4312],{"class":1647}," 3",[928,4314,1750],{"class":934},[928,4316,4317,4319,4321,4323],{"class":930,"line":952},[928,4318,1853],{"class":1175},[928,4320,1918],{"class":1175},[928,4322,3273],{"class":1241},[928,4324,4198],{"class":934},[928,4326,4327,4330],{"class":930,"line":963},[928,4328,4329],{"class":1175},"    try",[928,4331,1861],{"class":934},[928,4333,4334,4337,4340,4342,4345],{"class":930,"line":979},[928,4335,4336],{"class":934},"        TimeUnit.SECONDS.",[928,4338,4339],{"class":1241},"sleep",[928,4341,1644],{"class":934},[928,4343,4344],{"class":1647},"5",[928,4346,1977],{"class":934},[928,4348,4349,4352,4354,4357,4359],{"class":930,"line":994},[928,4350,4351],{"class":934},"    } ",[928,4353,2658],{"class":1175},[928,4355,4356],{"class":934}," (InterruptedException ",[928,4358,2665],{"class":2664},[928,4360,3424],{"class":934},[928,4362,4363,4366,4369],{"class":930,"line":1009},[928,4364,4365],{"class":934},"        e.",[928,4367,4368],{"class":1241},"printStackTrace",[928,4370,2646],{"class":934},[928,4372,4373],{"class":930,"line":1019},[928,4374,2202],{"class":934},[928,4376,4377],{"class":930,"line":1025},[928,4378,4203],{"class":948},[928,4380,4381,4383,4385,4387,4389],{"class":930,"line":1034},[928,4382,4208],{"class":934},[928,4384,1245],{"class":1175},[928,4386,3439],{"class":934},[928,4388,3442],{"class":1241},[928,4390,3445],{"class":934},[928,4392,4393],{"class":930,"line":1048},[928,4394,4221],{"class":948},[928,4396,4397,4399,4401],{"class":930,"line":1062},[928,4398,4226],{"class":934},[928,4400,3458],{"class":1241},[928,4402,3445],{"class":934},[928,4404,4405],{"class":930,"line":1076},[928,4406,4235],{"class":948},[928,4408,4409,4411,4413],{"class":930,"line":1085},[928,4410,4226],{"class":934},[928,4412,3472],{"class":1241},[928,4414,3475],{"class":934},[928,4416,4417],{"class":930,"line":1091},[928,4418,4248],{"class":948},[928,4420,4421],{"class":930,"line":1100},[928,4422,1891],{"class":934},[897,4424,4425],{},[907,4426,3889],{},[897,4428,4429],{},"执行过程中抛出异常：",[897,4431,4432,4433,4436],{},"org.springframework.transaction.",[907,4434,4435],{},"TransactionTimedOutException",": Transaction timed out: deadline was Fri Jun 04 16:25:39 CST 2022",[897,4438,4439],{},[2506,4440],{"alt":4441,"src":4442},"异常信息","\u002Fassets\u002Fjava\u002Fspring\u002Fspring-transaction\u002F2024-01-02-09-25-13.png",[901,4444,4446],{"id":4445},"事务属性回滚策略","事务属性：回滚策略",[897,4448,4449],{},[907,4450,4155],{},[897,4452,4453],{},"声明式事务默认只针对运行时异常回滚，编译时异常不回滚。",[897,4455,4456],{},"可以通过@Transactional中相关属性设置回滚策略",[2731,4458,4459,4462,4465,4468],{},[2734,4460,4461],{},"rollbackFor属性：需要设置一个Class类型的对象",[2734,4463,4464],{},"rollbackForClassName属性：需要设置一个字符串类型的全类名",[2734,4466,4467],{},"noRollbackFor属性：需要设置一个Class类型的对象",[2734,4469,4470],{},"rollbackFor属性：需要设置一个字符串类型的全类名",[897,4472,4473],{},[907,4474,4163],{},[919,4476,4478],{"className":1785,"code":4477,"language":1787,"meta":11,"style":11},"@Transactional(noRollbackFor = ArithmeticException.class)\n\u002F\u002F@Transactional(noRollbackForClassName = \"java.lang.ArithmeticException\")\npublic void buyBook(Integer bookId, Integer userId) {\n    \u002F\u002F查询图书的价格\n    Integer price = bookDao.getPriceByBookId(bookId);\n    \u002F\u002F更新图书的库存\n    bookDao.updateStock(bookId);\n    \u002F\u002F更新用户的余额\n    bookDao.updateBalance(userId, price);\n    System.out.println(1\u002F0);\n}\n",[925,4479,4480,4496,4501,4511,4515,4527,4531,4539,4543,4551,4569],{"__ignoreMap":11},[928,4481,4482,4484,4486,4488,4491,4493],{"class":930,"line":931},[928,4483,1832],{"class":934},[928,4485,4175],{"class":1175},[928,4487,1644],{"class":934},[928,4489,4490],{"class":1647},"noRollbackFor",[928,4492,1843],{"class":1175},[928,4494,4495],{"class":934}," ArithmeticException.class)\n",[928,4497,4498],{"class":930,"line":945},[928,4499,4500],{"class":948},"\u002F\u002F@Transactional(noRollbackForClassName = \"java.lang.ArithmeticException\")\n",[928,4502,4503,4505,4507,4509],{"class":930,"line":952},[928,4504,1853],{"class":1175},[928,4506,1918],{"class":1175},[928,4508,3273],{"class":1241},[928,4510,4198],{"class":934},[928,4512,4513],{"class":930,"line":963},[928,4514,4203],{"class":948},[928,4516,4517,4519,4521,4523,4525],{"class":930,"line":979},[928,4518,4208],{"class":934},[928,4520,1245],{"class":1175},[928,4522,3439],{"class":934},[928,4524,3442],{"class":1241},[928,4526,3445],{"class":934},[928,4528,4529],{"class":930,"line":994},[928,4530,4221],{"class":948},[928,4532,4533,4535,4537],{"class":930,"line":1009},[928,4534,4226],{"class":934},[928,4536,3458],{"class":1241},[928,4538,3445],{"class":934},[928,4540,4541],{"class":930,"line":1019},[928,4542,4235],{"class":948},[928,4544,4545,4547,4549],{"class":930,"line":1025},[928,4546,4226],{"class":934},[928,4548,3472],{"class":1241},[928,4550,3475],{"class":934},[928,4552,4553,4555,4557,4559,4561,4564,4567],{"class":930,"line":1034},[928,4554,2344],{"class":934},[928,4556,2347],{"class":1241},[928,4558,1644],{"class":934},[928,4560,2337],{"class":1647},[928,4562,4563],{"class":1175},"\u002F",[928,4565,4566],{"class":1647},"0",[928,4568,1977],{"class":934},[928,4570,4571],{"class":930,"line":1048},[928,4572,1891],{"class":934},[897,4574,4575],{},[907,4576,3889],{},[897,4578,4579],{},"虽然购买图书功能中出现了数学运算异常（ArithmeticException），但是我们设置的回滚策略是，当出现ArithmeticException不发生回滚，因此购买图书的操作正常执行",[897,4581,4582],{},[2506,4583],{"alt":4584,"src":4585},"异常回滚","\u002Fassets\u002Fjava\u002Fspring\u002Fspring-transaction\u002F2024-01-02-09-26-12.png",[901,4587,4589],{"id":4588},"事务属性隔离级别","事务属性：隔离级别",[897,4591,4592],{},[907,4593,4155],{},[897,4595,4596],{},"数据库系统必须具有隔离并发运行各个事务的能力，使它们不会相互影响，避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别，不同隔离级别对应不同的干扰程度，隔离级别越高，数据一致性就越好，但并发性越弱。",[897,4598,4599],{},"隔离级别一共有四种：",[2731,4601,4602,4609,4615,4621],{},[2734,4603,4604,4605,4608],{},"读未提交：READ UNCOMMITTED",[4606,4607],"br",{},"允许Transaction01读取Transaction02未提交的修改。",[2734,4610,4611,4612,4614],{},"读已提交：READ COMMITTED、",[4606,4613],{},"要求Transaction01只能读取Transaction02已提交的修改。",[2734,4616,4617,4618,4620],{},"可重复读：REPEATABLE READ",[4606,4619],{},"确保Transaction01可以多次从一个字段中读取到相同的值，即Transaction01执行期间禁止其它事务对这个字段进行更新。",[2734,4622,4623,4624,4626],{},"串行化：SERIALIZABLE",[4606,4625],{},"确保Transaction01可以多次从一个表中读取到相同的行，在Transaction01执行期间，禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题，但性能十分低下。",[897,4628,4629],{},"各个隔离级别解决并发问题的能力见下表：",[4631,4632,4633,4652],"table",{},[4634,4635,4636],"thead",{},[4637,4638,4639,4643,4646,4649],"tr",{},[4640,4641,4642],"th",{},"隔离级别",[4640,4644,4645],{},"脏读",[4640,4647,4648],{},"不可重复读",[4640,4650,4651],{},"幻读",[4653,4654,4655,4668,4680,4691],"tbody",{},[4637,4656,4657,4661,4664,4666],{},[4658,4659,4660],"td",{},"READ UNCOMMITTED",[4658,4662,4663],{},"有",[4658,4665,4663],{},[4658,4667,4663],{},[4637,4669,4670,4673,4676,4678],{},[4658,4671,4672],{},"READ COMMITTED",[4658,4674,4675],{},"无",[4658,4677,4663],{},[4658,4679,4663],{},[4637,4681,4682,4685,4687,4689],{},[4658,4683,4684],{},"REPEATABLE READ",[4658,4686,4675],{},[4658,4688,4675],{},[4658,4690,4663],{},[4637,4692,4693,4696,4698,4700],{},[4658,4694,4695],{},"SERIALIZABLE",[4658,4697,4675],{},[4658,4699,4675],{},[4658,4701,4675],{},[897,4703,4704],{},"各种数据库产品对事务隔离级别的支持程度：",[4631,4706,4707,4719],{},[4634,4708,4709],{},[4637,4710,4711,4713,4716],{},[4640,4712,4642],{},[4640,4714,4715],{},"Oracle",[4640,4717,4718],{},"MySQL",[4653,4720,4721,4731,4740,4748],{},[4637,4722,4723,4725,4728],{},[4658,4724,4660],{},[4658,4726,4727],{},"×",[4658,4729,4730],{},"√",[4637,4732,4733,4735,4738],{},[4658,4734,4672],{},[4658,4736,4737],{},"√(默认)",[4658,4739,4730],{},[4637,4741,4742,4744,4746],{},[4658,4743,4684],{},[4658,4745,4727],{},[4658,4747,4737],{},[4637,4749,4750,4752,4754],{},[4658,4751,4695],{},[4658,4753,4730],{},[4658,4755,4730],{},[897,4757,4758],{},[907,4759,4163],{},[919,4761,4763],{"className":1785,"code":4762,"language":1787,"meta":11,"style":11},"@Transactional(isolation = Isolation.DEFAULT)\u002F\u002F使用数据库默认的隔离级别\n@Transactional(isolation = Isolation.READ_UNCOMMITTED)\u002F\u002F读未提交\n@Transactional(isolation = Isolation.READ_COMMITTED)\u002F\u002F读已提交\n@Transactional(isolation = Isolation.REPEATABLE_READ)\u002F\u002F可重复读\n@Transactional(isolation = Isolation.SERIALIZABLE)\u002F\u002F串行化\n",[925,4764,4765,4784,4802,4820,4838],{"__ignoreMap":11},[928,4766,4767,4769,4771,4773,4776,4778,4781],{"class":930,"line":931},[928,4768,1832],{"class":934},[928,4770,4175],{"class":1175},[928,4772,1644],{"class":934},[928,4774,4775],{"class":1647},"isolation",[928,4777,1843],{"class":1175},[928,4779,4780],{"class":934}," Isolation.DEFAULT)",[928,4782,4783],{"class":948},"\u002F\u002F使用数据库默认的隔离级别\n",[928,4785,4786,4788,4790,4792,4794,4796,4799],{"class":930,"line":945},[928,4787,1832],{"class":934},[928,4789,4175],{"class":1175},[928,4791,1644],{"class":934},[928,4793,4775],{"class":1647},[928,4795,1843],{"class":1175},[928,4797,4798],{"class":934}," Isolation.READ_UNCOMMITTED)",[928,4800,4801],{"class":948},"\u002F\u002F读未提交\n",[928,4803,4804,4806,4808,4810,4812,4814,4817],{"class":930,"line":952},[928,4805,1832],{"class":934},[928,4807,4175],{"class":1175},[928,4809,1644],{"class":934},[928,4811,4775],{"class":1647},[928,4813,1843],{"class":1175},[928,4815,4816],{"class":934}," Isolation.READ_COMMITTED)",[928,4818,4819],{"class":948},"\u002F\u002F读已提交\n",[928,4821,4822,4824,4826,4828,4830,4832,4835],{"class":930,"line":963},[928,4823,1832],{"class":934},[928,4825,4175],{"class":1175},[928,4827,1644],{"class":934},[928,4829,4775],{"class":1647},[928,4831,1843],{"class":1175},[928,4833,4834],{"class":934}," Isolation.REPEATABLE_READ)",[928,4836,4837],{"class":948},"\u002F\u002F可重复读\n",[928,4839,4840,4842,4844,4846,4848,4850,4853],{"class":930,"line":979},[928,4841,1832],{"class":934},[928,4843,4175],{"class":1175},[928,4845,1644],{"class":934},[928,4847,4775],{"class":1647},[928,4849,1843],{"class":1175},[928,4851,4852],{"class":934}," Isolation.SERIALIZABLE)",[928,4854,4855],{"class":948},"\u002F\u002F串行化\n",[901,4857,4859],{"id":4858},"事务属性传播行为","事务属性：传播行为",[897,4861,4862],{},[907,4863,4155],{},[897,4865,4866],{},"什么是事务的传播行为？",[897,4868,4869],{},"在service类中有a()方法和b()方法，a()方法上有事务，b()方法上也有事务，当a()方法执行过程中调用了b()方法，事务是如何传递的？合并到一个事务里？还是开启一个新的事务？这就是事务传播行为。",[897,4871,4872],{},"一共有七种传播行为：",[2731,4874,4875,4881,4884,4887,4890,4893,4896],{},[2734,4876,4877,4878],{},"REQUIRED：支持当前事务，如果不存在就新建一个(默认)",[907,4879,4880],{},"【没有就新建，有就加入】",[2734,4882,4883],{},"SUPPORTS：支持当前事务，如果当前没有事务，就以非事务方式执行**【有就加入，没有就不管了】**",[2734,4885,4886],{},"MANDATORY：必须运行在一个事务中，如果当前没有事务正在发生，将抛出一个异常**【有就加入，没有就抛异常】**",[2734,4888,4889],{},"REQUIRES_NEW：开启一个新的事务，如果一个事务已经存在，则将这个存在的事务挂起**【不管有没有，直接开启一个新事务，开启的新事务和之前的事务不存在嵌套关系，之前事务被挂起】**",[2734,4891,4892],{},"NOT_SUPPORTED：以非事务方式运行，如果有事务存在，挂起当前事务**【不支持事务，存在就挂起】**",[2734,4894,4895],{},"NEVER：以非事务方式运行，如果有事务存在，抛出异常**【不支持事务，存在就抛异常】**",[2734,4897,4898,4899],{},"NESTED：如果当前正有一个事务在进行中，则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在，行为就像REQUIRED一样。",[907,4900,4901],{},"【有事务的话，就在这个事务里再嵌套一个完全独立的事务，嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。】",[897,4903,4904],{},[907,4905,4906],{},"②测试",[897,4908,4909],{},"创建接口CheckoutService",[919,4911,4913],{"className":1785,"code":4912,"language":1787,"meta":11,"style":11},"public interface CheckoutService {\n    void checkout(Integer[] bookIds, Integer userId);\n}\n",[925,4914,4915,4926,4950],{"__ignoreMap":11},[928,4916,4917,4919,4921,4924],{"class":930,"line":931},[928,4918,1853],{"class":1175},[928,4920,3321],{"class":1175},[928,4922,4923],{"class":1241}," CheckoutService",[928,4925,1861],{"class":934},[928,4927,4928,4930,4933,4935,4938,4941,4944,4946,4948],{"class":930,"line":945},[928,4929,3331],{"class":1175},[928,4931,4932],{"class":1241}," checkout",[928,4934,1644],{"class":934},[928,4936,4937],{"class":1175},"Integer",[928,4939,4940],{"class":934},"[] ",[928,4942,4943],{"class":2664},"bookIds",[928,4945,3282],{"class":934},[928,4947,3285],{"class":2664},[928,4949,1977],{"class":934},[928,4951,4952],{"class":930,"line":952},[928,4953,1891],{"class":934},[897,4955,4956],{},"创建实现类CheckoutServiceImpl",[919,4958,4960],{"className":1785,"code":4959,"language":1787,"meta":11,"style":11},"@Service\npublic class CheckoutServiceImpl implements CheckoutService {\n\n    @Autowired\n    private BookService bookService;\n\n    @Override\n    @Transactional\n    \u002F\u002F一次购买多本图书\n    public void checkout(Integer[] bookIds, Integer userId) {\n        for (Integer bookId : bookIds) {\n            bookService.buyBook(bookId, userId);\n        }\n    }\n}\n",[925,4961,4962,4968,4983,4987,4993,4999,5003,5009,5016,5021,5043,5057,5066,5071,5075],{"__ignoreMap":11},[928,4963,4964,4966],{"class":930,"line":931},[928,4965,1832],{"class":934},[928,4967,3362],{"class":1175},[928,4969,4970,4972,4974,4977,4979,4981],{"class":930,"line":945},[928,4971,1853],{"class":1175},[928,4973,1378],{"class":1175},[928,4975,4976],{"class":1241}," CheckoutServiceImpl",[928,4978,3374],{"class":1175},[928,4980,4923],{"class":1241},[928,4982,1861],{"class":934},[928,4984,4985],{"class":930,"line":952},[928,4986,1328],{"emptyLinePlaceholder":1327},[928,4988,4989,4991],{"class":930,"line":963},[928,4990,1870],{"class":934},[928,4992,1873],{"class":1175},[928,4994,4995,4997],{"class":930,"line":979},[928,4996,1878],{"class":1175},[928,4998,3260],{"class":934},[928,5000,5001],{"class":930,"line":994},[928,5002,1328],{"emptyLinePlaceholder":1327},[928,5004,5005,5007],{"class":930,"line":1009},[928,5006,1870],{"class":934},[928,5008,2095],{"class":1175},[928,5010,5011,5013],{"class":930,"line":1019},[928,5012,1870],{"class":934},[928,5014,5015],{"class":1175},"Transactional\n",[928,5017,5018],{"class":930,"line":1025},[928,5019,5020],{"class":948},"    \u002F\u002F一次购买多本图书\n",[928,5022,5023,5025,5027,5029,5031,5033,5035,5037,5039,5041],{"class":930,"line":1034},[928,5024,2100],{"class":1175},[928,5026,1918],{"class":1175},[928,5028,4932],{"class":1241},[928,5030,1644],{"class":934},[928,5032,4937],{"class":1175},[928,5034,4940],{"class":934},[928,5036,4943],{"class":2664},[928,5038,3282],{"class":934},[928,5040,3285],{"class":2664},[928,5042,3424],{"class":934},[928,5044,5045,5048,5051,5054],{"class":930,"line":1048},[928,5046,5047],{"class":1175},"        for",[928,5049,5050],{"class":934}," (Integer bookId ",[928,5052,5053],{"class":1175},":",[928,5055,5056],{"class":934}," bookIds) {\n",[928,5058,5059,5062,5064],{"class":930,"line":1062},[928,5060,5061],{"class":934},"            bookService.",[928,5063,3295],{"class":1241},[928,5065,3298],{"class":934},[928,5067,5068],{"class":930,"line":1076},[928,5069,5070],{"class":934},"        }\n",[928,5072,5073],{"class":930,"line":1085},[928,5074,2202],{"class":934},[928,5076,5077],{"class":930,"line":1091},[928,5078,1891],{"class":934},[897,5080,5081],{},"在BookController中添加方法：",[919,5083,5085],{"className":1785,"code":5084,"language":1787,"meta":11,"style":11},"@Autowired\nprivate CheckoutService checkoutService;\n\npublic void checkout(Integer[] bookIds, Integer userId){\n    checkoutService.checkout(bookIds, userId);\n}\n",[925,5086,5087,5093,5101,5105,5120,5131],{"__ignoreMap":11},[928,5088,5089,5091],{"class":930,"line":931},[928,5090,1832],{"class":934},[928,5092,1873],{"class":1175},[928,5094,5095,5098],{"class":930,"line":945},[928,5096,5097],{"class":1175},"private",[928,5099,5100],{"class":934}," CheckoutService checkoutService;\n",[928,5102,5103],{"class":930,"line":952},[928,5104,1328],{"emptyLinePlaceholder":1327},[928,5106,5107,5109,5111,5113,5115,5117],{"class":930,"line":963},[928,5108,1853],{"class":1175},[928,5110,1918],{"class":1175},[928,5112,4932],{"class":1241},[928,5114,1644],{"class":934},[928,5116,4937],{"class":1175},[928,5118,5119],{"class":934},"[] bookIds, Integer userId){\n",[928,5121,5122,5125,5128],{"class":930,"line":979},[928,5123,5124],{"class":934},"    checkoutService.",[928,5126,5127],{"class":1241},"checkout",[928,5129,5130],{"class":934},"(bookIds, userId);\n",[928,5132,5133],{"class":930,"line":994},[928,5134,1891],{"class":934},[897,5136,5137],{},"在数据库中将用户的余额修改为100元",[897,5139,5140],{},[907,5141,3889],{},[897,5143,5144],{},"可以通过@Transactional中的propagation属性设置事务传播行为",[897,5146,5147],{},"修改BookServiceImpl中buyBook()上，注解@Transactional的propagation属性",[897,5149,5150],{},"@Transactional(propagation = Propagation.REQUIRED)，默认情况，表示如果当前线程上有已经开启的事务可用，那么就在这个事务中运行。经过观察，购买图书的方法buyBook()在checkout()中被调用，checkout()上有事务注解，因此在此事务中执行。所购买的两本图书的价格为80和50，而用户的余额为100，因此在购买第二本图书时余额不足失败，导致整个checkout()回滚，即只要有一本书买不了，就都买不了",[897,5152,5153],{},"@Transactional(propagation = Propagation.REQUIRES_NEW)，表示不管当前线程上是否有已经开启的事务，都要开启新事务。同样的场景，每次购买图书都是在buyBook()的事务中执行，因此第一本图书购买成功，事务结束，第二本图书购买失败，只在第二次的buyBook()中回滚，购买第一本图书不受影响，即能买几本就买几本。",[901,5155,5156],{"id":5156},"全注解配置事务",[897,5158,5159],{},[907,5160,5161],{},"①添加配置类",[919,5163,5165],{"className":1785,"code":5164,"language":1787,"meta":11,"style":11},"import com.alibaba.druid.pool.DruidDataSource;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\nimport javax.sql.DataSource;\n\n@Configuration\n@ComponentScan(\"com.codermast.spring6\")\n@EnableTransactionManagement\npublic class SpringConfig {\n\n    @Bean\n    public DataSource getDataSource(){\n        DruidDataSource dataSource = new DruidDataSource();\n        dataSource.setDriverClassName(\"com.mysql.cj.jdbc.Driver\");\n        dataSource.setUrl(\"jdbc:mysql:\u002F\u002Flocalhost:3306\u002Fspring?characterEncoding=utf8&useSSL=false\");\n        dataSource.setUsername(\"codermast\");\n        dataSource.setPassword(\"123456\");\n        return dataSource;\n    }\n\n    @Bean(name = \"jdbcTemplate\")\n    public JdbcTemplate getJdbcTemplate(DataSource dataSource){\n        JdbcTemplate jdbcTemplate = new JdbcTemplate();\n        jdbcTemplate.setDataSource(dataSource);\n        return jdbcTemplate;\n    }\n\n    @Bean\n    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){\n        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();\n        dataSourceTransactionManager.setDataSource(dataSource);\n        return dataSourceTransactionManager;\n    }\n}\n",[925,5166,5167,5174,5181,5188,5195,5201,5208,5215,5222,5226,5233,5246,5253,5264,5268,5275,5287,5302,5317,5331,5345,5359,5366,5370,5374,5393,5411,5425,5436,5444,5449,5454,5461,5478,5493,5503,5511,5516],{"__ignoreMap":11},[928,5168,5169,5171],{"class":930,"line":931},[928,5170,1806],{"class":1175},[928,5172,5173],{"class":934}," com.alibaba.druid.pool.DruidDataSource;\n",[928,5175,5176,5178],{"class":930,"line":945},[928,5177,1806],{"class":1175},[928,5179,5180],{"class":934}," org.springframework.context.annotation.Bean;\n",[928,5182,5183,5185],{"class":930,"line":952},[928,5184,1806],{"class":1175},[928,5186,5187],{"class":934}," org.springframework.context.annotation.ComponentScan;\n",[928,5189,5190,5192],{"class":930,"line":963},[928,5191,1806],{"class":1175},[928,5193,5194],{"class":934}," org.springframework.context.annotation.Configuration;\n",[928,5196,5197,5199],{"class":930,"line":979},[928,5198,1806],{"class":1175},[928,5200,1816],{"class":934},[928,5202,5203,5205],{"class":930,"line":994},[928,5204,1806],{"class":1175},[928,5206,5207],{"class":934}," org.springframework.jdbc.datasource.DataSourceTransactionManager;\n",[928,5209,5210,5212],{"class":930,"line":1009},[928,5211,1806],{"class":1175},[928,5213,5214],{"class":934}," org.springframework.transaction.annotation.EnableTransactionManagement;\n",[928,5216,5217,5219],{"class":930,"line":1019},[928,5218,1806],{"class":1175},[928,5220,5221],{"class":934}," javax.sql.DataSource;\n",[928,5223,5224],{"class":930,"line":1025},[928,5225,1328],{"emptyLinePlaceholder":1327},[928,5227,5228,5230],{"class":930,"line":1034},[928,5229,1832],{"class":934},[928,5231,5232],{"class":1175},"Configuration\n",[928,5234,5235,5237,5240,5242,5244],{"class":930,"line":1048},[928,5236,1832],{"class":934},[928,5238,5239],{"class":1175},"ComponentScan",[928,5241,1644],{"class":934},[928,5243,2829],{"class":1248},[928,5245,1750],{"class":934},[928,5247,5248,5250],{"class":930,"line":1062},[928,5249,1832],{"class":934},[928,5251,5252],{"class":1175},"EnableTransactionManagement\n",[928,5254,5255,5257,5259,5262],{"class":930,"line":1076},[928,5256,1853],{"class":1175},[928,5258,1378],{"class":1175},[928,5260,5261],{"class":1241}," SpringConfig",[928,5263,1861],{"class":934},[928,5265,5266],{"class":930,"line":1085},[928,5267,1328],{"emptyLinePlaceholder":1327},[928,5269,5270,5272],{"class":930,"line":1091},[928,5271,1870],{"class":934},[928,5273,5274],{"class":1175},"Bean\n",[928,5276,5277,5279,5282,5285],{"class":930,"line":1100},[928,5278,2100],{"class":1175},[928,5280,5281],{"class":934}," DataSource ",[928,5283,5284],{"class":1241},"getDataSource",[928,5286,1924],{"class":934},[928,5288,5289,5292,5294,5297,5300],{"class":930,"line":1114},[928,5290,5291],{"class":934},"        DruidDataSource dataSource ",[928,5293,1245],{"class":1175},[928,5295,5296],{"class":1175}," new",[928,5298,5299],{"class":1241}," DruidDataSource",[928,5301,2646],{"class":934},[928,5303,5304,5307,5310,5312,5315],{"class":930,"line":1128},[928,5305,5306],{"class":934},"        dataSource.",[928,5308,5309],{"class":1241},"setDriverClassName",[928,5311,1644],{"class":934},[928,5313,5314],{"class":1248},"\"com.mysql.cj.jdbc.Driver\"",[928,5316,1977],{"class":934},[928,5318,5319,5321,5324,5326,5329],{"class":930,"line":1142},[928,5320,5306],{"class":934},[928,5322,5323],{"class":1241},"setUrl",[928,5325,1644],{"class":934},[928,5327,5328],{"class":1248},"\"jdbc:mysql:\u002F\u002Flocalhost:3306\u002Fspring?characterEncoding=utf8&useSSL=false\"",[928,5330,1977],{"class":934},[928,5332,5333,5335,5338,5340,5343],{"class":930,"line":1151},[928,5334,5306],{"class":934},[928,5336,5337],{"class":1241},"setUsername",[928,5339,1644],{"class":934},[928,5341,5342],{"class":1248},"\"codermast\"",[928,5344,1977],{"class":934},[928,5346,5347,5349,5352,5354,5357],{"class":930,"line":1492},[928,5348,5306],{"class":934},[928,5350,5351],{"class":1241},"setPassword",[928,5353,1644],{"class":934},[928,5355,5356],{"class":1248},"\"123456\"",[928,5358,1977],{"class":934},[928,5360,5361,5363],{"class":930,"line":1498},[928,5362,2114],{"class":1175},[928,5364,5365],{"class":934}," dataSource;\n",[928,5367,5368],{"class":930,"line":1521},[928,5369,2202],{"class":934},[928,5371,5372],{"class":930,"line":1527},[928,5373,1328],{"emptyLinePlaceholder":1327},[928,5375,5376,5378,5381,5383,5386,5388,5391],{"class":930,"line":1550},[928,5377,1870],{"class":934},[928,5379,5380],{"class":1175},"Bean",[928,5382,1644],{"class":934},[928,5384,5385],{"class":1647},"name",[928,5387,1843],{"class":1175},[928,5389,5390],{"class":1248}," \"jdbcTemplate\"",[928,5392,1750],{"class":934},[928,5394,5395,5397,5400,5403,5406,5409],{"class":930,"line":1559},[928,5396,2100],{"class":1175},[928,5398,5399],{"class":934}," JdbcTemplate ",[928,5401,5402],{"class":1241},"getJdbcTemplate",[928,5404,5405],{"class":934},"(DataSource ",[928,5407,5408],{"class":2664},"dataSource",[928,5410,2668],{"class":934},[928,5412,5413,5416,5418,5420,5423],{"class":930,"line":1564},[928,5414,5415],{"class":934},"        JdbcTemplate jdbcTemplate ",[928,5417,1245],{"class":1175},[928,5419,5296],{"class":1175},[928,5421,5422],{"class":1241}," JdbcTemplate",[928,5424,2646],{"class":934},[928,5426,5428,5430,5433],{"class":930,"line":5427},28,[928,5429,3696],{"class":934},[928,5431,5432],{"class":1241},"setDataSource",[928,5434,5435],{"class":934},"(dataSource);\n",[928,5437,5439,5441],{"class":930,"line":5438},29,[928,5440,2114],{"class":1175},[928,5442,5443],{"class":934}," jdbcTemplate;\n",[928,5445,5447],{"class":930,"line":5446},30,[928,5448,2202],{"class":934},[928,5450,5452],{"class":930,"line":5451},31,[928,5453,1328],{"emptyLinePlaceholder":1327},[928,5455,5457,5459],{"class":930,"line":5456},32,[928,5458,1870],{"class":934},[928,5460,5274],{"class":1175},[928,5462,5464,5466,5469,5472,5474,5476],{"class":930,"line":5463},33,[928,5465,2100],{"class":1175},[928,5467,5468],{"class":934}," DataSourceTransactionManager ",[928,5470,5471],{"class":1241},"getDataSourceTransactionManager",[928,5473,5405],{"class":934},[928,5475,5408],{"class":2664},[928,5477,2668],{"class":934},[928,5479,5481,5484,5486,5488,5491],{"class":930,"line":5480},34,[928,5482,5483],{"class":934},"        DataSourceTransactionManager dataSourceTransactionManager ",[928,5485,1245],{"class":1175},[928,5487,5296],{"class":1175},[928,5489,5490],{"class":1241}," DataSourceTransactionManager",[928,5492,2646],{"class":934},[928,5494,5496,5499,5501],{"class":930,"line":5495},35,[928,5497,5498],{"class":934},"        dataSourceTransactionManager.",[928,5500,5432],{"class":1241},[928,5502,5435],{"class":934},[928,5504,5506,5508],{"class":930,"line":5505},36,[928,5507,2114],{"class":1175},[928,5509,5510],{"class":934}," dataSourceTransactionManager;\n",[928,5512,5514],{"class":930,"line":5513},37,[928,5515,2202],{"class":934},[928,5517,5519],{"class":930,"line":5518},38,[928,5520,1891],{"class":934},[897,5522,5523],{},[907,5524,4906],{},[919,5526,5528],{"className":1785,"code":5527,"language":1787,"meta":11,"style":11},"import com.codermast.spring6.config.SpringConfig;\nimport com.codermast.spring6.controller.BookController;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\nimport org.springframework.test.context.junit.jupiter.SpringJUnitConfig;\n\npublic class TxByAllAnnotationTest {\n\n    @Test\n    public void testTxAllAnnotation(){\n        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);\n        BookController accountService = applicationContext.getBean(\"bookController\", BookController.class);\n        accountService.buyBook(1, 1);\n    }\n}\n",[925,5529,5530,5537,5544,5551,5557,5564,5571,5577,5581,5592,5596,5602,5613,5628,5649,5666,5670],{"__ignoreMap":11},[928,5531,5532,5534],{"class":930,"line":931},[928,5533,1806],{"class":1175},[928,5535,5536],{"class":934}," com.codermast.spring6.config.SpringConfig;\n",[928,5538,5539,5541],{"class":930,"line":945},[928,5540,1806],{"class":1175},[928,5542,5543],{"class":934}," com.codermast.spring6.controller.BookController;\n",[928,5545,5546,5548],{"class":930,"line":952},[928,5547,1806],{"class":1175},[928,5549,5550],{"class":934}," org.junit.jupiter.api.Test;\n",[928,5552,5553,5555],{"class":930,"line":963},[928,5554,1806],{"class":1175},[928,5556,1809],{"class":934},[928,5558,5559,5561],{"class":930,"line":979},[928,5560,1806],{"class":1175},[928,5562,5563],{"class":934}," org.springframework.context.ApplicationContext;\n",[928,5565,5566,5568],{"class":930,"line":994},[928,5567,1806],{"class":1175},[928,5569,5570],{"class":934}," org.springframework.context.annotation.AnnotationConfigApplicationContext;\n",[928,5572,5573,5575],{"class":930,"line":1009},[928,5574,1806],{"class":1175},[928,5576,1823],{"class":934},[928,5578,5579],{"class":930,"line":1019},[928,5580,1328],{"emptyLinePlaceholder":1327},[928,5582,5583,5585,5587,5590],{"class":930,"line":1025},[928,5584,1853],{"class":1175},[928,5586,1378],{"class":1175},[928,5588,5589],{"class":1241}," TxByAllAnnotationTest",[928,5591,1861],{"class":934},[928,5593,5594],{"class":930,"line":1034},[928,5595,1328],{"emptyLinePlaceholder":1327},[928,5597,5598,5600],{"class":930,"line":1048},[928,5599,1870],{"class":934},[928,5601,1906],{"class":1175},[928,5603,5604,5606,5608,5611],{"class":930,"line":1062},[928,5605,2100],{"class":1175},[928,5607,1918],{"class":1175},[928,5609,5610],{"class":1241}," testTxAllAnnotation",[928,5612,1924],{"class":934},[928,5614,5615,5618,5620,5622,5625],{"class":930,"line":1076},[928,5616,5617],{"class":934},"        ApplicationContext applicationContext ",[928,5619,1245],{"class":1175},[928,5621,5296],{"class":1175},[928,5623,5624],{"class":1241}," AnnotationConfigApplicationContext",[928,5626,5627],{"class":934},"(SpringConfig.class);\n",[928,5629,5630,5633,5635,5638,5641,5643,5646],{"class":930,"line":1085},[928,5631,5632],{"class":934},"        BookController accountService ",[928,5634,1245],{"class":1175},[928,5636,5637],{"class":934}," applicationContext.",[928,5639,5640],{"class":1241},"getBean",[928,5642,1644],{"class":934},[928,5644,5645],{"class":1248},"\"bookController\"",[928,5647,5648],{"class":934},", BookController.class);\n",[928,5650,5651,5654,5656,5658,5660,5662,5664],{"class":930,"line":1091},[928,5652,5653],{"class":934},"        accountService.",[928,5655,3295],{"class":1241},[928,5657,1644],{"class":934},[928,5659,2337],{"class":1647},[928,5661,1966],{"class":934},[928,5663,2337],{"class":1647},[928,5665,1977],{"class":934},[928,5667,5668],{"class":930,"line":1100},[928,5669,2202],{"class":934},[928,5671,5672],{"class":930,"line":1114},[928,5673,1891],{"class":934},[892,5675,5677],{"id":5676},"基于xml的声明式事务","基于XML的声明式事务",[901,5679,5680],{"id":5680},"场景模拟",[897,5682,5683],{},"参考基于注解的声明式事务",[901,5685,5687],{"id":5686},"修改spring配置文件","修改Spring配置文件",[897,5689,5690],{},"将Spring配置文件中去掉tx:annotation-driven 标签，并添加配置：",[919,5692,5694],{"className":921,"code":5693,"language":923,"meta":11,"style":11},"\u003Caop:config>\n    \u003C!-- 配置事务通知和切入点表达式 -->\n    \u003Caop:advisor advice-ref=\"txAdvice\" pointcut=\"execution(* com.codermast.spring.tx.xml.service.impl.*.*(..))\">\u003C\u002Faop:advisor>\n\u003C\u002Faop:config>\n\u003C!-- tx:advice标签：配置事务通知 -->\n\u003C!-- id属性：给事务通知标签设置唯一标识，便于引用 -->\n\u003C!-- transaction-manager属性：关联事务管理器 -->\n\u003Ctx:advice id=\"txAdvice\" transaction-manager=\"transactionManager\">\n    \u003Ctx:attributes>\n        \u003C!-- tx:method标签：配置具体的事务方法 -->\n        \u003C!-- name属性：指定方法名，可以使用星号代表多个字符 -->\n        \u003Ctx:method name=\"get*\" read-only=\"true\"\u002F>\n        \u003Ctx:method name=\"query*\" read-only=\"true\"\u002F>\n        \u003Ctx:method name=\"find*\" read-only=\"true\"\u002F>\n    \n        \u003C!-- read-only属性：设置只读属性 -->\n        \u003C!-- rollback-for属性：设置回滚的异常 -->\n        \u003C!-- no-rollback-for属性：设置不回滚的异常 -->\n        \u003C!-- isolation属性：设置事务的隔离级别 -->\n        \u003C!-- timeout属性：设置事务的超时属性 -->\n        \u003C!-- propagation属性：设置事务的传播行为 -->\n        \u003Ctx:method name=\"save*\" read-only=\"false\" rollback-for=\"java.lang.Exception\" propagation=\"REQUIRES_NEW\"\u002F>\n        \u003Ctx:method name=\"update*\" read-only=\"false\" rollback-for=\"java.lang.Exception\" propagation=\"REQUIRES_NEW\"\u002F>\n        \u003Ctx:method name=\"delete*\" read-only=\"false\" rollback-for=\"java.lang.Exception\" propagation=\"REQUIRES_NEW\"\u002F>\n    \u003C\u002Ftx:attributes>\n\u003C\u002Ftx:advice>\n",[925,5695,5696,5705,5710,5739,5747,5752,5757,5762,5783,5792,5797,5802,5826,5847,5868,5872,5877,5882,5887,5892,5897,5902,5940,5973,6006,6014],{"__ignoreMap":11},[928,5697,5698,5700,5703],{"class":930,"line":931},[928,5699,935],{"class":934},[928,5701,5702],{"class":938},"aop:config",[928,5704,942],{"class":934},[928,5706,5707],{"class":930,"line":945},[928,5708,5709],{"class":948},"    \u003C!-- 配置事务通知和切入点表达式 -->\n",[928,5711,5712,5714,5717,5720,5722,5725,5728,5730,5733,5735,5737],{"class":930,"line":952},[928,5713,955],{"class":934},[928,5715,5716],{"class":938},"aop:advisor",[928,5718,5719],{"class":1241}," advice-ref",[928,5721,1245],{"class":934},[928,5723,5724],{"class":1248},"\"txAdvice\"",[928,5726,5727],{"class":1241}," pointcut",[928,5729,1245],{"class":934},[928,5731,5732],{"class":1248},"\"execution(* com.codermast.spring.tx.xml.service.impl.*.*(..))\"",[928,5734,4063],{"class":934},[928,5736,5716],{"class":938},[928,5738,942],{"class":934},[928,5740,5741,5743,5745],{"class":930,"line":963},[928,5742,1154],{"class":934},[928,5744,5702],{"class":938},[928,5746,942],{"class":934},[928,5748,5749],{"class":930,"line":979},[928,5750,5751],{"class":948},"\u003C!-- tx:advice标签：配置事务通知 -->\n",[928,5753,5754],{"class":930,"line":994},[928,5755,5756],{"class":948},"\u003C!-- id属性：给事务通知标签设置唯一标识，便于引用 -->\n",[928,5758,5759],{"class":930,"line":1009},[928,5760,5761],{"class":948},"\u003C!-- transaction-manager属性：关联事务管理器 -->\n",[928,5763,5764,5766,5769,5771,5773,5775,5777,5779,5781],{"class":930,"line":1019},[928,5765,935],{"class":934},[928,5767,5768],{"class":938},"tx:advice",[928,5770,1370],{"class":1241},[928,5772,1245],{"class":934},[928,5774,5724],{"class":1248},[928,5776,4114],{"class":1241},[928,5778,1245],{"class":934},[928,5780,4033],{"class":1248},[928,5782,942],{"class":934},[928,5784,5785,5787,5790],{"class":930,"line":1025},[928,5786,955],{"class":934},[928,5788,5789],{"class":938},"tx:attributes",[928,5791,942],{"class":934},[928,5793,5794],{"class":930,"line":1034},[928,5795,5796],{"class":948},"        \u003C!-- tx:method标签：配置具体的事务方法 -->\n",[928,5798,5799],{"class":930,"line":1048},[928,5800,5801],{"class":948},"        \u003C!-- name属性：指定方法名，可以使用星号代表多个字符 -->\n",[928,5803,5804,5806,5809,5811,5813,5816,5819,5821,5824],{"class":930,"line":1062},[928,5805,966],{"class":934},[928,5807,5808],{"class":938},"tx:method",[928,5810,1395],{"class":1241},[928,5812,1245],{"class":934},[928,5814,5815],{"class":1248},"\"get*\"",[928,5817,5818],{"class":1241}," read-only",[928,5820,1245],{"class":934},[928,5822,5823],{"class":1248},"\"true\"",[928,5825,1411],{"class":934},[928,5827,5828,5830,5832,5834,5836,5839,5841,5843,5845],{"class":930,"line":1076},[928,5829,966],{"class":934},[928,5831,5808],{"class":938},[928,5833,1395],{"class":1241},[928,5835,1245],{"class":934},[928,5837,5838],{"class":1248},"\"query*\"",[928,5840,5818],{"class":1241},[928,5842,1245],{"class":934},[928,5844,5823],{"class":1248},[928,5846,1411],{"class":934},[928,5848,5849,5851,5853,5855,5857,5860,5862,5864,5866],{"class":930,"line":1085},[928,5850,966],{"class":934},[928,5852,5808],{"class":938},[928,5854,1395],{"class":1241},[928,5856,1245],{"class":934},[928,5858,5859],{"class":1248},"\"find*\"",[928,5861,5818],{"class":1241},[928,5863,1245],{"class":934},[928,5865,5823],{"class":1248},[928,5867,1411],{"class":934},[928,5869,5870],{"class":930,"line":1091},[928,5871,1886],{"class":934},[928,5873,5874],{"class":930,"line":1100},[928,5875,5876],{"class":948},"        \u003C!-- read-only属性：设置只读属性 -->\n",[928,5878,5879],{"class":930,"line":1114},[928,5880,5881],{"class":948},"        \u003C!-- rollback-for属性：设置回滚的异常 -->\n",[928,5883,5884],{"class":930,"line":1128},[928,5885,5886],{"class":948},"        \u003C!-- no-rollback-for属性：设置不回滚的异常 -->\n",[928,5888,5889],{"class":930,"line":1142},[928,5890,5891],{"class":948},"        \u003C!-- isolation属性：设置事务的隔离级别 -->\n",[928,5893,5894],{"class":930,"line":1151},[928,5895,5896],{"class":948},"        \u003C!-- timeout属性：设置事务的超时属性 -->\n",[928,5898,5899],{"class":930,"line":1492},[928,5900,5901],{"class":948},"        \u003C!-- propagation属性：设置事务的传播行为 -->\n",[928,5903,5904,5906,5908,5910,5912,5915,5917,5919,5922,5925,5927,5930,5933,5935,5938],{"class":930,"line":1498},[928,5905,966],{"class":934},[928,5907,5808],{"class":938},[928,5909,1395],{"class":1241},[928,5911,1245],{"class":934},[928,5913,5914],{"class":1248},"\"save*\"",[928,5916,5818],{"class":1241},[928,5918,1245],{"class":934},[928,5920,5921],{"class":1248},"\"false\"",[928,5923,5924],{"class":1241}," rollback-for",[928,5926,1245],{"class":934},[928,5928,5929],{"class":1248},"\"java.lang.Exception\"",[928,5931,5932],{"class":1241}," propagation",[928,5934,1245],{"class":934},[928,5936,5937],{"class":1248},"\"REQUIRES_NEW\"",[928,5939,1411],{"class":934},[928,5941,5942,5944,5946,5948,5950,5953,5955,5957,5959,5961,5963,5965,5967,5969,5971],{"class":930,"line":1521},[928,5943,966],{"class":934},[928,5945,5808],{"class":938},[928,5947,1395],{"class":1241},[928,5949,1245],{"class":934},[928,5951,5952],{"class":1248},"\"update*\"",[928,5954,5818],{"class":1241},[928,5956,1245],{"class":934},[928,5958,5921],{"class":1248},[928,5960,5924],{"class":1241},[928,5962,1245],{"class":934},[928,5964,5929],{"class":1248},[928,5966,5932],{"class":1241},[928,5968,1245],{"class":934},[928,5970,5937],{"class":1248},[928,5972,1411],{"class":934},[928,5974,5975,5977,5979,5981,5983,5986,5988,5990,5992,5994,5996,5998,6000,6002,6004],{"class":930,"line":1527},[928,5976,966],{"class":934},[928,5978,5808],{"class":938},[928,5980,1395],{"class":1241},[928,5982,1245],{"class":934},[928,5984,5985],{"class":1248},"\"delete*\"",[928,5987,5818],{"class":1241},[928,5989,1245],{"class":934},[928,5991,5921],{"class":1248},[928,5993,5924],{"class":1241},[928,5995,1245],{"class":934},[928,5997,5929],{"class":1248},[928,5999,5932],{"class":1241},[928,6001,1245],{"class":934},[928,6003,5937],{"class":1248},[928,6005,1411],{"class":934},[928,6007,6008,6010,6012],{"class":930,"line":1550},[928,6009,1012],{"class":934},[928,6011,5789],{"class":938},[928,6013,942],{"class":934},[928,6015,6016,6018,6020],{"class":930,"line":1559},[928,6017,1154],{"class":934},[928,6019,5768],{"class":938},[928,6021,942],{"class":934},[897,6023,6024,6025],{},"> 注意：基于xml实现的声明式事务，必须引入aspectJ的依赖\n>\n> ",[925,6026,6027],{},"xml &gt; &lt;dependency&gt; &gt;   &lt;groupId&gt;org.springframework&lt;\u002FgroupId&gt; &gt;   &lt;artifactId&gt;spring-aspects&lt;\u002FartifactId&gt; &gt;   &lt;version&gt;6.0.2&lt;\u002Fversion&gt; &gt; &lt;\u002Fdependency&gt; &gt; ",[6029,6030,6031],"style",{},"html pre.shiki code .sxrX7, html code.shiki .sxrX7{--shiki-light:#24292E;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sovSZ, html code.shiki .sovSZ{--shiki-light:#22863A;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sCsY4, html code.shiki .sCsY4{--shiki-light:#6A737D;--shiki-default:#6A737D;--shiki-dark:#6A737D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s8jYJ, html code.shiki .s8jYJ{--shiki-light:#D73A49;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .snPdu, html code.shiki .snPdu{--shiki-light:#6F42C1;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sIIMD, html code.shiki .sIIMD{--shiki-light:#032F62;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sBjJW, html code.shiki .sBjJW{--shiki-light:#005CC5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sP4rz, html code.shiki .sP4rz{--shiki-light:#E36209;--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":11,"searchDepth":945,"depth":945,"links":6033},[6034,6038,6043,6055],{"id":894,"depth":945,"text":895,"children":6035},[6036,6037],{"id":903,"depth":952,"text":903},{"id":1773,"depth":952,"text":1774},{"id":2512,"depth":945,"text":2512,"children":6039},[6040,6041,6042],{"id":2515,"depth":952,"text":2515},{"id":2565,"depth":952,"text":2565},{"id":2742,"depth":952,"text":2742},{"id":2790,"depth":945,"text":2790,"children":6044},[6045,6046,6047,6048,6049,6050,6051,6052,6053,6054],{"id":2793,"depth":952,"text":903},{"id":3764,"depth":952,"text":3764},{"id":3910,"depth":952,"text":3910},{"id":4139,"depth":952,"text":4140},{"id":4149,"depth":952,"text":4150},{"id":4271,"depth":952,"text":4272},{"id":4445,"depth":952,"text":4446},{"id":4588,"depth":952,"text":4589},{"id":4858,"depth":952,"text":4859},{"id":5156,"depth":952,"text":5156},{"id":5676,"depth":945,"text":5677,"children":6056},[6057,6058],{"id":5680,"depth":952,"text":5680},{"id":5686,"depth":952,"text":5687},"md",{},{"title":523,"description":11},"other\u002Fspring-series\u002Fspring\u002Fspring-transaction","gG78RsrsbljMFOwyy7Fyj7R7vweOgixZ_fa1zzsfLgM",1775496430590]