[转载]利用Routing特性提高ASP.NET MVC3站点的SEO权重

mikel阅读(1894)

[转载]大叔手记(14):利用Routing特性提高ASP.NET MVC3站点的SEO权重 – 汤姆大叔 – 博客园.

简介

我们在开发互联网程序的时候,有个很重要的事情就是做搜索引擎优化(SEO),我们都知道ASP.NET MVC程序提供了友好的URL以及永久重定向的支持,这些友好的URL是利用Routing系统的特性来支持的,但是在这个Routing里有个问题,就 是多个不同的地址和指向同一个action方法,那对于搜索引擎来说就意味着你的站点有很多地址的内容都是重复的。 本章内容将展示如果解决这一问题。

正文

对于SEO,一个地址对应一个唯一独立的内容是保证最好权重的一个重要步骤,所以我们需要确保每一个URL地址对应的内容都是不重复的(对于MVC 来说也就是不同的Action),但ASP.NET MVC3程序默认是有问题的,比如HomtController.Index这个Action方法就可以被对应到多个地址上,例如:

  1. http://abc.com (默认)
  2. http://abc.com/ (斜杠结尾)
  3. http://abc.com/Home (带有Controller)
  4. http://abc.com/Home/Action (带有Controller和Action)
  5. http://abc.com/home/Action (不同的情况)

等等

要解决这个问题,一个办法是使用IIE URL Rewrite Extension,但是它配置起来挺复杂的,所以在这里我们来利用MVC自己的特性来解决这一问题(通过注册全局的filter),添加如下class:

    public class RemoveDuplicateContentAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var routes = RouteTable.Routes;
            var requestContext = filterContext.RequestContext;
            var routeData = requestContext.RouteData;
            var dataTokens = routeData.DataTokens;
            if (dataTokens["area"] == null)
                dataTokens.Add("area", "");
            var vpd = routes.GetVirtualPathForArea(requestContext, routeData.Values);
            if (vpd != null)
            {
                var virtualPath = vpd.VirtualPath.ToLower();
                var request = requestContext.HttpContext.Request;
                if (!string.Equals(virtualPath, request.Path))
                {
                    filterContext.Result = new RedirectResult(virtualPath + request.Url.Query, true);
                }
            }
            base.OnActionExecuting(filterContext);
        }
    }

然后在Global里注册filter就可以了:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        filters.Add(new RemoveDuplicateContentAttribute());
    }

我们来解释一下:

首先,RemoveDuplicateContent filter获取当我的RequestContext和RouteData,然后判断如果当前没有使用Area的话就为DataToken添加一个空值, 这一点非常重要,因为如果不加的话,后面再使用Area特性的话就会出错。

接着,该filter通过RouteData获取虚拟path,然后toLower一下转换成小写。

然后,和当前request的path进行比较,如果不一致就重定向到小写的虚拟path上,这样,搜索引起就能识别当写多个请求其实都是对应到你的虚拟path那个真实的地址上,也就是说唯一对应那个action的地址上,以此来做到一个地址对应一个内容。

总结

对于SEO,确保一个地址对应一个内容是非常重要的,因为如果多个地址对应一个内容的话,搜索引起不知道到底你的这些地址哪个是最新哪个是旧的,因为可能就会降低你的权重。

英文原文:http://weblogs.ASP.NET/imranbaloch/archive/2011/12/19/solving-duplicate-content-issue-in-asp-net.aspx

同步与推荐

本文已同步至目录索引:《大叔手记全集》

大叔手记:旨在记录日常工作中的各种小技巧与资料(包括但不限于技术),如对你有用,请推荐一把,给大叔写作的动力。

[转载]jQuery mobile 框架,资源,书籍

mikel阅读(1273)

[转载]第一天 认识jQuery mobile 框架,资源,书籍 – youxiachai – 博客园.

前言

这里就不对JQuery mobile做过多的历史介绍,直接进行JQuery mobile的学习

jQuery mobile 框架纵览

1.jQuery mobile 的显示结构

如图1.1

Untitled Page

从上面的图示我们可以知道,一个完整的页面是由

header

content

footer

这三部分组成.那这三部分是不是一个jQuerymobile 页面必须拥有的元素呢,而且必须是这样的排序呢?答案是:不是,这些元素可以组合和排序可以任由自己自由选择,当然,我个人建议还是严格按照规范进行排序.

2,第一个Hello,World 的jQuery Mobile!

在进行我们第一个jQuery Mobile Hello world程序之前,我们要对html5进行一个简单的认识,因为,jQuery Mobile是基于Html5开发的.

1.文档的声明和jQuery Mobile的初始化

相比当初html 4 那种冗长的文档的声明相比,html5的文档声明无疑简洁很多,你只需在你的页面第一行输入:

1
<!DOCTYPE html>

这样就完成了一个html5 的文档类型声明了,接下来就开始一个标准的html的页面编写:

1
2
3
4
5
6
7
8
9
10
11
<html>
<head>
<meta charset="utf-8">
<title>Hello,Mobile,world</title>
<head>
<body>
</body>
</html>

这样一个标准的jQuery Mobile的文档格式就初始化完毕,这里,我使用了jQuery Mobile 的CDN 连接,如何是本地的话,只要做出相应的替换即可.

2.编写我们的页面

编码的步骤按照我上面的元素写即可,page,header,footer,注意!html5 新增的一个属性,data-role!!!

1
2
3
4
5
6
7
8
9
10
11
<div data-role="page">
<div data-role="header">
<h1>Hello,World!!</h1>
</div>
<div data-role="content">
<p>Hello,Mobile World!</p>
</div>
<div data-role="footer">
<h1>Coypright:youxiachai</h1>
</div>
</div>

这样就完成了我们第一个jQuery mobile的页面,很简单是不?

一个完整的jQuery mobile页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello,Mobile,world</title>
</head>
<body>
<div data-role="page">
<div data-role="header">
<h1>Hello,World</h1>
</div>
<div data-role="content">
<p>Hello,Mobile,World!!<p>
</div>
<div data-role="footer">
<h4>Copyright:youxiachai</h4>
</div>
</div>
</body>
</html>

我们的第一个jQuery Mobile就这样完成了,是不是简单到难以置信呢?没错,jQuery mobile就是这么方便,下面介绍一下,我们从那里获取学习的资源,第二天,我详细说说jQuery mobile 的几个常用组件,如果,你觉得这些很不够的话,我建议你,认真看下我提供的传送门

jQuery 的学习资源

http://jquerymobile.com/resources/

jQuery Mobile 已经提供了非常丰富的学习资源,涵盖了,jQuery mobile 项目的演示,书籍,框架,第三方插件,,工具,还有相应的教程和文章,当然,都是英文的…

jQuery 书籍介绍

以下都是豆瓣的传送门,书籍介绍我已经全部补全了

jQuery mobile

http://book.douban.com/subject/6724989/

jQuery Mobile First Look

http://book.douban.com/subject/6954448/

Using the CSS3 Mobile Pack for Adobe Fireworks CS5

http://book.douban.com/subject/7564687/

Adobe Dreamweaver Cs5.5 Studio Techniques

http://book.douban.com/subject/7070561/

对了大家应该更关心电子版吧.呵呵,正在上传中,因为版权问题,等课程完了就撤销…还有下载了的朋友不要到处传,就算要传也要记得不要写上转载地址

