[转载]使用Redis实现高并发分布式序列号生成服务 - 浮云-Mignet - 博客园

mikel阅读(1170)

来源: [转载]使用Redis实现高并发分布式序列号生成服务 – 浮云-Mignet – 博客园

序列号的构成

为建立良好的数据治理方案,作数据掌握、分析、统计、商业智能等用途,业务数据的编码制定通常都会遵循一定的规则,一般来讲,都会有自己的编码规则和自增序列构成。比如我们常见的身份证号、银行卡号、社保电脑号等等。

以某公司产品标识码(代表该产品的唯一编码)的构成为例:

规则定义:商品款号(8位)+颜色号(3位)+号型码(3位) (共14位)

其标识码为:62X19001 001 46A

业务含义为: 2009年男装秋冬季仿毛套西黑色170A版

简单来讲,业务编码是由规则和序列构成,规则是允许定义和编辑的,序列通常要求并发安全。整个序列号生成规则要求读写并发安全。

序列号生成方案

由于Redis的高性能,高并发和数据一致性的保证,以及断电数据不丢失,分布式扩展能力等优势。我们采用Redis存储并持久化序列和业务规则来配置和管理整个序列号的产生。

规则定义举例:前缀+时间(YYYYMMDD)+所使用的序列(指定长度),那么产生的序列号类似于SO20150520000124

具体规则可根据实际业务需求来设计。

实现要求:整个生成过程使用Jedis完成,保证原子事务。并通过压力测试。

序列号实现方案

1.         规则配置管理

在Redis的设计中,要想实现比如

select * from users where user.location=”shanghai”

这样的查询,是没办法通过value进行比较得出结果的。但是可以通过不同的数据结构类型来做到这一点。比如如下的数据定义

users:1 {name:Jack,age:28,location:shanghai}

users:2 {name:Frank,age:30,location:beijing}

users:location:shanghai [1]

其中users:1 users:2 分别定义了两个用户信息,通过Redis中的hash数据结构,而users:location:shanghai 记录了所有上海的用户id,通过集合数据结构实现。

这样通过两次简单的Redis命令调用就可以实现我们上面的查询。

Jedis jedis = jedisPool.getResource();

Set<String> shanghaiIDs = jedis.smembers(“users:location:shanghai”); //遍历该set

 //… //通过hgetall获取对应的user信息

jedis.hgetAll(“users:” + shanghaiIDs[0]);

通过诸如以上的设计,可以实现简单的条件查询。但是这样的问题也很多,首先需要多维护一个ID索引的集合,其次对于一些 复杂查询无能为力(当然也不能期望Redis实现像关系数据库那样的查询,Redis不是干这的)。针对本序列号生成方案,这种方式完全是够用的,可以直 接参考本节的代码示例。

如果想更进一步,Redis2.6集成了Lua(Redis是用ANSI C写的,可以想象支持Lua是一件很自然的事),可以通过eval命令,直接在RedisServer环境中执行Lua脚本,就是说可以让你用Lua脚 本,对Redis中存储的key value进行操作,这个意义就大了,甚至可以将系统所需的各种业务写成一个个lua脚本,提前加载进入Redis,然后对于请求的响应,只需要调用一个 个lua脚本就行。(当然这些操作也完全可以使用Jedis来完成,但显然lua效率更高)

比如,现在我们要实现一个‘所有年龄(age)大于28岁的用户(user)’这样一个查询,那么通过以下的Lua脚本就可以实现

public static final String SCRIPT = “local resultKeys={};”

 + “for k,v in ipairs(KEYS) do “

 + ”  local tmp = redis.call(‘hget’, v, ‘age’);”

+ ”  if tmp > ARGV[1] then “

+ ”   table.insert(resultKeys,v);”

 + ”  end;”

 + ” end;”

 + “return resultKeys;”;

执行脚本代码

Jedis jedis = jedisPool.getResource();

jedis.auth(auth);

List<String> keys = Arrays.asList(allUserKeys);

List<String> args = new ArrayList<>();

args.add(“28”);

List<String> resultKeys = (List<String>)jedis.evalsha(funcKey, keys, args);

return resultKeys;

注意,以上的代码中使用的是evalsha命令,该命令参数的不是直接用Lua脚本字符串,而是提前已经加载到 Redis中的函数的一个SHA索引,通过以下的代码将系统中所有需要执行的函数提前加载到Redis中,通常在自己的系统中维护一个函数哈希表 funcTable,后续需要实现什么功能,就从函数表中获取对应功能的SHA索引,通过evalsha调用就行。

String shaFuncKey = jedis.scriptLoad(SCRIPT);//加载脚本到Redis中,获取sha索引 funcTable.put(funcName_age, shaFuncKey);//添加到系统维护的函数表中

通过以上的方法,便可以使较为复杂的查询放到Redis中去执行,提高效率。

可见,想要将全部业务代码都使用lua脚本来实现的业务系统是可能的,lua脚本等同于关系型数据库中的存储过程或者函数。当然,全部使用lua的开发成本未必不大,毕竟不是关系型数据库,存储思维不同。

代码示例:

//配置生成规则(CRUD):

//假设销售单号生成规则:prefix+time+seq

//生成之后的结果类似于:SO20150520023014

//——模拟常规数据库操作——

//添加数据

shardedJedis.hset(“rules”, “somaster”,

 “name:销售单号,prefix:SO,time:YYYYMMDD,seq:seq_so,seq_len:6”);

