目录
1 准备工作
这是一个Web开发入门学习若依权限管理系统的记录帖
数据库刚接触,前端完全不懂,连蒙带抄实现一个最简单的子菜单,参考了系统中最不复杂的“岗位管理”模块。
环境准备:
- IDEA2019 + Maven配置
- MySQL + Navicat
需要知道的概念:
- Spring框架和 spring-boot
- MyBatis框架
- Shiro安全框架(暂时没用到)
- Thymeleaf模板(前端)
接下来是走一步看一步了,做到哪里不会就现查现学……
2 RuoYi-fast运行
在IDEA中直接下载引入若依:
登陆gitee - IDEA 工具栏 - VSC - Get from version control - URL输入Ruoyi-fast下载地址https://gitee.com/y_project/RuoYi-fast
等待下载完成,进行配置,doc文件夹下有《若依环境使用手册》,写得很清楚,好像没遇到什么难题,运行RuoYiApplication,登陆成功
3 实现一个系统管理子菜单:测试管理
3.1 实现过程
- 在数据库中新建一个test表,参考post表,加入几个条目
- resources - mybatis - system下新建TestMapper.xml编写数据库映射
新建中没有mapper选项:IDEA创建Mapper.xml文件 - com.ruoyi.project.system新建test包,创建需要的类和接口
1)因为test名字特殊,controller包里的TestController调试可以通过,但运行时报告了与com.ruoyi.project.tool.swagger下的类重名错误,导致系统启动失败,因此需要改个名字如MyTestController
2)编写时涉及名称或编码是否唯一问题时需要两个user constant值,在com.ruoyi.common.constant.UserConstants里添加:
/** 测试名称是否唯一的返回结果码 */
public final static String TEST_NAME_UNIQUE = "0";
public final static String TEST_NAME_NOT_UNIQUE = "1";
/** 测试编码是否唯一的返回结果码 */
public final static String TEST_CODE_UNIQUE = "0";
public final static String TEST_CODE_NOT_UNIQUE = "1";
- 编译TestMapper.xml可以检查上述所有,通过后开始写html
- resources - templates - system下添加test路径,编写(抄)需要的html
3.2 遇到的问题
3.2.1 不显示菜单错误
现在可以运行了,可是运行出来仿佛我加的程序从未存在……一开始以为系统菜单在main.html里修改,但main中所有左侧菜单都用变量统一表述了,没看懂,但应该不是改这个。然后在project里搜索了一下,发现相关条目保存在数据库的sys_menu表里,我没有在这里添加相应条目:
添加条目时,需要仔细检查层级有没有填对,信息有没有填完整,否则出现菜单打不开,变成404的现象。
顺便全面看了一下数据库里的表,发现还有一个sys_role_menu表,保存了系统操作角色id对应的菜单id:
不确定影不影响前端显示,总之把条目id先加上。
3.2.2 无法读取数据库表内容错误
现在再次启动服务器:
菜单里出现了我添加的“测试管理”条目,条目下所有功能也都显示了,但又有了新问题,读取不到我在数据库中添加的条目,应该是mapper.xml文件问题,检查了一下居然是包名打错字母,我晕😠
现在正常了:
3.2.3 页面功能实现错误
试用下页面上的功能,又出了问题:
- 搜索键没有反应,按照名称、编码、状态都无法搜索单一条目
- 编辑条目中更改状态出现错误,显示重名
- 新增或者编辑名称和编码不论写什么值都会显示已存在
- 删除条目报错:nested exception is org.apache.ibatis.builder.BuilderException: Error evaluating expression ‘array’. Return value (1) was not iterable.
可以说是除了重置和导出,其他所有功能都不能实现😰去寻找下原因
- 搜索功能:com.ruoyi.project.system.test.domain的Test类中所有set方法都没有声明类型和变量,修改后搜索功能正常了
- 编辑状态功能:com.ruoyi.project.system.test.controller的MyTestController类的editSave方法返回了
return toAjax(testService.insertTest(test));
从addSave复制过来漏改了……改成return toAjax(testService.updateTest(test));
状态信息就可以编辑了 - 删除功能:com.ruoyi.project.system.test.service的TestServiceImpl类的deleteTestByIds(String ids)方法返回值和实际值不符,返回int,而我在之前编写时直接
return testMapper.deleteTestByIds(ids);
改为在return语句前添加convert:
/**
* 批量删除test信息
*
* @param ids 需要删除的数据id
* @throws BusinessException
*/
@Override
public int deleteTestByIds(String ids) throws BusinessException
{
Long[] testIds = Convert.toLongArray(ids);
return testMapper.deleteTestByIds(testIds);
}
- 编辑名称功能:编辑名称时抛出异常:Request method ‘TEST’ not supported,是html文件里的
type: "post",
写成test了,此post非彼post,果然对html一窍不通还是不行😵 - 新增功能异常1:和上述一样修改后不再显示重名错误,但抛出了新的运行时异常:nested exception is org.apache.ibatis.builder.BuilderException: Error evaluating expression ‘remark !=null and remark != ‘‘0’’’.写得比较清楚,是xml文件里赋值时if声明的语法有错误,写多了一个0,改成
<if test="remark != null and remark != ''">#{remark},</if>
- 新增功能异常2:修改上述内容后又报了新的运行时异常:Error updating database. Cause: java.sql.SQLException: Field ‘test_id’ doesn’t have a default value在新增操作时没有设置id,而id没有默认值,导致id为空。在数据库的设计表中可以看到别的表的id都设置了自动递增,因此将新建的sys_test表的id也设置为自动递增,id就产生默认值了
现在增删查改都可以实现了:
前端更改后的数据库表:
虽然是简单内容,但是手打一遍出现了很多很多问题……把主要问题记录在了本帖中。
4 总结
关于后端六个文件的个人理解:
- TestMapper.xml:动态sql操作文件,描述了系统和数据库的映射关系
因为在namespace里填写了mapper接口<mapper namespace="com.ruoyi.project.system.test.mapper.TestMapper">
所以编译这个文件可以检查所有相关class和interface(吗?) - TestMapper.java:dao层接口文件,方法名和 xml 中定义的每个 statement 的 id 同名,输入参数类型和 xml 中定义的 statement 的parameterType 类型相同,返回类型和 xml 中定义的 statement 的resultType 类型相同
- ITestService.java:service层接口文件,定义方法的参数、返回值
- TestServiceImpl.java:service层类文件,实现ITestService接口的方法
- MyTestController.java:controller层类文件,接收前端请求,调用service层方法实现
- Test.java:domain层类文件,和数据库中的表一一对应的JavaBean,实现set和get方法
业务实现流程:controller层接受前端发来的请求,同时向后端发送请求,——>service 层——>serviceImpl实现service层,同时连接dao层,(在dao层中同样是接口)——>通过dao层去实现对数据库的操作——>在XML文件中通过namespace完成连接dao层
5 补充说明
以上demo没有做和user的依赖关联,是最最最简单的子菜单实现,用于自己了解系统各层的作用,和开发的简单流程。编写过程中学习的帖子们(部分):
如何阅读别人的代码 [原]
详细SpringBoot教程之入门(一)
做web开发,怎么能不懂cookie、session和token呢?
SpringBoot注解最全详解(整合超详细版本)
JavaWeb——Servlet(全网最详细教程包括Servlet源码分析)
mybatis看这一篇就够了,简单全面一发入魂
AJAX&JSON超级最详细讲解
Mybatis的mapper.xml配置文件——详解
MyBatis Mapper XML文件详解
为什么dao层和service层要用接口?
SpringMVC中的@Controller和@RequestMapping作用详解
controller层的作用
初学SpringBoot框架: Dao层、Service层、Controller层的作用
解析Java框架中entity层,mapper层,service层,controller各层作用
domain层详解
Java Bean详解
POJO和JavaBean的区别
Java 之 Serializable 序列化和反序列化的概念,作用的通俗易懂的解释
GET和POST两种基本请求方法的区别
6 所有新增代码
6.1 后端
TestMapper.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.ruoyi.project.system.test.mapper.TestMapper">
<resultMap type="Test" id="TestResult">
<id property="testId" column="test_id" />
<result property="testCode" column="test_code" />
<result property="testName" column="test_name" />
<result property="status" column="status" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
</resultMap>
<sql id="selectTestVo">
select test_id, test_code, test_name, status, create_by, create_time, remark
from sys_test
</sql>
<select id="selectTestList" parameterType="Test" resultMap="TestResult">
<include refid="selectTestVo" />
<where>
<if test="testCode != null and testCode != ''">
AND test_code like concat('%', #{testCode}, '%')
</if>
<if test="status != null and status != ''">
AND status = #{status}
</if>
<if test="testName != null and testName != ''">
AND test_name like concat('%', #{testName}, '%')
</if>
</where>
</select>
<select id="selectTestAll" resultMap="TestResult">
<include refid="selectTestVo" />
</select>
<select id="selectTestById" parameterType="Long" resultMap="TestResult">
<include refid="selectTestVo" />
where test_id = #{testID}
</select>
<select id="checkTestNameUnique" parameterType="String" resultMap="TestResult">
<include refid="selectTestVo" />
where test_name = #{testName} limit 1
</select>
<select id="checkTestCodeUnique" parameterType="String" resultMap="TestResult">
<include refid="selectTestVo" />
where test_code = #{testCode} limit 1
</select>
<delete id="deleteTestByIds" parameterType="Long">
delete from sys_test where test_id in
<foreach collection="array" item="testId" open="(" separator="," close=")">
#{testId}
</foreach>
</delete>
<update id="updateTest" parameterType="Test">
update sys_test
<set>
<if test="testCode != null and testCode != ''">test_code = #{testCode},</if>
<if test="testName != null and testName != ''">test_name = #{testName},</if>
<if test="status != null and status != ''">status = #{status},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
update_time = sysdate()
</set>
where test_id = #{testId}
</update>
<insert id="insertTest" parameterType="Test" useGeneratedKeys="true" keyProperty="testId">
insert into sys_test(
<if test="testId !=null and testId != 0">test_id,</if>
<if test="testCode !=null and testCode != ''">test_code,</if>
<if test="testName !=null and testName != ''">test_name,</if>