智能商贸系统03-完善头像、部门、完成添加/修改方法及解决懒加载no session/no serializer/n-to-n问题

本文详细介绍了智能商贸系统中如何完善头像和部门管理,包括新增头像属性和部门的domain配置。同时,针对懒加载过程中遇到的no session和no serializer问题,以及n-to-n关系处理的问题,提出了具体的解决办法,如使用OpenSessionInViewFilter、序列化注解以及处理关联对象的修改策略。
摘要由CSDN通过智能技术生成

1、完善头像

1.1、新增头像属性

①domain
在这里插入图片描述
②页面
在这里插入图片描述
注意:这里显示的头像是头像路径地址,需要通过formatter方法进行格式化,解决显示格式不一致,同时在对应的路径准备头像图片

//头像格式化
function imageFormt(v, r, i) {
    return `<img src="${v}" style="width: 50px;">`;
}

2、完善部门

  • 员工与部门是多对一的关系
  • 部门设置为懒加载的时候会出现no session问题,后面讲怎么解决
  • 部门外键一定与数据库一致
  • BaseQuery中创建查询规则需要注意属性问题【看代码】
  • 部门页面展示同样需要formatter格式化
  • 注意页面中部门name的属性问题
2.1、domain

①新建Department 类

@Entity
@Table(name = "department")
public class Department extends BaseDomain{

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Department{" +
                "name='" + name + '\'' +
                ", id=" + id +
                '}';
    }
}

②在Employee类中添加属性,并配置多对一关系

 	@ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="department_id")
    private Department department;
2.2、其他层

通过Ctr+F查找替换,勾选mach case区分大小写,将Employee有的复制一份并修改

  • query
  • repository
  • service
  • controller
3、页面

①/department/index.jsp
复制employee/index.jsp,注意内容修改,属性不同

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="/WEB-INF/views/head.jsp" %>
<html>
<head>
    <title>Title</title>
    <%--引入当前页面对应的js文件--%>
    <script src="/js/model/department.js"></script>
</head>
<body>

<div id="toolbar">
    <div style="margin-bottom:5px">
        <a href="#" data-method="add" class="easyui-linkbutton" iconCls="icon-add" plain="true">添加</a>
        <a href="#" data-method="edit" class="easyui-linkbutton" iconCls="icon-edit" plain="true">修改</a>
        <a href="#" data-method="delete" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
    </div>

    <form id="searchForm" method="post">
        部门: <input class="easyui-combobox" name="departmentid"
                   data-options="valueField:'id',textField:'name',url:'/department/list',panelHeight:'auto'"/>
        <a href="#" data-method="search" class="easyui-linkbutton" iconCls="icon-search">查询</a>
    </form>
</div>

<%--pagination:true分页条--%>
<table id="datagrid" class="easyui-datagrid"
       data-options="url:'/department/page',fitColumns:true,singleSelect:true,fit:true,pagination:true,toolbar:'#toolbar',onRowContextMenu:showMenu">
    <thead>
    <tr>
        <th data-options="field:'id',width:100">编码</th>
        <th data-options="field:'name',width:100" sortable="true">名称</th>
    </thead>
</table>

<%--添加或者修改的弹出框--%>
<div id="editDialog" class="easyui-dialog"
     data-options="iconCls:'icon-save',resizable:true,modal:true,closed:true,buttons:'#btns'">
    <form id="editForm" method="post">
        <table cellpadding="1">
            <tr>
                <td> 部门</td>
                <td>
                <td><input class="easyui-textbox" type="text" name="name" data-options="required:true"/></td>
                </td>
            </tr>
        </table>
    </form>
    <div id="btns">
        <a href="javascript:;" data-method="save" class="easyui-linkbutton c3" data-options="iconCls:'icon-ok'">确定</a>
        <a href="javascript:;" data-method="close" class="easyui-linkbutton c5"
           data-options="iconCls:'icon-cancel'">取消</a>
    </div>
</div>
<div id="gridMenu" class="easyui-menu" style="width:120px;">
    <div data-options="iconCls:'icon-add'" data-method="add">添加</div>
    <div data-options="iconCls:'icon-edit'" data-method="edit">修改</div>
    <div data-options="iconCls:'icon-remove'" data-method="del">删除</div>
</div>


</body>
</html>

②department.js

$(function () {

    //常用的元素都先在这里获取到
    var datagrid = $("#datagrid");
    var searchForm = $("#searchForm");
    var editForm = $("#editForm");
    var editDialog = $("#editDialog");

    $("*[data-method]").on("click", function () {
        //拿到点击触发的方法名
        var methodName = $(this).data("method");
        //动态调用方法
        xuxusheng[methodName]();

    })

    xuxusheng = {
        //方法的简写,需要javascript language version 6版本才行
        add() {
            //打开面板前把里面的数据清除
            editForm.form("clear");
            //修改添加框的标题
            editDialog.dialog({title:'添加用户'});
            //将密码框显示
            $("*[data-hide]").show();
            //禁用防止传参数
            $("*[data-hide]>td>input").textbox("enable");
            //把添加框(editDialog)打开
            editDialog.dialog("center").dialog("open");
        },
        save(){
            var url = "/department/save";
            console.debug(editForm);
            if ($("#departmentId").val()){
                url = "/department/update?_cmd=update";
            }
            editForm.form('submit', {
                //提交路径,变量名相同可以直接写
                url:url,

                onSubmit: function(){
                    // do some check
                    // return false to prevent submit;
                    //提交前验证
                    return $(this).form('validate');
                },
                //data是一个json字符串{"success":true}
                success:function(data){
                    //把一个Json字符串转成JSON对象
                    //eval("("+data+")")
                    var result = JSON.parse(data);
                    if(result.success){
                        //成功就刷新当前页面
                        datagrid.datagrid("reload");
                    }else{
                        $.messager.alert('警告',`添加失败,原因是:${result.msg}`,"warning");
                    }
                    //关闭对话框
                    xuxusheng.close();
                }
            });
        },
        close(){
            editDialog.dialog("close");
        },
        edit() {
            //获取选择的行
            var row = datagrid.datagrid("getSelected");
            //判断是否选择了一行
            //没有选择提示并结束
            if (!row) {
                $.messager.alert('提示', '请先选择一行进行修改', "info");
                return;
            }else {
                //将密码框隐藏
                $("*[data-hide]").hide();
                //禁用防止传参数
                $("*[data-hide]>td>input").textbox("disable");

                //修改添加框的标题
                editDialog.dialog({title:'修改用户'});
                console.debug(editDialog);


                //把添加框(editDialog)打开
                editDialog.dialog("center").dialog("open");

               //部门回显
               if (row.department){
                   //有部门就回显部门
                   row["department.id"] = row.department.id;
               }else {
                   //没有部门就回显空
                   row["department.id"]="";
               }
               //回显其他内容
                editForm.form('load',row);


            }

        },
        delete() {
            //获取选择的行
            var row = datagrid.datagrid("getSelected");
            //判断是否选择了一行
            if (!row) {
                //没有选择提示并结束
                $.messager.alert('提示', '请先选择一行进行删除', "info");
                return;
            }
            //选择了提示是否确认删除
            $.messager.confirm('确认', '您确认想要删除记录吗?', function (r) {
                if (r) {
                    //通过ajax删除
                    $.get("/department/delete", {id: row.id}, function (result) {
                        //如果成功,重新加载页面
                        if (result.success) {
                            datagrid.datagrid("reload");
                        }else{
                            //失败给出提示消息,这里使用飘~,可以使用el表达式,不用拼接字符串了
                            $.messager.alert('提示',`删除失败,原因是:${result.msg}`,"error");
                        }

                    });
                }
            });

        },
        search() {

            //1.拿到查询的值,
            // var username = $("#username").val();
            //var email = $("#email").val();
            //下面这个方法需要引入jquery.jdirk.js
            var params = searchForm.serializeObject();
            //2.进行查询
            datagrid.datagrid("load", params);
            //datagrid.datagrid("load", {username: username, email: email});
        }
    };

})
//头像格式化
function imageFormt(v,r,i) {
    return`<img src="${v}" style="width: 50px;">`;
}
//部门格式化
function deptFormt(v,r,i) {
    return v?v.name:"";
}

function showMenu(e, rowIndex, rowData) {
    //选中这个行
    $("#departmentGrid").datagrid("selectRow",rowIndex);
    //第0个位置的面板不支持相应功能
    e.preventDefault();
    $('#gridMenu').menu('show', {
        left: e.pageX,
        top: e.pageY
    });
}

3、添加/修改方法

  • 点击添加弹出对话框(设置对话框属性:居中,清除内容等)
  • 添加内容之后保存,调用save方法
  • 添加与修改通过是否有id进行判断
  • 修改的时候密码隐藏与禁用
  • 禁用密码造成数据丢失,通过@ModelAttribute(“xxUpdate”)解决数据丢失

在这里插入图片描述

3.1、employee.js代码

add方法

add() {
            //打开面板前把里面的数据清除
            editForm.form("clear");
            //修改添加框的标题
            editDialog.dialog({title: '添加用户'});
            //将密码框显示
            $("*[data-hide]").show();
            //禁用防止传参数
            $("*[data-hide]>td>input").textbox("enable");
            //把添加框(editDialog)打开
            editDialog.dialog("center").dialog("open");
        },

edit方法

edit() {
            //获取选择的行
            var row = datagrid.datagrid("getSelected");
            //判断是否选择了一行
            //没有选择提示并结束
            if (!row) {
                $.messager.alert('提示', '请先选择一行进行修改', "info");
                return;
            } else {
                //将密码框隐藏
                $("*[data-hide]").hide();
                //禁用防止传参数
                $("*[data-hide]>td>input").textbox("disable");

                //修改添加框的标题
                editDialog.dialog({title: '修改用户'});
                console.debug(editDialog);


                //把添加框(editDialog)打开
                editDialog.dialog("center").dialog("open");

                //部门回显
                if (row.department) {
                    //有部门就回显部门
                    row["department.id"] = row.department.id;
                } else {
                    //没有部门就回显空
                    row["department.id"] = "";
                }
                //回显其他内容
                editForm.form('load', row);


            }

        },

save方法

save() {
            var url = "/employee/save";
            if ($("#employeeId").val()) {
                url = "/employee/update?_cmd=update";
            }
            editForm.form('submit', {
                //提交路径,变量名相同可以直接写
                url: url,

                onSubmit: function () {
                    // do some check
                    // return false to prevent submit;
                    //提交前验证
                    return $(this).form('validate');
                },
                //data是一个json字符串{"success":true}
                success: function (data) {
                    //把一个Json字符串转成JSON对象
                    //eval("("+data+")")
                    var result = JSON.parse(data);
                    if (result.success) {
                        //成功就刷新当前页面
                        datagrid.datagrid("reload");
                    } else {
                        $.messager.alert('警告', `添加失败,原因是:${result.msg}`, "warning");
                    }
                    //关闭对话框
                    vivi.close();
                }
            });
        },
3.2、EmployeeController
//解决数据丢失
    @ModelAttribute("xxUpdate")
    public Employee xxUpdate(Long id, String _cmd) {
        if (_cmd != null) {
            Employee employee = employeeService.findOne(id);
            return employee;
        }
        return null;
    }
    //保存方法,返回一个json :{success:true/false,msg:xxx}
    @RequestMapping("/save")
    @ResponseBody
    public JsonResult save(Employee employee) {
        return saveOrUpdate(employee);
    }


    //修改方法,虽然和保存一样,但是为了权限问题将两个路径分开
    @RequestMapping("/update")
    @ResponseBody
    public JsonResult update(@ModelAttribute("xxUpdate")Employee employee) {
        return saveOrUpdate(employee);
    }

    //将保存和修改两个方法合并
    public JsonResult saveOrUpdate(Employee employee) {
        try {
            employeeService.save(employee);
            return new JsonResult();
        } catch (Exception e) {
            e.printStackTrace();
            return new JsonResult(false, e.getMessage());
        }
    }
}

4、解决no session问题

4.1、原因分析
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="department_id")
private Department department;

首先:这里的session是会话的意思,但是和之前的HttpSession是完全不同的东西,在hibernate框架框架中可以理解为sqlsession,与数据库交互的会话。
其次懒加载的含义是,在hibernate执行查询操作时,并不会真正去数据库中查找出数据,而是要等到使用时才去数据库中查找数据。
原因 :Hibernate 允许对关联对象、属性进行延迟加载【department】,但是必须保证延迟加载的操作限于同一个 sqlseesion范围之内进行。如果在controller层调用service将查询的结果返回给web层,这时sqlsession已经关闭。我们查询的结果中如果有懒加载的属性,不是同一个session,将拿不到这个属性,导致了懒加载数据的访问异常。

4.2 、解决办法

在web.xml文件配置 OpenSessionInViewFilter过滤器,这种方法是延迟session关闭的时机,把一个Hibernate Session和一次完整的请求过程对应的线程相绑定。

 		<filter>
            <filter-name>OpenSessionInViewFilter</filter-name>
            <filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>OpenSessionInViewFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>

5、解决no serializer问题

5.1、原因分析
  • jpa的懒加载对象自己为填加了一些属性,(“hibernateLazyInitializer”,“handler”,“fieldHandler”)
  • 这些属性会影响到SpringMVC返回Json,而返回时有个内省机制
  • 需要序列化对象有一个属性是一类类型,而你使用了Hibernate的延迟加载所以这里是个Hibernate的代理对象。该代理对象有些属性不能被序列化所以会报错。
5.2、解决办法,两种。

第一种

在懒加载属性上面加注解。注意,在实体类上面加是无效的。

 @JsonIgnoreProperties(value{“hibernateLazyInitializer”,“handler”,“fieldHandler”})
 private Department department;

第二种
第一步:创建一个新的类(重写com.fasterxml.jackson.databind.ObjectMapper)

package com.vivi.xxl.common;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class CustomMapper extends ObjectMapper {
    public CustomMapper() {
        this.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 设置 SerializationFeature.FAIL_ON_EMPTY_BEANS 为 false
        this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    }
}

第二步:在applicationContext-mvc.xml中添加配置

 <!-- Spring MVC 配置 解决 no searilzer -->
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>application/json; charset=UTF-8</value>
                        <value>application/x-www-form-urlencoded; charset=UTF-8</value>
                    </list>
                </property>
                <!-- No serializer:配置 objectMapper 为我们自定义扩展后的 CustomMapper,解决了返回对象有关系对象的报错问题 -->
                <property name="objectMapper">
                    <bean class="com.xuxusheng.aisell.common.CustomMapper"></bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

5、解决n-to-n问题

在这里插入图片描述

5.1、原因分析

一个持久化对象的OID被修改。
在本项目中,employee和department是属于多对一的关系,在修改员工中的部门时,出现了这个错误。
在这里插入图片描述
上图可以看到,在修改部门的时候,出现了department.id这个参数。
正常修改属性,是调用set()方法。
但是注意,这个 点 之前都会调用 get()方法,显示getDepartment,然后再set id,这里更改id的话就是修改的department的id。
而此时的department 是一个持久化对象,就会出现这个错误。

5.2、解决办法

当修改有关联对象的时候,将关联对象设置为空。

    //解决数据丢失
    @ModelAttribute("xxUpdate")
    public Employee xxUpdate(Long id, String _cmd) {
        if (_cmd != null) {
            Employee employee = employeeService.findOne(id);
            将修改的employee中的department对象设置为null
            employee.setDepartment(null);
            return employee;
        }
        return null;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值