声明式事务-02

3.事务的传播机制

事务的传播机制说明:

  1. 当有多个事务处理并存时,如何控制?

  2. 比如用户去购买两次商品(使用不同的方法),每个方法都是一个事务,那么如何控制呢?

    也就是说,某个方法本身是一个事务,然后该方法中又调用了其他一些方法,这些方法也是被@Transactional 修饰的,同样是事务。

  3. 问题在于:里层方法的事务是被外层方法事务管理?还是它本身作为一个独立的事务呢?这就涉及到事务的传播机制问题。

3.1事务传播机制种类

  • 事务传播的属性 / 种类:
传播属性 说明
REQUIRED (默认)如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并且在自己的事务内运行
REQUIRES_NEW 当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起
SUPPORTS 如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中
NOT_SUPPORTED 当前的方法不应该运行在事务中,如果有运行的事务,将它挂起
MANDATORY 当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常
NEVER 当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常
NESTED 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行

常用的只有前面两种:(1)REQUIRED,(2)REQUIRES_NEWREQUIRES_NEW


  • 事务传播的属性/种类机制分析

重点分析 REQUIRED 和 REQUIRES_NEW 两种事务传播属性,其他知道即可。

如下,有一个multiTxTest()方法,该方法中又有f1(),f2() 方法。所有方法都分别开启了声明式事务。

@Transactional
public void multiTxTest() {
f1(); //含事务 f2(); //含事务
}
  1. 如果f1(),f2() 的传播属性都是 REQUIRED,那么它们实际上是被Tx()的事务统一管理的。所有方法是一个整体,只要有一个方法的事务错误,那么两个方法都不会执行成功。

  2. 如果f1(),f2() 的传播属性都是 REQUIRES_NEW,那么f1(),f2()实际上是独立的事务,不会受到Tx()事务的影响。如果f1()错误,不会影响到f2(),反之亦然。

3.2应用实例

需求说明:

  1. 用户要去购买两次商品(使用不同的方法),每个方法都是一个事务,那么如何控制呢?
  2. 看一个具体的案例(用 required 和 requires_new 测试)

代码实现

1.GoodsDao.java

分别有6个方法:queryPriceById,queryPriceById2,updateBalance,updateBalance2,updateAmount,updateAmount2。

package com.li.tx.dao;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository; import javax.annotation.Resource; /**
* @author 李
* @version 1.0
*/
@Repository //将GoodsDao对象 注入到 spring 容器
public class GoodsDao {
@Resource
private JdbcTemplate jdbcTemplate; /**
* 根据商品id,查询对应的商品价格
* @param id
* @return
*/
public Float queryPriceById(Integer id) {
String sql = "select price from goods where goods_id = ?";
Float price = jdbcTemplate.queryForObject(sql, Float.class, id);
return price;
} /**
* 修改用户余额 [减少用户余额]
* @param user_id
* @param money
*/
public void updateBalance(Integer user_id, Float money) {
String sql = "update user_account set money=money-? where user_id=? ";
jdbcTemplate.update(sql, money, user_id);
} /**
* 修改商品库存量
* @param goods_id
* @param amount
*/
public void updateAmount(Integer goods_id, int amount) {
String sql = "update goods_amount set goods_num=goods_num-? where goods_id=? ";
jdbcTemplate.update(sql, amount, goods_id);
} //和queryPriceById的操作是一样的
public Float queryPriceById2(Integer id) {
String sql = "select price from goods where goods_id = ?";
Float price = jdbcTemplate.queryForObject(sql, Float.class, id);
return price;
} //和updateBalance的操作是一样的
public void updateBalance2(Integer user_id, Float money) {
String sql = "update user_account set money=money-? where user_id=? ";
jdbcTemplate.update(sql, money, user_id);
} //和updateAmount的操作是一样的
public void updateAmount2(Integer goods_id, int amount) {
String sql = "update goods_amount set goods_num=goods_num-? where goods_id=? ";
jdbcTemplate.update(sql, amount, goods_id);
}
}

