6-15笔记

  • 实现登录查询(根据用户名密码查询用户)。
  • 实现发布新闻(插入news表,同时更新用户news_count),使用事务保证一致性。

二、核心知识点(简要)

  • 事务:一组操作要么全部成功,要么全部失败。
  • setAutoCommit(false):关闭自动提交,开启事务。
  • commit()rollback():提交或回滚事务。
  • ResultSet:查询结果集,使用next()遍历。

三、操作步骤

1. 创建news

  • 打开SQLyog,选中weitout数据库,点+号,新建查询

  • 在新的询问中输入如下内容:

    CREATE TABLE `news` (-- 创建名为 news 的表
        `id` INT PRIMARY KEY AUTO_INCREMENT,-- 【主键】定义 id 字段,类型为整数,设为主键并开启自增
        `user_id` INT NOT NULL,--记录这条新闻是由哪个用户发布的,关联 user 表的 id
        `title` VARCHAR(200) NOT NULL, -- 【标题】
        `content` TEXT, -- 【正文】定义 content 字段,文本类型
        `publish_time` DATETIME DEFAULT CURRENT_TIMESTAMP,--【发布时间】定义 publish_time 字段,日期时间类型
        `view_count` INT DEFAULT 0,-- 【浏览量】默认为 0,后续可通过 UPDATE 语句累加
        FOREIGN KEY (`user_id`) REFERENCES `user`(`id`)-- 【外键约束】建立 user_id 与 user 表 id 之间的关联,确保发布的 user_id 必须在 user 表中真实存在
    );
    
  • 然后选中全部内容,点击运行

  • 点开weitoutiao数据库中的表,可以看到新增了一张名为news的表,再点击表数据,点击刷新,新的表就显示出来,表示news数据表已成功创)

2. 创建News实体类(包含id, userId, title, content, publishTime, viewCount)

  • 打开IDEA,继续在上个工程上修改,右键entity,新建java类,News类

  • News类中编写代码如下:

    package com.weitoutiao.entity;// 【包声明】表明该类属于 entity(实体)层
    
    import java.time.LocalDateTime;// 【导入包】引入 Java 8 的时间处理类,对应数据库中的 DATETIME 类型
    
    public class News {// News 实体类,对应数据库中的 news 表
        private Integer id; // 【主键】对应表中的 id 字段
        private Integer userId;// 【外键】对应表中的 user_id 字段,表示发布该新闻的用户ID
        private String title;// 存储新闻的简短标题
        private String content; // 存储新闻的详细内容
        private LocalDateTime publishTime;  //使用 LocalDateTime精确匹配数据库的DATETIME格式
        private Integer viewCount;// 统计该新闻被查看的次数
    
    }
    

3. 在UserDAO中添加login方法

  • login方法如下:

    public User login(String username, String password) {
        String sql = "SELECT id, username, role, news_count FROM user WHERE username=? AND password=?";
        // 定义 SQL 查询语句:从 user 表中查找匹配的用户名和密码,并只查询需要的字段(id, username, role, news_count)
        try (Connection conn = DBUtil.getConnection();// 调用工具类获取数据库连接对象
             PreparedStatement ps = conn.prepareStatement(sql)) {// 预编译 SQL 语句,生成 PreparedStatement 对象
            ps.setString(1, username); // 设置 SQL 语句中第一个问号 (?) 的值为传入的 username 参数
            ps.setString(2, password);
            ResultSet rs = ps.executeQuery();// 执行 SQL 查询语句,并将结果存储在 ResultSet 结果集对象中
            if (rs.next()) {// 判断结果集中是否有数据(即是否找到了匹配的用户)
                User user = new User();// 如果找到用户,实例化一个新的 User 对象用于封装数据
                user.setId(rs.getInt("id"));// 从结果集中获取 "id" 字段的值,并设置到 user 对象的 id 属性中
                user.setUsername(rs.getString("username"));
                user.setRole(rs.getString("role"));
                user.setNewsCount(rs.getInt("news_count"));
                return user;// 将封装好数据的 user 对象返回给调用者,表示登录成功
            }
        } catch (SQLException e) {// 捕获在执行数据库操作过程中可能发生的 SQL 异常
            e.printStackTrace();// 在控制台打印异常的堆栈信息,方便开发者调试和排查错误
        }
        return null; //如果没有找到匹配的用户(if 条件不成立)or发生了异常,则返回null,表示登录失败
    }
    
  • UserDao类中,login方法添加的位置如下:

  • ResultSet会爆红,鼠标悬停之上,选择导入类

  • user的set方法还会爆红,这是因为User类的选择器没有配置,再返回到User实体类中添加

  • 在User实体类中的空白位置右键,选择生成,再选择Getter和Setter,生成set和get方法

  • 可以全选中,一次全部生成

  • 最后生成的方法如下所示:

  • 再看UserDao类中的set已经不红了。

