当前位置: 首页 > 编程日记 > 正文

IAP超级详解,偷懒了,不用自己去翻译了

转载自:http://gaohaijun.blog.163.com/blog/static/176698271201143194018328/

一、In App Purchase概览

Store Kit代表App和App Store之间进行通信。程序将从App Store接收那些你想要提供的产品的信息,并将它们显示出来供用户购买。
当用户需要购买某件产品时,程序调用StoreKit来收集购买信息。

Store Kit的API只是为程序添加In App Purchase功能的一小部分。你需要决定如何去记录那些你想要提交的产品,如何在程序中将商店功能展现给用户,
还要考虑如何将用户购买的产品提交。本章的剩余部分会展示整个流程。

Products
产品可以是任意一项你想要出售的特性。产品在iTunes Connect中被组织,这和你添加一个新的App是一样的。支持的产品种类共有四种:
1. 内容型。包括电子书,电子杂志,照片,插图,游戏关卡,游戏角色,和其他的数字内容。
2. 扩展功能。这些功能已经包含在App内部。在未购买之前被锁定。例如,你可以在一个游戏程序中包含若干个小游戏,用户可以分别来购买这些游戏。
3. 服务。允许程序对单次服务收费。比如录音服务。
4. 订阅。支持对内容或服务的扩展访问。例如,你的程序可以每周提供财务信息或游戏门户网站的信息。应该设定一个合理的更新周期,以避免过于频繁的
提示困扰用户。要记住:你将负责跟踪订阅的过期信息,并且管理续费。App Store不会替你监视订阅的周期,也不提供自动收费的机制。

In App Purchase为创建产品提供了一种通用的机制,如何操作将由你负责。当你设计程序的时候,有以下几点需要注意:

1. 你必须提供电子类产品和服务。不要使用In App Purchase 去出售实物和实际服务。
2. 不能提供代表中介货币的物品,因为让用户知晓他们购买的商品和服务是很重要的。


通过App Store注册产品

每个你想要出售的产品都必须先通过iTunes Connect在App Store注册。你需提供产品的名称,描述,价格和其他在程序中用到的元数据。

需为产品指定唯一的标识符。当你的程序利用Store Kit和App Store通信时,会使用产品标识来取回产品的信息。如果用户购买某个商品时,程序可以用该标识来将产品标注为“已购买”。

App Store将前面提到过的产品种类简化为以下三种:

1. 消耗性商品。 该类商品在需要时被单次购买。比如,单次服务。
2. 非消耗性商品。 该类商品只需被某个用户购买一次,一旦被购买,和该用户iTunes 账户关联的设备都可以使用此商品。Store Kit为在多个设备上重新存储非消耗性商品提供了内置的支持。
3. 订阅类。订阅类商品拥有以上两种类型的特性。和消耗性商品一样,订阅类商品可以被多次购买; 你可以在程序内部加入自己的订阅计划更新机制。 另外,订阅类商品必须提供给和某一用户关联的所有设备。In App Purchase期望订阅类商品可以通过外部服务器交付。你必须为多个设备的订阅服务提供相应的支持。

关于注册产品的详细信息,请参考iTunes Connect Developer Guide文档。


交付方式

交付机制在程序In App Purchase的设计和实现种有很重要的意义。有两种基本的模型可以用来交付产品:内置类型(Built-in model)和服务器类型(Server model)。 不管使用那种模型,你都需要维护产品列表,并保证当用户购买后,成功的交付产品。

1. 内置产品类型
使用这种模型。 需要交付的产品已经在程序内部。 这种方式通常用在一些被锁定的功能上。 也可以用来交付在程序束(App Bundle)中的内容。 该方式的一个重要的优点是你可以及时的给客户交付产品,大多数的内置产品应为非消耗性商品。

注意:In App Purchase不提供购买补丁的功能。 如果需要更改app的bundle,你必须向App Store提交新的app版本。

为了标识产品,程序要在bundle中存储产品的标识符。内置模式下,Apple建议使用plist来纪录产品的标识符。 内容类应用可以使用折衷方式很方便的添加新的内容,而不改动程序本身。(原话为: Content-driven applications can use this to add new content without modifying the source for your application,不是很懂,感觉应该是说类似是用plist来管理产品列表,因此就不需要在添加新产品的时候改动程序了。再议。。。)

当成功购买产品后,程序应将锁定的功能解锁,提供给用户。 解锁的最简单方式是修改程序偏好设置(Application Preferences)。 当用户备份手机数据的时候,程序偏好设置也会随之备份。 程序可能需要建议用户在购买产品后备份手机以免丢失购买的内容。

