• [技术干货] 我们与Java的不解之缘---Java27岁生日快乐[转载]
    前言:直至今日,距离Java语言的产生以及有27年了。也许我们陪伴Java的时间可能仅有三四年,甚至仅有一两年,但是她在我们心中的地位是至高无上的。从第一个“hello world”开始,我们就与Java结下了不解之缘,这也意味着我们要与她“结伴一生”。因此,在Java这个特殊的27岁,小威将与各位小伙伴儿一起经历Java的发展之路以及今后对Java的学习。在这里,先谢谢各位小伙伴儿的支持了嘿嘿文章目录Java语言的特点Java语言的发展史Java框架及云原生个人用Java做过的小项目Java语言的特点Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程。Java具有简单性、面向对象、分布式、健壮性、平台独立与可移植性、多线程、动态性等特点。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等。简单性:Java语言继承了C++语言的优点,去掉了C++中学习起来比较难的多继承、指针等概念,所以Java语言学习起来更简单,使用起来也更方便。面向对象:Java是一种面向对象的编程语言。分布性:Java设计成支持在网络上应用,它是分布式语言。所以只要用Java编写了一个程序,就可以到处应用。可以节省大量人力物力。编译和解释性:Java编译程序生成字节码,而不是通常的机器码,这使得Java开发程序比用其他语言开发程序快很多。稳健性:Java刚开始被设计出来就是为了写高可靠和稳健的软件的。所以用Java写可靠的软件很容易。目前许多第三方交易系统、银行平台的前台和后台电子交易系统等都会用Java语言开发。安全性:Java的存储分配模型是它防御恶意代码的主要方法之一。所以很多大型企业级项目开发都会选择用Java开发。可移植性:Java并不依赖平台,用Java编写的程序可以运用到任何操作系统上。高性能:Java是一种先编译后解释的语言,所以它不如全编译性语言快。但Java设计者制作了“及时”编译程序,这样就可以实现全编译了。多线索性:Java是多线索语言,它可以同时执行多个程序,能处理不同任务。动态性:Java语言设计成适应于变化的环境,它是一个动态的语言。Java语言的发展史1995年,Sun公司推出了Java,但只是一种语言,还没有一个强大的开发类库;1996年,第一个JDK1.0的发布(包括运行环境JRE和开发环境JDK);1997年2月,SUN公司紧接着推出了JDK 1.1;1998年12月8日,JDK1.2——第二代Java平台的企业版J2EE发布;1999年6月,Java第二代平台2.0发布,并且Sun公司把Java体系分为三个方向:J2ME(Java2 Micro Edition,Java2平台的微型版),应用于移动、无线及有限资源的环境;J2SE(Java 2 Standard Edition,Java 2平台的标准版),应用于桌面环境;J2EE(Java 2Enterprise Edition,Java 2平台的企业版),应用于基于Java的应用服务器;注意:Java 2平台的发布,是Java发展过程中最重要的一个里程碑,标志着Java的应用开始普及。2000年5月,JDK1.3、JDK1.4和J2SE1.3相继发布,都大幅度的提高了Java的性能;2002年2月26日,J2SE1.4发布;2004年9月30日,J2SE1.5发布,成为Java语言发展史上的又一里程碑。为了表示该版本的重要性,J2SE 1.5更名为Java SE 5.0;2005年6月,在Java One大会上,Sun公司发布了Java SE 6。此时,Java的各种版本已经更名,已取消其中的数字2(Java6最经典,流传最久的版本)。如下所示:J2EE更名为JavaEEJ2SE更名为JavaSEJ2ME更名为JavaME2009年,Oracle收购Sun公司,每半年更新一次;2014年,JavaSE8.0发布,企业级开发。Java框架及云原生随着Java的发展,开发Java程序也变得越来越简单,Java框架也慢慢地流行了起来。我们学了Java基础,就要开始Java框架的学习了。那么,Java的框架主要有哪些呢?Java框架主要有:轻量级且开源的Spring,WEB界面SpringMVC,数据库持久化的MyBatis,SpringBoot,SpringCloud,jQuery,Netty,Log4j,Quartz,Redis,RabbitMQ等等。学过了Java基础的小伙伴儿们可以开始Java框架的学习了。下面介绍一下云原生Java和云原生有着很密集的联系,以后我们Java工程师也会朝着云原生发展。云原生是基于分布部署和统一运管的分布式云,以容器、微服务、DevOps等技术为基础建立的一套云技术产品体系。云原生应用也就是面向“云”而设计的应用,在使用云原生技术后,开发者无需考虑底层的技术实现,可以充分发挥云平台的弹性和分布式优势,实现快速部署、按需伸缩、不停机交付等。个人用Java做过的小项目在之前的Java学习中,自己大一的时候也曾做过几个小项目和小游戏,之前做完了保存了下来,今天就来分享给大家。用Map集合写的考试系统package java小项目;import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Random;import java.util.Scanner;import java.util.Set;public class 考试系统 {    public static void main(String[] args) {        // TODO 自动生成的方法存根        ArrayList <String>arr=new ArrayList<>();        ArrayList <Integer>arrl=new ArrayList();//存题号用,防止有重复题        String [][] a=new String[10][6];        HashMap <Integer,String>hm=new HashMap<>();                hm.put(0,"公共厕所()一国家首都:\n"+"A:美国\n"+"B:英国\n"+"C:中国\n"+"D:伦敦");        arr.add("D");        hm.put(1,"用什么拖地最干净:\n"+"A:拖把\n" +"B:扫帚\n"+"C:铲斗\n"+"D:用力");        arr.add("D");        hm.put(2,"老王的头发已经掉光了,为什么还一直去理发?\n"+"A:他有洁癖\n"+"B:他是理发师\n"+"C:他讲究男人的风度\n"+"D:他好久没洗头了");        arr.add("B");        hm.put(3,"失败为成功之母,那么成功为失败的什么?\n"+"A:之父\n" +"B:反义词\n"+"C:之儿\n"+"D:之母");        arr.add("B");        hm.put(4,"小白加小白等于什么?\n"+"A:小白俩\n" +"B:俩小白\n"+"C:大灰狼\n"+"D:小白兔");        arr.add("D");        hm.put(5,"历史上那个人物跑得最快?\n"+"A:曹操\n" +"B:刘备\n"+"C:诸葛亮\n"+"D:关羽");        arr.add("A");                hm.put(6,"蚊子咬在什么地方你不会觉得痒?\n"+"A:脚上\n" +"B:腿毛上\n"+"C:别人身上\n"+"D:背上");        arr.add("C");                hm.put(7,"汽车在右转弯时,哪个轮胎不转?\n"+"A:前轮胎\n" +"B:后轮胎\n"+"C:左轮胎\n"+"D:备用轮胎");        arr.add("D");        hm.put(8,"什么光会给人们带来痛苦?\n"+"A:黑光\n" +"B:暗光\n"+"C:强光\n"+"D:耳光");        arr.add("D");        hm.put(9,"什么情况下人会有两双眼睛?\n"+"A:睡觉的时候\n" +"B:变异的时候\n"+"C:两个人的时候\n"+"D:吃饭的时候");        arr.add("C");        int daxunhuan=0;        int kaoshi=0;//考试结束条件        while(daxunhuan!=999){        System.out.println("--------------------------------------");        System.out.println("单选题:");        System.out.println("--------------------------------------");        System.out.println("1:考试;      2:试题管理;      3:退出。");        System.out.println("     (请输入操作的代码1,2或3)"   );        Random r=new Random();        Scanner s=new Scanner(System.in);        int c=s.nextInt();        switch(c){        case 1:while(kaoshi!=888){            System.out.println("请问要答几道题");//要答e道题            int e=s.nextInt();            double f=100.0/e;//f为每一道题占多少分            int g=r.nextInt(10);            arrl.add(g);            for(int i=1;i<e;i++){                int h=r.nextInt(10);                if(arrl.contains(h)){                    i--;                }                else{                    arrl.add(h);                }                            }            int j=1;//j记录题号            int x=0;//x表示正确的题数,y反之            int y=0;            double sum=0;                for(Integer it:arrl){                    System.out.println("第"+j+"题");                    System.out.println(hm.get(it));                    System.out.println("请输入您的答案:");                    String str=s.next();                    j++;                    if(str.equals(arr.get(it))){                        sum+=f;                        x++;                        System.out.println("恭喜你,这道题答对了!");                    }                    else{                        y++;                        System.out.println("很抱歉,这道题你答错了!");                    }                }                System.out.println("您的得分是:"+sum);                System.out.println("正确的有"+x+"道");                System.out.println("错误的有"+y+"道");                if(sum>=60){                    System.out.println("恭喜你,成绩合格,请问还想继续答题吗?");                }                else{                    System.out.println("别学了,重开吧!");                }                System.out.println("-------------------------------");                System.out.println("若想退出考试请输入888,不想退出输入其他字符");                int k=s.nextInt();                kaoshi=k;                System.out.println("若想退出用户操作界面请输入999,若不想请随意输入");                int kk=s.nextInt();                daxunhuan=kk;                if(kk==999){                    System.out.println("您已成功退出用户操作界面!");                }            }        break;        case 2:System.out.println("-------------------------------");        System.out.println("试题管理:");        System.out.println("11,添加试题;  22,删除试题;  33,修改试题;  44,查找试题;  55,遍历试题。");        System.out.println("请输入您想操作的编号");        int l=s.nextInt();        switch(l){          case 11:System.out.println("请问您想添加几道题");        int m=s.nextInt();        int n=arr.size();        for(int w=n;w<=m+n-1;w++){            System.out.println("请添加题目:");                        String str=s.next();            hm.put(w, str);            System.out.println("请添加本题答案:");            String str2=s.next();            arr.add(str2);        }        System.out.println("您的题目已增加完毕");            break;         case 22:  System.out.println("请问要删除第几道题?");         int tishu=arr.size();         int p=s.nextInt();         arr.remove(p-1);         System.out.println("已删除答案!");         hm.remove(p-1);         for(int i=p;i<tishu;i++){             String str1=hm.get(i);                        hm.put(i-1, str1);              }         System.out.println("-------------------------------");         System.out.println("已删除试题!");         break;         case 33:System.out.println("-------------------------------");         System.out.println("请问要修改第几道题?");         int xiugai=s.nextInt();         System.out.println("-------------------------------");         System.out.println("您选择修改第"+xiugai+"道题,请修改题目:");         String str=s.next();         hm.put(xiugai-1, str);         System.out.println("题目修改完毕");         System.out.println("===============================");         System.out.println("请修改答案");         String str3=s.next();         arr.set(xiugai-1, str3);         System.out.println("答案修改完毕!");         break;         case 44:System.out.println("请问要查询第几题?");         int z=s.nextInt();         System.out.println("该题题目为:");         System.out.println(hm.get(z-1));         System.out.println("该题答案为:");                  System.out.println(arr.get(z-1));         System.out.println("-------------------------------");        break;         case 55:int jj=0;             Set set=hm.entrySet();         Iterator it =set.iterator();         while(it.hasNext()){             Map.Entry me=(Map.Entry)(it.next());             int ii=(int) (me.getKey());            jj=ii+1;             System.out.println("第"+jj+"题题目:");             System.out.println(me.getValue());             System.out.println("第"+jj+"题答案:");             System.out.println(arr.get(ii));             System.out.println("-------------------------------");         }         break;         default : daxunhuan=999;         System.out.println("您的输入有错误,已退出,请重开!");         break;        }//5选1括号        break;        case 3:System.out.println("若想退出请输入999");            int xx=s.nextInt();             daxunhuan=xx;             System.out.println("您已退出!");             break;               }//3选一括号            }//daxunhuan括号                }        }文章到这里就结束了,如果文章有什么需要改进的地方,还请大佬指正制作不用,感谢各位小伙伴儿们的支持小威在这里谢谢大家✨✨✨最后,各位陪伴了Java多少年了呢,可以在评论区里互相交流哟————————————————版权声明:本文为CSDN博主「小威要向诸佬学习呀」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/qq_53847859/article/details/125012673
  • [其他] 浅谈python和java的区别
    1、java的商业化公司支持多,如sap,oracle,ibm等,有商业化的容器,中间件,企业框架ejb。python的开源组织支持多,如qt,linux,google,很多开源程序都支持python, 如pyqt,redis,spark等。2、java主要用于商业逻辑强的领域,如商城系统,erp,oa,金融,保险等传统数据库事务领域,通过类似ssh框架事务代码,对商业数据库,如oralce,db2,sql server等支持较好,软件工程理念较强,适合软件工程式的多人开发模式。python主要用于web数据分析,科学计算,金融分析,信号分析,图像算法,数学计算,统计分析,算法建模,服务器运维,自动化操作,快速开发理念强,适合快速开发团队或个人敏捷模式。3、python有很多虚拟机实现,如cython,Pyston,pypy,jython, IronPython等等,适合用于业务语言,或插件语言,或面向领域语言,而java因为虚拟机巨大,很少用于插件语言,发布也不方便。4、python虚拟机没有java强,java虚拟机是java的核心,python的核心是可以很方便地使用c语言函数或c++库。5、python是全动态性的,可以在运行时自己修改自己的代码,java只能通过变通方法实现。python的变量是动态的,而java的变量是静态的,需要事先声明,所以java ide的代码提示功能优于python ide。6,python的产生几十年了,几十年前面向过程是主流,所以用python有好多程序用的是面向过程设计方法,很多概念从c语言过来的,class在python中是后加入的,而java是为了实现没有指针的c++(当年com组件用的引用记数,java用的虚拟机),主要采用面向对象的设计方法,很多概念是oop的概念。面向过程,相对简洁直观,但容易设计出面条程序,面向对象,相对抽象优雅,但容易过度抽象。7,在实际使用的python入门简单,但要学会用python干活,需要再学习python各种库,pyhton的强大在于库,为什么python的库强大,原因是python的库可以用python,c语言,c++等设计,再提供给python使用,所以无论gpu运行,神经网络,智能算法,数据分析,图像处理,科学计算,各式各样的库在等着你用。而java没有python那么多的开源库,很多库是商业公司内部使用,或发布出来只是一个jar包,看不到原始代码。python虚拟机因为编译性没有java的支持的好(或者说故意这么设计的),一般直接使用源码(linux),或源码简单打个包(如pyexe)。实例:在java类中直接执行python语句如:maven添加依赖<dependency>    <groupId>org.python</groupId>    <artifactId>jython-standalone</artifactId>    <version>2.7.0</version></dependency>在java类中写python语句PythonInterpreter interpreter = new PythonInterpreter();interpreter.exec("menu=[1,2,3,5,6,7]; ");interpreter.exec("print(sorted(menu));"); interpreter.exec("print sorted(menu);");
  • [技术干货] Java,自从看了你一眼,就再也无法忘怀[转载]
    文章目录一、初识Java二、我与Java差点擦肩而过三、我的第一个Java项目四、我用过的开发工具五、Java经久不衰的关键总结转眼间Java已经27岁了,Java在1995年5月23日诞生,诞生之后在到现在以经27年了,但依旧保持旺盛的活力,那是因为Java语言作为静态面向对象编程语言的代表,极好的实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程,Java具有简单性,面向对象,分布式,健壮性,安全性,平台独立与可移植性,多线程,动态性等特点,总的来说,Java是一门非常方便的语言,只要你能想到的方法,都有大佬已经帮你写好了,非常的方便,我相信Java在后续的发展中,一定会越来越好,我自己本身也是一个大学生,学习的也是Java语言,未来也会在Java这个领域一直干下去一、初识Java我首次接触Java的时候还是在我大学的时候第一节JavaSE课程,我们的Java老师给我们介绍Java的产生和发展,我对Java产生了憧憬,在课后敲下了我的第一行Java代码:public class Main {    public static void main(String[] args) {    System.out.println("hello world!!!");    }}我相信大部分的Java生涯都是从这行代码开始的,这是我Java生涯的起点,也是我和Java的故事的起点二、我与Java差点擦肩而过我现在是一名大三的学生,不准备考研的我当然是准备学习一门专业知识,出社会后找到一个满意的工作,对于我的专业来说,计算机科学与技术的学生,从事编程工作无非就是C/C++,Java,还有python,我就在考虑我到时候到底从事哪个领域,我在Java和C++ 中犹豫了很久,最终选择了Java,既然选择了它,我就要认真的对待它,毕竟我可是一个很专一的人,但是在我Java相处的时间里,开始的相处,我对它还不是很熟悉,我完全看不明白它,在一段时间里,我甚至觉得我和它不合适,我和它的在一起就是一个错误,但是再后来我发现之前是因为我不够了解它,在我和Java后面的陆续相处中,我发现Java非常的有意思,你越了解它越觉得它有意思三、我的第一个Java项目我的第一个Java项目,是一个很简单的命令行实现的图书管理系统,就这么一个很简单的小项目对于当时的我来说就觉得非常的困难,但是当你真的完成了它的时候,那种成就感是任何事情都带不来的,这个项目几乎包含了JavaSE基础语法部分,这也是对基础知识的一个检验四、我用过的开发工具在我上大学时的Java课上,我们的Java老师给我们推荐的是一款叫eclipse的软件,这也是我第一次使用开发工具,在当时连安装JDK,搭建环境对我来说都还是一个难题,我也是在这个开发工具上敲的我的第一行Java代码,后来在朋友的推荐下,我使用了IDEA这款开发工具,这款工具相对于eclipse来说要好用很多,非常的方便,而JDK,我一直使用的是JDK8五、Java经久不衰的关键1.无处不在、免费Java是免费下载和简单易用的,因此它对有远见卓识的开发人员和企业很有吸引力,而且可能只花很少的预算。因为Java无处不在,所以很容易向你的CTO或客户销售可靠、无风险的产品。开发人员和项目团队可以找到并指出许多基于Java的站点示例,以减轻高级管理人员或客户的担忧。想学习java的同学不妨报个Java培训班,可以节省学习时间,提高学习效率,在短时间内学有所成,还能找到一份不错的工作。2.平等机会申请Java编程语言支持分布式环境、internet使用和将在具有多个服务器和客户端的网络上运行的应用程序。它可以用于网站、移动应用程序、智能设备等,在小程序或应用程序模块中也同样有效。它适用于网络浏览器、在线商店、投票和论坛、表单处理、手机应用程序和消费产品。总结我的Java学习之路还很漫长,后面要学习的东西还很多,在后续我也会一直从事Java领域,希望Java会一直越来越好。原文链接:https://blog.csdn.net/weixin_57011679/article/details/124946316
  • [毕昇JDK] 【技术剖析】14. Java反射机制清空字符串导致业务异常分析
    > 编者按:笔者在处理业务线问题时遇到接口返回的内容和实际内容不一致的现象。根因是业务方通过Java反射机制将String类型敏感数据引用的value数组元素全部设置为'0',从而实现清空用户敏感数据的功能。这种清空用户敏感数据的方法会将字符串常量池相应地址的内容修改,进而导致所有指向该地址的引用的内容和实际值不一致的现象。## 背景知识JVM为了提高性能和减少内存开销,在实例化字符串常量时进行了优化。JVM在Java堆上开辟了一个字符串常量池空间(StringTable),JVM通过ldc指令加载字符串常量时会调用 StringTable::intern 函数将字符串加入到字符串常量池中。- StringTable::intern函数代码```coop StringTable::intern(Handle string_or_null, jchar* name, int len, TRAPS) { unsigned int hashValue = hash_string(name, len); int index = the_table()->hash_to_index(hashValue); oop found_string = the_table()->lookup(index, name, len, hashValue); // Found if (found_string != NULL) { ensure_string_alive(found_string); return found_string; } debug_only(StableMemoryChecker smc(name, len * sizeof(name[0]))); assert(!Universe::heap()->is_in_reserved(name), "proposed name of symbol must be stable"); Handle string; // try to reuse the string if possible if (!string_or_null.is_null()) { string = string_or_null; } else { string = java_lang_String::create_from_unicode(name, len, CHECK_NULL); }#if INCLUDE_ALL_GCS if (G1StringDedup::is_enabled()) { // Deduplicate the string before it is interned. Note that we should never // deduplicate a string after it has been interned. Doing so will counteract // compiler optimizations done on e.g. interned string literals. G1StringDedup::deduplicate(string()); }#endif // Grab the StringTable_lock before getting the_table() because it could // change at safepoint. oop added_or_found; { MutexLocker ml(StringTable_lock, THREAD); // Otherwise, add to symbol to table added_or_found = the_table()->basic_add(index, string, name, len, hashValue, CHECK_NULL); } ensure_string_alive(added_or_found); return added_or_found;}```- StringTable::intern 函数处理流程![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/20/1655693680014556176.png)- 字符串的创建方式 根据StringTable::intern函数处理流程,我们可以简单描绘如下6种常见的字符串的创建方式以及引用关系。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/20/1655693721592211530.png)## 现象 某业务线使用fastjson实现Java对象序列化功能,低概率出现接口返回的JSON数据的某个属性值和实际值不一致的现象。正确的属性值应该为"null",实际属性值却为"0000"。## 原因分析为了排除fastjson自身的嫌疑,我们将其替换jackson后,依然会低概率出现同样的现象。由于两个不同三方件同时存在这个问题的可能性不大,为此我们暂时排除fastjson引入该问题的可能性。为了找到该问题的根因,我们在环境中开启远程调试功能。待问题复现,调试代码时我们发现只要是指向"null"的引用,显示的内容全部变成"0000",由此我们初步怀疑字符串常量池中的"null"被修改成"0000"。一般导致常量池被修改有两种可能性:1. 第三方动态库引入的bug导致字符串常量池内容被修改;2. 在业务代码中通过Java反射机制主动修改字符串常量池内容;业务方排查项目中使用到的第三方动态库,未发现可疑的动态库,排除第一种可能性。排查业务代码中使用到Java反射的功能,发现清空密码功能会使用到Java反射机制,并且将String类型密码的value数组元素全部设置为'0'。业务出现的现象可以简单通过代码模拟:1. 在TestString对象类中定义一个nullStr属性,初始值为"null";2. 定义一个带有password属性的User类;3. 在main方法中创建一个密码为"null"的User对象,使用Java反射机制将密码字符串的所有字符全部修改为'0',分别在密码修改前后打印TestString对象nullStr属性值;## 复现代码```javaimport java.lang.reflect.Field;import java.util.Arrays;public class TestString { private String nullStr = "null"; public String getNullStr() { return nullStr; } static class User { private final String password; User(String password) { this.password = password; } public String getPassword() { return password; } } private static void clearPassword(User user) throws Exception { Field field = String.class.getDeclaredField("value"); field.setAccessible(true); char[] chars = (char[]) field.get(user.getPassword()); Arrays.fill(chars, '0'); } public static void main(String[] args) throws Exception { User user = new User("null"); TestString testString = new TestString(); System.out.println("before clear password >>>>"); System.out.println(" User.password:" + user.getPassword()); System.out.println("TestString.nullStr:" + testString.getNullStr()); System.out.println("--------------------------------"); clearPassword(user); System.out.println("after clear password >>>>"); System.out.println(" User.password:" + user.getPassword()); System.out.println("TestString.nullStr:" + testString.getNullStr()); }}```复现代码字符串引用关系如下图所示。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/20/1655693744310705244.png) User对象的password属性和TestString的nullStr属性引用都同时指向常量池中的"null"字符串,"null"字符串的value指向 {'n','u','l','l'} char数组。使用Java反射机制将User对象的password属性引用的value数组全部设置为'0',导致 TestString的nullStr属性值也变成了 "0000"。输出结果如下: ``` before clear password >>>> User.password:null TestString.nullStr:null -------------------------------- after clear password >>>> User.password:0000 TestString.nullStr:0000 ``` 通过输出结果我们可以发现在通过Java反射机制修改某一个字符串内容后,所有指向原字符串的引用的内容全部变成修改后的内容。## 总结在保存业务敏感数据时避免使用String类型保存,建议使用byte[]或char[]数组保存,然后通过Java反射机制清空敏感数据。## 后记如果遇到相关技术问题(包括不限于毕昇 JDK),可以通过 Compiler SIG 求助。Compiler SIG 每双周周二举行技术例会,同时有一个技术交流群讨论 GCC、LLVM 和 JDK 等相关编译技术,感兴趣的同学可以添加如下微信小助手入群。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/20/1655693821688262524.jpg)-----原文转载自毕昇编译-[Java反射机制清空字符串导致业务异常分析](https://mp.weixin.qq.com/s/jLkof6Z1MMzBJ7eODV0pWQ)关注毕昇编译获取编译技术更多信息![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/20/1655693932992153146.jpg)
  • [技术干货] 这是啥SQL,室友看了人傻了[转载]
    文章目录SQLite适应常规基本应用场景SQLite面对复杂场景尚有不足SPL全面支持各种数据源SPL的计算能力更强大优化体系结构SPL资料可以在Java应用中嵌入的数据引擎看起来比较丰富,但其实并不容易选择。Redis计算能力很差,只适合简单查询的场景。Spark架构复杂沉重,部署维护很是麻烦。H2\HSQLDB\Derby等内嵌数据库倒是架构简单,但计算能力又不足,连基本的窗口函数都不支持。相比之下,SQLite在架构性和计算能力上取得了较好的平衡,是应用较广的Java嵌入数据引擎。SQLite适应常规基本应用场景SQLite架构简单,其核心虽然是C语言开发的,但封装得比较好,对外呈现为一个小巧的Jar包,能方便地集成在Java应用中。SQLite提供了JDBC接口,可以被Java调用:Connection connection = DriverManager.getConnection("jdbc:sqlite::memory:");Statement st = connection.createStatement();st.execute("restore from d:/ex1");ResultSet rs = st.executeQuery("SELECT * FROM orders");1234SQLite提供了标准的SQL语法,常规的数据处理和计算都没有问题。特别地,SQLite已经能支持窗口函数,可以方便地实现很多组内运算,计算能力比其他内嵌数据库更强。SELECT x, y, row_number() OVER (ORDER BY y) AS row_number FROM t0 ORDER BY x;SELECT a, b, group_concat(b, '.') OVER ( ORDER BY a ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS group_concat FROM t1;12SQLite面对复杂场景尚有不足SQLite的优点亮眼,但对于复杂应用场景时还是有些缺点。Java应用可能处理的数据源多种多样,比如csv文件、RDB、Excel、Restful,但SQLite只处理了简单情况,即对csv等文本文件提供了直接可用的命令行加载程序:.import --csv --skip 1 --schema temp /Users/scudata/somedata.csv tab1对于其他大部分数据源,SQLite都没有提供方便的接口,只能硬写代码加载数据,需要多次调用命令行,整个过程很繁琐,时效性也差。以加载RDB数据源为例,一般的做法是先用Java执行命令行,把RDB库表转为csv;再用JDBC访问SQLite,创建表结构;之后用Java执行命令行,将csv文件导入SQLite;最后为新表建索引,以提高性能。这个方法比较死板,如果想灵活定义表结构和表名,或通过计算确定加载的数据,代码就更难写了。类似地,对于其他数据源,SQLite也不能直接加载,同样要通过繁琐地转换过程才可以。SQL接近自然语言,学习门槛低,容易实现简单的计算,但不擅长复杂的计算,比如复杂的集合计算、有序计算、关联计算、多步骤计算。SQLite采用SQL语句做计算,SQL优点和缺点都会继承下来,勉强实现这些复杂计算的话,代码会显得繁琐难懂。比如,某只股票最长的上涨天数,SQL要这样写:select max(continuousDays)-1from (select count(*) continuousDaysfrom (select sum(changeSign) over(order by tradeDate) unRiseDaysfrom (select tradeDate,case when price>lag(price) over(order by tradeDate) then 0 else 1 end changeSign from AAPL) )group by unRiseDays)123456这也不单是SQLite的难题,事实上,由于集合化不彻底、缺乏序号、缺乏对象引用等原因,其他SQL数据库也不擅长这些运算。业务逻辑由结构化数据计算和流程控制组成,SQLite支持SQL,具有结构化数据计算能力,但SQLite没有提供存储过程,不具备独立的流程控制能力,也就不能实现一般的业务逻辑,通常要利用Java主程序的判断和循环语句。由于Java没有专业的结构化数据对象来承载SQLite数据表和记录,转换过程麻烦,处理过程不畅,开发效率不高。前面提过,SQLite内核是C程序,虽然可以被集成到Java应用中,但并不能和Java无缝集成,和Java主程序交换数据时要经过耗时的转换才能完成,在涉及数据量较大或交互频繁时性能就会明显不足。同样因为内核是C程序,SQLite会在一定程度上破坏Java架构的一致性和健壮性。对于Java应用来讲,原生在JVM上的esProc SPL是更好的选择。SPL全面支持各种数据源esProc SPL是JVM下开源的嵌入数据引擎,架构简单,可直接加载数据源,可以通过JDBC接口被Java集成调用,并方便地进行后续计算。SPL架构简单,无须独立服务,只要引入SPL的Jar包,就可以部署在Java环境中。直接加载数据源,代码简短,过程简单,时效性强。比如加载Oracle:A1    =connect("orcl")2    =A1.query@x("select OrderID,Client,SellerID,OrderDate,Amount from orders order by OrderID")3    >env(orders,A2)对于SQLite擅长加载的csv文件,SPL也可以直接加载,使用内置函数而不是外部命令行,稳定且效率高,代码更简短:=T(“/Users/scudata/somedata.csv”)多种外部数据源。除了RDB和csv,SPL还直接支持txt\xls等文件,MongoDB、Hadoop、redis、ElasticSearch、Kafka、Cassandra等NoSQL,以及WebService XML、Restful Json等多层数据。比如,将HDSF里的文件加载到内存:A1    =hdfs_open(;"hdfs://192.168.0.8:9000")2    =hdfs_file(A1,"/user/Orders.csv":"GBK")3    =A2.cursor@t()4    =hdfs_close(A1)5    >env(orders,A4)    JDBC接口可以方便地集成。加载的数据量一般比较大,通常在应用的初始阶段运行一次,只须将上面的加载过程存为SPL脚本文件,在Java中以存储过程的形式引用脚本文件名:Class.forName("com.esproc.jdbc.InternalDriver");Connection conn =DriverManager.getConnection("jdbc:esproc:local://");CallableStatement statement = conn.prepareCall("{call init()}");statement.execute();1234SPL的计算能力更强大SPL提供了丰富的计算函数,可以轻松实现日常计算。SPL支持多种高级语法,大量的日期函数和字符串函数,很多用SQL难以表达的计算,用SPL都可以轻松实现,包括复杂的有序计算、集合计算、分步计算、关联计算,以及带流程控制的业务逻辑。丰富的计算函数。SPL可以轻松实现各类日常计算:A    B1    =Orders.find(arg_OrderIDList)    //多键值查找2    =Orders.select(Amount>1000 && like(Client,\"*S*\"))    //模糊查询3    = Orders.sort(Client,-Amount)    //排序4    = Orders.id(Client)    //去重5    =join(Orders:O,SellerId; Employees:E,EId).new(O.OrderID, O.Client,O.Amount,E.Name,E.Gender,E.Dept)    //关联标准SQL语法。SPL也提供了SQL-92标准的语法,比如分组汇总:$select year(OrderDate) y,month(OrderDate) m, sum(Amount) s,count(1) cfrom {Orders}Where Amount>=? and Amount<? ;arg1,arg2123函数选项、层次参数等方便的语法。功能相似的函数可以共用一个函数名,只用函数选项区分差别,比SQL更加灵活方便。比如select函数的基本功能是过滤,如果只过滤出符合条件的第1条记录,可使用选项@1:T.select@1(Amount>1000)二分法排序,即对有序数据用二分法进行快速过滤,使用@b:T.select@b(Amount>1000)有序分组,即对分组字段有序的数据,将相邻且字段值相同的记录分为一组,使用@b:T.groups@b(Client;sum(Amount))函数选项还可以组合搭配,比如:Orders.select@1b(Amount>1000)结构化运算函数的参数有些很复杂,比如SQL就需要用各种关键字把一条语句的参数分隔成多个组,但这会动用很多关键字,也使语句结构不统一。SPL使用层次参数简化了复杂参数的表达,即通过分号、逗号、冒号自高而低将参数分为三层:join(Orders:o,SellerId ; Employees:e,EId)更丰富的日期和字符串函数。除了常见函数,比如日期增减、截取字符串,SPL还提供了更丰富的日期和字符串函数,在数量和功能上远远超过了SQL,同样运算时代码更短。比如:季度增减:elapse@q(“2020-02-27”,-3) //返回2019-05-27N个工作日之后的日期:workday(date(“2022-01-01”),25) //返回2022-02-04字符串类函数,判断是否全为数字:isdigit(“12345”) //返回true取子串前面的字符串:substr@l(“abCDcdef”,“cd”) //返回abCD按竖线拆成字符串数组:“aa|bb|cc”.split(“|”) //返回[“aa”,“bb”,“cc”]SPL还支持年份增减、求季度、按正则表达式拆分字符串、拆出SQL的where或select部分、拆出单词、按标记拆HTML等大量函数。简化有序运算。涉及跨行的有序运算,通常都有一定的难度,比如比上期和同期比。SPL使用"字段[相对位置]"引用跨行的数据,可显著简化代码,还可以自动处理数组越界等特殊情况,比SQL窗口函数更加方便。比如,追加一个计算列rate,计算每条订单的金额增长率:=T.derive(AMOUNT/AMOUNT[-1]-1: rate)综合运用位置表达式和有序函数,很多SQL难以实现的有序运算,都可以用SPL轻松解决。比如,根据考勤表,找出连续 4 周每天均出勤达 7 小时的学生:A1    =Student.select(DURATION>=7).derive(pdate@w(ATTDATE):w)2    =A1.group@o(SID;~.groups@o(W;count(~):CNT).select(CNT==7).group@i(W-W[-1]!=7).max(~.len()):weeks)3    =A2.select(weeks>=4).(SID)简化集合运算,SPL的集合化更加彻底,配合灵活的语法和强大的集合函数,可大幅简化复杂的集合计算。比如,在各部门找出比本部门平均年龄小的员工:A1    =Employees.group(DEPT; (a=~.avg(age(BIRTHDAY)),~.select(age(BIRTHDAY)<a)):YOUNG)2    =A1.conj(YOUNG)计算某支股票最长的连续上涨天数:A1    =a=0,AAPL.max(a=if(price>price[-1],a+1,0))简化关联计算。SPL支持对象引用的形式表达关联,可以通过点号直观地访问关联表,避免使用JOIN导致的混乱繁琐,尤其适合复杂的多层关联和自关联。比如,根据员工表计算女经理的男员工:=employees.select(gender:“male”,dept.manager.gender:“female”)方便的分步计算,SPL集合化更加彻底,可以用变量方便地表达集合,适合多步骤计算,SQL要用嵌套表达的运算,用SPL可以更轻松实现。比如,找出销售额累计占到一半的前n个大客户,并按销售额从大到小排序:A    B2    =sales.sort(amount:-1)    /销售额逆序排序,可在SQL中完成3    =A2.cumulate(amount)    /计算累计序列4    =A3.m(-1)/2    /最后的累计即总额5    =A3.pselect(~>=A4)    /超过一半的位置6    =A2(to(A5))    /按位置取值流程控制语法。SPL提供了流程控制语句,配合内置的结构化数据对象,可以方便地实现各类业务逻辑。分支判断语句:A    B2    …    3    if T.AMOUNT>10000    =T.BONUS=T.AMOUNT*0.054    else if T.AMOUNT>=5000 && T.AMOUNT<10000    =T.BONUS=T.AMOUNT*0.035    else if T.AMOUNT>=2000 && T.AMOUNT<5000    =T.BONUS=T.AMOUNT*0.02循环语句:A    B1    =db=connect("db")    2    =T=db.query@x("select * from sales where SellerID=? order by OrderDate",9)3    for T    =A3.BONUS=A3.BONUS+A3.AMOUNT*0.014        =A3.CLIENT=CONCAT(LEFT(A3.CLIENT,4), " co.,ltd.")5         …与Java的循环类似,SPL还可用break关键字跳出(中断)当前循环体,或用next关键字跳过(忽略)本轮循环,不展开说了。计算性能更好。在内存计算方面,除了常规的主键和索引外,SPL还提供了很多高性能的数据结构和算法支持,比大多数使用SQL的内存数据库性能好得多,且占用内存更少,比如预关联技术、并行计算、指针式复用。优化体系结构SPL支持JDBC接口,代码可外置于Java,耦合性更低,也可内置于Java,调用更简单。SPL支持解释执行和热切换,代码方便移植和管理运营,支持内外存混合计算。外置代码耦合性低。SPL代码可外置于Java,通过文件名被调用,既不依赖数据库,也不依赖Java,业务逻辑和前端代码天然解耦。对于较短的计算,也可以像SQLite那样合并成一句,写在Java代码中:Class.forName("com.esproc.jdbc.InternalDriver");Connection conn =DriverManager.getConnection("jdbc:esproc:local://");Statement statement = conn.createStatement();String arg1="1000";String arg2="2000"ResultSet result = statement.executeQuery(=Orders.select(Amount>="+arg1+" && Amount<"+arg2+"). groups(year(OrderDate):y,month(OrderDate):m; sum(Amount):s,count(1):c)");123456解释执行和热切换。业务逻辑数量多,复杂度高,变化是常态。良好的系统构架,应该有能力应对变化的业务逻辑。SPL是基于Java的解释型语言,无须编译就能执行,脚本修改后立即生效,支持不停机的热切换,适合应对变化的业务逻辑。方便代码移植。SPL通过数据源名从数据库取数,如果需要移植,只要改动配置文件中的数据源配置信息,而不必修改SPL代码。SPL支持动态数据源,可通过参数或宏切换不同的数据库,从而进行更方便的移植。为了进一步增强可移植性,SPL还提供了与具体数据库无关的标准SQL语法,使用sqltranslate函数可将标准SQL转为主流方言SQL,仍然通过query函数执行。方便管理运营。由于支持库外计算,代码可被第三方工具管理,方便团队协作;SPL脚本可以按文件目录进行存放,方便灵活,管理成本低;SPL对数据库的权限要求类似Java,不影响数据安全。内外存混合计算。有些数据太大,无法放入内存,但又要与内存表共同计算,这种情况可利用SPL实现内外存混合计算。比如,主表orders已加载到内存,大明细表orderdetail是文本文件,下面进行主表和明细表的关联计算:A1    =file("orderdetail.txt").cursor@t()2    =orders.cursor()3    =join(A1:detail,orderid ; A2:main,orderid)4    =A3.groups(year(main.orderdate):y; sum(detail.amount):s)SQLite使用简单方便,但数据源加载繁琐,计算能力不足。SPL架构也非常简单,并直接支持更多数据源。SPL计算能力强大,提供了丰富的计算函数,可以轻松实现SQL不擅长的复杂计算。SPL还提供多种优化体系结构的手段,代码既可外置也可内置于Java,支持解释执行和热切换,方便移植和管理运营,并支持内外存混合计算。原文链接:https://blog.csdn.net/m0_60264772/article/details/125273709
  • [使用说明] 开始使用CodeArts
    本篇内容主要介绍使用CodeArts创建工程、代码补全和重构、运行调试代码、Build构建和测试相关的主要功能。一、下载安装CodeArtsCodeArts安装要求         至少需要 2 GB RAM ,但是推荐8 GB RAM;         至少需要 2.5 GB 硬盘空间,推荐SSD;         64位Microsoft Windows 10下载并安装CodeArts        >>>前往CodeArts官方下载页面        下载解压完成后,运行CodeArts-*.exe文件。        按照安装导航的步骤,选择个人安装配置进行安装。二、CodeArts登陆与激活        运行CodeArts,前往CodeArts登陆与激活三、创建或导入Java工程CodeArts支持选择四种Java模板创建工程,选择创建SpringBoot工程时,可根据需求选择(可多选)相应的第三方依赖,创建成功后第三方依赖被成功写入pom.xml或build.gradle文件。创建成功的提示弹窗中,点击→此窗口,工程在当前窗口打开。点击→新窗口,CodeArts将打开新窗口并加载已创建的工程。CodeArts工程创建完成,系统自动进入工程工作界面。创建工程实例:CodeArts同样可以导入已有的Java工程,见下方:四、语言服务初始化与日志查看加载项目时,Java语言服务会进行初始化,状态栏会有语言服务初始化过程提示信息,点击可查看更多语言服务初始化过程的日志。语言服务初始化过程中会启动相关服务、下载依赖的Jar包及进行Indexing,此过程受计算机性能、网速等因素影响会耗费一定的时间。语言服务初始化完成之前,语言服务相关功能(如代码补全、代码重构、查看类型定义等)将不会有很好的体验,此时需耐心等待语言服务初始化完成。下图:点击语言服务状态栏查看更多信息下图:语言服务未初始化完成时,代码补全提示‘Element information is not available during index update’当右下角弹出提示“Language Server is ready”时(见下图),说明语言服务已初始化完毕,此时可执行文件类和方法会出现运行按钮,我们就可以开始正常使用语言服务的全部功能。五、代码补全和重构语言服务初始化完成后,即可使用代码补全和代码重构等功能。代码补全列表中包含语言服务的补全及强大的AI智能补全(带有⊕图标),见下图。>>>更多代码补全使用详情通过顶部菜单“重构”功能或者右键菜单中“Refactor”即可使用代码重构的功能,CodeArts同样支持使用快捷键来快速进行代码重构。>>>更多重构使用详情CodeArts还提供了华为云SDK代码补全助手,自动生成华为云SDK使用的模板代码(右键菜单中可解锁启用)。语言服务的其它功能(如查找引用,转到声明等),请自行尝试使用。六、代码运行调试语言服务初始化完成后,在可运行的文件左侧会显示运行图标,选择run或者debug即可开始调试,调试控制台中将会显示调试运行的信息。CodeArts同样支持配置更多运行参数,可在代码左侧设置断点,debug进入断点后,可在右侧运行视图中查看调试相关信息(变量、监视、调用堆栈),也可以对断点进行管理。 CodeArts不支持并行运行,如果在当前文件或跨文件同时触发多个类和方法的运行,CodeArts将提示相应消息。七、构建工程CodeArts会在代码调试运行前自动进行工程构建,我们同样可以通过菜单项或者构建视图单独构建工程,构建视图右侧将显示构建过程信息。八、运行单元测试用例CodeArts在底部测试视图提供单元测试运行功能。运行当前文件所有用例,请点击类文件左侧绿色按钮,选择Run模式,底部栏自动打开测试视图,展示当前运行所有用例,右侧输出运行日志。运行当前单个测试用例,请点击用例左侧绿色按钮,选择Run模式或点击测试视图列表内用例右侧的绿色按钮,测试视图将展示当前运行的用例,点击用例后右侧输出该用例的运行日志。底部栏测试视图提供了用例管理的相关功能,如筛选、字母排序、耗时排序、运行日志、终止运行。
  • [问题求助] 设备接入IOTDA使用Java Demo获取不到token
    【功能模块】【操作步骤&问题现象】1、已经配置好了Java和IDEA2、这两步骤也做了,出现了以下报错:推测:没有获取到token,token中为空,怎么解?
  • [使用说明] CodeArts快捷键(一)
    CodeArts快捷键在编码过程中经常使用,能够极大提升编码效率。键盘快捷键编辑器CodeArts使用键盘快捷键编辑器提供丰富且简单的键盘快捷键编辑体验。您可以通过快捷键Ctrl+K Ctrl+S或点击左下角的Manage > Keyboard Shortcuts来打开快捷键编辑器。它列出了所有绑定及未绑定的可用命令,您可以通过使用右键菜单轻松更改/删除/重置其键绑定。顶部搜索框可帮助您查找命令或键绑定。 键盘快捷键编辑器主要包含Command、Keybinding、When、Source,其中您可以通过When值来查看何种条件下使用快捷键,如果您的快捷键没有when值,则键绑定为全局可用。检测键绑定冲突如果您自定义了键盘快捷键,有时可能会遇到键绑定冲突,即相同的键盘快捷键映射到多个命令。这可能会让您感到混乱,特别是当您在编辑器中操作快捷键,相同的快捷键绑定不同的When值时。 键盘快捷键编辑器有一个上下文菜单命令Show Same Keybindings,该命令将根据键盘快捷键过滤键绑定以显示冲突。选择一个您认为重复的快捷键,您可以查看是否定义了多个命令、键绑定的来源及触发条件。录制按键功能录制按键功能可以帮助您快速找到键绑定及命令,按快捷键Alt+K或点击Record Keys,在键盘敲快捷键,即可成功查找到该快捷键绑定的所有命令。默认已绑定快捷键基本的编辑功能命令快捷键功能剪切Ctrl+X/Shift+Delete剪切当前行或选定的内容到剪切板复制Ctrl+C/Ctrl+Insert复制当前行或选定的内容到剪切板粘贴Ctrl+V/Shift+Insert从剪切板粘贴删除行Ctrl+Shift+K删除当前行在下面插入行Ctrl+Shift+Enter/Shift+Enter在光标下方开始新的一行在上面插入行Ctrl+Alt+ Enter在光标上方开始新的一行向下移动行Ctrl+Shift+DownArrow/Shift+Alt+DownArrow将当前行向下移动向上移动行Ctrl+Shift+UpArrow/Shift+Alt+UpArrow将当前行向上移动向下复制行Ctrl+D将当前行向下复制撤消Ctrl+Z返回上一次操作恢复Ctrl+Y恢复上一次操作选择全部Ctrl+A全选内容光标撤消Shift+AIt+J撤消最后一次光标操作在行尾添加光标Shift+AIt+I在选定的每一行的末尾插入光标选择所有找到的查找匹配项Ctrl+Shift+Alt+J选择当前选择的所有匹配项更改所有匹配项Shift+F6选择当前单词的所有匹配项在下面添加光标Ctrl+Alt+DownArrow在下方插入光标在上面添加光标Ctrl+Alt+UpArrow在上方插入光标转到括号Ctrl+Shift+\跳转到匹配的括号行缩进Ctrl+]缩进代码行减少缩进Ctri+[反缩进代码转到行首Home转到当前行的顶部转到行尾End转到当前行的尾部转到文件末尾Ctrl+End转到文件末尾转到文件开头Ctrl+Home转到文件开头向下滚动行Ctrl+DownArrow向下滚动行向上滚动行Ctrl+UpArrow向上滚动行向下滚动页面Alt+PageDown向下滚动页面向上滚动页面Alt+PageUp向上滚动页面折叠Ctrl+-折叠所有代码块展开Ctrl++/Ctrl+=展开所有代码块添加行注释Ctrl+K Ctrl+C添加行注释删除行注释Ctrl+K Ctrl+U删除行注释切换行注释Ctrl+/注释/取消行注释切换块注释Ctrl+Shift+/注释/取消块注释查找Ctrl+F文件内查找替换Ctrl+R文件内替换查找下一个F3/Enter查找模式下,向下查找查找上一个Shift+F3查找模式下,向上查找切换Tab键移动焦点Ctrl+M使用 Tab 键设置焦点查看: 切换自动换行Alt+Z显示/隐藏自动换行丰富的语言编辑命令快捷键功能触发建议Ctrl+Shift+Space触发代码推荐格式化文档Shift+Alt+F格式化文件格式化选定内容Ctrl+K Ctrl+F格式选择的文件内容转到定义Ctrl+B/Ctrl+Enter跳转到光标所在的方法、类的定义处显示悬停Ctrl+Q显示鼠标所在位置的代码的简要信息速览定义Alt+F11快速打开光标所在方法、类的定义打开侧边的定义Ctrl+K F12分屏展示代码的定义快速修复Alt+Enter代码报错,提供修复方案转到引用Shift+F12跳转到引用重命名符号Shift+F6重命名符号展开选择Shift+Alt+RightArrow展开选择收起选择Shift+Alt+LeftArrow收起选择裁剪尾随空格Ctrl+K Ctrl+X去掉行末尾没用的空格Show Call HierarchyShift+Alt+H调用层次结构代码重构命令快捷键功能Introduce VariableCtrl+Alt+V引入变量Introduce FieldCtrl+Alt+Shift+F引入字段Introduce ConstantCtrl+Alt+C引入常量Introduce ParameterCtrl+Alt+Shift+P引入参数Extract MethodCtrl+Alt+Shift+M提取方法Copy classShift+F5复制类Change Class SignatureCtrl+F6更改类签名Change Method SignatureCtrl+F6更改方法签名Inline ParameterCtrl+Alt+Shift+P内联参数Inline MethodCtrl+Alt+Shift+L内联方法导航命令快捷键功能后退Ctrl+Alt+LeftArrow退回到上一个操作的地方前进Ctrl+Alt+RightArrow前进到上一个操作的地方转到上一编辑位置Ctrl+Shift+Backspace光标跳转到上一次编辑位置转到编辑器中的符号Ctrl+F12/Ctrl+Shift+Alt+N转到编辑器中的符号转到文件…Ctrl+Shift+N打开文件搜索面板转到行/列Ctrl+G当前文件跳转到行视图: 快速打开组中上一个最近使用过的编辑器Ctrl+Tab快速打开组中上一个最近使用过的编辑器编辑器/窗口管理命令快捷键功能关闭窗口Ctrl+W/Ctrl+Shift+W关闭CodeArts窗口视图: 拆分编辑器Ctrl+\拆分编辑器视图: 关闭编辑器Ctrl+F4关闭编辑器视图: 关闭组中的所有编辑器Ctrl+K W关闭组中的所有编辑器视图: 关闭所有编辑器Ctrl+K Ctrl+W关闭所有编辑器视图: 重新打开已关闭的编辑器Ctrl+Shift+T重新打开已关闭的编辑器视图: 聚焦于第一个编辑器组Ctrl+1聚焦于第一个编辑器组视图: 聚焦于第二个编辑器组Ctrl+2聚焦于第二个编辑器组视图: 聚焦于第三个编辑器组Ctrl+3聚焦于第三个编辑器组视图: 聚焦到上一组编辑器Shift+Alt+Tab聚焦到上一组编辑器视图: 聚焦到下一组编辑器Alt+Tab聚焦到下一组编辑器视图: 向左移动编辑器Ctrl+Shift+PageUp向左移动编辑器视图: 向右移动编辑器Ctrl+Shift+PageDown向右移动编辑器视图: 向左移动编辑器组Ctrl+K LeftArrow向左移动编辑器组视图: 向右移动编辑器组Ctrl+K RightArrow向右移动编辑器组文件/工程管理命令快捷键功能工程: 打开工程Ctrl+O打开工程工程: 打开工程属性Shift+Alt+F9打开工程属性工程: 打开文件夹...Ctrl+Shift+O打开文件夹…工程: 导入工程Ctrl+Shift+I弹出导入工程窗口工程: 关闭文件夹/工程Ctrl+K F关闭文件夹/工程工程: 新建工程Alt+P弹出新建工程窗口文件: 新建文件Ctrl+Alt+N弹出新建文件窗口文件: 保存Ctrl+S保存所有文件: 另存为...Ctrl+Shift+S另存为文件: 打开最近的文件…Ctrl+E打开最近的文件文件: 复制活动文件的路径Ctrl+Shift+C复制绝对路径文件: 复制活动文件的相对路径Ctrl+K Ctrl+Shift+C复制相对路径显示命令快捷键功能视图: 切换全屏Ctrl+Alt+F切换全屏切换禅模式Ctrl+K Z切换禅模式退出禅模式Escape Escape退出禅模式显示快速修复Ctrl+.显示问题的修复方案显示所有命令Ctrl+Shift+A打开命令面板视图: 显示资源管理器Alt+1显示/隐藏资源管理器视图视图: 显示搜索Alt+3打开文本搜索视图: 显示源代码管理Alt+9显示/隐藏源代码管理视图视图: 显示运行和调试Alt+5/Ctrl+Shift+F8显示/隐藏运行视图视图: 切换输出Ctrl+Shift+U显示/隐藏输出视图视图: 切换集成终端Alt+F12显示/隐藏终端视图Markdown: 打开预览Ctrl+Shift+V打开Markdown预览Markdown: 打开侧边预览Ctrl+K V在侧边打开Markdown预览搜索命令快捷键功能智能搜索Ctrl Ctrl/Ctrl+N/Ctrl+Shift+L打开智能搜索面板视图: 显示搜索Alt+3打开文本搜索面板搜索: 在文件中查找Ctrl+Shift+F在文件中查找搜索: 在文件中替换Ctrl+Shift+R在文件中替换显示下一个搜索词DownArrow/Alt+DownArrow显示下一个搜索词显示上一个搜索词UpArrow/Alt+UpArrow显示上一个搜索词设置命令快捷键功能首选项: 打开设置Ctrl+,打开设置首选项: 打开Java SmartAssist开发套件设置Ctrl+Alt+P打开Java SmartAssist开发套件设置首选项: 打开键盘快捷方式Ctrl+K Ctrl+S打开键盘快捷方式首选项: 颜色主题Ctrl+`切换颜色主题运行/调试/构建命令快捷键功能运行: 开始执行(不调试)Shift+F10运行,不进入断点开始调试项目Ctrl+Alt+D调试Project调试: 开始调试Shift+F9调试停止调试Ctrl+F2停止调试Debug: 单步调试F7单步执行,进入函数内部Debug: 单步跳出Shift+F8单步执行,跳出函数内部Debug: 单步跳过F8从断点处开始,执行单步语句Debug: 继续F9执行至下一个断点Debug: 停止Ctrl+F2停止调试构建工程Ctrl+Alt+U构建工程重构工程Ctrl+Alt+I重新构建工程
  • [java] 一文带你了解J.U.C的FutureTask、Fork/Join框架和BlockingQueue
    >摘要: J.U.C是Java并发编程中非常重要的工具包,今天,我们就来着重讲讲J.U.C里面的FutureTask、Fork/Join框架和BlockingQueue。本文分享自华为云社区《[【高并发】J.U.C组件扩展](https://bbs.huaweicloud.com/blogs/353871?utm_source=csdn&utm_medium=bbs-ex&utm_campaign=other&utm_content=content)》,作者: 冰 河 。# FutureTaskFutureTask是J.U.C(java.util.concurrent)下的,但不是AQS(AbstractQueuedSynchronizer)的子类。其对线程结果的处理值得借鉴和在项目中使用。Thread和Runnable执行完任务无法获取执行结果。Java1.5开始提供了Callable和Future,通过它们可以在任务执行完毕之后,得到任务执行的结果。# Callable与Runnable接口对比Callable:泛型接口,提供一个call()方法,支持抛出异常,并且执行后有返回值Runnable:接口,提供一个run()方法,不支持抛出异常,执行后无返回值# Future接口对于具体的Callable和Runnable任务,可以进行取消,查询任务是否被取消,查询是否完成以及获取结果等。Future可以监视目标线程调用call()的情况,当调用Future的get()方法时,就可以获得结果。此时,执行任务的线程可能不会直接完成,当前线程就开始阻塞,直到call()方法结束返回结果,当前线程才会继续执行。总之,Future可以得到别的线程任务方法的返回值。# FutureTask类实现的接口为RunnableFuture,而RunnableFuture接口继承了Runnable和Future两个接口,所以FutureTask类最终也是执行Callable类型的任务。如果FutureTask类的构造方法参数是Runnable的话,会转换成Callable类型。类实现了两个接口:Runnable和Future。所以,它即可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值,这样设计的好处如下:假设有一个很费时的逻辑,需要计算并且返回这个值,同时,这个值又不是马上需要,则可以使用Runnable和Future的组合,用另外一个线程去计算返回值,而当前线程在使用这个返回值之前,可以做其他的操作,等到需要这个返回值时,再通过Future得到。Future示例代码如下:```package io.binghe.concurrency.example.aqs; import lombok.extern.slf4j.Slf4j;import java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;@Slf4jpublic class FutureExample { static class MyCallable implements Callable{ @Override public String call() throws Exception { log.info("do something in callable"); Thread.sleep(5000); return "Done"; } } public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newCachedThreadPool(); Future future = executorService.submit(new MyCallable()); log.info("do something in main"); Thread.sleep(1000); String result = future.get(); log.info("result: {}", result); executorService.shutdown(); }}```FutureTask示例代码如下:```package io.binghe.concurrency.example.aqs; import lombok.extern.slf4j.Slf4j;import java.util.concurrent.Callable;import java.util.concurrent.FutureTask;@Slf4jpublic class FutureTaskExample { public static void main(String[] args) throws Exception{ FutureTask futureTask = new FutureTask(new Callable() { @Override public String call() throws Exception { log.info("do something in callable"); Thread.sleep(5000); return "Done"; } }); new Thread(futureTask).start(); log.info("do something in main"); Thread.sleep(1000); String result = futureTask.get(); log.info("result: {}", result); }}```# Fork/Join框架位于J.U.C(java.util.concurrent)中,是Java7中提供的用于执行并行任务的框架,其可以将大任务分割成若干个小任务,最终汇总每个小任务的结果后得到最终结果。基本思想和Hadoop的MapReduce思想类似。主要采用的是工作窃取算法(某个线程从其他队列里窃取任务来执行),并行分治计算中的一种Work-stealing策略## 为什么需要使用工作窃取算法呢?假如我们需要做一个比较大的任务,我们可以把这个任务分割为若干互不依赖的子任务,为了减少线程间的竞争,于是把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应,比如A线程负责处理A队列里的任务。但是有的线程会先把自己队列里的任务干完,而其他线程对应的队列里还有任务等待处理。干完活的线程与其等着,不如去帮其他线程干活,于是它就去其他线程的队列里窃取一个任务来执行。而在这时它们会访问同一个队列,所以为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列,被窃取任务线程永远从双端队列的头部拿任务执行,而窃取任务的线程永远从双端队列的尾部拿任务执行。## 工作窃取算法的优点:充分利用线程进行并行计算,并减少了线程间的竞争## 工作窃取算法的缺点:在某些情况下还是存在竞争,比如双端队列里只有一个任务时。并且该算法会消耗更多的系统资源,比如创建多个线程和多个双端队列。对于Fork/Join框架而言,当一个任务正在等待它使用Join操作创建的子任务结束时,执行这个任务的工作线程查找其他未被执行的任务,并开始执行这些未被执行的任务,通过这种方式,线程充分利用它们的运行时间来提高应用程序的性能。为了实现这个目标,Fork/Join框架执行的任务有一些局限性。## Fork/Join框架局限性:(1)任务只能使用Fork和Join操作来进行同步机制,如果使用了其他同步机制,则在同步操作时,工作线程就不能执行其他任务了。比如,在Fork/Join框架中,使任务进行了睡眠,那么,在睡眠期间内,正在执行这个任务的工作线程将不会执行其他任务了。(2)在Fork/Join框架中,所拆分的任务不应该去执行IO操作,比如:读写数据文件(3)任务不能抛出检查异常,必须通过必要的代码来出来这些异常Fork/Join框架的核心类Fork/Join框架的核心是两个类:ForkJoinPool和ForkJoinTask。ForkJoinPool负责实现工作窃取算法、管理工作线程、提供关于任务的状态以及执行信息。ForkJoinTask主要提供在任务中执行Fork和Join操作的机制。示例代码如下:```package io.binghe.concurrency.example.aqs; import lombok.extern.slf4j.Slf4j;import java.util.concurrent.ForkJoinPool;import java.util.concurrent.Future;import java.util.concurrent.RecursiveTask;@Slf4jpublic class ForkJoinTaskExample extends RecursiveTask { public static final int threshold = 2; private int start; private int end; public ForkJoinTaskExample(int start, int end) { this.start = start; this.end = end; } @Override protected Integer compute() { int sum = 0; //如果任务足够小就计算任务 boolean canCompute = (end - start)
  • [新手课堂] 关于Java数组的那些事
    数组是相同数据类型的元素的有序集合。为什么说是有序的呢?访问数组元素是通过数组下标进行的,下标从0开始,依次递增直到 length - 1(length为数组长度),所以可以理解为有序的。首先,来谈谈如何初始化数组?两种初始化方法:动态初始化、静态初始化。(这里以一维数组为例)//动态初始化int[] arr1 = new int[9];//声明了一个数组长度为9的一维数组//除了上面的初始化方式,还可以拆分为两步int[] arr2;arr2 = new int [9];//这两种都属于动态初始化,只是方式存在差异//静态初始化,声明数组长度为5的一维数组int[] arr3 = new int[]{1,2,3,4,5};//静态初始化的标准格式//也可以分为两步初始化//int[] arr3;//arr3 = new int[]{1,2,3,4,5};int[] arr4 = {1,2,3,4},5;//静态初始化的省略格式,不能分为两步既然声明了一个数组,那么数组是储存在哪里的呢?当使用"new"关键字创建数组对象后,会在堆中为数组分配相应的内存空间。总的来说,就是当你使用"new"关键字创建一个对象时,就会在堆中为该对象分配相应的内存空间。数组初始化,那么如何访问数组元素进行赋值和获取元素值呢?int[] arr = new int[5];//数组元素的值默认为0//数组元素赋值for(int i = 0; i < 5; i++){arr = 1;//这里就将数组下标为i的元素赋值为1}//既然对数组赋值了,那么如何获取数组元素的值System.out.println(arr);//这样是否会输出数组元素的值呢//答案是不会,这样输出的是数组的内存地址哈希值//方括号代表是数组,大写字母I代表为整型,后面的#则是十六进制数//这里介绍一种循环:for each循环for(int x:arr){System.out.println(x);//这样就能输出数组元素的值了,当然你也可以使用普通的for循环来进行访问//for(int i = 0; i < 5; i++){//System.out.println(arr);//}}以上就是Java数组中关于一维数组的简单介绍。
  • [交流吐槽] java基本数据类型
    package base;public class demo02 {    public static void main(String[] args) {        //八大基本数据类型        //整型        int num1 = 10;  //最常用        //范围在100多//        byte num2 = 200;        //short        short num3 = 20;        //long        long num4 = 40L;    //Long类型在数字后面加个L表示是long类型        //float 浮点数:小数        float num5 = 12.3F; //float类型加F,否则就报错        double num6 = 3.14159;        //字符        char name1 = 'a';//        char name2 = 'as';  //字符是一个        //字符串        //String 不是关键字,是一个类        String num7 = "asd";        //布尔值        boolean flag = true;    //真        boolean fla = false;    //假    }}
  • [技术干货] API工具--Apifox和Postman对比(区别)
    ## 前言Postman和Apifox有什么区别?他们之间分别有什么优势,感兴趣的同学可以继续往下看。 不吹不黑,只列功能,纯客观比对。## 一.功能列表对比 ### (一)接口设计与文档管理功能 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282565040530404.png)1.导入功能对比 Apifox的导入功能除了支持OpenApi之外,还支持yapi,RAP2,postman等国内用得比较多的接口文档导入,而Postman支持的格式相对较少。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282576741711183.png)![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282585176936279.png)2.在线分享功能对比 Postman的在线分享功能,付费版支持“只读”功能,Apifox分享功能支持选择过期日期、设置密码,选择分享内容的范围,选择环境等功能。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282596041219729.png)![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282605326923851.png)3.编辑接口文档对比 接口文档既可以纯粹的MD格式文档对接口做整体说明,也可以在单个接口内部对单个接口进行说明注释。Apifox会增加创建时间、负责人、所属业务分组等业务和协同层面的注释信息。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282614774579220.png)![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282622401738551.png)![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282631475197465.png)![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282640297691879.png)4.生成代码功能对比 Postman支持将接口生成代码,postman支持的接口和框架为4种,Apifox支持130多种语言和框架![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282649937591303.4)![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282657274516776.png)5.数据模型功能对比 在postman中没有这个功能,在Apifox中,由于本身具备接口设计的功能,因此会将实体类的相关参数封装成一个数据模型,供不同的接口调用,提高数据复用的效率,提高接口封装的程度,减少重复的工作。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282667876270264.png)### (二)接口调试功能对比 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282676280468705.png)对比了下,Postman基本依赖于JS脚本,通过编写脚本对接口进行调试。Apifox则是可视化调试界面为主,自定义脚本编辑为辅。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282684951710175.png)![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282692278583614.png)![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282700429181459.png)![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282708291131952.png)两者对比,在postman中需要写脚本才能实现的接口断言和提取变量、等待时间,在这里都能直接通过填写参数来完成、不需要写脚本。 而操作数据库这个功能postman则不支持。postman只支持js脚本,Apifox目前支持调用其他语言的外部函数和脚本,不过需要先安装相关的Python、java等环境。### (三)接口mock功能 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282717724329514.png)Postman也有mock功能,但它的mock服务需要自己搭建而且mock功能并不强。在Postman上执行API mock 需要经过3步: 第一步:创建 mock服务器,获得mock url 第二步:逐个编写并添加 mock 示例,供执行mock时返回对应的接口响应 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282725490242234.png)也就是说接口mock 出来的响应来源于先前调试已经有的,或者直接自己编辑一个响应进去,才能得到一个返回。mock server 只能返回自己手动添加进去的几条响应,而无法自己无限制创建出mock 数据。 第三步: 将mock url 复制到接口里进行调试。 而想要在 Apifox 内做接口 mock 只需要在`环境`中选择mock服务在响应参数中选择mock规则,点击发送请求,则mock服务会返回与实际业务返回高度相似的接口响应。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282738275949352.png)![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282746608606303.png)![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282754904742773.png)### (四)接口测试功能 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282765832339857.png)在Postman里写测试脚本,使用动态参数,接口响应断言,参数传递都通过写脚本来实现。 如果要作业务接口测试,需要写各种场景下的用例,同样是通过写脚本来修改参数用例的执行顺序和设置循环次数的。使用postman至少需要掌握基础的js语言。Apifox里面做自动化测试可视化程度相对较高一些,创建用例的时候可以在接口设计面板修改参数然后保存,场景用例可以添加不同的参数用例作为步骤,通过拖曳来选择用例的执行顺序。右侧的面板可以填写循环次数,接口间的参数传递和断言也可以在可视化面板提取出来。完成单个接口测试或者场景测试,都不需要写代码。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282780666517472.png)## 二.团队协作功能![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282790439655374.png)Postman的团队协作功能是付费的,3人以下团队可使用免费版协作,3人以上根据可用功能和人数有不同的价格版本。但通常一个团队不可能只有3个人,也就是说,有限开放的那点协同功能是无法支持正常的团队协作需求的。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282798642373509.png)Apifox的协同功能是免费的,团队成员的权限管理,接口数据同步、在线分享都没有障碍。 本身Apifox的定位和Postman就不一样,它一出生就是定位在API管理和协作上。 所以除了协作功能必须的权限管理和数据同步上,它也最大程度地做数据复用,尽量减少不必要的工作量。比如说接口调试的参数用例可以直接导入来做自动化测试,一个数据模型可以给多个接口使用,一套接口数据可以给后端做调试、前端做mock、测试做自动化。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282806721395621.png)## 三.Apifox 没有的功能 Postman支持fork GitHub上的代码,以及API 网关。这两块在Apifox上均没有相关的功能。两个工具的功能有相同的地方,但本质上各自的市场定位还是不同的,Postman打通了接口调试、测试、到线上监测,代码生成。而Apifox始终立身于前端、后端测试间基于接口的设计、调试、测试、文档管理等一系列接口的生命周期管理来发力。在相同的功能点上,Apifox基于本土互联网团队的协作模式和痛点,基本做到了人无我有,人有我优 的程度。因此如果基于各种原因,寻找Postman替代的开发们,不妨体验一下Apifox。## 四.产品价格从收费模式上看,postman是基础功能不收费,协作功能收费;Apifox是公网版本不收费,私有化部署收费。Apifox的SaaS版本也没有什么功能和团队人数的限制,对于我们常规的项目开发来说,免费版本就够用了。公网的SaaS版本,数据的确是放在他们服务器上的,但这点Postman其实也一样,而且postman的服务器可是放在国外的。如果大家的项目安全保密级别较高,想要做私有化部署,可以去他们官网咨询,这方面我没咨询过就不对比了。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282816062828538.png)### 下载地址**Apifox官网**:[www.apifox.cn](https://www.apifox.cn/?utm_source=liam)
  • [最佳实践] 使用IntelliJ IDEA Java创建和使用DLI表
    操作场景DLI支持用户编写代码创建Spark作业来创建数据库、创建DLI表或OBS表和插入表数据等操作。本示例完整的演示通过编写java代码、使用Spark作业创建数据库、创建表和插入表数据的详细操作,帮助您在DLI上进行作业开发。约束限制不支持的场景:在SQL作业中创建了数据库(database),编写程序代码指定在该数据库下创建表。例如在DLI的SQL编辑器中的某SQL队列下,创建了数据库testdb。后续通过编写程序代码在testdb下创建表testTable,编译打包后提交的Spark Jar作业则会运行失败。不支持创建加密的DLI表,即不支持创建DLI表时设置encryption=true。例如,如下建表语句不支持:CREATE TABLE tb1(id int) using parquet options(encryption=true)支持的场景在SQL作业中创建数据库(database),表(table) , 通过SQL或Spark程序作业读取插入数据。在Spark程序作业中创建数据库(database),表(table), 通过SQL或Spark程序作业读取插入数据。环境准备在进行Spark 作业访问DLI元数据开发前,请准备以下开发环境。表1 Spark Jar作业开发环境准备项说明操作系统Windows系统,支持Windows7以上版本。安装JDKJDK使用1.8版本。安装和配置IntelliJ IDEAIntelliJ IDEA为进行应用开发的工具,版本要求使用2019.1或其他兼容版本。安装Maven开发环境的基本配置。用于项目管理,贯穿软件开发生命周期。开发流程DLI进行Spark作业访问DLI元数据开发流程参考如下:图1 Spark作业访问DLI元数据开发流程表2 开发流程说明序号阶段操作界面说明1创建DLI通用队列DLI控制台创建作业运行的DLI队列。2OBS桶文件配置OBS控制台如果是创建OBS表,则需要上传文件数据到OBS桶下。配置Spark创建表的元数据信息“spark.sql.warehouse.dir”的存储路径。3新建Maven工程,配置pom文件IntelliJ IDEA参考样例代码说明,编写程序代码创建DLI表或OBS表。4编写程序代码5调试,编译代码并导出Jar包6上传Jar包到OBS和DLIOBS控制台将生成的Spark Jar包文件上传到OBS目录下和DLI程序包中。7创建Spark Jar作业DLI控制台在DLI控制台创建Spark Jar作业并提交运行作业。8查看作业运行结果DLI控制台查看作业运行状态和作业运行日志。步骤1:创建DLI通用队列第一次提交Spark作业,需要先创建队列,例如创建名为“sparktest”的队列,队列类型选择为“通用队列”。在DLI管理控制台的左侧导航栏中,选择“队列管理”。单击“队列管理”页面右上角“购买队列”进行创建队列。创建名为“sparktest”的队列,队列类型选择为“通用队列”。创建队列详细介绍请参考创建队列。单击“立即购买”,确认配置。配置确认无误,单击“提交”完成队列创建。步骤2:OBS桶文件配置如果需要创建OBS表,则需要先上传数据到OBS桶目录下。本次演示的样例代码创建了OBS表,测试数据内容参考如下示例,创建名为的testdata.csv文件。12,Michael27,Andy30,Justin进入OBS管理控制台,在“桶列表”下,单击已创建的OBS桶名称,本示例桶名为“dli-test-obs01”,进入“概览”页面。单击左侧列表中的“对象”,选择“上传对象”,将testdata.csv文件上传到OBS桶根目录下。在OBS桶根目录下,单击“新建文件夹”,创建名为“warehousepath”的文件夹。该文件夹路径用来存储Spark创建表的元数据信息“spark.sql.warehouse.dir”。步骤3:新建Maven工程,配置pom依赖以下通过IntelliJ IDEA 2020.2工具操作演示。打开IntelliJ IDEA,选择“File > New > Project”。图2 新建Project选择Maven,Project SDK选择1.8,单击“Next”。定义样例工程名和配置样例工程存储路径,单击“Finish”完成工程创建。如上图所示,本示例创建Maven工程名为:SparkJarMetadata,Maven工程路径为:“D:\DLITest\SparkJarMetadata”。在pom.xml文件中添加如下配置。<dependencies> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.11</artifactId> <version>2.3.2</version> </dependency></dependencies>图3 修改pom.xml文件在工程路径的“src > main > java”文件夹上鼠标右键,选择“New > Package”,新建Package和类文件。Package根据需要定义,本示例定义为:“com.huawei.dli.demo”,完成后回车。在包路径下新建Java Class文件,本示例定义为:DliCatalogTest。步骤4:编写代码编写DliCatalogTest程序创建数据库、DLI表和OBS表。完整的样例请参考Java样例代码,样例代码分段说明如下:导入依赖的包。import org.apache.spark.sql.SparkSession;创建SparkSession会话。创建SparkSession会话时需要指定Spark参数:"spark.sql.session.state.builder"、"spark.sql.catalog.class"和"spark.sql.extensions",按照样例配置即可。SparkSession spark = SparkSession .builder() .config("spark.sql.session.state.builder", "org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder") .config("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog") .config("spark.sql.extensions","org.apache.spark.sql.CarbonInternalExtensions"+","+"org.apache.spark.sql.DliSparkExtension") .appName("java_spark_demo") .getOrCreate();创建数据库。如下样例代码演示,创建名为test_sparkapp的数据库。spark.sql("create database if not exists test_sparkapp").collect();创建DLI表并插入测试数据。spark.sql("drop table if exists test_sparkapp.dli_testtable").collect();spark.sql("create table test_sparkapp.dli_testtable(id INT, name STRING)").collect();spark.sql("insert into test_sparkapp.dli_testtable VALUES (123,'jason')").collect();spark.sql("insert into test_sparkapp.dli_testtable VALUES (456,'merry')").collect();创建OBS表。如下示例中的OBS路径需要根据步骤2:OBS桶文件配置中的实际数据路径修改。spark.sql("drop table if exists test_sparkapp.dli_testobstable").collect();spark.sql("create table test_sparkapp.dli_testobstable(age INT, name STRING) using csv options (path 'obs://dli-test-obs01/testdata.csv')").collect();关闭SparkSession会话spark。spark.stop();步骤5:调试、编译代码并导出Jar包单击IntelliJ IDEA工具右侧的“Maven”,参考下图分别单击“clean”、“compile”对代码进行编译。编译成功后,单击“package”对代码进行打包。打包成功后,生成的Jar包会放到target目录下,以备后用。本示例将会生成到:“D:\DLITest\SparkJarMetadata\target”下名为“SparkJarMetadata-1.0-SNAPSHOT.jar”。步骤6:上传Jar包到OBS和DLI下登录OBS控制台,将生成的“SparkJarMetadata-1.0-SNAPSHOT.jar”Jar包文件上传到OBS路径下。将Jar包文件上传到DLI的程序包管理中,方便后续统一管理。登录DLI管理控制台,单击“数据管理 > 程序包管理”。在“程序包管理”页面,单击右上角的“创建”创建程序包。在“创建程序包”对话框,配置以下参数。包类型:选择“JAR”。OBS路径:程序包所在的OBS路径。分组设置和组名称根据情况选择设置,方便后续识别和管理程序包。图4 创建程序包单击“确定”,完成创建程序包。步骤7:创建Spark Jar作业登录DLI控制台,单击“作业管理 > Spark作业”。在“Spark作业”管理界面,单击“创建作业”。在作业创建界面,配置对应作业运行参数。具体说明如下:表3 Spark Jar作业参数填写参数名参数值所属队列选择已创建的DLI通用队列。例如当前选择步骤1:创建DLI通用队列创建的通用队列“sparktest”。作业名称(--name)自定义Spark Jar作业运行的名称。当前定义为:SparkTestMeta。应用程序选择步骤6:上传Jar包到OBS和DLI下中上传到DLI程序包。例如当前选择为:“SparkJarObs-1.0-SNAPSHOT.jar”。主类格式为:程序包名+类名。例如当前为:com.huawei.dli.demo.DliCatalogTest。Spark参数(--conf)spark.dli.metaAccess.enable=truespark.sql.warehouse.dir=obs://dli-test-obs01/warehousepath说明:spark.sql.warehouse.dir参数的OBS路径为步骤2:OBS桶文件配置中配置创建。访问元数据选择:是其他参数保持默认值即可。图5 创建Spark Jar作业单击“执行”,提交该Spark Jar作业。在Spark作业管理界面显示已提交的作业运行状态。查看作业运行结果在Spark作业管理界面显示已提交的作业运行状态。初始状态显示为“启动中”。如果作业运行成功则作业状态显示为“已成功”,通过以下操作查看创建的数据库和表。可以在DLI控制台,左侧导航栏,单击“SQL编辑器”。在“数据库”中已显示创建的数据库“test_sparkapp”。图6 查看创建的数据库双击数据库名,可以在数据库下查看已创建成功的DLI和OBS表。图7 查看表双击DLI表名dli_testtable,单击“执行”查询DLI表数据。图8 查询DLI表数据注释掉DLI表查询语句,双击OBS表名dli_testobstable,单击“执行”查询OBS表数据。图9 查询OBS表数据如果作业运行失败则作业状态显示为“已失败”,单击“操作”列“更多”下的“Driver日志”,显示当前作业运行的日志,分析报错原因。图10 查看Driver日志原因定位解决后,可以在作业“操作”列,单击“编辑”,修改作业相关参数后,单击“执行”重新运行该作业即可。后续指引如果您想通过Spark Jar作业访问其他数据源,请参考《使用Spark作业跨源访问数据源》。创建DLI表的语法请参考创建DLI表,创建OBS表的语法请参考创建OBS表。如果是通过API接口调用提交该作业请参考以下操作说明:调用创建批处理作业接口,参考以下请求参数说明。详细的API参数说明请参考《数据湖探索API参考》>《创建批处理作业》。将请求参数中的“catalog_name”参数设置为“dli”。conf 中需要增加"spark.dli.metaAccess.enable":"true"。如果需要执行DDL,则还要在conf中配置"spark.sql.warehouse.dir": "obs://bucket/warehousepath"。完整的API请求参数可以参考如下示例说明。{ "queue":"citest", "file":"SparkJarMetadata-1.0-SNAPSHOT.jar", "className":"DliCatalogTest", "conf":{"spark.sql.warehouse.dir": "obs://bucket/warehousepath", "spark.dli.metaAccess.enable":"true"}, "sc_type":"A", "executorCores":1, "numExecutors":6, "executorMemory":"4G", "driverCores":2, "driverMemory":"7G", "catalog_name": "dli"}Java样例代码本示例操作步骤采用Java进行编码,具体完整的样例代码参考如下:package com.huawei.dli.demo;import org.apache.spark.sql.SparkSession;public class DliCatalogTest { public static void main(String[] args) { SparkSession spark = SparkSession .builder() .config("spark.sql.session.state.builder", "org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder") .config("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog") .config("spark.sql.extensions","org.apache.spark.sql.CarbonInternalExtensions"+","+"org.apache.spark.sql.DliSparkExtension") .appName("java_spark_demo") .getOrCreate(); spark.sql("create database if not exists test_sparkapp").collect(); spark.sql("drop table if exists test_sparkapp.dli_testtable").collect(); spark.sql("create table test_sparkapp.dli_testtable(id INT, name STRING)").collect(); spark.sql("insert into test_sparkapp.dli_testtable VALUES (123,'jason')").collect(); spark.sql("insert into test_sparkapp.dli_testtable VALUES (456,'merry')").collect(); spark.sql("drop table if exists test_sparkapp.dli_testobstable").collect(); spark.sql("create table test_sparkapp.dli_testobstable(age INT, name STRING) using csv options (path 'obs://dli-test-obs01/testdata.csv')").collect(); spark.stop(); }}scala样例代码scala样例代码object DliCatalogTest { def main(args:Array[String]): Unit = { val sql = args(0) val runDdl =Try(args(1).toBoolean).getOrElse(true) System.out.println(s"sql is $sqlrunDdl is $runDdl") val sparkConf = new SparkConf(true) sparkConf .set("spark.sql.session.state.builder", "org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder") .set("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog") sparkConf.setAppName("dlicatalogtester") val spark = SparkSession.builder .config(sparkConf) .enableHiveSupport() .config("spark.sql.extensions",Seq("org.apache.spark.sql.CarbonInternalExtensions", "org.apache.spark.sql.DliSparkExtension").mkString(",")) .appName("SparkTest") .getOrCreate() System.out.println("catalog is "+ spark.sessionState.catalog.toString) if (runDdl) { val df = spark.sql(sql).collect() } else { spark.sql(sql).show() } spark.close() }}Python样例代码Python样例代码#!/usr/bin/python# -*- coding: UTF-8 -*-from __future__ import print_functionimport sysfrom pyspark.sql import SparkSessionif __name__ == "__main__": url = sys.argv[1] creatTbl = "CREATE TABLE test_sparkapp.dli_rds USING JDBC OPTIONS ('url'='jdbc:mysql://%s'," \ "'driver'='com.mysql.jdbc.Driver','dbtable'='test.test'," \ " 'passwdauth' = 'DatasourceRDSTest_pwd','encryption' = 'true')" % url spark = SparkSession \ .builder \ .enableHiveSupport() \ .config("spark.sql.session.state.builder","org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder")\ .config("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog")\ .config("spark.sql.extensions",','.join(["org.apache.spark.sql.CarbonInternalExtensions","org.apache.spark.sql.DliSparkExtension"]))\ .appName("python Spark testcatalog") \ .getOrCreate() spark.sql("CREATE database if notexists test_sparkapp").collect() spark.sql("drop table if existstest_sparkapp.dli_rds").collect() spark.sql(creatTbl).collect() spark.sql("select * fromtest_sparkapp.dli_rds").show() spark.sql("insert into tabletest_sparkapp.dli_rds select 12,'aaa'").collect() spark.sql("select * fromtest_sparkapp.dli_rds").show() spark.sql("insert overwrite tabletest_sparkapp.dli_rds select 1111,'asasasa'").collect() spark.sql("select * fromtest_sparkapp.dli_rds").show() spark.sql("drop tabletest_sparkapp.dli_rds").collect() spark.stop()
  • [最佳实践] 使用IntelliJ IDEA Java创建和使用Spark UDTF
    操作场景DLI支持用户使用Hive UDTF(User-Defined Table-Generating Functions)自定义表值函数,UDTF用于解决一进多出业务场景,即其输入与输出是一对多的关系,读入一行数据,输出多个值。约束限制在DLI Console上执行UDTF相关操作时,需要使用自建的SQL队列。跨账号使用UDTF时,除了创建UDTF函数的用户,其他用户如果需要使用时,需要先进行授权才可使用对应的UDTF函数。授权操作参考如下:登录DLI管理控制台,选择“ 数据管理 > 程序包管理”页面,选择对应的UDTF Jar包,单击“操作”列中的“权限管理”,进入权限管理页面,单击右上角“授权”,勾选对应权限。自定义函数中引用static类或接口时,必须要加上“try catch”异常捕获,否则可能会造成包冲突,导致函数功能异常。环境准备在进行UDTF开发前,请准备以下开发环境。表1 UDTF开发环境准备项说明操作系统Windows系统,支持Windows7以上版本。安装JDKJDK使用1.8版本。安装和配置IntelliJ IDEAIntelliJ IDEA为进行应用开发的工具,版本要求使用2019.1或其他兼容版本。安装Maven开发环境的基本配置。用于项目管理,贯穿软件开发生命周期。开发流程DLI下UDTF函数开发流程参考如下:图1 UDTF开发流程表2 开发流程说明序号阶段操作界面说明1新建Maven工程,配置pom文件IntelliJ IDEA参考操作步骤说明,编写UDTF函数代码。2编写UDTF函数代码3调试,编译代码并导出Jar包4上传Jar包到OBSOBS控制台将生成的UDTF函数Jar包文件上传到OBS目录下。5创建DLI的UDTF函数DLI控制台在DLI控制台的SQL作业管理界面创建使用的UDTF函数。6验证和使用DLI的UDTF函数DLI控制台在DLI作业中使用创建的UDTF函数。操作步骤新建Maven工程,配置pom文件。以下通过IntelliJ IDEA 2020.2工具操作演示。打开IntelliJ IDEA,选择“File > New > Project”。图2 新建Project选择Maven,Project SDK选择1.8,单击“Next”。定义样例工程名和配置样例工程存储路径,单击“Finish”完成工程创建。在pom.xml文件中添加如下配置。<dependencies> <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>1.2.1</version> </dependency></dependencies>图3 pom文件中添加配置在工程路径的“src > main > java”文件夹上鼠标右键,选择“New > Package”,新建Package和类文件。Package根据需要定义,本示例定义为:“com.huawei.demo”,完成后回车。在包路径下新建Java Class文件,本示例定义为:UDTFSplit。编写UDTF函数代码。完整样例代码请参考样例代码。UDTF的类需要继承“org.apache.hadoop.hive.ql.udf.generic.GenericUDTF”,实现initialize,process,close三个方法。UDTF首先会调用initialize方法,此方法返回UDTF的返回行的信息,如,返回个数,类型等。初始化完成后,会调用process方法,真正处理在process函数中,在process中,每一次forward()调用产生一行。如果产生多列可以将多个列的值放在一个数组中,然后将该数组传入到forward()函数。public void process(Object[] args) throws HiveException { // TODO Auto-generated method stub if(args.length == 0){ return; } String input = args[0].toString(); if(StringUtils.isEmpty(input)){ return; } String[] test = input.split(";"); for (int i = 0; i < test.length; i++) { try { String[] result = test.split(":"); forward(result); } catch (Exception e) { continue; } } }最后调用close方法,对需要清理的方法进行清理。编写调试完成代码后,通过IntelliJ IDEA工具编译代码并导出Jar包。单击工具右侧的“Maven”,参考下图分别单击“clean”、“compile”对代码进行编译。编译成功后,单击“package”对代码进行打包。图4 编译打包打包成功后,生成的Jar包会放到target目录下,以备后用。本示例将会生成到:“D:\MyUDTF\target”下名为“MyUDTF-1.0-SNAPSHOT.jar”。登录OBS控制台,将生成的Jar包文件上传到OBS路径下。说明:Jar包文件上传的OBS桶所在的区域需与DLI的队列区域相同,不可跨区域执行操作。(可选)可以将Jar包文件上传到DLI的程序包管理中,方便后续统一管理。登录DLI管理控制台,单击“数据管理 > 程序包管理”。在“程序包管理”页面,单击右上角的“创建”创建程序包。在“创建程序包”对话框,配置以下参数。包类型:选择“JAR”。OBS路径:程序包所在的OBS路径。分组设置和组名称根据情况选择设置,方便后续识别和管理程序包。单击“确定”,完成创建程序包。创建DLI的UDTF函数。登录DLI管理控制台,单击“SQL编辑器”,执行引擎选择“spark”,选择已创建的SQL队列和数据库。图5 选择队列和数据库在SQL编辑区域输入下列命令创建UDTF函数,单击“执行”提交创建。CREATE FUNCTION mytestsplit AS 'com.huawei.demo.UDTFSplit' using jar 'obs://dli-test-obs01/MyUDTF-1.0-SNAPSHOT.jar';重启原有SQL队列,使得创建的UDTF函数生效。登录数据湖探索管理控制台,选择“队列管理”,在对应“SQL队列”类型作业的“操作”列,单击“重启”。在“重启队列”界面,选择“确定”完成队列重启。验证和使用创建的UDTF函数。在查询语句中使用6中创建的UDTF函数,如:select mytestsplit('abc:123\;efd:567\;utf:890');图6 执行结果(可选)删除UDTF函数。如果不再使用该Function,可执行以下语句删除UDTF函数:Drop FUNCTION mytestsplit;样例代码UDTFSplit.java完整的样例代码参考如下所示:package com.huawei.demo;import java.util.ArrayList;import org.apache.commons.lang.StringUtils;import org.apache.hadoop.hive.ql.exec.UDFArgumentException;import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;import org.apache.hadoop.hive.ql.metadata.HiveException;import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;public class UDTFSplit extends GenericUDTF { @Override public void close() throws HiveException { // TODO Auto-generated method stub } @Override public void process(Object[] args) throws HiveException { // TODO Auto-generated method stub if(args.length == 0){ return; } String input = args[0].toString(); if(StringUtils.isEmpty(input)){ return; } String[] test = input.split(";"); for (int i = 0; i < test.length; i++) { try { String[] result = test.split(":"); forward(result); } catch (Exception e) { continue; } } } @Override public StructObjectInspector initialize(ObjectInspector[] args) throws UDFArgumentException { if (args.length != 1) { throw new UDFArgumentLengthException("ExplodeMap takes only one argument"); } if (args[0].getCategory() != ObjectInspector.Category.PRIMITIVE) { throw new UDFArgumentException("ExplodeMap takes string as a parameter"); } ArrayList<String> fieldNames = new ArrayList<String>(); ArrayList<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>(); fieldNames.add("col1"); fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); fieldNames.add("col2"); fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs); }}
  • [最佳实践] 使用IntelliJ IDEA Java创建和使用Spark UDF
    操作场景DLI支持用户使用Hive UDF(User Defined Function,用户定义函数)进行数据查询等操作,UDF只对单行数据产生作用,适用于一进一出的场景。约束限制在DLI Console上执行UDF相关操作时,需要使用自建的SQL队列。跨账号使用UDF时,除了创建UDF函数的用户,其他用户如果需要使用时,需要先进行授权才可使用对应的UDF函数。授权操作参考如下:登录DLI管理控制台,选择“ 数据管理 > 程序包管理”页面,选择对应的UDF Jar包,单击“操作”列中的“权限管理”,进入权限管理页面,单击右上角“授权”,勾选对应权限。自定义函数中引用static类或接口时,必须要加上“try catch”异常捕获,否则可能会造成包冲突,导致函数功能异常。环境准备在进行UDF开发前,请准备以下开发环境。表1 UDF开发环境准备项说明操作系统Windows系统,支持Windows7以上版本。安装JDKJDK使用1.8版本。安装和配置IntelliJ IDEAIntelliJ IDEA为进行应用开发的工具,版本要求使用2019.1或其他兼容版本。安装Maven开发环境的基本配置。用于项目管理,贯穿软件开发生命周期。开发流程DLI下UDF函数开发流程参考如下:图1 开发流程表2 开发流程说明序号阶段操作界面说明1新建Maven工程,配置pom文件IntelliJ IDEA参考操作步骤说明,编写UDF函数代码。2编写UDF函数代码3调试,编译代码并导出Jar包4上传Jar包到OBSOBS控制台将生成的UDF函数Jar包文件上传到OBS目录下。5创建DLI的UDF函数DLI控制台在DLI控制台的SQL作业管理界面创建使用的UDF函数。6验证和使用DLI的UDF函数DLI控制台在DLI作业中使用创建的UDF函数。操作步骤新建Maven工程,配置pom文件。以下通过IntelliJ IDEA 2020.2工具操作演示。打开IntelliJ IDEA,选择“File > New > Project”。图2 新建Project选择Maven,Project SDK选择1.8,单击“Next”。定义样例工程名和配置样例工程存储路径,单击“Finish”完成工程创建。在pom.xml文件中添加如下配置。<dependencies> <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>1.2.1</version> </dependency></dependencies>图3 pom文件中添加配置在工程路径的“src > main > java”文件夹上鼠标右键,选择“New > Package”,新建Package和类文件。Package根据需要定义,本示例定义为:“com.huawei.demo”,完成后回车。在包路径下新建Java Class文件,本示例定义为:SumUdfDemo。编写UDF函数代码。UDF函数实现,主要注意以下几点:自定义UDF需要继承org.apache.hadoop.hive.ql.UDF。需要实现evaluate函数,evaluate函数支持重载。详细UDF函数实现,可以参考如下样例代码:package com.huawei.demo;import org.apache.hadoop.hive.ql.exec.UDF; public class SumUdfDemo extends UDF { public int evaluate(int a, int b) { return a + b; } }编写调试完成代码后,通过IntelliJ IDEA工具编译代码并导出Jar包。单击工具右侧的“Maven”,参考下图分别单击“clean”、“compile”对代码进行编译。编译成功后,单击“package”对代码进行打包。打包成功后,生成的Jar包会放到target目录下,以备后用。本示例将会生成到:“D:\DLITest\MyUDF\target”下名为“MyUDF-1.0-SNAPSHOT.jar”。登录OBS控制台,将生成的Jar包文件上传到OBS路径下。说明:Jar包文件上传的OBS桶所在的区域需与DLI的队列区域相同,不可跨区域执行操作。(可选)可以将Jar包文件上传到DLI的程序包管理中,方便后续统一管理。登录DLI管理控制台,单击“数据管理 > 程序包管理”。在“程序包管理”页面,单击右上角的“创建”创建程序包。在“创建程序包”对话框,配置以下参数。包类型:选择“JAR”。OBS路径:程序包所在的OBS路径。分组设置和组名称根据情况选择设置,方便后续识别和管理程序包。单击“确定”,完成创建程序包。创建UDF函数。登录DLI管理控制台,单击“SQL编辑器”,执行引擎选择“spark”,选择已创建的SQL队列和数据库。图4 选择队列和数据库在SQL编辑区域输入下列命令创建UDF函数,单击“执行”提交创建。CREATE FUNCTION TestSumUDF AS 'com.huawei.demo.SumUdfDemo' using jar 'obs://dli-test-obs01/MyUDF-1.0-SNAPSHOT.jar';重启原有SQL队列,使得创建的Function生效。登录数据湖探索管理控制台,选择“队列管理”,在对应“SQL队列”类型作业的“操作”列,单击“重启”。在“重启队列”界面,选择“确定”完成队列重启。使用UDF函数。在查询语句中使用6中创建的UDF函数:select TestSumUDF(1,2);图5 执行结果(可选)删除UDF函数。如果不再使用UDF函数,可执行以下语句删除该函数:Drop FUNCTION TestSumUDF;
总条数:911 到第
上滑加载中