基于Vue+SpringBoot+MySQL实现个人博客系统(二)

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: 基于Vue+SpringBoot+MySQL实现个人博客系统(二)

文章目录


 实现个人博客的步骤如下:

一.设计数据库模型:首先需要设计博客数据库模型,包括博客文章、评论、分类、标签等表的设计。可以使用MySQL Workbench等工具进行建模,也可以手动编写SQL语句创建表。

下面是一个简单的数据库模型设计,包括以下表:

  1. 文章表(article):存储博客文章的信息,包括文章ID、文章标题、文章内容、发布时间、最后修改时间、文章所属分类ID等字段。
  2. 评论表(comment):存储博客文章的评论信息,包括评论ID、评论内容、评论时间、评论人昵称、评论人邮箱、评论人网址、评论所属文章ID等字段。
  3. 分类表(category):存储博客文章的分类信息,包括分类ID、分类名称等字段。
  4. 标签表(tag):存储博客文章的标签信息,包括标签ID、标签名称等字段。
  5. 文章-分类关联表(article_category):存储文章和分类之间的关联关系,包括文章ID和分类ID两个字段。
  6. 文章-标签关联表(article_tag):存储文章和标签之间的关联关系,包括文章ID和标签ID两个字段。

下面是数据库模型设计的SQL语句:

CREATE TABLE `article` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '文章ID',
  `title` varchar(255) DEFAULT NULL COMMENT '文章标题',
  `content` longtext COMMENT '文章内容',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '修改时间',
  `category_id` int(11) DEFAULT NULL COMMENT '文章所属分类ID',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章表';
CREATE TABLE `comment` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '评论ID',
  `content` longtext COMMENT '评论内容',
  `create_time` datetime DEFAULT NULL COMMENT '评论时间',
  `nickname` varchar(255) DEFAULT NULL COMMENT '评论人昵称',
  `email` varchar(255) DEFAULT NULL COMMENT '评论人邮箱',
  `website` varchar(255) DEFAULT NULL COMMENT '评论人网址',
  `article_id` int(11) DEFAULT NULL COMMENT '评论所属文章ID',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评论表';
CREATE TABLE `category` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '分类ID',
  `name` varchar(255) DEFAULT NULL COMMENT '分类名称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分类表';
CREATE TABLE `tag` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '标签ID',
  `name` varchar(255) DEFAULT NULL COMMENT '标签名称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='标签表';