图1-2显示了交付内置型产品的流程。

1. 程序通过bundle存储的plist文件得到产品标识符的列表。
2. 程序向App Store发送请求,得到产品的信息。
3. App Store返回产品信息。
4. 程序把返回的产品信息显示给用户(App的store界面)
5. 用户选择某个产品
6. 程序向App Store发送支付请求
7. App Store处理支付请求并返回交易完成信息。
8. App获取信息并提供内容给用户。

IAP超级详解,偷懒了,不用自己去翻译了 - papa_ln - papa

2. 服务器类型
使用这种方式,要提供另外的服务器将产品发送给程序。 服务器交付适用于订阅、内容类商品和服务,因为商品可以作为数据发送,而不需改动程序束。 例如,一个游戏提供的新的内容(关卡等)。 Store Kit不会对服务器端的设计和交互做出定义,这方面工作需要你来完成。 而且,Store Kit不提供验证用户身份的机制,你需要来设计。 如果你的程序需要以上功能,例如,纪录特定用户的订阅计划, 你需要自己来设计和实现。

图1-3 展示了服务器类型的购买过程。


IAP超级详解,偷懒了,不用自己去翻译了 - papa_ln - papa
 
1. 程序向服务器发送请求,获得一份产品列表。
2. 服务器返回包含产品标识符的列表。
3. 程序向App Store发送请求,得到产品的信息。
4. App Store返回产品信息。
5. 程序把返回的产品信息显示给用户(App的store界面)
6. 用户选择某个产品
7. 程序向App Store发送支付请求
8. App Store处理支付请求并返回交易完成信息。
9. 程序从信息中获得数据,并发送至服务器。
10. 服务器纪录数据,并进行审(我们的)查。
11. 服务器将数据发给App Store来验证该交易的有效性。
12. App Store对收到的数据进行解析,返回该数据和说明其是否有效的标识。
13. 服务器读取返回的数据,确定用户购买的内容。
14. 服务器将购买的内容传递给程序。

Apple建议在服务器端存储产品标识,而不要将其存储在plist中。 这样就可以在不升级程序的前提下添加新的产品。

在服务器模式下, 你的程序将获得交易(transaction)相关的信息,并将它发送给服务器。服务器可以验证收到的数据,并将其解码以确定需要交付的内容。 这个流程将在“验证store收据”一节讨论。

对于服务器模式,我们有安全性和可靠性方面的顾虑。 你应该测试整个环境来避免威胁。《Secure Coding Guide》文档中有相关的提示说明。

虽然非消耗性商品可以用内置模式来恢复,订阅类商品必须通过服务器来恢复。你要负责纪录订阅信息、恢复数据。 
消耗类商品也可以通过服务器方式来纪录。例如,由服务器提供的一项服务, 你可能需要用户在多个设备上重新获得结果。

取得产品信息

要在程序内部显示“商店”,需要从App Store得到信息来购建界面。 本章详细讲解如何从App Store获取产品信息。

向App Store发送请求

Store Kit提供了从App Store上请求数据的通用机制。 程序可以创建并初始化一个request对象, 为其附加delegate, 然后启动请求过程。请求将被发送到App Store,在那里被处理。 处理完成时, request对象的delegate方法将被异步调用,以获得请求的结果。 图2-1显示了请求的数据模型。

IAP超级详解,偷懒了,不用自己去翻译了 - papa_ln - papa
 
如果程序在请求期间退出,则需要重新发送请求。

下面讲解请求过程中用到的类:

SKRequest
SKRequest为request的抽象根类。

SKRequestDelegate
SKRequestDelegate是一个protocol, 实现用以处理请求结果的方法,比如请求成功,或请求失败。

发送获得产品信息的请求
程序使用products request来获得产品的信息。 要完成这一过程,程序需创建一个request对象,其中会包含一个产品标识的列表。之前提到过,你的程序既可以内置产品列表,又可以通过外部服务器来获得。

当发送请求时,产品标识会传送到App Store,App Store将会返回本地化信息(这些信息事先已经在iTunes Connect中设置好了),你将使用这些信息来购建内置商店的界面(显示商品名,描述,等等)。 图2-2显示了请求的过程。

IAP超级详解,偷懒了,不用自己去翻译了 - papa_ln - papa
 
SKProductsRequest
用来请求商品的信息。 创建时,我们将需要显示的商品列表加入该对象。

