精华内容
下载资源
问答
  • 复杂数据类型(signal函数)的解读-C语言基础

    千次阅读 多人点赞 2020-08-20 17:07:19
    这一篇文章要探讨的是C语言中复杂数据类型的解读。涉及到signal()函数数据类型的解读(并不解释signal()的作用)以及对于数据类型的理解,属于C语言基础篇。 在开始解读signal()这种复杂类型之前,先给大家分享一个...

    在这里插入图片描述这一篇文章要探讨的是C语言中复杂数据类型的解读。涉及到signal()函数数据类型的解读(并不解释signal()的作用)以及对于数据类型的理解,属于C语言基础篇。


    在开始解读signal()这种复杂类型之前,先给大家分享一个技巧。我老师曾经教过我。如果你想知道一个变量的数据类型,最简单的方法就是找到这个变量的定义处,然后把变量名去掉,剩下的就是这个变量的数据类型了。例如数组a的定义是int a[5]那么把变量名“a”去掉,剩下的int[5]便就是变量a的数据类型了。

    说到这,可能有人立刻就定义了一个int[5] a;来验证一下int[5]这个数据类型是否真的存在,然后发现居然在编译的时候就给报错拦下来了,现在可能正拿着39米长的大刀在找博主来的路上了。但你能不能先在40米那里等会儿,容我狡辩一下。

    确实,我得承认你直接定义一个int[5] a;是会连编译都过不了。但这并不是我的错啊!这不过是C语言语法规定导致的问题而已。C语言在定义变量的时候,规定把对于这个变量的描述关键字放在变量名的左边,把和数组相关的描述放在变量名的右边。至于为什么要这么规定,愚不才,答不上来。

    不过int[5] a;在Java里面其实是可以编译通过的。事实上甚至有很大一部分人推崇用int[5] a;来定义数组比用int a[5];来定义数组更加合适。
    因为前者能更加明确的表明变量a的数据类型。为什么在Java里面可以这么定义数组而在C语言里面不行呢?我估摸着应该是由于数组这个类型本身的特殊性导致的。首先C语言是一门很老的语言,C语言之父丹尼斯·里奇在创造C语言的时候可能并没有把数组当做是一个类型来看待。什么是数组?数组其实就是同一种数据类型数据的组合。所以在C语言中,数组的定义是int a[5];这样的,数组标示符[5]并没有参与到数组名a的描述中去。

    但是这里有一个问题,数组本身也应该是一个数据类型啊,所以你定义一个数组的时候,数组标识符[]即应该是脱离数组名的数据类型而对原数据类型组合的描述,又应该是参与到数组名本身数据类型的描述中去作为"数组名数据类型"的一部分。所以在后面Java之父詹姆斯·高斯林创造Java的时候,就考虑到了这个问题,便从int a[5]的定义语法中引申出int[5] a的语法。因为这样或许能更合理的描述数组a是个什么东西。好了,说了这么多,这也只是我极端个人主义的猜想而已,并没有去深究。


    但是,虽然在C语言中你并不能直接用int[5]这个数据类型来定义一个数组,但是这也并不能代表int[5]在C语言中就不是一个数据类型了。

    我们来看一下下面的代码:

    #include <stdio.h>
    int main(void)
    {
        int a[5] = {0};
    
        printf("(a):sizeof[%lu]\n", sizeof(a));
        printf("(int[5]):sizeof[%lu]\n", sizeof(int[5]));
        return 0;
    }
    

    这篇代码的运行结果是这样子的:

    在这里插入图片描述

    在C语言中,虽然我们不能直接用int[5]来定义一个数组变量,但是sizeof运算符却能够识别int[5]这个数据类型,而且用sizeof计算int[5]这个数据类型出来的值和直接求这个数组变量名得出来的值是一样的。这其实已经间接说明了int[5]其实就是数组变量a的数据类型了。这就和你定义了一个int a变量,然后用sizeof来求int和用sizeof来求a得到的结果是一样的一个道理。


    事实上,如果你不知道一长串关键字拼接在一起的东西是不是一个合法数据类型,那么你完全可以把它们扔到sizeof运算符里面,如果编译不报错的话,那么多半就是一个数据类型了。

    例如我们待会要分析的信号处理函数的原型很复杂像这样:

    void(*signal(int sig,void(*func)(int)))(int)

    按照规律,我们找到定义处把左边起第一个非关键字的单词去掉,那么剩下的便是它的数据类型了。

    void(*(int sig,void(*func)(int)))(int)

    很复杂的一个数据类型,不过不管它再复杂,它最根本的属性不过是一个函数类型而已。

    验证代码:

    #include <stdio.h>
    void f(void)
    {
    }
    int main(void)
    {
        printf("signal类型大小为[%lu]\n", 
        			sizeof(void(*(int sig,void(*func)(int)))(int)));
        printf("f类型大小为[%lu]\n", sizeof(f));
        return 0;
    }
    

    运行结果:

    在这里插入图片描述

    嗯,类型再复杂本质都是一样的。


    好了,说了那么多,我们是时候该来解读一下signal()这个类型到底是个什么样子了,我们又是如何看出来它是一个函数的。

    void(*signal(int sig,void(*func)(int)))(int)

    按照惯例,我们得先找到这个变量名字,这么长一串东西里面,那个单词才是这个变量的名字呢?按照规定,从左边看起,第一个非关键字signal,即是这个变量的名字。那么周围这一整串东西其实都是在描述它的,都是它的数据类型。我们就从这个变量名开始,向左右两边扩展的看。变量名的左边是一个*号,右边是一个()号。

    最靠近变量名的运算符:*signal()

    这里有些人可能就不理解了,右边明明只有一个“(”号而已,那里冒出来了个“)”号了?但是啊,()号这个东西就是整体出现的啊,有时候当你看到表达式里面出现()号的时候,你甚至可以不用管()号里面是什么,而直接看)号后面的东西,丝毫不影响你理解整个表达式。

    说了那么多,其实就是让你直接把:

    void(*signal(int sig,void(*func)(int)))(int)

    看成:

    void(*signal())(int)


    好了,找到了最靠近变量名的两个运算符了,那么接下来就要看优先级了,这直接决定了signal是个什么。查阅手册可知,()号的优先级是大于*号的,所以signal会先和()号匹配,于是signal首先是一个函数。知道了signal是个函数之后那就好办了,定义一个函数都需要提供什么?嗯,参数返回值。所以现在我们只要找到signal参数返回值就可以了。

    函数名后面的()里面的内容便是它的参数。所以我们刚刚忽略掉的()里面的内容就是这个函数的参数了。

    参数是:(int sig, void(*func)(int))

    一个整形变量:int sig
    一个函数指针:void(*func)(int)

    在这里别给我整个举一反三说func左右两边是*号和()号所以func它也是一个函数啊!

    在这里func只有左边没有右边,因为func*号是在()里面的,而()号里面就只有*funcfunc只能是和*号结合,所以func是一个指针。这个指针指向什么数据类型呢?在定义处把指针变量名func和指针符号*去掉,剩下的就是这个指针指向的数据类型了。

    指针指向的数据类型:void ()(int)

    ()号里面什么都没有,相当于这个()不存在

    最终指针指向的数据类型:void (int) 一个函数

    相当于函数void f(int a)去掉变量名f后的数据类型。所以func是一个函数指针,它指向函数f这种类型的函数。


    既然函数signal的参数找到了,那么把函数名signal和它的参数去掉,剩下的就是它的返回值了

    原型:

    void(*signal(int sig,void(*func)(int)))(int)

    去掉函数名和函数的参数之后:

    返回值是:void(*)(int)

    诶,有点眼熟。。。
    刚刚我们分析的signal的第二个参数是什么来着?

    第二个参数: void (*func)(int)
    要分析的返回值:void (*)(int)

    嗯,没错,signal的返回值就是func的类型。也是一个函数指针。而且,这两个东西对于signal来说其实是一样的

    如果在仅仅只讨论signal数据类型的时候

    void(*signal(int sig,void(*func)(int)))(int)

    其实就是

    void(*signal(int,void(*)(int)))(int)

    这两句在描述signal的数据类型的时候是等价的。因为数据类型本身就不包含对于非关键字的描述。


    所以,最后,我们总算是分析完signal的数据类型了。

    它首先是一个函数:

    signal()

    然后它的参数有两个,一个是整形变量,一个是指针,该指针指向的是void (int)这种类型的函数。

    signal(int sig,void(*func)(int))

    最后它有一个返回值,返回一个函数指针,这个指针也指向void (int)这种类型的函数。

    void(*signal(int sig,void(*func)(int)))(int)


    本文为十一一个人原创文章
    原文出处:https://blog.csdn.net/L_0x0b/article/details/89305392
    本人转载

    展开全文
  • mybatis association 复杂类型联合操作

    千次阅读 2016-11-17 15:46:59
    复杂类型联合 ; 许多查询结果合成这个类型 o 嵌套结果映射 – associations 能引用自身 , 或者从其它地方引用 · collection – 复杂类型集合 o 嵌套结果映射 – collections 能引用自身 , 或者从...

    高级结果映射

    MyBatis的创建基于这样一个思想:数据库并不是您想怎样就怎样的。虽然我们希望所有的数据库遵守第三范式或BCNF(修正的第三范式),但它们不是。如果有一个数据库能够完美映射到所有应用程序,也将是非常棒的,但也没有。结果集映射就是MyBatis为解决这些问题而提供的解决方案。例如,我们如何映射下面这条语句?

    [sql] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <select id="selectBlogDetails" parameterType="int" resultMap="detailedBlogResultMap">  
    2. select  
    3. B.id as blog_id,  
    4. B.title as blog_title,  
    5. B.author_id as blog_author_id,  
    6. A.id as author_id,  
    7. A.username as author_username,  
    8. A.password as author_password,  
    9. A.email as author_email,  
    10. A.bio as author_bio,  
    11. A.favourite_section as author_favourite_section,  
    12. P.id as post_id,  
    13. P.blog_id as post_blog_id,  
    14. P.author_id as post_author_id,  
    15. P.created_on as post_created_on,  
    16. P.section as post_section,  
    17. P.subject as post_subject,  
    18. P.draft as draft,  
    19. P.body as post_body,  
    20. C.id as comment_id,  
    21. C.post_id as comment_post_id,  
    22. C.name as comment_name,  
    23. C.comment as comment_text,  
    24. T.id as tag_id,  
    25. T.name as tag_name  
    26. from Blog B  
    27. left outer join Author A on B.author_id = A.id  
    28. left outer join Post P on B.id = P.blog_id  
    29. left outer join Comment C on P.id = C.post_id  
    30. left outer join Post_Tag PT on PT.post_id = P.id  
    31. left outer join Tag T on PT.tag_id = T.id  
    32. where B.id = #{id}  
    33. </select>  

    您可能想要把它映射到一个智能的对象模型,包括由一个作者写的一个博客,有许多文章(Post,帖子),每个文章由0个或者多个评论和标签。下面是一个复杂ResultMap 的完整例子(假定作者、博客、文章、评论和标签都是别名)。仔细看看这个例子,但是不用太担心,我们会一步步地来分析,一眼看上去可能让人沮丧,但是实际上非常简单的

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <resultMap id="detailedBlogResultMap" type="Blog">  
    2. <constructor>  
    3. <idArg column="blog_id" javaType="int"/>  
    4. </constructor>  
    5. <result property="title" column="blog_title"/>  
    6. <association property="author" column="blog_author_id" javaType=" Author">  
    7. <id property="id" column="author_id"/>  
    8. <result property="username" column="author_username"/>  
    9. <result property="password" column="author_password"/>  
    10. <result property="email" column="author_email"/>  
    11. <result property="bio" column="author_bio"/>  
    12. <result property="favouriteSection" column="author_favourite_section"/>  
    13. </association>  
    14. <collection property="posts" ofType="Post">  
    15. <id property="id" column="post_id"/>  
    16. <result property="subject" column="post_subject"/>  
    17. <association property="author" column="post_author_id" javaType="Author"/>  
    18. <collection property="comments" column="post_id" ofType=" Comment">  
    19. <id property="id" column="comment_id"/>  
    20. </collection>  
    21. <collection property="tags" column="post_id" ofType=" Tag" >  
    22. <id property="id" column="tag_id"/>  
    23. </collection>  
    24. <discriminator javaType="int" column="draft">  
    25. <case value="1" resultType="DraftPost"/>  
    26. </discriminator>  
    27. </collection>  
    28. </resultMap>  

    这个resultMap 的元素的子元素比较多,讨论起来比较宽泛。下面我们从概念上概览一下这个resultMap的元素。

     

    resultMap

    ·constructor实例化的时候通过构造器将结果集注入到类中

    oidArg– ID 参数; 将结果集标记为ID,以方便全局调用

    oarg注入构造器的结果集

    ·id结果集ID,将结果集标记为ID,以方便全局调用

    ·result注入一个字段或者javabean属性的结果

    ·association复杂类型联合;许多查询结果合成这个类型

    o嵌套结果映射– associations能引用自身,或者从其它地方引用

    ·collection复杂类型集合

    o嵌套结果映射– collections能引用自身,或者从其它地方引用

    ·discriminator使用一个结果值以决定使用哪个resultMap

    ocase基于不同值的结果映射

    §嵌套结果映射–case也能引用它自身, 所以也能包含这些同样的元素。它也可以从外部引用resultMap

     

    è最佳实践:逐步地生成resultMap,单元测试对此非常有帮助。如果您尝试一下子就生成像上面这样巨大的resultMap,可能会出错,并且工作起来非常吃力。从简单地开始,再一步步地扩展,并且进行单元测试。使用框架开发有一个缺点,它们有时像是一个黑合。为了确保达到您所预想的行为,最好的方式就是进行单元测试。这对提交bugs 也非常有用。

     

    下一节,我们一步步地查看这些细节。

    id, result元素

    <id property="id" column="post_id"/>

    <result property="subject" column="post_subject"/>

     

    这是最基本的结果集映射。id 和result 将列映射到属性或简单的数据类型字段(String, int, double, Date)

    这两者唯一不同的是,在比较对象实例时id 作为结果集的标识属性。这有助于提高总体性能,特别是应用缓存和嵌套结果映射的时候。

     

    Idresult属性如下:

     

    Attribute

    Description

    property

    映射数据库列的字段或属性。如果JavaBean 的属性与给定的名称匹配,就会使用匹配的名字。否则,MyBatis 将搜索给定名称的字段。两种情况下您都可以使用逗点的属性形式。比如,您可以映射到“username”,也可以映射到“address.street.number”

    column

    数据库的列名或者列标签别名。与传递给resultSet.getString(columnName)的参数名称相同。

    javaType

    完整Java类名或别名(参考上面的内置别名列表)。如果映射到一个JavaBean,那MyBatis 通常会自行检测到。然而,如果映射到一个HashMap,那您应该明确指定javaType 来确保所需行为。

    jdbcType

    这张表下面支持的JDBC类型列表列出的JDBC类型。这个属性只在insertupdatedelete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。如果您直接编写JDBC代码,在允许为空值的情况下需要指定这个类型。

    typeHandler

    我们已经在文档中讨论过默认类型处理器。使用这个属性可以重写默认类型处理器。它的值可以是一个TypeHandler实现的完整类名,也可以是一个类型别名。


    支持的JDBC类型

    MyBatis支持如下的JDBC类型:

    BIT

    FLOAT

    CHAR

    TIMESTAMP

    OTHER

    UNDEFINED

    TINYINT

    REAL

    VARCHAR

    BINARY

    BLOB

    NVARCHAR

    SMALLINT

    DOUBLE

    LONGVARCHAR

    VARBINARY

    CLOB

    NCHAR

    INTEGER

    NUMERIC

    DATE

    LONGVARBINARY

    BOOLEAN

    NCLOB

    BIGINT

    DECIMAL

    TIME

    NULL

    CURSOR

     



    Constructor元素

    <constructor>

    <idArg column="id" javaType="int"/>

    <arg column=”username” javaType=”String”/>

    </constructor>

     

    当属性与DTO,或者与您自己的域模型一起工作的时候,许多场合要用到不变类。通常,包含引用,或者查找的数据很少或者数据不会改变的的表,适合映射到不变类中。构造器注入允许您在类实例化后给类设值,这不需要通过public方法。MyBatis同样也支持private属性和JavaBeans的私有属性达到这一点,但是一些用户可能更喜欢使用构造器注入。构造器元素可以做到这点。

     

    考虑下面的构造器:

     

    public class User {

    //…

    public User(int id, String username) {

    //…

    }

    //…

    }

     

    为了将结果注入构造器,MyBatis需要使用它的参数类型来标记构造器。Java没有办法通过参数名称来反射获得。因此当创建constructor 元素,确保参数是按顺序的并且指定了正确的类型。

     

    <constructor>

    <idArg column="id" javaType="int"/>

    <arg column=”username” javaType=”String”/>

    </constructor>

     

    其它的属性与规则与idresult元素的一样。

     

    Attribute

    Description

    column

    数据库的列名或者列标签别名。与传递给resultSet.getString(columnName)的参数名称相同。

    javaType

    完整java类名或别名(参考上面的内置别名列表)。如果映射到一个JavaBean,那MyBatis 通常会自行检测到。然而,如果映射到一个HashMap,那您应该明确指定javaType 来确保所需行为。

    jdbcType

    支持的JDBC类型列表中列出的JDBC类型。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。如果您直接编写JDBC代码,在允许为空值的情况下需要指定这个类型。

    typeHandler

    我们已经在文档中讨论过默认类型处理器。使用这个属性可以重写默认类型处理器。它的值可以是一个TypeHandler实现的完整类名,也可以是一个类型别名。



    Association元素

    <association property="author" column="blog_author_id" javaType=" Author">

    <id property="id" column="author_id"/>

    <result property="username" column="author_username"/>

    </association>

    Association元素处理“has-one”(一对一)这种类型关系。比如在我们的例子中,一个Blog有一个Author。联合映射与其它的结果集映射工作方式差不多,指定propertycolumnjavaType(通常MyBatis会自动识别)、jdbcType(如果需要)、typeHandler

    不同的地方是您需要告诉MyBatis 如何加载一个联合查询。MyBatis使用两种方式来加载:

    ·Nested Select:通过执行另一个返回预期复杂类型的映射SQL语句(即引用外部定义好的SQL语句块)。

    ·Nested Results:通过嵌套结果映射(nested result mappings)来处理联接结果集(joined results)的重复子集。

    首先,让我们检查一下元素属性。正如您看到的,它不同于普通只有selectresultMap属性的结果映射。

    Attribute

    Description

    property

    映射数据库列的字段或属性。如果JavaBean 的属性与给定的名称匹配,就会使用匹配的名字。否则,MyBatis 将搜索给定名称的字段。两种情况下您都可以使用逗点的属性形式。比如,您可以映射到”username”,也可以映射到更复杂点的”address.street.number”

    column

    数据库的列名或者列标签别名。与传递给resultSet.getString(columnName)的参数名称相同。

    注意: 在处理组合键时,您可以使用column= “{prop1=col1,prop2=col2}”这样的语法,设置多个列名传入到嵌套查询语句。这就会把prop1prop2设置到目标嵌套选择语句的参数对象中。

    javaType

    完整java类名或别名(参考上面的内置别名列表)。如果映射到一个JavaBean,那MyBatis 通常会自行检测到。然而,如果映射到一个HashMap,那您应该明确指定javaType 来确保所需行为。

    jdbcType

    支持的JDBC类型列表中列出的JDBC类型。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。如果您直接编写JDBC代码,在允许为空值的情况下需要指定这个类型。

    typeHandler

    我们已经在文档中讨论过默认类型处理器。使用这个属性可以重写默认类型处理器。它的值可以是一个TypeHandler实现的完整类名,也可以是一个类型别名。

    联合嵌套选择(Nested Select for Association

    select

    通过这个属性,通过ID引用另一个加载复杂类型的映射语句。从指定列属性中返回的值,将作为参数设置给目标select 语句。表格下方将有一个例子。注意:在处理组合键时,您可以使用column={prop1=col1,prop2=col2}”这样的语法,设置多个列名传入到嵌套语句。这就会把prop1prop2设置到目标嵌套语句的参数对象中。

     例如:  

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <resultMap id=”blogResult” type=”Blog”>  
    2. <association property="author" column="blog_author_id" javaType="Author"  
    3. select=”selectAuthor”/>  
    4. </resultMap>  
    5.    
    6. <select id=”selectBlog” parameterType=”int” resultMap=”blogResult”>  
    7. SELECT * FROM BLOG WHERE ID = #{id}  
    8. </select>  
    9.    
    10. <select id=”selectAuthor” parameterType=”int” resultType="Author">  
    11. SELECT * FROM AUTHOR WHERE ID = #{id}  
    12. </select>  
    13. <wbr>  

    我们使用两个select语句:一个用来加载Blog,另一个用来加载AuthorBlogresultMap 描述了使用“selectAuthor”语句来加载author的属性。

    如果列名和属性名称相匹配的话,所有匹配的属性都会自动加载。

     

    译者注:

    上面的例子,首先执行<select id=selectBlog”>,执行结果存放到<resultMap id=blogResult”>结果映射中。“blogResult”是一个Blog类型,从<select id=selectBlog”>查出的数据都会自动赋值给”blogResult”的与列名匹配的属性,这时blog_idtitle等就被赋值了。同时“blogResult”还有一个关联属性"Author",执行嵌套查询select=”selectAuthor”后,Author对象的属性idusernamepasswordemailbio也被赋于数据库匹配的值。

     

    Blog

    {

    blog_id;

    title;

    Author author

    {

    id;

    username;

    password;

    email;

    bio;

     

    }

     

    }

     

    虽然这个方法简单,但是对于大数据集或列表查询,就不尽如人意了。这个问题被称为“N+1 选择问题”(N+1 Selects Problem)。概括地说,N+1选择问题是这样产生的:

    ·您执行单条SQL语句去获取一个列表的记录( “+1”)

    ·对列表中的每一条记录,再执行一个联合select 语句来加载每条记录更加详细的信息(“N”)

    这个问题会导致成千上万的SQL语句的执行,因此并非总是可取的。

    上面的例子,MyBatis可以使用延迟加载这些查询,因此这些查询立马可节省开销。然而,如果您加载一个列表后立即迭代访问嵌套的数据,这将会调用所有的延迟加载,因此性能会变得非常糟糕。

    鉴于此,这有另外一种方式。

    联合嵌套结果集(Nested Results for Association 

    resultMap

    一个可以映射联合嵌套结果集到一个适合的对象视图上的ResultMap 。这是一个替代的方式去调用另一个select 语句。它允许您去联合多个表到一个结果集里。这样的结果集可能包括冗余的、重复的需要分解和正确映射到一个嵌套对象视图的数据组。简言之,MyBatis 让您把结果映射‘链接’到一起,用来处理嵌套结果。举个例子会更好理解,例子在表格下方。

    您已经在上面看到了一个非常复杂的嵌套联合的例子,接下的演示的例子会更简单一些。我们把BlogAuthor表联接起来查询,而不是执行分开的查询语句:

    [sql] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <select id="selectBlog" parameterType="int" resultMap="blogResult">  
    2. select  
    3. B.id as blog_id,  
    4. B.title as blog_title,  
    5. B.author_id as blog_author_id,  
    6. A.id as author_id,  
    7. A.username as author_username,  
    8. A.password as author_password,  
    9. A.email as author_email,  
    10. A.bio as author_bio  
    11. from Blog B left outer join Author A on B.author_id = A.id  
    12. where B.id = #{id}  
    13. </select>  

    注意到这个连接(join),要确保所有的别名都是唯一且无歧义的。这使映射容易多了,现在我们来映射结果集:

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <resultMap id="blogResult" type="Blog">  
    2. <id property=”blog_id” column="id" />  
    3. <result property="title" column="blog_title"/>  
    4. <association property="author" column="blog_author_id" javaType="Author"  
    5. resultMap=”authorResult”/>  
    6. </resultMap>  
    7.    
    8. <resultMap id="authorResult" type="Author">  
    9. <id property="id" column="author_id"/>  
    10. <result property="username" column="author_username"/>  
    11. <result property="password" column="author_password"/>  
    12. <result property="email" column="author_email"/>  
    13. <result property="bio" column="author_bio"/>  
    14. </resultMap>  

    在上面的例子中,您会看到Blog的作者(“author”)联合一个“authorResult”结果映射来加载Author实例。

    重点提示:id元素在嵌套结果映射中扮演了非常重要的角色,您应该总是指定一个或多个属性来唯一标识这个结果集。事实上,如果您没有那样做,MyBatis也会工作,但是会导致严重性能开销。选择尽量少的属性来唯一标识结果,而使用主键是最明显的选择(即使是复合主键)。

    上面的例子使用一个扩展的resultMap 元素来联合映射。这可使Author结果映射可重复使用。然后,如果您不需要重用它,您可以直接嵌套这个联合结果映射。下面例子就是使用这样的方式: 

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <resultMap id="blogResult" type="Blog">  
    2. <id property=”blog_id” column="id" />  
    3. <result property="title" column="blog_title"/>  
    4. <association property="author" column="blog_author_id" javaType="Author">  
    5. <id property="id" column="author_id"/>  
    6. <result property="username" column="author_username"/>  
    7. <result property="password" column="author_password"/>  
    8. <result property="email" column="author_email"/>  
    9. <result property="bio" column="author_bio"/>  
    10. </association>  
    11. </resultMap>  

    在上面的例子中您已经看到如果处理“一对一”(“has one”)类型的联合查询。但是对于“一对多”(“has many”)的情况如果处理呢?这个问题在下一节讨论。

    Collection元素 

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <collection property="posts" ofType="domain.blog.Post">  
    2. <id property="id" column="post_id"/>  
    3. <result property="subject" column="post_subject"/>  
    4. <result property="body" column="post_body"/>  
    5. </collection>  

    collection元素的作用差不多和association元素的作用一样。事实上,它们非常相似,以至于再对相似点进行描述会显得冗余,因此我们只关注它们的不同点。

    继续我们上面的例子,一个Blog只有一个Author。但一个Blog有许多帖子(文章)。在Blog类中,会像下面这样定义相应属性: 

    private List<Post> posts;

    映射一个嵌套结果集到一个列表,我们使用collection元素。就像association 元素那样,我们使用嵌套查询,或者从连接中嵌套结果集。 

    集合嵌套选择(Nested Select for Collection

    首先我们使用嵌套选择来加载Blog的文章。 

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <resultMap id=”blogResult” type=”Blog”>  
    2. <collection property="posts" javaType=”ArrayList” column="blog_id"  
    3. ofType="Post" select=”selectPostsForBlog”/>  
    4. </resultMap>  
    5.    
    6. <select id=”selectBlog” parameterType=”int” resultMap=”blogResult”>  
    7. SELECT * FROM BLOG WHERE ID = #{id}  
    8. </select>  
    9.    
    10. <select id=”selectPostsForBlog” parameterType=”int” resultType="Author">  
    11. SELECT * FROM POST WHERE BLOG_ID = #{id}  
    12. </select>  

    一看上去这有许多东西需要注意,但大部分看起与我们在association元素中学过的相似。首先,您会注意到我们使用了collection元素,然后会注意到一个新的属性“ofType”。这个元素是用来区别JavaBean属性(或者字段)类型和集合所包括的类型。因此您会读到下面这段代码。

     

    <collection property="posts" javaType=”ArrayList” column="blog_id"

    ofType="Post" select=”selectPostsForBlog”/>

    è理解为:“一个名为posts,类型为PostArrayList集合(A collection of posts in an ArrayList of type Post)” 。

    javaType属性不是必须的,通常MyBatis 会自动识别,所以您通常可以简略地写成:

    <collection property="posts" column="blog_id" ofType="Post"

    select=”selectPostsForBlog”/>



    集合的嵌套结果集(Nested Results for Collection

    这时候,您可能已经猜出嵌套结果集是怎样工作的了,因为它与association非常相似,只不过多了一个属性“ofType”

    让我们看下这个SQL 

    [sql] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <select id="selectBlog" parameterType="int" resultMap="blogResult">  
    2. select  
    3. B.id as blog_id,  
    4. B.title as blog_title,  
    5. B.author_id as blog_author_id,  
    6. P.id as post_id,  
    7. P.subject as post_subject,  
    8. P.body as post_body,  
    9. from Blog B  
    10. left outer join Post P on B.id = P.blog_id  
    11. where B.id = #{id}  
    12. </select>  

    同样,我们把BlogPost两张表连接在一起,并且也保证列标签名在映射的时候是唯一且无歧义的。现在将BlogPost的集合映射在一起是多么简单:

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <resultMap id="blogResult" type="Blog">  
    2. <id property=”id” column="blog_id" />  
    3. <result property="title" column="blog_title"/>  
    4. <collection property="posts" ofType="Post">  
    5. <id property="id" column="post_id"/>  
    6. <result property="subject" column="post_subject"/>  
    7. <result property="body" column="post_body"/>  
    8. </collection>  
    9. </resultMap>  

    再次强调一下,id 元素是非常重要的。如果您忘了或者不知道id 元素的作用,请先读一下上面association一节。

    如果希望结果映射有更好的可重用性,您可以使用下面的方式:

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <resultMap id="blogResult" type="Blog">  
    2. <id property=”id” column="blog_id" />  
    3. <result property="title" column="blog_title"/>  
    4. <collection property="posts" ofType="Post" resultMap=”blogPostResult”/>  
    5. </resultMap>  
    6.    
    7. <resultMap id="blogPostResult" type="Post">  
    8. <id property="id" column="post_id"/>  
    9. <result property="subject" column="post_subject"/>  
    10. <result property="body" column="post_body"/>  
    11. </resultMap>  

    èNote:在您的映射中没有深度、宽度、联合和集合数目的限制。但应该谨记,在进行映射的时候也要考虑性能的因素。应用程序的单元测试和性能测试帮助您发现最好的方式可能要花很长时间。但幸运的是,MyBatis允许您以后可以修改您的想法,这时只需要修改少量代码就行了。

    关于高级联合和集合映射是一个比较深入的课题,文档只能帮您了解到这里,多做一些实践,一切将很快变得容易理解。


    Discriminator元素

    <discriminator javaType="int" column="draft">

    <case value="1" resultType="DraftPost"/>

    </discriminator>

     

    有时候一条数据库查询可能会返回包括各种不同的数据类型的结果集。Discriminator(识别器)元素被设计来处理这种情况,以及其它像类继承层次情况。识别器非常好理解,它就像java里的switch语句。

     

    Discriminator定义要指定columnjavaType属性。列是MyBatis将要取出进行比较的值,javaType用来确定适当的测试是否正确运行(虽然String在大部分情况下都可以工作),例:  

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <resultMap id="vehicleResult" type="Vehicle">  
    2. <id property=”id” column="id" />  
    3. <result property="vin" column="vin"/>  
    4. <result property="year" column="year"/>  
    5. <result property="make" column="make"/>  
    6. <result property="model" column="model"/>  
    7. <result property="color" column="color"/>  
    8. <discriminator javaType="int" column="vehicle_type">  
    9. <case value="1" resultMap="carResult"/>  
    10. <case value="2" resultMap="truckResult"/>  
    11. <case value="3" resultMap="vanResult"/>  
    12. <case value="4" resultMap="suvResult"/>  
    13. </discriminator>  
    14. </resultMap>  

    在这个例子中,MyBatis将会从结果集中取出每条记录,然后比较它的vehicle type的值。如果匹配任何discriminator中的case,它将使用由case指定的resultMap。这是排它性的,换句话说,其它的caseresultMap将会被忽略(除非使用我们下面说到的extended)。如果没有匹配到任何caseMyBatis只是简单的使用定义在discriminator块外面的resultMap。所以,如果carResult像下面这样定义:

     

    <resultMap id="carResult" type="Car">

    <result property=”doorCount” column="door_count" />

    </resultMap>

     

    那么,只有doorCount属性会被加载。这样做是为了与识别器cases群组完全独立开来,哪怕它与上一层的resultMap一点关系都没有。在刚才的例子里我们当然知道carsvehicles的关系,a Car is-a Vehicle。因此,我们也要把其它属性加载进来。我们要稍稍改动一下resultMap

     

    <resultMap id="carResult" type="Car"extends=”vehicleResult”>

    <result property=”doorCount” column="door_count" />

    </resultMap>

     

    现在,vehicleResultcarResult的所有属性都会被加载。

    可能有人会认为这样扩展映射定义有一点单调了,所以还有一种可选的更加简单明了的映射风格语法。例如:

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <resultMap id="vehicleResult" type="Vehicle">  
    2. <id property=”id” column="id" />  
    3. <result property="vin" column="vin"/>  
    4. <result property="year" column="year"/>  
    5. <result property="make" column="make"/>  
    6. <result property="model" column="model"/>  
    7. <result property="color" column="color"/>  
    8. <discriminator javaType="int" column="vehicle_type">  
    9. <case value="1" resultType="carResult">  
    10. <result property=”doorCount” column="door_count" />  
    11. </case>  
    12. <case value="2" resultType="truckResult">  
    13. <result property=”boxSize” column="box_size" />  
    14. <result property=”extendedCab” column="extended_cab" />  
    15. </case>  
    16. <case value="3" resultType="vanResult">  
    17. <result property=”powerSlidingDoor” column="power_sliding_door" />  
    18. </case>  
    19. <case value="4" resultType="suvResult">  
    20. <result property=”allWheelDrive” column="all_wheel_drive" />  
    21. </case>  
    22. </discriminator>  
    23. </resultMap>  

    è记住:对于这么多的结果映射,如果您不指定任何的结果集,那么MyBatis 会自动地将列名与属性相匹配。所以上面所举的例子比实际中需要的要详细。尽管如此,大部分数据库有点复杂,并且它并不是所有情况都是完全可以适用的。

    Cache元素

    MyBatis包含一个强大的、可配置、可定制的查询缓存机制。MyBatis 3 的缓存实现有了许多改进,使它更强大更容易配置。默认的情况,缓存是没有开启,除了会话缓存以外,它可以提高性能,且能解决循环依赖。开启二级缓存,您只需要在SQL映射文件中加入简单的一行:

     

    <cache/>

     

    这句简单的语句作用如下:

    ·所有映射文件里的select语句的结果都会被缓存。

    ·所有映射文件里的insertupdatedelete语句执行都会清空缓存

    ·缓存使用最近最少使用算法(LRU)来回收

    ·缓存不会被设定的时间所清空。

    ·每个缓存可以存储1024 个列表或对象的引用(不管查询方法返回的是什么)。

    ·缓存将作为“读/写”缓存,意味着检索的对象不是共享的且可以被调用者安全地修改,而不会被其它调用者或者线程干扰。

    所有这些特性都可以通过cache元素进行修改。例如:

    <cache

    eviction="FIFO"

    flushInterval="60000"

    size="512"

    readOnly="true"/>

     

    这种高级的配置创建一个每60秒刷新一次的FIFO 缓存,存储512个结果对象或列表的引用,并且返回的对象是只读的。因此在不用的线程里的调用者修改它们可能会引用冲突

     

    可用的回收算法如下:

    ·LRU最近最少使用:移出最近最长时间内都没有被使用的对象。

    ·FIFO先进先出:移除最先进入缓存的对象。

    ·SOFT软引用: 基于垃圾回收机制和软引用规则来移除对象(空间内存不足时才进行回收)。

    ·WEAK弱引用:基于垃圾回收机制和弱引用规则(垃圾回收器扫描到时即进行回收)。

    默认使用LRU

    flushInterval:设置任何正整数,代表一个以毫秒为单位的合理时间。默认是没有设置,因此没有刷新间隔时间被使用,在语句每次调用时才进行刷新。

    Size属性可以设置为一个正整数,您需要留意您要缓存对象的大小和环境中可用的内存空间。默认是1024

    readOnly属性可以被设置为true 或false。只读缓存将对所有调用者返回同一个实例。因此这些对象都不能被修改,这可以极大的提高性能。可写的缓存将通过序列化来返回一个缓存对象的拷贝。这会比较慢,但是比较安全。所以默认值是false

     

    使用自定义缓存

    除了上面已经定义好的缓存方式,您能够通过您自己的缓存实现来完全重写缓存行为,或者通过创建第三方缓存解决方案的适配器。

    <cache type=”com.domain.something.MyCustomCache”/>

    这个例子演示了如果自定义缓存实现。由type指定的类必须实现org.mybatis.cache.Cache接口。这个接口是MyBatis框架比较复杂的接口之一,先给个示例:

    [java] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. public interface Cache {  
    2. String getId();  
    3. int getSize();  
    4. void putObject(Object key, Object value);  
    5. Object getObject(Object key);  
    6. boolean hasKey(Object key);  
    7. Object removeObject(Object key);  
    8. void clear();  
    9. ReadWriteLock getReadWriteLock();  
    10. }  

    要配置您的缓存,简单地添加一个公共的JavaBeans 属性到您的缓存实现中,然后通过cache 元素设置属性进行传递,下面示例,将在您的缓存实现上调用一个setCacheFile(String file)方法。

     

    <cache type=”com.domain.something.MyCustomCache”>

    <property name=”cacheFile” value=”/tmp/my-custom-cache.tmp”/>

    </cache>

     

    您可以使用所有简单的JavaBeans属性,MyBatis会自动进行转换。

    需要牢记的是一个缓存配置和缓存实例都绑定到一个SQL Map 文件命名空间。因此,所有的这个相同命名空间的语句也都和这个缓存绑定。语句可以修改如何与这个缓存相匹配,或者使用两个简单的属性来完全排除它们自己。默认情况下,语句像下面这样来配置:

    <select ... flushCache=”false” useCache=”true”/>

    <insert ... flushCache=”true”/>

    <update ... flushCache=”true”/>

    <delete ... flushCache=”true”/> 

    因为有默认值,所以您不需要使用这种方式明确地配置这些语句。如果您想改变默认的动作,只需要设置flushCacheuseCache 属性即可。举个例子来说,在许多的场合下您可能排除缓存中某些特定的select语句。或者您想用select语句清空缓存。同样的,您也可能有一些update 语句在执行的时候不需要清空缓存。

    cache-ref元素

    回想上一节,我们仅仅只是讨论在某一个命名空间里使用或者刷新缓存。但有可能您想要在不同的命名空间里共享同一个缓存配置或者实例。在这种情况下,您就可以使用cache-ref 元素来引用另外一个缓存。

    <cache-ref namespace=”com.someone.application.data.SomeMapper”/>


    例子:

    接下来看下一个完整的sqlmap可以运行的ibatis文件:

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <?xml version="1.0" encoding="UTF-8" ?>  
    2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
    3. <mapper namespace="com.chudong.xy.dao.item.IItemDao">  
    4.   
    5.     <resultMap type="Item" id="itemResultMap" >  
    6.         <id property="id" column="id" />  
    7.         <result property="numIid" column="num_iid" />  
    8.         <result property="title" column="title" />  
    9.         <result property="subTitle" column="sub_title" />  
    10.         <result property="cat" column="cat" />  
    11.         <result property="picUrl" column="pic_url" />  
    12.         <result property="picThumUrl" column="pic_thum_url" />  
    13.         <result property="outerId" column="outer_id" />  
    14.         <result property="props" column="props" />      
    15.         <result property="marketPrice" column="market_price" />     
    16.         <result property="price" column="price" />      
    17.         <result property="num" column="num" />      
    18.         <result property="listTime" column="list_time" />   
    19.         <result property="delistTime" column="delist_time" />   
    20.         <result property="sales" column="sales" />      
    21.         <result property="created" column="created" />      
    22.         <result property="modified" column="modified" />    
    23.         <result property="enableStatus" column="enable_status" />   
    24.     </resultMap>  
    25.       
    26.     <!-- 与活动和搭配套餐进行关联 -->  
    27.     <resultMap type="Item" id="itemDetailResultMap" extends="itemResultMap">  
    28.         <association property="active" column="id" javaType="com.chudong.xy.domain.active.Active" select="selectActive"/>  
    29.         <collection property="withPackages" column="id" javaType="java.util.ArrayList" ofType="com.chudong.xy.domain.item.Item" select="selectWithPackages"/>    
    30.     </resultMap>  
    31.       
    32.     <resultMap type="Active" id="activeResultMap">  
    33.         <id property="id" column="id" />  
    34.         <result property="title" column="title" />  
    35.         <result property="picUrl" column="pic_url" />  
    36.     </resultMap>  
    37.   
    38.     <!-- 查询单个商品,并装载活动和搭配套餐 -->  
    39.     <select id="queryDetailById" parameterType="long" resultMap="itemDetailResultMap">  
    40.         SELECT * from item where id = #{id} and enable_status = 1  
    41.     </select>  
    42.       
    43.     <!-- 根据商品id得到一个活动 -->  
    44.     <select id="selectActive" parameterType="long" resultMap="activeResultMap">    
    45.         select id,title,pic_url from active where id = (select active_id from active_item where item_id = #{id} and enable_status=1 limit 0,1)   
    46.     </select>  
    47.     <!-- 根据商品id得到搭配套餐中的商品 -->  
    48.     <select id="selectWithPackages" parameterType="long" resultMap="itemResultMap">  
    49.         select * from item where id in(select mapping_id from item_relation where item_id = #{id} and type=1 and enable_status=1)  
    50.     </select>  
    51.       
    52. </mapper>  

    java类:

    [java] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. public class Item implements Serializable {  
    2.   
    3.     /** 
    4.      *  
    5.      */  
    6.     private static final long serialVersionUID = 3969923837162162882L;  
    7.   
    8.     /** 
    9.      *  
    10.      */  
    11.     private Long id;  
    12.       
    13.     /** 
    14.      * 商品编码 
    15.      */  
    16.     private Long numIid;  
    17.       
    18.     /** 
    19.      * 商品标题 
    20.      */  
    21.     private String title;  
    22.       
    23.     /** 
    24.      * 副标题 
    25.      */  
    26.     private String subTitle;  
    27.       
    28.     /** 
    29.      * 商品类目, 
    30.      */  
    31.     private Integer cat;  
    32.       
    33.     /** 
    34.      * 商品主图地址 
    35.      */  
    36.     private String picUrl;  
    37.       
    38.     /** 
    39.      * 商品缩略图 
    40.      */  
    41.     private String picThumUrl;  
    42.       
    43.     /** 
    44.      * 商家编码, 
    45.      */  
    46.     private String outerId;  
    47.       
    48.     /** 
    49.      * 商品属性 
    50.      */  
    51.     private String props;  
    52.       
    53.     /** 
    54.      * 销售价格 
    55.      */  
    56.     private Double price;  
    57.       
    58.     /** 
    59.      * 商品的市场价格 
    60.      */  
    61.     private Double marketPrice;  
    62.       
    63.     /** 
    64.      * 商品数量 
    65.      */  
    66.     private Integer num;  
    67.       
    68.     /** 
    69.      * 商品上架时间 
    70.      */  
    71.     private Date listTime;  
    72.       
    73.     /** 
    74.      * 商品下架时间 
    75.      */  
    76.     private Date delistTime;  
    77.       
    78.     /** 
    79.      * 商品销量 
    80.      */  
    81.     private Integer sales;    
    82.   
    83.     private Date created;  
    84.       
    85.     private Date modified;  
    86.       
    87.     /** 
    88.      * 数据可用状态,0表示不可用,1表示可用 
    89.      */  
    90.     private Integer enableStatus;  
    91.       
    92.     /** 
    93.      * 商品的url,不存储数据库 
    94.      */  
    95.     private String itemTaobaoUrl;  
    96.       
    97.     /** 
    98.      * 商品所关联的活动 
    99.      */  
    100.     private Active active;  
    101.       
    102.     /** 
    103.      * 该商品的搭配套餐 
    104.      */  
    105.     private List<Item> withPackages;  
    106.   
    107.     //省略了set get方法  
    108. }  




    展开全文
  • HashMap是存放引用类型数据的容器,只能存放引用数据类型,不能存放如int、long等基础类型的数据。 这里用实际的例子来演示如何解析HashMap,在这个Sample中,HashMap作为参数从Java传递到Native(C/C++)层,然后...

    前提

    Java HashMap 是基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用null值和null键。HashMap是存放引用类型数据的容器,只能存放引用数据类型,不能存放如int、long等基础类型的数据。
    这里用实际的例子来演示如何解析HashMap,在这个Sample中,HashMap作为参数从Java传递到Native(C/C++)层,然后在C代码中解析HashMap中的数据。Java中使用HashMap<String, Integer>存放学生成绩 “课程 - 分数”键值对,然后把这个学生成绩HashMap数据传递到Native中,用C/C++解析其中的数据,然后以字符串的形式把成绩信息返回到Java。

    准备工作:Java中构造和解析HashMap

    首先,在Java中构造一个课程分数HashMap。

    public HashMap<String, Integer> constructHashMap() {
        HashMap<String, Integer> courseScore = new HashMap<>();
        courseScore.put("Chinese", 90);
        courseScore.put("Math", 95);
        courseScore.put("Physics", 100);
        courseScore.put("Biology", 85);
        return courseScore;
    }

    在Java中实现返回字符串需求的话,可以使用下面的代码来解析HashMap中的数据。

    public String parseHashMap(HashMap<String, Integer> courseScore){
        StringBuilder info = new StringBuilder();
    
        Iterator iter = courseScore.entrySet().iterator();
        while (iter.hasNext()){
            Map.Entry entry = (Map.Entry) iter.next();
            String course = (String) entry.getKey();
            Integer score = (Integer) entry.getValue();
    
            info.append(course+": ");
            info.append(score+". ");
        }
        return String.valueOf(info);
    }

    在主函数中依次调用constructHashMap()parseHashMap()函数,会返回如下所示的字符串:

    "Physics: 100. Math: 95. Chinese: 90. Biology: 85. "

    Native层虽然使用的是C/C++编程语言,但是使用的确实Java的处理逻辑。在Native中解析HashMap数据结构就要完全按照parseHashMap()函数的逻辑进行数据处理。说白了,就是使用C/C++来模仿Java完成相应操作。
    在Native中模仿Java操作其实不难,只是比较 繁琐 !本来Java一句代码可能就需要四五句甚至更多语句来实现相同的功能。为了在Native中能更容易的模仿Java实现,现在我们来把上面的parseHashMap()函数中的语句尽量使用简单语句来实现。

    public String parseHashMap2(HashMap<String, Integer> courseScore){
        String info = "";
    
        Set set = courseScore.entrySet();
        Iterator iter = set.iterator();
    
        while (iter.hasNext()){
            Map.Entry entry = (Map.Entry) iter.next();
            String course = (String) entry.getKey();
            info = info + course + ": ";
    
            Integer score = (Integer) entry.getValue();
            info = info + score.intValue() + ". ";
        }
        return info;
    }

    进入正题:用C/C++解析Java HashMap

    首先Java中声明Native函数。

    public class JniManager {
        ...
        public native String nGetHashMapInfo(HashMap<String, Integer> info);
    }

    JNI_OnLoad函数中,函数名映射关系结构体JNINativeMethod中有如下描述。

    static const JNINativeMethod gMethods[] = {
            {"nGetHashMapInfo", "(Ljava/util/HashMap;)Ljava/lang/String;", (void *) jniGetHashMapInfo}
    };
    
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
        ...
        jint count = sizeof(gMethods) / sizeof(gMethods[0]);
    
        if (env->RegisterNatives(clazz, gMethods, count) != JNI_OK) {
            return result;
        }
        ....
    }

    在上一篇博客【Android JNI】在C/C++中调用Java中讲到,调用Java方法的步骤为:1. 获取类名jclass,2.获取方法ID jmethodID,3. 使用实例对象jobject和jmethod调用相应的方法。也就是说每次使用一个方法,都要先通过类获取到方法ID,并且要得到对象jobject
    我们在jniGetHashMapInfo()函数中解析Java HashMap<String, Integer>结构的课程成绩数据。

    jstring jniGetHashMapInfo(JNIEnv *env, jobject object, jobject hashMapInfo) {
        // 用于拼接字符串的数组
        char buff[100] = {0};
        // 用于拼接字符串的“游标”指针
        char *pos = buff;
    
        if (hashMapInfo == NULL)
            return env->NewStringUTF(buff);
    
        // 获取HashMap类entrySet()方法ID
        jclass hashmapClass = env->FindClass("java/util/HashMap");
        jmethodID entrySetMID = env->GetMethodID(hashmapClass, "entrySet", "()Ljava/util/Set;");
        // 调用entrySet()方法获取Set对象
        jobject setObj = env->CallObjectMethod(hashMapInfo, entrySetMID);
        // 调用size()方法获取HashMap键值对数量
    //  jmethodID sizeMID = env->GetMethodID(hashmapClass, "size", "()I");
    //  jint size = env->CallIntMethod(hashMapInfo, sizeMID);
    
        // 获取Set类中iterator()方法ID
        jclass setClass = env->FindClass("java/util/Set");
        jmethodID iteratorMID = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
        // 调用iterator()方法获取Iterator对象
        jobject iteratorObj = env->CallObjectMethod(setObj, iteratorMID);
    
        // 获取Iterator类中hasNext()方法ID
        // 用于while循环判断HashMap中是否还有数据
        jclass iteratorClass = env->FindClass("java/util/Iterator");
        jmethodID hasNextMID = env->GetMethodID(iteratorClass, "hasNext", "()Z");
        // 获取Iterator类中next()方法ID
        // 用于读取HashMap中的每一条数据
        jmethodID nextMID = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
    
        // 获取Map.Entry类中getKey()和getValue()的方法ID
        // 用于读取“课程-分数”键值对,注意:内部类使用$符号表示
        jclass entryClass = env->FindClass("java/util/Map$Entry");
        jmethodID getKeyMID = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
        jmethodID getValueMID = env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
    
        // HashMap只能存放引用数据类型,不能存放int等基本数据类型
        // 使用Integer类的intValue()方法获取int数据
        jclass integerClass = env->FindClass("java/lang/Integer");
        jmethodID valueMID = env->GetMethodID(integerClass, "intValue", "()I");
    
        // 循环检测HashMap中是否还有数据
        while (env->CallBooleanMethod(iteratorObj, hasNextMID)) {
            // 读取一条数据
            jobject entryObj = env->CallObjectMethod(iteratorObj, nextMID);
    
            // 提取数据中key值:String类型课程名字
            jstring courseJS = (jstring) env->CallObjectMethod(entryObj, getKeyMID);
            if (courseJS == NULL)   // HashMap允许null类型
                continue;
            // jstring转C风格字符串
            const char *courseStr = env->GetStringUTFChars(courseJS, NULL);
    
            // 提取数据中value值:Integer类型分数,并转为int类型
            jobject scoreObj = env->CallObjectMethod(entryObj, getValueMID);
            if (scoreObj == NULL)
                continue;
            int score = (int) env->CallIntMethod(scoreObj, valueMID);
    
            // 拼接字符串,sprintf函数返回拼接字符个数
            int strLen = sprintf(pos, "%s: ", courseStr);
            pos += strLen;
            int numLen = sprintf(pos, "%d. ", score);
            pos += numLen;
    
            // 释放UTF字符串资源
            env->ReleaseStringUTFChars(courseJS, courseStr);
            // 释放JNI局部引用资源
            env->DeleteLocalRef(entryObj);
            env->DeleteLocalRef(courseJS);
            env->DeleteLocalRef(scoreObj);
        }
    
        // 释放JNI局部引用: jclass jobject
        env->DeleteLocalRef(hashmapClass);
        env->DeleteLocalRef(setObj);
        env->DeleteLocalRef(setClass);
        env->DeleteLocalRef(iteratorObj);
        env->DeleteLocalRef(iteratorClass);
        env->DeleteLocalRef(entryClass);
        env->DeleteLocalRef(integerClass);
    
        // 生成jstring字符串并返回
        return env->NewStringUTF(buff);
    }
    

    调用public native String nGetHashMapInfo(HashMap<String, Integer> info); 函数 和 调用public String parseHashMap2(HashMap<String, Integer> courseScore)函数效果一样,都能返回下面这个成绩信息字符串。

    "Physics: 100. Math: 95. Chinese: 90. Biology: 85. "

    如果疑问,欢迎留言。

    展开全文
  • 问:Axis2中到底能否传递复杂以及自定义对象?  答:肯定可以    那如何传递呢?在开发过程中,基本类型已经不能满足要求。所以才有有相应的List,Map,以及User对象,Dog对象等等。  比如传递User对象,...
      问:Axis2中到底能否传递复杂以及自定义对象?
      答:肯定可以
     
      那如何传递呢?在开发过程中,基本类型已经不能满足要求。所以才有有相应的List,Map,以及User对象,Dog对象等等。
      比如传递User对象,我们想象一下,到底我们该怎么样,把这个对象传递过去呢?我们再根据前面讲的Webservice传递协议,该如何办呢?实际上发给服务器中是怎么样的格式呢?
      在做Webservice时,肯定很好奇,到底这个东东是如何传递过去的?就宛如,我们学计算机时,计算机处理的信息全部都是二进制,当我们问那图片,视频,怎么运作的在计算机?老师统统告是二进制。虽然我们知道二进制是0011这种形式,但是不是仍然有种想钻到计算机中,探个究竟,想真实的体会,或亲眼见识一下?
      所以呢,把抽象的东西具体化,在真实的环境中再现,这样才能深入理解其中的原理。

      那我们前面也说过,我们传入一个参数为int的方法,其实就是一段soap消息。那一个我们自定义对象,是如何传递呢?User对象在生成的wsdl中,其实是这样的一段代码:
     
    <xs:complexType name="User">
    <xs:sequence>
      <xs:element minOccurs="0" name="age" typke="xs:int" />
      <xs:element minOccurs="0" name="id" type="xs:long" />
      <xs:element minOccurs="0" name="name" nillable="true" type="xs:string" />
    </xs:sequence>
      </xs:complexType>
    
       再复杂的对象,实际上也是有基本类型组成。那我们看一下再调用时,传递的soap消息是如何的?
      那接下来进入我们的正题,对于复杂类型参数传递。
      我们实际上使用axiom方式调用服务。这种方式,同时也是Axis2的不同于Axis的一个新特性。
      首先我们拿一个例子做测试,深入剖析返回内部的传递。
      第一:测试返回值为自定义对象User对象。
      服务器端,还是按照上传的文档开发。方法如下:
     
     public User getUser()
         {
               User u= new User();
               u.setId(100);
               u.setName( "123ee");
                return u;
         }
    
      那客户端调用方式如下:
     
          public static void main(String[] args) throws Exception {
              EndpointReference targetEPR = new EndpointReference("http://localhost:8080/testWSServerByAxis2/services/myService?wsdl" );
         try {
                    Options options = new Options();
                    options.setTo(targetEPR);
               
                     //加载目标服务器
                    ServiceClient sender = new ServiceClient();
                    sender.setOptions(options);
          
                    OMFactory fac = OMAbstractFactory.getOMFactory();
                    OMNamespace omNs = fac.createOMNamespace("http://test.com" , "" );
                     //调用服务端的方法
                    OMElement method = fac.createOMElement("getUser" , omNs);
                     //给方法赋值参数值
                     //method.addChild(sayHello);
                     //返回OMElement对象
                    OMElement result = sender.sendReceive(method);
                    System. out.println( "clientResult="+result);
                     Iterator iterator=result.getChildElements();
                     while(iterator.hasNext()){
                         System. out.println( "ok");
                         OMNode omNode = (OMNode) iterator.next();
                          if(omNode.getType()==omNode. ELEMENT_NODE){
                               OMElement omElement = (OMElement) omNode;
                              System. out.println( "***="+omElement.getLocalName());
                                if (omElement.getLocalName().toLowerCase().equals("return" )) {
                                    User u = (User) BeanUtil.processObject(omElement, User. class, null, true, new DefaultObjectSupplier());
                                    System. out.println(u.getName());
                               }
                         }
                    }
               } catch (Exception e) {
                    e.printStackTrace();
               }
         }
    
     那给大家看一下,客户端调用服务器端的getUser方法,返回来的是个OMElement对象。在上篇博客中的RPC调用方式中,直接返回的是无法转化User对象,提示OmElement对象无法转化成User对象。
     好,我们看一下服务器给客户端返回的是什么?注意这句代码的输出:
     
    System. out.println( "clientResult="+result);
     clientResult=<ns:getUserResponse xmlns:ns="http://test.com"><ns:return xmlns:ax21="http://entity.com/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ax21:User"><ax21:age>0</ax21:age><ax21:id>100</ax21:id><ax21:name>123ee</ax21:name></ns:return></ns:getUserResponse>
    
      返回值是个soap消息,soap消息怎么会这样呢?为何不是那样的呢?他是根据什么来呢?为何是"http://test.com",又为何是ax21?大家对输出的信息是否也感到好奇呢?的确,我开始也是很好奇,那请跟着我的思路走,慢慢寻找答案。
     咦,突然发现http://test.com跟代码中的fac.createOMNamespace("http://test.com" , "" );有关联,哦,原来是命名空间呢。说道命名空间,不知道大家是否有点明白?嘻嘻,命名空间是从哪里来?是从wsdl来啊?对,返回的soap消息格式是根据服务端的wsdl信息生成的。那我们在仔细研究一下我们服务端生成的wsdl。
     wsdl截取了重要的一部分,跟大家解释一下其中的关键处。
     
       通过wsdl,我们就稍微明白服务器返回值信息。

       那下一步就是如何从返回值soap信息中抽取想要的User对象?
       其实我们就想要返回值信息中age,id信息。那我们脑子中第一个想法是什么呢?我们该如何取到呢?
       好,也许我们第一反应是,解析xml。类似java中的dom方式,解析xml。然后把相应的值,再拼接成User对象。
       恭喜你。答对了,思路确实如此的。
       但是,Axiom方式,不用我们一个个解析,然后自己去拼接。在Axiom中,自动会跟我们拼接成相应的对象,只要你告诉Axiom从哪到哪拼接完毕即可。
       继续观察服务器给我们返回的信息,看一下,我们想从那拼接成User对象?
       实际上,我们是想把<ax21:age>0</ax21:age><ax21:id>100</ax21:id><ax21:name>123ee</ax21:name>放到一个User对象中,可是Axiom是需要把一个整体标签中的东西封装,是寻找一个大标签,然后把其中的信息打包成用户想要的对象。而不是一个个查找。
       这样的话,应该是这个User信息的外边一层标签。就是<ns:return></ns:return>

       若是明白这个道理的话,那么我们Axiom方式调用就理解了。
       User u = (User) BeanUtil.processObject(omElement, User. class, null, true, new DefaultObjectSupplier());
       
       OK,讲到这,不知是否明白其中的原理了?若是懂这个例子的话,那么返回值是List<User>也是很简单了,直接用List.add(u)即可。然后返回值为list就有了。
       这节课讲的是返回值为List对象,这样的话,服务器端不用改动。客户端调用方式不使用RPC方式,而是使用Axiom,当然这种肯定也支持RPC支持的基本类型。那下节课给大家深入讲解传递参数为复杂定义类型。

    展开全文
  • 一,简单类型的传值 比如 public Users Get(int id) ,它可以使用两种方式获取: api/default/5$.get("/api/default",{id:90}, function (data) {/* 处理逻辑 */}); 前者不需要注明参数名,后者适用
  • 深入理解Java枚举类型(enum)

    万次阅读 多人点赞 2017-05-13 18:27:14
    【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) ...深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解
  • 举个例子吧,我是在使用DataSet和DataTable时发现的问题,所以就以此举例,其他类型没试过,不过应该是一样的public class myData : DataSet...{ public myData() ...{ this.Tables.Add( new myDataTable...
  • JavaScript:数据类型

    千次阅读 2016-05-31 15:21:36
    一、数据类型 ...还有一种复杂数据类型—Object,Object本质上是由一组无序的名值对组成的。 1、typeof  鉴于ECMAScript是松散类型的,因此需要有一种手段来检测给定变量的数据—typeof就是负责提供这
  • 所以就需要用到下面的方法进行处理复杂类型(List,map)   方法一: String cols="[{'id':'3'},{'id':'3'}]";  ObjectMapper mapper = new ObjectMapper();  JavaType javaType = mapper....
  • 引用类型数组

    千次阅读 2019-03-11 19:48:26
    引用类型数组的元素中放的是从基本类型构造而来的类和其他复杂数据类型的地址。 两种类型数组的图解 基本类型数组:arr是引用,放在栈内存中,new的部分属于对象,在堆内存中。arr里存的是对象的地址。 引用类型...
  • 一、基本类型 Java一种静态编程语言,所有变量和表达式是在编译时就确定的。同时,Java又是一种强类型语言,所有的变量和表达式都有具体的类型,并且每种类型是严格定义的。类型限制了变量可以hold什么样的值,...
  • 学习js经常看到这话——Javascript里面所有的数据类型都是对象 。 1.我们知道js ES5中数据类型分为基本数据类型复杂数据类型。 基本类型有5类,分别是null,undefined,number,string,boolen。 复杂数据...
  • 引用类型和基本类型区别

    万次阅读 2011-12-20 22:34:35
    今天明白了一个困扰很久的问题:引用类型和基本类型的区别与联系 要明白这个问题,首先需要理解堆栈的概念。那什么又是堆栈,有什么区别和联系呢? 堆:首先堆是动态分配的,JVM并不会自动释放这部分内存。只用...
  • 类型语言与弱类型语言

    千次阅读 2018-02-01 22:57:19
    类型语言允许将一块内存看做多种类型。比如直接将整型变量与字符变量相加。C and C++ 是静态语言,也是弱类型语言;Perl and PHP 是动态语言,但也是弱类型语言。 强类型语言在没有强制类型转化前,不允许两种...
  • jackson 解析复杂json问题

    千次阅读 2018-04-19 18:34:16
    jackson 解析复杂json问题 jackson常见的json生成和解析的工具包,下面一起看看它对于复杂的json解析时带来的问题 jackson简介 jackson依赖包 jackson解析javaBean jackson解析泛型的List jackson解析复杂的json ...
  • 主要是实现复杂的嵌套的Java对象,也就是对象嵌套对象的复杂对象,转换成json字符串。然后就是反过来,把复杂的json字符串转换成对应的嵌套的Java对象。 先上工具类。如下。 package com.lxk.json; import ...
  • Access表数据类型/字段类型

    千次阅读 2019-02-20 19:52:41
    今天要给大家分享的是数据类型。 数据类型对于初学者来说不太会选择,例如,如果某个字段的数据类型是文本,那么它可存储包括文本或数值字符的数据。但数据类型为数字的字段却只能存储数值数据。因此,必须了解每种...
  • 复杂网络简单理解

    万次阅读 多人点赞 2017-11-28 09:42:18
    通俗易懂的复杂网络 1 什么是复杂网络 1.1 直观理解 什么是复杂网络?对普通人而言,在媒体上看到复杂网络,首先想到的是互联网,实际上网络已经成为Internet的代名词,确实Internet从只有几个结点的简单的网络,...
  • 计算复杂性理论

    千次阅读 2019-06-09 00:24:03
    计算复杂性理论 在计算机算法中,计算复杂性是一个很重要的研究内容。计算复杂性理论(Computational complexity theory)被认为是理论计算机科学和数学的一个分支。 对于计算机而言,任何一个问题的求解都需要资源...
  • 静态类型与动态类型

    千次阅读 多人点赞 2015-06-18 10:05:50
    静态类型 & 动态类型
  • Ogre 复杂场景

    千次阅读 2010-04-05 11:31:00
    Mage小组 著Email: norman_chen@163.com renwind@163.com QQ: 18725262http://www.173d8.comhttp://blog.csdn.net/pizi0475复杂场景 OGRE除了我们已经见过的普通场景以外还支持大型复杂的场景,如:宽阔的野外地形...
  • spring data jpa 实现多条件复杂查询及多表联查

    万次阅读 多人点赞 2018-06-19 16:32:29
    最近发现JPA在处理单表时,很方便,但多表就比较复杂了。今天写一下端午这两天琢磨的多条件查询,还有多表联查。maven依赖啊,配置,继承写法等知识点不展开说了,之前写过一篇文章: spring boot 配置及使用 spring...
  • Spring Data JPA复杂动态查询

    千次阅读 2018-07-28 00:33:54
    一些复杂的查询,比如涉及到聚合函数、动态多条件等,着实有些棘手。在不够了解的情况下,觉得Spring Data JPA在这方面不太人性化,有时候我们干脆使用原生sql粗暴的来解决这类查询问题。但这与Spring Data JPA的...
  • 复杂XML与复杂java对象的相互转换

    千次阅读 2018-01-10 16:06:19
    * @Description: TODO(这里用一话描述这个方法的作用) 获取根节点下指定的节点 返回节点元素   * @Title: XMLUtil.java--> getEntitySetElement   * @author: hanwei   * @param @param node   * @...
  • 类型语言和弱类型语言

    万次阅读 多人点赞 2014-11-03 16:30:01
    编译型和解释型 我们先看看编译型,其实它和汇编语言是一样的:也是有一个负责翻译的程序来对我们的源代码进行转换,生成相对应的可执行代码。...但对于一个比较复杂的项目,为了方便管理,我
  • JavaScript中的类型

    千次阅读 2010-08-31 16:01:00
    一、关于类型什么叫做类型?简单地说,类型就是把内存中的一个二进制序列赋予某种意义。比如,二进制序列0100 0000 0111 0000 0001 0101 0100 1011 1100 0110 1010 0111 1110 1111 1001 1110如果看作是64位无符号...
  • SparkSql 数据类型转换

    千次阅读 2019-09-18 10:40:08
    数据类型转换这个在任何语言框架中都会涉及到,看起来非常简单,不过要把所有的数据类型都掌握还是需要一定的时间历练的 SparkSql数据类型 数字类型 ByteType:代表一个字节的整数。范围是-128到127 ShortType:...
  • Fastjson处理复杂层级关系的json对象

    千次阅读 2019-10-30 21:55:59
    处理复杂层级关系的json对象 我们一般的项目现在都是前后端分离,从前端传过来的都是序列化好的json数据,但是我们后台如何获取呢,那就不获取了呗~,上代???? 用到了阿里的Fastjson来处理 导入pom文件 <dependency&...
  • 娓娓道来复杂网络

    千次阅读 2014-09-14 22:46:23
    通俗易懂的复杂网络 1 什么事复杂网络 1.1 直观理解 什么是复杂网络?对普通人而言,在媒体上看到复杂网络,首先想到的是互联网,实际上网络已经成为Internet的代名词,确实Internet从只有几个结点的简单的网络,...
  • JAVA基础 引用类型变量和基本类型变量

    千次阅读 多人点赞 2016-08-16 22:32:27
    引用类型变量的名字是复杂数据的存储地点。包括类 数组(字符串) 接口 String s="Hello world!"; 许多人都写过这样的语句,但是,我们到底声明了什么呢?一休,你回答一下。 一休:声明了一个String对象,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 237,423
精华内容 94,969
关键字:

复杂句类型