扩展mysql_扩展mysql - 手把手教你写udf

1 MySQL简介

MySQL是最流行的开放源码SQL数据库管理系统,相对于Oracle,DB2等大型数据库系统,MySQL由于其开源性、易用性、稳定性等特点,受到个人使用者、中小型企业甚至一些大型企业的广泛欢迎,MySQL具有以下特点:

lMySQL是一种关联数据库管理系统,关联数据库将数据保存在不同的表中,而不是将所有数据放在一个大的仓库内,这样就增加了速度并提高了灵活性。

lMySQL软件是一种开放源码软件。

lMySQL数据库服务器具有快速、可靠和易于使用的特点。

lMySQL服务器工作在客户端/服务器模式下,或嵌入式系统中。

l有大量可用的共享MySQL软件。

2 MySQL内置函数

使用过MySQL的人都知道,MySQL有很多内置函数提供给使用者,包括字符串函数、数值函数、日期和时间函数等,给开发人员和使用者带来了很多方便。下面给几个例子:

l字符串函数

mysql> select ASCII('2');

+------------+

| ASCII('2') |

+------------+

|50 |

+------------+

打印字符的ASCII编码。

l数值函数

mysql> SELECT LOG(10,100);

+-------------+

| LOG(10,100) |

+-------------+

|2 |

+-------------+

打印以10为底,100的对数值。

l日期和时间函数

mysql> SELECT CURDATE();

+------------+

| CURDATE()|

+------------+

| 2011-11-11 |

+------------+

打印当前的日期。

这里简单举几个例子,如果想了解MySQL函数的全貌,请访问Mysql官方手册

3扩展MySQL函数------ UDF

MySQL的内置函数虽然丰富,但毕竟不能满足所有人的需要,有时候我们需要对表中的数据进行一些处理而内置函数不能满足需要的时候,就需要对MySQL进行一些扩展,幸运的是,MySQL给使用者提供了添加新函数的机制,这种使用者自行添加的MySQL函数就称为UDF(User Define Function)。其实除了UDF外,使用者还可以将函数添加为MySQL的固有(内建)函数,固有函数被编译进mysqld服务器中,称为永久可用的,不过这种方式较添加UDF

复杂,升级维护都较为麻烦,这里我们不做讨论。

无论你使用哪种方法去添加新函数,它们都可以被SQL声明调用,就像ABS()或SUM()这样的固有函数一样。

3.1 UDF的特性

l函数能返回字符串,整数或实数。

l你可以定义一次作用于一行的简单函数,或作用于多行的组的集合函数。

l提供给函数的信息使得函数可以检查传递给它们的参量的数目和类型。

l你可以让MySQL在将某参量传递给函数之前强制其为某一类型。

l你可以表示函数返回NULL或发生错误。

3.2 CREATE FUNCTION/DROP FUNCTION语法

CREATE [AGGREGATE] FUNCTIONfunction_nameRETURNS {STRING|INTEGER|REAL}

SONAMEshared_library_name

DROP FUNCTIONfunction_name

一个自定义函数(UDF)就是用一个象ABS()或SUM()这样的固有(内建)函数一样作用的新函数去扩展MySQL。

function_name是用在SQL声明中以备调用的函数名字。RETURNS子句说明函数返回值的类型。shared_library_name是共享目标文件的基本名,共享目标文件含有实现函数的代码。该文件必须位于一个能被你系统的动态连接者搜索的目录里。

你必须有mysql数据库的INSERT权限才能创建一个函数,你必须有mysql数据库的DELETE权限才能撤销一个函数。这是因为CREATE FUNCTION往记录函数名字,类型和共享名的mysql.func系统表里添加了一行,而DROP FUNCTION则是从表中删掉这一行。

值得注意的是,要使得UDF机制能够起作用,必须使用C或者C++编写函数,你的系统必须支持动态加载,而且你必须是动态编译的mysqld(非静态)。

3.3定义UDF

对于每个你想要使用在SQL声明中的函数,你应该定义相应的C(或C++)函数。

你为xxx()编写来实现接口的C/C++函数如下:

lxxx() (必有)

主函数。这是函数结果被计算的地方。SQL函数数据类型与C/C++函数返回类型的对应关系如下:

SQL类型

C/C++类型

STRING

char *

INTEGER

long long

REAL

double

lxxx_init() (可选)

对xxx()的初始化函数。它可以被用来:

检查传递给xxx()的参量数目。

