使用Ajax请求Action中数据,网页提示“org.apache.struts2.json.JSONException”与“java.sql.SQLException: Positioned Update not supported.”错误,如图:
笔者发现在程序中确实使用了Ajax与Action交互数据,web页面代码如下所示:
$(function() {$.post("Inspection_ready.action", null, function(data) {if (data.Clients.length != 0) {for ( var i in data.Clients) {$("<option value='"+data.Clients[i].cid+"'>" +data.Clients[i].cname + "</option>").appendTo($("#weituo"));}}});$("#weituo").change(function() {$.post("Inspection_selectClientById.action", {id : $("#weituo").val()}, function(data) {$("#simplename").val(data.Client.simplename);$("#simplename1").val(data.Client.simplename);});})}
Action中相应代码如下所示:
public String ready(){datemap = new HashMap<String,Object>();List<Client> clients = iinspection.getClientAll();//集合对象用于封装返回的clients集合数据datemap.put("Clients", clients);return SUCCESS;}public String selectClientById() {client = iinspection.selectBycid(id);//datemap集合对象,用于封装返回的client数据datemap.put("Client", client);return SUCCESS;}
Struts2配置文件struts.xml中的设置:
<action name="Inspection_*" class="inspectionAction" method="{1}"><result name="success" type="json" ><param name="root">datemap</param></result></action>
页面加载完毕后首先与服务器交互获取内容,用于为下拉列表初始化数据,当下拉列表切换选项时再与服务器交互根据选中内容检索数据,如图:
但初次交互时并没有出现问题,可以成功返回clients集合,如图所示:
说明Struts2的JSON插件可以成功根据集合自动生成JSON数据,但是当列表的change事件触发再次检索数据时,却发生了如题所述错误。
根据提问者描述,如果在Action类型之前添加“@Scope("prototype")”注解,程序出现错误,如果去除此项注解,程序可以正确运行,但返回的结果变为以下效果,如图:
这与程序的执行流程不符,因为第二次change检索数据并没有调用ready方法返回clients数据,笔者在调试过程中发现以下关键点:
1. Action注解:
@Controller@Scope("prototype")public class InspectionAction extends ActionSupport
2. Map注解:
@Resourceprivate Map<String,Object> datemap;
3. Map创建新对象:
datamap = new HashMap<String,Object>();List<Client> clients = iinspection.getClientAll();
我们逐条分析:
1.@Scope注解的作用:表示交由Spring托管对象的作用域,默认情况下作用域为“singleton”,即每次发起请求,都获取Spring容器中相同的对象实例。而Scope取值“prototype”时,表示非单例,即每次发起请求都会创建新的对象。
去掉Action类型之前的“@Scope("prototype")”注解,表示采用Spring的默认设置,即单例模式,此时Action中的所有成员如果没有重新赋值,则保留默认状态,以此可以解释第二次返回结果中为何包含“clients”集合,是由于Action单例,其中的datemap集合常驻容器并未清空。
2.@Resource注解的作用:类中成员属性的自动装配,默认按照属性名称自动装配,如果找不到与名称匹配的bean,则按照类型进行装配。以此可以解释添加“@Scope("prototype")”注解,以非单例方式创建Action并调用selectClientById方法时,可以直接使用datemap对象,而没有抛出NullPointerException异常,是由于Spring自动装配了datemap,而不用手动new此对象。既然datemap可以直接使用,为何还会抛出“JSONException”,经过调试笔者发现,初次访问进入ready()方法时,datemap对象为HashMap<k,v>类型,如图:
而此二次访问进入selectClientById方法时,datemap为LinkedHashMap类型,如图:
因此可以得出结论了,
问题的核心在于datemap = new HashMap<String,Object>();,初次访问时程序员手动构建datemap对象,第二次访问时使用了spring自动装配的datemap对象,默认数值不同,因此JSON无法封装。于是
决绝方案也可提出,无论是否对Action添加“@Scope("prototype")”注解,都不是造成本问题的核心原因,手动创建datemap对象才是核心原因,建议datemap之前不要添加注解,其属于此Action专有属性,无需在多个Action间共享,使用时手动开辟空间即可,不需要使用spring自动装配。