__jQuery_Mobile.pdf – 6.5 MB
__jQuery_Mobile_First_Look.pdf – 5.5 MB
__Adobe_Dreamweaver_CS5_5_Studio_Techniques__Designing_and_Developing_for_Mobile_with_jQuery__HTML5__and_CSS3.pdf – 8.3 MB

[转载]随机生成13位绝对无重复随机数的高效方法

mikel阅读(1010)

[转载]随机生成13位绝对无重复随机数的高效方法 – 天地不仁 – 博客园.

问题描述:随机生成13位绝对无重复随机数的高效方法。

问题思路:

1、    预先生成好所有无重复随机数并存储,按需取数;

2、    随机生成,即时比对当前所有已生成数。若存在,则重新生成。

3、    寻找一个好的无冲突的hash算法(或冲突概率较低)。

4、    按照一定的算法来生成伪随机数,要求满足一定数量级内无相似度或较低相似度。

随机就不可能不重复,故任何算法不可能实现真正的随机.只是能够在一定程度上防止高频度的碰撞及相似度,从而给外界一个随机的假像.

思路一的相关方法及问题:

事先生成1-10000000000000,然后分组打乱,重复若干次后,即可获得所有的10万亿的数据。生成13位数(10万亿)大概是2的43 次方,假设我们采用4个bit位存储一个数字(满足能表达数目至少大于九的二进制最小长度),那么一个13位的数需要52bit,即约为7Byte(字 节),乘以2的43次方,可以预算整个占用空间约为56TB。无论采用何种存储方式的优化,因为随机化后的相似度极低(动态规划中的邻灰度压缩算法,包括 使用数据库)优化后也是TB级的数据量,故此基于此思路上实现方法均是不现实的。

思路二的相关方法及问题:

1、  数据库用生成序列做主键(由数据库来完成不重复判断),插入失败则说明此数据已存

在,方法实现简单易行。

2、排序随机生成的随机数,采用二分查找法,并插入排序队列中的合适位置。

3、人工生成红黑树(AVL),每次插入时判断是否已存在。

此三种方法均可以实现,但是从效率而言第三种方法较好,通过数据库异常来确定数据是否重复性能明显是最差的。相比第二种方法,插入采用链表明显效率更高,但是要实现二分查找的随机访问必须要使用类似数组的序列。固采用第三种红黑树的方法是效率最高的。

但是这类方法有一个共同的缺陷,当已经生成好50%以上的数据后,这颗树的查找效率是没有问题的,但是这颗树(或者数组)的大小也快接近TB级了。 从这个角度来看反而不如第一种思路实现的方法简单。而且举一个例子,十个数里面如果已经取完八个不同的数据后再生成随机数,那么重复抽到已经生成的那八个 数中的一个的概率就非常大了,这样分析后可以得到,每生成一个随机数,需要遍历整颗树的次数会随着树的增大而增大,这是不可以接受的。

思路三的相关方法及问题:

1、依次Hash 1-10000000000000个数字,并将结果进行冲突检测。

2、  MD5类似强Hash算法加密固定区间内的数字,如果出现相同则说明无法逆向解密。(需要采用单向唯一映射修改长度)

3、  GUID的生成方法参考。

方法一,需要存储相应的结果来进行冲突检测,类似与思路二中的方法3,存储量非常大。方法二和方法三是同一种问题,MD5加密后是生成16或者32 位不重复字符,注意这里是字符(0-9a-zA-Z),而GUID随机生成的是128位的字符,故很难找到一种将16、32甚至128位映射到32位且每 一位由62(10+26+26)个字符集映射到10(0-9)位的数字字符集的方法来单向映射如此大的数字集之差。

思路四的相关方法及问题:

这个思路是我们目前推荐的且易于实现。

分段链接法;

首先我们将数据划分为6+6+1,这就将我们的数据量缩小到百万级。为什么这样划分,是根据一定的时间效率和后面的链接方法决定的。

下面我们来讨论随机生成6位随机数的方法:

1000000 个数中生成 100000 位随机数花时3s。

1000000 个数中生成 500000 位随机数花时16s。

1000000 个数中生成 800000 位随机数花时43s。

1000000 个数中生成 900000 位随机数花时 > 1h。

故我们选择第三种方案。

生成的数据截图如下:

使用同样的方法再重新生成一个6位随机数。

链接方法我们采用横向邻近无关联连接法:

如图:

(此处不使用数据库笛卡尔乘积,而采用程序读取部分数据,并在数据不足时自动缓存下一部分数据)

固此方法可以得到千亿级内不重复,八十万级数据量内无相似度;(对称链接)。而且对于整个的空间消耗都符合要求。

下面是关键的数据库随机选取随机数SQL脚本。

USE   Job

GO

CREATE   TABLE   tb2(id   char(6))

CREATE   UNIQUE   INDEX   IX_tb2   ON   tb2(id)

WITH   IGNORE_DUP_KEY

GO

DECLARE   @dt   datetime

SET   @dt   =   GETDATE()

SET   NOCOUNT   ON

DECLARE   @row   int

SET   @row   =   800000

WHILE   @row   > 0

BEGIN

RAISERROR( ‘need   %d   rows ‘,   10,   1,   @row)   WITH   NOWAIT

SET   ROWCOUNT   @row

INSERT   tb2   SELECT

id   =   RIGHT(100000000   +   CONVERT(bigint,   ABS(CHECKSUM(NEWID()))),  6)

FROM   syscolumns   c1,   sysobjects   o–,   syscolumns   c2

SET   @row   =   @row   –   @@ROWCOUNT

END

SELECT   BeginDate   =   @dt,   EndDate   =   GETDATE(),   Second   =   DATEDIFF(Second,   @dt,   GETDATE())

GO

SELECT   COUNT(*)   FROM   tb2

GO

数据打乱脚本

select identity(int,1,1)   as   rownumber, * into tmp_tb from tb order by NEWID();

select identity(int,1,1)   as   rownumber, * into tmp_tb2 from tb2 order by NEWID();

数据合并采用程序控制:

具体合并方法见上图。要点是记录在第一表中的位置,及错开度N。

[转载]T-SQL查询进阶--理解SQL Server中索引的概念,原理以及其他

mikel阅读(946)

[转载]T-SQL查询进阶–理解SQL Server中索引的概念,原理以及其他 – CareySon – 博客园.

简介


SQL Server中,索引是一种增强式的存在,这意味着,即使没有索引,SQL Server仍然可以实现应有的功能。但索引可以在大多数情况下大大提升查询性能,在OLAP中尤其明显.要完全理解索引的概念,需要了解大量原理性的知 识,包括B树,堆,数据库页,区,填充因子,碎片,文件组等等一系列相关知识,这些知识写一本小书也不为过。所以本文并不会深入讨论这些主题。

索引是什么


索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。

精简来说,索引是一种结构.在SQL Server中,索引和表(这里指的是加了聚集索引的表)的存储结构是一样的,都是B树,B树是一种用于查找的平衡多叉树.理解B树的概念如下图:

B-

理解为什么使用B树作为索引和表(有聚集索引)的结构,首先需要理解SQL Server存储数据的原理.

在SQL SERVER中,存储的单位最小是页(PAGE),页是不可再分的。就像细胞是生物学中不可再分的,或是原子是化学中不可再分的最小单位一样.这意味着,SQL SERVER对于页的读取,要么整个读取,要么完全不读取,没有折中.

