Oracle异常分类小记

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle异常分类小记相关的知识,希望对你有一定的参考价值。

参考技术A

  前几天看《Oracle PL/SQL Programming》的异常处理部分 粗粗看了一遍 觉得有点乱 根据自己的理解作了一下分类 并相应给了一些简例 不一定很准确 供参考

  

   Oracle 异常

   具名 Oracle 异常

   Oracle 为此类异常预先指定了异常号 异常信息 异常名称

   匿名 Oracle 异常

   Oracle 为此类异常预先指定了异常号 异常信息 但未指定异常名称

   自定义异常

   普通自定义异常

   Oracle 无关的业务逻辑异常 无异常号 异常信息 异常名称需要手工声明

   应用程序异常 Raise_Application_Error

   Oracle 无关的业务逻辑异常 此方法抛出的异常可以自定义异常号及异常信息 可以通过 Exception_Init 绑定到手工声明的异常名称上

   )Oracle异常

  Oracle异常总是由Oracle检测并自动抛出的

   )具名Oracle异常

  Oracle定义了 个具名的Oracle异常 比如

  Dup_val_on_index(ora )当中唯一索引所对应的列上键入重复值时

  No_data_found(ora )执行select into未返回行 或者引用了索引表未初始化的元素时

  流程

  A)在Exception模块按异常名进行处理

  示例

  DECLARE

  num number;

  BEGIN

  num:= / ;

  EXCEPTION

  when ZERO_DIVIDE then

  dbms_output put_line(SQLERRM);

  END;

  /

  ORA : divisor is equal to zero

  PL/SQL procedure successfully pleted

   )匿名Oracle异常

  Oracle中存在大量匿名的异常 比如

  ORA : parent key not found

  由于PL/SQL的异常处理模块只接受异常名称 不接受异常号(除了WHEN OTHERS语句 它可以捕获任意异常 不论你是否具有异常名称) 为便于处理 需要手工为其指定异常名称

  流程

  A)声明异常

  B)将此异常绑定到Oracle异常号上

  C)在Exception模块按异常名进行处理

  示例

  DECLARE

  ex EXCEPTION;

  PRAGMA EXCEPTION_INIT(ex );

  BEGIN

  insert into t values( );

  EXCEPTION

  when ex then

  dbms_output put_line(SQLERRM);

  END;

  /

  ORA : integrity constraint (ADMIN FK ) violated parent key not found

  PL/SQL procedure successfully pleted

   )自定义异常

  自定义异常总是开发者显式抛出来的

   )普通自定义异常

  流程

  A)声明异常

  B)使用Raise语句抛出异常

  C)在Exception模块按异常名进行处理

  示例

  DECLARE

  ex EXCEPTION;

  BEGIN

  RAISE ex;

  EXCEPTION

  when ex then

  dbms_output put_line(SQLERRM);

  dbms_output put_line( i raised a user defined exception ex );

  END;

  /

  User Defined Exception

  i raised a user defined exception ex

   )应用程序异常Raise_Application_Error(Num Msg)

  普通自定义异常既没有异常号(SQLCODE一律为 ) 也不能指定异常信息(SQLERRM一律为 User Defined Exception ) 如果想要为自定义异常指定异常号与异常信息 需要借助Raise_Application_Error(Num Msg)函数 这里的Num即异常号 范围是[ ] Msg则是异常信息

  对于此类异常 由于Num是自定义的 因此应该有相应的管理办法以避免重复使用异常号 比如可以将已使用的异常号保存在一张数据库中表 以便将来检查

  流程

  A)声明异常

  B)将此异常绑定到自定义的异常号上

  C)使用Raise_Application_Error(Num Msg)抛出异常 同时指定了异常号及异常信息

  D)在Exception模块按异常名进行处理

  示例

  CREATE OR REPLACE PROCEDURE mtest

  is

  ex EXCEPTION;

  PRAGMA EXCEPTION_INIT(ex );

  BEGIN

  Raise_Application_Error( raising my exception );

  EXCEPTION

  when ex then

  dbms_output put_line(SQLERRM);

  END;

  /

  Procedure created

  SQL> exec mtest

  ORA : raising my exception

