第二十节:学习Redis缓存数据库实现增删改查(自学Spring boot 3.x的第五天)

news2024/11/15 13:42:28

这节记录下如何使用redis缓存数据库。

第一步:

先在服务器端安装redis,

下载地址:Releases · tporadowski/redis · GitHub。

第二步:

安装redis客户端可视化管理软件redisDesktopmanager

Redis Desktop Manager - Download

第三步:

设置redis缓存服务器的密码。

方式一(临时密码):打开redis-server.exe,输入config set requirepass 密码
方式二:永久密码,修改redis的conf文件,在conf中搜索requirepass。记得修改了以后重启(先停止再启动)redis的服务,不然不生效。

第四步:Redis Desktop Manager管理软件,测试是否能连接上Redis服务器。

​​
在这里插入图片描述

第五步:开始配置Springboot中的redis

在application-local.yml文章中


spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test123?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password:
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.zaxxer.hikari.HikariDataSource
    # 连接池
    hikari:
      minimum-idle: 10
      maximum-pool-size: 20
      idle-timeout: 300000
      max-lifetime: 1800000
      connection-timeout: 30000
      pool-name: SpringHikariCP

  data:
    redis:
      host: 127.0.0.1
      password: 123456

第六步:编写测试代码

 @PostMapping
    public void save(){
      List<Student> studentList =   studentService.list();
      stringRedisTemplate.opsForValue().set("studentList", JSONUtil.toJsonStr(studentList));
    }

    @GetMapping
    public List<Student> list(){
        List<Student> list = JSONUtil.toList(stringRedisTemplate.opsForValue().get("studentList"),Student.class);
        return list;
    }

    @PutMapping
    public void update(){
        List<Student> list = JSONUtil.toList(stringRedisTemplate.opsForValue().get("studentList"),Student.class);
        Student student1 = new Student();
        student1.setName("小王");
        student1.setAge(18);
        student1.setCreate_time(new Date());
        list.add(student1);
        stringRedisTemplate.opsForValue().set("studentList",JSONUtil.toJsonStr(list));
        return ;
    }
    @DeleteMapping
    public void delete(){
        stringRedisTemplate.delete("studentList");
    }

第七步:观察redis存储结果

在这里插入图片描述

上面记录了操作步骤。
那么接下来我们对代码进行分析。

一:redis是如何获取applocation.yml中的配置参数的?

Redis并不直接“获取”application.yml中的配置参数,而是Spring Boot框架在启动过程中读取这些配置参数,并据此配置Redis连接。应用程序通过Spring Boot提供的接口(如RedisTemplate)与Redis进行交互,而无需直接关心连接参数的配置。这种方式简化了应用程序与Redis的集成过程,提高了开发效率。

二:StringRedisTemplate类什么?

StringRedisTemplate类是Spring Data Redis提供的一个用于操作Redis的模板类。它专门用于处理Redis中的String类型数据,提供了一系列简化操作的方法,使开发者可以方便地进行数据的读取、写入和删除等操作,而无需关心底层Redis的连接和数据序列化等细节。
主要特点和用法
简化的操作方法:StringRedisTemplate封装了一系列简化的操作方法,如设置、获取、删除String类型数据等,开发者可以直接调用这些方法来进行操作,而无需关心Redis的底层实现。
与Spring集成:StringRedisTemplate是基于Spring框架提供的RedisTemplate实现的,因此可以与Spring应用程序无缝集成,通过依赖注入的方式进行使用。
支持事务:StringRedisTemplate支持在事务中进行操作,可以保证多个操作的原子性,确保数据的一致性。
支持序列化:StringRedisTemplate支持将Java对象序列化成Redis数据格式,以及将Redis数据反序列化成Java对象,方便开发者进行数据存取操作。
提供回调接口:StringRedisTemplate还提供了一些回调接口,如RedisCallback和SessionCallback,开发者可以通过这些接口来执行自定义的Redis操作,更灵活地控制Redis的使用。
构造方法
StringRedisTemplate提供了多种构造方法,通常开发者会通过配置Redis连接信息(如主机名、端口号等)来创建连接工厂,然后使用连接工厂来创建StringRedisTemplate实例。
常用操作方法
以下是一些常用的操作方法示例:

设置值:通过opsForValue().set(String key, String value)方法可以设置键值对。
获取值:通过opsForValue().get(String key)方法可以根据键获取对应的值。
删除值:通过delete(String key)方法可以删除指定的键值对。
追加字符串:通过opsForValue().append(String key, String value)方法可以向指定的键对应的字符串值的末尾追加值。
自增自减:通过opsForValue().increment(String key)和opsForValue().decrement(String key)方法可以实现字符串类型数据的自增和自减操作。
设置过期时间:在设置值时,可以指定过期时间,如opsForValue().set(String key, String value, long timeout, TimeUnit unit)方法。
注意事项
在使用StringRedisTemplate时,需要注意Redis服务器的连接信息配置,包括主机名、端口号、密码等。
对于数据类型为String的Redis操作,StringRedisTemplate提供了非常便捷的方法,但对于其他类型的数据(如Hash、List、Set等),则可能需要使用RedisTemplate或其他工具类进行操作。
在进行大量数据操作时,需要注意Redis的性能和内存使用情况,避免造成不必要的性能瓶颈或内存溢出等问题。

StringRedisTemplate 类源码

继承结构
在这里插入图片描述

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.data.redis.core;

import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializer;

public class StringRedisTemplate extends RedisTemplate<String, String> {
    public StringRedisTemplate() {
        this.setKeySerializer(RedisSerializer.string());
        this.setValueSerializer(RedisSerializer.string());
        this.setHashKeySerializer(RedisSerializer.string());
        this.setHashValueSerializer(RedisSerializer.string());
    }

    public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
        this();
        this.setConnectionFactory(connectionFactory);
        this.afterPropertiesSet();
    }

    protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
        return new DefaultStringRedisConnection(connection);
    }
}

源码分析:
StringRedisTemplate 继承自 RedisTemplate<String, String>,这意味着它是一个泛型类,其中键(Key)和值(Value)的类型都限定为 String。

