Mybatis學習筆記(七)

Spring Boot集成

簡要描述:MyBatis-Plus與Spring Boot的深度集成,提供了自動配置、啟動器等特性,大大簡化了配置和使用。

核心概念

  • 自動配置:基于條件的自動配置機制
  • 啟動器:簡化依賴管理的starter
  • 配置屬性:通過application.yml進行配置
  • 條件化配置:根據環境動態調整配置

SpringBoot自動配置

簡要描述:Spring Boot通過自動配置機制,自動裝配MyBatis-Plus相關的Bean,無需手動配置大量的XML或Java配置。

核心概念

  • @EnableAutoConfiguration:啟用自動配置
  • @ConditionalOn*:條件化配置注解
  • AutoConfiguration類:自動配置類
  • spring.factories:自動配置發現機制

自動配置原理

// MyBatis-Plus自動配置類
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class, MybatisPlusAutoConfiguration.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisPlusProperties.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})
public class MybatisPlusAutoConfiguration implements InitializingBean {private static final Logger logger = LoggerFactory.getLogger(MybatisPlusAutoConfiguration.class);private final MybatisPlusProperties properties;private final Interceptor[] interceptors;private final TypeHandler[] typeHandlers;private final LanguageDriver[] languageDrivers;private final ResourceLoader resourceLoader;private final DatabaseIdProvider databaseIdProvider;private final List<ConfigurationCustomizer> configurationCustomizers;private final List<MybatisPlusPropertiesCustomizer> mybatisPlusPropertiesCustomizers;private final ApplicationContext applicationContext;public MybatisPlusAutoConfiguration(MybatisPlusProperties properties,ObjectProvider<Interceptor[]> interceptorsProvider,ObjectProvider<TypeHandler[]> typeHandlersProvider,ObjectProvider<LanguageDriver[]> languageDriversProvider,ResourceLoader resourceLoader,ObjectProvider<DatabaseIdProvider> databaseIdProvider,ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,ObjectProvider<List<MybatisPlusPropertiesCustomizer>> mybatisPlusPropertiesCustomizerProvider,ApplicationContext applicationContext) {this.properties = properties;this.interceptors = interceptorsProvider.getIfAvailable();this.typeHandlers = typeHandlersProvider.getIfAvailable();this.languageDrivers = languageDriversProvider.getIfAvailable();this.resourceLoader = resourceLoader;this.databaseIdProvider = databaseIdProvider.getIfAvailable();this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();this.mybatisPlusPropertiesCustomizers = mybatisPlusPropertiesCustomizerProvider.getIfAvailable();this.applicationContext = applicationContext;}@Bean@ConditionalOnMissingBeanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {// 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBeanMybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();factory.setDataSource(dataSource);factory.setVfs(SpringBootVFS.class);if (StringUtils.hasText(this.properties.getConfigLocation())) {factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));}applyConfiguration(factory);if (this.properties.getConfigurationProperties() != null) {factory.setConfigurationProperties(this.properties.getConfigurationProperties());}if (!ObjectUtils.isEmpty(this.interceptors)) {factory.setPlugins(this.interceptors);}if (this.databaseIdProvider != null) {factory.setDatabaseIdProvider(this.databaseIdProvider);}if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());}if (this.properties.getTypeAliasesSuperType() != null) {factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());}if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());}if (!ObjectUtils.isEmpty(this.typeHandlers)) {factory.setTypeHandlers(this.typeHandlers);}Resource[] mapperLocations = this.properties.resolveMapperLocations();if (!ObjectUtils.isEmpty(mapperLocations)) {factory.setMapperLocations(mapperLocations);}// 修改源碼支持定制化 GlobalConfigGlobalConfig globalConfig = this.properties.getGlobalConfig();//注入填充器this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);//注入主鍵生成器this.getBeanThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerator(i));//注入sql注入器this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);//注入ID生成器this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);//設置 GlobalConfig 到 MybatisSqlSessionFactoryBeanfactory.setGlobalConfig(globalConfig);return factory.getObject();}private <T> void getBeanThen(Class<T> clazz, Consumer<T> consumer) {if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) {consumer.accept(this.applicationContext.getBean(clazz));}}private void applyConfiguration(MybatisSqlSessionFactoryBean factory) {MybatisPlusConfiguration configuration = this.properties.getConfiguration();if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {configuration = new MybatisPlusConfiguration();}if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {for (ConfigurationCustomizer customizer : this.configurationCustomizers) {customizer.customize(configuration);}}factory.setConfiguration(configuration);}@Bean@ConditionalOnMissingBeanpublic SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {ExecutorType executorType = this.properties.getExecutorType();if (executorType != null) {return new SqlSessionTemplate(sqlSessionFactory, executorType);} else {return new SqlSessionTemplate(sqlSessionFactory);}}@Overridepublic void afterPropertiesSet() {if (!CollectionUtils.isEmpty(this.mybatisPlusPropertiesCustomizers)) {this.mybatisPlusPropertiesCustomizers.forEach(i -> i.customize(this.properties));}checkConfigFileExists();}private void checkConfigFileExists() {if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());Assert.state(resource.exists(),"Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");}}
}

自定義自動配置

// 自定義MyBatis-Plus配置
@Configuration
@AutoConfigureAfter(MybatisPlusAutoConfiguration.class)
public class CustomMybatisPlusAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 分頁插件PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);paginationInnerInterceptor.setMaxLimit(500L);paginationInnerInterceptor.setOverflow(false);interceptor.addInnerInterceptor(paginationInnerInterceptor);// 樂觀鎖插件interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());// 防全表更新與刪除插件interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());return interceptor;}@Bean@ConditionalOnMissingBeanpublic MetaObjectHandler metaObjectHandler() {return new CustomMetaObjectHandler();}@Bean@ConditionalOnMissingBeanpublic ISqlInjector sqlInjector() {return new CustomSqlInjector();}@Bean@ConditionalOnMissingBeanpublic IdentifierGenerator identifierGenerator() {return new CustomIdentifierGenerator();}
}// 自定義填充器
public class CustomMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());this.strictInsertFill(metaObject, "createBy", String.class, getCurrentUser());this.strictInsertFill(metaObject, "updateBy", String.class, getCurrentUser());this.strictInsertFill(metaObject, "deleted", Integer.class, 0);}@Overridepublic void updateFill(MetaObject metaObject) {this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());this.strictUpdateFill(metaObject, "updateBy", String.class, getCurrentUser());}private String getCurrentUser() {// 從SecurityContext或其他地方獲取當前用戶return "system";}
}

