一个js,在js实现跳转页面代码上输入子女的名字,性别,出生日期以及死亡日期,然后下面自动生成子女的个数

泠歌的推荐 | LOFTER(乐乎) - 每个人的理想国
LOFTER for ipad —— 记录生活,发现同好
泠歌 的推荐
转载自来源
转载自来源
{list photos as photo}
{if photo_index==0}{break}{/if}
品牌${make||'-'}
型号${model||'-'}
焦距${focalLength||'-'}
光圈${apertureValue||'-'}
快门速度${exposureTime||'-'}
ISO${isoSpeedRatings||'-'}
曝光补偿${exposureBiasValue||'-'}
镜头${lens||'-'}
{if defined('posts')&&posts.length>0}
{list posts as post}
{if post_index < 3}
{if post.type == 1 || post.type == 5}
{if !!post.title}${post.title|escape}{/if}
{if !!post.digest}${post.digest}{/if}
{if post.type == 2}
{if post.type == 3}
{if post.type == 4}
{if data.msgRank == 1}{/if}
{if data.askSetting == 1}{/if}
{if defined('posts')&&posts.length>0}
{list posts as post}
{if post.type == 2}
{if post.type == 3}
{if post.type == 4}
this.p={ dwrMethod:'querySharePosts', fpost:'',userId:921105,blogListLength:30};1.java语言是强类型还是弱类型语言?为什么?
Java 是强类语言,在使用变量时有如下规则:
?变量必须声明,并且初始化以后才能使用。
? 变量必须有明确的类型(type)。
? 变量不能重复定义。
javascript是一种弱类型语言,即变量在声明时,不能明确声明其类型
变量的类型是在运行时确定的,并且可以随时改变
2.JAVA的数据类型有哪些?
⑴基本数据类型(八种):
byte 1字节, short 2字节, int 4字节, long 8字节
4字节, double
【float 类型共 32位(不 int相同),其中 1位为符号位, 指数 8 位, 尾数23 位。】【double 类型能表示64位,其中1 位符号位,11位指数,52位尾数】
【浮点数的字面量默认是double】
char 2字节【unicode编码值】
boolean型:boolean1字节【值只有true和false】
隐式类型转换:
byteàshortàintàlongàfloatàdouble
⑵引用类型(自定义类型):对象:比如String
数组:int[]
接口:interface
3.JAVA中成员变量和局部变量的区别?
⑴成员变量:是在类范围内定义的(也叫成员属性)
类属性:使用static修饰的就是类属性。
作用域:类属性的作用域与这个类的生存范围相同,它作为类的一个成员,与类共存亡。只要类存在,程序就可以访问该类的类属性。
实例属性:不被static修饰的就是实例属性。
作用域:实例属性则从这个类的实例(对象)被创建开始存在,直到系统完全销毁这个实例,它作为实例(对象)的一个成员,与实例(对象)共存亡。只要实例存在,程序就可以访问该实例的实例属性。
⑵局部变量:在一个方法内定义的变量。(包括方法的形式参数)
1.形参:作用域是整个方法体
2.方法局部变量:一个代码块中
3.代码块局部变量:一个代码块中
注意:局部变量除了形式参数外,都必须显示初使化(必须显示指定初使值)。否则不可以访问它们。
形式参数不须显示初使化,它在被调用时由系统完成。
4.前++和后++的区别?
先将 i 的值作为整个表达的值,
然后将i 增加 1。
先将 i 增加1,
然后将 i 的值作为整个表达的值。
5. 短路运算符和非短路运算符的区别?
短路运算符[条件1 &&条件 2],如果条件1不成立,则条件2不执行;
非短路运算符[条件1 &条件 2],两个条件都会执行。
6.怎样取得数组的长度?
数组:arr.length集合:list.size()
字符串:str.length()
7.实现数组拷贝(复制)的2种方法?
System.arraycopy(src , srcPos, dest , destPos , length) ;
src &#8211; 源数组。
srcPos &#8211; 源数组中的起始位置。
dest &#8211; 目标数组。
destPos &#8211; 目标数据中的起始位置。
length &#8211; 要复制的数组元素的数量。
Arrays.copyOf(src , length):是 JDK1.6 版本提供的方法,比起System.arraycopy()使用更简便.
(注:当然,可以用新建数组用for循环的方式进行复制)
8.java中的4种访问制权限有哪些?分别作用范围是什么?
(1).public:最大访问控制权限,对所有的类都可见。
(2).protect:修饰的,在类内部、同一个包、子类中能访问
(3).default:包访问权限,即同一个包中的类可以可见。默认不显式指定访问控制权限时就是default包访问控制权限。
(4).private:最严格的访问控制权限,仅该类本身可见。
(注:访问控制修饰符可以修饰类,成员变量,方法,但是修饰类只用public和default)
9.JAVA5的新特性有哪些?
(1)循环(For-each循环)
for (type variable : array){ body}
for (type variable : arrayList){body}
而1.4必须是:
for (int i = 0; i & array. i++){ type variable = array[i];
for (int i = 0; i & arrayList.size(); i++){type variable = (type) arrayList.get(i); body}
以ArrayList为例,包括创建一个容器对象和取得容器内对象操作:
1.5 ArrayList&Type& arrayList =new ArrayList&Type&(); arrayList.get(i)
1.4 ArrayList arrayList =new ArrayList();
(Type) arrayList.get(i)
(3)自动装箱拆箱
在JDK5.0以前,在原始类型与相应的包装类之间的转化是不能自动完成的。要完成这种转化,需要手动调用包装类的构造函数,在JDK5.0环境中,可以自动转化:
1.5 Integer wrapper =
1.4 Integer wrapper = new Integer(n);
int n = wrapper.intValue();
自动装包/拆包大大方便了基本类型数据和它们包装类地使用。自动装包:基本类型自动转为包装类.(int && Integer);自动拆包:包装类自动转为基本类型.(Integer && int);
(4)静态导入
静态导入功能对于JDK 5.0以前的版本是不支持的。
import static java.lang.M
import static java.lang.S
1.5 out.println(sqrt(PI));
1.4 System.out.println(Math.sqrt(Math.PI));
(5)可变参数(Varargs)可变参数使程序员可以声明一个接受可变数目参数的方法。注意,可变参数必须是函数声明中的最后一个参数。在JDK1.5之前,可以用重载来实现,但是这样就需要写很多的重载函数。line1 public void write(Object&#8230; objs) {line2 for (Object obj: objs)line3 System.out.println(obj);line4 }
10.面向对象编程中几种对象组合方式——is-a /has-a/use-a:
(1).is-a组合:一个类继承具有相似功能的另一个类,根据需要在所继承的类基础上进行扩展。
优点:具有共同属性和方法的类可以将共享信息抽象到父类中,增强代码复用性,同时也是多态的基础。
缺点:子类中扩展的部分对父类不可见,另外如果共性比较少的时候使用继承会增加冗余代码。
(2).has-a组合:has-a组合是在一个类中引用另一个类作为其成员变量。
优点:可扩展性和灵活性高。在对象组合关系中应优先考虑has-a组合关系。
缺点:具有共性的类之间看不到派生关系。
(3).use-a组合:是一个类中使用到了另外一个类,依赖关系
11.构造方法(构造器)特点?
Java 中的构造器(构造方法)声明在类内部。
方法名与类名一致的方法叫构造方法
构造方法不能声明返回值类型。
构造方法可以包含参数,参数一般是创建对象实例必须依赖的条件(前提条件)。
子类默认调用父类的无参构造器,如果父类没有无参构造器,那么子类必需显示的去调用父类的有参构造器
如果一个类没有提供无参构造器,那么编译器将会自动提供一个无参构造器。
12.JAVA中属性和方法的静态绑定和动态绑定?
静态绑定:Java 根据引用变量类型查找属性
动态绑定:java 根据实际的对象查找方法
13. JavaBean 规范?
必须有包(package)
Java 类,具有无参数构造器
有用 getXxx() 和
setXxx() 声明的Bean属性
如:getName() 和
setName(String n) 声明的 Bean属性为:name, 不是否有实例变
量 name 无关
boolean类型的get方法可以有两种形式:getMarried() 戒者
isMarried()
必须实现序列化接口(注:在学习IO 的时候具体学习)
14.static关键字的特点?
static 静态关键字修饰:属性、方法、内部类、代码块
static 修饰的资源属于类级别,是全体对象实例共享的资源
static 变量在类加载期间初始化
静态代码块是在类加载期间运行的代码块,由于类只加载一次,所以静态代码块只执行一次!
15.final关键字的特点?
final可以修饰类,方法,变量
final 修饰的类,不能再被继承
final修饰的方法,不能覆盖final 方法
final 修饰的变量
final的局部变量,只能初始化不能改
final的方法参数,不能改
final 的引用,引用指向不能改,但是对象的属性可以改
16.常见的final类有哪些?
Java 的 String 就是 final类,不能被继承!
Math 是final类,不能被继承!
Integer 、Long、Character 等包装类是 final类,不能被继承!
17.抽象类和接口的区别?
抽象类&#8211;不具体的类
1 抽象方法,只有行为的概念,没有具体的行为实现。
使用:abstract 关键字修饰,并且没有方法体。
2 包含抽象方法的类,就一定是抽象类。
使用: abstract 关键字修饰,包含抽象方法。
如:平面图形一定可以计算面积。
public abstract class CRMSystem{
public abstract Client addClient(
String name, String qq);
3 抽象方法和抽象类非常适合作为系统的分析和设计的工具。
4 抽象类不能直接创建实例。可以定义引用变量。
5 抽象类只能被继承,一个具体类继承一个抽象类,必须实
现所有抽象方法。
1. 接口:全部的方法都是抽象方法,全部的属性都是常量。
接口用来表示纯抽象概念,没有任何具体的方法和属性。
2 .不能实例化,可以定义变量。
3 .接口变量可以引用具体实现类的实例。
4 .接口只能被实现,一个具体类实现接口,必须使用全部的
抽象方法。
5 .接口之间可以继承。
6 .一个具体类可以实现多个接口,实现多继承现象,表示:
一个概念即是XXX也是XXX.
7 .接口中的属性,默认是常量 public static final
8 .接中的方法一定是:public abstract
9 .实现一个接口,使用关键字implements, 实现实际上是
一种继承关系。接口和实现类是父子类型的关系
18.重载和重写的区别?
重载:方法名相同,参数不同(参数类型或者长度)
重载和修饰符和返回类型无关。
一是方法的参数列表必须改变,包括参数的类型,参数的个数多少,参数顺序。
二是重载对返回类型,访问修饰符,异常声明没有任何限制,可以作任意的修改。实质上,重载只是创建了一个方法而已,特殊的地方在于方法的名字。
重写:两同两小一大(规则)
两同:方法名相同参数类型相同
两小:返回值类型(基本数据类型要一致,引用类型可以是其子类)
抛出的异常要小(也可以抛出父类型的异常的部分异常,或者不抛出异常)
一大:访问控制修饰符大
(1)重写方法必须和被重写方法具有相同的参数列表,返回类型必须和被重写方法的返回类型相同或者是返回类型的子类型。
(2)重写方法的访问控制修饰符不能比被重写方法更严格(比如一个在父类中声明为public的方法重写成一个protected的方法)。
(3)只有实例方法才能被重写,超类中的final方法不能被重写。
(4)重写方法不能抛出新的检查异常,或者是抛出比被重写方法声明的检查异常更广泛的检查异常。
19.==和equals()的区别?
&#8220;==&#8221;,比较引用值和基本数据类型是否相等。
xxx.equals()方法比较对象的内容是否相等。默认的比较规则是:比较引用
20.为什么要同时覆盖hashcode()和equals()?
hashCode()方法要不equals 方法一同覆盖(Sun公司规定)
当两个对象equals 比较为true 时,应具有相同的hashCode()值
当两个对象equals 比较为false 时,应具有不相同的hashCode()值
hashCode() 值要稳定(一致性),一个对象创建以后就不应该再变化
默认的hashCode()值是当前堆对象地址转换的一个整数,这个整数不是内存地址!
在java的中,判断两个对象是否相等的规则是:
首先,判断两个对象的hashCode是否相等
如果不相等,认为两个对象也不相等
如果相等,则判断两个对象用equals运算是否相等
如果不相等,认为两个对象也不相等
如果相等,认为两个对象相等
21.String类有哪些常用的方法?
toLowerCase()
toUpperCase()
lastIndexOf()
endsWith()
startsWith()
substring(int start, int end)
substring(int start)
toCharArray()
22.String,StringBuilder,StringBuffer的区别?
String = char[] + 操作(复制创建新对象)
char[]不可变
StringBuilder = char[] + 对char[]操作(处理当前数组内容)
char[]可变
StringBuilder 是变长字符序列
StringBuilder 方法:append,insert &#8230; 都返回当前
StringBuilder 对象本身的引用
StringBuffer 和
StringBuilder API 几乎一样!
StringBuffer 是 java早期提供的(JDK1.0),速度稍慢,线程安全
StringBuilder 是 Java5 以后提供的(JDK5.0),速度快,非线程安全
23.谈谈集合框架的理解?
集合框架包括集合不映射(Collection and Map)
List 元素有先后次序的集合, 元素有 index 位置, 元素可以重复,继承自 Collection 接口,实现类: ArrayList, Vector, LinkedList
List 表示有先后次序的对象集合
ArrayList是使用变长数组算法实现的,ArrayList 实现自 List
ArrayList 和 Vector 的比较
Vector(1.0 版本提供的),线程安全的,效率稍低,也是使用变长数组算法实现的,继承自 List 接口
ArrayList,线程不安全的,效率高速度快(现在较常用)
2)ArrayList 和 LinkedList 的比较
LinkedList 是采用双向循环链表实现的 List
ArrayList 是采用变长数组算法实现的的 List
在ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList的中间插入或删除一个元素的开销是固定的。
ArrayList查询速度快,而LinkedList增删速度快
Set 元素无续, 不能重复添加, 是数学意义上的集合, 继承自 Collection 接口
实现类: HashSet(是一个只有 Key的 HashMap),使用Set时要重写hashCode,equals方法
HashMap 以键-值对(关键字:值)的形式存储对象,关键字 key 是唯一的、不重复的
key 可以是任何对象,Value可以任何对象
(key:value)成对放置在集合中
重复的 key 算一个,重复添加是替换操作(会覆盖原来的元素)
HashMap 根据 key 检索查找value 值
新,非线程安全,不检查锁,快
(JDK1.2 版本以前),线程安全,检查锁,慢一点(差的很小)
23.散列表的特点?
散列表中存放的对象是不连续的,所以称为&#8221;散列表&#8221;
散列表的优点:查找迅速
在 ArrayList 中查找 Mac,顺序查找,需要查找 5 次
在 HashMap 中(底层实现原理是散列表)查找 Mac,经过散列运算,仅需 1 次
24.java泛型的作用是什么?
泛型是 Java5 以后提出的语法现象,作用是在编译期检查的类型约束(运行期不检查泛型),泛型可以用来约束类中元素的类型
25.Collection和Collections的区别?
Collection是集合接口,下面有子接口,List,Set
集合的工具类为 Collections,同数组的工具类 Arrays 相同,其中提供了许多的方法,诸如排序、二分查找、打乱、填充等操作。
26.内部类的分类?各有什么特点?
根据位置的不同,Java 中的内部类分为四种:
静态内部类
使用 static 修饰,声明在类体中
静态内部类中可以访问外部类的静态成员
成员内部类
声明在类体中,不使用static,具有类的成员特征,也就是,必须有类的实例才能
创建内部类实例
内部类实例可以访问共享外部类的成员变量(很常用)
如:链表的节点就可以定义为内部类
局部内部类把类声明在方法中,就是局部内部类,作用域
类似局部变量(很少见)
匿名内部类
匿名类,非常常见,可以写在任何地方,就像一般的语句
语法更象是创建对象:Date d = new Date(){//&#8230;};
匿名类是对原类的一个继承,同时创建了实例,{} 就是继承以后的类体
类体中可使用所有类的语法
匿名类不能写构造器
匿名类可以从抽象类或者接口继承,必须提供抽象方法的实现
任何内部类都编译成独立的class 文件
最大的作用:封装!
27.怎么将Date和String互相转换?
28.Java中的异常理解?
异常是程序运行过程过程出现的错误,在Java中用类来描述,用对象来表示具体的异常。Java将其区分为Error与Exception,Error是程序无力处理的错误,Exception是程序可以处理的错误。
1)Error与Exception
Error是程序无法处理的错误,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。Error:一般指虚拟机相关问题,如虚拟机崩溃,虚拟机出错等这种错误无法恢复或不可捕获,将导致应用程序中断。对于Error一般不编写针对性代码对齐进行处理。
Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。程序中应当尽可能去处理这些异常。
2)运行时异常和非运行时异常
运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
自定义异常 (自己定义异常表达错误)
MyException extends Exception
检测(check)异常
MyException extends RuntimException 运行时(runtime)异常
29.JAVA中异常处理的方式有哪些?
1)try…catch…finally
//(尝试运行的)程序代码
}catch(异常类型异常的变量名){
//异常处理代码
注:子类异常的处理块必须在父类异常处理块的前面,否则会发生编译错误。
finally块中一定会执行吗?
2)throws,throw
throw关键字是用于方法体内部,用来抛出一个Throwable类型的异常。如果抛出了检查异常,则还应该在方法头部声明方法可能抛出的异常类型。
throws关键字用于方法体外部的方法声明部分,用来声明方法可能会抛出某些异常。仅当抛出了检查异常,该方法的调用者才必须处理或者重新抛出该异常。
30.实现序列化的作用?(implements Serializable)
序列化的作用是,将数据分解成字节流,以便存储在文件中或在网络上传输。
31.IO流的分类?以及常用流的写法?
分为:字节流和字符流或者输入流和输出流
InputStream is = new FileInputStream(&#8220;gbk.txt&#8221;);
Reader in = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(in);
PrintWriter out = new PrintWtirer(
new OutputStreamWriter(
new FileOutputStream(filename)));
32.创建线程的两种方式?
继承 Thread 类(extends Thread)或者实现Runnable 接口(implements Runnable)
继承 Thread 类
实现步骤:
继承 Thread 类, 覆盖run()方法, 提供并发运程的过程
创建这个类的实例
使用 start() 方法启动线程
实现 Runnable 接口
实现步骤:
实现 Runnable 接口, 实现run()方法, 提供并发运程的过程
创建这个类的实例, 用这个实例作为Thread 构造器参数,创建Thread 类
使用 start() 方法启动线程
33.线程的5 中状态
当程序使用new关键字创建了一个线程后,该线程就处于新建状态,此时线程还未启动,
当线程对象调用start()方法时,线程启动,进入Runnable 状态
可运行(就绪)状态
当线程处于Runnable 状态时,表示线程准备就绪,等待获取CPU
运行(正在运行)状态
假如该线程获取了CPU,则进入Running 状态,开始执行线程体,即run()方法中的内
如果系统叧有1个CPU,那么在仸意时间点则叧有1条线程处于Running 状态;
如果是双核系统,那么同一时间点会有2条线程处于Running 状态
但是,当线程数大于处理器数时,依然会是多条线程在同一个CPU 上轮换执行
当一条线程开始运行时,如果它不是一瞬间完成,那么它不可能一直处于Running 状态,
线程在执行过程中会被中断,目的是让其它线程获得执行的机会,像这样线程调度的策
略取决于底层平台。对于抢占式策略的平台而言,系统系统会给每个可执行的线程一小
段时间来处理仸务,当该时间段(时间片)用完,系统会剥夺该线程所占资源(CPU),
让其他线程获得运行机会。
调用yield()方法,可以使线程由Running 状态进入Runnable 状态
阻塞(挂起)状态
当如下情冴下,线程会进入阻塞状态:
线程调用了sleep()方法主动放弃所占CPU 资源
线程调用了一个阻塞式IO 方法(比如控制台输入方法),在该方法返回前,该线
&#8230;&#8230;
当正在执行的线程被阻塞时,其它线程就获得执行机会了。需要注意的是,当阻塞结束
时,该线程将进入Runnable 状态,而非直接进入Running 状态
当线程的run()方法执行结束,线程进入Dead 状态
需要注意的是,不要试图对一个已经死亡的线程调用start()方法,线程死亡后将不能再次作为线程执行,系统会抛出IllegalThreadStateException 异常
new运算创建线程后,线程进入New状态(初始状态)
start()方法后,线程从New状态进入Runnable 状态(就绪状态)
start()方法是在main()方法(Running 状态)中调用的
线程结束后,进入Dead 状态(死亡状态),被对象垃圾回收
main()方法结束后,其它线程,比如上例中p1和p2 开始抢着进入Running 状态
由谁抢到是底层操作系统决定(操作系统分配时间片)
单核处理器:在一个时间点上叧有一个线程在Running 状态;双核处理器:2 个
如果p1 进入Running 状态,当操作系统分配给它的时间片到期时,p1进入 Runnable
状态,p2进入Running 状态
在期间有可能其它的进程的线程获得时间片,那么p1和p2同时进入Runnable 状态,
等待操作系统分配时间片
线程进入Dead 状态后,叧能被垃圾回收,不能再开始
如果线程在运行过程中,自己调用了yield()方法,则主动由 Running 状态进入Runnable 状态
34.异步与同步的区别?
并发, 各干自己的。如: 一群人上卡车
步调一致的处理。如: 一群人上公交车
35.数据库中,char和varchar类型的区别?
表示定长字符串( 方便查询
) 最长放入n个字符
, 放入的数据如果不够n个字符则补空格
, 无论如何都占n个字符长度。
varchar(n)
表示变长字符串( 节省空间
) 最长放入n个字符
, 放入的数据是几个长度就占多大空间。
decode(a2,&#8217;A&#8217;,&#8217;AAAA&#8217;,&#8217;BBBB&#8217;) FROM
decode(a1,&#8217;A
&#8216;,&#8217;AAAA&#8217;,&#8217;BBBB&#8217;) FROM TT ; &#8211;此处是两个空格
36.在数据库中怎么做表的复制?(一条语句完成)?
create table emp_xxx as select * from emp_
(注:如何将一个表中的数据复制到另外一个表中?
insert into table(select * from table2);两个表结构一样
insert into table(name,age) (select name,age from table2);复制指定的字段
37.分别简述一下DDL,DML,DQL,TCL,DCL?
1)数据定义语言DDL( Data Definition Language ) , 是 SQL语言集中负责数据结构定义不数据库对象定义的语言
, 主要有create、alter、drop 和truncate 四种常用语句。
DDL对数据结构起作用。
数据库对象的创建
修改数据库对象
删除数据库对象
清空表数据
2)数据操纵语言DML( Data Manipulation Language ) , 用户通过它可以实现对数据表的基本操作, 即对表中数据的增、删、改。
DML对数据起作用。
3)数据查询语言DQL( Data Query Language ) , 用户主要通过它实现对数据的查询操作。
4)TCL事务控制语句是用来对DML操作进行确认的。
5)数据控制语言(Data Control Language,DCL)用于对用户授权或撤销其权限,也可使用角色实现对用户的批量授权或撤销权限,在应用开发层面较少用到。
grant(授予权限)/revoke(回收权限)
38.Oracle常用的单行函数有哪些?
round( 数字
, 小数点后的位数)用于数字的四舍五入
trunc( 数字
, 小数点后的位数)用于截取,如果没有第二个参数
, 默认是 0
to_date()和to_char()是时间处理的函数
将字符串数据按指定格式转换为日期数据
将日期数据按指定格式转换为字符串数据
coalesce( 参数列表
)函数的作用:返回参数列表中第一个非空参数
, 参数列表中最后一个值通常为常量
decode()函数是Oracle 中等价于case when语句的函数
, 作用同case 语句相同。
decode 函数语法如下:
decode(判断条件
, 匹配1 , 值 1 , 匹配2 , 值2 , …
nvl(bonus,0) 空值转换函数
39.常用的组函数有哪些?
avg/sum操作数字
max/min可以操作各种数据类型
组函数默认忽略空值
40.判断语句是否正确?
select ename,count(*) from emp where ename=&#8217;KING&#8217;
select count(*),sum(sal) fro
在SELECT列表中所有未包含在组函数中的列都应该包含在 GROUP BY 子句中。
包含在GROUP BY 子句中的列不必包含在SELECT列表中。
你有答案? 你对以上面试题有意见? 你想发表你的见解? 写下来吧!你的分享将会让很多人受益!
相关面试题
版权声明:本站大部分内容为原创! 另有少部分内容整理于网络,如需转载本站内容或关切版权事宜请联系站长。未经允许,严禁复制转载本站内容,否则将追究法律责任。
本站欢迎与同类网站建立友情链接,请联系QQ:为一个 iOS 应用编写一个简单的 Node.js/MongoDB Web 服务
在当今这个协作和社交应用的世界里,其关键是要有一个能简单构建和易于部署的后台。许多组织机构都依赖于一个应用栈(Application Stack),其使用下面三项技术:Node.js、Express、MongoDB。
本文由翻译自raywenderlich的《》
在当今这个协作和社交应用的世界里,其关键是要有一个能简单构建和易于部署的后台。许多组织机构都依赖于一个应用栈(Application Stack),其使用下面三项技术:
这个栈对于移动应用来说相当流行,因为原生数据格式是JSON,它容易被应用解析,例如通过使用 Cocoa 的 NSJSONSerialization 类或其它类似的解析器。
在本教程中,你将学会如何搭建了一个 Node.js 环境,驱动 Express;在此平台之上,你将构建一个通过 REST API 来提供一个 MongoDB 数据库的服务器,就像这样:&
在一个 HTML 表格中呈现的后端数据库
本教程的第二部分重点放在 iOS 应用端。你将构建一个很酷的叫做&有趣的地方&的应用,标记有趣的位置,让其它用户能够找出他们附近有趣的地方。下面稍微窥探一下你将构建的应用:
TourMyTown 的主视图
本教程假设你已经了解了 JavaScript 和 Web 开发的基础,但对 Node.js、Express 以及 MongoDB 都不熟悉。
一个 Node+Mongo 案例
大多数 Objective-C 开发者都不太熟悉 JavaScript ,但它对于 Web 开发者来说是极其常见的语言。因为这个原因,Node 作为一个 Web 框架收获了大量人气,但还有更多原因使其成为后端服务的绝好选择:
1. 内建的服务器功能
2. 通过它的包管理器做到良好的项目管理
3. 一个快速的 JavaScript 引擎,也就是 V8
4. 异步事件驱动编程模型
一个异步的关于事件和回调的编程模型非常适合服务器,它要等待许多事情,例如到来的请求以及通过其它服务(例如 MongoDB)的内部进程通信。
MongoDB 是一个低开销的数据库,其所有实体都是自由形式 BSON && &二进制 JSON& && 文档。这能让你同异构数据打交道,而且处理各种各样的数据格式也变得很容易。因为 BSON 与 JSON 兼容,构建一个 REST API 就很简单&&服务器代码能够传递请求到数据驱动器而不需要很多的中间处理。
Node 和 MongoDB 在本质上都具有可扩展性,能够轻松地在跨越分布式模型中的多个机器,实现同步;这个组合对于不具有均匀分布负载的应用来说是一个理想选择。
本教程假设你使用 OS X Mountain Lion 或 Mavericks ,Xcode 及其 command line tools 都已经安装好了。
第一步是安装
。就像 CocoaPods 为 Cocoa 管理各种包 和 Gem 为 Ruby 管理各种包一样,Homebrew 管理 OS X 上的 Unix 工具。它构建在 Ruby 和 Git 之上,而且它具有高度的灵活性和可定制性。
如果你已经安装了 Homebrew ,那就可以跳过下面的步骤。不然,打开终端执行下列命令来安装 Homebrew :
ruby&-e&&$(curl&-fsSL&/Homebrew/homebrew/go/install)&&
&注意:cURL 是使用 URL 请求来发送和接收文件与数据的称手工具。此处你使用它加载 Homebrew 安装脚本&&在本教程后面,你还会使用它与 Node 服务器交互。
一旦安装好 Homebrew ,就在终端输入下面的命令:
brew&update&
这只是更新 Homebrew ,让你拥有最新的软件包列表。
现在,通过 Homebrew 安装 MongoDB ,使用下面的命令:
brew&install&mongodb&
记下 MongoDB 被安装的位置,它就在输出的&Summary&中。稍后你将用它加载 MongoDB 服务。
从下载并运行 Node.js 安装器。
一旦安装完成,你就马上测试 Node.js 是否安装成功。
在终端里输入:
这能让你进入 Node.js 的交互式运行环境,在此你可以执行 JavaScript 表达式。
在提示符后输入下面的表达式:
console.log(&Hello&World&);&
你将得到如下输出:
Hello&World&undefined&
console.log 在 Node.js 中相当于 NSLog 。当然,console 的输出流比 NSLog 的要复杂得多:它有 、console.assert、console.error 以及你期望的从更先进的记录器例如 CocoaLumberjack 而来的其它流。
写在输出里的 &undefined& 值是 console.log 的返回值,而 console.log 没有返回值。 因为 Node.js 总是显示出所有表达式的输出,无论其返回值是否有定义。
&注意:如果你以前使用过 JavaScript ,你需要知道 Node.js 环境和浏览器环境之间有些许不同。全局对象被叫做 global 而不是 window 。在 Node.js 交互提示符后键入 global 并按下回车就会显示 global 命名空间里所有的方法和对象;当然,直接使用 来做参考更容易些。&
global 对象有所有预定义的常数、函数以及数据类型,都可用于所有运行在 Node.js 环境里的程序。任何用户创造的变量同样也都添加到全局上下文对象。基本上 global 的输出将列出所有内存中可以访问的事物。
运行一个 Node.js 脚本
Node.js 的交互式环境对于玩耍和调试 JavaScript 表达式是很棒的,但通常你都会使用脚本文件来做实际的事情。就像 iOS 应用包含有 Main.m 作为其入口点,Node.js 的默认入口点就是 index.js 。然而,不同于 Objective-C ,这里没有 main 函数;相反, index.js 将从头到尾的执行。
按下 Control+C 两次以退出 Node.js Shell。执行下面的命令,新建一个目录以保存你的脚本:
mkdir&~/Documents/NodeTutorial&
然后执行下面的命令进入新建的目录并使用你默认的文本编辑器新建一个脚本文件:
cd&~/Documents/NodeTutorial/;&edit&index.js&
在 index.js 中添加如下代码:
console.log(&Hello&World.&);&
保存你的工作,回到终端执行下面的命令看看你的脚本如何运行:
node&index.js&
再一次,我们看到了熟悉的 &Hello World& 输出。你也可以执行 node . 来运行你的脚本,. 就会默认查找 index.js 。&
固然,一个 &Hello World& 脚本成不了一个服务器,但这是测试你的安装是否成功的快速方式。下一节将向你介绍 Node.js 包的世界,这会成为你那闪亮的新 Web 服务器的基础!
Node.js 应用程序都被分成不同的包,这就是 Node.js 世界的&框架&。 Node.js 自带有几个基础且强大的包,但还有超过 50000 个由活跃的开发社区提供的公开包&&如果你不能找到你需要的包,你自己也可以比较容易地创造。
&注意:查看&可得到所有可用包的列表
用下列代码替换 index.js 的内容:
&var&http&=&require('http');&&&http.createServer(function&(req,&res)&{&&&res.writeHead(200,&{'Content-Type':&'text/html'});&&&res.end('&html&&body&&h1&Hello&World&/h1&&/body&&/html&');&}).listen(3000);&&console.log('Server&running&on&port&3000.');&
依次按照编号好的注释看看:
1. require 引入(import)模块(module)到当前文件。本次你引入了 HTTP 库。
你创建一个 Web 服务,它对简单的 HTTP 请求的回应是发送一个 200 应答,并将页面内容放在应答里。
2. Node.js 作为一个运行时环境的最大的优势之一就是他的 事件驱动模型(event-driven model) 。它围绕着异步调用的回调函数的概念来设计。在上面的例子里,你正监听 3000 端口等着传入的 HTTP 请求。当你收到一个请求,你的脚本调用 function (req, res) {&} 并返回一个应答给调用者。
保存你的文件,回到终端并执行如下命令:
node&index.js&
你将在控制台看到如下输出:
打开你最喜欢的浏览器导航至
;好好瞧着, Node.js 正在提供给你的是一个 &Hello World& 页面。
你的脚本还在哪里,耐心地等待从 3000 端口传入的 HTTP 请求。要干掉(kill)你的 Node 实例,只需在终端按下 Ctrl+C。
&注意:Node 包通常由顶层函数或引入的对象写就。通过使用 require ,这个函数在之后就被分配给一个顶层变量。这样有助于以一个健全的方式管理范围(scope)以及暴露(expose)模块的 API 。稍后在本教程中你会看到如何创建一个自定义模块,你将为 MongoDB 添加一个驱动器。
NPM && 使用外部 Node 模块
前一小节覆盖了 Node.js 内建的模块,那第三方的模块该怎么处理呢?例如你之后需要的 Express 模块,它为你的服务器平台提供路由中间件。
外部模块同样可以使用 require 函数引入到文件里,但你需要分开下载它们然后才能用于你的 Node 实例。
Node.js 使用 npm&&Node 包模块&&来下载、安装以及管理包依赖。如果你熟悉 CocoaPods 或者 Ruby gems ,那么你对 npm 也会觉得熟悉。你的 Node.js 应用程序使用 package.json ,它专门定义配置和 npm 依赖。
使用 Express
Express 是一个流行的 Node.js 模块,提供路由中间件。为什么你会需要这个独立的包呢?考虑下面的情形。
如果你只使用 http 模块自身,你不得不分开解析每个请求的位置以找出提供什么内容给请求者&&如此这般,事情很快就会变得难以处理。
然而,用 Express 你就能容易地为每个请求定义路由和回调。 Express 同样让为基于 HTTP 动词(例如 POST, PUT, GET, DELETE, HEAD, 等)以提供不同的回调变得很容易。
HTTP 动词的简要介绍
一个 HTTP 请求包含一个方式&&或者动词&&的值。默认值是 GET ,它是为了获取数据,例如浏览器中 Web 页面。 POST 意味着上传数据,例如提交 Web 表单。对于 Web API 来说, POST 通常用于添加数据,但它同样可用于远程处理调用类型端点(but it can also be used for remote procedure call-type endpoints.)。
PUT 与 POST 的不同在于它通常用于替换已有数据。在实践中, POST 和 PUT 通常以同样的方式使用:在请求 Body 里提供实体以放进后端的数据存储里。 DELETE 用于从你的后端数据存储里移除条目。
POST、GET、PUT 以及 DELETE 就是 HTTP 实现的 CRUD 模型 && Create、Read、Update 以及 Delete。
还有其它一些少有人知的 HTTP 动词。 HEAD 表现得像一个 GET 但只返回应答头而没有 Body 。这有助于最小化数据传输,如果应答头中的信息足够确定是否有可用的新数据。其它动词如 TRACE 和 CONNECT 用于网络路由。
添加一个包到 Node 实例
在终端里执行下列命令:
edit&package.json&
这会创建一个新的 package.json ,它将包含你的包配置和依赖。
添加如下代码到 package.json 中:
{&&&&&name&:&&mongo-server&,&&&&&version&:&&0.0.1&,&&&&&private&:&true,&&&&&dependencies&:&{&&&&&&&express&:&&3.3.4&&&&&}&&}&
这个文件定义了一些元数据,例如项目的名字和版本,一些脚本,以及对于你的目的来说最重要的包依赖。下面说明每行的意思:
1. name 是项目的名字。
2. version 是项目目前的版本。
3. private 防止项目被意外地公开,如果你设置其为 true 。
4. dependencies 是一个包含你应用使用的模块的列表。
依赖以 键/值 形式接受模块名和版本。你的依赖列表包含有 3.3.4 版本的 Express; 如果你想指明 Node.js 去使用最新版本的包,你可以使用通配符&*&。
保存文件,在终端里执行下列命令:
npm&install&
你会看到如下输出:&
install 下载并安装 package.json 指定的依赖&&以及你的依赖本身的依赖!:] &&存进一个叫做 node_modules 的目录,并让你的应用程序使用它们。
一旦 npm 完成,你就可以在你的应用程序中使用 Express 了。
在 index.js 中找到下列行:
var&http&=&require('http');&
并添加 Express 的 require 调用,如下所示:
var&http&=&require('http'),&express&=&require('express');&
这就引入了 Express 包,并将其存在变量 express 中。
添加如下代码到 index.js,就在刚在添加的区域的下面:
var&app&=&express();&app.set('port',&process.env.PORT&||&3000);&
这就创建了一个 Express 应用并设置其默认端口为 3000 。你可以通过创建一个环境变量 PORT 来覆盖此默认值。这种类型的自定义在开发工具中非常方便,特别是如果你有多个应用程序监听这好几个端口。
添加如下代码到 index.js ,就在刚刚添加的区域的下面:
app.get('/',&function&(req,&res)&{&&&res.send('&html&&body&&h1&Hello&World&/h1&&/body&&/html&');&});&
这就创建了一个路由处理器(route handler),它是给定 URL 的请求处理者链的花哨名字。Express 匹配请求中指定的路径并执行适当的回调。
你上面的回调告诉 Express 去匹配根路径 &/& 并返回一个给定的 HTML 。 send 为你格式化各种响应头&&例如 content-type 和 status code && 如此你就能专注于编写伟大代码了。
最后,替换 http.createServer(...) 为下面的实现:
http.createServer(app).listen(app.get('port'),&function(){&console.log('Express&server&listening&on&port&'&+&app.get('port'));&&});&
这比之前的稍微紧凑些。 app 分开实现 function(req,res) 回调,而不是在 createServer 这里内联地包含它们。你同样添加了一个完成处理器回调,一旦端口准备好接收请求它就会被调用。现在你的应用在打印 &listening& 消息到控制台之前就等着端口准备好。
为了审查,index.js 整个看起来如下所示:
var&http&=&require('http'),&&&&&express&=&require('express');&&var&app&=&express();&&app.set('port',&process.env.PORT&||&3000);&&app.get('/',&function&(req,&res)&{&&&res.send('&html&&body&&h1&Hello&World&/h1&&/body&&/html&');&});&&http.createServer(app).listen(app.get('port'),&function(){&&&console.log('Express&server&listening&on&port&'&+&app.get('port'));&});&
保存你的文件,并在终端执行下列命令:
node&index.js&
回到浏览器,重新载入 去看看你的 Hello World 页面是否依然加载。
你的页面看起来与之前没有区别,但有不止一种方法可以查看引擎盖下发生了什么事。
创建终端的另一个实例,并执行如下命令:
curl&-v&http:&
你会看到如下输出:
curl 吐出你的 HTTP 请求的头和内容,给你显示服务传来的东西的原始细节。注意 X-Powered-By : Express 头;Express 会自动添加这个元数据到应答里。
使用 Express 提供内容
用 Express 提供静态文件非常容易。
添加如下语句到 index.js 顶部的 require 区域:
path&=&require('path');&
再添加下面一行到 app.set 语句之后:
app.use(express.static(path.join(__dirname,&'public')));&
这就告诉 Express 去使用 express.static 中间件 ,它为到来的请求提供静态文件作为应答。
path.join(__dirname, 'public') 映射本地子目录 public 到基路由 / ; 它使用 Node.js path 模块创建一个平台无关的子目录字符串。
index.js 现在看起来如下:
&&var&http&=&require('http'),&&&&&&express&=&require('express'),&&&&&&path&=&require('path');&&&var&app&=&express();&app.set('port',&process.env.PORT&||&3000);&&app.use(express.static(path.join(__dirname,&'public')));&&app.get('/',&function&(req,&res)&{&&&res.send('&html&&body&&h1&Hello&World&/h1&&/body&&/html&');&});&&http.createServer(app).listen(app.get('port'),&function(){&&&console.log('Express&server&listening&on&port&'&+&app.get('port'));&});&
使用了静态处理器,任何在 /public 中的东西都可以由其名字访问到。
为了证明这一点,按下 Control+C 干掉你的 Node 实例,然后执行下面的命令:
mkdir&public;&edit&public/hello.html&
添加如下代码到 hello.html :
&html&&/body&Hello&World&/body&&/html&&
这就创建了一个新的 public 目录并创建了一个基础的静态 HTML 文件。
再次用命令 node index.js 重启你的 Node 实例。 浏览器打开
你就会看到这个新创建的页面,如下所示:&
静态页面是不错,但 Express 的真正威力是动态路由。 Express 在路由字符串上使用一个正则表达是匹配器,允许你为路由定义参数。
举个例子,路由字符串可以包含下列元素:
1. 静态元素&& /files 只会匹配 http://localhost:3000/pages (译者注:似乎有点问题,应该只会匹配 http://localhost:3000/files)
2. 以&:&开头的参数&& /files/:filename 匹配 /files/foo 和 /files/bar,但不能匹配 /files
3. 以&?&结尾的可选参数&&/files/:filename? 匹配 /files/foo 也能匹配 /files
4. 正则表达式&& /^\/people\/(\w+)/ 匹配 /people/jill 和 /people/john
要试试看,就添加下列路由到 index.js 中 app.get 语句后:
app.get('/:a?/:b?/:c?',&function&(req,res)&{&&&&&res.send(req.params.a&+&'&'&+&req.params.b&+&'&'&+&req.params.c);&});&
这就创建了一个新的路由,它接收三个路径层级并在应答 Body 中显示这些路径组件。任何由:开始的东西都映射到所提供名字的请求参数上。
重启你的 Node 实例,再将浏览器转到 。你将看到如下页面:
&hi& 是 req.params.a 的值,&every& 是req.params.b 的值,最后 &body& 分配给 req.params.c 。
路由匹配对于构建 REST API 来说很有用,你可以用动态路径指定后端数据存储的特定元素。
除了 app.get , Express 还支持 app.post、app.put、app.delete 等等。
错误处理与模版化 Web 视图
服务器错误可用一到两种方式处理。你可以传递一个异常给调用栈&&这样做可能干掉应用&&或者你可以捕捉错误并返回一个合适的错误码。
HTTP 1.1 协议在 4xx 和 5xx 范围内定义了好几个错误码。 400 段的错误用于用户错误,例如请求一个不存在的条目:一个熟悉的错误码是 404 Not Found 错误。500 段的错误表示服务器错误,例如超时或者编程错误(比如 null 解除引用(null dereference))。
你将添加一个捕捉所有(catch-all)的路由并在请求内容不能被找到时返回一个 404 页面。因为路由处理器按照它们设置 app.use 或 app.verb 的顺序添加,一个捕捉所有(catch-all)的路由可以添加在路由链的最后。
添加如下代码到 index.js ,就在最后的 app.get 与调用 http.createServer 之间:
app.use(function&(req,res)&{&&
res.render('404',&{url:req.url});&&});&
这些代码会导致 404 页面的加载,如果此处没有前一个调用使用 res.send() .
这里有一些值得记录的点:
1. app.use(callback) 匹配所有请求。当它被放在所有 app.use 和 app.verb 的列表的最后,callback 就会成为捕捉所有(catch-all)。
2. res.render(view, params)调用使用模版引擎( templating engine)渲染的输出填充响应 Body 。 一个模版引擎使用磁盘上一个叫做&View&的模版文件并用一组键值参数替换其中的变量以生成一个新的文档。
等等&&一个&模版引擎&?这货搞什么飞机?
Express 能使用好几种模版引擎来提供视图。要让这个例子工作起来,你将添加一个流行的 Jade 模版引擎到你的应用程序中。
Jade 是一种简单的语言,它避开括号并使用空白符来代替,以确定 HTML 标签的顺序和内容。它同样可以使用变量、条件判断、迭代以及分支以便动态地创建 HTML 文档。
更新 package.json 中的依赖为:
{&&&&dependencies&:&{&&&&&&express&:&&3.3.4&,&&&&&&jade&:&&1.1.5&&&}&
回到终端,干掉你的 Node 实例,并执行如下命令:
npm&update&
这将下载并安装 jade 包。
添加如下代码到 index.js ,就在第一个 app.set 之后:
app.set('views',&path.join(__dirname,&'views'));&&app.set('view&engine',&'jade');&
上面的第一行指定了视图模版的位置,第二行就设置 jade 作为视图渲染引擎。
在终端执行如下命令:
mkdir&&edit&views/404.jade&
添加下列代码到 404.jade 中:
doctype&html&body&&&&&h1=&'Could&not&load&page:&'&+&url&
Jade 模版中的前两行创建了一个有 body 元素的新 HTML 文档。第三行通过缩进创在 body 内建了一个 h1 元素。间距在 Jade 中很重要!
h1 元素的文本由 &Could not load page& 和作为 index.js 中 res.render() 的一部分传递的 url 参数串联而成。
作为一个快速检查,你的 index.js 现在看起来如下:
var&http&=&require('http'),&&&&&express&=&require('express'),&&&&&path&=&require('path');&&&var&app&=&express();&&app.set('port',&process.env.PORT&||&3000);&&&app.set('views',&path.join(__dirname,&'views'));&&&app.set('view&engine',&'jade');&&&app.use(express.static(path.join(__dirname,&'public')));&&app.get('/',&function&(req,&res)&{&&&res.send('&html&&body&&h1&Hello&World&/h1&&/body&&/html&');&&});&&&app.use(function&(req,res)&{&&&&&res.render('404',&{url:req.url});&});&&http.createServer(app).listen(app.get('port'),&function(){&&&&console.log('Express&server&listening&on&port&'&+&app.get('port'));&});&
重启你的 Node 实例,使用浏览器加载 URL
。你将看到如下页面:
现在你的 index.js 中有足够的启动代码去接收传入的请求并提供一些基本的响应。而缺失的部分就是数据库持久化,它能将这些东西变成一个有用的Web应用程序,能够被一个移动应用所利用。
介绍 MongoDB
MongoDB 是一个存储 JSON 对象的数据库。不像 SQL 数据库,类似 Mongo 的 NoSQL 数据库 不支持实体关系。进一步说明,没有预定义的模式,所以同一集合里的实体不需要有同样的字段或符合预定义的模式。
MongoDB 同样提供了强大的的查询语言 map-reduce 以及对定位数据的支持。MongoDB 因其扩展、复制和碎片(scale, replicate and shard)能力而广受欢迎。扩展和高可用特性不在本教程覆盖范围。
MongoDB 最大的缺点是缺少关系支持,而且在内存映射实际的数据库文件时可能会占用过多内存。这些问题可以通过仔细构造的数据得到缓解;这将在本教程的第二部分进行说明。
因为 MongoDB 文档 和 JSON 的亲密关系,MongoDB 对于 Web 和移动应用都是很棒的选择。 MongoDB 不存储原始 JSON;而是叫做 BSON(即 Binary JSON) 格式的文档,这对于数据存储和查询来说更有效率。BSON 同时还支持比 JSON 更多的数据类型,例如日期和C类型(C-type)。
添加 MongoDB 到你的项目中
MongoDB 是一个原生应用程序,通过驱动器(drivers)访问。有好多种驱动器可用于几乎任何环境,当然包括 Node.js 。MongoDB 驱动器连接 MongoDB 服务器并发出命令去更新或读取数据。
这就意味着你需要运行一个 MongoDB 实例以在一个打开的端口上监听。幸运的是,这就是你的下一个步骤!:]
新开一个终端窗口并执行如下命令:
cd&/usr/local/opt/mongodb/;&mongod&
&译者注:这里可能会发生错误 ERROR: dbpath (/data/db) does not exist.,试试先创建一个自定义路径,再用 mongod --dbpath '~/somepath' 来启动服务器。
这就能启动一个 MongoDB 守护服务器。
现在 MongoDB 已经启动,运行在默认端口 27017 上。
虽然 MongoDB 驱动器提供了数据库连接,但它依然需要被连接到服务器以便转换传入的 HTTP 请求为适当的数据库命令。
创建一个 MongoDB 集合驱动器(Collection Driver)
还记得你之前实现的 /:a/:b/:c 路由吗?如果你可以使用这个模式去查找数据库实体如何?
既然 MongoDB 文档被组织为集合,那么路由就可以很简单如: /:collection/:entity ,这能让你以超级 fashion 的 RESTful 的方式使用一个简单的地址系统去访问对象。
干掉你的 Node 实例并在终端执行下列命令:
edit&collectionDriver.js&
添加如下代码到 collectionDriver.js :
var&ObjectID&=&require('mongodb').ObjectID;&
这一行引入了各个需要的包;在本例中,是来自 MongoDB 包的 ObjectID 。
注意:如果你比较熟悉传统数据库,你可能明白术语&主键&。MongoDB有类似的概念:默认来说,新实体都会被分配一个唯一的 _id 字段,其类型为 ObjectID ,这是 MongoDB 用来优化查找和插入的。因为 ObjectID 是一个 BSON 类型而不是 JSON 类型,你必须转换任何传入的字符串为 ObjectID ,如果它们用于和一个 _id 字段进行比较。
添加如下代码到 collectionDriver.js 刚才那行后面:
CollectionDriver&=&function(db)&{&&&&this.db&=&&};&
这个函数定义了 CollectionDriver 构造器方法;它存储一个 MongoDB 客户端实例以便之后使用。在 JavaScript 中, this 是当前上下文的引用,就像 Objective-C 中的 self 。
继续添加如下代码当刚刚添加的代码块下面:
CollectionDriver.prototype.getCollection&=&function(collectionName,&callback)&{&&&&this.db.collection(collectionName,&function(error,&the_collection)&{&&&&&&if(&error&)&callback(error);&&&&&&else&callback(null,&the_collection);&&&&});&&};&
这一段定义了一个帮助方法 getCollection 以便通过名字去获取一个 Mongo 集合。你通过添加函数到 prototype 定义了类方法。
db.collection(name,callback) 获取集合对象并返回集合&&或一个错误&&给回调。
继续添加如下代码到刚才添加的代码块下面:
CollectionDriver.prototype.findAll&=&function(collectionName,&callback)&{&&&&&&this.getCollection(collectionName,&function(error,&the_collection)&{&&&&&&&&&if(&error&)&callback(error);&&&&&&&&else&{&&&&&&&&&&the_collection.find().toArray(function(error,&results)&{&&&&&&&&&&&&&if(&error&)&callback(error);&&&&&&&&&&&&else&callback(null,&results);&&&&&&&&&&});&&&&&&&&}&&&&&&});&&};&
A 行的 CollectionDriver.prototype.findAll 获取集合,如果没有如不能访问 MongoDB 服务器这样的错误,它就调用 B 行的 find() 。这将返回所有找到的对象。
find() 返回一个数据游标(data cursor),它可用于遍历匹配对象。find() 同样能接受一个选择器对象来过滤结果。 toArray() 组织所有的结果为一个数组并将其传递给回调。最后回调返回给调用者一个找到的对象的数组或者一个错误。
继续添加如下代码到刚才添加的代码块下面:
CollectionDriver.prototype.get&=&function(collectionName,&id,&callback)&{&&&&&&&this.getCollection(collectionName,&function(error,&the_collection)&{&&&&&&&&&&if&(error)&callback(error);&&&&&&&&&&else&{&&&&&&&&&&&&&&var&checkForHexRegExp&=&new&RegExp(&^[0-9a-fA-F]{24}$&);&&&&&&&&&&&&&&&if&(!checkForHexRegExp.test(id))&callback({error:&&invalid&id&});&&&&&&&&&&&&&&else&the_collection.findOne({'_id':ObjectID(id)},&function(error,doc)&{&&&&&&&&&&&&&&&&&&&if&(error)&callback(error);&&&&&&&&&&&&&&&&&&else&callback(null,&doc);&&&&&&&&&&&&&&});&&&&&&&&&&}&&&&&&});&&};&
在 A 行, CollectionDriver.prototype.get 使用 _id 从一个集合中获取单个条目。类似于 prototype.findAll 方法,这个调用首先获取一个集合对象然后在返回的对象上执行一个 findOne 。因为这匹配 _id 字段,本例中的一个 find() 或 findOne() 将会使用正确的来匹配它。
MongoDB 存储 _id 字段为 BSON 类型 ObjectID 。在上面的 C 行,ObjectID() 接受一个字符串并将其转换为一个 BSON ObjectID 去匹配集合。然而,ObjectID() 很小气,需要适当的十六进制字符串否则它会返回一个错误:因此,B 行会先用正则作检查。
这不能保证有一个与 _id 匹配的对象,但它保证 ObjectID 能够传递字符串。选择器 {'_id':ObjectID(id)} 使用提供的 id 匹配 _id 字段。
注意:从一个不存在的集合或实体中读取不是一个错误&& MongoDB 驱动器只会返回一个空的容器。
继续添加如下代码到刚才添加的代码块下面:
exports.CollectionDriver&=&CollectionD&
这一行定义或暴露实体用于其他应用程序,它们以一个需求模块列在 collectionDriver.js 中。
保存你的修改&&你完成了这个文件!现在你需要一个方式去调用这个文件。
使用你的集合驱动器
要调用你的 collectionDriver ,首先添加下面一行到 package.json 中的 dependencies 内:
&mongodb&:&1.3.23&&
在终端执行下列命令:
npm&update&
这将下载并安装 MongoDB 包。
在终端执行下列命令:
edit&views/data.jade&
现在添加下列代码到 data.jade 中,注意缩进层级:
body&&&&&&h1=&collection&&&&&&#objects&&&&&&&&&&table(border=1)&&&&&&&&&&&&if&objects.length&&&0&&&&&&&&&&&&&&&&-&each&val,&key&in&objects[0]&&&&&&&&&&&&&&&&&&&&th=&key&&&&&&&&&&&&&-&each&obj&in&objects&&&&&&&&&&&&&&tr.obj&&&&&&&&&&&&&&&&-&each&val,&key&in&obj&&&&&&&&&&&&&&&&&&td.key=&val&
这个模版渲染一个集合到一个 HTML 表格中,使其对人类可读。
添加下列代码到 index.js ,就在 path = require('path') 那行下面:
MongoClient&=&require('mongodb').MongoClient,&&Server&=&require('mongodb').Server,&&CollectionDriver&=&require('./collectionDriver').CollectionD&
这里你包含了来自 MongoDB 模块的 MongoClient 和 Server 对象以及你新创建的 CollectionDriver 。
添加下列代码到 index.js ,就在最后一行 app.set 的下面:
var&mongoHost&=&'localHost';&&var&mongoPort&=&27017;&&var&collectionD&&&var&mongoClient&=&new&MongoClient(new&Server(mongoHost,&mongoPort));&&&mongoClient.open(function(err,&mongoClient)&{&&&&&if&(!mongoClient)&{&&&&&&&&console.error(&Error!&Exiting...&Must&start&MongoDB&first&);&&&&&&&&process.exit(1);&&&&&}&&&&var&db&=&mongoClient.db(&MyDatabase&);&&&&&&collectionDriver&=&new&CollectionDriver(db);&&&});&
上面 A 行假设 MongoDB 实例是本地运行在端口 27017 。如果你已经在其他地方运行过 MongoDB 服务器,那你就需要修改这些值,但在本教程中就留下它们吧。
B 行创建了一个新的 MongoClient 并调用 C 行的 open 试图建立一个连接。如果你的连接尝试失败了,那很可能是你还没有启动你的 MongoDB 服务器。在无连接的情况下,应用将在 D 行退出。
如果客户端连接成功,它就在 E 行打开 MyDatabase 数据库。一个 MongoDB 实例可以包含多个数据库,每一个都有唯一的命名空间和唯一的数据。最后,你在 F 行创建 CollectionDriver 对象并传递一个处理器给 MongoDB 客户端。
用下列语句替换 index.js 中的头两行 app.get 调用:
app.get('/:collection',&function(req,&res)&{&&&&&&var&params&=&req.&&&&&&collectionDriver.findAll(req.params.collection,&function(error,&objs)&{&&&&&&&&&&&&&if&(error)&{&res.send(400,&error);&}&&&&&&&&&&&&&else&{&&&&&&&&&&&&&&&&&if&(req.accepts('html'))&{&&&&&&&&&&&&&&&&&&&&&res.render('data',{objects:&objs,&collection:&req.params.collection});&&&&&&&&&&&&&&&&&}&else&{&&&&&&&&&&&&&&&&res.set('Content-Type','application/json');&&&&&&&&&&&&&&&&&&&&&res.send(200,&objs);&&&&&&&&&&&&&&&&&}&&&&&&&&&&&}&&&&&&});&&});&&&app.get('/:collection/:entity',&function(req,&res)&{&&&&&&var&params&=&req.&&&&&var&entity&=&params.&&&&&var&collection&=&params.&&&&&if&(entity)&{&&&&&&&&&collectionDriver.get(collection,&entity,&function(error,&objs)&{&&&&&&&&&&&&&if&(error)&{&res.send(400,&error);&}&&&&&&&&&&&&else&{&res.send(200,&objs);&}&&&&&&&&&&});&&&&&}&else&{&&&&&&&&res.send(400,&{error:&'bad&url',&url:&req.url});&&&&&}&&});&
这就创建了两个新路由 /:collection 和 /:collection/:entity 。它们分别调用 collectionDriver.findAll 和 collectionDriver.get 方法并返回 JSON 对象、HTML 文档或一个错误。
当你在 Express 中 定义 /collection ,它将明确匹配 &collection& 。然而,如果你定义如 A 行的路由 /:collection 那么它将匹配任何存储在 B 行的 req.params 集合中的第一层路径。在本例中,你使用 C 行的 CollectionDriver 的 findAll 定义的端点去匹配任何到 MongoDB 的 URL 。
如果查询成功,那么代码会在 E 行的头中检查,是否请求会接受一个 HTML 结果。如果是,那 F 行就从 data.jade 模版存储渲染过的 HTML 到应答中。这将简单地呈现集合内容到一个 HTML 表格中。
默认情况下,Web 浏览器会在它们的请求中指定它们接受 HTML 。当其他类型的客户端请求这个端点,例如 iOS 应用使用 NSURLSession ,这个方法就会在 G 行返回一个机器可读的 JSON 文档。 与 H 行, res.send() 会返回由集合驱动器生成的 JSON 文档和一个成功码。
这个例子中,对于两层 URL 指定的位置, I 行将其作为集合名和实体 _id 对待。之后你在 J 行使用 collectionDriver 的 get() 请求特定的实体。如果那个实体被找到,你就在 K 行将其作为 JSON 文档返回。
保存你的工作,重启你的 Node 实例, 检查你的 mongod 守护进程是否依然在运行,然后将浏览器指向 ;你将看到如下页面:
怎么什么都没有?发生了什么事?
哦,等等&&那是因为你还没有添加任何数据呢。是时候了!
与数据同行
从一个空空如也的数据库里读取对象一点儿也不有趣。要测试功能,就要有一个添加实体到数据库的途径。
在 CollectionDriver.js 中添加下列新的原型方法,就在 exports.CollectionDriver 行之前:
&&CollectionDriver.prototype.save&=&function(collectionName,&obj,&callback)&{&&&&&&this.getCollection(collectionName,&function(error,&the_collection)&{&&&&&&&&&if(&error&)&callback(error)&&&&&&&&else&{&&&&&&&&&&obj.created_at&=&new&Date();&&&&&&&&&&&the_collection.insert(obj,&function()&{&&&&&&&&&&&&&callback(null,&obj);&&&&&&&&&&});&&&&&&&&}&&&&&&});&&};&
就像 findAll 和 get ,A 行的 save 首先检索集合对象。之后回调取得提供的实体并再添加一个字段记录创建的日期(如 B 行所示)。最后,你在 C 行插入修改后的对象到集合里。 insert 同时会自动添加一个 _id 。
添加下列代码到 index.js ,就在刚才添加的 get 方法之后:
app.post('/:collection',&function(req,&res)&{&&&&&&&var&object&=&req.&&&&&&var&collection&=&req.params.&&&&&&collectionDriver.save(collection,&object,&function(err,docs)&{&&&&&&&&&&&&if&(err)&{&res.send(400,&err);&}&&&&&&&&&&&&&else&{&res.send(201,&docs);&}&&&&&&&&});&&});&
这就在 A 行为 POST 动词创建了一个新的路由,它通过调用你刚刚添加到你的驱动器里的 save() 将 Body 当作一个对象插入到指定的集合里。当资源被创建后,B 行就返回 HTTP 201 成功码。
只有最后一块了。添加下列代码到 index.js ,就在 app.set 行后面,但在 app.use 或 app.get 行之前:
app.use(express.bodyParser());&
这会告诉 Express 去解析传入的 Body 数据;如果它是 JSON,那么用它创建一个 JSON 对象。通过将这个调用提前,Body 解析将在其他路由处理器之前调用。这样 req.body 就能直接作为 JavaScript 对象传递给驱动器。
再次重启你的 Node 实例,在终端里执行下列命令,插入一个测试对象到你的数据库:
curl&-H&&Content-Type:&application/json&&-X&POST&-d&'{&title&:&Hello&World&}'&http:&
你会在控制台看到记录的返回信息,如下所示:
现在转到你的浏览器,并重新加载 ;你就会在表格中看到你插入的项目。
更新与删除数据
你已经实现了 CRUD 中的 Create 和 Read 操作&&还剩下 Update 和 Delete 。这些都比较简单,遵循与其他两个一样的模式。
添加下列代码到 CollectionDriver.js,就在 exports.CollectionDriver 行之前:
&&CollectionDriver.prototype.update&=&function(collectionName,&obj,&entityId,&callback)&{&&&&&&this.getCollection(collectionName,&function(error,&the_collection)&{&&&&&&&&&&if&(error)&callback(error);&&&&&&&&&&else&{&&&&&&&&&&&&&&obj._id&=&ObjectID(entityId);&&&&&&&&&&&&&&&obj.updated_at&=&new&Date();&&&&&&&&&&&&&&&the_collection.save(obj,&function(error,doc)&{&&&&&&&&&&&&&&&&&&&if&(error)&callback(error);&&&&&&&&&&&&&&&&&&else&callback(null,&obj);&&&&&&&&&&&&&&});&&&&&&&&&&}&&&&&&});&&};&
update() 函数接受一个对象,并在 C 行使用 collectionDriver 的 save() 方法在集合中更新它。这假设 Body 的 _id 与 A 行指定的路由一样。B 行添加一个 updated_at 字段作为对象更新时间。添加一个修改时间戳是一个好主意,有助于理解数据在你的应用程序的生命周期里是如何改变的。
注意这个更新用新对象操作取代了之前的对象&&这里并没有属性级别的更新支持。
添加下列代码到 CollectionDriver.js,就在 exports.CollectionDriver 行之前:
&&CollectionDriver.prototype.delete&=&function(collectionName,&entityId,&callback)&{&&&&&&this.getCollection(collectionName,&function(error,&the_collection)&{&&&&&&&&&&&if&(error)&callback(error);&&&&&&&&&&else&{&&&&&&&&&&&&&&the_collection.remove({'_id':ObjectID(entityId)},&function(error,doc)&{&&&&&&&&&&&&&&&&&&&if&(error)&callback(error);&&&&&&&&&&&&&&&&&&else&callback(null,&doc);&&&&&&&&&&&&&&});&&&&&&&&&&}&&&&&&});&&};&
delete() 与其他 CRUD 一样的操作。 在 A 行,它获取集合对象,然后在 B 行用提供的 id 调用 remove() 。
现在你需要两个新的路由来处理这些操作。幸运的是,PUT 和 DELETE 动词已经存在,所以你可以用与 GET 一样的语义创建处理器。
添加如下代码到 index.js ,就在 app.post() 调用之后:
app.put('/:collection/:entity',&function(req,&res)&{&&&&&&&var&params&=&req.&&&&&&var&entity&=&params.&&&&&&var&collection&=&params.&&&&&&if&(entity)&{&&&&&&&&&collectionDriver.update(collection,&req.body,&entity,&function(error,&objs)&{&&&&&&&&&&&&&if&(error)&{&res.send(400,&error);&}&&&&&&&&&&&&else&{&res.send(200,&objs);&}&&&&&&&&&&});&&&&&}&else&{&&&&&&&&&var&error&=&{&&message&&:&&Cannot&PUT&a&whole&collection&&};&&&&&&&&&res.send(400,&error);&&&&&}&&});&
这个 put 回调遵循同单实体 get 一样的模式:你在集合上匹配名字和 _id ,如 A 行所示。和 post 路由一样, 在 B 行 put 传递来自 Body 的 JSON 对象到 collectionDriver 里新写的 update() 方法中。
更新的对象将在应答中返回(C 行),所以客户端可以解析到任何服务器更新的字段,例如 updated_at 。
添加如下代码到 index.js ,就在刚添加的 put 方法之后:
app.delete('/:collection/:entity',&function(req,&res)&{&&&&&&&var&params&=&req.&&&&&&var&entity&=&params.&&&&&&var&collection&=&params.&&&&&&if&(entity)&{&&&&&&&&&collectionDriver.delete(collection,&entity,&function(error,&objs)&{&&&&&&&&&&&&&if&(error)&{&res.send(400,&error);&}&&&&&&&&&&&&else&{&res.send(200,&objs);&}&&&&&&&&&&});&&&&&}&else&{&&&&&&&&&var&error&=&{&&message&&:&&Cannot&DELETE&a&whole&collection&&};&&&&&&&&&res.send(400,&error);&&&&&}&&});&
delete 端点非常类似于 put,如 A 行所示,除了 delete 不需要一个 Body。在 B 行,你传递参数给 collectionDriver 里的 delete() 方法,如果删除操作成功,那么你就在 C 行返回一个原始对象和一个 200 应答码。
如果上述操作中发生任何错误,你就返回一个适当的错误码。
保存你的工作,并重启你的 Node 实例。
在终端执行下列命令,替换 {_id} 为上一个 POST 调用的返回值:
curl&-H&&Content-Type:&application/json&&-X&PUT&-d&'{&title&:&Good&Golly&Miss&Molly&}'&http://localhost:3000/items/{_id}&
你会在终端看到如下应答:&
转到浏览器,重新载入
;你会在表格中看到你修改的条目:&
在终端里执行下列命令以删除你的记录:
curl&-H&&Content-Type:&application/json&&-X&DELETE&&http:&
你会看到 curl 收到的响应:&
重新载入 ,我能确定,你的实体不见了。&
就这样,你使用 Node.js、Express 以及 MongoDB 完成了你的整个 CRUD 模型!
下一步怎么走?
这里是完成的,它包含有上面教程里所有的代码。
你的服务器现在准备好应对客户端的连接并开始传输数据。在本教程的下一部分里,你将构建一个 iOS 应用来连接你的新服务器,并利用一些 MongoDB 和 Express 的炫酷特性。
关于 MongoDB 的更多信息,看看 。
CocoaChina是全球最大的苹果开发中文社区,官方微信每日定时推送各种精彩的研发教程资源和工具,介绍app推广营销经验,最新企业招聘和外包信息,以及Cocos2d引擎、Cocostudio开发工具包的最新动态及培训信息。关注微信可以第一时间了解最新产品和服务动态,微信在手,天下我有!
请搜索微信号&CocoaChina&关注我们!
CocoaChina是全球最大的苹果开发中文社区,官方微信每日定时推送各种精彩的研发教程资源和工具,介绍app推广营销经验,最新企业招聘和外包信息,以及Cocos2d引擎、Cocos Studio开发工具包的最新动态及培训信息。关注微信可以第一时间了解最新产品和服务动态,微信在手,天下我有!
请搜索微信号“CocoaChina”关注我们!
关注微信 每日推荐
扫一扫 浏览移动版

我要回帖

更多关于 qq三国js副本子女 的文章

 

随机推荐