复盘工作2024-11

复盘工作-2024-11-01

1.问题:(编辑、查看页共用的)jsp页面当是从“查看”按钮点击进入时,禁用页面上“创建分票”按钮的最推荐方法:直接隐藏掉,通过jQuery的.hide()方法;以及这种思路相关我错误用法的分析:

/**
     * 问题:(编辑、查看页共用的)jsp页面当是从“查看”按钮点击进入时,禁用页面上“创建分票”按钮的最推荐方法:
     * 1.直接隐藏掉,通过jQuery的.hide()方法
     * <a href="#" class="easyui-linkbutton l-btn l-btn-plain" plain="true" icon="icon-add"
     * onclick="updateRwCreateGzps(' 创建分票','','pmLpSelectPersonList','100%','100%')" id="">
     *         <span class="l-btn-left"><span class="l-btn-text icon-add l-btn-icon-left">创建分票</span></span>
     * </a>
     * jQuery的.hide()方法封装了“设置元素的display属性为none”这一操作,即其实现了.css('display', 'none');
     * display是css属性,而不是HTML属性,所以不能用.prop('display', 'none');
     */
    $(function () {
        // 这里我一开始写错了:location.href.url.indexOf('load=createFpDetail') != -1
        // 应该是location.href。
        // location.href属于window.location对象,这个href属性代表了当前窗口(或标签页)的url地址
        // location.href值如下:
        // http://localhost:8092/项目名/pmLpGzpController.do?
        // selectPersonList&gzpId=4028e4fa9240eb4a01924226fc300010
        // &load=createFpDetail&_=1730462929196
        // js里字符串的indexOf api:例如:a.indexOf(b),返回字符串a中子串b第一次出现的索引,
        // 如果存在则其值为0、1、2...(即索引)
        // 若返回-1,则说明不含子串b
        console.log('location.href: ' + location.href);
        if (location.href.indexOf('load=createFpDetail') != -1) {
            // url中含有load=createFpDetail,说明是点“查看”来打开的该页面
            // (查看按钮点击后url里拼接load=createFpDetail的代码这里省略)
            debugger;
            // 我一开始写的.prop('disabled', true);无效。
            // 因为对<a>标签(即超连接)本身并不支持disabled属性
            // disabled主要用于表单元素,例如<button><input><select><textarea>的禁用
            // $('.easyui-linkbutton.l-btn.l-btn-plain').prop('disabled', true);

            // 正确写法
            $('.easyui-linkbutton.l-btn.l-btn-plain').hide();

            // jQuery的.hide()方法封装了“设置元素的css属性,即设置元素的display属性为none”这一操作,即其实现了.css('display', 'none');
            // $('.easyui-linkbutton.l-btn.l-btn-plain').css('display', 'none');

            // 如下写法无效。display是css属性,而不是HTML属性,所以不能用.prop('display', 'none');
            // $('.easyui-linkbutton.l-btn.l-btn-plain').prop('display', 'none');
            // 同理.attr('display', 'none');也不好使,因为display不是HTML属性
            // $('.easyui-linkbutton.l-btn.l-btn-plain').attr('display', 'none');
        }
    })

复盘工作-2024-11-03

1.接上个问题:方法2(不好,不如直接隐藏掉按钮)禁用按钮的点击事件

/**
     * 问题:(编辑、查看页共用的)jsp页面当是从“查看”按钮点击进入时,禁用页面上“创建分票”按钮的最推荐方法:
     * 方法2(不好,不如直接隐藏掉按钮)禁用按钮的点击事件
     * <a href="#" class="easyui-linkbutton l-btn l-btn-plain" plain="true" icon="icon-add"
     * onclick="updateRwCreateGzps(' 创建分票','','pmLpSelectPersonList','100%','100%')" id="">
     *         <span class="l-btn-left"><span class="l-btn-text icon-add l-btn-icon-left">创建分票</span></span>
     * </a>
     */
    $(function () {
        console.log('location.href: ' + location.href);
        if (location.href.indexOf('load=createFpDetail') != -1) {
            // url中含有load=createFpDetail,说明是点“查看”来打开的该页面
            // (查看按钮点击后url里拼接load=createFpDetail的代码这里省略)
            debugger;
            // 我一开始写的如下错误代码会报错:TypeError: $(...).onclick is not a function
            // 错误代码:$('.easyui-linkbutton.l-btn.l-btn-plain').onclick(function () {
            //     错误代码:tip('查看页面不允许创建分票');
            // 错误代码:})
            // 分析:jQuery中不能使用原生dom属性例如onclick。而应该使用.click(function(){}),
            // 或.on('click', function(){});.on('click', function(){});这种更合适
            // $('.easyui-linkbutton.l-btn.l-btn-plain').click(function (event) {
            //     debugger;
            //     event.preventDefault();
            //     tip('test:查看页面不允许创建分票');
            // })

            // 使用.on('click', function(){});
            $('.easyui-linkbutton.l-btn.l-btn-plain').on('click', function (event) {
                // 阻止默认事件
                event.preventDefault();
                tip('查看页面不允许创建分票');
            })
        }
    })