lishixinzhi/Article/program/Oracle/201311/18175

iOS分类底层实现原理小记

http://www.jianshu.com/p/b7169a5a558e

OS 分类底层是怎么实现的?
本文将分如下四个模块进行探究

  1. 分类的结构体
  2. 编译时的分类
  3. 分类的加载
  4. 总结

本文使用的runtime源码版本是 objc4 - 680
文中类与分类代码如下

//类
@interface Person : NSObject
@property (nonatomic ,copy) NSString *presonName;
@end

@implementation Person
- (void)doSomeThing{
    NSLog(@"Person");
}
@end
// 分类
@interface Person(categoryPerson)
@property (nonatomic ,copy) NSString *categoryPersonName;
@end

@implementation Person(categoryPerson)
- (void)doSomeThing{
    NSLog(@"categoryPerson");
}
@end

1.分类的结构体

struct _category_t {
    const char *name;//类名
    struct _class_t *cls;//类
    const struct _method_list_t *instance_methods;//category中所有给类添加的实例方法的列表(instanceMethods)
    const struct _method_list_t *class_methods;//category中所有添加的类方法的列表(classMethods)
    const struct _protocol_list_t *protocols;//category实现的所有协议的列表(protocols)
    const struct _prop_list_t *properties;//category中添加的所有属性(instanceProperties)
};

struct category_t {
    const char *name; // 类名
    classref_t cls;   // 分类所属的类
    struct method_list_t *instanceMethods;  // 实例方法列表
    struct method_list_t *classMethods;     // 类方法列表
    struct protocol_list_t *protocols;      // 遵循的协议列表
    struct property_list_t *instanceProperties; // 属性列表

    // 如果是元类,就返回类方法列表;否则返回实例方法列表
    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) {
            return classMethods;
        } else {
            return instanceMethods;
        }
    }

    // 如果是元类,就返回 nil,因为元类没有属性;否则返回实例属性列表,但是...实例属性
    property_list_t *propertiesForMeta(bool isMeta) {
        if (isMeta) {
            return nil; // classProperties;
        } else {
            return instanceProperties;
        }
    }
};

2.编译时的分类

2.1分类的属性