SKProductsRequestDelegate
该protocol定义了处理App Store响应的方法。 

SKProductsResponse
SKProductsResponse对象为App Store返回的响应信息。里面包含两个列表(当然是NSArray了):一是经过验证有效的商品,
@property(nonatomic, readonly) NSArray *products
另外一个是无法被识别的商品信息:
@property(nonatomic, readonly) NSArray * invalidProductIdentifiers
有几种原因将造成商品标识无法被识别,如拼写错误(当然),被标记为不可出售(unavailable for sale),或是对商品信息的改变没有传送到所***** Store的服务器。(这个原因不是很清楚,再议)。

SKProduct
SKProduct对象包含了在App Store上注册的商品的本地化信息。

购买商品
当用户准备购买商品时,程序向App Store请求支付信息,然后App Store将会创建持久化的交易信息,并继续处理支付流程,即使用户重启程序,这个过程亦是如此。App Store同步待定交易的列表到程序中,并在交易状态发生改变时向程序发送更新的数据。

收集支付信息

要收集支付信息, 你的程序可以创建一个payment的对象,将它放到支付队列中,如图3-1所示。

IAP超级详解,偷懒了,不用自己去翻译了 - papa_ln - papa
 
1. 一个SKPayment的对象,包含了"Sword"的商品标识,并且制定购买数量为1。
2. 使用addPayment:方法将SKPayment的对象添加到SKPaymentQueue里。
3. SKPaymentmentQueue包含的所有请求商品,
4. 使用SKPaymentTransactionObserver的paymentQueue: updatedTransactions: 方法来检测所有完成的购买,并发送购买的商品。
5. 最后,使用finishTransaction:方法完成交易。

当payment的对象被添加到支付队列中的时候, 会创建一个持久保存的transaction对象来存放它。 当支付被处理后,transaction被更新。 程序中将实现一个观察者(observer)对象来获取transaction更新的消息。 观察者应该为用户提供购买的商品,然后将transaction从队列中移除。

下面介绍在购买过程中用到的几个类:
SKPayment
要收集支付信息,先要了解一下支付对象。 支付对象包含了商品的标识(identifier)和要购买商品的数量(quantity)(数量可选)。你可以把同一个支付对象重复放入支付队列,,每一次这样的动作都相当于一次独立的支付请求。

用户可以在Settings程序中禁用购买的功能。 因此在请求支付之前,程序应该首先检查支付是否可以被处理。 调用SKPaymentQueue的canMakePayments方法来检查。

SKPaymentQueue
支付队列用以和App Store之间进行通信。 当新的支付对象被添加到队列中的时候, Store Kit向App Store发送请求。 Store Kit将会弹出对话框询问用户是否确定购买。 完成的交易将会返回给程序的observer对象。

SKPaymentTransaction
transaction对象在每次添加新的payment到队列中的时候被创建。 transaction对象包含了一些属性,可以让程序确定当前的交易状态。

程序可以从支付队列那里得到一份审核中的交易列表,但更常用的做法还是等待支付队列告知交易状态的更新。

SKPaymentTransactionObserver
在程序中实现SKPaymentTransactionObserver的协议,然后把它作为SKPaymentQueue对象的观察者。该观察者的主要职责是:检查完成的交易,交付购买的内容,和把完成后的交易对象从队列中移除。

在程序一启动,就应该为支付队列指定对应的观察者对象,而不是等到用户想要购买商品的时候。 Transaction对象在程序退出时不会丢失。程序重启时, Store Kit继续执行未完成的交易。 在程序初始化的时候添加观察者对象,可以保证所有的交易都被程序接收(也就时说,如果有未完成的transaction,如果程序重启,就重新开始了,如果稍候再添加观察者,就可能会漏掉部分交易的信息)。


恢复交易信息(Transactions)
当transaction被处理并从队列移除之后,正常情况下,程序就再也看不到它们了。 如果你的程序提供的是非消耗性的或是订阅类的商品,就必须提供restore的功能,使用户可以在其他设备上重新存储购买信息。

Store Kit提供内建的功能来重新存储非消耗商品的交易信息。 调用SKPaymentQueue的restoreCompletedTransactions的方法来重新存储。对于那些之前已经完成交易的非消耗性商品,Apple Store生成新的,用于恢复的交易信息。 它包含了原始的交易信息。你的程序可以拿到这个信息,然后继续为购买的功能解锁。 当之前所有的交易都被恢复时, 就会调用观察者对象的paymentQueueRestoreCompletedTransactionsFinished方法。