在数据库检索来说,对于磁盘IO扫描是最消耗时间的.因为磁盘扫描涉及很多物理特性,这些是相当消耗时间的。所以B树设计的初衷是为了减少对于磁盘的扫描 次数。如果一个表或索引没有使用B树(对于没有聚集索引的表是使用堆heap存储),那么查找一个数据,需要在整个表包含的数据库页中全盘扫描。这无疑会 大大加重IO负担.而在SQL SERVER中使用B树进行存储,则仅仅需要将B树的根节点存入内存,经过几次查找后就可以找到存放所需数据的被叶子节点包含的页!进而避免的全盘扫描从 而提高了性能.

下面,通过一个例子来证明:

在SQL SERVER中,表上如果没有建立聚集索引,则是按照堆(HEAP)存放的,假设我有这样一张表:

1

现在这张表上没有任何索引,也就是以堆存放,我通过在其上加上聚集索引(以B树存放)来展现对IO的减少:

2

理解聚集和聚集索引


在SQL SERVER中,最主要的两类索引是聚集索引和非聚集索引。可以看到,这两个分类是围绕聚集这个关键字进行的.那么首先要理解什么是聚集.

聚集在索引中的定义:

为了提高某个属性(或属性组)的查询速度,把这个或这些属性(称为聚集码)上具有相同值的元组集中存放在连续的物理块称为聚集。

简单来说,聚集索引就是:

3

在SQL SERVER中,聚集的作用就是将某一列(或是多列)的物理顺序改变为和逻辑顺序相一致,比如,我从adventureworks数据库的employee中抽取5条数据:

4

当我在ContactID上建立聚集索引时,再次查询:

5

在SQL SERVER中,聚集索引的存储是以B树存储,B树的叶子直接存储聚集索引的数据:

grid.ai

因为聚集索引改变的是其所在表的物理存储顺序,所以每个表只能有一个聚集索引.

非聚集索引

因为每个表只能有一个聚集索引,如果我们对一个表的查询不仅仅限于在聚集索引上的字段。我们又对聚集索引列之外还有索引的要求,那么就需要非聚集索引了.

非聚集索引,本质上来说也是聚集索引的一种.非聚集索引并不改变其所在表的物理结构,而是额外生成一个聚集索引的B树结构,但叶子节点是对于其所在表的引 用,这个引用分为两种,如果其所在表上没有聚集索引,则引用行号。如果其所在表上已经有了聚集索引,则引用聚集索引的页.

一个简单的非聚集索引概念如下:

6

可以看到,非聚集索引需要额外的空间进行存储,按照被索引列进行聚集索引,并在B树的叶子节点包含指向非聚集索引所在表的指针.

MSDN中,对于非聚集索引描述图是:

grid.ai

可以看到,非聚集索引也是一个B树结构,与聚集索引不同的是,B树的叶子节点存的是指向堆或聚集索引的指针.

通过非聚集索引的原理可以看出,如果其所在表的物理结构改变后,比如加上或是删除聚集索引,那么所有非聚集索引都需要被重建,这个对于性能的损耗是相当大的。所以最好要先建立聚集索引,再建立对应的非聚集索引.

聚集索引 VS 非聚集索引


前面通过对于聚集索引和非聚集索引的原理解释.我们不难发现,大多数情况下,聚集索引的速度比非聚集索引要略快一些.因为聚集索引的B树叶子节点直接存储数据,而聚集索引还需要额外通过叶子节点的指针找到数据.

还有,对于大量连续数据查找,非聚集索引十分乏力,因为非聚集索引需要在非聚集索引的B树中找到每一行的指针,再去其所在表上找数据,性能因此会大打折扣.有时甚至不如不加非聚集索引.

因此,大多数情况下聚集索引都要快于非聚集索引。但聚集索引只能有一个,因此选对聚集索引所施加的列对于查询性能提升至关紧要.

索引的使用


索引的使用并不需要显式使用,建立索引后查询分析器会自动找出最短路径使用索引.

但是有这种情况.当随着数据量的增长,产生了索引碎片后,很多存储的数据进行了不适当的跨页,会造成碎片(关于跨页和碎片以及填充因子的介绍,我会在后续文章中说到)我们需要重新建立索引以加快性能:

比如前面的test_tb2上建立的一个聚集索引和非聚集索引,可以通过DMV语句查询其索引的情况:

SELECT index_type_desc,alloc_unit_type_desc,avg_fragmentation_in_percent,fragment_count,avg_fragment_size_in_pages,page_count,record_count,avg_page_space_used_in_percent
FROM sys.dm_db_index_physical_stats(DB_ID('AdventureWorks'),OBJECT_ID('test_tb2'),NULL,NULL,'Sampled')

7

我们可以通过重建索引来提高速度:

ALTER INDEX idx_text_tb2_EmployeeID ON test_tb2 REBUILD

还有一种情况是,当随着表数据量的增大,有时候需要更新表上的统计信息,让查询分析器根据这些信息选择路径,使用:

那么什么时候知道需要更新这些统计信息呢,就是当执行计划中估计行数和实际表的行数有出入时:

8

使用索引的代价


我最喜欢的一句话是”everything has price”。我们通过索引获得的任何性能提升并不是不需要付出代价。这个代价来自几方面.

1.通过聚集索引的原理我们知道,当表建立索引后,就以B树来存储数据.所以当对其进行更新插入删除时,就需要页在物理上的移动以调整B树.因此当更新插 入删除数据时,会带来性能的下降。而对于非聚集索引,当更新表后,非聚集索引也需要进行更新,相当于多更新了N(N=非聚集索引数量)个表。因此也下降了 性能.

2.通过上面对非聚集索引原理的介绍,可以看到,非聚集索引需要额外的磁盘空间。

3.前文提过,不恰当的非聚集索引反而会降低性能.

所以使用索引需要根据实际情况进行权衡.通常我都会将非聚集索引全部放到另外一个独立硬盘上,这样可以分散IO,从而使查询并行.

总结


本文从索引的原理和概念对SQL SERVER中索引进行介绍,索引是一个很强大的工具,也是一把双刃剑.对于恰当使用索引需要对索引的原理以及数据库存储的相关原理进行系统的学习.

[转载]20个代码生成框架

mikel阅读(1105)

[转载]20个代码生成框架 – skyme – 博客园.

1.1 CodeSmith

一款人气很旺国外的基于模板的dotnet代码生成器

官方网站:http://www.codesmithtools.com

官方论坛:http://forum.codesmithtools.com/default.aspx

版权形式:30天试用

开源:否

需要先注册确认后才能下载

1.2 MyGenerator

MyGenerator是又一个国外很不错的代码生成工具,有人觉得比CodeSmith简单、好用。所有api可以在帮助菜单中找到。

官方网站:http://www.mygenerationsoftware.com/portal/default.aspx

官方论坛:

版权形式:免费

开源:否

1.3 NHibernate.

NHibernate是Hibernate公司在Java自动生成领域取得巨大成功后推出的一款ORM工具.

官方网站:http://www.hibernate.org/

官方论坛:

版权形式:免费

开源:否

1.4 湛蓝.Net代码生成器

一款基于软件自动生成理念,能直接生成应用的dotnet代码生成器

官方网站:http://www.dotnetcoding.net

官方论坛:http://dotnetcoding.cnblogs.com/

版权形式:免费

开源:否

1.5 动软.NET代码自动生成器

一款人气很旺的免费C#代码生成器

官方网站:http://www.maticsoft.com

官方论坛:

版权形式:免费

开源:否

1.6 CodePlus

专为SQL server C#语言设计的代码生成器,功能还是很强大

官方网站:http://www.CodePlus.com

官方论坛:

版权形式:需要少量的注册费用

开源:否

下载地址很神秘

1.7 CodeMaker

asp,jsp,php代码生成工具,自动生成维护数据库信息的动态网页的代码生成器。它可以帮助ASP、JSP、PHP开发人员快速的开发简单的 数据库维护程序。无需任何编码,只需将数据库结构导入到CodeMaker中并做简单的设置,CodeMaker即可生成完整的数据库操作页面。用 CodeMaker可以简单快速的创建网站后台维护程序。提高开发效率数十倍以上。

官方网站:http://www.mhua.com

官方论坛:

版权形式:免费

开源:否

1.8 非非.Net代码生成器

可以使用本工具生成VB.Net和C#语言的代码,以及三层架构与ORM架构代码,并且使用的ORM持久化组件是开源的,您可以在本软件的安装目录下找到它

官方网站:

官方论坛:

版权形式:免费

开源:否

1.9 BMW业务模型及代码生成器

一款人气很旺的免费C#代码生成器

官方网站:

官方论坛:

版权形式:免费

开源:否

1.10 飞鹰CoolCoder

专门为采用nhibernate做关系对象影射架构的系统提供代码的工具,简单易用,虽然不提供源码,我们可以用反编译工具对其反编译看源码。这是个很不错的学习机会。

官方网站:

官方论坛:

版权形式:免费

开源:否

1.11 AutoCoder自动代码生成器