2.GoodsService.java,分别有两个方法buyGoodsByTx,buyGoodsByTx02

package com.li.tx.service;

import com.li.tx.dao.GoodsDao;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource;
import java.beans.Transient; /**
* @author 李
* @version 1.0
*/
@Service //将GoodsService对象注入到容器中
public class GoodsService {
@Resource
private GoodsDao goodsDao; /**
* 进行商品购买的方法
* @param userId
* @param goodsId
* @param amount
*/
@Transactional
public void buyGoodsByTx(int userId, int goodsId, int amount) {
//输出购买的相关信息
System.out.println("用户购买信息 userId=" + userId
+ " goodsId=" + goodsId + " 购买数量=" + amount); //1.得到商品价格
Float price = goodsDao.queryPriceById(goodsId);
//2.减少用户余额
goodsDao.updateBalance(userId, price * amount);
//3.减少商品库存量
goodsDao.updateAmount(goodsId, amount); System.out.println("用户购买成功...");
} /**
* 进行商品购买的方法02,调用的是GoodsDao的2后缀的方法
* @param userId
* @param goodsId
* @param amount
*/
@Transactional
public void buyGoodsByTx02(int userId, int goodsId, int amount) {
//输出购买的相关信息
System.out.println("用户购买信息 userId=" + userId
+ " goodsId=" + goodsId + " 购买数量=" + amount); //1.得到商品价格
Float price = goodsDao.queryPriceById2(goodsId);
//2.减少用户余额
goodsDao.updateBalance2(userId, price * amount);
//3.减少商品库存量
goodsDao.updateAmount2(goodsId, amount); System.out.println("用户购买成功...");
}
}

3.MultiplyService.java

package com.li.tx.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; /**
* @author 李
* @version 1.0
*/
@Service
public class MultiplyService {
@Resource
private GoodsService goodsService; /**
* 说明
* 1.multiBuyGoodsByTx() 方法中,有两次购商品的操作
* 2.buyGoodsByTx 和 buyGoodsByTx02 都是声明式事务
* 3.并且buyGoodsByTx 和 buyGoodsByTx02使用的传播属性为默认的 REQUIRED,
* 即会当做一个整体事务来处理
*/
@Transactional
public void multiBuyGoodsByTx() {
goodsService.buyGoodsByTx(1, 1, 1);
goodsService.buyGoodsByTx02(1, 1, 1);
}
}

4.测试

//测试事务的传播机制
@Test
public void multiBuyGoodsByTx(){
ApplicationContext ioc =
new ClassPathXmlApplicationContext("tx.xml");
MultiplyService multiplyService = ioc.getBean(MultiplyService.class); multiplyService.multiBuyGoodsByTx();
}

测试结果:购买成功

测试前数据:

表结构详见上一篇

测试后数据:

5.在GoodsDao的updateAmount2()方法中添加错误字符,使其不能成功执行:

因为 buyGoodsByTx() 和buyGoodsByTx02() 的事务传播属性都是required,且都在multiBuyGoodsByTx()方法内部,因此它们被视为一个整体。当 buyGoodsByTx02() 执行出现错误,两个方法将会一起回滚。

执行4.的测试代码,测试结果:出现异常。

测试后数据:

仍然是之前的数据,说明两个方法一起进行了事务回滚。

6.将GoodsService 的 buyGoodsByTx() / buyGoodsByTx02() 方法的事务传播属性改为REQUIRES_NEW。


这时两个方法的事务是独立的,buyGoodsByTx02() 失败不会造成 buyGoodsByTx() 的回滚。

7.再执行4.测试方法,结果如下:仍然出现异常

但是只有 buyGoodsByTx() 方法操作改变了数据。

测试前数据:

测试后数据:

说明只有 buyGoodsByTx02() 方法进行了回滚。

4.事务的隔离机制

4.1事务隔离级别说明

MySQL 隔离级别定义了事务与事务之间的隔离程度