shardedJedis.hset(“rules”, “pomaster”,

 “name:采购单号,prefix:PO,time:YYYYMMDD,seq:seq_po,seq_len:6”);

shardedJedis.hset(“rules”, “test”, “name:test,prefix:PO,time:YYYYMMDD,seq:seq_po,seq_len:6”);

//判断某个值是否存在

System.out.println(shardedJedis.hexists(“rules”, “test”));

// 删除指定的值

System.out.println(shardedJedis.hdel(“rules”, “test”));

// 获取指定的值

System.out.println(shardedJedis.hget(“rules”, “somaster”));

// 获取所有的keys

System.out.println(shardedJedis.hkeys(“rules”));

// 获取所有的values

System.out.println(shardedJedis.hvals(“rules”));

//更新 = 插入同名的key

System.out.println(“update before:”+shardedJedis.hvals(“rules”));

System.out.println(shardedJedis.hset(“rules”, “somaster”, “new test somaster”));

System.out.println(“update after:”+shardedJedis.hvals(“rules”));

我示例代码中使用的是hash而不是直接使用key-value来存储,是更优的方案。至此CRUD都能直接满足了,最后,你获取到所有values,需要自己处理分页。

也可以使用list和set组合的方式存储。这种方式是将list index和set key对应起来,根据序号进行分页是容易的,但在每次新增和删除时,都需要修改序号和key的对应关系。

两者相比,使用hash的成本显然更低,也不易出错。

2.         序列号的使用

Redis中对序列的生成早已考虑周到,使用单线程操作序列的方式以保证并发安全,同时,使用也极其简单。更多操作详见官网API

代码示例

//sequence

System.out.println(“seq:”+shardedJedis.incr(“seq”));

System.out.println(“seq:”+shardedJedis.incr(“seq”));

System.out.println(“another_seq:”+shardedJedis.incr(“another_seq”));

最后,生成序列服务只需要通过对应的规则名,获取规则表达式,解析之后结合序列号,最终生成即可。

并发测试

这里我们使用CyclicBarrier做并发测试,CyclicBarrier会开启指定数量的线程,等待这些线程就绪之后,同时执行测试内容,以达到真实并发的测试目的。

Loadrunner等压力测试工具也能完成测试任务。

