athrunwang

纪元
数据加载中……

11 个重要的数据库设计规则

  • 简介

    在您开始阅读这篇文章之前,我得明确地告诉您,我并不是一个数据库设计领域的大师。以下列出的11点是我对自己在平时项目实践和阅读中学习到的经验总结出来的个人见解。我个人认为它们对我的数据库设计提供了很大的帮助。实属一家之言,欢迎拍砖 : )

    我之所以写下这篇这么完整的文章是因为,很多开发者一参与到数据库设计,就会很自然地把 “三范式” 当作银弹一样来使用。他们往往认为遵循这个规范就是数据库设计的唯一标准。由于这种心态,他们往往尽管一路碰壁也会坚持把项目做下去。

    如果你对 “三范式” 不清楚,请点击这里(FQ)一步一步的了解什么是“三范式”。

    大家都说标准规范是重要的指导方针并且也这么做着,但是把它当作石头上的一块标记来记着(死记硬背)还是会带来麻烦的。以下11点是我在数据库设计时最优先考虑的规则。

  • 规则 1:弄清楚将要开发的应用程序是什么性质的(OLTP 还是 OPAP)?

    当你要开始设计一个数据库的时候,你应该首先要分析出你为之设计的应用程序是什么类型的,它是 “事务处理型”(Transactional) 的还是 “分析型” (Analytical)的?你会发现许多开发人员采用标准化做法去设计数据库,而不考虑目标程序是什么类型的,这样做出来的程序很快就会陷入性能、客户定制化的问题当中。正如前面所说的,这里有两种应用程序类型, “基于事务处理” 和 “基于分析”,下面让我们来了解一下这两种类型究竟说的是什么意思。

    事务处理型:这种类型的应用程序,你的最终用户更关注数据的增查改删(CRUD,Creating/Reading/Updating/Deleting)。这种类型更加官方的叫法是 “OLTP” 。
    分析型:这种类型的应用程序,你的最终用户更关注数据分析、报表、趋势预测等等功能。这一类的数据库的 “插入” 和 “更新” 操作相对来说是比较少的。它们主要的目的是更加快速地查询、分析数据。这种类型更加官方的叫法是 “OLAP” 。

     

    那么换句话说,如果你认为插入、更新、删除数据这些操作在你的程序中更为突出的话,那就设计一个规范化的表否则的话就去创建一个扁平的、不规范化的数据库结构。 

    以下这个简单的图表显示了像左边Names和Address这样的简单规范化的表,怎么通过应用不规范化结构来创建一个扁平的表结构。 

     

  • 规则 2:将你的数据按照逻辑意义分成不同的块,让事情做起来更简单

    这个规则其实就是 “三范式” 中的第一范式。违反这条规则的一个标志就是,你的查询使用了很多字符串解析函数
    例如 substring、charindex等等。若真如此,那就需要应用这条规则了。

    比如你看到的下面图片上有一个有学生名字的表,如果你想要查询学生名字中包含“Koirala”,但不包含“Harisingh”的记录,你可以想象一下你将会得到什么样的结果。

    所以更好的做法是将这个字段拆分为更深层次的逻辑分块,以便我们的表数据写起来更干净,以及优化查询。


     

  • 规则 3:不要过度使用 “规则 2”

    开发者都是一群很可爱的生物。如果你告诉他们这是一条解决问题的正路,他们就会一直这么做下去,做到过了头导致了一些不必要的后果。这也可以应用于我们刚刚在前面提到的规则2。当你考虑字段分解时,先暂停一下,并且问问你自己是否真的需要这么做。正如所说的,分解应该是要符合逻辑的。

    例如,你可以看到电话号码这个字段,你很少会把电话号码的ISD代码单独分开来操作(除非你的应用程序要求这么做)。所以一个很明智的决定就是让它保持原样,否则这会带来更多的问题。

     

  • 规则 4:把重复、不统一的数据当成你最大的敌人来对待

    集中那些重复的数据然后重构它们。我个人更加担心的是这些重复数据带来的混乱而不是它们占用了多少磁盘空间。

    例如下面这个图表,你可以看到 "5th Standard" 和 "Fifth standard" 是一样的意思,它们是重复数据。现在你可能会说是由于那些录入者录入了这些重复的数据或者是差劲的验证程序没有拦住,让这些重复的数据进入到了你的系统。现在,如果你想导出一份将原本在用户眼里十分困惑的数据显示为不同实体数据的报告,该怎么做呢?



    解决方法之一是将这些数据完整地移到另外一个主表,然后通过外键引用过来。在下面这个图表中你可以看到我们是如何创建一个名为 “Standards”(课程级别) 的主表,然后同样地使用简单的外键连接过去。



  • 规则 5:当心被分隔符分割的数据,它们违反了“字段不可再分”

    前面的规则2即“第一范式”说的是避免 “重复组” 。下面这个图表作为其中的一个例子解释了 “重复组”是什么样子的。如果你仔细的观察 syllabus(课程) 这个字段,会发现在这一个字段里实在是填充了太多的数据了。像这些字段就被称为 “重复组” 了。如果我们又得必须使用这些数据,那么这些查询将会十分复杂并且我也怀疑这些查询会有性能问题。


    这些被塞满了分隔符的数据列需要特别注意,并且一个较好的办法是将这些字段移到另外一个表中,使用外键连接过去,同样地以便于更好的管理。



    那么,让我们现在就应用规则2(第一范式) “避免重复组” 吧。你可以看到上面这个图表,我创建了一个单独的 syllabus(课程) 表,然后使用 “多对多” 关系将它与 subject(科目) 表关联起来。

    通过这个方法,主表(student表)的 syllabus(课程) 字段就不再有重复数据和分隔符了。

  • 规则 6:当心那些仅仅部分依赖主键的列



    留心注意那些仅仅部分依赖主键的列。例如上面这个图表,我们可以看到这个表的主键是 Roll No.+Standard
    。现在请仔细观察 syllabus 字段,可以看到 syllabus(课程) 字段仅仅关联(依赖) Standard(课程级别) 字段而不是直接地关联(依赖)某个学生(Roll No. 字段)。

    Syllabus(课程) 字段关联的是学生正在学习的哪个课程级别(Standard字段)而不是直接关联到学生本身。那如果明天我们要更新教学大纲(课程)的话还要痛苦地为每个同学也修改一下,这明显是不符合逻辑的(不正常的做法)。更有意义的做法是将这些字段从这个表移到另外一个表,然后将它们与 Standard(课程级别)表关联起来。

    你可以看到我们是如何移动 syllabus(课程)字段并且同样地附上 Standard 表。

    这条规则只不过是 “三范式” 里的 “第二范式”:“所有字段都必须完整地依赖主键而不是部分依赖”。

  • 规则 7:仔细地选择派生列



    如果你正在开发一个 OLTP 型的应用程序,那强制不去使用派生字段会是一个很好的思路,除非有迫切的性能要求,比如经常需要求和、计算的 OLAP 程序,为了性能,这些派生字段就有必要存在了。

    通过上面的这个图表,你可以看到 Average 字段是如何依赖 Marks 和 Subjects 字段的。这也是冗余的一种形式。因此对于这样的由其他字段得到的字段,需要思考一下它们是否真的有必要存在。

    这个规则也被称为 “三范式” 里的第三条:“不应该有依赖于非主键的列” 。 我的个人看法是不要盲目地运用这条规则,应该要看实际情况,冗余数据并不总是坏的。如果冗余数据是计算出来的,看看实际情况再来决定是否应用这第三范式。

  • 规则 8:如果性能是关键,不要固执地去避免冗余


    不要把 “避免冗余” 当作是一条绝对的规则去遵循。如果对性能有迫切的需求,考虑一下打破常规。常规情况下你需要做多个表的连接操作,而在非常规的情况下这样的多表连接是会大大地降低性能的。

  • 规则 9:多维数据是各种不同数据的聚合

    OLAP 
    项目主要是解决多维数据问题。比如你可以看看下面这个图表,你会想拿到每个国家、每个顾客、每段时期的销售额情况。简单的说你正在看的销售额数据包含了三个维度的交叉。



    为这种情况做一个实际的设计是一个更好的办法。简单的说,你可以创建一个简单的主要销售表,它包含了销售额字段,通过外键将其他所有不同维度的表连接起来。

     

     

  • 规则 10:将那些具有“名值表”特点的表统一起来设计

    很多次我都遇到过这种 “名值表” 。 “名值表” 意味着它有一些键,这些键被其他数据关联着。比如下面这个图表,你可以看到我们有 Currency(货币型)和 Country(国家)这两张表。如果你仔细观察你会发现实际上这些表都只有键和值。


    对于这种表,创建一个主要的表,通过一个 Type(类型)字段来区分不同的数据将会更有意义。

  • 规则 11:无限分级结构的数据,引用自己的主键作为外键

    我们会经常碰到一些无限父子分级结构的数据(树形结构?)。例如考虑一个多级销售方案的情况,一个销售人员之下可以有多个销售人员。注意到都是 “销售人员” 。也就是说数据本身都是一种。但是层级不同。这时候我们可以引用自己的主键作为外键来表达这种层级关系,从而达成目的。


    这篇文章的用意不是叫大家不要遵循范式,而是叫大家不要盲目地遵循范式。根据你的项目性质和需要处理的数据类型来做出正确的选择。

     

