版权提醒
本系列文章为原创内容。转载请注明文章来源。

一般的方法

我在使用 Ajax 时,遇到了需要返回多项数据的需求,我自然地想到了使用 JSON 的方法。

我查找了许多在 Struts2 中使用 JSON 的教程,给出的方法大都是使用 struts2-json-plugin ,然后在 struts.xml 中设置类似下面的项:

<!-- 需要继承struts-default和json-default -->
<package name="Login" extends="struts-default,json-default"> 
        <action name="login">
            <result>/login.jsp</result>
        </action>
        <action name="loginaction" class="com.hpe.struts.LoginAction">
            <result type="json"><!-- 返回的数据类型为json -->
                <param name="res">data</param><!-- 这个地方的data就是在LoginAction中定义的data,它必须要有getter、setter,这样就可以将data传递到前台页面上,LoginAction类中所有具有getter的字段都会被封装到一个类中,传递到前台  {"data":"{\"msg\":\"登录成功!\",\"status\":\"1\"}","mmp":2000} -->
            </result>
        </action>
    </package>

因为这篇文章不介绍这种方法,如果需要,可以看看另一位大大的博客: Struts2中通过Ajax传递json数据

遇到的问题

也许这个方法没有问题,但我的项目在使用 Struts2 的同时,还需要使用 Spring Data JPA 操作数据库。

当我尝试使用上面的方法时,总是遇到这样的错误:

org.hibernate.LazyInitializationException:  failed to lazily initialize a collection of role: com.chiyu.entity.CustomerEntity.bordersByCid, could not initialize proxy - no Session

这个问题看起来像是和 JPA 设置有关的错误,但只要不使用 JSON 作为的返回值类型,这个错误就会消失,而且一切有关数据库的操作也完全正常。

看来这个方法不适合我,只能另辟蹊径了。

我的解决方案

我们知道, JSON 类型的数据可以很方便地转换成字符串,也可以从字符串很方便地转回来。

而且我们知道,在 Struts2 中,使用 stream 类型作为 Ajax 的返回值,可以非常方便地传递字符串等内容。

那么,直接使用 Struts2 以 stream 形式向页面传递 JSON 格式的字符串不就行了。

具体方法

Action 内容

使用了 Alibaba 的 fastjson ,处理后台的 JSON 数据。

@Controller
public class CustomerManager extends ActionSupport {
    //用于获取从页面返回的客户信息。
    private CustomerEntity customer;
    private List<CustomerEntity> customerList;
    //用于对客户信息进行各种操作的服务层,由Spring Bean赋值
    private CustomerService customerService;
    //用于使用AJAX技术执行客户信息添加删除更新时向网页传递信息
    private InputStream inputStream;

    public CustomerEntity getCustomer() {
        return customer;
    }

    public void setCustomer(CustomerEntity customer) {
        this.customer = customer;
    }

    public List<CustomerEntity> getCustomerList() {
        return customerList;
    }

    public void setCustomerList(List<CustomerEntity> customerList) {
        this.customerList = customerList;
    }

    public CustomerService getCustomerService() {
        return customerService;
    }

    @Autowired
    public void setCustomerService(CustomerService customerService) {
        this.customerService = customerService;
    }

    public InputStream getInputStream() {
        return inputStream;
    }

    public void setInputStream(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    //访问客户信息管理页面,并返回所有客户信息。
    @Override
    public String execute() throws Exception {
        //返回所有客户信息
        customerList = customerService.findAllCustomers();
        return SUCCESS;
    }

    //添加或更新客户信息方法,使用AJAX,返回json字符串
    public String addOrUpdateCustomer(){
	//执行对一条客户信息的新增或修改
        if (customer.getCid() == null || customer.getCid().isEmpty()){
            customer.setCid(UUID.randomUUID().toString().replace("-", ""));
        }
        customer = customerService.insertOrUpdateCustomer(customer);
	//创建JSON对象
        JSONObject object = new JSONObject();
	//向JSON对象中增加数据
        object.put("result","操作成功");
        object.put("cid",customer.getCid());
	//将JSON对象转换成字符串,再转为utf-8字节流
        inputStream=new ByteArrayInputStream(JSON.toJSONString(object).getBytes(StandardCharsets.UTF_8));
        return "addOrUpdateCustomerResult";
    }
}

注意事项:

  • private InputStream inputStream; 是 Struts 返回给页面的数据,需要有 get 和 set 方法。

Struts.xml 内容

<package name="background" namespace="/admin" extends="struts-default" strict-method-invocation="false">
        <action name="customerManager" class="customerManager">
            <result name="success">/admin/admin_customer.jsp</result>
            <result name="addOrUpdateCustomerResult" type="stream">
                <!-- 设置返回给浏览器的数据类型 -->
                <param name="contentType">text/html</param>
                <!--指定获取InputStream的方法,getInputStream(),约定:去掉get,后面部分使用camel写法 -->
                <param name="inputName">inputStream</param>
            </result>
        </action>
</package>

注意事项:
因为我的项目集成了 Spring ,使用可以使用 class="customerManager" 这样的写法,如果没有使用 Spring ,请写完整类名。

页面中的 Ajax 方法

需要 jQuery 。

    <!--对应提交按钮-->
    function submitCustomerFunc(btn){
        //防止多次提交,点击提交按钮后,此按钮失效
        $(btn).attr("disabled",true);
	//获得用户输入的值
        let cid = $("#input_cid").val();
        let cname = $("#input_cname").val();
        let phone = $("#input_phone").val();
        let address = $("#input_address").val();
        let password = $("#input_password").val();
	//使用 AJAX
        $.ajax({
	    //请求发送的地址
            url:"customerManager!addOrUpdateCustomer.action",
	    //使用post方式发送
            type:"post",
	    //向服务器发送的内容
            data:{"customer.cid":cid,"customer.cname":cname,"customer.phone":phone,"customer.address":address,"customer.password":password},
	    //接收到的数据类型
            dataType:"text",
	    //请求失败后执行
            error:function (){
                alert("操作失败!");
                //可以再次尝试提交
                $(btn).attr("disabled",false);
            },
	    //请求成功,但具体操作可能是失败的,如果操作失败,data中的内容将是包含错误信息的html文件。
            success:function (data){
		//如果是失败的,这样可以显示错误信息,为了防止最终用户看到后台错误信息,这个方法应当在开发结束后删除。
                //alert(data);
                // 尝试将可能是json字符串的data转化为json对象
                let obj = jQuery.parseJSON(data);
		//确保操作成功后执行以下内容
                if (obj.result === "操作成功"){
		    //显示“操作成功”
                    alert(obj.result);
		    //获取服务器返回的用户id
                    cid = obj.cid;
                    //使用户能创建下一条新的记录。
                   $("#addCustomerButton").attr("disabled",false);
		    //移除用于接收用户输入的一行表格
                    $("#newLine").remove();
		    //创建一行表格,用于显示用户输入的结果
                    $("#customerList").append("<tr><th scope='row'>" + cid + "</th> <td>" + cname + "</td> <td>" + phone + "</td> <td>" + address + "</td> <td>" + password + "</td> <td> <div class='btn-group mr-2'> <button type='button' class='btn btn-sm btn-outline-secondary' onclick='updateCustomerFunc(this)'>修改</button><button type='button' class='btn btn-sm btn-outline-secondary' onclick='deleteCustomerFunc(this)'>删除</button></div></td></tr>'");
                }else {
                    //可以再次尝试提交
                    $(btn).attr("disabled",false);
                }
            }
        })
    }