<div class="container">
<div class="line number1 index0 alt2"><code class="java keyword">public</code> <code class="java keyword">class</code> <code class="java plain">CyclicBarrierTest {</code></div>
<div class="line number2 index1 alt1"><code class="java spaces">    </code><code class="java comments">//初始化</code></div>
<div class="line number3 index2 alt2"><code class="java spaces">    </code><code class="java plain">JedisPoolTest test = </code><code class="java keyword">new</code> <code class="java plain">JedisPoolTest();</code></div>
<div class="line number4 index3 alt1"><code class="java spaces">    </code><code class="java keyword">public</code> <code class="java keyword">static</code> <code class="java keyword">void</code> <code class="java plain">main(String[] args) {</code></div>
<div class="line number5 index4 alt2"><code class="java spaces">         </code><code class="java keyword">int</code> <code class="java plain">count = </code><code class="java value">1000</code><code class="java plain">;</code></div>
<div class="line number6 index5 alt1"><code class="java spaces">         </code><code class="java plain">CyclicBarrier cyclicBarrier = </code><code class="java keyword">new</code> <code class="java plain">CyclicBarrier(count);</code></div>
<div class="line number7 index6 alt2"><code class="java spaces">         </code><code class="java plain">ExecutorService executorService = Executors.newFixedThreadPool(count);</code></div>
<div class="line number8 index7 alt1"><code class="java spaces">         </code><code class="java keyword">for</code> <code class="java plain">(</code><code class="java keyword">int</code> <code class="java plain">i = </code><code class="java value">0</code><code class="java plain">; i &lt; count; i++)</code></div>
<div class="line number9 index8 alt2"><code class="java spaces">              </code><code class="java plain">executorService.execute(</code><code class="java keyword">new</code> <code class="java plain">CyclicBarrierTest().</code><code class="java keyword">new</code> <code class="java plain">Task(cyclicBarrier));</code></div>
<div class="line number10 index9 alt1"></div>
<div class="line number11 index10 alt2"><code class="java spaces">         </code><code class="java plain">executorService.shutdown();</code></div>
<div class="line number12 index11 alt1"><code class="java spaces">         </code><code class="java keyword">while</code> <code class="java plain">(!executorService.isTerminated()) {</code></div>
<div class="line number13 index12 alt2"><code class="java spaces">              </code><code class="java keyword">try</code> <code class="java plain">{</code></div>
<div class="line number14 index13 alt1"><code class="java spaces">                   </code><code class="java plain">Thread.sleep(</code><code class="java value">10</code><code class="java plain">);</code></div>
<div class="line number15 index14 alt2"><code class="java spaces">              </code><code class="java plain">} </code><code class="java keyword">catch</code> <code class="java plain">(InterruptedException e) {</code></div>
<div class="line number16 index15 alt1"><code class="java spaces">                   </code><code class="java plain">e.printStackTrace();</code></div>
<div class="line number17 index16 alt2"><code class="java spaces">              </code><code class="java plain">}</code></div>
<div class="line number18 index17 alt1"><code class="java spaces">         </code><code class="java plain">}</code></div>
<div class="line number19 index18 alt2"><code class="java spaces">    </code><code class="java plain">}</code></div>
<div class="line number20 index19 alt1"></div>
<div class="line number21 index20 alt2"><code class="java spaces">    </code><code class="java keyword">public</code> <code class="java keyword">class</code> <code class="java plain">Task </code><code class="java keyword">implements</code> <code class="java plain">Runnable {</code></div>
<div class="line number22 index21 alt1"><code class="java spaces">         </code><code class="java keyword">private</code> <code class="java plain">CyclicBarrier cyclicBarrier;</code></div>
<div class="line number23 index22 alt2"></div>
<div class="line number24 index23 alt1"><code class="java spaces">         </code><code class="java keyword">public</code> <code class="java plain">Task(CyclicBarrier cyclicBarrier) {</code></div>
<div class="line number25 index24 alt2"><code class="java spaces">              </code><code class="java keyword">this</code><code class="java plain">.cyclicBarrier = cyclicBarrier;</code></div>
<div class="line number26 index25 alt1"><code class="java spaces">         </code><code class="java plain">}</code></div>
<div class="line number27 index26 alt2"></div>
<div class="line number28 index27 alt1"><code class="java spaces">         </code><code class="java color1">@Override</code></div>
<div class="line number29 index28 alt2"><code class="java spaces">         </code><code class="java keyword">public</code> <code class="java keyword">void</code> <code class="java plain">run() {</code></div>
<div class="line number30 index29 alt1"><code class="java spaces">              </code><code class="java keyword">try</code> <code class="java plain">{</code></div>
<div class="line number31 index30 alt2"><code class="java spaces">                   </code><code class="java comments">// 等待所有任务准备就绪</code></div>
<div class="line number32 index31 alt1"><code class="java spaces">                   </code><code class="java plain">cyclicBarrier.await();</code></div>
<div class="line number33 index32 alt2"><code class="java spaces">                   </code><code class="java comments">// 测试内容</code></div>
<div class="line number34 index33 alt1"><code class="java spaces">                </code><code class="java comments">// 待测试的url</code></div>
<div class="line number35 index34 alt2"><code class="java spaces">                    </code><code class="java plain">String host = </code><code class="java string">"<a href="http://172.25.2.14/seqno?">http://172.25.2.14/seqno?</a>"</code><code class="java plain">;</code></div>
<div class="line number36 index35 alt1"><code class="java spaces">                    </code><code class="java plain">String para = </code><code class="java string">"sysTemNo=ERP&amp;seqName=WH-ZONE-ID&amp;iVar=00"</code><code class="java plain">;</code></div>
<div class="line number37 index36 alt2"><code class="java spaces">                    </code><code class="java plain">System.out.println(host + para);</code></div>
<div class="line number38 index37 alt1"><code class="java spaces">                    </code><code class="java plain">URL url = </code><code class="java keyword">new</code> <code class="java plain">URL(host);</code></div>
<div class="line number39 index38 alt2"><code class="java spaces">                    </code><code class="java plain">HttpURLConnection connection = (HttpURLConnection) url.openConnection();</code></div>
<div class="line number40 index39 alt1"><code class="java spaces">                    </code><code class="java comments">// connection.setRequestMethod("POST");</code></div>
<div class="line number41 index40 alt2"><code class="java spaces">                    </code><code class="java comments">// connection.setRequestProperty("Proxy-Connection", "Keep-Alive");</code></div>
<div class="line number42 index41 alt1"><code class="java spaces">                    </code><code class="java plain">connection.setDoOutput(</code><code class="java keyword">true</code><code class="java plain">);</code></div>
<div class="line number43 index42 alt2"><code class="java spaces">                    </code><code class="java plain">connection.setDoInput(</code><code class="java keyword">true</code><code class="java plain">);</code></div>
<div class="line number44 index43 alt1"><code class="java spaces">                    </code><code class="java plain">PrintWriter out = </code><code class="java keyword">new</code> <code class="java plain">PrintWriter(connection.getOutputStream());</code></div>
<div class="line number45 index44 alt2"><code class="java spaces">                    </code><code class="java plain">out.print(para);</code></div>
<div class="line number46 index45 alt1"><code class="java spaces">                    </code><code class="java plain">out.flush();</code></div>
<div class="line number47 index46 alt2"><code class="java spaces">                    </code><code class="java plain">out.close();</code></div>
<div class="line number48 index47 alt1"><code class="java spaces">                    </code><code class="java plain">BufferedReader in = </code><code class="java keyword">new</code> <code class="java plain">BufferedReader(</code><code class="java keyword">new</code> <code class="java plain">InputStreamReader(connection.getInputStream()));</code></div>
<div class="line number49 index48 alt2"><code class="java spaces">                    </code><code class="java plain">String line = </code><code class="java string">""</code><code class="java plain">;</code></div>
<div class="line number50 index49 alt1"><code class="java spaces">                    </code><code class="java plain">String result = </code><code class="java string">""</code><code class="java plain">;</code></div>
<div class="line number51 index50 alt2"><code class="java spaces">                    </code><code class="java keyword">while</code> <code class="java plain">((line = in.readLine()) != </code><code class="java keyword">null</code><code class="java plain">) {</code></div>
<div class="line number52 index51 alt1"><code class="java spaces">                        </code><code class="java plain">result += line;</code></div>
<div class="line number53 index52 alt2"><code class="java spaces">                    </code><code class="java plain">}</code></div>
<div class="line number54 index53 alt1"><code class="java spaces">                    </code><code class="java plain">System.out.println(result);</code></div>
<div class="line number55 index54 alt2"><code class="java comments">//                   System.out.println(test.getJedis().incr("seq"));</code></div>
<div class="line number56 index55 alt1"><code class="java comments">//                System.out.println(test.getShardedJedis().incr("seq"));</code></div>
<div class="line number57 index56 alt2"><code class="java spaces">              </code><code class="java plain">} </code><code class="java keyword">catch</code> <code class="java plain">(Exception e) {</code></div>
<div class="line number58 index57 alt1"><code class="java spaces">                   </code><code class="java plain">e.printStackTrace();</code></div>
<div class="line number59 index58 alt2"><code class="java spaces">              </code><code class="java plain">}</code></div>
<div class="line number60 index59 alt1"><code class="java spaces">         </code><code class="java plain">}</code></div>
<div class="line number61 index60 alt2"><code class="java spaces">    </code><code class="java plain">}</code></div>
<div class="line number62 index61 alt1"><code class="java plain">}</code></div>
<div class="line number62 index61 alt1">

