欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【Debug日志】1:初学S2SH的一些bug

程序员文章站 2022-03-23 12:54:25
...

当我通过反复翻看笔记,解决了下面第一个错误”域模型注入失败”时,我发觉要开始写Debug日志了,有些错误实在太容易忽略。

缺失public无参构造器使域模型注入失败

错误

当执行到:

Login usrLgn = ln_d.validate(ln.getXh(), ln.getPassword());

ln空引用异常,它是Action中的成员:

private Login ln;

本应通过域模型注入进来,反复检查找不到错。

解决

笔记11有总结过:

在域模型注入时就是要先用这个无参构造器建立出一个对象,再用setter方法往里面传值的

检查我的Login类(一个PO类),确实因为只写了有参构造器覆盖了原来的无参构造器,手动写一个即可。注意需要是public类型,才能控制反转给框架。

错把HQL当SQL尝试操作表和字段

错误

当执行到:

Query qry = sssn.createQuery("from LOGIN where XH=? and PASSWORD=?");

出现错误:

org.hibernate.hql.internal.ast.QuerySyntaxException: LOGIN is not mapped [from LOGIN where XH=? and PASSWORD=?]

解决

因为HQL逻辑上操作的是PO对象和对象的属性,而不是数据库表和表中字段。应当改为类名和属性名:

Query qry = sssn.createQuery("from Login where xh=? and password=?");

javassit的jar包冲突

错误

当执行到:

Login lgn = (Login) qry.uniqueResult();// 执行查询

报错:

java.lang.ClassCastException: myModel.Student_$$_javassist_1 cannot be cast to javassist.util.proxy.Proxy

解决

一个Hibernate的,一个Sturts2的:
【Debug日志】1:初学S2SH的一些bug
删去一个低版本的,然后祈求高版本的能向下兼容即可。

文件上传缺少辅助对象 & 表单提交重复

错误

修改个人信息,表单中可以上传文件:
【Debug日志】1:初学S2SH的一些bug
提交时总是返回INPUT视图。检查控制台发现警告:

严重: Developer Notification (set struts.devMode to false to disable this message):
Unexpected Exception caught setting 'phoContentType' on 'class myAction.MyInfoAction: Error setting expression 'phoContentType' with value ['image/png', ]
五月 11, 2018 10:00:26 上午 com.opensymphony.xwork2.interceptor.ParametersInterceptor error
严重: Developer Notification (set struts.devMode to false to disable this message):
Unexpected Exception caught setting 'phoFileName' on 'class myAction.MyInfoAction: Error setting expression 'phoFileName' with value ['照片_去文字.png', ]
五月 11, 2018 10:00:26 上午 com.opensymphony.xwork2.interceptor.ParametersInterceptor error
严重: Developer Notification (set struts.devMode to false to disable this message):
Unexpected Exception caught setting 'stu.stuId' on 'class myAction.MyInfoAction: Error setting expression 'stu.stuId' with value ['1', '刘知昊', ]
五月 11, 2018 10:00:26 上午 com.opensymphony.xwork2.util.LocalizedTextUtil warn
警告: Missing key [invalid.fieldvalue.stuId] in bundles [[org/apache/struts2/struts-messages, com/opensymphony/xwork2/xwork-messages]]!
五月 11, 2018 10:00:26 上午 com.opensymphony.xwork2.util.LocalizedTextUtil warn
警告: Missing key [invalid.fieldvalue.stu.stuId] in bundles [[org/apache/struts2/struts-messages, com/opensymphony/xwork2/xwork-messages]]!

似乎和照片上传有关,在JSP中这一部分是:

<tr>
    <td>照片:</td>
    <!-- 上传照片 -->
    <td>
        <s:file name="pho"/>
    </td>
</tr>

在Action中关于它只组合了:

private File pho;// 照片文件

并提供了getter和setter。

解决

为文件上传的File对象提供两个辅助字段:

private String phoContentType;// 该文件对象(pho)的类型
private String phoFileName;// 该文件对象(pho)上传时的文件名

