JavaEE

4 篇文章 0 订阅
2 篇文章 0 订阅

 

目录

个人总结:...3

1      实验一:Struts基础...6

1.1            创建一个Action多方法调用的Struts2应用程序(课本p.27).6

1.1.1        实验所遇到的问题及解决方案【重点】...6

1.1.2        实验收获与总结...9

2      实验二:Struts2基础应用——基于Struts2框架的用户登录模块...9

2.1            基础实验——Struts2框架搭建...9

2.1.1        实验所遇到的问题及解决方案【重点】...9

2.1.2        实验收获与总结...9

2.2            提高实验——Struts2标签...10

2.2.1        实验所遇到的问题及解决方案【重点】...10

2.2.2        实验收获与总结...11

2.3            扩展实验——Struts2的国际化...11

2.3.1        实验所遇到的问题及解决方案【重点】...11

2.3.2        实验收获与总结...11

3      实验三:Struts2的控制器组件Action——登录用户的功能扩展...13

3.1            基础实验——Action的自定义方法...13

3.1.1        实验所遇到的问题及解决方案【重点】...13

3.1.2        实验收获与总结...14

3.2            提高实验——ActionSupport与输入校验...14

3.2.1        实验所遇到的问题及解决方案【重点】...14

3.2.2        实验收获与总结...15

3.3            Action类与Servlet API. 19

3.3.1        实验所遇到的问题及解决方案【重点】...19

3.3.2        实验收获与总结...19

4      实验四:Struts2的工作流程——登录用户的高级功能...21

4.1            基础实验——拦截器与过滤器...21

4.1.1        实验所遇到的问题及解决方案【重点】...21

4.1.2        实验收获与总结...22

4.2            提高实验——值栈与OGNL.23

4.2.1        实验所遇到的问题及解决方案【重点】...23

4.2.2        实验收获与总结...23

4.3            扩展实验——Struts2的异常处理...24

4.3.1        实验所遇到的问题及解决方案【重点】...24

4.3.2        实验收获与总结...24

5      实验五 Hibernate基础应用——基于Hibernate框架的用户登录模块... 24

5.1            基础实验——Hibernate框架搭建...24

5.1.1        实验所遇到的问题及解决方案【重点】...24

5.1.2        实验收获与总结...26

5.2            提高实验——持久化对象与Hibernate映射文件...29

5.2.1        实验所遇到的问题及解决方案【重点】...29

5.2.2        实验收获与总结...30

5.3            扩展实验——粒度设计...39

5.3.1        实验所遇到的问题及解决方案【重点】...39

5.3.2        实验收获与总结...42

6      实验六:Hibernate的体系结构——登录用户信息的增删改查...43

6.1            基础实验——Hibernate常用API.43

6.1.1        实验所遇到的问题及解决方案【重点】...43

6.1.2        实验收获与总结...45

6.2            提高实验——HQL语言...45

6.2.1        实验所遇到的问题及解决方案【重点】...45

6.2.2        实验收获与总结...45

6.3            扩展实验——深入Hibernate配置文件...46

6.3.1        实验所遇到的问题及解决方案【重点】...46

6.3.2        实验收获与总结...47

7      实验七:Hibernate关联关系映射——登录用户的地址管理...47

7.1            基础实验——多对一/一对多关联...47

7.1.1        实验所遇到的问题及解决方案【重点】...47

7.1.2        实验收获与总结...49

7.2            提高实验——多对多关联...50

7.2.1        实验所遇到的问题及解决方案【重点】...50

7.2.2        实验收获与总结...52

7.3            扩展实验——一对一关联...53

7.3.1        实验所遇到的问题及解决方案【重点】...53

7.3.2        实验收获与总结...55

8      实验八:SSH整合(Spring4+Struts2+Hibernate4)——基于SSH的用户注册模块块...56

8.1            基础实验——Spring框架搭建...56

8.1.1        实验所遇到的问题及解决方案【重点】...56

8.1.2        实验收获与总结...57

8.2            提高实验——Spring与Hibernate的整合...59

8.2.1        实验所遇到的问题及解决方案【重点】...59

8.2.2        实验收获与总结...61

8.3            扩展实验——Spring、Struts与Hibernate的整合...61

8.3.1        实验所遇到的问题及解决方案【重点】...61

8.3.2        实验收获与总结...62

9      实验九:Spring的核心机制:控制反转(IoC)——登录用户的购物车...64

9.1            基础实验——Spring容器中的依赖注入...64

9.1.1        实验所遇到的问题及解决方案【重点】...64

9.1.2        实验收获与总结...64

9.2            提高实验——Spring容器中的Bean.66

9.2.1        实验所遇到的问题及解决方案【重点】...66

9.2.2        实验收获与总结...67

9.3            扩展实验——深入Spring容器...69

9.3.1        实验所遇到的问题及解决方案【重点】...69

9.3.2        实验收获与总结...69

10             实验十:Sping的面向切面编程(AOP)——用户登录模块的增强处理块...72

10.1         基础实验——使用@AspectJ实现AOP.72

10.1.1      实验所遇到的问题及解决方案【重点】...72

10.1.2      实验收获与总结...72

10.2         提高实验——Spring AOP实现事务管理... 73

10.2.1      实验所遇到的问题及解决方案【重点】...73

10.2.2      实验收获与总结...75

10.3         扩展实验——Spring AOP的核心工作原理:代理和代理工厂... 76

10.3.1      实验所遇到的问题及解决方案【重点】...76

10.3.2      实验收获与总结...76

11             本课程相关书籍阅读:...77

 


 

个人总结:

框架文件命名规则:

校验文件命名规则:

1.      Action类名-validation.xml  (对Action类进行校验,与Action类同路径)

2.      Action类名-Action别名-validation.xml  (对Action类的某一个方法进行校验,与Action类同路径)

3.      Validation.xml(注册校验规则,放在src下)

4.      hibernate.cfg.xml  (用于配置Hibernate和数据库的连接信息)

5.      类名.hbm.xml映射文件(用来描述持久化类和数据库表,数据列之间的对应关系,应该和类名.class放在同一个文件夹下)

6.      hibernate.properties(Hibernate的配置文件,位于src下)

7.      hibernate.cfg.xml(Hibernate的配置文件,位于src下,当调用Configuration的configure()方法时,Hibernate自动加载该文件)

8.      applicationContext.xml(spring的配置文件,位于src下)

 

Eclipse 下struts工程的创建流程

1.       添加struts支持包,在web-INF—lib下(也可以在build path下添加user libraty)

2.       web.xml添加filter和filter—mapping

3.       在src下添加struts-xml,写<package ><actoin>

 

myeclipse下struts工程的创建流程

1.       创建web project,选javaEE5.0

2.       右键,添加struts capability

a)       选择struts2.1.

