[TOC]

前言

有时候咱们须要疾速启动一些我的项目,然而环境往往折腾了良久,因而弄一个能够重用的疾速搭建的教程,docker几乎就是这方面的神器,Docker 是一个开源的利用容器引擎,让开发者能够打包他们的利用以及依赖包到一个可移植的镜像中,而后公布到任何风行的 Linux或Windows操作系统的机器上,也能够实现虚拟化。

本教程基于的前提条件:

  • 机器曾经装置配置好JDK1.8,并且环境变量曾经配置胜利
  • Maven曾经配置好,IDEA中我的项目应用的默认Maven也配置胜利
  • 本地机器装置好Docker
  • 顺便提一句,我用navicat作为数据库可视化操作工具

我的项目地址:https://github.com/Damaer/Dem...

我的项目目录

├── src :源代码|        ├── main|        |        ├── java|   |   |     ├── com.aphysia.springbootdemo|   |   |     |     ├── config:配置|   |   |     |     |     ├── RedisConfig:redis配置|   |   |     |     ├── constant:常量|   |   |     |     |     ├── RedisConfig:redis常量|   |   |     |     ├── controller:控制器|   |   |     |     ├── mapper:数据库操作接口|   |   |     |     ├── model:实体类|   |   |     |     ├── service:逻辑解决层,包含接口以及实现类|   |   |     |     |        ├── impl:接口实现类|   |   |     |     ├──util:工具类|   |   |     |     |        ├── RedisUtil:redis工具类|   |   |     |     ├──SpringdemoApplication:启动类|        |        ├── resource|   |   |     ├── mapper 数据库操作sql|   |   |     ├── application.yml:全局配置类|   |   |     ├── user.sql: 初始化mysql|        ├──    test: 测试类    ├── pom.xml :我的项目maven依赖关系

整体的目录如下:

搭建我的项目

1. docker装置启动mysql以及redis

1.1 装置mysql

查问mysql最新的镜像:

docker search mysql

拉取最新的mysql版本

docker pull mysql:latest

启动mysql,用户名root,明码123456

docker run -itd --name mysql-test -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql

能够通过docker ps 查看是否装置胜利

% docker psCONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS      PORTS                                                  NAMES574d30f17868   mysql     "docker-entrypoint.s…"   14 months ago   Up 2 days   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql-test

1.2 装置redis

查问redis的镜像

docker search redis

拉取redis的最新镜像

% docker pull redis:latestlatest: Pulling from library/rediseff15d958d66: Pull complete 1aca8391092b: Pull complete 06e460b3ba1b: Pull complete def49df025c0: Pull complete 646c72a19e83: Pull complete db2c789841df: Pull complete Digest: sha256:619af14d3a95c30759a1978da1b2ce375504f1af70ff9eea2a8e35febc45d747Status: Downloaded newer image for redis:latestdocker.io/library/redis:latest

docker images能够查看咱们装置了哪些镜像,能够看到其实我之前也装置过redis的镜像:

% docker imagesREPOSITORY               TAG       IMAGE ID       CREATED         SIZEredis                    latest    40c68ed3a4d2   3 days ago      113MBredis                    <none>    84c5f6e03bf0   14 months ago   104MBmysql                    latest    e1d7dc9731da   14 months ago   544MBdocker/getting-started   latest    1f32459ef038   16 months ago   26.8MB

让咱们启动一下redis的容器:

% docker run -itd --name redis-test -p 6379:6379 redis7267e14faf93a0e416c39eeaaf51705dc4b6dc3507a68733c20a2609ade6dcd6

能够看到docker外面当初有redismysql两个容器在跑了:

2. 初始化数据库

次要是创立数据库以及测试应用的数据表,初始化数据库的语句:

drop database IF EXISTS test;CREATE DATABASE test;use test;DROP TABLE IF EXISTS `user`;CREATE TABLE `user` (  `id` int(11) NOT NULL,  `name` varchar(255) DEFAULT "",  `age` int(11) DEFAULT 0,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO `user` VALUES (1, '李四', 11);INSERT INTO `user` VALUES (2, '王五', 11);

初始化数据如下:

3.创立我的项目

在IDEA中,File --> New --> Project --> Spring Initializr(抉择JDK 8):

点击Next:

抉择Web 上面的Spring WebSQL上面的 JDBC API,MybatisNoSQL下的Redis,也能够不选,间接在pom文件里本人退出即可:

<img src="https://markdownpicture.oss-cn-qingdao.aliyuncs.com/blog/20211121213058.png" style="zoom:70%;" />

一路点Next,最初Finish,创立好之后,记得更新一下Maven,装置依赖包。

4.初始化代码

4.1 全局配置文件以及启动类

全局配置文件application.yml

server:  port: 8081spring:  #数据库连贯配置  datasource:    driver-class-name: com.mysql.cj.jdbc.Driver    url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&useSSL=false    username: root    password: 123456  redis:    host: 127.0.0.1     ## redis所在的服务器IP    port: 6379    ##明码,我这里没有设置,所以不填    password:    ## 设置最大连接数,0为有限    pool:      max-active: 8      min-idle: 0      max-idle: 8      max-wait: -1#mybatis的相干配置mybatis:  #mapper配置文件  mapper-locations: classpath:mapper/*.xml  type-aliases-package: com.aphysia.spingbootdemo.model  #开启驼峰命名  configuration:    map-underscore-to-camel-case: truelogging:  level:    root: debug

启动类SpringdemoApplication:

import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.EnableAutoConfiguration;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@MapperScan("com.aphysia.springdemo.mapper")public class SpringdemoApplication {    public static void main(String[] args) {        SpringApplication.run(SpringdemoApplication.class, args);    }}

4.2 实体类

与数据库中user表对应的实体类User.java:

package com.aphysia.springdemo.model;public class User {    int id;    String name;    int age;    public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }}

4.3 Redis工具类

Redis配置类RedisConfig