AutoCoder自动代码生成器是一个根据模板自动生成代码的代码生成工具,根据模板的不同,可以生成任何语言(如:ASP、C#、 C++BUILDER、DELPHI、JAVA、JSP、PHP、VB、VC.NET……),不同层次结构(B/S、C/S、n-tiger……),基于 不同数据库(ORACLE、MSSQL、MYSQL、DB2、ACCESS、ODBC、OLE……)的代码和开发文档。开发人员无需任何编码,只需将数据 库结构导入到AutoCoder中,并做简单的设置即可生成所需的代码,可以大幅度地提高开发效率!!!系统同时也提供了一个模板制作工具 ModeMaker,用户可以方便的制作或修改模板,使得生成的代码更适合您使用!系统附带了十几个模板,基本上可以满足用户多方位的需求

官方网站:

官方论坛:

版权形式:免费

开源:否

1.12 wawa Code Pro代码生成器

一款开源的用VBScript写的代码生成器

官方网站:

官方论坛:

版权形式:免费

开源:是

1.13 MyGeneration

MyGeneration 是一款不错的ORM和代码生成工具,它基于模板(Template)工作,安装好MyGeneration 后自带了很多模板,并且提供在线模板库提供模板升级和允许用户自定义模板。MyGeneration 的模板可以用C#, VB.NET, JScript, and VBScript编写。使用MyGeneration 可以为Gentle.NET, Opf3, NHibernate等生成ORM架构或ORM文件,为多种数据库生成存储过程,为.Net项目生成C#、VB.NET 程序代码,PHP、HTML等页面代码。

官方网站:http://www.mygenerationsoftware.com/portal/default.aspx

官方论坛:http://www.mygenerationsoftware.com/phpbb3/index.php

版权形式:免费

开源:是

1.14 iBATIS的代码生成工具 Ibator

Ibator 是一个 iBATIS 的代码生成工具,它自动扫描数据库中的所有表,并生成Bean类和对应的配置文件。同时还帮你生成CRUD操作方法。提供一个独立运行的界面以及 Eclipse 的插件。

官方网站:http://ibatis.apache.org/ibator.html

官方论坛:

版权形式:免费

开源:是

1.15 代码自动生成工具 Acceleo

Acceleo是MDA(Model Driven Architecture:模型驱动体系结构)的一个代码自动生成工具,Acceleo能把模型转换为Java,C#,PHP等代码。Acceleo基于 Eclipse平台以及EMF(Eclipse模型框架),开放源代码,可以免费使用。

官方网站:http://www.acceleo.org/pages/home/en

官方论坛:

版权形式:免费

开源:是

1.16 最快速的java代码生成器 rapid-generator

rapid-generator是一个生成器引擎,让你可以专注与代码生成器模板的编写, 可以生成如ibatis,ibatis3,hibernate,spring_mvc,struts2等等代码.

该项目是 Rapid Framework 框架的一部分。

官方网站:http://code.google.com/p/rapid-generator/

官方论坛:

版权形式:免费

开源:是

1.17 开源代码生成插件 J2EE Spider

J2EE Spider是一个开源代码生成插件。它能够以界面引导方式产生代码,定制代码生成模板。J2EE Spider当前能够生成基于Struts/JSF,Spring,Hibernate等框架的源代码。

J2EE Spider的主要特性如下:

•支持以界面引导方式产生代码

•支持与Eclipse集成

•支持增量构建

•支持正向逆向工程

•产生代码的国际化

•支持利用模版生成代码

•支持利用多个框架

J2EE Spider除了提供可定制的页面模版外,还可以在需要时候把新的技术集成进来。不过,其只对以下几种提供“立等可用的服务”:

•Web 框架:Struts、JSF和Mentawa(一个来自巴西的应用框架)

•依赖注入容器:Spring

•O/R 映射: Hibernate

•页面模版:SiteMesh 和Facelets

官方网站:http://www.spideronrails.org/

官方论坛:

版权形式:免费

开源:是

1.18 slave4j

slave4j是一个eclipse的插件,也是一个springmvc,spring,hibernate的整合框架

slave4j插件能帮你做什么?

搭建spring,springmvc,hibernate开发环境

代码生成:生成curd代码,包括jsp

slave4j框架能帮你做什么?

封装了hibernate常用操作

提供了常用的工具类(1.0.0版本提供的比较少,在以后的版本中继续添加)

官方网站:http://code.google.com/p/slave4j/

官方论坛:

版权形式:免费

开源:是

1.19 JunJava

JunJava 一个java程序代码自动生产的工具。

针对JavaEE或者是JAVASE,AutoJava能快速的生成可以由目前最流行的结构框架搭建的项目,如Hibernater,Spring,Struts等自动组合配置。

通过连接数据库,选择任意数据表生成实体模型,支持简单SQL语句查询,直接浏览数据表结构和数据。

支持多项目管理,文件编辑,一键式生成代码。快速构建基础框架代码,并支持MVC 3层构架。

生成的项目可以直接导入Eclipse,并且可以直接运行部署Tomcat服务器中,大大减少程序员的工作量。

官方网站:

官方论坛:

版权形式:免费

开源:是

1.20 XDoclet

XDoclet 是一个通用的代码生成实用程序,是一个扩展的Javadoc Doclet引擎(现已与Javadoc Doclet独立),XDoclet是EJBDoclet的后继者,而EJBDoclet是由Rickard Oberg发起的。它允许您使用象 JavaDoc 标记之类的东西来向诸如类、方法和字段之类的语言特征添加元数据。随后,它利用这些额外的元数据来生成诸如部署描述符和源代码之类的相关文件。可以让你创 建自己的javadoc @tags进而利用XDoclet中的Templet enging基于这些@tags生成源代码或其他文件(例如xml的deployment descriptors)。

XDoclet 继承了 JavaDoc 引擎的思想,允许根据定制 JavaDoc 标记生成代码和其他文件。当然,XDoclet 也可以访问整个解析树。这样,它就可以访问类、类的包结构和类的方法。

XDoclet 提供了自己的模板引擎。该模板引擎在概念上类似于 JavaServer Pages(JSP)技术。它实质上包含两类标记:块标记(block tag)和内容标记(content tag)。块标记控制如 Java 编程语言中的 if 和 for 语句之类的流。内容标记打印当前解析树上下文的片段,如类名称、方法名称和参数,等等。

官方网站:http://xdoclet.sourceforge.net/xdoclet/index.html

官方论坛:

版权形式:免费

开源:是

[转载]ASP.NET MVC3关于生成纯静态后如何不再走路由直接访问静态页面

mikel阅读(1434)

[转载]ASP.NET MVC3关于生成纯静态后如何不再走路由直接访问静态页面 – wxj200589 – 博客园.

问题描述:高访问量类型的电子商务网站,需要将一些不是经常变化的页面生成静态页面,然后普通用户就可以直接访问这些静态页面而不用再访问需要连接数据库的动态页面。那么ASP.NET MVC3中如何做到这一点呢?

要解决这个问题,我们需要先了解ASP.NET应用程序的生命周期,先看下面作者整理的一张图片:

ASP.NET生命周期 (2)

从图中我们可以清楚的看到:通用IIS访问应用程序时,每次的单个页面URL访问时,都会先经过HttpApplication 管线处理请求,走过BeginRequest 事件之后才会去走路由访问具体的Controller和Action,最后结束的时候会请求EndRequest事件。下面用一张图来表示这个顺序:

image

注意图中标示的红色部分就是我们要实现的部分,实现如下:

1 新建MyHandler.cs

public class MyHandler:IHttpModule
    {
        public void Init(HttpApplication application)
        {
            application.BeginRequest +=
                (new EventHandler(this.Application_BeginRequest));
            application.EndRequest +=
                (new EventHandler(this.Application_EndRequest));
        }
private void Application_BeginRequest(Object source,
        EventArgs e)
        {
            // Create HttpApplication and HttpContext objects to access
            // request and response properties.
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;
            string filePath = context.Request.FilePath;
            string fileExtension =
                VirtualPathUtility.GetExtension(filePath);
            if (fileExtension.Equals(".html"))
            {
                context.Response.WriteFile(context.Server.MapPath(filePath));//直接走静态页面
//此处可以加入缓存,条件也可以根据需要来自己定义
                context.Response.End();
            }

        }
        private void Application_EndRequest(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;
            string filePath = context.Request.FilePath;
            string fileExtension =
                VirtualPathUtility.GetExtension(filePath);
            if (fileExtension.Equals(".html"))
            {
                context.Response.Write("<hr><h1><font color=red>" +
                    "HelloWorldModule: End of Request</font></h1>");
            }
        }

        public void Dispose() { }
    }

2. web.config中加入以下代码,才会运行自定义的管道处理类

   1:  <httpModules>
   2:        <add name="MvcTest.MyHandler" type="MvcTest.MyHandler"/>
   3:      </httpModules>

运行一下自己的代码,看看效果你就全明白了!

补充:根据@小尾鱼的提示,如果直接在自己的项目文件下生产了和URL中一样的目录文件,比如访 问:yourdomin.com/product/1.html,你的项目文件夹下真的存在product/1.html这个路径,那么IIS会直接去请 求这个静态页面,如果项目中使用了自定义的管道处理程序,那么这个静态页仍然会走我们的自定义管道处理程序,我们可以在这里通过缓存来实现要不要重新成长 静态页或删除过期产品的静态页,如果不使用此方法,只能去写执行计划,定时跑这些静态文件了,修改Application_BeginRequest

   1:  private void Application_BeginRequest(Object source,
   2:          EventArgs e)
   3:          {
   4:              // Create HttpApplication and HttpContext objects to access
   5:              // request and response properties.
   6:              HttpApplication application = (HttpApplication)source;
   7:              HttpContext context = application.Context;
   8:              string filePath = context.Request.FilePath;
   9:              string fileExtension =
  10:                  VirtualPathUtility.GetExtension(filePath);
  11:              if (fileExtension.Equals(".html"))
  12:              {
  13:                  //判断缓存是否存在,不存在加入缓存,调用生成静态的类和方法
  14:                  //产品过期,移除静态文件,302重定向
  15:                  if (System.IO.File.Exists(context.Server.MapPath(filePath)))
  16:                  {
  17:                      context.Response.WriteFile(context.Server.MapPath(filePath));
  18:                      context.Response.End();
  19:                  }
  20:  
  21:  
  22:              }

思路大体如此。

[转载]sqlite数据库中自增key的设定,autoincrement 和 rowid

mikel阅读(1031)

[转载]sqlite数据库中自增key的设定,autoincrement 和 rowid – 吴艾伟 – 博客园.

在用SQLite设计表时,每个表都有一个自己的整形id值作为主键,其实可以不指定这 么一个id值,SQLite内部本来就会为每个表加上一个 rowid,这个rowid可以当成一个隐含的字段使用,但是由sqlite引擎来维护的,在 3.0以前rowid是32位的整数,3.0以后是 64位的整数,为什么不直接使用这个内部的rowid作为每个表的id主键呢。

以下是我的界面设计图:

以下DDL代码:

大家也可以去官网看:http://www.sqlite.org/autoinc.html

用指定INTEGER PRIMARY KEY AUTOINCREMENT 和不指定自增长字段用rowid有什么区别:

使用自增长字段为主键有不少问题,比如维护或是在大型分布应用中主键冲突的解决等。在一些大型分布应用中主键一般选用guid,这可以有效的避免主键冲突,减少对主键维护的工程。当然,对于中小型的应用,自增长字段的好处更多一些,简单、快速。

Sqlite中,一个自增长字段定义为INTEGER PRIMARY KEY AUTOINCREMENT,那么在插入一个新数据时,只需要将这个字段的值指定为NULL,即可由引擎自动设定其值,引擎会设定为最大的 rowid+1。当然,也可以设置为非NULL的数字来自己指定这个值,但这样就必须自己小心,不要引起冲突。当这个rowid的值大于所能表达的最大值 9223372036854775807 (3.0及以后版本的rowid最大值)后,rowid的新值会这个最大数之前随机找一个没被使用了的值。所以在rowid达到最大值前,rowid的值 是严格单调增加的。 INTEGER PRIMARY KEY AUTOINCREMENT 自增长字段的算法与rowid稍微有些不同。 第一,在达到最大值后,rowid会找已被删除的字段对应的rowid作为新值,而自增长字段则会丢出一个SQLITE_FULL的错误。 第二,自增长字段在增加新值时,是找一个从没被使用过的rowid作为新值,而rowid则是找最大已存在的rowid+1。这里对应用的影响会比较大, 尤其是一些对id值有依赖的元记录,只适合使用自增长字段而不能用rowid

第三,使用自增长字段,引擎会自动产生一个sqlite_sequence表,用于记录每个表的自增长字段的已使用的最大值,用户可以看到,并可以 用使用 Update、Delete和Insert操作,但不建议这么使用,这会让引擎混乱。如果使用rowid,也会有这么一个内部表,用户可以维护rowid 值,但看不到。 这么看来,如果直接使用rowid来代替自增加字段,根据两者的细微的差别,需要注意是否与自己的应用冲突,如果没有冲突,那么用rowid会更快一点。


SQLite中创建自增字段,一个声明为 INTEGER PRIMARY KEY 的字段将自动增加。

从 SQLite 的 2.3.4 版本开始,如果你将一个表中的一个字段声明为 INTEGER PRIMARY KEY,那么无论你何时向该表的该字段插入一个 NULL 值,这个 NULL 值将自动被更换为比表中该字段所有行的最大值大 1 的整数;如果表为空,那么将被更换为 1。

一个新的API函数 sqlite3_last_insert_rowid() 返回最近的插入操作的整形键.

注意这个整型键始终比之前插入表中的最后一个键大1。

新键相对于表中的已有键来说是唯一的,但它可能与之前从表中删除的键值重叠。

要始终得到在整个表中唯一的键,在INTEGER PRIMARY KEY的声明之前加关键词AUTOINCREMENT.这样被选的键将总是比表中已存在的最大键大1。若可能的最大键已存在于表中,INSERT操作将失败并返回一个SQLITE_FULL错误码.

[转载]9个有用的jQuery日历和网页设计师的日期选择器插件

mikel阅读(968)

[转载]9个有用的jQuery日历和网页设计师的日期选择器插件 – 创想中国(羲闻) – 博客园.

事件日历和日期选择器是是我们开发当中经常遇到的问题,这个时候我们就需要用JQuery插件来实现,昨天我在做项目的时候,遇到了个问题就是如何显示倒计时问题,我这个时候就用JQuery插件解决的,今天给大家分享9个其他的时间选择器插件

1。FullCalendar

FullCalendar是一个著名的jQuery日历插件,它提供了拖和下降功能,与谷歌日历整合,并能够通过JSON获取事件等功能。设计人员可以轻松定制外观和感觉的日历,而开发人员可以利用用户触发的事件暴露挂钩。

2. Astonishing iCal-like Calendar

这是一个使用jQuery和CSS来构建一个iPhone风格的日历应用程序的教程。

3 jQuery UI的DatePicker

jQuery UI的DatePicker的是一个高度可配置的插件,他添加到您的网页的日期选择器的功能。您可以自定义的日期格式和语言,限制了可选择的日期范围,并容易添加按钮和其他导航选项。

4。jMonthCalendar

jMonthCalendar支持全月日历和事件。这个插件的有趣的部分是它允许开发者开发互动的日历。

日期选择器

MIT和GPL许可证下发布的日期选择器是一个jQuery日历插件。它有很多的选项和功能。例如,你可以有多个日历组件,标记日期作为特殊的日子,很容易通过C​​SS自定义,并允许不同的选择模式。所有这些都可以轻松配置。

6。jQuery的周历插件

jQuery的周历是一个插件,其中将包括一个Web应用程序中的周历。事件可以作为一个数组或JSON提供,这些事件可以随意拖动,下拉并调整其大小。有很多的选项和事件的回调,可以参考通过官方插件页面。

简单的jQuery日期选择器插件

这是一个非常简单的的日期选择器插件。但是,它提供了很多有益和必要的日期选择器功能。例如,您可以轻松地浏览,以月和年通过下拉选择框下降。

8jQuery的日期选择器

这是一个干净,不显眼的jQuery插件,它允许你轻松地添加日期选择器,Web窗体和网页。有很多这个简单的插件的配置和选项。如何使用这个插件的更多例子,你可以参考插件页面。

9。jQuery的互动日期范围选择器

这个强大的日期范围选择器是一个修改后的版本的jQuery UI的DatePicker组件。它有一个更快的日期选择预设日期/范围和平稳过渡的清单。最新版本使用了jQuery UI 1.7和jQuery UI CSS框架。随着jQuery UI的升级,用户现在可以切换日历主题容易。

[转载]ASP.NET MVC3的伪静态实现

mikel阅读(992)

[转载]ASP.NET MVC3的伪静态实现 – wxj200589 – 博客园.

最近使用ASP.NET MVC3开发B2C电子商务系统,为了SEO的优化工作,需要通过路由实现伪静态URL,后续再根据需要生成真正的静态页面,不直接走路由访问具体的页面。现在开始研究第一步,如何定义自己的路由规则,达到伪静态的功能需求。

基本实现原理如下图:

首先,关于命名空间。

路由的功能是为了让所有ASP.NET网站开发都可以使用,所以dll并没有在MVC中,而是在System.Web中的System.web.Routing。

现在我们为了我们实际的需求,实现MVC3中的自定义路由功能(继承RouteBase,重写RouteData和VirtualPathData)。

下面的例子实现以下目的:输入一个youdomin.com/product/123.html,执行TestController中Index.

第一步:实现TestRoute

1 RouteData 每次访问URL都会从此入口

通过httpContext.Request.AppRelativeCurrentExecutionFilePath 获取我们访问的url地址,根据地址进行分析:是不是符合我们的规则,符合我们规则我们就走特定的Controller和Action。代码如下:

public class TestRoute:RouteBase
{
private string[] urls;
public TestRoute(params string[]targetUrls) {
urls = targetUrls;
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
RouteData result = null;
string requestedURL =
httpContext.Request.AppRelativeCurrentExecutionFilePath+httpContext.Request.PathInfo;
requestedURL = requestedURL.Substring(2).Trim(/);

if (requestedURL.Contains(urls.ToArray().GetValue(0).ToString()))
{
result = new RouteData(thisnew MvcRouteHandler());
result.Values.Add(controllerTest);
result.Values.Add(actionIndex);
result.Values.Add(p, requestedURL);
}
return result;
}

public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return null;
}

}

上面例子中,我们根据判断Url中是否符合某个特定的值来特定执行特定Controller和特定Action,没有就返回null。

第二步,在Global.aspx中注册我们自己的路与规则:

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute({resource}.axd/{*pathInfo});

routes.Add(new TestRoute(product));
routes.MapRoute(
Default// Route name
{controller}/{action}/{id}// URL with parameters
new { controller = Home, action = Index, id = UrlParameter.Optional } // Parameter defaults
);

}

protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();

RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}

注意上述代码红色部分,Application_Start() 中注册一个路由规则,RegisterRoutes(RouteTable.Routes)然后在RegisterRoutes方法中加入如下代码:

routes.Add(new TestRoute(“product”));

注明:TestRoute 是上面我们自己定义的路由,实现RouteBase的类。

第三步:新建第一步中测试用的Controller

public class TestController:Controller
{
public ActionResult Index(string p)
{
ViewData[t] =p;
return View(“”);
}
}

第四步:新建一个视图

@{
Layout = null;
}

<!DOCTYPE html>

<html>
<head>
<title></title>
</head>
<body>
<div>
<!–此处会显示你输入的URL地址–>
@ViewData[t].ToString()
</div>
</body>
</html>

第五步,直接输入URl测试

比如:http://127.0.0.1/product/1.html

后续补充:

主要内容:如何让前台列表展示页显示以上5步中实现的伪静态URL?

经过实际验证,发现RouteBase中VirtualPathData的实现就能解决以上问题。.net Route其实已经实现了这个双向解析的问题,通过输入URL,从RouteData进入,根据自己的路由规则进行解析到相对应的Controller和 Action,然后在使用URL.Action的地方从VirtualPathData解析出符合路由规则的URL地址,具体代码如下:

public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
if (values["controller"].ToString().Contains("Test"))
{
return new VirtualPathData(this, "product/" + values["p"] + ".html");
}
else
return null;
}

可以替换第一步中TestRoute类中的GetVirtualPath方法,查看实际效果。

[转载]菜鸟的Android 学习笔记 之四大组件总结

mikel阅读(1000)

[转载]菜鸟的Android 学习笔记 之四大组件总结 – 行者,无疆 – 博客园.

Activity :

activity是Android提供的界面,所有和用户交互的事情都发生在这个类。