4. 创建NewsDAO实现发布新闻(事务)

  • 右键dao包名,新建NewsDAO

  • NewsDAO类中的代码如下:

    package com.weitoutiao.dao;// 【包声明】表明该类属于 dao(数据访问)层
    
    import com.weitoutiao.util.DBUtil;// 【导入工具类】引入自定义的数据库连接工具
    import java.sql.Connection;// 【导入类】数据库连接接口
    import java.sql.PreparedStatement;// 【导入类】预编译 SQL 语句接口
    import java.sql.SQLException;// 【导入异常】SQL 异常处理类
    
    public class NewsDAO {//新闻数据访问对象
        //发布新闻的方法
        public boolean publish(int userId, String title, String content) {
            //【定义 SQL】向 news 表中插入一条新记录,使用 ? 占位符防注入
            String insertSql = "INSERT INTO news (user_id, title, content) VALUES (?, ?, ?)";
            // 【定义 SQL】根据用户 id,将该用户的发帖总数 (news_count) 累加 1
            String updateSql = "UPDATE user SET news_count = news_count + 1 WHERE id = ?";
            Connection conn = null;
            PreparedStatement psInsert = null;
            PreparedStatement psUpdate = null;
            try {
                conn = DBUtil.getConnection();// 【获取连接】通过工具类获取一个活跃的数据库连接
                conn.setAutoCommit(false);  // 开启事务,关闭自动提交功能!这是保证数据一致性的关键一步
                psInsert = conn.prepareStatement(insertSql);// 【准备插入语句】预编译第一条 SQL(插入新闻)
                psInsert.setInt(1, userId);// 【设置参数】依次为占位符赋值:用户ID、标题、内容
                psInsert.setString(2, title);
                psInsert.setString(3, content);
                psInsert.executeUpdate();// 【执行插入】将新闻写入数据库(此时尚未真正落盘)
                psUpdate = conn.prepareStatement(updateSql);// 【准备更新语句】预编译第二条 SQL(增加用户发帖数)
                psUpdate.setInt(1, userId);// 【设置参数】指定要更新的用户 ID
                psUpdate.executeUpdate();// 【执行更新】增加发帖计数(同样尚未真正落盘)
                conn.commit();  // 提交事务,两步操作都成功后,统一提交!让所有修改真正生效
                return true; // 【返回结果】全部顺利执行,返回 true 表示发布成功
            } catch (SQLException e) {// 【捕获异常】如果上述任何一步发生错误(如插入成功但更新失败),进入此块
               // 【回滚事务】检查连接是否存在,保证“插入新闻”和“更新用户发帖数”一致
                if (conn != null) {
                    try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); }
                }
                e.printStackTrace();
                return false;
            } finally {
                try { if (psInsert != null) psInsert.close(); } catch (SQLException e) {}
                try { if (psUpdate != null) psUpdate.close(); } catch (SQLException e) {}
                try { if (conn != null) conn.close(); } catch (SQLException e) {}
            }
        }
    }
    

