Servlet 读取 InitParameter 参数的中文乱码

li3807 2016-12-17 10:05:46
Servlet 代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

String name = "";
ServletConfig servletConfig = getServletConfig();
if (servletConfig != null) {
name = servletConfig.getInitParameter("name");
}

response.getWriter().append("Served at: ").append(request.getContextPath());
response.getWriter().append("<br/>").append("Name is ").append(name);
}

Web.xml 配置:
<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>HelloWorld</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>李四</param-value>
</init-param>
</servlet>

访问页面显示
Served at: /DrsoftWeb
Name is ??
...全文
169 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
tec_crazy_man 2016-12-17
  • 打赏
  • 举报
回复
1 上面的代码编辑出错了,多了<span>。其实就是在你的代码加一句 resp.setContextType(..); 让java采用utf-8的编码格式输出,同时让网页使用utf-8的格式显示。 如果没有这么设置的话,tomcat其实一般是采用ISO8859-1进行编码的,它不懂中文,就会把李四弄成??发出去。你的浏览器的编码应该是gbk,或者utf-8。这些都支持?? ,所以结果显示:?? 而不是 火星文:~@# 2 resp.setContentType("text/html;charset=utf-8"); 这句话实现了两个功能。 2.1 response.setHeader("Content-Type", "text/html;charset=utf-8"); 设置一个响应头,说明发送回去的信息的格式,是采用utf-8进行编码,服务器(tomcat)就按照这个来进行。 2.2 response.setCharacterEncoding("utf-8"); 告诉浏览器,你丫打开我返回的信息要采用utf-8的编码进行解析,别乱用其它。
tec_crazy_man 2016-12-17
  • 打赏
  • 举报
回复
package ServletDemo;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
ServletConfig servletConfig = getServletConfig();
String name = servletConfig.getInitParameter("name");
System.out.println(name);
// RequestDispatcher requestDispatcher = req.getRequestDispatcher("/basePath.jsp");
// requestDispatcher.forward(req, resp);
resp.getWriter().write(name);
}
}

Struts+Spring+Hibernate实现上传下载      本文将围绕SSH文件上传下载的主题,向您详细讲述如何开发基于SSH的Web程序。SSH各框架的均为当前最新版本:   •Struts 1.2   •Spring 1.2.5   •Hibernate 3.0   本文选用的数据库为Oracle 9i,当然你可以在不改动代码的情况下,通过配置文件的调整将其移植到任何具有Blob字段类型的数据库上,如MySQL,SQLServer等。   总体实现   上传文件保存到T_FILE表中,T_FILE表结构如下: 图 1 T_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的属性来获取表单提交的文件数据。   工程的类按SSH的层次结构划分为数据持久层、业务层和Web层;WEB-INF下的applicationContext.xml为Spring的配置文件,struts-config.xml为Struts的配置文件,file-upload.jsp为文件上传页面,file-list.jsp为文件列表页面。   本文后面的章节将从数据持久层->业务层->Web层的开发顺序,逐层讲解文件上传下载的开发过程。   数据持久层   1、领域对象及映射文件   您可以使用Hibernate Middlegen、HIbernate Tools、Hibernate Syhchronizer等工具或手工的方式,编写Hibernate的领域对象和映射文件。其中对应T_FILE表的领域对象Tfile.java为:   代码 1 领域对象Tfile 1. package sshfile.model; 2. public class Tfile 3.{ 4. private String fileId; 5. private String fileName; 6. private byte[] fileContent; 7. private String remark; 8. …//getter and setter 9. }   特别需要注意的是:数据库表为Blob类型的字段在Tfile中的fileContent类型为byte[]。Tfile的Hibernate映射文件Tfile.hbm.xml放在Tfile .java类文件的相同目录下:   代码 2 领域对象映射文件 1. <?xml version="1.0"?> 2. <!DOCTYPE hibernate-mapping PUBLIC 3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > 5. <hibernate-mapping> 6. <class name="sshfile.model.Tfile" table="T_FILE"> 7. <id name="fileId" type="java.lang.String" column="FILE_ID"> 8. <generator class="uuid.hex"/> 9. </id> 10. <property name="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(String fileId)   •save(Tfile tfile)   •List findAll()   TfileDAOHibernate提供了对TfileDAO接口基于Hibernate的实现,如代码 3所示:   代码 3 基于Hibernate 的fileDAO实现类 1. package sshfile.dao; 2. 3. import sshfile.model.*; 4. import org.springframework.orm.hibernate3.support.HibernateDaoSupport; 5. import java.util.List; 6. 7. public class TfileDAOHibernate 8. extends HibernateDaoSupport implements TfileDAO 9. { 10. public Tfile findByFildId(String fileId) 11. { 12. return (Tfile) getHibernateTemplate().get(Tfile.class, fileId); 13. } 14. public void save(Tfile tfile) 15. { 16. getHibernateTemplate().save(tfile); 17. getHibernateTemplate().flush(); 18. } 19. public List findAll() 20. { 21. return getHibernateTemplate().loadAll(Tfile.class); 22. } 23. }   TfileDAOHibernate通过扩展Spring提供的Hibernate支持类HibernateDaoSupport而建立,HibernateDaoSupport封装了HibernateTemplate,而HibernateTemplate封装了Hibernate所提供几乎所有的的数据操作方法,如execute(HibernateCallback action),load(Class entityClass, Serializable id),save(final Object entity)等等。   所以我们的DAO只需要简单地调用父类的HibernateTemplate就可以完成几乎所有的数据库操作了。   由于Spring通过代理Hibernate完成数据层的操作,所以原Hibernate的配置文件hibernate.cfg.xml的信息也转移到Spring的配置文件中:   代码 4 Spring中有关Hibernate的配置信息 1. <beans> 2. <!-- 数据源的配置 //--> 3. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 4. destroy-method="close"> 5. <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/> 6. <property name="url" value="jdbc:oracle:thin:@localhost:1521:ora9i"/> 7. <property name="username" value="test"/> 8. <property name="password" value="test"/> 9. </bean> 10. <!-- Hibernate会话工厂配置 //--> 11. <bean id="sessionFactory" 12. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 13. <property name="dataSource" ref="dataSource"/> 14. <property name="mappingDirectoryLocations"> 15. <list> 16. <value>classpath:/sshfile/model</value> 17. </list> 18. </property> 19. <property name="hibernateProperties"> 20. <props> 21. <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop> 22. <prop key="hibernate.cglib.use_reflection_optimizer">true</prop> 23. </props> 24. </property> 25. </bean> 26. <!-- Hibernate 模板//--> 27. <bean id="hibernateTemplate" 28. class="org.springframework.orm.hibernate3.HibernateTemplate"> 29. <property name="sessionFactory" ref="sessionFactory"/> 30. </bean> 31. <!--DAO配置 //--> 32. <bean id="tfileDAO" class="sshfile.dao.TfileDAOHibernate"> 33. <property name="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中。   需要指定的是Spring 1.2.5提供了两套Hibernate的支持包,其中Hibernate 2相关的封装类位于org.springframework.orm.hibernate2.*包中,而Hibernate 3.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所示:   代码 5 Lob字段的处理句柄配置 1. <beans> 2. … 3. <bean id="nativeJdbcExtractor" 4. class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor" 5. lazy-init="true"/> 6. <bean id="lobHandler" 7. class="org.springframework.jdbc.support.lob.OracleLobHandler" lazy-init="true"> 8. <property name="nativeJdbcExtractor"> 9. <ref local="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。Spring 1.2.5提供了两个lobHandler:   •DefaultLobHandler:适用于大部分的数据库,如SqlServer,MySQL,对Oracle 10g也适用,但不适用于Oracle 9i(看来Oracle 9i确实是个怪胎,谁叫Oracle 公司自己都说Oracle 9i是一个过渡性的产品呢)。   •OracleLobHandler:适用于Oracle 9i和Oracle 10g。   由于我们的数据库是Oracle9i,所以使用OracleLobHandler。   在配置完LobHandler后, 还需要将其注入到sessionFactory的Bean中,下面是调用后的sessionFactory Bean的配置:   代码 6 将lobHandler注入到sessionFactory中的配置 1. <beans> 2. … 3. <bean id="sessionFactory" 4. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 5. <property name="dataSource" ref="dataSource"/> 6. <!-- 为处理Blob类型字段的句柄声明 //--> 7. <property name="lobHandler" ref="lobHandler"/> 8. … 9. </bean> 10. … 11. </beans>   如第7所示,通过sessionFactory的lobHandler属性进行注入。  业务层   1、业务层接口   "面向接口而非面向类编程"是Spring不遗余力所推荐的编程原则,这条原则也已经为大部开发者所接受;此外,JDK的动态代理只对接口有效,否则必须使用CGLIB生成目标类的子类。我们依从于Spring的倡导为业务类定义一个接口:   代码 7 业务层操作接口 1. public interface FileService 2. { 3. void save(FileActionForm fileForm);//将提交的上传文件保存到数据表中 4. List getAllFile();//得到T_FILE所示记录 5. void write(OutputStream os,String fileId);//将某个文件的文件数据写出到输出流中 6. String getFileName(String fileId);//获取文件名 7. }   其中save(FileActionForm fileForm)方法,将封装在fileForm中的上传文件保存到数据库中,这里我们使用FileActionForm作为方法入参,FileActionForm是Web层的表单数据对象,它封装了提交表单的数据。将FileActionForm直接作为业务层的接口入参,相当于将Web层传播到业务层中去,即将业务层绑定在特定的Web层实现技术中,按照分层模型学院派的观点,这是一种反模块化的设计,但在"一般"的业务系统并无需提供多种UI界面,系统Web层将来切换到另一种实现技术的可能性也微乎其微,所以笔者觉得没有必要为了这个业务层完全独立于调用层的过高目标而去搞一个额外的隔离层,浪费了原材料不说,还将系统搞得过于复杂,相比于其它原则,"简单"始终是最大的一条原则。   getAllFile()负责获取T_FILE表所有记录,以便在网页上显示出来。   而getFileName(String fileId)和write(OutputStream os,String fileId)则用于下载某个特定的文件。具体的调用是将Web层将response.getOutputStream()传给write(OutputStream os,String fileId)接口,业务层直接将文件数据输出到这个响应流中。具体实现请参见错误!未找到引用源。节下载文件部分。   2、业务层接口实现类   FileService的实现类为FileServiceImpl,其中save(FileActionForm fileForm)的实现如下所示:   代码 8 业务接口实现类之save() 1. … 2. public class FileServiceImpl 3. implements FileService 4. { 5. private TfileDAO tfileDAO; 6. public void save(FileActionForm fileForm) 7. { 8. Tfile tfile = new Tfile(); 9. try 10. { 11. tfile.setFileContent(fileForm.getFileContent().getFileData()); 12. } 13. catch (FileNotFoundException ex) 14. { 15. throw new RuntimeException(ex); 16. } 17. catch (IOException ex) 18. { 19. throw new RuntimeException(ex); 20. } 21. tfile.setFileName(fileForm.getFileContent().getFileName()); 22. tfile.setRemark(fileForm.getRemark()); 23. tfileDAO.save(tfile); 24. } 25. … 26. }   在save(FileActionForm fileForm)方法里,完成两个步骤:   其一,象在水桶间倒水一样,将FileActionForm对象中的数据倒入到Tfile对象中;   其二,调用TfileDAO保存数据。   需要特别注意的是代码的第11行,FileActionForm的fileContent属性为org.apache.struts.upload.FormFile类型,FormFile提供了一个方便的方法getFileData(),即可获取文件的二进制数据。通过解读FormFile接口实现类DiskFile的原码,我们可能知道FormFile本身并不缓存文件的数据,只有实际调用getFileData()时,才从磁盘文件输入流中获取数据。由于FormFile使用流读取方式获取数据,本身没有缓存文件的所有数据,所以对于上传超大体积的文件,也是没有问题的;但是,由于数据持久层的Tfile使用byte[]来缓存文件的数据,所以并不适合处理超大体积的文件(如100M),对于超大体积的文件,依然需要使用java.sql.Blob类型以常规流操作的方式来处理。   此外,通过FileForm的getFileName()方法就可以获得上传文件的文件名,如第21行代码所示。   write(OutputStream os,String fileId)方法的实现,如代码 9所示:   代码 9 业务接口实现类之write() 1. … 2. public class FileServiceImpl 3. implements FileService 4. { 5. 6. public void write(OutputStream os, String fileId) 7. { 8. Tfile tfile = tfileDAO.findByFildId(fileId); 9. try 10. { 11. os.write(tfile.getFileContent()); 12. os.flush(); 13. } 14. catch (IOException ex) 15. { 16. throw new RuntimeException(ex); 17. } 18. } 19. … 20. }   write(OutputStream os,String fileId)也简单地分为两个操作步骤,首先,根据fileId加载表记录,然后将fileContent写入到输出流中。   3、Spring事务配置   下面,我们来看如何在Spring配置文件中为FileService配置声明性的事务 1. <beans> 2. … 3. <bean id="transactionManager" 4. class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 5. <property name="sessionFactory" ref="sessionFactory"/> 6. </bean> 7. <!-- 事务处理的AOP配置 //--> 8. <bean id="txProxyTemplate" abstract="true" 9. class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 10. <property name="transactionManager" ref="transactionManager"/> 11. <property name="transactionAttributes"> 12. <props> 13. <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> 14. <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop> 15. <prop key="save">PROPAGATION_REQUIRED</prop> 16. <prop key="write">PROPAGATION_REQUIRED,readOnly</prop> 17. </props> 18. </property> 19. </bean> 20. <bean id="fileService" parent="txProxyTemplate"> 21. <property name="target"> 22. <bean class="sshfile.service.FileServiceImpl"> 23. <property name="tfileDAO" ref="tfileDAO"/> 24. </bean> 25. </property> 26. </bean> 27. </beans>   Spring的事务配置包括两个部分:   其一,定义事务管理器transactionManager,使用HibernateTransactionManager实现事务管理;   其二,对各个业务接口进行定义,其实txProxyTemplate和fileService是父子节点的关系,本来可以将txProxyTemplate定义的内容合并到fileService中一起定义,由于我们的系统仅有一个业务接口需要定义,所以将其定义的一部分抽象到父节点txProxyTemplate中意义确实不大,但是对于真实的系统,往往拥有为数众多的业务接口需要定义,将这些业务接口定义内容的共同部分抽取到一个父节点中,然后在子节点中通过parent进行关联,就可以大大简化业务接口的配置了。   父节点txProxyTemplate注入了事务管理器,此外还定义了业务接口事务管理的方法(允许通过通配符的方式进行匹配声明,如前两个接口方法),有些接口方法仅对数据进行读操作,而另一些接口方法需要涉及到数据的更改。对于前者,可以通过readOnly标识出来,这样有利于操作性能的提高,需要注意的是由于父类节点定义的Bean仅是子节点配置信息的抽象,并不能具体实现化一个Bean对象,所以需要特别标注为abstract="true",如第8行所示。   fileService作为一个目标类被注入到事务代理器中,而fileService实现类所需要的tfileDAO实例,通过引用3.2节中定义的tfileDAO Bean注入。   Web层实现   1、Web层的构件和交互流程   Web层包括主要3个功能:   •上传文件。   •列出所有已经上传的文件列表,以供点击下载。   •下载文件。   Web层实现构件包括与2个JSP页面,1个ActionForm及一个Action:   •file-upload.jsp:上传文件的页面。   •file-list.jsp:已经上传文件的列表页面。   •FileActionForm:file-upload.jsp页面表单对应的ActionForm。   •FileAction:继承org.apache.struts.actions.DispatchAction的Action,这样这个Action就可以通过一个URL参数区分中响应不同的请求。   Web层的这些构件的交互流程如图 6所示: 图 6 Web层Struts流程图   其中,在执行文件上传的请求时,FileAction在执行文件上传后,forward到loadAllFile出口中,loadAllFile加载数据库中所有已经上传的记录,然后forward到名为fileListPage的出口中,调用file-list.jsp页面显示已经上传的记录。   2、FileAction功能   Struts 1.0的Action有一个弱项:一个Action只能处理一种请求,Struts 1.1中引入了一个DispatchAction,允许通过URL参数指定调用Action中的某个方法,如http://yourwebsite/fileAction.do?method=upload即调用FileAction中的upload方法。通过这种方式,我们就可以将一些相关的请求集中到一个Action当中编写,而没有必要为某个请求操作编写一个Action类。但是参数名是要在struts-config.xml中配置的: 1. <struts-config> 2. <form-beans> 3. <form-bean name="fileActionForm" type="sshfile.web.FileActionForm" /> 4. </form-beans> 5. <action-mappings> 6. <action name="fileActionForm" parameter="method" path="/fileAction" 7. type="sshfile.web.FileAction"> 8. <forward name="fileListPage" path="/file-list.jsp" /> 9. <forward name="loadAllFile" path="/fileAction.do?method=listAllFile" /> 10. </action> 11. </action-mappings> 12. </struts-config>   第6行的parameter="method"指定了承载方法名的参数,第9行中,我们还配置了一个调用FileAction不同方法的Action出口。   FileAction共有3个请求响应的方法,它们分别是:   •upload(…):处理上传文件的请求。   •listAllFile(…):处理加载数据库表中所有记录的请求。   •download(…):处理下载文件的请求。   下面我们分别对这3个请求处理方法进行讲解。   2.1 上传文件   上传文件的请求处理方法非常简单,简之言之,就是从Spring容器中获取业务层处理类FileService,调用其save(FileActionForm form)方法上传文件,如下所示: 1. public class FileAction 2. extends DispatchAction 3. { 4. //将上传文件保存到数据库中 5. public ActionForward upload(ActionMapping mapping, ActionForm form, 6. HttpServletRequest request, 7. HttpServletResponse response) 8. { 9. FileActionForm fileForm = (FileActionForm) form; 10. FileService fileService = getFileService(); 11. fileService.save(fileForm); 12. return mapping.findForward("loadAllFile"); 13. } 14. //从Spring容器中获取FileService对象 15. private FileService getFileService() 16. { 17. ApplicationContext appContext = WebApplicationContextUtils. 18. getWebApplicationContext(this.getServlet().getServletContext()); 19. return (FileService) appContext.getBean("fileService"); 20. } 21. … 22. }   由于FileAction其它两个请求处理方法也需要从Spring容器中获取FileService实例,所以我们特别提供了一个getFileService()方法(第15~21行)。重构的一条原则就是:"发现代码中有重复的表达式,将其提取为一个变量;发现类中有重复的代码段,将其提取为一个方法;发现不同类中有相同的方法,将其提取为一个类"。在真实的系统中,往往拥有多个Action和多个Service类,这时一个比较好的设置思路是,提供一个获取所有Service实现对象的工具类,这样就可以将Spring 的Service配置信息屏蔽在一个类中,否则Service的配置名字散落在程序各处,维护性是很差的。   2.2 列出所有已经上传的文件   listAllFile方法调用Servie层方法加载T_FILE表中所有记录,并将其保存在Request域中,然后forward到列表页面中: 1. public class FileAction 2. extends DispatchAction 3. { 4. … 5. public ActionForward listAllFile(ActionMapping mapping, ActionForm form, 6. HttpServletRequest request, 7. HttpServletResponse response) 8. throws ModuleException 9. { 10. FileService fileService = getFileService(); 11. List fileList = fileService.getAllFile(); 12. request.setAttribute("fileList",fileList); 13. return mapping.findForward("fileListPage"); 14. } 15. }   file-list.jsp页面使用Struts标签展示出保存在Request域中的记录: 1. <%@page contentType="text/html; charset=GBK"%> 2. <%@taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%> 3. <%@taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%> 4. <html> 5. <head> 6. <title>file-download</title> 7. </head> 8. <body bgcolor="#ffffff"> 9. <ol> 10. <logic:iterate id="item" name="fileList" scope="request"> 11. <li> 12. <a href='fileAction.do?method=download&fileId= 13. <bean:write name="item"property="fileId"/>'> 14. <bean:write name="item" property="fileName"/> 15. </a> 16. </li> 17. </logic:iterate> 18. </ol> 19. </body> 20. </html>   展现页面的每条记录挂接着一个链接地址,形如:fileAction.do?method=download&fileId=xxx,method参数指定了这个请求由FileAction的download方法来响应,fileId指定了记录的主键。   由于在FileActionForm中,我们定义了fileId的属性,所以在download响应方法中,我们将可以从FileActionForm中取得fileId的值。这里涉及到一个处理多个请求Action所对应的ActionForm的设计问题,由于原来的Action只能对应一个请求,那么原来的ActionForm非常简单,它仅需要将这个请求的参数项作为其属性就可以了,但现在一个Action对应多个请求,每个请求所对应的参数项是不一样的,此时的ActionForm的属性就必须是多请求参数项的并集了。所以,除了文件上传请求所对应的fileContent和remark属性外还包括文件下载的fileId属性: 图 7 FileActionForm   当然这样会造成属性的冗余,比如在文件上传的请求中,只会用到fileContent和remark属性,而在文件下载的请求时,只会使用到fileId属性。但这种冗余是会带来好处的--它使得一个Action可以处理多个请求。   2.3 下载文件   在列表页面中点击一个文件下载,其请求由FileAction的download方法来响应,download方法调用业务层的FileService方法,获取文件数据并写出到response的响应流中。通过合理设置HTTP响应头参数,将响应流在客户端表现为一个下载文件对话框,其代码如下所示:   代码 10 业务接口实现类之download 1. public class FileAction 2. extends DispatchAction 3. { 4. … 5. public ActionForward download(ActionMapping mapping, ActionForm form, 6. HttpServletRequest request, 7. HttpServletResponse response) 8. throws ModuleException 9. { 10. FileActionForm fileForm = (FileActionForm) form; 11. FileService fileService = getFileService(); 12. String fileName = fileService.getFileName(fileForm.getFileId()); 13. try 14. { 15. response.setContentType("application/x-msdownload"); 16. response.setHeader("Content-Disposition", 17. "attachment;" + " filename="+ 18. new String(fileName.getBytes(), "ISO-8859-1")); 19. fileService.write(response.getOutputStream(), fileForm.getFileId()); 20. } 21. catch (Exception e) 22. { 23. throw new ModuleException(e.getMessage()); 24. } 25. return null; 26. } 27. }   第15~18行,设置HTTP响应头,将响应类型设置为application/x-msdownload MIME类型,则响应流在IE中将弹出一个文件下载的对话框,如图 4所示。IE所支持的MIME类型多达26种,您可以通过这个网址查看其他的MIME类型: http://msdn.microsoft.com/workshop/networking/moniker/overview/appendix_a.asp。   如果下载文件的文件名含有中文字符,如果不对其进行硬编码,如第18行所示,客户文件下载对话框中出现的文件名将会发生乱码。 第19行代码获得response的输出流,作为FileServie write(OutputStream os,String fileId)的入参,这样文件的内容将写到response的输出流中。   3、web.xml文件的配置 Spring容器在何时启动呢?我可以在Web容器初始化来执行启动Spring容器的操作,Spring提供了两种方式启动的方法:   •通过org.springframework.web.context .ContextLoaderListener容器监听器,在Web容器初始化时触发初始化Spring容器,在web.xml中通过<listener></listener>对其进行配置。   •通过Servlet org.springframework.web.context.ContextLoaderServlet,将其配置为自动启动的Servlet,在Web容器初始化时,通过这个Servlet启动Spring容器。   在初始化Spring容器之前,必须先初始化log4J的引擎,Spring也提供了容器监听器和自动启动Servlet两种方式对log4J引擎进行初始化:  •org.springframework.web.util .Log4jConfigListener  •org.springframework.web.util.Log4jConfigServlet   下面我们来说明如何配置web.xml启动Spring容器:   代码 11 web.xml中对应Spring的配置内容 1. <web-app> 2. <context-param> 3. <param-name>contextConfigLocation</param-name> 4. <param-value>/WEB-INF/applicationContext.xml</param-value> 5. </context-param> 6. <context-param> 7. <param-name>log4jConfigLocation</param-name> 8. <param-value>/WEB-INF/log4j.properties</param-value> 9. </context-param> 10. <servlet> 11. <servlet-name>log4jInitServlet</servlet-name> 12. <servlet-class>org.springframework.web.util.Log4jConfigServlet</servlet-class> 13. <load-on-startup>1</load-on-startup> 14. </servlet> 15. <servlet> 16. <servlet-name>springInitServlet</servlet-name> 17. <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class> 18. <load-on-startup>2</load-on-startup> 19. </servlet> 20. … 21. </web-app>   启动Spring容器时,需要得到两个信息:Spring配置文件的地址和Log4J属性文件,这两上信息分别通过contextConfigLocationWeb和log4jConfigLocation容器参数指定,如果有多个Spring配置文件,则用逗号隔开,如: /WEB-INF/applicationContext_1.xml, /WEB-INF/applicationContext_1.xm2   由于在启动ContextLoaderServlet之前,必须事先初始化Log4J的引擎,所以Log4jConfigServlet必须在ContextLoaderServlet之前启动,这通过<load-on-startup>来指定它们启动的先后顺序。   乱码是开发Web应用程序一个比较老套又常见问题,由于不同Web应用服务器的默认编码是不一样的,为了方便Web应用在不同的Web应用服务器上移植,最好的做法是Web程序自身来处理编码转换的工作。经典的作法是在web.xml中配置一个编码转换过滤器,Spring就提供了一个编码过滤器类CharacterEncodingFilter,下面,我们为应用配置上这个过滤器: 1. <web-app> 2. … 3. <filter> 4. <filter-name>encodingFilter</filter-name> 5. <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 6. <init-param> 7. <param-name>encoding</param-name> 8. <param-value>GBK</param-value> 9. </init-param> 10. </filter> 11. <filter-mapping> 12. <filter-name>encodingFilter</filter-name> 13. <url-pattern>/*</url-pattern> 14. </filter-mapping> 15. … 16. </web-app>   Spring的过滤器类是org.springframework.web.filter.CharacterEncodingFilter,通过encoding参数指定编码转换类型为GBK,<filter-mapping>的配置使该过滤器截获所有的请示。   Struts的框架也需要在web.xml中配置,想必读者朋友对Struts的配置都很熟悉,故在此不再提及,请参见本文所提供的源码。   总结   本文通过一个文件上传下载的Web应用,讲解了如何构建基于SSH的Web应用,通过Struts和FormFile,Spring的LobHandler以及Spring为HibernateBlob处理所提供的用户类BlobByteArrayType ,实现上传和下载文件的功能仅需要廖廖数行的代码即告完成。读者只需对程序作稍许的调整,即可处理Clob字段:   •领域对象对应Clob字段的属性声明为String类型;   •映射文件对应Clob字段的属性声明为org.springframework.orm.hibernate3.support.ClobStringType类型。
1. jpivot加入其它应用中 解压jpivot.war 文件,在你的应用当中你需要下面的这些文件: /wcf/** /jpivot/** /WEB-INF/wcf/** /WEB-INF/jpivot/** /WEB-INF/lib/** 最后你还需要对比 jpivot.war 里的 web.xml 文件去修改你自己的 web.xml 文件。 2. 例子说明 一个Schema的xml文件,定义立方体;(feeSchema.xml) 一个jsp文件(fee.jsp),使用jpivot的标签(jp:mondrianQuery),写入多维查询表达式。 2.1. 数据库连接  直接用jdbc  在应用服务器中定义数据源 例子在tomcat中定义了数据源feeDS 2.2. 各种维度类型  一般维度 例子中定义了维度:[term].[所有终端] 按终端id统计缴费情况。  多level维度 例子中定义了维度:[agentTerm].[所有终端] 按代理商,终端两级统计缴费情况  有父子关系维度 例子中定义了维度:[area].[所有地区] 按组织机构级别统计缴费情况 在定义父子级别时,要指定parentColumn和nullParentValue 且要定义Closure,如果不定义,指标钻取的数据有问题。 Closure表针对维度表计算distance,Closure表的生产参见例子中的存储过程sp_zycreateorgclosure 例如: 3. Mondrian Schema详解 3.1. Schema Schema 定义了一个多维数据库。包含了一个逻辑模型,而这个逻辑模型的目的是为了书写 MDX 语言的查询语句。这个逻辑模型实际上提供了这几个概念: Cubes (立方体)、维度( Dimensions )、层次( Hierarchies )、级别( Levels )、和成员( Members )。而一个 schema 文件就是编辑这个 schema 的一个 xml 文件。在这个文件中形成逻辑模型和数据库物理模型的对应。 3.2. Cube 一个 Cube 是一系列维度 (Dimension) 和度量 (Measure) 的集合区域。在 Cube 中, Dimension 和 Measure 的共同地方就是共用一个事实表。 Cube 中的有以下几个属性: 属性名 含义 name Cube 的名字 caption 标题 , 在表示层显示的 cache 是否对 Cube 对应的实表用 mondrian 进行存储 , 默认为 true enabled 是布尔型的 , 如果是被激活 ,Cubes 就执行 , 否则就不予理睬,默认为 true Cube 里面有一个全局的标签定义了所用的事实表的表名 3.3. Dimension 他是一个层次( Hierarchies )的集合 , 维度一般有其相对应的维度表 . 他的组成是由层次( Hierarchies )而层次( Hierarchies )又是有级别( Level )组成 . 其属性如下: 属性名 含义 name Dimension 的名称 type 类型,有两个可选的类型: StandarDimension 和 TimeDimension ,默认为 StandardDimension caption 标题 , 在表示层显示的 UsagePrefix 加前缀 , 消除歧义 foreignKey 外键,对应事实表中的一个列,它通过 元素中的主键属性连接起来。 3.4. Hierarchy 你一定要指定其中的各种关系 , 如果没有指定 , 就默认 Hierarchy 里面装的是来自立方体中的真实表 . 属性如下: 属性名 含义 name Hierarchy 的名称,该值可以为空,为空时表示 Hirearchy 的名字和 Dimension 的名字相同。当一个 Dimension 有多个 Hierarchy 时,注意 name 值要唯一。 hasAll 布尔型的 , 决定是否包含全部的成员 member allMemberName 所有成员的名字 , 也就是总的标题 , 例如: allMemberName= “全部产品” allLevelName 所有级别的名字,它会覆盖其下所有的 Member 的 name 和所有的 Level 的 name 属性的值。 allMemberCaption 例如 : allMemberCaption= “全部产品”这个是在表示层显示的内容 PrimaryKey 通过主键来确定成员,该主键指的是成员表中的主键,该主键同时要与 Dimension 里设置的 foreignKey 属性对应的字段形成外键对应关系 primaryKeyTable 如果成员表不只一个,而是多个表通过 join 关系形成的,那么就要通过这个属性来指明 join 的这些表中,哪一个与 Dimension 里设置的 foreignKey 属性形成外键关系。通过该属性来指明主表 caption 标题 , 在表示层显示的 defaultMember memberReaderClass 设定一个成员读取器,默认情况下 Hierarchy 都是从关系型数据库里读取的,如果你的数据不在 RDBMS 里面的话,你可以通过自定义一个 member reader 来表现一个 Hierarchy 。 3.5. Level 级别 , 他是组成 Hierarchy 的部分。属性很多,并且是 schema 编写的关键,使用它可以构成一个结构树, Level 的先后顺序决定了 Level 在这棵树上的的位置,最顶层的 Level 位于树的第一级,依次类推。 Level 的属性如下: 属性名 含义 name 名称 table 该 Level 要使用的表名 column 用上面指定的表中某一列作为该 Level 的关键字 nameColumn 用来显示的时候使用,如果不定义,那么就采用上面的 column 的值来进行显示。 oridinalColumn 定义该 Level 上的成员的显示顺序,如果不指定,那么采用 column 的值。 parentColumn 在一个有父 - 子关系的 Hierarchy 当中,当前 Level 引用的是其父成员的列名。好比是一张部门表,在一张表里表现部门的上下级关系,一个是主键,肯定还有一个字段为连接到该主键的外键的列名,这里的 parentColumn 指的就是这个列名。 nullParentValue 如果当前的 Level 是有上下级关系(设置了 parentColumn 属性),如果该 Level 又处于顶级,我们需要将顶级的数据取出来,这里指的是位于顶级的父成员的值,有些数据库不支持 null, 那么也可以使用 ’0’ 或 ’-1’ 等,这就表示顶级的成员的父 ID 为 ’0’ 或为 ’-1’ 。 type 数据类型,默认值为 string 。当然还可以是 Numeric 、 Integer 、 Boolean 、 Date 等。 uniqueMembers 该属性用于优化产生的 SQL ,如果你知道这个级别和其父级别交叉后的值或者是维度表中给定的级别所有的值是唯一的,那么就可以设置该值为 true ,否则为 false 。 levelType 该 Level 的类型,默认为 regular (正常的),如果你在其 Dimension 属性 type 里选择了 TimeDimension 那么这里就可以选择 TimeYears 、 TimeQuarters 、 TimeMonth 、 TimeWeeds 、 TimeDays 。 hideMemberIf 在什么时候不隐藏该成员,可选的值有三个: Never 、 IfBlankName 、 IfParentName approxRowCount 该属性可以用来提高性能,可以通过指定一个数值以减少判断级别、层次、维度基数的时间,该属性在通过使用 XMLA 连接 Mondrian 很有用处。 caption 标题 , 在表示层显示的 captionColumn 用来显示标题的列 formatter 该属性定义了 Member.getCaption() 方法返回的动作值,这里需要是一个实现了 mondrian.olap.MemberFormatter 接口的类,用来对 Caption 地值进行格式化。 3.6. Join 对于一个 Hierarchy 来说,有两种方式为其指定:一种是直接通过一个 Table 标签指定;一种是通过 Join 将若干张表连接起来指定。一旦采用 Join 的话,那么就要在 Hierarchy 里的 primaryKeyTable 属性指定主表。 3.7. Measure Measure 就是我们要计算的数值,操作的核心。它的属性如下: 属性名 含义 name 名称 aggregator 要采用的计算函数 column 要计算的列名 formatString 计算结果的显示格式。 visible 是否可见 datatype 数据类型,默认为 Numeric formatter 采用类来对该 Measure 的值进行格式,具体参考 Level 的 formatter 属性。 caption 标题,用来显示时使用。 4. JPivot标签库使用详解 4.1. Introduce JPivot 是一套基于 Mondrian 的 OLAP 前端展现工具,它提供了一套标签库来解决的 OLAP 的展现层问题。 在一个 JSP 页面当中,如果要使用 JPivot 标签库,除了要配置相关的配置文件外,我们需要在 JSP 头的位置声明对 JPivot 标签库的引用,引用方法如下: <%@ taglib uri = "http://www.tonbeller.com/jpivot" prefix = "jp" %> JPivot 标签库包含以下几个标签: chart chooseQuery clickable destroyQuery mondrianQuery navitator print scalarQuery setParam table testQuery xmlaQuery 接下来我们将详细介绍这些标签的具体用法及相关参数的含义。 4.2. chart 创建一个图表组件,这个图表组件的标签不能直接产生可见输出,它必须通过 WCF 的渲染标签才行。 chart 标签的主要属性如下: 属性名 必须 数据类型 是否能动态计算指定 描述 id 是 String 是 标签的 ID visible 否 boolean 是 用来设置该组件是否可见,当该属性为 false 时 WCF 标签半角不显示该组件。 role 否 String 是 Role 的名称,如: role=”tomcat” 就允许当前用户角色为 tomcat 的访问该控件, role=”!tomcat” 则就允许除角色为 tomcat 以外的所有的用户访问。 query 是 OlapModel 是 一个 mondrianQuery 标签的 ID 值 baseDisplayURL 否 String 是 显示图表的链接,链接的后面还要添加参数“ ?=filename=[ 临时图表文件的名称 ] ” controllerURL 否 String 是 链接到 JPivot Controller 的 URL ,该属性在一些复杂环境下比如 Portal 之类就很有用。 4.3. chooseQuery 从前面创建的若干个查询当中选择一个合适的查询(通过查询名称进行选择),例如: SELECT ... SELECT ... ... 它的属性比较简单,只有 id 和 queryName 两个,而且比较容易理解。 4.4. clickable 该标签的作用是给一个 dimension 或一个 level 里的所有的 members 加上超链,使得它们变的可以进行点击操作。生成的 URL 中包含这个 member 的唯一名称,这个标签必须要在一个 table 或一个 query 的标签里嵌套使用。 这个动作还依赖于该标签的 sessionParam 属性,如果该属性存在,那么参数值将在页面显示之前写到 com.tonbeller.jpivot.param.SessionParamPool 当中,如果不存在该属性,参数将会被编码到超链接当中。 例如: select .. from Sales 该标签的主要属性如下: 属性名 必须 数据类型 是否能动态计算指定 描述 urlPattern 否 String 是 该属性已过时,使用 page 属性替代它。 page 否 String 是 目标页面的名称,该属性的值必须要以“ / ”开始,如果不设置,那么会采用当前页面的名称。 uniqueName 否 String 是 一个 dimension 或 hierarchy 或 level 的唯一名称,用来标识哪个 member 将可以点击。 menuLabel 否 String 是 如果多次定义 clickable ,那么它将通过一个右键来进行显示,这里的值就是右键菜单的名称。 sessionParam 否 String 是 参数名称,它将会被回写到 com.tonbeller.jpivot.param.SessionParamPool 当中。 propertyName 否 String 是 该属性如果存在,该 member 属性的值将会被带到 com.tonbeller.jpivot.param.Parameter 的 sqlValue 的属性当中。 propertyPrefix 否 String 是 如果存在该属性,多个 SessionParam 将会被创建,每一个 member 的名字的开始部分将采用该属性的值。 providerClass 否 String 是 如果存在该属性,这个类的实例将会从当前的 member 中获取 SessionParam 对象的实例,这里的类必须实现 com.tonbeller.jpivot.table.navi.ClickableMember.ParameterProvider 接口。 4.5. destroyQuery 该标签的主要作用是销毁所有的查询,它的用法如下: 它只有一个 ID 属性。 4.6. mondrianQuery 该标签的主要作用是让 mondrian 执行指定的查询,该标签需要指定一个 JDBC 的 datasource 或单独指定 JDBC 连接的各个属性从而可以让 mondrian 连接到指定的数据库。 它的用法如下: select {[Measures].[Unit Sales], [Measures].[Store Cost], [Measures].[Store Sales]} on columns, {[Product].[All Products]} ON rows from Sales where ([Time].[1997]) 它的主要属性如下: 属性名 必须 数据类型 是否能动态计算指定 描述 id 是 String 是 标签的 ID jdbcDriver 否 String 是 要连接到目标数据库的驱动类名 jdbcUrl 否 String 是 目标数据库的 URL jdbcUser 否 String 是 登录数据库的用户名 jdbcPassword 否 String 是 登录数据库的密码 dataSource 否 String 是 可以连接到数据库的 JNDI 名称,如 jdbc/SampleDB ,当使用它时另外四个单独连接数据库的属性不可以再用。 catalogUri 是 String 是 Mondrian Schema 文件所在的路径,指定路径时需要从 web 应用的根开始,如 /WEB-INF/FoodMart.xml 。 role 否 String 是 指定角色,该角色的值来自 Mondrian Schema 里定义的角色。 dynResolver 否 String 是 指定一个用来解析 Mondrian Schema 里定义的动态变量的解析类。 connectionPooling 否 String 是 当该值为 false 时,将从连接池里阻止 Mondrian dynLocale 否 String 是 设置动态解析 Mondrian Schema 里的 Local dataSourceChangeListener 否 String 是 指定一个类用来检测 datasource 的变化。 queryName 否 String 是 这个属性允许保留多个查询,对于每一个查询,最后一个查询将会被存储下来,可以使用 chooseQuery 标签在查询间进行切换。 stackMode 否 boolean 是 如果设置为 false ,那么所有的查询将同时被处理;如果设置为 true ,那么将保持其原有的顺序,例如: 查询名称为 qn1 的创建后,标签就马上将其显示出来 ; 接下来创建 qn2 ,现在 stack 里有 qn1 和 qn2 , qn2 也将被显示出来。 4.7. navigator 创建一个导航组件,这个组件不会直接输出,它必须通过 WCF 的标签来 render 输出。 示例用法: 该标签的主要属性如下: 属性名 必须 数据类型 是否能动态计算指定 描述 id 是 String 是 标签的 ID query 是 OlapModel 是 指定一个 mondrianQuery 标签的名称 visible 否 boolean 是 设置该组件是否可见,如果设置为 false 那么 WCF 的 render 标签将不会 render 该组件。 role 否 String 是 角色的名称,可以加前缀“!”,表示“除 … 角色之外”都可以访问该组件,否则就是只有该角色才能访问该组件。 4.8. print 创建一个打印控件,该控件不能直接输出,你必须创建 WCF form 来调用打印的 servlet 来生成 XLS/PDF 文件。 该标签只有一个 ID 属性。 4.9. scalarQuery 创建一个包含由单个单元格的组成的 OLAP 结果的会话属性,它的值由标签的属性提供。 示例用法: formattedValue="#{some.bean.otherProperty}" caption="Some Caption" /> 它的主要属性如下: 属性名 必须 数据类型 是否能动态计算指定 描述 id 是 String 是 标签的 ID value 是 String 是 是一个 EL 的表达式,通过 EL 表达式来计算值,这里是一个数字。 formattedValue 否 String 是 用一个 EL 表达式来计算格式化后的值,是一个字符串 caption 否 String 是 用一个 EL 表达式来生成 caption 的值,这里是一个字符串 queryName 否 String 是 请参考 mondrianQuery 标签 stackMode 否 boolean 是 请参考 mondrianQuery 标签 4.10. selectProperties 创建一个选择属性的组件,该组件不会直接输出,必须要与 WCF 的 render 标签结合。 示例用法: 它的主要属性如下: 属性名 必须 数据类型 是否能动态计算指定 描述 id 是 String 是 标签的 ID table 是 TableComponent 是 该属性用来指定一个表格组件。 visible 否 boolean 是 设置该组件是否可见,如果设置为 false 那么 WCF 组件将不会显示该组件。 role 否 String 是 指定角色,同样可以使用“!” 4.11. setParam 通过 get 或 post 或 session 里的值为 MDX 查询设置查询参数。示例用法: SELECT ... Parameter("Param01", ... WHERE ... or 它的主要属性如下: 属性名 必须 数据类型 是否能动态计算指定 描述 query 是 OlapModel 是 指定一个 mondrianQuery 标签的名称 httpParam 否 String 是 http 参数的名称,如果存在该属性,那么它的值将会被告解析并设置到 mdx 查询的 parameter 当中 sessionParam 否 String 是 Session 的参数名称 mdxParam 是 String 是 MDX 的 parameter 的名称 4.12. table 创建一个 pivot 的表格组件,该组件要借助于 WCF 的 render 标签输出。 示例用法: 该标签的主要属性如下: 属性名 必须 数据类型 是否能动态计算指定 描述 id 是 String 是 标签的 ID visible 否 boolean 是 设置组件是否可见,如果为 false 那么 WCF 的标签将不会将其 render 。 role 否 String 是 可访问该组件的角度,支持“!”用法 query 是 OlapModel 是 指定一个 mondrianQuery 标签的名称 5. 多维查询表达式 5.1. 什么是 MDX MDX 的全称是 Mutil Dimensional Expressions ,是由 Microsoft , Hyperion 等公司研究多维查询表达式,是所有 OLAP 高级分析所采用的核心查询语言。 MDX 可以用来进行以下操作: 1. calculated members (计算成员) 2. Calculated Cells (计算单元) 3. Security Settion (安全设置) 4. Custom member formula ( 自定义函数 ) 5. Custom level rollup () 6. Actions (动作) 7. Named “ sets ” ( 命名集合 ) 8. Server side formatting (服务器数据格式化) 5.2. MDX 的基本结构 MDX 的基本结构有三种: Members 、 Tuple 、 Set 。 5.2.1. Members 指的是维度树上的一个节点,这里有一点需要指出,量度也是一个特殊的维度,所以对于普通维度上的 Member 可以有几下几种表示方法: [Customer] 或 [Time].[1996] 等,对于特殊的维度——量度而言,也可以表示一个 Member ,如: [Measures].[ unit sales] 等。 Member 的表示方法就是用中括号的形式,“ [……] ”。 5.2.2. Tuple Tuple :是由若干个 Members 组成,每一个维度上最多只能有一个 Member ,对于一个 Tuple 而言至少有一个维度,多则不限,同时对于没有列出来的那就表示为默认的 Member 。 Tuple 的表示方法是小括号“ () ”,因为其又有 Member 构成,所以通常的格式为“ ([…],[…][…],…) ”;示例: a) ([Regin ].[USA]) b) ([product].[ computers],[time].[2008]) 。 5.2.3. Sets 同一维度上若干个 Members 的集合,或者是若干个 Tuples 的集合,但这里有一个地方需要注意,那就是如果是若干个 Tuple 组成的集合是,各个 Tuple 里的 Member 之间存在着一定的对应关系。集合的表示方法用大括号“ {} ”,所以可能的表示方法为: a) {[time].[2008],[time].[2009],[time].[2000]} ,这里 Set 是由同一维度的若干个 Member 组成。 b) {([computer],[usa ]),([ mobile],[china])} ,这个 Set 是由两个 Tuples 组成,这里大家可以看到,在第一个 Tuple 当中,第一个 Member 是名为 computer 的产品,所以后面的 Tuple 的第一个 Member 也必须是一个产品,所以我们这里看到的是 mobile ,第一个 Tuple 里第二个 Member 是一个国家,所以第二个 Tuple 的第二个 Member 也必须是一个国家名,依次类推。 5.3. MDX 查询语句 一个标准的 MDX 查询语句就是由我们前面介绍的 MDX 的三个基本对象构成,也就是 Member 、 Tuple 、 Set 。 一个标准的 MDX 查询的语法如下: SELECT Set ON COLUMNS, Set ON ROWS FROM Cube WHERE Tuple 示例: SELECT {[time].[ 1997],[time].[1998]} ON COLUMNS, {([product].[drink],[customer].[gender].[F]),( [product].[food],[customer].[gender].[M])} ON ROWS FROM [Sales] WHERE ([Measures].[ StoreSales ]) 这个 MDX 查询就表示:查询时间为 1997 和 1998 两年的 drink 类产品,女消费者和 food 类产品男消费者的 Store Sales 。 5.4. MDX 表达式 5.4.1. children 它的作用是列出指定分类下的所有项,如 1. [product type].[food].children ,这就表示列出产品类型为 food 的所有产品。 2. [region ].[country].children ,这就表示列出所有的国家名称。 该函数不能用在 Tuple 中 5.4.2. CurrentMember 当前某个 Member ,举例: 某个产品销售额贡献度计算,对于某一个产品或一种类型的产品而言它的销售额贡献度指的是它的销售额与所有产品销售额的比值,此时,如果我们要用 MDX 来计算这个贡献度的值是可以采用 CurrentMember 来实现,如: 此时我们的 MDX 表达可以是: ([product].currentMember ,[ Measures].[stores sales]) / ([product].[ all products],[Measures].[stores sales]) 同时因为默认情况下指的就是 currentMember ,所以上面的 currentMember 可以去掉: ([Measures].[ stores sales]) / ([product].[ all products],[Measures].[stores sales]) 所有 MDX 表达式的写法是由若干个 Tuple 组成 ,比如上面的这个表达式就是由两个 Tuple 的相除构成。 5.4.3. prevMember 和 nextMember 这两个元素分别指的是当前 Member 的上一个 Member 和当前 Member 的下一个 Member 。 利用这两个元素我们可以来实现实际当中常见的同期比(前期比),所谓的同期比指的 是: (当前 member- 当前 member 的前一个 member ) / 当前 member 的前一个 member 对应到 MDX 表达式我们可以用 prevMember 和 nextMember 来实现,如: (([time].currentMember ,[ Measures].[store sales])-([time].prevMember ,[Measures].[storessales ])) / ([time].prevMember ,[ Measures].[store sales]) 当然因为 currentMember 可以省略,所以上面的写法也可以是: (([ Measures ].[store sales])-([time].prevMember ,[Measures].[storessales ])) / ([time].prevMember ,[ Measures].[store sales]) 5.4.4. Parents 、 FirstChild 、 Descendants 下面的这张图就显示一个树 这张图中 all 这个节点是 97 和 98 的 parents , 97 和 98 也是 all 的 children ,同时 97 是 all 的 firstChild 如果我们要取到 97 下的所有的月份,那么我们就需要用 Descendants 来实现,它的语法是: Descendants( [time].[97],month) ,这里的 month 是一个 level 的名称。所有它的格式为: Descendants([ 一个 Member], 一个 level 的名称 ) 比如在食品销售当中,我们要知道哪一类的食品更受消费者欢迎,这样我们需要只知道各类食品的销量,比如食物( food )、饮料 (drink) 等的具体销量 (unit sales) ,然后再计算出所有食品总的销量( total sales ) , 如果采用 MDX 我们可以很容易的实现,采用 MDX 后的计算哪种食品更受欢迎的表达式如下: ([food].currentMember ,[ Measures].[unit sales]) / ([food].currentMember.parent ,[ Measures].[total sales]) 因为 currentMember 可以省略,所以上面的写法又可以是: ( [ Measures].[unit sales]) / ([food].currentMember.parent ,[ Measures].[total sales]) 5.5. 参见MDX的基本语法及概念.pdf 6. 常见问题 6.1. MDX编辑器中文乱码  web.xml中添加过滤器 Set Character Encoding com.tonbeller.wcf.charset.CharsetFilter <init-param> encoding UTF-8 init-param> Set Character Encoding /* 注意,该filter-mapping放在所有filter-mapping的最前面  jsp页面编码用UTF-8(testpage.jsp) 6.2. pdf中文显示“#” 1).在WEB-INF\jpivot\print目录下建立userconfig.xml文件。内容如下:(仅配置黑体,fontBaseDir属性我也没有配置,源码中有) 2). 下载Apache的FOP包,应用所需JAR包,生成simhei.xml。Java命令行为: java -cp D:\op\fop.jar;D:\op\avalon-framework-4 .2.0.jar;D:\op\commons-logging-1.0.4.jar;D:\op\commons-io-1.1.jar;D:\op\xmlgraph ics-commons-1.4.jar org.apache.fop.fonts.apps.TTFReader C:\WINDOWS\Fonts\simhei. ttf simhei.xml 3). 将simhei.xml,以及simhei.ttf文件放到WEB-INF\jpivot\print目录下。 4 ). 修改WEB-INF\jpivot\table目录下的fo_mdxtable.xsl文件,把所有的font-family对应成SimHei,(以SimHei开头)。 6.3. pdf格式修改(原格式行太宽) 修改jpivot\WebRoot\WEB-INF\jpivot\table\fo_mdxtable.xsl 中font-size,line-height,padding 6.4. 钻取数据的格式化 在利用jpivot钻取的时候,发现出来的数字数据都精确到了小数点后两位,我可不想所有数字都这么精确,连带出来的id值也被这样格式化了,悲惨! 我的解决方法:取消格式定义 找到wcf.jar,修改com.tonbeller.wfc.format.config.xml 删除double和nandouble的定义 6.5. mondrian 的志的配置 将log4j.xml放置到web-inf/classes下 文件内容如下: 6.6. schema workbench schema的编辑器,界面化编辑schema。 但不知道为什么,在我的本机上,在打开,新建schema文件,都非常慢。 可以到http://sourceforge.net/projects/mondrian/files/下载,最新版本psw-ce-3.2.1.13885 7. 参考资料 http://mondrian.pentaho.com/documentation/ mondrian的官方网站 http://jacky6024.javaeye.com/ 比较有用的中文资料

67,512

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