OA2_springAndStruts2项目bug日志
摘要:在是基于struts2+spring4实现的,先记录在开发过程中的bug日志:
struts2+spring4
bug1:spring.xml扫包地址配置错误后,启动服务器没有打印异常信息,在登陆时无法跳转到登陆action--控制台没有任何信息。
类目录下的spring.xml配置如下:
<context:annotation-config/>
<context:component-scan base-package="com.chen"></context:component-scan>
上面的扫包地址配置错误。正确的应该是base-package="com.wql"
在登陆action中有用到自动注入dao:
@Autowired
EmplDao emplDao;
按常理说,spring.xml配置错误后,应该在加载服务器时提示错误信息,可是没有看到。加载服务器的信息如下:
信息: Initializing Spring root WebApplicationContext log4j:WARN No appenders could be found for logger (org.springframework.web.context.ContextLoader). log4j:WARN Please initialize the log4j system properly. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. 九月 27, 2015 1:32:45 上午 org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom 信息: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [141] milliseconds. 九月 27, 2015 1:32:47 上午 org.apache.coyote.AbstractProtocol start 信息: Starting ProtocolHandler ["http-bio-8081"] 九月 27, 2015 1:32:47 上午 org.apache.coyote.AbstractProtocol start 信息: Starting ProtocolHandler ["ajp-bio-8009"] 九月 27, 2015 1:32:47 上午 org.apache.catalina.startup.Catalina start 信息: Server startup in 6549 ms 九月 27, 2015 1:32:48 上午 org.apache.jasper.compiler.TldLocationsCache tldScanJar 信 息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time. |
从以上加载信息来看,应该包含了两条信息:1.警告:请初始化log4j;2.扫包的结果是没有扫到-- contained no TLDs
先不管它,在登陆页面提交登陆,没有任何反应!既不能提交至登陆action,控制台也没有任何信息。想必肯定有异常,但是控制台却没有打印。
针对此问题,现配置log4j的配置问题:log4j.properties(放在类路径下)。
现在在登陆页面点击登陆,log4j终于打印了异常信息:
Unable to instantiate Action, com.wql.action.LoginAction, defined for 'loginAction' in namespace '/login'Error creating bean with name 'com.wql.action.LoginAction': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.wql.Dao.EmplDao com.wql.action.LoginAction.emplDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.wql.Dao.EmplDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} - action - file:/E:/Spring2/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/OA2_02/WEB-INF/classes/struts.xml:32:89 at com.opensymphony.xwork2.DefaultActionInvocation.createAction(DefaultActionInvocation.java:314) at com.opensymphony.xwork2.DefaultActionInvocation.init(DefaultActionInvocation.java:395) at com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:194) at org.apache.struts2.impl.StrutsActionProxy.prepare(StrutsActionProxy.java:63) at org.apache.struts2.impl.StrutsActionProxyFactory.createActionProxy(StrutsActionProxyFactory.java:37) at com.opensymphony.xwork2.DefaultActionProxyFactory.createActionProxy(DefaultActionProxyFactory.java:58) at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:554) at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81) at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:504) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:421) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1074) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745) Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.wql.action.LoginAction': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.wql.Dao.EmplDao com.wql.action.LoginAction.emplDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.wql.Dao.EmplDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:384) at com.opensymphony.xwork2.spring.SpringObjectFactory.autoWireBean(SpringObjectFactory.java:218) at com.opensymphony.xwork2.spring.SpringObjectFactory.buildBean(SpringObjectFactory.java:198) at com.opensymphony.xwork2.spring.SpringObjectFactory.buildBean(SpringObjectFactory.java:164) at com.opensymphony.xwork2.ObjectFactory.buildBean(ObjectFactory.java:178) at com.opensymphony.xwork2.factory.DefaultActionFactory.buildAction(DefaultActionFactory.java:22) at com.opensymphony.xwork2.ObjectFactory.buildAction(ObjectFactory.java:148) at com.opensymphony.xwork2.DefaultActionInvocation.createAction(DefaultActionInvocation.java:295) ... 25 more Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.wql.Dao.EmplDao com.wql.action.LoginAction.emplDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.wql.Dao.EmplDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:508) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289) ... 34 more Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.wql.Dao.EmplDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1100) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:960) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:855) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480) ... 36 more |
异常信息分析:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.wql.action.LoginAction': Injection of autowired dependencies failed;
这是spring框架的beanFactory中的BeanCreationException:它不能在名为com.wql.action.LoginAction中创建bean:自动注入autowired失败;
nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.wql.Dao.EmplDao com.wql.action.LoginAction.emplDao;
嵌套的异常beanFactory中的BeanCreationException:自动注入字段失败:com.wql.Dao.EmplDao com.wql.action.LoginAction.emplDao;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.wql.Dao.EmplDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
嵌套的异常是beanFactory中的NoSuchBeanDefinitionException:没有一个bean类型符合当前依赖bean类型:[com.wql.Dao.EmplDao]。依赖的注解模式定义Autowired的required为true.
小结:出现该异常的根源是不能再LoginAction注入com.wql.Dao.EmplDao类型的字段,自动注入autowire出现异常。其中异常信息NoSuchBeanDefinitionException也
也表明是依赖注入的bean没有定义。检查spring.xml配置,base-package="com.chen"写错了,因为在加载服务器时WebApplicationContext在初始化时没有通过
扫包注入com.wql.Dao.EmplDao类型的bean实例,所以这里会出现异常。
解决方式:将扫包地址改为:base-package="com.wql"。
备注:如果将类路径下log4j.properties删去,在扫包地址配置错误的情况下:base-package="com.chen",在登陆页面点击登陆没有任何反应,控制台也没有任何
异常提示--由此看来,配置log4j.properties很有必要。
bug2:easyui无法获取page(当前页),获取的值为null
出错的原因是在js文件中的url写错了,加上在请求的action中有默认的方法,而此方法是跳转到显示用户列表的地址的,核心问题是跳转的方式是“重定向”!所以就导致了此问题。
原来在userManager.js中的url:
url:'emplAction',
struts-action的配置是这样的:
<action name="emplAction" class="com.chen.action.EmplAction" method="toListEmp"> <result name="toUserManager" >/easyuiTest/userManager.jsp</result> <result name="test" >/test.jsp</result> <result name="tolist" type="redirectAction">emplAction!listEmpl.action</result> <result name="false">/login.jsp</result> <result name="list">/WEB-INF/jsp/userManager.jsp</result> <result name="toAddEmpl">/WEB-INF/jsp/addEmpl.jsp</result> <result name="toEditEmpl" type="dispatcher">/WEB-INF/jsp/editEmpl.jsp</result> </action>
emplAction中的toListEmp方法、listEmpl方法:【分析:从原来的代码可以看出,js在向emplAction发出请求后,服务器会请求emplAction的toListEmp方法进行处理,此方法会返回tolist,
即重定向至emplAction!listEmpl.action,这样,在listEmpl方法中自然获取不到page了,而在之前的toListEmp方法中可以获取到,因为struts默认的是请求转发。】
public String toListEmp(){ System.out.println(req.getParameter("page")+";"+req.getParameter("rows")+"@@");//2;10@@ return "tolist"; }public void listEmpl() throws IOException{ System.out.println(req.getParameter("page")+";"+req.getParameter("rows")+"--");//null;null-- ... }
解决方法:去掉struts-action的默认方法method="toListEmp",修改js的请求url
<action name="emplAction" class="com.chen.action.EmplAction"> <result name="toUserManager" >/easyuiTest/userManager.jsp</result> <result name="test" >/test.jsp</result> <result name="tolist" type="redirectAction">emplAction!listEmpl.action</result> <result name="false">/login.jsp</result> <result name="list">/WEB-INF/jsp/userManager.jsp</result> <result name="toAddEmpl">/WEB-INF/jsp/addEmpl.jsp</result> <result name="toEditEmpl" type="dispatcher">/WEB-INF/jsp/editEmpl.jsp</result> </action>
url:'emplAction!listEmpl.action',
修改后的url直接请求emplAction中的listEmpl方法,是请求转发,所以可以获取page.
public void listEmpl() throws IOException{ System.out.println(req.getParameter("page")+";"+req.getParameter("rows")+"--");//1;10-- ... }
小结:这个小问题很坑,有时问题不在于它是大是小,而是它的隐蔽性,小问题如果藏得较深,那也着实令人头疼啊。不过,最重要的是检查问题的思维习惯,我检查这个问题用了太多的时间,原因在于我检查方式不对,起初我如论如何也没想到是“请求地址”这个小鬼捣乱,想是不是因为是因为jsp放在WEF-INF目录下引起的呢?就把它放在WebContent下面测试,不行,上网搜也没有相应结果,又想是不是easyui的js引用有问题呢(原来是网页引用的easyui的js),换成引用项目中的easyui的js,也不行,最后干脆拿另一个项目(可以获取easyui的page)进行比较,才发现这个细节:两个url写法不同,于是修改url,可以了,才发现这个问题。现在我想,其实如果仔细分析下请求地址或debug调试一下,就不会浪费这么多时间。只是缘于当时心里太焦躁了--因为这是我绝不曾料想到会出现的问题。所以当出现问题后,一定要冷静分析,不管它是大是小,是在意想之中还是意想之外。此外,由此次经历可以得知,不光要养成冷静、耐心检查问题的心理,养成合理地检查问题的习惯也非常重要,当出现问题后,人的思维习惯总是先从经验推断入手,如果根据自己的经验推断没有解决问题,那么一定不要心浮气躁,而是静下心来结合程序的运行流程对代码的各个环节进行耐心地检查,结合debug调试,相信只要不是陌生的代码,应该就可以得到解决;如果是比较陌生的问题,那么再结合上网搜索以及建立测试项目、代码进行解决--这种方式比较消耗时间,故不优先选择。
Bug3:Oracle 查询异常:ORA-00918: 未明确定义列
查询语句:
select * from (select e.eid,e.name,e.sex,to_char(e.birthday,'yyyy-mm-dd'),d.dname,j.name,to_char(e.hiredate,'yyyy-mm-dd'),js.status,rownum rn from t_empl e,t_dept d,t_job j,t_jobstatus js where e.deptno=d.did and e.job=j.jid and e.status=js.jsid and e.eid!=6 and js.status!='经理') where rn>=2 and rn<=10; |
具体异常信息如下:
ORA-00918: 未明确定义列
00918. 00000 - "column ambiguously defined"
*Cause:
*Action:
行 1 列 8 出错
需说明的是,如果去掉分页,查询正常,即:
select e.eid,e.name,e.sex,to_char(e.birthday,'yyyy-mm-dd'),d.dname,j.name,to_char(e.hiredate,'yyyy-mm-dd'),js.status,rownum rn from t_empl e,t_dept d,t_job j,t_jobstatus js where e.deptno=d.did and e.job=j.jid and e.status=js.jsid and e.eid!=6 and js.status!='经理' |
但有问题就是有问题,不能说因为避免此异常而放弃分页的功能吧?
上网查询大致得知是因为列名重复而导致的,但是有很疑惑,因为在Mysql即使查询的字段中有列名重复的情况,在前面加上别名好像就可以了,所以当时觉得好像玄了吧唧的就没管它,现抽出时间把此项目中的各个表id,name有重复的全部改了,一查,OK了。原觉得玄乎的玩意儿,没啥。
那么是哪里的列名重复呢?--即上面加粗的字段。
在数据库中查看,原来这两张表的都有NAME这一列:
现在改过来:
再查下:
select * from (select e.eid,e.ename,e.sex,to_char(e.birthday,'yyyy-mm-dd'),d.dname,j.jname,to_char(e.hiredate,'yyyy-mm-dd'),js.status,rownum rn from t_empl e,t_dept d,t_job j,t_jobstatus js where e.deptno=d.did and e.job=j.jid and e.status=js.jsid and e.eid!=6 and js.status!='经理') where rn>=2 and rn<=10; |
查询 结果:
小结:很多我们认识玄乎的玩意儿不玄乎,我们也不能总拿自己过往的经验去看待不同的事物,因为每个事物都有自己的特性,只有了解它,理解它才是硬道理。
Bug4:ORA-04098: 触发器 'SCOTT.AUTO01' 无效且未通过重新验证
--A.bug描述及分析
/*
--bug1.在插入一条员工数据时,出现以下异常:
java.sql.SQLException: ORA-04098: 触发器 'SCOTT.AUTO01' 无效且未通过重新验证
Query: insert into t_empl values(null,?,?,?,to_date(?,'yyyy-mm-dd'),to_date(?,'yyyy-mm-dd'),?,?,?,?) Parameters: [a, 123, 男, 1987, 1987-01-01, 0, 1, 1, 1]
at com.chen.dao.BaseDaoImp.update(BaseDaoImp.java:64)
at com.chen.dao.EmplDaoImpl.addEmp(EmplDaoImpl.java:15)
分析:
打开com.chen.dao.EmplDaoImpl.addEmp(EmplDaoImpl.java:15),发现SQL语句是这样写的:
String sql = "insert into t_empl values(null,?,?,?,to_date(?,'yyyy-mm-dd'),to_date(?,'yyyy-mm-dd'),?,?,?,?)";
用Oracle SQL Developer工具打开"oa"连接的"T_EMPL"表,选择"SQL"栏,发现触发器是这样定义的:
CREATE OR REPLACE TRIGGER "SCOTT"."AUTO01"
before insert on "SCOTT"."T_EMPL"
for each row
begin
if inserting then
if :NEW."ID" is null then
select SEQUENCE1.nextval into :NEW."ID" from dual;
end if;
end if;
end;
/
ALTER TRIGGER "SCOTT"."AUTO01" ENABLE;
该触发器的触发事件是对T_EMPL表的插入动作,当新插入的员工ID为null时,查询序列SEQUENCE1的下一个值,即实现了序列自增。但问题是,
在我的eclipse导入OA项目,通过PL/SQL Developer工具导入该项目所需sql文件后,通过select * from user_sequences;查询并未
发现序列SEQUENCE1!所以在插入一条员工数据时,就导致了上述的SQL异常:触发器 'SCOTT.AUTO01' 无效且未通过重新验证--因为序列SEQUENCE1
并不存在而致使触发器未通过验证。
*/
--B.解决方法
--1.创建序列
create sequence SEQ_EMPL;
--2.修改触发器
CREATE OR REPLACE TRIGGER "SCOTT"."AUTO01"
before insert on "SCOTT"."T_EMPL"
for each row
begin
if inserting then
if :NEW."ID" is null then
select SEQ_EMPL.nextval into :NEW."ID" from dual;
end if;
end if;
end;
--3.修改sql
String sql = "insert into t_empl values(SEQ_EMPL.nextval,?,?,?,to_date(?,'yyyy-mm-dd'),to_date(?,'yyyy-mm-dd'),?,?,?,?)";
--C.测试
/*1.插入员工yy,成功,yy的ID为1;2.插入员工tt,失败,异常信息为“违反唯一约束条件”,查询t_empl表,发现李四的
ID为2,所以出现此异常;3.插入员工aa,bb,uu,成功,其员工号依次为3,4,5。*/
--D.小结:
/*1.因为Oracle有序列,用PL/SQL Developer工具导入SQL并不能导入原SQL的序列,所以需重新创建。2.创建序列应见名知意,所以删除了
序列SEQUENCE1,取代的序列为:SEQ_EMPL。
*/