JHHK

欢迎来到我的个人网站
行者常至 为者常成

bug菌_整合mysql

目录

数据库

本章先认识两种常用数据库,再完成 MySQL 的安装、建库建表,最后看看读取数据库的几种方式。

一、常用数据库对比

对比项 SQLite MySQL
类型 嵌入式数据库 专业级数据库服务
适用场景 本地 / 单机使用,比如手机 App 本地存储 网站、后台系统、高并发业务
数据文件 xx.db 文件,可以直接读取 xx.ibd 文件,不能直接读取,需要导入
本机准备 mac 自带 sqlite 需要安装并启动服务

SQLite 使用单个 .db 文件存储,复制文件即可迁移,但只适合单机场景。

MySQL 的 .ibd 文件不能直接打开:数据库中的 ibd 文件都带有身份标识,外来数据库文件无法直接识别。
所以需要先在 MySQL 中创建一个新的 yy.ibd(建库建表),再把 xx.ibd 的数据导入,才能读取。

二、MySQL 的安装与初始化

1. 安装、启动、登录与查看

## 安装 MySQL
brew install mysql

## 启动 MySQL 服务
brew services start mysql

## 设置 root 密码(重要)这里会进行一系列的初始化设置,请根据英文提示进行选择
mysql_secure_installation

## 登录mysql,输入下面指令会让输入上一步设置的密码,进行登录    
mysql -u root -p

## 当看到下面这样的提示,证明已经登录成功了     
╰─ mysql -u root -p         
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 11
Server version: 9.6.0 Homebrew

Copyright (c) 2000, 2026, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

## 查看所有数据库
show databases;

## 退出数据库
exit;

# 停掉 MySQL
brew services stop mysql

2. 建库建表

## 创建一个数据库
create database HelloSpring;

## 使用这个数据库。看到Database changed说明现在所有的 SQL 操作,都会针对这个项目库生效。
mysql> use HelloSpring;
Database changed
mysql>


## 创建一个表
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `name` varchar(255) NOT NULL COMMENT '用户名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `sex` varchar(2) DEFAULT NULL COMMENT '性别',
  `address` varchar(255) DEFAULT NULL COMMENT '住址',
  `describes` varchar(255) DEFAULT NULL COMMENT '描述',
  `image` varchar(255) DEFAULT NULL COMMENT '头像',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=60 DEFAULT CHARSET=utf8;


## 查看表
show tables;

## 查看表结构
desc user;

3. 插入测试数据

## 插入三条数据
INSERT INTO `user`(`id`, `name`, `age`, `sex`, `address`, `describes`, `image`) VALUES (1, '刘亦菲', 70, '女', '上海闵行', '这是db1的用户', './template/image/刘亦菲.jpg');
INSERT INTO `user`(`id`, `name`, `age`, `sex`, `address`, `describes`, `image`) VALUES (2, '杨幂', 20, '女', '北京朝阳', '这是db1的用户', './template/image/杨幂.jpg');
INSERT INTO `user`(`id`, `name`, `age`, `sex`, `address`, `describes`, `image`) VALUES (3, '彭于晏', 30, '男', '河北邯郸', '这是db1的用户', './template/image/彭于晏.jpg');


## 查看表内的数据
select * from user;

4. 重置 root 密码(忘记密码时使用,数据不受影响)

# 1. 停掉 MySQL
brew services stop mysql

# 2. 跳过权限校验启动(后台运行)
/opt/homebrew/opt/mysql/bin/mysqld --skip-grant-tables --skip-networking &

# 3. 免密登录并重置密码
mysql -u root <<'EOF'
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY '这里换成你的新密码';
EOF

# 4. 关掉免密实例,正常启动
kill %1
brew services start mysql

# 5. 用新密码验证
mysql -u root -p

三、数据库的读取方式

1、可视化工具:用 Navicat 等客户端连接数据库,填好地址、端口、账号密码即可读取。

2、命令行:连接 MySQL 后,通过指令和 SQL 直接读取。

3、IDEA Database:IDEA 自带的数据库工具,但可视化功能需要专业版。

4、代码读取:Spring Boot 整合 MySQL,通过代码读取数据库(下一章介绍)。

四、补充

正常的商业级开发,数据库是不会部署在本地的,一般是部署在云服务器。
后台开发者,只需要连接数据库就可以了。