并提供getter和setter,消除了前两个警告。但依然返回INPUT视图,现在警告是:

严重: Developer Notification (set struts.devMode to false to disable this message):
Unexpected Exception caught setting 'stu.stuId' on 'class myAction.MyInfoAction: Error setting expression 'stu.stuId' with value ['1', '刘知昊', ]
五月 11, 2018 10:05:03 上午 com.opensymphony.xwork2.util.LocalizedTextUtil warn
警告: Missing key [invalid.fieldvalue.stuId] in bundles [[org/apache/struts2/struts-messages, com/opensymphony/xwork2/xwork-messages]]!
五月 11, 2018 10:05:03 上午 com.opensymphony.xwork2.util.LocalizedTextUtil warn
警告: Missing key [invalid.fieldvalue.stu.stuId] in bundles [[org/apache/struts2/struts-messages, com/opensymphony/xwork2/xwork-messages]]!

错误信息中说stu.stuId被传入了一个值列表,里面有一个值1,还有一个值是名字。应该是页面提交问题,检查发现:

<tr>
    <td>学号:</td>
    <td>
        <!-- 学号是只读的,不能修改 -->
        <input type="text" name="stu.stuId" value="<s:property value="#request.stu.stuId"/>" readOnly/>
    </td>
</tr>
<tr>
    <td>姓名:</td>
    <td>
        <input type="text" name="stu.stuId" value="<s:property value="#request.stu.name"/>" />
    </td>
</tr>

在传姓名时的域模型注入目标使用了学号,所以出错,改正即可。

此外,学号既然是只读的,应当只显示不可提交,而不是仅在JSP上使其readOnly,学号可以在服务器端通过会话查出来。

懒加载导致没有获得成员对象

错误

Struts has detected an unhandled exception:

Messages:   
failed to lazily initialize a collection of role: myModel.Course.student, could not initialize proxy - no Session
File:   org/hibernate/collection/internal/AbstractPersistentCollection.java
Line number:    575

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: myModel.Course.student, could not initialize proxy - no Session

Course使用了student的集合作为成员,表示一个课有多个选课学生:

private Set<Student> student = new HashSet<Student>();

并且配置了映射文件:

<!-- N课程对应M个学生,加载课程时不必立即加载选课学生,控制反转-->
<set name="student" table="SC" lazy="true" inverse="true">
    <key column="COURSEID"/>
    <many-to-many class="Student" column="STUID"/>
</set>

意为在加载课程对象时不必立即加载选课学生,且inverse="true"控制反转将控制权交给学生对象,表示这个N-M映射关系由学生(的选课退课)来维护。

在学生提交个人信息时,为了不改变选课信息,将选课信息重新写到了域模型注入的对象中:

// 学生要提交自己的修改个人信息
public String updtCmmt() throws Exception {
    StudentDAO stu_d = new StudentDAOImp();
    ProfessDAO pf_d = new ProfessDAOImp();
    // 把提交的专业整合到提交的其它信息(Student对象)里
    stu.setZy(pf_d.getOneProfess(pf.getPfId()));
    // 处理传上来的照片文件
    if (this.pho != null) {
        // ...把照片存入数据库...
    } else {
        stu.setZp(stu_d.getOneStu(stu.getStuId()).getZp());
    }
    // 处理课程(也是直接重新设定一次)
    stu.setCourse(stu_d.getOneStu(stu.getStuId()).getCourse());
    System.out.println(stu);
    // 更新
    stu_d.update(stu);
    return SUCCESS;
}

解决

获取的课程中显然没有再去包含选课学生,这对单个学生来说也是不必要的。而PO持久化时这样的课程却是异常的,因为其中的学生没有了。

可以先去读出DB中的对象,在Session关闭后成为脱管对象,再在该对象上修改属性再重新变为PO存入DB。

// 学生要提交自己的修改个人信息
public String updtCmmt() throws Exception {
    StudentDAO stu_d = new StudentDAOImp();
    ProfessDAO pf_d = new ProfessDAOImp();
    // 获取PO的脱管对象
    Student stu_tmp = stu_d.getOneStu(stu.getStuId());
    // 处理专业
    stu_tmp.setZy(pf_d.getOneProfess(pf.getPfId()));
    // 处理传上来的照片文件
    if (this.pho != null) {
        // ...把照片存入数据库...
    } else {
        // 什么都不用做,脱管对象里自己有之前的
    }
    // 处理其它没有依赖其它对象的直接属性
    stu_tmp.setBz(stu.getBz());
    stu_tmp.setCssj(stu.getCssj());
    stu_tmp.setName(stu.getName());
    stu_tmp.setSex(stu.getSex());
    stu_tmp.setZp(stu.getZp());
    stu_tmp.setZxf(stu.getZxf());
    // 课程不需要动,在PO脱管里
    // 更新,让脱管对象变回PO,并持久化到数据库
    stu_d.update(stu_tmp);
    return SUCCESS;
}

照片没有成功存到blob字段中

错误

在Hibernate自顶向下将byte[]生成为tinyblob后,不能按课本上的方法上传照片。查了一下tinyblob只能支持最大255B的文件,改为blob(最大65KB)依然不行。

检查了表单,有配置enctype="multipart/form-data"无误;检查了映射文件中该字段的映射:

<property name="zp" type="binary" column="ZP"/>

Java的byte[]映射类型为binary,也没有错。

检查了文件对象本身传入Action的情况,编写(注意如果为该文件对象建立了输入流,要先将输入流关闭):

// 存到本地试试
String dataDir = "E:/home/";
File saveFile = new File(dataDir, phoFileName);
pho.renameTo(saveFile);

能够正确上传到本地,说明传入Action也是正常的:
【Debug日志】1:初学S2SH的一些bug
在read之后尝试输出也没有问题:

for (byte b : buffer) {
    System.out.println(b + " ");
}

能够输出很多不同的数字。在数据库里改blob的长度也改不了,说明这种大型对象长度是自动的,不是手工设定的,它只有一个最大限制长度。

解决

直到我在后面看到了这句代码:

stu_tmp.setZp(stu.getZp());

被它覆盖了,删去就好。最终这部分的代码:

// 学生要提交自己的修改个人信息
public String updtCmmt() throws Exception {
    StudentDAO stu_d = new StudentDAOImp();
    ProfessDAO pf_d = new ProfessDAOImp();
    // 获取PO的脱管对象
    Student stu_tmp = stu_d.getOneStu(stu.getStuId());
    // 处理专业
    stu_tmp.setZy(pf_d.getOneProfess(pf.getPfId()));
    // 处理传上来的照片文件
    if (this.pho != null) {
        // 存到本地试试
        // String dataDir = "E:/home/";
        // File saveFile = new File(dataDir, phoFileName);
        // pho.renameTo(saveFile);
        // 读到byte数组里
        InputStream fis = new FileInputStream(pho);
        byte[] buffer = new byte[fis.available()];
        fis.read(buffer);
        // 尝试直接设定给stu_tmp这个脱管对象
        stu_tmp.setZp(buffer);
        fis.close();
        System.out.println("[scss]pho对象不为空");
    } else {
        // 什么都不用做,脱管对象里自己有之前的
        System.out.println("[debug]pho对象为空");
    }
    // 处理其它没有依赖其它对象的直接属性
    stu_tmp.setBz(stu.getBz());
    stu_tmp.setCssj(stu.getCssj());
    stu_tmp.setName(stu.getName());
    stu_tmp.setSex(stu.getSex());
    stu_tmp.setZxf(stu.getZxf());
    // 课程不需要动,在PO脱管里
    // 更新,让脱管对象变回PO,并持久化到数据库
    stu_d.update(stu_tmp);
    return SUCCESS;
}

上传效果:
【Debug日志】1:初学S2SH的一些bug

相关标签: Debug