【適用范圍】IE,JSP
【問題描述和定位】業務需要把一個html頁面中的內容導出到excle文件里面,一個常用的方法是在需要導出的jsp頁面中增加:
response.setContentType("application/vnd.ms-Excel; charset=gb2312");
response.setHeader("Content-disposition","attachment;filename=excel文件名.xls");
但是經常遇到會把身份證等數字比較長的數據改成科學計數法來顯示。
【解決方案和步驟】
在html頁面里面加上如下css,然后在出現問題的字段應用這種style就可以了。
<style type="text/css">
<!--
td {
background-color: #FFFFFF;
}
.txt
{padding-top:1px;
padding-right:1px;
padding-left:1px;
mso-ignore:padding;
color:black;
font-size:11.0pt;
font-weight:400;
font-style:normal;
text-decoration:none;
font-family:宋體;
mso-generic-font-family:auto;
mso-font-charset:134;
mso-number-format:"\@"; //關鍵是這里
text-align:general;
vertical-align:middle;
mso-background-source:auto;
mso-pattern:auto;
white-space:nowrap;}
-->
</style>
示例:
<TD class="txt" align="center">
<bean:write id="ResultSet" property="VW_SETTLHINT/DEALBILLID"/>
</TD>
posted @
2009-08-07 16:26 xzc 閱讀(2245) |
評論 (0) |
編輯 收藏
試共同條件:
數據總數為110011條,每條數據條數為19個字段。
電腦配置為:P4 2.67GHz,1G內存。
一、POI、JXL、FastExcel比較
POI、JXL、FastExcel均為java第三方開源導出Excel的開源項目。
導出方案一:一次性全部導出到一個Excel文件中。
實際情況均報OutOfMemery錯誤,以下數據為報OutOfMemery數據時,數據到的最大數據數目,如表1所示:
表1:報OutOfMemery錯誤時所能處理的數據量
|
FastExecl |
POI |
JXL |
10000數據/sheet |
37465 |
28996 |
42270 |
5000數據/sheet |
39096 |
31487 |
46270 |
3000數據/sheet |
39000 |
32493 |
47860 |
小結:
多分sheet能一定程度上減少內存的使用,但是均因為程序中創建的Cell(即為Excel中的一個單元格)無法釋放,消耗大量內存,導致OutOfMemery錯誤;JXL表現最好,創建Cell內存使用較少。
導出方案二:先分多個Excel文件將數據全部導出,然后對多個Excel文件進行合并。
首先,測試將全部數據導出所用的時間,如表2所示,數據均測試三次取平均。
表2:導出全部數據所用時間
|
FastExecl |
POI |
JXL |
10000數據/文件 |
68s |
33s |
30s |
5000數據/文件 |
68s |
32s |
33s |
3000數據/文件 |
59s |
33s |
39s |
小結:
均成功導出Excel文件,原因是導出一個Excel文件,釋放所占用的創建Cell的內存。
FastExecl表現最差,POI表現穩定,JXL隨著數據的增大,速度一定程度上增快。
然后,進行整合,由于將多Excel合并成一個Excel文件的功能只有POI所有,故使用POI測試,結果如表3所示。
注:數據量大合并還會報OutOfMemery錯誤,故合并總數據量以5萬為準。
表3:合并5萬數據所用時間
|
時間 |
10000數據/文件 |
11s |
5000數據/文件 |
11s |
3000數據/文件 |
11s |
小結:
使用POI對文件進行合并速度較快,但有數據量的限制。
總結:方案二比較可行,但是數據量有限制,為5萬條。
二、導出XML 的電子表格
導出的格式類似為純文本,能實現大數據量的存儲,并能實現分Sheet查看,且能添加簡單的樣式,符合項目要求。經實際測試Excel2003和Excel2007均能識別并正常打開查看。使用時間測試如表4所示,數據均測試3次取平均。
表4:生成全部數據所用時間
|
時間 |
10000數據/sheet |
28.0秒 |
20000數據/sheet |
30.1秒 |
30000數據/sheet |
28.1秒 |
40000數據/sheet |
26.5秒 |
50000數據/shee |
28.2秒 |
55000數據/sheet |
26.8秒 |
59000數據/sheet |
30.1秒 |
59500數據/sheet |
發生假死機現象 |
60000數據/sheet |
發生假死機現象 |
但是導出的數據為XML不是純正的Excel文件,如使用Excel文件的xls后綴保存,打開文件會彈出警告,但不影響閱讀。
且經實際測試,在Access2007和Access2003中可通過導入外部數據的方式,將導出的XML導入進Access數據庫。
三、總結
項目要求是大數據量導出Excel文件,POI、JXL、FastExcel不能完全滿足要求;使用XML 的電子表格導出實現了大數據量導出,但是格式為XML不是純正的Excel文件,為曲線救國。兩種導出形式的比較,如表5所示。
表5:合并5萬數據所用時間
|
POI、JXL、FastExcel |
XML 的電子表格 |
導出數據格式 |
為純Execl文件 |
為XML文件 |
導出數據量 |
小 |
較大 |
能否分Sheet |
能 |
能 |
能否添加樣式 |
能 |
能 |
能否添加圖片 |
POI 能 |
不能 |
導出數據能否導入Access |
能 |
能 |
|
posted @
2009-08-07 15:57 xzc 閱讀(1810) |
評論 (0) |
編輯 收藏
研究了很久新出的 Spring 2.5, 總算大致明白了如何用標注定義 Bean, 但是如何定義和注入類型為 java.lang.String 的 bean 仍然未解決, 希望得到高人幫助.
總的來看 Java EE 5 的標注開發方式開來是得到了大家的認可了.
@Service 相當于定義 bean, 自動根據 bean 的類名生成一個首字母小寫的 bean
@Autowired 則是自動注入依賴的類, 它會在類路徑中找成員對應的類/接口的實現類, 如果找到多個, 需要用 @Qualifier("chineseMan") 來指定對應的 bean 的 ID.
一定程度上大大簡化了代碼的編寫, 例如一對一的 bean 映射現在完全不需要寫任何額外的 bean 定義了.
下面是代碼的運行結果:
man.sayHello()=抽你丫的
SimpleMan said: Hi
org.example.EnglishMan@12bcd4b said: Fuck you!
代碼:
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config/>
<context:component-scan base-package="org.example"/>
</beans>
測試類:
import org.example.IMan;
import org.example.SimpleMan;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
SimpleMan dao = (SimpleMan) ctx.getBean("simpleMan");
System.out.println(dao.hello());
IMan man = (IMan) ctx.getBean("usMan");
System.out.println(man.sayHello());
}
}
自動探測和注入bean的類:
package org.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class SimpleMan {
// 自動注入名稱為 Man 的 Bean
@Autowired(required = false)
@Qualifier("chineseMan")
//@Qualifier("usMan")
private IMan man;
/**
* @return the man
*/
public IMan getMan() {
return man;
}
/**
* @param man the man to set
*/
public void setMan(IMan man) {
this.man = man;
}
public String hello() {
System.out.println("man.sayHello()=" + man.sayHello());
return "SimpleMan said: Hi";
}
}
一個接口和兩個實現類:
package org.example;
/**
* 抽象的人接口.
* @author BeanSoft
* @version 1.0
*/
public interface IMan {
/**
* 打招呼的抽象定義.
* @return 招呼的內容字符串
*/
public String sayHello();
}
package org.example;
import org.springframework.stereotype.Service;
/**
* 中國人的實現.
* @author BeanSoft
*/
@Service
public class ChineseMan implements IMan {
public String sayHello() {
return "抽你丫的";
}
}
package org.example;
import org.springframework.stereotype.Service;
/**
* @author BeanSoft
* 美國大兵
*/
@Service("usMan")
// 這里定義了一個 id 為 usMan 的 Bean, 標注里面的屬性是 bean 的 id
public class EnglishMan implements IMan {
public String sayHello() {
return this + " said: Fuck you!";
}
}
posted @
2009-06-25 15:06 xzc 閱讀(331) |
評論 (0) |
編輯 收藏
Spring中autowire屬性
|
 |