复盘工作-2024-11-05

1.问题:从旧功能复制代码过来后没有改了input、textarea标签的id字段但没改name字段,那么html提交表单时,后端从实体中获取该字段时为null。

分析:后端根据前端input、textarea标签的name字段(而不是id字段),来给(接收到的)实体赋值。所以input、textarea标签的id和name字段都要修改为正确的。

前端表单元素:

<!-- 从旧功能复制代码过来后没有改了input、textarea标签的id字段但没改name字段,那么html提交表单时,后端从实体中获取该字段时为null。
                                分析:后端根据前端input、textarea标签的name字段(而不是id字段),来给(接收到的)实体赋值。所以input、textarea标签的id和name字段都要修改为正确的。 -->
                                <textarea id="delayReason" style="width:600px;" class="inputxt" rows="2" name="delayReason" datatype="*">${pmYjRwcPage.delayReason}</textarea>

后端:

@RequestMapping(params = "practiceDoUpdateDelayInfo")
@ResponseBody
public AjaxJson practiceDoUpdateDelayInfo(PmYjRwcEntity pmYjRwc, HttpServletRequest request) {
    // 当前端提交的form表单里没有name='delayReason'的输入框时,后端这里pmYjRwc实体中就获取不到delayReason,其值为null
    // 只有当前端提交的form表单里有name='delayReason'的输入框时,后端这里pmYjRwc实体中才能获取到前端录入的delayReason
    String delayReason = pmYjRwc.getDelayReason();
    System.out.println(delayReason);
    // 具体业务代码省略
}

2.问题:(1)给表格加文件类型图标:js动态根据文件扩展名来给表格设置不同的背景图片来实现;(2)相关样式梳理;(3)js里switch语句练习

jsp页面:

<t:dgCol title="文件名称"  field="fileName" formatterjs="addBackground"
   align="left" query="true" queryMode="single"  width="600"></t:dgCol>
<script src="webpage/com/pmis/archive/practicePmArchiveList.js"></script>

practicePmArchiveList.js:

/**
 * (1)给表格加文件类型图标:js动态根据文件扩展名来给表格设置不同的背景图片来实现;(2)相关样式梳理;(3)js里switch语句练习
 *
 * css是静态的样式表语言,它在页面加载时已经确定,且不支持动态传递参数。
 *      无法实现根据参数(这里即文件扩展名)来设置不同的backgroundImage的url路径,
 * 所以采用js来实现,创建span,然后根据文件扩展名动态设置属性(即不同的backgroundImage的url路径)
 * @param value
 * @param row
 * @param index
 * @returns {string}
 */