英文原文,OSChina原创翻译。

posted @ 2012-04-18 21:57 AthrunWang 阅读(205) | 评论 (0)编辑 收藏
Oracle常用SQL命令

--创建用户
CREATE USER "APITEST"  PROFILE "DEFAULT"
    IDENTIFIED BY "apitest" DEFAULT TABLESPACE "LOUSHANG"
    TEMPORARY TABLESPACE "TEMP"
    ACCOUNT UNLOCK;

--为用户指定表空间
GRANT UNLIMITED TABLESPACE TO "APITEST";

--为用户授权
GRANT "CONNECT" TO "APITEST";
GRANT "DBA" TO "APITEST";
GRANT "RESOURCE" TO "APITEST";

--将锁定用户解锁
alter user <用户名> account unlock;

--修改用户密码
alter user <用户名> identified by <新密码>;

--删除用户
drop user  apitest; ----仅仅是删除用户,
drop user apitest cascade ;----会删除此用户名下的所有表和视图。

---查看当前用户信息
select * from user_users;

---查询当前数据库实例中有哪些用户
select * from dba_users order by username;

---查看当前用户拥有的角色
select * from user_role_privs;

---查看当前用户所拥有的表
select * from user_tables;

---查看当前用户所拥有表的列
select * from USER_TAB_COLUMNS ;

---显示特权用户(一般包括sys、system)
select * from v$pwfile_users;

---查询当前用户所拥有的所有对象(表、视图、索引、存储函数和过程等)
select * from user_objects

----查看序列号
select * from user_sequences;

---查看当前用户所有的视图
select * from  user_views;

--查看当前连接信息
select SID,SERIAL#,USERNAME,MACHINE,LOGON_TIME from v$session where username='APITEST';

--断开指定连接
alter system kill session '530,49177';

 

posted @ 2012-03-27 11:13 AthrunWang 阅读(209) | 评论 (0)编辑 收藏
分页栏

DAO层的代码分页代码:
public PageModel findByPageModel(String hql,PageModel pm) {
  pm.setTotalCount(this.getHibernateTemplate().find(hql).size());
  pm.setGoToHref(ServletActionContext.getRequest().getServletPath().replace("/",""));
  int totalCount = pm.getTotalCount();
  int pageSize = pm.getPageSize();
  int totalPage = (totalCount+pageSize-1)/pageSize ;
  int currentPage = pm.getCurrentPage() ;
  pm.setTotalPage(totalPage);
  int offset = (currentPage-1)*pageSize;
  pm.setList(this.getSession().createQuery(hql).setFirstResult(offset).setMaxResults(pageSize).list());
  return pm;
 }


分页的JAVABEAN:
public class PageModel {
 private int currentPage;
 private int pageSize;
 private int totalCount;
 private int totalPage;
 private List list ;
 private String goToHref;
 
 public int getCurrentPage() {
  if(currentPage<=0) currentPage=1;
  return currentPage;
 }
 public void setCurrentPage(int currentPage) {
  this.currentPage = currentPage;
 }
 public int getPageSize() {
  if(pageSize<=0) pageSize=10;
  return pageSize;
 }
 public void setPageSize(int pageSize) {
  this.pageSize = pageSize;
 }
 public int getTotalCount() {
  return totalCount;
 }
 public void setTotalCount(int totalCount) {
  this.totalCount = totalCount;
 }
 public int getTotalPage() {
  return totalPage;
 }
 public void setTotalPage(int totalPage) {
  this.totalPage = totalPage;
 }
 public List getList() {
  return list;
 }
 public void setList(List list) {
  this.list = list;
 }
 public String getGoToHref() {
  return goToHref;
 }
 public void setGoToHref(String goToHref) {
  this.goToHref = goToHref;
 }
}


JSP页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<link rel="stylesheet" type="text/css" href="<%=basePath %>findByHql/pagingBar/css/pagingBar.css">

 <input type="button" class="firstPage commonPage"  alt="首页"   title="首页"/>
 <input type="button" class="beforePage commonPage" alt="上一页" title="上一页"/>
 <input type="button" class="nextPage commonPage"   alt="下一页" title="下一页"/>
 <input type="button" class="lastPage commonPage"   alt="尾页"   title="尾页" />
 
 <input type="hidden" id="currentPage" value="${requestScope.pm.currentPage }" />
 <input type="hidden" id="totalPage" value="${requestScope.pm.totalPage }" />
 <input type="hidden" id="goToHref" value="${requestScope.pm.goToHref }" />
 
 <span class="cp">当前第${requestScope.pm.currentPage }页</span>
 <span class="tc"> 相关资讯:${requestScope.pm.totalCount }条</span>
 <span class="ps">每页${requestScope.pm.pageSize }条 </span>
 <span class="tp">共${requestScope.pm.totalPage}页</span>

<script type="text/javascript" src="<%=basePath%>js/jquery.js"></script>
<script type="text/javascript">
 (function($) {
  var currentPage = parseInt($('#currentPage').val());
  var totalPage = parseInt($('#totalPage').val());
  var toHref = $('#goToHref').val();
  $('.firstPage').bind('click', function() {
   goToHref(1);
  });
  $('.nextPage').bind('click', function() {
   if (currentPage >= totalPage)
    goToHref(totalPage);
   else
    goToHref(currentPage + 1);
  });
  $('.beforePage').bind('click', function() {
   if (currentPage <= 1)
    goToHref(1);
   else
    goToHref(currentPage - 1);
  });
  $('.lastPage').bind('click', function() {
   goToHref(totalPage);
  });
  function goToHref(cp) {
   document.location.href = toHref+"?currentPage=" + cp;
  }
 })(jQuery)
</script>


CSS:下面有几张图片需要自己找...
/*点击栏*/
.commonPage{
 width: 16px;
 height: 16px;
 border: none;
 cursor: pointer;
}
.firstPage{
 background: url("../images/page-first.png") no-repeat;
}

.nextPage{
 background: url("../images/page-next.png") no-repeat;
}

.beforePage{
 background: url("../images/page-prev.png") no-repeat;
}

.lastPage{
 background: url("../images/page-last.png") no-repeat;
}

/*显示栏*/
.cp,.tc,.ps,.tp{
 font-size: 14px;
}

在action中调用DAO层的方法,给currentPage和pageSize设置初始值,然后就返回一个list到你分页的页面迭代,以后就直接嵌套在分页页面中就行

posted @ 2012-03-27 10:19 AthrunWang 阅读(329) | 评论 (0)编辑 收藏
主题:Oracle面试问题-技术篇

这也许是你一直期待的文章,在关注这部分技术问题的同时,请务必阅读有关面试中有关个人的问题和解答。这里的回答并不是十分全面,这些问题可以通过多个角度来进行解释,也许你不必在面试过程中给出完全详尽的答案,只需要通过你的解答使面试考官了解你对ORACLE概念的熟悉程度。

1.解释冷备份和热备份的不同点以及各自的优点

