最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot+MyBatis-Plus+Dynamic-Datasource讀寫分離完整指南

 更新時間:2025年11月12日 09:04:54   作者:IT橘子皮  
在SpringBoot項目中,使用MyBatis-Plus和Dynamic-Datasource實現(xiàn)讀寫分離是一種常見且高效的架構(gòu)選擇,下面就來詳細(xì)介紹一下SpringBoot讀寫分離,感興趣的可以了解一下

在SpringBoot項目中,使用MyBatis-Plus和Dynamic-Datasource實現(xiàn)讀寫分離是一種常見且高效的架構(gòu)選擇。下面我將為你詳細(xì)講解完整的實現(xiàn)方案,包括環(huán)境準(zhǔn)備、配置、代碼實現(xiàn)以及注意事項。

1. 讀寫分離概述與核心概念

讀寫分離是一種常見的數(shù)據(jù)庫優(yōu)化方案,其核心思想是將數(shù)據(jù)庫的寫操作(INSERT、UPDATE、DELETE)和讀操作(SELECT)分發(fā)到不同的數(shù)據(jù)庫節(jié)點上。主數(shù)據(jù)庫(Master)負(fù)責(zé)處理所有寫操作,而從數(shù)據(jù)庫(Slave)負(fù)責(zé)處理讀操作,通過數(shù)據(jù)庫的主從復(fù)制機(jī)制保持?jǐn)?shù)據(jù)同步。

1.1 核心價值

  • 提升并發(fā)性能:將讀請求分散到多個從庫,減輕主庫壓力
  • 提高系統(tǒng)可用性:單個從庫故障不影響讀服務(wù)
  • 優(yōu)化響應(yīng)速度:專庫專用,避免讀寫操作相互阻塞

1.2 技術(shù)架構(gòu)

應(yīng)用程序 → Dynamic-Datasource → 主庫(寫操作)
                     ↓
                   從庫(讀操作)

2. 環(huán)境準(zhǔn)備與依賴配置

2.1 添加Maven依賴

首先在項目的pom.xml中添加必要的依賴:

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- MyBatis-Plus 啟動器 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.2</version>
    </dependency>
    
    <!-- Dynamic-Datasource 啟動器(核心依賴) -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        <version>3.6.1</version>
    </dependency>
    
    <!-- MySQL 驅(qū)動 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
    
    <!-- 連接池(可選,HikariCP已內(nèi)置) -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.16</version>
    </dependency>
</dependencies>

2.2 配置文件設(shè)置

application.yml中配置主從數(shù)據(jù)源:

spring:
  datasource:
    dynamic:
      primary: master  # 設(shè)置默認(rèn)數(shù)據(jù)源為主庫
      strict: false   # 是否嚴(yán)格匹配數(shù)據(jù)源,false時未匹配到指定數(shù)據(jù)源則使用默認(rèn)數(shù)據(jù)源
      datasource:
        # 主庫配置(寫操作)
        master:
          url: jdbc:mysql://master-host:3306/core?useSSL=false&serverTimezone=Asia/Shanghai
          username: admin
          password: master@123
          driver-class-name: com.mysql.cj.jdbc.Driver
        # 從庫配置(讀操作)- 支持多個從庫
        slave1:
          url: jdbc:mysql://slave1-host:3306/core?useSSL=false&serverTimezone=Asia/Shanghai
          username: readonly
          password: slave@123
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave2:
          url: jdbc:mysql://slave2-host:3306/core?useSSL=false&serverTimezone=Asia/Shanghai
          username: readonly
          password: slave@123
          driver-class-name: com.mysql.cj.jdbc.Driver
      
      # 負(fù)載均衡策略配置(多個從庫時生效)
      strategy: 
        slave: round_robin  # 從庫負(fù)載均衡策略:random(隨機(jī))/round_robin(輪詢)
      
      # 連接池配置(HikariCP)
      hikari:
        max-pool-size: 20      # 最大連接數(shù)
        min-idle: 5           # 最小空閑連接
        connection-timeout: 30000  # 連接超時時間(ms)
        idle-timeout: 600000  # 空閑連接超時時間(ms)
        max-lifetime: 1800000 # 連接最大生命周期(ms)

# MyBatis-Plus 配置
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true  # 開啟駝峰命名轉(zhuǎn)換
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 打印SQL日志(調(diào)試用)
  global-config:
    db-config:
      id-type: auto  # 主鍵策略自增