function addBackground(value, row, index) {
    // 获取文件类型(扩展名)
    let fileExtension = row.fileExtension;
    /**
     * js连续判断 switch格式,两种用法:
     * switch(true) {
     *     case (用于判断true还是false的表达式):
     *          // 该情况下要执行的语句;
     *          // break;跳出switch语句,不再执行后续case和default
     *          break;
     *     case (用于判断true还是false的表达式):
     *          // 该情况下要执行的语句;
     *          // break;跳出switch语句,不再执行后续case和default
     *          break;
     *     default:
     *          // 当表达式不等于任何一个value时执行的代码;
     * }
     * 或
     * switch(变量值) {
     *     case (常量值1):
     *          // 该情况下要执行的语句;
     *          // break;跳出switch语句,不再执行后续case和default
     *          break;
     *     case (常量值2):
     *          // 该情况下要执行的语句;
     *          // break;跳出switch语句,不再执行后续case和default
     *          break;
     *     default:
     *          // 当表达式不等于任何一个value时执行的代码;
     * }
     */
    // switch(true) {
    //     case ('png' === fileExtension || 'jpg' === fileExtension):
    //         fileExtension = 'jpg';
    //         break;
    //     case ('doc' === fileExtension || 'docx' === fileExtension):
    //         fileExtension = 'docx';
    //         break;
    //     case ('pdf' === fileExtension):
    //         fileExtension = 'pdf';
    //         break;
    //     case ('xls' === fileExtension || 'xlsx' === fileExtension):
    //         fileExtension = 'xlsx';
    //         break;
    //     case ('zip' === fileExtension || 'rar' === fileExtension):
    //         fileExtension = 'zip';
    //         break;
    //     default:
    //         fileExtension = 'default';
    // }
    // 等价于如下用法:
    // switch(fileExtension) {
    //     case ('png'):
    //         fileExtension = 'jpg';
    //         break;
    //     case ('jpg'):
    //         fileExtension = 'jpg';
    //         break;
    //     case ('doc'):
    //         fileExtension = 'docx';
    //         break;
    //     case ('docx'):
    //         fileExtension = 'docx';
    //         break;
    //     case ('pdf'):
    //         fileExtension = 'pdf';
    //         break;
    //     case ('xls'):
    //         fileExtension = 'xlsx';
    //         break;
    //     case ('xlsx'):
    //         fileExtension = 'xlsx';
    //         break;
    //     case ('zip'):
    //         fileExtension = 'zip';
    //         break;
    //     case ('rar'):
    //         fileExtension = 'zip';
    //         break;
    //     default:
    //         debugger;
    //         fileExtension = 'default';
    // }

    /**
     * 优化上述代码:
     */
    switch(fileExtension) {
        case ('png'):
        case ('jpg'):
            fileExtension = 'jpg';
            break;
        case ('doc'):
        case ('docx'):
            fileExtension = 'docx';
            break;
        case ('pdf'):
            fileExtension = 'pdf';
            break;
        case ('xls'):
        case ('xlsx'):
            fileExtension = 'xlsx';
            break;
        case ('zip'):
        case ('rar'):
            fileExtension = 'zip';
            break;
        default:
            fileExtension = 'default';
    }

    /**
     * 如下写法是错误的
     *
     * 分析:switch语句在js里并不是一个表达式,而是一个语句,
     * 这意味着switch不能直接返回一个值给变量,它只能根据条件执行不同的代码块
     * switch(变量名或表达式) {
     *     case value1:
     *          // 当表达式等于value1时执行的代码
     *          break;
     *     case value1:
     *          // 当表达式等于value1时执行的代码
     *          break;
     *     default:
     *          // 当表达式不等于任何一个value时执行的代码
     * }
     */
    // fileExtension = switch(fileExtension) {
    //     case ('png'):
    //         return 'jpg';
    //         break;
    //     case ('rar'):
    //         return 'zip';
    //         break;
    //     default:
    //         return 'default';
    // }

    /**
     * 如下写法是错误的:
     *
     * 如下写法无法实现预期逻辑,
     * case ('png' || 'jpg')最终只会取'png'
     * 分析:'png' || 'jpg',是||运算,最终返回true or false,这时'png'被视为true,所以该表达式最终返回true,
     * 然而switch是与case后面的常量值进行严格相等===判断,
     * 即case ('png' || 'jpg')最终返回的是'png',
     * (即并没有'jpg')所以最终无法实现预期逻辑
     *
     * 即case ('png' || 'jpg')实际等同于case ('png'),
     */
    // 错误代码:
    // switch(fileExtension) {
    //     case ('png' || 'jpg'):
    //         fileExtension = 'jpg';
    //         break;
    //     case ('doc' || 'docx'):
    //         fileExtension = 'docx';
    //         break;
    //     case ('pdf'):
    //         fileExtension = 'pdf';
    //         break;
    //     case ('xls' || 'xlsx'):
    //         fileExtension = 'xlsx';
    //         break;
    //     case ('zip' || 'rar'):
    //         fileExtension = 'zip';
    //         break;
    //     default:
    //         fileExtension = 'default';
    // }

    /**
     * 创建span元素
     * 1.一开始我写的是错的:let span = document.createElement('<span>');
     * F12会报错:Uncaught DOMException: Failed to execute 'createElement' on 'Document':
     * The tag name provided ('<span>') is not a valid name.
     * 2.分析:document.createElement();接收一个参数,参数是HTML标签的名称,而不是整个标签。
     * 即应该是document.createElement('span');且标签名称应使用小写。
     * document.createElement()会创建一个新元素,并返回该元素的引用。
     */
    let span = document.createElement('span');
    /**
     * 设置背景图片
     * 1.我一开始写的是错的:span.style.backgroundImage('url(/images/archive/' + fileExtension + '.png');
     * F12里报错:TypeError: span.style.backgroundImage is not a function
     * 2.分析:
     * 2.1错误1:在js中,span.style.backgroundImage不是一个函数,而是一个属性。
     * (后面设置的display、transform等等也同理,都是属性,而不是函数)
     * 因此不能像调用函数那样给其传值。
     * 2.2错误2:
     */
    span.style.backgroundImage = 'url(images/archive/' + fileExtension + '.png)';
    /**
     * span.style.display='inline-block'使元素即可以设置宽高,又可以像内联元素一样排列。
     */
    span.style.display = 'inline-block';
    /**
     * 向下偏移5px
     * 分析:英文里transform意为“改变”
     * transform属性,用于元素的移动、旋转等
     * translateY()函数是transform属性的一个值
     * translateY(参数值),延Y轴平移,y轴向下为正向,参数值为正数时,即向下平移;参数值为负数时,即向上平移;
     */
    span.style.transform = 'translateY(5px)';
    /**
     * 设置图片大小
     * 1.我一开始写的是错误的:span.style.backgroundSize='contains';
     * 2.分析:span.style.backgroundSize有两个属性值可以设置为与“包含”有关的行为,分别是“contain”和“cover”。
     * contain:保持图像的宽高比,缩放图像使其完全适应背景定位区域,同时确保图像的至少一个维度(宽或高)等于背景定位区域的相应维度。
     * cover:保持图像的宽高比,缩放图像使其完全覆盖背景定位区域,同时确保图像的至少一个维度(宽或高)等于或超过背景定位区域的相应维度。
     */
    span.style.backgroundSize = 'contain';
    span.style.backgroundPosition = 'center center';
    span.style.width = '20px';
    span.style.height = '20px';
    span.style.backgroundRepeat = 'no-repeat';
    return span.outerHTML + value + '.' + row.fileExtension;
}