Activity的生命周期:

OnCreate() 创建的时候会调用这个方法 设置这个Activity设置布局控件,初始化数据等。

OnStart() 当控件被我们看到的时候,就调用OnStart()方法 在OnCreate()之后 或者OnStop()后调用。

OnResume() 当重新获得用户焦点的时候就调用这个方法。

onPause() 保护现场用的 可能被另外一个透明的Dialog窗口覆盖,失去焦点,但是他仍然和窗口管理器保持连接,系统可以继续保护Activity的内部状态。

onStop()  停止和OnStart()对应,注意OnStop()和OnPause()不一样的地方是他是完全被另外一个窗口覆灭。也就是失去焦点而且不可见。

onDestory()销毁

创建一个Activity的要点

1.一个Activity就是一个类,并且这个类要继承Activity

2.需要复写onCreate方法

3.要在Androidmanifest.xml进行配置(四大组件都要进行配置)

4.为Activity添加必要的控件

在一个Activity启动另外一个Activity

Intent intent=new Intent(CurrentActivity.this,OtherActivity.class) //OtherActivity也要在AndroidManifest.xml中注册。

startActivity(intent);

Intent还可以放入各种数据 传入下一个Activity。

可以用下面的方式接收传过来的数据:

还可以用Bundel的方法发送数据。Bundel就像一个承载数据的List<> 里面可以放各种数据。但是他的key只能是string类型的,放入Intent的方式也是intent.putExtra(bundle);

Service

Service在什么时候会用到呢?比如说音乐程序的时候,需要后台运行,但是又看不到界面,此时就用到Service。这也是Service和Activity最大的不同。

Service 分类:

本地Service和远程Service。

本地Service是同一个进程内Service彼此之间共同的内存区域。

远程Service是同一个系统内的不同进程之间访问(注意是同一个系统内,不是不同系统)。

Service生命周期:

onCreate()->onStart()->onDestory() 三个方法 继承的时候如果要用到记得重新写下。

创建一个Service要点:

1.  创建一个类,要继承Service

2.  要在androidManifest.xml中进行配置

3.  在Activity中配置调用它

Broadcast Receiver

在android中可以通过broadcast告诉其他程序发生了什么事情。比如电源,比如短信,比如信号。

Broadcast Receiver相比于其他组件比较特别的地方是他还提供了一种所谓的热注册,就是不用写在androidManifest.xml中,直接用

registerReceiver(Broadcastreceiver,IntentFilter)注册。然后用unregisterReceiver()消除注册。

一些常用的Action:

ACTION_CALL                                              activity                        启动一个电话.
ACTION_EDIT                                               activity                       显示用户编辑的数据.
ACTION_MAIN                                               activity                      作为Task中第一个

Activity启动
ACTION_SYNC                                             activity                         同步手机与数据服务器上的数据.
ACTION_BATTERY_LOW                           broadcast receiver       电池电量过低警告.
ACTION_HEADSET_PLUG                        broadcast receiver        插拔耳机警告
ACTION_SCREEN_ON                               broadcast receiver        屏幕变亮警告.
ACTION_TIMEZONE_CHANGED              broadcast receiver         改变时区警告.

Content Provider

这个组件比较复杂

ContentProvider可以支持多个应用的数据共享。可以用来操作音频,视频,图片,私人通讯录等等。但是要记得获取适当的读取权限。当然也可以用来公开自己的数据。(难道Content Provider是全局变量?整个手机的程序都能访问得到?)

创建Content Provider 要点:

1.  创建一个Content Provider首先要继承ContentProvider类。

2.       在androidManifest中配置

3.       在Activity类中调用insert() query()可以调用

实际上所有的contentprovider用户都不能直接访问contentprovider实例,只能通过ContentResolver的中 间代理。而要得到ContentResolver的实例可以用Activity的getContentResolver方法。如下

可以自己去调用ContentProvider相应的方法。

Uri使用方法:来自http://blog.sina.com.cn/s/blog_5688414b0100xagp.html

一、Content Provider基本概念

1、ContentProvider为存储和获取数据提供了统一的接口。ContentProvide对数据进行封装,不用关心数据存储的细节。使用表的形式来组织数据。

Android学习十九:ContentProvider初步

2、使用ContentProvider可以在不同的应用程序之间共享数据。

3、Android为常见的一些数据提供了默认的ContentProvider(包括音频、视频、图片和通讯录等)。

ContentProvider所提供的函数:

query(),insert(),update(),delete(),getType(),onCreate()等。

二、URI统一资源标识符)的使用方法

为系统的每一个资源给其一个名字,比方说通话记录。

1、每一个ContentProvider都拥有一个公共的URI,这个URI用于表示这个ContentProvider所提供的数据。

2、Android所提供的ContentProvider都存放在android.provider包中。 将其分为A,B,C,D 4个部分:

Android学习十九:ContentProvider初步

A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;”content://”

B:URI 的标识,它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的 类名。这个标识在 元素的 authorities属性中说明:一般是定义该ContentProvider的包.类的名称;”content://hx.android.text.myprovider”

C:路径,不知道是不是路径,通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就ok了;”content://hx.android.text.myprovider/tablename”

D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部; “content://hx.android.text.myprovider/tablename/#” #表示数据id

三、ContentProvider的实现过程

自己实现ContentProvider不常见,因为可能不需要和别的应用程序交换数据。使用内置的ContentProvider比较多。

1、定义一个CONTENT_URI常量,提供了访问ContentProvider的标识符。

public static final Uri CONTENT_URI = Uri.parse("content://com.example.codelab.transportationprovider");

其中:content是协议

Com.exmaple.codelab.transportationprovider是类名,包含完整的包名。

Uri.parse将一个字符串转换成Uri类型。

如果Provider包含子表,同样定义包含字表的CONTENT_URI。

content://com.example.codelab.transportationprovider/train
content://com.example.codelab.transportationprovider/air/domestic
content://com.example.codelab.transportationprovider/air/international

然后定义列,确保里面包含一个_id的列。

2、定义一个类,继承ContentProvider。

public class FirstContentProvider extends ContentProvider

先介绍一下ContentProvider用到的UriMatcher。UriMatcher的一个重要的函数是match(Uri uri)。这个函数可以匹配Uri,根据传入的不同Uri返回不同的自定义整形值,以表明Uri访问的不同资源的类型。

例如:

      public static final UriMatcher uriMatcher;
      static {
                     uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
                     uriMatcher.addURI(Book.AUTHORITY, "item", Book.ITEM);
                     uriMatcher.addURI(Book.AUTHORITY, "item/#", Book.ITEM_ID);
              }

这里UriMatcher类型的静态字段是用来匹配传入到ContentProvider中的Uri的类。其构造方法传入的匹配码是使用match()方法匹配根路径时返回的值,这个匹配码可以为一个大于零的数表示匹配根路径或传入-1,即常量UriMatcher.NO_MATCH表示不匹配根路径。 addURI()方法是用来增加其他URI匹配路径的,第一个参数传入标识ContentProvider的AUTHORITY字符串。第二个参数传入需要匹配的路径,这里的#号为通配符,代表匹配任意数字,另外还可以用*来匹配任意文本。第三个参数必须传入一个大于零的匹配码,用于match()方法对相匹配的URI返回相对应的匹配码。 例如:sMatcher.addURI(“com.test.provider.personprovider”, “person”, 1);如果match()方法匹配content://com.test.provider.personprovider/person路径,返回匹配码为1。