3. 數(shù)據(jù)源與MyBatis-Plus配置類

3.1 動態(tài)數(shù)據(jù)源自動配置

Dynamic-Datasource starter已經(jīng)提供了自動配置,大多數(shù)情況下無需額外配置。但如果需要自定義,可以創(chuàng)建配置類:

@Configuration
@MapperScan("com.example.mapper")  // 指定MyBatis mapper接口的掃描路徑
public class DataSourceConfig {
    
    /**
     * 配置動態(tài)數(shù)據(jù)源
     * Dynamic-Datasource會自動根據(jù)application.yml的配置創(chuàng)建數(shù)據(jù)源
     * 此處可以添加自定義配置
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.dynamic")
    public DynamicDataSourceProperties dynamicDataSourceProperties() {
        return new DynamicDataSourceProperties();
    }
    
    /**
     * 配置MyBatis-Plus攔截器(分頁插件等)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分頁插件
        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        paginationInterceptor.setMaxLimit(1000L);  // 設(shè)置最大分頁限制
        interceptor.addInnerInterceptor(paginationInterceptor);
        
        // 可以添加其他插件,如樂觀鎖插件等
        return interceptor;
    }
}

4. 業(yè)務(wù)層實現(xiàn)讀寫分離

4.1 使用@DS注解手動切換數(shù)據(jù)源

最直接的方式是在Service層使用@DS注解指定數(shù)據(jù)源:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    /**
     * 寫操作:使用@DS("master")指定主庫
     * 注意:寫操作建議都使用主庫,確保數(shù)據(jù)一致性
     */
    @DS("master")  // 指定使用主庫
    @Transactional(rollbackFor = Exception.class)  // 添加事務(wù)管理
    @Override
    public boolean createUser(User user) {
        // 參數(shù)校驗
        if (user == null || StringUtils.isBlank(user.getUsername())) {
            throw new IllegalArgumentException("用戶信息不完整");
        }
        
        // 設(shè)置創(chuàng)建時間
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        
        // 執(zhí)行插入操作,這里會使用主庫
        boolean result = save(user);
        
        // 這里可以添加其他業(yè)務(wù)邏輯
        log.info("創(chuàng)建用戶成功,用戶ID: {}", user.getId());
        
        return result;
    }
    
    /**
     * 讀操作:使用@DS("slave")指定從庫
     * 框架會根據(jù)負(fù)載均衡策略選擇slave1或slave2
     */
    @DS("slave")  // 指定使用從庫(負(fù)載均衡)
    @Override
    public User getUserById(Long id) {
        // 參數(shù)校驗
        if (id == null || id <= 0) {
            throw new IllegalArgumentException("用戶ID不合法");
        }
        
        // 查詢用戶信息,這里會使用從庫
        User user = getById(id);
        
        if (user == null) {
            log.warn("未找到對應(yīng)用戶,用戶ID: {}", id);
            throw new RuntimeException("用戶不存在");
        }
        
        return user;
    }
    
    /**
     * 批量查詢:同樣使用從庫
     */
    @DS("slave")
    @Override
    public List<User> listUsersByCondition(UserQuery query) {
        // 構(gòu)建查詢條件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        
        if (StringUtils.isNotBlank(query.getUsername())) {
            wrapper.like(User::getUsername, query.getUsername());
        }
        
        if (query.getStatus() != null) {
            wrapper.eq(User::getStatus, query.getStatus());
        }
        
        // 添加排序
        wrapper.orderByDesc(User::getCreateTime);
        
        // 執(zhí)行查詢,使用從庫
        return list(wrapper);
    }
    
    /**
     * 更新操作:必須使用主庫
     */
    @DS("master")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateUser(User user) {
        if (user == null || user.getId() == null) {
            throw new IllegalArgumentException("用戶信息不完整");
        }
        
        // 設(shè)置更新時間
        user.setUpdateTime(LocalDateTime.now());
        
        // 執(zhí)行更新,使用主庫
        boolean result = updateById(user);
        
        if (result) {
            log.info("更新用戶成功,用戶ID: {}", user.getId());
        } else {
            log.error("更新用戶失敗,用戶ID: {}", user.getId());
        }
        
        return result;
    }
    
