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

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

 更新時(shí)間: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動(dòng)態(tài)切換幾種方式的相關(guān)資料,需要的朋友可以參考下

功能目標(biāo)

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

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

模塊組成

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

AOP 切面:攔截

@SwitchDatabase

注解,自動(dòng)切庫

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

使用方式

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

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

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

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

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

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

自動(dòng)切換實(shí)現(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ù)庫編號(hào)
}

注解會(huì)被 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ù)庫編號(hào)(需擴(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
 * 動(dòng)態(tài) Redis 數(shù)據(jù)庫連接工廠。
 * 核心原理:
 * - 使用 ThreadLocal 保存當(dāng)前線程應(yīng)使用的 Redis 數(shù)據(jù)庫編號(hào)(db index)
 * - 重寫 getConnection() 方法,在每次獲取連接時(shí)調(diào)用 select(db),實(shí)現(xiàn)動(dòng)態(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ù)庫編號(hào)
     */
    public void setCurrentDatabase(int db) {
        databaseHolder.set(db);
    }

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

    /**
     * 每次從連接池中獲取連接時(shí)都執(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ù)庫切換器
 * 封裝對(duì) 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ù)庫編號(hào)
     */
    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í)行完畢后自動(dòng)切換回原 db,避免影響線程后續(xù)邏輯;
  • DynamicDatabaseConnectionFactory

重寫了 getConnection() 方法,確保每次連接池獲取連接時(shí)都執(zhí)行 select(db),真正實(shí)現(xiàn)動(dòng)態(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è)置帶過期時(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 保存每個(gè)線程的 Redis DB index;
  • 每次切換數(shù)據(jù)庫僅在當(dāng)前線程內(nèi)有效;
  • 切面自動(dòng)恢復(fù)原數(shù)據(jù)庫,確保線程復(fù)用時(shí)數(shù)據(jù)一致性。

配置參考

application.yml:

spring:
  redis:
    host: localhost
    port: 6379
    password: your_password
    database: 0 # 默認(rèn)數(shù)據(jù)庫,可通過注解/參數(shù)動(dòng)態(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ù)時(shí)自動(dòng) fallback 到配置文件默認(rèn) db。

總結(jié)對(duì)比表格

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

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

相關(guān)文章

  • Java的LinkedHashSet解析

    Java的LinkedHashSet解析

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

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

    這篇文章主要介紹了關(guān)于Process的waitFor死鎖問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    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)用性能降低問題解決,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Java自定義映射resultMap定義及用法

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

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

    mybatis?plus中如何編寫sql語句

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

    記一次springboot中用undertow的坑

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

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

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

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

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

    基于SpringBoot實(shí)現(xiàn)抽獎(jiǎng)活動(dòng)的四種策略

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

最新評(píng)論

曲水县| 大渡口区| 仲巴县| 文昌市| 桐梓县| 武汉市| 汝州市| 荆州市| 青阳县| 福安市| 江安县| 长宁县| 砀山县| 朝阳县| 淳安县| 通化县| 合水县| 贵南县| 昭觉县| 万源市| 松江区| 司法| 陈巴尔虎旗| 瓦房店市| 秦安县| 新沂市| 桃源县| 江阴市| 尉氏县| 宣化县| 金沙县| 金秀| 开江县| 娱乐| 合川市| 格尔木市| 通河县| 金沙县| 轮台县| 浑源县| 海伦市|