测试结果:

单台Redis未经任何设置,500并发100% pass,到1000并发时只有67%pass率,此时存在连接超时和被拒的情形。但不存在任何重复号码或丢失号码。500并发数其实已经完全满足我当前 系统的要求。考虑到Redis本身可以集群扩展,完全能够应对将来更高的并发需求。

[转载]iOS开发笔记3:XML/JSON数据解析 - colinhou - 博客园

mikel阅读(822)

来源: [转载]iOS开发笔记3:XML/JSON数据解析 – colinhou – 博客园

这篇主要总结在iOS开发中XML/JSON数据解析过程用到的方法。XML数据解析主要使用SAX方式的NSXMLParser以及DOM方式的GDataXML,JSON数据解析主要使用NSJSONSerialization。

    1.XML解析之NSXMLParser

NSXMLParser是iOS原生的XML解析方式,采用SAX方式进行解析,特点是解析的时候从根元素开始,按顺序依次对每一个元素进行解析。

以下图所示XML文档为例进行解析

(1)首先是实例化一个NSXMLParser,设置代理监听解析过程,开始解析

(2)通过NSXMLParser代理方法对XML文档进行解析

(3)解析效果

    2.XML解析之GDataXML

GDataXML是谷歌制作的一个解析XML数据的类库,基于libxml2,特点是以DOM方式解析,会把XML文档数据一次性加载到内存中,再对数据进行解析。

下载地址:https://github.com/neonichu/GDataXML

还是使用上一节用到的XML文档为例,使用前需要进行配置

(1)设置头文件路径

 

(2)设置链接参数

(3)设置编译参数(GDataXML是非arc的)

加载整个XML文档,对元素进行遍历解析即可  

    3.JSON解析之NSJSONSerialization

JSON数据格式有如下要求,故得到数据后进行对应解析即可,在开发中常用的是将JSON解析为Foundation对象

拿到JSON数据直接使用NSJSONSerialization解析即可

对于JSON字符串最外层既不是NSArray也不是NSDictionary时,option选择NSJSONReadingAllowFragments即可

难得休息

mikel阅读(884)

天天瞎忙,都不知道忙啥!

偶然人家问起自己有啥爱好?都回答不上来了!真的是没时间整啥爱好!除了程序就是程序!除了项目就是项目!

好在选了个自己喜欢的职业,不至于每天干自己不喜欢的事儿了。

有些时候,真的应该让自己慢下来,离开程序离开代码,放下虚拟世界的一切,回归现实过真实的生活。

要想规范化,要学会说不

mikel阅读(1013)

一个初创公司没有规范化的流程、产品报价、产品功能说明、公司管理制度等等,就和一盘散沙没有什么区别,只有公司老板一个人苦逼的拼命跑业务做技术,那不是创业,那是找死。

要想规范化,要学会说不!

很多时候初创公司头疼的是人,没有效益的时候没有激情和斗志,都是很吃等死,和行尸走肉没什么区别;项目效益不错,激情澎湃各个只要给钱就卖,根本不顾及成本和日后的开支;老板看到的是每日的进账和开销,只要进账多就好,这是典型的No Zuo No Die!进账多未必是好事儿,要看这些进账是不是透支了未来的人工成本。

没业务愁,有业务更愁。项目接了不少,各个都说能让公司的踏上一个新的台阶儿,可哪个是靠谱的,都没进行过成本核算,就匆匆上面,人力物力都投入进去了,才发现原来是个南柯一梦,水太深,人太少,估计不足,导致半途而废的常常有。

人来了不出活儿,愁!人来人往本来就是公司的正常情况,但是太频繁或者太慢都不是好事儿,人员青黄不接,有能力的留不住,没能力的赖着不走,然后招人又后继乏力,招不到合适的,公司没人就好比赛车手没有维修工和教练,什么时候能开出个名次,只能听天由命,指着老板一个人累得粗骨扬灰了,都回天无术。

总而言之,麻雀虽小五脏俱全,不要以为哥几个创业开公司全靠兄弟义气,啥管理不管理,规范不规范的,有活大家做,挣钱大家分。学不会说不,就别提规范化,挨不过哥们义气,“不”字说不出口,于人于己都不利。买卖是买卖,交情是交情。

[转载]基于SpringMVC与jquery的ajax提交表单的若干情况详解 - lx_hust - 博客园

mikel阅读(865)

来源: [转载]基于SpringMVC与jquery的ajax提交表单的若干情况详解 – lx_hust – 博客园

