Struts+Spring+Hibernate实现上传下载.docx
- 文档编号:23828066
- 上传时间:2023-05-21
- 格式:DOCX
- 页数:21
- 大小:26.54KB
Struts+Spring+Hibernate实现上传下载.docx
《Struts+Spring+Hibernate实现上传下载.docx》由会员分享,可在线阅读,更多相关《Struts+Spring+Hibernate实现上传下载.docx(21页珍藏版)》请在冰豆网上搜索。
Struts+Spring+Hibernate实现上传下载
文件的上传和下载在J2EE编程已经是一个非常古老的话题了,也许您马上就能掰着指头数出好几个著名的大件:
如SmartUpload、Apache的FileUpload。
但如果您的项目是构建在Struts+Spring+Hibernate(以下称SSH)框架上的,这些大件就显得笨重而沧桑了,SSH提供了一个简捷方便的文件上传下载的方案,我们只需要通过一些配置并辅以少量的代码就可以完好解决这个问题了。
文件的上传和下载在J2EE编程已经是一个非常古老的话题了,也许您马上就能掰着指头数出好几个著名的大件:
如SmartUpload、Apache的FileUpload。
但如果您的项目是构建在Struts+Spring+Hibernate(以下称SSH)框架上的,这些大件就显得笨重而沧桑了,SSH提供了一个简捷方便的文件上传下载的方案,我们只需要通过一些配置并辅以少量的代码就可以完好解决这个问题了。
本文将围绕SSH文件上传下载的主题,向您详细讲述如何开发基于SSH的Web程序。
SSH各框架的均为当前最新版本:
·Struts1.2
·Spring1.2.5
·Hibernate3.0
本文选用的数据库为Oracle9i,当然你可以在不改动代码的情况下,通过配置文件的调整将其移植到任何具有Blob字段类型的数据库上,如MySQL,SQLServer等。
总体实现
上传文件保存到T_FILE表中,T_FILE表结构如下:
图1T_FILE表结构
其中:
·FILE_ID:
文件ID,32个字符,用Hibernate的uuid.hex算法生成。
·FILE_NAME:
文件名。
·FILE_CONTENT:
文件内容,对应Oracle的Blob类型。
·REMARK:
文件备注。
文件数据存储在Blob类型的FILE_CONTENT表字段上,在Spring中采用OracleLobHandler来处理Lob字段(包括Clob和Blob),由于在程序中不需要引用到oracle数据驱动程序的具体类且屏蔽了不同数据库处理Lob字段方法上的差别,从而撤除程序在多数据库移植上的樊篱。
1.首先数据表中的Blob字段在Java领域对象中声明为byte[]类型,而非java.sql.Blob类型。
2.数据表Blob字段在Hibernate持久化映射文件中的type为org.springframework.orm.hibernate3.support.BlobByteArrayType,即Spring所提供的用户自定义的类型,而非java.sql.Blob。
3.在Spring中使用org.springframework.jdbc.support.lob.OracleLobHandler处理Oracle数据库的Blob类型字段。
通过这样的设置和配置,我们就可以象持久化表的一般字段类型一样处理Blob字段了。
以上是Spring+Hibernate将文件二进制数据持久化到数据库的解决方案,而Struts通过将表单中file类型的组件映射为ActionForm中类型为org.apache.struts.upload.FormFile的属性来获取表单提交的文件数据。
综上所述,我们可以通过图2,描绘出SSH处理文件上传的方案:
图2SSH处理文件上传技术方案
文件上传的页面如图3所示:
图3文件上传页面
文件下载的页面如图4所示:
图4文件下载页面
该工程的资源结构如图5所示:
图5工程资源结构
工程的类按SSH的层次结构划分为数据持久层、业务层和Web层;WEB-INF下的applicationContext.xml为Spring的配置文件,struts-config.xml为Struts的配置文件,file-upload.jsp为文件上传页面,file-list.jsp为文件列表页面。
本文后面的章节将从数据持久层->业务层->Web层的开发顺序,逐层讲解文件上传下载的开发过程。
数据持久层
1、领域对象及映射文件
您可以使用HibernateMiddlegen、HIbernateTools、HibernateSyhchronizer等工具或手工的方式,编写Hibernate的领域对象和映射文件。
其中对应T_FILE表的领域对象Tfile.java为:
代码1领域对象Tfile1.packagesshfile.model;
2.publicclassTfile
3.{
4.privateStringfileId;
5.privateStringfileName;
6.privatebyte[]fileContent;
7.privateStringremark;
8.…//getterandsetter
9.}
特别需要注意的是:
数据库表为Blob类型的字段在Tfile中的fileContent类型为byte[]。
Tfile的Hibernate映射文件Tfile.hbm.xml放在Tfile.java类文件的相同目录下:
代码2领域对象映射文件1.<?
xmlversion="1.0"?
>
2.<!
DOCTYPEhibernate-mappingPUBLIC
3."-//Hibernate/HibernateMappingDTD3.0//EN"
4.">
5.<hibernate-mapping>
6.<classname="sshfile.model.Tfile"table="T_FILE">
7.<idname="fileId"type="java.lang.String"column="FILE_ID">
8.<generatorclass="uuid.hex"/>
9.</id>
10.<propertyname="fileContent"
11.type="org.springframework.orm.hibernate3.support.BlobByteArrayType"
12.column="FILE_CONTENT"lazy="true"/>
13.…//其它一般字段的映射
14.</class>
15.</hibernate-mapping>
fileContent字段映射为Spring所提供的BlobByteArrayType类型,BlobByteArrayType是用户自定义的数据类型,它实现了Hibernate的org.hibernate.usertype.UserType接口。
BlobByteArrayType使用从sessionFactory获取的Lob操作句柄lobHandler将byte[]的数据保存到Blob数据库字段中。
这样,我们就再没有必要通过硬编码的方式,先insert然后再update来完成Blob类型数据的持久化,这个原来难伺候的老爷终于被平民化了。
关于lobHandler的配置请见本文后面的内容。
此外lazy="true"说明地返回整个Tfile对象时,并不返回fileContent这个字段的数据,只有在显式调用tfile.getFileContent()方法时才真正从数据库中获取fileContent的数据。
这是Hibernate3引入的新特性,对于包含重量级大数据的表字段,这种抽取方式提高了对大字段操作的灵活性,否则加载Tfile对象的结果集时如果总是返回fileContent,这种批量的数据抽取将可以引起数据库的"洪泛效应"。
2、DAO编写和配置
Spring强调面向接口编程,所以我们将所有对Tfile的数据操作的方法定义在TfileDAO接口中,这些接口方法分别是:
·findByFildId(StringfileId)
·save(Tfiletfile)
·ListfindAll()
TfileDAOHibernate提供了对TfileDAO接口基于Hibernate的实现,如代码3所示:
代码3基于Hibernate的fileDAO实现类1.packagesshfile.dao;
2.
3.importsshfile.model.*;
4.importorg.springframework.orm.hibernate3.support.HibernateDaoSupport;
5.importjava.util.List;
6.
7.publicclassTfileDAOHibernate
8.extendsHibernateDaoSupportimplementsTfileDAO
9.{
10.publicTfilefindByFildId(StringfileId)
11.{
12.return(Tfile)getHibernateTemplate().get(Tfile.class,fileId);
13.}
14.publicvoidsave(Tfiletfile)
15.{
16.getHibernateTemplate().save(tfile);
17.getHibernateTemplate().flush();
18.}
19.publicListfindAll()
20.{
21.returngetHibernateTemplate().loadAll(Tfile.class);
22.}
23.}
TfileDAOHibernate通过扩展Spring提供的Hibernate支持类HibernateDaoSupport而建立,HibernateDaoSupport封装了HibernateTemplate,而HibernateTemplate封装了Hibernate所提供几乎所有的的数据操作方法,如execute(HibernateCallbackaction),load(ClassentityClass,Serializableid),save(finalObjectentity)等等。
所以我们的DAO只需要简单地调用父类的HibernateTemplate就可以完成几乎所有的数据库操作了。
由于Spring通过代理Hibernate完成数据层的操作,所以原Hibernate的配置文件hibernate.cfg.xml的信息也转移到Spring的配置文件中:
代码4Spring中有关Hibernate的配置信息1.<beans>
2.<!
--数据源的配置//-->
3.<beanid="dataSource"class="mons.dbcp.BasicDataSource"
4.destroy-method="close">
5.<propertyname="driverClassName"value="oracle.jdbc.driver.OracleDriver"/>
6.<propertyname="url"value="jdbc:
oracle:
thin:
@localhost:
1521:
ora9i"/>
7.<propertyname="username"value="test"/>
8.<propertyname="password"value="test"/>
9.</bean>
10.<!
--Hibernate会话工厂配置//-->
11.<beanid="sessionFactory"
12.class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
13.<propertyname="dataSource"ref="dataSource"/>
14.<propertyname="mappingDirectoryLocations">
15.<list>
16.<value>classpath:
/sshfile/model</value>
17.</list>
18.</property>
19.<propertyname="hibernateProperties">
20.<props>
21.<propkey="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
22.<propkey="hibernate.cglib.use_reflection_optimizer">true</prop>
23.</props>
24.</property>
25.</bean>
26.<!
--Hibernate模板//-->
27.<beanid="hibernateTemplate"
28.class="org.springframework.orm.hibernate3.HibernateTemplate">
29.<propertyname="sessionFactory"ref="sessionFactory"/>
30.</bean>
31.<!
--DAO配置//-->
32.<beanid="tfileDAO"class="sshfile.dao.TfileDAOHibernate">
33.<propertyname="hibernateTemplate"ref="hibernateTemplate"/>
34.</bean>
35.…
36.</beans>
第3~9行定义了一个数据源,其实现类是apache的BasicDataSource,第11~25行定义了Hibernate的会话工厂,会话工厂类用Spring提供的LocalSessionFactoryBean维护,它注入了数据源和资源映射文件,此外还通过一些键值对设置了Hibernate所需的属性。
其中第16行通过类路径的映射方式,将sshfile.model类包目录下的所有领域对象的映射文件装载进来,在本文的例子里,它将装载进Tfile.hbm.xml映射文件。
如果有多个映射文件需要声明,使用类路径映射方式显然比直接单独指定映射文件名的方式要简便。
第27~30行定义了Spring代理Hibernate数据操作的HibernateTemplate模板,而第32~34行将该模板注入到tfileDAO中。
需要指定的是Spring1.2.5提供了两套Hibernate的支持包,其中Hibernate2相关的封装类位于org.springframework.orm.hibernate2.*包中,而Hibernate3.0的封装类位于org.springframework.orm.hibernate3.*包中,需要根据您所选用Hibernate版本进行正确选择。
3、Lob字段处理的配置
我们前面已经指出Oracle的Lob字段和一般类型的字段在操作上有一个明显的区别--那就是你必须首先通过Oracle的empty_blob()/empty_clob()初始化Lob字段,然后获取该字段的引用,通过这个引用更改其值。
所以要完成对Lob字段的操作,Hibernate必须执行两步数据库访问操作,先Insert再Update。
使用BlobByteArrayType字段类型后,为什么我们就可以象一般的字段类型一样操作Blob字段呢?
可以确定的一点是:
BlobByteArrayType不可能逾越Blob天生的操作方式,原来是BlobByteArrayType数据类型本身具体数据访问的功能,它通过LobHandler将两次数据访问的动作隐藏起来,使Blob字段的操作在表现上和其他一般字段业类型无异,所以LobHandler即是那个"苦了我一个,幸福十亿人"的那位幕后英雄。
LobHandler必须注入到Hibernate会话工厂sessionFactory中,因为sessionFactory负责产生与数据库交互的Session。
LobHandler的配置如代码5所示:
代码5Lob字段的处理句柄配置1.<beans>
2.…
3.<beanid="nativeJdbcExtractor"
4.class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"
5.lazy-init="true"/>
6.<beanid="lobHandler"
7.class="org.springframework.jdbc.support.lob.OracleLobHandler"lazy-init="true">
8.<propertyname="nativeJdbcExtractor">
9.<reflocal="nativeJdbcExtractor"/>
10.</property>
11.</bean>
12.…
13.</beans>
首先,必须定义一个能够从连接池中抽取出本地数据库JDBC对象(如OracleConnection,OracleResultSet等)的抽取器:
nativeJdbcExtractor,这样才可以执行一些特定数据库的操作。
对于那些仅封装了Connection而未包括Statement的简单数据连接池,SimpleNativeJdbcExtractor是效率最高的抽取器实现类,但具体到apache的BasicDataSource连接池,它封装了所有JDBC的对象,这时就需要使用CommonsDbcpNativeJdbcExtractor了。
Spring针对几个著名的Web服务器的数据源提供了相应的JDBC抽取器:
·WebLogic:
WebLogicNativeJdbcExtractor
·WebSphere:
WebSphereNativeJdbcExtractor
·JBoss:
JBossNativeJdbcExtractor
在定义了JDBC抽取器后,再定义lobHandler。
Spring1.2.5提供了两个lobHandler:
·DefaultLobHandler:
适用于大部分的数据库,如SqlServer,MySQL,对Oracle10g也适用,但不适用于Oracle9i(看来Oracle9i确实是个怪胎,谁叫Oracle公司自己都说Oracle9i是一个过渡性的产品呢)。
·OracleLobHandler:
适用于Oracle9i和Oracle10g。
由于我们的数据库是Oracle9i,所以使用OracleLobHandler。
在配置完LobHandler后,还需要将其注入到sessionFactory的Bean中,下面是调用后的sessionFactoryBean的配置:
代码6将lobHandler注入到sessionFactory中的配置1.<beans>
2.…
3.<beanid="sessionFactory"
4.class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
5.<propertyname="dataSource"ref="dataSource"/>
6.<!
--为处理Blob类型字段的句柄声明//-->
7.<propertyname="lobHandler"ref="lobHandler"/>
8.…
9.</bean>
10.…
11.</beans>
如第7所示,通过sessionFactory的lobHandler属性进行注入。
业务层
1、业务层接口
"面向接口而非面向类编程"是Spring不遗余力所推荐的编程原则,这条原则也已经为大部开发者所接受;此外,JDK的动态代理只对接口有效,否则必须使用CGLIB生成目标类的子类。
我们依从于Spring的倡导为业务类定义一个接口:
代码7业务层操作接口1.publicinterfaceFileService
2.{
3.voidsave(FileActionFormfileForm);//将提交的上传文件保存到数据表中
4.ListgetAllFile();//得到T_FILE所示记录
5.voidwrite(OutputStreamos,StringfileId);//将某个文件的文件数据写出到输出流中
6.StringgetFileName(StringfileId);//获取文件名
7.}
其中save(FileActionFormfileForm)方法,将封装在fileForm中的上传文件保存到数据库中,这里我们使用FileActionForm作为方法入参,FileActionForm是Web层的表单数据对象,它封装了提交表单的数据。
将FileActionForm直接作为业务层的接口入参,相当于将Web层传播到业务层中去,即将业务层绑定在特定的Web层实现技术中,按照分层模型学院派的观点,这是一种反模块化的设计,但在"一般"的业务系统并无需提供多种UI界面,系统Web层将来切换到另一种实现技术的可能性也微乎其微,所以笔者觉得没有必要为了这个业务层完全独立于调用层的过高目标而去搞一个额外的隔离层,浪费了原材料不说,还将系统搞得过于复杂,相比于其它原则,"简单"始终是最大的一条原则。
getAllFile()负责获取T_FILE表所有记录,以便在网页上显示出来。
而getFileName(StringfileId)和write(OutputStreamos,StringfileId)则用于下载某个特定的文件。
具体的调用是将Web层将response.getOutputStream()传给write(OutputStreamos,StringfileId)接口,业务层直接将文件数据输出到这个响应流中。
具体实现请参见错误!
未找到引用源。
节下载文件部分。
2、业务层接口实现类
FileService的实现类为FileServiceImpl,其中save(FileActionFormfileForm)的实现如下所示:
代码8业务接口实现类之save()1.…
2.publicclassFileServiceImpl
3.implementsFileService
4.{
5.privateTfileDAOtfileDAO;
6.publicvoidsave(FileActionFormfileForm)
7.{
8.Tfiletfile=newTfile
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Struts Spring Hibernate 实现 上传下载