JDBC数据库开发入门

这篇博客介绍了JDBC的基础知识,包括JDBC的概念、原理、核心类接口及其使用,如Connection、Statement、ResultSet。通过Hello JDBC逐步讲解了如何连接MySQL数据库、发送SQL语句以及处理结果集。还涉及PreparedStatement的使用,防止SQL注入,JdbcUtils工具类的实现,DAO模式在登录注册案例中的应用,以及Java中时间类型和大数据处理的相关内容。最后讨论了MySQL的预编译和批处理功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JDBC入门

步骤:

  • 导jar包:驱动!
  • 加载驱动类:Class.forName(“类名”);
  • 给出url,username,password,其中url记下来
  • 使用DriverManager类来得到Connection对象

1.什么是JDBC

JDBC(java DataBase Connectivity)就是Java数据库连接,即用Java语言来操作数据库

2.JDBC原理

JDBC是由SUN公司提供的,一套访问数据库的规范(就是一组接口),并提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN公司的规范提供一套访问自己公司的数据库服务器的API出现。

SUN公司提供的命名规范名为JDBC,各个厂商提供的,遵循了JDBC规范的,可以访问自己数据库的API被称为驱动。

jdbc

3.JDBC核心类(接口)介绍

JDBC中的核心类有:DriverManager,Connnection,Statement和ResultSet

DriverManager(驱动管理器)的作用有两个:

  • 注册驱动:这可以让JDBC知道要使用的是哪个驱动
  • 获取Connection:如果可以获取到Connection,那么说明已经与数据库连接上了

Connection对象表示连接,与数据库的通讯都是通过这个对象展开的:

  • Connection最为重要的一个方法就是用来获取Statement对象

Statement是用来向数据库发送SQL语句的,这样数据库就会执行发送过来的SQL语句:

  • void executeUpdate(String sql):执行更新操作(insert,update,delete等)
  • ResultSet executeQuery(String sql):执行查询操作,数据库在执行查询后会把查询结果ResultSet返回

ResultSet对象表示查询结果集,只有在执行查询操作后才会有结果集的产生

  • 结果集是一个二维的表格,有行有列。
  • 操作结果集要利用ResultSet内部的“行光标”,以及获取当前行上的每一列上的数据
    • boolean next():是行光标移动到下一行,并返回移动后的行是否存在
    • XXX getXXX(int col):获取当前行指定 列上的值

4.Hello JDBC

4.1 mysql数据库的驱动
        /**
         * 所有的java.sql.Driver实现类,都提供了static块,
         * 块内的代码就是把自己注册到DriverManager中
         */
     Class.forName("com.mysql.jdbc.Driver");   //加载驱动类(注册驱动)
        /*等价于*/
     com.mysql.jdbc.Driver driver = new com.mysql.jdbc.Driver();   //注册驱动
     DriverManager.registerDriver(driver);
  • jbdc4.0之后,每个驱动jar包中,META-INF目录下提供了一个名为java.sql.Driver的文件
    • 文件的内容就是该接口的实现类名称
    • 可以不用自己加载驱动类
    • 但为了兼容,要写
4.2 获取连接
//jdbc协议的格式 jdbc:厂商的名称:子协议(由厂商自己规定)(http协议的格式 http://主机:端口/页面路径)
//对mysql而言,它的子协议结构://主机:端口号/数据库名称
String url = "jdbc:mysql://localhost:3306/test";
String username = "root";
String password = "";

//使用url,username,password,得到连接对象
Connection connection = DriverManager.getConnection(url,username,password);
4.3 获取Statement
//通过Connection对象创建Statement
//Statement语句->发送器,它的功能就是向数据库发送SQL语句
Statement statement = connection.createStatement();
4.4 发送SQL增、删、改
//使用Statement发送sql语句
String sql = "INSERT INTO users VALUES('ITCAST_0003','wangwu',38,'male')"; 
                                              //引号内只能有一条sql语句,且不加分号

String sql = "UPDATE users SET name='zhaoliu',age=22,gender='female' WHERE 
                                              number='ITCAST_0003'";
    
String sql = "DELETE FROM users";