MySQL隔离级别(4种) 脏读 不可重复读 幻读 加锁读
读未提交(Read uncommitted) v v v 不加锁
读已提交(Read committed) x v v 不加锁
可重复读(Repeatable read) x x x 不加锁
可串行化(Serializable) x x x 加锁

关于可重复读会不会发生幻读问题:

SQL92标准有,mysql数据库改进了,解决了这个级别的幻读问题。

  • 事务隔离级别说明
  1. Spring声明式事务的默认隔离级别,就是 mysql 数据库默认的隔离级别,一般为 REPREATABLE_READ

    查看源码可知:Use the default isolation level of the underlying datastore. All other levels correspond to the JDBC isolation levels.

  2. 查看数据库的隔离级别 SELECT @@global.tx_isolation

4.2事务隔离级别的设置和测试

整体思路如下:

在开启了声明式事务的某方法中,查询两次数据。在第一次查询后,先在控制台中修改该数据(在终端中默认为自动提交),方法再进行第二次的查询。查看两次查询的数据是否相同。通过这样的方法来模拟两个客户端,测试声明式事务的隔离级别。


1.修改GoodsService.java,先测试默认隔离级别,增加方法 buyGoodsByTxISOLATION()

/**
* 在默认下,声明式事务使用的隔离界别为 可重复读-Repeatable read
*/
@Transactional
public void buyGoodsByTxISOLATION() {
//查询两次商品的价格
Float price = goodsDao.queryPriceById(1);
System.out.println("第一次查询的价格=" + price); Float price2 = goodsDao.queryPriceById(1);
System.out.println("第二次查询的价格=" + price2); }

并在方法如下位置打上断点

2.测试方法

//测试声明式事务的隔离级别
@Test
public void buyGoodsByTxISOLATIONTest() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("tx.xml");
GoodsService goodsService = ioc.getBean(GoodsService.class); goodsService.buyGoodsByTxISOLATION();
}

3.点击debug,当光标跳转到断点时,可以看到第一次查询的 price=10

4.这时我们在控制台修改该数据为 15

5.然后点击Step Over,发现第二次查询的价格仍然为 10

这说明Spring的声明是事务的默认隔离级别为 可重复读。

6.将方法buyGoodsByTxISOLATION() 的事务隔离级别改为 读已提交

读已提交表示只要是提交的数据,在当前事务中都可以读取到最新数据

同时和之前一样打上断点。

7.测试方法不变,点击debug,光标跳转到断点时,可以看到第一次查询时 price=15

8.此时在控制台将该数据改为 20

9.点击Step Over,可以看到第二次查询的数据已经变成了 20

说明当前事务的隔离级别为 读已提交。

4.3事务的超时回滚

  • 基本介绍
  1. 如果一个事务执行的时间超过某个时间限制,就让该事务回滚。
  2. 可以通过设置事务超时回滚来实现
  • 基本语法

例子:超时回滚代码实现

1.GoodsService 中增加方法 buyGoodsByTxTimeout(),并设置事务超时时间为2s。为了模拟超时效果,在方法中休眠4s。

/**
* 1.timeout = 2,表示该方法如果执行时间超过了两秒,就进行回滚
* 2.如果没有设置 timeout,则默认该值为 -1,表示使用默认超时时间,
* 一般为连接的数据库的默认超时时间
*/
@Transactional(timeout = 2)
public void buyGoodsByTxTimeout(int userId, int goodsId, int amount){
//输出购买的相关信息
System.out.println("用户购买信息 userId=" + userId
+ " goodsId=" + goodsId + " 购买数量=" + amount);
//1.得到商品价格
Float price = goodsDao.queryPriceById2(goodsId);
//2.减少用户余额
goodsDao.updateBalance2(userId, price * amount);
//模拟超时
System.out.println("==========超时开始4s=========");
try {
Thread.sleep(4000);//休眠4s
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("==========超时结束4s========="); //3.减少商品库存量
goodsDao.updateAmount2(goodsId, amount);
System.out.println("用户购买成功...");
}

2.测试方法

//测试超时 timeout 属性
@Test
public void buyGoodsByTxTimeoutTest() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("tx.xml");
GoodsService goodsService = ioc.getBean(GoodsService.class);
goodsService.buyGoodsByTxTimeout(1, 1, 1);
}

