详细Android连接远程的MySQL数据库实例
wptr33 2025-01-20 17:01 34 浏览
前言
第一次连接远程MySQL数据,在此留下笔记,针对遇到的总是也一并给出,在此说明,连接远程数据库在我没遇到的问题暂时无法给出,我尽量按写程的时间最全的给出代码。连接的方法是采用mysql-connector-java-5.1.30-bin.jar包进行连接。欢迎讨论。
说明:
- 以下记录按时间先后顺利给出,采用同样的环境应该可以重现。
- 代码部分也由顺序给出,如:某一个方法或者类后面的代码可能比前面贴出的全,也可能是增加了内容,也可能是解决了前面的某个问题。
- 为了程序的可扩展性,本示例采用数据与UI分离写法,自定义了页面的基类BasePager,initView()和initData()
环境配置
服务器:阿里云服务器 ECS
操作系统:centos 7
MySQL:version: 5.7.27
连接服务器软件:FinalShell
MySQL管理软件:RoboDB MySQL Manager
安卓开发软件:Androi Studio 3.5.2
查看MySQL:version方法
在登陆服务器后输入mysql -uroot -p再输入密码。
[root@njzhw ~]# mysql -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1234354
Server version: 5.7.27 MySQL Community Server (GPL)
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
新建Android工程
新建一个空白Activity的工程文,目前采用最新的各类文件版本,
build.gradle
app的build.gradle文件如下:所有支持均已最新版本。minSdkVersion 19因受我电脑外挂android模拟器的限制,采用了19。高了模拟器运行不起来。
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "cn.plczl.myapplication"
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
activity_main.xml布局文件
MainActivity所对应的activity_main.xml的布局文只有一个FrameLayout,所有数据显示采用另外的代码实现。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="@+id/fl_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
MainActivity类修改为继承于Activity
新建好的MainActivity是继承于AppCompatActivity。区别是软件运行时不需要其默认的标题栏
百度的区别:
Activity 是其它 Activity 的基类,包括 AppCompatActivity。默认带标题栏,从字面理解,App兼容Activity,意思是允许我们轻易地将API 21+的特性应用到之前的那些老的,不兼容的Activity上面。例如很容易地将Toolbar添加到Activity上
代码如下:
package cn.plczl.mysql;
import android.app.Activity;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
新建一个基类
用于子页面来继承,这样可以有多个页面采用自定义的一类进行界面、数据分离处理。
在工程中新建一个base的包,包中建立一个BasePager的类。代码如下:本基类无需要继承,
主要代码如下:
- 其中有一个构造函数public BasePager(Context context),用于获取上下文和初始化视图(rootView);
- 一个抽象的initView方法,强制由调用的页面来实现,以实现不同页面有不同的UI结构;
- 一个初始化数据的方法(initData),本方法为一个空方法,由继承它的子页面来实现数据填充。在子页面中还可以在本方法中进行耗时的联网,数据请求,数据显示的操作。
注意: - 因public abstract View initView();这个是一个抽象的方法,所对应的public abstract class BasePager 类也必须是抽象的,也就是由abstract关键字。
- public Context conetxt;要修改为public,如果自生成时可能是这样的,private final Context conetxt;因为这个conetxt要被其它的类调用。
package com.atguigu.mysql_dome.Base;
import android.content.Context;
import android.view.View;
public abstract class BasePager {
public Context conetxt;
public View rootview;
//本类的构造函数
public BasePager(Context context){
this.conetxt=context;
rootview=initView();
}
//初始化视图的抽象类。强制由继承本类的其它类来实现
public abstract View initView();
//初始化视图的空方法,执行联网、数据请求、数据显示操作,本空方法不能缺少。
public void initData(){
}
}
新建一个子页面
在工程中新建一个包(pager)来存放各子页面,在其包下新建一个EthMainPager的子页面,
代码如下:初步的实现了构造方法,initView()方法和initData方法。
其在程序中显示“初始化了EthMainPager”的文字。
package cn.plczl.mysql.pager;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.widget.TextView;
import cn.plczl.mysql.base.BasePager;
import cn.plczl.mysql.utils.LogUtil;
public class EthMainPager extends BasePager {
private TextView textView;
public EthMainPager(Context context) {
super(context);
}
@Override
public View initView() {
textView=new TextView(conetxt);
textView.setGravity(Gravity.CENTER);
textView.setTextSize(20);
return textView;
}
@Override
public void initData() {
super.initData();
LogUtil.e("初始化了EthMainPager");
textView.setText("初始化了EthMainPager");
}
}
将EthMainPager页面放入MainActivity中并显示。
修改MainActivity,为程序扩展,将EthMainPager放入一个basePagers的集合中,自定义了setFragment、getEthMainPager方法,新建了一个ReplacePager类
public class MainActivity extends FragmentActivity {
private ArrayList<BasePager> basePagers;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
basePagers = new ArrayList<>();
basePagers.add(new EthMainPager(this));
setFragment();
}
private void setFragment() {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
//这里采用了replace方法,如果只是一个页面,也可以采用add方法。
fragmentTransaction.replace(R.id.fl_main,new ReplacePager(getEthMainPager()));
fragmentTransaction.commit();
}
private BasePager getEthMainPager() {
BasePager basePager=basePagers.get(0);
if(basePager!=null){
basePager.initData();
}
return basePager;
}
public static class ReplacePager extends Fragment {
public BasePager currPager;
public ReplacePager(BasePager basePager) {
this.currPager=basePager;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return currPager.rootView;
}
}
}
为使程序看起来简捷,将public static class ReplacePager extends Fragment抽取出来形成一个单独的类。
将其抽取至base目录中。文件名为ReplacePager
在MainActivity加入包含import cn.plczl.mysql.base.ReplacePager;
package cn.plczl.mysql.base;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
public class ReplacePager extends Fragment {
public BasePager currPager;
public ReplacePager(BasePager basePager) {
this.currPager=basePager;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return currPager.rootView;
}
}
初步运行界面
以上代码完成初步的界面搭建,初步运行起来的画面如下:
子页面的完善编写
接下来的基本操作都将在EthMainPager子页面中去实现界面,联网和数据请示以及数据显示的操作。
子页面的布局
在EthMainPager的public View initView() 加入一条View view=View.inflate(conetxt, R.layout.ethmainpager,null);,用于加载ethmainpager.xml布局文,不再采用上面程序中的如下代码来初始化子页面了,initView体现了页面的初始化,
textView=new TextView(conetxt);
textView.setGravity(Gravity.CENTER);
textView.setTextSize(20);
return textView;
@Override
public View initView() {
View view=View.inflate(conetxt, R.layout.ethmainpager,null);
return view;
}
ethmainpager.xml文件
这里采用简单的一个线性布局,里面有四个TextView,这个程序是Dome性质,只要参调出MySQL数据一张表中库中的两个数据和另一张表中的一个数据,
这里设定能调出数据库最近更新的日期和时间就完成任务。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#23D3FE">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginRight="20dp"
android:layout_marginLeft="20dp">
<TextView
android:id="@+id/tv_1"
android:layout_width="wrap_content"
android:layout_height="25dp"
android:text="数据更新时间"
android:padding="2dp"
android:textSize="16sp"
android:gravity="left"
android:layout_marginLeft="5dp"/>
<TextView
android:id="@+id/tv_data1"
android:layout_width="wrap_content"
android:layout_height="25dp"
android:text="2020-01-27"
android:padding="2dp"
android:textSize="16sp"
android:gravity="left"
android:layout_toRightOf="@id/tv_1"
android:layout_marginLeft="5dp"/>
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="25dp"
android:text="21:00:00"
android:padding="2dp"
android:textSize="16sp"
android:gravity="left"
android:layout_toRightOf="@id/tv_data1"
android:layout_marginRight="20dp"/>
</RelativeLayout>
<TextView
android:id="@+id/tv_version"
android:layout_width="wrap_content"
android:layout_height="25dp"
android:text="21:00:00"
android:padding="2dp"
android:textSize="16sp"
android:gravity="left"
android:layout_marginLeft="22dp"
android:layout_toRightOf="@id/tv_data"
android:layout_marginRight="20dp"/>
</LinearLayout>
实例化控件
将用于要显进日期和时间的两个TextView实例化。
private TextView tv_data;
private TextView tv_time;
private TextView tv_version;
public EthMainPager(Context context) {
super(context);
}
@Override
public View initView() {
View view = View.inflate(conetxt, R.layout.ethmainpager,null);
tv_data = view.findViewById(R.id.tv_data);
tv_time = view.findViewById(R.id.tv_time);
tv_version = view.findViewById(R.id.tv_version);
return view;
}
导入jar包
采用mysql-connector-java-5.1.30-bin.jar进行MySQL连接,
将下载好的mysql-connector-java-5.1.30-bin.jar复制到工程的libs文件夹下,然点在mysql-connector-java-5.1.30-bin.jar点右键——>点击Add As Library...
完成后build.gradle文件中有这个包的加入。
导入后的build.gradle如下,最后一行代码就是导入了mysql-connector-java-5.1.30-bin.jar
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "cn.plczl.myapplication"
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation files('libs/mysql-connector-java-5.1.30-bin.jar')
}
数据库连接工具类
编写了连接数据库的工具类DBUtils.jave。用于要对数据操作的调用,代码如下:
其中有三个方法:
- getConn用于连接数据,其中String dbName参数为要连接的数据名字,
- getEthData方法是获得eth数据库中的apidate表的最新一行的数据方法。
- getVersion方法是eth数据库中的version表中的version字段的的最新一行的数据方法。
调用的方法分别以一个数组和字符串返回。
package cn.plczl.mysql.utils;
import android.util.Log;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
//连接mySql数据库方法
public class DBUtils {
private static final String TAG ="Dbutils";
private static String driver="com.mysql.jdbc.Driver";
//这里是MySQL的用户名
private static String user="root";
//这里是我的MySQL密码,本文作了隐藏处理,
private static String password = "xxxxxxx";
public static Connection getConn(String dbName){
Connection connection = null;
try{
Class.forName(driver);
//数据的IP地址,本文中的地址不是我的真实地址,请换为你的真实IP地址。
String ip="39.10.011";
String port="3306";
String url = "jdbc:mysql://" + ip + ":" + port +"/" + dbName;
connection= DriverManager.getConnection(url, user, password);
Log.e("数据库连接", "成功!");
} catch (Exception e) {
Log.e("数据库连接", "失败!");
e.printStackTrace();
}
return connection;
}
public static String[] getEthData() {
String rsdata[]=new String[24];
Connection connection = getConn("eth");
if (connection!=null){
String sql="select * from apidate order by id DESC limit 1";
try{
java.sql.Statement statement = connection.createStatement();
ResultSet rSet = statement.executeQuery(sql);
while (rSet.next()) {
rsdata[0]=rSet.getString("date");//日期
rsdata[1]=rSet.getString("time");//时间
rsdata[2]=rSet.getString("capital").substring(0,4);//人民币总收益
rsdata[3]=rSet.getString("worker_length");//矿机总数
rsdata[4]=rSet.getString("worker_lenght_online");//在线矿机数
rsdata[5]=rSet.getString("worker_length_offline");//离线矿机数
rsdata[6]=rSet.getString("dead_sl");//失线矿机数
rsdata[7]=rSet.getString("hash_24_hour");//24小时平均算力
rsdata[8]=rSet.getString("last_day_value").substring(0,9);//过去24小时收益
rsdata[9]=rSet.getString("hash_15");//15分钟平均算力
rsdata[10]=rSet.getString("balance").substring(0,9);//当前余额
rsdata[11]=rSet.getString("local_hash");//本地算力
rsdata[12]=rSet.getString("value").substring(0,9);//总收益
rsdata[13]=rSet.getString("quanwangsl")+" TH/s";//全网算力
rsdata[14]=rSet.getString("f2poolsl")+" TH/s";//全网算力
rsdata[15]=rSet.getString(
"meiM_Eth")+" ETH ≈ ¥"
+String.valueOf(Double.valueOf(rSet.getString("meiM_Eth"))
*Double.valueOf(rSet.getString("meiyuanzhisu"))
*Double.valueOf(rSet.getString("usdCnyRate")))
.substring(0,5);//每M收益
rsdata[16]= String.valueOf(
Double.valueOf(rSet.getString("waikuangnandu"))/10000/10000/10000/1000)
.substring(0,5)+" P";//当前难度
rsdata[17]="#34;+rSet.getString(
"price")+" ≈ ¥"
+String.valueOf(Double.valueOf(rSet.getString("price"))
*Double.valueOf(rSet.getString("usdCnyRate")))
.substring(0,4);//币价usdCnyRate
rsdata[18]="¥"+rSet.getString("ETC_index");//etc指数
rsdata[19]=rSet.getString("ETC_zhangdie");//etc涨跌
rsdata[20]=rSet.getString("ETH_zuigaojia");//eth最高价
rsdata[21]=rSet.getString("ETH_zuidijia");//eth最低价
rsdata[22]=rSet.getString("riseandfall");//ETH涨跌
rsdata[23]=rSet.getString("id");//数据库数据条数
Log.e(TAG,"数组组装成功");
}
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
};
Log.e(TAG,"数组已返回");
return rsdata;
}
public static String getVersion() {
String version=null;
Connection connection = getConn("eth");
if (connection!=null){
String sql="select version from version";
try{
java.sql.Statement statement = connection.createStatement();
ResultSet rSet = statement.executeQuery(sql);
while (rSet.next()) {
version=rSet.getString("version");//日期
Log.e(TAG,"数组组装成功==="+version);
}
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
};
Log.e(TAG,"数组已返回==========");
return version;
}
}
获得数据更新
在EthMainPagerr的initData()方法中写了两个方法,分别连接两个数据库获得数据并显示。
getData()方法是调用DBUtils数据库连接工具中的getEthData()
getVersion();方法是调用DBUtils数据库连接工具中的getVersion()
采用在子线程中连接数据库和Handler切换到主线程进行数据更新。
@Override
public void initData() {
super.initData();
LogUtil.e("初始化了EthMainPager");
getData();
getVersion();
}
getData()
简单的new了一个Thread子线程,用于调用 ethdata=DBUtils.getEthData();方法,将数据存入ethdata数组中。通过handler.sendEmptyMessage(0);返回主线程。
private void getData() {
new Thread(){
@Override
public void run() {
super.run();
ethdata=DBUtils.getEthData();
handler.sendEmptyMessage(0);
}
}.start();
}
handler
在handler中更换tv_data.setText(ethdata[0]);tv_time.setText(ethdata[1]);获得的数据。
private Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
LogUtil.e(String.valueOf(ethdata.length));
tv_data.setText(ethdata[0]);
tv_time.setText(ethdata[1]);
}
};
getVersion()
与 getData()类似。
version=DBUtils.getVersion();方法,将数据存入version数组中。通过versionhandle.sendEmptyMessage(1);返回主线程。
private void getVersion() {
new Thread(){
@Override
public void run() {
super.run();
version=DBUtils.getVersion();
versionhandle.sendEmptyMessage(1);
}
}.start();
}
versionhandle
在versionhandle中更新tv_version.setText(version);获得的数据。
private Handler versionhandle = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
LogUtil.e(String.valueOf(ethdata.length));
tv_version.setText(version);
}
};
最终运行的结果
软件运行结果
数据库中的数据
相关推荐
- MySQL进阶五之自动读写分离mysql-proxy
-
自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...
- 3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?
-
引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...
- 一文由浅入深带你完全掌握MySQL的锁机制原理与应用
-
本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...
- 验证Mysql中联合索引的最左匹配原则
-
后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...
- MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)
-
目录1.索引基础...
- 你会看 MySQL 的执行计划(EXPLAIN)吗?
-
SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...
- MySQL 从入门到精通(四)之索引结构
-
索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...
- mysql总结——面试中最常问到的知识点
-
mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...
- mysql总结——面试中最常问到的知识点(2)
-
首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...
- MySQL基础全知全解!超详细无废话!轻松上手~
-
本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...
- 深入剖析 MySQL 中的锁机制原理_mysql 锁详解
-
在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...
- Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析
-
引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...
- MySQL基础篇:DQL数据查询操作_mysql 查
-
一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...
- MySql:索引的基本使用_mysql索引的使用和原理
-
一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
程序员的开源月刊《HelloGitHub》第 71 期
-
详细介绍一下Redis的Watch机制,可以利用Watch机制来做什么?
-
假如有100W个用户抢一张票,除了负载均衡办法,怎么支持高并发?
-
Java面试必考问题:什么是乐观锁与悲观锁
-
如何将AI助手接入微信(打开ai手机助手)
-
redission YYDS spring boot redission 使用
-
SparkSQL——DataFrame的创建与使用
-
一文带你了解Redis与Memcached? redis与memcached的区别
-
如何利用Redis进行事务处理呢? 如何利用redis进行事务处理呢英文
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)