Spring Boot 整合 mysql(Spring Boot 自带的 JdbcTemplate)

JDBC = Java Database Connectivity

JDBC:接口标准(契约)
数据库驱动:mysql-connector-j 是 JDBC 接口的实现(真正干活和数据库通信的)
JdbcTemplate / MyBatis / SpringDataJPA:工具 / 框架层,包装 JDBC 调用,简化开发,它们是上层封装

Spring Boot 官方提供了开箱即用的 JdbcTemplate 工具类,它对原生 JDBC 操作进行了轻量封装,大幅简化了数据库连接、语句创建、资源释放等重复样板代码的编写流程,让开发者可以专注于 SQL 本身的业务逻辑实现。

整合流程只有三步:加依赖 → 配置数据源 → 写测试验证。
前提:MySQL 服务已启动,并且已经按上一章创建好 HelloSpring 数据库和 user 表。

一、添加依赖

在 pom.xml 的 <dependencies> 中添加下面的依赖,各依赖的作用和必要性如下:

依赖 作用 是否必需
mysql-connector-j Java 程序与 MySQL 通信的驱动,没有它连不上数据库
它是JDBC接口规范的实现
必需
spring-boot-starter-jdbc 提供 JdbcTemplate、连接池(默认 HikariCP)和事务管理
它是上层封装,包装了JDBC的调用
必需
jakarta.annotation-api 提供 @Resource 注解,用于注入JdbcTemplate对象 必需
lombok 自动生成 getter/setter/toString,减少样板代码 强烈推荐(可选)
mybatis-plus-boot-starter 不写 SQL 即可完成增删改查,本章用不到,下一章详解 可选
mybatis-plus-generator、freemarker 代码生成器及其模板引擎 可选

pom.xml 完整配置:

<dependencies>
        <!--
        作用:帮你自动生成 getter、setter、toString、构造方法不用手写一堆 getXXX() setXXX(),代码更干净。
        必须吗? 非常推荐,几乎所有项目都用。
        -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>


        <!--引入jdbc stater 目的是使用JDBC模板接口-->
        <!--
        作用:Spring 自带的简单数据库操作工具可以用 JdbcTemplate 操作数据库。
        必须吗? 你现在可以删掉!因为你用了 MyBatis-Plus,它已经包含了这个功能。
        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>


        <!-- MySQL 驱动(新版,必须用这个) -->
        <!--
        作用:Java 程序和 MySQL 说话的 “翻译官”没有它,SpringBoot 根本连不上数据库。
        必须吗? 必须!连数据库必用。
        -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>


        <!--mybatis-plus-->
        <!--
        作用:数据库操作神器!不用写 SQL,就能实现增删改查。你现在的项目核心依赖。
        必须吗? 必须!你就是用它操作 user 表。
        -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>


        <!--代码生成器(可选)-->
        <!--
        作用 自动生成:实体类、Mapper、Service、Controller输入表名 → 一键生成全套代码。
        必须吗?新手可以不用,属于辅助工具。
        -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.3.1</version>
        </dependency>


        <!-- 代码生成器需要模板引擎 -->
        <!--
        作用:代码生成器生成代码时需要的模板。
        必须吗?不用代码生成器就可以删。
        -->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
        </dependency>


        <!--
        作用:解决你最开始的报错:Cannot resolve symbol 'Resource'让你能使用 @Resource 注解注入对象。
        必须吗?必须!
        -->
        <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>2.1.1</version>
        </dependency>
</dependencies>

二、配置数据源

配置文件位于 HelloSpring/config/application.yam,每一行的作用见注释:

##jdbc:mysql://             用 JDBC 协议连接 MySQL
#localhost                  数据库在本机(你自己的电脑)
#3306                       MySQL 默认端口号(固定不用改)
#HelloSpring                你要连接的数据库名字(就是你创建的那个库)
#serverTimezone=GMT%2B8     设置时区为 东八区(中国时间),必须加,否则报错
#useUnicode=true            使用 Unicode 字符集
#characterEncoding=utf-8    编码用 UTF-8,防止中文乱码
spring.datasource.url=jdbc:mysql://localhost:3306/HelloSpring?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
#MySQL 的登录用户名
spring.datasource.username=root
#MySQL 登录密码(改成你自己设置的 root 密码)
spring.datasource.password=123456
#MySQL 驱动类作用:让 Java 程序能 “听懂” MySQL 的语言
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