3、实现query,insert,update,delete,getType和onCreate方法。

4、在AndroidManifest.xml当中进行声明。

<!-- android:name是完成ContentProvider类的全称
             android:authorities是和FirstProvidermetaData中的常量AUTHORITY的值一样,否则会报错
         -->
        <provider android:name="com.bj.FirstContentProvider"
            android:authorities="com.bj.firstcontentprovider"
            />

四、具体代码

Activity19Activity.java

public class Activity19Activity extends Activity {

private Button queryButton = null;

private Button insertButton = null;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

queryButton = (Button) this.findViewById(R.id.query);

queryButton.setOnClickListener(newQueryListener());

insertButton = (Button) this.findViewById(R.id.insert);

insertButton.setOnClickListener(newInsertListener());

System.out.println(getContentResolver().getType(FirstProvidermetaData.UserTableMetaData.CONTENT_URI));

}

class InsertListener implementsOnClickListener {

@Override

public void onClick(View v) {

// TODOAuto-generated method stub

ContentValues values = new ContentValues();

values.put(FirstProvidermetaData.UserTableMetaData.USER_NAME,

“michal”);

Uri uri = getContentResolver()

.insert(

FirstProvidermetaData.UserTableMetaData.CONTENT_URI,

values);

System.out.println(“uri—>” + uri.toString());

}

}

class QueryListener implementsOnClickListener {

public void onClick(View v) {

Cursor c = getContentResolver().query(

FirstProvidermetaData.UserTableMetaData.CONTENT_URI, null,

null, null, null);

while (c.moveToNext()) {

System.out.println(c.getString(c.getColumnIndex(“username”)));

}

}

}

}

FirstContentProvider.java

public class FirstContentProvider extendsContentProvider {

// 当别的程序来访问这个ContentProvider,是通过Uri来访问的,UriMatcher检查是否符合标准

// uri起一个规则,返回数字

public static final UriMatcher uriMatcher;

// 下面定义两个规则

public static final int INCOMING_USER_COLLECTION = 1;

public static final int INCOMING_USER_SINGLE = 2;

private DatabaseHelper dh;

static {//下面的users前面不能加/

uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

uriMatcher.addURI(FirstProvidermetaData.AUTHORITY, “users”,

INCOMING_USER_COLLECTION);

uriMatcher.addURI(FirstProvidermetaData.AUTHORITY, “users/#”,

INCOMING_USER_SINGLE);

}

//有点类似于SQL里面表的别名,这个也是给列其别名,必须要用

//列的别名还是原来的名,没必要修改

public static HashMap<String,String> userProjectionMap;

static{

userProjectionMap = newHashMap<String,String>();

userProjectionMap.put(UserTableMetaData._ID, UserTableMetaData._ID );

userProjectionMap.put(UserTableMetaData.USER_NAME, UserTableMetaData.USER_NAME);

}

@Override

public int delete(Uri uri, String selection, String[] selectionArgs) {

// TODOAuto-generated method stub

System.out.println(“delete”);

return 0;

}

// 根据传入的URI,返回该URI所表示的数据类型

// 也就是说,我们通过URI要访问的数据,返回什么类型

@Override

public String getType(Uri uri) {

// TODOAuto-generated method stub

System.out.println(“getType”);

switch (uriMatcher.match(uri)) {

case INCOMING_USER_COLLECTION:

// UserTableMetaDataFirstProvidermetaData的内部类

return UserTableMetaData.CONTENT_TYPE;

case INCOMING_USER_SINGLE:

return UserTableMetaData.CONTENT_TYPE_ITEM;

default:

throw new IllegalArgumentException(“Unknown uri” + uri);

}

}

@Override

public Uri insert(Uri uri, ContentValues values) {

// TODOAuto-generated method stub

System.out.println(“insert”);

SQLiteDatabase db = dh.getWritableDatabase();

//返回表中自动增长的列的值,否则返回-1

long rowId = db.insert(UserTableMetaData.TABLE_NAME,null, values);

if(rowId>0){

//rowId追加到后面

//contentUris:用来处理Uri的工具类

Uri insertedUserUri = ContentUris.withAppendedId(UserTableMetaData.CONTENT_URI, rowId);

//通知监听器,数据已经改变

getContext().getContentResolver().notifyChange(insertedUserUri, null);

return insertedUserUri;

}

throw new SQLException(“Failed to insert row into “+uri);

}

//是一个回调方法,所以说在ContentProvider创建的时候执行

//也就是创建这个DatabaseHelper对象

@Override

public boolean onCreate() {

// TODOAuto-generated method stub

//getContext得到当前正在运行着的context

dh = new DatabaseHelper(getContext(),FirstProvidermetaData.DATABASE_NAME);

System.out.println(“on create”);

SQLiteDatabase db = dh.getReadableDatabase();

return true;

}

//projection:查询的列有哪些

//selection:where子句的内容,可以用?

//selectionArgs:占位符对应的参数

//sortOrder:排序

@Override

public Cursor query(Uri uri, String[] projection, String selection,

String[] selectionArgs, String sortOrder) {

// TODOAuto-generated method stub

System.out.println(“query”);

//创建一个查询的语句

SQLiteQueryBuilder qb = new SQLiteQueryBuilder();

switch(uriMatcher.match(uri)){

case INCOMING_USER_COLLECTION:

//设置查询哪张表

qb.setTables(UserTableMetaData.TABLE_NAME);

qb.setProjectionMap(userProjectionMap);

break;

case INCOMING_USER_SINGLE:

qb.setTables(UserTableMetaData.TABLE_NAME);

qb.setProjectionMap(userProjectionMap);

//添加where条件,getPathSegments:得到uripath部分content:XXX/user/1get(1)得到1

qb.appendWhere(UserTableMetaData._ID+”=”+uri.getPathSegments().get(1));

break;

}

String orderBy;

if(TextUtils.isEmpty(sortOrder)){

orderBy = UserTableMetaData.DEFAULT_SORT_ORDER;

}

else

{

orderBy = sortOrder;

}

SQLiteDatabase db = dh.getWritableDatabase();

//下面的query使用qb这个对象

Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);

//也是通知下

c.setNotificationUri(getContext().getContentResolver(), uri);

System.out.println(“query”);

return c;

}

@Override

public int update(Uri uri, ContentValues values, String selection,

String[] selectionArgs) {

// TODOAuto-generated method stub

return 0;

}

}

FirstProvidermetaData.java

public class FirstProvidermetaData {

public static final String AUTHORITY=”com.bj.firstcontentprovider”; //继承了contentprovider的类的全名

//数据库名称

public static final String DATABASE_NAME = “FirstProvider.db”;

//数据库的版本

public static final int DATABASE_VERSION = 1;

//表名

public static final String USERS_TABLE_NAME = “users”;

public static final class UserTableMetaData implements BaseColumns{

//表名

public static final String TABLE_NAME=”users”;

//访问该ContentProviderURI

public static final Uri CONTENT_URI=Uri.parse(“content://”+AUTHORITY+”/users”);

public static final String CONTENT_TYPE=”vnd.android.cursor.dir/vnd.firstprovider.user”;

public static final String CONTENT_TYPE_ITEM=”vnd.android.cursor.item/vnd.firstprovider.user”;

//列名,在users表中添加一个名为name的列

public static final String USER_NAME=”name”;

//默认排序方式

public static final String DEFAULT_SORT_ORDER=”_id desc”;

}

}

DatabaseHelper.java见Android学习十六:SQLite使用方法