2016.08.10 Struts2 @Action @Result
转发与重定向
@result 的转发与跳转 ,转发默认,跳转页面要写 “type”
//跳转页面
@Result(name="success",location="/foreground/test.jsp",type="redirect")
//跳转action ,不用写后缀
@Result(name="success",location="/foreground/testAction",type="redirectAction")
这里有个表格参考
已配置类型名 | 类 名 | 描 述 |
---|---|---|
dispatcher | org.apache.struts2.dispatcher.ServletDispatcherResult | 默认结果类型,用来呈现JSP页面 |
chain | com.opensymphony.xwork2.ActionChainResult | 将action和另外一个action链接起来 |
freemarker | org.apache.struts2.views.freemarker.FreemarkerResult | 呈现Freemarker模板 |
httpheader | org.apache.struts2.dispatcher.HttpHeaderResult | 返回一个已配置好的HTTP头信息响应 |
redirect | org.apache.struts2.dispatcher.ServletRedirectResult | 将用户重定向到一个已配置好的URL |
redirectAction | org.apache.struts2.dispatcher.ServletActionRedirectResult | 将用户重定向到一个已定义好的action |
stream | org.apache.struts2.dispatcher.StreamResult | 将原始数据作为流传递回浏览器端,该结果类型对下载的内容和图片非常有用 |
velocity | org.apache.struts2.dispatcher.VelocityResult | 呈现Velocity模板 |
xslt | org.apache.struts2.views.xslt.XSLTResult | 呈现XML到浏览器,该XML可以通过XSL模板进行转换 |
plaintext | org.apache.struts2.dispatcher.PlainTextResult | 返回普通文本类容 |
传递参数
- request传参
直接将值存在request对象中,不能是重定向
ServletActionContext.getRequest().setAttribute("test", "王尼玛");
- @Result(params={“键”,”值”,”键”,”值”,”键”,”变量名”…})
需要有username的getter/setter ,此方法是url传参,即url地址会发生改变时例如 redirect/redirectAction,即转发才能获取到参数值
@Action(value="login",results={@Result(name="success",location="/foreground/test.jsp",params={"id","123","un","username"},type="redirect")})
//或者
@Action(value="login",results={@Result(name="success",location="/foreground/test.jsp",params={"id","123","un","${user.username}"},type="redirect")})
- @Result(location=”xx.jsp?xx=${xx}”)
使用url带参传递
@Action(value="login",results={@Result(name="success",location="/foreground/test.jsp?id=${userid}")})
可我为什么没成功呢!!! 答案是 错误的(没成功过),只能静态传值
@Action(value="login",results={@Result(name="success",location="/foreground/test.jsp?open=false")})
这种模式对forward 和 redirect / redirectAction 都有效
2016.08.12 @Action 之 url 请求
- 类似luan.iteye.com/blog/181650 这种请求 ,
blog为命名空间namespace,181650匹配通配符
@Action(value="/blog/*",results={@Result(name="success",location="/login.jsp",type="redirect")})
如代码,此时的访问URL即为
xxx.xxx.xxx/blog/xx.do
//或
xxx.xxx.xxx/blog/xx.action
//或
xxx.xxx.xxx/blog/xx
这取决于struts.xml配置的后缀名
<!-- 指定被struts2处理的请求后缀类型。多个用逗号隔开 -->
<constant name="struts.action.extension" value="do,action,"/>
- 页面位于WEB-INF下面时不能使用重定向redirect
2016.08.24 @Action 之 Return JSON
ajax请求json 返回json对象,注解实现方法:
类声明
@Namespace("/user")
@Results( { @Result(name = ActionSupport.SUCCESS, type = "json"),
@Result(name = ActionSupport.ERROR, type = "json") })
@ParentPackage(value="json-default")
@Controller
public class UserAction extends ActionSupport {}
方法声明
@Action(value="login", results={@Result(type="json", params={"root","msg"})})
public String login(){}
参数”root”不能更改 msg可以修改,为一个Map对象名,需要有get方法
private Map<String, Object> msg = new HashMap<>();;
public Map<String, Object> getMsg(){
return msg;
}
Login方法内部:
public String login(){
HttpSession session = ServletActionContext.getRequest().getSession();
msg.put("code", ActionCode.USER_SUCCESS);
UserInfo userInfo = us.getUserInfo(find);
session.setAttribute("userInfo ", userInfo);
return SUCCESS;
}
us 是一个service对象,ActionCode为自定义类
//重点在于
@ParentPackage(value="json-default")
@Result(type="json", params={"root","msg"})
//不写第一句我没成功,root为既定参数不能修改,type必须为json
2016.08.26 文件上传-图片上传,form提交
FileUpload 插件
上传插件很多,好坏熟练者能区别我们就摸不着头脑,不是不想去搞清,实在是脑子笨加工作时没时间让你去专研..
- github.com这个网站提供的算得上精品至少我这么认为.只不过提供的例子和API 英文的想要看懂有些麻烦感觉不是很详细.
提供了一系列JS文件 但是他们之间的依耐性太高所以不很方便
/*cors/jquery.postmessage-transport.js
cors/jquery.xdr-transport.js
vendar/jquery.ui.widget.js
app.js
jquery.fileupload-angular.js
jquery.fileupload-audio.js
jquery.fileupload-image.js
jquery.fileupload-jquery-ui.js
jquery.fileupload-process.js
jquery.fileupload-ui.js
jquery.fileupload-validate.js
jquery.fileupload-video.js
jquery.fileupload.js
jquery.iframe-transport.js
main.js */
//总共就这些,用得到的看需求定制
最低需求使用的js文件就有五六个(必须的)
//jQuery 1.8以上版本
<script type="text/javascript" src="js/jquery-1.12.4.js"></script>
//Widget是一些封装好的组件,例如日期控件
<script type="text/javascript" src="js/upload/js/vendor/jquery.ui.widget.js"></script>
//扩展iframe数据传输
<script type="text/javascript" src="js/upload/js/jquery.iframe-transport.js"></script>
//上传核心类
<script type="text/javascript" src="js/upload/js/jquery.fileupload.js"></script>
//IE下需要载入解决跨域问题
<script type="text/javascript" src="js/upload/js/cors/jquery.xdr-transport.js"></script>
//网上找到的一个图片预览插件
<script type="text/javascript" src="js/upload/js/images-preview.js"></script>
HTML代码
<div>
<div>
<div id="upload_progress"></div>
<img id="imgHead" class="lazyloadimg show_fave" src="images/user_180.gif" style="width: 150px;height: 150px; display: block;" />
</div>
<div>
<a href="javascript:;" class="a-upload">
<input id="fileupload" type='file' name="file" data-url="upload/uploadimage" />点击这里上传文件
</a>
<input id="new_face" type='hidden' name="new_face" value="这里是返回的图片路径或者图片ID看服务器返回而定" />
</div>
</div>
样式如图
A标签覆盖INPUT ,input透明CSS如下
/*a upload */
.a-upload {
padding: 4px 10px;
height: 20px;
line-height: 20px;
position: relative;
cursor: pointer;
color: #888;
background: #fafafa;
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
display: inline-block;
*display: inline;
*zoom: 1
}
.a-upload input {
position: absolute;
font-size: 100px;
left: 0;
top: 0;
opacity: 0;
filter: alpha(opacity=0);
cursor: pointer;
width: 200px;
}
.a-upload:hover {
color: #444;
background: #eee;
border-color: #ccc;
text-decoration: none
}
一般预览图片有两种方式
1 上传前预览
2 上传后显示
前者需要考虑一系列问题,最关键的还是兼容性问题就IE扯淡
第二种考虑网络问题,网速过慢导致半天没显示
JavaScript 代码
$(document).ready(function(){
//fileupload为file input ID,uploadPreview()图片预览插件调用方式,全参数调用,uploadPreview({}),{}为一个对象,可以根据需要填写,就如下面
$("#fileupload").uploadPreview({ Img: "imgHead", Width: 150, Height: 150, ImgType: ["gif", "jpeg", "jpg", "bmp", "png"], Callback: function () {}});
/**
*$("#fileupload").fileupload();默认为自动上传,也即是选择文件过后自动上传不带参数调用(调用相当于添加事件,不必多次调用,除非修改参数)
*参数可以写在()内也可以写在 input标签中
*例如:<input id="fileupload" type='file' name="file" data-url="upload/uploadimage" />,这个data-url就是参数相当于
$("#fileupload").fileupload({url:'upload/uploadimage'});
*/
$("#fileupload").fileupload({
dataType:"JSON",//返回数据为JSON对象浏览器还是js会自动转,格式不正确就出错
done:function(e,data){//done方法为成功时执行
var result = data.result;//data中包含一些属性,其中result为返回结果对象
if(result.code == 0){//code为 返回的JSON对象
$("#_progress").text("上传成功");
}
else if(result.code==0)$("#_progress").text("上传失败");
},
progress:function(e,data){//progress方法为进度信息
var progress = parseInt(data.loaded/data.total*100,10);//loaded和total可以计算出进度
$("#upload_progress").css("width",1.7*progress);//变化进度条长度,此种写法太二
},
formData:{type:"userlogo"}//为上传的额外数据,一个自定义对象,其中包含你想上传的参数,例如 xx:"yaaa"
});
});
图片预览插件本身没问题,但和上传功能一起使用就有些问题,当然只在IE上
问题如下: IE9 以下我只有IE9,上传成功后(无刷新),图片预览会不能使用,导致异常,从而使之后上传图片无效.
研究出的问题如下, fileupload插件,上传时会创建iframe,而导致input,有一段时间在”异界”,而上传成功后iframe删除,input从异界回来,有一些些微的差别,这个差别是IE不能容忍的.为此我只能在第二次上传时刷新页面,丢失数据保证图片上传成功.
ImagesPreview 图片预览
/*
*名称:图片上传本地预览插件 v1.1
*作者:周祥
*时间:2013年11月26日
*介绍:基于JQUERY扩展,图片上传预览插件 目前兼容浏览器(IE 谷歌 火狐) 不支持safari
*插件网站:http://keleyi.com/keleyi/phtml/image/16.htm
*参数说明: Img:图片ID;Width:预览宽度;Height:预览高度;ImgType:支持文件类型;Callback:选择文件显示图片后回调方法;
*使用方法:
<div>
<img id="ImgPr" width="120" height="120" /></div>
<input type="file" id="up" />
把需要进行预览的IMG标签外 套一个DIV 然后给上传控件ID给予uploadPreview事件
$("#up").uploadPreview({ Img: "ImgPr", Width: 120, Height: 120, ImgType: ["gif", "jpeg", "jpg", "bmp", "png"], Callback: function () { }});
*/
jQuery.fn.extend({
uploadPreview: function (opts) {
var _self = this,
_this = $(this);
opts = jQuery.extend({
Img: "ImgPr",
Width: 100,
Height: 100,
ImgType: ["gif", "jpeg", "jpg", "bmp", "png"],
Callback: function () {}
}, opts || {});
_self.getObjectURL = function (file) {
var url = null;
if (window.createObjectURL != undefined) {
url = window.createObjectURL(file);
} else if (window.URL != undefined) {
url = window.URL.createObjectURL(file);
} else if (window.webkitURL != undefined) {
url = window.webkitURL.createObjectURL(file);
}
return url;
};
_this.on("change",function () {
if (this.value) {
if (!RegExp("\.(" + opts.ImgType.join("|") + ")$", "i").test(this.value.toLowerCase())) {
alert("选择文件错误,图片类型必须是" + opts.ImgType.join(",") + "中的一种");
this.value = "";
return false;
}
if (!jQuery.support.scriptEval) {//浏览器检测与jQuery版本有关,1.9以前用$.browser.msie
try {
$("#" + opts.Img).attr('src', _self.getObjectURL(this.files[0]));
} catch (e) {
var src = "";
var obj = $("#" + opts.Img);
var div = obj.parent("div")[0];
_this.select();
if (top != self) {
window.parent.document.body.focus();
} else {
_this.blur();
}
try {
src = document.selection.createRange().text;
} catch (e) {
window.location.href = window.location.href;
}
document.selection.empty();
obj.hide();
obj.parent("div").css({
'filter': 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)',
'width': opts.Width + 'px',
'height': opts.Height + 'px'
});
div.filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = src;
}
} else {
$("#" + opts.Img).attr('src', _self.getObjectURL(this.files[0]));
}
opts.Callback();
}
});
}
});
修改内容
//if ($.browser.msie)修改为可以有很多选择
if (!jQuery.support.scriptEval){}
//src = document.selection.createRange().text;修改为
try {
src = document.selection.createRange().text;//IE第二次上传会无法访问
} catch (e) {
window.location.href = window.location.href;//刷新页面
}
JAVA后台Action
@Namespace("/upload")
@Controller
public class FilesUpload extends ActionSupport {
private File file;//对应上传的inputName貌似
private String fileContentType;//文件类型
private String fileFileName;//文件名
//getter setter ,以上应为struts自动解析注入
/**
* 返回消息
*/
private Map<String, Object> msg = new HashMap<>();
//getter setter msg
//Action配置为无result,使用response返回结果,除非你有跳转
@Action(value = "uploadimage")
public void uploadImage() {
HttpServletResponse response=ServletActionContext.getResponse();//用于获取输出流
//response.setContentType("application/json;charset=UTF-8");//默认为application/json,或者其他,IE在上传文件后返回时,提示下载,而不是JSON数据
response.setContentType("text/html;charset=UTF-8");//设置成text/html,后IE下jQuery能获取到json数据,而不会被IE拦截当成文件下载
PrintWriter out;//打印工具
try {
out = response.getWriter();
if(file==null){
msg.put("code", ActionCode.FILE_UPLOAD_ERROR);
}else{
String type = ServletActionContext.getRequest().getParameter("type");//这个就是dataform参数上传的数据
String realpath = ServletActionContext.getServletContext().getRealPath("/images");//获取images的绝对路径
switch(type){//根据我设置的type保存在不同目录
case "userlogo":
realpath +="\\user";
break;
case "news":
realpath +="\\news";
break;
case "companylogo":
realpath +="\\company";
break;
case "job":
realpath +="\\job";
break;
default:;
}
//D:\apache-tomcat-6.0.18\webapps\struts2_upload\images
System.out.println("realpath: "+realpath);
if (file != null) {
File savefile = new File(new File(realpath), fileFileName);
if (!savefile.getParentFile().exists())//上级是否存在
savefile.getParentFile().mkdirs();//不存在创建文件夹
FileUtils.copyFile(file, savefile);//struts提供的复制文件
//struts上传时文件会放在配置的默认目录,使用copyfile后会删除或者会话结束后删除
msg.put("code", ActionCode.FILE_UPLOAD_SUCCESS);//将结果存在Map中
msg.put("filename", fileFileName);
}
}
out.print(MyJSON.toJson(msg, false));//转成json对象,false为包含时间与否
//这里设计一些bug,不包含实体类可以直接用
//JsonObject.toJson(Object);转
out.flush();//挤出输出流
out.close();//关闭输出流
} catch (IOException e) {
e.printStackTrace();//出错后处理
}
}
}
JsonDateValueProcessor.java
/**
*日期Date类型转json时需要经过转换,日期类型转换格式配置
*/
public class JsonDateValueProcessor implements JsonValueProcessor {
private String format ="yyyy-MM-dd HH:mm:ss";
public JsonDateValueProcessor() {
super();
}
public JsonDateValueProcessor(String format) {
super();
this.format = format;
}
@Override
public Object processArrayValue(Object paramObject,
JsonConfig paramJsonConfig) {
return process(paramObject);
}
@Override
public Object processObjectValue(String paramString, Object paramObject,
JsonConfig paramJsonConfig) {
return process(paramObject);
}
private Object process(Object value){
System.out.println(value.getClass());
if(value instanceof Date){
SimpleDateFormat sdf = new SimpleDateFormat(format,Locale.CHINA);
return sdf.format(value);
}
return value == null ? "" : value.toString();
}
}
MyJSON.JAVA
/**
* @Description: TODO 将对象转换成JSONObject
* @param @param obj 带有属性getXX()方法的对象 或者字符串键值对"{'test':'test!'}"
* @param @param hasDate 是否带有日期类型
* @return JSONObject
* @throws 若字符串或对象无效时抛出异常 Object 必须以键值对存在
* @author Mr.Wang
* @date 2016年8月4日
*/
public static JSONObject toJson(Object obj,boolean hasDate){
JsonConfig cfg = new JsonConfig();
cfg.setExcludes(new String[]{"handler","hibernateLazyInitializer"});
if(hasDate)cfg.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor());
try {
JSONObject json = JSONObject.fromObject(obj,cfg);
return json;
} catch (Exception e) {
Logger.getLogger(UserServiceImpl.class).error("转换失败", e);
return null;
}
}
验证码图片返回-@Action
/**
*重点为Result的type,为stream,参数需要
*contentType:"image/jpeg",影响返回结果
*inputName:"imageStream",imageStream为类属性需要get方法
*contentType/inputName/bufferSize为struts规定名字不能为其他
**/
private ByteArrayInputStream imageStream;//文件输入流
@Action(value = "getSecurityCode", results = { @Result(name="success",type="stream",params = {"contentType","image/jpeg","inputName","imageStream","bufferSize","2048"}) })
public String getPicture() {
String securityCode = SecurityCode.getSecurityCode(4,
SecurityCodeLevel.Hard, false);
// 获取默认难度和长度的验证码,验证码生成BaiDu能找到就不贴了
// String securityCode = SecurityCode.getSecurityCode();
setImageStream(SecurityImage.getImageAsInputStream(securityCode));//将imageStream属性赋予一个输入流即可
// 放入session
ActionContext.getContext().getSession()
.put("SESSION_SECURITY_CODE", securityCode);
return SUCCESS;
}
JavasSript
其中有用的两个方法
后台也要转换一次防止恶意提交
/**
*字符串转义
*若想 < > 等符号 以字符串形式显示 则使用此函数,并且不需要反转
**/
function html_encode(str)
{
var s = "";
if (str.length == 0) return "";
s = str.replace(/&/g, ">");
s = s.replace(/</g, "<");
s = s.replace(/>/g, ">");
s = s.replace(/ /g, " ");
s = s.replace(/\'/g, "'");
s = s.replace(/\"/g, """);
s = s.replace(/\n/g, "<br/>");
return s;
}
/**
*字符串转义反转
*若需要将被转义的 <>以HTML代码显示 则需要反转
**/
function html_decode(str)
{
var s = "";
if (str.length == 0) return "";
s = str.replace(/>/g, "&");
s = s.replace(/</g, "<");
s = s.replace(/>/g, ">");
s = s.replace(/ /g, " ");
s = s.replace(/'/g, "\'");
s = s.replace(/"/g, "\"");
s = s.replace(/<br>/g, "\n");
return s;
}
2016.10.25 自定义EL函数
有时需要用到静态方法,而用标签实现又很麻烦,这就用到了自定义EL函数
首先 用标签实现:
经营范围:
<span>
<c:set var="pdtypes" value="<%=ProductTypes.getTypes() %>"/>
<c:set var="busScope" value=""/>
<c:forEach var="item" items="${fn:split(content['supplier'].supBusScope,',')}">
<c:forEach var="type" items="${pdtypes}">
<c:if test="${type.pdTid==item}">
<c:if test="${busScope!=''}">
<c:set var="busScope" value="${busScope}${','}${type.illustration}"/>
</c:if>
<c:if test="${busScope==''}">
<c:set var="busScope" value="${type.illustration}"/>
</c:if>
</c:if>
</c:forEach>
</c:forEach>
${busScope}
</span>
如上supBusScope值为 1,2,3 分割后遍历,types从静态方法中取集合,遍历比较,有些麻烦
简单的做法应该就是直接调用静态方法了
销售范围:
<span>
<%
String[] ids = ((Supplier)((Map)request.getAttribute("content")).get("supplier")).getSupBusScope().split(",");
String str="";
for(int i =0 ;i<ids.length;i++){
if(str.length()==0)str+=ProductTypes.getCnType(Integer.parseInt(ids[i]));
else str += ","+ProductTypes.getCnType(Integer.parseInt(ids[i]));
}
out.print(str);
%>
</span>
但这样并不美观,反显杂乱
然后就是用配置文件了,配置后的代码为:
经营范围:<span>
<c:set var="beforeStr" value=""/>
<c:forEach var="item" items="${fn:split(content['supplier'].supBusScope,',')}">
${beforeStr==""?"":","}${mf:getCnPdType(item)}
<c:set var="beforeStr" value=","/>
</c:forEach>
</span>
简单很多,若不是需要有分隔符代码就三行
具体配置:
- 创建一个XML文件,名为MyFunction.tld,放在WEB-INF下若不是需要另配置,可以点一个已知tld文件的uri进去复制
例如:
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
或者直接复制:
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<!--该文件主要是受这个的影响:http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd -->
<!-- -->
<tlib-version>1.0</tlib-version><!-- 定义的版本 -->
<short-name>mf</short-name><!--这个名字可以随便取,尽量与文件名相同,这样我们知道文件在哪儿 -->
<uri>http:/www.agr.com/el/MyFunction</uri><!-- 这个地址是随便取得。到时候jsp页面引入这个地址 -->
<!-- 定义函数 -->
<function>
<description>以产品类别ID获取产品类别中文名</description>
<!-- 自定义函数名称(随意) -->
<name>getCnPdType</name>
<!-- 定义函数的类全名称 -->
<function-class>com.information.table.type.ProductTypes</function-class>
<!--java.lang.Integer:返回值类型 getCnType:类定义的函数名(函数类型) -->
<function-signature>java.lang.String getCnType( java.lang.Integer )</function-signature>
</function>
</taglib>
必要的都注释了此文件名为:MyFunction.tld
uri:http:/www.agr.com/el/MyFunction
shortName:mf
其中一个函数为(可以多个function)
getCnPdType —此名可以自定义不必与方法名相同
此函数指向方法:java.lang.String getCnType( java.lang.Integer )
此方法所属类:com.information.table.type.ProductTypes
此方法为静态
2. 配置目录tld目录,若在WEB-INF下则可省略,在web.xml中修改:<jsp-config><!-- 这个步骤可以省略,前提是这个tld文件在WEB-INF目录下-->
<taglib>
<taglib-uri>http://www.agr.com/el/MyFunction</taglib-uri>
<taglib-location>/WEB-INF/MyFunction.tld</taglib-location>
</taglib>
lt;/jsp-config>
3. 使用tld,在JSP页面中调用<%@taglib prefix="mf" uri="http:/www.agr.com/el/MyFunction" %>
使用方式:<!--${mf:方法名(参数)},此处为:-->${mf:getCnPdType(1)}
原方法定义为:public static String getCnType(Integer tid);
以上…
2016.10.27 Spring Hibernate 操作
update 奇怪问题
异常
error:a different object with the same identifier value was already associated with the session
即:同一个事务方法中出现了一个以上同ID的实体对象,例如
public void changeSupplier(Supplier sup) {
log.info("修改供货商信息");
Supplier old = this.get(sup.getSupId());
if(old.getPicId()!=sup.getPicId()){
if(old.getPicId()!=null){
//第n次设置图片,需要删除原先图片
picSrv.forcedDeletion(old.getPicId());
}
picSrv.setPictureUse(sup.getPicId());
}
supDao.update(sup);
}
此方法,传进来一个sup,此sup是在action中查询出并修改过的.然后放进来保存,思路没问题.由于保存时需要处理原先属性值比如关联的记录,所以再次查询出old进行操作后再保存sup,这又不得不思考该不该分离,没人教 没经验.思考:
问题列表:
- 采用两个service方法处理(前提数据有效性通过)
- 第一个方法用来清除数据操作
- 第二个方法用作保存
- 清除的是有外键关联的数据而非全部
- 清除前要知道哪些被修改
- 可以new个空对象,将原始对象和表单数据相比较,有变动的存入空对象,然后再在第一个方法中判断这个空对象中不为空的属性是不是有外键关联,再做清除
想来应该是比较麻烦的,而且,事务不能回滚,还有就是一种思路:
- 定义的update方法添加多个参数例如:
public void change(Object changed,Integer id);
- 调用方法前将对象ID赋值于变量,然后设置为空.
- 此时方法内部再get()的对象应该是不报错的。(失败,action中调用get后,对象一直在session中,so)
此外一种思路:
- 定义的update方法添加两个参数如:
public void changeSupplier(Supplier newSup,Supplier oldSup);
- 也即是只查询一次获取 old,然后new 一个空对象再用一个工具类
PropertyUtils.copyProperties(sup, old);
复制值,操作时修改克隆体.保存时,处理本体对象外键属性后保存克隆体.
最后一种思路:
- 使用session.evict(obj);方法将对象从session中剥离
- 底层dao留一个接口给service调用.
- 代码详情:
public void changeSupplier(Supplier sup) {
log.info("修改供货商信息");
Supplier old = this.get(sup.getSupId());
//从session中剥离
supDao.evict(old);
if(old.getPicId()!=sup.getPicId()){
if(old.getPicId()!=null){
//第n次设置图片,需要删除原先图片
picSrv.forcedDeletion(old.getPicId());
}
picSrv.setPictureUse(sup.getPicId());
}
supDao.update(sup);
}
代码只多了一行supDao.evict(old)
,具体会不会有后遗症暂未遇到。
To Be Continue . .