1 Star 0 Fork 25

风咏 / GoSqlGo

forked from drinkjava2 / MyServerless 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Apache-2.0

为避免误解,在此声明:

本项目架构上没有什么安全问题,有质疑安全、注入问题的,在吐槽之前请将本说明完整看完。

GoSqlGo简介 | Description

天下武功,唯快不破,程序无非就是接收用户输入、存到数据库。GoSqlGo能让前端直接存取数据库,独立完成项目开发。

缘起 | Origin

一直认为,开发效率最高的方式不是让MVC架构极简(SpringBoot/jFinal),而是彻底省略掉MVC架构和后端程序员,直接由前端搞定一切,由多层架构变成两层,在前端直接写SQL,缩短界面和数据库之间的距离,才是最快的开发途径。基于此理念,在2011年本人在这里写了一句预言,没想到技术的发展如此之慢,现在要自己亲手去实现它了,这就是GoSqlGo项目,如果名字翻译成中文,可以翻成"Sql冲冲冲冲冲",这个比较形象,它表达了SQL为王,一路狂奔,冲到了前端的意思。界面和逻辑混合是一种肮脏的开发模式,但是就象臭豆腐一样闻着臭,吃着吃着就觉得香了。

GoSqlGo是一个运行于后端的开发工具,它的最大特点就是在运行期动态编译客户端Java代码,所有SQL和Java代码都可以在前端Html页面完成,可以彻底甩掉后端。开发完成后再利用打包工具将SQL和Java从前端移到后端,以实现安全。忘掉MVC吧,因为现在架构变成MV两层了;忘掉FreeMaker之类模板吧,因为Java内嵌到HTML里去了;忘掉后端程序员吧,因为前端把后端的活给干了(这叫大前端);忘掉前端校验吧,因为后端校验这活也归前端了,前端校验能偷懒就偷吧。

简介 | Features

用一个例子来说明直接在前端Javascript里写SQL和Java代码,以下html示例,实测通过,完整文件位于这里
这是一个转账业务的演示,开发阶段把所有的SQL和业务逻辑都写在html里面,在布署阶段再由打包工具抽取到服务端:

<!DOCTYPE html>
<html>
<head>
<style> ...略... </style>
<script src="/js/jquery-1.11.3.min.js"></script>
<script src="/js/jquery-ajax-ext.js"></script>
<script src="/js/gosqlgo.js"></script>
</head>
<body>
    <script> 
	  document.write($java(`return new WebBox("/page/menu.html").setAttribute("title", $1);`, "Transaction demo, use jQuery")); 
	  function getUserListHtml(){ 
		  var users=$$qryMapList(`select * from account where amount>=? order by id`,0);
		  var html="User List:<br/>";
		  for(var i=0;i<users.length;i++) 
			  html+="User ID:" +  users[i].ID+", AMOUNT:"+ users[i].AMOUNT+"<br/>"; 
	      return html;		   
	  } 
	</script>   
	<div id="msgid" class="msg"></div> 
	<p id="Users">
	    <script>document.write(getUserListHtml());</script>   
	</p>
	
	<section>
		<header>Account A</header>
		<div id="A" class="amount">
			<script>
				document.write($qry(`select amount from account where id=? and amount>=?`, 'A',0));
			</script>
		</div>
	</section>
	<section>
		<header>Account B</header>
		<div id="B" class="amount">
			<script>
				document.write($$qryEntity(`com.demo.entity.Account, select * from account where id=?`, 'B').amount);
			</script>
		</div>
	</section>
	<script>
	  function transfer(from, to, money){ 
		 var rst = $javaTx(`#TransferMoney 
						int money=Integer.parseInt($3);
						if(money<=0) 
						  throw new SecurityException("Money<=0, IP:"+ getRequest().getRemoteAddr());
						Account a=new Account().setId($1).load();
						if(a.getAmount()<money)
						   return "Error:No enough balance!";
						Account b=new Account().setId($2).load();
						a.setAmount(a.getAmount()-money).update();
						b.setAmount(b.getAmount()+money).update(); 
						return "Transfer Success!|"+a.getAmount()+"|"+b.getAmount();
						`,	from,to,money);   
		  if(rst.startsWith("Transfer Success!")) { 
			  var words=rst.split('|');
	 	      $("#msgid").text(words[0]); 
	 	      $("#"+from).text(words[1]);
	 	      $("#"+to).text(words[2]);
	 	      $("#msgid").css("background", "#dfb");
	 	      $("#Users").html(getUserListHtml());
		  }
		  else  if(rst.startsWith("Error:")) { 
			     $("#msgid").text(rst.substring(6));
		         $("#msgid").css("background", "#ffbeb8");
		  } 
		}
	</script>
	<section>
		<header>Transfer</header>
		<form onsubmit="return false" action="##" method="post">
			<input name="amount" value="100" class="amount">
			<button name="btnA2B" value="true" onclick="transfer('A','B',100)">From
				account A to account B</button>
			<button name="btnB2A" value="true" onclick="transfer('B','A',100)">From
				account B to account A</button>
		</form>
	</section>