    /**
     * 刪除操作:必須使用主庫
     */
    @DS("master")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteUser(Long id) {
        if (id == null || id <= 0) {
            throw new IllegalArgumentException("用戶ID不合法");
        }
        
        // 執(zhí)行刪除,使用主庫
        boolean result = removeById(id);
        
        if (result) {
            log.info("刪除用戶成功,用戶ID: {}", id);
        } else {
            log.warn("刪除用戶失敗,用戶ID: {}", id);
        }
        
        return result;
    }
}

4.2 基于AOP的自動數(shù)據(jù)源切換(推薦)

對于大型項目,手動添加@DS注解比較繁瑣,可以通過AOP自動根據(jù)方法類型切換數(shù)據(jù)源:

@Aspect
@Component
@Slf4j
public class DataSourceAspect {
    
    /**
     * 定義切點:攔截Service層的所有方法
     */
    @Pointcut("execution(* com.example.service..*.*(..))")
    public void servicePointcut() {}
    
    /**
     * 前置通知:在方法執(zhí)行前選擇數(shù)據(jù)源
     */
    @Before("servicePointcut()")
    public void before(JoinPoint joinPoint) {
        // 獲取方法名
        String methodName = joinPoint.getSignature().getName();
        
        // 根據(jù)方法名前綴判斷是讀操作還是寫操作
        if (isReadOperation(methodName)) {
            // 讀操作使用從庫
            DynamicDataSourceContextHolder.push("slave");
            log.debug("切換數(shù)據(jù)源到從庫,方法名: {}", methodName);
        } else {
            // 寫操作使用主庫
            DynamicDataSourceContextHolder.push("master");
            log.debug("切換數(shù)據(jù)源到主庫,方法名: {}", methodName);
        }
    }
    
    /**
     * 后置通知:清理數(shù)據(jù)源上下文
     */
    @After("servicePointcut()")
    public void after() {
        DynamicDataSourceContextHolder.clear();
        log.debug("清除數(shù)據(jù)源上下文");
    }
    
    /**
     * 判斷是否為讀操作
     * 可以根據(jù)方法名前綴進(jìn)行判斷
     */
    private boolean isReadOperation(String methodName) {
        // 常見的讀操作方法前綴
        String[] readPrefixes = {"get", "select", "list", "query", "find", "search", "count", "check"};
        
        for (String prefix : readPrefixes) {
            if (methodName.startsWith(prefix)) {
                return true;
            }
        }
        return false;
    }
}

使用AOP后,Service層的代碼可以簡化為:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean createUser(User user) {
        // 自動使用主庫(AOP根據(jù)方法名判斷)
        user.setCreateTime(LocalDateTime.now());
        return save(user);
    }
    
    @Override
    public User getUserById(Long id) {
        // 自動使用從庫(AOP根據(jù)方法名前綴"get"判斷)
        return getById(id);
    }
    
    // 其他方法無需添加@DS注解,由AOP自動處理
}

5. 事務(wù)處理策略

事務(wù)處理是讀寫分離中的關(guān)鍵問題,需要特別注意:

5.1 單數(shù)據(jù)源事務(wù)

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    
    /**
     * 單數(shù)據(jù)源事務(wù):所有操作都在主庫執(zhí)行
     * 使用@DS("master")確保即使有AOP配置也使用主庫
     */
    @DS("master")  // 顯式指定主庫
    @Transactional(rollbackFor = Exception.class)  // 聲明式事務(wù)
    @Override
    public void createOrder(Order order) {
        // 1. 保存訂單主信息(主庫)
        save(order);
        
        // 2. 更新庫存(主庫)
        updateStock(order);
        
        // 3. 記錄操作日志(主庫)
        logOrderOperation(order);
        
        // 如果任何一步失敗,整個事務(wù)回滾
    }
    
    /**
     * 只讀事務(wù):可以指定從庫
     */
    @DS("slave")
    @Transactional(readOnly = true)  // 只讀事務(wù),有優(yōu)化效果
    @Override
    public Order getOrderDetail(Long orderId) {
        return getById(orderId);
    }
}

5.2 多數(shù)據(jù)源事務(wù)處理

對于涉及多個數(shù)據(jù)源的復(fù)雜事務(wù),需要使用分布式事務(wù)解決方案:

@Service
@Slf4j
public class DistributedTransactionService {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private OrderService orderService;
    
