复盘工作-2024-09
复盘工作-2024-09-07
1.HTML当文档元素加载完后执行的简易写法:
/* 我一开始的写法是错误的: 错误:$.(document).ready(function(){}) 运行程序浏览器控制台会报错 错误:$.(function(){}) 运行程序浏览器控制台会报错 点号用于访问对象的方法或属性。 应该直接用$来调用jQuery的方法 $(function() {})等价于$(document).ready(function() {}) */ /* $(document).ready(function() { debugger; alert(2); }) */ /**/ $(function() { debugger; alert(1); });
复盘工作-2024-09-08
1.sql根据记录字段值判断取不同的值:case when a then b else c end as d:
select case when t.AZDD is null then '' else t.AZDD end as AZDD from pm_xtgs_qr_code t where t.ID = '8a8a8a8b8f9508d6018f950b8ac80026';
复盘工作-2024-09-09
1.可变参数:
private void printArray(int[] args) { System.out.println("练习可变参数:直接遍历数组"); // for(类型 变量名 : 要遍历的实现了Iterable接口的变量名),增强for循环,从jdk5开始引入 for (int i : args) { System.out.println(i); } } /** * 验证可变参数 * * 可变参数,varargs,即variable-length arguments,从jdk5开始引入 * 用法(定义):类型... 变量名 * 可接受0个或其他任意数量个数的 该类型的 变量,作为参数 * 例如int... args,可接受0个或其他任意数量的int类型的参数,作为参数 * * 如果传数组进来,也行。会自动将数组转成多个参数。例如将入参new int[]{1, 2}转换为1, 2 * @param args */ private void printVariableLengthArguments(int... args) { System.out.println("练习可变参数:使用可变参数"); for (int i : args) { System.out.println(i); } } private void practice() { // 练习可变参数:直接遍历数组 // 声明数组:new int[]{1, 2};而不是我一开始的错误的:new int("1", "2") System.out.println("练习可变参数:直接遍历数组"); this.printArray(new int[]{1, 2}); // 练习可变参数:使用可变参数 System.out.println("练习可变参数:使用可变参数"); this.printVariableLengthArguments(3, 4); this.printVariableLengthArguments(); System.out.println("练习可变参数:使用可变参数:如果传数组进来,自动将数组转成多个参数"); this.printVariableLengthArguments(new int[]{5, 6}); }
2关于sql中and条件的顺序是否会影响性能:
private void practiceAndOrder() { StringBuilder stringBuilder = new StringBuilder(); String str1 = " where columnA = 1 and columnB = 2 "; String str2 = " where columnB = 2 and columnA = 1 "; // 在开发中,无需过分关注and条件的顺序对性能的影响。str1和str2用哪个都行,可以根据实际拼接sql时方便程度自行决定。 // 因为这是sql的设计原则之一:and条件顺序无关性:and逻辑运算的各条件顺序不影响sql执行的结果和性能 // oracle也必然遵循了这一原则。 }
3.关于向数组中加入元素,尤其是动态的加入:实现了Collection接口的对象的toArray(T[] arr)方法:
/** * 练习向数组中加入元素,尤其是动态的加入:实现了Collection接口的对象的toArray(T[] arr)方法 */ private void practiceAddItemToArray() { // 方式1:声明时直接初始化: System.out.println("声明时直接初始化:"); String[] arr1 = {"hello", "world", "java"}; for (String s : arr1) { System.out.println(s); } // 方式2:先声明数组然后赋值元素: int size = 4; String[] arr2 = new String[size]; for (int i = 0; i < size; i++) { arr2[i] = "Element" + i; } System.out.println("先声明数组然后赋值元素:"); for (String s : arr2) { System.out.println(s); } // 方式3:动态的向数组中加入元素,例如数组大小编译时未知、需要在程序运行时添加或删除元素: List<String> strList = new ArrayList<>(); strList.add("hello"); strList.add("word"); strList.add("SpringMVC"); strList.add("Java"); // toArray(T[] arr)是Collection接口中定义的。List接口继承了Collection接口。而Collection接口定义了toArray()方法 // 需要传入一个数组类型,这里只会用到数组的类型,不会用到数组的具体内容,只使用其类型信息 // toArray(T[] arr)方法返回一个新的数组,包含ArrayList中的所有元素 String[] arr3 = strList.toArray(new String[0]); System.out.println("动态的向数组中加入元素:"); for (String s : arr3) { System.out.println(s); } }
复盘工作-2024-09-11
1.java后端处理带有反斜杠\的sql:
/** * 当要执行的sql中有反斜杠(backslash)\时,java代码需对其前面加个\表示转义。否则会报错(java会将其识别为转义字符的开始) * 即java代码里用两个\\来表示一个反斜杠\。 * * 反斜杠 backslash \ * 斜杠 slash / * * regexp_substr是oracle提供的函数,通过正则表达式从字符串中提取子串 * regexp_substr(x.xlmc, '\d+')提取xlmc字段中第一个连续的数字序列,例如从“产品编号12345描述”中提取出“12345” */ private void practiceSqlWithBackslash() { StringBuilder builder = new StringBuilder(); builder.append("select x.id, x.xlmc, to_number(regexp_substr(x.xlmc, '\\d+')) BH from PM_SB_ZWYC_XL x"); List<Map<String, Object>> mapList = systemService.findForJdbc(builder.toString()); System.out.println("练习处理sql中的反斜杠:"); for (Map<String, Object> map : mapList) { System.out.println(map.get("BH").toString()); } }
2.js控制隐藏/显示元素:通过属性选择器:
/** * 隐藏一个元素:选中元素后,通过.hide()方法来隐藏 * * 这里为测试方便调用框架查询方法,无实际意义 */ function pmSjbQrCodeStatisticsListsearch() { debugger; // 一开始我写的是错误的:$(name='ywdw').hide(); // 正确的应该是通过属性选择器来选择:通过[属性名="属性值"]来选择 $('[name="ywdw"]').hide(); } /** * 恢复显示一个(被隐藏了的)元素:选中元素后,通过.show()方法来恢复显示 * * 这里为测试方便调用框架查询方法,无实际意义 */ function searchpmSjbQrCodeStatisticsListReset() { debugger; $('[name="ywdw"]').show(); }
复盘工作-2024-09-12
1.js判空,以及假如基于具体需求判空:
let arr = new Array(); arr.push('1'); console.log('练习js获取数组长度.length方法:自测数组长度是:' + arr.length); /** * js通用判空 * * 一般js判空是比较 null、''、undefined、NaN * 与NaN比较使用Number.isNaN(val) * * NaN:not a number的缩写 * Number是js内置的对象,用于严格判断是否严格等于NaN */ function isEmpty(val) { return val === null || val === '' || val === undefined || Number.isNaN(val); } /** * js判空:基于具体业务,假如空数组[]、空对象{}也需要被判为空 * * Array.isArray(val)判断val是否是数组, * 若是数组,再通过.length属性取数组长度 * * typeof val === 'object'判断val是否是对象类型, * 若是对象,则再通过Object.keys(val)获取val对象的可枚举属性的键组成的数组 * 若该数组长度为0,则证明val是空对象 */ function isEmptyOrEmptyArrayOrEmptyObj(val) { return val === null || val === '' || val === undefined || Number.isNaN(val) || (Array.isArray(val) && val.length === 0) || (typeof val === 'object' && Object.keys(val).length === 0); } /** * 测试判空方法 */ console.log('isEmpty(null):' + isEmpty(null));// true // js里转义字符也是反斜杠,与java相同 console.log('isEmpty(\'\'):' + isEmpty(''));// true console.log('isEmpty(undefined):' + isEmpty(undefined));// true console.log('isEmpty(NaN):' + isEmpty(NaN));// true console.log('isEmpty(false):' + isEmpty(false));// false console.log('isEmpty([]):' + isEmpty([]));// false console.log('isEmpty({}):' + isEmpty({}));// false /** * 测试判空方法:基于具体业务,假如空数组[]、空对象{}也需要被判为空 */ console.log('isEmptyOrEmptyArrayOrEmptyObj(null):' + isEmptyOrEmptyArrayOrEmptyObj(null));// true console.log('isEmptyOrEmptyArrayOrEmptyObj(\'\'):' + isEmptyOrEmptyArrayOrEmptyObj(''));// true console.log('isEmptyOrEmptyArrayOrEmptyObj(undefined):' + isEmptyOrEmptyArrayOrEmptyObj(undefined));// true console.log('isEmptyOrEmptyArrayOrEmptyObj(NaN):' + isEmptyOrEmptyArrayOrEmptyObj(NaN));// true console.log('isEmptyOrEmptyArrayOrEmptyObj(false):' + isEmptyOrEmptyArrayOrEmptyObj(false));// false console.log('isEmptyOrEmptyArrayOrEmptyObj([]):' + isEmptyOrEmptyArrayOrEmptyObj([]));// true console.log('isEmptyOrEmptyArrayOrEmptyObj({}):' + isEmptyOrEmptyArrayOrEmptyObj({}));// true
复盘工作-2024-09-13~202409-16
1.html包含两个标签页的页面初始化、及标签(切换)点击事件:
<!-- 这个#divBox无实际意义,仅用于验证结构伪类选择器里first和first-child的区别 --> <div id="pracBox"> <ul id="pracUl"> <div id="pracInnerDiv">id为pracUl的ul下的第一个子元素:id为pracInnerDiv的div</div> <li>练习li-1-不能被$("#pracBox li:first-child").hide()选中并隐藏(因为其不是其父元素的第一个子元素,第一个子元素是pracInnerDiv</li> <li>练习li-2-可以被$("#pracBox li:eq(1)").hide()选中并隐藏</li> </ul> </div> <script> $(document).ready(function() { /** * 隐藏(#content下所有具有hideTab类的)所有标签的内容,目的是在页面加载时仅显示一个标签的内容 * jQuery中$()函数用于选择或查找html元素,并返回这些元素的jQuery对象 * #content id选择器 * .hideTab 类选择器 * #content .hideTab 后代选择器(选择所有后代,对太爷来说,爷爷 爸爸 孙子 全是其后代) * .hide()是jQuery提供的方法,用于隐藏($()匹配的)对象 */ $("#content .hideTab").hide(); /** * 激活第一个标签。为#tabs下的第一个li元素设置id为current,以此标记它为当前激活的标签 * (id为current就能标记为激活,应该是与jeecg框架封装的效果有关) * #tabs li:first 结构伪类选择器,选择#tabs下第一个li元素 * 而#tabs li:first-child 也是结构伪类选择器,但first-child 选择#tabs下所有作为其父元素第一个子元素的li元素 * 即只有当li元素恰好是它直接父元素的第一个子元素时,它才会被选中。 * 这个父元素可能是#tabs的直接子元素(例如ul或ol),也可能是嵌套更深的元素。 */ $("#tabs li:first").attr("id", "current"); /** * 仅用于练习,无实际意义 * $("#pracBox li:first-child").hide();不能隐藏掉#parcBox下第一个li。 * 因为这个li不是其直接父元素(即#pracBox)的第一个子元素(#pracBox的第一个子元素是<div id="pracInnerDiv"></div>) * $("#pracBox li:eq(1)").hide();能选中#pracBox下的 * 第二个li(即练习li-2)并隐藏掉。(备注:eq,equal with index,索引相同。索引从0开始算。 * eq(1)即我们传统意义上口语数数的第二个li,而第一个li使用first就可以选中,li:first相当于li:eq(0)) */ $("#pracBox li:first-child").hide(); $("#pracBox li:eq(1)").hide(); /** * 显示第一个标签的内容。.fadeIn()是jQuery函数,淡入显示某元素,透明度从0(完全透明)变为1(完全不透明) * 我一开始写的$("#content .hideTab:first").fadeIn;有两问题:1.用div:first更好, * 这样可以避免需要确认第一个div始终是hideTab类的一个实例;2.应该写为.fadeIn()而不是.fadeIn */ $("#content div:first").fadeIn(); /** * 标签点击事件处理 * 我一开始写的是错的:$("#tabs a").onclick(function(e){});因为onclick不是jQuery对象的一个方法。 * 解析:应该写为$("#tabs a").click(function(e){}); * * jQuery的.click(function(e){}); * 1.是jQuery提供的方法,用于为匹配的元素集合中的每个元素添加一个点击事件监听器 * 2.其内部实际调用.on('click', handler);方法 * 3.其允许使用jQuery强大功能,例如选择器,链式调用 * * 原生JavaScript的.onclick=function(e){}; * 1.是原生JavaScript的属性,用于为单个元素直接添加一个点击事件处理函数 * 2.不支持选择器,并且一次只能为一个元素添加一个处理函数(如果再次设置.onclick,之前的处理函数将被覆盖。 */ $("#tabs a").click(function (e) { /** * 阻止默认行为,(阻止原超链接例如跳转等行为) */ e.preventDefault(); // 隐藏所有内容 $("#content .hideTab").hide(); /** * 重置标签的ID。 * 我一开始写的有问题:$("#tabs a").attr("id", "");应该是$("#tabs li").attr("id", ""); * 分析: * 1.选择器差异: * $("#tabs li").attr("id", "");用于将#tabs下所有li的id属性置为空字符串。这通常用于重置或清除这些 * li元素的id属性,以便后续可以重新给当前激活的li标签设置id。 * $("#tabs a").attr("id", "");将#tabs下所有a元素的id属性设置为空字符串。然而,在标准html标签页(tabs) * 实现中,a标签通常用于点击以切换内容,但其本身并不直接用作内容的容器或表示当前激活的标签。因此,将a标签的 * id置为空字符串对于管理标签页的激活状态来说并不是有用的操作。 * 2.功能目的: * $("#tabs li").attr("id", "");是为了清除所有li标签的id,这样可以确保只有一个li(即当前激活的那个)有特定的 * id(例如current),从而可以通过CSS或JavaScript更容易地识别和管理它。 * $("#tabs a").attr("id", "");对管理标签页的激活状态没帮助,因为a标签本身并不直接控制内容的隐藏与显示 */ $("#tabs li").attr("id", ""); /** * 激活当前(被点击)标签。我一开始写错了:$($(this)).attr("id", "current"); * 应该是$(this).parent().attr("id", "current"); * 1.大体解析: * 1.1我一开始错误瞎写的:$($(this)).attr("id", "current"); * $(this)获取当前(被点击)元素,然后再套一层$()没有用,仍是当前(被点击)元素, * 再.attr("id", "current");即对当前被点击元素a设置id为current,无意义 * 1.2正确的:$(this).parent().attr("id", "current");解析: * $(this)选中当前(被点击)元素(即被点击的a),然后.parent选中其父元素(即li), * .attr("id", "current");将li的id属性置为current * 2.细节解析: * 2.1 $(this) * 2.1.1 $(this).parent().attr("id", "current");被触发时(通常是在一个事件处理函数里,例如点击事件), * this关键字会引用触发该事件的元素。 * 在当前上下文中,在$("#tabs a").click(function(e){});内, * this指的是当前被点击的a标签的dom元素。 * 2.1.2 $(this)将这个dom元素转换成一个jQuery对象。这样就可以用jQuery提供的 * 一系列方法和属性来操作这个元素了。 * 2.2 .parent() * 2.2.1 .parent()是jQuery中的一个方法,它用于获取当前jQuery对象集合中每个元素的直接父元素, * 并将这些父元素包装成一个新的jQuery对象返回。 * 2.2.2 本例中,调用.parent()会找到a标签的直接父元素,即li标签,然后转换成jQuery对象返回。 */ $(this).parent().attr("id", "current"); /** * 显示对应的内容。 * 我一开始瞎写的错误代码:$("$parent(this)").fadeIn; * 正确的应该是:$("#" + $(this).attr("title")).fadeIn(); */ $("#" + $(this).attr("title")).fadeIn(); // $("#tabs a").click(function(e){});最后一定加;分号 }); }); </script>
2.html里点击不同区域触发展示不同页面并给出被点击区域的激活效果:
<script> /** * 设备域点击切换事件触发函数 * @param sbArea */ function changeBaseView(sbArea) { if (!isEmpty(sbArea) && sbArea != originalSbArea) { /** * 将iframe的src置为空字符串,从而重置iframe内容 */ var iframe = document.getElementById('iframe'); iframe.src = ''; /** * 显示iframe内容(页面中默认通过css将iframe设置为display:none了) * 我一开始写的是错的:bmTab.style.display = block; * CSS中属性值应该被当作字符串来处理。需要用引号包起来。 * 应该是bmTab.style.display = 'block'; * 详细解析: * bmTab是一个指向html中元素的引用(通过document.getElementById获取的) * .style是访问该元素样式的一个接口, * .display是样式对象中的一个属性,用于控制样式的显示类型。 * .display设置为'block'时,是告诉浏览器以块级元素的方式来展示该元素。 * 设置为'none'时,该元素就是不可见的。 */ let bmTab = document.getElementById('bm-tab'); bmTab.style.display = 'block'; // 设置iframe新内容 iframe.src = 'pmSjbQrCode' + sbArea + 'Controller.do?statisticsList'; /** * 页面上除了被点击的(设备域)div外,其余均去掉“激活”样式 * document.querySelectorAll('.top-tap-check-state')选择页面上 * 所有具有类名'top-tap-check-state'的元素,并将这些元素的集合存储到elements里。 * querySelectorAll方法返回一个NodeList对象,类似于数组 * element.classList是一个实时的DOMTokenList集合,元素的类列表。 * .classList.remove是一个dom元素的方法,从一个元素的类列表中移除一个或多个类名。 * 调用remove方法时,classList集合会立即更新,页面上该元素的类属性也会相应的更新。 */ let elements = document.querySelectorAll('.top-tap-check-state'); elements.forEach(element => { element.classList.remove('top-tap-check-state'); }) /** * 给选中的设备域div设置激活样式 * 我一开始写的是:document.getElementById('top-table-' + sbArea).classList.add('top-tap-check-state'); * 这是在原有的类基础上加上'top-tap-check-state'类 * 更好的是用原版写法:document.getElementById('top-table-'+sbArea).className = 'top-tap-check-state'; * 备注:不能写成.className('top-tap-check-state');因为className是一个属性,而不是方法。 */ document.getElementById('top-table-' + sbArea).className = 'top-tap-check-state'; } } </script> <style> .bm-tab{ display: none; } </style>
复盘工作-2024-09-17~2024-09-18
1.jQuery移除页面数据字典组件中的某个选项:模糊匹配和完全精确匹配:
$(document).ready(function(){ /** * 移除select的某个option,例如"---请选择---" * select[name="ywdw"] option 后代选择器 * :contains("---请选择---") 过滤出内容包含"---请选择---"的(选项);备注:contains是模糊匹配 * * 完全相同匹配: * 我第一次写的是错的; * 错误:$('select[name="ywdw"] option').filter(item => function () { * 错误: return item.innerHTML().toUpperCase() === '---请选择---'; * 错误:}).remove(); * 错误原因解析: * 1. .filter()函数,其回调函数的返回期望是一个布尔值,true/false(表示是否保留当前元素),而不是一个函数 * 我的上述写法是尝试在箭头函数中返回一个函数。 * 2. jQuery中获取dom元素的文本或html内容,用的是.html(),或.text()(对于纯文本内容,.text()比.html()更合适), * 而.innerHTML是原生JavaScript dom元素的属性。 * 我第二次写的也是错的; * 错误:$('select[name="ywdw"] option').filter($(this).text()==='---请选择---').remove() * 错误原因解析: * 1.filter方法的回调函数中没有正确使用this关键字。 * 在jQuery的.filter()方法中,回调函数会被自动调用,并且每次调用时this都会被设置为当前迭代的元素。 * 但是我的写法里,直接在.filter()方法外部使用了$(this),这里的this不指向任何特定的<option>元素。 * 2.并且.filter()期望回调函数返回一个布尔值,表示是否保留当前元素, * 而我写的$(this).text()==='---请选择---'只是进行了比较操作,(并没有返回) * 正确的写法: * $('select[name="ywdw"] option').filter(function() { * return $(this).text()==='---请选择---'; * }).remove(); */ // $('select[name="ywdw"] option:contains(---请选择--- ")').remove(); $('select[name="ywdw"] option').filter(function() { return $(this).text()==='---请选择---'; }).remove(); });
复盘工作-2024-09-20
1.java类中定义常量List
/** * java类中定义常量List */ private static final List<String> MY_CONSTANT_LIST = Collections.unmodifiableList(Arrays.asList("Java", "Spring", "Oracle")); /** * 在业务接口中验证常量List */ @RequestMapping(params = "datagrid") public void datagrid(PmLpGzpEntity pmLpGzp,HttpServletRequest request, HttpServletResponse response, DataGrid dataGrid) { System.out.println("====================================================="); System.out.println("输出常量List:"); for (String s : MY_CONSTANT_LIST) { System.out.println(s); } System.out.println("验证确实是常量List:尝试向其中插入元素,会报错:"); try { MY_CONSTANT_LIST.add("Hibernate"); } catch (Exception e) { e.printStackTrace(); System.out.println("错误:" + e.getMessage()); } System.out.println("====================================================="); MY_CONSTANT_LIST.add("VUE"); // 后续语句不会执行,因为上一句MY_CONSTANT_LIST.add("VUE");已经报错了,未try catch,程序不会继续走后续代码 System.out.println("尝试添加后,再次输出常量List:"); for (String s : MY_CONSTANT_LIST) { System.out.println(s); } }
2.练习获取request中的请求参数ParameterMap,以及遍历Map并获取其中每个键值对的键和值:
@RequestMapping(params = "datagrid") public void datagrid(PmXtgsQrCodeEntity pmXtgsQrCode,HttpServletRequest request, HttpServletResponse response, DataGrid dataGrid) { /** * 练习获取request中的请求参数ParameterMap * * .getParameterMap()是Java servlet API中HttpServletRequest的一个方法, * 返回Map<String, String[]>。因为一个请求参数可能对应多个值,所以是String[]。 * * parameterMap.entrySet()返回Set集合(包含映射的每一个键值对构成的set集合), * 然后可以通过entry遍历parameterMap(的entrySet()) * entry.getKey();获取键值对的键(key) * entry.getValue();获取键值对的值(value) * 解析: * .entrySet()是Map接口的一部分,返回一个Set<Map.Entry<K,V>>, * 这个集合包含了Map中所有的键值对(Entry)。 * 每个Entry都是一个键值对的表示,它提供了获取键(getKey())和值(getValue())的方法。 */ Map<String, String[]> parameterMap = request.getParameterMap(); for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) { String key = entry.getKey(); System.out.println("key: " + key); String[] value = entry.getValue(); System.out.println("value: "); for (String s : value) { System.out.println(" " + s); } } }
复盘工作-2024-09-21
1.myeclipse开发时js缓存相关问题解决:
1.1首先使用chrome无痕模式,更改js后默认刷新页面就能加载最新代码;若未生效,则ctrl+shift+delete清浏览器缓存;
1.2若上述未生效(即js文件位于web应用内,且更改没有通过浏览器刷新或清除缓存后生效),则在myeclipse的server选项卡里右击(已经部署到Tomcat的)项目,选择redeploy/reload application,即重新部署redeploy下项目;
1.3在实际开发中,上述两步骤可以解决大多数静态资源更新相关问题。
1.4另外有个project/clean菜单,
1.4.1用于清理项目的构建目录(通常是bin或target目录),并重新构建项目。还可以解决一些由于旧的编译文件或资源文件引起的问题。
1.3.2使用场景:如果改变了项目的构建路径、依赖关系或其他构建相关的设置,并且这些更改没有反映在运行时,这时可以考虑使用clean功能。
2.EasyUI获取列表选中行
/** * EasyUI获取列表选中行 * 解析: * <t:datagrid name="pmSjbQrCodeList">,是EasyUI的datagrid组件, * 对于js,上述代码会产生一个id为pmSjbQrCodeList的table并将datagrid追加到这个table上 * 即浏览器控制台可以看到的<table width="100%" id="pmSjbQrCodeList" * toolbar="#pmSjbQrCodeListtb" style="display: none;"></table> * 所以可以通过$('#pmSjbQrCodeList') id选择器来选择到这个table * getSelections是EasyUI的datagrid组件的方法,用于返回所有的选中行,返回一个数组 */ let rowData = $('#pmSjbQrCodeList').datagrid('getSelections'); console.log('输出所选行:'); /** * 一开始我写的是错的:i < rowData.size。 * js获取数组长度,应该用rowData.length; */ if (rowData.length > 0) { for (let i = 0; i < rowData.length; i++) { console.log(rowData[i]); } } else { console.log('没选中行'); }
3.sql:group by语句后要接原始列名,因为sql引擎在解析group by语句时还没有看到select列表中定义的别名。order by也是同理,后面接原始列名。
/*这里一定是group by z.P_ID,而不能是group by SSZF。 若写成group by SSZF,会报错:不是group by表达式 解析:sql引擎在解析group by时还没有看到select列表中定义的别名。 所以group by后面要跟原始列名,而不是跟select xx as 后的别名字段。*/ /*这里order by z.P_ID无实际业务意义(实际并不用该字段排序),仅为了说明order by最好也是跟原始列名。 尽管有的数据库支持order by后接select列表定义的别名(例如mysql、oracle), 但这并不是所有数据库都支持的行为。为了兼容性和可读性,最好跟原始列名。*/ select max(z.YWDW) as YWDW, z.P_ID as SSZF, count(z.ID) as TOTAL_NUM, count(q.ID) as HAS_GEN_NUM, (count(z.ID) - count(q.ID)) as NOT_GEN_NUM from v_code_znyc z left join pm_sjb_qr_code q on z.ID = q.SB_ID group by z.P_ID order by z.P_ID;
4.参数化查询时,写模糊查询时参数绑定符号?的位置、%拼接方法及位置问题
/** * 参数化查询时,写模糊查询时参数绑定符号?的位置、%拼接方法及位置问题。 * 我一开始写的是错误的: * 错误:notGenQrSbBuilder.append(" and z.SBMC like '%?%' "); 备注:notGenQrSbBuilder是用于构建查询条件的StringBuilder * 错误:argList.add(sbmc); * 备注:argList是后续要用于转换成数组(然后作为参数传递给可接受可变参数的框架查询方法)的List<String> * sbmc是查询参数“设备名称” * 错误解析: * 不能把参数绑定符号(例如?)写在字符串常量内部(例如'%?%'),这样会被数据库会将?识别为字符串的一部分, * 而不是一个可以替换的参数,最终导致参数化查询时报错“无效的索引列” * 正确的应该是java代码里,将%%和查询参数sbmc拼接后的结果(作为一个整体),作为参数化查询的参数 */ if (!StringUtil.isEmptyOrStrNull(sbmc)) { // 错误代码: // notGenQrSbBuilder.append(" and z.SBMC like '%?%' "); // argList.add(sbmc); // 正确代码: notGenQrSbBuilder.append(" and z.SBMC like ? "); argList.add("%" + sbmc + "%"); }
复盘工作-2024-09-22
1.myeclipse redeploy完成的标志:控制台输出:信息: 已完成重新加载名为/xxxx的上下文(备注:xxxx为项目名)
2.java中continue和break:
2.1停止本次迭代中的后续语句,继续执行下一次迭代中的内容,使用continue
2.2直接结束整个循环,使用break
/** * java中continue和break * 1.停止本次迭代中的后续语句,继续执行下一次迭代中的内容,使用continue * 2.直接结束整个循环,使用break */ System.out.println("停止本次迭代中的后续内容,继续执行下一次迭代中的内容,使用continue:"); // 输出 0 1 2 3 4 6 7 8 9 for (int i = 0; i < 10; i++) { if (i == 5) { continue; } System.out.println(i); } System.out.println("直接结束整个循环,使用break:"); // 输出 0 1 2 3 4 for (int i = 0; i < 10; i++) { if (i == 5) { break; } System.out.println(i); }
3.myeclipse中java类定义修改后,redeploy application即可,无需restart server。
redeploy(重新部署):清理服务器上已有的项目文件,然后重新部署最新内容。通常包括JSP、XML、HTML、jar包及class文件等。
而restart重启服务器:如果没有影响Tomcat运行的配置修改,无需进行restart server操作。
4.将组装查询sql及参数的代码抽成公共方法后,封装该dto,用于该公共方法向业务方法返回sql及参数数组,(返回参数数组便于业务方法获取后,直接传给接收可变参数作为参数的框架查询方法)。解析:参数传递时,封装dto,便于存取。
dto:
/** * @author * @Description: 将组装查询sql及参数的代码抽成公共方法后, * 封装该dto,用于该公共方法向业务方法返回sql及参数数组 * (返回参数数组便于业务方法获取后,直接传给接收可变参数作为参数的框架查询方法) * 解析: * 参数传递时,封装dto,便于存取 * @date 2024/9/22 */ public class PracSqlWithParams { /** * 查询sql */ private String sql; /** * 查询参数构成的对象数组 */ Object[] params; /** * 构造函数 * @param sql * @param params */ public PracSqlWithParams(String sql, Object[] params) { this.sql = sql; this.params = params; } public String getSql() { return sql; } public Object[] getParams() { return params; } }
练习:获取未生成二维码设备列表的sql及参数
/** * 练习:获取未生成二维码设备列表的sql及参数 * @param request * @return */ private PracSqlWithParams pracGenerateSearchNotGenSqlWithParams(HttpServletRequest request) { StringBuilder notGenBuilder = new StringBuilder(); notGenBuilder.append(" select z.ID, "); notGenBuilder.append(" z.YWDW, "); notGenBuilder.append(" case "); notGenBuilder.append(" when z.xl_id is null then "); notGenBuilder.append(" 'KGZ' "); notGenBuilder.append(" else "); notGenBuilder.append(" 'XL' "); notGenBuilder.append(" end as TYPE_XL_OR_KGZ, "); notGenBuilder.append(" z.p_id as SSZF, "); notGenBuilder.append(" z.SBLXBM, "); notGenBuilder.append(" z.SBMC "); notGenBuilder.append(" from v_code_znyc z "); notGenBuilder.append(" left join pm_sjb_qr_code q "); notGenBuilder.append(" on z.id = q.sb_id "); notGenBuilder.append(" where z.ewm = '1' "); notGenBuilder.append(" and q.id is null "); // 参数list List<String> argList = new ArrayList<>(); String ywdw = StringUtil.getEncodePra(request.getParameter("ywdw")); if (!StringUtil.isEmptyOrStrNull(ywdw)) { notGenBuilder.append(" and z.ywdw = ? "); argList.add(ywdw); } String typeXlOrKgz = StringUtil.getEncodePra(request.getParameter("typeXlOrKgz")); if (PmisCommonConstant.TYPE_XL_OR_KGZ_XL.equals(typeXlOrKgz)) { notGenBuilder.append(" and z.xl_id is not null "); } else if (PmisCommonConstant.TYPE_XL_OR_KGZ_KGZ.equals(typeXlOrKgz)) { notGenBuilder.append(" and z.xl_id is null "); } String sszf = StringUtil.getEncodePra(request.getParameter("sszf")); if (!StringUtil.isEmptyOrStrNull(sszf)) { notGenBuilder.append(" and z.p_id = ? "); argList.add(sszf); } String sblxbm = StringUtil.getEncodePra(request.getParameter("sblxbm")); if (!StringUtil.isEmptyOrStrNull(sblxbm)) { notGenBuilder.append(" and z.sblxbm = ? "); argList.add(sblxbm); } String sbmc = StringUtil.getEncodePra(request.getParameter("sbmc")); if (!StringUtil.isEmptyOrStrNull(sbmc)) { notGenBuilder.append(" and z.sbmc like ? "); argList.add("%" + sbmc + "%"); } return new PracSqlWithParams(notGenBuilder.toString(), argList.toArray()); }
5.增强for循环,从jdk5开始支持
/** * 增强for循环,jdk5开始支持 */ System.out.println("====================练习增强for循环:==================="); int[] arr = {1, 2, 3, 4, 5}; for (int i : arr) { System.out.println(i); }
复盘工作-2024-09-23
1.练习:后端转码获取前端输入的查询内容:ISO8859-1转UTF-8。读取ISO8859-1编码的appQuery的byte[],然后以UTF-8编码返回字符串。
后端:
/** * 练习:后端转码获取前端输入的查询内容:ISO8859-1转UTF-8 * * String s = new String(appQuery.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); * 解析: * 读取ISO8859-1编码的appQuery的byte[],然后以UTF-8编码返回字符串 * @param request * @param dataGrid * @return */ @RequestMapping("/practiceGetList") public DataGrid practiceGetList(HttpServletRequest request, DataGrid dataGrid) { String appQuery = request.getParameter("appQuery"); if (!StringUtil.isEmptyOrStrNull(appQuery)) { // 前端查询“二”时,这里输出:后端直接输出原始ISO8859-1编码的查询参数:appQuery:??? System.out.println("后端直接输出原始ISO8859-1编码的查询参数:appQuery:" + appQuery); String s = new String(appQuery.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); // 前端查询“二”时,这里输出:读取ISO8859-1编码的appQuery的byte[],然后以UTF-8编码返回字符串:s:二 System.out.println("读取ISO8859-1编码的appQuery的byte[],然后以UTF-8编码返回字符串:s:" + s); } // 省略具体查询代码 return dataGrid; }
前端:
encodeURI是js提供的将统一资源标识符(URI)的组件进行编码。对ASCII字母和数字、-、_、.、!、~、*、'、(、) 以及 # 等字符进行编码,但会对其他所有字符进行编码(如空格会被转换为 %20)。从而避免用户输入的其他字符在URL中导致问题。通过encodeURI进行编码,可以确保用户输入搜索词可以安全的用于生成查询字符串并作为URL的一部分发送。
/** * 查询事件 * * let inputValue = document.getElementById('searchInputId').getElementsByTagName('input')[0].value; * 解析: * document.getElementById('searchInputId')通过id获取元素, * 然后.getElementsByTagName('input')在通过id找到元素内部,查找所有的input元素, * .getElementsByTagName返回的是一个HTMLCollection(一个类数组对象),包含了所有匹配的元素,所以通过[0]来访问第一个input元素, * 最后通过.value获取input元素的值 * * encodeURI(filter); * 解析: * encodeURI是js提供的将统一资源标识符(URI)的组件进行编码。 * 对ASCII字母和数字、-、_、.、!、~、*、'、(、) 以及 # 等字符进行编码,但会对其他所有字符进行编码(如空格会被转换为 %20)。 * 从而避免用户输入的其他字符在URL中导致问题。 * 通过encodeURI进行编码,可以确保用户输入搜索词可以安全的用于生成查询字符串并作为URL的一部分发送。 * * 注意我一开始写的是错的: * 错误:filter = '?appQuery=' + inputValue; * 错误:filter = encodeURI(filter); * 应该是只对用户输入进行encodeURI。即: * inputValue = encodeURI(inputValue); * filter = '?appQuery=' + inputValue; * * @param event */ goSearch(event) { const _self = this; let filter = ''; if ('input' === event) { let inputValue = document.getElementById('searchInputId').getElementsByTagName('input')[0].value; debugger; // 首先要判空 if (!_self.commonUtils.isStrIsNull(inputValue)) { inputValue = encodeURI(inputValue); filter = '?appQuery=' + inputValue; } } _self.defaultSearchTodoTask.params.filter = filter; _self.pracGetListTodoTask(1, false); },
前端请求时url:
_self.request.getList( { url: 'workflow/practiceGetList' + _self.defaultSearchTodoTask.params.filter, params: _self.defaultSearchTodoTask.params }