// Person(categoryPerson) 属性列表
static struct /*_prop_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count_of_properties;
    struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_Person_$_categoryPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_prop_t),
    1,
    {{"categoryPersonName","[email protected]\"NSString\",C,N"}}
};

// Person 属性列表
static struct /*_prop_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count_of_properties;
    struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_prop_t),
    1,
    {{"presonName","[email protected]\"NSString\",C,N,V_presonName"}}
};

对比上述代码可以发现:在分类中可以声明属性,并且同样会生成一个 _prop_list_t 的结构体

2.2分类的实例变量?

// Person 实例变量列表

static struct /*_ivar_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count;
    struct _ivar_t ivar_list[1];
} _OBJC_$_INSTANCE_VARIABLES_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_ivar_t),
    1,
    {{(unsigned long int *)&OBJC_IVAR_$_Person$_presonName, "_presonName", "@\"NSString\"", 3, 8}}
};

因为 _category_t 这个结构体中并没有 _ivar_list_t
所以在编译时系统没有Person(categoryPerson) 没有生成类似的相应结构体,也没有生成 _categoryPersonName

2.3分类的实例方法

// Person 实例方法结构体
static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[3];
} _OBJC_$_INSTANCE_METHODS_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    3,
    {{(struct objc_selector *)"doSomeThing", "[email protected]:8", (void *)_I_Person_doSomeThing},
    {(struct objc_selector *)"presonName", "@[email protected]:8", (void *)_I_Person_presonName},
    {(struct objc_selector *)"setPresonName:", "[email protected]:[email protected]", (void *)_I_Person_setPresonName_}}
};

// Person(categoryPerson )实例方法结构体
static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_categoryPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"doSomeThing", "[email protected]:8", (void *)_I_Person_categoryPerson_doSomeThing}}
};

对比上述方法可以看到:虽然分类可以声明属性,但是编译时,系统并没有生成分类属性的 get/set 方法,所以,这就是为什么分类要利用
runtime 动态添加属性,如何动态添加属性,有兴趣的同学可以查看下面文章 iOS分类中通过runtime添加动态属性

2.4分类的结构体

// Person(categoryPerson ) 结构体
static struct _category_t _OBJC_$_CATEGORY_Person_$_categoryPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
    "Person",
    0, // &OBJC_CLASS_$_Person,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_categoryPerson,
    0,
    0,
    (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_categoryPerson,
};

这是系统在编译时实例化 _category_t 生成的
_OBJC_$_CATEGORY_Person_$_categoryPerson

2.5分类数组

static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
    &_OBJC_$_CATEGORY_Person_$_categoryPerson,
};

编译器最后生成了一个数组,数组的元素就是我们创建的各个分类,用来在运行时加载分类。

3.分类的加载

3.1加载分类调用栈

_objc_init
└──map_2_images
    └──map_images_nolock
        └──_read_images

分类加载的调用栈如上述

  • _objc_init 算是整个 objc4 的入口,进行了一些初始化操作,注册了镜像状态改变时的回调函数
  • map_2_images 主要是加锁并调用 map_images_nolock
  • map_images_nolock 在这个函数中,完成所有 class 的注册、fixup等工作,还有初始化自动释放池、初始化 side table 等工作并在函数后端调用了 _read_images
  • _read_images 方法干了很多苦力活,比如加载类、Protocol、Category,加载分类的代码就写在 _read_images 函数的尾部

该调用栈入口函数 void _objc_init(void) 在 objc-os.mm 中,有兴趣的同学可以去看看这些函数里都做了什么

3.2 _read_images 中加载分类的源码

加载分类的源码主要做了两件事

  • 把category的实例方法、协议以及属性添加到类上
  • 把category的类方法和协议添加到类的metaclass上

略去与本文无关的代码,得到如下代码

// 获取 镜像中的所有分类
category_t **catlist = _getObjc2CategoryList(hi, &count);
// 遍历 catlist
for (i = 0; i < count; i++) {
    category_t *cat = catlist[i];
    Class cls = remapClass(cat->cls);
    if (cat->instanceMethods ||  cat->protocols
        ||  cat->instanceProperties) 
    {
        addUnattachedCategoryForClass(cat, cls, hi);
        if (cls->isRealized()) {
            remethodizeClass(cls);
            classExists = YES; 
        }
    }
    if (cat->classMethods  ||  cat->protocols
        /* ||  cat->classProperties */)
    {
        addUnattachedCategoryForClass(cat, cls->ISA(), hi);
        if (cls->ISA()->isRealized()) {
            remethodizeClass(cls->ISA());
        }
    }
}