int r = statement.executeUpdate(sql);     //该函数的返回值操作行数
System.out.println(r);
4.5 发送SQL查询语句
//调用Statement的ResultSet rs = execuQuery(String querySql)
ResultSet resultSet = statement.executeQuery
                      ("select * from users");
System.out.println(resultSet);  //显示未解析的结果集
4.6 读取结果集中的数据
//把行光标移动到第一行,可以使用next()方法完成
while(resultSet.next())   //把光标向下移动一行,并判断下一行是否存在
{
    String number = resultSet.getString(1);
                              //通过列编号来获取该列的值
    String name = resultSet.getString("name"); 
                              //通过列名称来获取该列的值
    int age = resultSet.getInt(3);
    String gender = resultSet.getString(4);
    System.out.println(number + "," + name + "," + age 
                        + "," + gender);
}
4.7 关闭
// 倒关(先得到的资源后关闭,后得到的资源先关闭)
resultSet.close();
statement.close();
connection.close();   //这个必须要关
4.8 规范化代码

规范化代码就是无论是否出现异常,都要关闭ResultSet,Statement,以及Connection

在try外给出引用的定义,在try内为对象实例化,在finally中进行关闭

public static  void  fun() 
throws ClassNotFoundException, SQLException 
{
        Connection connection = null;   //定义引用
        Statement statement = null;
        ResultSet resultSet = null;;
        try
        {
            String driverClassName = "com.mysql.jdbc.Driver";
            String url = "jdbc:mysql://localhost:3306/px190731";
            String username = "root";
            String password = "";
            Class.forName(driverClassName);
            connection = DriverManager.getConnection(url,username,password);  //实例化

            statement = connection.createStatement();
            String sql = "select * from users";
            resultSet = statement.executeQuery(sql);

            while (resultSet.next())
            {
                System.out.println(resultSet.getString(1) + "," +
                                   resultSet.getString(2) + resultSet.getInt(3) 
                                   + "," + resultSet.getString(4));
            }
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }
        finally
        {
            //关闭
            if(resultSet != null)
            {
                resultSet.close();
            }
            if(statement != null)
            {
                statement.close();
            }
            if(connection != null)
            {
                connection.close();
            }
        }
     }

JDBC对象介绍

1.JDBC中的主要类(接口)

2.DriverManager

  • getConnection()方法,获得Connection对象:
Class.forName(driverClassName);
Connection connection = DriverManager.getConnection(url,username,password);

注意,上面的代码可能出现两种异常:

  1. ClassNotFoundException:这个异常是在第一句上出现的,出现这个异常有两种可能:
    1. 没有给出mysql的jar包
    2. 把类名打错了,正确类名为:com.mysql.jdbc.Driver
  2. SQLException:这个异常出现在第二句,出现这个异常就是三个问题出错
  • 对于DriverManager.registerDriver()方法了解即可,注册驱动一般用Class.forName()

3.Connection

Connection最重要的方法是获取Statement:

  • Statement statement = connection.createStatement();

  • Statement statement = connection.createStatement(int,int);

    这两个参数是用来确定创建的Statement能生成什么样的结果集

4.Statement

Statement最为重要的方法是:

  • int executeUpdate(String sql);
    • 执行更新操作,即insert,update,delete语句
    • 也可以执行create table,alter table,drop table等语句,但很少用
    • 返回值为操作行数
  • ResultSet executeQuery(String sql);
    • 执行查询操作,即select语句
    • 返回ResultSet,即结果集
  • booleab execute();
    • 可以用来执行executeUpdate和executeQuery两个方法能执行的所有sql语句,即可以执行增删改查所有sql语句
    • 该方法的返回的是一个boolean类型,表示SQL语句是否有结果集
    • 如果用该方法执行更新语句,那么还要调用int getUpdateCount()来获取insert,update,delete语句所影响的行数
    • 如果使用该方法执行查询语句,那么还要调用ResultSet getResult()来获取select语句的查询结果

5.ResultSet之滚动结果