复盘工作-2024-11-06

1.问题:sql向oracle库里某表加两个字段,分别是varchar2(32)和DATE类型

分析:语法:alter table 表名 add 列名 varchar2(30)(即类型);

alert table 表名 add 列名 date(即类型);

comment on column 表名.列名 is 注释;

/* 
问题:sql向oracle库里某表加两个字段,分别是varchar2(32)和DATE类型
分析:语法:alter table 表名 add 列名 varchar2(30)(即类型);
alert table 表名 add 列名 date(即类型);
comment on column 表名.列名 is 注释;
*/

/* 我一开始写错了:alter table 表名 add column 列名 varchar2(30)
正确的:去掉column关键词:alter table 表名 add 列名 varchar2(30)*/
alter table practice_rwc add delay_person_id varchar2(32);
/* 我一开始写错了:comment on practice_rwc.delay_person_id is '延期人ID';
正确的:要加column关键词:comment on column practice_rwc.delay_person_id is '延期人ID';*/
comment on column practice_rwc.delay_person_id is '延期人ID';

/* date类型 */
alter table practice_rwc add delay_create_date date;
comment on column practice_rwc.delay_create_date is '延期创建时间';

复盘工作-2024-11-07

1.问题:测试发现前端请求无法进入后端接口里的断点

原因:控制器接口应该定义为public而不是private

分析:springmvc使用反射机制来查找和调用控制器方法。反射机制要求被调用的方法必须是可访问的。而java中用private访问修饰符修饰的方法,对于类外部(包括框架代码)是不可访问的。spring通过依赖注入和aop(面向切面编程)代理来管理bean的生命周期和行为。这些机制也依赖于反射机制来工作,同样受到java访问控制规则的限制。 所以使用springmvc框架时,控制器接口都应声明为public。

