945-SpringBoot+MyBatis搭建迷你小程序

https://www.imooc.com/learn/945

简介:用Spring Boot框架大大简化了新Spring应用的初始搭建以及开发过程,在开发人员中越来越受到欢迎。微信小程序作为目前炙手可热的应用,很有可能在未来占据轻应用的市场。本门课程的主要目的是将两者结合起来,同时希望作为入门翔仔之前实战课的一个更低门槛的课程进行讲解。本课分为三大部分:第一部分带领大家明确需求,从零开始进行SpringBoot+MyBatis的框架搭建;第二部分咱们将依据业务模块配合UT实现后端完整的增删改查功能;最后我们将来到微信小程序开发部分,帮大家入门微信小程序开发,并教会大家前后联调。老师相关实战课程:http://coding.imooc.com/class/144.html 老师的面试课程: https://coding.imooc.com/class/303.html

讲师源码-前端
讲师源码-后端

第1章 课程介绍

本章节讲解本门课程的主旨,便于同学掌握课程的来龙去脉。

1-1 课程介绍

学习本门课程的作用:

  • 学会从0搭建后端的SpringBoot + MyBatis框架
  • 微信小程序入门
  • 规范的代码编写

本门课程的框架:

  • 从0搭建后端的springboot + mybatis框架
  • 实现后端的业务功能
  • 实现本地微信小程序的前端开发
  • 前端与后端的联调

技术储备要求:

  • 基本的Java知识
  • 基本的前端开发知识(简单了解HTML,JS等)
  • Spring , Mybatis基础知识(不会也没关系)

1-2 最终效果展示

登录、区域信息的增删改查

第2章 项目设计及框架搭建

本章节讲解项目的需求分析、数据库设计以及后端SpringBoot + Mybatis框架的搭建

2-1 SpringBoot的搭建与启动上

Spring官网

server.context-path=/demo //设置上下文路径  
server.port=8888 //设置端口号

2-3 功能点的明确

区域信息的增删改查

2-4 表设计与实体类的创建

  • 表设计

    CREATE DATABASE IF NOT EXISTS `imooc_945_mini_app`;
    USE `imooc_945_mini_app`;
    
    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    -- Table structure for tb_area
    -- ----------------------------
    DROP TABLE IF EXISTS `tb_area`;
    CREATE TABLE `tb_area` (
      `area_id`        INT(2)      NOT NULL AUTO_INCREMENT
      COMMENT '主键ID',
      `area_name`      VARCHAR(200) CHARACTER SET utf8
      COLLATE utf8_general_ci      NOT NULL
      COMMENT '名称',
      `priority`       INT(2)      NOT NULL DEFAULT 0
      COMMENT '权重,越大越排前显示',
      `create_time`    DATETIME(0) NULL     DEFAULT NULL
      COMMENT '创建时间',
      `last_edit_time` DATETIME(0) NULL     DEFAULT NULL
      COMMENT '更新时间',
      PRIMARY KEY (`area_id`) USING BTREE,
      UNIQUE INDEX `UK_AREA`(`area_name`) USING BTREE
    )
      ENGINE = InnoDB
      AUTO_INCREMENT = 1
      CHARACTER SET = utf8
      COLLATE = utf8_general_ci
      COMMENT = '区域信息'
      ROW_FORMAT = DYNAMIC;
    
    SET FOREIGN_KEY_CHECKS = 1;
  • 实体类

    // 区域信息
    public class Area {
        // 主键ID
        private Integer areaId;
        // 名称
        private String areaName;
        // 权重,越大越排前显示
        private Integer priority;
        // 创建时间
        private Date createTime;
        // 更新时间
        private Date lastEditTime;
    }

第3章 项目开发

本章节带领大家从零开始从Dao到Controller开发一个完整的带单元测试增删改查后端。

  • 配置
  • 接口
  • mapper
  • ut,即单元测试

3-1 pom的配置

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>
<!--数据库-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!--连接池-->
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.2</version>
</dependency>

3-2 mybatis-config的配置

<?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>
        <!--使用JDBC的getGeneratedKeys获取数据库自增主键值-->
        <setting name="useGeneratedKeys" value="true"/>

        <!--使用列标签替换列别名 默认:true-->
        <setting name="useColumnLabel" value="true"/>

        <!--开启驼峰命名转换:Table{create_time} -> Entity{createTime}-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
</configuration>