在日常的业务中,我们往往使用的是ajax提交页面数据,而不用form的action来提交整个表单。现在我来分享一下我在日常工作中遇到的一些问题。

一、$.post、$.get、$.ajax三者的区别:

顾名思义,$.post和$.get分别是采用post方式和get方式向服务器发送请求。两者的不同是,get请求的参数是在url直接以 url?name1=value1&name2=value2的形式拼接而成,而post请求的参数会以请求正文的形式传送到服务器,这个学习过 javaweb知识的应该都知道,在此也不赘述。

在这里主要想讲的是关于同异步发送请求的。$.post和$.get默认是采用异步的形式向服务器发送请求,但我们的需求中很多情况需要得到服务器的返回值来判断下一步的操作,这个时候就需要用到$.ajax了。

<div class="container">
<div class="line number1 index0 alt2"><code class="javascript keyword">var</code> <code class="javascript plain">flag=</code><code class="javascript keyword">false</code><code class="javascript plain">;</code></div>
<div class="line number2 index1 alt1"><code class="javascript plain">$.ajax({</code></div>
<div class="line number3 index2 alt2"><code class="javascript spaces">             </code><code class="javascript plain">type: </code><code class="javascript string">"get"</code><code class="javascript plain">,</code></div>
<div class="line number4 index3 alt1"><code class="javascript spaces">             </code><code class="javascript plain">url: </code><code class="javascript string">"xxxxxx"</code><code class="javascript plain">,</code></div>
<div class="line number5 index4 alt2"><code class="javascript spaces">             </code><code class="javascript plain">async:</code><code class="javascript keyword">false</code><code class="javascript plain">,</code></div>
<div class="line number6 index5 alt1"><code class="javascript spaces">             </code><code class="javascript plain">data: {username:name, password:pwd},</code></div>
<div class="line number7 index6 alt2"><code class="javascript spaces">             </code><code class="javascript plain">dataType: </code><code class="javascript string">"json"</code><code class="javascript plain">,</code></div>
<div class="line number8 index7 alt1"><code class="javascript spaces">             </code><code class="javascript plain">success: </code><code class="javascript keyword">function</code><code class="javascript plain">(data){</code></div>
<div class="line number9 index8 alt2"><code class="javascript spaces">                                   </code><code class="javascript keyword">if</code><code class="javascript plain">(data&gt;0){</code></div>
<div class="line number10 index9 alt1"><code class="javascript spaces">                                        </code><code class="javascript plain">flag=</code><code class="javascript keyword">true</code><code class="javascript plain">;</code></div>
<div class="line number11 index10 alt2"><code class="javascript spaces">                                   </code><code class="javascript plain">}</code></div>
<div class="line number12 index11 alt1"><code class="javascript spaces">                      </code><code class="javascript plain">}</code></div>
<div class="line number13 index12 alt2"><code class="javascript spaces">         </code><code class="javascript plain">});</code></div>
<div class="line number13 index12 alt2">

  如以上代码,type来设置请求方式,async则设置的是同步或者异步,默认为true异步的,此时设置为false。如果用通常的$.post和$.get的话,flag的值是不会随着返回值data的值发生改变的。

二、文件上传

ajax上传文件一直都是一个比较头疼的问题,在这里我用的是JQuery的一个扩展框架,JQuery.form.js,此包提供了一 个$ajaxSubmit方法,很好的解决了springmvc下文件上传的问题,当然,form表单的method为post,type为 multipart/form-data,示例代码如下:


$(#id).ajaxSubmit({
type: 'post',
url: 'xxxxxx',
data: {
uername: name,
content: content
},
success: function(data) {
//回调函数
}
});

此时,表单数据和文件数据会保存在request中传入服务器。后台获取代码如下:

<pre>MutlipartHttpServletRequest  multipart =(MutlipartHttpServletRequest )request;
//把request转为上传文件专用的request
Map&lt;String,String[]&gt; dataMap = request.getParameterMap();
//获取普通表单数的&lt;name,value&gt;键值对
Map&lt;String,Multipart&gt; fileMap = request.getFileMap();
//获取上传文件的键值对,当未上传文件时,这个键值对会以普通数据存在于dataMap中而不是fieMap中

暂时这么多把,想起来了再加。

开发要做的就是绑架客户

mikel阅读(954)

最近的项目大费周折的开发出来,结果到了交付日期,到客户那发现和客户的需求出入不小,还得调整,主要还是细节,客户看到的是一个二维的表格,根本不知道怎么把表格做成系统,他们习惯了excel记录数据和操作,突然一下从二维四维变成关联性很强的数据表格,一下子提不出合理的方案,只能引导客户挤牙膏般的才能出来真是需求。

很多开发项目都是匆匆调研后,就开始做了,结果往往浪费了最宝贵的时间在修改上。所以说开发首先要做的就是要绑架客户,让客户随时随地在你的身边,然后开发的过程也是个不断迭代的过程,包括界面的设计和功能的开发,都需要先出测试版让客户确认,再进行,否则你就等着交付延期面对无止境的修改吧。

这样客户对你的开发过程也有了解,不至于不知道产生误解,认为这么简单的功能,这么长时间还没做出来,随着你的开发过程的客户全程参与,这样再次开发新的功能,客户也就习惯了你的开发节奏和工作方式,他会自然而然很配合的和你探讨功能需求,这样不至于你不了解业务,客户不了解你的开发流程,导致双方驴唇不对马嘴的抱怨了。

