SpringBoot+MaBatis搭建迷你微信小程序(入门级、含源码)

前言

由于对微信小程序的好奇心驱动,便学习了一个简单的搭建迷你微信小程序的小项目,后端主要用到了SpringBoot+MyBatis技术,而微信小程序的前端主要用到了Vue框架的技术和对微信开发者工具的简单使用。

项目展示:

在这里插入图片描述

一、项目设计及框架搭建

1、SpringBoot搭建

在IDEA中创建Spring Initializr,
在这里插入图片描述
在这里插入图片描述

2、表设计与实体类的创建

创建数据库名demo,运行下列sql语句:

CREATE TABLE tb_area (

	area_id int(2) NOT NULL auto_increment,

	area_name varchar(200) NOT NULL,

	priority int(2) NOT NULL DEFAULT'0',

	create_time datetime DEFAULT NULL,

	last_edit_time datetime DEFAULT NULL,

	PRIMARY KEY(area_id),

	UNIQUE KEY UK_AREA(area_name)

)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

在这里插入图片描述

实体类com/dem/entity/ Area.java:(get、set)

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

二、项目开发

1、pom配置

<!--        mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
<!--        连接池-->
    <dependency>
        <groupId>com.mchange</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.5.5</version>
    </dependency>

2、MyBatis配置

mybatis-config.xml:

<?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、datasource和sessionfactorybean的配置

这个datasource数据库连接是很麻烦的,我们可以直接在application.properties进行配置:


spring.datasource.url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.username = root
spring.datasource.password = 1214
spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver

spring.datasource.max-active=20 //指定连接池中最大的活跃连接数
spring.datasource.max-idle=8 //指定连接池中连接的最大的空闲连接数量
spring.datasource.min-idle=8 //指定必须保持连接的最小值
spring.datasource.initial-size=10 //指定启动连接池时,初始建立的连接数量

这也可以实现其目的。


application.properties:

#DataSource
#数据库驱动
jdbc.driver=com.mysql.cj.jdbc.Driver
#数据库链接
jdbc.url=jdbc:mysql://localhost:3306/demo?serverTimezone=GMT
#数据库用户名
jdbc.username=root
#数据库密码
jdbc.password=1214

DataSourceConfiguration.java:

@Configuration      //告诉spring容器需要来到这个类下面来检查相关的bean
@MapperScan("com.demo.dao")     //配置mybatis mapper的扫描路径
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;

    @Bean(name = "dataSource")
    public ComboPooledDataSource createDataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(jdbcDriver);
        dataSource.setJdbcUrl(jdbcUrl);
        dataSource.setUser(jdbcUsername);
        dataSource.setPassword(jdbcPassword);
        //关闭连接后不自动commit
        dataSource.setAutoCommitOnClose(false);
        return dataSource;
    }
}

application.properties添加:

#MyBatis
mybatis_config_file=mybatis-config.xml
mapper_path=/mapper/**.xml
entity_package=com.demo.entity

SessionFactoryConfiguration.java:

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;

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactoryBean createSqlSessionFactoryBean() throws IOException {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        //扫描mybatis-config.xml配置文件
        sqlSessionFactoryBean.setConfigLocation(new ClassPathResource(mybatisConfigFilePath));
        //读取jar文件信息
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        //指定mapper文件的扫描路径
        String packagSearchPath = PathMatchingResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + mapperPath;//读取路径
        sqlSessionFactoryBean.setMapperLocations(resolver.getResources(packagSearchPath));//设置路径
        //指定数据源
        sqlSessionFactoryBean.setDataSource(dataSource);
        //指定映射实体类package的扫描路径
        sqlSessionFactoryBean.setTypeAliasesPackage(entityPackage);
        return sqlSessionFactoryBean;
    }
}

4、dao的创建

AreaDao:

public interface AreaDao {
    //列出区域列表
    List<Area> queryArea();

    //根据Id列出具体区域
    Area queryAreaById(int areaId);

    //插入区域信息
    int insertArea(Area area);

    //更新区域信息
    int updateArea(Area area);

    //删除区域信息
    int deleteArea(int areaId);
}

5、mapper的编写

操作数据库的sql语句编写
AreaDao.xml:

<?xml version = "1.0" encoding = "UTF-8"?>
<!DOCTYPE mapper PUBLIC
        "-//mybatis.org//DTD com.example.Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.demo.dao.AreaDao">
    <select id="queryArea" resultType="com.demo.entity.Area">
        SELECT area_id, area_name,
        priority, create_time, last_edit_time
        FROM tb_area
        ORDER BY priority
        DESC
    </select>

    <select id="queryArea" resultType="com.demo.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.demo.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.demo.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>

    <delete id="deleteArea">
        DELETE FROM
        tb_area
        WHERE
        area_id =
        #{areaId}
    </delete>
</mapper>

6、dao层开发

下面就简单的进行一些单元测试类:

AreaDaoTest.java:

@RunWith(SpringRunner.class)
@SpringBootTest
@FixMethodOrder(MethodSorters.NAME_ASCENDING) // 按方法名大小升序执行
public class AreaDaoTest {
    //通过spring容器注入Dao的实现类
    @Autowired
    private AreaDao areaDao;

    @Test
    public void testAQueryArea() {
        List<Area> areaList = areaDao.queryArea();
        // 验证预期值和实际值是否相符
        assertEquals(2, areaList.size());
    }

    @Test
    public void testBInsertArea() {
        //创建一个区域对象
        Area area = new Area();
        area.setAreaName("测试区域");
        area.setCreateTime(new Date());
        area.setPriority(1);
        //将该对象实例添加入库
        int effectedNum = areaDao.insertArea(area);
        //检测影响行数
        assertEquals(1, effectedNum);
        //校验总数是否+1
        List<Area> areaList = areaDao.queryArea();
        assertEquals(3, areaList.size());
    }

    @Test
    public void testCQueryAreaById() {
        Area area = areaDao.queryAreaById(2);
        assertEquals("小可爱", area.getAreaName());
    }

    @Test
    public void testDUpateArea() {
        List<Area> areaList = areaDao.queryArea();
        for (Area area : areaList) {
            if ("测试区域".equals(area.getAreaName())) {
                // 对比之前的priority值
                assertEquals(1, area.getPriority().intValue());
                area.setPriority(2);
                int effectedNum = areaDao.updateArea(area);
                assertEquals(1, effectedNum);
            }
        }
    }

    @Test
    public void testEDeleteArea() {
        List<Area> areaList = areaDao.queryArea();
        for (Area area : areaList) {
            if ("测试区域".equals(area.getAreaName())) {
                int effectedNum = areaDao.deleteArea(area.getAreaId());
                assertEquals(1, effectedNum);
            }
        }
        // 重新获取一次列表,看看总数是否少1
        areaList = areaDao.queryArea();
        assertEquals(2, areaList.size());
    }
}

可以通过@Ignore来阻碍其一方法的运行,从而进行每一个单元测试

7、service层的实现

services 存放业务得逻辑操作,需要用到事务层来操作数据

/**
 * 对标spring-service里面的transactionManager
 * 继承TransactionManagementConfigurer是因为开启annotation-driven
 */
@Configuration
// 首先使用注解 @EnableTransactionManagement 开启事务支持后
// 在Service方法上添加注解 @Transactional 便可
@EnableTransactionManagement
public class TransactionManagementConfiguration implements TransactionManagementConfigurer {

    @Autowired
    // 注入DataSourceConfiguration里边的dataSource,通过createDataSource()获取
    private DataSource dataSource;

    @Override
    /**
     * 关于事务管理,需要返回PlatformTransactionManager的实现
     */
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return new DataSourceTransactionManager(dataSource);
    }
}

AreaService接口:

public interface AreaService {

    /**
     * 获取区域列表
     */
    List<Area> getAreaList();

    /**
     * 通过区域Id获取区域信息
     */
    Area getAreaById(int areaId);

    /**
     * 增加区域信息
     */
    boolean addArea(Area area);

    /**
     * 修改区域信息
     */
    boolean modifyArea(Area area);

    /**
     * 删除区域信息
     */
    boolean deleteArea(int areaId);
}

AreaServiceImpl.java:

@Service
public class AreaServiceImpl implements AreaService {
    @Autowired
    private AreaDao areaDao;

    @Override
    public List<Area> getAreaList() {
        // 返回所有的区域信息
        return areaDao.queryArea();
    }

    @Override
    public Area getAreaById(int areaId) {
        return areaDao.queryAreaById(areaId);
    }

