JDBC规范四-Statement详解 jdbc规范四-statement详解解析
yuyutoo 2024-10-12 00:49 11 浏览 0 评论
本节我们来学习JDBC API中比较重要的部分—Statement接口及它的子接口PreparedStatement和CallableStatement。Statement接口中定义了执行SQL语句的方法,这些方法不支持参数输入,PreparedStatement接口中增加了设置SQL参数的方法,CallableStatement接口继承自PreparedStatement,在此基础上增加了调用存储过程以及检索存储过程调用结果的方法。
1.java.sql.Statement接口
Statement是JDBC API操作数据库的核心接口,具体的实现由JDBC驱动来完成。Statement对象的创建比较简单,需要调用Connection对象的createStatement()方法,例如:
// 获取Connection对象
Connection connection = DriverManager.getConnection("jdbc:hsqldb:mem:mybatis",
"sa", "");
Statement statement = connection.createStatement();
在应用程序中,每个Connection对象可以同时创建多个Statement对象,例如;
// 获取Connection对象
Connection connection = DriverManager.getConnection("jdbc:hsqldb:mem:mybatis",
"sa", "");
Statement statement1 = connection.createStatement();
Statement statement2 = connection.createStatement();
此外,Connection接口中还提供了几个重载的createStatement()方法,用于通过Statement对象指定ResultSet(结果集)的属性,例如:
Connection conn = dataSource.getConnection(user,passwd);Statement stmt = conn.createStatement(
ResultSet.TYPE SCROLL INSENSITIVEResultSet.CONCUR UPDATABLE
ResultSet.HOLD CURSORS OVER COMMIT):
上面的代码中,我们创建了一个Statement对象,通过参数指定该Statement对象创建的ResultSet对象是可滚动的,而且是可以修改的,当修改提交时ResultSet不会被关闭。关于ResultSet的更多细节会在后面的章节中介绍。
Statement的主要作用是与数据库进行交互,该接口中定义了一些数据库操作以及检索SQL执行结果相关的方法,具体如下:
#批量执行 SQL
void addBatch(String sgl)
void clearBatch()
int[] executeBatch()
#执行未知 SQL语句
boolean execute(String sgl)
execute(String sql,int autoGeneratedKeys)boolean
boolean execute(String sgl,int门 columnIndexes)
boolean execute(String sql, String[] columnNames)#执行查询语句
ResultSet executeQuery(String sql)#执行更新语句,包括UPDATE、DELETE、INSERT
intexecuteUpdate(String sql)
int executeUpdate(String sgl,int autoGeneratedKeys)int executeUpdate(String sgl,int[] columnIndexes)
int executeUpdate(String sgl,String[] columnIndexes)
int executeUpdate(String sgl,String[] columnNames)
#SOL 执行结果处理
long getLargeUpdateCount()
ResultSet getResultSet()
int getUpdateCount()
boolean getMoreResults()
boolean getMoreResults(int current)
ResultSet getGeneratedKeys()
#JDBC4.2新增,数据量大于 Integer.MAX VALUE 时使用
long[] executeLargeBatch()
long executeLargeUpdate(String sql
long executeLargeUpdate(String sql,int autoGeneratedKeys)
longexecuteLargeUpdate(Stringsq1,int[] columnIndexes)
long executeLargeUpdate(Stringsg1,String[] columnNames)
#取消 SOL 执行,需要数据库和驱动支持
void cancel()
#关闭statement对象
void close()
void closeOnCompletion()
Statement接口中提供的与数据库交互的方法比较多,具体调用哪个方法取决于SQL语句的类型。
如果使用Statement执行一条查询语句,并返回一个结果集(ResultSet对象),则可以调用executeQuery()方法。
如果SQL语句是一个返回更新数量的DML语句,则需要调用executeUpdate()方法,该方法有几个重载的方法,下详细介绍。
int executeUpdate(String sql):执行一个UPDATE、INSERT或者DELETE语句,返回更新数量。
int executeUpdate(String sql, int autoGeneratedKeys):执行一个UPDATE、INSERT或者DELETE语句。当SQL语句是INSERT语句时,autoGeneratedKeys参数用于指定自动生成的键是否能够被检索,取值为Statement.RETURN_GENERATED_KEYS或Statement.NO_GENERATED_KEYS。当参数值为Statement.RETURN_GENERATED_KEYS时,INSERT语句自动生成的键能够被检索。当我们向数据库中插入一条记录,希望获取这条记录的自增主键时,可以调用该方法,指定第二个参数值为Statement.RETURN_GENERATED_KEYS。
int executeUpdate(String sql, int[] columnIndexes):执行一个UPDATE、INSERT或者DELETE语句,通过columnIndexes参数告诉驱动程序哪些列中自动生成的键可以用于检索。columnIndexes数组用于指定目标表中列的索引,这些列中自动生成的键必须能够被检索。如果SQL语句不是INSERT语句,columnIndexes参数将会被忽略。
int executeUpdate(String sql, String[]columnNames):这个方法的作用和executeUpdate(String sql, int[]columnIndexes)相同,不同的是columnNames参数是一个String数组,通过字段名的方式指定哪些字段中自动生成的键能够被检索。如果SQL语句不是INSERT语句,columnNames参数就会被忽略。
注意如果数据库支持返回的更新数量大于Integer.MAX_VALUE,则需要调用executeLargeUpdate()方法。
当我们在执行数据库操作之前,若不确定SQL语句的类型,则可以调用excute()方法。该方法也有几个重载的方法,分别说明如下。
boolean execute(String sql):执行一个SQL语句,通过返回值判断SQL类型,当返回值为true时,说明SQL语句为SELECT语句,可以通过Statement接口中的getResultSet()方法获取查询结果集;否则为UPDATE、INSERT或者DELETE语句,可以通过Statement接口中的getUpdateCount()方法获取影响的行数。
boolean execute(String sql, int autoGeneratedKeys):该方法通过autoGeneratedKeys参数(只对INSERT语句有效)指定INSERT语句自动生成的键是否能够被检索。
boolean execute(String sql, String[]columnNames):columnNames参数是一个String数组,通过字段名的方式指定哪些字段中自动生成的键能够被检索。如果SQL语句不是INSERT语句,则columnNames参数会被忽略。
注意当数据库支持返回影响的行数大于Integer.MAX_VALUE时,需要使用getLargeUpdateCount()方法。
另外,execute()方法可能返回多个结果。我们可以通过Statement对象的getMoreResults()方法获取下一个结果,当getMoreResults()方法的返回值为true时,说明下一个结果为ResultSet对象;当返回值为false时,说明下一个结果为影响行数,或者没有更多结果。
默认情况下,每次调用getMoreResults()方法都会关闭上一次调用getResultSet()方法返回的ResultSet对象。但是,我们可以通过重载getMoreResults()方法的参数指定是否关闭ResultSet 对象。
Statement接口中定义了3个常量可以用作getMoreResults()的参数,具体如下。
CLOSE_CURRENT_RESULT:表明当返回下一个ResultSet对象时,当前ResultSet对象应该关闭。
KEEP_CURRENT_RESULT:表明当返回下一个ResultSet对象时,当前ResultSet对象不关闭。
CLOSE_ALL_RESULTS:表明当返回下一个ResultSet对象时,当前所有未关闭的ResultSet对象都关闭。
如果当前结果是影响行数,而不是ResultSet对象,则getMoreResults()方法的参数将会被忽略。为了确定JDBC驱动是否支持通过getMoreResults()方法获取下一个结果,我们可以调用DatabaseMetaData接口提供的supportsMultipleOpenResults()方法,DatabaseMetaData的相关细节将会在后面的章节中介绍。
除此之外,Statement接口中还提供了几个方法,用于批量执行SQL语句,分别为:
void addBatch(String sql):把一条SQL语句添加到批量执行的SQL列表中。
void clearBatch():清空批量执行的SQL列表。
int[]executeBatch():批量地执行SQL列表中的语句。
Statement接口中除了提供操作数据库相关的方法外,还提供了一系列属性相关的方法,这些方法用于设置或获取Statement相关的属性,代码如下:
#Statement 属性相关
Connection getConnection()
int getFetchDirection()
int getFetchSize()
ResultSet getGeneratedKeys()
int getMaxFieldSize()
int getMaxRows()
boolean getMoreResults()
boolean getMoreResults(int current)
int getQueryTimeout()
int getResultSetConcurrency()
int getResultSetHoldability()
int getResultSetType()
boolean isClosed()
booleanisCloseOnCompletion()
boolean isPoolable()
void setCursorName(String name)
void setEscapeProcessing(boolean enable)
void setFetchDirection(int direction)
void setFetchSize(int rows)
void setLargeMaxRows(long max)
void setMaxFieldSize(int max)
void setMaxRows(int max)
void setPoolable(boolean poolable)
void setQueryTimeout(int seconds)
2.java.sql.PreparedStatement接口
PreparedStatement接口继承自Statement接口,在Statement接口的基础上增加了参数占位符功能。PreparedStatement接口中增加了一些方法,可以为占位符设置值。PreparedStatement的实例表示可以被预编译的SQL语句,执行一次后,后续多次执行时效率会比较高。使用PreparedStatement实例执行SQL语句时,可以使用“?”作为参数占位符,然后使用PreparedStatement接口中提供的方法为占位符设置参数值。
PreparedStatement对象的创建比较简单,与Statement类似,只需要调用Connection对象的prepareStatement()方法。与创建Statement对象不同的是,prepareStatement()方法需要提供一个SQL语句作为参数,例如:
// 获取Connection对象
Connection connection = dataSource.getConnection();
PreparedStatement stmt = connection.prepareStatement("insert into " +
"user(create_time, name, password, phone, nick_name) " +
"values(?,?,?,?,?);");
stmt.setString(1,"2010-10-24 10:20:30");
stmt.setString(2,"User1");
stmt.setString(3,"test");
stmt.setString(4,"18700001111");
stmt.setString(5,"User1");
前面的章节中有提到过,使用createStatement()方法创建Statement对象时,可以通过参数指定ResultSet的特性。与createStatement()方法类似,prepareStatement()也可以通过重载的方法指定ResultSet的特性.
PreparedStatement接口中定义了一系列的Setter方法,用于为SQL语句中的占位符赋值,这些Setter方法名称遵循set<Type>格式,其中Type为数据类型。例如,setString()方法用于为参数占位符设置一个字符串类型的值。这些Setter方法一般都有两个参数,第一个参数为int类型,表示参数占位符的位置(从1开始);第二个参数为占位符指定的值。
需要注意的是,在使用PreparedStatement对象执行SQL语句之前必须为每个参数占位符设置对应的值,否则调用executeQuery()、executeUpdate()或execute()等方法时会抛出SQLException异常。
PreparedStatement对象设置的参数在执行后不能被重置,需要显式地调用clearParameters()方法清除先前设置的值,再为参数重新设置值即可。
注意 在使用PreparedStatement对象执行SQL时,JDBC驱动通过setAsciiStream()、setBinaryStream()、setCharacterStream()、setNCharacterStream()或setUnicodeStream()等方法读取参数占位符设置的值。这些参数值必须在下一次执行SQL时重置掉,否则将会抛出SQLException异常。
对于一个给定的Statement对象,在execute()、executeQuery()、executeUpdate()、executeBatch()或clearParameters()方法调用之前,如果占位符已经使用setXXX()方法设置值,应用程序不可以再次调用setXXX()方法修改已经设置的值。但是应用程序可以在execute()、executeQuery()、executeUpdate()、executeBatch()或clearParameters()方法调用后,再次调用setXXX()方法覆盖先前设置的值。不遵循这一约束可能会导致不可预知的结果。
我们在使用setXXX()方法为参数占位符设置值时存在一个数据转换过程。setXXX()方法的参数为Java数据类型,需要转换为JDBC类型(java.sql.Types中定义的SQL类型),这一过程由JDBC驱动来完成。Java类型与JDBC类型之间的对应关系如表所示。
PreparedStatement接口中提供了一个setObject()方法,可以将Java类型转换为JDBC类型。该方法可以接收三个参数,第一个参数为占位符位置,第二个参数为Java对象,第三个参数是要转换成的JDBC类型。如果Java对象与JDBC类型不兼容,就会抛出SQLException异常。
下面是使用setObject()方法将Java中的Integer类型转换为JDBC中的SHORT类型的案例,具体代码如下:
Integer value = new Integer(15);
ps.setObject(1, value, java.sql.Types.SHORT);
另外,setObject()方法可以只接收两个参数,不用指定JDBC类型。这种情况下,JDBC驱动会按照表2-2中的映射关系将Java类型隐式地转换为对应的JDBC类型,例如:
Integer value= new Integer(15);// Integer 类型会转换为java.sql.Types.INTEGER
ps.setObject(1, value);
PreparedStatement接口中提供了一个setNull()方法,可以将占位符参数设置为JDBC的NULL。该方法接收两个参数,第一个参数为占位符的位置,第二个参数为JDBC类型。该方法的语法格式如下:
ps.setNul1(2, java.sql.Types.VARCHAR)
如果接收Java对象的setXXX()方法参数为null,则该参数的占位符被设置为JDBC的NULL。
JDBC API中提供了一个ParameterMetaData接口,用于描述PreparedStatement对象的参数信息,包括参数个数、参数类型等。PreparedStatement接口中提供了一个getParameterMetaData()方法,用于获取ParameterMetaData实例。下面是使用ParameterMetaData获取参数信息的案例,代码如下:
public void testJdbc() {
try {
// 创建DataSource实例
DataSourceFactory dsf = new UnpooledDataSourceFactory();
Properties properties = new Properties();
InputStream configStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("database.properties");
properties.load(configStream);
dsf.setProperties(properties);
DataSource dataSource = dsf.getDataSource();
// 获取Connection对象
Connection connection = dataSource.getConnection();
PreparedStatement stmt = connection.prepareStatement("insert into " +
"user(create_time, name, password, phone, nick_name) " +
"values(?,?,?,?,?);");
stmt.setString(1,"2010-10-24 10:20:30");
stmt.setString(2,"User1");
stmt.setString(3,"test");
stmt.setString(4,"18700001111");
stmt.setString(5,"User1");
ParameterMetaData pmd = stmt.getParameterMetaData();
for(int i = 1; i <= pmd.getParameterCount(); i++) {
String typeName = pmd.getParameterTypeName(i);
String className = pmd.getParameterClassName(i);
System.out.println("第" + i + "个参数," + "typeName:" + typeName + ", className:" + className);
}
stmt.execute();
// 关闭连接
IOUtils.closeQuietly(stmt);
IOUtils.closeQuietly(connection);
} catch (Exception e) {
e.printStackTrace();
}
}
3.java.sql.CallableStatement接口
CallableStatement接口继承自PreparedStatement接口,在PreparedStatement的基础上增加了调用存储过程并检索调用结果的功能。与Statement、PreparedStatement一样,CallableStatement对象也是通过Connection对象创建的,我们只需要调用Connection对象的prepareCall()方法即可,例如:
CallableStatement cstmt = conn.prepareCall("call validate(?,?)")
CallableStatement对象可以使用3种类型的参数:IN、OUT和INOUT。可以将参数指定为序数参数或命名参数,必须为IN或INOUT参数的每个参数占位符设置一个值,必须为OUT或INOUT参数中的每个参数占位符调用registerOutParameter()方法。存储过程参数的数量、类型和属性可以使用DatabaseMetaData接口提供的getProcedureColumns()方法获取。需要注意的是,使用setXXX()方法为参数占位符设置值时,下标必须从1开始。语句中的字面量参数值不会增加参数占位符的序数值,例如:
CallableStatement cstmt = con.prepareCall("(CALL PROC(?"Literal Value",?)}");
cstmt.setString(1,"First");
cstmt.setString(2,"Third");
命名参数可以用来指定特定的参数,这在存储过程有多个给定默认值的参数时特别有用,命名参数可以用于为那些没有默认值的参数设置值,参数名称可以通过DatabaseMetaData对象的getProcedureColumns()方法返回的COLUMN_NAME字段获取。例如,在下面的案例中,COMPLEX_PROC存储过程可以接收10个参数,但是只有第1个和第5个参数(PARAM_1和PARAM_5)需要设置值。
CallableStatement cstmt = con.prepareCall("{CALL COMPLEX PROC(?,?)}");
cstmt.setString("PARAM 1","Price");
cstmt.setFloat("PARAM 5",150.25);
CallableStatement接口中新增了一些额外的方法允许参数通过名称注册和检索。
DatabaseMetaData接口中提供了supportsNamedParameters()方法,用于判断JDBC驱动是否支持指定命名参数。
对于IN参数的设置,调用CallableStatement接口中提供的setXXX()方法即可;但是对于OUT和INOUT参数,在CallableStatement执行之前,必须为每个参数调用CallableStatement接口中提供的registerOutParameter()方法,例如:
CallableStatement cstmt = conn.prepareCall("(CALL GET NAME AND NUMBER(?, ?))");
cstmt.registerOutParameter(1,java.sql.Types.STRING);
cstmt.registerOutParameter(2,java.sql.Types.FLOAT);
cstmt.execute() ;
//取 OUT 参数值
String name = cstmt.getString(1);
float number = cstmt.getFloat(2);
与Statement、PreparedStatement类似,CallableStatement也是使用executeQuery()、executeUpdate()、execute()等方法执行存储过程的调用,返回结果可能是ResultSet对象或者影响的行数,存储过程调用结果的处理与Statement对象执行SQL结果的处理过程类似,这里就不重复介绍了。
4.获取自增长的键值
目前大多数数据库都支持自增长主键,当向表中插入数据时,数据库引擎可以自动生成自增长主键。Statement接口中提供了getGeneratedKeys()方法,用于获取数据库自动生成的值,该方法返回一个ResultSet对象,我们可以从ResultSet对象中获取数据库中所有自增长的键值。
Statement接口中的execute()、executeUpdate()和Connection接口的prepareStatement()方法都可以接收一个可选的参数,该参数用于指定由数据库生成的值是否可以被检索,例如:
public void testJdbc() {
try {
Class.forName("org.hsqldb.jdbcDriver");
// 获取Connection对象
Connection conn = DriverManager.getConnection("jdbc:hsqldb:mem:mybatis",
"sa", "");
Statement stmt = conn.createStatement();
String sql = "insert into user(create_time, name, password, phone, nick_name) " +
"values('2010-10-24 10:20:30','User1','test','18700001111','User1');";
stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
ResultSet genKeys = stmt.getGeneratedKeys();
if(genKeys.next()) {
System.out.println("自增长主键:" + genKeys.getInt(1));
}
IOUtils.closeQuietly(stmt);
IOUtils.closeQuietly(conn);
} catch (Exception e) {
e.printStackTrace();
}
}
另外,Statement接口中还提供了execute()、executeUpdate()重载方法,能够通过下标或者字段名指定哪些字段中自动生成的值可以被检索,例如:
public void testJdbc2() {
try {
Class.forName("org.hsqldb.jdbcDriver");
// 获取Connection对象
Connection conn = DriverManager.getConnection("jdbc:hsqldb:mem:mybatis",
"sa", "");
Statement stmt = conn.createStatement();
// 指定主键
String[] columnNames = new String[]{"id"};
String sql = "insert into user(create_time, name, password, phone, nick_name) " +
"values('2010-10-24 10:20:30','User1','test','18700001111','User1');";
stmt.executeUpdate(sql);
sql = "insert into user(create_time, name, password, phone, nick_name) " +
"values('2010-10-24 10:20:30','User1','test','18700001111','User1');";
stmt.executeUpdate(sql, columnNames);
ResultSet genKeys = stmt.getGeneratedKeys();
if(genKeys.next()) {
System.out.println("自增长主键:" + genKeys.getInt(1));
}
IOUtils.closeQuietly(stmt);
IOUtils.closeQuietly(conn);
} catch (Exception e) {
e.printStackTrace();
}
}
相关推荐
- ETCD 故障恢复(etc常见故障)
-
概述Kubernetes集群外部ETCD节点故障,导致kube-apiserver无法启动。...
- 在Ubuntu 16.04 LTS服务器上安装FreeRADIUS和Daloradius的方法
-
FreeRADIUS为AAARadiusLinux下开源解决方案,DaloRadius为图形化web管理工具。...
- 如何排查服务器被黑客入侵的迹象(黑客 抓取服务器数据)
-
---排查服务器是否被黑客入侵需要系统性地检查多个关键点,以下是一份详细的排查指南,包含具体命令、工具和应对策略:---###**一、快速初步检查**####1.**检查异常登录记录**...
- 使用 Fail Ban 日志分析 SSH 攻击行为
-
通过分析`fail2ban`日志可以识别和应对SSH暴力破解等攻击行为。以下是详细的操作流程和关键分析方法:---###**一、Fail2ban日志位置**Fail2ban的日志路径因系统配置...
- 《5 个实用技巧,提升你的服务器安全性,避免被黑客盯上!》
-
服务器的安全性至关重要,特别是在如今网络攻击频繁的情况下。如果你的服务器存在漏洞,黑客可能会利用这些漏洞进行攻击,甚至窃取数据。今天我们就来聊聊5个实用技巧,帮助你提升服务器的安全性,让你的系统更...
- 聊聊Spring AI Alibaba的YuQueDocumentReader
-
序本文主要研究一下SpringAIAlibaba的YuQueDocumentReaderYuQueDocumentReader...
- Mac Docker环境,利用Canal实现MySQL同步ES
-
Canal的使用使用docker环境安装mysql、canal、elasticsearch,基于binlog利用canal实现mysql的数据同步到elasticsearch中,并在springboo...
- RustDesk:开源远程控制工具的技术架构与全场景部署实战
-
一、开源远程控制领域的革新者1.1行业痛点与解决方案...
- 长安汽车一代CS75Plus2020款安装高德地图7.5
-
不用破解原车机,一代CS75Plus2020款,安装车机版高德地图7.5,有红绿灯读秒!废话不多讲,安装步骤如下:一、在拨号状态输入:在电话拨号界面,输入:*#518200#*(进入安卓设置界面,...
- Zookeeper使用详解之常见操作篇(zookeeper ui)
-
一、Zookeeper的数据结构对于ZooKeeper而言,其存储结构类似于文件系统,也是一个树形目录服务,并通过Key-Value键值对的形式进行数据存储。其中,Key由斜线间隔的路径元素构成。对...
- zk源码—4.会话的实现原理一(会话层的基本功能是什么)
-
大纲1.创建会话...
- Zookeeper 可观测性最佳实践(zookeeper能够确保)
-
Zookeeper介绍ZooKeeper是一个开源的分布式协调服务,用于管理和协调分布式系统中的节点。它提供了一种高效、可靠的方式来解决分布式系统中的常见问题,如数据同步、配置管理、命名服务和集群...
- 服务器密码错误被锁定怎么解决(服务器密码错几次锁)
-
#服务器密码错误被锁定解决方案当服务器因多次密码错误导致账户被锁定时,可以按照以下步骤进行排查和解决:##一、确认锁定状态###1.检查账户锁定状态(Linux)```bash#查看账户锁定...
- zk基础—4.zk实现分布式功能(分布式zk的使用)
-
大纲1.zk实现数据发布订阅...
- 《死神魂魄觉醒》卡死问题终极解决方案:从原理到实战的深度解析
-
在《死神魂魄觉醒》的斩魄刀交锋中,游戏卡死犹如突现的虚圈屏障,阻断玩家与尸魂界的连接。本文将从技术架构、解决方案、预防策略三个维度,深度剖析卡死问题的成因与应对之策,助力玩家突破次元壁障,畅享灵魂共鸣...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
推荐7个模板代码和其他游戏源码下载的网址
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
-
- ETCD 故障恢复(etc常见故障)
- 在Ubuntu 16.04 LTS服务器上安装FreeRADIUS和Daloradius的方法
- 如何排查服务器被黑客入侵的迹象(黑客 抓取服务器数据)
- 使用 Fail Ban 日志分析 SSH 攻击行为
- 《5 个实用技巧,提升你的服务器安全性,避免被黑客盯上!》
- 聊聊Spring AI Alibaba的YuQueDocumentReader
- Mac Docker环境,利用Canal实现MySQL同步ES
- RustDesk:开源远程控制工具的技术架构与全场景部署实战
- 长安汽车一代CS75Plus2020款安装高德地图7.5
- Zookeeper使用详解之常见操作篇(zookeeper ui)
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)