    /**
     * 使用@DSTransactional實現(xiàn)多數(shù)據(jù)源分布式事務(wù)
     * 注意:需要集成Seata等分布式事務(wù)框架
     */
    // @DSTransactional  // 分布式事務(wù)注解(需要額外配置)
    @Transactional(rollbackFor = Exception.class)
    public void placeOrder(Order order, User user) {
        try {
            // 1. 用戶操作(主庫)
            userService.updateUser(user);
            
            // 2. 訂單操作(主庫)
            orderService.createOrder(order);
            
            // 模擬業(yè)務(wù)異常
            if (order.getAmount() == null) {
                throw new RuntimeException("訂單金額不能為空");
            }
            
            log.info("下單成功");
        } catch (Exception e) {
            log.error("下單失敗,已回滾", e);
            throw e;  // 拋出異常觸發(fā)回滾
        }
    }
}

6. 高級功能與優(yōu)化策略

6.1 多從庫負(fù)載均衡

當(dāng)配置多個從庫時,Dynamic-Datasource支持多種負(fù)載均衡策略:

spring:
  datasource:
    dynamic:
      datasource:
        master:
          # 主庫配置...
        slave1:
          # 從庫1配置...
        slave2:
          # 從庫2配置...
        slave3:
          # 從庫3配置...
      strategy:
        slave: round_robin  # 負(fù)載均衡策略
        # 可選值:
        # random - 隨機(jī)選擇(默認(rèn))
        # round_robin - 輪詢
        # weight_round_robin - 加權(quán)輪詢(需要額外配置權(quán)重)

負(fù)載均衡策略對比:

策略類型描述適用場景
random隨機(jī)選擇從庫簡單的讀負(fù)載均衡
round_robin輪詢選擇從庫從庫配置相近,需要均勻分布
weight_round_robin根據(jù)權(quán)重選擇從庫配置不同,按性能分配負(fù)載

6.2 健康檢查與故障轉(zhuǎn)移

@Component
@Slf4j
public class DataSourceHealthCheck {
    
    @Autowired
    private DataSource dataSource;
    
    /**
     * 定時檢查從庫健康狀態(tài)
     */
    @Scheduled(fixedRate = 60000)  // 每分鐘執(zhí)行一次
    public void checkSlaveHealth() {
        // 這里可以實現(xiàn)從庫健康檢查邏輯
        // 如果某個從庫不可用,可以動態(tài)將其從負(fù)載均衡池中移除
        
        log.info("執(zhí)行從庫健康檢查...");
        // 實際實現(xiàn)中可以使用JDBC測試連接或查詢數(shù)據(jù)庫狀態(tài)
    }
    
    /**
     * 獲取從庫同步延遲
     */
    public Long getReplicationDelay(String slaveName) {
        // 執(zhí)行SQL查詢從庫同步狀態(tài)
        // SHOW SLAVE STATUS 可以獲取Seconds_Behind_Master等信息
        // 如果延遲過大,可以臨時將該從庫標(biāo)記為不可用
        
        return 0L;  // 返回延遲秒數(shù)
    }
}

7. 測試與驗證

7.1 單元測試

@SpringBootTest
@Slf4j
class ReadWriteSeparationTest {
    
    @Autowired
    private UserService userService;
    
    /**
     * 測試寫操作(應(yīng)該路由到主庫)
     */
    @Test
    void testWriteOperation() {
        User user = new User();
        user.setUsername("testUser");
        user.setEmail("test@example.com");
        
        boolean result = userService.createUser(user);
        
        assertTrue(result);
        log.info("寫操作測試通過,應(yīng)路由到主庫");
    }
    
    /**
     * 測試讀操作(應(yīng)該路由到從庫)
     */
    @Test
    void testReadOperation() {
        User user = userService.getUserById(1L);
        
        assertNotNull(user);
        log.info("讀操作測試通過,應(yīng)路由到從庫");
    }
    
    /**
     * 測試事務(wù)內(nèi)的數(shù)據(jù)源選擇
     */
    @Test
    void testTransactionalOperation() {
        // 測試事務(wù)方法,應(yīng)始終使用主庫
        User user = userService.getUserById(1L);
        assertNotNull(user);
        
        log.info("事務(wù)內(nèi)操作測試完成");
    }
}

7.2 驗證數(shù)據(jù)源路由

application.yml中開啟SQL日志,驗證讀寫分離是否生效:

# 開啟MyBatis SQL日志
logging:
  level:
    com.example.mapper: debug  # Mapper接口的包路徑
    com.baomidou.dynamic.datasource: debug  # Dynamic-Datasource日志