如果用户试图购买已经买过的非消耗性商品,程序会收到一个常规的交易信息,而不是恢复的交易信息。但是用户不会被再次收费。程序 应把这类交易和原始的交易同等对待。

订阅类服务和消耗类商品不会被Store Kit自动恢复。 要恢复这些商品,你必须在用户购买这些商品时,在你自己的服务器上记录这些交易信息, 并且为用户的设备提供恢复交易信息的机制。

在程序中添加Store功能
本章为添加购买功能的指导

详细流程:

准备工作当然是添加StoreKit.framework了。
然后是具体的步骤:

1. 决定在程序内出售的商品的类型。
之前提到过,程序内可以出售的新feature类型是有限制的。 Store Kit不允许我们下载新的代码。 你的商品要么可以通过当前的代码工作(bundle类型),要么可以通过服务器下载(当然,这里下载的为数据文件,代码是不可以的)。 如果要修改源代码,就只能老实的升级了。

2. 通过iTunes Connect注册商品
每次添加新商品的时候都需要执行这一步骤。 每个商品都需要一个唯一的商品标识。 App Store通过这个标识来查找商品信息并处理支付流程。 注册商品标识的方法和注册程序的方法类似。

要了解如何创建和注册商品信息,请参考“iTunes Connect Developer Guide”文档。

3. 检测是否可以进行支付
用户可以禁用在程序内部支付的功能。在发送支付请求之前,程序应该检查该功能是否被开启。程序可在显示商店界面之前就检查该设置(没启用就不显示商店界面了),也可以在用户发送支付请求前再检查,这样用户就可以看到可购买的商品列表了。

例子:
if([SKPaymentQueue canMakePayments])
{
    ...//Display a store to the user
}
else
{
    ...//Warn the user that purchases are disabled.
}

4. 获得商品的信息
程序创建SKProductsRequest对象,用想要出售的商品的标识来初始化, 然后附加上对应的委托对象。 该请求的响应包含了可用商品的本地化信息。

//这里发送请求
- (void)requestProductData
{
    SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:
    [NSSet setWithObject: kMyFeatureIdentifier]];
    
    request.delegate = self;
    [request start];
}

//这个是响应的delegate方法
- (void)productsRequest: (SKProductsRequest *)request
didReceiveResponse: (SKProductsResponse *)response
{
    NSArray *myProduct = response.products;

    //生成商店的UI
    [request autorelease];
}

5. 添加一个展示商品的界面
Store Kit不提供界面的类。 这个界面需要我们自己来设计并实现。

6. 为支付队列(payment queue)注册一个观察者对象
你的程序需要初始化一个transaction observer对象并把它指定为payment queue的观察者。

上代码:

MyStoreObserver *observer = [[MyStoreObserver alloc]init];
[[SKPaymentQueue defaultQueue]addTransactionObserver: observer];

应该在程序启动的时候就添加好观察者,原因前面说过,重启后程序会继续上次未完的交易,这时就添加观察者对象就不会漏掉之前的交易信息。

7. 在MyStoreObserver类中执行paymentQueue: updatedTransactions: 方法。
这个方法会在有新的交易被创建,或者交易被更新的时候被调用。

- (void)paymentQueue: (SKPaymentQueue *)queue updatedTransactions: (NSArray *)transactions
{
    for(SKPaymentTransaction * transaction in transactions)
    {
        switch(transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased:
                [self completeTransaction: transaction];
                break;
            case SKPaymentTransactionStateFailed:
                [self failedTransaction: transaction];
                break;
            case SKPaymentTransactionStateRestored:
                [self restoreTransaction: transaction];
            default:
                break;
        }
    }
}

上面的函数针对不同的交易返回状态,调用对应的处理函数。

8. 观察者对象在用户成功购买一件商品时,提供相应的内容,以下是在交易成功后调用的方法
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
    //你的程序需要实现这两个方法
    [self recordTransaction: transaction];
    [self provideContent: transaction.payment.productIdentifier];
    
    //将完成后的交易信息移出队列
    [[SKPaymentQueue defaultQueue]finishTransaction: transaction];
}

交易成功的信息包含transactionIdentifier和transactionReceipt的属性。其中,transactionReceipt记录了支付的详细信息,这个信息可以帮助你跟踪、审(我们的)查交易,如果你的程序是用服务器来交付内容,transactionReceipt可以被传送到服务器,然后通过App Store验证交易。(之前提到的server模式,可以参考以前的图)