ResultSet内部维护了一个行光标(游标),ResultSet提供了一些了的方法来移动游标

  • 移动游标:

    • 相对移动:
      • boolean previous():把光标向上挪一行
      • boolean next():把光标向下挪一行,返回值表示当前行是否存在
      • boolean relative(int row):相对位移
        • 当row为正数时,表示向下移动row行
        • 为负数时,表示向上移动row行
    • 绝对移动:
      • void beforeFirst():把光标放到第一行前面,这也是光标的默认位置
      • void afterLast():把光标放到最后一行的后面
      • boolean first():把光标放到第一行的位置上,返回值表示调动光标是否成功
      • boolean last():把光标方放最后一行的位置上
      • boolean absolute(int row):绝对位移,把光标移动到指定的行上
  • 判断游标位置:

    • boolean isBeforeFirst():当前光标位置是否在第一行前面
    • boolean isAfterEnd():当前光标位置是否在最后一行后面
    • boolean isFirst():当前光标位置是否在第一行上
    • boolean isLast():当前光标位置是否在最后一行上
  • intgetRow():返回当前光标所有行

    resultSet.last();    //把光标移动到最后一行
    System.out.println(resultSet.getRow());     //得到结果集的行数
    
    

6.ResultSet的特性(是否可滚动、是否敏感、是否可更新)

当使用Connection的createStatement时,已经确定了Statement生成的结果集是什么特性

  • 是否可滚动
  • 是否敏感,结果集数据是否能跟随数据库变化
  • 是否可更新,结果集的更新是否可以影响到数据库

Statement createStatement(); 生成的结果集:不滚动,不敏感,不可更新

Statement createStatement(int resultSetType,int resultSetConcurrency);

第一个参数:

  • ResultSet.TYPE_FORWARD_ONLY:不滚动结果集
  • ResultSet.TYPE_SCROLL_INSENSITIVE:滚动结果集,但结果集数据不会再跟随数据库而变化
  • ResultSet.TYPE_SCROLL_SENSITIVE:滚动结果集,但结果集数据不会再跟随数据库而变化(没有数据库驱动支持他)

第二个参数:

  • CONCUR_READ_ONLY:结果集是只读的,不能通过修改结果集而反向影响数据库
  • CONCUR_UPDATABLE:结果集是可更新的,对结果集的更新可以反向影响数据库
  • 结果集是否支持滚动:
    • 结果集默认是不可滚动的,此时只能使用next()方法移动游标,其他方法都不可使用
    • 结果集是否支持滚动,要从Connection类的createStatement()方法设置

7.ResultSet之获取列数据(获取结果集元数据)

ResultSet提供一个系列的getXxx()方法

  • getInt,getString,getDouble,getDate,getTime,getObject
  • 参数为列序号或者列名称
  • 其中getString和getObject是万能的
  • 注意,索引值是从1开始

ResultSet中对行的操作

  • 得到元数据:resultSet.getMetaDate(),返回值为ResultSetMetaData
  • 获取结果集列数:int getColumnCount()
  • 获取指定列的列名:String getColumnName(int colIndex)
int count = resultSet.getMetaData().getColumnCount();
while(resultSet.next())           //输出结果集
{
      for(int i = 1 ; i <= count ; i++)
      {
            System.out.print(resultSet.getObject(i));
            if(i < count)
            {
                  System.out.print(",");
             }
      }
      System.out.println();
}

PreparedStatement

1.什么是SQL攻击

在需要用户输入的地方,用户输入的是SQL语句的片段,最终用户输入的SQL片段与我们DAO中写的SQL语句合成一个完整的SQL语句。

2.演示SQL攻击

    /**
     * 登录
     * 使用username和password去查询数据
     * 若查出结果集,说明正确,返回true
     * 若查不出结果,说明用户名或密码错误,返回false
     */
    public static boolean login(String username, String password) 
    throws ClassNotFoundException, SQLException 
    {
        /**
         * 一、得到Connection
         * 二、得到Statement
         * 三、得到ResultSet
         * 四、rs.next()返回的是什么,我们就返回什么
         */
        String driverClassName = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/px190731";
        String mysqlUsername = "root";
        String mysqlPassword = "";
        Class.forName(driverClassName);
        Connection connection = 
                   DriverManager.getConnection(url,mysqlUsername,mysqlPassword);
        Statement statement = connection.createStatement();
        
        String sql = "select * from t_user where username='" + username + "' 
                      and password='" + password + "'";
  //select * from t_user where username='a'or 'a'='a' and password='a'or 'a'='a'  sql攻击
        System.out.println(sql);
        ResultSet resultSet = statement.executeQuery(sql);
        return resultSet.next();
    }