StringRedisTemplate()这个构造函数通过调用 setKeySerializer、setValueSerializer、setHashKeySerializer 和 setHashValueSerializer 方法,将键、值、哈希键和哈希值的序列化器都设置为 RedisSerializer.string()。这意味着所有的键和值在存入 Redis 之前都会被转换成字符串,从 Redis 取出时也会从字符串转换回原来的类型(在这个场景下,由于本身就是字符串,所以实际上不需要转换)。

三:StringRedisTemplate和RedisTemplate区别在哪里

StringRedisTemplate和RedisTemplate在Spring框架中都是用于操作Redis数据库的模板类,但它们之间存在一些明显的区别,主要体现在以下几个方面:

1. 序列化策略

  • StringRedisTemplate:采用的是String的序列化策略,即它将Java对象序列化为字符串形式存储在Redis中,同时也将Redis中的字符串反序列化为Java对象。这种方式简化了操作,因为字符串是Redis中最基本的数据类型,支持的操作简单高效。
  • RedisTemplate:默认采用的是JDK的序列化策略(JdkSerializationRedisSerializer),即它将Java对象序列化为字节数组存储在Redis中。这种方式虽然可以处理任意类型的Java对象,但在Redis-cli中查看时可能会显示为乱码。不过,RedisTemplate的序列化策略是可以自定义的,常用的还有Jackson2JsonRedisSerializer等,可以将对象序列化为JSON字符串存储在Redis中。

2. 存储数据类型

  • StringRedisTemplate:专注于处理Redis中的String类型数据,提供了一系列简化操作String类型数据的方法。
  • RedisTemplate:是一个泛型类,可以对Redis中的任意类型数据进行操作,包括但不限于String、Hash、List、Set和Sorted Set等。它提供了丰富的API来支持这些数据类型的操作。

3. 使用范围

  • StringRedisTemplate:由于其专注于String类型数据,因此适用于存储简单的键值对、缓存数据、计数器等场景。
  • RedisTemplate:由于其泛型特性和丰富的API,适用于需要存储复杂对象或进行复杂查询的场景。如果你需要存取复杂的对象,并且不想做额外的处理,那么RedisTemplate可能是一个更好的选择。

4. 继承关系

  • StringRedisTemplate:实际上是RedisTemplate的一个子类,它在继承RedisTemplate的基础上,将泛型类型指定为了String,从而限制了其只能处理String类型的键值对。

5. 示例

假设我们有一个User对象需要存储到Redis中:

  • 使用StringRedisTemplate时,如果直接尝试存储User对象,会报错,因为StringRedisTemplate的set方法只接受String类型的键和值。我们需要先将User对象转换为String(例如通过JSON序列化),然后再存储。
  • 使用RedisTemplate时,如果采用默认的JDK序列化策略,可以直接存储User对象,但在Redis-cli中查看时会是乱码。如果采用JSON序列化策略(如Jackson2JsonRedisSerializer),则可以将User对象序列化为JSON字符串后存储,这样在Redis-cli中查看时会是可读的JSON字符串。

综上所述,StringRedisTemplate和RedisTemplate在序列化策略、存储数据类型、使用范围、继承关系等方面存在明显的区别。在选择使用哪个模板类时,需要根据具体的应用场景和需求来决定。

四:RedisTemplate源码

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
    private boolean enableTransactionSupport = false;
    private boolean exposeConnection = false;
    private boolean initialized = false;
    private boolean enableDefaultSerializer = true;
    @Nullable
    private RedisSerializer<?> defaultSerializer;
    @Nullable
    private ClassLoader classLoader;
    @Nullable
    private RedisSerializer keySerializer = null;
    @Nullable
    private RedisSerializer valueSerializer = null;
    @Nullable
    private RedisSerializer hashKeySerializer = null;
    @Nullable
    private RedisSerializer hashValueSerializer = null;
    private RedisSerializer<String> stringSerializer = RedisSerializer.string();
    @Nullable
    private ScriptExecutor<K> scriptExecutor;
    private final BoundOperationsProxyFactory boundOperations = new BoundOperationsProxyFactory();
    private final ValueOperations<K, V> valueOps = new DefaultValueOperations(this);
    private final ListOperations<K, V> listOps = new DefaultListOperations(this);
    private final SetOperations<K, V> setOps = new DefaultSetOperations(this);
    private final StreamOperations<K, ?, ?> streamOps = new DefaultStreamOperations(this, ObjectHashMapper.getSharedInstance());
    private final ZSetOperations<K, V> zSetOps = new DefaultZSetOperations(this);
    private final GeoOperations<K, V> geoOps = new DefaultGeoOperations(this);
    private final HyperLogLogOperations<K, V> hllOps = new DefaultHyperLogLogOperations(this);
    private final ClusterOperations<K, V> clusterOps = new DefaultClusterOperations(this);

    public RedisTemplate() {
    }

    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        if (this.defaultSerializer == null) {
            this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
        }

        if (this.enableDefaultSerializer) {
            if (this.keySerializer == null) {
                this.keySerializer = this.defaultSerializer;
            }

            if (this.valueSerializer == null) {
                this.valueSerializer = this.defaultSerializer;
            }

            if (this.hashKeySerializer == null) {
                this.hashKeySerializer = this.defaultSerializer;
            }

            if (this.hashValueSerializer == null) {
                this.hashValueSerializer = this.defaultSerializer;
            }
        }

        if (this.scriptExecutor == null) {
            this.scriptExecutor = new DefaultScriptExecutor(this);
        }

        this.initialized = true;
    }

    public boolean isExposeConnection() {
        return this.exposeConnection;
    }

    public void setExposeConnection(boolean exposeConnection) {
        this.exposeConnection = exposeConnection;
    }

    public boolean isEnableDefaultSerializer() {
        return this.enableDefaultSerializer;
    }

    public void setEnableDefaultSerializer(boolean enableDefaultSerializer) {
        this.enableDefaultSerializer = enableDefaultSerializer;
    }

    public void setEnableTransactionSupport(boolean enableTransactionSupport) {
        this.enableTransactionSupport = enableTransactionSupport;
    }

    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Nullable
    public RedisSerializer<?> getDefaultSerializer() {
        return this.defaultSerializer;
    }

    public void setDefaultSerializer(RedisSerializer<?> serializer) {
        this.defaultSerializer = serializer;
    }

    public void setKeySerializer(RedisSerializer<?> serializer) {
        this.keySerializer = serializer;
    }

    public RedisSerializer<?> getKeySerializer() {
        return this.keySerializer;
    }

    public void setValueSerializer(RedisSerializer<?> serializer) {
        this.valueSerializer = serializer;
    }

    public RedisSerializer<?> getValueSerializer() {
        return this.valueSerializer;
    }

    public RedisSerializer<?> getHashKeySerializer() {
        return this.hashKeySerializer;
    }

    public void setHashKeySerializer(RedisSerializer<?> hashKeySerializer) {
        this.hashKeySerializer = hashKeySerializer;
    }

    public RedisSerializer<?> getHashValueSerializer() {
        return this.hashValueSerializer;
    }

    public void setHashValueSerializer(RedisSerializer<?> hashValueSerializer) {
        this.hashValueSerializer = hashValueSerializer;
    }

    public RedisSerializer<String> getStringSerializer() {
        return this.stringSerializer;
    }

    public void setStringSerializer(RedisSerializer<String> stringSerializer) {
        this.stringSerializer = stringSerializer;
    }

    public void setScriptExecutor(ScriptExecutor<K> scriptExecutor) {
        this.scriptExecutor = scriptExecutor;
    }

    @Nullable
    public <T> T execute(RedisCallback<T> action) {
        return this.execute(action, this.isExposeConnection());
    }

    @Nullable
    public <T> T execute(RedisCallback<T> action, boolean exposeConnection) {
        return this.execute(action, exposeConnection, false);
    }

    @Nullable
    public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
        Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
        Assert.notNull(action, "Callback object must not be null");
        RedisConnectionFactory factory = this.getRequiredConnectionFactory();
        RedisConnection conn = RedisConnectionUtils.getConnection(factory, this.enableTransactionSupport);

        Object var11;
        try {
            boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
            RedisConnection connToUse = this.preProcessConnection(conn, existingConnection);
            boolean pipelineStatus = connToUse.isPipelined();
            if (pipeline && !pipelineStatus) {
                connToUse.openPipeline();
            }

            RedisConnection connToExpose = exposeConnection ? connToUse : this.createRedisConnectionProxy(connToUse);
            T result = action.doInRedis(connToExpose);
            if (pipeline && !pipelineStatus) {
                connToUse.closePipeline();
            }

            var11 = this.postProcessResult(result, connToUse, existingConnection);
        } finally {
            RedisConnectionUtils.releaseConnection(conn, factory);
        }

        return var11;
    }

    public <T> T execute(SessionCallback<T> session) {
        Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
        Assert.notNull(session, "Callback object must not be null");
        RedisConnectionFactory factory = this.getRequiredConnectionFactory();
        RedisConnectionUtils.bindConnection(factory, this.enableTransactionSupport);

        Object var3;
        try {
            var3 = session.execute(this);
        } finally {
            RedisConnectionUtils.unbindConnection(factory);
        }

        return var3;
    }

    public List<Object> executePipelined(SessionCallback<?> session) {
        return this.executePipelined(session, this.valueSerializer);
    }

    public List<Object> executePipelined(SessionCallback<?> session, @Nullable RedisSerializer<?> resultSerializer) {
        Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
        Assert.notNull(session, "Callback object must not be null");
        RedisConnectionFactory factory = this.getRequiredConnectionFactory();
        RedisConnectionUtils.bindConnection(factory, this.enableTransactionSupport);

        List var4;
        try {
            var4 = (List)this.execute((connection) -> {
                connection.openPipeline();
                boolean pipelinedClosed = false;

                List var7;
                try {
                    Object result = this.executeSession(session);
                    if (result != null) {
                        throw new InvalidDataAccessApiUsageException("Callback cannot return a non-null value as it gets overwritten by the pipeline");
                    }

                    List<Object> closePipeline = connection.closePipeline();
                    pipelinedClosed = true;
                    var7 = this.deserializeMixedResults(closePipeline, resultSerializer, this.hashKeySerializer, this.hashValueSerializer);
                } finally {
                    if (!pipelinedClosed) {
                        connection.closePipeline();
                    }

                }

                return var7;
            });
        } finally {
            RedisConnectionUtils.unbindConnection(factory);
        }

        return var4;
    }

    public List<Object> executePipelined(RedisCallback<?> action) {
        return this.executePipelined(action, this.valueSerializer);
    }

    public List<Object> executePipelined(RedisCallback<?> action, @Nullable RedisSerializer<?> resultSerializer) {
        return (List)this.execute((connection) -> {
            connection.openPipeline();
            boolean pipelinedClosed = false;

            List var7;
            try {
                Object result = action.doInRedis(connection);
                if (result != null) {
                    throw new InvalidDataAccessApiUsageException("Callback cannot return a non-null value as it gets overwritten by the pipeline");
                }

                List<Object> closePipeline = connection.closePipeline();
                pipelinedClosed = true;
                var7 = this.deserializeMixedResults(closePipeline, resultSerializer, this.hashKeySerializer, this.hashValueSerializer);
            } finally {
                if (!pipelinedClosed) {
                    connection.closePipeline();
                }

            }

            return var7;
        });
    }

    public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) {
        return this.scriptExecutor.execute(script, keys, args);
    }

    public <T> T execute(RedisScript<T> script, RedisSerializer<?> argsSerializer, RedisSerializer<T> resultSerializer, List<K> keys, Object... args) {
        return this.scriptExecutor.execute(script, argsSerializer, resultSerializer, keys, args);
    }

    public <T extends Closeable> T executeWithStickyConnection(RedisCallback<T> callback) {
        Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
        Assert.notNull(callback, "Callback object must not be null");
        RedisConnectionFactory factory = this.getRequiredConnectionFactory();
        RedisConnection connection = this.preProcessConnection(RedisConnectionUtils.doGetConnection(factory, true, false, false), false);
        return (Closeable)callback.doInRedis(connection);
    }

    private Object executeSession(SessionCallback<?> session) {
        return session.execute(this);
    }

    protected RedisConnection createRedisConnectionProxy(RedisConnection connection) {
        Class<?>[] ifcs = ClassUtils.getAllInterfacesForClass(connection.getClass(), this.getClass().getClassLoader());
        return (RedisConnection)Proxy.newProxyInstance(connection.getClass().getClassLoader(), ifcs, new CloseSuppressingInvocationHandler(connection));
    }

    protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
        return connection;
    }

    @Nullable
    protected <T> T postProcessResult(@Nullable T result, RedisConnection conn, boolean existingConnection) {
        return result;
    }

    public Boolean copy(K source, K target, boolean replace) {
        byte[] sourceKey = this.rawKey(source);
        byte[] targetKey = this.rawKey(target);
        return (Boolean)this.doWithKeys((connection) -> {
            return connection.copy(sourceKey, targetKey, replace);
        });
    }

    public Boolean hasKey(K key) {
        byte[] rawKey = this.rawKey(key);
        return (Boolean)this.doWithKeys((connection) -> {
            return connection.exists(rawKey);
        });
    }

    public Long countExistingKeys(Collection<K> keys) {
        Assert.notNull(keys, "Keys must not be null");
        byte[][] rawKeys = this.rawKeys(keys);
        return (Long)this.doWithKeys((connection) -> {
            return connection.exists(rawKeys);
        });
    }

    public Boolean delete(K key) {
        byte[] rawKey = this.rawKey(key);
        Long result = (Long)this.doWithKeys((connection) -> {
            return connection.del(new byte[][]{rawKey});
        });
        return result != null && result.intValue() == 1;
    }

    public Long delete(Collection<K> keys) {
        if (CollectionUtils.isEmpty(keys)) {
            return 0L;
        } else {
            byte[][] rawKeys = this.rawKeys(keys);
            return (Long)this.doWithKeys((connection) -> {
                return connection.del(rawKeys);
            });
        }
    }

    public Boolean unlink(K key) {
        byte[] rawKey = this.rawKey(key);
        Long result = (Long)this.doWithKeys((connection) -> {
            return connection.unlink(new byte[][]{rawKey});
        });
        return result != null && result.intValue() == 1;
    }

    public Long unlink(Collection<K> keys) {
        if (CollectionUtils.isEmpty(keys)) {
            return 0L;
        } else {
            byte[][] rawKeys = this.rawKeys(keys);
            return (Long)this.doWithKeys((connection) -> {
                return connection.unlink(rawKeys);
            });
        }
    }

    public DataType type(K key) {
        byte[] rawKey = this.rawKey(key);
        return (DataType)this.doWithKeys((connection) -> {
            return connection.type(rawKey);
        });
    }

    public Set<K> keys(K pattern) {
        byte[] rawKey = this.rawKey(pattern);
        Set<byte[]> rawKeys = (Set)this.doWithKeys((connection) -> {
            return connection.keys(rawKey);
        });
        return this.keySerializer != null ? SerializationUtils.deserialize(rawKeys, this.keySerializer) : rawKeys;
    }

    public Cursor<K> scan(ScanOptions options) {
        Assert.notNull(options, "ScanOptions must not be null");
        return (Cursor)this.executeWithStickyConnection((connection) -> {
            return new ConvertingCursor(connection.scan(options), this::deserializeKey);
        });
    }

    public K randomKey() {
        byte[] rawKey = (byte[])this.doWithKeys(RedisKeyCommands::randomKey);
        return this.deserializeKey(rawKey);
    }

    public void rename(K oldKey, K newKey) {
        byte[] rawOldKey = this.rawKey(oldKey);
        byte[] rawNewKey = this.rawKey(newKey);
        this.doWithKeys((connection) -> {
            connection.rename(rawOldKey, rawNewKey);
            return null;
        });
    }

    public Boolean renameIfAbsent(K oldKey, K newKey) {
        byte[] rawOldKey = this.rawKey(oldKey);
        byte[] rawNewKey = this.rawKey(newKey);
        return (Boolean)this.doWithKeys((connection) -> {
            return connection.renameNX(rawOldKey, rawNewKey);
        });
    }

    public Boolean expire(K key, final long timeout, final TimeUnit unit) {
        byte[] rawKey = this.rawKey(key);
        long rawTimeout = TimeoutUtils.toMillis(timeout, unit);
        return (Boolean)this.doWithKeys((connection) -> {
            try {
                return connection.pExpire(rawKey, rawTimeout);
            } catch (Exception var8) {
                return connection.expire(rawKey, TimeoutUtils.toSeconds(timeout, unit));
            }
        });
    }

    public Boolean expireAt(K key, final Date date) {
        byte[] rawKey = this.rawKey(key);
        return (Boolean)this.doWithKeys((connection) -> {
            try {
                return connection.pExpireAt(rawKey, date.getTime());
            } catch (Exception var4) {
                return connection.expireAt(rawKey, date.getTime() / 1000L);
            }
        });
    }

    public Boolean persist(K key) {
        byte[] rawKey = this.rawKey(key);
        return (Boolean)this.doWithKeys((connection) -> {
            return connection.persist(rawKey);
        });
    }

    public Long getExpire(K key) {
        byte[] rawKey = this.rawKey(key);
        return (Long)this.doWithKeys((connection) -> {
            return connection.ttl(rawKey);
        });
    }

    public Long getExpire(K key, TimeUnit timeUnit) {
        byte[] rawKey = this.rawKey(key);
        return (Long)this.doWithKeys((connection) -> {
            try {
                return connection.pTtl(rawKey, timeUnit);
            } catch (Exception var4) {
                return connection.ttl(rawKey, timeUnit);
            }
        });
    }

    public Boolean move(K key, final int dbIndex) {
        byte[] rawKey = this.rawKey(key);
        return (Boolean)this.doWithKeys((connection) -> {
            return connection.move(rawKey, dbIndex);
        });
    }

    public byte[] dump(K key) {
        byte[] rawKey = this.rawKey(key);
        return (byte[])this.doWithKeys((connection) -> {
            return connection.dump(rawKey);
        });
    }

    public void restore(K key, byte[] value, long timeToLive, TimeUnit unit, boolean replace) {
        byte[] rawKey = this.rawKey(key);
        long rawTimeout = TimeoutUtils.toMillis(timeToLive, unit);
        this.doWithKeys((connection) -> {
            connection.restore(rawKey, rawTimeout, value, replace);
            return null;
        });
    }

    @Nullable
    private <T> T doWithKeys(Function<RedisKeyCommands, T> action) {
        return this.execute((connection) -> {
            return action.apply(connection.keyCommands());
        }, true);
    }

    public List<V> sort(SortQuery<K> query) {
        return this.sort(query, this.valueSerializer);
    }

    public <T> List<T> sort(SortQuery<K> query, @Nullable RedisSerializer<T> resultSerializer) {
        byte[] rawKey = this.rawKey(query.getKey());
        SortParameters params = QueryUtils.convertQuery(query, this.stringSerializer);
        List<byte[]> vals = (List)this.doWithKeys((connection) -> {
            return connection.sort(rawKey, params);
        });
        return SerializationUtils.deserialize(vals, resultSerializer);
    }

    public <T> List<T> sort(SortQuery<K> query, BulkMapper<T, V> bulkMapper) {
        return this.sort(query, bulkMapper, this.valueSerializer);
    }

    public <T, S> List<T> sort(SortQuery<K> query, BulkMapper<T, S> bulkMapper, @Nullable RedisSerializer<S> resultSerializer) {
        List<S> values = this.sort(query, resultSerializer);
        if (values != null && !values.isEmpty()) {
            int bulkSize = query.getGetPattern().size();
            List<T> result = new ArrayList(values.size() / bulkSize + 1);
            List<S> bulk = new ArrayList(bulkSize);
            Iterator var8 = values.iterator();

            while(var8.hasNext()) {
                S s = var8.next();
                bulk.add(s);
                if (bulk.size() == bulkSize) {
                    result.add(bulkMapper.mapBulk(Collections.unmodifiableList(bulk)));
                    bulk = new ArrayList(bulkSize);
                }
            }

            return result;
        } else {
            return Collections.emptyList();
        }
    }

    public Long sort(SortQuery<K> query, K storeKey) {
        byte[] rawStoreKey = this.rawKey(storeKey);
        byte[] rawKey = this.rawKey(query.getKey());
        SortParameters params = QueryUtils.convertQuery(query, this.stringSerializer);
        return (Long)this.doWithKeys((connection) -> {
            return connection.sort(rawKey, params, rawStoreKey);
        });
    }

    public void watch(K key) {
        byte[] rawKey = this.rawKey(key);
        this.executeWithoutResult((connection) -> {
            connection.watch(new byte[][]{rawKey});
        });
    }

    public void watch(Collection<K> keys) {
        byte[][] rawKeys = this.rawKeys(keys);
        this.executeWithoutResult((connection) -> {
            connection.watch(rawKeys);
        });
    }

    public void unwatch() {
        this.executeWithoutResult(RedisTxCommands::unwatch);
    }

    public void multi() {
        this.executeWithoutResult(RedisTxCommands::multi);
    }

    public void discard() {
        this.executeWithoutResult(RedisTxCommands::discard);
    }

    public List<Object> exec() {
        List<Object> results = this.execRaw();
        return this.getRequiredConnectionFactory().getConvertPipelineAndTxResults() ? this.deserializeMixedResults(results, this.valueSerializer, this.hashKeySerializer, this.hashValueSerializer) : results;
    }

    public List<Object> exec(RedisSerializer<?> valueSerializer) {
        return this.deserializeMixedResults(this.execRaw(), valueSerializer, valueSerializer, valueSerializer);
    }

    protected List<Object> execRaw() {
        List<Object> raw = (List)this.execute(RedisTxCommands::exec);
        return raw == null ? Collections.emptyList() : raw;
    }

    public List<RedisClientInfo> getClientList() {
        return (List)this.execute(RedisServerCommands::getClientList);
    }

    public void killClient(String host, int port) {
        this.executeWithoutResult((connection) -> {
            connection.killClient(host, port);
        });
    }

    public void replicaOf(String host, int port) {
        this.executeWithoutResult((connection) -> {
            connection.replicaOf(host, port);
        });
    }

    public void replicaOfNoOne() {
        this.executeWithoutResult(RedisServerCommands::replicaOfNoOne);
    }

    public Long convertAndSend(String channel, Object message) {
        Assert.hasText(channel, "a non-empty channel is required");
        byte[] rawChannel = this.rawString(channel);
        byte[] rawMessage = this.rawValue(message);
        return (Long)this.execute((connection) -> {
            return connection.publish(rawChannel, rawMessage);
        }, true);
    }

    private void executeWithoutResult(Consumer<RedisConnection> action) {
        this.execute((it) -> {
            action.accept(it);
            return null;
        }, true);
    }

    public ClusterOperations<K, V> opsForCluster() {
        return this.clusterOps;
    }

    public GeoOperations<K, V> opsForGeo() {
        return this.geoOps;
    }

    public BoundGeoOperations<K, V> boundGeoOps(K key) {
        return (BoundGeoOperations)this.boundOperations.createProxy(BoundGeoOperations.class, key, DataType.ZSET, this, RedisOperations::opsForGeo);
    }

    public <HK, HV> BoundHashOperations<K, HK, HV> boundHashOps(K key) {
        return (BoundHashOperations)this.boundOperations.createProxy(BoundHashOperations.class, key, DataType.HASH, this, (it) -> {
            return it.opsForHash();
        });
    }

    public <HK, HV> HashOperations<K, HK, HV> opsForHash() {
        return new DefaultHashOperations(this);
    }

    public HyperLogLogOperations<K, V> opsForHyperLogLog() {
        return this.hllOps;
    }

    public ListOperations<K, V> opsForList() {
        return this.listOps;
    }

    public BoundListOperations<K, V> boundListOps(K key) {
        return (BoundListOperations)this.boundOperations.createProxy(BoundListOperations.class, key, DataType.LIST, this, RedisOperations::opsForList);
    }

    public BoundSetOperations<K, V> boundSetOps(K key) {
        return (BoundSetOperations)this.boundOperations.createProxy(BoundSetOperations.class, key, DataType.SET, this, RedisOperations::opsForSet);
    }

    public SetOperations<K, V> opsForSet() {
        return this.setOps;
    }

    public <HK, HV> StreamOperations<K, HK, HV> opsForStream() {
        return this.streamOps;
    }

    public <HK, HV> StreamOperations<K, HK, HV> opsForStream(HashMapper<? super K, ? super HK, ? super HV> hashMapper) {
        return new DefaultStreamOperations(this, hashMapper);
    }

    public <HK, HV> BoundStreamOperations<K, HK, HV> boundStreamOps(K key) {
        return (BoundStreamOperations)this.boundOperations.createProxy(BoundStreamOperations.class, key, DataType.STREAM, this, (it) -> {
            return this.opsForStream();
        });
    }

    public BoundValueOperations<K, V> boundValueOps(K key) {
        return (BoundValueOperations)this.boundOperations.createProxy(BoundValueOperations.class, key, DataType.STRING, this, RedisOperations::opsForValue);
    }

    public ValueOperations<K, V> opsForValue() {
        return this.valueOps;
    }

    public BoundZSetOperations<K, V> boundZSetOps(K key) {
        return (BoundZSetOperations)this.boundOperations.createProxy(BoundZSetOperations.class, key, DataType.ZSET, this, RedisOperations::opsForZSet);
    }

    public ZSetOperations<K, V> opsForZSet() {
        return this.zSetOps;
    }

    private byte[] rawKey(Object key) {
        Assert.notNull(key, "non null key required");
        if (this.keySerializer == null && key instanceof byte[] bytes) {
            return bytes;
        } else {
            return this.keySerializer.serialize(key);
        }
    }

    private byte[] rawString(String key) {
        return this.stringSerializer.serialize(key);
    }

    private byte[] rawValue(Object value) {
        if (this.valueSerializer == null && value instanceof byte[] bytes) {
            return bytes;
        } else {
            return this.valueSerializer.serialize(value);
        }
    }

    private byte[][] rawKeys(Collection<K> keys) {
        byte[][] rawKeys = new byte[keys.size()][];
        int i = 0;

        Object key;
        for(Iterator var4 = keys.iterator(); var4.hasNext(); rawKeys[i++] = this.rawKey(key)) {
            key = var4.next();
        }

        return rawKeys;
    }

    private K deserializeKey(byte[] value) {
        return this.keySerializer != null ? this.keySerializer.deserialize(value) : value;
    }

    @Nullable
    private List<Object> deserializeMixedResults(@Nullable List<Object> rawValues, @Nullable RedisSerializer valueSerializer, @Nullable RedisSerializer hashKeySerializer, @Nullable RedisSerializer hashValueSerializer) {
        if (rawValues == null) {
            return null;
        } else {
            List<Object> values = new ArrayList();
            Iterator var6 = rawValues.iterator();

            while(true) {
                while(var6.hasNext()) {
                    Object rawValue = var6.next();
                    if (rawValue instanceof byte[]) {
                        byte[] bytes = (byte[])rawValue;
                        if (valueSerializer != null) {
                            values.add(valueSerializer.deserialize(bytes));
                            continue;
                        }
                    }

                    if (rawValue instanceof List) {
                        List list = (List)rawValue;
                        values.add(this.deserializeMixedResults(list, valueSerializer, hashKeySerializer, hashValueSerializer));
                    } else {
                        if (rawValue instanceof Set) {
                            Set set = (Set)rawValue;
                            if (!set.isEmpty()) {
                                values.add(this.deserializeSet(set, valueSerializer));
                                continue;
                            }
                        }

                        if (rawValue instanceof Map) {
                            Map map = (Map)rawValue;
                            if (!map.isEmpty() && map.values().iterator().next() instanceof byte[]) {
                                values.add(SerializationUtils.deserialize(map, hashKeySerializer, hashValueSerializer));
                                continue;
                            }
                        }

                        values.add(rawValue);
                    }
                }

                return values;
            }
        }
    }

    private Set<?> deserializeSet(Set rawSet, @Nullable RedisSerializer valueSerializer) {
        if (rawSet.isEmpty()) {
            return rawSet;
        } else {
            Object setValue = rawSet.iterator().next();
            if (setValue instanceof byte[] && valueSerializer != null) {
                return SerializationUtils.deserialize(rawSet, valueSerializer);
            } else {
                return setValue instanceof Tuple ? this.convertTupleValues(rawSet, valueSerializer) : rawSet;
            }
        }
    }

    private Set<ZSetOperations.TypedTuple<V>> convertTupleValues(Set<Tuple> rawValues, @Nullable RedisSerializer valueSerializer) {
        Set<ZSetOperations.TypedTuple<V>> set = new LinkedHashSet(rawValues.size());

        Tuple rawValue;
        Object value;
        for(Iterator var4 = rawValues.iterator(); var4.hasNext(); set.add(new DefaultTypedTuple(value, rawValue.getScore()))) {
            rawValue = (Tuple)var4.next();
            value = rawValue.getValue();
            if (valueSerializer != null) {
                value = valueSerializer.deserialize(rawValue.getValue());
            }
        }

        return set;
    }
}