9. 如果交易是恢复过来的(restore),我们用这个方法来处理:
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
    [self recordTransaction: transaction];
    [self provideContent: transaction.payment.productIdentifier];

    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
这个过程完成购买的过程类似。 恢复的购买内容提供一个新的交易信息,这个信息包含了新的transaction的标识和receipt数据。 如果需要的话,你可以把这些信息单独保存下来,供追溯审(我们的)查之用。但更多的情况下,在交易完成时,你可能需要覆盖原始的transaction数据,并使用其中的商品标识。

10. 交易过程失败的话,我们调用如下的方法:
- (void)failedTransaction: (SKPaymentTransaction *)transaction
{
    if(transaction.error.code != SKErrorPaymentCancelled)
    {
        //在这类显示除用户取消之外的错误信息
    }

    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

通常情况下,交易失败的原因是取消购买商品的流程。 程序可以从error中读出交易失败的详细信息。

显示错误信息不是必须的,但在上面的处理方法中,需要将失败的交易从支付队列中移除。 一般来说,我们用一个对话框来显示错误信息,这时就应避免将用户取消购买这个error显示出来。

11. 组织好程序内“商店”的UI。当用户选择一件商品时, 创建一个支付对象,并放到队列中。
SKPayment *payment = [SKPayment paymentWithProductIdentifier: kMyFeatureIdentifier];
[[SKPaymentQueue defaultQueue] addPayment: payment];

如果你的商店支持选择同一件商品的数量,你可以设置支付对象的quantity属性
SKMutablePayment *payment = [SKMutablePayment paymentWithProductIdentifier: kMyFeatureIdentifier];
payment.quantity = 3;
[[SKPaymentQueue defaultQueue] addPayment: payment];


下一步:
本章中所示代码可用于内置型商品模式(Built-in)。 如果你的程序要使用服务器来发布商品,你需要负责设计和执行iPhone程序和你的服务器之间的通信。服务器应该验证数据并为程序提供内容。

验证store的收据

使用服务器来交付内容,我们还需要做些额外的工作来验证从Store Kit发送的收据信息。

重要信息:来自Store的收据信息的格式是专用的。 你的程序不应直接解析这类数据。可使用如下的机制来取出其中的信息。

验证App Store返回的收据信息
当交易完成时,Store Kit告知payment observer这个消息,并返回完成的transaction。 SKPaymentTransaction的transactionReceipt属性就包含了一个经过签名的收据信息,其中记录了交易的关键信息。你的服务器要负责提交收据信息来确定其有效性,并保证它未经过篡改。 这个过程中,信息被以JSON数据格式发送给App Store,App Store也以JSON的格式返回数据。
(大家可以先了解一下JSON的格式)

验证收据的过程:

1. 从transaction的transactionReceipt属性中得到收据的数据,并以base64方式编码。
2. 创建JSON对象,字典格式,单键值对,键名为"receipt-data", 值为上一步编码后的数据。效果为:
{
    "receipt-data"    : "(编码后的数据)"
}

3. 发送HTTP POST的请求,将数据发送到App Store,其地址为:
https://buy.itunes.apple.com/verfyReceipt

4. App Store的返回值也是一个JSON格式的对象,包含两个键值对, status和receipt:
{
    "status"    : 0,
    "receipt"    : { … }
}

如果status的值为0, 就说明该receipt为有效的。 否则就是无效的。

App Store的收据
发送给App Store的收据数据是通过对transaction中对应的信息编码而创建的。 当App Store验证收据时, 将从其中解码出数据,并以"receipt"的键返回。 返回的响应信息是JSON格式,被包含在SKPaymentTransaction的对象中(transactionReceipt属性)。Server可通过这些值来了解交易的详细信息。 Apple建议只发送receipt数据到服务器并使用receipt数据验证和获得交易详情。 因为App Store可验证收据信息,返回信息,保证信息不被篡改,这种方式比同时提交receipt和transaction的数据要安全。(这段得再看看)

表5-1为交易信息的所有键,很多的键都对应SKPaymentTransaction的属性。
备注:一些键取决于你的程序是链接到App Store还是测试用的Sandbox环境。更多关于sandbox的信息,请查看"Testing a Store"一章。

Table 5-1 购买信息的键:

键名        描述
quantity     购买商品的数量。对应SKPayment对象中的quantity属性
product_id    商品的标识,对应SKPayment对象的productIdentifier属性。
transaction_id        交易的标识,对应SKPaymentTransaction的transactionIdentifier属性
purchase_date    交易的日期,对应SKPaymentTransaction的transactionDate属性
original_-transaction_id    对于恢复的transaction对象,该键对应了原始的transaction标识
original_purchase_-date    对于恢复的transaction对象,该键对应了原始的交易日期
app_item_id    App Store用来标识程序的字符串。一个服务器可能需要支持多个server的支付功能,可以用这个标识来区分程序。链接sandbox用来测试的程序的不到这个值,因此该键不存在。
version_external_-identifier    用来标识程序修订数。该键在sandbox环境下不存在
bid    iPhone程序的bundle标识
bvrs    iPhone程序的版本号

测试Store功能
开发过程中,我们需要测试支付功能以保证其工作正常。然而,我们不希望在测试时对用户收费。 Apple提供了sandbox的环境供我们测试。

备注:Store Kit在模拟器上无法运行。 当在模拟器上运行Store Kit的时候,访问payment queue的动作会打出一条警告的log。测试store功能必须在真机上进行。

Sandbox环境
使用Sandbox环境的话,Store Kit并没有链接到真实的App Store,而是链接到专门的Sandbox环境。 SandBox的内容和App Store一致,只是它不执行真实的支付动作。 它会返回交易成功的信息。 Sandbox使用专门的iTunes Connect测试 账户。不能使用正式的iTunes Connect账户来测试。

要测试程序,需要创建一个专门的测试账户。你至少需要为程序的每个区域创建至少一个测试账户。详细信息,请查看iTunes Connect Developer Guide文档。

在Sandbox环境中测试
步骤:
1. 在测试的iPhone上退出iTunes账户
Settings中可能会记录之前登录的账户,进入并退出。

重要信息:不能在Settings 程序中通过测试账户登录。

2. 运行程序
当你在程序的store中购买商品后,Store kit提示你去验证交易。用测试账户登录,并批准支付。 这样虚拟的交易就完成了。

在Sandbox中验证收据
验证的URL不同了:
NSURL *sandboxStoreURL = [[NSURL alloc]initWithString: 
@"https://sandbox.itunes.apple.com/verifyReceipt"];

转载于:https://www.cnblogs.com/java20130723/archive/2012/09/30/3212089.html

相关文章:

linux负载均衡(什么是负载均衡)

linux负载均衡(什么是负载均衡) 一、总结 一句话总结: 建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。 关键点一&#xff1a…

win8数据源设置mysql_Win8系统ODBC数据源有何重要功能?

对计算机发展比较有研究的朋友一定会知道ODBC,它是一个比较古老的东西,发展到现在Win8系统上版本已经是3.8了。微软虽然没有对ODBC做很大的更新,但是正因为ODBC是一个比较成熟和古老的规范,因此它的作用才显得不那么突出&#xff…

【HTML/XML 11】XML和HTML的混合使用

导读:在前面介绍了很多关于XML和HTML的东西,他们其实各有各的好处,在很多时候都需要结合起来使用。现在已经有XML和HTML结合的产物:XHTML(可扩展超文本标记语言)。在本篇博客中,则主要介绍通过引…

web架构之mysql服务器

SQL概述结构化查询语言(Structured Query Language)简称SQL,是一种特殊目的的编程语言,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统;同时也是数据库脚本文件的扩展名。从上可以看出我们数据库…

ORM查询语言(OQL)简介--概念篇

相关文章内容索引: ORM查询语言(OQL)简介--概念篇ORM查询语言(OQL)简介--实例篇ORM查询语言(OQL)简介--高级篇:脱胎换骨ORM查询语言(OQL)简介--高级篇&#x…

mysql优化 top_Top 20+ MySQL Best Practices【sql优化】

Database operations often tend to be the main bottleneck for most webapplications today. It’s not only the DBA’s (database administrators)that have to worry about these performance issues. We as programmersneed to do our part by structuring tables proper…

hibernate分页

分页从网上考的&#xff0c;好用。这个框架 /** * 用于分页的工具类 * author 莫取网名 */ public class Pager<T> {private List<T> list; //对象记录结果集 private int total 0; // 总记录数 private int limit 10; // 每页显示记录数 private int pages 1; …

Scrum Meeting 博客汇总

Scrum Meeting 博客汇总 一、Scrum Meeting 1. Alpha 第一次 Scrum Meeting第二次 Scrum Meeting第三次 Scrum Meeting第四次 Scrum Meeting第五次 Scrum Meeting第六次 Scrum Meeting第七次 Scrum Meeting第八次 Scrum Meeting第九次 Scrum Meeting第十次 Scrum Meeting第十一…

mysql字符串外键约束_MySQL中的约束函数主外键

/*select语句有6大子句&#xff1a;(1)from子句(2)where子句(3)group by子句(4)having子句(5)order by子句(6)limit子句强调&#xff1a;每一个select的6大子句的顺序是(1)-(6)(1)from子句&#xff0c;后面跟表&#xff0c;视图&#xff0c;多行多列的二维表的结构from意思从哪…

POJ 3683 【2-sat+求一组可行解】.cpp

题意&#xff1a; 有一个牧师要给好几对新婚夫妇准备婚礼.. 已知每对新婚夫妇的有空的时间以及婚礼持续时间.. 问是否可以让每对新婚夫妇都得到该牧师的祝福~ 如果可以就输出YES以及可行解 不可以就输出NO 输入&#xff1a; 一个n 表示有n对新婚夫妇 接下来n行每行a b c 表示在…

Matlab并行编程方法1

相信很多朋友在利用matlab进行计算时&#xff0c;会遇到循环次数过大&#xff0c;或者是单次计算量过大的问题&#xff0c;比如需要计算的数值阵列数据量过大&#xff0c;利用传统的编程方式&#xff0c;跑一次程序几个小时&#xff0c;都要等的急死了是不是呢&#xff1f;如果…

使用postman修改SAP Marketing Cloud contact主数据

Marketing Cloud里的contact主数据&#xff0c;创建成功后也不是所有字段都能够被修改。在Personal data区域的字段是可以被修改的。 比如我在“客户属性”字段里维护了一些值&#xff1a; 然后点保存&#xff1a; 其中第二个batch操作是通过一个roundtrip读取contact模型下多个…

java判断一个对象是否为空_Java中判断对象是否为空的方法的详解

首先来看一下工具StringUtils的判断方法&#xff1a;一种是org.apache.commons.lang3包下的&#xff1b;另一种是org.springframework.util包下的。这两种StringUtils工具类判断对象是否为空是有差距的&#xff1a;StringUtils.isEmpty(CharSequence cs); //org.apache.commons…

解决cocos2dx 3.x 导入cocostudio的ui界面出现错位问题

笔者今天发现导入cocostudio的ui界面时&#xff0c;会有部分控件出现错位的现象&#xff0c;后来我看了一下源码&#xff0c;发现是部分控件是没有继承 Layout类&#xff0c;导致不能设置控件位置造成&#xff0c;原因可以看看cocos2dx 源码的CCSGUIReader.cpp文件的函数&#…

Media Queries

支持情况罗列成如下表&#xff1a; Media Queries 使用 说起CSS3的新特性&#xff0c;就不得不提到 Media Queries 。 本文比较详细&#xff0c;所以很多实际中用不到。所以如果只是想简单了解Media Queries&#xff0c;推荐参考 CSS3 Media Queries 。 CSS2.1定义了 Media 的部…

AndroidStudio脚本命令指定AAR生成目录与版本号

A build.gradle全局常量&#xff1a; //根路径def ROOT_PATH rootProject.rootDir.pathdef GROUP "com.genialsir.mobileads"def MOB_SDK_VERSION_NAME "1.1.2" 复制代码 B 在当前库项目的build.gradle文件中android{}中配置如下&#xff1a; //自定义a…

文本文件 java_简单的用java实现读/写文本文件的示例

简单的用java实现读/写文本文件的示例更新时间&#xff1a;2008年07月26日 13:09:26 作者&#xff1a;同时也展示了如果从输入流中读出来内容写入输出流中(仅限文本流)三个例子可以独立存在&#xff0c;所以根据需要只看其中一个就行了。/** 简单的读/写文本文件的示例* 这里…

7个华丽的基于Canvas的HTML5动画

说起HTML5&#xff0c;可能让你印象更深的是其基于Canvas的动画特效&#xff0c;虽然Canvas在HTML5中的应用并不全都是动画制作&#xff0c;但其动画效果确实让人震惊。本文收集了7个最让人难忘的HTML5 Canvas动画&#xff0c;包括画板、文字、图表等&#xff0c;希望你会喜欢。…

网络工程师课程---4、网络层(网关是什么)

网络工程师课程---4、网络层&#xff08;网关是什么&#xff09; 一、总结 一句话总结&#xff1a; 必在当前网段&#xff1a;你到达另外一个网段必过的一个端口&#xff0c;所以必在当前网段 1、icmp如何检测双向通路的连通性&#xff1f; ping 命令 2、计算机1-65535这些端口…

团队分数分配方法——BY 李栋

作为一个团队&#xff0c;自然是一体的&#xff0c;所以要摒弃个人开发的不良习惯&#xff0c;互帮互助&#xff0c;共同进步。以期望在项目过程中能够不使一个人掉队&#xff0c;不因一个人的工作而使全队进度拖延。所以在分工的基础上还是要互帮互助&#xff0c;团队整体的分…

autowired java_Java 基础之Autowired 是否是自动注入

Java 基础之Autowired 是否是自动注入相信很多人对Autowired 注解理解不深入&#xff0c;或者是认为此注解就是spring的自动注入。相信看完本篇文章&#xff0c;你会有更加不一样的理解。首先我们先看下什么是手动注入&#xff1f;在我们的spring应用程序中&#xff0c;定义多个…

Eclipse进行可视化的GUI开发3大GUI插件

Eclipse进行可视化的GUI开发3大GUI插件 转自http://www.cnblogs.com/NationWoo/archive/2011/05/31/2065176.htmlEclipse并不自带GUI的可视化开发工具&#xff0c;那么如果要在Eclipse进行可视化的GUI开发&#xff0c;就需要依靠第三方的插件。 1. Visual Editor Eclipse官方提…

【BZOJ 4016】[FJOI2014]最短路径树问题

&#xff01; 卡时过了 为什么我的这么慢&#xff1f;姿势不对&#xff1f;&#xff1f;&#xff1f; -->谢 ws_fqk 我的Do&#xff08;num[i]&#xff09;应该用 DO(root) 找了半天的root居然没有用.... define的教训永远忘不了了&#xff01;&#xff01;&#xff01; 优化…

mongodb索引--从55.7秒到毫秒级别

从头开始&#xff0c;验证mongodb的索引的好处。(window7环境下) 下载mongodb服务器&#xff0c;并解压到d盘&#xff0c;并使用以下命令启动 mongod --dbpath D:\mongodb\datamongo客户端Robo 3T 去官网下载&#xff0c;安装准备数据&#xff0c;条数为1亿public static void …

汉字转16进制java_java实现汉字转unicode与汉字转16进制实例

本文实例讲述了java实现汉字转unicode与汉字转16进制的实现方法。分享给大家供大家参考。具体实现方法如下&#xff1a;一、汉字转unicodepublic static String toUnicode(String s){String as[] new String[s.length()];String s1 "";for (int i 0; i < s.len…

使用pytest对django项目单元测试

2019独角兽企业重金招聘Python工程师标准>>> 背景 使用django开发了个人博客&#xff0c;欲单元测试&#xff0c;后遍寻网络&#xff0c;然相关资料甚少&#xff0c;遂成此文&#xff0c;望对汝有所助 环境 pytestpytest-djangopytest-covpytest-htmldjangomixer测试…

jquery通知插件toastr

使用方法3个简单步骤对于其他API调用,看到演示。<link href"toastr.css" rel"stylesheet"/> <script src"toastr.js"></script> //显示一个信息没有标题 toastr.info(Are you the 6 fingered man?)其他选项/显示一个警告,没有…

1282. Game Tree

http://acm.timus.ru/problem.aspx?space1&num1282 简单博弈 注意题意的理解 代码&#xff1a; #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<vector> #include<queue> #include<map> #i…

java web dao_JavaWeb项目,DAO应该怎么写?

有一张字段足够多的表&#xff0c;要对它进行各种各样的查询&#xff1a;根据字段A根据字段B&#xff0c;或者根据字段A和B&#xff0c;或者再加上字段C&#xff0c;然后可能还要加上分页&#xff0c;排序等等的逻辑。现在的项目的DAO层为了满足上面这些需要出现了很多参数列表…

2016 - 1- 21 - RunLoop使用(2016-1-24修改一次)(2016 - 1 - 24 再次修改)

一&#xff1a;常驻线程 &#xff1a;当需要一个线程一直处理一些耗时操作时&#xff0c;可以让它拥有一个RunLoop。具体代码如下&#xff1a; 1.通过给RunloopMode里加源来保证RunLoop不直接退出。 这里有个很重要得知识点&#xff0c;runloop对象如果mode为空得话&#xff…