检查参量是否为必需的类型,或者,除此之外,在主函数被调用的时候告诉MySQL将参量强制为想要的类型。

分配主函数需要的内存。

指定结果的最大长度。

指定(对于REAL函数)小数的最多位数。

指定结果是否可以为NULL。

lxxx_deinit()(可选)

对xxx()的去初始化函数。它释放初始化函数分配的内存。

当SQL声明调用XXX()时,MySQL调用初始化函数xxx_init(),让它执行必要的设置,比如,检查参量或分配内存。如果xxx_init()返回一个错误,SQL声明会退出并给出错误信息,而主函数和去初始化函数并没有被调用。否则,主函数xxx()对每一行都被调用一次。所有行都处理完之后,调用去初始化函数xxx_deinit()执行必要的清除。

对于象SUM()一样工作的集合函数,你也必须提供如下的函数:

lxxx_clear()(在5.1版本中必须)

对一个新组重置当前集合值为初试集合值,但不插入任何参量。

lxxx_add()(必须)

添加参量到当前集合值。

MySQL按下列操作来处理集合UDF:

1.调用xxx_init()让集合函数分配它需要用来存储结果的内存。

2.按照GROUP BY表达式来排序表。

3.为每个新组中的第一行调用xxx_clear()函数。

4.为属于同组的每一个新行调用xxx_add()函数。

5.当组改变时或每组的最后一行被处理完之后,调用xxx()来获取集合结果。

6.重复,以上3步直到所有行被处理完。

7.调用xxx_deinit()函数去释放UDF分配的内存。

所有函数必须时线程安全的,这不仅对主函数,对初始化和去初始化函数也一样,也包括集合函数要求的附加函数。这个要求的一个结果就是,你不能分配任何变化的全局或静态变量。如果你需要内存,你可以在xxx_init()函数分配内存,然后在xxx_deinit()函数释放掉。

3.3.1主要数据结构

UDF_INIT

typedef struct st_udf_init

{

my_bool maybe_null;/* 1 if function can return NULL */

unsigned int decimals;/* for real functions */

unsigned long max_length;/* For string functions */

char*ptr;/* free pointer for function data */

my_bool const_item;/* 0 if result is independent of arguments */

} UDF_INIT;

lmy_bool maybe_null

如果xxx()能返回NULL,xxx_init()应使maybe_null为1。其默认值是1。

lunsigned int decimals

