10.5 执行SQL语句的方式

Posted 星星和西西

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了10.5 执行SQL语句的方式相关的知识,希望对你有一定的参考价值。

一、使用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。

以上是关于10.5 执行SQL语句的方式的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis如何防止SQL注入

MyBatis怎么防止SQL注入

SQL Select 语句的用法

.NET Entity Framework(EF)使用SqlQuery直接操作SQL查询语句或者执行过程

Microsoft SQL Server 代码片段收集

mybatis以及预编译如何防止SQL注入