一、使用executeLargeUpdate方法执行DDL和DML语句
statement提供了三个方法执行SQL语句,executeQuery()用于执行查询语句,executeLargeUpdate(或executeUpdate())用于执行DDL和DML语句,区别在于执行DDL语句后返回0,执行DML语句后的返回值为受影响的记录条数。
1.1 执行DDL语句,executeUpdate()返回0
下面程序示范了executeUpdate()方法创建数据表。该示例并没有把数据库连接信息写在程序中,而是使用一个mysql.ini文件(就是一个properties文件)来保存数据库的连接信息,这是比较成熟做法——当需要把程序从开发环境移植到生产环境时,无须修改源代码,只需要修改mysql.ini配置文件即可:
package section4;
import java.util.*;
import java.io.*;
import java.sql.*;
public class ExecuteDDL
{
private String driver;//数据库驱动
private String url;//数据库url
private String user;//用户名
private String pass;//数据库密码
void initParam(String paramFile)
//用于加载配置文件属性
throws Exception
{
//使用Properties类加载文件属性
Properties props=new Properties();
props.load(new FileInputStream(paramFile));
this.driver=props.getProperty("driver");
this.url=props.getProperty("url");
this.user=props.getProperty("user");
this.pass=props.getProperty("pass");
}
int createTable(String sql)
throws Exception
{
int n;
//加载驱动
Class.forName(driver);
try(
//获取数据库连接
Connection conn = DriverManager.getConnection(url,user,pass);
//创建Statement对象
Statement stmt=conn.createStatement())
{
//执行DDL语句
n=stmt.executeUpdate(sql);
}
return n;
}
public static void main(String[] args)
throws Exception
{
var exeDDL=new ExecuteDDL();
exeDDL.initParam("src\\\\mysql.ini");
int n= exeDDL.createTable( "create table jdbc_test(" +
"jdbc_id int auto_increment primary key," +
"jdbc_name varchar(255)," +
"jdbc_desc text);");
System.out.println(n);//执行DDL语句,executeUpadte()返回0
System.out.println("建表成功");
}
}
现在查看数据库select_test是否存在一个名叫jdbc的表格:
1.2 执行DML语句,返回受影响的记录条数
下面程序将在上面创建的jdbc_test表中插入几条记录,Mysql的扩展语法中,insert into可以插入多条记录:
package section4;
import java.util.*;
import java.io.*;
import java.sql.*;
public class ExecuteDML
{
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile)
throws Exception
{
//使用properties加载属性文件
var porps=new Properties();
porps.load(new FileInputStream(paramFile));
driver=porps.getProperty("driver");
url=porps.getProperty("url");
user=porps.getProperty("user");
pass=porps.getProperty("pass");
}
public int insertData(String sql)
throws Exception
{
//加载驱动
Class.forName(driver);
try( //try()在代码块接受后会自行关闭打开的资源
//获取数据库连接
Connection conn=DriverManager.getConnection(url,user,pass);
//创建Statement对象
Statement stmt =conn.createStatement()
)
{
//执行DML语句,返回受影响的记录条数
return stmt.executeUpdate(sql);
}
}
public static void main(String[] args)
{
var exeDML=new ExecuteDML();
try {
exeDML.initParam("src\\\\mysql.ini");
int result=exeDML.insertData("insert into jdbc_test(jdbc_name,jdbc_desc)" +
"select s.student_name,t.teacher_name " +
"from student_table s,teacher_table t " +
"where s.java_teacher=t.teacher_id;");
System.out.println("-----系统中一个有" + result + "条记录受影响-----");
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
输出:-----系统中一个有6条记录受影响-----
看一下jdbc_test表是不是预期那样:
{{uploading-image-545228.png(uploading...)}}
二、使用execute方法执行SQL语句
Statement的execute()方法可以执行任何SQL语句,当它执行SQL语句时比较麻烦,通常没有必要使用execute()方法来执行SQL语句,使用executeQuery()或executeUpdata()方法更简单。但如果不清楚SQL语句的类型,则只能使用execute()方法来执行SQL语句。
使用execute()方法执行SQL语句的返回值只是Boolean值,它表明执行该SQL语句是否返回了ResultSet对象。Statement提供两个如下方法来获取执行结果:
(1)getResultSet():获取该Statement执行查询语句所返回的Statement对象。
(2)getUpdata():获取该Statement()执行DML语句所影响的记录条数。执行DDL语句返回0.
下面程序示范了使用Statement的execute()方法执行任意SQL语句,执行不同的SQL语句时,产生不同的输出。
package section4;
import java.io.*;
import java.sql.*;
import java.util.Properties;
public class ExecuteSQL
{
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile)
throws Exception
{
//使用properties加载属性文件
Properties props=new Properties();
props.load(new FileInputStream(paramFile));
driver=props.getProperty("driver");
url=props.getProperty("url");
user=props.getProperty("user");
pass=props.getProperty("pass");
}
public void executesql(String sql)
throws Exception
{
//加载驱动
Class.forName(driver);
try(
//获取数据库连接
Connection conn= DriverManager.getConnection(url,user,pass);
//创建Statement来创建数据库连接
Statement stmt=conn.createStatement();
)
{
//执行SQL语句,返回boolean值是否包含ResultSet
Boolean hasResultSet=stmt.execute(sql);
//如何执行结果有ResultSet结果集
if(hasResultSet)
{
try(
ResultSet rs=stmt.getResultSet();
)
{
//ResultSetMetaData是用于分析结果集的元数据接口
ResultSetMetaData rsmd=rs.getMetaData();
int columnCount=rsmd.getColumnCount();
//迭代输出ResultSet对象
while(rs.next())
{
//依次输出每条记录
for(var i=0;i<columnCount;i++)
{
System.out.print(rs.getString(i+1)+"\\t");
}
System.out.print("\\n");
}
}
}
else
{
System.out.println("该语句受影响的记录条数为"
+stmt.getUpdateCount()+"条");
}
}
}
public static void main(String[] args)
throws Exception
{
var es=new ExecuteSQL();
es.initParam("src\\\\mysql.ini");
System.out.println("-----执行删除表的DDL语句-------");
es.executesql("drop table if exists my_test");
System.out.println("----执行建表的DDL语句-------");
es.executesql("create table if not exists my_test(" +
"test_id int auto_increment primary key," +
"test_name varchar(255));");
System.out.println("-----执行插入数据的DML语句-----");
es.executesql("insert into my_test(test_name) " +
"select student_name from student_table");
System.out.println("-----执行查询语句----");
es.executesql("select *from my_test");
}
}
-----执行删除表的DDL语句-------
该语句受影响的记录条数为0条
----执行建表的DDL语句-------
该语句受影响的记录条数为0条
-----执行插入数据的DML语句-----
该语句受影响的记录条数为7条
-----执行查询语句----
1 张三
2 张三
3 李四
4 王五
5 _王五
6 null
7 赵六
Process finished with exit code 0
三、使用PreparedStatement执行SQL语句
如果经常需要反复执行一条结构相似的SQL语句,例如下面两条SQL语句:
insert into student_table values(null,\'张三\',1);
insert into student_table values(null,\'李四\',1);
对于这种情况可以使用占位符(?)参数的SQL语句来代表它:
insert into student_table values(null,?,?);
为了满足这种功能,JDBC提供了PreparedStatement接口,它是Statement接口的子接口,它可以预编译SQL语句,预编译后的SQL语句被存储在PrepareStatement对象中,然后可以使用该对象多次高效地执行语句。简而言之,使用PrepareStatement比使用Statement效率高。
创建PreparedStatement对象使用Connection的prepareStatement()方法,该方法需要传入一个SQL语句,该SQL字符串可以包含占位符参数:
//创建一个PreparedStatement()
pstmt=conn.preparedStatement("insert into student_table values(null,?,?)");
PreparedStatement也提供了executeQuery()、executeUpdata()、execute()方法来执行SQL语句,只是这三个方法无须再传入SQL语句,因为PreparedStatement已存储了预编译的SQL语句。但是这些预编译语句使用了占位符,所以必须为其传入参数值,PreparedStatement提供了一系列的setXxx(int index,Xxx value)方法来传入参数值。
提示:如果已知预编译SQL语句各参数的类型,则使用相应的setXxx()方法传入参数;如果不清楚则使用setObject()方法传入参数,由PreparedStatement对象来负责类型转换。
下面示范使用Statement和PreparedStatement分别插入100条记录对比:
package section4;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.*;
import java.util.Properties;
public class PreparedStatementTest
{
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile)
throws Exception
{
Properties props=new Properties();
props.load(new FileInputStream(paramFile));
driver=props.getProperty("driver");
url=props.getProperty("url");
user=props.getProperty("user");
pass=props.getProperty("pass");
//加载驱动
Class.forName(driver);
}
public void insertUseStatement()
throws Exception
{
long star=System.currentTimeMillis();
try(
//获取数据连接
Connection conn= DriverManager.getConnection(url,user,pass);
//使用Connection对象创建一个Statement对象
Statement stmt=conn.createStatement()
)
{
for(var i=0;i<100;i++)
{
stmt.executeUpdate("insert into student_table values(" +
"null,\'姓名"+i+"\' ,1)");
}
System.out.println("使用Statement耗时:"+
(System.currentTimeMillis()-star));
}
}
public void insertUsePrepare()
throws Exception
{
long star=System.currentTimeMillis();
try(
Connection conn=DriverManager.getConnection(url,user,pass);
PreparedStatement pstmt=conn.prepareStatement("insert into student_table values(null,?,1)");
)
{
for(var i=0;i<100;i++)
{
pstmt.setString(1,"姓名"+i);
pstmt.executeUpdate();
}
System.out.println("使用PreparedStatement耗时:"+
(System.currentTimeMillis()-star));
}
}
public static void main(String[] args)
throws Exception
{
var pt=new PreparedStatementTest();
pt.initParam("src\\\\mysql.ini");
pt.insertUseStatement();
pt.insertUsePrepare();
}
}
使用Statement耗时:12136
使用PreparedStatement耗时:11881
Process finished with exit code 0
四、使用CallableStatement调用存储过程
4.1 首先在MySQL数据库创建一个简单的存储过程add_pro:
delimeter//
create procedure add_pro(a int,b int,out sum int)
begin
set sum=a+b;
end;
//
上面的SQL语句将MySQL的结束符改为双斜线(//)。这样可以在创建存储过程中使用分隔号(MySQL默认使用分号作为语句结束的分隔符)。上面创建了名为add_pro的存储过程,该存储过程包含三个系数:a,b是传入参数,而sum使用out修饰。是传出参数。
4.2 创建CallableStatement对象
调用CallableStatement,可以通过Connection的prepareCall()方法创建CallableStatement对象,创建该对象需要传入调用存储过程的SQL语句。调用存储过程的SQL语句总是这样的格式:{call 过程吗(?,?,?...)},其中?作为存储过程参数作为占位符。例如下面创建了调用上面存储过程的CallableStatement对象。
//使用Connection来创建一个CallableStatement对象
cstmt=conn.prepareCall("{call add_pro(?,?,?)}");
4.3设置传入传出参数
所谓传入参数就是Java程序必须为这些参数传入值,可以通过CallableStatement的setXxx()方法为传入参数设置值;所谓传出参数就是Java程序可以通过该参数获取存储过程里的值,CallableStatement需要调用registerOutParameter()方法来注册该参数。
//注册CallableStatement的第三个参数是int类型
xstmt.registerOutParameter(3,Types.INTEGER);
经过上面步骤后,就可以调用CallableStatement的execute()方法来执行那个存储过程,执行结束后通过CallableStatement对象的getXxx(int index)方法来获取指定传出参数的值。下面程序示范了如何调用存储过程的值:
package section4;
import java.io.FileInputStream;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Types;
import java.util.Properties;
public class CallableStatementTest
{
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile)
throws Exception
{
//使用Properties类加载文件属性
Properties props=new Properties();
props.load(new FileInputStream(paramFile));
driver=props.getProperty("driver");
url=props.getProperty("url");
user=props.getProperty("user");
pass=props.getProperty("pass");
}
public void callProcedure()
throws Exception
{
//加载驱动
Class.forName(driver);
try(
//创建连接
Connection conn=DriverManager.getConnection(url,user,pass);
//获取Callable
CallableStatement cstmt=conn.prepareCall("{call add_pro(?,?,?)}")
)
{
cstmt.setInt(1,4);
cstmt.setInt(2,5);
cstmt.registerOutParameter(3, Types.INTEGER);
//执行存储过程
cstmt.execute();
//获取并存储过程传出参数的值
System.out.println("执行结果:"+cstmt.getInt(3));
}
}
public static void main(String[] args)
throws Exception
{
var ct=new CallableStatementTest();
ct.initParam("src\\\\mysql.ini");
ct.callProcedure();
}
}
输出结果:执行结果:9
上面程序中可以看到这个简单存储过程的执行结果,传入参数分别为4,5,执行方法后传出总和为9。