3-3 datasource和sessionfactorybean的配置

  • 配置datasource到ioc容器里面

    @Configuration
    // 配置mybatis mapper的扫描路径
    @MapperScan("com.tuyrk.dao")
    public class DataSourceConfiguration {
        @Value("${jdbc.driver}")
        private String jdbcDriver;
        @Value(("${jdbc.url}"))
        private String jdbcUrl;
        @Value("${jdbc.username}")
        private String jdbcUsername;
        @Value("${jdbc.password}")
        private String jdbcPassword;
    
        // 生成与spring-dao.xml对应的bean dataSource
        @Bean(name = "dataSource")
        public ComboPooledDataSource createDataSource() throws PropertyVetoException {
            // 生成datasource实例
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            // 跟配置文件一样设置以下信息
            // 驱动
            dataSource.setDriverClass(jdbcDriver);
            // 数据库连接URL
            dataSource.setJdbcUrl(jdbcUrl);
            // 设置用户名
            dataSource.setUser(jdbcUsername);
            // 设置用户密码
            dataSource.setPassword(jdbcPassword);
            // 配置c3p0连接池的私有属性
            // 连接池最大线程数
            dataSource.setMaxPoolSize(30);
            // 连接池最小线程数
            dataSource.setMinPoolSize(10);
            //关闭连接后不自动commit
            dataSource.setAutoCommitOnClose(false);
            // 连接超时时间
            dataSource.setCheckoutTimeout(10000);
            // 连接失败重试次数
            dataSource.setAcquireRetryAttempts(2);
            return dataSource;
        }
    }
  • 创建sqlSessionFactoryBean实例

    @Configuration
    public class SessionFactoryConfiguration {
        // mybatis-config.xml配置文件的路径
        @Value("${mybatis_config_file}")
        private String mybatisConfigFilePath;
    
        // mybatis mapper文件所在的路径
        @Value("${mapper_path}")
        private String mapperPath;
    
        // 实体类所在的Package
        @Value("${entity_package}")
        private String entityPackage;
    
        @Autowired
        @Qualifier("dataSource")
        private DataSource dataSource;
    
        // 创建sqlSessionFactoryBean实例 并且设置configtion 设置mapper映射路径 设置datasource数据源
        @Bean(name = "sqlSessionFactory")
        public SqlSessionFactoryBean createSqlSessionFactoryBean() throws IOException {
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            // 设置mybatis configuration 扫描路径
            sqlSessionFactoryBean.setConfigLocation(new ClassPathResource(mybatisConfigFilePath));
            // 添加mapper 扫描路径
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            String packageSearchPath = PathMatchingResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + mapperPath;
            sqlSessionFactoryBean.setMapperLocations(resolver.getResources(packageSearchPath));
            // 设置dataSource
            sqlSessionFactoryBean.setDataSource(dataSource);
            // 设置typeAlias 包扫描路径
            sqlSessionFactoryBean.setTypeAliasesPackage(entityPackage);
            return sqlSessionFactoryBean;
        }
    }

3-4 dao的创建

public interface AreaDao {
    // 列出区域列表
    List<Area> queryArea();
    // 根据Id列出具体区域
    Area queryAreaById(Integer areaId);
    // 插入区域信息
    Integer insertArea(Area area);
    // 更新区域信息
    Integer updateArea(Area area);
    // 删除区域信息
    Integer deleteArea(Integer areaId);
}

3-5 mapper的编写

