知识点1:thymeleaf传来的都是String类型,转为int类型的java语法如下
int user_id = Integer.parseInt(request.getParameter("user_id"));
知识点2:如果前端的<input>
标签用了diasbled
,则该输入框的内容不仅前端无法修改,提交表单时也无法传值到 controller
层
如果只是想在前端对输入框input
禁用修改,但要保持将值传到后台的能力,则 可以使用 readonly
属性
参考链接:
知识点3:主外键表关联时删除项时需要的注意事项
需要先删除子表(即带外键的表),再删除父表(即其主键是其它表外键的表)
拓展链接:
知识点4:多表联查的语句示例
SELECT fc.first_category_id, sc.second_category_id, fc.first_category_name, sc.second_category_name FROM demo.tb_first_category fc LEFT JOIN demo.tb_second_category sc ON fc.first_category_id = sc.second_category_id;
参考链接:
知识点5:修改列名称的mysql语句示例
alter table tb_first_category modify name_before_modified name_after_modified unsigned NOT NULL after first_category_id;
参考链接
问题6:页面切换时如何传参
问题描述
在一个index.html
里,用 thymeleaf
的th:replace
引入了5个功能模块的页面,切换功能的逻辑是,选中哪个功能则显示哪个功能的页面,将其 display
属性设为block
, 其它功能模块的属性则设为 none
用 javascript
代码实现如下:
function changePage(id){//用于实现页面切换
var parent = document.getElementById("fragParent");
var allFragment=parent.getElementsByClassName("fragment");
console.log(allFragment);
console.log(allFragment.length);
for (var i=0;i < allFragment.length;i++){
if(parseInt(id)==parseInt(i)) {
allFragment[i].style.display = "block";
}
else{
allFragment[i].style.display = "none";
}
}
}
现在的问题是,在这样的切换页面思路下,如何实现各子页面切换时的数据传递。
错误的思路
一开始的思路是写不同的路由,点击链接跳转不同的路由再跳回主页,结果这样的逻辑出错了,因为每次跳回都相当于一次刷新,之前的display
状态也就失效了
正确的思路
因为6个子页面都是在父页面里的,只不过用js
控制了其display
属性,所以应该在 index.html
加载的时候就 将各子页面需要变量在index
跳转的controller
层将之全部传到index
页面。
知识点7:带条件的多表联查更新语句示例
UPDATE demo.tb_first_category fc INNER JOIN tb_second_category sc ON fc.first_category_id = sc.first_category_id SET fc.first_category_name="测试一级目录",sc.second_category_name="测试二级目录" WHERE sc.second_category_id=10
参考链接:
知识点8:多表关联删除
下面的语句实现的功能是删除id=1
的 table1
表的行,以及满足 a.id = b.id
条件的 table2
的对应行
DELETE a,b FROM table1 a INNER JOIN table2 b ON a.id = b.aid WHERE a.id=1
问题9:类型管理
功能中,点击特定行的删除
按钮,结果表单提交了错误的 数据行 到后台,每次提交的都是列表中第一行
问题描述
预期结果:
在前端 html
页面有一个数据列表,点击要删除的数据行对应的删除按钮,该行对应的表单被提交到后台,经过后台处理后删除数据库中对应的信息
实际结果:
在前端的列表中,不管点击数据列表哪一行,表单提交到后台controller
层的都是第一行。
后来觉得应该是提交时提交了全部行,但是变量只能接收第一个值,所以不管点哪一行后台得到的都是第一行的数据。
解决方案
之前是将<form>
标签写在了列表标签<ul>
外面,导致相当于整个数据列表都是一个表单里的内容了,所以不管点击哪行的按钮提交的都是包含了整个列表内容的表单,只要把 <form>
标签放在 <ul>
里,只包围每一行数据的 li
标签即可
<ul class="list_b" th:each="c : ${categorylist}">
<form method="post" action="/categoryOperation">
<li><input name="second_category_id" th:value="${c.second_category_id}"></li>
<li><input name="first_category_name" th:value="${c.first_category_name}"</li>
<li><input name="second_category_name" th:value="${c.second_category_name}"</li>
</form>
</ul>
问题10:实现提交表单时利用 confirm()
函数跳出二次确认表单,点击 确认
则提交,点击 取消
则不提交
直接用 onclick="confirm('确定删除?');"
是不行的,虽然在弹窗中点击 确定
确实会返回 true
, 点击 取消
会返回 false
,但是返回false
时依然会提交表单,正确的写法应该如下:即应该在 confirm()
返回 false
时执行return false;
语句:
onclick="if(!confirm('are you ok?')){return false;}"
问题11:路由的路径问题
问题描述
情况:html
页面文件kindadd,html
和其对应的 css
文件 main.css
的目录结构如下(除了main.css
和kinadd.html
之外其它都是目录):
templates
--css
----main.css
--EndPages
----kindadd.html
spring boot
中的 application.properties
文件配置的路径如下:
#默认css调用路径是static(在resources目录之后),现在改变其路径为templates
spring.resources.static-locations=classpath:/templates/
-
如果在
kindAdd.html
中引入样式表的路径写为css/main.css
:kindAdd.html
提交的表单的action
属性设为/categoryAdd
,控制层的路由也设为/categoryAdd
控制层函数处理完
return
的html页面路径 为EndPages/kindAdd
期望结果是:正确跳转到
.../EndPages/kindAdd.html
实际结果是:实际结果是跳转到
.../categoryAdd
(注意没有html尾缀,相当于是路由的路径,这是浏览器上显示的地址,不知道算不算是实际跳转的地址),于是加载的css路径就变为.../css/main.css
, 而正确路径是.../EndPages/css/main.css
,所以无法正确加载 -
如果在
kindadd.html
中引入样式表的路径写为EndPages/css/main.css
:那么在从别的页面(前往该页面的路由为/EndPages,其html路径为/EndPages/index.html) 点击链接跳转到
EndPages/kindAdd.html
的时候,跳转完毕后加载的css路径为.../EndPages/EndPages/css/main.css
,导致在这一步的css样式无法被正确加载
解决方案
最终我的解决方案是在 kindadd.html
里引用 main.css
的路径写为 css/main.css
,这样就不会在从index.html
跳转过来的时候css加载路径多一个EndPages
,同时为了让在 kindAdd
提交表单后的跳转能正确加载出css,将其表单提交的action
属性和对应的路由设置为EndPages/categoryAdd
//todo: 了解spring boot页面和控制层链接跳转的逻辑以及css样式文件引用的相对逻辑
知识点12: mysql
查询 SELECT
多条件搜索
直接用AND
或 OR
连接即可,复合运算有歧义时用括号括起来
参考链接
知识点13:带条件的多表联查
(下面这段代码还是有问题,详见问题14)
SELECT fc.first_category_id, sc.second_category_id, fc.first_category_name, sc.second_category_name FROM demo.tb_first_category fc LEFT JOIN demo.tb_second_category sc ON fc.first_category_id = sc.second_category_id WHERE first_category_name REGEXP "哈哈" OR second_category_name REGEXP "哈哈" limit 9;
注意用LEFT JOIN
左连接,当右表没有与左表匹配的记录的时候也能输出右表
(问题其实是变量写错的问题,ON
的条件应该是 fc.first_category_id = sc.first_category_id
,最终正确代码如问题15所叙述。
问题14:对多表联查时出现的问题的解决思路
本来是带条件的联合搜索,排查思路如下:
- 以为是关联表插入时出现问题,结果去数据库发现插入时是没问题的
- 后来发现搜索时只能搜索出主表或子表的结果,想到了应该用
LEFT JOIN
而不是INNER JOIN
连接,结果还是出现以下关联不起来的问题 - 以为是搜索条件设置不对,上网搜了一下将
WHERE
改为AND
,把后面OR
连接的条件用括号括起来结果问题更大,会搜出很多无关的结果 - 最后把
WHERE
后面的条件全去了发现还是无法将两个表关联输出,终于发现是ON
后面的条件写错了,应该是fc.first_category_id=sc.first_category_id
...
启示:这段代码是从上面的多表联查的代码里拷下来然后添加条件修改的,出现问题时一直以为是后面添加条件导致的问题,其实在上面多表联查时就有这个问题了,然而当时没有仔细检查结果,看到输出了就以为是对的了,导致后面debug更难发现问题
结果first_category_id
最终正确代码:
SELECT fc.first_category_id, sc.second_category_id, fc.first_category_name, sc.second_category_name FROM demo.tb_first_category fc LEFT JOIN demo.tb_second_category sc ON fc.first_category_id = sc.first_category_id WHERE first_category_name REGEXP #{arg0} OR second_category_name REGEXP #{arg0};
知识点15: 数据库查询语句的返回类型设置为String
时, 只能接受一个返回值
因为数据库里同一个name
有多条数据,如果用名字搜索的话会返回多个Id
返回的错误结果的截图:
返回的结果值过多问题16 :一个报错奇怪的问题: 实际上html中thymeleaf
的语法写错了引起的(也可能是html标签没关闭导致的)
排查思路
在这之前已经将controller
层新增的代码全给注释掉了,也检查了index
主页的路由发现也没问题
在浏览器的控制台查看错误说找不到一个定义在index.html
的javascript
函数,查看源码往下拉确实看不到,把该js
函数定义放在body
里也不行,由于将代码跑起来后再浏览器查看的源码是全部的源码,包括th:include
的页面也一并显示,很长,于是想到了是引用的页面引起的问题
在index
中用 thymeleaf
引用了其它几个界面,本来都没什么问题,但是在我修改了其引用的其中一个页面foodlist.html
后,原先在index
定义的</body>
之外</html>
之内的js
代码就识别不了了,说找不到在那定义的函数,如果把foodlist.html
文件 <body>
里的代码全注释了就没这么问题,注释其它引用的页面就不行
最后通过全部注释,逐段取消注释的方式找到了问题所在,其实应该能想到的,主要修改的就是这一段和另一段,要出现问题最有可能在这两段。想到的是 goodlist
的问题,去后台 debug
发现果然没有取到goodlist
,返回的全为null
这告诉我们一步一debug很重要!
最后发现的问题是entity
层写的对象的一个变量名称跟数据库的变量名不一样(之前已经试过mysql语句了,所以只能是在之后的封装出现问题了)
**后来发现如果html
页面的语法有问题,比如说某个标签少了一半符号( 如<li
) 也会报错 **
...
<div class="list_b_c">
<ul class="list_b" th:each="g : ${goodslist}">
<form method="post" action="/brandOperation">
<li class="b20"><input name="commodity_id" th:value="${g.commodity_id}" style="border: none;text-align: center;" readonly="true"></li>
<li class="b20"><input name="cname" th:value="${g.cname}" style="border: none;text-align: center;" readonly="true"></li>
<li class="b20"><input name="name" th:value="${g.name}" style="border: none;text-align: center;" readonly="true"></li>
<li class="b20"><input name="sc_name" th:value="${g.second_category_name}" style="border: none;text-align: center;" readonly="true"></li>
<input type="submit" name="submitInput" value="编辑" class="inputbutton">
<input type="submit" name="submitInput" value="删除" class="inputbutton" onclick="if(!confirm('are you ok?')){return false;}">
</form>
</ul>
</div>
...
知识点17:在提交按钮里写js代码会使表单的 required
属性失效
我在<button>
里添加的 js
代码是
onclick="alert('保存成功!');"
知识点18:mysql
GROUP_CONCAT
列表操作
将订单order_id
与其对应的多个商品idcommodity_id
以 逗号连接的字符串
的形式返回
SELECT o.order_id,
o.state,
od.quantity
GROUP_CONCAT(od.commodity_id ORDER BY od.commodity_id) AS commoditys
FROM tb_order o
LEFT JOIN tb_orders_detail od ON o.order_id = od.order_id
GROUP BY o.order_id;
问题19:一个神奇的问题,因为自动补全了相似的变量名导致一直发现不了问题
这件事告诉我们自动补全既能减少因为变量命名引发的bug,也可能导致变量命名引发的bug,因为会自动补全相似的变量命名反而更难发现问题
写着写着突然dao层扫描不到了,整个 service
层的函数调用的dao
层层对象都出现了红线,于是我就想着肯定是引用或者IDEA本身的问题,重启,查bug都不行,还把dao层对象删了重新声明,问题就在这里,本来应该是重启IDEA就解决的,结果因为我重新声明dao层对象的时候IDEA自动补全了一个跟后面用的变量名很像但不是的变量,导致问题转移了而且因为之前都能运行我也不会考虑是变量命名的问题,找了一个小时找不到来去吃饭睡了一觉,回到教室再看出现问题的地方发现提醒的问题不是说原来的类找不到了而是说声明的这个变量没有使用,然后就发现我新声明的变量跟下面使用的不对应....掀凳子.jpg
问题20:搞混了插入语句和更新语句,插入语句不能带条件
如果要更新应该用update
,可以带条件来更新指定数据行,如果要插入的话是插入新数据,不能带条件
知识点21:更新数据库上某字段的数值: 在原基础上增加
update demo.tb_flavor_commodity SET stock=stock+10 WHERE flavor_commodity_id=1;
问题22:又一个神奇的问题,因为在dao
层的数据库语句中的mysql
语句写少了个花括号,结果报路径错误...
之前也试过mysql
语句写错了,都是能成功运行的,只有在访问特定页面需要调用该函数时才会报错提示mysql
语句错了,结果这次直接整个项目都运行不了,还报了一大版的错误看上去就像是找不到dao
层一样
最后实在没法了仔细地看报错发现这个报错真有特点,不是把错误根源放在前面,而是放在最后面...大概意思是A错了因为B错了,B错了因为C错了....最后应该是F错了...,而且是一长句,不拖到最后面根本看不出什么问题
mysql
被错误地写成了如下
...value(#{arg0}),#{arg1,#{arg2}
改正改过后应该是
...value(#{arg0},#{arg1},#{arg2})
报错信息如下:
2019-07-08 16:44:07.404 WARN 14480 --- [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'endPagesController': Unsatisfied dependency expressed through field 'endpagesservice'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'endPagesServiceImpl': Unsatisfied dependency expressed through field 'endpagesdao'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'endPagesDao' defined in file [D:\Git\codehub_download\ShopProject\target\classes\com\example\demo\dao\EndPagesDao.class]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: org.apache.ibatis.builder.BuilderException: Could not find value method on SQL annotation. Cause: org.apache.ibatis.builder.BuilderException: Parsing error was found in mapping #{arg1,#{arg2}. Check syntax #{property|(expression), var1=value1, var2=value2, ...}
知识22:mysql
给表取了别名之后,就不能用原来的名字了
原来的代码
SELECT tb_order.order_id, tb_order.state,tb_order_detail.detail_id,tb_order_detail.quantityGROUP_CONCAT(tb_commodity.cname) FROM tb_order o LEFT JOIN tb_order_detail od ON o.order_id = od.order_id LEFT JOIN tb_commodity c ON od.commodity_id=c.commodity_id GROUP BY o.order_id;
这段 mysql
语句有三个问题:
-
tb_order_detail.quantity
和GROUP_CONCAT
之间应该有逗号隔开 -
GROUP_CONCAT(tb_commodity.cname)
和FROM
之间应该有cname
,并且该cname
之前不能有表名,即不能是tb_commodity.cname
或cname
,而GROUP_CONCAT()
括号里的表名可以带也可以不带 - 后面
LEFT JOIN
后给三个表起了别名,所以前面SELECT
的变量的表名也应该用别名,用原名会报错
之前我觉得在变量名前加上表名更严谨避免多个表中有重名变量,但是现在发现有时候写表名不仅是多次一举的行为,还可能因此引发错误,比如上面的第2点,cname就不能带表名,带了就报错
后来发现GROUP_CONCAT(c.name) 相当于SELECT
出来的变量名,所以和前一个变量应该用逗号隔开。这个 cname
是新变量名,表示GROUP_CONCAT(c.cname)
连接成字符串后的新名称, 所以不能加表名.
前缀
后来的代码
SELECT o.order_id,o.state,od.detail_id,od.quantity, GROUP_CONCAT(c.cname) cname FROM tb_order o LEFT JOIN tb_orders_detail od ON o.order_id = od.order_id LEFT JOIN tb_commodity c ON od.commodity_id=c.commodity_id GROUP BY o.order_id
将od.quantity 也聚合起来,并且将两个聚合名设为 quantity_group
, cname_group
:
SELECT o.order_id,o.state,od.detail_id,GROUP_CONCAT(od.quantity) quantity_group, GROUP_CONCAT(c.cname) cname_group FROM tb_order o LEFT JOIN tb_orders_detail od ON o.order_id = od.order_id LEFT JOIN tb_commodity c ON od.commodity_id=c.commodity_id GROUP BY o.order_id
参考链接:
MySQL连表操作LEFT JOIN和GROUP_CONCAT函数分组使用
知识点23:java的嵌套列表与嵌套数组
一开始时我是用嵌套列表的,后来遇到错误没想到是声明长度的问题就换了二维数组了,后来发现如果只声明了一维的大小没有声明二维的话,虽然编辑器不会报错,但是运行项目时会报错
如下所示,这样会出错:
//orderlist.size()是一个列表的长度,sgroup.length是一个数组的长度
String[][] arr =new String[orderlist.size()][];
for (int j=0;j<sgroup.length;j++){
arr[i][j]=sgroup[j];//这条语句会引发错误,因为arr[i][j]的第二维长度没有声明,相当于第二维长度为0
}
可以声明时只初始化行数,之后再初始化列数
参考链接
java 数组 声明时只初始化行数,之后再初始化列数的嵌套数组
知识点24:thymeleaf
应该不可以迭代两个对象(列表/数组等)
thymeleaf
是可以嵌套迭代的,但是不可以并列迭代两个对象,如下所示:在同一个<ul>
列表中,只迭代orderlist
列表变量或只迭代二维数组 all_commodity
(嵌套迭代了) 都是可以的,但是这两个变量放同一个<ul>
列表里迭代就会报错。
<ul class="list_b" th:each="o:${orderlist}" th:each="one_order:${all_commodity}">
<li style="width:17%"><input name="cname" th:value="${o.cname_group}" style="width:100%;border: none;text-align: center;" readonly="true"></li>
<select class="form-control" name="name" style="width:100px;margin-left: 210px;">
<option th:each="oo:${one_order}" th:value="${oo}" th:text="${oo}">初始值</option>
</select>
</ul>
知识点25:英文搜索的技巧:在了解thymeleaf
是否可以迭代两个兑现更多过程中,我在搜了几次前面的结果都不是我想要的,然后我用英文搜,结果一搜就出来了
我的中文搜索关键词 themeleaf th:each 在同一区域同时迭代两个对象
我的英文搜索关键词thymeleaf th:each iterate two object in one area
第一个搜索结果的链接如下:
Thymeleaf th:each two iterate two listings - Stack Overflow
里面的答案说了thymeleaf
无法这样做,变通的方法是把它们放到一个map
(中文搜索结果中我看到有提到map
,也想到可能要把这个 list
和 arr
组一个 map
)了,另外一个方法就是创建一个对象,这个对象包含了要迭代的这个list
和 arr
(这个方法其实之前我也想到了,就是觉得麻烦)
Unfortunately you cannot do it that way.
Two options that I can think of right now:
- If the lists are of equal size and indexes correspond to same object, put them in a Map and iterate the map. That way you will get the room and roomType
- (Preferred) Create a object and save the room and roomType in it, then add it to single List and iterate the list.
I prefer the second method because you can guarantee what you are actually passing into the list and onto the view layer for processing.
知识点26:java里的变量声明问题:变量重新赋值后再添加到列表,添加到列表时没有更新变量
其实变量的声明和初始化在循环外的时候,每次打印 order_all_in_one.orderOne
也是不一样的,即每次循环的order_all_in_one
都是得到了更新的,但是添加到order_all_in_one_list
后,之后再用循环输出order_all_in_one_list
的元素又都是一样的...非常奇怪,把order_all_in_one
的声明和初始化放在循环里就没这个问题。
后来发现变量(可能只是针对对象变量)声明可以在循环外只声明一次,但是初始化要在循环内,即每次赋值后,下一次重新赋值前需要重新初始化
List<OrderAllInOneEntity_yrh> order_all_in_one_list=new ArrayList<>();
OrderAllInOneEntity_yrh order_all_in_one;/*只需声明一次*/
for(int i=0;i<orderlist.size();i++){
order_all_in_one=new OrderAllInOneEntity_yrh();/*该对象变量每次重新赋值前都要重新初始化*/
order_all_in_one.orderOne=orderlist.get(i);//一个订单聚合类对象
//System.out.println("look here!=================");
//System.out.println(order_all_in_one.orderOne);
String one_order_cname=orderlist.get(i).cname_group;//一个订单的全部商品:用逗号连接的字符串
String[] sgroup=one_order_cname.split(",");//每个订单的商品切割后存到一个临时数组中
order_all_in_one.cnameOneLineArr=sgroup;//一个订单的全部商品:一维数组
order_all_in_one_list.add(order_all_in_one);
}
网友评论