    @Transactional
    @Override
    public boolean addArea(Area area) {
        // 空值判断,主要是判断areaName不为空
        if (area.getAreaName() != null && !"".equals(area.getAreaName())) {
            // 设置默认值
            area.setCreateTime(new Date());
            area.setLastEditTime(new Date());
            try {
                int effectedNum = areaDao.insertArea(area);
                if (effectedNum > 0) {
                    return true;
                } else {
                    throw new RuntimeException("添加区域信息失败!");
                }
            } catch (Exception e) {
                throw new RuntimeException("添加区域信息失败:" + e.toString());
            }
        } else {
            throw new RuntimeException("区域信息不能为空!");
        }
    }

    @Transactional
    @Override
    public boolean modifyArea(Area area) {
        // 空值判断,主要是areaId不为空
        if (area.getAreaId() != null && area.getAreaId() > 0) {
            // 设置默认值
            area.setLastEditTime(new Date());
            try {
                // 更新区域信息
                int effectedNum = areaDao.updateArea(area);
                if (effectedNum > 0) {
                    return true;
                } else {
                    throw new RuntimeException("更新区域信息失败!");
                }
            } catch (Exception e) {
                throw new RuntimeException("更新区域信息失败:" + e.toString());
            }
        } else {
            throw new RuntimeException("区域信息不能为空!");
        }
    }

    @Transactional
    @Override
    public boolean deleteArea(int areaId) {
        if (areaId > 0) {
            try {
                // 删除区域信息
                int effectedNum = areaDao.deleteArea(areaId);
                if (effectedNum > 0) {
                    return true;
                } else {
                    throw new RuntimeException("删除区域信息失败!");
                }
            } catch (Exception e) {
                throw new RuntimeException("删除区域信息失败:" + e.toString());
            }
        } else {
            throw new RuntimeException("区域Id不能为空!");
        }
    }
}

8、业务controller方法的实现

AreaController.java:

@RestController
@RequestMapping("/superadmin")
public class AreaController {
    @Autowired
    private AreaService areaService;

    /**
     * 获取所有的区域信息
     *
     * @return
     */
    @RequestMapping(value = "/listarea", method = RequestMethod.GET)
    private Map<String, Object> listArea() {
        Map<String, Object> modelMap = new HashMap<String, Object>();
        List<Area> list = new ArrayList<Area>();
        // 获取区域列表
        list = areaService.getAreaList();
        modelMap.put("areaList", list);
        return modelMap;
    }

    /**
     * 通过区域Id获取区域信息
     *
     * @return
     */
    @RequestMapping(value = "/getareabyid", method = RequestMethod.GET)
    private Map<String, Object> getAreaById(Integer areaId) {
        Map<String, Object> modelMap = new HashMap<String, Object>();
        // 获取区域信息
        Area area = areaService.getAreaById(areaId);
        modelMap.put("area", area);
        return modelMap;
    }

    /**
     * 添加区域信息
     *
     * @param areaStr
     * @param request
     * @return
     * @throws IOException
     * @throws JsonMappingException
     * @throws JsonParseException
     */
    @RequestMapping(value = "/addarea", method = RequestMethod.POST)
    private Map<String, Object> addArea(@RequestBody Area area)
            throws JsonParseException, JsonMappingException, IOException {
        Map<String, Object> modelMap = new HashMap<String, Object>();
        // 添加区域信息
        modelMap.put("success", areaService.addArea(area));
        return modelMap;
    }

    /**
     * 修改区域信息,主要修改名字
     *
     * @param areaStr
     * @param request
     * @return
     * @throws IOException
     * @throws JsonMappingException
     * @throws JsonParseException
     */
    @RequestMapping(value = "/modifyarea", method = RequestMethod.POST)
    private Map<String, Object> modifyArea(@RequestBody Area area)
            throws JsonParseException, JsonMappingException, IOException {
        Map<String, Object> modelMap = new HashMap<String, Object>();
        // 修改区域信息
        modelMap.put("success", areaService.modifyArea(area));
        return modelMap;
    }

    @RequestMapping(value = "/removearea", method = RequestMethod.GET)
    private Map<String, Object> removeArea(Integer areaId) {
        Map<String, Object> modelMap = new HashMap<String, Object>();
        // 修改区域信息
        modelMap.put("success", areaService.deleteArea(areaId));
        return modelMap;
    }
}

9、统一异常功能的实现

GlobalExceptionHandler.java:

/**
 * 统一异常处理类
 */