</body>
</html>

另外还有两个演示:
演示2:GoSqlGo结合Vue的使用。
演示3: 在前端定义一个完整的实体类并进行DDL生成、建表、表单输入检查、表单提交和存盘。

查看演示:在windows下点击demo\gsg-jbooox\run_undertow_embedded.bat批处理即可。

GoSqlGo运行在服务端,只需要进行简单的数据源设定、ActiveRecord实体类定义就可以了(也可以定义在html里,见例3)。配置详见com.demo.InitConfig类内容。
GoSqlGo是一个工具,而不是一个框架,它是通过在Web容器里添加一个Servlet过滤器,处理.gsg访问,也就是说,它通常寄生在其它后端框架里,这样的优点是可以直接共享其它框架的Session和工具类。

GoSqlGo使用需要在客户端添加gosqlgo.js,业务逻辑和SQL直接写在客户端的Javascript里,示范项目里主要有以下方法:

$java(String, Object...) 执行多行Java语句。第一个参数是Java本体,后面是参数,在Java里可以用$1,$2...来访问。  
$javaTx(String, Object...) 执行多行Java语句并开启事务,如果有异常发生,事务回滚。
$qry(String, Object...) 将SQL查询结果的第一行第一列作为字符串值返回,第一个参数是SQL,后面是SQL参数    
$qryArray(String, Object...)  返回SQL查询的第一行数据,格式为Object[]的JSON字符串  
$qryArrayList(String, Object...)  返回多行查询结果为List<数组>的JSON字符串    
$qryTitleArrayList(String, Object...)  返回多行查询结果,为List<数组>的JSON格式,但第一行内容是各个列的标题  
$qryMap(String, Object...) 返回SQL查询的第一行数据,为Map的JSON格式  
$qryMapList(String, Object...)  返回SQL查询的多行数据,为List<Map>的JSON格式  
$qryEntity(String, Object...)  返回第一行数据为实体的JSON格式,SQL写法是实体类名+逗号+SQL, 示例:$qryEntity(`a.b.Demo, select * from demo`); 
$qryEntityList(String, Object...)  返回多行数据为List<实体>的JSON格式,SQL写法是实体类名+逗号+SQL, 示例:$qryEntityList(`a.b.Demo, select * from demo`);  
以上函数还可以用$$开头直接返回Javascript对象,例如$$qryMapList(...)就等效于JSON.parse($qryMapList(...))

开发阶段,以上方法中第一个字符串参数,必须用键盘ESC下方的单引号括起来(这是Javascript的特殊单引号,支持多行文本),而不能用普通的双引号或单引号。

依赖和运行 | Dependency and Run

GoSqlGO使用需要在服务端项目里加入以下依赖:

    <dependency>
      <groupId>com.github.drinkjava2</groupId>
      <artifactId>gosqlgo</artifactId>
      <version>1.1.0</version>
    </dependency>

它主要有两个功能:
1.动态编译客户端传来的SQL/Java,生成Java类,例如以下调用会动态地根据Java源码生成Java类实例:
DynamicCompileEngine.instance.javaCodeToClass(className, classSrc);
这个功能前端程序员不需要了解,因为通常前端拿到手的是后端已经精心装配好的如gsg-jbooox这样的开箱即用的整合包。

2.打包工具,将前端的SQL/Java抽出到后端,或反之。详见"开发和布署"一节。

原则上GoSqlGo可以使用在任意Servlet容器内,并可搭配不同的DAO工具使用,但是学习它最简单的方法是运行它的示例项目"gsg-jbooox",这是个微型的开箱即用的服务端整合包。
"gsg-jbooox"项目需Java8或以上,用git clone下载后, 有几种运行方式:

方式1,发布war包到本机的Tomcat7或Tomcat8目录下执行:

运行:修改run_tomcat_local.bat批处理文件中的TomcatFolder为本机Tomcat目录,并执行

方式2, 命令行方式在嵌入式Tomcat上运行,这种方式本机不需要安装Tomcat, Maven会自动下载

运行:双击运行run_tomcat_embedded.bat批处理即可

方式3, 命令行方式在嵌入式Jetty上运行,这种方式本机不需要安装Servlet容器, Maven会自动下载Jetty

运行:双击运行run_jetty_embedded.bat批处理即可

方式4, 命令行方式在嵌入式Undertow上运行,这种方式本机不需要安装Servlet容器, Maven会自动下载Undertow

运行:双击运行run_undertow_embedded.bat批处理即可

方式5, 导入到Eclipse中运行或调试

  1. 运行run_tomcat_embedded.bat批处理一次
  2. 运行maven_eclipse_eclipse.bat批处理,生成eclipse配置
  3. 打开Eclipse,导入项目,并运行其中的MainApp.java的main方法

查看结果:在浏览器输入 http://localhost

开发和布署 | Develop & Product

开发阶段:GoSqlGo在服务端运行,它自带一个动态编译工具,前端发来的SQL和Java片段,被运态编译为Java类,并调用服务端ORM工具,最后返回文本或JSON对象。
在Sql/Java片段里面,返回值可以分为以下类型,但客户端最终收到的都是一个字符串:

  1. 字符串类型,可以为普通字符串或JSON字符串,由客户端来自行解析。
  2. WebBox对象,可以用new WebBox().setText()方法来设定一个字符串,或用new WebBox("xxx.htm")方法来嵌入一个服务端页面。
  3. 异常抛出,服务端会捕获并在服务端控制台输出异常信息,并返回空字符串。
  4. 空值,服务端会返回"null"字符串,由客户端来自行解析。
  5. 其它Java对象,服务端会对Java对象调用JSON.toJSONString(obj)方法进行字符串化,由客户端来自行解析收到的JSON字符串。
    注意以上只是gsg-jbooox示范项目对返回值的包装,用户实际项目中可以自定义自已的服务端处理逻辑(继承于ServletTemplate类)。

布署阶段:通过一个打包工具(DeployTool),将前端所有的SQL和原生Java片段打包到服务端去,静态存为可调试的Java源文件,原有客户端的SQl和JAVA代码在打包后将成为类似于$gsg('C9GK90J27','A');之类的通过ID进行的调用,以实现安全性,打包工具有四个方法,在IDE里可以使用:

DeployTool.goServ();    //将HTML/Javascript中的sql和java代码抽取出来,生成Java类发布到项目源码的deploy目录下, 但如果SQL/Java片段开头含有"FRONT"关键字将不抽取
DeployTool.goFront();   //逆操作,将抽取出来的SQL/Java类再塞回到HTML/Javascript中,但如果Java源码有"// GSG SERV"注释将不抽取   
DeployTool.goServForce();    //同goServ,但是忽略FRONT关键字,一律强制抽取到服务端,以实现安全性  
DeployTool.goFrontForce();    //同goFront,但是忽略"// GSG SERV"注释,一律强制抽取到前端,以实现可读性   

注意在打包之前必须调用GoSqlGoEnv.registerGsgTemplate()方法登记所有的自定义模板类,例如:GoSqlGoEnv.registerGsgTemplate("javaTx", JavaTxTemplate.class); 详见 com.demo.InitConfig 中的示例。注意所有模板类必须继承ServletTemplate类,并且以"方法名+Template"命名,这是一个约定。
Windows环境下,先运行run_undertow_embedded.bat一遍后,再点击goServ.bat批处理文件即可进行打包操作,再次运行run_undertow_embedded.bat就可以发现客户端的SQL和Java消失了,取而代之的是$gsg这种调用方法。

对于Sql/Java字符串片段,有以下可选控制字符:
SERV,永远留在后端,一旦用DeployTool.goServ()命令抽取到服务端,就不能用goFront命令塞回到前端,必须用goFrontForce命令才能塞回到前端。
FRONT, 永远留在前端,goServ命令对它不起作用,直到使用goServForce命令才会被强制抽取到Server端。
FULL,定义完整的Java类,package、类声明等都必须写全,这是一种比较特殊的用法,与在服务器定义一个类等同。
#xxxxx形式,手工指定类名,如果不定义类名, 则由工具随机自动生成,用goFront命令塞回到前端时会删除这个随机类名。
import开头,为标准Java的import语句
示例:

$java('SERV #ReadAmount import abc.DemoUser; return new DemoUser().loadById($1).getAmount();', 'u1');   

在类根目录(项目的resources目录)下,有一个名为GoSqlGo.properties的配置文件,可以进行一些配置,例如配置deploy目录、template目录、设定开发/生产阶段。

常见问题 | FAQ

  • 安全上有没有问题?
    没有安全问题,但有信息泄露问题。在java方法里,可以手工进行参数合法性检查,不存在安全漏洞。但是在qry这类SQL方法里,没有进行参数校验的语句,如果遇到客户端恶意传入非法SQL参数,例如想要调入用户A的账户,却传入了用户C的账户ID,这时候就会出现非法存取,甚至出现扫表情况,整张表的信息都会泄露。qry系列方法可以防止SQL注入攻击,但不能防止扫表造成信息泄露。
    为什么有这么大的信息泄露漏洞还要保留SQL类方法?
    因为作者认为方便性要大于安全性,这是故意这样设计的。例如对于github、FB、OSC这种社交类网站,除了用户密码外,大多数表格内容都是可以公开的,即使出现非法参数或扫表,也不是件大事。当然扫表太历害就成了DDOS攻击了。对于不允许扫表攻击的表格,可以在后台qry模板上加拦截器登录、防扫表检查,或强制只能用java方法,先对输入参数进行合法性检查。

  • 为什么示例项目gsg-jBooox采用jSqlBox这么小众的DAO工具?
    因为jSqlBox是本人写的DAO工具,打广告用的,它的DAO功能很全,可以号称SQL写法百科全书。如果前端对jSqlBox不感冒,可以对公司的后端提要求,搭配不同的DAO工具即可。GoSqlGo的所有方法命名和后台模板是可以自行定制的。目前只有gsg-jbooox示例,今后可能发行SpringBoot、jFinal等多个示范项目。
    GoSqlGo与其说是个产品,不如说是个概念,稍有经验的后端都可以在它的基础上开发出支持其它ORM、MVC框架的后端平台, GoSqlGo核心是一个编译、打包工具,它不是一个框架。

  • (小鹏提出)Java写在HTML/Javascript里没有错误检查、语法提示,及重构功能。
    这个将来可以通过IDE插件解决,但目前只能运行goServ.bat批处理将Sql/Java抽取成Java源码类,在IDE里找错、更正后再用goFront.bat批处理塞回到HTML里去,也可以干脆就不塞回去了,后者就对应传统的前后端分离开发情形。

  • 与Node.js相比 GoSqlGo与Node.js本质上类似,是运行在服务端的,但它区别在:1)可以将后端代码直接写在前端 2)采用Java语言,适合于对Java有偏好的人群,事实上Java也是后端的主流语言。

  • 与GraphQL相比 1)GraphQL相当于一种特殊的查询语法,相比于GoSqlGo直接使用SQL和Java,在使用体验上还是有差距的。2)GraphQL有安全性问题,因为它允许根据用户传入的动态结构在服务器上拉取数据,这就造成可攻击的漏洞太多了,非常难以防范,这是架构设计问题,这个缺陷无法根除。所以GraphQL通常只能用于内容发布类网站如Facebook等,或仅充当简单的通信接口。GoSqlGo在发布后它的SQL是不可见的,只允许客户端改变输入参数,而不能改变SQL本体或业务逻辑,所以更安全。

  • 为什么没有https配置的演示
    这个是运维人员和运维阶段要考虑的事,难度不大,没有演示的必要。

相关开源项目 | Related Projects

期望 | Futures

GoSqlGo已发布,对它感兴趣的请加关注,或发issue提出完善意见。也欢迎同学们提交GoSqlGo演示示例,GoSqlGo如果用好了,结合前端的可视化组件,可以实现类似Delphi一样的开发效率。

版权 | License

Apache 2.0

关注我 | About Me

Github
码云

点赞 | Star

点赞很重要,必须的

Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2018 drinkjava2 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

简介

天下武功,唯快不破,程序无非就是接收用户输入、存到数据库。GoSqlGo能让前端直接存取数据库,独立完成项目开发。 展开 收起
Java
Apache-2.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/fysoft2006/gosqlgo.git
git@gitee.com:fysoft2006/gosqlgo.git
fysoft2006
gosqlgo
GoSqlGo
master

搜索帮助

14c37bed 8189591 565d56ea 8189591