CREATE TABLE `article_category` (
  `article_id` int(11) NOT NULL COMMENT '文章ID',
  `category_id` int(11) NOT NULL COMMENT '分类ID',
  PRIMARY KEY (`article_id`,`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章分类关联表';
CREATE TABLE `article_tag` (
  `article_id` int(11) NOT NULL COMMENT '文章ID',
  `tag_id` int(11) NOT NULL COMMENT '标签ID',
  PRIMARY KEY (`article_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章标签关联表';

以上是一个简单的博客数据库模型设计,可以根据实际需求进行调整和优化。

二.搭建后端环境:使用Spring Boot框架搭建后端环境,包括配置Maven、集成MyBatis、配置数据库连接等。可以使用IDEA等开发工具进行开发。

下面是使用Spring Boot框架搭建后端环境的步骤:

1.安装Java开发环境:在电脑上安装JDK开发环境,建议使用JDK8及以上版本。

下面是安装Java开发环境的步骤:
1. 下载JDK安装包:在Oracle官网下载对应操作系统的JDK安装包([https://www.oracle.com/java/technologies/javase-downloads.html ↗](https://www.oracle.com/java/technologies/javase-downloads.html))。
2. 安装JDK:运行下载的JDK安装包,按照提示完成JDK的安装。如果是Windows系统,可以选择默认安装路径。
3. 配置环境变量:将JDK的安装路径配置到系统环境变量中,包括JAVA_HOME、CLASSPATH、PATH三个环境变量。
   - JAVA_HOME:JDK的安装路径,例如:C:\Program Files\Java\jdk1.8.0_221
   - CLASSPATH:Java类库搜索路径,一般设置为当前目录,例如:.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar
   - PATH:系统执行命令的搜索路径,需要在原有的路径后面添加JDK的bin目录,例如:%PATH%;%JAVA_HOME%\bin
4. 验证JDK安装:打开命令行窗口,输入java -version命令,查看是否输出了JDK版本信息,如果输出了,则说明JDK安装成功。
安装Java开发环境可能会因操作系统不同而略有差异,以上步骤仅供参考。

2.安装Maven:Maven是一个构建工具,可以自动管理Java项目中的依赖关系和构建过程,建议安装最新版本的Maven。

下面是安装Maven的步骤:
1. 下载Maven安装包:在Apache官网下载最新版本的Maven安装包([https://maven.apache.org/download.cgi ↗](https://maven.apache.org/download.cgi))。
2. 解压Maven安装包:将下载的Maven安装包解压到任意目录下,例如:C:\apache-maven-3.8.3。
3. 配置环境变量:将Maven的bin目录添加到系统环境变量的PATH中,例如:%PATH%;C:\apache-maven-3.8.3\bin。
4. 验证Maven安装:打开命令行窗口,输入mvn -v命令,查看是否输出了Maven版本信息,如果输出了,则说明Maven安装成功。
安装Maven可能会因操作系统不同而略有差异,以上步骤仅供参考。

3.创建Spring Boot项目:可以使用Spring Initializr创建Spring Boot项目,选择Maven项目、选择相应的Spring Boot版本、选择Web、MyBatis等相关依赖。

下面是使用Spring Initializr创建Spring Boot项目的步骤:
1. 打开Spring Initializr:在浏览器中打开Spring Initializr([https://start.spring.io/ ↗](https://start.spring.io/))。
2. 选择项目配置:选择Maven项目、选择相应的Spring Boot版本、选择Web、MyBatis等相关依赖,可以根据实际需求进行选择。
3. 生成项目:点击“Generate”按钮,生成项目的压缩包。
4. 解压项目:将生成的项目压缩包解压到任意目录下,例如:C:\springboot-project。
5. 导入项目:使用IDEA等开发工具,导入解压后的项目,即可开始开发。
创建Spring Boot项目的具体步骤可能会因开发工具不同而略有差异,以上步骤仅供参考。

4.配置数据库连接:在application.properties或application.yml文件中配置数据库连接信息,包括数据库URL、用户名、密码等。

下面是在Spring Boot项目中配置数据库连接的步骤:

  1. 在项目的src/main/resources目录下创建application.properties或application.yml文件,用于存储配置信息。
  2. 在配置文件中添加数据库连接信息,例如:
# MySQL数据库连接信息
spring.datasource.url=jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
```
其中,spring.datasource.url属性指定数据库的URL,spring.datasource.username和spring.datasource.password属性指定数据库的用户名和密码,spring.datasource.driver-class-name属性指定数据库驱动的类名。
  1. 保存配置文件,重启项目,即可使用配置的数据库连接信息连接到相应的数据库。

以上是在Spring Boot项目中配置数据库连接的基本步骤,可以根据实际需求进行调整和优化。

5.配置MyBatis:在application.properties或application.yml文件中配置MyBatis相关信息,包括MyBatis配置文件路径、Mapper文件路径等。

下面是在Spring Boot项目中配置MyBatis的步骤:

  1. 在项目的src/main/resources目录下创建mybatis-config.xml文件,用于存储MyBatis的配置信息。
  2. 在配置文件中添加MyBatis配置信息,例如:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="cacheEnabled" value="true" />
        <setting name="lazyLoadingEnabled" value="true" />
        <setting name="aggressiveLazyLoading" value="true" />
    </settings>
</configuration>
```
其中,settings节点中可以配置MyBatis的各种设置,例如是否启用缓存、是否启用延迟加载等。
  1. 在配置文件中添加Mapper文件的路径信息,例如:
mybatis.mapper-locations=classpath:mapper/*.xml
  1. 其中,mybatis.mapper-locations属性指定Mapper文件的路径,可以使用通配符指定多个Mapper文件。
  2. 在Spring Boot的配置文件中添加MyBatis相关的配置信息,例如:
# MyBatis配置文件路径
mybatis.config-location=classpath:mybatis-config.xml
  1. 其中,mybatis.config-location属性指定MyBatis的配置文件路径。
  2. 在Mapper接口中使用@Mapper注解,告诉Spring Boot需要将该接口注册为Mapper。
  3. 在Mapper XML文件中编写SQL语句,实现与数据库的交互操作。

以上是在Spring Boot项目中配置MyBatis的基本步骤,可以根据实际需求进行调整和优化。

6.编写实体类:根据数据库模型设计,编写与数据库表对应的实体类。

下面是编写实体类的基本步骤:

  1. 根据数据库模型设计,确定实体类的属性和对应的数据库表字段。
  2. 在项目的src/main/java目录下创建实体类的包,例如com.example.demo.entity。
  3. 在实体类的包中创建与数据库表对应的实体类,例如User类。
  4. 在实体类中添加属性和对应的getter/setter方法,例如:
public class User {
    private Long id;
    private String name;
    private Integer age;
    // 省略getter/setter方法
}
````
其中,id、name、age属性对应数据库表中的id、name、age字段。
  1. (可选)使用注解或XML方式实现实体类和数据库表之间的映射关系,例如:
  • 使用注解方式:
@TableName("user")
public class User {
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @TableField("name")
    private String name;
    @TableField("age")
    private Integer age;
    // 省略getter/setter方法
}
  • 使用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.example.demo.mapper.UserMapper">
    <resultMap id="userMap" type="com.example.demo.entity.User">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
    </resultMap>
</mapper>

以上是编写实体类的基本步骤,可以根据实际需求进行调整和优化。

7.编写Mapper类:编写Mapper接口和Mapper XML文件,实现与数据库的交互操作,包括增删改查等。

编写Mapper类,需要分别编写Mapper接口和Mapper XML文件,实现与数据库的交互操作,包括增删改查等。

以下是一个可能的示例:

  1. ArticleMapper接口
public interface ArticleMapper {
    // 根据ID查询文章信息及其所属分类和标签信息
    Article findArticleById(int id);
    // 查询所有文章信息及其所属分类和标签信息
    List<Article> findAllArticles();
    // 新增文章信息
    int addArticle(Article article);
    // 更新文章信息
    int updateArticle(Article article);
    // 删除文章信息
    int deleteArticle(int id);
}
  1. ArticleMapper XML文件
<!-- namespace指定Mapper接口所在的包名和接口名 -->
<mapper namespace="com.example.mapper.ArticleMapper">
    <!-- 根据ID查询文章信息及其所属分类和标签信息 -->
    <select id="findArticleById" resultType="com.example.bean.Article">
        SELECT a.id, a.title, a.content, a.create_time, a.update_time, c.id AS category_id, c.name AS category_name,
            GROUP_CONCAT(t.id ORDER BY t.id SEPARATOR ',') AS tag_ids, GROUP_CONCAT(t.name ORDER BY t.id SEPARATOR ',') AS tag_names
        FROM article a
        LEFT JOIN article_category ac ON a.id = ac.article_id
        LEFT JOIN category c ON ac.category_id = c.id
        LEFT JOIN article_tag at ON a.id = at.article_id
        LEFT JOIN tag t ON at.tag_id = t.id
        WHERE a.id = #{id}
    </select>
    <!-- 查询所有文章信息及其所属分类和标签信息 -->
    <select id="findAllArticles" resultType="com.example.bean.Article">
        SELECT a.id, a.title, a.content, a.create_time, a.update_time, c.id AS category_id, c.name AS category_name,
            GROUP_CONCAT(t.id ORDER BY t.id SEPARATOR ',') AS tag_ids, GROUP_CONCAT(t.name ORDER BY t.id SEPARATOR ',') AS tag_names
        FROM article a
        LEFT JOIN article_category ac ON a.id = ac.article_id
        LEFT JOIN category c ON ac.category_id = c.id
        LEFT JOIN article_tag at ON a.id = at.article_id
        LEFT JOIN tag t ON at.tag_id = t.id
        GROUP BY a.id
    </select>
    <!-- 新增文章信息 -->
    <insert id="addArticle" parameterType="com.example.bean.Article">
        INSERT INTO article(title, content, create_time, update_time)
        VALUES(#{title}, #{content}, #{createTime}, #{updateTime})
    </insert>
    <insert id="addArticleCategory" parameterType="java.util.Map">
        INSERT INTO article_category(article_id, category_id)
        VALUES(#{articleId}, #{categoryId})
    </insert>
    <insert id="addArticleTag" parameterType="java.util.Map">
        INSERT INTO article_tag(article_id, tag_id)
        VALUES(#{articleId}, #{tagId})
    </insert>
    <!-- 更新文章信息 -->
    <update id="updateArticle" parameterType="com.example.bean.Article">
        UPDATE article
        SET title = #{title}, content = #{content}, update_time = #{updateTime}
        WHERE id = #{id}
    </update>
    <delete id="deleteArticleCategory" parameterType="int">
        DELETE FROM article_category WHERE article_id = #{id}
    </delete>
    <delete id="deleteArticleTag" parameterType="int">
        DELETE FROM article_tag WHERE article_id = #{id}
    </delete>
    <!-- 删除文章信息 -->
    <delete id="deleteArticle" parameterType="int">
        DELETE FROM article WHERE id = #{id}
    </delete>
</mapper>

上述示例代码中,Mapper接口中定义了增删改查等方法,对应的Mapper XML文件中定义了对应的SQL语句,使用#{}占位符来引用方法参数或实体类属性,使用resultType指定查询结果的返回类型。同时,为了处理多对多关系,SQL语句中使用了LEFT JOIN和GROUP_CONCAT等关键字,将文章与分类、标签等信息查询出来,并使用Map参数来实现多表数据的插入操作。

8.编写Service类:编写Service类,实现业务逻辑的处理,并调用Mapper类中的方法实现数据的读写操作。

编写Service类,需要实现业务逻辑的处理,并调用Mapper类中的方法实现数据的读写操作。

以下是一个可能的示例:

  1. ArticleService类
@Service
public class ArticleService {
    @Autowired
    private ArticleMapper articleMapper;
    @Autowired
    private CategoryMapper categoryMapper;
    @Autowired
    private TagMapper tagMapper;
    // 新增文章信息
    public boolean addArticle(Article article, List<Integer> categoryIds, List<Integer> tagIds) {
        // 新增文章信息
        int rows = articleMapper.addArticle(article);
        if (rows <= 0) {
            return false;
        }
        // 新增文章分类关联信息
        for (Integer categoryId : categoryIds) {
            Map<String, Integer> map = new HashMap<>();
            map.put("articleId", article.getId());
            map.put("categoryId", categoryId);
            articleMapper.addArticleCategory(map);
        }
        // 新增文章标签关联信息
        for (Integer tagId : tagIds) {
            Map<String, Integer> map = new HashMap<>();
            map.put("articleId", article.getId());
            map.put("tagId", tagId);
            articleMapper.addArticleTag(map);
        }
        return true;
    }
    // 更新文章信息
    public boolean updateArticle(Article article, List<Integer> categoryIds, List<Integer> tagIds) {
        // 更新文章信息
        int rows = articleMapper.updateArticle(article);
        if (rows <= 0) {
            return false;
        }
        // 删除文章分类关联信息
        articleMapper.deleteArticleCategory(article.getId());
        // 新增文章分类关联信息
        for (Integer categoryId : categoryIds) {
            Map<String, Integer> map = new HashMap<>();
            map.put("articleId", article.getId());
            map.put("categoryId", categoryId);
            articleMapper.addArticleCategory(map);
        }
        // 删除文章标签关联信息
        articleMapper.deleteArticleTag(article.getId());
        // 新增文章标签关联信息
        for (Integer tagId : tagIds) {
            Map<String, Integer> map = new HashMap<>();
            map.put("articleId", article.getId());
            map.put("tagId", tagId);
            articleMapper.addArticleTag(map);
        }
        return true;
    }
    // 删除文章信息
    public boolean deleteArticle(int id) {
        // 删除文章信息
        int rows = articleMapper.deleteArticle(id);
        if (rows <= 0) {
            return false;
        }
        // 删除文章分类关联信息
        articleMapper.deleteArticleCategory(id);
        // 删除文章标签关联信息
        articleMapper.deleteArticleTag(id);
        return true;
    }
    // 根据ID查询文章信息及其所属分类和标签信息
    public Article findArticleById(int id) {
        Article article = articleMapper.findArticleById(id);
        if (article != null) {
            // 查询文章所属的分类信息
            List<Category> categories = categoryMapper.findCategoriesByArticleId(article.getId());
            article.setCategories(categories);
            // 查询文章所属的标签信息
            List<Tag> tags = tagMapper.findTagsByArticleId(article.getId());
            article.setTags(tags);
        }
        return article;
    }
    // 查询所有文章信息及其所属分类和标签信息
    public List<Article> findAllArticles() {
        List<Article> articles = articleMapper.findAllArticles();
        if (articles != null && !articles.isEmpty()) {
            for (Article article : articles) {
                // 查询文章所属的分类信息
                List<Category> categories = categoryMapper.findCategoriesByArticleId(article.getId());
                article.setCategories(categories);
                // 查询文章所属的标签信息
                List<Tag> tags = tagMapper.findTagsByArticleId(article.getId());
                article.setTags(tags);
            }
        }
        return articles;
    }
}

上述示例代码中,ArticleService类中注入了ArticleMapper、CategoryMapper和TagMapper等Mapper类,通过调用Mapper类中的方法实现对数据库的增删改查等操作,并在此基础上实现了业务逻辑的处理。在新增和更新文章信息时,通过调用articleMapper的addArticleCategory和addArticleTag方法,新增文章与分类、标签之间的关联信息;在更新文章信息时,先删除原有的分类、标签关联信息,再新增新的分类、标签关联信息;在删除文章信息时,同样需要删除文章与分类、标签之间的关联信息。在查询文章信息时,通过调用categoryMapper和tagMapper的方法查询文章所属的分类和标签信息,并将查询结果设置到Article对象的categories和tags属性中,返回给调用方。

  1. 编写Controller类:编写Controller类,处理HTTP请求,调用Service类中的方法返回相应的数据。
  2. 启动项目:使用IDEA等开发工具启动项目,测试接口是否正常。编写Controller类,处理HTTP请求,调用Service类中的方法返回相应的数据。以下是一个可能的示例:
  1. ArticleController类
@RestController
@RequestMapping("/articles")
public class ArticleController {
    @Autowired
    private ArticleService articleService;
    // 新增文章信息
    @PostMapping("")
    public ResponseEntity<?> addArticle(@RequestBody Article article, @RequestParam List<Integer> categoryIds, @RequestParam List<Integer> tagIds) {
        boolean result = articleService.addArticle(article, categoryIds, tagIds);
        if (result) {
            return ResponseEntity.ok().build();
        } else {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
    // 更新文章信息
    @PutMapping("/{id}")
    public ResponseEntity<?> updateArticle(@PathVariable int id, @RequestBody Article article, @RequestParam List<Integer> categoryIds, @RequestParam List<Integer> tagIds) {
        article.setId(id);
        boolean result = articleService.updateArticle(article, categoryIds, tagIds);
        if (result) {
            return ResponseEntity.ok().build();
        } else {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
    // 删除文章信息
    @DeleteMapping("/{id}")
    public ResponseEntity<?> deleteArticle(@PathVariable int id) {
        boolean result = articleService.deleteArticle(id);
        if (result) {
            return ResponseEntity.ok().build();
        } else {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
    // 根据ID查询文章信息及其所属分类和标签信息
    @GetMapping("/{id}")
    public ResponseEntity<?> findArticleById(@PathVariable int id) {
        Article article = articleService.findArticleById(id);
        if (article != null) {
            return ResponseEntity.ok(article);
        } else {
            return ResponseEntity.notFound().build();
        }
    }
    // 查询所有文章信息及其所属分类和标签信息
    @GetMapping("")
    public ResponseEntity<?> findAllArticles() {
        List<Article> articles = articleService.findAllArticles();
        return ResponseEntity.ok(articles);
    }
}
  1. 上述示例代码中,ArticleController类处理了HTTP请求,并通过调用ArticleService类中的方法返回相应的数据。在新增、更新和删除文章信息时,通过@RequestBody注解将请求体中的JSON数据转化为Article对象,并通过@RequestParam注解获取请求参数中的categoryIds和tagIds,传递给ArticleService类中的方法进行处理。在查询文章信息时,通过@PathParm注解获取请求参数中的文章ID,并通过调用ArticleService类中的方法获取文章信息。最后,通过ResponseEntity对象返回HTTP响应,其中ok()方法返回状态码200,notFound()方法返回状态码404。

以上是使用Spring Boot框架搭建后端环境的基本步骤,可以根据实际需求进行调整和优化。

9.编写对应的代码生成器

代码生成器可以根据数据库表结构自动生成Model、Mapper、Service、Controller等类的代码,提高开发效率和代码质量。以下是一个可能的示例:

  1. CodeGenerator类
public class CodeGenerator {
    private static final String DRIVER_CLASS_NAME = "com.mysql.cj.jdbc.Driver";
    private static final String DATABASE_URL = "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8";
    private static final String USER_NAME = "root";
    private static final String PASSWORD = "123456";
    private static final String BASE_PACKAGE = "com.example.demo";
    private static final String MAPPER_PACKAGE = "mapper";
    private static final String SERVICE_PACKAGE = "service";
    private static final String CONTROLLER_PACKAGE = "controller";
    private static final String MODEL_PACKAGE = "model";
    private static final String MAPPER_SUFFIX = "Mapper";
    private static final String SERVICE_SUFFIX = "Service";
    private static final String CONTROLLER_SUFFIX = "Controller";
    private static final String MODEL_SUFFIX = "";
    public static void main(String[] args) throws Exception {
        // 创建FreeMarker配置对象
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
        // 设置FreeMarker模板文件所在目录
        cfg.setClassForTemplateLoading(CodeGenerator.class, "/templates");
        // 初始化数据源
        DataSource dataSource = createDataSource();
        // 获取数据库中所有的表名
        List<String> tableNames = getTableNames(dataSource);
        // 循环处理每个表
        for (String tableName : tableNames) {
            // 获取表的元数据信息
            List<ColumnMetaData> columnMetaDatas = getTableMetaData(dataSource, tableName);
            // 生成Mapper接口文件
            generateMapper(cfg, tableName, columnMetaDatas);
            // 生成Model类文件
            generateModel(cfg, tableName, columnMetaDatas);
            // 生成Service类文件
            generateService(cfg, tableName, columnMetaDatas);
            // 生成Controller类文件
            generateController(cfg, tableName, columnMetaDatas);
        }
    }
    // 创建数据源
    private static DataSource createDataSource() {
        HikariConfig config = new HikariConfig();
        config.setDriverClassName(DRIVER_CLASS_NAME);
        config.setJdbcUrl(DATABASE_URL);
        config.setUsername(USER_NAME);
        config.setPassword(PASSWORD);
        HikariDataSource dataSource = new HikariDataSource(config);
        return dataSource;
    }
    // 获取所有表名
    private static List<String> getTableNames(DataSource dataSource) throws SQLException {
        List<String> tableNames = new ArrayList<>();
        try (Connection connection = dataSource.getConnection()) {
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet resultSet = metaData.getTables(null, null, null, new String[]{"TABLE"});
            while (resultSet.next()) {
                String tableName = resultSet.getString("TABLE_NAME");
                tableNames.add(tableName);
            }
        }
        return tableNames;
    }
    // 获取表的元数据信息
    private static List<ColumnMetaData> getTableMetaData(DataSource dataSource, String tableName) throws SQLException {
        List<ColumnMetaData> columnMetaDatas = new ArrayList<>();
        try (Connection connection = dataSource.getConnection()) {
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet resultSet = metaData.getColumns(null, null, tableName, null);
            while (resultSet.next()) {
                String columnName = resultSet.getString("COLUMN_NAME");
                String columnType = resultSet.getString("TYPE_NAME");
                String columnComment = resultSet.getString("REMARKS");
                boolean isNullable = resultSet.getBoolean("NULLABLE");
                columnMetaDatas.add(new ColumnMetaData(columnName, columnType, columnComment, isNullable));
            }
        }
        return columnMetaDatas;
    }
    // 生成Mapper接口文件
    private static void generateMapper(Configuration cfg, String tableName, List<ColumnMetaData> columnMetaDatas) throws IOException, TemplateException {
        Map<String, Object> model = new HashMap<>();
        model.put("package", BASE_PACKAGE + "." + MAPPER_PACKAGE);
        model.put("tableName", tableName);
        model.put("modelName", toCamelCase(tableName, true) + MODEL_SUFFIX);
        model.put("columns", columnMetaDatas);
        String fileName = toCamelCase(tableName, true) + MAPPER_SUFFIX + ".java";
        generateFile(cfg, model, "mapper.ftl", fileName);
    }
    // 生成Model类文件
    private static void generateModel(Configuration cfg, String tableName, List<ColumnMetaData> columnMetaDatas) throws IOException, TemplateException {
        Map<String, Object> model = new HashMap<>();
        model.put("package", BASE_PACKAGE + "." + MODEL_PACKAGE);
        model.put("tableName", tableName);
        model.put("modelName", toCamelCase(tableName, true) + MODEL_SUFFIX);
        model.put("columns", columnMetaDatas);
        String fileName = toCamelCase(tableName, true) + MODEL_SUFFIX + ".java";
        generateFile(cfg, model, "model.ftl", fileName);
    }
    // 生成Service类文件
    private static void generateService(Configuration cfg, String tableName, List<ColumnMetaData> columnMetaDatas) throws IOException, TemplateException {
        Map<String, Object> model = new HashMap<>();
        model.put("package", BASE_PACKAGE + "." + SERVICE_PACKAGE);
        model.put("tableName", tableName);
        model.put("modelName", toCamelCase(tableName, true) + MODEL_SUFFIX);
        model.put("mapperName", toCamelCase(tableName, true) + MAPPER_SUFFIX);
        model.put("serviceSuffix", SERVICE_SUFFIX);
        model.put("columns", columnMetaDatas);
        String fileName = toCamelCase(tableName, true) + SERVICE_SUFFIX + ".java";
        generateFile(cfg, model, "service.ftl", fileName);
    }
    // 生成Controller类文件
    private static void generateController(Configuration cfg, String tableName, List<ColumnMetaData> columnMetaDatas) throws IOException, TemplateException {
        Map<String, Object> model = new HashMap<>();
        model.put("package", BASE_PACKAGE + "." + CONTROLLER_PACKAGE);
        model.put("tableName", tableName);
        model.put("modelName", toCamelCase(tableName, true) + MODEL_SUFFIX);
        model.put("serviceName", toCamelCase(tableName, true) + SERVICE_SUFFIX);
        model.put("controllerSuffix", CONTROLLER_SUFFIX);
        model.put("columns", columnMetaDatas);
        String fileName = toCamelCase(tableName, true) + CONTROLLER_SUFFIX + ".java";
        generateFile(cfg, model, "controller.ftl", fileName);
    }
    // 生成文件
    private static void generateFile(Configuration cfg, Map<String, Object> model, String templateFileName, String outputFileName) throws IOException, TemplateException {
        Template template = cfg.getTemplate(templateFileName);
        File outputDir = new File("src/main/java/" + BASE_PACKAGE.replace('.', '/') + "/" + templateFileName.replace(".", "/") + "/");
        if (!outputDir.exists()) {
            outputDir.mkdirs();
        }
        File outputFile = new File(outputDir, outputFileName);
        Writer writer = new FileWriter(outputFile);
        template.process(model, writer);
        writer.close();
    }
    // 将下划线命名转换为驼峰命名
    private static String toCamelCase(String str, boolean capitalizeFirstLetter) {
        StringBuilder sb = new StringBuilder();
        boolean capitalize = capitalizeFirstLetter;
        for (int i = 0; i < str.length(); i++) {
            char ch = str.charAt(i);
            if (ch == '_') {
                capitalize = true;
            } else {
                if (capitalize) {
                    sb.append(Character.toUpperCase(ch));
                    capitalize = false;
                } else {
                    sb.append(ch);
                }
            }
        }
        return sb.toString();
    }
}

上述示例代码中,CodeGenerator类根据数据库表结构自动生成Model、Mapper、Service、Controller等类的代码。在main方法中,首先创建FreeMarker配置对象,然后初始化数据源,获取数据库中所有的表名,循环处理每个表,依次生成Mapper接口文件、Model类文件、Service类文件和Controller类文件。在生成每个类文件时,使用FreeMarker模板引擎读取对应的模板文件,并根据模板中的变量生成代码文件。其中,模板文件中的变量包括包名、表名、类名、属性名、方法名等信息。生成的类文件存储在src/main/java目录下,按照包名和类名的层次结构进行组织。

三.实现后端接口:根据前面设计的数据库模型,实现相应的后端接口,包括博客文章的增删改查、评论的增删改查等。

实现后端接口需要使用一个Web框架,如SpringMVC、SpringBoot等。以下是一个可能的示例,使用SpringBoot框架和MyBatis持久化框架:

1.定义实体类

public class Blog {
    private Long id;
    private String title;
    private String content;
    private Date createTime;
    private Date updateTime;
    private List<Comment> comments;
    // getter/setter省略
}
public class Comment {
    private Long id;
    private Long blogId;
    private String content;
    private Date createTime;
    private Date updateTime;
    // getter/setter省略
}

2.定义Mapper接口

@Mapper
public interface BlogMapper {
    Blog findById(Long id);
    List<Blog> findAll();
    List<Blog> findByTitle(String title);
    void save(Blog blog);
    void update(Blog blog);
    void deleteById(Long id);
}
@Mapper
public interface CommentMapper {
    Comment findById(Long id);
    List<Comment> findByBlogId(Long blogId);
    void save(Comment comment);
    void update(Comment comment);
    void deleteById(Long id);
}

3.定义Service接口

public interface BlogService {
    Blog findById(Long id);
    List<Blog> findAll();
    List<Blog> findByTitle(String title);
    void save(Blog blog);
    void update(Blog blog);
    void deleteById(Long id);
}
public interface CommentService {
    Comment findById(Long id);
    List<Comment> findByBlogId(Long blogId);
    void save(Comment comment);
    void update(Comment comment);
    void deleteById(Long id);
}

4.实现Service接口

@Service
@Transactional
public class BlogServiceImpl implements BlogService {
    @Autowired
    private BlogMapper blogMapper;
    @Autowired
    private CommentMapper commentMapper;
    @Override
    public Blog findById(Long id) {
        Blog blog = blogMapper.findById(id);
        if (blog != null) {
            List<Comment> comments = commentMapper.findByBlogId(id);
            blog.setComments(comments);
        }
        return blog;
    }
    @Override
    public List<Blog> findAll() {
        return blogMapper.findAll();
    }
    @Override
    public List<Blog> findByTitle(String title) {
        return blogMapper.findByTitle(title);
    }
    @Override
    public void save(Blog blog) {
        blog.setCreateTime(new Date());
        blog.setUpdateTime(new Date());
        blogMapper.save(blog);
    }
    @Override
    public void update(Blog blog) {
        blog.setUpdateTime(new Date());
        blogMapper.update(blog);
    }
    @Override
    public void deleteById(Long id) {
        commentMapper.deleteById(id);
        blogMapper.deleteById(id);
    }
}
@Service
@Transactional
public class CommentServiceImpl implements CommentService {
    @Autowired
    private CommentMapper commentMapper;
    @Override
    public Comment findById(Long id) {
        return commentMapper.findById(id);
    }
    @Override
    public List<Comment> findByBlogId(Long blogId) {
        return commentMapper.findByBlogId(blogId);
    }
    @Override
    public void save(Comment comment) {
        comment.setCreateTime(new Date());
        comment.setUpdateTime(new Date());
        commentMapper.save(comment);
    }
    @Override
    public void update(Comment comment) {
        comment.setUpdateTime(new Date());
        commentMapper.update(comment);
    }
    @Override
    public void deleteById(Long id) {
        commentMapper.deleteById(id);
    }
}

5.定义Controller类

@RestController
@RequestMapping("/api/blogs")
public class BlogController {
    @Autowired
    private BlogService blogService;
    @GetMapping("/{id}")
    public ResponseEntity<Blog> getBlogById(@PathVariable Long id) {
        Blog blog = blogService.findById(id);
        if (blog != null) {
            return ResponseEntity.ok(blog);
        } else {
            return ResponseEntity.notFound().build();
        }
    }
    @GetMapping
    public ResponseEntity<List<Blog>> getAllBlogs() {
        List<Blog> blogs = blogService.findAll();
        return ResponseEntity.ok(blogs);
    }
    @PostMapping
    public ResponseEntity<Void> createBlog(@RequestBody Blog blog) {
        blogService.save(blog);
        return ResponseEntity.created(URI.create("/api/blogs/" + blog.getId())).build();
    }
    @PutMapping("/{id}")
    public ResponseEntity<Void> updateBlog(@PathVariable Long id, @RequestBody Blog blog) {
        Blog existingBlog = blogService.findById(id);
        if (existingBlog != null) {
            blog.setId(id);
            blogService.update(blog);
            return ResponseEntity.noContent().build();
        } else {
            return ResponseEntity.notFound().build();
           }
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteBlog(@PathVariable Long id) {
        Blog existingBlog = blogService.findById(id);
        if (existingBlog != null) {
            blogService.deleteById(id);
            return ResponseEntity.noContent().build();
        } else {
            return ResponseEntity.notFound().build();
        }
    }
}
@RestController
@RequestMapping("/api/comments")
public class CommentController {
    @Autowired
    private CommentService commentService;
    @GetMapping("/{id}")
    public ResponseEntity<Comment> getCommentById(@PathVariable Long id) {
        Comment comment = commentService.findById(id);
        if (comment != null) {
            return ResponseEntity.ok(comment);
        } else {
            return ResponseEntity.notFound().build();
        }
    }
    @GetMapping("/blog/{blogId}")
    public ResponseEntity<List<Comment>> getCommentsByBlogId(@PathVariable Long blogId) {
        List<Comment> comments = commentService.findByBlogId(blogId);
        return ResponseEntity.ok(comments);
    }
    @PostMapping
    public ResponseEntity<Void> createComment(@RequestBody Comment comment) {
        commentService.save(comment);
        return ResponseEntity.created(URI.create("/api/comments/" + comment.getId())).build();
    }
    @PutMapping("/{id}")
    public ResponseEntity<Void> updateComment(@PathVariable Long id, @RequestBody Comment comment) {
        Comment existingComment = commentService.findById(id);
        if (existingComment != null) {
            comment.setId(id);
            commentService.update(comment);
            return ResponseEntity.noContent().build();
        } else {
            return ResponseEntity.notFound().build();
        }
    }
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteComment(@PathVariable Long id) {
        Comment existingComment = commentService.findById(id);
        if (existingComment != null) {
            commentService.deleteById(id);
            return ResponseEntity.noContent().build();
        } else {
            return ResponseEntity.notFound().build();
        }
    }
}

6.测试接口

可以使用Postman等工具测试上述接口的正确性和可用性。例如,可以发送以下HTTP请求:

GET /api/blogs/1

返回响应:

{
    "id": 1,
    "title": "My First Blog",
    "content": "This is my first blog post.",
    "createTime": "2021-01-01T00:00:00Z",
    "updateTime": "2021-01-01T00:00:00Z",
    "comments": [
        {
            "id": 1,
            "blogId": 1,
            "content": "Great post!",
            "createTime": "2021-01-02T00:00:00Z",
            "updateTime": "2021-01-02T00:00:00Z"
        },
        {
            "id": 2,
            "blogId": 1,
            "content": "I disagree with your point.",
            "createTime": "2021-01-03T00:00:00Z",
            "updateTime": "2021-01-03T00:00:00Z"
        }
    ]
}

四.搭建前端环境:使用Vue框架搭建前端环境,可以使用Vue CLI等工具进行创建。

使用Vue CLI搭建前端环境的步骤如下:

1.安装Node.js

Vue CLI需要Node.js环境,可以从Node.js官网下载并安装对应操作系统的Node.js版本。

2.安装Vue CLI

打开命令行工具(如Windows上的cmd或PowerShell、macOS上的终端),运行以下命令安装Vue CLI:

npm install -g @vue/cli

该命令会在全局范围内安装Vue CLI。

3.创建新的Vue项目

在命令行工具中进入要创建Vue项目的目录,运行以下命令:

vue create my-project

其中,my-project是项目的名称,可以根据实际情况进行修改。

在运行命令后,Vue CLI会提示选择要使用的特性,如Babel、TypeScript、CSS预处理器等。按照需要进行选择或保持默认设置即可。

4.运行Vue项目

在项目目录下,运行以下命令启动本地开发服务器:

npm run serve

该命令会启动本地的开发服务器,并在浏览器中打开网页。在开发过程中,可以在编辑器中修改代码并保存,浏览器会自动刷新并显示最新的页面。

五.实现前端页面:根据设计的页面原型,使用Vue框架实现前端页面,包括博客列表、博客详情、评论列表、分类标签等。

以下是一个可能的示例,使用Vue框架和Axios库进行实现:

1.安装Axios

Axios是一个基于Promise的HTTP库,用于发送Ajax请求。在命令行工具中运行以下命令安装Axios:

npm install axios

2.定义Vue组件

在Vue项目的src/components目录下,定义以下组件:

BlogList.vue:用于显示博客列表。代码如下:

<template>
  <div>
    <h2>Blog List</h2>
    <ul>
      <li v-for="blog in blogs" :key="blog.id">
        <router-link :to="{ name: 'BlogDetail', params: { id: blog.id } }">{{ blog.title }}</router-link>
        <p>{{ blog.content }}</p>
        <span>{{ blog.createTime }}</span>
      </li>
    </ul>
  </div>
</template>
<script>
import axios from 'axios';
export default {
  data() {
    return {
      blogs: [],
    };
  },
  mounted() {
    axios.get('/api/blogs').then((response) => {
      this.blogs = response.data;
    });
  },
};
</script>

BlogDetail.vue:用于显示单篇博客的详情和评论列表。代码如下:

<template>
  <div>
    <h2>{{ blog.title }}</h2>
    <p>{{ blog.content }}</p>
    <span>{{ blog.createTime }}</span>
    <h3>Comments</h3>
    <ul>
      <li v-for="comment in comments" :key="comment.id">
        <p>{{ comment.content }}</p>
        <span>{{ comment.createTime }}</span>
      </li>
    </ul>
  </div>
</template>
<script>
import axios from 'axios';
export default {
  data() {
    return {
      blog: null,
      comments: [],
    };
  },
  mounted() {
    const id = this.$route.params.id;
    axios.get(`/api/blogs/${id}`).then((response) => {
      this.blog = response.data;
      this.comments = this.blog.comments;
    });
  },
};
</script>

TagList.vue:用于显示博客的分类标签。代码如下:

<template>
  <div>
    <h2>Tags</h2>
    <ul>
      <li v-for="tag in tags" :key="tag">
        <router-link :to="{ name: 'BlogList', query: { tag: tag } }">{{ tag }}</router-link>
      </li>
    </ul>
  </div>
</template>
<script>
import axios from 'axios';
export default {
  data() {
    return {
      tags: [],
    };
  },
  mounted() {
    axios.get('/api/tags').then((response) => {
      this.tags = response.data;
    });
  },
};
</script>

3.定义Vue路由

在Vue项目的src/router/index.js文件中,定义以下路由:

import Vue from 'vue';
import VueRouter from 'vue-router';
import BlogList from '@/components/BlogList.vue';
import BlogDetail from '@/components/BlogDetail.vue';
Vue.use(VueRouter);
const routes = [
  {
    path: '/',
    redirect: '/blogs',
  },
  {
    path: '/blogs',
    name: 'BlogList',
    component: BlogList,
  },
  {
    path: '/blogs/:id',
    name: 'BlogDetail',
    component: BlogDetail,
  },
];
const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes,
});
export default router;

4.在Vue项目的src/App.vue文件中,添加以下代码:

<template>
  <div id="app">
    <div>
      <router-link to="/blogs">Blog List</router-link>
      <router-link to="/tags">Tags</router-link>
    </div>
    <router-view />
  </div>
</template>
<script>
export default {
  name: 'App',
};
</script>

5.运行Vue项目

在命令行工具中运行以下命令启动本地开发服务器:

npm run serve

在浏览器中打开http://localhost:8080 ↗,即可看到博客列表和分类标签。点击博客标题或分类标签,可以进入博客详情或显示对应标签的博客列表。

六.集成后端接口:在前端页面中集成后端接口,通过axios等工具调用后端接口获取数据展示在页面上。

在前端页面中集成后端接口,可以通过axios等工具调用后端接口获取数据展示在页面上。以下是一个可能的示例:

1.定义后端接口

在后端(例如Node.js)中,定义以下接口:

GET /api/blogs:获取博客列表
GET /api/blogs/:id:获取指定id的博客
GET /api/tags:获取博客分类标签列表

在接口中,可以返回JSON格式的数据,例如:

[
  {
    "id": 1,
    "title": "My First Blog",
    "content": "This is my first blog",
    "createTime": "2022-01-01T00:00:00.000Z",
    "tags": ["tag1", "tag2"],
    "comments": [
      {
        "id": 1,
        "content": "Great blog",
        "createTime": "2022-01-02T00:00:00.000Z"
      },
      {
        "id": 2,
        "content": "Nice work",
        "createTime": "2022-01-03T00:00:00.000Z"
      }
    ]
  },
  {
    "id": 2,
    "title": "My Second Blog",
    "content": "This is my second blog",
    "createTime": "2022-01-04T00:00:00.000Z",
    "tags": ["tag2", "tag3"],
    "comments": [
      {
        "id": 3,
        "content": "Keep going",
        "createTime": "2022-01-05T00:00:00.000Z"
      }
    ]
  }
]

2.在前端页面中调用后端接口

在Vue项目的src/components目录中,修改之前定义的组件,使用axios库调用后端接口获取数据。例如,在BlogList.vue组件中,可以修改mounted方法如下:

mounted() {
  axios.get('/api/blogs').then((response) => {
    this.blogs = response.data;
  }).catch((error) => {
    console.error(error);
  });
}

在BlogDetail.vue组件中,可以修改mounted方法如下:

mounted() {
  const id = this.$route.params.id;
  axios.get(`/api/blogs/${id}`).then((response) => {
    this.blog = response.data;
    this.comments = this.blog.comments;
  }).catch((error) => {
    console.error(error);
  });
}

在TagList.vue组件中,可以修改mounted方法如下:

mounted() {
  axios.get('/api/tags').then((response) => {
    this.tags = response.data;
  }).catch((error) => {
    console.error(error);
  });
}

3.运行Vue项目并测试

在命令行工具中运行以下命令启动本地开发服务器:

npm run serve

在浏览器中打开http://localhost:8080 ↗,即可看到博客列表和分类标签。点击博客标题或分类标签,可以进入博客详情或显示对应标签的博客列表。在后端接口返回的JSON数据中,可以包含博客内容、分类标签、评论等信息,前端页面可以根据需要进行展示。

七.部署上线:将前端页面和后端接口部署到服务器上,可以使用Nginx等工具进行部署和代理。

将前端页面和后端接口部署到服务器上,可以使用Nginx等工具进行部署和代理。以下是一个可能的示例:

1.部署后端接口

将后端接口部署到服务器上,可以使用PM2等工具进行管理和启动。例如,在服务器上创建一个名为myapp的目录,将后端代码上传到该目录下,然后使用以下命令安装PM2并启动应用:

npm install pm2 -g
pm2 start index.js --name myapp

其中,index.js是后端代码的入口文件,myapp是应用的名称。使用pm2 logs myapp命令可以查看应用的日志。

2.部署前端页面

在本地使用npm run build命令可以将Vue项目打包成静态文件,然后将打包后的文件上传到服务器上。例如,在服务器上创建一个名为myapp的目录,将打包后的文件上传到该目录下。在Nginx配置文件中添加以下配置:

server {
  listen 80;
  server_name myapp.com;
  root /path/to/myapp/dist;
  index index.html;
  location / {
    try_files $uri $uri/ /index.html;
  }
}

其中,myapp.com是域名或服务器IP地址,/path/to/myapp是前端页面的根目录。在location / {}中,try_files指令用于尝试访问请求的文件或目录,如果不存在则返回index.html文件。

3.配置反向代理

如果前端页面和后端接口部署在不同的服务器上,可以使用Nginx等工具进行反向代理。例如,在前端服务器上的Nginx配置文件中,可以添加以下配置:

server {
  listen 80;
  server_name myapp.com;
  root /path/to/myapp/dist;
  index index.html;
  location /api/ {
    proxy_pass http://backend-server/;
  }
  location / {
    try_files $uri $uri/ /index.html;
  }
}

其中,myapp.com是域名或服务器IP地址,/path/to/myapp是前端页面的根目录,backend-server是后端接口的地址。在location /api/ {}中,proxy_pass指令用于将请求转发到后端接口。在location / {}中,try_files指令用于尝试访问请求的文件或目录,如果不存在则返回index.html文件。

4.重启Nginx和PM2

在修改Nginx配置文件或部署新的代码后,需要重启Nginx和PM2才能使更改生效。可以使用以下命令重启Nginx和PM2:

nginx -s reload
pm2 restart myapp

其中,myapp是应用的名称。使用pm2 logs myapp命令可以查看应用的日志。

实现个人博客需要前后端技术的综合运用,需要具备一定的数据库、后端、前端开发经验。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1天前
|
关系型数据库 MySQL 数据库连接
用Navicat备份Mysql演示系统数据库的时候出:Too Many Connections
用Navicat备份Mysql演示系统数据库的时候出:Too Many Connections
|
2天前
|
前端开发 Java 关系型数据库
Java医院绩效考核系统源码B/S架构+springboot三级公立医院绩效考核系统源码 医院综合绩效核算系统源码
作为医院用综合绩效核算系统,系统需要和his系统进行对接,按照设定周期,从his系统获取医院科室和医生、护士、其他人员工作量,对没有录入信息化系统的工作量,绩效考核系统设有手工录入功能(可以批量导入),对获取的数据系统按照设定的公式进行汇算,且设置审核机制,可以退回修正,系统功能强大,完全模拟医院实际绩效核算过程,且每步核算都可以进行调整和参数设置,能适应医院多种绩效核算方式。
20 2
|
2天前
|
运维 监控 Java
springboot基层区域HIS系统源码
医疗(医院)机构正式使用云HIS系统之前,要先进行院内基础数据的配置,主要在数据管理模块中进行,由系统管理员来操作。
9 0
|
4天前
|
存储 关系型数据库 MySQL
9.3 【MySQL】系统表空间
9.3 【MySQL】系统表空间
10 0
|
4天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
45 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
4天前
|
安全 关系型数据库 MySQL
CentOS 7系统加固详细方案SSH FTP MYSQL加固
CentOS 7系统加固详细方案SSH FTP MYSQL加固
|
1天前
|
关系型数据库 MySQL 数据库
docker MySQL删除数据库时的错误(errno: 39)
docker MySQL删除数据库时的错误(errno: 39)
|
2天前
|
存储 Oracle 关系型数据库
oracle 数据库 迁移 mysql数据库
将 Oracle 数据库迁移到 MySQL 是一项复杂的任务,因为这两种数据库管理系统具有不同的架构、语法和功能。
14 0
|
9天前
|
关系型数据库 MySQL Linux
【MySQL-10】数据库函数-案例演示【字符串/数值/日期/流程控制函数】(代码演示&可cv代码)
【MySQL-10】数据库函数-案例演示【字符串/数值/日期/流程控制函数】(代码演示&可cv代码)
【MySQL-10】数据库函数-案例演示【字符串/数值/日期/流程控制函数】(代码演示&可cv代码)
|
9天前
|
SQL 关系型数据库 MySQL
【MySQL-5】DDL的数据库操作:查询&创建&删除&使用(可cv代码+演示图)
【MySQL-5】DDL的数据库操作:查询&创建&删除&使用(可cv代码+演示图)