继承结构
在这里插入图片描述
很明显StringRedisTemplate和RedisTemplate是父类和子类关系。

在上面源码里看到,StringRedisTemplate并无重写RedisTemplate的任何方法。所以拥有RedisTemplate的所有方法。

五:StringRedisTemplate的主要方法有哪些?怎么设置缓存过期时间?

StringRedisTemplate是Spring Framework提供的一个用于操作Redis数据库中String类型数据的模板类。它基于RedisTemplate实现,并专注于处理Redis中的String类型数据,提供了一系列简化操作的方法。以下是StringRedisTemplate的一些常用方法讲解:

1. 字符串操作

设置和获取值
  • opsForValue().set(String key, String value):向Redis中存入一个字符串类型的键值对。
  • opsForValue().get(Object key):根据键获取对应的值。
设置带有过期时间的值
  • opsForValue().set(String key, String value, long timeout, TimeUnit unit):向Redis中存入一个字符串类型的键值对,并设置过期时间。timeout是过期时长,unit是时间单位(如秒、分钟等)。
递增和递减
  • opsForValue().increment(String key):将存储在键中的数字值增加1。
  • opsForValue().increment(String key, long delta):将存储在键中的数字值增加指定的增量值delta
  • opsForValue().decrement(String key)opsForValue().decrement(String key, long delta):与递增操作类似,但用于减少数字值。