自动化生成mapper工具:

  • <?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.tuyrk.dao.AreaDao">
      <!-- 查询区域信息列表 -->
      <select id="queryArea" resultType="com.tuyrk.entity.Area">
          SELECT
              area_id,
              area_name,
              priority,
              create_time,
              last_edit_time
          FROM tb_area
          ORDER BY priority DESC;
      </select>
      <!-- 根据区域ID,查询区域信息 -->
      <select id="queryAreaById" resultType="com.tuyrk.entity.Area">
          SELECT
              area_id,
              area_name,
              priority,
              create_time,
              last_edit_time
          FROM tb_area
          WHERE area_id = #{areaId}
      </select>
      <!-- 新增区域信息 -->
      <insert id="insertArea" useGeneratedKeys="true" keyProperty="areaId" keyColumn="area_id"
              parameterType="com.tuyrk.entity.Area">
          INSERT INTO tb_area (area_name, priority, create_time, last_edit_time)
          VALUES (#{areaName}, #{priority}, #{createTime}, #{lastEditTime});
      </insert>
      <!-- 更新区域信息 -->
      <update id="updateArea" parameterType="com.tuyrk.entity.Area">
          update tb_area
          <set>
              <if test="areaName != null">area_name = #{areaName},</if>
              <if test="priority != null">priority = #{priority},</if>
              <if test="lastEditTime != null">last_edit_time = #{lastEditTime}</if>
          </set>
          where area_id = #{areaId};
      </update>
      <!-- 根据区域ID,删除区域信息 -->
      <delete id="deleteArea">
          DELETE FROM tb_area
          WHERE area_id = #{areaId};
      </delete>
    </mapper>

    3-6 dao层开发

    @Autowired
    private AreaDao areaDao;

areaDao红线报错解决方案

  • 【File】==>【settings】==>【Inspections】==>【Spring】==>【Spring Core】==>【Code】==>【Autowiring for Bean Class】==>【Warning】

UT单元测试:

  • AreaDaoTest.java的编写

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class AreaDaoTest {
        //通过spring容器注入Dao的实现类
        @Autowired
        private AreaDao areaDao;
        @Test
        public void queryArea() {
            List<Area> areaList = areaDao.queryArea();
            // 验证预期值和实际值是否相符
            assertEquals(2, areaList.size());
        }
        // ...
    }

如此繁多的框架应该学习哪一个

  • 答:Spring。多看Spring源码,学习框架设计。

    3-7 service层的实现

  • 配置
    对标spring-service里面的transactionManager
    继承TransactionManagementConfiguration是因为开启annotation-driven

    @Configuration
    // 首先使用注解 @EnableTransactionManagement 开启事务支持后
    // 在Service方法上添加注解 @Transactional 便可
    @EnableTransactionManagement// 开启事务的管理
    public class TransactionManagementConfiguration implements TransactionManagementConfigurer {
    
        @Autowired
        // 注入DataSourceConfiguration里边的DataSource,通过createDataSource()获取
        private DataSource dataSource;
    
         // 关于事务管理,需要返回PlatformTransactionManager的实现
        @Override
        public PlatformTransactionManager annotationDrivenTransactionManager() {
            return new DataSourceTransactionManager(dataSource);
        }
    }
  • 接口

    public interface AreaService {
        // 获取区域列表
        List<Area> getAreaList();
        // 通过区域Id获取区域信息
        Area getAreaById(int areaId);
        // 增加区域信息
        boolean addArea(Area area);
        // 修改区域信息
        boolean modifyArea(Area area);
        // 删除区域信息
        boolean deleteArea(int areaId);
    }
  • 实现类

    3-8 业务controller方法的实现

  • 业务Controller方法的实现

  • 统一异常处理

浏览器JSON格式化查看插件:JsonView

3-9 统一异常处理功能的实现

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Map<String, Object> handlerException(HttpServletRequest request, Exception e) {
        Map<String, Object> modelMap = new HashMap<>();
        modelMap.put("success", false);
        modelMap.put("errMsg", e.getMessage());
        return modelMap;
    }
}

第4章 微信小程序的入门及前后联调

本章节将带领大家入门微信小程序,并实现本项目的前端功能,同时进行前后端联调。

4-1 微信小程序简介

小程序开发文档

小程序代码构成:

  1. JSON配置
  2. WXML模板
  3. WXSS样式
  4. JS逻辑交互

    4-2 微信开发者工具简介

    下载

模拟器、编辑器、调试器

4-3 创建新的小程序

【项目目录】==>【APPID(测试号)】==>【项目名称】==>【建立普通快速启动模板】

4-4 列表页开发

修改标题:

  • 在*.json中添加"navigationBarTitleText": "区域信息列表"

组件:

  • view:类似于html的div
  • scroll-view:可滚动试图区域
  • text:文本
<!--pages/list/list.wxml-->
<view class='container'>
  <!-- 表头 -->
  <view class='widget'>
    <text class='column'>ID</text>
    <text class='column'>区域名</text>
    <text class='column'>优先级</text>
    <text class='link-column'>操作</text>
  </view>
  <!-- 数据展示区域 -->
  <scroll-view scroll-y="true">
    <view>
      <block wx:for="{{list}}">
        <view class="widget">
          <text class='column'>{{item.areaId}}</text>
          <text class='column'>{{item.areaName}}</text>
          <text class='column'>{{item.priority}}</text>
          <view class='link-column'>
            <navigator class='link' url='../operation/operation?areaId={{item.areaId}}'>编辑</navigator>|
            <text class='link' bindtap='deleteArea' data-areaid='{{item.areaId}}' data-areaname='{{item.areaName}}' data-index='{{index}}'>删除</text>
          </view>
        </view>
      </block>
    </view>
  </scroll-view>
  <!-- 按钮 -->
  <button type='primary' bindtap='addArea'>添加区域信息</button>
