JDBC规范四-Statement详解 jdbc规范四-statement详解解析
yuyutoo 2024-10-12 00:49 3 浏览 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();
}
}
相关推荐
- 史上最全的浏览器兼容性问题和解决方案
-
微信ID:WEB_wysj(点击关注)◎◎◎◎◎◎◎◎◎一┳═┻︻▄(页底留言开放,欢迎来吐槽)●●●...
-
- 平面设计基础知识_平面设计基础知识实验收获与总结
-
CSS构造颜色,背景与图像1.使用span更好的控制文本中局部区域的文本:文本;2.使用display属性提供区块转变:display:inline(是内联的...
-
2025-02-21 16:01 yuyutoo
- 写作排版简单三步就行-工具篇_作文排版模板
-
和我们工作中日常word排版内部交流不同,这篇教程介绍的写作排版主要是用于“微信公众号、头条号”网络展示。写作展现的是我的思考,排版是让写作在网格上更好地展现。在写作上花费时间是有累积复利优势的,在排...
- 写一个2048的游戏_2048小游戏功能实现
-
1.创建HTML文件1.打开一个文本编辑器,例如Notepad++、SublimeText、VisualStudioCode等。2.将以下HTML代码复制并粘贴到文本编辑器中:html...
- 今天你穿“短袖”了吗?青岛最高23℃!接下来几天气温更刺激……
-
最近的天气暖和得让很多小伙伴们喊“热”!!! 昨天的气温到底升得有多高呢?你家有没有榜上有名?...
- CSS不规则卡片,纯CSS制作优惠券样式,CSS实现锯齿样式
-
之前也有写过CSS优惠券样式《CSS3径向渐变实现优惠券波浪造型》,这次再来温习一遍,并且将更为详细的讲解,从布局到具体样式说明,最后定义CSS变量,自定义主题颜色。布局...
- 你的自我界限够强大吗?_你的自我界限够强大吗英文
-
我的结果:A、该设立新的界限...
- 行内元素与块级元素,以及区别_行内元素和块级元素有什么区别?
-
行内元素与块级元素首先,CSS规范规定,每个元素都有display属性,确定该元素的类型,每个元素都有默认的display值,分别为块级(block)、行内(inline)。块级元素:(以下列举比较常...
-
- 让“成都速度”跑得潇潇洒洒,地上地下共享轨交繁华
-
去年的两会期间,习近平总书记在参加人大会议四川代表团审议时,对治蜀兴川提出了明确要求,指明了前行方向,并带来了“祝四川人民的生活越来越安逸”的美好祝福。又是一年...
-
2025-02-21 16:00 yuyutoo
- 今年国家综合性消防救援队伍计划招录消防员15000名
-
记者24日从应急管理部获悉,国家综合性消防救援队伍2023年消防员招录工作已正式启动。今年共计划招录消防员15000名,其中高校应届毕业生5000名、退役士兵5000名、社会青年5000名。本次招录的...
- 一起盘点最新 Chrome v133 的5大主流特性 ?
-
1.CSS的高级attr()方法CSSattr()函数是CSSLevel5中用于检索DOM元素的属性值并将其用于CSS属性值,类似于var()函数替换自定义属性值的方式。...
- 竞走团体世锦赛5月太仓举行 世界冠军杨家玉担任形象大使
-
style="text-align:center;"data-mce-style="text-align:...
- 学物理能做什么?_学物理能做什么 卢昌海
-
作者:曹则贤中国科学院物理研究所原标题:《物理学:ASourceofPowerforMan》在2006年中央电视台《对话》栏目的某期节目中,主持人问过我一个的问题:“学物理的人,如果日后不...
-
- 你不知道的关于这只眯眼兔的6个小秘密
-
在你们忙着给熊本君做表情包的时候,要知道,最先在网络上引起轰动的可是这只脸上只有两条缝的兔子——兔斯基。今年,它更是迎来了自己的10岁生日。①关于德艺双馨“老艺...
-
2025-02-21 16:00 yuyutoo
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 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)