3.防止SQL攻击

不允许在可替换之处出现改变SQL语句的符号

4.PreparedStatement是什么?

  • 它是Statement接口的子接口
  • 强大之处:
    1. 防SQL攻击
    2. 提高代码的可读性,可维护性
    3. 提高效率
  • 预处理的原理
    • 服务器的工作:
      • 校验sql语句的语法
      • 编译:把sql语句变成一个与函数相似的东西
      • 执行:调用函数
    • PreparedStatement
      • 前提:连接的数据库必须支持预处理。(现在几乎没有不支持的)
      • 每个preparedStatement都与一个sql模板绑定在一起,先把sql模板给数据库,数据库先进行校验,然后再进行编译,执行时只是把参数传递过去而已
      • 若第二次执行时,就不用再次校验语法,也不用再次编译,直接执行

5.PreparedStatement的使用

用法

  • 如何得到PreparedStatement对象
    • 如何得到PreparedStatement对象
      • 给出SQL模板
      • 调用Connection的PreparedStatement preparedStatement(String sql模板):即创建它时就让它与一条SQL模板绑定
      • 调用preparedStatement的setXXX()系列方法给sql模板中的?赋值
      • 调用preparedStatement的executeUpdate()或executeQuery(),但它的方法都没有参数
String sql = "select * from t_user where username=? and password=?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);

/**
 * 二、为参数赋值
 */
preparedStatement.setString(1,username);  //给第1个问号赋值,值为username
preparedStatement.setString(2,password);  //给第2个问号赋值,值为password

ResultSet resultSet = preparedStatement.executeQuery();   
                                  //调用查询方法,向数据库发送查询语句
resultSet.close();
preparedStatement.clearParameters();    //再次使用时需要把原来的设置清空
preparedStatement.setString(1,username2);
preparedStatement.setString(2.password2);
resultSet = preparedStatement.executeQuery();   

JdbcUtils工具类

1.JdbcUtils的作用

为了在更改连接的数据库时,不用去修改代码,编写一个JdbcUtils类,让他从配置文件中读取配置参数,然后创建连接对象

2.JdbcUtils代码

driverClassName = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/px190731
username = root
password =

public class JdbcUtils 
{
    private static Properties properties = null;

    //只在JbdcUtils类被加载时执行一次
    static
    {
        //给properties进行初始化,即加载bdconfig.properties文件到properties对象中
        try
        {
            InputStream inputStream = JdbcUtils.class.getClassLoader().
                            getResourceAsStream("bdconfig.properties");
            properties = new Properties();
            properties.load(inputStream);
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }

        //加载驱动类
        try 
        {
            Class.forName(properties.getProperty("driverClassName"));
        } 
        catch (ClassNotFoundException e) 
        {
            e.printStackTrace();
        }
    }
    public static Connection getConnection() throws SQLException 
    {
        /**
         * 1.加载配置文件
         * 2.加载驱动类
         * 3.调用DriverManager.getConnection()
         */
        String url = properties.getProperty("url");
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        return DriverManager.getConnection(url,username,password);
    }
}

public static void fun() 
throws SQLException, IOException, ClassNotFoundException 
{
    Connection connection = JdbcUtils.getConnection();
}

UserDao

1.DAO模式

DAO(Data Access Object)模式就是写一个类,把访问数据库的代码封装起来。DAO在数据库与业务逻辑(Service)之间。

  • 实体域,即操作的对象,例如我们操作的表时uesr表,那么就需要先写一个User类
  • DAO模式需要先提供一个DAO借口
  • 然后再提供一个DAO接口的实现类
  • 再编写一个DAO工厂,Service通过工厂来获取DAO实现

2.代码

/**
 * UserDao接口
 */
public interface UserDao 
{
    public void addUser(User form);
    public User findByUsername(String username);
}

/**
 * UserDao的某一实现类
 */
public class UserDaoImpl implements UserDao { ... }

/**
 * 给出一个配置文件,文件中给出UserDao接口的实现类名称
 * 通过工厂中的方法获取实现类的类型,通过反射完成创建对象
 */
cn.itcast.usesrmng.dao.UserDao:'cn.itcast.usesrmng.dao.UserDaoImpl'

/**
 * 通过配置文件得到dao实现类的名称
 * 通过类名称,完成创建类对象(反射完成的)
 */
public class DaoFactory
{
    private static Properties props = null;
    static
    {
        //加载配置文件的内容到props对象中
        try
        {
            InputStream in = DaoFactory.class.getClassLoader().
                             getResourceAsStream("dao.properties");
            props = new Properties();
            props.load(in);
        }
        catch(Exception e)
        {
            throw new RuntimeException(e);
        }
    }
    
    /**
     * 返回一个具体UserDao的实现类对象
     */
    public UserDao getUserDao()
    {
        //得到dao实现类的名称
        String daoClassName = props.
                              getProperty("cn.itcast.usermng.dao.UserDao");
        //通过反射来创建实现类的对象
        try
        {
            Class class = Class.forName(daoClassName);
            return (UserDao)class.newInstance();
        }
        catch(Exception e)
        {
            throw new RuntimeException(e);
        }
    }
}

public class UserService
{
    //把具体实现类的创建,隐藏到工厂中
    private UserDao userDao = DaoFactory.getUserDao();
    ...
}

3.修改登录注册案例,其中dao层为jdbc

public class JdbcUserDao implements UserDao
{
    @Override
    public void addUser(User form) 
    {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try
        {
            /**
             * 一、得到连接
             */
            connection = JdbcUtils.getConnection();
            /**
             * 二、准备sql模板,得到pstmt
             */
            String sql = "INSERT INTO user VALUES(?,?,?,?)";
            preparedStatement = connection.prepareStatement(sql);
            /**
             * 三、为pstmt中的问号赋值
             */
            preparedStatement.setString(1,form.getUsername());
            preparedStatement.setString(2,form.getPassword());
            preparedStatement.setInt(3,form.getAge());
            preparedStatement.setString(4,form.getGender());
            /**
             * 四、执行
             */
            preparedStatement.executeUpdate();
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }
        finally 
        {
            try 
            {
                if (preparedStatement != null) preparedStatement.close();
                if (connection != null) preparedStatement.close();
            }
            catch (SQLException e){}
        }
    }

    @Override
    public User findByUsername(String username) 
    {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try
        {
            connection = JdbcUtils.getConnection();
            String sql = "SELECT * FROM user WHERE username=?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1,username);
            preparedStatement.executeQuery();
            /**
             * 五、把rs转换成User类型,返回
             * ORM -- > 对象关系映射
             */
            if(resultSet == null)
            {
                return null;
            }
            if (resultSet.next())
            {
                User user = new User();
                user.setUsername(resultSet.getString("username"));
                user.setPassword(resultSet.getString("password"));
                user.setAge(resultSet.getInt("age"));
                user.setGender(resultSet.getString("gender"));
                return user;
            }
            else
            {
                return null;
            }
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }
        finally 
        {
            try 
            {
                if (preparedStatement != null) preparedStatement.close();
                if (connection != null) preparedStatement.close();
                if (resultSet != null) resultSet.close();
            }
            catch (SQLException e){}
        }
    }
}

时间类型

1.Java中的时间类型

  • java.sql包下给出三个与数据库相关的日期时间类型,分别是:

    1. Date:表示日期,只有年月日,没有时分秒,会丢失时间
    2. Time:表示时间,只有时分秒,没有年月日,会丢失日期
    3. Timestamp:表示时间戳,有年月日时分秒,以及毫秒

    这三个类都是java.util.Date的子类

  • 数据库类型与java中类型的对应关系

    DATE -> java.sql.Date

    TIME -> java.sql.Time

    TIMESTAMP -> java.sql.Timestamp

