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

SpringBoot+Redis多DB動態(tài)切換幾種方式總結(jié)

 更新時間:2025年09月06日 11:27:42   作者:自燃人~  
在現(xiàn)代Web應(yīng)用開發(fā)中,緩存是提升系統(tǒng)性能的重要手段,Redis作為高性能的內(nèi)存數(shù)據(jù)庫,與SpringBoot的完美結(jié)合為我們提供了強(qiáng)大的緩存解決方案,這篇文章主要介紹了SpringBoot+Redis多DB動態(tài)切換幾種方式的相關(guān)資料,需要的朋友可以參考下

功能目標(biāo)

該方案實現(xiàn)了 Redis 多數(shù)據(jù)庫(多 DB index)環(huán)境下,通過注解或顯式指定的方式在代碼層自動切換 Redis 數(shù)據(jù)庫,無需手動 select 操作,支持線程安全、可恢復(fù)的動態(tài)切換機(jī)制。

動態(tài)切換 Redis 庫的方案適合一些小型或簡單的應(yīng)用場景,主要用于 資源節(jié)約、簡單隔離、邏輯清晰 的情況。但隨著應(yīng)用的增長和流量的增加,使用 Redis 集群或其他分布式緩存方案可能會更加適應(yīng)需求。

模塊組成

組件說明
@SwitchDatabase注解:方法粒度聲明 Redis 使用哪個數(shù)據(jù)庫
DatabaseSwitchAspect

AOP 切面:攔截

@SwitchDatabase

注解,自動切庫

DynamicDatabaseConnectionFactoryRedis 連接工廠擴(kuò)展:支持線程級別的 select(db)
RedisDatabaseSwitcher切庫控制器:封裝切換邏輯
RedisUtilRedis 工具類:封裝常用 Redis 操作,同時支持傳入 db 參數(shù)切庫
RedisConfig配置類:初始化動態(tài)連接工廠和 RedisTemplate

使用方式

方式一:使用 @SwitchDatabase 注解自動切庫(推薦)

適合無侵入的服務(wù)方法層使用,自動切庫,自動恢復(fù)。

@SwitchDatabase(1)
public void updateUserCache(String userId) {
    redisUtil.set("user:" + userId, "someData");
}

方式二:工具類中手動指定 db 切換(適用于底層封裝或靈活需求)

redisUtil.set("shop:token:" + token, "abc", 60, TimeUnit.MINUTES, 2);

String val = redisUtil.get("shop:token:" + token, String.class, 2);

自動切換實現(xiàn)邏輯

1.@SwitchDatabase

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author chenxuefeng
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SwitchDatabase {
    int value();// 目標(biāo)數(shù)據(jù)庫編號
}

注解會被 AOP 切面攔截;

2.DatabaseSwitchAspect

import com.zrr.platform.reids.support.DynamicDatabaseConnectionFactory;
import com.zrr.platform.reids.support.RedisDatabaseSwitcher;
import com.zrr.platform.reids.annotation.SwitchDatabase;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 切面:攔截 @SwitchDatabase 注解的方法
 * - 在方法執(zhí)行前切換數(shù)據(jù)庫
 * - 執(zhí)行完成后恢復(fù)原數(shù)據(jù)庫
 * @author zrr
 */
@Aspect
@Component
public class DatabaseSwitchAspect {
    @Autowired
    private RedisDatabaseSwitcher switcher;

    @Around("@annotation(switchDatabase)")
    public Object switchDatabase(ProceedingJoinPoint joinPoint, SwitchDatabase switchDatabase) throws Throwable {
        // 記錄當(dāng)前數(shù)據(jù)庫
        int originalDb = getCurrentDatabase();
        try {
            // 切換到目標(biāo)數(shù)據(jù)庫
            switcher.switchDatabase(switchDatabase.value());
            return joinPoint.proceed();
        } finally {
            // 恢復(fù)原來的數(shù)據(jù)庫
            switcher.switchDatabase(originalDb);
        }
    }

    private int getCurrentDatabase() {
        // 從連接工廠獲取當(dāng)前數(shù)據(jù)庫編號(需擴(kuò)展 DynamicDatabaseConnectionFactory)
        return ((DynamicDatabaseConnectionFactory) switcher.getConnectionFactory()).getCurrentDatabase();
    }
}

3.數(shù)據(jù)庫連接工廠 和 數(shù)據(jù)庫切換器

3.1 數(shù)據(jù)庫連接工廠