配置文件詳解

簡要描述:Spring Boot通過application.yml或application.properties文件提供了豐富的配置選項,可以靈活配置MyBatis-Plus的各種特性。

核心配置項

  • 數據源配置:數據庫連接相關配置
  • MyBatis配置:MyBatis核心配置
  • MyBatis-Plus配置:MyBatis-Plus特有配置
  • 日志配置:SQL日志輸出配置

完整配置示例

# application.yml
spring:# 數據源配置datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=falseusername: rootpassword: password# 連接池配置(使用Druid)druid:initial-size: 5min-idle: 5max-active: 20max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000validation-query: SELECT 1test-while-idle: truetest-on-borrow: falsetest-on-return: falsepool-prepared-statements: truemax-pool-prepared-statement-per-connection-size: 20# 配置監控統計攔截的filtersfilters: stat,wall,slf4j# 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000# 配置web監控web-stat-filter:enabled: trueurl-pattern: /*exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"stat-view-servlet:enabled: trueurl-pattern: /druid/*reset-enable: falselogin-username: adminlogin-password: admin# MyBatis-Plus配置
mybatis-plus:# 配置文件位置config-location: classpath:mybatis-config.xml# Mapper XML文件位置mapper-locations: classpath*:mapper/**/*Mapper.xml# 實體類包路徑type-aliases-package: com.example.entity# 類型處理器包路徑type-handlers-package: com.example.typehandler# 執行器類型executor-type: simple# 配置屬性configuration-properties:key1: value1key2: value2# MyBatis原生配置configuration:# 開啟駝峰命名轉換map-underscore-to-camel-case: true# 開啟緩存cache-enabled: true# 設置懶加載lazy-loading-enabled: true# 設置積極懶加載aggressive-lazy-loading: false# 允許多結果集multiple-result-sets-enabled: true# 允許使用列標簽use-column-label: true# 允許使用自定義緩存use-generated-keys: false# 給予被嵌套的resultMap以字段-屬性的映射支持auto-mapping-behavior: partial# 對于未知的SQL查詢,允許返回不同的結果集以達到通用的效果auto-mapping-unknown-column-behavior: warning# 配置默認的執行器default-executor-type: simple# 對于批量更新操作緩存SQL以提高性能default-statement-timeout: 25# 設置超時時間default-fetch-size: 100# 允許在嵌套語句中使用分頁safe-row-bounds-enabled: false# 允許在嵌套語句中使用分頁safe-result-handler-enabled: true# 是否開啟自動駝峰命名規則映射map-underscore-to-camel-case: true# 本地緩存機制local-cache-scope: session# 數據庫超廠商標識jdbc-type-for-null: other# 指定當結果集中值為null的時候如何處理call-setters-on-nulls: false# 指定MyBatis增加到日志名稱的前綴log-prefix: mybatis-plus# 指定MyBatis所用日志的具體實現log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl# 使用實際參數名稱use-actual-param-name: true# 返回map時true:當查詢返回的所有列都為空時,MyBatis返回null false:當查詢返回的所有列都為空時,MyBatis返回一個空的Mapreturn-instance-for-empty-row: false# 指定VFS的實現vfs-impl: org.mybatis.spring.boot.autoconfigure.SpringBootVFS# 指定默認的類型別名超類default-scripting-language-driver: org.apache.ibatis.scripting.xmltags.XMLLanguageDriver# 全局配置global-config:# 是否控制臺 print mybatis-plus 的 LOGObanner: true# 是否初始化 SqlRunnerenable-sql-runner: false# 數據庫配置db-config:# 主鍵類型(AUTO:數據庫自增 NONE:無狀態 INPUT:自行輸入 ASSIGN_ID:分配ID ASSIGN_UUID:分配UUID)id-type: ASSIGN_ID# 表名前綴table-prefix: t_# 字段名前綴column-prefix: # 表名是否使用駝峰轉下劃線命名table-underline: true# 字段是否使用駝峰轉下劃線命名column-underline: true# 大寫命名capital-mode: false# 表關鍵詞 keywordkey-generator: com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator# 邏輯刪除全局值(默認 1、表示已刪除)logic-delete-value: 1# 邏輯未刪除全局值(默認 0、表示未刪除)logic-not-delete-value: 0# 字段驗證策略insert-strategy: not_nullupdate-strategy: not_nullwhere-strategy: not_null# 日志配置
logging:level:# MyBatis日志com.example.mapper: debug# 根日志級別root: info# SQL日志org.springframework.jdbc: debugpattern:console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"file:name: logs/application.logmax-size: 10MBmax-history: 30

事務管理集成

簡要描述:Spring Boot與MyBatis-Plus的事務管理集成,支持聲明式事務、編程式事務等多種事務管理方式。