测试结果:出现异常,显示事务超时。

测试前数据:

测试后数据:

数据没有进行改变,说明事务超时,并进行了回滚。

5.练习

要求:模拟一个用户,进行银行转账,购买淘宝商品的业务。数据表,dao层,service层自己设置,要求保证数据一致性。

  1. seller [卖家表]
  2. buyer [买家表]
  3. goods [商品表[有库存量属性]]
  4. taoBao [taoBao表,提取入账成交额的 10%]
  5. 要求简单实现,使用声明式事务完成
  6. 要求创建新的spring容器文件 shopping_ioc.xml,完成测试

实现

1.创建表格,并插入初始数据

-- buyer表
CREATE TABLE `buyer`(
buyer_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
buyer_name VARCHAR(32) NOT NULL DEFAULT '',
buyer_money DOUBLE NOT NULL DEFAULT 0.0
)CHARSET=utf8; INSERT INTO `buyer` VALUES(NULL,'张三', 1000);
INSERT INTO `buyer` VALUES(NULL,'李四', 2000); -- seller表
CREATE TABLE `seller`(
seller_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
seller_name VARCHAR(32) NOT NULL DEFAULT '',
seller_money DOUBLE NOT NULL DEFAULT 0.0
)CHARSET=utf8 ; INSERT INTO `seller` VALUES(NULL,'卖家1', 0);
INSERT INTO `seller` VALUES(NULL,'卖家2', 0); -- goods表
CREATE TABLE `goods`(
goods_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
goods_name VARCHAR(32) NOT NULL DEFAULT '',
price DOUBLE NOT NULL DEFAULT 0.0,
seller_id INT UNSIGNED,
goods_num INT UNSIGNED DEFAULT 0
)CHARSET=utf8 ; INSERT INTO `goods` VALUES(NULL,'小风扇', 10.00, 1, 100);
INSERT INTO `goods` VALUES(NULL,'小台灯', 12.00, 1, 100);
INSERT INTO `goods` VALUES(NULL,'可口可乐', 3.00, 2, 100); -- taoBao表
CREATE TABLE `taoBao`(
taoBao_money DOUBLE NOT NULL DEFAULT 0.0
)CHARSET=utf8 ; INSERT INTO `taoBao` VALUES(0);

2.ShopDao

package com.li.tx.hw.dao;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository; import javax.annotation.Resource; /**
* @author 李
* @version 1.0
*/
@Repository
public class ShopDao {
@Resource
private JdbcTemplate jdbcTemplate; //通过商品id,查询商品价格
public Double queryGoodsPrice(int goodsId) {
String sql = "SELECT price FROM goods WHERE goods_id=?";
return jdbcTemplate.queryForObject(sql, Double.class, goodsId);
} //通过商品id,查询商品所属的卖家id
public Integer queryGoodsOwner(int goodsId) {
String sql = "SELECT seller_id FROM goods WHERE goods_id=?";
return jdbcTemplate.queryForObject(sql, Integer.class, goodsId);
} //通过商品id,修改商品库存量
public void updateGoodsNum(int goodsId, int shopNum) {
String sql = "UPDATE goods SET goods_num=goods_num-? WHERE goods_id=?";
jdbcTemplate.update(sql, shopNum, goodsId);
} //通过买家id,修改买家余额
public void updateBuyerMoney(Integer buyerId, Double money) {
String sql = "UPDATE buyer SET buyer_money=buyer_money-? WHERE buyer_id=?";
jdbcTemplate.update(sql, money, buyerId);
} //通过卖家id,修改卖家余额
public void updateSellerMoney(Integer sellerId, Double money) {
String sql = "UPDATE seller SET seller_money=seller_money+? WHERE seller_id=?";
jdbcTemplate.update(sql, money, sellerId);
} //修改 taoBao余额
public void updateTaobaoMoney(Double money) {
String sql = "UPDATE taoBao SET taoBao_money=taoBao_money+?";
jdbcTemplate.update(sql, money);
}
}