当 IDENTITY_INSERT 设置为 OFF 时,不能为表中的标识列插入显式值

mikel阅读(846)

  • {“当 IDENTITY_INSERT 设置为 OFF 时,不能向表 ‘OrderList’ 中的标识列插入显式值”}

    对于这个异常可以从两个角度来处理:A:数据库执行语句  B:直接修改NHibernate中持久化类映射配置文件id节点

    A数据库执行语句:

    问题描述:当在数据库表主键设计为 (Orderid  int identity primary key),相对这个主键IDENTITY_INSERT默认设置为OFF,就是不能够显示插入主键id的值,例子如下:

    insert into OrderList(id,OrderName) values(4520,’电子传票订单’)

    执行上面语句会提示一个错误:

    服务器: 消息 544,级别 16,状态 1,行 1
    当 IDENTITY_INSERT 设置为 OFF 时,不能向表 ‘OrderList’ 中的标识列插入显式值。

    其中关于主键一条记录,当我们想把这条记录的id设置成我们自定义的4520时出现上面的错误,如果我们添加一些设置,修改方法如下:

    –允许将显式值插入表的标识列中 ON-允许  OFF-不允许
    set identity_insert OrderList ON–打开

    insert into OrderList(id,ordername,createdate)
    values(4520,’set’,getdate())

    set identity_insert OrderList OFF–关闭

    在执行这个插入语句时多了一个设置,该设置的语法是

    –设置语法:

    SET IDENTITY_INSERT database.[ owner.] table ON OFF 
    允许将显式值插入表的标识列中

    参数说明:
    database:针对数据库
    table:针对某张表

    ON:允许插入显式值插入 标识列
    OFF:不允许

    注意:

    当 <wbr>IDENTITY_INSERT <wbr>设置为 <wbr>OFF <wbr>时,不能为表中的标识列插入显式值
    –问题注意

    (1)任何时候,会话中只有一个表的 IDENTITY_INSERT 属性可以设置为 ON。如果某个表已将此属性设置为 ON,并且为另一个表发出了 SET IDENTITY_INSERT ON 语句,则 Microsoft® SQL Server™ 返回一个错误信息,指出 SET IDENTITY_INSERT 已设置为 ON 并报告此属性已设置为 ON 的表

    (2)如果插入值大于表的当前标识值,则 SQL Server 自动将新插入值作为当前标识值使用

    (3)SET IDENTITY_INSERT 的设置是在执行或运行时设置,而不是在分析时设置

    上面执行语句中:把要执行的语句前后加上该设置,当然上面针对事一条记录插入操作,在这条记录插入操作后,如果再次插入数据时,没有启用该设置,Orderid主键列会根据上面自定义表示4520,自动增长到4521.这个操作很灵活关键看个人怎么利用.

    B:修改配置文件

    我们通过直接修改配置文件来处理这个问题,

    出错时文件配置:

    –hibernate持久化类配置 注意id中Generator子节点设置Class属性为:assigned自动增长
    <hibernate-mapping xmlns=”urn:nhibernate-mapping-2.2″>
        <class name=”TestHibernateExpre.Entities.OrderListModel,TestHibernateExpre” table=”OrderList”>
            <id name=”Orderid” column=”id” type=”int”>
                <!–id中参数的设置问:native/assigned/foreign/increment–>
                <generator class=”assigned”></generator>
            </id>
        </class>

    修改后文件配置:

    –Hibernate中关于持久化类的配置  注意id下Generator子节点 class属性配置为native
    <hibernate-mapping xmlns=”urn:nhibernate-mapping-2.2″>
        <class name=”TestHibernateExpre.Entities.OrderListModel,TestHibernateExpre” table=”OrderList”>
            <id name=”Orderid” column=”id” type=”int”>
                <!–id中参数的设置问:native/assigned/foreign/increment–>
                <generator class=”native”></generator>
            </id>
          </class>

时间都去哪儿了 转眼间就老了?

mikel阅读(1939)

刚刚在闺女的幼儿园给她过完生日,有点儿小激动,一帮孩子突然唱起了《时间都去哪儿了》,一下子被戳中了,感动了。

转眼间闺女都这么大了,自己能不老吗?!时间过得真快!自己还是没好好珍惜,不过还有机会,有梦想就大胆的去追吧!万一实现了呢!

时间催人老,时间不等人,时不我待的互联网+大潮同样是一蹉而就的机会,抓住了也就抓住了,错失了就永远的没有机会了!

SNF快速开发平台3.0之BS页面展示和九大优点-部分页面显示效果-Asp.net+MVC4.0+WebAPI+EasyUI+Knockout - 王春天 - 博客园

mikel阅读(1689)

来源: SNF快速开发平台3.0之BS页面展示和九大优点-部分页面显示效果-Asp.net+MVC4.0+WebAPI+EasyUI+Knockout – 王春天 – 博客园

一)经过多年的实践不断优化、精心维护、运行稳定、功能完善

能经得起不同实施策略下客户的折腾,能满足各种情况下客户的复杂需求。

二)编码实现简单易懂、符合设计模式等理念

上手快,见效快、方便维护,能控制软件项目后期维护风险。

三)有严谨的分层理念、完全符合主流的SOA理念架构

程序可以采用不同的实施策略、架构需求,方便维护、方便扩展。

四)符合RBAC的权限理念、同时也能满足国内小型软件项目的灵活性需求

不仅符合国际通用标准,又能满足国内的小型软件项目的灵活设置需求。

