• 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)+"	");
                            }
                            System.out.print("
    ");
                        }
                    }
                }
                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。

  • 相关阅读:
    const 深悟理解
    深拷贝与浅拷贝深究
    结队开发-最大子数组
    软件工程个人作业02
    四则运算关于加括号的思路
    实践出真知-所谓"java没有指针",那叫做引用!
    写代码的好习惯—先构思
    团队合作
    阿超超的四则运算 想啊想啊
    Github+阿超运算
  • 原文地址:https://www.cnblogs.com/weststar/p/12690962.html
Copyright © 2020-2023  润新知