文章目录
- 一.设计数据库模型:首先需要设计博客数据库模型,包括博客文章、评论、分类、标签等表的设计。可以使用MySQL Workbench等工具进行建模,也可以手动编写SQL语句创建表。
- 二.搭建后端环境:使用Spring Boot框架搭建后端环境,包括配置Maven、集成MyBatis、配置数据库连接等。可以使用IDEA等开发工具进行开发。
- 1.安装Java开发环境:在电脑上安装JDK开发环境,建议使用JDK8及以上版本。
- 2.安装Maven:Maven是一个构建工具,可以自动管理Java项目中的依赖关系和构建过程,建议安装最新版本的Maven。
- 3.创建Spring Boot项目:可以使用Spring Initializr创建Spring Boot项目,选择Maven项目、选择相应的Spring Boot版本、选择Web、MyBatis等相关依赖。
- 4.配置数据库连接:在application.properties或application.yml文件中配置数据库连接信息,包括数据库URL、用户名、密码等。
- 5.配置MyBatis:在application.properties或application.yml文件中配置MyBatis相关信息,包括MyBatis配置文件路径、Mapper文件路径等。
- 6.编写实体类:根据数据库模型设计,编写与数据库表对应的实体类。
- 7.编写Mapper类:编写Mapper接口和Mapper XML文件,实现与数据库的交互操作,包括增删改查等。
- 8.编写Service类:编写Service类,实现业务逻辑的处理,并调用Mapper类中的方法实现数据的读写操作。
- 9.编写对应的代码生成器
实现个人博客的步骤如下:
一.设计数据库模型:首先需要设计博客数据库模型,包括博客文章、评论、分类、标签等表的设计。可以使用MySQL Workbench等工具进行建模,也可以手动编写SQL语句创建表。
下面是一个简单的数据库模型设计,包括以下表:
- 文章表(article):存储博客文章的信息,包括文章ID、文章标题、文章内容、发布时间、最后修改时间、文章所属分类ID等字段。
- 评论表(comment):存储博客文章的评论信息,包括评论ID、评论内容、评论时间、评论人昵称、评论人邮箱、评论人网址、评论所属文章ID等字段。
- 分类表(category):存储博客文章的分类信息,包括分类ID、分类名称等字段。
- 标签表(tag):存储博客文章的标签信息,包括标签ID、标签名称等字段。
- 文章-分类关联表(article_category):存储文章和分类之间的关联关系,包括文章ID和分类ID两个字段。
- 文章-标签关联表(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项目中配置数据库连接的步骤:
- 在项目的src/main/resources目录下创建application.properties或application.yml文件,用于存储配置信息。
- 在配置文件中添加数据库连接信息,例如:
# 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属性指定数据库驱动的类名。
- 保存配置文件,重启项目,即可使用配置的数据库连接信息连接到相应的数据库。
以上是在Spring Boot项目中配置数据库连接的基本步骤,可以根据实际需求进行调整和优化。
5.配置MyBatis:在application.properties或application.yml文件中配置MyBatis相关信息,包括MyBatis配置文件路径、Mapper文件路径等。
下面是在Spring Boot项目中配置MyBatis的步骤:
- 在项目的src/main/resources目录下创建mybatis-config.xml文件,用于存储MyBatis的配置信息。
- 在配置文件中添加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的各种设置,例如是否启用缓存、是否启用延迟加载等。
- 在配置文件中添加Mapper文件的路径信息,例如:
mybatis.mapper-locations=classpath:mapper/*.xml
- 其中,mybatis.mapper-locations属性指定Mapper文件的路径,可以使用通配符指定多个Mapper文件。
- 在Spring Boot的配置文件中添加MyBatis相关的配置信息,例如:
# MyBatis配置文件路径 mybatis.config-location=classpath:mybatis-config.xml
- 其中,mybatis.config-location属性指定MyBatis的配置文件路径。
- 在Mapper接口中使用@Mapper注解,告诉Spring Boot需要将该接口注册为Mapper。
- 在Mapper XML文件中编写SQL语句,实现与数据库的交互操作。
以上是在Spring Boot项目中配置MyBatis的基本步骤,可以根据实际需求进行调整和优化。
6.编写实体类:根据数据库模型设计,编写与数据库表对应的实体类。
下面是编写实体类的基本步骤:
- 根据数据库模型设计,确定实体类的属性和对应的数据库表字段。
- 在项目的src/main/java目录下创建实体类的包,例如com.example.demo.entity。
- 在实体类的包中创建与数据库表对应的实体类,例如User类。
- 在实体类中添加属性和对应的getter/setter方法,例如:
public class User { private Long id; private String name; private Integer age; // 省略getter/setter方法 } ```` 其中,id、name、age属性对应数据库表中的id、name、age字段。
- (可选)使用注解或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文件,实现与数据库的交互操作,包括增删改查等。
以下是一个可能的示例:
- ArticleMapper接口
public interface ArticleMapper { // 根据ID查询文章信息及其所属分类和标签信息 Article findArticleById(int id); // 查询所有文章信息及其所属分类和标签信息 List<Article> findAllArticles(); // 新增文章信息 int addArticle(Article article); // 更新文章信息 int updateArticle(Article article); // 删除文章信息 int deleteArticle(int id); }
- 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类中的方法实现数据的读写操作。
以下是一个可能的示例:
- 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属性中,返回给调用方。
- 编写Controller类:编写Controller类,处理HTTP请求,调用Service类中的方法返回相应的数据。
- 启动项目:使用IDEA等开发工具启动项目,测试接口是否正常。编写Controller类,处理HTTP请求,调用Service类中的方法返回相应的数据。以下是一个可能的示例:
- 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); } }
- 上述示例代码中,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等类的代码,提高开发效率和代码质量。以下是一个可能的示例:
- 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命令可以查看应用的日志。
实现个人博客需要前后端技术的综合运用,需要具备一定的数据库、后端、前端开发经验。