2.时间类型相互转换

  • 领域对象(domain)中的所有属性不能出现java.sql包下的东西

    • ResultSet#getDate()返回的是java.sql.Date
    • PreparedStatement#setDate(int,Date),其中第二个参数也是java.sql.Date
  • 时间类型的转换:

    • java.util.Date -> java.sql.Date、Time、Timestamp

      • 使用util的Date转换成毫秒值

      • 使用毫秒值创建sql的Date、Time、Timestamp

      • java.util.Date date = new java.util.Date();
        long l = date.getTime();
        java.sql.Date sqlDate = new java.sql.Date(l);
        
        
    • java.sql.Date、Time、Timestamp -> java.util.Date

      • java.util.Date date = new java.sql.Date();
      • 这一步不需要处理了,因为java.sql.Date是java.util.Date的子类

大数据

1.什么是大数据

所谓大数据,就是大的字节数据,或大的字符数据。

  • 标准SQL中有如下的数据类型来保存大数据类型

    类型长度
    tinyblob2^8-1B(258B)
    blob2^16-1B(64K)
    mediumblob2^24-1B(16M)
    longblob2^32-1B(4G)
    tinyclob2^8-1B(258B)
    clob2^16-1B(64K)
    mediumclob2^24-1B(16M)
    longclob2^32-1B(4G)
  • 但是在mysql中没有提供tinyclob,clob,mediumclob,longclob四种类型,而是使用如下四种类型来处理文本大数据

    类型长度
    tinytext2^8-1B(256B)
    text2^16-1B(64K)
    mediumtext2^24-1B(16M)
    longtext2^32-1B(4G)

2.把mp3保存到数据库中

CREATE TABLE t_music(
    id int PRIMARY KEY AUTO_INCREMENT,
    filename varchar(100),
    data mediumblob
    );

public class Demo 
{
    public static void main(String[] args) throws IOException, SQLException 
    {
        fun1();
        fun2();
    }

    /**
     * 将mp3文件存入数据库
     */
    public static void fun1() throws SQLException, IOException 
    {
        /**
         * 1.得到connection
         * 2.给出sql模板,创建pstmt
         * 3.设置sql模板中的参数
         * 4.执行
         */
        Connection connection = JdbcUtils.getConnection();
        String sql = "INSERT INTO t_music VALUES(?,?,?)";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setInt(1,1);
        preparedStatement.setString(2,"xx.mp3");
        /**
         * 需要得到Blob
         * 1.有文件,目标是Blob
         * 2.先把文件变成byte[]
         * 3.在使用byte[]创建Blob
         */
        //把文件转换成byte[]
        byte[] bytes = IOUtils.toByteArray(new FileInputStream("D:/xx.mp3"));
        //使用byte[]创建Blob
        Blob blob = new SerialBlob(bytes);
        //设置参数
        preparedStatement.setBlob(3,blob);
        preparedStatement.executeUpdate();
    }

    /**
     * 从数据库读取mp3
     */
    public static void fun2() throws SQLException, IOException {
        /**
         * 1.得到connection
         * 2.给出sql模板,创建pstmt
         * 3.设置sql模板中的参数
         * 4.执行
         */
        Connection connection = JdbcUtils.getConnection();
        String sql = "SELECT * FROM t_music";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        ResultSet resultSet = preparedStatement.executeQuery();
        /**
         * 5.获取rs中名为data的列数据
         */
        if(resultSet.next())
        {
            Blob blob = resultSet.getBlob("data");
            /**
             * 把Blob变成硬盘上的文件
             * 1.通过Blob得到输入流对象
             * 2.自己创建输出流对象
             * 3.把输入流的数据写入到输出流中
             */
            InputStream inputStream = blob.getBinaryStream();
            OutputStream outputStream = new FileOutputStream("C:/Download/xx.mp3");
            IOUtils.copy(inputStream,outputStream);
        }
    }
}

注意,需在my.ini中添加配置:max_allowed_packet=10485760,才可存储较大文件

批处理

