-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path21198.html
285 lines (276 loc) · 41 KB
/
21198.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>SpringBoot(四):事务管理 | 征蓬</title><meta name="keywords" content="SpringBoot"><meta name="author" content="huy"><meta name="copyright" content="huy"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="事务管理相关内容。">
<meta property="og:type" content="article">
<meta property="og:title" content="SpringBoot(四):事务管理">
<meta property="og:url" content="http://example.com/21198.html">
<meta property="og:site_name" content="征蓬">
<meta property="og:description" content="事务管理相关内容。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://example.com/pics/scenery/10.jpg">
<meta property="article:published_time" content="2021-02-10T08:35:32.000Z">
<meta property="article:modified_time" content="2022-01-27T00:41:21.638Z">
<meta property="article:author" content="huy">
<meta property="article:tag" content="SpringBoot">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://example.com/pics/scenery/10.jpg"><link rel="shortcut icon" href="/pics/avatar/8.png"><link rel="canonical" href="http://example.com/21198"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/node-snackbar/0.1.16/snackbar.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="/font/wcsf.ttf" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = {
root: '/',
algolia: undefined,
localSearch: {"path":"search.xml","languages":{"hits_empty":"找不到您查询的内容:${query}"}},
translate: undefined,
noticeOutdate: undefined,
highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
copy: {
success: '复制成功',
error: '复制错误',
noSupport: '浏览器不支持'
},
relativeDate: {
homepage: false,
post: false
},
runtime: '',
date_suffix: {
just: '刚刚',
min: '分钟前',
hour: '小时前',
day: '天前',
month: '个月前'
},
copyright: undefined,
lightbox: 'fancybox',
Snackbar: {"chs_to_cht":"你已切换为繁体","cht_to_chs":"你已切换为简体","day_to_night":"你已切换为深色模式","night_to_day":"你已切换为浅色模式","bgLight":"#49b1f5","bgDark":"#121212","position":"bottom-left"},
source: {
jQuery: 'https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.6.0/jquery.min.js',
justifiedGallery: {
js: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/js/jquery.justifiedGallery.min.js',
css: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/css/justifiedGallery.min.css'
},
fancybox: {
js: 'https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.js',
css: 'https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.css'
}
},
isPhotoFigcaption: false,
islazyload: false,
isanchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
title: 'SpringBoot(四):事务管理',
isPost: true,
isHome: false,
isHighlightShrink: false,
isToc: true,
postUpdate: '2022-01-27 08:41:21'
}</script><noscript><style type="text/css">
#nav {
opacity: 1
}
.justified-gallery img {
opacity: 1
}
#recent-posts time,
#post-meta time {
display: inline !important
}
</style></noscript><script>(win=>{
win.saveToLocal = {
set: function setWithExpiry(key, value, ttl) {
if (ttl === 0) return
const now = new Date()
const expiryDay = ttl * 86400000
const item = {
value: value,
expiry: now.getTime() + expiryDay,
}
localStorage.setItem(key, JSON.stringify(item))
},
get: function getWithExpiry(key) {
const itemStr = localStorage.getItem(key)
if (!itemStr) {
return undefined
}
const item = JSON.parse(itemStr)
const now = new Date()
if (now.getTime() > item.expiry) {
localStorage.removeItem(key)
return undefined
}
return item.value
}
}
win.getScript = url => new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = url
script.async = true
script.onerror = reject
script.onload = script.onreadystatechange = function() {
const loadState = this.readyState
if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
script.onload = script.onreadystatechange = null
resolve()
}
document.head.appendChild(script)
})
win.activateDarkMode = function () {
document.documentElement.setAttribute('data-theme', 'dark')
if (document.querySelector('meta[name="theme-color"]') !== null) {
document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
}
}
win.activateLightMode = function () {
document.documentElement.setAttribute('data-theme', 'light')
if (document.querySelector('meta[name="theme-color"]') !== null) {
document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
}
}
const t = saveToLocal.get('theme')
if (t === 'dark') activateDarkMode()
else if (t === 'light') activateLightMode()
const asideStatus = saveToLocal.get('aside-status')
if (asideStatus !== undefined) {
if (asideStatus === 'hide') {
document.documentElement.classList.add('hide-aside')
} else {
document.documentElement.classList.remove('hide-aside')
}
}
const detectApple = () => {
if (GLOBAL_CONFIG_SITE.isHome && /iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
document.documentElement.classList.add('apple')
}
}
detectApple()
})(window)</script><link rel="stylesheet" href="/css/custom.css" media="defer" onload="this.media='all'"><!-- hexo injector head_end start --><link rel="stylesheet" href="https://npm.elemecdn.com/hexo-butterfly-tag-plugins-plus@latest/lib/assets/font-awesome-animation.min.css" media="defer" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-butterfly-tag-plugins-plus@latest/lib/tag_plugins.min.css" media="defer" onload="this.media='all'"><script async src="https://npm.elemecdn.com/hexo-butterfly-tag-plugins-plus@latest/lib/assets/carousel-touch.js"></script><!-- hexo injector head_end end --><meta name="generator" content="Hexo 5.4.1"></head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="/pics/Asoul/8.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data"><div class="data-item is-center"><div class="data-item-link"><a href="/archives/"><div class="headline">文章</div><div class="length-num">54</div></a></div></div><div class="data-item is-center"><div class="data-item-link"><a href="/tags/"><div class="headline">标签</div><div class="length-num">17</div></a></div></div><div class="data-item is-center"><div class="data-item-link"><a href="/categories/"><div class="headline">分类</div><div class="length-num">12</div></a></div></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('/pics/scenery/10.jpg')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">征蓬</a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">SpringBoot(四):事务管理</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-02-10T08:35:32.000Z" title="发表于 2021-02-10 16:35:32">2021-02-10</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-01-27T00:41:21.638Z" title="更新于 2022-01-27 08:41:21">2022-01-27</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/SpringBoot/">SpringBoot</a></span></div><div class="meta-secondline"></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><blockquote>
<p>上一篇文章中,能够访问数据库、使用 Mybatis 进行操作。</p>
<p>本篇将介绍事务管理相关的配置、操作等。</p>
</blockquote>
<h2 id="简介-7">简介</h2>
<p>程序中对数据进行操作时,有可能会发生意想不到的情况。异常发生后,通常会导致后续的操作无法完成,已经操作过的数据不一定完整或可靠。因此,需要在程序发生异常时将本次操作的数据全部回滚。</p>
<p>事务的作用就是保证每一次针对数据库的操作都是可靠的,在执行失败时,回退本次操作的全部过程,让操作恢复到原始状态,问题修复后,再重新执行。</p>
<p>例如:在购买商品时,需要创建订单、减少商品库存等操作,如果减少库存失败,则会出现超卖的情况。所以需要将这两个操作放在一个事务中,能够保证操作全部成功或者全部失败。</p>
<h2 id="使用">使用</h2>
<h3 id="依赖导入">依赖导入</h3>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.mybatis.spring.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>mybatis-spring-boot-starter<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>2.1.4<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure>
<h3 id="代码">代码</h3>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">TblUserInfoMapper</span> </span>{</span><br><span class="line"> <span class="function">Integer <span class="title">insertUser</span><span class="params">(TblUserInfoEntity userInfoEntity)</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">"com.example.demo.mapper.TblUserInfoMapper"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">insert</span> <span class="attr">id</span>=<span class="string">"insertUser"</span> <span class="attr">parameterType</span>=<span class="string">"com.example.demo.entity.TblUserInfoEntity"</span>></span></span><br><span class="line"> INSERT INTO TBL_USER_INFO (ID, USERNAME, USERAGE) VALUES (#{id}, #{userName}, #{userAge})</span><br><span class="line"> <span class="tag"></<span class="name">insert</span>></span></span><br><span class="line"><span class="tag"></<span class="name">mapper</span>></span></span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">TblUserInfoService</span> </span>{</span><br><span class="line"> <span class="function">Integer <span class="title">insertUser</span><span class="params">(TblUserInfoEntity userInfoEntity)</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TblUserInfoServiceImpl</span> <span class="keyword">implements</span> <span class="title">TblUserInfoService</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 添加 @Autowired 报错的话,需要在 mapper 类上添加 @Repository 注解,将其纳入 ioc 管理</span></span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> TblUserInfoMapper userInfoMapper;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="meta">@Transactional</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Integer <span class="title">insertUser</span><span class="params">(TblUserInfoEntity userInfoEntity)</span> </span>{</span><br><span class="line"></span><br><span class="line"> Integer flag = userInfoMapper.insertUser(userInfoEntity);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException();</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping("/userInfo")</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TblUserInfoController</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> TblUserInfoService userInfoService;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@RequestMapping("/insert")</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> JsonResult <span class="title">insertUser</span><span class="params">()</span> </span>{</span><br><span class="line"></span><br><span class="line"> TblUserInfoEntity user = <span class="keyword">new</span> TblUserInfoEntity();</span><br><span class="line"> user.setId(<span class="string">"123"</span>);</span><br><span class="line"> user.setUserName(<span class="string">"张三"</span>);</span><br><span class="line"> user.setUserAge(<span class="string">"23"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(userInfoService.insertUser(user) > <span class="number">0</span>){</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> JsonResult();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> JsonResult(<span class="string">""</span>, <span class="string">"数据插入失败"</span>, <span class="string">"0"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="测试">测试</h3>
<p>在 <code>TblUserInfoServiceImpl</code> 类中的方法添加了 <em>@Transactional</em> 注解,然后在方法内部手动抛出了异常:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">throw new RuntimeException();</span><br></pre></td></tr></table></figure>
<p>进行测试后,发现数据库中并没有这条记录,由此可见,事务生效了。</p>
<h2 id="事务注解属性详解">事务注解属性详解</h2>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target({ElementType.TYPE, ElementType.METHOD})</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> Transactional {</span><br><span class="line"> <span class="meta">@AliasFor("transactionManager")</span></span><br><span class="line"> <span class="function">String <span class="title">value</span><span class="params">()</span> <span class="keyword">default</span> ""</span>;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@AliasFor("value")</span></span><br><span class="line"> <span class="function">String <span class="title">transactionManager</span><span class="params">()</span> <span class="keyword">default</span> ""</span>;</span><br><span class="line"></span><br><span class="line"> String[] label() <span class="keyword">default</span> {};</span><br><span class="line"></span><br><span class="line"> <span class="function">Propagation <span class="title">propagation</span><span class="params">()</span> <span class="keyword">default</span> Propagation.REQUIRED</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function">Isolation <span class="title">isolation</span><span class="params">()</span> <span class="keyword">default</span> Isolation.DEFAULT</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">timeout</span><span class="params">()</span> <span class="keyword">default</span> -1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function">String <span class="title">timeoutString</span><span class="params">()</span> <span class="keyword">default</span> ""</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">readOnly</span><span class="params">()</span> <span class="keyword">default</span> <span class="keyword">false</span></span>;</span><br><span class="line"></span><br><span class="line"> Class<? extends Throwable>[] rollbackFor() <span class="keyword">default</span> {};</span><br><span class="line"></span><br><span class="line"> String[] rollbackForClassName() <span class="keyword">default</span> {};</span><br><span class="line"></span><br><span class="line"> Class<? extends Throwable>[] noRollbackFor() <span class="keyword">default</span> {};</span><br><span class="line"></span><br><span class="line"> String[] noRollbackForClassName() <span class="keyword">default</span> {};</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面是事务注解的源码,这部分内容将详细介绍注解的其他属性。</p>
<h3 id="transactionManager">transactionManager()</h3>
<p>事务应用的管理器是哪一个,常用的管理器有如下四种:</p>
<table>
<thead>
<tr>
<th>名称</th>
<th>实现</th>
</tr>
</thead>
<tbody>
<tr>
<td>JDBC</td>
<td>DataSourceTransactionManager</td>
</tr>
<tr>
<td>JPA</td>
<td>JapTransactionManager</td>
</tr>
<tr>
<td>Hibernate</td>
<td>HibernateTransactionManager</td>
</tr>
<tr>
<td>JDO</td>
<td>JdoTransactionManager</td>
</tr>
<tr>
<td>分布式</td>
<td>JtaTransactionManager</td>
</tr>
</tbody>
</table>
<h3 id="isolation">isolation()</h3>
<p>这个属性表示隔离级别</p>
<table>
<thead>
<tr>
<th>隔离级别</th>
<th>级别值</th>
<th>导致的问题</th>
</tr>
</thead>
<tbody>
<tr>
<td>Read-Uncommitted</td>
<td>0</td>
<td>导致脏读</td>
</tr>
<tr>
<td>Read-Committed</td>
<td>1</td>
<td>避免脏读,允许不可重复读和幻影读</td>
</tr>
<tr>
<td>Repeatable-Read</td>
<td>2</td>
<td>避免脏读,不可重复读,允许幻影读</td>
</tr>
<tr>
<td>Serializable</td>
<td>3</td>
<td>串行化读。事务只能一个一个执行避免了避免脏读、不可重复读、允许幻影读。<strong>执行效率慢。</strong></td>
</tr>
</tbody>
</table>
<h3 id="timeout">timeout()</h3>
<p>事务的超时设置,要求返回事务必须在多少秒内完成。</p>
<h3 id="readOnly">readOnly()</h3>
<p>事务只读属性。</p>
<p>从该属性设置为 true 开始,直到这个事务完成执行后,其他事务提交的数据将不可见。</p>
<h3 id="rollbackFor">rollbackFor()</h3>
<p>导致事务回滚的异常类数组。</p>
<h3 id="rollbackForClassName">rollbackForClassName()</h3>
<p>导致事务回滚的异常类名称的数组。</p>
<h3 id="noRollbackFor">noRollbackFor()</h3>
<p>不会导致事务回滚的异常类数组。</p>
<h3 id="noRollbackForClassName">noRollbackForClassName()</h3>
<p>不会导致事务回滚的异常类名称的数组。</p>
<h2 id="常见问题-3">常见问题</h2>
<h3 id="异常没有被捕获">异常没有被捕获</h3>
<p>当出现的异常没有被捕获到时,会导致事务继续执行,不会回滚。</p>
<p><strong>举例:</strong></p>
<p>当手动抛出 SQL 异常时,该事务会继续执行</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TblUserInfoServiceImpl</span> <span class="keyword">implements</span> <span class="title">TblUserInfoService</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> TblUserInfoMapper userInfoMapper;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="meta">@Transactional</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Integer <span class="title">insertUser</span><span class="params">(TblUserInfoEntity userInfoEntity)</span> </span>{</span><br><span class="line"> Integer flag = userInfoMapper.insertUser(userInfoEntity);</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> SQLException();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>**解决:**需要在 <code>@Transaction</code> 注解中指定异常,使用 rollbackFor 属性</p>
<p><strong>示例:</strong> <code>@Transaction(rollbackFor = Exception.class)</code></p>
<h3 id="事务的范围">事务的范围</h3>
<p>在并发时,使用了事务注解,还会出现两条相同的信息。</p>
<p><strong>举例:</strong></p>
<p>在考虑并发问题时,可能会在业务层代码上增加 <strong>synchronized</strong> 关键字。</p>
<p>在用户信息表中,插入记录前会判断是否存在相同用户名的用户,如果存在则不会插入,不存在时才会新增记录。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TblUserInfoServiceImpl</span> <span class="keyword">implements</span> <span class="title">TblUserInfoService</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> TblUserInfoMapper userInfoMapper;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="meta">@Transactional</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">insertUser</span><span class="params">(TblUserInfoEntity userInfoEntity)</span> </span>{</span><br><span class="line"> <span class="comment">// 业务逻辑</span></span><br><span class="line"> userInfoMapper.insertUser(userInfoEntity);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>解决:</strong></p>
<p>在调用该 service 的地方枷锁,保证锁的范围大于事务范围。</p>
<p>在加锁的代码执行完成后,锁被释放,但是事务还在执行的过程中,此时线程二进入。也就是说,在第二个事务的眼中,当前数据库的状态和第一个事务开始时数据库的状态一样。</p>
<p>由于 MySQL innodb 的默认隔离级别是可重复读,在线程二事务开始时,线程一的事务还没有提交,数据未更新。第二个线程也进行了插入操作,导致了脏数据。</p>
<h2 id="参考-23">参考</h2>
<ul>
<li>
<p>[1] <a target="_blank" rel="noopener" href="https://blog.didispace.com/spring-boot-learning-21-3-10/">Spring Boot 2.x基础教程:事务管理入门</a></p>
</li>
<li>
<p>[2] <a target="_blank" rel="noopener" href="https://blog.didispace.com/transactional-not-rollback/">为什么加了@Transactional注解,事务没有回滚?</a></p>
</li>
<li>
<p>[3] <a target="_blank" rel="noopener" href="https://www.jianshu.com/p/5687e2a38fbc">@Transaction必知必会</a></p>
</li>
<li>
<p>[4] 《SpringBoot经典学习笔记》 —— 倪升武</p>
</li>
</ul>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="mailto:undefined">huy</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="http://example.com/21198.html">http://example.com/21198.html</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外,均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="http://example.com" target="_blank">征蓬</a>!</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/SpringBoot/">SpringBoot</a></div><div class="post_share"><div class="social-share" data-image="/pics/scenery/10.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://jsdelivr.pai233.top/npm/social-share.js/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://jsdelivr.pai233.top/npm/social-share.js/dist/js/social-share.min.js" defer></script></div></div><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/11802.html" title="Bean的加载和控制"><img class="cover" src="/pics/scenery/03.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-05-02</div><div class="title">Bean的加载和控制</div></div></a></div><div><a href="/48587.html" title="SpringBoot(一):日志框架配置"><img class="cover" src="/pics/scenery/03.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-01-29</div><div class="title">SpringBoot(一):日志框架配置</div></div></a></div><div><a href="/35740.html" title="SpringBoot(七):Application文件配置"><img class="cover" src="/pics/scenery/01.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-01-20</div><div class="title">SpringBoot(七):Application文件配置</div></div></a></div><div><a href="/23140.html" title="SpringBoot(三):数据库"><img class="cover" src="/pics/scenery/18.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-02-10</div><div class="title">SpringBoot(三):数据库</div></div></a></div><div><a href="/3737.html" title="SpringBoot(五):Swagger2配置和服务器部署"><img class="cover" src="/pics/scenery/15.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-12-09</div><div class="title">SpringBoot(五):Swagger2配置和服务器部署</div></div></a></div><div><a href="/4811.html" title="SpringBoot(八):Thymeleaf基础功能"><img class="cover" src="/pics/scenery/03.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-02-03</div><div class="title">SpringBoot(八):Thymeleaf基础功能</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%AE%80%E4%BB%8B-7"><span class="toc-number">1.</span> <span class="toc-text">简介</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8"><span class="toc-number">2.</span> <span class="toc-text">使用</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BE%9D%E8%B5%96%E5%AF%BC%E5%85%A5"><span class="toc-number">2.1.</span> <span class="toc-text">依赖导入</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%A3%E7%A0%81"><span class="toc-number">2.2.</span> <span class="toc-text">代码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B5%8B%E8%AF%95"><span class="toc-number">2.3.</span> <span class="toc-text">测试</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BA%8B%E5%8A%A1%E6%B3%A8%E8%A7%A3%E5%B1%9E%E6%80%A7%E8%AF%A6%E8%A7%A3"><span class="toc-number">3.</span> <span class="toc-text">事务注解属性详解</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#transactionManager"><span class="toc-number">3.1.</span> <span class="toc-text">transactionManager()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#isolation"><span class="toc-number">3.2.</span> <span class="toc-text">isolation()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#timeout"><span class="toc-number">3.3.</span> <span class="toc-text">timeout()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#readOnly"><span class="toc-number">3.4.</span> <span class="toc-text">readOnly()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#rollbackFor"><span class="toc-number">3.5.</span> <span class="toc-text">rollbackFor()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#rollbackForClassName"><span class="toc-number">3.6.</span> <span class="toc-text">rollbackForClassName()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#noRollbackFor"><span class="toc-number">3.7.</span> <span class="toc-text">noRollbackFor()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#noRollbackForClassName"><span class="toc-number">3.8.</span> <span class="toc-text">noRollbackForClassName()</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98-3"><span class="toc-number">4.</span> <span class="toc-text">常见问题</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%82%E5%B8%B8%E6%B2%A1%E6%9C%89%E8%A2%AB%E6%8D%95%E8%8E%B7"><span class="toc-number">4.1.</span> <span class="toc-text">异常没有被捕获</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BA%8B%E5%8A%A1%E7%9A%84%E8%8C%83%E5%9B%B4"><span class="toc-number">4.2.</span> <span class="toc-text">事务的范围</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8F%82%E8%80%83-23"><span class="toc-number">5.</span> <span class="toc-text">参考</span></a></li></ol></div></div></div></div></main><footer id="footer" style="background-image: url('/pics/scenery/10.jpg')"><div id="footer-wrap"><div class="copyright">©2021 - 2022 By huy</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="local-search"><div class="search-dialog"><div class="search-dialog__title" id="local-search-title">本地搜索</div><div id="local-input-panel"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div></div><hr/><div id="local-search-results"></div><span class="search-close-button"><i class="fas fa-times"></i></span></div><div id="search-mask"></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/node-snackbar/0.1.16/snackbar.min.js"></script><script src="/js/search/local-search.js"></script><div class="js-pjax"></div><script defer="defer" id="ribbon" src="https://jsdelivr.pai233.top/npm/butterfly-extsrc@1/dist/canvas-ribbon.min.js" size="150" alpha="0.6" zIndex="-1" mobile="false" data-click="false"></script><script id="click-heart" src="https://jsdelivr.pai233.top/npm/butterfly-extsrc@1/dist/click-heart.min.js" async="async" mobile="false"></script></div><!-- hexo injector body_end start --><script async src="//at.alicdn.com/t/font_2032782_8d5kxvn09md.js"></script><!-- hexo injector body_end end --></body></html>