3.ShopService

package com.li.tx.hw.service;

import com.li.tx.hw.dao.ShopDao;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; /**
* @author 李
* @version 1.0
*/
@Service
public class ShopService {
@Resource
private ShopDao shopDao; @Transactional
public void shopping(int buyerId, int goodsId, int goodsNum) {
System.out.println("用户购买信息 buyerId=" + buyerId
+ " goodsId=" + goodsId + " 购买数量=" + goodsNum); //查询商品价格
Double goodsPrice = shopDao.queryGoodsPrice(goodsId);
System.out.println("商品价格=" + goodsPrice); //查询商品卖家
Integer sellerId = shopDao.queryGoodsOwner(goodsId);
System.out.println("商品所属卖家=" + sellerId); //减少商品库存量
shopDao.updateGoodsNum(goodsId, goodsNum);
System.out.println("商品库存-" + goodsNum); //修改买家余额
shopDao.updateBuyerMoney(buyerId, goodsPrice * goodsNum);
System.out.println("买家余额-" + goodsPrice * goodsNum); //将成交额的 90% 转入卖家余额
shopDao.updateSellerMoney(sellerId, goodsPrice * goodsNum * 0.9);
System.out.println("卖家余额+" + goodsPrice * goodsNum * 0.9); //将成交额的 10% 转入taoBao余额
shopDao.updateTaobaoMoney(goodsPrice * goodsNum * 0.1);
System.out.println("taoBao余额+" + goodsPrice * goodsNum * 0.1); System.out.println("购买成功...");
}
}

4.配置容器文件

<!--配置要扫描的包-->
<context:component-scan base-package="com.li.tx.hw"/> <!--引入外部的属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/> <!--配置数据源对象-->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSources">
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.pwd}"/>
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
</bean> <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSources"/>
</bean> <!--配置事务管理器对象
1.DataSourceTransactionManager 这个对象是进行事务管理的
2.一定要配置数据源属性,即指定该事务管理器 是对哪个数据源进行事务控制
-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
id="dataSourceTransactionManager">
<property name="dataSource" ref="dataSources"/>
</bean> <!--配置:启用基于注解的声明式事务管理功能-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

5.jdbc.properties

jdbc.user=root
jdbc.pwd=123456
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring

6.测试

@Test
public void shoppingTest() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("shopping_ioc.xml");
ShopService shopService = ioc.getBean(ShopService.class);
shopService.shopping(1, 1, 10);
}

测试结果:

测试后的数据:

7.测试数据一致性:

修改sql,使其无法执行:

测试结果:出现异常。

查看数据库表,数据没有改变。说明事务进行了回滚。

day16-声明式事务-02的更多相关文章

  1. spring4声明式事务—02 xml配置方式

    1.配置普通的 controller,service ,dao 的bean. <!-- 配置 dao ,service --> <bean id="bookShopDao& ...

  2. spring4声明式事务—02 xml配置方式

    1.配置普通的 controller,service ,dao 的bean. <!-- 配置 dao ,service --> <bean id="bookShopDao& ...

  3. spring声明式事务管理总结

    事务配置 首先在/WEB-INF/applicationContext.xml添加以下内容: <!-- 配置事务管理器 --> <bean id="transactionM ...

  4. Spring声明式事务管理

    一.Spring 的声明式事务管理概述 1.Spring 的声明式事务管理在底层是建立在 AOP 的基础之上的.其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法 ...

  5. spring笔记--事务管理之声明式事务

    事务简介: 事务管理是企业级应用开发中必不可少的技术,主要用来确保数据的完整性和一致性, 事务:就是一系列动作,它们被当作一个独立的工作单元,这些动作要么全部完成,要么全部不起作用. Spring中使 ...

  6. 全面分析 Spring 的编程式事务管理及声明式事务管理

    开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本 ...

  7. 事务管理(下) 配置spring事务管理的几种方式(声明式事务)

    配置spring事务管理的几种方式(声明式事务) 概要: Spring对编程式事务的支持与EJB有很大的区别.不像EJB和Java事务API(Java Transaction API, JTA)耦合在 ...

  8. spring 声明式事务管理

    简单理解事务: 比如你去ATM机取5000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉5000元钱:然后ATM出5000元钱.这两个步骤必须是要么都执行要么都不执行.如果银行卡扣除了5000块但 ...

  9. spring aop 声明式事务管理

    一.声明式事务管理的概括 声明式事务(declarative transaction management)是Spring提供的对程序事务管理的方式之一. Spring的声明式事务顾名思义就是采用声明 ...

  10. Spring声明式事务管理基于@Transactional注解

    概述:我们已知道Spring声明式事务管理有两种常用的方式,一种是基于tx/aop命名空间的xml配置文件,另一种则是基于@Transactional 注解.         第一种方式我已在上文为大 ...

随机推荐

  1. ES6学习笔记(十四)module的简单使用

    1.前言 module模块机制是es6新引入的,它解决了作用域的问题,使代码更加规范和结构化. 下面简单的使用一下. 2.基本使用 2.1 模块和脚本的区别 模块代码运行在严格模式下,并且没有任何办法 ...

  2. 用map来统计数组中各个字符串的数量

    1.背景 想要统计这一个字符串数组中每一个非重复字符串的数量,使用map来保存其key和value.这个需求在实际开发中经常使用到,我以前总是新建一个空数组来记录不重复字符串,并使用计数器计数,效率低 ...

  3. vue3的学习笔记:MVC、Vue3概要、模板、数据绑定、用Vue3 + element ui、react框架实现购物车案例

    一.前端MVC概要 1.1.库与框架的区别 框架是一个软件的半成品,在全局范围内给了大的约束.库是工具,在单点上给我们提供功能.框架是依赖库的.Vue是框架而jQuery则是库. 1.2.MVC(Mo ...

  4. 抠网页标题栏logo(图标)

    1.打开自己需要抠的网页,例如百度页面 2.在这个网页链接后面+" /favicon.ico " 就可以提取ico图片 3.回车进去,右键鼠标,选择另存为图片就可以成功保存网页中的 ...

  5. 【红队技巧】Windows存储的密码获取

    [红队技巧]Windows存储的密码获取 免责声明: 使用前提 支持版本 利用方式 参考: 免责声明: 本文章仅供学习和研究使用,严禁使用该文章内容对互联网其他应用进行非法操作,若将其用于非法目的,所 ...

  6. 网络yum源下载

    思路一: 按照本地网罗源,然后使用reposync直接将源同步下载到本地 wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/re ...

  7. bugku web2

    打开全是滑稽.... 直接查看源码(用firefox的F12来查看,其他的会注释掉)可以得到flag(抓包不知道怎么回事抓不到,不然应该也可以看到注释的flag)

  8. ES系列二之常见问题解决

    上篇ES系列一之java端API操作结束后本以为就相安无事了,但生产的问题是层出不穷的:下面我就再记录下近几周遇到的问题以及解决方案: 一 更新ES信息报错 报错信息如下: Use Elasticse ...

  9. MyBatis01:框架概述、环境搭建及入门案例、自定义框架

    课程介绍 第一天:mybatis的入门 mybatis的概述 mybatis的环境搭建 mybatis入门案例 自定义mybatis框架(目的:了解mybatis的执行细节) 第二天:mybatis基 ...

  10. 【JVM调优】Day02:CMS的三色标记算法、分区的G1回收器、短时停顿的ZGC回收器

    一.CMS及其三色标记算法 1.核心 标记整个图谱的过程分为多步 多个线程相互工作,才能标记完 标记的算法,JVM虚拟机.go语言使用的都是三色标记算法 2.含义 从那个地方开始,用三种颜色替代 一开 ...