</view>
  • 列表展示

    onShow: function() {
      var that = this;
      wx.request({
        url: 'http://127.0.0.1:8888/demo/superadmin/listarea',
        data: {},
        method: 'GET',
        success: function(res) {
          var list = res.data.areaList;
          console.log(res);
          if (list == null) {
            var toastText = '获取数据失败' + res.data.errMsg;
            wx.showToast({
              title: toastText,
              icon: '',
              duration: 2000
            })
          } else {
            that.setData({
              list: list
            });
          }
        }
      })
    }
  • 删除功能

    deleteArea: function(e) {
      var that = this;
      // 0. 询问用户是否删除该信息
      wx.showModal({
        title: '提示',
        content: '确定要删除[' + e.target.dataset.areaname + ']吗?',
        success: function(sm) {
          // 如果点击确认,
          if (sm.confirm) {
            // 1. 获取areaId,将其传递给后台做删除操作
            wx.request({
              url: 'http://127.0.0.1:8888/demo/superadmin/removearea',
              data: {
                "areaId": e.target.dataset.areaid
              },
              method: 'GET',
              success: function(res) {
                var result = res.data.success;
                var toastText = "删除成功!";
                if (result != true) {
                  toastText = "刪除失敗!";
                } else {
                  // 2. 更新list值
                  that.data.list.splice(e.target.dataset.index, 1);
                  that.setData({
                    list: that.data.list
                  })
                }
                // 彈出提示
                wx.showToast({
                  title: toastText,
                  icon: '',
                  duration: 2000
                })
              }
            })
          }
        }
      })
    }
  • 跳转到添加区域信息

    addArea: function() {
      wx.navigateTo({
        url: '../operation/operation'
      });
    }

4-7 列表页前后端联调

4-8 区域信息编辑页的开发

<!--pages/operation/operation.wxml-->
<view class="container">
  <form bindsubmit='formSubmit' bindreset='formReset'>
    <!-- 输入框  -->
    <view class='row'>
      <text>区域名:</text>
      <input type='text' name='areaName' placeholder='请输入区域名' value='{{areaName}}'/>
    </view>
    <view class='row'>
      <text>优先级:</text>
      <input type='number' name='priority' placeholder='数值越大越靠前' value='{{priority}}'/>
    </view>
    <!-- 按钮  -->
    <view class='row'>
      <button type='primary' form-type='submit'>提交</button>
      <button type='primary' form-type='reset'>清空</button>
    </view>
  </form>
</view>
  • 区域信息添加
  • 区域信息更新
  • 页面的初始数据

    data: {
      areaId: undefined,
      areaName: '',
      priority: '',
      addUrl: 'http://127.0.0.1:8888/demo/superadmin/addarea',
      modifyUrl: 'http://127.0.0.1:8888/demo/superadmin/modifyarea'
    }
  • 编辑页获取区域信息

    onLoad: function(options) {
      var that = this;
      // 页面初始化 options为页面跳转所带来的参数
      this.setData({
        areaId: options.areaId
      });
      if (options.areaId == undefined) {
        return;
      }
      // 向后端发送请求,获取areaId的区域信息
      wx.request({
        url: 'http://127.0.0.1:8888/demo/superadmin/getareabyid',
        data: {
          "areaId": options.areaId
        },
        method: 'GET',
        success: function(res) {
          var area = res.data.area;
          if (area == undefined) {
            var toastText = "获取数据失败" + res.data.errMsg;
            wx.showToast({
              title: toastText,
              icon: '',
              duration: 2000
            });
          } else {
            that.setData({
              areaName: area.areaName,
              priority: area.priority
            });
          }
        }
      })
    }
  • 提交区域信息表单

    formSubmit: function (e) {
      var that = this;
      // 默认为新增操作
      var formData = e.detail.value;
      var url = that.data.addUrl;
    
      // 如果areaId不为空,则为更新操作
      if (that.data.areaId != undefined) {
        formData.areaId = that.data.areaId;
        url = that.data.modifyUrl;
      }
      wx.request({
        url: url,
        data: JSON.stringify(formData),
        method: 'POST',
        header: {
          'Content-Type': 'application/json'
        },
        success: function(res) {
          var result = res.data.success;
          var toastText = "操作成功";
          if (result != true) {
            toastText = "操作失败" + res.data.errMsg;
          }
          wx.showToast({
            title: toastText,
            icon: '',
            duration: 2000
          });
          if (that.data.areaId == undefined) {
            wx.redirectTo({
              url: '../list/list'
            })
          }
        }
      });
    }

4-9 区域信息编辑页的联调

第5章 课程总结与展望

从整体上复习下本门课程的知识点

  1. HelloWorld Controller
  2. 表设计和实体类的创建
  3. dao层配置:mybatis-config,
  4. service层配置:事务,接口及实现类
  5. Controller层:增删改查
  6. 微信小程序,微信开发者工具

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 tuyrk@qq.com

文章标题:945-SpringBoot+MyBatis搭建迷你小程序

文章字数:3.6k

本文作者:神秘的小岛岛

发布时间:2019-07-06, 23:20:09

最后更新:2019-11-05, 19:27:12

原始链接:https://www.tuyrk.cn/imooc/945-mini-app/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