在这篇文章中,我们将带领您了解OC+RAC(六)核心方法bind的全貌,包括oraclerac核心技术详解的相关情况。同时,我们还将为您介绍有关android系统核心机制binder(09)binde
在这篇文章中,我们将带领您了解OC+RAC(六) 核心方法bind的全貌,包括oracle rac核心技术详解的相关情况。同时,我们还将为您介绍有关android 系统核心机制binder(09)binder java层实现、bindColumn、bindParam与bindValue的区别、etcd通信接口之客户端API核心方法实战、html 5 本地数据库-- Web Sql Database核心方法openDatabase、transaction、executeSql 详解的知识,以帮助您更好地理解这个主题。
本文目录一览:- OC+RAC(六) 核心方法bind(oracle rac核心技术详解)
- android 系统核心机制binder(09)binder java层实现
- bindColumn、bindParam与bindValue的区别
- etcd通信接口之客户端API核心方法实战
- html 5 本地数据库-- Web Sql Database核心方法openDatabase、transaction、executeSql 详解
OC+RAC(六) 核心方法bind(oracle rac核心技术详解)
-(void)_test6{ RACSignal *signal = [RACSignal createSignal:^RACdisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"first value"]; [subscriber sendNext:@"second value"]; [subscriber sendNext:@"third value"]; [subscriber sendCompleted]; return nil; }]; RACSignal *bindSignal = [signal bind:^RACSignalBindBlock{ return ^RACSignal *(id value,BOOL *stop) { Nsstring *ovalue = value; if ([ovalue isEqualToString:@"first value"]) { return [RACSignal createSignal:^RACdisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"first value bind after"]; [subscriber sendCompleted]; return nil; }]; } if ([ovalue isEqualToString:@"second value"]) { *stop = YES; return [RACSignal createSignal:^RACdisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"second value bind after"]; [subscriber sendCompleted]; return nil; }]; } if ([ovalue isEqualToString:@"third value"]) { return [RACSignal createSignal:^RACdisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"third value bind after"]; [subscriber sendCompleted]; return nil; }]; } return nil; }; }]; [bindSignal subscribeNext:^(id _Nullable x) { NSLog(@"x====%@",x); }]; }
再来一个例子: -(void)_test7{ //1.创建信号 RACSubject * subject = [RACSubject subject]; //2.绑定信号 RACSignal * bindSignal = [subject bind:^RACSignalBindBlock _Nonnull{ return ^RACSignal * (id value,BOOL *stop){ NSLog(@"%@",value); return [RACReturnSignal return:[Nsstring stringWithFormat:@"一顿操作猛如虎 %@",value]]; }; }]; //3.订阅信号 [bindSignal subscribeNext:^(id _Nullable x) { NSLog(@"绑定接收到!! %@",x); }]; //4.发送 [subject sendNext:@"发送原始的数据"]; }
android 系统核心机制binder(09)binder java层实现
本章关键点总结 & 说明:
这里关注➕ Binder Java实现部分,主要谈了 java实现框架和demo,最后分析了 关键类 Binder、JavaBBinderHolder、JavaBBinder以及他们之间的关系。
1 binder java层的整体框架
1.1 整体框架图说明
Binder机制在C++层已经有了完整的实现。因此Java层完全不用重复实现,而是通过JNI衔接了C++层以复用其实现。因此java层的架构相对比较简单。如下所示:
同时这里用另一个图来说明下binder java层、 JNI的衔接以及binder C++层,如下所示:
1.2 binder相关类解读
类 名称 类 说明
IBinder Java层,提供了使用transact方法来调用远程服务的机制,以及DeathRecepient接口
Binder 实现IBinder接口,封装JNI实现,Java层Binder服务基类,BnXXX代表
BinderProxy 实现IBinder接口,封装JNI实现。提供transact方法调用远程服务,BpXXX代表
JavaBBinderHolder 内部存储了JavaBBinder
JavaBBinder 将C++端的onTransact调用传递到Java端
BinderInternal 仅供Binder框架使用的类,内部有一个GcWatcher类,专门用于处理和Binder相关的垃圾回收。
Parcel 承载通信数据,Java层和C++层都有涉及
这里对binder的java层框架又了一个简单了解后,我们接下来看看binder在java层的案例,即如何实现一个java层的binder通信模型。
1.3 Java层Binder架构总结
该图给出的是Java层与Native层的关键类BinderProxy 与BpBinder,JavaBBinder之间的关系以及客户端与服务端的关系。
对于代表客户端的BinderProxy来说,Java层的BinderProxy在Native层对应一个BpBinder对象。凡是从Java层发出的请求,首先从Java层的BinderProxy传递到Native层的BpBinder,继而由BpBinder将请求发送到Binder驱动。
对于代表服务端的Service来说,Java层的Binder在Native层有一个JavaBBinder对象。前面介绍过,所有Java层的Binder在Native层都对应为JavaBBinder,而JavaBBinder仅起到中转作用,即把来自客户端的请求从Native层传递到Java层。
1.4 java层 binder FLAG_ONEWAY机制解读
IBinder接口类中定义了一个叫FLAG_ONEWAY的整型,该变量的意义非常重要。
当客户端利用Binder机制发起一个跨进程的函数调用时,调用方(即客户端)一般会阻塞,直到服务端返回结果。
这种方式和普通的函数调用是一样的。但是在调用Binder函数时,在指明了 FLAG_ONEWAY标志后,调用方只要把请求发送到Binder驱动即可返回,而不用等待服务端的结果,这就是一种所谓的非阻塞方式。
在Native 层中,涉及的Binder调用基本都是阻塞的,但是在Java层的framework中,使用FLAG_ONEWAY进行Binder调用的情况非常多, 以后经常会碰到。
使用FLAG_ONEWAY进行函数调用的程序在设计上的特点:对于使用FLAG_ONEWAY的函数来说,客户端仅向服务端发出了请求,但是并不能确定服务端是否处理了该请求。所以,客户端一般会向服务端注册一个回调(同样是跨进程的Binder调用),一旦服务端处理了该请求, 就会调用此回调来通知客户端处理结果。这种回调函数也大多采用FLAG_ONEWAY的方式。
2 binder Java层实现框架案例
2.1 aidl文件与 IHelloService.java
android在java层使用了 一个叫aidl的文件模式,编译后可以直接生成IHelloService.java
/** {@hide} */
interface IHelloService
{
void sayhello();
int sayhello_to(String name);
}
2.2 生成 IHelloService.java
把 IHelloService.aidl,放入 frameworks/base/core/java/android/os,修改 frameworks/base/Android.mk 添加一行
core/java/android/os/IVibratorService.aidl \
+ core/java/android/os/IHelloService.aidl \
执行 $mmm frameworks/base,它会生成:
./out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/IHelloService.java
打开该文件,如下:
public interface IHelloService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements IHelloService
{
private static final java.lang.String DESCRIPTOR = "IHelloService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an IHelloService interface,
* generating a proxy if needed.
*/
public static IHelloService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof IHelloService))) {
return ((IHelloService)iin);
}
return new IHelloService.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_sayhello:
{
data.enforceInterface(DESCRIPTOR);
this.sayhello();
reply.writeNoException();
return true;
}
case TRANSACTION_sayhello_to:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
int _result = this.sayhello_to(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements IHelloService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public void sayhello() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_sayhello, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public int sayhello_to(java.lang.String name) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
mRemote.transact(Stub.TRANSACTION_sayhello_to, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_sayhello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_sayhello_to = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public void sayhello() throws android.os.RemoteException;
public int sayhello_to(java.lang.String name) throws android.os.RemoteException;
}
这里 直接生成了IHelloService.Stub 和IHelloService.Stub.Proxy,一个相当于C++层的BnXXX(java层不需要在意传输数据的通信架构,即不需要在意服务端的RPC通信框架,仅需要搞定业务层代码sayhello和 syahello_to),一个是客户端的代理BpXXX(这里直接生成,不需要再写代码,非常简单),因此这里只要编写一个HelloService继承IHelloService.Stub即可。
2.3 业务层代码 HelloService
import android.util.Slog;
public class HelloService extends IHelloService.Stub {
private static final String TAG = "HelloService";
private int cnt1 = 0;
private int cnt2 = 0;
public void sayhello() throws android.os.RemoteException {
cnt1++;
Slog.i(TAG, "sayhello : cnt = "+cnt1);
}
public int sayhello_to(java.lang.String name) throws android.os.RemoteException {
cnt2++;
Slog.i(TAG, "sayhello_to "+name+" : cnt = "+cnt2);
return cnt2;
}
}
这里就直接实现了 服务端方法 的处理流程。基于此开始编写测试应用
2.4 TestServer实现
import android.util.Slog;
import android.os.ServiceManager;
public class TestServer {
private static final String TAG = "TestServer";
public static void main(String args[])
{
/* add Service */
Slog.i(TAG, "add hello service");
ServiceManager.addService("hello", new HelloService());
while (true)
{
try {
Thread.sleep(100);
} catch (Exception e){}
}
}
}
2.5 TestClient实现
import android.util.Slog;
import android.os.ServiceManager;
import android.os.IBinder;
/* test_client <hello> [name] */
public class TestClient {
private static final String TAG = "TestClient";
public static void main(String args[])
{
if (args.length == 0)
{
System.out.println("Usage: need parameter: <hello> [name]");
return;
}
if (args[0].equals("hello"))
{
IBinder binder = ServiceManager.getService("hello");//getService
if (binder == null)
{
Slog.i(TAG, "can not get hello service");
return;
}
IHelloService svr = IHelloService.Stub.asInterface(binder);
if (args.length == 1)
{
try {
svr.sayhello();
System.out.println("call sayhello");
Slog.i(TAG, "call sayhello");
} catch (Exception e) {}
}
else
{
try {
int cnt = svr.sayhello_to(args[1]);
System.out.println("call sayhello_to "+args[1]+" : cnt = "+cnt);
Slog.i(TAG, "call sayhello_to "+args[1]+" : cnt = "+cnt);
} catch (Exception e) {
System.out.println("call sayhello_to , err :"+e);
Slog.i(TAG, "call sayhello_to , err : "+e);
}
}
}
}
}
2.6 Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := HelloService.java IHelloService.java TestServer.java
LOCAL_MODULE := TestServer
include $(BUILD_JAVA_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := HelloService.java IHelloService.java TestClient.java
LOCAL_MODULE := TestClient
include $(BUILD_JAVA_LIBRARY)
2.7 运行测试程序
这里直接生成了两个java端测试程序,但是执行却成了问题,编译结束后,我们要把生成的TestServer.jar 和 TestClient.jar拷贝到机器上,因此这里我么要借助于 app_process,执行步骤如下所示:
$CLASSPATH=/mnt/android_fs/TestServer.jar app_process / TestServer &
$CLASSPATH=/mnt/android_fs/TestClient.jar app_process / TestClient hello
$CLASSPATH=/mnt/android_fs/TestClient.jar app_process / TestClient hello wds
执行步骤仅供参开,了解其中原理才是关键。
3 java层binder 关键类Binder、JavaBBinderHolder和JavaBBinder之间的关系
3.1 从Binder 入手
Java层的 Binder的构造函数,代码如下:
public Binder() {
init();
...
}
Binder构造函数中会调用init方法,如下所示:
private native final void init();
找到native方法的注册,如下所示:
static const JNINativeMethod gBinderMethods[] = {
/* name, signature, funcPtr */
{ "getCallingPid", "()I", (void*)android_os_Binder_getCallingPid },
{ "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
{ "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
{ "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
{ "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
{ "getThreadStrictModePolicy", "()I", (void*)android_os_Binder_getThreadStrictModePolicy },
{ "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
{ "init", "()V", (void*)android_os_Binder_init },
{ "destroy", "()V", (void*)android_os_Binder_destroy }
};
init方法对应的native实现函数为android_os_Binder_init ,其实现的代码如下:
static void android_os_Binder_init(JNIEnv* env, jobject obj)
{
JavaBBinderHolder* jbh = new JavaBBinderHolder();//创建一个JavaBBinderHolder对象
if (jbh == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
return;
}
ALOGV("Java Binder %p: acquiring first ref on holder %p", obj, jbh);
jbh->incStrong((void*)android_os_Binder_init);
//将这个JavaBBinderHolder对象保存到Java Binder对象的mObject成员中
env->SetLongField(obj, gBinderOffsets.mObject, (jlong)jbh);
}
由构造函数的分析可知,Java的Binder对象将和一个Native的JavaBBinderHolder对象相关联。
3.2 JavaBBinderHolder定义如下:
class JavaBBinderHolder : public RefBase
{
public:
sp<JavaBBinder> get(JNIEnv* env, jobject obj)
{
AutoMutex _l(mLock);
sp<JavaBBinder> b = mBinder.promote();//promote,弱引用转强引用
if (b == NULL) {
b = new JavaBBinder(env, obj);//创建一个JavaBBinder,obj实际上是Java层中的Binder对象
mBinder = b;
}
return b;
}
sp<JavaBBinder> getExisting()
{
AutoMutex _l(mLock);
return mBinder.promote();
}
private:
Mutex mLock;
wp<JavaBBinder> mBinder;
};
JavaBBinderHolder仅从RefBase派生,所以它不属于Binder家族。Java层的Binder和Native层的一个与Binder家族无关的对象绑定的原因:JavaBBinderHolder类的get函数中创建了一个JavaBBinder对象,这个对象就是从BBinder派生的。关系如下所示:
class JavaBBinder : public BBinder
通过JavaBBinderHolder的get函数的调用分析3者之间的关系,从下面这句代码开始分析:
//其中,data是Parcel对象,service此时还是ActivityManagerService
data.writeStrongBinder(service);
writeStrongBinder会做一个替换工作,下面是它的native代码实现:
public final void writeStrongBinder(IBinder val) {
nativeWriteStrongBinder(mNativePtr, val);
}
继续分析nativeWriteStrongBinder,代码实现如下:
private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
找到native方法的注册,如下所示:
static const JNINativeMethod gParcelMethods[] = {
{"nativeDataSize", "(J)I", (void*)android_os_Parcel_dataSize},
...
{"nativeWriteStrongBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder},
...
}
分析android_os_Parcel_writeStrongBinder的实现,代码如下所示:
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);//parcel是一个Native的对象
if (parcel != NULL) {
//writeStrongBinder的真正参数是ibinderForJavaObject的返回值
const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
}
}
继续分析ibinderForJavaObject的实现,代码如下所示:
sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
if (obj == NULL) return NULL;
//逻辑说明:
//如果Java的obj是Binder类,则首先获得JavaBBinderHolder对象,然后调用它的get函数。
//而这个get将返回一个JavaBBinder
if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
JavaBBinderHolder* jbh = (JavaBBinderHolder*)env->GetLongField(obj, gBinderOffsets.mObject);
return jbh != NULL ? jbh->get(env, obj) : NULL;
}
//如果obj是BinderProxy类,则返回Native的BpBinder对象
if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
return (IBinder*)env->GetLongField(obj, gBinderProxyOffsets.mObject);
}
return NULL;
}
分析后发现:
Java层中所有的Binder对应的都是这个JavaBBinder(不同的Binder对象对应不同的JavaBBinder对象)
下图展示了Java Binder(Java)、JavaBBinderHolder(C++)和JavaBBinder(C++)的关系。如下所示:
总结如下:
Java层的Binder通过mObject指向一个Native层的JavaBBinderHolder对象。
Native层的JavaBBinderHolder对象通过mBinder成员变量指向一个Native的JavaBBinder对象。
Native的JavaBBinder对象又通过mObject变量指向一个Java层的Binder对象。
3.3 JavaBBinder
Java层的Binder架构中,JavaBBinder却是一个和业务完全无关的对象。它如何实现不同业务呢?
@1 为此开始分析onTransact函数,当收到请求时,JavaBBinder会调用它的这个函数。
class JavaBBinder : public BBinder
{
public:
JavaBBinder(JNIEnv* env, jobject object)
: mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object))
{...}
...
protected:
virtual ~JavaBBinder(){...}
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
{
JNIEnv* env = javavm_to_jnienv(mVM);
...
IPCThreadState* thread_state = IPCThreadState::self();
const int32_t strict_policy_before = thread_state->getStrictModePolicy();
//调用Java层Binder对象的execTranscat函数
jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);
...
return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;
}
...
private:
JavaVM* const mVM;
jobject const mObject;
};
本例中mObject就是HelloService,现在调用它的execTransact函数,该函数在Binder(Java类)中实现,代码如下:
private boolean execTransact(int code, long dataObj, long replyObj,
int flags) {
Parcel data = Parcel.obtain(dataObj);
Parcel reply = Parcel.obtain(replyObj);
boolean res;
try {
//调用onTransact函数,派生类可以重新实现这个函数,以完成业务功能
res = onTransact(code, data, reply, flags);
} catch (RemoteException e) {
if ((flags & FLAG_ONEWAY) != 0) {
Log.w(TAG, "Binder call failed.", e);
} else {
reply.setDataPosition(0);
reply.writeException(e);
}
e.printStackTrace(); /// M: ALPS00303655
res = true;
} catch (RuntimeException e) {
if ((flags & FLAG_ONEWAY) != 0) {
Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
} else {
reply.setDataPosition(0);
reply.writeException(e);
}
e.printStackTrace(); /// M: ALPS00303655
res = true;
} catch (OutOfMemoryError e) {
// Unconditionally log this, since this is generally unrecoverable.
Log.e(TAG, "Caught an OutOfMemoryError from the binder stub implementation.", e);
RuntimeException re = new RuntimeException("Out of memory", e);
re.printStackTrace(); /// M: ALPS00303655
reply.setDataPosition(0);
reply.writeException(re);
res = true;
}
checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
reply.recycle();
data.recycle();
StrictMode.clearGatheredViolations();
return res;
}
这里会调用子类的onTransact函数,IHelloService.stub类实现了onTransact函数,部分代码如下:
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_sayhello:
{
data.enforceInterface(DESCRIPTOR);
this.sayhello();
reply.writeNoException();
return true;
}
case TRANSACTION_sayhello_to:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
int _result = this.sayhello_to(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
@2 由此可以看出,JavaBBinder仅是一个传声筒,它本身不实现任何业务函数,其工作是:
收到请求时,只是简单地调用它所绑定的Java层Binder对象的exeTransact。
该Binder对象的exeTransact调用其子类实现的onTransact函数。
子类的onTransact函数将业务又派发给其子类来完成。请读者务必注意其中的多层继承关系。
通过这种方式,来自客户端的请求就能传递到正确的Java Binder对象了。
4 总结
Binder的目的虽简单(即打开binder设备,然后读请求和 写回复),但架构繁琐(编写各种接口类和封装类等)。我们在研究源码时,一定要搞清楚目的。实现只不过是达到该目的的一种手段和方式。脱离目的的实现,缘木求鱼,很容易偏离事物本质。
bindColumn、bindParam与bindValue的区别
bindColumn:绑定一列到一个 PHP 变量(类似于list()函数为变量赋值)
<?php
//连接数据库函数
functionconnect() {
try {
$dbh = new PDO("mysql:host=localhost;dbname=test",''root'',''root'');
return $dbh;
} catch(Exception $e){
echo $e->getMessage();
}
}
// 使用bindColumn读取数据
functionreadDataByColumn($dbh) {
$sql = ''SELECT id,name FROM users''; # 读取users表中id和name字段
try {
$stmt = $dbh->prepare($sql);
$stmt->execute();
/* 通过列号绑定,将ID这一列的值绑定到$id这个变量上 */
$stmt->bindColumn(1, $id);
/* 通过列名绑定,将name这一列字段的值绑定到$name这个变量上 */
$stmt->bindColumn(''name'', $name);
while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {
$data = $id . "\t" . $name . "\t";
echo"<pre>";
print $data;
}
}
catch (PDOException $e) {
print $e->getMessage();
}
}
$dbh = connect(); // 连接数据库
readDataByColumn($dbh); // 使用bindColumn读取数据
?>
运行结果如下:
1 Michael
2 Andy
3 xiaoming
bindParam:绑定一个参数到指定的变量名(类似于占位符)
<?php
//连接数据库函数
functionconnect() {
try {
$dbh = new PDO("mysql:host=localhost;dbname=test",''root'',''root'');
return $dbh;
} catch(Exception $e){
echo $e->getMessage();
}
}
// 使用bindColumn读取数据
functionreadDataByParam($dbh) {
$num = 10;
$username = ''%m%'';
$sql = ''SELECT id, name FROM users WHERE id < :num and name like :username'';
try {
$stmt = $dbh->prepare($sql); // 预处理
$stmt->bindParam('':num'', $num, PDO::PARAM_INT); // 数字类型
$stmt->bindParam('':username'', $username, PDO::PARAM_STR); // 字符串类型
$stmt->execute(); // 执行SQL语句
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { // $row是一行,使用while依次输出下一行
$data = $row[''id''] . "\t" . $row[''name''] . "\t";
echo"<pre>";
print $data;
}
}
catch (PDOException $e) {
print $e->getMessage();
}
}
$dbh = connect(); // 连接数据库
readDataByParam($dbh); // 使用bindColumn读取数据
?>
bindValue — 把一个值绑定到一个参数(与bindParam类似)
<?php
//连接数据库函数
functionconnect() {
try {
$dbh = new PDO("mysql:host=localhost;dbname=test",''root'',''root'');
return $dbh;
} catch(Exception $e){
echo $e->getMessage();
}
}
// 使用bindColumn读取数据
functionreadDataByValue($dbh) {
$num = 10;
$username = ''%m%'';
$sql = ''SELECT id, name FROM users WHERE id < :num and name like :username'';
try {
$stmt = $dbh->prepare($sql); // 预处理
$stmt->bindValue('':num'', 10, PDO::PARAM_INT); // 数字类型
$stmt->bindValue('':username'', ''%m%'', PDO::PARAM_STR); // 字符串类型
$stmt->execute(); // 执行SQL语句
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { // $row是一行,使用while依次输出下一行
$data = $row[''id''] . "\t" . $row[''name''] . "\t";
echo"<pre>";
print $data;
}
}
catch (PDOException $e) {
print $e->getMessage();
}
}
$dbh = connect(); // 连接数据库
readDataByValue($dbh); // 使用bindColumn读取数据
?>
bindParam和bindValue的区别
- PDOStatement::bindParam不能绑定常量,而bindValue可以绑定常量 如 $stm->bindParam(":sex",$sex); //正确 $stm->bindParam(":sex","female"); //错误 $stm->bindValue(":sex",$sex); //正确 $stm->bindValue(":sex","female"); //正确
- bindParam 变量被以引用方式绑定到点位符上,而且仅仅当调用PDOStatement::execute()时才会去计算具体被绑定变量在PDOStatement::execute()被调用时的值. 例如,使用bindParam方式:
<?php
$sex = ''male'';
$s = $dbh->prepare(''SELECT name FROM students WHERE sex = :sex'');
$s->bindParam('':sex'', $sex); // use bindParam to bind the variable
$sex = ''female'';
$s->execute(); // 将执行 WHERE sex = ''female''
使用bindvalue方式:
<?php
$sex = ''male'';
$s = $dbh->prepare(''SELECT name FROM students WHERE sex = :sex'');
$s->bindValue('':sex'', $sex); // use bindValue to bind the variable''s value
$sex = ''female'';
$s->execute(); // 将执行 WHERE sex = ''male''
etcd通信接口之客户端API核心方法实战
前言
我们在前面介绍了 etcd 的整体架构。学习客户端与 etcd 服务端的通信以及 etcd 集群节点的内部通信接口对于我们更好地使用和掌握 etcd 组件很有帮助,也是所必需了解的内容。我们将会介绍 etcd 的 gRPC 通信接口以及客户端的实践。
etcd clientv3 客户端
etcd 客户端 clientv3 接入的示例将会以 Go 客户端为主,读者需要准备好基本的开发环境。
首先是 etcd clientv3 的初始化,我们根据指定的 etcd 节点,建立客户端与 etcd 集群的连接。
cli,err := clientv3.New(clientv3.Config{ Endpoints:[]string{"localhost:2379"}, DialTimeout: 5 * time.Second, })
如上的代码实例化了一个 client,这里需要传入的两个参数:
- Endpoints:etcd 的多个节点服务地址,因为我是单点本机测试,所以只传 1 个。
- DialTimeout:创建 client 的首次连接超时,这里传了 5 秒,如果 5 秒都没有连接成功就会返回 err;值得注意的是,一旦 client 创建成功,我们就不用再关心后续底层连接的状态了,client 内部会重连。
etcd 客户端初始化
解决完包依赖之后,我们初始化 etcd 客户端。客户端初始化代码如下所示:
// client_init_test.go package client import ( "context" "fmt" "go.etcd.io/etcd/clientv3" "testing" "time" ) // 测试客户端连接 func TestEtcdClientInit(t *testing.T) { var ( config clientv3.Config client *clientv3.Client err error ) // 客户端配置 config = clientv3.Config{ // 节点配置 Endpoints: []string{"localhost:2379"}, DialTimeout: 5 * time.Second, } // 建立连接 if client, err = clientv3.New(config); err != nil { fmt.Println(err) } else { // 输出集群信息 fmt.Println(client.Cluster.MemberList(context.TODO())) } client.Close() }
如上的代码,预期的执行结果如下:
=== RUN TestEtcdClientInit
&{cluster_id:14841639068965178418 member_id:10276657743932975437 raft_term:3 [ID:10276657743932975437 name:"default" peerURLs:"http://localhost:2380" clientURLs:"http://0.0.0.0:2379" ] {} [] 0} <nil>
--- PASS: TestEtcdClientInit (0.08s)
PASS
可以看到 clientv3 与 etcd Server 的节点 localhost:2379 成功建立了连接,并且输出了集群的信息,下面我们就可以对 etcd 进行操作了。
client 定义
接着我们来看一下 client 的定义:
type Client struct { Cluster KV Lease Watcher Auth Maintenance // Username is a user name for authentication. Username string // Password is a password for authentication. Password string }
注意,这里显示的都是可导出的模块结构字段,代表了客户端能够使用的几大核心模块,其具体功能介绍如下:
- Cluster:向集群里增加 etcd 服务端节点之类,属于管理员操作。
- KV:我们主要使用的功能,即操作 K-V。
- Lease:租约相关操作,比如申请一个 TTL=10 秒的租约。
- Watcher:观察订阅,从而监听最新的数据变化。
- Auth:管理 etcd 的用户和权限,属于管理员操作。
- Maintenance:维护 etcd,比如主动迁移 etcd 的 leader 节点,属于管理员操作
以上就是etcd通信接口之客户端API核心方法实战的详细内容,更多关于etcd通信接口客户端API的资料请关注其它相关文章!
- Go操作etcd的实现示例
- 使用client-go工具调用kubernetes API接口的教程详解(v1.17版本)
- 使用Go HTTP客户端打造高性能服务
- 详解Go语言RESTful JSON API创建
html 5 本地数据库-- Web Sql Database核心方法openDatabase、transaction、executeSql 详解
Web SQL数据库API实际上不是HTML5规范的组成部分,而是单独的规范。它通过一套API来操纵客户端的数据库。Safari、Chrome、Firefox、Opera等主流浏览器都已经支持Web SQL Database。HTML5的Web SQL Databases的确很诱惑人,当你发现可以用与mysql查询一样的查询语句来操作本地数据库时,你会发现这东西挺有趣的。今天,我们一起来了解HTML 5的Web SQL Database API。
下面将一一将介绍怎样创建打开数据库,创建表,添加数据,更新数据,删除数据,删除表 。
先介绍三个核心方法
1、openDatabase:这个方法使用现有数据库或创建新数据库创建数据库对象。
2、transaction:这个方法允许我们根据情况控制事务提交或回滚。
3、executeSql:这个方法用于执行真实的SQL查询。
第一步:打开连接并创建数据库
var dataBase = openDatabase("student", "1.0", "学生表", 1024 * 1024, function () { });
if (!dataBase) {
alert("数据库创建失败!");
} else {
alert("数据库创建成功!");
}
解释一下openDatabase方法打开一个已经存在的数据库,如果数据库不存在,它还可以创建数据库。几个参数意义分别是:
1,数据库名称。
2,版本号 目前为1.0,不管他,写死就OK。
3,对数据库的描述。
4,设置数据的大小。
5,回调函数(可省略)。
初次调用时创建数据库,以后就是建立连接了。
创建的数据库就存在本地,路径如下:
C:\Users\Administrator\AppData\Local\Google\Chrome\User Data\Default\databases\http_localhost_* 。
创建的是一个sqllite数据库,可以用SQLiteSpy打开文件,可以看到里面的数据。SQLiteSpy是一个绿色软件,可以百度一下下载地址或SQLiteSpy官方下载:SQLiteSpy。
第二步:创建数据表

this.createTable=function() {
dataBase.transaction( function(tx) {
tx.executeSql(
"create table if not exists stu (id REAL UNIQUE, name TEXT)",
[],
function(tx,result){ alert(''创建stu表成功''); },
function(tx, error){ alert(''创建stu表失败:'' + error.message);
});
});
}

解释一下,
executeSql函数有四个参数,其意义分别是:
1)表示查询的字符串,使用的SQL语言是SQLite 3.6.19。(必选)
2)插入到查询中问号所在处的字符串数据。(可选)
3)成功时执行的回调函数。返回两个参数:tx和执行的结果。(可选)
4)一个失败时执行的回调函数。返回两个参数:tx和失败的错误信息。(可选)
第三步:执行增删改查
1)添加数据:

this.insert = function () {
dataBase.transaction(function (tx) {
tx.executeSql(
"insert into stu (id, name) values(?, ?)",
[id, ''徐明祥''],
function () { alert(''添加数据成功''); },
function (tx, error) { alert(''添加数据失败: '' + error.message);
} );
});

2)查询数据

this.query = function () {
dataBase.transaction(function (tx) {
tx.executeSql(
"select * from stu", [],
function (tx, result) { //执行成功的回调函数
//在这里对result 做你想要做的事情吧...........
},
function (tx, error) {
alert(''查询失败: '' + error.message);
} );
});
}

特别提醒
上面代码中执行成功的回调函数有一参数result。
result:查询出来的数据集。其数据类型为 SQLResultSet ,就如同C#中的DataTable。
SQLResultSet 的定义为:
interface SQLResultSet {
readonly attribute long insertId;
readonly attribute long rowsAffected;
readonly attribute SQLResultSetRowList rows;
};
其中最重要的属性—SQLResultSetRowList 类型的 rows 是数据集的“行” 。
rows 有两个属性:length、item 。
故,获取查询结果的第一行列名为name的值 :result.rows.item(0).name 。
3)更新数据

this.update = function (id, name) {
dataBase.transaction(function (tx) {
tx.executeSql(
"update stu set name = ? where id= ?",
[name, id],
function (tx, result) {
},
function (tx, error) {
alert(''更新失败: '' + error.message);
});
});
}

4)删除数据

this.del = function (id) {
dataBase.transaction(function (tx) {
tx.executeSql(
"delete from stu where id= ?",
[id],
function (tx, result) {
},
function (tx, error) {
alert(''删除失败: '' + error.message);
});
});
}

5)删除数据表
this.dropTable = function () {
dataBase.transaction(function (tx) {
tx.executeSql(''drop table stu'');
});
}
关于OC+RAC(六) 核心方法bind和oracle rac核心技术详解的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于android 系统核心机制binder(09)binder java层实现、bindColumn、bindParam与bindValue的区别、etcd通信接口之客户端API核心方法实战、html 5 本地数据库-- Web Sql Database核心方法openDatabase、transaction、executeSql 详解的相关信息,请在本站寻找。
本文标签: