Spring整合Mybatis原理
Spring整合Mybatis原理
1、@MapperScan注解发挥作用
在Spring整合Mybatis的时候,只需要一个@MapperScan注解就可以来进行操作,所以更加好奇的是@MapperScan底层是怎么来做到的。
下面先来研究一下@MapperScan:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
...
}
@Import注解导入了一个MapperScannerRegistrar。而在Spring启动的时候,会在org.Springframework.context.support.AbstractApplicationContext#refresh方法中会来执行@Import导入的类,看下如何来解析@Import注解的。直接来到org.Springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass会来解析@Import注解导入进来的类,那么下面就需要来看一下MapperScannerRegistrar做了什么事情。
1.1、导入MapperScannerRegistrar类
下面来看一下MapperScannerRegistrar的结构体系
1.1.2、执行ImportBeanDefinitionRegistrar接口中的registerBeanDefinitions方法
MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,而实现了接口中的方法,那么在注册BeanDefinition之前,就会来执行ImportBeanDefinitionRegistrar接口中的方法
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
那么只需要看一下MapperScannerRegistrar中的registerBeanDefinitions方法是如何来进行实现的:
1.2、MapperScannerConfigurer
而在上面的代码中,主要是注册了一个MapperScannerConfigurer类型的BeanDefinition。
那么来看一下这个BeanDefinition有什么特点:
MapperScannerRegistrar实现了BeanDefinitionRegistryPostProcessor接口。而这个接口中又会在ConfigurationClassPostProcessor之后发挥作用,而MapperScannerRegistrar对应的BeanDefinition是在此之前来进行解析的,所以将会在下面来解析MapperScannerRegistrar对应的BeanDefinition
那么就会来执行MapperScannerConfigurer中的postProcessBeanDefinitionRegistry方法
1.2、ClassPathMapperScanner
那么看一下Spring-Mybatis中自定义整合的ClassPathMapperScanner中的注册过滤器和扫描逻辑:
将扫描出来的类利用包含过滤器添加到当前逻辑中。
那么来看一下扫描逻辑:
然后可以看到只要接口的类
1.2.1、对筛选出来的BeanDefinition进行处理
然后对扫描出来的接口来进行筛选判断:
org.mybatis.Spring.mapper.ClassPathMapperScanner#processBeanDefinitions
1、首先获取得到接口的全限定类型。如:com.guang.dao.UserDao;
2、将当前的BeanClass设置成MapperFactoryBean,而这个类是FactoryBean类型的。在创建对象的时候,说明是要用getObject方法来创建对象的;
3、设置MapperFactoryBean构造函数中的值。在MapperFactoryBean构造函数中是Class类型,而这里设置的是String,在创建的时候Spring回来进行转换;
4、将MapperFactoryBean的注入模型设置为By-Type。也就是说,MapperFactoryBean中的setXxx中的属性会从容器中来进行查找;
那么看一下MapperFactoryBean的构造方法:
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
这里会将mapperInterface在进行赋值的时候将java.lang.String转换成Class类型,表示对应的接口。
第二个:
说明了在MapperFactoryBean中的setXxxx方法对应的xxx属性,Spring会自动来进行赋值。而MapperFactoryBean继承了SqlSessionDaoSupport,在SqlSessionDaoSupport类中的set方法中存在setSqlSessionFactory方法和setSqlSessionTemplate方法,所以容器中如果存在着对应类型的对象的时候,那么Spring到时候来进行赋值的时候将会从容器中来进行获取得到对应的bean。
所以:SqlSessionTemplate我们可以自己配置,但是SqlSessionFactory就得由我们自己来进行配置了。
1.3、SqlSessionFactoryBean类结构体系
SqlSessionFactoryBean类就是来帮我们创建SqlSessionFactory,看下SqlSessionFactory的结构如下所示:
SqlSessionFactoryBean也是一个FactoryBean类型的,产生的对象的类型是:SqlSessionFactory。
看一下对应的getObjectType方法:
public Class<? extends SqlSessionFactory> getObjectType() {
return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
}
看一下对应的getObject方法:
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
// 调用对应的buildSqlSessionFactory方法产生一下sqlSessionFactory
this.sqlSessionFactory = buildSqlSessionFactory();
}
而根据buildSqlSessionFactory方法可以知道,我们可以通过对SqlSessionFactoryBean对象的属性来进行设置,从而实现对Configuration对象属性的设置。
其中有一行代码值得注意下:
targetConfiguration.setEnvironment(new Environment(this.environment,this.transactionFactory == null ?
new SpringManagedTransactionFactory() : this.transactionFactory,
this.dataSource));
对于Environment对象来说,看看构造函数:
public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {
if (id == null) {
throw new IllegalArgumentException("Parameter 'id' must not be null");
}
if (transactionFactory == null) {
throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null");
}
this.id = id;
if (dataSource == null) {
throw new IllegalArgumentException("Parameter 'dataSource' must not be null");
}
this.transactionFactory = transactionFactory;
this.dataSource = dataSource;
}
需要的第二个参数是TransactionFactory(事务工厂),Spring-Mybatis整合包中,利用SpringManagedTransactionFactory类实现TransactionFactory,重写其中的方法,让当前事务交给Spring来进行处理。
那么后续Mybatis如果存在事务操作的时候,将会由Spring中的事务来进行处理。
1.3.1、SqlSessionFactoryBean使用
这里需要注意的是,下面的使用方式是通过配置类的方式来进行注入的。
因为@MapperScan注解导入的MapperScannerRegistrar的作用就是来注册一个MapperScannerConfigurer类型的BeanDefinition。
所以在下面可以直接利用@Bean注入一个对应类型的BeanDefinition,然后给BeanDefinition来设置一些属性而已。
其中在MapperScannerConfigurer实现InitializingBean接口中的afterPropertiesSet方法中判断了扫描包不能够为空,也就是说要求必须设置basePackage属性
第一种使用方式
第二种使用方式
结合SpringBoot项目使用
@ConfigurationProperties(
prefix = "mybatis"
)
public class MybatisProperties {
public static final String MYBATIS_PREFIX = "mybatis";
private String typeAliasesPackage;
private String[] mapperLocations;
private Integer batchExecuteSize = 1000;
public MybatisProperties() {
}
public String getTypeAliasesPackage() {
return this.typeAliasesPackage;
}
public void setTypeAliasesPackage(String typeAliasesPackage) {
this.typeAliasesPackage = typeAliasesPackage;
}
public String[] getMapperLocations() {
return this.mapperLocations;
}
public void setMapperLocations(String[] mapperLocations) {
this.mapperLocations = mapperLocations;
}
public Integer getBatchExecuteSize() {
return this.batchExecuteSize;
}
public void setBatchExecuteSize(Integer batchExecuteSize) {
this.batchExecuteSize = batchExecuteSize;
}
}
@Configuration
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceConfig.class})
@MapperScan({"com.guang.common.dao"})
public class MybatisConfig {
private static final String CONFIG_LOCATION = "classpath:mybatis/mybatis-config.xml";
private static final String COMMON_MAPPER_LOCATIONS = "classpath:com/guang/common/dao/mapper/*Mapper.xml";
private final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
public MybatisConfig() {
}
@Bean(
name = {"sqlSessionFactory"}
)
@ConditionalOnClass({DataSource.class})
@ConditionalOnBean({DataSource.class})
public SqlSessionFactory sqlSessionFactory(MybatisProperties mybatisProperties, DataSource dataSource, ObjectProvider<Interceptor> interceptorObjectProvider) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setConfigLocation(this.getConfigLocation());
sqlSessionFactoryBean.setMapperLocations(this.getMapperLocations(mybatisProperties));
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setTypeAliasesPackage(mybatisProperties.getTypeAliasesPackage());
List<Interceptor> interceptorList = (List)interceptorObjectProvider.stream().collect(Collectors.toList());
sqlSessionFactoryBean.setPlugins((Interceptor[])interceptorList.toArray(new Interceptor[0]));
return sqlSessionFactoryBean.getObject();
}
@Primary
@Bean(
name = {"simpleSqlSessionTemplate"}
)
@ConditionalOnClass({SqlSessionFactory.class})
@ConditionalOnBean({SqlSessionFactory.class})
public SqlSessionTemplate simpleSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory, ExecutorType.SIMPLE);
}
@Bean(
name = {"batchSqlSessionTemplate"}
)
@ConditionalOnClass({SqlSessionFactory.class})
@ConditionalOnBean({SqlSessionFactory.class})
public SqlSessionTemplate batchSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory, ExecutorType.BATCH);
}
@Bean(
name = {"mapperBatchExecutor"}
)
@ConditionalOnClass({SqlSessionTemplate.class})
@ConditionalOnBean(
value = {SqlSessionTemplate.class},
name = {"batchSqlSessionTemplate"}
)
public MapperBatchExecutor mapperBatchExecutor(@Qualifier("batchSqlSessionTemplate") SqlSessionTemplate batchSqlSessionTemplate, MybatisProperties mybatisProperties) {
return new MapperBatchExecutor(batchSqlSessionTemplate, mybatisProperties.getBatchExecuteSize());
}
@Bean(
name = {"pagingInterceptor"}
)
public PagingInterceptor pagingInterceptor() {
return new PagingInterceptor();
}
private Resource getConfigLocation() {
return this.resourceResolver.getResource("classpath:mybatis/mybatis-config.xml");
}
private Resource[] getMapperLocations(MybatisProperties mybatisProperties) throws IOException {
Resource[] mapperLocations = this.resourceResolver.getResources("classpath:com/guang/common/dao/mapper/*Mapper.xml");
String[] var3 = mybatisProperties.getMapperLocations();
int var4 = var3.length;
for(int var5 = 0; var5 < var4; ++var5) {
String mapperLocation = var3[var5];
Resource[] addedMapperLocations = this.resourceResolver.getResources(mapperLocation);
Resource[] originMapperLocations = mapperLocations;
mapperLocations = new Resource[mapperLocations.length + addedMapperLocations.length];
System.arraycopy(originMapperLocations, 0, mapperLocations, 0, originMapperLocations.length);
System.arraycopy(addedMapperLocations, 0, mapperLocations, originMapperLocations.length, addedMapperLocations.length);
}
return mapperLocations;
}
}
1.4、SqlSessionTemplate
首先看一下体系结构图
实现了SqlSession接口,那么就有了SqlSession的功能。
说明,我们可以使用SqlsesseionTemplate对象来代替SqlSession对象来进行使用。
1.4.1、构造方法
看一下对应的构造方法,只有一个构造方法。也就是说,如果想要来设置SqlSessionTemplate成为bean,那么当前容器中必须要有一个SqlSessionFactory类型的Bean。
注意这里的参数信息:
- 1、SqlSessionFactory是创建SqlSessionTemplate对象时候传入进来的,然后赋值给当前SqlSessionTemplate类中的属性sqlSessionFactory;
- 2、ExecutorType执行器类型是从当前SqlSessionFactory获取得到的,然后赋值给当前SqlSessionTemplate类中的属性ExecutorType;
- 3、sqlSessionProxy是sqlSession的代理对象,是利用JDK动态代理产生的对象;
1.4.2、增删改查方法
随便看一下都是类似如下所示,使用sqlSessionProxy来进行执行的
public <T> T selectOne(String statement, Object parameter) {
return this.sqlSessionProxy.selectOne(statement, parameter);
}
那么执行方法的时候,肯定会指定到InvocationHandler中的invoke方法中来。
那么来看一下SqlSessionInterceptor中的invoke方法。
1.4.3、SqlSessionInterceptor
从上面来看,可以知道是一个InvocationHandler,那么直接来看一下对应的invoke方法实现:
Spring整合Mybatis后为什么一级缓存失效
Spring-Mybatis提供的用来整合Spring和Mybatis的,所以又利用到了Spring中的事务中的内容,那么重点来研究一下这里的代码:
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
首先要获取得到对应的SqlSession,SqlSession是mybatis中的门面。获取得到了SqlSession之后,才能够来执行对应的方法。
那么看一下如何获取得到SqlSession的。
首先看看是如何放入到当前线程上下文的。
这里有几个需要注意的点:
- 1、mybatis中的数据源和@Transactional注解或者是TransactionTemplate使用的要是同一个数据源;
- 2、一定要在开启事务的情况,获取的才是同一个SqlSession;没有开启事务,每次获取得到的SqlSession都是新创建的;
然后看下执行阶段,如下所示:
@Transactional
public void insert(){
userMapper.insert(new User());
orderMapper.insert(new Order());
}
看一下对应的执行
首先执行的时候判断
public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
notNull(session, NO_SQL_SESSION_SPECIFIED);
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
// 判断是否是同一个SqlSession
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
return (holder != null) && (holder.getSqlSession() == session);
}
如果是同一个SqlSession,那么就先不提交!如果不是同一个SqlSession,那么一个方法执行完成之后,就执行对应的SqlSession.commit()方法,各自占用一个数据库连接。最终如果正常的执行的话,始终占用的是同一个数据库连接,在同一个数据库连接中来执行对应的方法。
Mybatis中的一级缓存是基于SqlSession来实现的,所以在执行同一个sql时,如果使用的是同一个SqlSession对象,那么就能利用到一级缓存,提高sql的执行效率。 但是在Spring整合Mybatis后,如果没有执行某个方法时,该方法上没有加@Transactional注解,也就是没有开启Spring事务,那么后面在执行具体sql时,没执行一个sql时都会新生成一个SqlSession对象来执行该sql,这就是我们说的一级缓存失效(也就是没有使用同一个SqlSession对象),而如果开启了Spring事务,那么该Spring事务中的多个sql,在执行时会使用同一个SqlSession对象,从而一级缓存生效,具体的底层执行流程在上图。
个人理解:实际上Spring整合Mybatis后一级缓存失效并不是问题,是正常的实现,因为,一个方法如果没有开启Spring事务,那么在执行sql时候,那就是每个sql单独一个事务来执行,也就是单独一个SqlSession对象来执行该sql,如果开启了Spring事务,那就是多个sql属于同一个事务,那自然就应该用一个SqlSession来执行这多个sql。所以,在没有开启Spring事务的时候,SqlSession的一级缓存并不是失效了,而是存在的生命周期太短了(执行完一个sql后就被销毁了,下一个sql执行时又是一个新的SqlSession了)。
sqlsession的提交和回滚在哪里
对于SqlSession来说,最终的commit和rollback方法,都会调用到connection.commit()和connection.rollback()方法上去。
而在Spring接管了事务之后,在Spring控制的事务中,也会在方法提交或者回滚之后释放数据库连接。
流程
Spring整合Mybatis之后SQL执行流程: Spring整合Mybatis之后SQL执行流程 | ProcessOn免费在线作图,在线流程图,在线思维导图 |
三、总结
由很多框架都需要和Spring进行整合,而整合的核心思想就是把其他框架所产生的对象放到Spring容器中,让其成为Bean。
比如Mybatis,Mybatis框架可以单独使用,而单独使用Mybatis框架就需要用到Mybatis所提供的一些类构造出对应的对象,然后使用该对象,就能使用到Mybatis框架给我们提供的功能,和Mybatis整合Spring就是为了将这些对象放入Spring容器中成为Bean,只要成为了Bean,在我们的Spring项目中就能很方便的使用这些对象了,也就能很方便的使用Mybatis框架所提供的功能了。
Mybatis-Spring 新版本底层源码执行流程
1、通过@MapperScan导入了MapperScannerRegistrar类;
2、MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法;
3、在registerBeanDefinitions方法中定义了一个ClassPathMapperScanner对象,用来扫描mapper,设置ClassPathMapperScanner对象可以扫描到接口,因为在Spring中是不会扫描接口的
4、同时因为ClassPathMapperScanner中重写了isCandidateComponent方法,导致isCandidateComponent只会认为接口是备选者Component
通过利用Spring的扫描后,会把接口扫描出来并且得到对应的BeanDefinition
5、接下来把扫描得到的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType
6、扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个FactoryBean
7、在MapperFactoryBean中的getObject方法中,调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为Spring容器中的Bean
8、sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生
9、MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean和SqlSessionTemplate类型的bean。(注意要注册两个的话,记得要在SIMPLESQLSESSIONTEMPLATE上加上@Primary注解)
10、如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性
11、而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就产生一个Mapper接口代理对象
到时候,当执行该代理对象的某个方法时,就会进入到Mybatis框架的底层执行流程,详细的请看下图
Spring整合Mybatis之后SQL执行流程: Spring整合Mybatis之后SQL执行流程 | ProcessOn免费在线作图,在线流程图,在线思维导图 |
有点遗漏的步骤,我在这里补充一下:
带来的好处是,可以不使用@MapperScan注解,而可以直接定义一个Bean,比如:
@Bean
public static MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.luban");
return mapperScannerConfigurer;
}
Spring整合Mybatis原理的更多相关文章
- 深入源码理解Spring整合MyBatis原理
写在前面 聊一聊MyBatis的核心概念.Spring相关的核心内容,主要结合源码理解Spring是如何整合MyBatis的.(结合右侧目录了解吧) MyBatis相关核心概念粗略回顾 SqlSess ...
- Spring整合Mybatis原理简单分析
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" ...
- 【springboot spring mybatis】看我怎么将springboot与spring整合mybatis与druid数据源
目录 概述 1.mybatis 2.druid 壹:spring整合 2.jdbc.properties 3.mybatis-config.xml 二:java代码 1.mapper 2.servic ...
- spring基础:什么是框架,框架优势,spring优势,耦合内聚,什么是Ioc,IOC配置,set注入,第三方资源配置,综合案例spring整合mybatis实现
知识点梳理 课堂讲义 1)Spring简介 1.1)什么是框架 源自于建筑学,隶属土木工程,后发展到软件工程领域 软件工程中框架的特点: 经过验证 具有一定功能 半成品 1.2)框架的优势 提高开发效 ...
- Spring学习总结(六)——Spring整合MyBatis完整示例
为了梳理前面学习的内容<Spring整合MyBatis(Maven+MySQL)一>与<Spring整合MyBatis(Maven+MySQL)二>,做一个完整的示例完成一个简 ...
- Spring学习总结(五)——Spring整合MyBatis(Maven+MySQL)二
接着上一篇博客<Spring整合MyBatis(Maven+MySQL)一>继续. Spring的开放性和扩张性在J2EE应用领域得到了充分的证明,与其他优秀框架无缝的集成是Spring最 ...
- 分析下为什么spring 整合mybatis后为啥用不上session缓存
因为一直用spring整合了mybatis,所以很少用到mybatis的session缓存. 习惯是本地缓存自己用map写或者引入第三方的本地缓存框架ehcache,Guava 所以提出来纠结下 实验 ...
- 2017年2月16日 分析下为什么spring 整合mybatis后为啥用不上session缓存
因为一直用spring整合了mybatis,所以很少用到mybatis的session缓存. 习惯是本地缓存自己用map写或者引入第三方的本地缓存框架ehcache,Guava 所以提出来纠结下 实验 ...
- spring整合mybatis错误:class path resource [config/spring/springmvc.xml] cannot be opened because it does not exist
spring 整合Mybatis 运行环境:jdk1.7.0_17+tomcat 7 + spring:3.2.0 +mybatis:3.2.7+ eclipse 错误:class path reso ...
- spring 整合Mybatis 《报错集合,总结更新》
错误:java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldExcepti ...
随机推荐
- USB转TTL串口 (CH340 G)
为什么USB要转TTL串口[1]? 单片机串口基本采用TTL电平. 家用电脑很少有串口,但是有USB接口 USB的电平与TTL电平不兼容. 所以需要将USB电平转化为TTL电平. USB是什么? 接口 ...
- Python函数用法和底层分析
目录 Python函数用法和底层分析 函数的基本概念 Python 函数的分类 核心要点 形参和实参 文档字符串(函数的注释) 返回值 函数也是对象,内存底层分析 变量的作用域(全局变量和局部变量) ...
- CH579(Cortex-M0)网络IAP升级介绍及问题解答--(持续更新) 网络升级
CH579网络IAP升级的源码可以从码云(Gitee)上git clone下来. 码云地址:https://gitee.com/maji19971221/wch-ch57x 以下是一个朋友问的几个常见 ...
- [常用工具] live555的搭建
live555是一个为流媒体提供解决方案的跨平台的C++开源项目,它实现了对标准流媒体传输协议如RTP/RTCP.RTSP.SIP等的支持.使用live555可以播放rtsp流.本文主要是在linux ...
- Spark详解(03) - Spark3.0.0运行环境安装
Spark详解(03) - Spark3.0.0运行环境安装 Spark运行模式 Spark常见部署模式: Local模式:在本地部署单个Spark服务 所谓的Local模式,就是不需要其他任何节点资 ...
- AcWing第85场周赛
这场周赛是手速局hh 死或生 某国正在以投票的方式决定 2 名死刑犯(编号 1∼2)的生死. 共有 n 组人员(编号 1∼n)参与投票,每组 10 人. 每组成员只参与一名死刑犯的投票,其中第 i 组 ...
- VMware Workstation Pro 16安装CentOS7超详细图文步骤
安装的需要 VMware官方网站安装VMware Workstation Pro 16: https://download3.vmware.com/software/wkst/file/VMware- ...
- (8)go-micro微服务Mysql配置
目录 一 gorm介绍 二 gorm安装 1.1 下载依赖 1.2 使用MySQL驱动 三 CURD操作 1. 查询 1.1 单行查询 1.2 多行查询 2. 插入数据 3. 更新数据 4. 删除数据 ...
- Java学习笔记:2022年1月8日
Java学习笔记:2022年1月8日 摘要:这天主要学习了HTML超文本标记语言以及CSS层叠样式表的基本知识,主要就是通过这两种技术进行基本的网页渲染. 目录 Java学习笔记:2022年1月8日 ...
- MQ系列11:如何保证消息可靠性传输(除夕奉上)
MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 MQ系 ...