核心概念

  • 聲明式事務:通過@Transactional注解管理事務
  • 編程式事務:通過TransactionTemplate管理事務
  • 事務傳播:事務的傳播行為
  • 事務隔離:事務的隔離級別

事務配置

@Configuration
@EnableTransactionManagement
public class TransactionConfig {@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}@Beanpublic TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {return new TransactionTemplate(transactionManager);}
}

聲明式事務使用

@Service
@Transactional(rollbackFor = Exception.class)
public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate UserRoleMapper userRoleMapper;// 默認事務配置public void saveUser(User user) {userMapper.insert(user);}// 只讀事務@Transactional(readOnly = true)public User findById(Long id) {return userMapper.selectById(id);}// 指定傳播行為@Transactional(propagation = Propagation.REQUIRES_NEW)public void saveUserWithNewTransaction(User user) {userMapper.insert(user);}// 指定隔離級別@Transactional(isolation = Isolation.READ_COMMITTED)public void updateUser(User user) {userMapper.updateById(user);}// 復雜事務場景@Transactional(rollbackFor = Exception.class)public void saveUserWithRoles(User user, List<Long> roleIds) {// 保存用戶userMapper.insert(user);// 保存用戶角色關系for (Long roleId : roleIds) {UserRole userRole = new UserRole();userRole.setUserId(user.getId());userRole.setRoleId(roleId);userRoleMapper.insert(userRole);}// 模擬異常,測試事務回滾if (user.getUsername().equals("error")) {throw new RuntimeException("模擬異常");}}
}

編程式事務使用