@ControllerAdvice
public class GlobalExceptionHandler {

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

以上便是关于一个微信小程序在后台的简单编写,当然你也可以进行优化,下面我们先进行微信小程序的前端开发ヾ(◍°∇°◍)ノ゙

三、微信小程序的前端入门

可以在微信小程序的文档进行查看相关内容:微信官方文档-小程序(✪ω✪)
可先进行下载一个微信开发者工具:微信开发者工具┗( ▔, ▔ )┛

下载点击进入后,微信扫一扫登录后:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
具体文件内容的介绍,请移至慕课网的视频进行学习或查阅微信小程序的文档进行学习,下面便开始前端小程序的开发:

选择pages鼠标右键选择新建Page后命名(list),完成后便会生成对应的四个文件

具体的小程序操作就不详细解释了,毕竟我一个后端的,会一些简单的使用了解一下就好,若有兴趣可自行进行学习

其具体源码,请自行观看:

1、list.js

// pages/list/list.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    list: []
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {

  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {
    var that = this;
    wx.request({
      url: "http://127.0.0.1:8080/superadmin/listarea",
      data: {},
      method: 'GET',
      success: function (res) {
        var list = res.data.areaList;
        if (list == null) {
          var toastText = '获取数据失败' + res.data.errMsg;
          wx.showToast({
            title: toastText,
            icon: '',
            duration: 2000
          });
        } else {
          that.setData({
            list: list
          });
        }
      }
    })
  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  },
  addArea: function () {
    wx.navigateTo({
      url: '../operation/operation',
    })
  },
  deleteArea: function (e) {    
    var that = this;
    wx.showModal({
      title: '提示',
      content: '确定要删除[' + e.target.dataset.areaname + ']吗?',
      success: function (sm) {
        if (sm.confirm) {
          // 用户点击了确定 可以调用删除方法了
          wx.request({
            url: "http://127.0.0.1:8080/superadmin/removearea",
            data: { "areaId": e.target.dataset.areaid },
            method: 'GET',
            success: function (res) {
              var result = res.data.success
              var toastText = "删除成功!";
              if (result != true) {
                toastText = "删除失败" + res.data.errMsg;
              } else{
                that.data.list.splice(e.target.dataset.index, 1)
                //渲染数据
                that.setData({
                  list: that.data.list
                });
              }
              wx.showToast({
                title: toastText,
                icon: '',
                duration: 2000
              });
            }
          })
        }
      }
    })
  }
})

2、list.json

{
  "navigationBarTitleText": "区域信息列表"
}

3、list.wxml

<!--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">
          <view>
            <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>
        </view>
      </block>
    </view>
  </scroll-view>
  <button type="primary" bindtap="addArea">添加区域信息</button>
</view>

4、list.wxss

/* pages/list/list.wxss */

.widget {
  position: relative;
  margin-top: 5rpx;
  margin-bottom: 5rpx;
  padding-top: 10rpx;
  padding-bottom: 10rpx;
  padding-left: 40rpx;
  padding-right: 40rpx;
  border: #ddd 1px solid;
}

.container {
  height: 100%;
  display: table;
  align-items: center;
  justify-content: space-between;
  box-sizing: border-box;
  padding-top: 10rpx;
  padding-bottom: 10rpx;
  text-align: center;
}

.column {
  width: 4rem;
  display: table-cell;
}

.link-column {
  width: 6rem;
  display: table-cell;
}

.link {
  color: blue;
  display: inline-table;
}

四、总结

至此便是我学习的整个一个简单的微信小程序的流程,通过这门课程对微信小程序开发有了些许了解。

慕课网:SpringBoot+MyBatis搭建迷你小程序视频٩(๑>◡<๑)۶

前端源码٩(๑❛ᴗ❛๑)۶

后端源码(๑๑)

至此你可能比较好奇为什么在datasource的配置中搞这么复杂,直接在application.properties配置不香吗!??,下面便是该讲师的回应:

补充一下,datasource这些配置是可以在application.properties里配置,不过视频里这样做是有两个考虑:
1. 可以让同学了解一下第三方datasource的配置方法,spring自带的是可以在application.properties里配置的
2. 和之前的实战课衔接,因为实战课程既讲了SpringMVC同时讲了SpringBoot,在教大家SpringMVC迁移到SpringBoot的时候,咱们用的是类似的方式将XML转成了Bean,所以为了无缝衔接起来
实战过程中,黑猫白猫,能抓住老鼠的都是好猫。并且大一些的项目数据库连接池用的是自定义而非spring自带的多一些)

至此一个简单的微信小程序的入门就完成了ヾ(๑╹◡╹)ノ"

  • 9
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值