2. 哈希操作

  • opsForHash().put(String key, Object hashKey, Object value):向哈希表中添加一个字段。
  • opsForHash().entries(String key):获取存储在哈希表中指定键的所有字段和值。

3. 列表操作

  • opsForList().leftPush(String key, String value):将一个或多个值插入到列表头部。
  • opsForList().rightPush(String key, String value):将一个或多个值插入到列表尾部。
  • opsForList().range(String key, long start, long end):通过索引区间返回列表中的一部分元素。

4. 集合操作

  • opsForSet().add(String key, String... values):向集合添加一个或多个成员。
  • opsForSet().members(String key):返回集合中的所有成员。

5. 有序集合操作

  • opsForZSet().add(String key, double score, String value):将一个或多个成员及其分数加入到有序集合中。
  • opsForZSet().range(String key, long start, long end):通过索引区间返回有序集合中指定区间的成员。

6. 其他常用方法

  • delete(String key, Object... keys):根据提供的键删除缓存中的数据。
  • hasKey(String key):检查键是否存在。
  • getExpire(String key, TimeUnit unit):获取键的剩余生存时间,并转换成指定的时间单位。

注意事项

  • 使用StringRedisTemplate时,通常不需要额外配置序列化器,因为它默认使用String序列化策略。但如果需要存储复杂对象,可能需要自定义序列化器。
  • StringRedisTemplate提供的方法非常丰富,这里只列举了一些常用方法。更多方法可以通过查看官方文档或源码来了解。