/**
     * 问题:测试发现前端请求无法进入后端接口里的断点
     * 原因:控制器接口应该定义为public而不是private
     * 分析:springmvc使用反射机制来查找和调用控制器方法。
     * 反射机制要求被调用的方法必须是可访问的。
     * 而java中用private访问修饰符修饰的方法,对于类外部(包括框架代码)是不可访问的。
     *
     * spring通过依赖注入和aop(面向切面编程)代理来管理bean的生命周期和行为。
     * 这些机制也依赖于反射机制来工作,同样受到java访问控制规则的限制。
     *
     * 所以使用springmvc框架时,控制器接口都应声明为public。
     * @param swbm
     * @return
     */
    @RequestMapping(method = RequestMethod.GET,value = "checkSwbm")
    @ResponseBody
    public Result checkSwbm(String swbm){
        // 省略具体业务代码
    }

2.问题:访问功能,后端报错:org.hibernate.hql.internal.ast.QuerySyntaxException:PmSjbQrCodeEntity is not mapped [from PmSjbQrCodeEntity where sbId=?

解决:这种错误通常是hibernate没扫描到实体类造成的。

分析:在spring配置文件中,当使用通配符(例如*.)来指定hibernate应该扫描的包时, 这个通配符只能匹配直接子包,而不能递归地匹配多层子包。 即<value>com.pmis.sbtz.*.entity</value>只能匹配com.pmis.sbtz.somepackage.entity包下的实体。 所以要加上<value>com.pmis.sbtz.tz.*.entity</value>这样才能匹配到com.pmis.sbtz.tz.somepackage.entity包下的实体。

spring-mvc-hibernate.xml:

<property name="packagesToScan">
    <list>
        <value>com.pmis.sbtz.*.entity</value><!-- pmis -->
        <!-- 问题:访问功能,后端报错:org.hibernate.hql.internal.ast.QuerySyntaxException:
            PmSjbQrCodeEntity is not mapped [from PmSjbQrCodeEntity where sbId=?
            解决:这种错误通常是hibernate没扫描到实体类造成的。
            分析:在spring配置文件中,当使用通配符(例如*.)来指定hibernate应该扫描的包时,
            这个通配符只能匹配直接子包,而不能递归地匹配多层子包。
            即<value>com.pmis.sbtz.*.entity</value>只能匹配com.pmis.sbtz.somepackage.entity包下的实体。
            即匹配不到PmSjbQrCodeEntity在com.pmis.sbtz.sjbsb.tz.entity包下的实体类,
            所以要加上<value>com.pmis.sbtz.sjbsb.*.entity</value>这样才能匹配到com.pmis.sbtz.sjbsb.somepackage.entity包下的实体-->
        <value>com.pmis.sbtz.sjbsb.*.entity</value>
    </list>
</property>

3.idea里怎样快速查看某个controller有哪些方法:编辑窗口打开该controller后,在idea左下角点击“Structure”选项卡就能看到类的方法。

4.问题:(1)在jsp里写样式:用<style><style>标签;(2)图片背景时,url的写法;(3)display属性为inline-block,而不是我一开始错误的:设置position属性为inline-block

<script type="text/javascript">
 function addBackground(value, row, index) {
   return "<span class='fileExtensionBackground'></span>" + value + "." + row.fileExtension;
 }
 </script>
<!-- 我一开始写错了:<script type="text/css">...这种不对,应该在<style></style>里写样式 -->
<style>
 .fileExtensionBackground{
  /* 一开始我写做:background-image: url("/images/archive/pdf.png");
  报错:GET http://localhost:8092/images/archive/pdf.png 404 (Not Found)
  分析:
  1.当写做/images时,浏览器会尝试从网站根目录开始查找图片,实际上图片并不在网站根目录下的/images/archive/文件夹中,
  所以会报错404 not defined
  2.正确的:写做url("images/archive/pdf.png");时,浏览器会相对于当前HTML文件的位置来查找图片。这个我就记住就行了。
  */
  background-image: url("images/archive/pdf.png");
  background-repeat: no-repeat;
  background-position: center center;
  background-size: contain;
  /* 我一开始写错了:position: inline-block;这样图片无法展示,
   应该是display: inline-block;
   分析:display属性控制元素的显示类型。
   其取值例如:block即块级元素;inline即内联元素;
   inline-block:像内联元素一样不会占据整行,但可以设置宽高。
   而position属性决定了元素的定位方式。
   其取值例如:relative即相对定位;absolute即绝对定位。*/
  display: inline-block;
  width: 20px;
  height: 20px;
  transform: translateY(5px);
 }
</style>

复盘工作2024-11-14:

1.<f7-list-input class="block-input" placeholder="暂无数据" :disabled="!getEditStatus('bdepBcaqcs')||ticketWriteType!true||ticketType!1?'disabled':'is-required data-item-required'" resizable label="补充安全措施" type="textarea" :value="mainTable.bdepBcaqcs" @input="(event) => this.handleInput(event, 'list-item-8', 'bdepBcaqcs')"></f7-list-input>报错:Invalid prop: type check failed for prop "disabled". Expected Boolean, got String with value "is-required data-item-required".

分析:disabled属性需要提供个true/false值,而不是这里提供的字符串’disabled'和'is-required data-item-required'。

这里可以根据具体需求,改为如下两种:

1.1:disabled="!getEditStatus('bdepBcaqcs')||ticketWriteType!true||ticketType!1" 这种控制是否disabled

1.2:class="!getEditStatus('bdepBcaqcs')||ticketWriteType!true||ticketType!1?'disabled':'is-required data-item-required'" 这种是根据条件使用不同的样式,备注:项目里已分别定义了’disabled'和'is-required data-item-required'这两种样式。

2.业务js里怎样使用常量js里定义的常量:

分析:常量js里定义的常量是全局作用域,而业务js同样是定义在全局作用域,所以直接使用即可。但要注意在引入业务js的业务jsp页面里,需要先引入常量js,再引入业务js,这里涉及到js加载顺序问题。

业务jsp里:

<!-- 引入常量js -->
<script src='webpage/common/js/constants.js'></script>
<!-- 引入业务js -->
<script src='webpage/com/项目名/common/workflow/wfTodoTasksList.js'></script>

常量js里:即constants.js:

// 山东
const PRACTICE_PROVINCE_SHANDONG = 'Shandong';

业务js里:即wfTodoTasksList.js:

function practiceMethod() {
    // 直接使用即可
    console.log(PRACTICE_PROVINCE_SHANDONG);
}

3.<f7-list inline-labels no-hairlines-md class="first-page">这代码里inline-labels no-hairlines-md是什么?

分析:这是framework7里,两个类名,定义了<f7-list>的某些样式或行为。我知道这点就够了。

4.<f7-list-input label="单位" type="text" :value="mainTable.sysCompanyName" @input="mainTable.sysCompanyName = $event.target.value" disabled></f7-list-input>和<f7-list-input readonly label="单位" type="text" :value="mainTable.sysCompanyName" @input="mainTable.sysCompanyName = $event.target.value"></f7-list-input>有什么异同?哪个好

分析:这个我记住就行:disabled和readonly都能实现禁用编辑;基于实际使用场景,readonly更好,因为readonly允许用户选中input框的内容(后续可以用于复制等操作)。

并且当设置disabled或readonly后,如果确定业务上始终不可编辑,则建议删掉冗余的input事件:@input="mainTable.sysCompanyName = $event.target.value"

5.关于项目里常用的<f7-list>、<f7-list-item>、<f7-accordion-content>、<f7-list-input>结构:分析如下,我记住即可:accordion /əˈkɔːdiən/ 手风琴

<!-- f7-list定义一个列表 -->
<f7-list>
    <!-- f7-list-item定义列表项 -->
    <f7-list-item>
        <!-- f7-accordion-content定义手风琴样式 -->
        <f7-accordion-content>
            <!-- f7-list-input定义输入框组件,例如文本输入框、选择器、单选多选 -->
            <f7-list-input></f7-list-input>
        </f7-accordion-content>    
    </f7-list-item>
</f7-list>

6.关于ul li:last-child{background: green;}和ul li:last{background: green;}的区别:

分析:ul li:last-child是标准的css选择器,选中ul下最后一个li元素,注意:如果ul下的最后一个子元素不是li,则这个li不会被选中。

ul li:last不是标准的css选择器,而是jQuery提供的选择器,其选中ul的最后一个li元素,注意:即使这li不是ul的最后一个子元素,这个li也会被选中。

复盘工作2024-11-15:

1.PmLpGzpEntity pmLpGzpEntity = new PmLpGzpEntity();然后代码中手动设置id为uuid,最后调框架的saveOrUpdate();方法,运行时后端报错:org.hibernate.HibernateException: identifier of an instance of com.pmis.lp.entity.PmLpChildlistEntity was altered from 4028e5389292f67c019293475dd80004 to null

分析:当实体id字段的getId()方法上有自动生成id的注解时(即给id设置了自动生成策略),如果代码中手动再设置id,然后调用saveOrUpdate方法,因为手动设置了id,hibernate在缓存中根据此代码里手动生成的id查找不到对象,就会报这错。

解决方法:PmLpGzpEntity pmLpGzpEntity = new PmLpGzpEntity();创建对象后,代码中不手动设置id,设置其他业务字段后,直接调用save()方法;然后通过pmLpGzpEntity.getId()方法获取id,用于后续业务操作。

实体上配置的id的自动生成策略:

@Id
    @GeneratedValue(generator = "paymentableGenerator")
    @GenericGenerator(name = "paymentableGenerator", strategy = "uuid")
    @Column(name ="ID",nullable=false,length=32)
    public java.lang.String getId(){
        return this.id;
    }

复盘工作-2024-11-20

1.练习:前后端请求路径映射,以及前端向后端传数组参数,以“发布计划”接口为例

后端:

@Controller
@RequestMapping("/pmYjXsZnxsjhController")
public class PmYjXsZnxsjhController extends BaseController {
    @RequestMapping(params = "practiceDoFbjh")
    @ResponseBody
    public AjaxJson practiceDoFbjh(@RequestParam(value = "ids[]", required = false) List<String> ids) {
        System.out.println(ids);
        AjaxJson ajaxJson = new AjaxJson();
        ajaxJson.setMsg("计划发布成功");
        ajaxJson.setSuccess(true);
        return ajaxJson;
    }

    /**
     * 练习:前后端请求路径映射,以及前端向后端传数组参数,以“发布计划”接口为例
     *
     * 问题1:路径映射:
     * @RequestMapping(params = "prefixpracticeDoFbjh")会检查请求路径中是否含有参数:prefixpracticeDoFbjh,若有则会匹配该请求。
     * 即当url为'pmYjXsZnxsjhController.do?prefixpracticeDoFbjh'时,会请求到该接口。
     *
     * 疑问:当分别有:practiceDoFbjh、prefixpracticeDoFbjh、practiceDoFbjhSuffix、prefixpracticeDoFbjhSuffix
     *         这4个@RequesMapping(params = "")的值时,是怎么匹配的呢?
     * 验证结果:当url为'pmYjXsZnxsjhController.do?prefixpracticeDoFbjh'时,会请求到prefixpracticeDoFbjh接口,
     *         而不会请求到practiceDoFbjh接口。
     * 原因分析:1.当分别有:practiceDoFbjh、prefixpracticeDoFbjh、practiceDoFbjhSuffix、prefixpracticeDoFbjhSuffix
     *         这4个@RequesMapping(params = "")的值时,后端会检测路径中是否含有这4个值,
     *         当url为'pmYjXsZnxsjhController.do?prefixpracticeDoFbjh'时,practiceDoFbjh、prefixpracticeDoFbjh都满足,
     *         这时,springmvc会精确匹配到完全相同的路径:prefixpracticeDoFbjh。
     *         2.我测试实际当后端没有 @RequestMapping(params = "prefixpracticeDoFbjh") 接口时,
     *         路径请求prefixpracticeDoFbjh接口后端会直接报错。
     * 结论:结合例子试验,我就记住该怎么用就行了:@RequestMapping(params = "")这种路径映射,就可以理解为完全匹配。
     *         即要请求到@RequestMapping(params = "methodA")接口,则url写法就应该是:'pmYjXsZnxsjhController.do?methodA'。
     *
     * 问题2:前端向后端传数组参数
     * 分析:@RequestParam(value = "ids[]", required = false) List<String> ids
     *         springmvc中,@RequesParam用于将http请求参数区数据绑定到控制器方法的参数上,
     *         value属性指定了请求参数的名称。value="ids[]",期望从http请求中获取名为ids的参数。
     *         []指明ids是数组,(在springmvc中,即使不使用[],主要参数类型为List或数组,spring也会将多个具有相同名称的参数值绑定到这个列表上。
     *         因此value="ids"和value="ids[]"在绝大多数情况下是等效的,但value="ids[]"更清晰)
     *         required=false指明ids这参数不是必传的。
     *         List<String> ids,指明期望接收一个String类型的列表。springmvc会将请求中名为ids[]的所有参数值转成字符串并添加到列表中。
     *
     *     补充:@ResponseBody声明该方法的返回值应作为http响应体返回,而不是解析作为视图名。
     *         由于@ResponseBody的存在,springmvc会自动将AjaxJson对象(这是jeecg框架封装的实体类用于后端返回信息)转换成JSON格式的字符串,
     *         并作为http响应体返回给前端。
     */
    @RequestMapping(params = "prefixpracticeDoFbjh")
    @ResponseBody
    public AjaxJson prefixpracticeDoFbjh(@RequestParam(value = "ids[]", required = false) List<String> ids) {
        System.out.println(ids);
        AjaxJson ajaxJson = new AjaxJson();
        ajaxJson.setMsg("计划发布成功");
        ajaxJson.setSuccess(true);
        return ajaxJson;
    }

    @RequestMapping(params = "practiceDoFbjhSuffix")
    @ResponseBody
    public AjaxJson practiceDoFbjhSuffix(@RequestParam(value = "ids[]", required = false) List<String> ids) {
        System.out.println(ids);
        AjaxJson ajaxJson = new AjaxJson();
        ajaxJson.setMsg("计划发布成功");
        ajaxJson.setSuccess(true);
        return ajaxJson;
    }

    @RequestMapping(params = "prefixpracticeDoFbjhSuffix")
    @ResponseBody
    public AjaxJson prefixpracticeDoFbjhSuffix(@RequestParam(value = "ids[]", required = false) List<String> ids) {
        System.out.println(ids);
        AjaxJson ajaxJson = new AjaxJson();
        ajaxJson.setMsg("计划发布成功");
        ajaxJson.setSuccess(true);
        return ajaxJson;
    }
}

前端:

function practiceDoFbjh(title,url, id,width,height) {
    let rows = $('#' + id).datagrid('getSelections');
    if (rows != null && rows.length > 0) {
        let selectedIds = [];
        for (let i = 0; i < rows.length; i++) {
            selectedIds.push(rows[i].id);
        }
        $.ajax({
            url: 'pmYjXsZnxsjhController.do?practiceDoFbjh',
            type: 'post',
            data: {
                ids: selectedIds
            },
            cache: false,
            success: function (jsonObject) {
                let d = $.parseJSON(jsonObject);
                if (d.success) {
                    tip(d.msg);
                    // 后续业务逻辑省略
                }
            }
        })
    }
}

function prefixpracticeDoFbjh(title,url, id,width,height) {
    let rows = $('#' + id).datagrid('getSelections');
    // js获取数组长度,用.length属性:获取数组或字符串长度,使用.length属性
    if (rows != null && rows.length > 0) {
        let selectedIds = [];
        for (let i = 0; i < rows.length; i++) {
            selectedIds.push(rows[i].id);
        }
        // $.ajax是jQuery库提供的一个方法。用于执行异步http(Ajax)请求
        $.ajax({
            // url 请求路径
            url: 'pmYjXsZnxsjhController.do?prefixpracticeDoFbjh',
            // type设置请求类型
            type: 'post',
            // data参数包含要发送到服务端的数据,这里是一个对象{ids: selectedIds}
            data: {
                // 传递id数组
                ids: selectedIds
            },
            // cache: false,不缓存请求的响应
            cache: false,
            // success是回调函数,当请求成功时执行,接收服务端返回的数据作为参数
            success: function (jsonObject) {
                // $.parseJSON将JSON字符串转换成js对象
                let d = $.parseJSON(jsonObject);
                if (d.success) {
                    tip(d.msg);
                    // 后续业务逻辑省略
                }
            }
        })
    }
}

 

posted on 2024-11-03 18:56  平凡力量  阅读(29)  评论(0)    收藏  举报