案例2:进阶版 - SpringBoot + MySQL + Redis(企业级常用)

目录结构

springboot-demo/
├── docker-compose.yml
├── .env
└── mysql/
    └── init.sql  # MySQL初始化脚本(建库、建表、插数据)

1. .env文件

# MySQL配置
MYSQL_ROOT_PASSWORD=root123
MYSQL_DATABASE=demo_db
MYSQL_USER=demo
MYSQL_PASSWORD=Demo@123!
# 应用配置
SPRING_PORT=8080
REDIS_PORT=6379

2. mysql/init.sql(初始化脚本)

-- 创建表
USE demo_db;
CREATE TABLE IF NOT EXISTS user (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    age INT
);
-- 插入测试数据
INSERT INTO user (name, age) VALUES ('Tom', 20), ('Jerry', 18);

注意:当同时设置环境变量和挂载 init.sql 时,镜像会先执行环境变量,再执行 init.sql 里的内容

3. docker-compose.yml

volumes:
  mysql-data:
  redis-data:

卷的路径是在宿主机上面的

[root@jenkins-server ~]# cd /var/lib/docker/volumes/
version: '3.8'

# 自定义网络(服务隔离)
networks:
  demo-network:
    driver: bridge

# 数据卷(持久化MySQL/Redis数据)
volumes:
  mysql-data:
  redis-data:

services:
  # MySQL服务
  mysql:
    image: mysql:8.0
    container_name: demo-mysql
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_DATABASE=${MYSQL_DATABASE}
      - MYSQL_USER=${MYSQL_USER}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
    volumes:
      - mysql-data:/var/lib/mysql  # 持久化数据,容器删除不丢失
      - ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql  # 初始化脚本
    networks:
      - demo-network
    restart: always
    # 健康检查:确保MySQL就绪后再启动后端
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u${MYSQL_USER}", "-p${MYSQL_PASSWORD}"]
      interval: 5s
      timeout: 5s
      retries: 5

  # Redis服务
  redis:
    image: redis:alpine
    container_name: demo-redis
    command: redis-server --requirepass 123456  # 设置Redis密码
    volumes:
      - redis-data:/data
    networks:
      - demo-network
    restart: always
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 3s
      timeout: 3s
      retries: 3

  # SpringBoot后端服务(替换为自己的镜像,或用build构建)
  springboot-app:
    #image: springboot-demo:1.0  # 替换为你的SpringBoot镜像
    build: ./backend  # 若有Dockerfile,用build替代image
    container_name: demo-springboot
    ports:
      - "${SPRING_PORT}:8080"
    environment:
      # 数据库连接(用服务名作为主机名)
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/${MYSQL_DATABASE}?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
      - SPRING_DATASOURCE_USERNAME=${MYSQL_USER}
      - SPRING_DATASOURCE_PASSWORD=${MYSQL_PASSWORD}
      # Redis配置
      - SPRING_REDIS_HOST=redis
      - SPRING_REDIS_PORT=${REDIS_PORT}
      - SPRING_REDIS_PASSWORD=123456
    networks:
      - demo-network
    restart: on-failure
    # 依赖MySQL和Redis,且等待其健康检查通过
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_started

补充:SpringBoot后端服务完整代码(适配上述Compose配置)

以下代码可直接创建项目、编写,打包后配合Compose启动,完美适配案例2的MySQL、Redis配置,包含实体类、数据库操作、Redis缓存、接口测试,无需额外修改配置。