五)不仅支持多数据库也支持多种开发语言、多语言界面

支持B/S、C/S系统,可以支持Java语言开发项目的接口功能WebService,SQLServer、Oracle数据库等。

六)提供全部的设计文档、源码实现、配套说明文档

相关设计文档、配套资料可以在不同软件项目中反复使用,提高工作效率。

七)完全支持最终用户的个性化2次开发实际需求

开放接口、开放源码、开放数据库结构设计。

八)技术优点:

基于 ASP.NET MVC4.0 + WebAPI + EasyUI + Knockout 的架构设计开发

采用MVC的框架模式,具有耦合性低、重用性高、生命周期成本低、可维护性高、有利软件工程化管理等优点

采用WebAPI,客户端完全摆脱了代理和管道来直接进行交互

采用EasyUI前台UI界面插件,可轻松的打造出功能丰富并且美观的UI界面

采用Knockout,提供了一个数据模型与用户UI界面进行关联的高层次方式(采用行为驱动开发)

九)封装了大部分比较实用的控件和组件:

如自动完成控件、拼音模糊输入控件、日期控件、导出组件、下拉组件(单多选)、弹出窗口组件(单多选)、用户选择组件(单多选)、组织机构选择、角色选择等

下面就展示一下已经完成的页面效果:

登录页面:

主页:

菜单管理页面:

组织机构:

权限:

日志页面:

异常页面:

控件展示:

作者:王春天 2014-12-02

作者Blog:http://www.cnblogs.com/spring_wang

原文:http://www.cnblogs.com/spring_wang/p/4138492.html

如果觉得还不错,欢迎转载。

 

本系列文章列表如下:

基于MVC4.0+WebAPI+EasyUI+Knockout的Web开发框架的系列文章:

SNF快速开发平台3.0之–完美的代码生成器SNF.CodeGenerator-快速开发者的利器

基于MVC4+EasyUI的Web开发框架–Spring.Net.FrameworkV3.0总体介绍

SNF快速开发平台3.0之MVC通用控件库展示-Asp.net+MVC4.0+WebAPI+EasyUI+Knockout

SNF快速开发平台3.0之BS页面展示和九大优点-部分页面显示效果-Asp.net+MVC4.0+WebAPI+EasyUI +Knockout

SNF快速开发平台3.0之-界面个性化配置+10种皮肤+7种菜单-Asp.net+MVC4.0+WebAPI+EasyUI+Knockout

SNF快速开发平台3.0之-CS页面-Asp.net+Spring.Net.Framework

SNF快速开发平台3.0之–系统里广播的作用–迅速及时、简明扼要的把信息发送给接收者

SNF快速开发平台3.0之–asp.net mvc4 强大的导出和不需要上传文件的批量导入EXCEL

SNF快速开发平台3.0之–文件批量上传-统一附件管理器-在线预览文件(有互联网和没有两种)

SNF快速开发平台3.0之– MVC 打印解决方案

作者: 王春天
出处: http://www.cnblogs.com/spring_wang/
Email: spring_best@yeah.net
QQ交流:903639067 
QQ群:322581894
关于作者:高级工程师。专注于微软平台项目架构、管理和企业解决方案,多年项目开发与管理经验,精通DotNet,DB(SqlServer、Oracle等)技术。熟悉Java、VB及PB开发语言。在面向对象、面向服务以及数据库领域有一定的造诣。现从事项目实施、开发、架构等工作。并从事用友软件产品U8、U9、PLM 客开工作。
如有问题或建议,请多多赐教!
本文版权归作者和CNBLOGS博客共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通过邮箱或QQ 联系我,非常感谢。

 

Asp.Net MVC 自定义的MVC框架(非EF操作数据库) - 二进制小男人 - 博客园

mikel阅读(921)

来源: Asp.Net MVC 自定义的MVC框架(非EF操作数据库) – 二进制小男人 – 博客园

  一些废话:在 北京辞职回家不知不觉中已经半年多了,这半年中有过很多的彷徨,困惑,还有些小小难受。半年时间算是我人生以来遇到过的最困苦的时候。理想的工作跟我擦肩 而过,驾照也没有考过,年后这一改革…,毕竟遇到多大的阻力就有多大的成长,感觉这半年自己也成长了不少,不是刚出校门桀骜不驯的牛犊,也不是在北京 每个月拿4000多块,然后月光的毕业生。自始至终,我对于技术的追求也并没有松懈过,最近一段时间也根据自己小小的想法,与以前公司的一个框架原理,通 过ASP.NET MVC为基础半实现(为什么是半实现?因为还没写完!汗!。为什么没用“开发”一词,感觉自己技术还没到。)了一个小小的框架,希望我这以小小的瓦片能引 出您的珍珠碗。

我所希望的:这框架其实想法有想法到现在基础的实现,已经有三个月了,由于断断续续的coding,到现在才写成博客。写成博客的目的,不是在此炫耀,也不是在此哗众取宠,而是真心的希望各位大牛,给小菜批评佐证,可以说是欢迎来拍砖,但是把您宝贵的意见留下,小菜在此跪谢!以下言归正传。

    一.框架的目的与原理:

目的:根据ASP.NET MVC框架原理为基础,实现基于配置的MVC应用框架,最终以框架为基础,快速高效的应用与BS的开发。一个新的业务不需要构建C#的代码,不写

Controller与Action,只需要配置View与Mod.Config.而View的显示只可以根据已定义的分布视图,或是完全自定的View界面实现。实现高效的开发。