觀察控制臺輸出,應(yīng)該能看到類似日志:

2025-11-11 10:00:00 | Master DataSource  | INSERT INTO user ...
2025-11-11 10:00:05 | Slave DataSource   | SELECT * FROM user ...

8. 生產(chǎn)環(huán)境注意事項

8.1 主從同步延遲處理

@Service
public class CriticalReadService {
    
    @Autowired
    private UserMapper userMapper;
    
    /**
     * 對于一致性要求高的讀操作,強(qiáng)制走主庫
     */
    @DS("master")  // 強(qiáng)制使用主庫,避免讀取舊數(shù)據(jù)
    public User getCriticalUserInfo(Long userId) {
        // 例如:賬戶余額、訂單狀態(tài)等關(guān)鍵信息
        return userMapper.selectById(userId);
    }
    
    /**
     * 檢查數(shù)據(jù)是否已同步
     */
    public boolean waitForReplication(Long userId, int maxWaitSeconds) {
        for (int i = 0; i < maxWaitSeconds; i++) {
            User masterUser = getFromMaster(userId);
            User slaveUser = getFromSlave(userId);
            
            if (Objects.equals(masterUser, slaveUser)) {
                return true;  // 數(shù)據(jù)已同步
            }
            
            try {
                Thread.sleep(1000);  // 等待1秒
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
        return false;  // 同步超時
    }
}

8.2 監(jiān)控與告警

# 連接池監(jiān)控配置
spring:
  datasource:
    dynamic:
      druid:
        # 開啟監(jiān)控統(tǒng)計
        filters: stat,wall,log4j
        web-stat-filter:
          enabled: true
        stat-view-servlet:
          enabled: true
          url-pattern: /druid/*
          reset-enable: false
          login-username: admin
          login-password: admin

9. 常見問題與解決方案

9.1 事務(wù)內(nèi)數(shù)據(jù)源切換失效

問題:在@Transactional方法內(nèi),數(shù)據(jù)源在方法開始時確定,方法內(nèi)部調(diào)用無法切換數(shù)據(jù)源。

解決方案

@Service
public class TransactionalService {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    @Transactional
    public void transactionalMethod() {
        // 方法內(nèi)需要切換數(shù)據(jù)源時,通過代理對象調(diào)用
        TransactionalService self = applicationContext.getBean(TransactionalService.class);
        
        // 通過代理對象調(diào)用,可以正常切換數(shù)據(jù)源
        self.readOperation();  // 使用從庫
        self.writeOperation();  // 使用主庫
    }
    
    @DS("slave")
    public void readOperation() {
        // 讀操作
    }
    
    @DS("master")
    public void writeOperation() {
        // 寫操作
    }
}

9.2 動態(tài)增減數(shù)據(jù)源

@Service
public class DynamicDataSourceService {
    
    @Autowired
    private DynamicDataSourceProvider dataSourceProvider;
    
    /**
     * 運行時動態(tài)添加數(shù)據(jù)源
     */
    public void addDataSource(String dataSourceName, DataSourceProperty property) {
        DynamicDataSourceContextHolder.addDataSource(dataSourceName, property);
        log.info("動態(tài)添加數(shù)據(jù)源: {}", dataSourceName);
    }
    
    /**
     * 運行時移除數(shù)據(jù)源
     */
    public void removeDataSource(String dataSourceName) {
        DynamicDataSourceContextHolder.removeDataSource(dataSourceName);
        log.info("動態(tài)移除數(shù)據(jù)源: {}", dataSourceName);
    }
}

總結(jié)

通過SpringBoot + MyBatis-Plus + Dynamic-Datasource實現(xiàn)讀寫分離,可以顯著提升數(shù)據(jù)庫讀性能和高可用性。關(guān)鍵點包括:

  1. 正確配置:確保主從數(shù)據(jù)源配置正確,負(fù)載均衡策略合理
  2. 合理使用注解:在適當(dāng)?shù)姆椒ㄉ鲜褂?code>@DS注解或通過AOP自動切換
  3. 事務(wù)管理:注意事務(wù)內(nèi)的數(shù)據(jù)源行為,關(guān)鍵操作強(qiáng)制走主庫
  4. 監(jiān)控告警:建立完善的監(jiān)控體系,及時發(fā)現(xiàn)主從延遲等問題
  5. 故障處理:實現(xiàn)從庫故障自動轉(zhuǎn)移和恢復(fù)機(jī)制