解答:热备份针对归档模式的数据库,在数据库仍旧处于工作状态时进行备份。而冷备份指在数据库关闭后,进行备份,适用于所有模式的数据库。热备份的优点在于当备份时,数据库仍旧可以被使用并且可以将数据库恢复到任意一个时间点。冷备份的优点在于它的备份和恢复操作相当简单,并且由于冷备份的数据库可以工作在非归档模式下,数据库性能会比归档模式稍好。(因为不必将archive log写入硬盘)

2.你必须利用备份恢复数据库,但是你没有控制文件,该如何解决问题呢?

解答:重建控制文件,用带backup control file 子句的recover 命令恢复数据库。

3.如何转换init.ora到spfile?

解答:使用create spfile from pfile 命令.

4.解释data block , extent 和 segment的区别(这里建议用英文术语)

解答:data block是数据库中最小的逻辑存储单元。当数据库的对象需要更多的物理存储空间时,连续的data block就组成了extent . 一个数据库对象拥有的所有extents被称为该对象的segment.

5.给出两个检查表结构的方法

解答:1.DESCRIBE命令

2.DBMS_METADATA.GET_DDL 包

6.怎样查看数据库引擎的报错

解答:alert log.

7.比较truncate和delete 命令

解答:两者都可以用来删除表中所有的记录。区别在于:truncate是DDL操作,它移动HWK,不需要rollback segment .而Delete是DML操作, 需要rollback segment 且花费较长时间.

8.使用索引的理由

解答:快速访问表中的data block

9.给出在STAR SCHEMA中的两种表及它们分别含有的数据

解答:Fact tables 和dimension tables. fact table包含大量的主要的信息而dimension tables 存放对fact table 某些属性描述的信息

10.FACT Table上需要建立何种索引?

解答:位图索引 (bitmap index)

11. 给出两种相关约束?

解答:主键和外键

12. 如何在不影响子表的前提下,重建一个母表

解答:子表的外键强制实效,重建母表,激活外键

13. 解释归档和非归档模式之间的不同和它们各自的优缺点

解答:归档模式是指你可以备份所有的数据库 transactions并恢复到任意一个时间点。非归档模式则相反,不能恢复到任意一个时间点。但是非归档模式可以带来数据库性能上的少许提高.

14. 如何建立一个备份控制文件?

解答:Alter database backup control file to trace.

15. 给出数据库正常启动所经历的几种状态 ?

解答:STARTUP NOMOUNT – 数据库实例启动

STARTUP MOUNT - 数据库装载

STARTUP OPEN – 数据库打开

16. 哪个column可以用来区别V$视图和GV$视图?

解答:INST_ID 指明集群环境中具体的 某个instance 。

17. 如何生成explain plan?

解答:运行utlxplan.sql. 建立plan 表

针对特定SQL语句,使用 explain plan set statement_id = 'tst1' into plan_table

运行utlxplp.sql 或 utlxpls.sql察看explain plan

18. 如何增加buffer cache的命中率?

解答:在数据库较繁忙时,适用buffer cache advisory 工具,查询v$db_cache_advice.如果有必要更改,可以使用 alter system set db_cache_size 命令

19. ORA-01555的应对方法?

解答:具体的出错信息是snapshot too old within rollback seg , 通常可以通过增大rollback seg来解决问题。当然也需要察看一下具体造成错误的SQL文本

20. 解释$ORACLE_HOME和$ORACLE_BASE的区别?

解答:ORACLE_BASE是oracle的根目录,ORACLE_HOME是oracle产品的目录。

posted @ 2012-03-21 17:33 AthrunWang 阅读(200) | 评论 (0)编辑 收藏
技术债务(母鸡的遭遇)

技术债务, 是指匆忙的实现一个功能,却对现有的程序库造成了破坏(在实现的过程中污染了代码库的设计),这对于一些项目经理/客户来说就像是天书奇谈。也许他们是明 白的,只是不愿意承认罢了,我估计是这样的。不管怎样,我想起来一个小故事,当下次遇到这种情况,需要向他们解释增加某些新功能的代价时,也可用讲这个故 事给他们听。

一个农夫有3只母鸡。每只母鸡每天下一个蛋。农夫跟当地的一个食品店老板做生意。食品店老板每天从农夫那里买2给鸡蛋放在店里出售。一切都很好,直到有一天,食品店老板出现在农夫家里:

食品店老板: 哎呀,今天我需要一些鸡肉。

农夫: 鸡肉?你和我的生意里可不包括这些。

食品店老板: 我知道。但我真的需要一些鸡肉。我计划要做一个B2S(S是胃的缩写)模式的PaaS(P是肉禽的缩写)平台。

农夫: 什么?

食品店老板: 非常重要的东西。你可以提供我一些鸡肉吗?

农夫: 这样呀,事情不是那么容易办到 — 我要孵化鸡蛋,等小鸡长大了才能给你…少说也要一个月吧。

食品店老板: 一个月?太久了…我以为你现在就能给我呢。

农夫: 时间有自己的脚步,你必须耐心一点等。

食品店老板: 可是,为什么你不能在现有的母鸡中杀一个呢?这样一来,我有了鸡肉,你每天还能产两个蛋。这就够了,不是吗?

农夫: 可是,我不觉得这是一个好主意。这会把我推向一个没有回旋余地的境况,万一剩下的鸡中有一只突然出了什么意外怎么办。

食品店老板: 放心啦,不会发生那样的事的…我真的非常非常需要鸡肉!杀一只鸡吧!

农夫: 那好吧,我想我可以…

于是,农夫拿起一把刀,把他的一只母鸡送入了天堂。食品店老板得到了他的鸡肉,返回了食品店。

一周后,食品店老板又一次来到了农夫家里:

食品店老板: 你好,我来了!

农夫: 你好,有什么事?

食品店老板: 你听我说 — 你的鸡肉好极了。事实上,它是如此的鲜美,卖的如此的好,你必须要再给我一只鸡。最迟明天早上。

农夫: 这是不可能的事。如果我要再杀一只鸡给你,我就没法每天提供你两个鸡蛋了。

食品店老板: 哦,别那么紧张!客户需要鸡肉,我已经答应客户明天早上提供给他们了…

农夫: 不行,绝对不能这么干。如果我这么做,我就履行不了我和你的协议了,你知道吗?如果我这么做,我就没法提供你足够的鸡蛋了。

食品店老板: 可是我真的真的需要鸡肉!明天早上之前!否则客户会发飙的,地球将会塌陷,世界末日将会到来!给我一只鸡吧,现在!

农夫: 那好吧,如果你非要这么不顾后果的想要,那就拿去吧!但是,从现在开始,鸡蛋我是没法提供你了,明白?

食品店老板: 当然,当然。但我相信是个很聪明的人,我猜你能找到方法解决这个问题。再见!

食品店老板离开回到了店里。

第二天:

食品店老板: 嗨,鸡蛋呢?

农夫: 你什么意思?

食品店老板: 鸡蛋。你只给了我一个鸡蛋。发生了什么事?

农夫: 发生了什么事?我有3只鸡,你拿走了两只。现在就剩下一只。一只鸡,一个鸡蛋。我认为我解释的已经很清楚了。

食品店老板: 但是合同里并没有这些!合同里说的很清楚 — 你每天提供我2给鸡蛋!你现在让我向客户怎么交代?

农夫: 哦,情况我很明白。我无能为力。

食品店老板: 好吧,好吧,不谈这事了。咱们聊点其它事情…要是能再能点鸡肉就好了。你再给我一些吧?

所以,千万别学农夫 — 坚决拒绝为了当前利益而长久的破坏你的代码库的无理要求,如果你被强迫这样做,拒绝承担这样的任务 — 也不要做食品店老板 — 不要做提出这样不合理的要求,你要为自己的决定承担后果。

posted @ 2012-03-19 10:00 AthrunWang 阅读(287) | 评论 (1)编辑 收藏
apache shiro与spring的环境搭建