5. 修改Main测试登录和发布新闻

  • 具体内容如下:

    package com.weitoutiao;// 【包声明】这是项目的根包,通常用于存放程序的启动类 Main
    
    import com.weitoutiao.dao.UserDAO;// 【导入 DAO 类】引入用户数据访问对象
    import com.weitoutiao.dao.NewsDAO;// 【导入 DAO 类】引入新闻数据访问对象
    import com.weitoutiao.entity.User;// 【导入实体类】引入 User 实体类,用于接收登录返回的数据
    
    public class Main {
        public static void main(String[] args) {
            UserDAO userDAO = new UserDAO();
            User user = userDAO.login("testuser", "123456");// 【调用登录方法】传入硬编码的用户名和密码进行验证
            if (user != null) {
                System.out.println("登录成功,用户ID:" + user.getId());
                NewsDAO newsDAO = new NewsDAO();
                boolean published = newsDAO.publish(user.getId(), "我的第一条新闻", "内容");
                System.out.println(published ? "发布成功" : "发布失败");
            } else {
                System.out.println("登录失败");
            }
        }
    }
    

6.运行测试

  • 创建SpringBoot 3.1.5项目,配置数据库连接和MyBatis-Plus。
  • 编写HelloController验证项目启动。

二、核心知识点(简要)

  • SpringBoot:自动配置,简化Spring应用开发。
  • application.yml:配置文件,使用YAML格式。
  • @RestController:标记控制器,返回JSON。
  • MyBatis-Plus:增强MyBatis,简化数据库操作。

三、操作步骤

1. 创建项目

  • 文件➡️新建➡️项目

  • 配置如下:

    配置完成后,点下一步

  • 先选一下SpringBoot的版本,用3.5.15,依赖选这3个:Spring Web、MySQL Driver、Lombok。

  • 选完之后,右侧会显示已添加的依赖,最后点击创建

2. 添加MyBatis-Plus依赖(在pom.xml中):

  • 添加的内容:

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        <version>3.5.5</version>
    </dependency><!--告别繁琐的 Connection、PreparedStatement-->
    
  • pom.xml中添加的位置如下,添加完之后点一下maven的刷新按钮,下载依赖,就不会红了。

3. 配置application.yml

  • src/main/resources文件夹中,找到application.properties文件,重命名为application.yml

  • 其中内容改为:

    #--JDBC:DBUtil
    # ======================== 服务器配置 ========================
    server:
      port: 8080 # 【端口号】指定当前 Web 应用启动时监听的端口为 8080
    # ======================== Spring 基础配置 ========================
    spring:
      application:
        name: WeiTouTiao-SpringBoot # 【应用名称】为当前项目命名,方便在日志或注册中心中识别
      datasource: # 【数据库连接 URL】指定 MySQL 数据库的地址、库名及连接参数
        url: jdbc:mysql://localhost:3306/weitoutiao? #本机 3306 端口下的 weitoutiao 数据库
        useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8
        username: root # 【数据库账号】登录 MySQL 的用户名
        password: root # 【数据库密码】登录 MySQL 的密码
        driver-class-name: com.mysql.cj.jdbc.Driver
    # MyBatis-Plus 框架配置 
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
        map-underscore-to-camel-case: true
      global-config:
        db-config:
          id-type: auto
          # 【逻辑删除字段】指定表中用于标记“是否被删除”的字段名为 deleted
          logic-delete-field: deleted
          logic-delete-value: 1
          logic-not-delete-value: 0
    

4.编写HelloController

  • 右键com.weitoutiao包名,新建controller.HelloController

  • HelloController类中的代码如下:

    // 【包声明】表明该类属于 controller(控制)层,专门负责处理前端请求
    package com.weitoutiao.controller;
    
    import org.springframework.web.bind.annotation.GetMapping;// 【导入注解】引入 GET 请求映射注解
    import org.springframework.web.bind.annotation.RestController;// 【导入注解】引入 REST 控制器注解
    // 【REST 控制器注解】这是一个组合注解(相当于 @Controller + @ResponseBody)
                    // 作用1:告诉 Spring 这是一个处理 Web 请求的控制器类,并将其注册到容器中
                    // 作用2:将该类中所有方法的返回值直接作为 HTTP 响应体(通常是 JSON 或纯文本)返回给前端
    @RestController
    public class HelloController {
        @GetMapping("/hello")// 【GET 请求映射】限定该方法只处理 GET 类型的 HTTP 请求
        public String hello() { // 当浏览器或前端访问 http://localhost:8080/hello 时,就会触发这个方法
            return "微头条后端启动成功!";// 【返回响应】将字符串直接写入 HTTP 响应体中
        }
    }
    