1. 项目结构(Maven)
backend/
├── src/
│   ├── main/
│   │   ├── java/com/example/demo/
│   │   │   ├── DemoApplication.java          # 启动类
│   │   │   ├── config/
│   │   │   │   ├── RedisConfig.java          # Redis配置
│   │   │   ├── controller/
│   │   │   │   ├── UserController.java       # 接口控制器
│   │   │   ├── entity/
│   │   │   │   ├── User.java                 # 实体类(对应MySQL user表)
│   │   │   ├── mapper/
│   │   │   │   ├── UserMapper.java           # 数据库操作(MyBatis)
│   │   │   ├── service/
│   │   │   │   ├── UserService.java          # 业务逻辑
│   │   │   │   └── impl/
│   │   │   │       └── UserServiceImpl.java  # 业务实现
│   │   └── resources/
│   │       ├── application.yml              # 项目配置(贴合Compose环境变量)
│   │       └── mybatis/
│   │           └── mapper/
│   │               └── UserMapper.xml        # MyBatis映射文件
│   └── test/
│       └── java/com/example/demo/
│           └── DemoApplicationTests.java     # 测试类
├── pom.xml                                   # Maven依赖
└── Dockerfile                                # 构建镜像配置
2. Maven依赖(pom.xml)
<?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.7.10</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springboot-demo</artifactId>
    <version>1.0</version>
    <name>springboot-demo</name>
    <description>SpringBoot Demo 适配 Docker Compose</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- SpringBoot Web 核心依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- MySQL 驱动(SpringBoot 父项目自动管理版本) -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
            <scope>runtime</scope>
        </dependency>

        <!-- MyBatis 整合 SpringBoot -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.1</version>
        </dependency>

        <!-- Redis 整合 SpringBoot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- Lombok 简化代码 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- SpringBoot 打包插件(生成可执行jar) -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
3. 核心配置(application.yml)

完全贴合Compose中的环境变量,无需手动修改,启动时自动读取Compose配置的MySQL、Redis信息。

server:
  port: 8080  # SpringBoot 端口,与 Compose 映射一致

spring:
  # 数据库配置(读取 Compose 环境变量)
  datasource:
    url: ${SPRING_DATASOURCE_URL}
    username: ${SPRING_DATASOURCE_USERNAME}
    password: ${SPRING_DATASOURCE_PASSWORD}
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      maximum-pool-size: 10
      minimum-idle: 5

  # Redis 配置(读取 Compose 环境变量)
  redis:
    host: ${SPRING_REDIS_HOST}
    port: ${SPRING_REDIS_PORT}
    password: ${SPRING_REDIS_PASSWORD}
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0