小数位数。默认值是传到主函数的参量里小数的最大位数。(例如,如果函数传递1.34, 1.345,和1.3,那么默认值为3,因为1.345有3位小数。

lunsigned int max_length

结果的最大长度。max_length的默认值因函数的结果类型而异。对字符串函数,默认值是结果的最大长度。对整型函数,默认是21位。对实型函数,默认是13再加上initid->decimals指示的小数位数。(对数字函数,长度包含正负号或者小数点符)。

如果想返回团值,你可以把max_length设为从65KB到16MB。这个内存不会被分配,但是如果有临时数据需要存储,这个设置了的值被用来决定使用哪种列的类型。

lchar *ptr

函数可以用作本身目的的指针。比如,函数可以用initid->ptr来在分配了的内存内部通讯。xxx_init()应该分配内存,并指派给这个指针:

initid->ptr = allocated_memory;

在xxx()和xxx_deinit()中,借用initid->ptr来使用或释放内存。

UDF_ARGS

enum Item_result /*返回结果类型*/

{

STRING_RESULT=0,

REAL_RESULT,

INT_RESULT,

ROW_RESULT,

DECIMAL_RESULT

};

typedef struct st_udf_args

{

unsigned int arg_count;/* Number of arguments */

enum Item_result *arg_type;/* Pointer to item_results */

char **args;/* Pointer to argument */

unsigned long *lengths;/* Length of string arguments */

char *maybe_null;/* Set to 1 for all maybe_null args */

char **attributes;/* Pointer to attribute name */

unsigned long *attribute_lengths;/* Length of attribute arguments */

} UDF_ARGS;

lunsigned int arg_count

参数个数。如果你需要你的函数带着某个数目的参量被调用,在初始化函数检查这个值,例如:

if (args->arg_count != 2)

{

strcpy(message,"XXX() requires two arguments");

return 1;

}

lenum Item_result *arg_type

参数类型列表。要确信一个参量是给定类型的,并且如果不是的话就返回一个错误,请检查初始化函数中的arg_type数列。比如:

if (args->arg_type[0] != STRING_RESULT ||

args->arg_type[1] != INT_RESULT)

{

strcpy(message,"XXX() requires a string and an integer");

return 1;

}

要求你函数的参量是某一类型的另一方法是,使用初始化函数设置arg_type元素为你想要的类型。对所有对xxx()的调用而言,这会导致MySQL强制参量为这些类型。比如,要指定头两个参量强制成字符串和整数,在xxx_init()中分别:

args->arg_type[0] = STRING_RESULT;

args->arg_type[1] = INT_RESULT;

lchar **args参数列表

对主函数的每次调用,args->args包含为每个当前处理的行传递的实际参量。

如下使用参量i的函数:

给一个STRING_RESULT型的参量作为一个字符串加一个长度,可以允许所有二进制数或任意长度的数处理。字符串内容作为args->args[i],而字符串长度为args->lengths[i]。你不能采用null结尾的字符串。

对一个INT_RESULT型的参量,你必须转换args->args[i]为一个long long值:

long long int_val;

int_val = *((long long*) args->args[i]);

对一个REAL_RESULT型参量,你必须转换args->args[i]为一个双精度值:

doublereal_val;

real_val = *((double*) args->args[i]);

lunsigned long *lengths

对初始化函数,lengths数列表示对每个参量的最大字符串长度。你不要改变它。对主函数的每次调用,lengths包含了对当前处理行传递的任何字符串参量的实际长度。对于INT_RESULT或REAL_RESULT类型的参量,lengths仍包含参量的最大长度(对初始化函数)。

3.3.2简单函数

这里说明简单SQL函数的C/C++主函数xxx()的编写,注意返回值和参数会有所不同,这取决于你说明的SQL函数xxx()在CREATE FUNCTION声明中返回的是STRING,INTEGER类型还是REAL类型。

对于STRING型函数:

char *xxx(UDF_INIT *initid, UDF_ARGS *args,

char *result, unsigned long *length,

char *is_null, char *error);

对于INTEGER型函数:

long long xxx(UDF_INIT *initid, UDF_ARGS *args,

char *is_null, char *error);

对于REAL型函数:

double xxx(UDF_INIT *initid, UDF_ARGS *args,

char *is_null, char *error);

初始化和去初始化函数如下说明:

my_bool xxx_init(UDF_INIT *initid, UDF_ARGS *args, char *message);

void xxx_deinit(UDF_INIT *initid);

initid参数被传递给所有的三个函数。它指向UDF_INIT结构,这个结构被用来在函数之间交换信息。

3.3.3集合函数

这里介绍创建集合UDF之时需要定义的不同函数。

lxxx_reset()

当MySQL在一个新组中发现第一行时调用这个函数。它对这个组重置任何内部总和变量,然后使用给定的UDF_ARGS参量作为内部总和值的第一个值。如下说明xxx_reset()函数:

char *xxx_reset(UDF_INIT *initid, UDF_ARGS *args,

char *is_null, char *error);

在MySQL5.1版中UDF接口不需要或不使用xxx_reset()函数,而是使用xxx_clear()函数作为替代。但是如果你想让UDF也能在老版本的服务器上运行,你也可以定义xxx_reset()和xxx_clear()函数。(如果你使用了这两个函数,xxx_reset()函数在很多情况下可以通过调用函数来内部实现,即调用xxx_clear()函数重置所有变量,然后添加UDF_ARGS参量作为组的第一个值。)

lxxx_clear()

当MySQL需要重置总和结果时调用此函数。对每一个新组,在开始之时调用它,但是它也可以被调用来为一个没有匹配行在其中的查询重置值。如下说明xxx_clear():

char *xxx_clear(UDF_INIT *initid, char *is_null, char *error);

在调用xxx_clear()之前is_null被设置指向CHAR(0)。

如果发生错误,你可以存储一个值在error参量指向的变量中。error指向一单字节变量,而不是一个字符串缓冲区。

xxx_clear()是MySQL 5.1必须的。

lxxx_add()

为同组所有的行调用这个函数。你应该用它在UDF_ARGS参量中向内部总和变量加值。

char *xxx_add(UDF_INIT *initid, UDF_ARGS *args,

char *is_null, char *error);

对集合UDF而言xxx()函数应该用与非集合UDF一样的方法来说明。

对一个集合UDF,MySQL在组内所有行被处理之后调用xxx()函数。这里你应该一般不会接触到它的UDF_ARGS参量,但是取而代之地根据内部总和变量返回给你值。

is_null和error的指针参量和所有到xxx_reset(), xxx_clear(), xxx_add()和xxx()调用一样。你可以用这个来提醒你获取一个错误或无论xxx()是否返回NULL的一个结果。你不能把一个字符串存到error!error指向单字节变量而不是字符串缓冲区。

*is_null对每一个组都重置(调用xxx_clear()前),*error从不重置。

如果xxx()返回时,*is_null或*error被设置,MySQL返回NULL作为组函数的结果。

3.3.4错误处理

如果没有错误发生,初始化函数应该返回0,否则就返回1。如果有错误发生,xxx_init()应该在message参数存储一个以null结尾的错误消息。该消息被返回给客户端。消息缓冲区是MYSQL_ERRMSG_SIZE字符长度,但你应该试着把消息保持在少于80个字符,以便它能适合标准终端屏幕的宽度。

对于long long和double类型的函数,主函数xxx()的返回值是函数值。字符函数返回一个指向结果的指针,并且设置*result和*length为返回值的内容和长度。例如:

memcpy(result, "result string", 13);

*length = 13;

被传给xxx()函数的结果缓冲区是255字节长。如果你的结果适合这个长度,你就不需要担心对结果的内存分配。

如果字符串函数需要返回一个超过255字节的字符串,你必须用malloc()在你的xxx_init()函数或者xxx()函数里为字符串分配空间,并且在xxx_deinit()函数里释放此空间。你可以将已分配内存存储在UDF_INIT结构里的ptr位置以备将来xxx()调用。

要在主函数中指明一个NULL的返回值,设置*is_null为1:

*is_null = 1;

要在主函数中指明错误返回,设置*error为1:

*error = 1;

如果xxx()对任意行设置*error为1,对于任何XXX()被调用的语句处理的当前行和随后的任意行,该函数值为NULL(甚至都不为随后的行调用xxx())。

4范例

4.1编译安装

安装mysql开发包

[root@rocket mysql_udf]# yum -y install mysql-devel

编译udf链接库

代码:udf_str.cpp

#include #include#include#include#include#include

extern "C"{//str_reverse

my_bool str_reverse_init(UDF_INIT* initid, UDF_ARGS* args, char*message);void str_reverse_deinit(UDF_INIT*initid);char* str_reverse(UDF_INIT* initid, UDF_ARGS* args, char* result, unsigned long* length, char* is_null, char *error);//LengthAll

my_bool mysum_init(UDF_INIT* initid, UDF_ARGS* args, char*message);void mysum_deinit(UDF_INIT*initid);void mysum_reset(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error);void mysum_clear(UDF_INIT *initid, char *is_null, char *error);void mysum_add(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error);long long mysum(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error);

}char* StrData = 0;int gSum = 0;//str_reverse ==================================================

my_bool str_reverse_init(UDF_INIT* initid, UDF_ARGS* args, char*message)

{if (args->arg_count != 1)

{

strcpy(message,"wrong number of arguments: str_reverse() requires one argument");return 1;

}if (args->arg_type[0] !=STRING_RESULT)

{

strcpy(message,"str_reverse() requires a string as parameter");return 1;

}

StrData= (char*)malloc(4096);

memset(StrData,0, 4096);

initid->maybe_null = 1;

initid->max_length = 32;

initid->ptr =StrData;return 0;

}void str_reverse_deinit(UDF_INIT*initid)

{free(StrData);

}char* str_reverse(UDF_INIT* initid, UDF_ARGS* args, char* result, unsigned long* length, char* is_null, char *error)

{if (args->arg_type[0] ==STRING_RESULT)

{if (strlen(args->args[0]) > 256)

{

strncpy(StrData, args->args[0], 4096);

StrData[4096-1] = 0;

std::reverse(StrData, StrData+strlen(StrData));returnStrData;

}else{

strncpy(result, args->args[0], 256);

result[256-1] = 0;

std::reverse(result, result+strlen(result));*length = (unsigned long)strlen(result);returnresult;

}

}returnNULL;

}//LengthAll ==================================================

my_bool mysum_init(UDF_INIT* initid, UDF_ARGS* args, char*message)

{if (args->arg_count != 1)

{

strcpy(message,"wrong number of arguments: mysum() requires one argument");return 1;

}if (args->arg_type[0] !=INT_RESULT)

{

strcpy(message,"wrong argument type of arguments: mysum() requires int");return 1;

}

gSum= 0;return 0;

}void mysum_deinit(UDF_INIT*initid)

{

}void mysum_reset(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)

{

gSum= 0;

}void mysum_clear(UDF_INIT *initid, char *is_null, char *error)

{

gSum= 0;

}void mysum_add(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)

{

gSum+= *(int*)(args->args[0]);

}long long mysum(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error)

{returngSum;

}

[root@rocket mysql_udf]# g++ -I/usr/include/mysql -shared -fPIC -o udf_str.so udf_str.cpp

查找插件路径

37b3647722a2c4b2e871fd9b43d8c730.png

安装插件函数

DROP FUNCTION IF EXISTS str_reverse;

DROP FUNCTION IF EXISTS mysum;

CREATE FUNCTION str_reverse RETURNS string SONAME 'udf_str.so';

CREATEAGGREGATEFUNCTION mysum RETURNSINTEGERSONAME 'udf_str.so';

注意这里的返回值不能写错,不然运行的时候mysql服务器会崩溃!

查看安装结果

2e4af8b16ca286867d2f9f220c8a1136.png

4.2运行

运行str_reverse

2776e2ea680c4f8a529edc7fd948af23.png

运行mysum,先创建一些数据

mysql> create database test;

mysql> use test;

mysql> CREATE TABLE salary( name varchar(64) NOT NULL DEFAULT '' COMMENT 'name', salary int(11) NOT NULL DEFAULT 0 COMMENT 'salary', primary key(name) )ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT 'test';

b56ded35dc6ab7c956bad75d12ed1031.png

mysql> insert into salary values ('zhangsan', 11380), ('lisi', 12000), ('wangwu', 8789);

4baa615abd8e5d5f948f6948e33d9dbd.png

mysql> select mysum(name) from salary;

ERROR 1123 (HY000): Can't initialize function 'mysum'; wrong argument type of arguments: mysum() requires int

这里故意使用name为参数,可以看到我们在程序里打印的错误信息。

执行正确的语句

7331d640972060ab4ff97ea9ef4e76c2.png

可以看到mysum实现了和内置函数sum一样的功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/300651.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

I want go to school

那天,村里来了英语老师,她教我们说了第一句英语:“I want go to school.” 你听到了吗?那是最震撼的呐喊!

这些肢体语言竟然是这个意思! | 今日最佳

世界只有3.14 % 的人关注了青少年数学之旅(图源网络,侵权删)

下午去银行办理按揭手续,不得已调休半天

可恶的银行非得在工作日才办理按揭手续,不是人民的银行为人民吗?既然存款利率要降,以后还打算收取存款费用,那银行就是[或者打算是]地道的服务行业,周末对公业务不开就算了,个人按揭也闭门,没有…

Scott Hanselman 喊你来看看最新的极简APII

近日,Scott Hanselman 在社交网络上发布了 .NET 的最新消息:现代的 C# 特性和 ASP.NET Core 中新的极简 API 正在 .NET 6 中开始出现。你可以在 DamianEdwards 的仓库中看到一些示例,并与团队分享你的想法! (点击原文链接查看仓库…

全球孩子迷恋手机/iPad,其实罪魁祸首是父母!

全世界只有3.14 % 的人关注了青少年数学之旅我从来没有想过,现在孩子的童年会是这样子的。他们手上捧着的不是书籍,而是小手掌都握不过来的手机、iPad;他们嘴上谈论的不是作业,而是许多成年人都搞不懂的王者和吃鸡。很多父母说&am…

oracle vm 安装虚拟机小bug

2019独角兽企业重金招聘Python工程师标准>>> 如果是iso文件是64位, 那么在创建虚拟电脑时选择的系统就是window7 x64。否则报错:0000225错误. 转载于:https://my.oschina.net/u/1174884/blog/175596

庄表伟:License之外,社区的规则与潜规则 | DEV. Together 2021 中国开发者生态峰会...

内容来源:2021 年 6 月 5 日,由 SegmentFault 思否主办的 2021 中国开发者生态峰会圆满落幕。会上,华为云产品专家、开源社理事长庄表伟发表了主题为《License之外,社区的规则与潜规则》的演讲。分享嘉宾:庄表伟&#…

GridView 简单扩展

usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Text;usingSystem.Web;usingSystem.Web.UI;usingSystem.Web.UI.WebControls;namespaceFanfajin.MyWebControls{ /**//// <summary> /// 增强的 GridView 控件 扩展 /// &…

为了偷吃东西你能有多拼?! | 今日最佳

世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源网络&#xff0c;侵权删&#xff09;

Nacos真香,从零到一学起来

Nacos是阿里巴巴开源的微服务管理平台&#xff0c;可以帮助开发者快速实现动态服务发现、服务配置、服务元数据及流量管理。这篇文章主要来讲一下Nacos作为配置中心和注册中心的使用。1 安装 1.1 linux下安装首先搭建一套单机版的Nacos集群。Nacos的安装有两种方式&#xff0c;…

自律的程度,决定你人生的高度

全世界只有3.14 % 的人关注了青少年数学之旅对于不少用户来说&#xff0c;微信已经成为了获取新资讯的重要途径之一。如何保证自己找到「值得看」的内容&#xff1f;每天更新的文章这么多&#xff0c;哪些才值得你看呢&#xff1f;今天就为大家推荐一些颜值与才华并存的好评公众…

标准MD5 .Net,实现!的对与错!

关键之处在,从byte到字符串的处理下面是一个错误的实现! /**//// <summary> /// 返回32位md5十六进制加密 /// </summary> /// <param name"strValue">待加密的字符串</param> /// <returns></return…

[导入]Asp.net中动态在中加入Scrpit标签

许久以前&#xff0c;写过一篇《asp.net页中动态加入样式表文件》&#xff0c;后来发现在使用时如果每页都写这么个函数真是很麻烦&#xff0c;于是自己写了一个Page的派生。将这个函数加了进去。/**////<summary>///作者 邹健 ///日期 20070202 ///重载的Page类。…

android 多布局

做为最后的方法&#xff0c;也是最后一个才会考虑的方法&#xff0c;那就是为不同的尺寸界面单独写布局。不到万不得已不要用这个方法&#xff0c;相信不少人和我一样都被逼着用过这个方法吧。需要说明的是&#xff0c;横竖屏切换使用不同布局也是用这个方法解决的&#xff1b;…

万万想不到:吸烟的辐射量比原子弹爆炸点还厉害!

全世界只有3.14 %的人关注了青少年数学之旅近日&#xff0c;外国视频网站上一段关于世界上辐射最高地点的短片火了起来。片中&#xff0c;一位外国小哥亲身探秘了那些广为人知的辐射地点如切尔诺贝利、福岛核电站等&#xff0c;并且给出了究竟哪里的辐射最高的结论。什么是辐射…

视频专辑:Hibernate 视频

为什么80%的码农都做不了架构师&#xff1f;>>> 专辑&#xff1a;Hibernate 视频 简介:郭宏志 Hibernate从入门到深入,郭宏志气主讲 1 郭宏志 Hibernate 01 简介 2013-10-29 13:50 | 播放(11) | 评论(0) | 时长:1:17:58 2 郭宏志 Hibernate 02 配置 2013-10-29…

人体的血管连起来竟能绕地球两圈!?| 今日最佳

世界只有3.14 % 的人关注了青少年数学之旅你知道怎么回复吗&#xff1f;这五花肉真。。白啊来自唐代的敦煌文书。。。《放妻书》好像发现了点什么不可告人的秘密如果人体的血管首尾相连总长度约为96560.64千米大概可以绕地球2.5圈密集恐惧症表示已经qu世了&#xff08;图源网络…

云原生系统之弹性模式

大纲1.云原生系统的弹性模式resiliency pattern 1.1 服务故障的雪崩效应 1.2 回应之前云原生--弹性请求的疑问&#xff1f;2. 弹性模式&#xff1a;作用在下游请求消息上3. 短期中断的响应码4. Polly经典策略5. Golang 断路器模式德国哲学家尼采说过&#xff1a;那些杀…

js null加法的处理

首先看一段JS高级编程中&#xff0c;有关加法()操作符的解释&#xff1a;一个简单的测试&#xff1a;alert(3 null); // 3 alert(3 null); // 3null可见对于null的处理&#xff0c;并不是简单的取String。ecma262的定义如下&#xff1a;对第7、8步骤的理解&#xff1a;7. 如果…

符合WEB标准的div+css导航下拉菜单

<html xmlns"http://www.w3.org/1999/xhtml"lang"zh-CN"><head><meta http-equiv"Content-Type"content"text/html; charsetgb2312"/><title>52css.com - css菜单演示</title><style type"text…