5.运行测试

核心知识点

LambdaQueryWrapper:类型安全的查询条件构造器。

*分页插件**:自动拦截SQL添加LIMIT,并查询总记录数。

Page:分页参数对象。

三、操作步骤

  1. 创建分页配置类

    右键com.weitoutiao,创建config.MyBatisPlusConfig

    类中代码如下:

    package com.weitoutiao.config;
    
    import com.baomidou.mybatisplus.annotation.DbType;
    import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class MyBatisPlusConfig {
    
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            // 创建分页插件并指定数据库类型(根据实际使用的数据库选择)
            PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
            // 可选:超出最大页码时是否处理(false表示返回空数据,true表示返回最后一页)
            paginationInterceptor.setOverflow(false);
            // 将分页插件添加到拦截器容器[reference:0]
            interceptor.addInnerInterceptor(paginationInterceptor);
            return interceptor;
        }
    }
    
  2. 创建Service(使用MyBatis-Plus的IService)

    右键com.weitoutiao,创建service.NewsService

    NewsService中的代码如下:

    package com.weitoutiao.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.weitoutiao.entity.News;
    
    public interface NewsService extends IService<News> {
    }
    

    右键service,创建impl.NewsServiceImpl的实现类

    NewsServiceImpl中的代码如下:

    package com.weitoutiao.service.impl;
    
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.weitoutiao.entity.News;
    import com.weitoutiao.mapper.NewsMapper;
    import com.weitoutiao.service.NewsService;
    import org.springframework.stereotype.Service;
    
    @Service
    public class NewsServiceImpl extends ServiceImpl<NewsMapper, News> implements NewsService {
    }
    
  3. 测试分页

    打开test文件夹中的testSelect文件,在代码最后添加如下测试代码:

    @SpringBootTest
    class NewsServiceTest {
        @Autowired
        private NewsService newsService;
        @Test
        void testPage() {
            Page<News> page = new Page<>(1, 5);// 第1步:准备分页参数(我要第1页,每页5条)
            LambdaQueryWrapper<News> wrapper = new LambdaQueryWrapper<>();// 创建条件构造器(一个装条件的盒子)
            wrapper.orderByDesc(News::getPublishTime);//放条件:按发布时间排序
            Page<News> result = newsService.page(page, wrapper);// 调用 Service 的分页方法,把分页参数和条件盒子一起传进去
            System.out.println("总记录数:" + result.getTotal());
            System.out.println("当前页数据:" + result.getRecords());
        }
    }
    

    红色错误的代码,可以鼠标悬停到错误位置上,选择导入类即可解决

    点击testPage旁边的运行按钮,进行测试

    会报如下错误:

    因为 MyBatis-Plus 开启了逻辑删除功能,但 news 表中没有 deleted 字段

  4. 所以需要打开SQLyog, 在查询中执行如下语句

    ALTER TABLE `news` ADD COLUMN `deleted` TINYINT DEFAULT 0 COMMENT '逻辑删除(0未删除,1已删除)';
    

    SQLyog中,先选中该条语句,再点击执行

    点刷新,表中就会多一列deleted

  5. 回到IDEA中,再执行testPage的测试,还会报如下错误

    需要在SQLyog中再执行如下语句:

    ALTER TABLE `news` 
    ADD COLUMN `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间';
    
  6. 回到IDEA中,再执行testPage的测试,程序log已成功打印

posted @ 2026-06-18 15:02  伊沃·凡特森  阅读(1)  评论(0)    收藏  举报