# MyBatis 核心配置(关键:确保映射文件路径正确)
mybatis:
  mapper-locations: classpath:mybatis/mapper/*.xml  # 匹配 XML 文件路径
  type-aliases-package: com.example.demo.entity     # 实体类包名
  configuration:
    map-underscore-to-camel-case: true  # 下划线转驼峰(适配数据库字段)
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 打印 SQL 日志(便于调试)
4. 实体类(User.java)

对应MySQL中的user表(与init.sql中的表结构一致)。

package com.example.demo.entity;

import lombok.Data;

@Data  // Lombok注解,自动生成getter、setter、toString等方法
public class User {
    private Integer id;
    private String name;
    private Integer age;
}
5. 数据库操作(UserMapper.java + UserMapper.xml)
// UserMapper.java(接口)
package com.example.demo.mapper;

import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;

@Mapper
public interface UserMapper {
    // 查询所有用户
    List<User> findAll();
    // 根据ID查询用户
    User findById(Integer id);
}
<?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">

<!-- namespace 必须与 Mapper 接口全类名完全一致 -->
<mapper namespace="com.example.demo.mapper.UserMapper">

    <!-- 结果映射(可选,建议添加,避免字段映射问题) -->
    <resultMap id="UserResultMap" type="com.example.demo.entity.User">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
    </resultMap>

    <!-- findAll 方法:id 必须与接口方法名完全一致 -->
    <select id="findAll" resultMap="UserResultMap">
        SELECT id, name, age FROM user
    </select>

    <!-- findById 方法:适配接口的 findById 方法 -->
    <select id="findById" parameterType="java.lang.Integer" resultMap="UserResultMap">
        SELECT id, name, age FROM user WHERE id = #{id}
    </select>

</mapper>
6. 业务逻辑(UserService.java + UserServiceImpl.java)
// UserService.java(接口)
package com.example.demo.service;

import com.example.demo.entity.User;
import java.util.List;

public interface UserService {
    // 查询所有用户(从Redis缓存,无缓存则查数据库)
    List<User> findAll();
    // 根据ID查询用户
    User findById(Integer id);
}
// UserServiceImpl.java(实现类)
package com.example.demo.service.impl;

import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // 缓存key前缀
    private static final String USER_KEY = "user:";

    @Override
    public List<User> findAll() {
        // 先查Redis缓存
        String key = USER_KEY + "all";
        List<User> userList = (List<User>) redisTemplate.opsForValue().get(key);
        if (userList == null || userList.isEmpty()) {
            // 缓存不存在,查数据库
            userList = userMapper.findAll();
            // 存入Redis,设置过期时间1小时
            redisTemplate.opsForValue().set(key, userList, 1, TimeUnit.HOURS);
        }
        return userList;
    }

    @Override
    public User findById(Integer id) {
        String key = USER_KEY + id;
        User user = (User) redisTemplate.opsForValue().get(key);
        if (user == null) {
            user = userMapper.findById(id);
            if (user != null) {
                redisTemplate.opsForValue().set(key, user, 1, TimeUnit.HOURS);
            }
        }
        return user;
    }
}
7. 接口控制器(UserController.java)

提供HTTP接口,可通过curl或浏览器访问,验证服务是否正常。

package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;

@RestController
@RequestMapping("/user")  // 接口前缀
public class UserController {

    @Resource
    private UserService userService;

    // 查询所有用户:GET http://localhost:8080/user
    @GetMapping
    public List<User> findAll() {
        return userService.findAll();
    }

    // 根据ID查询用户:GET http://localhost:8080/user/1
    @GetMapping("/{id}")
    public User findById(@PathVariable Integer id) {
        return userService.findById(id);
    }
}
8. 启动类(DemoApplication.java)
package com.example.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.example.demo.mapper")  // 扫描MyBatis接口
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}
9. Redis配置(RedisConfig.java)

解决Redis序列化问题,避免存入缓存的数据乱码。

package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        // 字符串序列化器
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        // JSON序列化器
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();

        // key使用字符串序列化
        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);

        // value使用JSON序列化
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);

        template.afterPropertiesSet();
        return template;
    }
}
10. Dockerfile(构建SpringBoot镜像)

放在backend目录下,配合Compose的build字段构建镜像(替代image字段)。

# 基础镜像(Java 8)
FROM registry.cn-hangzhou.aliyuncs.com/testpm-k8s/openjdk:8-jre-slim

# 工作目录
WORKDIR /app

# 复制打包后的jar包(maven打包后在target目录下)
COPY target/springboot-demo-1.0.jar app.jar

# 暴露端口(与SpringBoot端口、Compose端口映射一致)
EXPOSE 8080

# 启动命令
ENTRYPOINT ["java", "-jar", "app.jar"]
11. 打包与启动说明
  1. 打包SpringBoot项目:进入backend目录,执行 mvn clean package,生成target目录下的jar包。

  2. 修改Compose中的springboot-app服务:将 image: springboot-demo:1.0 注释,启用build: ./backend

  3. 启动Compose:执行 docker-compose up -d,Compose会自动构建SpringBoot镜像并启动所有服务。

  4. 验证接口:
    查询所有用户:curl http://localhost:8080/user,返回init.sql中的测试数据(Tom、Jerry)。

  5. 根据ID查询:curl http://localhost:8080/user/1,返回ID为1的用户信息。

4. 启动与验证

# 进入目录
cd springboot-demo
# 验证配置文件语法
docker-compose config
# 启动服务
docker-compose up -d
# 查看后端日志(排查启动问题)
docker-compose logs -f springboot-app
# 验证MySQL数据(进入容器)
docker-compose exec mysql mysql -u demo_user -p demo_db
# 执行SQL:SELECT * FROM user; 查看测试数据
# 验证接口(假设后端有/user接口)
curl http://localhost:8080/user
# 停止服务(保留数据)
docker-compose down
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