三、编写测试用例验证

测试类位于 src/test/java/com/lxy/hellospring/HelloSpringApplicationTests.java。

  • @SpringBootTest:启动完整的 Spring 容器,这样测试类里才能注入 JdbcTemplate。
  • @Resource:把容器中的 JdbcTemplate 注入进来。
  • RowMapper:把结果集中的一行数据映射成一个 UserEntity 对象,列名要和实体字段一一对应。
package com.lxy.hellospring;
import com.lxy.hellospring.module.user.model.UserEntity;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;




@SpringBootTest
@Slf4j
class HelloSpringApplicationTests {

    @Resource
    private JdbcTemplate jdbcTemplate;


    @Test
    void contextLoads() {
        String sql ="select * from user";
        List<UserEntity> users = jdbcTemplate.query(sql, new RowMapper<UserEntity>() {
            @Override
            public UserEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
                UserEntity user = new UserEntity();
                user.setId(rs.getInt("id"));
                user.setAge(rs.getInt("age"));
                user.setName(rs.getString("name"));
                user.setSex(rs.getString("sex"));
                user.setAddress(rs.getString("address"));
                user.setDescribes(rs.getString("describes"));
                user.setImage(rs.getString("image"));
                return user;
            }
        });
        log.info("查询成功");
        users.forEach(p->{
            log.info("用户{}",p);
        });
    }
}

运行测试后,控制台会输出三条用户数据,说明 Spring Boot 已经成功连上 MySQL 并完成查询:

查询成功
用户UserEntity(id=1, name=刘亦菲, age=70, sex=女, address=上海闵行, describes=这是db1的用户, image=./template/image/刘亦菲.jpg)
用户UserEntity(id=2, name=杨幂, age=20, sex=女, address=北京朝阳, describes=这是db1的用户, image=./template/image/杨幂.jpg)
用户UserEntity(id=3, name=彭于晏, age=30, sex=男, address=河北邯郸, describes=这是db1的用户, image=./template/image/彭于晏.jpg)

本章用的是 Spring Boot 自带的 JdbcTemplate:SQL 要手写,结果集要自己映射。
实际项目更常用下一章的 MyBatis,或者直接用 MyBatis-Plus,把 SQL 和映射的重复工作省掉。

Spring Boot 整合MyBatis

MyBatis 是 Java 的持久层框架,封装了 JDBC 中重复的代码(获取连接、执行 SQL、结果映射),SQL 仍然由我们自己编写。
MyBatis-Plus 是 MyBatis 的增强工具,包含了 MyBatis 的全部功能,还额外提供不用写 SQL 的增删改查。
两者只引一个即可(MyBatis-Plus 已包含 MyBatis),本章先讲原生 MyBatis。

一、添加依赖

依赖 作用 是否必需
mybatis-spring-boot-starter MyBatis 官方的 Spring Boot 启动器,自动创建 SqlSessionFactory 并扫描 Mapper 必需
mysql-connector-j MySQL 驱动 必需
spring-boot-starter-jdbc 提供数据源和事务,MyBatis starter 已间接包含 不用重复引
mybatis-spring 把 Mapper 接口注册成 Spring Bean,starter 已包含 不用单独引

Spring Boot 3 必须使用 3.x 版本的 starter:2.x 内部依赖的是低版本 mybatis-spring,与 Spring Boot 3 冲突,启动会报错。
所以这里直接用 3.0.3,也就不需要再单独指定 mybatis-spring 的版本了。

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

二、配置文件

数据库连接沿用上一章的 spring.datasource.*,再补充 MyBatis 自己的配置:

## 数据源(沿用上一章)
spring.datasource.url=jdbc:mysql://localhost:3306/HelloSpring?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