這種架構(gòu)在讀寫比例高的應(yīng)用中能帶來顯著的性能提升,但也需要處理好主從同步延遲等一致性問題。

到此這篇關(guān)于SpringBoot+MyBatis-Plus+Dynamic-Datasource讀寫分離完整指南的文章就介紹到這了,更多相關(guān)SpringBoot MyBatis-Plus 讀寫分離內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java使用J4L識別驗證碼的操作方法

    Java使用J4L識別驗證碼的操作方法

    這篇文章主要介紹了Java使用J4L識別驗證碼的操作方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-02-02
  • jdk8?FunctionalInterface注解源碼解讀

    jdk8?FunctionalInterface注解源碼解讀

    這篇文章主要介紹了jdk8?FunctionalInterface注解源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • Java實現(xiàn)批量修改文件名和重命名的方法

    Java實現(xiàn)批量修改文件名和重命名的方法

    這篇文章主要介紹了Java實現(xiàn)批量修改文件名和重命名的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java的Hibernate框架中的組合映射學(xué)習(xí)教程

    Java的Hibernate框架中的組合映射學(xué)習(xí)教程

    組合映射即是指主對象和子對象關(guān)聯(lián)且擁有相同的生命周期的映射關(guān)系,這里我們將舉一些數(shù)據(jù)操作的實例,來講解Java的Hibernate框架中的組合映射學(xué)習(xí)教程
    2016-07-07
  • Spring學(xué)習(xí)筆記之RedisTemplate的配置與使用教程

    Spring學(xué)習(xí)筆記之RedisTemplate的配置與使用教程

    這篇文章主要給大家介紹了關(guān)于Spring學(xué)習(xí)筆記之RedisTemplate配置與使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用spring具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-06-06
  • 舉例講解Java中synchronized關(guān)鍵字的用法

    舉例講解Java中synchronized關(guān)鍵字的用法

    這篇文章主要介紹了Java中synchronized關(guān)鍵字的用法,針對synchronized修飾方法的使用作出了簡單講解和演示,需要的朋友可以參考下
    2016-04-04
  • Mybatis動態(tài)sql超詳細(xì)講解

    Mybatis動態(tài)sql超詳細(xì)講解

    動態(tài)SQL是MyBatis的強(qiáng)大特性之一,顧名思義就是會動的SQL,即是能夠靈活的根據(jù)某種條件拼接出完整的SQL語句,下面這篇文章主要給大家介紹了關(guān)于Mybatis動態(tài)sql的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • SpringBoot接口防重復(fù)提交的三種解決方案

    SpringBoot接口防重復(fù)提交的三種解決方案

    在Web開發(fā)中,防止用戶重復(fù)提交表單是一個常見的需求,用戶可能會因為網(wǎng)絡(luò)延遲、誤操作等原因多次點擊提交按鈕,導(dǎo)致后臺接收到多個相同的請求,本文將介紹幾種在Spring Boot中實現(xiàn)接口防重復(fù)提交的方法,需要的朋友可以參考下
    2024-11-11
  • java 注解的基礎(chǔ)詳細(xì)介紹

    java 注解的基礎(chǔ)詳細(xì)介紹

    這篇文章主要介紹了java 注解的基礎(chǔ)詳細(xì)介紹的相關(guān)資料,希望通過本文大家能掌握注解的使用方法,需要的朋友可以參考下
    2017-09-09
  • Java多線程 線程同步與死鎖

    Java多線程 線程同步與死鎖

    這篇文章主要介紹了 Java多線程 線程同步與死鎖的相關(guān)資料,需要的朋友可以參考下
    2017-07-07

最新評論

体育| 太原市| 兴文县| 崇州市| 二手房| 沈阳市| 外汇| 阿拉善左旗| 类乌齐县| 宁城县| 宜丰县| 五莲县| 岳西县| 肇东市| 芮城县| 威宁| 肥乡县| 赤壁市| 蒲江县| 馆陶县| 泸定县| 英超| 靖安县| 新建县| 庆阳市| 江津市| 镇江市| 永靖县| 泰安市| 永福县| 广灵县| 凌海市| 广德县| 姚安县| 杨浦区| 大邑县| 佛学| 柏乡县| 萝北县| 色达县| 碌曲县|