package com.aphysia.springdemo.config;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;@Configurationpublic class RedisConfig {    @Autowired    private RedisTemplate redisTemplate;    @Bean    public RedisTemplate redisTemplateInit() {        //设置序列化Key的实例化对象        redisTemplate.setKeySerializer(new StringRedisSerializer());        //设置序列化Value的实例化对象        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());        return redisTemplate;    }}

Redis 常量类 RedisConstant

package com.aphysia.springdemo.constant;public class RedisConstant {    public static String ALL_USER_KEY = "allUser";}

Redis 工具类 RedisUtil:

package com.aphysia.springdemo.util;import java.util.Collection;import java.util.List;import java.util.Map;import java.util.Set;import java.util.concurrent.TimeUnit;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Component;import org.springframework.util.CollectionUtils;import javax.annotation.Resource;@Componentpublic class RedisUtil {    @Resource    private RedisTemplate<String, Object> redisTemplate;    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {        this.redisTemplate = redisTemplate;    }    /**     * 指定缓存生效工夫     *     * @param key  键     * @param time 工夫(秒)     */    public boolean expire(String key, long time) {        try {            if (time > 0) {                redisTemplate.expire(key, time, TimeUnit.SECONDS);            }            return true;        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    /**     * 依据key 获取过期工夫     *     * @param key 键 不能为null     * @return 工夫(秒) 返回0代表为永恒无效     */    public long getExpire(String key) {        return redisTemplate.getExpire(key, TimeUnit.SECONDS);    }    /**     * 判断key是否存在     *     * @param key 键     * @return true 存在 false不存在     */    public boolean hasKey(String key) {        try {            return redisTemplate.hasKey(key);        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    /**     * 删除缓存     *     * @param key 能够传一个值 或多个     */    @SuppressWarnings("unchecked")    public void del(String... key) {        if (key != null && key.length > 0) {            if (key.length == 1) {                redisTemplate.delete(key[0]);            } else {                redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));            }        }    }    //============================String=============================    /**     * 一般缓存获取     *     * @param key 键     * @return 值     */    public Object get(String key) {        return key == null ? null : redisTemplate.opsForValue().get(key);    }    /**     * 一般缓存放入     *     * @param key   键     * @param value 值     * @return true胜利 false失败     */    public boolean set(String key, Object value) {        try {            redisTemplate.opsForValue().set(key, value);            return true;        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    /**     * 一般缓存放入并设置工夫     *     * @param key   键     * @param value 值     * @param time  工夫(秒) time要大于0 如果time小于等于0 将设置无限期     * @return true胜利 false 失败     */    public boolean set(String key, Object value, long time) {        try {            if (time > 0) {                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);            } else {                set(key, value);            }            return true;        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    /**     * 递增     *     * @param key   键     * @param delta 要减少几(大于0)     * @return     */    public long incr(String key, long delta) {        if (delta < 0) {            throw new RuntimeException("递增因子必须大于0");        }        return redisTemplate.opsForValue().increment(key, delta);    }    /**     * 递加     *     * @param key   键     * @param delta 要缩小几(小于0)     * @return     */    public long decr(String key, long delta) {        if (delta < 0) {            throw new RuntimeException("递加因子必须大于0");        }        return redisTemplate.opsForValue().increment(key, -delta);    }    //================================Map=================================    /**     * HashGet     *     * @param key  键 不能为null     * @param item 项 不能为null     * @return 值     */    public Object hget(String key, String item) {        return redisTemplate.opsForHash().get(key, item);    }    /**     * 获取hashKey对应的所有键值     *     * @param key 键     * @return 对应的多个键值     */    public Map<Object, Object> hmget(String key) {        return redisTemplate.opsForHash().entries(key);    }    /**     * HashSet     *     * @param key 键     * @param map 对应多个键值     * @return true 胜利 false 失败     */    public boolean hmset(String key, Map<String, Object> map) {        try {            redisTemplate.opsForHash().putAll(key, map);            return true;        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    /**     * HashSet 并设置工夫     *     * @param key  键     * @param map  对应多个键值     * @param time 工夫(秒)     * @return true胜利 false失败     */    public boolean hmset(String key, Map<String, Object> map, long time) {        try {            redisTemplate.opsForHash().putAll(key, map);            if (time > 0) {                expire(key, time);            }            return true;        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    /**     * 向一张hash表中放入数据,如果不存在将创立     *     * @param key   键     * @param item  项     * @param value 值     * @return true 胜利 false失败     */    public boolean hset(String key, String item, Object value) {        try {            redisTemplate.opsForHash().put(key, item, value);            return true;        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    /**     * 向一张hash表中放入数据,如果不存在将创立     *     * @param key   键     * @param item  项     * @param value 值     * @param time  工夫(秒)  留神:如果已存在的hash表有工夫,这里将会替换原有的工夫     * @return true 胜利 false失败     */    public boolean hset(String key, String item, Object value, long time) {        try {            redisTemplate.opsForHash().put(key, item, value);            if (time > 0) {                expire(key, time);            }            return true;        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    /**     * 删除hash表中的值     *     * @param key  键 不能为null     * @param item 项 能够使多个 不能为null     */    public void hdel(String key, Object... item) {        redisTemplate.opsForHash().delete(key, item);    }    /**     * 判断hash表中是否有该项的值     *     * @param key  键 不能为null     * @param item 项 不能为null     * @return true 存在 false不存在     */    public boolean hHasKey(String key, String item) {        return redisTemplate.opsForHash().hasKey(key, item);    }    /**     * hash递增 如果不存在,就会创立一个 并把新增后的值返回     *     * @param key  键     * @param item 项     * @param by   要减少几(大于0)     * @return     */    public double hincr(String key, String item, double by) {        return redisTemplate.opsForHash().increment(key, item, by);    }    /**     * hash递加     *     * @param key  键     * @param item 项     * @param by   要缩小记(小于0)     * @return     */    public double hdecr(String key, String item, double by) {        return redisTemplate.opsForHash().increment(key, item, -by);    }    //============================set=============================    /**     * 依据key获取Set中的所有值     *     * @param key 键     * @return     */    public Set<Object> sGet(String key) {        try {            return redisTemplate.opsForSet().members(key);        } catch (Exception e) {            e.printStackTrace();            return null;        }    }    /**     * 依据value从一个set中查问,是否存在     *     * @param key   键     * @param value 值     * @return true 存在 false不存在     */    public boolean sHasKey(String key, Object value) {        try {            return redisTemplate.opsForSet().isMember(key, value);        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    /**     * 将数据放入set缓存     *     * @param key    键     * @param values 值 能够是多个     * @return 胜利个数     */    public long sSet(String key, Object... values) {        try {            return redisTemplate.opsForSet().add(key, values);        } catch (Exception e) {            e.printStackTrace();            return 0;        }    }    /**     * 将set数据放入缓存     *     * @param key    键     * @param time   工夫(秒)     * @param values 值 能够是多个     * @return 胜利个数     */    public long sSetAndTime(String key, long time, Object... values) {        try {            Long count = redisTemplate.opsForSet().add(key, values);            if (time > 0) expire(key, time);            return count;        } catch (Exception e) {            e.printStackTrace();            return 0;        }    }    /**     * 获取set缓存的长度     *     * @param key 键     * @return     */    public long sGetSetSize(String key) {        try {            return redisTemplate.opsForSet().size(key);        } catch (Exception e) {            e.printStackTrace();            return 0;        }    }    /**     * 移除值为value的     *     * @param key    键     * @param values 值 能够是多个     * @return 移除的个数     */    public long setRemove(String key, Object... values) {        try {            Long count = redisTemplate.opsForSet().remove(key, values);            return count;        } catch (Exception e) {            e.printStackTrace();            return 0;        }    }    //===============================list=================================    /**     * 获取list缓存的内容     *     * @param key   键     * @param start 开始     * @param end   完结  0 到 -1代表所有值     * @return     */    public List<Object> lGet(String key, long start, long end) {        try {            return redisTemplate.opsForList().range(key, start, end);        } catch (Exception e) {            e.printStackTrace();            return null;        }    }    /**     * 获取list缓存的长度     *     * @param key 键     * @return     */    public long lGetListSize(String key) {        try {            return redisTemplate.opsForList().size(key);        } catch (Exception e) {            e.printStackTrace();            return 0;        }    }    /**     * 通过索引 获取list中的值     *     * @param key   键     * @param index 索引  index>=0时, 0 表头,1 第二个元素,顺次类推;index<0时,-1,表尾,-2倒数第二个元素,顺次类推     * @return     */    public Object lGetIndex(String key, long index) {        try {            return redisTemplate.opsForList().index(key, index);        } catch (Exception e) {            e.printStackTrace();            return null;        }    }    /**     * 将list放入缓存     *     * @param key   键     * @param value 值     * @return     */    public boolean lSet(String key, Object value) {        try {            redisTemplate.opsForList().rightPush(key, value);            return true;        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    /**     * 将list放入缓存     *     * @param key   键     * @param value 值     * @param time  工夫(秒)     * @return     */    public boolean lSet(String key, Object value, long time) {        try {            redisTemplate.opsForList().rightPush(key, value);            if (time > 0) expire(key, time);            return true;        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    /**     * 将list放入缓存     *     * @param key   键     * @param value 值     * @return     */    public boolean lSet(String key, List<Object> value) {        try {            redisTemplate.opsForList().rightPushAll(key, value);            return true;        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    /**     * 将list放入缓存     *     * @param key   键     * @param value 值     * @param time  工夫(秒)     * @return     */    public boolean lSet(String key, List<Object> value, long time) {        try {            redisTemplate.opsForList().rightPushAll(key, value);            if (time > 0) expire(key, time);            return true;        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    /**     * 依据索引批改list中的某条数据     *     * @param key   键     * @param index 索引     * @param value 值     * @return     */    public boolean lUpdateIndex(String key, long index, Object value) {        try {            redisTemplate.opsForList().set(key, index, value);            return true;        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    /**     * 移除N个值为value     *     * @param key   键     * @param count 移除多少个     * @param value 值     * @return 移除的个数     */    public long lRemove(String key, long count, Object value) {        try {            Long remove = redisTemplate.opsForList().remove(key, count, value);            return remove;        } catch (Exception e) {            e.printStackTrace();            return 0;        }    }}

4.4 Mysql 数据库操作

数据库的sql文件UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="com.aphysia.springdemo.mapper.UserMapper">    <select id="getAllUsers" resultType="com.aphysia.springdemo.model.User">        SELECT * FROM user    </select>    <update id="updateUserAge" parameterType="java.lang.Integer">        update user set age=age+1 where id =#{id}    </update></mapper>

对应的mapper接口UserMapper.java

package com.aphysia.springdemo.mapper;import com.aphysia.springdemo.model.User;import java.util.List;public interface UserMapper {    List<User> getAllUsers();    int updateUserAge(Integer id);}

4.5 Service层

先定义一个操作User的接口类UserService,蕴含两个办法,查问所有的user以及更新user的年龄:

package com.aphysia.springdemo.service;import com.aphysia.springdemo.model.User;import java.util.List;public interface UserService {    public List<User> getAllUsers();    public void updateUserAge();}

接口实现类UserServiceImpl为了证实Redis可用,咱们查问所有的用户的时候,退出了Redis缓存,优先从Redis中加载数据

package com.aphysia.springdemo.service.impl;import com.aphysia.springdemo.constant.RedisConstant;import com.aphysia.springdemo.mapper.UserMapper;import com.aphysia.springdemo.model.User;import com.aphysia.springdemo.service.UserService;import com.aphysia.springdemo.util.RedisUtil;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import org.springframework.util.CollectionUtils;import javax.annotation.Resource;import java.util.List;@Service("userService")public class UserServiceImpl implements UserService {    @Resource    UserMapper userMapper;    @Autowired    RedisUtil redisUtil;    @Override    public List<User> getAllUsers() {        List<User> users = (List<User>) redisUtil.get(RedisConstant.ALL_USER_KEY);        if(CollectionUtils.isEmpty(users)){            users = userMapper.getAllUsers();            redisUtil.set(RedisConstant.ALL_USER_KEY,users);        }        return users;    }    @Override    @Transactional    public void updateUserAge() {        redisUtil.del(RedisConstant.ALL_USER_KEY);        userMapper.updateUserAge(1);        userMapper.updateUserAge(2);    }}

4.6 Controller 管制层

减少一个测试层TestController:

package com.aphysia.springdemo.controller;import com.aphysia.springdemo.model.User;import com.aphysia.springdemo.service.UserService;import com.aphysia.springdemo.util.RedisUtil;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import java.util.List;@Controllerpublic class TestController {    @Autowired    UserService userService;    @RequestMapping("/getUserList")    @ResponseBody    public List<User> getUserList() {        return userService.getAllUsers();    }    @RequestMapping("/update")    @ResponseBody    public int update() {        userService.updateUserAge();        return 1;    }}

4.7 pom依赖

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.5.6</version>        <relativePath/> <!-- lookup parent from repository -->    </parent>    <groupId>com.aphysia</groupId>    <artifactId>springdemo</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>springdemo</name>    <description>Demo project for Spring Boot</description>    <properties>        <java.version>1.8</java.version>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>        <!--mysql数据库驱动-->        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <scope>runtime</scope>        </dependency>        <!--mybatis-->        <dependency>            <groupId>org.mybatis.spring.boot</groupId>            <artifactId>mybatis-spring-boot-starter</artifactId>            <version>2.1.0</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-redis</artifactId>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>

测试

启动我的项目,输出链接:http://localhost:8081/getUser...,能够获取到所有的user:

咱们更新一下所有的用户年龄,调用http://localhost:8081/update,返回1

再次拜访http://localhost:8081/getUserList,能够看到年龄全副都变成12:

怎么晓得Redis失效了呢?最好就是debug,或者间接看控制台,咱们曾经开启了debug级别的日志:

还有一种形式,下载Redis-desktop-manager,能够间接可视化查看:

至此,一个demo我的项目就实现了,能够每次copy进去初始化应用。

【作者简介】
秦怀,公众号【秦怀杂货店】作者,技术之路不在一时,山高水长,纵使迟缓,驰而不息。集体写作方向:Java源码解析JDBCMybatisSpringredis分布式剑指OfferLeetCode等,认真写好每一篇文章,不喜爱题目党,不喜爱花里胡哨,大多写系列文章,不能保障我写的都完全正确,然而我保障所写的均通过实际或者查找材料。脱漏或者谬误之处,还望斧正。

剑指Offer全副题解PDF

2020年我写了什么?

开源编程笔记