## Mapper XML 文件的位置
mybatis.mapper-locations=classpath:mapper/*.xml
## 实体类所在包,XML 里可以直接写类名
mybatis.type-aliases-package=com.lxy.hellospring.module.user.model
## 下划线转驼峰:user_name -> userName
mybatis.configuration.map-underscore-to-camel-case=true

三、编写 Mapper

1. 注解方式(SQL 简单时)

package com.lxy.hellospring.module.user.mapper;

import com.lxy.hellospring.module.user.model.UserEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface UserMapper {

    @Select("select * from user")
    List<UserEntity> findAll();
}

@Mapper 告诉 MyBatis:这是一个 Mapper 接口,由它生成实现类并注册到 Spring 容器。
如果 Mapper 很多,可以在启动类上写一次 @MapperScan("com.lxy.hellospring.module.user.mapper"),接口上就不用再加 @Mapper。

2. XML 方式(SQL 复杂时)

接口里只声明方法,SQL 写到 XML 中:

UserEntity findById(Integer id);

src/main/resources/mapper/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.lxy.hellospring.module.user.mapper.UserMapper">

    <select id="findById" resultType="UserEntity">
        select id, name, age, sex, address, describes, image
        from user
        where id = #{id}
    </select>
</mapper>

注意:

  • namespace 必须是 Mapper 接口的全限定名,id 必须和方法名一致。
  • XML 中 <、<= 等符号要转义成 &lt;、&lt;=,或者用 <![CDATA[ ... ]]> 包起来,否则会解析报错。

四、测试验证

@SpringBootTest
@Slf4j
class UserMapperTests {

    @Resource
    private UserMapper userMapper;

    @Test
    void findAll() {
        userMapper.findAll().forEach(p -> log.info("用户{}", p));
    }
}

能打印出 user 表的三条数据,说明 MyBatis 整合成功。

五、换成 MyBatis-Plus

MyBatis-Plus 完全包含 MyBatis,把 mybatis-spring-boot-starter 换成它即可:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.16</version>
</dependency>

Mapper 接口继承 BaseMapper<UserEntity> 后,增删改查都不用写 SQL:

@Mapper
public interface UserMapper extends BaseMapper<UserEntity> {
}

注意:MyBatis-Plus 内部依赖的 mybatis-spring 版本较低时,同样会和 Spring Boot 3.x 冲突,此时额外指定 mybatis-spring 3.0.3 即可。

Spring Boot 整合Druid

一、Druid 介绍

Druid 是阿里巴巴开源的数据库连接池,可以替换 Spring Boot 默认的 HikariCP,并额外提供 SQL 监控、慢 SQL 统计、SQL 防火墙(防注入)等能力。

HikariCP 是什么?

HikariCP 是一个高性能的数据库连接池,从 Spring Boot 2 开始就是默认的连接池。
它的设计目标只有一个:把连接管理做到最快、最轻。代码量很小,没有额外的监控、审计功能,所以性能是同类里最好的。
也就是说,前面整合 MySQL、整合 MyBatis 时,项目里用的其实已经是 HikariCP 了,只是我们没有感知到。

连接池的作用:数据库连接创建和销毁都很耗时,连接池会预先建好一批连接反复复用,用完归还而不是关闭。

HikariCP 和 Druid 的区别

对比项 HikariCP Druid
定位 纯连接池,专注性能 连接池 + 监控、安全中间件
性能 业界公认最快最轻 略逊于 HikariCP,业务中基本感知不到
监控 没有内置页面,只能看日志 自带 /druid 监控页,可看 SQL 执行次数、慢 SQL、连接池状态
安全 无 提供 wall 过滤器,可拦截 SQL 注入
配置 少量参数即可 参数多,功能也多
来源 Spring Boot 2+ 默认自带 阿里巴巴开源,需手动引入 starter
适用场景 大多数项目,追求简单高效 需要 SQL 监控、审计、防注入的后台系统

简单说:只要连接池就用 HikariCP,想顺便看 SQL 执行情况就用 Druid。两者选一个即可,不需要同时引入。

连接池和 ORM 互不影响:不管用 JdbcTemplate 还是 MyBatis,都可以换成 Druid。

二、添加依赖

Spring Boot 3 必须用 druid-spring-boot-3-starter,Spring Boot 2 才用 druid-spring-boot-starter:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-3-starter</artifactId>
    <version>1.2.28</version>
</dependency>

三、配置数据源

引入 starter 后,数据源配置前缀变成 spring.datasource.druid,把上一章的 spring.datasource.* 改成下面的形式:

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

spring.datasource.druid.url=jdbc:mysql://localhost:3306/HelloSpring?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
spring.datasource.druid.username=root
spring.datasource.druid.password=123456
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver

## 连接池大小
spring.datasource.druid.initial-size=5
spring.datasource.druid.min-idle=5
spring.datasource.druid.max-active=20
## 获取连接的最长等待时间(毫秒)
spring.datasource.druid.max-wait=60000

## stat:监控统计;wall:防 SQL 注入
spring.datasource.druid.filters=stat,wall

四、开启监控页面

spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=admin

启动项目后访问 http://localhost:8080/druid,用上面的账号密码登录,就能看到数据源、SQL 执行次数、慢 SQL 等统计。
生产环境要修改默认账号密码,并用 allow / deny 限制可访问的 IP。

五、验证是否生效

@SpringBootTest
@Slf4j
class DataSourceTests {

    @Resource
    private DataSource dataSource;

    @Test
    void printDataSource() {
        log.info("当前数据源:{}", dataSource.getClass().getName());
    }
}

控制台打印 com.alibaba.druid.pool.DruidDataSource,说明已经成功替换默认的 HikariCP。
之后正常调用 Mapper,在监控页面里就能看到对应的 SQL 记录。

Controller、Service、Mapper 分层总结

一个请求从浏览器进来,经过 Controller → Service → Mapper → 数据库,再把结果原路返回。
这几层各管一件事,职责分清楚了,代码才好维护。下面以“根据 id 查询用户”为例。

一、各层的作用

层 典型注解 作用 不该做的事
Controller @RestController、@RequestMapping 接收 HTTP 请求、接收/校验参数、调用 Service、返回 JSON 不写业务逻辑,不直接调 Mapper
Service(接口) 无 定义业务方法,告诉调用方“能做什么” 不写实现
ServiceImpl @Service 实现业务逻辑:校验、组装数据、事务控制 @Transactional 不处理 HTTP 的 request/response
Mapper @Mapper 与数据库交互,执行 SQL 并映射结果 不写业务判断,不调用 Service

补充:ServiceImpl implements UserService 建立了接口和实现类的映射关系;@Service 把它注册成 Bean,Controller 注入的是接口,实际执行的是实现类。

二、一次请求的调用链

浏览器 / 前端
   │  HTTP 请求:GET /user/1
   ▼
Controller          接收参数,决定调用哪个业务方法
   │  userService.findById(1)
   ▼
UserService(接口)
   │  实际执行实现类
   ▼
UserServiceImpl     校验参数、处理业务、控制事务
   │  userMapper.findById(1)
   ▼
UserMapper          执行 SQL
   │
   ▼
MySQL

返回时方向相反:MySQL 的结果由 Mapper 映射成 UserEntity,交给 Service,再交给 Controller,最后由 Spring MVC 序列化成 JSON 返回给前端。
请求参数(@PathVariable、@RequestParam、@RequestBody)在 Controller 处理;SQL 只出现在 Mapper。

三、代码示例

Controller:只负责接参和返回,不写业务。

@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserService userService;

    @GetMapping("/{id}")
    public UserEntity getById(@PathVariable Integer id) {
        return userService.findById(id);
    }
}

Service 接口:定义能力。

public interface UserService {
    UserEntity findById(Integer id);
}

ServiceImpl:写业务,注入 Mapper。

@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserMapper userMapper;

    @Override
    public UserEntity findById(Integer id) {
        return userMapper.findById(id);
    }
}

Mapper:只和数据库打交道。

@Mapper
public interface UserMapper {

    @Select("select * from user where id = #{id}")
    UserEntity findById(Integer id);
    // UserEntity 是数据库表 user 的映射类,包含所有字段,mybatis会根据反射完成映射
    // @TableName("user")
    // 注意:仅 MyBatis-Plus 内置方法(selectById/insert等)识别此注解;自定义 @Select / XML 的 SQL 不感知它
}

四、为什么要分层

  • 职责单一:Controller 管 HTTP,Service 管业务,Mapper 管 SQL,出问题时容易定位。
  • 便于复用:同一个 Service 可以同时给网页接口和 App 接口使用。
  • 便于替换:从 JdbcTemplate 换成 MyBatis、再换成 MyBatis-Plus,只需要改 Mapper,Controller 和 Service 基本不动。
  • 便于测试:Service 可以脱离 Web 层单独写单元测试。

项目很小的时候,Controller 直接调 Mapper 也能跑;但业务一复杂,就会变成“接口里全是 SQL 和判断”,所以标准的写法还是这三层。


行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.