总的来说,StringRedisTemplate是Spring Data Redis中一个非常实用的工具类,它简化了Redis的操作,使开发者能够更轻松地将Redis集成到Spring应用程序中。

六:opsForSet()和opsForZSet()有什么区别?

opsForSet()opsForZSet()RedisTemplate类中提供的两个方法,它们分别用于操作Redis中的SET和ZSET(Sorted Set)数据结构。以下是这两个方法的主要区别:

1. 数据结构特性

  • opsForSet():用于操作SET数据结构。SET是一个无序的、不包含重复元素的字符串集合。SET中的元素没有特定的顺序,且每个元素都是唯一的。
  • opsForZSet():用于操作ZSET数据结构。ZSET是一个有序的、不包含重复元素的字符串集合,每个元素都会关联一个分数(score),Redis会根据这个分数对元素进行排序。

2. 支持的操作

  • opsForSet():支持的操作包括添加元素(SADD)、删除元素(SREM)、判断元素是否存在(SISMEMBER)、获取集合中的所有元素(SMEMBERS)、获取集合大小(SCARD)等。SET主要用于存储不需要排序的、唯一的元素集合。
  • opsForZSet():除了支持类似SET的添加(ZADD)、删除(ZREM)和查找(ZSCORE)操作外,还支持根据分数范围查询元素(ZRANGEBYSCORE、ZREVRANGEBYSCORE)、获取元素的排名(ZRANK、ZREVRANK)、计算分数范围内的元素数量(ZCOUNT)等高级功能。ZSET主要用于需要根据分数进行排序或统计的场景。