import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;

/**
 * @author zrr
 * 動態(tài) Redis 數(shù)據(jù)庫連接工廠。
 * 核心原理:
 * - 使用 ThreadLocal 保存當(dāng)前線程應(yīng)使用的 Redis 數(shù)據(jù)庫編號(db index)
 * - 重寫 getConnection() 方法,在每次獲取連接時調(diào)用 select(db),實現(xiàn)動態(tài)切換
 */
public class DynamicDatabaseConnectionFactory extends LettuceConnectionFactory {
    private final ThreadLocal<Integer> databaseHolder = ThreadLocal.withInitial(() -> 0);

    public DynamicDatabaseConnectionFactory(RedisStandaloneConfiguration config,
                                            LettucePoolingClientConfiguration clientConfig) {
        super(config, clientConfig);
    }
    /**
     * 設(shè)置當(dāng)前線程使用的數(shù)據(jù)庫編號
     */
    public void setCurrentDatabase(int db) {
        databaseHolder.set(db);
    }

    /**
     * 獲取當(dāng)前線程設(shè)置的數(shù)據(jù)庫編號
     */
    public int getCurrentDatabase() {
        return databaseHolder.get();
    }
    /**
     * 初始化后默認(rèn)執(zhí)行一次 select(db),確保連接池創(chuàng)建時使用默認(rèn) db
     */
    @Override
    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        // 初始化默認(rèn)數(shù)據(jù)庫
        RedisConnection connection = getConnection();
        connection.select(databaseHolder.get());
        connection.close();
    }

    /**
     * 每次從連接池中獲取連接時都執(zhí)行 select(db)
     */
    @Override
    public RedisConnection getConnection() {
        RedisConnection connection = super.getConnection();
        connection.select(databaseHolder.get());
        return connection;
    }

}

3.2.數(shù)據(jù)庫切換器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.stereotype.Component;

/**
 * @author zrr
 * Redis 數(shù)據(jù)庫切換器
 * 封裝對 DynamicDatabaseConnectionFactory 的調(diào)用
 */
@Component
public class RedisDatabaseSwitcher {
    @Autowired
    private RedisConnectionFactory connectionFactory;
    /**
     * 切換 Redis 數(shù)據(jù)庫(當(dāng)前線程內(nèi)有效)
     */
    public void switchDatabase(int db) {
        if (connectionFactory instanceof DynamicDatabaseConnectionFactory) {
            ((DynamicDatabaseConnectionFactory) connectionFactory).setCurrentDatabase(db);
        }
    }

    /**
     * 獲取當(dāng)前的數(shù)據(jù)庫編號
     */
    public int getCurrentDatabase() {
        if (connectionFactory instanceof DynamicDatabaseConnectionFactory) {
            return ((DynamicDatabaseConnectionFactory) connectionFactory).getCurrentDatabase();
        }
        throw new IllegalStateException("The RedisConnectionFactory is not of type DynamicDatabaseConnectionFactory");
    }

    public RedisConnectionFactory getConnectionFactory() {
        return this.connectionFactory;
    }

}

記錄當(dāng)前線程數(shù)據(jù)庫 db,并切換至目標(biāo) db;

  • 方法執(zhí)行完畢后自動切換回原 db,避免影響線程后續(xù)邏輯;
  • DynamicDatabaseConnectionFactory

重寫了 getConnection() 方法,確保每次連接池獲取連接時都執(zhí)行 select(db),真正實現(xiàn)動態(tài)切庫;

4.RedisConfig配置

import com.zrr.platform.reids.support.DynamicDatabaseConnectionFactory;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
 * @author zrr
 */
@Configuration
public class RedisConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.database:0}")
    private int defaultDatabase;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName(host);
        config.setPort(port);
        config.setPassword(password);
        // 默認(rèn)數(shù)據(jù)庫
        config.setDatabase(defaultDatabase);

        LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
                .poolConfig(new GenericObjectPoolConfig())
                .build();


        DynamicDatabaseConnectionFactory factory = new DynamicDatabaseConnectionFactory(config, clientConfig);
        //關(guān)鍵:關(guān)閉共享連接,支持 select(db)
        factory.setShareNativeConnection(false);
        return factory;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }


}

5.RedisUtil工具類

import com.zrr.platform.reids.support.RedisDatabaseSwitcher;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author zrr
 */