@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate TransactionTemplate transactionTemplate;public void createOrder(Order order) {transactionTemplate.execute(status -> {try {// 業務邏輯orderMapper.insert(order);// 其他操作processOrderItems(order);return null;} catch (Exception e) {// 手動回滾status.setRollbackOnly();throw new RuntimeException("訂單創建失敗", e);}});}private void processOrderItems(Order order) {// 處理訂單項}
}

測試環境配置

簡要描述:為MyBatis-Plus配置測試環境,包括單元測試、集成測試等不同層次的測試配置。

核心概念

  • @SpringBootTest:Spring Boot測試注解
  • @MybatisTest:MyBatis專用測試注解
  • TestContainers:容器化測試
  • H2數據庫:內存數據庫測試

測試依賴配置

<dependencies><!-- Spring Boot Test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- MyBatis-Plus Test --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter-test</artifactId><version>3.5.3.1</version><scope>test</scope></dependency><!-- H2數據庫 --><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>test</scope></dependency><!-- TestContainers --><dependency><groupId>org.testcontainers</groupId><artifactId>junit-jupiter</artifactId><scope>test</scope></dependency><dependency><groupId>org.testcontainers</groupId><artifactId>mysql</artifactId><scope>test</scope></dependency>
</dependencies>

測試配置文件

# application-test.yml
spring:datasource:driver-class-name: org.h2.Driverurl: jdbc:h2:mem:testdb;MODE=MySQL;DATABASE_TO_LOWER=TRUEusername: sapassword: h2:console:enabled: truepath: /h2-consolesql:init:schema-locations: classpath:schema.sqldata-locations: classpath:data.sqlmode: alwaysmybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:db-config:id-type: autologic-delete-value: 1logic-not-delete-value: 0logging:level:com.example.mapper: debug

單元測試示例

// Mapper層測試
@MybatisTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserMapperTest {@Autowiredprivate TestEntityManager entityManager;@Autowiredprivate UserMapper userMapper;@Testvoid testInsert() {// 準備測試數據User user = new User();user.setUsername("test");user.setEmail("test@example.com");// 執行測試int result = userMapper.insert(user);// 驗證結果assertThat(result).isEqualTo(1);assertThat(user.getId()).isNotNull();}@Testvoid testSelectById() {// 準備測試數據User user = new User();user.setUsername("test");user.setEmail("test@example.com");entityManager.persistAndFlush(user);// 執行測試User found = userMapper.selectById(user.getId());// 驗證結果assertThat(found).isNotNull();assertThat(found.getUsername()).isEqualTo("test");}@Testvoid testSelectByCondition() {// 準備測試數據User user1 = new User();user1.setUsername("test1");user1.setEmail("test1@example.com");entityManager.persistAndFlush(user1);User user2 = new User();user2.setUsername("test2");user2.setEmail("test2@example.com");entityManager.persistAndFlush(user2);// 執行測試QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.like("username", "test");List<User> users = userMapper.selectList(wrapper);// 驗證結果assertThat(users).hasSize(2);}
}

Service層測試

@SpringBootTest
@ActiveProfiles("test")
@Transactional
@Rollback
class UserServiceTest {@Autowiredprivate UserService userService;@Testvoid testSaveUser() {// 準備測試數據User user = new User();user.setUsername("test");user.setEmail("test@example.com");// 執行測試userService.saveUser(user);// 驗證結果assertThat(user.getId()).isNotNull();assertThat(user.getCreateTime()).isNotNull();}@Testvoid testSaveUserWithRoles() {// 準備測試數據User user = new User();user.setUsername("test");user.setEmail("test@example.com");List<Long> roleIds = Arrays.asList(1L, 2L);// 執行測試userService.saveUserWithRoles(user, roleIds);// 驗證結果assertThat(user.getId()).isNotNull();// 驗證角色關系QueryWrapper<UserRole> wrapper = new QueryWrapper<>();wrapper.eq("user_id", user.getId());List<UserRole> userRoles = userRoleMapper.selectList(wrapper);assertThat(userRoles).hasSize(2);}@Testvoid testTransactionRollback() {// 準備測試數據User user = new User();user.setUsername("error"); // 觸發異常user.setEmail("error@example.com");List<Long> roleIds = Arrays.asList(1L, 2L);// 執行測試并驗證異常assertThrows(RuntimeException.class, () -> {userService.saveUserWithRoles(user, roleIds);});// 驗證事務回滾QueryWrapper<User> userWrapper = new QueryWrapper<>();userWrapper.eq("username", "error");List<User> users = userMapper.selectList(userWrapper);assertThat(users).isEmpty();}
}

TestContainers集成測試

@SpringBootTest
@Testcontainers
class UserServiceIntegrationTest {@Containerstatic MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0").withDatabaseName("testdb").withUsername("test").withPassword("test");@DynamicPropertySourcestatic void configureProperties(DynamicPropertyRegistry registry) {registry.add("spring.datasource.url", mysql::getJdbcUrl);registry.add("spring.datasource.username", mysql::getUsername);registry.add("spring.datasource.password", mysql::getPassword);}@Autowiredprivate UserService userService;@Testvoid testUserOperations() {// 測試用戶操作User user = new User();user.setUsername("integration-test");user.setEmail("integration@example.com");userService.saveUser(user);User found = userService.findById(user.getId());assertThat(found).isNotNull();assertThat(found.getUsername()).isEqualTo("integration-test");}
}

性能優化與監控

簡要描述:MyBatis-Plus的性能優化涉及SQL優化、緩存策略、連接池配置、監控診斷等多個方面,通過合理的配置和使用可以顯著提升應用性能。

核心概念

  • SQL性能分析:分析SQL執行效率
  • 慢查詢優化:識別和優化慢查詢
  • 緩存策略:合理使用緩存提升性能
  • 連接池優化:優化數據庫連接池配置
  • 監控診斷:實時監控和問題診斷

SQL性能分析

簡要描述:通過各種工具和插件分析SQL執行性能,識別性能瓶頸。

核心概念

  • 執行計劃分析:分析SQL執行計劃
  • 性能監控插件:MyBatis-Plus性能監控插件
  • SQL統計:SQL執行統計信息
  • 性能指標:關鍵性能指標監控

性能監控插件配置

// 性能分析插件
@Configuration
public class PerformanceConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// SQL性能規范插件interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());// 分頁插件PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);paginationInnerInterceptor.setMaxLimit(1000L);interceptor.addInnerInterceptor(paginationInnerInterceptor);return interceptor;}@Bean@Profile("dev")public PerformanceInterceptor performanceInterceptor() {PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();// 設置SQL執行最大時長,超過自動停止運行,有助于發現問題performanceInterceptor.setMaxTime(1000);// 設置SQL格式化,默認falseperformanceInterceptor.setFormat(true);return performanceInterceptor;}
}// 自定義性能監控插件
@Component
public class CustomPerformanceInterceptor implements Interceptor {private static final Logger logger = LoggerFactory.getLogger(CustomPerformanceInterceptor.class);@Overridepublic Object intercept(Invocation invocation) throws Throwable {long startTime = System.currentTimeMillis();try {Object result = invocation.proceed();long endTime = System.currentTimeMillis();long executeTime = endTime - startTime;// 記錄執行時間if (executeTime > 100) { // 超過100ms的SQL記錄警告MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];Object parameter = invocation.getArgs()[1];logger.warn("慢SQL檢測 - 執行時間: {}ms, SQL ID: {}, 參數: {}", executeTime, mappedStatement.getId(), parameter);}return result;} catch (Exception e) {long endTime = System.currentTimeMillis();long executeTime = endTime - startTime;logger.error("SQL執行異常 - 執行時間: {}ms, 異常信息: {}", executeTime, e.getMessage());throw e;}}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 設置屬性}
}

執行計劃分析工具

// 執行計劃分析工具
@Component
public class ExecutionPlanAnalyzer {@Autowiredprivate SqlSessionFactory sqlSessionFactory;public List<ExecutionPlan> analyzeExecutionPlan(String sql, Object... params) {try (SqlSession sqlSession = sqlSessionFactory.openSession()) {// 執行EXPLAIN分析String explainSql = "EXPLAIN " + sql;List<Map<String, Object>> results = sqlSession.selectList("analyzeExecutionPlan", Map.of("sql", explainSql, "params", params));return results.stream().map(this::convertToExecutionPlan).collect(Collectors.toList());}}private ExecutionPlan convertToExecutionPlan(Map<String, Object> result) {ExecutionPlan plan = new ExecutionPlan();plan.setId((Integer) result.get("id"));plan.setSelectType((String) result.get("select_type"));plan.setTable((String) result.get("table"));plan.setType((String) result.get("type"));plan.setPossibleKeys((String) result.get("possible_keys"));plan.setKey((String) result.get("key"));plan.setKeyLen((String) result.get("key_len"));plan.setRef((String) result.get("ref"));plan.setRows((Long) result.get("rows"));plan.setExtra((String) result.get("Extra"));return plan;}public boolean hasPerformanceIssues(List<ExecutionPlan> plans) {for (ExecutionPlan plan : plans) {// 檢查是否有性能問題if ("ALL".equals(plan.getType()) || // 全表掃描plan.getRows() > 10000 || // 掃描行數過多plan.getExtra().contains("Using filesort") || // 文件排序plan.getExtra().contains("Using temporary")) { // 使用臨時表return true;}}return false;}
}

慢查詢優化

簡要描述:識別、分析和優化慢查詢,提升數據庫查詢性能。

核心概念

  • 慢查詢日志:記錄執行時間超過閾值的SQL
  • 索引優化:合理創建和使用索引
  • 查詢重寫:優化SQL語句結構
  • 分頁優化:優化大數據量分頁查詢

慢查詢監控配置

# application.yml
spring:datasource:druid:# 慢SQL記錄filter:stat:enabled: trueslow-sql-millis: 1000log-slow-sql: true# 監控配置stat-view-servlet:enabled: trueurl-pattern: /druid/*reset-enable: falseweb-stat-filter:enabled: trueurl-pattern: /*exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"# MyBatis-Plus慢查詢配置
mybatis-plus:configuration:# 開啟SQL日志log-impl: org.apache.ibatis.logging.slf4j.Slf4jImplglobal-config:# 性能分析插件enable-sql-runner: truelogging:level:# 開啟SQL日志com.example.mapper: debug# Druid慢SQL日志druid.sql.Statement: debug

慢查詢優化策略

// 慢查詢優化服務
@Service
public class SlowQueryOptimizationService {@Autowiredprivate UserMapper userMapper;// 優化前:全表掃描public List<User> findUsersByNameBad(String name) {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.like("username", "%" + name + "%"); // 前綴模糊查詢,無法使用索引return userMapper.selectList(wrapper);}// 優化后:使用索引public List<User> findUsersByNameGood(String name) {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.likeRight("username", name); // 右模糊查詢,可以使用索引return userMapper.selectList(wrapper);}// 優化前:N+1查詢問題public List<UserWithRoles> findUsersWithRolesBad() {List<User> users = userMapper.selectList(null);return users.stream().map(user -> {List<Role> roles = roleMapper.selectByUserId(user.getId()); // N+1查詢return new UserWithRoles(user, roles);}).collect(Collectors.toList());}// 優化后:批量查詢public List<UserWithRoles> findUsersWithRolesGood() {List<User> users = userMapper.selectList(null);if (users.isEmpty()) {return Collections.emptyList();}List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());// 批量查詢角色List<UserRole> userRoles = userRoleMapper.selectList(new QueryWrapper<UserRole>().in("user_id", userIds));// 構建用戶角色映射Map<Long, List<Role>> userRoleMap = userRoles.stream().collect(Collectors.groupingBy(UserRole::getUserId,Collectors.mapping(ur -> roleMapper.selectById(ur.getRoleId()),Collectors.toList())));return users.stream().map(user -> new UserWithRoles(user, userRoleMap.getOrDefault(user.getId(), Collections.emptyList()))).collect(Collectors.toList());}// 分頁查詢優化public IPage<User> findUsersWithPagination(int current, int size, String keyword) {Page<User> page = new Page<>(current, size);QueryWrapper<User> wrapper = new QueryWrapper<>();if (StringUtils.hasText(keyword)) {wrapper.and(w -> w.like("username", keyword).or().like("email", keyword));}// 優化:只查詢必要字段wrapper.select("id", "username", "email", "create_time");return userMapper.selectPage(page, wrapper);}// 大數據量分頁優化(游標分頁)public List<User> findUsersWithCursorPagination(Long lastId, int size) {QueryWrapper<User> wrapper = new QueryWrapper<>();if (lastId != null) {wrapper.gt("id", lastId);}wrapper.orderByAsc("id");wrapper.last("LIMIT " + size);return userMapper.selectList(wrapper);}
}

索引優化建議

-- 創建合適的索引
-- 1. 單列索引
CREATE INDEX idx_user_username ON user(username);
CREATE INDEX idx_user_email ON user(email);
CREATE INDEX idx_user_create_time ON user(create_time);-- 2. 復合索引(注意字段順序)
CREATE INDEX idx_user_status_create_time ON user(status, create_time);
CREATE INDEX idx_user_dept_status ON user(dept_id, status);-- 3. 覆蓋索引(包含查詢所需的所有字段)
CREATE INDEX idx_user_cover ON user(status, username, email, create_time);-- 4. 前綴索引(對于長字符串字段)
CREATE INDEX idx_user_description ON user(description(50));-- 5. 函數索引(MySQL 8.0+)
CREATE INDEX idx_user_upper_username ON user((UPPER(username)));

緩存優化策略

簡要描述:合理使用MyBatis一級緩存、二級緩存以及外部緩存系統,提升查詢性能。

核心概念

  • 一級緩存:SqlSession級別的緩存
  • 二級緩存:Mapper級別的緩存
  • 外部緩存:Redis等外部緩存系統
  • 緩存策略:緩存的使用策略和失效機制

MyBatis緩存配置

# application.yml
mybatis-plus:configuration:# 開啟二級緩存cache-enabled: true# 本地緩存作用域local-cache-scope: session# 懶加載配置lazy-loading-enabled: trueaggressive-lazy-loading: false

Redis緩存集成

// Redis緩存配置
@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);// 設置序列化器Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);ObjectMapper mapper = new ObjectMapper();mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);serializer.setObjectMapper(mapper);template.setKeySerializer(new StringRedisSerializer());template.setHashKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(30)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())).disableCachingNullValues();return RedisCacheManager.builder(factory).cacheDefaults(config).build();}
}

Spring Cache注解使用

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;// 緩存查詢結果@Cacheable(value = "users", key = "#id")public User findById(Long id) {return userMapper.selectById(id);}// 緩存查詢結果(條件緩存)@Cacheable(value = "users", key = "#username", condition = "#username.length() > 3")public User findByUsername(String username) {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("username", username);return userMapper.selectOne(wrapper);}// 更新時清除緩存@CacheEvict(value = "users", key = "#user.id")public void updateUser(User user) {userMapper.updateById(user);}// 刪除時清除緩存@CacheEvict(value = "users", key = "#id")public void deleteUser(Long id) {userMapper.deleteById(id);}// 清除所有緩存@CacheEvict(value = "users", allEntries = true)public void clearAllCache() {// 清除所有用戶緩存}// 更新緩存@CachePut(value = "users", key = "#user.id")public User saveUser(User user) {userMapper.insert(user);return user;}
}

連接池優化

簡要描述:優化數據庫連接池配置,提升數據庫連接效率和應用性能。

核心概念

  • 連接池大小:合理設置連接池大小
  • 連接超時:設置合適的連接超時時間
  • 連接驗證:連接有效性驗證
  • 連接監控:連接池狀態監控

Druid連接池優化配置

# application.yml
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedruid:# 初始連接數initial-size: 10# 最小空閑連接數min-idle: 10# 最大活躍連接數max-active: 100# 獲取連接等待超時時間max-wait: 60000# 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒time-between-eviction-runs-millis: 60000# 配置一個連接在池中最小生存的時間,單位是毫秒min-evictable-idle-time-millis: 300000# 配置一個連接在池中最大生存的時間,單位是毫秒max-evictable-idle-time-millis: 900000# 用來檢測連接是否有效的sql,要求是一個查詢語句validation-query: SELECT 1# 建議配置為true,不影響性能,并且保證安全性test-while-idle: true# 申請連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能test-on-borrow: false# 歸還連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能test-on-return: false# 是否緩存preparedStatement,也就是PSCachepool-prepared-statements: true# 要啟用PSCache,必須配置大于0,當大于0時,poolPreparedStatements自動觸發修改為truemax-pool-prepared-statement-per-connection-size: 20# 配置監控統計攔截的filters,去掉后監控界面sql無法統計,'wall'用于防火墻filters: stat,wall,slf4j# 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=2000# 合并多個DruidDataSource的監控數據use-global-data-source-stat: true# 配置web監控web-stat-filter:enabled: trueurl-pattern: /*exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"session-stat-enable: falsesession-stat-max-count: 1000principal-session-name: adminprincipal-cookie-name: adminprofile-enable: true# 配置監控頁面stat-view-servlet:enabled: trueurl-pattern: /druid/*# IP白名單(沒有配置或者為空,則允許所有訪問)allow: 127.0.0.1,192.168.163.1# IP黑名單 (存在共同時,deny優先于allow)deny: 192.168.1.73# 禁用HTML頁面上的"Reset All"功能reset-enable: false# 登錄名login-username: admin# 登錄密碼login-password: 123456

HikariCP連接池優化配置

# application.yml
spring:datasource:type: com.zaxxer.hikari.HikariDataSourcehikari:# 連接池名稱pool-name: HikariCP# 最小空閑連接數minimum-idle: 10# 最大連接池大小maximum-pool-size: 100# 自動提交auto-commit: true# 空閑連接存活最大時間,默認600000(10分鐘)idle-timeout: 600000# 連接池最大生命周期,0表示無限生命周期,默認1800000即30分鐘max-lifetime: 1800000# 連接超時時間,默認30000即30秒connection-timeout: 30000# 測試連接是否可用的查詢語句connection-test-query: SELECT 1# 連接初始化SQLconnection-init-sql: SET NAMES utf8mb4# 數據庫連接超時時間,默認30秒,即30000validation-timeout: 5000# 空閑連接檢測周期,默認30000毫秒keepalive-time: 30000# 是否允許連接泄露檢測leak-detection-threshold: 60000

連接池監控

// 連接池監控服務
@Service
public class DataSourceMonitorService {@Autowiredprivate DataSource dataSource;public DataSourceStats getDataSourceStats() {if (dataSource instanceof DruidDataSource) {return getDruidStats((DruidDataSource) dataSource);} else if (dataSource instanceof HikariDataSource) {return getHikariStats((HikariDataSource) dataSource);}return new DataSourceStats();}private DataSourceStats getDruidStats(DruidDataSource druidDataSource) {DataSourceStats stats = new DataSourceStats();stats.setActiveCount(druidDataSource.getActiveCount());stats.setPoolingCount(druidDataSource.getPoolingCount());stats.setMaxActive(druidDataSource.getMaxActive());stats.setCreateCount(druidDataSource.getCreateCount());stats.setDestroyCount(druidDataSource.getDestroyCount());stats.setConnectCount(druidDataSource.getConnectCount());stats.setCloseCount(druidDataSource.getCloseCount());return stats;}private DataSourceStats getHikariStats(HikariDataSource hikariDataSource) {DataSourceStats stats = new DataSourceStats();HikariPoolMXBean poolBean = hikariDataSource.getHikariPoolMXBean();stats.setActiveCount(poolBean.getActiveConnections());stats.setPoolingCount(poolBean.getIdleConnections());stats.setMaxActive(hikariDataSource.getMaximumPoolSize());stats.setTotalConnections(poolBean.getTotalConnections());return stats;}@Scheduled(fixedRate = 30000) // 每30秒監控一次public void monitorDataSource() {DataSourceStats stats = getDataSourceStats();// 記錄監控日志logger.info("數據源監控 - 活躍連接: {}, 空閑連接: {}, 最大連接: {}", stats.getActiveCount(), stats.getPoolingCount(), stats.getMaxActive());// 檢查連接池健康狀況if (stats.getActiveCount() > stats.getMaxActive() * 0.8) {logger.warn("連接池使用率過高: {}%", (double) stats.getActiveCount() / stats.getMaxActive() * 100);}}
}

監控與診斷

簡要描述:通過各種監控工具和診斷手段,實時監控MyBatis-Plus應用的性能狀況。

核心概念

  • 性能指標監控:關鍵性能指標的實時監控
  • 健康檢查:應用健康狀況檢查
  • 鏈路追蹤:分布式鏈路追蹤
  • 告警機制:異常情況告警

Actuator監控配置

# application.yml
management:endpoints:web:exposure:include: "*"endpoint:health:show-details: alwaysmetrics:enabled: truemetrics:export:prometheus:enabled: truedistribution:percentiles-histogram:http.server.requests: truepercentiles:http.server.requests: 0.5, 0.9, 0.95, 0.99

自定義健康檢查

// 數據庫健康檢查
@Component
public class DatabaseHealthIndicator implements HealthIndicator {@Autowiredprivate SqlSessionFactory sqlSessionFactory;@Overridepublic Health health() {try (SqlSession sqlSession = sqlSessionFactory.openSession()) {// 執行簡單查詢測試數據庫連接sqlSession.selectOne("SELECT 1");return Health.up().withDetail("database", "Available").withDetail("validationQuery", "SELECT 1").build();} catch (Exception e) {return Health.down().withDetail("database", "Unavailable").withDetail("error", e.getMessage()).build();}}
}// MyBatis-Plus健康檢查
@Component
public class MybatisPlusHealthIndicator implements HealthIndicator {@Autowiredprivate UserMapper userMapper;@Overridepublic Health health() {try {// 測試基本CRUD操作long count = userMapper.selectCount(null);return Health.up().withDetail("mybatis-plus", "Available").withDetail("userCount", count).build();} catch (Exception e) {return Health.down().withDetail("mybatis-plus", "Unavailable").withDetail("error", e.getMessage()).build();}}
}

性能指標收集

// 自定義性能指標
@Component
public class MybatisPlusMetrics {private final MeterRegistry meterRegistry;private final Counter sqlExecutionCounter;private final Timer sqlExecutionTimer;private final AtomicLong slowQueryCount = new AtomicLong(0);public MybatisPlusMetrics(MeterRegistry meterRegistry) {this.meterRegistry = meterRegistry;this.sqlExecutionCounter = Counter.builder("mybatis.sql.executions").description("Total SQL executions").register(meterRegistry);this.sqlExecutionTimer = Timer.builder("mybatis.sql.duration").description("SQL execution duration").register(meterRegistry);Gauge.builder("mybatis.sql.slow.count").description("Number of slow queries").register(meterRegistry, this, MybatisPlusMetrics::getSlowQueryCount);}public void recordSqlExecution(String sqlId, long duration) {sqlExecutionCounter.increment(Tags.of("sql.id", sqlId,"status", "success"));sqlExecutionTimer.record(duration, TimeUnit.MILLISECONDS,Tags.of("sql.id", sqlId));}public void recordSqlError(String sqlId, String errorType) {sqlExecutionCounter.increment(Tags.of("sql.id", sqlId,"status", "error","error.type", errorType));}private double getSlowQueryCount() {return slowQueryCount.get();}public void incrementSlowQueryCount() {slowQueryCount.incrementAndGet();}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/95536.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/95536.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/95536.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

機器人伴侶的智能升級:Deepoc具身智能模型如何重塑成人伴侶體驗

引言&#xff1a;機器人伴侶市場的技術變革需求隨著人工智能技術的飛速發展和人們情感需求的多元化&#xff0c;機器人成人伴侶市場正在經歷前所未有的增長。傳統機器人伴侶已經能夠滿足基礎的交互需求&#xff0c;但在智能化、情感化和個性化方面仍存在明顯不足。這正是深算紀…

metabase基礎使用技巧 (dashboard, filter)

這是metabase系列分享文章的第2部分。本文將介紹metabase的基礎概念和使用介紹 question question是metabase中提供的通過UI化操作就能實現簡單的 快捷 直接的BI查詢。 點擊右側的New -> Question即可創建Question&#xff0c;可以理解為一個格式化的查詢&#xff1a; 這里…

機器人成人伴侶的智能化升級:Deepoc具身模型賦能沉浸式體驗

引言&#xff1a;成人機器人市場的技術革新需求隨著人工智能和機器人技術的快速發展&#xff0c;成人陪伴機器人行業正經歷從簡單機械運動到智能化交互的轉型。據市場研究數據顯示&#xff0c;全球成人機器人市場規模預計將在2026年突破100億美元&#xff0c;年復合增長率保持在…

Go語言企業級權限管理系統設計與實現

最近跟著學長再寫河南師范大學附屬中學圖書館的項目&#xff0c;學長交給了我一個任務&#xff0c;把本項目的權限管理給吃透&#xff0c;然后應用到下一個項目上。 我當然是偷著樂吶&#xff0c;因為讀代碼的時候&#xff0c;總是莫名給我一種公費旅游的感覺。 本來就想去了解…

Java應用快速部署Tomcat指南

將Java應用部署到Apache Tomcat服務器是開發Web應用過程中常見的任務。Tomcat是一個免費且開源的Servlet容器,它為Java應用提供了運行環境。本文將介紹如何準備你的Java應用,并將其部署到Tomcat服務器上。 Java 應用部署 tomcat 的根目錄結構 Tomcat中默認網站根目錄是$CAT…

Java 學習筆記(基礎篇2)

1. 分支結構① if 語句&#xff1a;(1) 雙分支&#xff1a;if (條件) {// 語句體1 } else {// 語句體2 }(2) 多分支if (條件1) {// 語句體1 } else if (條件2) {// 語句體2 } else {// 語句體N }② switch 語句&#xff1a;(1) 語法&#xff1a;如果都不是&#xff08;default&…

谷歌云代理商:用 AI 啟航,Gemini 重塑旅游酒店行業新體驗

本文由谷歌云谷歌地圖官方授權代理商、高級合作伙伴 CloudAce云一 整理發布。谷歌云谷歌地圖在中國授權代理商名單&#xff1a;Cloud Ace云一&#xff0c;全球20分公司&#xff0c;國內核心城市多個據點&#xff0c;谷歌云與谷歌地圖代理商、頂級合作伙伴&#xff08;Premier P…

springboot+vue實現通過poi完成excel

前端1、按鈕<el-buttontype"text"size"mini"click"handleExport">導出</el-button>2、方法//導出async handleExport() {if (!this.activityId) {this.$message.warning(活動ID不存在);return;}try {this.loading true;const res …

JMeter性能測試詳細版(適合0基礎小白學習--非常詳細)

01性能測試的概念 02性能測試的概念 基準測試 負載測試 穩定性測試 其他&#xff1a;并發測試、壓力測試、回歸測試等 壓力測試就是在系統強負載的情況下&#xff0c;是否會出現功能隱患問題&#xff0c;出現問題后是否可以盡快恢復 負載測試和壓力測試的區別: 1,核心目標不…

QT6(創建第一個QT項目)

編寫第一個QT項目 QT官網 安裝完QT后的界面 創建第一個項目 這里我們選擇第一個就好 下一步 下一步 選擇CMake&#xff0c;QMake是QT的CMAKE&#xff08;現在官方自己都不推薦了&#xff09; 下一步 選擇QWidget我們先創建一個最簡單的窗口程序 QMainWindow&#xff1a;主窗…

Golang指針操作

在 Go 語言&#xff08;Golang&#xff09;中&#xff0c;* 和 & 是與指針相關的兩個重要操作符。 理解它們對于掌握 Go 的內存管理和函數參數傳遞機制非常關鍵。 文章目錄一、& 操作符&#xff1a;取地址&#xff08;Address-of&#xff09;示例&#xff1a;二、* 操…

微服務從0到1

微服務從0到1實施步驟與注意事項一、核心實施步驟??需求分析與架構設計??明確業務邊界?&#xff1a;根據業務模塊&#xff08;如用戶管理、訂單系統&#xff09;劃分服務職責&#xff0c;避免服務職責重疊或耦合?。?定義接口契約?&#xff1a;通過 OpenAPI/Swagger 規范…

小程序排名優化:功能迭代如何助力排名攀升

小程序的功能不是一成不變的&#xff0c;持續的功能迭代不僅能滿足用戶不斷變化的需求&#xff0c;也是提升排名的重要途徑。平臺更傾向于推薦那些不斷更新、功能完善的小程序&#xff0c;因為它們能為用戶提供更優質的服務。合理規劃功能迭代方向和節奏&#xff0c;能讓小程序…

Unity TextMeshPro(二)優化

文章目錄前言一、字體打包優化二、ab打包冗余1、問題1、解決方法三、字體靜態優化四、擴展總結前言 優化TextMeshPro包體大小的方法記錄。 一、字體打包優化 游戲開發階段通常使用Fast打包方式&#xff0c;在正式項目發布的時候需要切換一下打包方式&#xff0c;重新將字體打…

C++ 之 【簡介 set、multiset、map、multimap 的使用】

目錄 1.序列式、關聯式容器 2.鍵值對 3.set 3.1set的簡介 3.2set的常用函數 4.multiset 5.map 5.1map的簡介 5.2map的常用函數 6.multimap 7.練習題 1.序列式、關聯式容器 vector、deque、list、forward_list、array等是CSTL中的序列式容器 其核心特性是 元素按插入…

數據結構——排序(升級篇:快速排序、堆排序、希爾排序、計數排序)

1. 快速排序&#xff08;Quick Sort&#xff09; 原理&#xff1a; 選擇一個基準值&#xff08;pivot&#xff09;將數組分成兩部分&#xff1a;小于 pivot 的放左邊&#xff0c;大于 pivot 的放右邊。然后遞歸處理 工作過程示例&#xff1a; 示例數組&#xff1a;[5, 3, 8, 4,…

C++:淺嘗gdb

hp window11 wsl ubuntu what is gdb&#xff1f; GNU調試器&#xff08;英語&#xff1a;GNU Debugger&#xff0c;縮寫&#xff1a;GDB&#xff09;&#xff0c;是GNU軟件系統中的標準調試器&#xff0c;此外GDB也是個具有移攜性的調試器&#xff0c;經過移攜需求的調修與…

Android輸入法一些常用的命令

Android開發過程可能會遇到Android輸入法異常的問題&#xff0c;可以通過如下命令來查看和修改系統的輸入法。方便調試。 獲取當下系統的所有輸入法 adb shell ime list獲取當前的可用輸入法 adb shell ime list -s獲取當前的輸入法 adb shell settings get secure default_inp…

Sklearn 機器學習 手寫數字識別 加載并查看數據

??親愛的技術愛好者們,熱烈歡迎來到 Kant2048 的博客!我是 Thomas Kant,很開心能在CSDN上與你們相遇~?? 本博客的精華專欄: 【自動化測試】 【測試經驗】 【人工智能】 【Python】 Sklearn 機器學習 手寫數字識別:加載并查看數據 在機器學習入門案例中,手寫數字識別…

衛星通信鏈路預算之七:上行載噪比計算

在前面的文章中我們介紹了衛星通信鏈路計算的基礎知識&#xff0c;包括&#xff1a; 信噪比分配&#xff1b; 帶寬和功帶平衡原則&#xff1b; EIRP和G/T&#xff1b; 輸入回退&#xff1b; 輸入飽和通量密度SFD&#xff1b; 輸出回退&#xff1b; 這次我們正式進入正題…