b)       /*

c)       UserLibrary

3.       在web-INF-lib 下添加struts支持包

(对比myeclipseeclipsemyeclipse帮我们做了以下工作,1.添加struts.xml2.写了filterfilter-mapping 3.添加struts支持包)

 

IntelliJ IDEA下struts工程的创建流程

1.       创建一个project

2.       创建一个model,选择java enterprise,选择tomcat,jre的版本。选择struts2,library选择download,IDEA会帮我们下载,是最新的。点击next。

(对比IntelliJ IDEAeclipsemyeclipse帮我们做了以下工作,1.添加struts.xml2.写了filterfilter-mapping 3.添加struts支持包)

 

java 下Hibernate工程需要的步骤

1.      创建Hibernate的配置文件

2.      创建数据库表和持久化类

3.      创建对象-关系映射

4.      通过Hibernate API编写访问数据库的代码

若使用eclipse编写Hibernate程序,为了方便写hibernate配置文件和映射文件,可安装Hibernate tools,DB view 具体的安装过程:

http://www.cnblogs.com/sky230/p/5876589.htmlhttp://www.cnblogs.com/kevinq/p/4995206.html

 

Myeclipse下Hibernate工程的创建过程

1.     创建数据库表。

2.     创建工程,添加Hibernate包。

3.     DB Broswer 配置数据库信息

4.     添加Hibernate能力。

5.     Hibernate逆向工程。

6.     编写java 程序。

 

Myeclipse下Spring工程的创建过程

1.       写javaBean类,要包含一个无参构造函数。

2.       加入spring必备包。

3.       编写spring的对象配置文件

4.       编写java程序,加载spring配置文件。

 

Myeclipse下Spring+Hibernate工程的创建过程

1.       创建数据库表。

2.       创建工程,添加spring和hibernate必备包。

3.       DB Broswer 配置数据库信息

4.       先添加spring能力,便于使用spring管理SessionFactory,此时myeclipse帮我们添加了applicationContext.xml。

5.       再添加Hibernate能力,不生成SessionFactory类,让spring为我们管理session。

6.       Hibernate逆向工程。

7.       编写java 程序。

 

Myeclipse下Spring+Hibernate+struts工程的创建过程

1.       新建数据库表(先添加主外键关系,再添加数据)

2.       新建工程,添加支持ssh框架的jar包,并添加引用

3.       添加Spring支持。

4.       在DB brower中配置数据库。

5.       添加struts支持,修改web.xml,添加 listener,context-param

<context-param>

       <param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/classes/applicationContext.xml</param-value>

</context-param>

<listener>

    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

6.       新建struts.properties

7.       添加Hibernate支持,就是在applicationContext.xml配置文件中添加管理hibernate的Bean。

8.       Hibernate逆向工程,并手动修改bean中的数据类型。

9.       新建dao类,daoImp类(继承HibernateDaoSupport)

10.    在applicationContext.xml中添加实现类的bean。

1        实验一:Struts基础

1.1     创建一个Action多方法调用的Struts2应用程序(课本p.27)

1.1.1   实验所遇到的问题及解决方案【重点】

方法一:使用 * 与占位符 的方法,利用jsp页面的action 带有的方法名称信息分别调用不同的方法。

<form action="loginAction_login"method="post">

<action name="loginAction_*"class="com.LoginAction" method="{1}" >

方法二:利用在form的action中指定method的方法来调用action。

加上下面几句:

         <constantname="struts.enable.DynamicMethodInvocation"value="true"/>

<form action="loginAction!login.action"method="post">

 

struts2 使用struts.xml来管理配置,也使用struts.properties来管理常量。

struts按以下顺序加载常量:

struts-default.xml ;   struts-plugin.xml ; struts.xml  ; struts.properties  ;  web.xml

 

<actionname="*">

         <result>/{1}.jsp</result>

</action>

该片段的作用是如果请求a.jsp,那么进入a.jsp页面。如果请求b.jsp页面,那么进入b.jsp页面。通过这种方式,可以避免让浏览者直接访问系统的jsp页面。而是让struts管理所有的用户请求。

如果省略了result中的name属性,那么将采用默认的name属性值,默认success

 

除非请求的URL与Action的name绝对相同,否则按先后顺序来决定由那个Action来处理用户请求。因此,应该将name”*”的action配置在最后。

 

学会dispatcherredirectredirect-actionchain的用法。

1.       dispatchertype为dispather时,是先执行class中的方法,再将请求转发到result中的页面。

2.       redirecttype为redirect时,还是会执行action的class中的方法,但是,result中页面的请求是新发起的,是当action处理完用户请求的时候,再次向视图资源发送一次新的请求。所以action的class的属性是不可见的,即丢失了action的处理结果。

3.       redirect-action当一个action处理完后,直接将请求重定向到另一个action。而前一个action的处理结果丢失。使用方法:

<actionname="login"class="com.LoginAction">

                            <resulttype="redirectAction">

                                     <paramname="actionName">newAction</param>

                                     <paramname="namespace">/</param>

                            </result>

                   </action>

4.       chain:将请求转发到另一个action中,包含前一个action的处理结果。

 

问题1:

将myeclipse的tomcat和JDK修改为自己安装的tomcat和jdk,在修改了以下的位置后还是报错,

1.      Window—preferences—myeclipse—servers—tomcat—tomcat6.x,然后指定tomcat所在路径(特别强调:单选框选中enable);

2.      window--preferences--myeclipse—servers—tomcat—tomcat6.x—jdk,然后指定jdk所在的路径。

3.      window—preferences—java—installed jres中选中新的jdk,可见myeclipse软件本身也带有支持mytomcat的jdk。

出现的错误是

java.lang.ClassNotFoundException:org.apache.juli.logging.LogFactory

解决方法:

window--preferences--myeclipse—servers—tomcat—tomcat6.x --path第一个选项中加入这个tomcat-juli.jar,这个包位于tomcat根目录的bin下。

问题2:

myeclipse中运行java web是,总是跳出classloader.class

解决方法:

选择debug视图,run—remove all breakpoints

问题3:

将tomcat下webapps和work下的以前运行的工程删掉再运行现有的工程时出现大量错误。将删除的文件重新恢复以后再重新运行,发现又可以运行了。奇怪的是以前直接手动删除tomcat下的工程并不会出现这样的问题。

解决方法:

解决这个问题过程中学习了两个知识点

1.      tomcat 中有两个文件夹webapps和work,两个文件夹下都有工程信息。webapps下放的是编译后的工程,而work是当访问web工程时,将访问到的工程的jsp页面翻译成servlet文件和相应的编译好的.java文件。这就是为什么在第一次访问javaweb工程时特别慢的原因,当第二次访问时tomcat会直接将work中相应的文件调入内存中。

2.      在MyEclipse下部署的web项目到tomcat下“不会”修改conf/server.xml文件。但是eclipse部署时候就“会”修改,server.xml会在文件末尾部分的<Host>...</Host>中多出来一个<Content..../>标签。我的内容如下:

<Context docBase="C:\SoftwareInstall\apache-tomcat-8.0.3\webapps\chap2_9" path="/chap2_9" reloadable="true" source="org.eclipse.jst.jee.server:chap2_9"/>  

由于我同时使用eclipse和myeclipse,所以出现了上述问题。最终的解决方法是删除相关的<Content..../>内容即可。另一个解决方法是在eclipse的如下页面的红色框中除去不用的页面,其在tomcat中的信息也会被除去。

 

问题4:

myeclipse中的debug模式和run模式的区别。

解决方法:

的在debug模式下,我们可以通过设置断点来查看程序运行时的详细信息,同时在不重启web server的情况下,我们修改的内容会马上起作用。

问题5:

使用在form中指定method方法来调用action时,如:

<formaction="loginAction!login.action" method="post">后,页面提示错误,错误为:

There is no Action mapped fornamespace [/] and action name [loginAction!login] associated with context path[/page27].

解决方法:

在struts.xml文件中加上下一句。

<constantname="struts.enable.DynamicMethodInvocation" value="true"/>

注意:value为true

问题6:

IntelliJ 14怎么把java ee项目部署到tomcat的webapp目录下?

解决方法:

C:\Users\DELL\.IntelliJIdea14\system\tomcat\Unnamed_sgm-portal\work\Catalina\localhost\sgm-portal

问题7:

使用eclipse开发Struts工程时,没有直接把jar包加到web-INF下的lib中,没有添加了外部的引用,这样程序跑不通。

解决方法:

eclipse对放到WEB-INF/lib 下面的jar 部署的时候会直接部署到tomcat容器下面去。链接在外部的jar包不会,所以平时自己的项目都是推荐将jar直接放到WEB-INF/lib下面这样将工程压缩给其他人时,直接可以使用,而不用担心缺少jar包的问题。

问题8:

struts.xml为什么不放在web-INF-classes

解决方法:

当eclipse生成,部署web项目时,会自动将src下除了*.java的所有文件都复制到web-INF-classes下。

问题9:

做第一题的时候,因为没有加入struts需要的jar包,在创建的类要继承ActionSupport时,系统提示错误,刚开始觉得很奇怪,后来和已经成功运行的程序做比较发现没有加入jar包。

解决:因为ActionSupport是在struts 的jar包中定义的,所以没有添加时直接引用是不合法的。以后记得添加。

1.1.2   实验收获与总结

1.      学习了在idea编译器的使用方法以及怎么使用idea创建,删除一个javaweb工程。

2.      了解了struts的使用方法。

3.      安装和学习了git的使用方法,学习了《GitHub入门与实践》这本书。

2        实验二:Struts2基础应用——基于Struts2框架的用户登录模块

2.1     基础实验——Struts2框架搭建

2.1.1   实验所遇到的问题及解决方案【重点】

问题1:

datetimepicker的效果没有显示出来。

问题解决:

先导入struts2-dojo-plugin-2.1.6.jar,再在jsp页面最开头加上:

<%@taglib prefix="sx" uri="/struts-dojo-tags" %>

在<head></head>中加入<sx:head/>

2.1.2   实验收获与总结

学习了标签的使用方法。

2.2     提高实验——Struts2标签

2.2.1   实验所遇到的问题及解决方案【重点】

问题1:

在做checkboxlist时出现以下错误:

HTTPStatus 500 - The following has evaluated to null or missing:

typeException report

message Thefollowing has evaluated to null or missing:

descriptionTheserver encountered an internal error that prevented it from fulfilling thisrequest.

exception

org.apache.jasper.JasperException: The following has evaluated to null or missing:
==> parameters.name  [in template "template/simple/checkboxlist.ftl" at line 64, column 32]
 
问题解决:

发现没有写checkboxlist的name属性,将name属性添加进去后问题解决

问题3:

做输入验证的时候,出现以下错误。

严重: Caught exception while loadingfile com/bh/action/RegistAction-validation.xml 

http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd - [unknown location] 

at com.opensymphony.xwork2.util.DomHelper.parse(DomHelper.Java:119) 

问题解决:

将XXX-validation.xml 中的

<?xml version="1.0"encoding="utf-8"?>

<!-- 指定校验配置文件的DTD信息 -->

<!DOCTYPE validators PUBLIC

"-//OpenSymphony Group//XWorkValidator 1.0.3//EN"

"http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">

<!-- 校验文件的根元素 -->

修改为

<?xml version="1.0"encoding="utf-8"?>

<!-- 指定校验配置文件的DTD信息 -->

 <!DOCTYPE validators PUBLIC

 "-//Apache Struts//XWork Validator 1.0.3//EN"

 "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">

<!-- 校验文件的根元素 -->

问题4:

struts国际化,将中文转换为ASCII码,显示结果还是乱码。

问题解决:

native2ascii -encodingUTF-8 messagesource.propertis messagesource_zh_CN.propertis

2.2.2   实验收获与总结

学习了struts标签的使用方法。

2.3     扩展实验——Struts2的国际化

2.3.1   实验所遇到的问题及解决方案【重点】

问题:

当国际化的资源文件在某一个包下时,该怎么访问资源文件

解决方法:

struts.custom.i18n.resources=cn.edu.zjut.local.message

cn.edu.zjut.local为包名,message为baseName

当message为具体的资源文件名时,页面显示的是选择的国家的语言

2.3.2   实验收获与总结

实现了中英文切换

学习了struts国际化配置与使用方法。

资源文件的命名格式如下:

1.      baseName_language_country.properties

2.      baseName_language.properties

3.      baseName.properties

其中baseName是资源文件的基本名,我们可以自定义,但language和country必须Java支持的语言和国家。如:

中国大陆: baseName_zh_CN.properties

美国: baseName_en_US.properties

当准备好资源文件之后,在struts.xml中通过struts.custom.i18n.resources常量把资源文件定义为全局资源文件,如下:

<constant name="struts.custom.i18n.resources" value=“csdn" />

csdn为资源文件的基本名。

在JSP页面中使用<s:text name=“”/>标签输出国际化信息:

<s:text name=“user”/>,name为资源文件中的key

在Action类中,可以继承ActionSupport,使用getText()方法得到国际化信息,该方法的第一个参数用于指定资源文件中的key。

在表单标签中,通过key属性指定资源文件中的key,如:

<s:textfield name="realname" key="user"/>

国际化—输出带占位符的国际化信息

资源文件中的内容如下:

welcome= {0},欢迎来到传智播客{1}

在jsp页面中输出带占位符的国际化信息

<s:text name="welcome">

   <s:param><s:property value="realname"/></s:param>

<s:param>学习</s:param>

</s:text>

国际化—包范围资源文件

在一个大型应用中,整个应用有大量的内容需要实现国际化,如果我们把国际化的内容都放置在全局资源属性文件中,显然会导致资源文件变的过于庞大、臃肿,不便于维护,这个时候我们可以针对不同模块,使用包范围来组织国际化文件。

方法如下:

java的包下放置package_language_country.properties资源文件,package为固定写法,处于该包及子包下的action都可以访问该资源。当查找指定key的消息时,系统会先从package资源文件查找,当找不到对应的key时,才会从常量struts.custom.i18n.resources指定的资源文件中寻找。

也可以为某个action单独指定资源文件,方法如下:

Action类所在的路径,放置ActionClassName_language_country.properties资源文件,ActionClassNameaction类的简单名称。

当查找指定key的消息时,系统会先从ActionClassName_language_country.properties资源文件查找,如果没有找到对应的key,然后沿着当前包往上查找基本名为package 的资源文件,一直找到最顶层包。如果还没有找到对应的key,最后会从常量struts.custom.i18n.resources指定的资源文件中寻找

国际化—JSP中直接访问某个资源文件。

3        实验三:Struts2的控制器组件Action——登录用户的功能扩展

3.1     基础实验——Action的自定义方法

3.1.1   实验所遇到的问题及解决方案【重点】

方法一:使用 * 与占位符 的方法,利用jsp页面的action 带有的方法名称信息分别调用不同的方法。

<form action="loginAction_login"method="post">

<action name="loginAction_*"class="com.LoginAction" method="{1}" >

方法二:利用在form的action中指定method的方法来调用action。

加上下面几句:

         <constantname="struts.enable.DynamicMethodInvocation"value="true"/>

<form action="loginAction!login.action"method="post">

方法三:在<s:submit的value属性中写调用方法。

<s:submit name="submit"key="login.submit.button" value="login"/>

3.1.2   实验收获与总结

Action类是多实例的,Action类的属性是线程安全的;Action类的生命周期,框架先实例化 Action 对象,再调用 Action 的 set*** 函数,封装表单数据,然后调用 execute 函数,进行处理。在第二次提交时,Action会重新实例化,说明每一个 Action 对象都服务一个请求,这和 Servlet 的原理是不一样的。

 

在JSP页面中,可以通过Struts2标签调用Action中对应的getter方法,从而输出Action的属性值;

action的result的type可选范围及其含义:

1.      dispatchertype为dispather时,是先执行class中的方法,再将请求转发到result中的页面。

2.      redirecttype为redirect时,还是会执行action的class中的方法,但是,result中页面的请求是新发起的,是当action处理完用户请求的时候,再次向视图资源发送一次新的请求。所以action的class的属性是不可见的,即丢失了action的处理结果。

3.      redirect-action当一个action处理完后,直接将请求重定向到另一个action。而前一个action的处理结果丢失。使用方法:

4.      chain跳转到另一个action,原先的action的处理结果还可以访问。在URL中只显示第一个action的地址

5.      stream: 用于向浏览器返回一个InputStream(一般用于文件下载)

6.      freemarker: 用于FreeMarker整合的结果类型

7.      plaintext: 用于显示某个页面的原始代码的结果类型

8.      chart: 用于整合JFreeChart的结果类型

9.      jsf: 用于JSF整合的结果类型

10.   httpheader: 用于控制特殊的HTTP行为的结果类型

11.   jasper: 用于JasperReports整合的结果类型

12.   tiles: 用于Tiles整合的结果类型

13.   velocity: 用于Velocity整合的结果类型

14.   slt: 用于XML/XSTL整合的结果类型

 

因为每次提交account都会重新实例化,所以count的值不会变。

 

因为type是redirect,页面重定向,原先的action处理结果丢失,所以不会出现count。

3.2     提高实验——ActionSupport与输入校验

3.2.1   实验所遇到的问题及解决方案【重点】

问题1:

在验证actionmessage时出现以下错误信息

解决:

将<s:submit  key=”submit”/>

改成

<s:submit  value = "%{getText('submit')}"/>

或者

struts.xml中的  <constantname="struts.devMode" value="true" />去掉开发模式

问题2:

做“使用校验器对用户注册的请求参数进行校验,要求注册时两次密码输入相同”时,发现不管两次输入的密码是一样的或者不一样的,都会出现两次密码输入不一致的域错误提醒,后来发现在validation的para写的表达式错误,原来是

<field-validatortype="fieldexpression"short-circuit="true">

                            <paramname="expression">loginUser.password=loginUser.repassword</param>

                            <message>两次密码输入必须一致</message>

                   </field-validator>

后改为

<field-validatortype="fieldexpression"short-circuit="true">

                            <paramname="expression">loginUser.password==loginUser.repassword</param>

                            <message>两次密码输入必须一致</message>

                   </field-validator>

少了一个“=”。

问题3:

利用基于ActionSupport类validateXXX()的方法来验证用户的账号信息是否输入,但不管账号信息没有输入,都出不来field的错误信息。

解决:

后来加了一个判断用户账号信息是否为空字符串,就可以了,原来struts的表单当用户未输入信息的时候返回的是空字符串。

3.2.2   实验收获与总结

通过做习题,我对struts的内置类型装换以及它的出错处理有了比较好的理解。

 

学会了怎么使用校验器校验,以及如何结合校验器和国际化。

做法:在name中写想要验证的字段,调用对应的内置校验器,在message的key中写国际化信息。

<fieldname="loginUser.birthday">

                   <field-validationtype="conversion">

                            <paramname="repopulateField"> false</param>

                            <messagekey="$getText('invalid.fieldvalue.loginUser.birthday')"></message>

                   </field-validation>

         </field>

Struts2内建常用的校验器

1.        Required(必填校验器,要求field的值不能为null);

2.        Requiredstring(必填字符串校验器,要求field的值不能为null,并且长度大于0,默认情况下会对字符串前后去空格)

3.        Requiredlength(字符串长度校验器,要求field的值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength指定参数最大长度,trim参数指定校验field之前是否去除字符串前后的空格)

4.        Regex(正则表达式校验器,检查被校验的field是否匹配一个正则表达式,expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认为true)

5.        Int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)

6.        Double(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)

7.        Fieldexpression(字段OGNL表达式校验器,要求field满足一个ognl表达式,expression参数指定ognl表达式)该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过

8.        Email(邮件地址校验器,要求如果field的值非空,则必须是合法的邮件地址)

9.        url(网址校验器,要求如果field的值非空,则必须是合法的url地址)

10.     data(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)

11.     conversion(转换校验器,指定在类型转换失败时,提示的错误信息)

12.     visitor(用于校验action中的复合属性,它指定一个校验文件用于校验复合属性中的属性)

13.     expression(ognl表达式校验器,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过,该校验器不可用在字段校验器风格的配置中)

 

因为validateXxx()方法无返回类型,所以我想validateXxx()方法是只能验证域的。

 

域级别的错误信息返回类型为input,而action级别错误的返回信息由方法决定。

即若jsp页面使用的是struts的标签,那么就可以直接输出域错误信息,而不用在页面中加入<s:fielderror/>标签。

但如果想要输出action级别的信息,还是要在页面中加入<s:actionmessage/>

关于为什么要在userbean里写get,set方法以及在action里写get,set方法的说明。

使用校验器校验的方法

1.      采用手工编写代码实现。

2.      基于XML配置方式实现。

采用手写实现validation()或validationFunctionName()方法就行。如果校验失败的话会转发到一个名为input的视图,所以在Action中需要添加一个input名字的视图。在视图中可以使用<s:fielderror/>对错误信息的输出

通过重写Valiedate()方法实现,validate()方法会校验action中所有与execute方法签名相同的方法。当某个数据校验失败时,我们应该调用addFieldError()方法往系统的fieldErrors添加校验失败信息(为了使用addFieldError()方法,action可以继承ActionSupport),如果系统的fieldError包含失败信息,Struts2会将请求转发到名为input的result。在input视图中可以通过<s:fielderror/>显示失败信息。

使用基于XML配置方式实现输入校验时,Action也需要继承ActionSupport,并且提供校验文件,校验文件盒action类放在同一个包下,文件的取名格式为:ActionClassName-validation.xml,其中ActionClassName为action的简单类名,-validate为固定写法。如果Action类为cn.itcast.UserAction,那么该文件的取名应为:UserAction-validation.xml

<message>为校验失败后的提示信息,如果需要国际化,可以为nessage指定key属性,key的值为资源文件中的key。在这个校验文件中,对acition中字符串类型的username属性进行验证,首先要求调用trim()方法去掉空格,然后判断用户名是否为空!

 

输入校验的流程:

1.      类型转化器对请求参数执行类型转换,并把转换后的值赋给action中的属性。

2.      如果在执行类型转换的过称中出现异常,系统会将异常信息保存到ActionContext,conversionError拦截器将异常信息封装到fieldError里,不管类型转换是否出现异常,都会进入第三步

3.      系统通过反射技术先调用action中的validateXxxx()方法,Xxxx为方法名。

4.      再调用action中的validate()方法。

5.      经过上面4步,如果系统中的fieldError存在错误信息(即存放错误信息的集合的size大于0),系统自动将请求转发至名称为input的视图。如果系统中的fieldErrors没有任何错误信息,系统将执行action中的处理方法。

 

校验规则文件中主要元素的作用和配置方法

配置方法书上有,只要将fieldname改成要验证的字段信息,再调用相应的校验器即可。

 

使用国际化资源文件的步骤及方法

写资源文件

1.        Action 范围资源文件: 在Action类文件所在的路径建立名为

ActionName_language_country.properties的文件

2.        包范围资源文件: 在包的根路径下建立文件名为package_language_country.properties的属性文件,一旦建立,处于该包下的所有 Action 都可以访问该资源文件。注意:包范围资源文件的 baseName 就是package,不是Action所在的包名。

3.        全局资源文件

Ø  命名方式:basename_language_country.properties

Ø  struts.xml <constantname="struts.custom.i18n.resources" value="baseName"/>

在jsp,action中调用资源文件的key。

 

常用的内置类型转换器及其使用方法

1.        boolean和Boolean:完成String和布尔型之间的转换

2.        char和Character:完成String和字符型之间的转换

3.        int和Integer:完成String和整型之间的转换

4.        long和Long:完成String和长整型之间的转换

5.        float和Float:完成String和单精度浮点型之间的转换

6.        double和Double:完成String和双精度浮点型之间的转换

7.        Date:完成String和日期类型之间的转换,日期格式为用户请求本地的SHORT格式

8.        数组:该类型在数据转换时,必须满足要转换的数据中每一个元素都能转换成数组的类型,但若程序员自定义类型转换器,则需要根据情况判断

9.        集合:在使用集合类型转换器时,如果集合众的数据无法确定,可以先将起封装到一个String类型的集合中,然后再用到某个元素时,在进行手动转换

类型转换实在页面与Action互相传递数据时发生的

 

3.3     Action类与ServletAPI

3.3.1   实验所遇到的问题及解决方案【重点】

购物车关键代码:

ShopppingCart shopppingCart = newShopppingCart();

session.setAttribute("shoppingcart",shopppingCart);

                                    

本次实验比较简单,所以没有遇到任何问题。

3.3.2   实验收获与总结

struts的ognl context 结构图

 

Action中访问Servlet API的四种方法

Ø  通过Action来访问servlet 内置对象

Ø  通过接口RequestAware  , SessionAware  ,  ApplicationAware访问servlet内置对象

Ø  通过接口ServletContextAware,ServletRequestAware,ServletResponseAware访问servlet API

Ø  通过ServletActionContext的静态方法访问

 

通过Action来访问servlet 内置对象

private Map request,session,application;

 

//获取ActionContext对象

         ActionContext ctx= ActionContext.getContext();

         //通过ActionContext对象获取请求、会话和上下文对象相关联的Map对象

         request=(Map) ctx.get("request");

         session=(Map) ctx.getSession();

         application=(Map) ctx.getApplication();

 

通过接口RequestAware  , SessionAware  ,  ApplicationAware访问servlet内置对象

implementsRequestAware  , SessionAware  ,  ApplicationAware接口

 

private Map request,session,application;

 

实现以下方法:

@Override

         public voidsetApplication(Map<String, Object> application) {

                  

                   this.application=application;

                  

         }

         @Override

         public voidsetSession(Map<String, Object> session) {

                   // TODOAuto-generated method stub

                   this.session =session;

                  

         }

         @Override

         public voidsetRequest(Map<String, Object> request) {

                   // TODOAuto-generated method stub

                   this.request =request;

         }

 

通过接口ServletContextAware,ServletRequestAware,ServletResponseAware访问servlet API

implementsServletContextAware,ServletRequestAware,ServletResponseAware

        

privateServletContextapplication;

         private HttpServletRequestrequest;

         privateHttpServletResponseresponse;

         private HttpSessionsession;

 

@Override

         public voidsetServletResponse(HttpServletResponse response) {

                   // TODOAuto-generated method stub

                   this.response =response;

         }

         @Override

         public voidsetServletRequest(HttpServletRequest request) {

                   // TODOAuto-generated method stub

                   this.request =request;

         }

         @Override

         public voidsetServletContext(ServletContext application) {

                   // TODOAuto-generated methodstubapplication

                   this.application =application;

         }

        

 

通过ServletActionContext的静态方法访问

         private ServletContextapplication =ServletActionContext.getServletContext();

 

         private HttpServletRequestrequest = ServletActionContext.getRequest();

 

         private HttpServletResponseresponse =ServletActionContext.getResponse();

 

         private HttpSessionsession =request.getSession();

4        实验四:Struts2的工作流程——登录用户的高级功能

4.1     基础实验——拦截器与过滤器

4.1.1   实验所遇到的问题及解决方案【重点】

做该实验未遇到问题,但通过做题,学习了很多知识。

 

4.1.2   实验收获与总结

若开发者为action指定另外的拦截器,那么系统就不再使用默认的拦截器。若要使用,则需在struts.xml的action中手动配置。

<interceptor-ref  name=”defaultStack”>

 

1. 如果在拦截器中,我们不使用invocation.invoke()来完成堆栈中下一个元素的调用,而是直接返回一个字符串作为执行结果,那么整个执行将被中止。

2. 我们可以以invocation.invoke()为界,将拦截器中的代码分成2个部分,在invocation.invoke()之前的代码,将会在Action之前被依次执行,而在invocation.invoke()之后的代码,将会在Action之后被逆序执行。

整个拦截器的核心部分是invocation.invoke()这个函数的调用位置。

 

拦截器方法过滤,当没有指定拦截action的方法时,默认拦截该方法。

当既指定拦截action中的方法,又指定不拦截action中的方法,拦截器最后会拦截该方法。

 

拦截器调用顺序

在拦截方法之前,配置在前面的拦截器会先起作用,在拦截方法之后,配置在后面的拦截器先起作用。

 

拦截器和过滤器的区别

1.        拦截器是基于Java的反射机制的,而过滤器是基于函数回调

2.        过滤器依赖与servlet容器,而拦截器不依赖与servlet容器

3.        拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用

4.        拦截器可以访问action上下文、值栈里的对象,而过滤器不能

5.        在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次

 

Struts2自定义拦截器的基本开发步骤和配置方法

1.      首先,定义一个拦截器类,这个拦截器类要继承AbstractInterceptorInterceptor接口,实现intercept方法。

2.      struts.xml中用刚刚实现的拦截器类定义一个拦截器

<interceptors>

<interceptorname="authority"class="com.AuthorityInterceptor"></interceptor>

</interceptors>

3.      使用拦截器

<interceptor-refname="authority"/>

<interceptor-refname="defaultStack"/>

 

Struts2自定义过滤器的基本开发步骤和配置方法

1.      定义一个过滤器方法,该方法实现Filter接口。重写diFilter()方法。

2.      在web.xml配置该过滤器。

<filter>

                   <filter-name>accessFilter</filter-name>

                   <filter-class>com.AccessFilter</filter-class>

</filter>

<filter-mapping>

                   <filter-name>accessFilter</filter-name>

                   <url-pattern>*.jsp</url-pattern>

</filter-mapping>

4.2     提高实验——值栈与OGNL

4.2.1   实验所遇到的问题及解决方案【重点】

做该实验时没有遇到问题。

4.2.2   实验收获与总结

使用OGNL获取session、application等其它对象的方法

如图:OGNL在获取除ValueStack以外的对象,如:application,session,request,parameter,attr,要加#前缀。

 

<s:urlvalue="items.{name}[0]"/>

 <br/>

 <s:url value="%{items.{name}[0]}"/>

url的value代表的是字符串,所以这样写的结果会输出items.{name}[0],如果想要得到

items.{name}[0]的值,就要用%{  }将字符串包含起来。

4.3     扩展实验——Struts2的异常处理

4.3.1   实验所遇到的问题及解决方案【重点】

该实验比较简单,没有遇到问题。但通过该实验,练习了在struts抛出以及处理异常的方法。

4.3.2   实验收获与总结

struts中处理异常的方法

1.      定义一个异常类。该异常类继承了Exception类。

2.      在方法中抛出异常 。

throws Exception

3.      在struts.xml的action中处理该异常。

<exception-mappingresult="userExcp"exception="com.UserException"/>

<result name="userExcp">/loginException.jsp</result>

4.      在JSP页面中输出异常

<s:propertyvalue="exception.message"/>

5        实验五Hibernate基础应用——基于Hibernate框架的用户登录模块

5.1     基础实验——Hibernate框架搭建

5.1.1   实验所遇到的问题及解决方案【重点】

问题1:

前一天能运行的程序现在运行出现问题,以下是eclipse和nacivat出现的错误提示。

解决方法:

后修改了访问数据库的密码,重新启动mysql。

问题2:

解决方法:

由于使用的书上的工程,包括web.xml,对比后发现书上的web.xml的头部说明和自己版本的web.xml文件不一样,后来把这个web.xml文件删了自己写一个就行了。

问题3:

运行程序,遇到这样一个问题:

解决方法:

原来的代码:

Configuration cfg = newConfiguration(); 

sessionFactory = cfg.buildSessionFactory();

修改后的代码:

Configuration cfg = newConfiguration(); 

cfg.configure("/hibernate.cfg.xml");//加载配置信息

sessionFactory = cfg.buildSessionFactory();

添加了寻找hibernate.cfg.xml文件的路径。

或者添加HibernateSessionFactory的java类。

问题4:

解决方法:

该问题发生的原因是jdk版本不兼容。因为MyEclipse的jre是1.6,而我安装的tomcat是1.8,所以我将运行环境的jre的版本改为1.6.。这样,问题解决。

5.1.2   实验收获与总结

1.      表的主键最好不要具有任何业务含义,因为任何有业务含义的字段都有可能随着业务需求的改变而被变化。(不要给主键赋予任何业务含义)。一种比较常见的做法是使用代理主键。例如:为表定义一个不具有任何含义的ID字段。

2.      关联是有方向的。关联的方向决定了程序操作实体域对象的方式。

a)       单向关联

b)       双向关联

3.      一个SessionFactory对象对应一个数据库。

4.      Hibernate中的Session和Java Web中的HttpSession没有任何关系。Hibernate的Session不是线程安全的,应该避免多个线程共享一个Session实例,它是轻量级的。每个Session都有自己的缓存。通常将一个session对象和一个数据库事物绑定,也就是说,每执行一个数据库事物,就创建一个session实例。最后要执行close()方法。

5.      HQL是面向对象的,它引用的是了类名及类的属性,而不是表名和字段。

6.      java应用中使用Hibernate的步骤:

a)       创建Hibernate的配置文件

b)       创建数据库表和持久化类

c)        创建对象-关系映射

d)       通过Hibernate API编写访问数据库的代码

7.      hibernat.show_sql=”true”那么在程序运行时,控制台会输出sql语句。想要输出格式化的sql语句,可使用format_sql。

8.      Hibernate不要求持久化类必须实现java.io.Serializale接口,除非该类对象要在网络中流动,则必须要实现java.io.Serializale接口。

9.      Hibernate要求持久化类必须提供一个不带参数的默认方法。因为在程序运行时,Hibernate会运用java的反射机制,调用java.lang.reflect.Constructor.newInstance()方法来构造持久化类的实例。

10.  Hibernate的ORM映射文件,在<class> 中<id>子元素必须存在且只能出现一次。且<id>先定义,<property>后定义。

否则会出现以下异常:

11.   如果没有设置<class>table属性,则默认把类名当做表名

12.  <property>的name属性指定类的属性和表的字段的映射。

13.   <property>的column属性指定与类的属性映射的表的字段名,如果没有column属性,则hibernate直接以类的属性作为字段名。总体来说,column是描述数据库表中的字段属性的。只有not-null会影响Hibernate运行时的行为。

14.  <property>的not-null属性为true,表明不允许为空,默认是false。它的意义是在开发和测试阶段能捕捉到表述层和业务逻辑层应该处理而未处理的异常。提醒开发人员加入必要的数据验证逻辑。

15.  <property>的type属性指定Hibernate映射类型。

16.   java,hibernate,数据库之间的类型对应关系。

17.  如果没有为属性设置hibernate类型,则hibernate会运用java反射机制识别出持久化类的java类型,然后使用与之对应的默认的hibernate映射类型。

18.   Configuration创建了SessionFactory对象后,SessionFactory不会和Configuration对象关联,因此,如果再修改Configuration对象的配置信息,不会与SessionFactory对象的行为有任何影响。

19.  hibernate事务:事务就是指作为单个逻辑工作单元执行的一组数据操作,这些操作要么必须全部成功,要么必须全部失败,以保证数据的一致性和完整性。

20.  关于为什么控制台输出的sql语句有“?”。

5.2     提高实验——持久化对象与Hibernate映射文件

5.2.1   实验所遇到的问题及解决方案【重点】

问题1:

做hibernate联合主键题时,报错,如图:

解决方法:

如上图的提示,dao.ItemPK必须implementsjava.io.Serializable。在implements java.io.Serializable以后,再运行,问题解决。做这一题的最大收获是学到了在网络上传输的对象需要implementsjava.io.Serializable。

问题2:

在将isbn和title作为联合主键以后,页面上的isbn和title都不显示。

解决方法:

原来代码:

<td><s:propertyvalue="isbn"/></td>

<td><s:propertyvalue="title"/></td>

原因是isbn和title作为联合主键以后,需要将isbn和title封装成一个类,如题,作为类ItemPK的属性,那么,在页面就需要通过类ItemPK的对象引用。

修改后的代码:

<td><s:propertyvalue="ipk.isbn"/></td>

<td><s:propertyvalue="ipk.title"/></td>

修改后运行,isbn和title成功显示。

问题3:

hibernatesave getSession().save 返回值

解决方法:

看hibernate的API 是返回存储对象时生成的主键 。

5.2.2   实验收获与总结

Hibernate中主键的各种生成策略:

1.       assigned

用于手工分配主键生成器,一旦指定为这个了,Hibernate就不在自动为程序做主键生成器了。没有指定<generator>标签时,默认就是assigned主键的生成方式。

2.       increment

对主键值采取自动顺序增长的方式生成新的主键,值默认从1开始。原理:在当前应用实例中维持一个变量,以保存当前最大值,之后每次需要生成主键值的时候将此值加1作为主键.不依赖于底层的数据库,因此所有的数据库都可以使用。只适用于单个Hibernate应用进程访问访问同一个数据库的情况,在集群环境下不推荐适用他。

3.       hilo

hilo(高低位方式high low)是hibernate中最常用的一种生成方式,需要一张额外的表保存hi的值。保存hi值的表至少有一条记录(只与第一条记录有关),否则会出现错误。可以跨数据库。

4.       seqhilo

与hilo类似,通过hi/lo算法实现的主键生成机制,只是将hilo中的数据表换成了序列sequence,需要数据库中先创建sequence,适用于支持sequence的数据库,如Oracle

5.       sequence

采用数据库提供的sequence机制生成主键,需要数据库支持sequence。如oralce、DB、SAP DB、PostgerSQL、McKoi中的sequence。MySQL这种不支持sequence的数据库则不行(可以使用identity)。

6.       native

native由hibernate根据使用的数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式,灵活性很强。如果能支持identity则使用identity,如果支持sequence则使用sequence。

7.       uuid

UUID使用128位UUID算法生成主键,能够保证网络环境下的主键唯一性,也就能够保证在不同数据库及不同服务器下主键的唯一性。所以使用于所有数据库。

8.       foreign

使用另外一个相关联的对象的主键作为该对象主键。主要用于一对一关系中。

 

Hibernate中对象的三种状态:

1.      瞬态(transient: 不曾进行持久化,未与任何Session相关联

由new命令开辟内存空间的新对象,也就是创建model层的类对象

User user = new User();

瞬态的特点:

a)       对象没有和session关联

b)       在数据库中没有和瞬态对象关联的对象

持久状态:

2.      持久化状态(persistent: 持久的实例在数据库中有对应的几率,并拥有一个持久化标识(identifier)。持久化对象总是与Session和Transaction相关联,在一个Session中,对持久化对象的改变不会马上对数据库进行变更,而必须在Transaction终止,也就是执行commit()之后,才在数据库中真正运行SQL进行变更,持久对象的状态才会与数据库进行同步。在同步之前的持久对象成为脏(dirty)对象

瞬态转换为持久对象:

a)       通过Session的save()和saveOrUpdate()方法把一个瞬时对象与数据库相关联,这个瞬时对象就成为持久化对象。

持久化对象的特点:

a)       和Session实例关联

b)       在数据库中有和持久对象关联的记录

3.      脱管状态(detached:与持久对象关联的Session被关闭后,对象就像变成了脱管对象。对脱管对象的引用依然有效,对象可继续被修改。

托管对象的特点:

a)     本质上和瞬态对象相同

b)     只是比瞬态对象多了一个数据库标志值id

持久对象转换为脱管对象:

当执行close()或者clear(),evict()之后,持久对象变为脱管对象。

瞬态对象转为持久对象

通过Session的update(),saveOrUpadate和lock()等方法,把脱管对象变为持久对象。

游离状态的实例可以通过调用save()persist()或者saveOrUpdate()方法进行持久化。持久化实例可以通过调用 delete()变成游离状态。通过get()load()方法得到的实例都是持久化状态的。游离状态的实例可以通过调用 update()0saveOrUpdate()lock()或者replicate()进行持久化。游离或者自由状态下的实例可以通过调用merge()方法成为一个新的持久化实例。

save()和persist()将会引发SQL的INSERT,delete()会引发SQLDELETE,而update()或merge()会引发SQLUPDATE。对持久化(persistent)实例的修改在刷新提交的时候会被检测到,它也会引起SQLUPDATEsaveOrUpdate()或者replicate()会引发SQLINSERT或者UPDATE。

其具体实现并不一定是线程安全的。每个线程/事务应该从一个SessionFactory获取自己的session实例。

更详细的说明:http://blog.csdn.net/communicate_/article/details/8452786

 

Hibrenate的session的load()和get()方法的区别:

1.      当数据库中不存在对应的ID数据时,get方法返回null,而load方法会抛出一个ObjectNotFoundException异常。

2.      load方法可返回实体的代理类实例,而get方法永远直接返回实体类。

3.      load方法可以充分利用内部缓存和二级缓存中的现有数据,而get方法则仅仅在内部缓存中进行数据查找,如没有发现对应数据,将越过二级缓存,直接调用SQL完成数据读取。

 

一个典型的事务应该使用下面的形式:

 Sessionsess = factory.openSession();

 Transaction tx;

 try {

     tx = sess.beginTransaction();

     //dosome work

     ...

     tx.commit();

 }

 catch(Exception e) {

     if (tx!=null) tx.rollback();

    throw e;

 }

 finally{

    sess.close();

 }

HibernateSession操作的解释:

Method Summary

 Transaction

beginTransaction()

          开始一个工作单元并且返回相关联的事务(Transaction)对象。

 void

cancelQuery()

          终止执行当前查询。

 void

clear()

          完整的清除这个session。

 Connection

close()

          停止这个Session,通过中断JDBC连接并且清空(cleaning up)它。

 Connection

connection()

          获取这个Session的JDBC连接。

 

如果这个session使用了积极的collection释放策略(如CMT-容器控制事务的环境下),关闭这个调用的连接的职责应该由当前应用程序负责。

 boolean

contains(Object object)

          检查这个对象实例是否与当前的Session关联(即是否为Persistent状态)。

 Criteria

createCriteria(Class persistentClass)

          为给定的实体类或它的超类创建一个新的Criteria实例。

 Criteria

createCriteria(Class persistentClass, String alias)

          根据给定的实体类或者它的超类创建一个新的Criteria实例,并赋予它(实体类)一个别名。

 Criteria

createCriteria(String entityName)

          根据给定的实体的名称(name),创建一个新的Criteria实例。

 Criteria

createCriteria(String entityName, String alias)

          根据给定的实体的名称(name),创建一个新的Criteria实例,并赋予它(实体类)一个别名

 Query

createFilter(Object collection, String queryString)

          根据给定的collection和过滤字符串(查询条件)创建一个新的Query实例。

 Query

createQuery(String queryString)

          根据给定的HQL查询条件创建一个新的Query实例。

 SQLQuery

createSQLQuery(String queryString)

          根据给定的SQL查询条件创建一个新的SQLQuery实例。

 void

delete(Object object)

          从数据库中移除持久化(persistent)对象的实例。

 void

delete(String entityName, Object object)

          从数据库中移除持久化(persistent)对象的实例。

 void

disableFilter(String filterName)

          禁用当前session的名称过滤器。

 Connection

disconnect()

          断开Session与当前的JDBC连接。

 Filter

enableFilter(String filterName)

          打开当前session的名称过滤器。

 void

evict(Object object)

          将当前对象实例从session缓存中清除。

 void

flush()

          强制提交刷新(flush)Session。

 Object

get(Class clazz, Serializable id)

          根据给定标识和实体类返回持久化对象的实例,如果没有符合条件的持久化对象实例则返回null。

 Object

get(Class clazz, Serializable id, LockMode lockMode)

          根据给定标识和实体类返回持久化对象的实例,如果没有符合条件的持久化对象实例则返回null。

 Object

get(String entityName, Serializable id)

          返回与给定的实体命名和标识匹配的持久化实例,如果没有对应的持久化实例则返回null。

 Object

get(String entityName, Serializable id, LockMode lockMode)

          返回与给定的实体类和标识所匹配的持久化实例,如果没有对应的持久化实例则返回null。

 CacheMode

getCacheMode()

          得到当前的缓存模式。

 LockMode

getCurrentLockMode(Object object)

          检测给定对象当前的锁定级别。

 Filter

getEnabledFilter(String filterName)

          根据名称获取一个当前允许的过滤器(filter)。

 EntityMode

getEntityMode()

          获取这个session有效的实体模式。

 String

getEntityName(Object object)

          返回一个持久化对象的实体名称。

 FlushMode

getFlushMode()

          获得当前的刷新提交(flush)模式。

 Serializable

getIdentifier(Object object)

          获取给定的实体对象实例在Session的缓存中的标识,如果该实例是自由状态(Transient)的或者与其它Session关联则抛出一个异常。

 Query

getNamedQuery(String queryName)

          从映射文件中根据给定的查询的名称字符串获取一个Query(查询)实例。

 Session

getSession(EntityMode entityMode)

          根据给定的实体模式(Entity Mode)开始一个新的有效的Session。

 SessionFactory

getSessionFactory()

          获取创建这个session的SessionFactory实例。

 SessionStatistics

getStatistics()

          获取这个session的统计信息。

 Transaction

getTransaction()

          获取与这个session关联的Transaction(事务)实例。 instance associated with this session.

 boolean

isConnected()

          检查当前Session是否处于连接状态。

 boolean

isDirty()

          当前Session是否包含需要与数据库同步的(数据状态)变化 ?如果我们刷新提交(flush)这个session是否会有SQL执行?

 boolean

isOpen()

          检查当前Session是否仍然打开。

 Object

load(Class theClass, Serializable id)

          在符合条件的实例存在的情况下,根据给定的实体类和标识返回持久化状态的实例。

 Object

load(Class theClass, Serializable id, LockMode lockMode)

          在符合条件的实例存在的情况下,根据给定的实体类、标识及指定的锁定等级返回持久化状态的实例。

 void

load(Object object, Serializable id)

          将与给定的标示对应的持久化状态(值)复制到给定的自由状态(trasient)实例上。

 Object

load(String entityName, Serializable id)

          在符合条件的实例存在的情况下,根据给定的实体类和标识返回持久化状态的实例。

 Object

load(String entityName, Serializable id, LockMode lockMode)

          在符合条件的实例存在的情况下,根据给定的实体类、标识及指定的锁定等级返回持久化状态的实例。

 void

lock(Object object, LockMode lockMode)

          从给定的对象上获取指定的锁定级别。

 void

lock(String entityName, Object object, LockMode lockMode)

          从给定的对象上获取指定的锁定级别。

 Object

merge(Object object)

          将给定的对象的状态复制到具有相同标识的持久化对象上。

 Object

merge(String entityName, Object object)

          将给定的对象的状态复制到具有相同标识的持久化对象上。

 void

persist(Object object)

          将一个自由状态(transient)的实例持久化。

 void

persist(String entityName, Object object)

          将一个自由状态(transient)的实例持久化。

 void

reconnect()

          不推荐的。 手工的重新连接只应用于应用程序提供连接的情况,在这种情况下或许应该使用reconnect(java.sql.Connection)

 void

reconnect(Connection connection)

          重新连接到给定的JDBC连接。

 void

refresh(Object object)

          从数据库中重新读取给定实例的状态。

 void

refresh(Object object, LockMode lockMode)

          根据指定的锁定模式(LockMode),从数据库中重新读取给定实例的状态。

 void

replicate(Object object, ReplicationMode replicationMode)

          使用当前的标识值持久化给定的游离状态(Transient)的实体。

 void

replicate(String entityName, Object object, ReplicationMode replicationMode)

          使用当前的标识值持久化给定的游离状态(Transient)的实体。

 Serializable

save(Object object)

          首先为给定的自由状态(Transient)的对象(根据配置)生成一个标识并赋值,然后将其持久化。

 Serializable

save(String entityName, Object object)

          首先为给定的自由状态(Transient)的对象(根据配置)生成一个标识并赋值,然后将其持久化。

 void

saveOrUpdate(Object object)

          根据给定的实例的标识属性的值(注:可以指定unsaved-value。一般默认null。)来决定执行 save() 或update()操作。

 void

saveOrUpdate(String entityName, Object object)

          根据给定的实例的标识属性的值(注:可以指定unsaved-value。一般默认null。)来决定执行 save() 或update()操作。

 void

setCacheMode(CacheMode cacheMode)

          设置刷新提交模式。

 void

setFlushMode(FlushMode flushMode)

          设置刷新提交模式。

 void

setReadOnly(Object entity, boolean readOnly)

          将一个未经更改的持久化对象设置为只读模式,或者将一个只读对象标记为可以修改的模式。

 void

update(Object object)

          根据给定的detached(游离状态)对象实例的标识更新对应的持久化实例。

 void

update(String entityName, Object object)

          根据给定的detached(游离状态)对象实例的标识更新对应的持久化实例。

重点:利用Hibernate进行基本的数据库操作

1.        添加操作:Session.saveOrUpdate(Object);若主键在数据库里就修改该条数据,否则保存数据

2.        删除操作:Session.delete(Object);

3.        修改操作:Session.saveOrUpdate(Object);或者Session.update(Object);

4.        查询操作:ObjectSession.get(PO对应的类,主键);

如Studentstu = (Student) session.get(Student.class,"0002");查询学号为 0002 的学生,若该学生不存在,则返回 null

 

savesaveOrUpdateupdatepersist的区别

saveupdate的区别

1.        save的作用是把一个新的对象保存。

2.        update的作用是把一个脱管状态的对象保存。

savesaveOrUpdate的区别

1.        save通过INSERT语句将对象保存到数据库,产生一个新的ID,将数据插入到数据库,并返回一个Serializable对象。

2.        saveOrUpdate能根据对象是否已存在,而执行插入或更新

显然saveOrUpdate更加灵活,但它需要额外判断对象是否已存在

updatesaveOrUpdate的区别

通常在以下的场景会使用到update()或saveOrUpdate():

update():

程序在第一个session中加载对象

该对象被传递到表现层

该对象发生了一点改动

该对象被返回到业务逻辑

程序调用第二个session的update()方法持久这些改动

saveOrUpdate():

a)       如果对象已经在本session中持久化了,不做任何事

b)       如果另一个与本session关联的对象拥有相同的持久化标识(identifier),抛出一个异常

c)        如果对象没有持久化标识(identifier)属性,对其调用save()

d)       如果对象的持久标识(identifier)表明其是一个新实例化的对象,对其调用save()

e)        如果对象是附带版本信息的(通过<version>或<timestamp>) 并且版本属性的值表明其是一个新实例化的对象,save()它。

f)         否则update() 这个对象

savepersist的区别

  1. 返回类型不同:save返回Serializable对象,而persist返回void
  2. ID赋值时机不同:二者同样用于将transient实例持久化,但persist不保证ID值立即赋给持久化实例,可能会在flush的时候给ID赋值。
  3. transaction外的行为不同:如果在transaction之外调用,persist保证会立即执行INSERT语句;而save则不保证(save返回一个identifier,如果必须执行INSERT来获取该identifier,则就会立即执行INSERT,而不论是在transaction之内或之外)
  4. 使用场景:由于上述第三点区别,persist方法适用于被扩展的Session上下文的长期运行的会话中(useful in long-running conversations with an extended Session context);而save则不适用。

 

flushupdate区别

1.        update操作的是在脱管状态的对象

2.        而flush是操作的在持久状态的对象。

3.        默认情况下,一个持久状态的对象是不需要update的,只要你更改了对象的值,等待hibernate flush就自动保存到数据库了。

4.        hibernate flush发生再几种情况下:

a)       调用某些查询的时候

b)       transaction commit的时候

c)        手动调用flush的时候

 

session.save(user);

作用:利用Hibernate将实体类对象保存到数据库中。

过程;

session.flush()方法的作用其实就是让session的缓存的数据(session就是一级缓存)刷入到数据库里面去,让数据库同步,你可以更简单的理解就是,强制让session的数据和数据库的数据同步,在使用flush方法一般之前都是对一个对象进行CRUD的操作,然后你调用flush方法,就及时的同步到数据库里面去,其实session.flush()方法用的最好的一块是在处理大量数据的时候我们可以控制数量,比如,我们要存储1万个对象,我们可以这样做。

 

session.evict(obj) :会把指定的缓冲对象进行清除, s.evict将对象从 s会话中拆离,这时 s会从 entityEntries中将这个对象移出。

 

session.clear() :把缓冲区内的全部对象清除,但不包括操作中的对象。

 

inverse: 表示“是否放弃维护关联关系”(在Java里两个对象产生关联时,对数据库中表的影响),hibernate的缺省值为false。在<one-to-many><many-to-many>关联关系的集合定义中使用,inverse="true"表示该对象不维护关联关系。该属性的值在使用有序集合时(list,array等)时一般设置为false.<one-to-many>维护关联关系就是更新外键;<many-to-many>维护关联关系就是在中间表中增减记录!

 

cascade inverse有什么区别?  可以这样理解,cascade定义的是关系两端对象到对象的级联关系;而inverse定义的是关系和对象的级联关系。

https://my.oschina.net/u/943923/blog/137397

 

设置复合主键的方法和步骤

1.      编写复合主键类,并增加setter和getter方法。

2.      改写逆向工程生成的类,将主键对象作为属性之一。

3.      改写配置文件,将主键类中每个属性和表中的列对应,并指定复合主键的类型

<composite-idname="ipk"class="dao.ItemPK">

         <key-propertyname="isbn"column="ISBN"></key-property>                          <key-propertyname="title"column="title"></key-property>

</composite-id>

4.      使用复合主键操作数据库

Session session = util.HibernateSessionFactory.getSession();

StudentPK spk = new StudentPK();

spk.setStuno("0001");

spk.setStuname("张三");

Student stu =(Student)session.get(Student.class, spk);

if(stu!=null){

       System.out.println(stu.getStusex());

}

util.HibernateSessionFactory.closeSession();

5.3     扩展实验——粒度设计

5.3.1   实验所遇到的问题及解决方案【重点】

问题1;

做hibernate的用户注册,运行时出现以下问题。

严重: Exceptionoccurred during processing request: Unable to instantiate default tuplizer[org.hibernate.tuple.component.PojoComponentTuplizer]

org.hibernate.HibernateException: Unable toinstantiate default tuplizer[org.hibernate.tuple.component.PojoComponentTuplizer]

         atorg.hibernate.tuple.component.ComponentTuplizerFactory.constructTuplizer(ComponentTuplizerFactory.java:101)

         atorg.hibernate.tuple.component.ComponentTuplizerFactory.constructDefaultTuplizer(ComponentTuplizerFactory.java:122)

         atorg.hibernate.tuple.component.ComponentMetamodel.<init>(ComponentMetamodel.java:81)

         atorg.hibernate.mapping.Component.getType(Component.java:180)

         atorg.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:310)

         atorg.hibernate.mapping.Property.isValid(Property.java:241)

         atorg.hibernate.mapping.PersistentClass.validate(PersistentClass.java:496)

         atorg.hibernate.mapping.RootClass.validate(RootClass.java:270)

         atorg.hibernate.cfg.Configuration.validate(Configuration.java:1358)

解决方法:

org.hibernate.tuple.component.PojoComponentTuplizer可以看出,应该是配置文件的compoent出错,查看后,发现compoent的类写错:

原来的代码:

<component class=" cn.edu.zjut.po.ContactInfo"name="contactInfo">

修改后的代码:

<component class="dao.ContactInfo"name="contactInfo">

问题2;

还是做用户登录时的问题。错误如图:

解决方法:

说明是save时出错。错误提示,ids for this class must bemanually assigned before calling save(): dao.Customer,说明customer在保存时未被赋值。

问题3:

插入到数据库的的中文出现乱码,如图:

解决方法:

在hibernate.cfg.xml中的连接配置添加:

useUnicode=true&amp;characterEncoding=UTF-8

代码:

<propertyname="connection.url">

         jdbc:mysql://127.0.0.1:3306/hibernatedb?useUnicode=true&amp;characterEncoding=UTF-8

</property>

问题4:

选择日期的显示有问题,月份总是显示00,但插入到数据库中时没有问题的。

解决方法:

原来是格式输入错误。

原来的代码:

<sx:datetimepickername="loginUser.birthday"language="zh_CN"

                   type="date"displayFormat="yyyy-mm-dd"label="请输入生日"/>

现在的代码:

<sx:datetimepickername="loginUser.birthday"language="zh_CN"

                   type="date"displayFormat="yyyy-MM-dd"label="请输入生日"/>

问题5:

不管在注册的jsp页面中输入的是男还是女,最后注册成功都只显示女。数据库中也都是0,代表女生。

解决方法:

原来的代码:

<s:radioname="loginUser.sex"list="#{'1':'男','0':'女' }"label="请输入性别"></s:radio>

现在的代码:

<s:radioname="loginUser.sex"list="#{'true':'男','false':'女' }"label="请输入性别"></s:radio>

修改了list的key值。

问题6:

做基于性能的粒度设计的题,即将一个类拆分成两个类,ItemBasic和ItemDetail类,在做具体查询的时候,我使用的是ItemBasic类,查询结果是没有错的,但查询过程出现了一点问题,如图,hibernate进行了两次查询,与原本想要提高查询效率而进行粒度设计的初衷不符。所以,我想可能是哪里出了问题。后来看结果页面,查询结果多了一倍。

解决方法:

该问题应该是系统本身的问题,在刷新一次数据库后,自动解决。

解决后的JSP页面显示:

5.3.2   实验收获与总结

在实际应用中,并不都是一张表与一个实体类映射,往往可能会有一张表跟多个实体类映射的情况,称为粒度设计。

1.      若表中的某些字段联合起来能表示持久化类中的某一个属性,那么可以进行基于设计的粒度设计,将表跟多个类映射;类和类之间使用关联关系;只需要一个映射文件,使用component元素进行映射。

<componentclass="dao.ContactInfo"name="contactInfo">

                            <propertygenerated="never"lazy="false"name="zipcode"

                                     type="java.lang.String">

                                     <columnlength="10"name="Zipcode"/>

                            </property>

                            <propertygenerated="never"lazy="false"name="fax"

                                     type="java.lang.String">

                                     <columnlength="20"name="Fax"/>

                            </property>

</component>

2.      若表中的某些字段不经常使用,而且占有空间较大,则可以使用基于性能的粒度设计,一个表可以映射为多个类;每个类对应一个*.hbm.xml文件根据实际情况,使用不同的类。

Hibernate配置文件hibernate.cfg.xml中的“connection.autocommit”属性的作用

为JDBC连接池重点连接开启自动提交(不推荐开启此选项),可选值:true、false

指定Hibernate在何时释放JDBC连接. 默认情况下,直到Session被显式关闭或被断开连接时,才会释放JDBC连接. 对于应用程序服务器的JTA数据源, 你应当使用after_statement, 这样在每次JDBC调用后,都会主动的释放连接. 对于非JTA的连接, 使用after_transaction在每个事务结束时释放连接是合理的. auto将为JTA和CMT事务策略选择after_statement, 为JDBC事务策略选择after_transaction.

修改Myeclipse的JSP页面的默认打开方式:

依次点击:

[MyEclipse]-> [Preferences] ->[General] -> [Editors] -> [File Associations] ,找到*.jsp,选择[MyEclipse JSP Editor,点击default,然后点击[OK]按钮即可

6        实验六:Hibernate的体系结构——登录用户信息的增删改查

6.1     基础实验——Hibernate常用API

6.1.1   实验所遇到的问题及解决方案【重点】

问题1:

在写struts的全局化结果时,出现以下错误。

The content of element type "package" must match "(result-types?,interceptors?,default-interceptor-

 ref?,default-action-ref?,default-class-ref?,global-results?,global-exception-mappings?,action*)".

我的代码:

<global-result>

         <resultname="fail">/CURDFail.jsp</result>

</global-result>

问题解决:

该问题的意思是说:

package里元素必须按照一定的顺序排列:

result-types

interceptors

default-interceptor-ref

default-action-ref

default-class-ref

global-results

global-exception-mappings

action*(所有action放到最后)

但是我的struts.xml里只有global-result,排查后发现原来是标签写错了。result少了一个‘s’,修改后问题解决。

修改后的代码:

<global-results>

         <resultname="fail">/CURDFail.jsp</result>

</global-results>

记录该问题的原因是解决该问题的另一个方法带来的知识点比较重要。

问题2:

写好了所有的代码,运行register.jsp时发现以下的错误:

严重: Dispatcherinitialization failed

Unable to load configuration. -action –

file:/C:/SoftwareInstall/apache-tomcat-8.0.3/webapps/hibernate-prj2/WEB-INF/classes/struts.xml:10:66

问题解决:

从控制台的错误提示上可以看出,是struts.xml的第10行出现问题。查看struts的第10行代码,发现包地址写错,导致框架找不到这个文件。

原代码:

<actionname="Userlogin"class="com.UserAction"method="login">

         <resultname="success">/loginSuccess.jsp</result>

         <resultname="fail">/login.jsp</result>

</action>

修改后的代码:

<actionname="Userlogin"class="dao.UserAction"method="login">

         <resultname="success">/loginSuccess.jsp</result>

         <resultname="fail">/login.jsp</result>

</action>

问题3

调试的时候代码乱跳,调试A工程的时候代码调到B工程,点会到A工程的正在运行的文件,点击下一步的时候,又调到B文件。

问题解决:

关闭B工程,右键B工程,点击close,再次调试的时候问题解决。

问题4:

在测试代码的修改用户密码的功能的时候,出现错误,页面总是显示修改不成功。

问题解决:

原代码:

Customer customertemp = (Customer)dao.getSession().

load(Customer.class, loginUser.getCustomerId());

修改后的代码:

Customer customertemp = (Customer)dao.getSession().

get(Customer.class, loginUser.getCustomerId());

将load(Customer.class,loginUser.getCustomerId())改为get(Customer.class,loginUser.getCustomerId())。

6.1.2   实验收获与总结

1.        做这一题,最主要的学习到了Hibernate的session和java web的session是没有什么关系的,作用和功能都不一样。Hibernate的session用来执行对象的增改查等与数据库相关的操作,而java的session是一个会话,可以将数据保存在会话中。

2.        关于dao包和service包的分层理解:

dao包执行有关数据库操作的工作。service是调用dao包的方法的,至于为什么要分dao包和service包,首先,比如在保存一个对象之前,可能要对对象的内容做些修改,那么这些修改的内容将在service包中操作,或者,有时候要执行查询操作,用户只是提供查询的关键字,而具体的HQL语言是要程序员来写的,那么这时候HQL语言将在service中编写,写好后再调用dao包的查询方法,dao包和service包很好的将具体的数据库操作和数据处理操作分开。

6.2     提高实验——HQL语言

6.2.1   实验所遇到的问题及解决方案【重点】

本次实验没有遇到任何问题,但通过这次实验,练习了如何使用HQL语言进行查询。

6.2.2   实验收获与总结

HQL语句语法

from子句

from 类名 [as  对象名] [ where 属性条件 ] as 对象名

示例代码:

Session session =util.HibernateSessionFactory.getSession();

String hql = "from Student where stusex=''";

Query query = session.createQuery(hql);

List list = query.list();

for(int i=0;i<list.size();i++){

       Studentstu = (Student)list.get(i);

       System.out.println(stu.getStuname());

}

util.HibernateSessionFactory.closeSession();

select子句

select 属性 from 类名 as 对象名 [ where属性条件 ]

示例代码:

Session session =util.HibernateSessionFactory.getSession();

String hql = "select stuno, stuname from Student where stusex=''";

Query query = session.createQuery(hql);

List list = query.list();

for(int i=0;i<list.size();i++){

       Object[]objs = (Object[])list.get(i);

       System.out.println(objs[0]+ " " + objs[1]);

}

util.HibernateSessionFactory.closeSession();

带参数的HQL语句

Session session =util.HibernateSessionFactory.getSession();

String sex = "";

String hql = "select stuno,stuname from Student wherestusex=:sex";

Query query = session.createQuery(hql);

query.setString("sex", sex);

List list = query.list();

for(int i=0;i<list.size();i++){

       Object[]objs = (Object[])list.get(i);

       System.out.println(objs[0]+ " " + objs[1]);

}

util.HibernateSessionFactory.closeSession();

where子句

select from User where userDesc like ‘%李’;

order by子句

select from User where userDesc like ‘%李’ order by age asc;

聚合函数

select count(*) from user

group by子句

select sum(*) from student group by name

6.3     扩展实验——深入Hibernate配置文件

6.3.1   实验所遇到的问题及解决方案【重点】

问题1

加入hibernate数据库连接池后,原本可以运行的运行程序出错。

出错信息:

The content of element type"session-factory" must match "(property*,mapping*,(class-cache|

 collection-cache)*,event*,listener*)".

解决方法:

将数据库配置信息放到<mappingresource="dao/Customer.hbm.xml" />句子上面。

6.3.2   实验收获与总结

hibernate数据库连接池配置方法:

在hibernate.cfg.xml中加入以下代码:

<propertyname="hibernate.use_sql_comments">true</property>

<propertyname="hibernate.c3p0.max_size">20</property>

<propertyname="hibernate.c3p0.min_size">1</property>

<propertyname="hibernate.c3p0.timeout">1800</property>

<property name="hibernate.c3p0.max_statements">50</property>

7        实验七:Hibernate关联关系映射——登录用户的地址管理

7.1     基础实验——多对一/一对多关联

7.1.1   实验所遇到的问题及解决方案【重点】

问题1

hibernate的级联关系默认是none,也就是不级联,假如mony-to-one的在save mony的对象时,one的对象没有在数据库中,会报错,错误如下:

Exception in thread "main" org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transientinstance before flushing: dao.Customer

    atorg.hibernate.engine.internal.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:294)

    atorg.hibernate.type.EntityType.getIdentifier(EntityType.java:537)

    atorg.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:311)

    atorg.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:321)

问题2

save操作在事物以外无法保存到数据库中,必须要在事物以内。

以下红色的代码去掉就不能保存到数据库中。

public static void main(String[] strg)

    {

       Session session =HibernateSessionFactory.getSession();

       Transaction tr = session.beginTransaction();

       Customer customer = new Customer();

    //  customer = (Customer)session.get(Customer.class, 13);

        customer.setAccount("123");

       customer.setName("zhufaming爸爸");

       Address address = new Address();

       address.setPhone("13858012624");

//     address.setCustomer(customer);

       session.save(address);

       tr.commit();

    }

问题解决:

Hibernate中对数据库的session操作除了查询操作外,都必须在事务(Transaction)提交后才能执行。

问题3

今天打开hibernate要运行工程时 ,报错,错误的提示为:

问题解决:

这句话的意思是选定的服务器已启用,但是没有配置properly.deployment它不会允许的;

解决方法:

开发工具下,以MyEclipse为例

preferences-> Myeclipse -> server -> tomcat -> tomcat 7.X-> jdk  选择你安装的jdk,就可以了。

一个较好的问题4

Exception in thread "main" org.hibernate.TransientObjectException: object references an unsaved transient ins

问题解决:

出现上述错误的原因是hibernate在清理缓存的时候,发现缓存中的对象数据和数据库中的数据不一致,而hibernate无法自动持久化对象,所以会报TransientObjectException异常。

问题5

运行程序报错,出现以下问题:

Exception occurred during processing request: dao.Customer_$$_javassist_1cannot be cast to javassist.util.proxy.Proxy

java.lang.ClassCastException:dao.Customer_$$_javassist_1 cannot be cast to javassist.util.proxy.Proxy

问题解决:

loginUser = (Customer)c_dao.getSession().load(Customer.class, loginUser.getCustomerId());

将上面的代码修改为:

loginUser = (Customer) c_dao.getSession().get(Customer.class,loginUser.getCustomerId());

 

7.1.2   实验收获与总结

在关系数据库中,只存在外键参照关系,而且总是由many关照one因为这样才能消除数据冗余关系数据库实际上只支持多对一或一对一的单向关联。

一般对many-to-one,many-to-many不设置级联,在<one-to-one><one-to-many>中设置级联。因为会产生冗余。

cascade的默认值是none。可选 sava-update,all,delete,none

1.        单向多对一关联

private Customer customer;

将多端的外键属性改为对象。

<many-to-onename="customer"class="dao.Customer"fetch="select">

   <columnname="cust_id"/>

</many-to-one>

因为属性customer的类型是Customer类型,而外键cust_id的类型是int类型,他们的类型不匹配,所以不能用<property>元素来映射,使用<many-to-one>来映射。

a)       many-to-one中的name代表的是类中的属性。

b)       many-to-one中的class代表的是待映射的持久化类的属性customer的类型

c)        column中的name代表的是当前配置文件代表的关联表的外键

d)       many-to-one中的not-null 如果为true,表示customer不能为空,默认是false

e)        lazy如果是proxy,表示使用延迟搜索并使用代理,是默认值,如果是false,代表使用立即搜索,如果是true,代表使用延迟搜索。

以多对一为例

fetch ="select"是在查询的时候先查询出一端的实体,然后在根据一端的查询出多端的实体,会产生1+n条sql语句;

fetch ="join"是在查询的时候使用外连接进行查询,不会差生1+n的现象。

2.        单向一对多关联

private Set addresses = new HashSet(0);

在一端的类中添加多端的Set集合。

<setname="addresses"inverse="true">

   <key>

      <columnname="cust_id"/>

   </key>

       <one-to-manyclass="dao.Address"/>

</set>

由于在Customer表中没有与addresses对应的字段,所以不能用<property>字段,而是使用set字段。

a)       set中的name代表的是待映射的持久化类Customer的属性名addresses

b)       key元素设定与所关联的持久化类对应的外键,即Address的外键cust_id

c)        one-to-many中的class代表的是关联的持久化类Address

d)       set中的inversetrue的时候,不能通过customeraddresses属性添加或修改外键属性。

set中的inversefalse的时候,可以通过customeraddresses属性添加或修改外键属性。

总之,inverse是用来维护关联关系的,当设为true的时候,表示待映射的持久化类不维护关联关系,当设为false时,表示待映射的持久化类负责维护关联关系。

在映射一对多的时候,应该在one方把set元素的inverse设为true,可以提高程序的性能。

在建立两个对象的双向关联时,应该同时修改关联两端的对象的相应属性。

比如:

address.setCustomer(customer);//一方

customer.getAddresses().add(address);//多方

可以提高程序的健壮性。

同样,解除关联关系的时候也应当修改关联两端的对象的相应属性。

customer1.getAddresses().remove(address1);  //多方

address1.setCustomer(null); //一方

1.name:映射类属性的名字

2.column:关联的字段

3.class:关联类的名字

4.cascade:设置操作中的级联策略 可选值为 all所有操作情况均进行级联、none所有操作情况均不进行级联、save-update执行save和update操作时级联、delete执行删除操作时级联

5.fetch:设置抓取数据的策略 默认值为select序列选择抓取 可选值为join外连接抓取

6.lazy   (可选 - 默认为  proxy ): 默认情况下,单点关联是经过代理的;lazy="true" 指定此属性应该在实例变量第一次被访问时应该延迟抓取(fetche lazily);lazy="false" 指定此关联总是被预先抓取。

7.2     提高实验——多对多关联

7.2.1   实验所遇到的问题及解决方案【重点】

问题1

完成多对多的对象映射配置后,运行程序,出现以下错误。

严重: find by hql failed

org.hibernate.exception.SQLGrammarException: could not extract ResultSet

    atorg.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:80)

    atorg.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)

    atorg.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126)

    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:112)

问题解决:

could not extract ResultSet,可以看出,问题 出现的原因是结果集为空,由于做的是多对多关联,在添加关联表join后,没有在里面添加数据,所以不能搜索出任何东西,在join里添加东西后,问题解决。

问题2

上一个问题解决后,再运行程序。页面显示有问题,如:下面的地址显示不全。

问题解决:

<class name="dao.Address"table="address" catalog="hibernatedb">

table错误,应该为:

<class name="dao.Address"table="address2" catalog="hibernatedb">

修改后问题解决,如图:

问题3

完成地址删除功能后,运行程序,点击页面的删除按钮,不起作用。

问题解决:

原来的代码直接删除了函数参数中的对象,而不是从数据库中get得到的对象,hibernate认为两个对象是不一样的,虽然两个对象的内容完全一样。所以以后在删除时,最好先从数据库里获取这个对象。

原来的代码:

loginUser.getAddresses().remove(address);// 删除地址关联。

现在的代码:

Address addressnew = (Address)c_dao.getSession().load(Address.class, address.getAddressId());

loginUser.getAddresses().remove(addressnew);// 删除地址关联。

7.2.2   实验收获与总结

双向多对多关联

hibernate的多对多关系配置步骤:

1.        首先,在数据库中建立多对多关联的两张表,并建立关联表。

2.        为有多对多关联的两张表建立持久化类

3.        在两个类中添加set集合

4.        修改持久化类的配置文件。如下:

<setname="addresses"inverse="false"table="cus_addre2"cascade="all"catalog="hibernatedb"lazy="false">

    <key>

       <columnname="cust_id"/>

    </key>

        <many-to-manycolumn="addr_id"class="dao.Address"/>

</set>

a)       set中的name代表的是待映射的持久化配置文件的类的属性。

b)       set中的table代表的是关联表cus_addre2

c)        set中的catalog代表的是所作操作的数据库。

d)       key中的name代表的是关联表cus_addre2的外键

e)        many-to-many中的column代表的是

f)         many-to-many中的class代表的是待映射持久化配置文件的类的属性的类型

 

到底在哪用inverse="ture"? 

inverse属性默认是false的,就是说关系的两端都来维护关系。这个意思就是说,如有一个Student, Teacher和TeacherStudent表,Student和Teacher是多对多对多关系,这个关系由TeacherStudent这个表来表现。那么什么时候插入或删除TeacherStudent表中的记录来维护关系呢?在用hibernate时,我们不会显示的对TeacherStudent表做操作TeacherStudent的操作是hibernate帮我们做的。hibernate就是看hbm文件中指定的是""维护关系,那个在插入或删除""时,就会触发对关系表的操作。前提是"谁"这个对象已经知道这个关系了,就是说关系另一头的对象已经set或是add到"谁"这个对象里来了。前面说过inverse默认是false,就是关系的两端都维护关系,对其中任一个操作都会处发对表系表的操作。当在关系的一头,如Student中的bag或set中用了inverse="true"时,那就代表关系是由另一关维护的(Teacher)。就是说当这插入Student时,不会操作TeacherStudent表,即使Student已经知道了关系。只有当Teacher插入或删除时才会处发对关系表的操作。所以,当关系的两头都用inverse="true"是不对的,就会导致任何操作都不处发对关系表的操作。当两端都是inverse="false"或是default值是,在代码对关系显示的维护也是不对的,会导致在关系表中插入两次关系。 

在一对多关系中inverse就更有意义了。在多对多中,在哪端inverse="true"效果差不多(在效率上)。但是在一对多中,如果要一方维护关系,就会使在插入或是删除"一"方时去update"多"方的每一个与这个"一"的对象有关系的对象。而如果让"多"方面维护关系时就不会有update操作,因为关系就是在多方的对象中的,直指插入或是删除多方对象就行了。当然这时也要遍历"多"方的每一个对象显示的操作修关系的变化体现到DB中。不管怎样说,还是让"多"方维护关系更直观一些。 

 

cascadeinverse有什么区别? 

可以这样理解,cascade定义的是关系两端对象到对象的级联关系;而inverse定义的是关系和对象的级联关系。

7.3     扩展实验——一对一关联

7.3.1   实验所遇到的问题及解决方案【重点】

问题1

配置好一对一关联以后运行程序,出现以下问题:

严重: find by hql failed

org.hibernate.exception.SQLGrammarException: could not extract ResultSet

    at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:80)

    atorg.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)

    atorg.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126)

问题解决:

该问题的报错原因是查找出来的address的结果集为空。修改代码:当为空时返回null。

问题2

一对一关联并没有关联到,但数据是保存进去了。

问题解决:

一对一关联关系的维护总是由有外键的类维护的,在添加地址的时候,我让没有外键的类维护关联关系。所以虽然数据可以保存,但是关联没有保存。

原来的代码:

loginUser = (Customer) c_dao.getSession().get(Customer.class,

loginUser.getCustomerId());

loginUser.setAddress(address);

修改后的代码:

c_dao.getSession().save(address);

address.setCustomer(loginUser);

c_dao.getSession().update(address);

问题3

一对一关联关系删除用户的地址出错,调用系统的session的delete方法删除用户地址,调试后发现执行这段代码出异常。

问题解决:

后来尝试先用户的address置为空,调用系统的session的delete方法删除用户地址,就成功了。代码如下:

loginUser = (Customer) c_dao.findById(loginUser.getCustomerId());

address = loginUser.getAddress();

loginUser.setAddress(null);

c_dao.getSession().delete(address);

问题4

一对一地址保存后跳转的页面不对,确切的说是request中的address为空。

问题解决:

原来的代码:

c_dao.getSession().save(address);

address.setCustomer(loginUser);

c_dao.getSession().update(address);

tran.commit();

request.put("loginUser", loginUser);

保存在request中的loginUser的address为空。

修改后的代码:

loginUser = (Customer) c_dao.getSession().get(Customer.class,

                  loginUser.getCustomerId());

c_dao.getSession().save(address);

loginUser.setAddress(address);

address.setCustomer(loginUser);

c_dao.getSession().update(address);

tran.commit();

修改后页面显示正常

7.3.2   实验收获与总结

双向一对一的关联关系映射文件配置外键关联

many-to-one总是设成“inverse=false”,所以,总是由有外键的对象维护关联关系。配置成one-to-one的对象不维护关联关系。

<many-to-onename="customer"class="dao.Customer"fetch="select"

           unique="true">

<columnname="cust_id"></column>

</many-to-one>

a)       many-to-one中的name表示的是待映射的持久化类的属性

b)       many-to-one中的class表示的是待映射的持久化类的属性的类型

c)        many-to-one中的unique表示的是待映射的持久化类是唯一的。

d)       column中的name表示的是表的外键字段。

<one-to-onename="address"class="dao.Address"cascade="all"

    property-ref="customer">

</one-to-one>

one-to-one中的name表示的是待映射的持久化类的属性。

one-to-one中的class表示的是待映射的持久化类的属性的类型。

one-to-one中的property-ref指定关联类的属性名,这个属性将会和本类的主键相对应。如果没有指定,会使用对方关联类的主键。property-ref,不是数据库表中的字段名,而是定义的java类中的属性名,一定要注意

 

双向一对一的关联关系映射文件配置方法主键关联

<one-to-one name="address" class="dao.Address"/>

 

<idname="addressId"type="int">

       <columnname="addressID"/>

       <generatorclass="foreign">

           <paramname="property">customer</param>         </generator>

</id>

 

<one-to-onename="customer"class="dao.Customer"cascade="all"

           constrained="true"/>

8        实验八:SSH整合(Spring4+Struts2+Hibernate4)——基于SSH的用户注册模块块

8.1     基础实验——Spring框架搭建

8.1.1   实验所遇到的问题及解决方案【重点】

该实验比较简单,在做的过程中没有遇到问题,但通过做这个实验,对spring的控制反转有了比较好的理解。就是将创建哪个具体对象的控制权交到spring的xml文件中。

8.1.2   实验收获与总结

spring的功能模块

1.        Spring Core模块:提供控制反转(IoC)容器,是Spring框架的核心机制,其它特性都基于IoC之上;

2.        Spring Context模块:提供对Spring中对象的框架式访问方式;

3.        Spring DAO模块:提供了集成JDBC的封装包;

4.        Spring ORM模块:提供了集成常用ORM框架的封装包;

5.        Spring Web模块:提供了Web开发以及集成Web框架的封装包;

6.        Spring AOP模块:提供了面向切面编程(AOP)的实现;

7.        Spring MVC模块:提供了一个MVC框架;

 

Spring Bean对象创建时机:默认是随容器创建,通过lazy-init可以改变对象实例化时机

1.        默认lazy-init=default||false:

当spring容器实例化的时候,并把容器中对象全部完成实例化

<beanid="bean" class="com.tarena.entity.Bean"  lazy-init="false"/>

2.        lazy-init="true"

当从spring容器中获取对象时候在对对象实例始化

3.        设置全局default-lazy-init="true"

整个配置文件中对象都实例化延迟     

<beansdefault-lazy-init="true"></beans>

注意:在使用定时器的时候,不能使用lazy-init="true"

spring创建对象的时间由对象在配置文件中的位置决定。

 

Bean对象的创建模式

1.        singleton:spring容器对象默认是单例模式每次只成一个实例。

<bean id=""class="" scope="singleton"/>    

2.        prototype:多例,spring容器会每次都为对象产生一个新实例。

    <bean id="" class=""scope="prototype"/>    

scope:在web开发中使用request,session.

 

Bean对象初始化和销毁

1.        在spring配置文件定义销毁方法和初始化方法

<beaninit-method="init" destroy-method="destroy">

2.        在Bean 对象中定义销毁方法和初始化方法

public void init(){}

public void destroy(){}

3.        spring容器自动调用销毁方法和初始化方法

 

<bean>元素的id属性和 name属性的区别

一般情况下,配置一个Bean时,通过指定一个id属性作为Bean的名称.

1.        id 属性在IoC容器中必须是唯一的

2.        id 的命名要满足XML对ID属性命名规范:

必须以字母开始,可以使用字母、数字、连字符、下划线、句话、冒号

3.        使用name属性,就可以使用很多特殊字符,早期在struts1 和 spring整合<beanname="/login" class="....LoginAction" /> name中含有/ ,使用id会报错

4.        如果Bean的名称中含有特殊字符,就需要使用name属性

例如:<beanname="#person" class="cn.itcast.bean.Person"/>

5.        因为name属性可以相同,所以后出现Bean会覆盖之前出现的同名的Bean,xml的解析是从上到下的,所以先配置的bean会被加载到内存中,那么后配置的具有相同namebean就会覆盖前面的.

6.        如果bean元素没有id只有name ,name 属性值可以作为id 使用

 

bean元素scope属性配置Bean的作用域

<bean>元素scope属性,在spring规范中scope属性有五个取值:

1.        scope="singleton" 单例,在Spring IoC容器中仅存在一个Bean实例(默认的scope)

默认情况下:托管给spring默认在spring容器中只有一个Bean实例对象.

2.        scope="prototype" 多例,每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean()

3.        scope="request" 用于web开发,该作用域仅适用于WebApplicationContext环境.

每次HTTP请求都会创建一个新的Bean,Bean放入request范围,request.setAttribute("xxx"),在同一个request获得同一个Bean

4.        scope="session" 用于web开发, 该作用域仅适用于WebApplicationContext环境.

同一个HTTP Session 共享一个Bean不同Session使用不同Bean,Bean放入Session范围

5.        scope="globalSession"该作用域仅适用于WebApplicationContext环境.

一般用于Porlet应用环境 , 分布式系统存在全局session概念,如果不是porlet环境,globalSession 等同于Session . Porlet是Servlet的一个升级,Porlet主要用于分布式系统.

在开发中主要使用scope="singleton" scope="prototype"

 

bean标签的属性

1.        scope:用来配置springbean的作用域

2.        singleton:表示bean为单例的

3.        abstract:设置为true,将该bean仅仅作为模板使用,应用程序上下文不会试图预先初始化它

4.        lazy-init:设为true,延迟加载,该bean不会在ApplicationContext启动时提前被实例化,而是第一次向容器通过getBean索取bean时实例化

注:只对singleton的bean起作用

5.        autowire:自动装配

6.        dependency-check:依赖检查

7.        depends-on:表示一个bean的实例化依靠另一个bean先实例化

8.        autowire-candidate:设为false,容器在查找自动装配对象时,将不考虑该bean,即它不会被考虑作为其他bean自动装配的候选者,但是该bean本身可以使用自动装配来注入其他bean

9.        primary:该bean优先被注入

10.     init-method:初始化bean时调用的方法

11.     destory-method:容器销毁之前所调用的方法

12.     factory-method:当调用factory-method所指向的方法时,才开始实例化bean

13.     factory-bean:调用静态工厂方法的方式创建bean

 

bean的子元素

1.        meta:元数据,当需要使用里面的信息时可以通过key获取

2.        lookup-method:获取器注入,是把一个方法声明为返回某种类型的bean但实际要返回的bean是在配置文件里面配置的

3.        replaced-method:可以在运行时调用新的方法替换现有的方法,还能动态的更新原有方法的逻辑

4.        constructor-arg:对bean自动寻找对应的构造函数,并在初始化的时候将设置的参数传入进去

5.        property:基本数据类型赋值

6.        qualifier:通过Qualifier指定注入bean的名称

 

spring在项目中可以带来下面的好处

1.        降低组件之间的耦合度,实现软件各层之间的解耦。

Controller——>Service——>DAO

2.        可以使用容器提供的众多服务,如:事务管理服务、消息服务等等。当我们使用容器管理事务时,开发人员就不再需要手工控制事务.也不需处理复杂的事务传播。

3.        容器提供单例模式支持,开发人员不再需要自己编写实现代码。

4.        容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能。

5.        容器提供的众多辅作类,使用这些类能够加快应用的开发,如: JdbcTemplate、 HibernateTemplate。

6.        Spring对于主流的应用框架提供了集成支持,如:集成Hibernate、JPA、Struts等,这样更便于应用的开发。

7.        使用spring容器可以提供的服务:事务管理服务,JMS服务,Spring Core核心服务,持久化服务等。

8.        如果使用Spring, 我们就不再需要手工控制事务,例如在Hibernate中控制事务的语句 session.beginTransaction();session.getTransaction().commit();

9.        使用Spring,不再需要我们处理复杂的事务传播行为。

8.2     提高实验——Spring与Hibernate的整合

8.2.1   实验所遇到的问题及解决方案【重点】

问题1

做这个实验最大的困难是spring和hibernate包的管理,在单独做spring和hibernate工程的时候,包和包之间没有冲突,但是和在一起就有问题了,后来在网上找了一个ssh整体的包,加入工程,错误就自动消除了。

问题2

自己写了一个测试类,测试使用spring管理对象,将对象的内容插入到数据库中。

Exception in thread "main"

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerDao' defined inclass path resource [applicationContext.xml]: Error setting property values;nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'sessionFactory' of bean class[cn.edu.zjut.pojo.CustomerDAO]: Bean property 'sessionFactory' is not writableor has an invalid setter method. Does the parameter type of the setter matchthe return type of the getter?

解决方法:

出错的原因是SessionFactory未在applicationContextxml中配置。配置后问题解决。

问题3

自己写了一个测试类,测试使用spring管理对象,将对象的内容插入到数据库中。

Failed to convert property value of type[org.hibernate.impl.SessionFactoryImpl] to required type[org.springframework.context.ApplicationContext] for property 'sessionFactory';nested exception isjava.lang.IllegalArgumentException: Cannot convert value of type[org.hibernate.impl.SessionFactoryImpl] to required type[org.springframework.context.ApplicationContext] for property 'sessionFactory':no matching editors or conversion strategy found

解决方法:

原来的代码写错了,后修改如下:

SessionFactory sessionFactory =null;

    public SessionFactorygetSessionFactory() {

       returnsessionFactory;

    }

    public void setSessionFactory(SessionFactorysessionFactory) {

       this.sessionFactory = sessionFactory;

    }

    public void save(Customer transientInstance) {

       Transaction tran = null;

       Session session = null;

       try {

           session = (Session) sessionFactory.openSession();

           tran = session.beginTransaction();

           session.save(transientInstance);

           tran.commit();

       } catch (RuntimeException re) {

           if(tran !=null)tran.rollback();

           throw re;

       } finally {

           session.close();

       }

       System.out.println(transientInstance.getAccount());

    }

注意,SessionFactory的set方法是一定要写的,写了以后spring才可注入。

applicationFactory.xml的配置如下:

<beanid="customerDao"class="cn.edu.zjut.pojo.CustomerDAO">

       <propertyname="sessionFactory"ref="sessionFactory"></property>

    </bean>

问题4

插入中文后数据库显示的是乱码。

解决方法:

将applicationContext.xml的数据库配置代码修改如下:

<propertyname="url"value="jdbc:mysql://127.0.0.1:3306/hibernatedb?useUnicode=true&amp;characterEncoding=UTF-8">

       </property>

8.2.2   实验收获与总结

Myeclipse下Spring+Hibernate工程的创建过程

1.       创建数据库表。

2.       创建工程,添加spring和hibernate必备包。

3.       DB Broswer 配置数据库信息

4.       先添加spring能力,便于使用spring管理SessionFactory,此时myeclipse帮我们添加了applicationContext.xml。

5.       再添加Hibernate能力,不生成SessionFactory类,让spring为我们管理session。

6.       Hibernate逆向工程。

7.       编写java 程序。

8.3     扩展实验——Spring、Struts与Hibernate的整合

8.3.1   实验所遇到的问题及解决方案【重点】

本次实验除了配置上和其他实验不一样,基本的步骤都是一样的,所以在做的过程中没有遇到任何困难。这是本次实验成功后的截图:

页面:

数据库:

由于我注册的时候用的是中文,所以在applicationContext.xml配置文件中还是要配置一下数据库的字符编码,代码如下:

<propertyname="url"

    value="jdbc:mysql://127.0.0.1:3306/hibernatedb?useUnicode=true&amp;characterEncoding=UTF-8">

       </property>

8.3.2   实验收获与总结

Myeclipse下Spring+Hibernate+struts工程的创建过程

1.       新建数据库表(先添加主外键关系,再添加数据)

2.       新建工程,添加支持ssh框架的jar包,并添加引用

3.       添加Spring支持。

4.       在DB brower中配置数据库。

5.       添加struts支持,修改web.xml,添加 listener,context-param

6.       新建struts.properties

7.       添加Hibernate支持,就是在applicationContext.xml配置文件中添加管理hibernate的Bean。

8.       Hibernate逆向工程,并手动修改bean中的数据类型。

9.       新建dao类,daoImp类(继承HibernateDaoSupport)

10.    在applicationContext.xml中添加实现类的bean。

 

Spring容器对Struts2核心控制器Action的管理

首先,要配置监听器和指定applicationContext.xml文件的位置。

<context-param>

       <param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/classes/applicationContext.xml</param-value>

</context-param>

<listener>

    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

其次,要在applicationContext.xml文件中配置action类。使得action类的所有对象都从applicationContext.xml配置文件中获得。

struts配置文件的action标签的class属性要改为在applicationContext.xml配置文件中配置好的bean的id值。

 

web.xml文件中添加监听器的目的

打开ContextLoaderListener的源码,发现ContextLoaderListener实现了ServletContextListener接口。

实现了ServletContextListener接口的作用就是当项目一经启动,就会激活实现了此接口的类方法,可以进行相关的初始化操作。 ServletContextListener接口实现了 

publicvoid contextInitialized(ServletContextEvent event)与 

publicvoid contextDestroyed(ServletContextEvent event) 

两个方法, 意味着项目一经启动,会进入contextInitialized方法中,进行Spring的相关配置。并且contextInitialized方法有ServletContext参数,可以在web.xml中配置参数,用来ServletContext读取相关Spring配置文件, 一般 Dao, Service 的 Spring 配置都会在 listener 里加载。 项目退出时激活contextDestroyed方法。

 

Spring容器对Bean实例的管理

bean 是Spring 管理的基本单位,在Spring 的J2EE应用中,所有的组件都是bean,bean 包括数据源Hibernate 的SessionFactory 及事务管理器等。Spring 里的bean 是非常广义的概念,任何的java对象,Java 组件都可被当成bean 处理。甚至这些组件并不是标准的JavaBean。

        整个应用中各层的对象都处于Spring 的管理下,这些对象以bean 的方式存在。Spring负责创建bean 实例,并管理其生命周期。bean 在Spring 容器中运行时,无须感受Spring容器的存在,一样可以接受Spring 的依赖注入,包括bean 属性的注入,合作者的注入及依赖关系的注入等。

        Spring的容器有两个接口:BeanFactoryApplicationContext ,这两个接口的实例也被称为Spring 上下文,它们都是产生bean 的工厂, bean 是Spring 工厂产生的实例。在Spring 产生bean 实例时,需要知道每个bean 的实现类,而bean 实例的使用者面向接口,无须关心bean 实例的实现类。因为Spring 工厂负责维护bean 实例的实例化,所以使用者无须关心实例化。

       bean 定义通常使用XML 配置文件。正确定义的bean由Spring 提供实例化,以及依赖关系的注入。bean实例通过BeanFactory 访问。对于大部分J2EE应用,bean 通过AppliactionContext 提供访问,因为AppliactionContext 是BeanFactory 的子接口,提供比BeanFactory 更多的功能。

9        实验九:Spring的核心机制:控制反转(IoC)——登录用户的购物车

9.1     基础实验——Spring容器中的依赖注入

9.1.1   实验所遇到的问题及解决方案【重点】

该实验比较简单,所以没有遇到任何问题,但是对spring的构造注入的练习使得我对该方法有了比较好的理解。

9.1.2   实验收获与总结

spring构造器注入方法:

在bean元素下用constructor-arg元素表示构造方法的参数,其中constructor-arg元素的index属性表示构造方法中参数的索引值。

<beanid="item1"class="cn.edu.zjut.bean.Item" scope="singleton">

       <constructor-argindex="0"type="java.lang.String">

           <value>978-7-121-12345-1</value>

       </constructor-arg>

       <constructor-argindex="1"type="java.lang.String">

           <value>JAVAEE技术实验指导教程</value>

       </constructor-arg>

       <constructor-argindex="2"type="java.lang.String">

           <value>WEB程序设计知识回顾、轻量级JAVAEE应用框架、企业级EJB组件编程技术、JAVAEE综合应用开发</value>

       </constructor-arg>

       <constructor-argindex="3"type="double">

           <value>19.95</value>

       </constructor-arg>

</bean>

List属性对象注入方法:

<beanid="shoppingcart"class="cn.edu.zjut.bean.ShoppingCart">

    <propertyname="itemsOrdered">  

       <list>

           <refbean="itemorder1"/>

           <refbean="itemorder2"/>

       </list> 

     </property>

</bean>

Spring中使用MapSetList、数组、属性集合的注入方法配置文件

private String name;

private String [] empName;//数组

private List<Employee> empList;//list集合

private Set<Employee> empsets;//set集合

private Map<String,Employee> empMaps;//map集合

private Properties pp;//Properties的使用

 

<!-- 给数组注入值 -->

<property name="empName">

       <list>

              <value>小明</value>

              <value>小明小明</value>

              <value>小明小明小明小明</value>

       </list>

</property>

 

<!-- list注入值 list中可以有相同的对象 -->

<property name="empList">

       <list>

              <refbean="emp2" />

              <refbean="emp1"/>

              <refbean="emp1"/>

              <refbean="emp1"/>

              <refbean="emp1"/>

              <refbean="emp1"/>

              <refbean="emp1"/>

       </list>

</property>

 

<!-- set注入值 set不能有相同的对象 -->

<property name="empsets">

       <set>

              <refbean="emp1" />

              <refbean="emp2"/>

              <refbean="emp2"/>

              <refbean="emp2"/>

              <refbean="emp2"/>

       </set>

</property>

 

<!-- map注入值 map只有key不一样,就可以装配value -->

<property name="empMaps">

       <map>

              <entrykey="11" value-ref="emp1" />

              <entrykey="22" value-ref="emp2"/>

              <entrykey="22" value-ref="emp1"/>

       </map>

</property>

 

<!-- 给属性集合配置 -->

<property name="pp">

       <props>

              <propkey="pp1">abcd</prop>

              <propkey="pp2">hello</prop>

       </props>

</property>

</bean>

 

<bean id="emp1"class="com.hsp.collection.Employee">

       <propertyname="name" value="北京"/>

       <propertyname="id" value="1"/>

</bean>

<bean id="emp2"class="com.hsp.collection.Employee">

       <propertyname="name" value="天津"/>

       <propertyname="id" value="2"/>

</bean>

9.2     提高实验——Spring容器中的Bean

9.2.1   实验所遇到的问题及解决方案【重点】

问题1

在测试 destroy方法时没有出现效果

解决方法:

singleton作用域的Bean通常会随着容器的关闭而销毁,但问题是:ApplicationContext容器在什么时候关闭呢?在基于Web的ApplicationContext实现中,系统已经提供了相应的代码保证关闭Web应用时恰当的关闭Spring容器。但对于一个非Web应用的环境下,为了让Spring容器优雅的关闭,并自动调用singleton上的相应回调方法,则需要在JVM里面注册一个关闭钩子(shutdown hook),这样就可以保证Spring容器被恰当关闭,并自动执行singleton的Bean里面的相应回调方法。

添加代码:

ApplicationContext ctx= new ClassPathXmlApplicationContext(

       "applicationContext.xml");

IItemOrder itemorder2 = (IItemOrder) ctx.getBean("itemorder2");

((AbstractApplicationContext) ctx).registerShutdownHook();

效果如图:

通过该实验练习了spring框架bean的三种创建方式,和怎么在bean初始化和销毁前执行特定的方法。

9.2.2   实验收获与总结

Bean依赖关系注入之后的行为:

Spring提供了两种方式在Bean全部属性设置成功后执行特定的行为:

1.        在Spring配置文件中使用init-method属性:这个属性指定某个方法在Bean全部依赖关系设置结束后自动执行。这个方法写在Bean里面。使用这种方法不需要将代码与Spring耦合在一起,代码污染小,推荐使用。

2.        让Bean实现InitializingBean接口:该接口提供了一个afterPropertiesSet()throwsException方法,在Bean里面实现它。

Bean销毁之前的行为:

与定制初始化行为相似,Spring也提供了两种方式定制在Bean销毁之前的特定行为:

1.        使用destroy-method属性:指定某个方法在Bean销毁之前被自动执行。使用这种方法,不需要将代码与Spring的接口耦合在一起,代码污染小,推荐使用。

2.        实现DisposableBean接口:该接口提供了一个destroy() throws Exception的方法。在Bean里面实现它,这个方法将在Bean销毁之前被Spring调用。

 

1singleton作用域

‍‍当一个bean的作用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。换言之,当把一个bean定义设置为singleton作用域时,Spring IOC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singletoncache)中,并且所有针对该bean的后续请求和引用都将返回被缓存的对象实例,这里要注意的是singleton作用域和GOF设计模式中的单例是完全不同的,单例设计模式表示一个ClassLoader中只有一个class存在,而这里的singleton则表示一个容器对应一个bean,也就是说当一个bean被标识为singleton时候,springIOC容器中只会存在一个该bean

<beanid="role"class="spring.chapter2.maryGame.Role"

scope="singleton"/>

2prototype

‍prototype作用域部署的bean,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)都会产生一个新的bean实例,相当与一个new的操作,对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被singleton作用域bean占用资源的一种可行方式是,通过使用bean的后置处理器,该处理器持有要被清除的bean的引用。)

3request

‍request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,配置实例: request、session、global session使用的时候首先要在初始化web的web.xml中做如下配置:如果你使用的是Servlet 2.4及以上的web容器,那么你仅需要在web应用的XML声明文件web.xml中增加下述ContextListener即可:

<listener>

  <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>

</listener>

4session

‍session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效

5global session

‍globalsession作用域类似于标准的HTTPSession作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用.

再议singletonprototype

scope=”prototype”没写的问题,项目中对一个表的增删该操作是用一个action,这个 action的add,update,delete,save这些方法, 添加和修改是共用一个页面,当页面得到id时代表进行的修改操作,反之是添加操作。因为在配置spring的bean是忘了写 scope=”prototype” 所以每次添加时都显示最后一次访问过的记录,scope=”prototype” 会在该类型的对象被请求 时创建一个新的action对象。如果没有配置scope=prototype则添加的时候不会新建一个action,他任然会保留上次访问的过记录的信息。struts2的Action不是线程安全的,要求在多线程环境下必须是一个线程对应一个独立的实例,不能使用singleton所以,我们在Spring配置struts2 Action Bean时,需要加上属性scope=prototype”或singleton=false”。

singleton模式指的是对某个对象的完全共享,包括代码空间和数据空间,说白了,如果一个类是singleton的,假如这个类有成员变量,那么这个成员变量的值是各个线程共享的(有点类似于static的样子了),当线程A往给变量赋了一个值以后,线程B就能读出这个值。因此,对于前台Action,肯定不能使用singleton的模式,必须是一个线程请求对应一个独立的实例。推而广之,只要是带数据成员变量的类,为了防止多个线程混用数据,就不能使用singleton。对于我们用到的ServiceDao,之所以用了singleton,就是因为他们没有用到数据成员变量,如果谁的 Service需要数据成员变量,请设置singleton=false

有状态的bean都使用Prototype作用域,而对无状态的bean则应该使用singleton作用域。

在Spring2.0中除了以前的Singleton和Prototype外又加入了三个新的web作用域,分别为request、session和 global session。如果你希望容器里的某个bean拥有其中某种新的web作用域,除了在bean级上配置相应的scope属性,还必须在容器级做一个额外的初始化配置。即在web应用的web.xml中增加这么一个

ContextListener:org.springframework.web.context.request.RequestContextListener

以上是针对Servlet2.4以后的版本。比如Request作用域

有状态会话bean

每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。

无状态会话bean

bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean   的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean但无状态会话bean  并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。

 

继承了InitializingBean后,spring自动执行该类的afterPropertiesSet方法。

init方法在afterPropertiesSet方法执行后执行。

 

Spring 容器中的 Bean 是有生命周期的,Spring 允许 Bean 在初始化完成后以及销毁前执行特定的操作。下面是常用的三种指定特定操作的方法:

1.        通过实现InitializingBean/DisposableBean 接口来定制初始化之后/销毁之前的操作方法;

2.        通过<bean> 元素的 init-method/destroy-method属性指定初始化之后 /销毁之前调用的操作方法。

3.        在指定方法上加上@PostConstruct或@PreDestroy注解来制定该方法是在初始化之后还是销毁之前调用。

9.3     扩展实验——深入Spring容器

9.3.1   实验所遇到的问题及解决方案【重点】

spring国际化资源配置后,运行效果如下:

9.3.2   实验收获与总结

1、Spring容器最基本的接口是BeanFactory,BeanFactory负责配置、创建、管理bean,包括管理bean与bean之间的依赖关系,BeanFactory有一个实现类:org.springframework.beans.factory.xml.XmlBeanFactory;

2、ApplicationContext是BeanFactory的子接口,对于大部分JavaEE应用而言,使用它作为Spring容器更方便,ApplicationContext的常用实现类是FileSystemXmlApplicationContext和ClassPathXmlApplicationContext;

3、ApplicationContext接口继承MessageSource接口,用以实现国际化功能;

4、Spring支持事件机制,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext的事件处理。

 

spring中使用XmlBeanFactory创建BeanFactory实例的方法

FileSystemResource isr = new FileSystemResource(

              "src/applicationContext.xml");

XmlBeanFactory factory =newXmlBeanFactory(isr);

IItemOrder itemorder3 = (IItemOrder) factory.getBean("itemorder2");

测试结果如图:

spring中使用ClassPathXmlApplicationContext创建BeanFactory实例的方法

ApplicationContext ctx = new ClassPathXmlApplicationContext(

           "applicationContext.xml");

IItemOrder itemorder3 = (IItemOrder) ctx.getBean("itemorder2");

测试结果如图:

spring中使用FileSystemXmlApplicationContext 创建BeanFactory实例的方法

ApplicationContext ctx = new FileSystemXmlApplicationContext(

       "src/applicationContext.xml");

IItemOrder itemorder3 = (IItemOrder) ctx.getBean("itemorder2");

测试结果如图:

SpringApplicationContext掌握国际化的基本步骤

1.        创建国家化资源。message_en_US.properties和message_zh_CN.properties

2.        修改Spring配置文件applicationContext.xml,增加名为messageSource的bean实例以完成国际化的配置。

<beanid="messageSource"

       class="org.springframework.context.support.ResourceBundleMessageSource">

       <propertyname="basenames">

           <list>

              <value>message</value>

              <!-- 如果有多个资源文件,全部列在此处 -->

           </list>

       </property>

    </bean>

3.        测试配置好的国际化资源。

getMessage()的三个方法:

String getMessage(Stringcode,Object[]args,String default,Local loc);

String getMessage(Stringcode,Object[]args,Local loc);

StringgetMessage(MessageSourceResolvable,Local loc);

 

Spring的事件机制,掌握Spring完成事件监听及处理的基本步骤

spring的事件机制采用了观察者设计模式。

关键点在于ApplicationContext的两个成员:ApplicationEvent 和ApplicationListenter。

1.        ApplicatonEvent是java事件在Spring中的实现,要自定义一个spring事件,需要继承自ApplicatonEvent;

2.        ApplicationListener则是一个监听器,自定义监听器同样需要实现ApplicationListener接口,并重写onApplicationEvent(ApplicationEventevent)方法。

3.        定义完事件和监听器后需要在spring中进行配置,为监听器配置为一个bean:

<beanclass="com.lincoln.spring.EmailListener"/>。

4.        触发事件:直接调用ApplicationContext的publishEvent(event)方法。

10  实验十:Sping的面向切面编程(AOP)——用户登录模块的增强处理块

10.1基础实验——使用@AspectJ实现AOP

10.1.1  实验所遇到的问题及解决方案【重点】

实验结果如图:

10.1.2  实验收获与总结

面向切面编程的相关术语有:

1.        通知、增强处理(Advice 就是想要的功能,也就是安全、事物、日志等。先定义好,然后在想用的地方用一下。包含Aspect的一段处理代码。

2.        连接点(JoinPoint 就是spring允许你是通知(Advice)的地方,基本每个方法的前、后(两者都有也行),或抛出异常是时都可以是连接点。

3.        切入点(Pointcut 上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有十几个连接点了对吧,但是你并不想在所有方法附件都使用通知(使用叫织入),你只是想让其中几个,在调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。

4.        切面(Aspect 切面是通知和切入点的结合。连接点就是为了让你好理解切点搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过方法名中的befor,after,around等就能知道),二切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。

5.        引入(introduction 允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通知定义的)用到目标类中吗。

6.        目标(target 引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被织入切面。

7.        代理(proxy 怎么实现整套AOP机制的,都是通过代理,这个一会儿给细说。

8.        织入(weaving) 把切面应用到目标对象来创建新的代理对象的过程。有三种方式,spring采用的是运行时,为什么是运行时。

9.        目标对象 – 项目原始的Java组件。

10.     AOP代理  – 由AOP框架生成java对象。

11.     AOP代理方法 = advice+ 目标对象的方法

@Aspect //声明切面,标记类 

public class Audience

 

@Pointcut("execution(* *.perform(..))")//定义切点,标记方法 

public void performance() {} 

 

@Before("performance()"//切点之前执行 

public ....       

 

@AfterReturning("performance()"//切点之后执行 

public ... 

 

@AfterThrowing("performance()") //切点抛出异常后执行 

public ... 

 

AOP用来封装横切关注点,具体可以在下面的场景中使用

1.        Authentication 权限

2.        Caching 缓存

3.        Context passing 内容传递

4.        Error handling 错误处理

5.        Lazy loading 懒加载

6.        Debugging 调试

7.        logging, tracing, profiling and monitoring 记录跟踪 优化 校准

8.        Performance optimization 性能优化

9.        Persistence 持久化

10.     Resource pooling 资源池

11.     Synchronization 同步

12.     Transactions 事务

10.2提高实验——Spring AOP实现事务管理

10.2.1  实验所遇到的问题及解决方案【重点】

问题1

为工程添加完数据源后,运行程序报错。

Cannot find the declaration of element'beans'.

解决方法:

因为由程序自动配置数据库,打开applicationContext.xml配置文件后发现,数据库和SessionFactory的bean在自己配置的bean下面,后来将datasource和SessionFactory挪到自己配置的bean上方以后问题解决。

问题2

更正上一个问题后再运行,出现了如下问题:

Exception in thread "main" org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allowcreation of non-transactional one here

    atorg.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)

解决方法:

原来的代码:

    public Session getSession() {

       returnsessionFactory.getCurrentSession();

       //return sessionFactory.openSession();

    }

修改后的代码:

    public Session getSession() {

//     returnsessionFactory.getCurrentSession();

       returnsessionFactory.openSession();

    }

解决方法:

出现这个问题主要是由于我调用了getCurrentSession()方法,所以需要在配置文件中把这个绑定到当前线程中取,其次如果绑定了仍然出现这个错误,那么就是你整合的问题了,在spring3整合hibernate4的版本的时候,就会出现这个问题。

问题3

在applicationContext.xml中添加以下代码报错。

<beanid="transactionManager"   class="org.springframework.orm.hibernate4.HibernateTransactionManager">

       <propertyname="sessionFactory">

           <refbean="sessionFactory"/>

       </property>

    </bean>

Class'org.springframework.orm.hibernate4.HibernateTransactionManager' not found

解决方法:

添加 包,spring-orm-4.0.0.RELEASE.jar

问题4

配置事物管理器的时候,出现以下错误:

Class 'org.springframework.orm.hibernate4.HibernateTransactionManager'not found

原来的代码:

<beanid="transactionManager"

       class="org.springframework.orm.hibernate4.HibernateTransactionManager">

       <propertyname="sessionFactory">

           <refbean="sessionFactory"/>

       </property>

    </bean>

修改后的代码:

<beanid="transactionManager"

       class="org.springframework.orm.hibernate3.HibernateTransactionManager">

       <propertyname="sessionFactory">

           <refbean="sessionFactory"/>

       </property>

    </bean>

10.2.2  实验收获与总结

Spring框架基于AOP实现声明式事务管理的基本办法:

1.        在applicationContext.xml中配置事务管理器

    <!-- 配置事务管理器 -->

    <beanid="transactionManager"

    class="org.springframework.orm.hibernate3.HibernateTransactionManager">

       <propertyname="sessionFactory">

           <refbean="sessionFactory"/>

       </property>

    </bean>

2.        添加事务管理

    <tx:adviceid="txAdvice"transaction-manager="transactionManager">

       <tx:attributes>

           <tx:methodname="add*"propagation="REQUIRED"/>

           <tx:methodname="update*"propagation="REQUIRED"/>

           <tx:methodname="delete*"propagation="REQUIRED"/>

           <tx:methodname="del*"propagation="REQUIRED"/>

           <tx:methodname="*"read-only="true"/>

       </tx:attributes>

    </tx:advice>

3.        定义事务拦截切面

<aop:config>

       <aop:pointcutid="allServiceMethod"

           expression="execution(* cn.edu.zjut.service.*.*(..))"/>

       <aop:advisorpointcut-ref="allServiceMethod"advice-ref="txAdvice"/>

    </aop:config>

10.3扩展实验——Spring AOP的核心工作原理:代理和代理工厂

10.3.1  实验所遇到的问题及解决方案【重点】

实验结果:

实现过程中包冲突严重,最后是使用了myeclipse自带的包,解决了包冲突问题。

10.3.2  实验收获与总结

Spring提供了4种实现AOP的方式:

1.经典的基于代理的AOP

2.@AspectJ注解驱动的切面

3.POJO切面;

4.注入式AspectJ切面;

 

在SpringAOP中支持4中类型的通知:

1:before advice 在方法执行前执行。

2:after  returning advice 在方法执行后返回一个结果后执行。

3:after  throwing advice 在方法执行过程中抛出异常的时候执行。

4:Around  advice 在方法执行前后和抛出异常时执行,相当于综合了以上三种通知

 

1:before  advice

beforeadvice将在方法执行前执行,创建一个实现MethodBeforeAdvice接口的类能够定义执行方法前的操作。

2: after  advice

在方法运行返回结果后将执行这个 afterReturning方法,创建的这个类必须实现:AfterReturningAdvice接口

3:after  throwing advice

当方法执行抛出一个异常后,会执行这个方法,创建一个类实现:ThrowsAdvice接口,创建一个afterThrowing拦截:IllegalArgumentException异常。

4:Around  advice

这个advice 联合了上面的三个advices,在方法执行期间执行,创建一个类实现MethodInterceptor接口,需要在方法中执行Object result =methodInvocation.proceed();方法才能得到执行,否则方法不会执行。

11  本课程相关书籍阅读:

《GitHub入门与实践》[日] 大塚弘记/著 (完)

《Struts2 框架应用开发教程》王建国/著(部分)

Struts2 技术内幕-深入解析Struts2架构设计与实现原理 》陆舟/著

---->>>该书通过理解struts框架的实现原理来更好的指导基于框架的web应用开发(部分)

《Struts 2.x权威指南》(第3版)李刚/著 (部分)

《Struts2+Hibernate+Spring整合开发技术详解》蒲子明/著 (部分)

《轻量级JavaEE企业版应用实战—Struts2+Spring3+Hibernate》李刚/著 (部分)

《Java Web编程技术》沈泽刚/著 (部分)

《Java程序设计实用教程》高飞/著(部分)

《JavaEE轻量级框架Struts2+Spring+Hibernate整合开发》史胜辉/著(完)

《JavaEE技术实验教程》 韩珊珊/著(完)

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值