做上述事情主要用到是如下两个函数

  • addUnattachedCategoryForClass(cat, cls, hi) 为类添加未依附的分类
    执行过程伪代码:
    1.取得存储所有 unattached 分类的列表

    NXMapTable *cats = unattachedCategories();

    2.从 cats 列表中找倒 cls 对应的 unattached 分类的列表

    category_list *list = (category_list *)NXMapGet(cats, cls);

    3.将新来的分类 cat 添加刚刚开辟的位置上

    list->list[list->count++] = (locstamped_category_t){cat, catHeader};

    4.将新的 list 重新插入 cats 中,会覆盖老的 list

    NXMapInsert(cats, cls, list);

    执行完上述过程后,系统将这个分类放到了一个 cls 对应的 unattached 分类的 list 中(有点绕口....),这个 list 会在 remethodizeClass(cls) 方法用到

  • remethodizeClass(cls)
    执行过程伪代码:
    1.取得 cls 类的 unattached 的分类列表

    category_list *cats = unattachedCategoriesForClass(cls, false/*not realizing*/)

    2.将 unattached 的分类列表 attach 到 cls 类上

    attachCategories(cls, cats, true /* 清空方法缓存 flush caches*/);

    执行完上述过程后,系统就把category的实例方法、协议以及属性添加到类上

  • 最后再来看一下
    attachCategories(cls, cats, true /* 清空方法缓存 flush caches*/)内部的实现过程
    1.在堆上创建方法、属性、协议数组,用来存储分类的方法、属性、协议

    method_list_t **mlists = (method_list_t **)malloc(cats->count * sizeof(*mlists));
    property_list_t **proplists = (property_list_t **)malloc(cats->count * sizeof(*proplists));
    protocol_list_t **protolists = (protocol_list_t **)malloc(cats->count * sizeof(*protolists));

    2.遍历 cats ,取出各个分类的方法、属性、协议,并填充到上述代码创建的数组中

    int mcount = 0; // 记录方法的数量
    int propcount = 0; // 记录属性的数量
    int protocount = 0; // 记录协议的数量
    int i = cats->count; // 从后开始,保证先取最新的分类
    bool fromBundle = NO; // 记录是否是从 bundle 中取的
    while (i--) { // 从后往前遍历
      auto& entry = cats->list[i]; // 分类,locstamped_category_t 类型
      // 取出分类中的方法列表;如果是元类,取得的是类方法列表;否则取得的是实例方法列表
      method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
      if (mlist) {
          mlists[mcount++] = mlist; // 将方法列表放入 mlists 方法列表数组中
          fromBundle |= entry.hi->isBundle(); // 分类的头部信息中存储了是否是 bundle,将其记住
      }
      // 取出分类中的属性列表,如果是元类,取得是nil
      property_list_t *proplist = entry.cat->propertiesForMeta(isMeta);
      if (proplist) {
          proplists[propcount++] = proplist; // 将属性列表放入 proplists 属性列表数组中
      }
      // 取出分类中遵循的协议列表
      protocol_list_t *protolist = entry.cat->protocols;
      if (protolist) {
          protolists[protocount++] = protolist; // 将协议列表放入 protolists 协议列表数组中
      }
    }

    3.取出 cls 的 class_rw_t 数据

    auto rw = cls->data();

    4.存储方法、属性、协议数组到 rw

    // 准备 mlists 中的方法
    prepareMethodLists(cls, mlists, mcount/*方法列表的数量*/, NO/*不是基本方法*/, fromBundle/*是否来自bundle*/);
    // 将新属性列表添加到 rw 中的属性列表数组中
    rw->properties.attachLists(proplists, propcount);
    // 释放 proplists
    free(proplists); // 释放 proplists
    // 将新协议列表添加到 rw 中的协议列表数组中
    rw->protocols.attachLists(protolists, protocount);
    // 释放 protolists
    free(protolists); // 释放 protolists
    // 将新协议列表添加到 rw 中的协议列表数组中
    rw->protocols.attachLists(protolists, protocount);
    // 释放 protolists
    free(protolists);

4.总结

至此,本文接近尾声
希望本文的读者能够了解到

  • 分类的结构体
  • 分类中是否能添加属性
  • 分类中是否有实例变量
  • 分类是如何 attach 到类上的

行笔简陋,如有问题,敬请指正













以上是关于Oracle异常分类小记的主要内容,如果未能解决你的问题,请参考以下文章

oracle存储过程报937错误

Mybatis在oracle批量更新

oracle 存储过程异常处理

请教各位高手 oracle 存储过程 如何获得 捕获异常的内容

oracle异常(-)

Oracle存储过程的异常处理