default-autowire="x"
x有4個選擇:byName,byType,constructor和autodetect
我感覺byName和byType用的多點
1. byName:
Service.java
public class Service
{
Source source;
public void setSource(Source source)
{
this.source = source;
}
}
|
applicationContext.xml
<beans
...
default-autowire="byName">
<bean id="source" class="cn.hh.spring.DBCPSource" scope="prototype"/>
<bean id="service" class="cn.hh.spring.Service" scope="prototype">
</bean>
</beans>
|
cn.hh.spring.DBCPSource實現了Source接口
xml中并沒有給 bean service配Source屬性,但在beans中設置了autowire="byName",這樣配置文件會自動根據 cn.hh.spring.Service 中的setSource找bean id="Source"的bean ,然后自動配上去,如果沒找到就不裝配。
注意:byName的name是java中setXxxx 的Xxxx, 和上面設置的Source source中source拼寫毫無關系,完全可以是
public class Service
{
Source source1;
public void setSource(Source source1)
{
this.source1 = source1;
}
}
|
結果相同。
2. byType:
Service.java同上
applicationContext.xml
<beans
...
default-autowire="byType">
<bean id="dbcpSource" class="cn.hh.spring.DBCPSource" scope="prototype"/>
<bean id="service" class="cn.hh.spring.Service" scope="prototype">
</bean>
</beans>
|
同樣沒有配置setSource,autowire改成 "byType",配置文件會找實現了Source接口的bean,這里 cn.hh.spring.DBCPSource 實現了Source接口,所以自動裝配,如果沒找到則不裝配。
如果同個配制文件中兩個bean實現了Source接口,則報錯。
這里的 Type是指setSource(Source source)中參數的類型。
3. constructor:
試圖在容器中尋找與需要自動裝配的bean的構造函數參數一致的一個或多個bean,如果沒找到則拋出異常。
4. autodetect:
首先嘗試使用constructor來自動裝配,然后再使用 byType方式。
|
|
posted @
2009-06-25 09:44 xzc 閱讀(2036) |
評論 (0) |
編輯 收藏
摘要: 不錯的資料,轉過來,方便日后查看使用!!!
--監控索引是否使用
alter index &index_name monitoring usage;
alter index &index_name nomonitoring usage;
select * from v$object_usage where index_name = &index_name;
--求...
閱讀全文
posted @
2009-06-18 15:09 xzc 閱讀(999) |
評論 (1) |
編輯 收藏
有關表分區的一些維護性操作:
一、添加分區
以下代碼給SALES表添加了一個P3分區
ALTER TABLE SALES ADD PARTITION P3 VALUES LESS THAN(TO_DATE('2003-06-01','YYYY-MM-DD'));
注意:以上添加的分區界限應該高于最后一個分區界限。
以下代碼給SALES表的P3分區添加了一個P3SUB1子分區
ALTER TABLE SALES MODIFY PARTITION P3 ADD SUBPARTITION P3SUB1 VALUES('COMPLETE');
二、刪除分區
以下代碼刪除了P3表分區:
ALTER TABLE SALES DROP PARTITION P3;
在以下代碼刪除了P4SUB1子分區:
ALTER TABLE SALES DROP SUBPARTITION P4SUB1;
注意:如果刪除的分區是表中唯一的分區,那么此分區將不能被刪除,要想刪除此分區,必須刪除表。
三、截斷分區
截斷某個分區是指刪除某個分區中的數據,并不會刪除分區,也不會刪除其它分區中的數據。當表中即使只有一個分區時,也可以截斷該分區。通過以下代碼截斷分區:
ALTER TABLE SALES TRUNCATE PARTITION P2;
通過以下代碼截斷子分區:
ALTER TABLE SALES TRUNCATE SUBPARTITION P2SUB2;
四、合并分區
合并分區是將相鄰的分區合并成一個分區,結果分區將采用較高分區的界限,值得注意的是,不能將分區合并到界限較低的分區。以下代碼實現了P1 P2分區的合并:
ALTER TABLE SALES MERGE PARTITIONS P1,P2 INTO PARTITION P2;
五、拆分分區
拆分分區將一個分區拆分兩個新分區,拆分后原來分區不再存在。注意不能對HASH類型的分區進行拆分。
ALTER TABLE SALES SBLIT PARTITION P2 AT(TO_DATE('2003-02-01','YYYY-MM-DD'))
INTO (PARTITION P21,PARTITION P22);
六、接合分區(coalesca)
結合分區是將散列分區中的數據接合到其它分區中,當散列分區中的數據比較大時,可以增加散列分區,然后進行接合,值得注意的是,接合分區只能用于散列分區中。通過以下代碼進行接合分區:
ALTER TABLE SALES COALESCA PARTITION;
七、重命名表分區
以下代碼將P21更改為P2
ALTER TABLE SALES RENAME PARTITION P21 TO P2;
九、跨分區查詢
select sum( *) from (
(select count(*) cn from t_table_SS PARTITION (P200709_1)
union all
select count(*) cn from t_table_SS PARTITION (P200709_2));
十、查詢表上有多少分區
SELECT * FROM useR_TAB_PARTITIONS WHERE TABLE_NAME='tableName'
十一、查詢索引信息
select object_name,object_type,tablespace_name,sum(value)
from v$segment_statistics
where statistic_name IN ('physical reads','physical write','logical reads')and object_type='INDEX'
group by object_name,object_type,tablespace_name
order by 4 desc
--顯示數據庫所有分區表的信息:
select * from DBA_PART_TABLES
--顯示當前用戶可訪問的所有分區表信息:
select * from ALL_PART_TABLES
--顯示當前用戶所有分區表的信息:
select * from USER_PART_TABLES
--顯示表分區信息 顯示數據庫所有分區表的詳細分區信息:
select * from DBA_TAB_PARTITIONS
--顯示當前用戶可訪問的所有分區表的詳細分區信息:
select * from ALL_TAB_PARTITIONS
--顯示當前用戶所有分區表的詳細分區信息:
select * from USER_TAB_PARTITIONS
--顯示子分區信息 顯示數據庫所有組合分區表的子分區信息:
select * from DBA_TAB_SUBPARTITIONS
--顯示當前用戶可訪問的所有組合分區表的子分區信息:
select * from ALL_TAB_SUBPARTITIONS
--顯示當前用戶所有組合分區表的子分區信息:
select * from USER_TAB_SUBPARTITIONS
--顯示分區列 顯示數據庫所有分區表的分區列信息:
select * from DBA_PART_KEY_COLUMNS
--顯示當前用戶可訪問的所有分區表的分區列信息:
select * from ALL_PART_KEY_COLUMNS
--顯示當前用戶所有分區表的分區列信息:
select * from USER_PART_KEY_COLUMNS
--顯示子分區列 顯示數據庫所有分區表的子分區列信息:
select * from DBA_SUBPART_KEY_COLUMNS
--顯示當前用戶可訪問的所有分區表的子分區列信息:
select * from ALL_SUBPART_KEY_COLUMNS
--顯示當前用戶所有分區表的子分區列信息:
select * from USER_SUBPART_KEY_COLUMNS
--怎樣查詢出oracle數據庫中所有的的分區表
select * from user_tables a where a.partitioned='YES'
--刪除一個表的數據是
truncate table table_name;
--刪除分區表一個分區的數據是
alter table table_name truncate partition p5;
注:分區根據具體情況選擇。
表分區有以下優點:
1、數據查詢:數據被存儲到多個文件上,減少了I/O負載,查詢速度提高。
2、數據修剪:保存歷史數據非常的理想。
3、備份:將大表的數據分成多個文件,方便備份和恢復。
4、并行性:可以同時向表中進行DML操作,并行性性能提高。
================================================
索引:
1、一般索引:
create index index_name on table(col_name);
2、Oracle 分區索引詳解
語法:Table Index
CREATE [UNIQUE|BITMAP] INDEX [schema.]index_name
ON [schema.]table_name [tbl_alias]
(col [ASC | DESC]) index_clause index_attribs
index_clauses:
分以下兩種情況
1. Local Index
就是索引信息的存放位置依賴于父表的Partition信息,換句話說創建這樣的索引必須保證父表是Partition
1.1 索引信息存放在父表的分區所在的表空間。但是僅可以創建在父表為HashTable或者composite分區表的。
LOCAL STORE IN (tablespace)
1.2 僅可以創建在父表為HashTable或者composite分區表的。并且指定的分區數目要與父表的分區數目要一致
LOCAL STORE IN (tablespace) (PARTITION [partition [LOGGING|NOLOGGING] [TABLESPACE {tablespace|DEFAULT}] [PCTFREE int] [PCTUSED int] [INITRANS int] [MAXTRANS int] [STORAGE storage_clause] [STORE IN {tablespace_name|DEFAULT] [SUBPARTITION [subpartition [TABLESPACE tablespace]]]])
1.3 索引信息存放在父表的分區所在的表空間,這種語法最簡單,也是最常用的分區索引創建方式。
Local
1.4 并且指定的Partition 數目要與父表的Partition要一致
LOCAL (PARTITION [partition
[LOGGING|NOLOGGING]
[TABLESPACE {tablespace|DEFAULT}]
[PCTFREE int]
[PCTUSED int]
[INITRANS int]
[MAXTRANS int]
[STORAGE storage_clause]
[STORE IN {tablespace_name|DEFAULT]
[SUBPARTITION [subpartition [TABLESPACE tablespace]]]])
Global Index
索引信息的存放位置與父表的Partition信息完全不相干。甚至父表是不是分區表都無所謂的。語法如下:
GLOBAL PARTITION BY RANGE (col_list)
( PARTITION partition VALUES LESS THAN (value_list)
[LOGGING|NOLOGGING]
[TABLESPACE {tablespace|DEFAULT}]
[PCTFREE int]
[PCTUSED int]
[INITRANS int]
[MAXTRANS int]
[STORAGE storage_clause] )
但是在這種情況下,如果父表是分區表,要刪除父表的一個分區都必須要更新Global Index ,否則索引信息不正確
ALTER TABLE TableName DROP PARTITION PartitionName Update Global Indexes
--查詢索引
select object_name,object_type,tablespace_name,sum(value)
from v$segment_statistics
where statistic_name IN ('physical reads','physical write','logical reads')and object_type='INDEX'
group by object_name,object_type,tablespace_name
order by 4 desc
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/thinker28754/archive/2009/03/06/3962309.aspx
posted @
2009-06-18 11:08 xzc 閱讀(2224) |
評論 (1) |
編輯 收藏
I、關系數據庫設計范式介紹
1.1 第一范式(1NF)無重復的列
所謂第一范式(1NF)是指數據庫表的每一列都是不可分割的基本數據項,同一列中不能有多個值,即實體中的某個屬性不能有多個值或者不能有重復的屬性。如果出現重復的屬性,就可能需要定義一個新的實體,新的實體由重復的屬性構成,新實體與原實體之間為一對多關系。在第一范式(1NF)中表的每一行只包含一個實例的信息。簡而言之,第一范式就是無重復的列。
說明:在任何一個關系數據庫中,第一范式(1NF)是對關系模式的基本要求,不滿足第一范式(1NF)的數據庫就不是關系數據庫。
1.2 第二范式(2NF)屬性完全依賴于主鍵[消除部分子函數依賴]
第二范式(2NF)是在第一范式(1NF)的基礎上建立起來的,即滿足第二范式(2NF)必須先滿足第一范式(1NF)。第二范式(2NF)要求數據庫表中的每個實例或行必須可以被惟一地區分。為實現區分通常需要為表加上一個列,以存儲各個實例的惟一標識。例如員工信息表中加上了員工編號(emp_id)列,因為每個員工的員工編號是惟一的,因此每個員工可以被惟一區分。這個惟一屬性列被稱為主關鍵字或主鍵、主碼。
第二范式(2NF)要求實體的屬性完全依賴于主關鍵字。所謂完全依賴是指不能存在僅依賴主關鍵字一部分的屬性,如果存在,那么這個屬性和主關鍵字的這一部分應該分離出來形成一個新的實體,新實體與原實體之間是一對多的關系。為實現區分通常需要為表加上一個列,以存儲各個實例的惟一標識。簡而言之,第二范式就是屬性完全依賴于主鍵。
1.3 第三范式(3NF)屬性不依賴于其它非主屬性[消除傳遞依賴]
滿足第三范式(3NF)必須先滿足第二范式(2NF)。簡而言之,第三范式(3NF)要求一個數據庫表中不包含已在其它表中已包含的非主關鍵字信息。例如,存在一個部門信息表,其中每個部門有部門編號(dept_id)、部門名稱、部門簡介等信息。那么在的員工信息表中列出部門編號后就不能再將部門名稱、部門簡介等與部門有關的信息再加入員工信息表中。如果不存在部門信息表,則根據第三范式(3NF)也應該構建它,否則就會有大量的數據冗余。簡而言之,第三范式就是屬性不依賴于其它非主屬性。
II、范式應用實例剖析
下面以一個學校的學生系統為例分析說明,這幾個范式的應用。首先第一范式(1NF):數據庫表中的字段都是單一屬性的,不可再分。這個單一屬性由基本類型構成,包括整型、實數、字符型、邏輯型、日期型等。在當前的任何關系數據庫管理系統(DBMS)中,傻瓜也不可能做出不符合第一范式的數據庫,因為這些DBMS不允許你把數據庫表的一列再分成二列或多列。因此,你想在現有的DBMS中設計出不符合第一范式的數據庫都是不可能的。
首先我們確定一下要設計的內容包括那些。學號、學生姓名、年齡、性別、課程、課程學分、系別、學科成績,系辦地址、系辦電話等信息。為了簡單我們暫時只考慮這些字段信息。我們對于這些信息,說關心的問題有如下幾個方面。
- 學生有那些基本信息
- 學生選了那些課,成績是什么
- 每個課的學分是多少
- 學生屬于那個系,系的基本信息是什么。
2.1 第二范式(2NF)實例分析
首先我們考慮,把所有這些信息放到一個表中(學號,學生姓名、年齡、性別、課程、課程學分、系別、學科成績,系辦地址、系辦電話)下面存在如下的依賴關系。
(學號)→ (姓名, 年齡,性別,系別,系辦地址、系辦電話)
(課程名稱) → (學分)
(學號,課程)→ (學科成績)
2.1.1 問題分析
因此不滿足第二范式的要求,會產生如下問題
數據冗余: 同一門課程由n個學生選修,"學分"就重復n-1次;同一個學生選修了m門課程,姓名和年齡就重復了m-1次。
更新異常:
1)若調整了某門課程的學分,數據表中所有行的"學分"值都要更新,否則會出現同一門課程學分不同的情況。
2)假設要開設一門新的課程,暫時還沒有人選修。這樣,由于還沒有"學號"關鍵字,課程名稱和學分也無法記錄入數據庫。
刪除異常 : 假設一批學生已經完成課程的選修,這些選修記錄就應該從數據庫表中刪除。但是,與此同時,課程名稱和學分信息也被刪除了。很顯然,這也會導致插入異常。
2.1.2 解決方案
把選課關系表SelectCourse改為如下三個表:
- 學生:Student(學號,姓名, 年齡,性別,系別,系辦地址、系辦電話);
- 課程:Course(課程名稱, 學分);
- 選課關系:SelectCourse(學號, 課程名稱, 成績)。
2.2 第三范式(3NF)實例分析
接著看上面的學生表Student(學號,姓名, 年齡,性別,系別,系辦地址、系辦電話),關鍵字為單一關鍵字"學號",因為存在如下決定關系:
(學號)→ (姓名, 年齡,性別,系別,系辦地址、系辦電話)
但是還存在下面的決定關系
(學號) → (所在學院)→(學院地點, 學院電話)
即存在非關鍵字段"學院地點"、"學院電話"對關鍵字段"學號"的傳遞函數依賴。
它也會存在數據冗余、更新異常、插入異常和刪除異常的情況。 (數據的更新,刪除異常這里就不分析了,可以參照2.1.1進行分析)
根據第三范式把學生關系表分為如下兩個表就可以滿足第三范式了:
學生:(學號, 姓名, 年齡, 性別,系別);
系別:(系別, 系辦地址、系辦電話)。
總結
上面的數據庫表就是符合I,II,III范式的,消除了數據冗余、更新異常、插入異常和刪除異常。
posted @
2009-01-04 11:53 xzc 閱讀(44881) |
評論 (17) |
編輯 收藏
自從接觸Java和JSP以來,就不斷與Java的中文亂碼問題打交道,現在終于得到了徹底的解決,現將我們的解決心得與大家共享。
一、Java中文問題的由來
Java的內核和class文件是基于unicode的,這使Java程序具有良好的跨平臺性,但也帶來了一些中文亂碼問題的麻煩。原因主要有兩方面,Java和JSP文件本身編譯時產生的亂碼問題和Java程序于其他媒介交互產生的亂碼問題。
首先Java(包括JSP)源文件中很可能包含有中文,而Java和JSP源文件的保存方式是基于字節流的,如果Java和JSP編譯成class文件過程中,使用的編碼方式與源文件的編碼不一致,就會出現亂碼。基于這種亂碼,建議在Java文件中盡量不要寫中文(注釋部分不參與編譯,寫中文沒關系),如果必須寫的話,盡量手動帶參數-ecoding GBK或-ecoding gb2312編譯;對于JSP,在文件頭加上<%@ page contentType="text/html;charset=GBK"%>或<%@ page contentType="text/html;charset=gb2312"%>基本上就能解決這類亂碼問題。
本文要重點討論的是第二類亂碼,即Java程序與其他存儲媒介交互時產生的亂碼。很多存儲媒介,如數據庫,文件,流等的存儲方式都是基于字節流的,Java程序與這些媒介交互時就會發生字符(char)與字節(byte)之間的轉換,例如從頁面提交表單中提交的數據在Java程序里顯示亂碼等情況。
如果在以上轉換過程中使用的編碼方式與字節原有的編碼不一致,很可能就會出現亂碼。
二、解決方法
對于流行的Tomcat來說,有以下兩種解決方法:
1) 更改 D:\Tomcat\conf\server.xml,指定瀏覽器的編碼格式為“簡體中文”:
方法是找到 server.xml 中的
<Connector port="8080" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true" URIEncoding='GBK' />
標記,粗體字是我添加的。
可以這樣驗證你的更改是否成功:在更改前,在你出現亂碼的頁面的IE瀏覽器,點擊菜單“查看|編碼”,會發現“西歐(ISO)”處于選中狀態。而更改后,點擊菜單“查看|編碼”,會發現“簡體中文(GB2312)”處于選中狀態。
b)更該 Java 程序,我的程序是這樣的:
public class ThreeParams extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html; charset=GBK");
...
}
}
粗體字是必需要有的,它的作用是讓瀏覽器把Unicode字符轉換為GBK字符。這樣頁面的內容和瀏覽器的顯示模式都設成了GBK,就不會亂碼了。
posted @
2008-11-22 20:39 xzc 閱讀(719) |
評論 (0) |
編輯 收藏
3.8.1. 利用MessageSource
實現國際化
ApplicationContext
接口擴展了MessageSource
接口,因而提供了消息處理的功能(i18n或者國際化)。與HierarchicalMessageSource
一起使用,它還能夠處理嵌套的消息,這些是Spring提供的處理消息的基本接口。讓我們快速瀏覽一下它所定義的方法:
-
String getMessage(String code, Object[] args, String default, Locale loc):用來從MessageSource
獲取消息的基本方法。如果在指定的locale中沒有找到消息,則使用默認的消息。args中的參數將使用標準類庫中的MessageFormat
來作消息中替換值。
-
String getMessage(String code, Object[] args, Locale loc):本質上和上一個方法相同,其區別在:沒有指定默認值,如果沒找到消息,會拋出一個NoSuchMessageException
異常。
-
String getMessage(MessageSourceResolvable resolvable, Locale locale)
:上面方法中所使用的屬性都封裝到一個MessageSourceResolvable
實現中,而本方法可以指定MessageSourceResolvable
實現。
當一個ApplicationContext
被加載時,它會自動在context中查找已定義為MessageSource
類型的bean。此bean的名稱須為messageSource
。如果找到,那么所有對上述方法的調用將被委托給該bean。否則ApplicationContext
會在其父類中查找是否含有同名的bean。如果有,就把它作為MessageSource
。如果它最終沒有找到任何的消息源,一個空的StaticMessageSource
將會被實例化,使它能夠接受上述方法的調用。
Spring目前提供了兩個MessageSource
的實現:ResourceBundleMessageSource
和StaticMessageSource
。它們都繼承NestingMessageSource
以便能夠處理嵌套的消息。StaticMessageSource
很少被使用,但能以編程的方式向消息源添加消息。ResourceBundleMessageSource
會用得更多一些,為此提供了一下示例:
<beans>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>format</value>
<value>exceptions</value>
<value>windows</value>
</list>
</property>
</bean>
</beans>
這段配置假定在你的classpath中有三個資源文件(resource bundle),它們是format
, exceptions
和windows
。通過ResourceBundle,使用JDK中解析消息的標準方式,來處理任何解析消息的請求。出于示例的目的,假定上面的兩個資源文件的內容為…
# in 'format.properties'
message=Alligators rock!
# in 'exceptions.properties'
argument.required=The '{0}' argument is required.
下面是測試代碼。因為ApplicationContext
實現也都實現了MessageSource
接口,所以能被轉型為MessageSource
接口
public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("message", null, "Default", null);
System.out.println(message);
}
上述程序的輸出結果將會是...
Alligators rock!
總而言之,我們在'beans.xml'
的文件中(在classpath根目錄下)定義了一個messageSource
bean,通過它的basenames
屬性引用多個資源文件;而basenames
屬性值由list元素所指定的三個值傳入,它們以文件的形式存在并被放置在classpath的根目錄下(分別為format.properties
,exceptions.properties
和windows.properties
)。
再分析個例子,這次我們將著眼于傳遞參數給查找的消息,這些參數將被轉換為字符串并插入到已查找到的消息中的占位符(譯注:資源文件中花括號里的數字即為占位符)。
<beans>
<!-- this MessageSource
is being used in a web application -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="baseName" value="WEB-INF/test-messages"/>
</bean>
<!-- let's inject the above MessageSource
into this POJO -->
<bean id="example" class="com.foo.Example">
<property name="messages" ref="messageSource"/>
</bean>
</beans>
public class Example {
private MessageSource messages;
public void setMessages(MessageSource messages) {
this.messages = messages;
}
public void execute() {
String message = this.messages.getMessage("argument.required",
new Object [] {"userDao"}, "Required", null);
System.out.println(message);
}
}
調用execute()
方法的輸出結果是...
The 'userDao' argument is required.
對于國際化(i18n),Spring中不同的MessageResource
實現與JDK標準ResourceBundle中的locale解析規則一樣。比如在上面例子中定義的messageSource
bean,如果你想解析British (en-GB) locale的消息,那么需要創建format_en_GB.properties
,exceptions_en_GB.properties
和windows_en_GB.properties
三個資源文件。
Locale解析通常由應用程序根據運行環境來指定。出于示例的目的,我們對將要處理的(British)消息手工指定locale參數值。
# in 'exceptions_en_GB.properties'
argument.required=Ebagum lad, the '{0}' argument is required, I say, required.
public static void main(final String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.UK);
System.out.println(message);
}
上述程序運行時的輸出結果是...
Ebagum lad, the 'userDao' argument is required, I say, required.
MessageSourceAware
接口還能用于獲取任何已定義的MessageSource
引用。任何實現了MessageSourceAware
接口的bean將在創建和配置的時候與MessageSource
一同被注入。
posted @
2008-11-22 16:51 xzc 閱讀(4104) |
評論 (3) |
編輯 收藏
因為希望把SpringSide搞成國際化項目,i18n就成了必做的事情。
照抄appfuse,折騰了很久后才發現appfuse式的sample總是只顧著演示自己的一畝三分地而忽略了很多其他東西。
1.從基礎開始,沒有Spring時,Java的i18n是這樣的:
1.1 jsp環境
首先寫一個messages.zh_CN.properties文件,放在class-path也就是/WEB-INF/classes里 welcome=歡迎 然后用native2ascii.exe把它轉為 welcome=\u6b22\u8fce
在web.xml中定義messages文件
<context-param>
<param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
<param-value>messages</param-value>
</context-param>
最后在jsp里使用
<%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>
<
fmt:message key="welcome"/>
如果有多個Resource Bundle文件, 就要在jsp里用<ftm:bundle>定義了.
1.2 pure Java環境
ResourceBundle rb = ResourceBundle.getBundle("messages");
String welcome = rb.getString("welcome");
2.Spring的增強及appfuse的做法
Spring增加了MessageSource的概念,一是ApplicationContext將充當一個單例的角色,不再需要每次使用i18時都初始化一次ResourceBundle,二是可以代表多個Resource Bundle.
在ApplicationContext的定義文件中,增加如下節點:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"/>
</bean>
則在pure java環境中。 context.getMessage("welcome", null, Locale.CHINA)
而在jsp環境中,Controller調用JSTL viewResolver再調用Jsp時,<fmt:message>將繼續發揮它的功效。
因此,appfuse等sample都是在appfuse-servlet.xml 中定義一個<messageSource>。
3.Better Practice
3.1 要不要定義javax.servlet.jsp.jstl.fmt.localizationContext
[定義]
Appfuse等sample,都是假定大家完全使用Controller作訪問入口,jsp甚至藏在了/web-inf/中。而很不幸,大家的項目可能還是有很多直接訪問jsp的地方,而直接訪問jsp時,<messageSource>節點是沒有作用的。
但如果定義了javax...localizationContext, 又會讓MessageSource失效......
3.2
messageSource定義在ApplicationContext.xml還是appfuse-servlet.xml
ApplicationContext*.xml由ContextLoaderListener載入,
而appfuse-servlet.xml靠dispatchServlet載入,
并擁有一個指向ApplcationContex*.xml指針。所以,appfuse-servlet.xml能看到定義在ApplcationContext里的東西,而反之做不到。
明顯, 把<messageSource>定義在ApplicationContext.xml 能獲得更好的可見性。
但是appfuse沒有在pure Java代碼中使用i18n,也就沒有考慮這個問題。
3.3 堅決不用雞肋級<spring:message> tag
連appfuse也不用它,可見多么雞肋。因為fmt在找不到資源時,最多顯示???welcome???,而<spring:message>則會拋出異常,誰會喜歡這種定時炸彈阿。
3.4 有趣的theme 解決"做成圖片的文字"的國際化
theme也就是把message的原理發揮了一下,讓不同語言的美術字圖片的路徑也可以定義在theme_zh_CN.properties和theme_en_US.properties中。終于有一個不那么雞肋的spring tag了。
4.簡單歸納
1. jstl中仍然使用標準的<ftm:message>及其定義?
2.java中使用spring的<messageSource>實現單例
3.用<spring:theme>解決那些做成圖片的文字的國際化問題
4.Spring 還有session,cookie locale resolver, 到時可以看一下.
posted @
2008-11-22 16:40 xzc 閱讀(1478) |
評論 (3) |
編輯 收藏