@Component
@RequiredArgsConstructor
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private RedisDatabaseSwitcher switcher;

    // ================== String 操作 ==================

    /**
     * 設(shè)置緩存
     */
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 設(shè)置帶過期時間的緩存
     */
    public void set(String key, Object value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    public void set(String key, Object value, long timeout, TimeUnit unit, int db) {
        int originalDb = switcher.getCurrentDatabase();
        try {
            // 切換到目標(biāo)數(shù)據(jù)庫
            switcher.switchDatabase(db);
            // 設(shè)置值
            set(key, value, timeout, unit);
        } finally {
            // 恢復(fù)原始數(shù)據(jù)庫
            switcher.switchDatabase(originalDb);
        }
    }
    /**
     * 獲取緩存并轉(zhuǎn)換為指定類型
     */
    @SuppressWarnings("unchecked")
    public <T> T get(String key, Class<T> clazz) {
        Object val = redisTemplate.opsForValue().get(key);
        return (!clazz.isInstance(val)) ? null : (T) val;
    }

    /**
     * 從指定 Redis 數(shù)據(jù)庫獲取緩存
     */
    public <T> T get(String key, Class<T> clazz, int db) {
        int original = switcher.getCurrentDatabase();
        try {
            switcher.switchDatabase(db);
            return get(key, clazz);
        } finally {
            switcher.switchDatabase(original);
        }
    }

    /**
     * 刪除緩存
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 判斷緩存是否存在
     */
    public boolean hasKey(String key) {
        Boolean result = redisTemplate.hasKey(key);
        return result != null && result;
    }

    // ================== Hash 操作 ==================

    public void hSet(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }

    public Object hGet(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    public void hDelete(String key, String field) {
        redisTemplate.opsForHash().delete(key, field);
    }

    // ================== List 操作 ==================

    public void lPush(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }

    public Object lPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    public Object rPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    // ================== Set 操作 ==================

    public void sAdd(String key, Object... values) {
        redisTemplate.opsForSet().add(key, values);
    }

    public Set<Object> sMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    // ================== 分布式鎖 ==================

    private static final String UNLOCK_LUA =
            "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                    "   return redis.call('del', KEYS[1]) " +
                    "else " +
                    "   return 0 " +
                    "end";

    /**
     * 嘗試加鎖(默認(rèn) Redis DB)
     */
    public boolean tryLock(String key, String uniqueId, long expireSeconds) {
        Boolean success = redisTemplate.opsForValue().setIfAbsent(key, uniqueId, Duration.ofSeconds(expireSeconds));
        return Boolean.TRUE.equals(success);
    }

    /**
     * 嘗試加鎖(指定 Redis DB)
     */
    public boolean tryLock(String key, String uniqueId, long expireSeconds, int db) {
        int originalDb = switcher.getCurrentDatabase();
        try {
            switcher.switchDatabase(db);
            return tryLock(key, uniqueId, expireSeconds);
        } finally {
            switcher.switchDatabase(originalDb);
        }
    }

    /**
     * 解鎖(默認(rèn) Redis DB)
     */
    public boolean unlock(String key, String uniqueId) {
        Long result = redisTemplate.execute(
                new DefaultRedisScript<>(UNLOCK_LUA, Long.class),
                Collections.singletonList(key),
                uniqueId
        );
        return result != null && result == 1L;
    }

    /**
     * 解鎖(指定 Redis DB)
     */
    public boolean unlock(String key, String uniqueId, int db) {
        int originalDb = switcher.getCurrentDatabase();
        try {
            switcher.switchDatabase(db);
            return unlock(key, uniqueId);
        } finally {
            switcher.switchDatabase(originalDb);
        }
    }
}

線程安全說明

  • 使用ThreadLocal 保存每個線程的 Redis DB index;
  • 每次切換數(shù)據(jù)庫僅在當(dāng)前線程內(nèi)有效;
  • 切面自動恢復(fù)原數(shù)據(jù)庫,確保線程復(fù)用時數(shù)據(jù)一致性。

配置參考

application.yml:

spring:
  redis:
    host: localhost
    port: 6379
    password: your_password
    database: 0 # 默認(rèn)數(shù)據(jù)庫,可通過注解/參數(shù)動態(tài)切換

示例:完整流程

import com.zll.platform.reids.annotation.SwitchDatabase;
import com.zll.platform.reids.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 緩存AOP切換庫測試類
 * @author chenxuefeng
 */
@Component
public class RedisDemoService {
    @Autowired
    private RedisUtil redisUtil;

    @SwitchDatabase(3)
    public void testWithDb3() {
        redisUtil.set("key-db3", "value3", 5, TimeUnit.MINUTES);
        String value = redisUtil.get("key-db3", String.class);
        System.out.println("從數(shù)據(jù)庫3中讀取的值: " + value);
    }

    @SwitchDatabase(4)
    public void testWithDb4() {
        redisUtil.set("key-db4", "value4", 5, TimeUnit.MINUTES);
        String value = redisUtil.get("key-db4", String.class);
        System.out.println("從數(shù)據(jù)庫4中讀取的值: " + value);
    }

    @SwitchDatabase(5)
    public void testLocked() {
        boolean locked = redisUtil.tryLock("lock:order", String.valueOf(123), 600);
        String value = redisUtil.get("lock:order", String.class);
        System.out.println("從數(shù)據(jù)庫5中讀取鎖的值: " + value);
    }
}


//測試類
@SpringBootTest(classes = ZllPlatformApplication.class)
public class RedisUtilTest {
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RedisDemoService demoService;
    @Autowired
    private RedisDatabaseSwitcher switcher;

    @Test
    public void testLocked() {
        // 簡單加鎖
        boolean locked = redisUtil.tryLock("lock:order", String.valueOf(123), 600);
        System.out.println(locked);
        // 簡單釋放鎖
        boolean unlocked = redisUtil.unlock("lock:order", String.valueOf(123));
        System.out.println(unlocked);
    }

    @Test
    public void testLocked5() {
        demoService.testLocked();
    }


    @Test
    public void testDb3() {
        // 這里走的是代理,AOP 能生效
        demoService.testWithDb3();
    }

    @Test
    @SwitchDatabase(4)
    public void testDb4() {
        demoService.testWithDb4();
    }

    @Test
    public void testStringSetAndGet5() {
        switcher.switchDatabase(5); // 切到 db5

        redisUtil.set("test", "test", 1, TimeUnit.HOURS);
        String value = redisUtil.get("test", String.class);
        System.out.println("Get from Redis (db5): " + value);
    }


    @Test
    public void testMultiDbIsolation() {
        redisUtil.set("key1", "val-db1", 10, TimeUnit.MINUTES, 1);
        redisUtil.set("key1", "val-db2", 10, TimeUnit.MINUTES, 2);

        String v1 = redisUtil.get("key1", String.class, 1);
        String v2 = redisUtil.get("key1", String.class, 2);

        System.out.println("db1 value: " + v1);
        System.out.println("db2 value: " + v2);
    }

    @Test
    public void testStringSetAndGet() {
        redisUtil.set("test", "test", 1, TimeUnit.HOURS);
        String value = redisUtil.get("test", String.class);
        System.out.println("Get from Redis: " + value);
    }

    @Test
    public void testList() {
        redisUtil.lPush("myList", "v1");
        Object val = redisUtil.lPop("myList");
        System.out.println("Pop from List: " + val);
    }

    @Test
    public void testSet() {
        redisUtil.sAdd("mySet", "a", "b", "c");
        Set<Object> members = redisUtil.sMembers("mySet");
        System.out.println("Set Members: " + members);
    }

    @Test
    public void testHash() {
        redisUtil.hSet("myHash", "field1", "value1");
        Object val = redisUtil.hGet("myHash", "field1");
        System.out.println("Hash Value: " + val);
    }

后期擴(kuò)展建議

  • 支持 Redisson 多數(shù)據(jù)源:與 RedisDatabaseSwitcher 一致管理 RedissonClient 切換;
  • ?? 整合分布式鎖:Redisson 支持看門狗機(jī)制的鎖,配合 DB 切庫也能安全使用;
  • ?? 支持緩存前綴隔離 + 庫隔離混合策略:既可多庫也能前綴隔離;
  • ?? 全局默認(rèn) db 支持:如未指定注解或參數(shù)時自動 fallback 到配置文件默認(rèn) db。

總結(jié)對比表格

對比項自動切庫方案(動態(tài) DB)單庫方案(傳統(tǒng))
開發(fā)復(fù)雜度高,需要自定義配置、切面低,開箱即用
代碼簡潔性高,一套工具支持多DB低,可能需要多個 RedisTemplate
多租戶支持強(qiáng),天然適配弱,需自定義邏輯
隔離性好,不同庫物理隔離差,需靠 key 前綴區(qū)分
性能稍低,有 AOP 切面和上下文切換高,無額外邏輯
可擴(kuò)展性強(qiáng)
兼容 Redis Cluster? 不支持(PS:后期上云時記得選擇主從模式)? 支持

到此這篇關(guān)于SpringBoot+Redis多DB動態(tài)切換幾種方式總結(jié)的文章就介紹到這了,更多相關(guān)SpringBoot Redis多DB動態(tài)切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java的LinkedHashSet解析

    Java的LinkedHashSet解析

    這篇文章主要介紹了Java的LinkedHashSet解析,Set接口的哈希表和鏈表實現(xiàn),具有可預(yù)測的迭代順序,此實現(xiàn)與 HashSet的不同之處在于它維護(hù)一個雙向鏈表,該列表貫穿其所有條目,這個鏈表定義了迭代順序,需要的朋友可以參考下
    2023-09-09
  • 關(guān)于Process的waitFor死鎖問題及解決方案

    關(guān)于Process的waitFor死鎖問題及解決方案

    這篇文章主要介紹了關(guān)于Process的waitFor死鎖問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 一些常用Java命令及應(yīng)用總結(jié)

    一些常用Java命令及應(yīng)用總結(jié)

    這篇文章主要介紹了一些常用Java命令及應(yīng)用的相關(guān)資料,詳細(xì)講解了如何使用這些命令來排查Cpu和內(nèi)存飆高的問題,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-04-04
  • Java Code Cache滿導(dǎo)致應(yīng)用性能降低問題解決

    Java Code Cache滿導(dǎo)致應(yīng)用性能降低問題解決

    這篇文章主要介紹了Java Code Cache滿導(dǎo)致應(yīng)用性能降低問題解決,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Java自定義映射resultMap定義及用法

    Java自定義映射resultMap定義及用法

    MyBatis的每一個查詢映射的返回類型都是ResultMap,當(dāng)我們提供返回類型屬性是resultType時,MyBatis會自動給我們把對應(yīng)值賦給resultType所指定對象的屬性,當(dāng)我們提供返回類型是resultMap時,將數(shù)據(jù)庫中列數(shù)據(jù)復(fù)制到對象的相應(yīng)屬性上,可以用于復(fù)制查詢,兩者不能同時用
    2022-11-11
  • mybatis?plus中如何編寫sql語句

    mybatis?plus中如何編寫sql語句

    這篇文章主要介紹了mybatis?plus中如何編寫sql語句,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • 記一次springboot中用undertow的坑

    記一次springboot中用undertow的坑

    這篇文章主要介紹了記一次springboot中用undertow的坑,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Spring 事務(wù)神器TransactionTemplate用法及避坑指南

    Spring 事務(wù)神器TransactionTemplate用法及避坑指南

    TransactionTemplate是Spring框架提供的編程式事務(wù)管理模板類,它結(jié)合了聲明式事務(wù)和編程式事務(wù)的優(yōu)點,解決了復(fù)雜場景下的事務(wù)控制問題,本文給大家介紹Spring 事務(wù)神器TransactionTemplate 全解析,感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • java中Filter過濾器處理中文亂碼的方法

    java中Filter過濾器處理中文亂碼的方法

    java中Filter過濾器處理中文亂碼的方法,需要的朋友可以參考一下
    2013-05-05
  • 基于SpringBoot實現(xiàn)抽獎活動的四種策略

    基于SpringBoot實現(xiàn)抽獎活動的四種策略

    抽獎活動是產(chǎn)品運營中常見的用戶激勵和互動手段,通過隨機(jī)性和獎勵刺激用戶參與度,提升用戶活躍度和留存率,在技術(shù)實現(xiàn)上,抽獎系統(tǒng)涉及到隨機(jī)算法、獎品分配、防作弊機(jī)制等多方面內(nèi)容,本文將介紹基于SpringBoot實現(xiàn)抽獎活動的5種策略,需要的朋友可以參考下
    2025-06-06

最新評論

建德市| 库车县| 天台县| 通河县| 龙井市| 新宁县| 漳州市| 含山县| 唐河县| 北川| 凉山| 奉化市| 潜山县| 榕江县| 上蔡县| 于都县| 大港区| 拉孜县| 连南| 延津县| 綦江县| 库车县| 会理县| 广水市| 同江市| 延川县| 稷山县| 武隆县| 深泽县| 老河口市| 开平市| 淳化县| 龙川县| 达孜县| 威宁| 仲巴县| 泊头市| 柳河县| 广平县| 沈丘县| 平定县|