3. 排序与分数

  • opsForSet():由于SET是无序的,因此不支持根据任何特定顺序对元素进行排序或检索。
  • opsForZSet():ZSET中的元素会根据关联的分数进行排序。可以通过分数范围或排名来检索元素,这使得ZSET非常适合用于实现排行榜、评分系统等需要排序或统计的场景。

4. 性能考虑

  • opsForSet():由于SET是无序的,且每个元素都是唯一的,因此其操作时间复杂度一般为O(1),即常数时间复杂度,这使得它在处理大量数据时具有较高的效率。
  • opsForZSet():虽然ZSET提供了排序功能,但其操作时间复杂度一般为O(logN),其中N是集合中元素的数量。这意味着随着集合大小的增加,操作所需的时间也会相应增加,但仍然保持较高的效率。

综上所述,opsForSet()opsForZSet()的主要区别在于它们操作的数据结构特性、支持的操作、排序与分数以及性能考虑等方面。在选择使用哪个方法时,应根据实际需求进行权衡和选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2151368.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GAMES101(13节Ray Tracing)

Ray Tracing 基本原理&#xff1a; 我们知道为什么会看到物体的颜色&#xff0c;因为光线照射物体&#xff0c;未被吸收的光线反射到人眼&#xff0c;因此&#xff0c;我们看到的颜色&#xff0c;就是光的一部分&#xff0c;光线追踪就是模拟这个过程 光线假设&#xff1a; …

DHCP协议原理(网络协议)

DHCP简介 定义 DHCP&#xff08;动态主机配置协议&#xff09;是一种网络管理协议&#xff0c;能够自动为局域网中的每台计算机分配IP地址及其他网络配置参数&#xff0c;包括子网掩码、默认网关和DNS服务器等。这一机制极大简化了网络管理&#xff0c;尤其在大型局域网中&am…

聊聊AUTOSAR:基于Vector MICROSAR的TC8测试开发方案

技术背景 车载以太网技术作为汽车智能化和网联化的重要组成部分&#xff0c;正逐步成为现代汽车网络架构的核心&#xff0c;已广泛应用于汽车诊断&#xff08;如OBD&#xff09;、ECU软件更新、智能座舱系统、高清摄像头环视泊车系统等多个领域。 在这个过程中&#xff0c;ET…

CSS 的元素显示模式简单学习

目录 1. 元素显示模式 1.1 概述 1.2 块元素 1.3 行元素 1.4 行内块元素 1.5 元素显示模式总结 2. 元素显示模式转换 3. 单行文字垂直居中 4. 案例演示 1. 元素显示模式 1.1 概述 1.2 块元素 1.3 行元素 1.4 行内块元素 1.5 元素显示模式总结 2. 元素显示模式转换 3. 单…

通过markdown表格批量生成格式化的word教学单元设计表格