原理:众所周知ASP.NET MVC 框架根据用户的URL找到Controller并通过Action调用加载数据,并返回View。该框架的原理,是通过一个带参数的固定的Controller

动态解析,传入的URL,并根据URL参数,动态的创建出数据库操作实例,根据Mod.config文件,动态的加载每个Action的数据实例,返回以数据实例显示View,而View中的数

据显示啊,操作等通过分部页功能实现。

二.框架的架构

具体的操作请求实例:

下面通过一个请求,具体的解析一下怎样定位一个请求操作。

程序的主界面是自定义编译过的Ext只保留布局功能(Ext-all.js,Ext-base.js,+css总共大小280K左右,比原版的800K压缩了不少),以下是图:

以角色管理为例子:传入的URL 为解析后为

可以看到URL中并没有RBAC 的 Controller 也没有 Roles的 Action,所有Controller与Action都是通过BaseController与Load Action来加载,根据url参数创建出操作数据库实体类,并通过Url参数找到对应的View下面的文件目录,将每个Action与View使用的数 据存入Mod.config文件中,每次访问一个Action时都回去Mod.config取出数据的实例,Mod.config是自定义的 ConfigSection,就以“角色管理为例子”它配置了一个GridView分页需要使用的数据,Code:

Mod.Config

根据Mod.config的配置信息,生成数据模型,利用反射与缓存,加载数据信息,Code:

复制代码
   ViewElement viewModels = GetCachedViewElement(filePath, actionName);
            if (viewModels != null)
            {
                foreach (ModelElement model in viewModels.Models)
                {
                    object virtualModel = new object();
                    virtualModel = Assembly.Load(modelAssembly).CreateInstance(modelAssembly + "." + model.Type);
                    foreach (AttributeElement attribute in model.Attributes)
                    {
                        AttributeReflecter.SetPropertyValue(virtualModel, attribute.Name, attribute.Value);
                    }
                    Database a = DatabaseFactory.CreateDatabase("Sys");
                    (virtualModel as IExecute).Execute(a);
                    ViewData[model.Name] = virtualModel;
                }
            }
复制代码

返回视图:

所使用的分部页:

复制代码
@using Binary.MVC.Library.Extends
@model Binary.MVC.Library.Model.GridModel
           
<table id="GridView">
    <thead>
        <tr>
            @foreach (Binary.MVC.Library.Model.HeaderModel header in Model.HeaderMC)
            {
                <th width="@header.W">@header.N@if(header.I){<img align="absmiddle" src="@Url.Content("http://www.cnblogs.com/Content/Images/rarrow.gif")" title="单击此处以排序" />}</th>
            }
        </tr>
    </thead>
    <tbody>
        @foreach (Binary.MVC.Library.Model.GridRow row in Model.Rows)
        {
            <tr>
                @foreach (Binary.MVC.Library.Model.FieldModel field in Model.FieldMC)
                {
                    if (field.I)
                    {
                    <td>@row[field.N]</td>
                    }
                    else
                    {
                     <td>@Html.RenderGridAction(row.PKey,field.N)</td>   
                    }
                }
            </tr>
        }
        @if (Model.ShowEmpty)
        {
            for (int i = 0; i < Model.PageSize - Model.PageCount; i++)
             {
                <tr>
                @foreach(Binary.MVC.Library.Model.FieldModel field in Model.FieldMC)
                {
                    <td>&nbsp</td>
                }
                </tr>
             }
        }
    </tbody>
    <tfoot>
    <tr><td colspan="@Model.FieldMC.Count">
    <table id="GridView-footer"><tr>
      <td align="left">
        <a title="第一页"><img src="http://www.cnblogs.com/http://www.cnblogs.com/Content/Images/pg-first.gif"/></a>
        <a title="前一页"><img src="http://www.cnblogs.com/http://www.cnblogs.com/Content/Images/pg-prev.gif"/></a>
        <a title="后一页"><img src="http://www.cnblogs.com/http://www.cnblogs.com/Content/Images/pg-next.gif"/></a>
        <a title="最后页"><img src="http://www.cnblogs.com/http://www.cnblogs.com/Content/Images/pg-last.gif"/></a>
        <a title="最后页"><img src="http://www.cnblogs.com/http://www.cnblogs.com/Content/Images/excel.png"/></a>
      </td>
    </tr></table></td></tr>
    </tfoot>
</table>
复制代码

这样一个类似于 GridView的分部页就实现了。以此来加载每个Action 所需的 Model。

最后的啰嗦:

这篇文章写的很笼统,旨在阐述清楚框架的基本原理。其实里面有很多巧妙的实现。由于我跟人有点强迫症,所以对代码的质量要求比较高,这个给我自己带来了很大的工作量。现在有几个问题,希望大家一起 讨论一下,更希望得到大牛的帮助:

1. 由于我的设计中Controller与Action是通过固定的LoadAction来解析的,请问有没有什么办法直接根据一个URL来用一个方法来解析,而并非是根据具体的Controller

中的Action才可以解析得到,ASP.NET MVC 中是要根据URL找到Controller中Action才会返回视图,现在是根据URL做统一的处理,所以每次不必要创建Controller与

Action。请问大牛们还有更好的实现方式不?

2. 由于使用了IO操作与反射,第一加载的时候性能会有所下降,请问还有更好的办法没有?

 

 

 

博客不易,转载请注明出处。Binarysoft.