1.Statement批处理

  • 批处理就是一批一批的处理,而不是一个一个的处理
  • 批处理只针对更新(增、删、改)语句,而与查询语句无关
  • 可以多次调用Statement类的addBatch(String sql)方法,把需要执行的所有SQL语句添加到一个“批”中,然后调用Statement类的executeBatch()方法来执行当前“批”中的语句
    • void addBatch(String sql):添加一条语句到“批”中
    • int[] executeBatch():执行“批”中的所有语句。返回值表示每条语句所影响的行数据
    • void clearBatch():清空“批”中的所有语句
    • 执行了“批”之后,“批”中的SQL语句就会被清空。也就是说,连续两次调用executeBatch()相当于只调用一次。
for(int i=0;i<1000;i++)
{
    String number = "s_10" + i ;
    String name = "stu" + i ;
    int age = 20 + i ;
    String gender = i%2==0?"male":"female";
    String sql = "INSERT INTO stu 
                  VALUES('"+number+"','"+name+"',"+age+",'"+gender"'")";
    statement.addBatch(sql);
}
    statement.executeBatch();

2.PrepareadStatement批处理

PreparedStatement的批处理有所不同,因为每个PreparedStatement对象都绑定一条SQL模板,所以向PreparedStatement中添加的不是SQL语句,而是给“?”赋值

    /**
     * pstmt对象内部有集合
     * 1.用循环疯狂向pstmt中添加sql参数,它有自己的模板,使用一组参数与模板可以匹配出一条sql语句
     * 2.调用它的执行批方法,完成向数据库发送
     */
    public static void fun() throws SQLException 
    {
        /**
         * pstmt:
         * >添加参数到批中
         * >执行批
         */
        Connection connection = JdbcUtils.getConnection();
        String sql = "INSERT INTO t_user VALUES(?,?)";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //添加参数
        for(int i=0;i<1000;i++)
        {
            preparedStatement.setString(1,"stu_"+ i);
            preparedStatement.setString(2,"s" + i);
            preparedStatement.addBatch();   //添加批,这一组参数就保存到集合中了
        }
        long start = System.currentTimeMillis();
        preparedStatement.executeBatch();  //执行批
        long end = System.currentTimeMillis();
        System.out.println(end-start);   //1485->17

    }

附:

MySQL的预编译功能

预编译的好处

  1. 防SQL攻击
  2. 提高代码的可读性,可维护性
  3. 提高效率

MySQL执行预编译

MySQL执行预编译分为3步:

  • 执行预编译语句
  • 设置变量
  • 执行语句

如果需要再次该语句,就不再需要编译语句了

  • 设置变量
  • 执行语句

使用Statement执行预编译

使用Statement执行预编译就是把上面的SQL语句执行一次

        Connection connection = JdbcUtils.getConnetion();
        Statement statement = connection.createStatement();
        statement.executeUpdate("prepare myfun from 'select * from 
                                 users where username=?'");
        statement.executeUpdate("set @str='xxx'");
        ResultSet resultSet = statement.executeQuery("execute myfun using @str");
        while(resultSet.next())
        {
            System.out.print(resultSet.getObject(1)+",");
            System.out.println(resultSet.getObject(2));
        }
        resultSet.close();
        statement.close();
        connection.close();

useServerPrepStmts参数

默认使用PreparedStatement是不能执行预编译的,需要在url中给出useServerPrepStmts=true参数(MySQL Server 4.1之前的版本是不支持预编译的,在5.0.5之后的版本,默认是没有开启预编译功能的)

例如:jdbc:mysql://localhost:3306/users?useServerPrepStmts=true

这样才能保证mysql驱动会先把SQL语句发送给服务器进行预编译,然后在执行executeQuery()时只是把参数发送给服务器

cachePrepStmts参数

当使用不同的PreparedStatement对象来执行相同的SQL语句时,还是会出现编译两次的现象,这是因为驱动没有缓存编译后的函数key,导致二次编译。如果希望缓存编译后有函数的key,那么就要设置cachePrepStmts参数为true。例如:

jdbc:mysql://localhost:3306/users?useServerPrepStmts=true&cachePrepStmts=true

打开批处理

MySQL的批处理也需要通过参数来打开:rewriteBatchedStatements=true

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值