素材&#xff1a; 模板&#xff1a; 代码&#xff1a; import pandas as pd from python_docx_replace import docx_replace,docx_get_keys from docx import Document from docxcompose.composer import Composerdef parse_markdown_tables(file_path):with open(file_path,…

DOCKER 数据库管理软件自己开发--———未来之窗行业应用跨平台架构

- 数据异地容灾服务--未来之窗智慧数据服务 DATA REMOTE DISASTER RECOVERY SERVICE -CyberWin Future Docker-数据查看 CyberWin DATA Viewer 1.docker 样式 mysqli://root:密码172.17.0.2:端口/数据库 阿雪技术观 拥抱开源与共享&#xff0c;见证科技进步奇迹&#xff0c;…

AMD小胜!锐龙7 9700X VS. i7- 14700K网游对比

一、前言&#xff1a;两款高端处理器的网游对比测试 半个月前&#xff0c;我们做了锐龙5 9600X与i5-14600K的网游帧率测试&#xff0c;结果有点意外&#xff0c;几款游戏平均下来&#xff0c;锐龙5 9600X比i5-14600K竟然强了19%之多。 今天我们将会对锐龙7 9700X和i7-14700K进行…

【高阶数据结构】二叉搜索树的插入、删除和查找(精美图解+完整代码)

&#x1f921;博客主页&#xff1a;醉竺 &#x1f970;本文专栏&#xff1a;《高阶数据结构》 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多《高阶数据结构》点击专栏链接查看&a…

【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(上)

文章目录 前言一、ArkTS基本介绍1、 ArkTS组成2、组件参数和属性2.1、区分参数和属性的含义2.2、父子组件嵌套 二、装饰器语法1.State2.Prop3.Link4.Watch5.Provide和Consume6.Observed和ObjectLink代码示例&#xff1a;示例1&#xff1a;&#xff08;不使用Observed和ObjectLi…

Windows11家庭版修改用户密码策略为永不过期。

今天有个朋友找到我说&#xff0c;他的电脑密码老是过期然后需要修改&#xff0c;让我帮忙改一下密码策略&#xff0c;改为永不过期。 下面就来操作一下吧。 这里有个小小的坑&#xff0c;就是win11的家庭版是没有 gpedit.msc的&#xff0c;也就不能直接cmd打开本地策略便器&…

【WebGis开发 - Cesium】获取视野中心点,并设置顶视图视角

引言 项目开发过程中遇到一个需求&#xff0c;通过一个按钮切换视角为顶视图。 分析了一下这个模糊的需求&#xff0c;首先没有给出切换顶视图后俯视的区域范围&#xff0c;其次没有给出俯视点的高度。 这里可以粗略的认为当前的侧俯视的角度下观看的范围即为俯视的区域范围&am…

视频美颜SDK核心功能解析:打造高效直播美颜工具方案详解

随着直播行业的迅猛发展&#xff0c;用户对于直播画质和个人形象的要求越来越高。视频美颜SDK作为一项关键技术&#xff0c;已经成为各大直播平台和短视频应用的重要组成部分。通过实时美颜技术&#xff0c;用户能够在直播过程中呈现出更加理想的形象&#xff0c;从而提升直播体…

实验一:Windows下的IIS服务器配置和管理

第一次实验隐藏关很多&#xff0c;稍不留神服务器就寄了。 实验一完成后会有联网问题&#xff0c;问题解决详见番外篇。 实验内容 任务一&#xff1a; 1、建立一个基于主机名www.study.com的站点&#xff0c;站点的主目录为C:\inetpub\wwwroot&#xff0c;给站点建立一个虚拟…

Codeforces Round 973 (Div. 2) F1. Game in Tree (Easy Version)(思维题 博弈)

题目 思路来源 乱搞ac 题解 两个人的策略是一样的&#xff0c;把1到u的路径标记&#xff0c; 如果能走旁边的链&#xff08;也就是当前点&#xff0c;刨去标记链以外的子树中最长的链&#xff09;&#xff0c; 使得对面走剩余的连通块无法比你大&#xff0c;就走旁边的链&…

业务资源管理模式语言16

示例&#xff1a; 图25 描述了PayForTheResourceTransaction 的一个实例。其中&#xff0c;“Sale”扮演“Resource Transaction”&#xff0c;“Accounts Receivable”扮演“Payment”。 图25——PayForTheResourceTransaction 模式实例 相关模式&#xff1a; PayForTheRes…

特殊类的设计与类型转换

特殊类的设计 1.请设计一个不能被拷贝的类 拷贝只会放生在两个场景中&#xff1a;拷贝构造函数以及赋值运算符重载&#xff0c;因此想要让一个类禁止拷贝&#xff0c;只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。 C98 C98是怎么设计的呢&#xff1f; class Cop…

从零开始讲DDR(3)——DDRC与DDRPYH

一、DDR的使用 在之前的文章中我们介绍了DDR的基本概念&#xff0c;但是DDR内存的操作不仅仅是简单的数据读取和写入&#xff0c;它包括许多时序要求和信号调度。为了让DDR内存有效运作&#xff0c;系统需要在逻辑层和物理层之间进行大量的协作。我们拿出一张DDR的操作简化状态…

MySQL程序

目录 MySQL程序 常用的MySQL的程序 mysqld程序 mysql客户端 客户端命令的常用的选项 配置文件 配置文件语法 MySQL客户端命令 ​编辑 .sql 文件中执行SQL语句 mysqlcheck &#xff08;表维护程序&#xff09; Mysqldump&#xff08;数据库备份程序&#xff09; mysql…

单片机项目合集列表——Excel合集列表目录查阅(持续更新)

阿齐Archie《单片机项目合集》专栏项目 为方便查找本专栏的项目&#xff0c;特整理Excel合集列表供查阅&#xff08;可搜索或按系列查找&#xff09; 持续更新链接如下&#xff1a; 阿齐单片机项目合集 (kdocs.cn)https://www.kdocs.cn/l/cmrxCxJN05YN 打开链接如下Exce表所…

【开源免费】基于SpringBoot+Vue.JS网上购物商城(JAVA毕业设计)

本文项目编号 T 041 &#xff0c;文末自助获取源码 \color{red}{T041&#xff0c;文末自助获取源码} T041&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析5.4 用例设计 六、核…