[代码] web.xml
<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
             <!--spring 的配置文件-->
             classpath:/applicationContext-hibernate.xml
        </param-value>
   </context-param>
 
   <!-- shiro -->
   <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
             <param-name>targetFilterLifecycle</param-name>
             <param-value>true</param-value>
        </init-param>
   </filter>
 
   <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
   </filter-mapping>
 
   <!-- Listeners -->
   <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener>
[代码] applicationContext-hibernate.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:aop="http://www.springframework.org/schema/aop"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

     <!-- SessionFactory, DataSource, etc. omitted -->

     <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
          <property name="driverClassName" value="${jdbc.driverClassName}" />
          <property name="url" value="${jdbc.url}" />
          <property name="username" value="${jdbc.username}" />
          <property name="password" value="${jdbc.password}" />
     </bean>

     <bean id="sessionFactory"
          class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
          <property name="dataSource" ref="dataSource" />
          <property name="packagesToScan">
               <list>
                    <value>org.projects.graduates.domain</value>
               </list>
          </property>
          <property name="hibernateProperties">
               <value>hibernate.dialect=${hibernate.dialect}</value>
          </property>
     </bean>

     <bean id="txManager"
          class="org.springframework.orm.hibernate3.HibernateTransactionManager">
          <property name="sessionFactory" ref="sessionFactory" />
     </bean>

     <tx:advice id="txAdvice" transaction-manager="txManager">
          <tx:attributes>
               <tx:method name="get*" read-only="true" />
               <tx:method name="find*" read-only="true" />
               <tx:method name="*" propagation="REQUIRED" />
          </tx:attributes>
     </tx:advice>

     <aop:config>
          <aop:pointcut id="appOperation"
               expression="execution(* org.projects.graduates.app.GradApplication.*(..))" />
          <aop:advisor advice-ref="txAdvice" pointcut-ref="appOperation" />
     </aop:config>

     <!-- shiro -->
     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
          <property name="securityManager" ref="securityManager" />
          <property name="loginUrl" value="/login.action" />
          <property name="successUrl" value="/main.action" />
          <property name="unauthorizedUrl" value="/login.action" />
          <property name="filterChainDefinitions">
               <value>
                    /index.action = anon
                    /login.action = anon
                    /main.action = authc, roles[admin]
                    /course/** = authc, roles[admin]
               </value>
          </property>
     </bean>

   
     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
     <!--设置自定义realm-->
          <property name="realm" ref="myRealm" />
     </bean>
   
     <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
   
     <!--myRealm 继承自AuthorizingRealm-->
     <bean id="myRealm" class="org.projects.graduates.shiro.GradRealm" ></bean>

     <bean
          class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
          <property name="staticMethod"
               value="org.apache.shiro.SecurityUtils.setSecurityManager" />
          <property name="arguments" ref="securityManager" />
     </bean>

</beans>

[代码] org.projects.graduates.shiro.GradRealm

public class GradRealm extends AuthorizingRealm {

     private SecurityApplication securityApplication = new SecurityApplicationImpl();

     public GradRealm() {
          super();
          //设置认证token的实现类
          setAuthenticationTokenClass(UsernamePasswordToken.class);
          //设置加密算法
          setCredentialsMatcher(new HashedCredentialsMatcher(Sha1Hash.ALGORITHM_NAME));
        
     }
     //授权
     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
          String loginName = (String) principalCollection.fromRealm(getName()).iterator().next();
          User user = securityApplication.findby(loginName);
          if (null == user) {
               return null;
          } else {
               SimpleAuthorizationInfo result = new SimpleAuthorizationInfo();
             
               result.addRoles(UserRoles.findRoleNamesOf(user));
               for (Role role : UserRoles.findRolesOf(user)) {
                    result.addStringPermissions(role.getPermissions());
               }
             
               return result;

          }
     }

     //认证
     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
          UsernamePasswordToken upToken = (UsernamePasswordToken) token;
          User user = securityApplication.findby(upToken.getUsername());
          if (user != null) {
               return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
          }
          return null;
     }
}


posted @ 2012-03-06 10:07 AthrunWang 阅读(4982) | 评论 (0)编辑 收藏
海量用户积分排名算法探讨

问题

某海量用户网站,用户拥有积分,积分可能会在使用过程中随时更新。现在要为该网站设计一种算法,在每次用户登录时显示其当前积分排名。用户最大规模为2亿;积分为非负整数,且小于100万。

PS: 据说这是迅雷的一道面试题,不过问题本身具有很强的真实性,所以本文打算按照真实场景来考虑,而不局限于面试题的理想环境。

存储结构

首先,我们用一张用户积分表user_score来保存用户的积分信息:

表结构:

image

示例数据:

image 

下面的算法会基于这个基本的表结构来进行。

算法1:简单SQL查询

首先,我们很容易想到用一条简单的SQL语句查询出积分大于该用户积分的用户数量:

select 1 + count(t2.uid) as rank

from user_score t1, user_score t2

where t1.uid = @uid and t2.score > t1.score

对于4号用户我们可以得到下面的结果:

image

算法1总结

优点:简单,利用了SQL的功能,不需要复杂的查询逻辑,也不引入额外的存储结构,对小规模或性能要求不高的应用不失为一种良好的解决方案。

缺点:需要对user_score表进行全表扫描,还需要考虑到查询的同时若有积分更新会对表造成锁定,在海量数据规模和高并发的应用中,性能是无法接受的。

算法2:均匀分区设计

在许多应用中缓存是解决性能问题的重要途径,我们自然会想能不能把用户排名用Memcached缓存下来呢?不过再一想发现缓存似乎帮不上什么忙,因为用户排名是一个全局性的统计性指标,而并非用户的私有属性,其他用户的积分变化可能会马上影响到本用户的排名。然而,真实的应用中积分的变化其实也是有一定规律的,通常一个用户的积分不会突然暴增减,一般用户总是要在低分区混迹很长一段时间才会慢慢升入高分区,也就是说用户积分的分布总体说来是有区段的,我们进一步注意到高分区用户积分的细微变化其实对低分段用户的排名影响不大。于是,我们可以想到按积分区段进行统计的方法,引入一张分区积分表score_range:

表结构:

image

数据示例:

image

表示[from_score, to_score)区间有count个用户。若我们按每1000分划分一个区间则有[0, 1000), [1000, 2000), …, [999000, 1000000)这1000个区间,以后对用户积分的更新要相应地更新score_range表的区间值。在分区积分表的辅助下查询积分为s的用户的排名,可以首先确定其所属区间,把高于s的积分区间的count值累加,然后再查询出该用户在本区间内的排名,二者相加即可获得用户的排名。

乍一看,这个方法貌似通过区间聚合减少了查询计算量,实则不然。最大的问题在于如何查询用户在本区间内的排名呢?如果是在算法1中的SQL中加上积分条件:

select 1 + count(t2.uid) as rank

from user_score t1, user_score t2

where t1.uid = @uid and t2.score > t1.score and t2.score < @to_score

在理想情况下,由于把t2.score的范围限制在了1000以内,如果对score字段建立索引,我们期望本条SQL语句将通过索引大大减少扫描的user_score表的行数。不过真实情况并非如此,t2.score的范围在1000以内并不意味着该区间内的用户数也是1000,因为这里有积分相同的情况存在!二八定律告诉我们,前20%的低分区往往集中了80%的用户,这就是说对于大量低分区用户进行区间内排名查询的性能远不及对少数的高分区用户,所以在一般情况下这种分区方法不会带来实质性的性能提升。

算法2总结

优点:注意到了积分区间的存在,并通过预先聚合消除查询的全表扫描

缺点:积分非均匀分布的特点使得性能提升并不理想

算法3:树形分区设计

均匀分区查询算法的失败是由于积分分布的非均匀性,那么我们自然就会想,能不能按二八定律,把score_range表设计为非均匀区间呢?比如,把低分区划密集一点,10分一个区间,然后逐渐变成100分,1000分,10000分 … 当然,这不失为一种方法,不过这种分法有一定的随意性,不容易把握好,而且整个系统的积分分布会随着使用而逐渐发生变化,最初的较好的分区方法可能会变得不适应未来的情况了。我们希望找到一种分区方法,既可以适应积分非均匀性,又可以适应系统积分分布的变化,这就是树形分区。

我们可以把[0, 1,000,000)作为一级区间;再把一级区间分为两个2级区间[0, 500,000), [500,000, 1,000,000),然后把二级区间二分为4个3级区间[0, 250,000), [250,000, 500,000), [500,000, 750,000), [750,000, 1,000,000),依此类推,最终我们会得到1,000,000个21级区间[0,1), [1,2) … [999,999, 1,000,000)。这实际上是把区间组织成了一种平衡二叉树结构,根结点代表一级区间,每个非叶子结点有两个子结点,左子结点代表低分区间,右子结点代表高分区间。树形分区结构需要在更新时保持一种不变量(Invariant):非叶子结点的count值总是等于其左右子结点的count值之和。

image

以后,每次用户积分有变化所需要更新的区间数量和积分变化量有关系,积分变化越小更新的区间层次越低。总体上,每次所需要更新的区间数量是用户积分变量的log(n)级别的,也就是说如果用户积分一次变化在百万级,更新区间的数量在二十这个级别。在这种树形分区积分表的辅助下查询积分为s的用户排名,实际上是一个在区间树上由上至下、由粗到细一步步明确s所在位置的过程。比如,对于积分499,000,我们用一个初值为0的排名变量来做累加;首先,它属于1级区间的左子树[0, 500,000),那么该用户排名应该在右子树[500,000, 1,000,000)的用户数count之后,我们把该count值累加到该用户排名变量,进入下一级区间;其次,它属于3级区间的[250,000, 500,000),这是2级区间的右子树,所以不用累加count到排名变量,直接进入下一级区间;再次,它属于4级区间的…;直到最后我们把用户积分精确定位在21级区间[499,000, 499,001),整个累加过程完成,得出排名!

虽然,本算法的更新和查询都涉及到若干个操作,但如果我们为区间的from_score和to_score建立索引,这些操作都是基于键的查询和更新,不会产生表扫描,因此效率更高。另外,本算法并不依赖于关系数据模型和SQL运算,可以轻易地改造为NoSQL等其他存储方式,而基于键的操作也很容易引入缓存机制进一步优化性能。

算法3总结

优点:结构稳定,不受积分分布影响;每次查询或更新的复杂度为积分最大值的log(n)级别,且与用户规模无关,可以应对海量规模;不依赖于SQL,容易改造为NoSQL等其他存储方式

缺点:算法相对更复杂

总结

上面介绍了用户积分排名的3种算法,算法1简单易于理解和实现,适用于小规模和低并发应用;算法3引入了更复杂的树形分区结构,但是性能优越,可以应用于海量规模和高并发。本问题是一个开放性的问题,相信一定还有其他优秀的算法和解决方案,欢迎探讨!

posted @ 2012-03-02 17:27 AthrunWang 阅读(248) | 评论 (0)编辑 收藏
StringUtils详细介绍

public static void TestStr(){
    //null 和 ""操作~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //判断是否Null 或者 ""
    //System.out.println(StringUtils.isEmpty(null));
    //System.out.println(StringUtils.isNotEmpty(null));
    //判断是否null 或者 "" 去空格~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //System.out.println(StringUtils.isBlank("  "));
    //System.out.println(StringUtils.isNotBlank(null));
    //去空格.Null返回null~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //System.out.println(StringUtils.trim(null));
    //去空格,将Null和"" 转换为Null
    //System.out.println(StringUtils.trimToNull(""));
    //去空格,将NULL 和 "" 转换为""
    //System.out.println(StringUtils.trimToEmpty(null));
    //可能是对特殊空格符号去除??
    //System.out.println(StringUtils.strip("大家好  啊  \t"));
    //同上,将""和null转换为Null
    //System.out.println(StringUtils.stripToNull(" \t"));
    //同上,将""和null转换为""
    //System.out.println(StringUtils.stripToEmpty(null));
    //将""或者Null 转换为 ""
    //System.out.println(StringUtils.defaultString(null));
    //仅当字符串为Null时 转换为指定的字符串(二参数)
    //System.out.println(StringUtils.defaultString("", "df"));
    //当字符串为null或者""时,转换为指定的字符串(二参数)
    //System.out.println(StringUtils.defaultIfEmpty(null, "sos"));
    //去空格.去字符~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //如果第二个参数为null去空格(否则去掉字符串2边一样的字符,到不一样为止)
    //System.out.println(StringUtils.strip("fsfsdf", "f"));
    //如果第二个参数为null只去前面空格(否则去掉字符串前面一样的字符,到不一样为止)
    //System.out.println(StringUtils.stripStart("ddsuuu ", "d"));
    //如果第二个参数为null只去后面空格,(否则去掉字符串后面一样的字符,到不一样为止)
    //System.out.println(StringUtils.stripEnd("dabads", "das"));
    //对数组没个字符串进行去空格。
    //ArrayToList(StringUtils.stripAll(new String[]{" 中华 ", "民 国 ", "共和 "}));
    //如果第二个参数为null.对数组每个字符串进行去空格。(否则去掉数组每个元素开始和结尾一样的字符)
    //ArrayToList(StringUtils.stripAll(new String[]{" 中华 ", "民 国", "国共和国"}, "国"));
    //查找,判断~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //判断2个字符串是否相等相等,Null也相等
    //System.out.println(StringUtils.equals(null, null));
    //不区分大小写比较
    //System.out.println(StringUtils.equalsIgnoreCase("abc", "ABc"));
    //查找,不知道怎么弄这么多查找,很多不知道区别在哪?费劲~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //普通查找字符,如果一参数为null或者""返回-1
    //System.out.println(StringUtils.indexOf(null, "a"));
    //从指定位置(三参数)开始查找,本例从第2个字符开始查找k字符
    //System.out.println(StringUtils.indexOf("akfekcd中华", "k", 2));
    //未发现不同之处
    //System.out.println(StringUtils.ordinalIndexOf("akfekcd中华", "k", 2));
    //查找,不区分大小写
    //System.out.println(StringUtils.indexOfIgnoreCase("adfs", "D"));
    //从指定位置(三参数)开始查找,不区分大小写
    //System.out.println(StringUtils.indexOfIgnoreCase("adfs", "a", 3));
    //从后往前查找
    //System.out.println(StringUtils.lastIndexOf("adfas", "a"));
    //未理解,此结果为2
    //System.out.println(StringUtils.lastIndexOf("d饿abasdafs我", "a", 3));
    //未解,此结果为-1
    //System.out.println(StringUtils.lastOrdinalIndexOf("yksdfdht", "f", 2));
    //从后往前查,不区分大小写
    //System.out.println(StringUtils.lastIndexOfIgnoreCase("sdffet", "E"));
    //未解,此结果为1
    //System.out.println(StringUtils.lastIndexOfIgnoreCase("efefrfs看", "F" , 2));
    //检查是否查到,返回boolean,null返回假
    //System.out.println(StringUtils.contains("sdf", "dg"));
    //检查是否查到,返回boolean,null返回假,不区分大小写
    //System.out.println(StringUtils.containsIgnoreCase("sdf", "D"));
    //检查是否有含有空格,返回boolean
    //System.out.println(StringUtils.containsWhitespace(" d"));
    //查询字符串跟数组任一元素相同的第一次相同的位置
    //System.out.println(StringUtils.indexOfAny("absfekf", new String[]{"f", "b"}));
    //查询字符串中指定字符串(参数二)出现的次数
    //System.out.println(StringUtils.indexOfAny("afefes", "e"));
    //查找字符串中是否有字符数组中相同的字符,返回boolean
    //System.out.println(StringUtils.containsAny("asfsd", new char[]{'k', 'e', 's'}));
    //未理解与lastIndexOf不同之处。是否查到,返回boolean
    //System.out.println(StringUtils.containsAny("啡f咖啡", "咖"));
    //未解
    //System.out.println(StringUtils.indexOfAnyBut("seefaff", "af"));
    //判断字符串中所有字符,都是出自参数二中。
    //System.out.println(StringUtils.containsOnly("中华华", "华"));
    //判断字符串中所有字符,都是出自参数二的数组中。
    //System.out.println(StringUtils.containsOnly("中华中", new char[]{'中', '华'}));
    //判断字符串中所有字符,都不在参数二中。
    //System.out.println(StringUtils.containsNone("中华华", "国"));
    //判断字符串中所有字符,都不在参数二的数组中。
    //System.out.println(StringUtils.containsNone("中华中", new char[]{'中', '达人'}));
    //从后往前查找字符串中与字符数组中相同的元素第一次出现的位置。本例为4
    //System.out.println(StringUtils.lastIndexOfAny("中国人民共和国", new String[]{"国人", "共和"}));
    //未发现与indexOfAny不同之处  查询字符串中指定字符串(参数二)出现的次数
    //System.out.println(StringUtils.countMatches("中国人民共和中国", "中国"));
    //检查是否CharSequence的只包含Unicode的字母。空将返回false。一个空的CharSequence(长()= 0)将返回true
    //System.out.println(StringUtils.isAlpha("这是干什么的2"));
    //检查是否只包含Unicode的CharSequence的字母和空格('')。空将返回一个空的CharSequence假(长()= 0)将返回true。
    //System.out.println(StringUtils.isAlphaSpace("NBA直播 "));
    //检查是否只包含Unicode的CharSequence的字母或数字。空将返回false。一个空的CharSequence(长()= 0)将返回true。
    //System.out.println(StringUtils.isAlphanumeric("NBA直播"));
    //如果检查的Unicode CharSequence的只包含字母,数字或空格('')。空将返回false。一个空的CharSequence(长()= 0)将返回true。
    //System.out.println(StringUtils.isAlphanumericSpace("NBA直播"));
    //检查是否只包含ASCII可CharSequence的字符。空将返回false。一个空的CharSequence(长()= 0)将返回true。
    //System.out.println(StringUtils.isAsciiPrintable("NBA直播"));
    //检查是否只包含数值。
    //System.out.println(StringUtils.isNumeric("NBA直播"));
    //检查是否只包含数值或者空格
    //System.out.println(StringUtils.isNumericSpace("33 545"));
    //检查是否只是空格或""。
    //System.out.println(StringUtils.isWhitespace(" "));
    //检查是否全是英文小写。
    //System.out.println(StringUtils.isAllLowerCase("kjk33"));
    //检查是否全是英文大写。
    //System.out.println(StringUtils.isAllUpperCase("KJKJ"));
    //交集操作~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //去掉参数2字符串中在参数一中开头部分共有的部分,结果为:人民共和加油
    //System.out.println(StringUtils.difference("中国加油", "中国人民共和加油"));
    //统计2个字符串开始部分共有的字符个数
    //System.out.println(StringUtils.indexOfDifference("ww.taobao", "www.taobao.com"));
    //统计数组中各个元素的字符串开始都一样的字符个数
    //System.out.println(StringUtils.indexOfDifference(new String[] {"中国加油", "中国共和", "中国人民"}));
    //取数组每个元素共同的部分字符串
    //System.out.println(StringUtils.getCommonPrefix(new String[] {"中国加油", "中国共和", "中国人民"}));
    //统计参数一中每个字符与参数二中每个字符不同部分的字符个数
    //System.out.println(StringUtils.getLevenshteinDistance("中国共和发国人民", "共和国"));
    //判断开始部分是否与二参数相同
    //System.out.println(StringUtils.startsWith("中国共和国人民", "中国"));
    //判断开始部分是否与二参数相同。不区分大小写
    //System.out.println(StringUtils.startsWithIgnoreCase("中国共和国人民", "中国"));
    //判断字符串开始部分是否与数组中的某一元素相同
    //System.out.println(StringUtils.startsWithAny("abef", new String[]{"ge", "af", "ab"}));
    //判断结尾是否相同
    //System.out.println(StringUtils.endsWith("abcdef", "def"));
    //判断结尾是否相同,不区分大小写
    //System.out.println(StringUtils.endsWithIgnoreCase("abcdef", "Def"));
    //字符串截取~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //截取指定位置的字符,null返回null.""返回""
    //System.out.println(StringUtils.substring("国民党", 2));
    //截取指定区间的字符
    //System.out.println(StringUtils.substring("中国人民共和国", 2, 4));
    //从左截取指定长度的字符串
    //System.out.println(StringUtils.left("说点什么好呢", 3));
    //从右截取指定长度的字符串
    //System.out.println(StringUtils.right("说点什么好呢", 3));
    //从第几个开始截取,三参数表示截取的长度
    //System.out.println(StringUtils.mid("说点什么好呢", 3, 2));
    //截取到等于第二个参数的字符串为止
    //System.out.println(StringUtils.substringBefore("说点什么好呢", "好"));
    //从左往右查到相等的字符开始,保留后边的,不包含等于的字符。本例:什么好呢
    //System.out.println(StringUtils.substringAfter("说点什么好呢", "点"));
    //这个也是截取到相等的字符,但是是从右往左.本例结果:说点什么好
    //System.out.println(StringUtils.substringBeforeLast("说点什么好点呢", "点"));
    //这个截取同上是从右往左。但是保留右边的字符
    //System.out.println(StringUtils.substringAfterLast("说点什么好点呢?", "点"));
    //截取查找到第一次的位置,和第二次的位置中间的字符。如果没找到第二个返回null。本例结果:2010世界杯在
    //System.out.println(StringUtils.substringBetween("南非2010世界杯在南非,在南非", "南非"));
    //返回参数二和参数三中间的字符串,返回数组形式
    //ArrayToList(StringUtils.substringsBetween("[a][b][c]", "[", "]"));
    //分割~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //用空格分割成数组,null为null
    //ArrayToList(StringUtils.split("中华 人民  共和"));
    //以指定字符分割成数组
    //ArrayToList(StringUtils.split("中华 ,人民,共和", ","));
    //以指定字符分割成数组,第三个参数表示分隔成数组的长度,如果为0全体分割
    //ArrayToList(StringUtils.split("中华 :人民:共和", ":", 2));
    //未发现不同的地方,指定字符分割成数组
    //ArrayToList(StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-"));
    //未发现不同的地方,以指定字符分割成数组,第三个参数表示分隔成数组的长度
    //ArrayToList(StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 2));
    //分割,但" "不会被忽略算一个元素,二参数为null默认为空格分隔
    //ArrayToList(StringUtils.splitByWholeSeparatorPreserveAllTokens(" ab   de fg ", null));
    //同上,分割," "不会被忽略算一个元素。第三个参数代表分割的数组长度。
    //ArrayToList(StringUtils.splitByWholeSeparatorPreserveAllTokens("ab   de fg", null, 3));
    //未发现不同地方,分割
    //ArrayToList(StringUtils.splitPreserveAllTokens(" ab   de fg "));
    //未发现不同地方,指定字符分割成数组
    //ArrayToList(StringUtils.splitPreserveAllTokens(" ab   de fg ", null));
    //未发现不同地方,以指定字符分割成数组,第三个参数表示分隔成数组的长度
    //ArrayToList(StringUtils.splitPreserveAllTokens(" ab   de fg ", null, 2));
    //以不同类型进行分隔
    //ArrayToList(StringUtils.splitByCharacterType("AEkjKr i39:。中文"));
    //未解
    //ArrayToList(StringUtils.splitByCharacterTypeCamelCase("ASFSRules234"));
    //拼接~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //将数组转换为字符串形式
    //System.out.println(StringUtils.concat(getArrayData()));
    //拼接时用参数一得字符相连接.注意null也用连接符连接了
    //System.out.println(StringUtils.concatWith(",", getArrayData()));
    //也是拼接。未发现区别
    //System.out.println(StringUtils.join(getArrayData()));
    //用连接符拼接,为发现区别
    //System.out.println(StringUtils.join(getArrayData(), ":"));
    //拼接指定数组下标的开始(三参数)和结束(四参数,不包含)的中间这些元素,用连接符连接
    //System.out.println(StringUtils.join(getArrayData(), ":", 1, 3));
    //用于集合连接字符串.用于集合
    //System.out.println(StringUtils.join(getListData(), ":"));
    //移除,删除~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //删除所有空格符
    //System.out.println(StringUtils.deleteWhitespace(" s 中 你 4j"));
    //移除开始部分的相同的字符
    //System.out.println(StringUtils.removeStart("www.baidu.com", "www."));
    //移除开始部分的相同的字符,不区分大小写
    //System.out.println(StringUtils.removeStartIgnoreCase("www.baidu.com", "WWW"));
    //移除后面相同的部分
    //System.out.println(StringUtils.removeEnd("www.baidu.com", ".com"));
    //移除后面相同的部分,不区分大小写
    //System.out.println(StringUtils.removeEndIgnoreCase("www.baidu.com", ".COM"));
    //移除所有相同的部分
    //System.out.println(StringUtils.remove("www.baidu.com/baidu", "bai"));
    //移除结尾字符为"\n", "\r", 或者 "\r\n".
    //System.out.println(StringUtils.chomp("abcrabc\r"));
    //也是移除,未解。去结尾相同字符
    //System.out.println(StringUtils.chomp("baidu.com", "com"));
    //去掉末尾最后一个字符.如果是"\n", "\r", 或者 "\r\n"也去除
    //System.out.println(StringUtils.chop("wwe.baidu"));
    //替换~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //替换指定的字符,只替换第一次出现的
    //System.out.println(StringUtils.replaceOnce("www.baidu.com/baidu", "baidu", "hao123"));
    //替换所有出现过的字符
    //System.out.println(StringUtils.replace("www.baidu.com/baidu", "baidu", "hao123"));
    //也是替换,最后一个参数表示替换几个
    //System.out.println(StringUtils.replace("www.baidu.com/baidu", "baidu", "hao123", 1));
    //这个有意识,二三参数对应的数组,查找二参数数组一样的值,替换三参数对应数组的值。本例:baidu替换为taobao。com替换为net
    //System.out.println(StringUtils.replaceEach("www.baidu.com/baidu", new String[]{"baidu", "com"}, new String[]{"taobao", "net"}));
    //同上,未发现不同
    //System.out.println(StringUtils.replaceEachRepeatedly("www.baidu.com/baidu", new String[]{"baidu", "com"}, new String[]{"taobao", "net"}));
    //这个更好,不是数组对应,是字符串参数二和参数三对应替换.(二三参数不对应的话,自己看后果)
    //System.out.println(StringUtils.replaceChars("www.baidu.com", "bdm", "qo"));
    //替换指定开始(参数三)和结束(参数四)中间的所有字符
    //System.out.println(StringUtils.overlay("www.baidu.com", "hao123", 4, 9));
    //添加,增加~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //复制参数一的字符串,参数二为复制的次数
    //System.out.println(StringUtils.repeat("ba", 3));
    //复制参数一的字符串,参数三为复制的次数。参数二为复制字符串中间的连接字符串
    //System.out.println(StringUtils.repeat("ab", "ou", 3));
    //如何字符串长度小于参数二的值,末尾加空格补全。(小于字符串长度不处理返回)
    //System.out.println(StringUtils.rightPad("海川", 4));
    //字符串长度小于二参数,末尾用参数三补上,多于的截取(截取补上的字符串)
    //System.out.println(StringUtils.rightPad("海川", 4, "河流啊"));
    //同上在前面补全空格
    //System.out.println(StringUtils.leftPad("海川", 4));
    //字符串长度小于二参数,前面用参数三补上,多于的截取(截取补上的字符串)
    //System.out.println(StringUtils.leftPad("海川", 4, "大家好"));
    //字符串长度小于二参数。在两侧用空格平均补全(测试后面补空格优先)
    //System.out.println(StringUtils.center("海川", 3));
    //字符串长度小于二参数。在两侧用三参数的字符串平均补全(测试后面补空格优先)
    //System.out.println(StringUtils.center("海川", 5, "流"));
    //只显示指定数量(二参数)的字符,后面以三个点补充(参数一截取+三个点=二参数)
    //System.out.println(StringUtils.abbreviate("中华人民共和国", 5));
    //2头加点这个有点乱。本例结果: ...ijklmno
    //System.out.println(StringUtils.abbreviate("abcdefghijklmno", 12, 10));
    //保留指定长度,最后一个字符前加点.本例结果: ab.f
    //System.out.println(StringUtils.abbreviateMiddle("abcdef", ".", 4));
    //转换,刷选~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //转换第一个字符为大写.如何第一个字符是大写原始返回
    //System.out.println(StringUtils.capitalize("Ddf"));
    //转换第一个字符为大写.如何第一个字符是大写原始返回
    //System.out.println(StringUtils.uncapitalize("DTf"));
    //反向转换,大写变小写,小写变大写
    //System.out.println(StringUtils.swapCase("I am Jiang, Hello"));
    //将字符串倒序排列
    //System.out.println(StringUtils.reverse("中国人民"));
    //根据特定字符(二参数)分隔进行反转
    //System.out.println(StringUtils.reverseDelimited("中:国:人民", ':'));
}

//将数组转换为List
private static void ArrayToList(String[] str){
    System.out.println(Arrays.asList(str) + " 长度:" + str.length);
}

//获得集合数据
private static List getListData(){
    List list = new ArrayList();
    list.add("你好");
    list.add(null);
    list.add("他好");
    list.add("大家好");
    return list;
}

//获得数组数据
private static String[] getArrayData(){
    return (String[]) getListData().toArray(new String[0]);
}

public static void main(String[] args) {
    TestStr();
}

posted @ 2012-02-20 10:02 AthrunWang 阅读(27294) | 评论 (3)编辑 收藏
myeclipse优化方案 myeclipse 10 优化

1 、关闭MyEclipse的自动validation        windows > perferences > myeclipse > validation        将Build下全部勾取消        如果你需要验证某个文件的时候,我们可以单独去验证它。方法是:        在需要验证的文件上( 右键 -> MyEclipse -> Run   Validation 。
2、 启动优化,关闭不需要使用的模块       所以可以将一些不使用的模块禁止 加载启动。       Window > Preferences > General > Startup andy Shutdown 在这里列出的是MyEclipse启动时加载的模块       我这里只让它加载tomcat6 勾选 MyEclipse EASIE Tomcat 7 。            WTP :一个跟myeclipse差不多的东西,主要差别是 WTP 是免费的,如果使用myeclipse,这个可以取消            Mylyn:组队任务管理工具,类似于 CVS ,以任务为单位管理项目进度,没用到的可以取消            Derby:一种保存成 jar 形式的数据库,我没用到,取消            一大排以 MyEclipse EASIE 打头的启动项:myeclipse 支持的服务器,只选自己用的,其他取消,           比如我只选了    tomcat 。  

第一项: 启动功能介绍和样例(红色为需要保留的文件,此为本人样例,请按需选择)

  1. Automatic Updates Scheduler  //自动升级调度   
  2. MyEclipse QuickSetup  //快速启动   
  3. MyEclipse Derby  //derby是一个开源数据库的名字
  4. MyEclipse EASIE Geronimo  1   //同色都是应用服务器的名字   
  5. MyEclipse EASIE Geronimo  2     
  6. MyEclipse EASIE JBOSS  2     
  7. MyEclipse EASIE JBOSS  3     
  8. MyEclipse EASIE JBOSS  4     
  9. MyEclipse EASIE JBOSS  5     
  10. MyEclipse EASIE JBOSS    
  11. MyEclipse EASIE Jetty  4     
  12. MyEclipse EASIE Jetty  5     
  13. MyEclipse EASIE Jetty  6     
  14. MyEclipse EASIE Jetty    
  15. MyEclipse EASIE JOnAS  3     
  16. MyEclipse EASIE JOnAS  4     
  17. MyEclipse EASIE JOnAS    
  18. MyEclipse EASIE JRun  4     
  19. MyEclipse EASIE JRun    
  20. MyEclipse EASIE Oracle  10  AS    
  21. MyEclipse EASIE Oracle  9  AS     
  22. MyEclipse EASIE Oracle AS    
  23. MyEclipse EASIE Orion  1     
  24. MyEclipse EASIE Orion  2     
  25. MyEclipse EASIE Resin  2     
  26. MyEclipse EASIE Resin  3     
  27. MyEclipse EASIE Resin    
  28. MyEclipse EASIE Sun  8 .x    
  29. MyEclipse EASIE Sun  8     
  30. MyEclipse EASIE Sun  9     
  31. MyEclipse EASIE Glassfish  2     
  32. MyEclipse EASIE Glassfish  1     
  33. MyEclipse EASIE Sun One    
  34. MyEclipse EASIE MyEclipse Tomcat  6  Server     
  35. MyEclipse EASIE Tomcat  4     
  36. MyEclipse EASIE Tomcat  5     
  37. MyEclipse EASIE Tomcat  6   
  38. MyEclipse EASIE Tomcat  7    
  39. MyEclipse EASIE Tomcat     
  40. MyEclipse EASIE WebLogic  10     
  41. MyEclipse EASIE WebLogic  6     
  42. MyEclipse EASIE WebLogic  7     
  43. MyEclipse EASIE WebLogic  8     
  44. MyEclipse EASIE WebLogic  9     
  45. MyEclipse EASIE WebLogic    
  46. MyEclipse EASIE WebSphere  5     
  47. MyEclipse EASIE WebSphere  6.1     
  48. MyEclipse EASIE WebSphere  6     
  49. MyEclipse EASIE WebSphere  4     
  50. MyEclipse Examples  //样例   
  51. MyEclipse Memory Monitor  //内存监控   
  52. MyEclipse Tapestry Integration  //插件集成   
  53. MyEclipse JSP Debug Tooling  //jsp调试插件   
  54. MyEclipse File Creation Wizards  //文件创建程序    
  55. ICEfaces Integration for MyEclipse //基于Ajax的JSF开发框架()
  56. MyEclipse Backward Compatibility  //后台功能   
  57. MyEclipse Perspective Plug-in  //透视图插件     
  58. Pluse Collaboration Control Center //Eclipse的网页管理中心
  59. eclipse-cs 4.x.x -> 5.0.0 Migration Plug-in  //Eclipse插件兼容组件
  60. Mozilla Debug UI Plug-in(Incubation)  //Mozilla调试插件(Mozilla是一款浏览器)    
  61. Dynamic Languages ToolKit Core UI //对入PHP等动态语言支持的用户接口
  62. WTP Webservice UI Plug-in  //Web 服务视图插件   
  63. JavaServer Faces Tools - Core  //jsf工具核心包    
  64. Automatic Updates Scheduler //自动更新
  65. Service policy  //Web提供的服务性能目标定义,自动管理
  66. Atfdebug Plug-in(Incubation)  //动态语言的调试工具
  67. Auxiliary Web Module Support for MeEclipse// 辅助的Web模块支持.(可能是Struts等文件自动添加)
  68. JSF Editor Preview Support  for  MyEclipse //jsf编辑器   

第二项: MyEclipse Validation

 

  由于文件导入的时候,不能保证文件的正确性.所以在启动服务前需要做一下验证.包括语法等.

  另外可以自己添加需要的验证模块.如checkStyle的验证.

3 、去掉MyEclipse的拼写检查(如果你觉的有用可以不去) 拼写检查会给我们带来不少的麻烦,我们的方法命名都会是单词的缩写,他也会提示有错, 所以最好去掉,没有多大的用处 Window > perferences > General > Editors > Text Editors > Spelling > 将Enable spell checking复选框的勾选去掉。 4 、修改MyEclipse编辑JSP页面时的编辑工具 Window > perferences > General > Editors > File Associations > 在File types 中选择 *.jsp > 在Associated editors 中将"MyEclipse JSP Editor"设置为默认。 还有XML文件的默认编辑器
5. 关闭自动更新  1).window->Perferences->General->Startup and Shutdown 勾掉 Automatic Updates Scheduler(自动更新调度程序)   2).window->Perferences->MyEclipse->Maven4MyEclipse 勾上 Enable Maven4MyEclipse featrures ;确定关闭窗口;该步骤是为了显示第3步中的Maven节点   3).window->Perferences->MyEclipse->Maven4MyEclipse  勾掉 Download repository index updates on startup

第六步: 更改内存使用文件

1、打开 eclipse.ini


-showsplash
com.genuitec.myeclipse.product
--launcher.XXMaxPermSize
256M
-vmargs
-Dosgi.requiredJavaVersion=1.5
-Xms256m
-Xmx1024m   
-Dosgi.splashLocation=e:MyEclipse 6.0eclipseMyEclipseSplash.bmp
-Duser.language=en
-XX:PermSize=128M
-XX:MaxPermSize=256M
把下面的那个 -XX:MaxPermSize 调大,比如 -XX:MaxPermSize=512M,再把 -XX:PermSize 调成跟 -XX:MaxPermSize一样大
原因:大家一定对这个画面很熟悉吧:

几乎每次 eclipse 卡到当都是因为这个非堆内存不足造成的,把最大跟最小调成一样是因为不让 myeclipse 频繁的换内存区域大小

注意:XX:MaxPermSize 和 Xmx 的大小之和不能超过你的电脑内存大小

第七步: 修改Struts-config.xml文件打开错误

有时点击myeclipse里的struts的xml配置文件,会报错:

Error opening the editorUnable to open the editor ,unknow the editor id…..

把这个窗口关闭后才出正确的xml文件显示,这个我们这样改:

windows–>perferences–>general–>editors->file associations选择*.xml,选择myeclipse xml editor点default,ok

posted @ 2012-01-15 10:56 AthrunWang 阅读(26803) | 评论 (2)编辑 收藏
InputStream与String,Byte之间互转

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 *
 * @author Andy.Chen
 * @mail Chenjunjun.ZJ@gmail.com
 *
 */
public class InputStreamUtils {
    
    final static int BUFFER_SIZE = 4096;
    
    /**
     * 将InputStream转换成String
     * @param in InputStream
     * @return String
     * @throws Exception
     *
     */
    public static String InputStreamTOString(InputStream in) throws Exception{
        
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] data = new byte[BUFFER_SIZE];
        int count = -1;
        while((count = in.read(data,0,BUFFER_SIZE)) != -1)
            outStream.write(data, 0, count);
        
        data = null;
        return new String(outStream.toByteArray(),"ISO-8859-1");
    }
    
    /**
     * 将InputStream转换成某种字符编码的String
     * @param in
     * @param encoding
     * @return
     * @throws Exception
     */
         public static String InputStreamTOString(InputStream in,String encoding) throws Exception{
        
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] data = new byte[BUFFER_SIZE];
        int count = -1;
        while((count = in.read(data,0,BUFFER_SIZE)) != -1)
            outStream.write(data, 0, count);
        
        data = null;
        return new String(outStream.toByteArray(),"ISO-8859-1");
    }
    
    /**
     * 将String转换成InputStream
     * @param in
     * @return
     * @throws Exception
     */
    public static InputStream StringTOInputStream(String in) throws Exception{
        
        ByteArrayInputStream is = new ByteArrayInputStream(in.getBytes("ISO-8859-1"));
        return is;
    }
    
    /**
     * 将InputStream转换成byte数组
     * @param in InputStream
     * @return byte[]
     * @throws IOException
     */
    public static byte[] InputStreamTOByte(InputStream in) throws IOException{
        
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] data = new byte[BUFFER_SIZE];
        int count = -1;
        while((count = in.read(data,0,BUFFER_SIZE)) != -1)
            outStream.write(data, 0, count);
        
        data = null;
        return outStream.toByteArray();
    }
    
    /**
     * 将byte数组转换成InputStream
     * @param in
     * @return
     * @throws Exception
     */
    public static InputStream byteTOInputStream(byte[] in) throws Exception{
        
        ByteArrayInputStream is = new ByteArrayInputStream(in);
        return is;
    }
    
    /**
     * 将byte数组转换成String
     * @param in
     * @return
     * @throws Exception
     */
    public static String byteTOString(byte[] in) throws Exception{
        
        InputStream is = byteTOInputStream(in);
        return InputStreamTOString(is);
    }

}

posted @ 2012-01-09 20:02 AthrunWang 阅读(1449) | 评论 (0)编辑 收藏
仅列出标题
共8页: 上一页 1 2 3 4 5 6 7 8 下一页