18【PreparedStatement接口详细解析】_写出preparedstatement接口的常用方法,及其描述(1)

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;

/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo08_Login {

public static void main(String[] args) throws Exception {
    //让用户输入用户名和密码
    Scanner scanner = new Scanner(System.in);

    System.out.println("请输入用户名:");
    String name = scanner.nextLine();

    System.out.println("请输入密码:");
    String password = scanner.nextLine();

    // 使用工具类获取连接
    Connection conn = JdbcUtils.getConnection();

    //创建语句对象
    Statement stmt = conn.createStatement();

    //执行DQL查询,通过字符串拼接的方式得到SQL语句
    String sql = "select \* from user where name='" + name + "' and password='" + password + "'";

    //得到结果集
    ResultSet rs = stmt.executeQuery(sql);

    System.out.println("输入的SQL语句: " + sql);

    //判断是否有记录
    if (rs.next()) {
        System.out.println("登录成功," + name);
    } else {
        System.out.println("登录失败");
    }

    // 使用工具类释放资源
    JdbcUtils.close(conn, stmt, rs);
}

}


* 失败:


![在这里插入图片描述](https://img-blog.csdnimg.cn/6297b350bbe34c7fb360545d849d90e9.png#pic_center)


* 成功:


![在这里插入图片描述](https://img-blog.csdnimg.cn/f314add3da054b06a135e2da1f26d421.png#pic_center)


##### 3)问题引入


* 当我们输入以下密码,我们发现我们账号和密码都不对竟然登录成功了



abc’ or ‘1’='1


![在这里插入图片描述](https://img-blog.csdnimg.cn/b232d06b9b244bfd930b91c49152fd90.png#pic_center)


* 问题分析:



select * from user where name=‘admin’ and password=‘abc’ or ‘1’=‘1’;

select * from user where false and false or true;

select * from user where false or true;

– 查询所有的记录
select * from user where true;


我们让用户输入的密码和SQL语句进行字符串拼接。**用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义**,以上问题称为SQL注入。要解决SQL注入就不能让用户输入的密码和我们的SQL语句进行简单的字符串拼接。


#### 1.1.2 PreparedStatement 解决SQL注入


* PreparedStatement接口中的方法:




| 方法 | 描述 |
| --- | --- |
| boolean execute() | 执行任何的SQL语句,如果SQL执行的SQL语句有返回值,则返回true,否则返回false |
| int executeUpdate() | 执行DML,增删改的操作。Tips:没有参数。 SQL语句在创建PreparedStatement对象的时候就已经提供了,所以执行的时候没有SQL语句 返回:影响的行数 |
| ResultSet executeQuery() | 执行DQL,查询操作 返回:结果集 |


* 案例代码:



package com.dfbz.demo;

import com.dfbz.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;

/**
* @author lscl
* @version 1.0
* @intro: 使用PreparedStatement改写登录程序
*/
public class Demo09_Login_PreparedStatement {

public static void main(String[] args) {
    //让用户输入用户名和密码
    Scanner scanner = new Scanner(System.in);
    System.out.println("请输入用户名:");
    String name = scanner.nextLine();

    System.out.println("请输入密码:");
    String password = scanner.nextLine();

    //声明对象
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;

    try {
        //访问数据库,创建连接
        conn = JdbcUtils.getConnection();

        //创建预编译的SQL语句
        ps = conn.prepareStatement("select \* from user where name=? and password=?");

        //替换占位符
        ps.setString(1, name);
        ps.setString(2, password);

        //执行SQL语句,查询
        rs = ps.executeQuery();
        if (rs.next()) {
            System.out.println("登录成功");
        } else {
            System.out.println("登录失败");
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        JdbcUtils.close(conn, ps, rs);
    }
}

}


执行效果:


![在这里插入图片描述](https://img-blog.csdnimg.cn/4d53eccce03344708bd8446698f7792d.png#pic_center)


#### 1.1.3 PreparedStatement 简介


PreparedStatement 是 Statement 的子类,也能执行Statement之前的所有操作,其中最主要的功能就是提供了**占位符传参处理**、**预编译等功能**;我们实际开发中PreparedStatement会使用的更多;


* PreparedStatement 占位符参数处理:




| PreparedStatement的方法 | 说明 |
| --- | --- |
| void setXxx(int 参数1,参数2) Xxx数据类型 | 替换SQL语句中的占位符 参数1: 占位符的位置,第几个位置,从1开始 参数2: 用来替换占位符的真实的值 |
|  |  |
| void setDouble(int parameterIndex, double x) | 将指定参数设置为给定 Java double 值。 |
| void setFloat(int parameterIndex, float x) | 将指定参数设置为给定 Java REAL 值。 |
| void setInt(int parameterIndex, int x) | 将指定参数设置为给定 Java int 值。 |
| void setLong(int parameterIndex, long x) | 将指定参数设置为给定 Java long 值。 |
| void setObject(int parameterIndex, Object x) | 使用给定对象设置指定参数的值。 |
| void setString(int parameterIndex, String x) | 将指定参数设置为给定 Java String 值。 |


##### 1)执行DML操作


* 示例代码:



package com.dfbz.demo;

import com.dfbz.utils.JdbcUtils;
import org.junit.Test;

import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
* @author lscl
* @version 1.0
* @intro: PreparedStatement实现增删改操作
*/
public class Demo10_PreparedStatement执行DML {

/\*

preparedStatement向学生表中添加一条记录
*/
@Test
public void add() throws SQLException {
//创建连接对象
Connection conn = JdbcUtils.getConnection();
//创建预编译语句对象
PreparedStatement ps = conn.prepareStatement(“INSERT into student VALUES (null,?,?,?,?)”);

    //设置参数替换占位符
    ps.setString(1, "小陈");
    ps.setString(2, "女");
    ps.setDate(3, Date.valueOf("1987-02-10"));   //静态方法,将一个字符串转成日期类型
    ps.setString(4, "安徽宣城");

    //执行SQL语句
    int i = ps.executeUpdate();

    //关闭资源
    JdbcUtils.close(conn, ps);

    //输出影响的行数
    System.out.println("影响了" + i + "行");
}

/\*

将id为1的用户:
1)姓名更新为"明明"
2)性别换成女(改)
3)出生日期改为"1994-03-19"
4)地址改为"陕西汉中"
*/
@Test
public void update() throws SQLException {
//创建连接对象
Connection conn = JdbcUtils.getConnection();

    //创建预编译语句对象
    PreparedStatement ps = conn.prepareStatement("UPDATE student set name=?, gender=?,birthday=?,address=? where id=?");

    //设置参数替换占位符
    ps.setString(1, "明明");
    ps.setString(2, "女");
    ps.setDate(3, Date.valueOf("1994-03-19"));
    ps.setString(4, "陕西汉中");
    ps.setInt(5, 1);

    //执行SQL语句
    int i = ps.executeUpdate();
    //关闭资源
    JdbcUtils.close(conn, ps);
    //输出影响的行数
    System.out.println("影响了" + i + "行");
}

/\*\*

* 将id为4的学员删除
*/
@Test
public void delete() throws SQLException {
//创建连接对象
Connection conn = JdbcUtils.getConnection();
//创建预编译语句对象
PreparedStatement ps = conn.prepareStatement(“DELETE FROM student where id=?”);
//设置参数替换占位符
ps.setInt(1, 4);
//执行SQL语句
int i = ps.executeUpdate();
//关闭资源
JdbcUtils.close(conn, ps);
//输出影响的行数
System.out.println(“影响了” + i + “行”);
}
}


##### 2)执行DQL操作


* 表与类之间的关系


![在这里插入图片描述](https://img-blog.csdnimg.cn/3754d6da79ce432ea97c7ab5a19d9624.png#pic_center)


在实际开发中,我们查询出来的一条数据刚好对应的是Java中的一个对象,一张表对应Java中的一个类;当我们查询到一条数据时,应该创建一个对应的对象来接收这些数据;


【案例】:使用PreparedStatement查询id为1的一条学生数据,封装成一个学生Student对象


学生对象:



package com.dfbz.entity;

import java.util.Date;

/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Student {
private Integer id;
private String name;
private String gender;
private Date birthday;
private String address;

@Override
public String toString() {
    return "Student{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", gender='" + gender + '\'' +
            ", birthday=" + birthday +
            ", address='" + address + '\'' +
            '}';
}

public Student() {
}

public Student(Integer id, String name, String gender, Date birthday, String address) {
    this.id = id;
    this.name = name;
    this.gender = gender;
    this.birthday = birthday;
    this.address = address;
}

public Integer getId() {
    return id;
}

public void setId(Integer id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getGender() {
    return gender;
}

public void setGender(String gender) {
    this.gender = gender;
}

public Date getBirthday() {
    return birthday;
}

public void setBirthday(Date birthday) {
    this.birthday = birthday;
}

public String getAddress() {
    return address;
}

public void setAddress(String address) {
    this.address = address;
}

}


* 代码:



package com.dfbz.demo;

import com.dfbz.entity.Student;
import com.dfbz.utils.JdbcUtils;
import org.junit.Test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo11_PreparedStatement执行DQL {

/\*\*

* 查询id为1的一条记录封装成一个学生对象
*
* @throws SQLException
*/
@Test
public void query() throws SQLException {
//得到连接对象
Connection conn = JdbcUtils.getConnection();
//得到预编译语句对象
PreparedStatement ps = conn.prepareStatement(“select * from student where id=?”);
//设置占位符
ps.setInt(1, 1);
//执行SQL语句
ResultSet rs = ps.executeQuery();
//创建一个学生对象
Student student = new Student();
//封装成一个学生对象
if (rs.next()) {
student.setId(rs.getInt(“id”)); //从结果集中取出值,封装给学生对象
student.setName(rs.getString(“name”));
student.setGender(rs.getString(“gender”));
student.setBirthday(rs.getDate(“birthday”));
student.setAddress(rs.getString(“address”));
}
//关闭连接对象
JdbcUtils.close(conn, ps, rs);
System.out.println(student);
}
}




---


【需求】: 查询所有的学生类,封装成List<Student>返回


* 开发步骤:


1. 创建一个集合用于封装所有的记录
2. 得到连接对象
3. 得到语句对象,SQL语句没有占位符
4. 每次循环封装一个学生对象
5. 把数据放到集合中
6. 关闭连接
7. 使用数据,循环输出学生对象


* 代码:



/**
* 查询所有学生
*
* @throws SQLException
*/
@Test
public void findAll() throws SQLException {
//创建集合
List students = new ArrayList<>();
//创建连接对象
Connection conn = JdbcUtils.getConnection();
//创建预编译语句对象
PreparedStatement ps = conn.prepareStatement(“select * from student”);
//查询
ResultSet rs = ps.executeQuery();
//使用while循环
while (rs.next()) {
//每次循环创建一个学生对象
Student student = new Student();
student.setId(rs.getInt(“id”)); //从结果集中取出值,封装给学生对象
student.setName(rs.getString(“name”));
student.setGender(rs.getString(“gender”));
student.setBirthday(rs.getDate(“birthday”));
student.setAddress(rs.getString(“address”));
//封装好的学生对象放在集合中
students.add(student);
}
//关闭连接
JdbcUtils.close(conn, ps, rs);

for (Student student : students) {
    System.out.println(student);
}

}


##### 3)PreparesStatement 使用小结




| 功能 | 实现 |
| --- | --- |
| 得到PreparesStatement对象的方法 | 通过连接对象创建 |
| 设置占位符的方法 | setXxx(占位符位置, 真实值) |
| 执行DML的方法 | executeUpdate() |
| 执行DQL的方法 | executeQuery() |


#### 1.1.4 PreparedStatement 参数处理原理


我们之前了解到:Statement在参数拼接上会出现SQL注入问题,我们的解决方案是将参数的传递换成了PreparedStatement的占位符,以此来解决SQL注入问题,那么PreparedStatement是如何解决SQL注入的呢?


我们来翻一翻PreparedStatement的源码:


* 1)打开`PreparedStatement`类的`setString()`方法:


![在这里插入图片描述](https://img-blog.csdnimg.cn/3fe5e33c0be34804a9edf5501d01d451.png#pic_center)


setString方法全部源码:



public void setString(int parameterIndex, String x) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
// if the passed string is null, then set this column to null
if (x == null) {
setNull(parameterIndex, Types.CHAR);
} else {
checkClosed();

            int stringLength = x.length();

            if (this.connection.isNoBackslashEscapesSet()) {
                // Scan for any nasty chars

                boolean needsHexEscape = isEscapeNeededForString(x, stringLength);

                if (!needsHexEscape) {
                    byte[] parameterAsBytes = null;

                    StringBuilder quotedString = new StringBuilder(x.length() + 2);
                    quotedString.append('\'');
                    quotedString.append(x);
                    quotedString.append('\'');

                    if (!this.isLoadDataQuery) {
                        parameterAsBytes = StringUtils.getBytes(quotedString.toString(), this.charConverter, this.charEncoding,
                                this.connection.getServerCharset(), this.connection.parserKnowsUnicode(), getExceptionInterceptor());
                    } else {
                        // Send with platform character encoding
                        parameterAsBytes = StringUtils.getBytes(quotedString.toString());
                    }

                    setInternal(parameterIndex, parameterAsBytes);
                } else {
                    byte[] parameterAsBytes = null;

                    if (!this.isLoadDataQuery) {
                        parameterAsBytes = StringUtils.getBytes(x, this.charConverter, this.charEncoding, this.connection.getServerCharset(),
                                this.connection.parserKnowsUnicode(), getExceptionInterceptor());
                    } else {
                        // Send with platform character encoding
                        parameterAsBytes = StringUtils.getBytes(x);
                    }

                    setBytes(parameterIndex, parameterAsBytes);
                }

                return;
            }

            String parameterAsString = x;
            boolean needsQuoted = true;

            if (this.isLoadDataQuery || isEscapeNeededForString(x, stringLength)) {
                needsQuoted = false; // saves an allocation later

                StringBuilder buf = new StringBuilder((int) (x.length() \* 1.1));

                buf.append('\'');

                //
                // Note: buf.append(char) is \_faster\_ than appending in blocks, because the block append requires a System.arraycopy().... go figure...
                //

                // 核心部分
                for (int i = 0; i < stringLength; ++i) {
                    char c = x.charAt(i);

                    switch (c) {
                        case 0: /\* Must be escaped for 'mysql' \*/
                            buf.append('\\');
                            buf.append('0');

                            break;

                        case '\n': /\* Must be escaped for logs \*/
                            buf.append('\\');
                            buf.append('n');

                            break;

                        case '\r':
                            buf.append('\\');
                            buf.append('r');

                            break;

                        case '\\':
                            buf.append('\\');
                            buf.append('\\');

                            break;

                        case '\'':
                            buf.append('\\');
                            buf.append('\'');

                            break;

                        case '"': /\* Better safe than sorry \*/
                            if (this.usingAnsiMode) {
                                buf.append('\\');
                            }

                            buf.append('"');

                            break;

                        case '\032': /\* This gives problems on Win32 \*/
                            buf.append('\\');
                            buf.append('Z');

                            break;

                        case '\u00a5':
                        case '\u20a9':
                            // escape characters interpreted as backslash by mysql
                            if (this.charsetEncoder != null) {
                                CharBuffer cbuf = CharBuffer.allocate(1);
                                ByteBuffer bbuf = ByteBuffer.allocate(1);
                                cbuf.put(c);
                                cbuf.position(0);
                                this.charsetEncoder.encode(cbuf, bbuf, true);
                                if (bbuf.get(0) == '\\') {
                                    buf.append('\\');
                                }
                            }
                            buf.append(c);
                            break;

                        default:
                            buf.append(c);
                    }
                }

                buf.append('\'');

                parameterAsString = buf.toString();
            }

            byte[] parameterAsBytes = null;

            if (!this.isLoadDataQuery) {
                if (needsQuoted) {
                    parameterAsBytes = StringUtils.getBytesWrapped(parameterAsString, '\'', '\'', this.charConverter, this.charEncoding,
                            this.connection.getServerCharset(), this.connection.parserKnowsUnicode(), getExceptionInterceptor());
                } else {
                    parameterAsBytes = StringUtils.getBytes(parameterAsString, this.charConverter, this.charEncoding, this.connection.getServerCharset(),
                            this.connection.parserKnowsUnicode(), getExceptionInterceptor());
                }
            } else {
                // Send with platform character encoding
                parameterAsBytes = StringUtils.getBytes(parameterAsString);
            }

            setInternal(parameterIndex, parameterAsBytes);

            this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.VARCHAR;
        }
    }
}

![在这里插入图片描述](https://img-blog.csdnimg.cn/19dc114cdfbb4193b1f1a3d36ea1b56c.png#pic_center)


最终传递的参数如下:


![在这里插入图片描述](https://img-blog.csdnimg.cn/1a2bd2d0cb2b4c11b7d9f83e7559058e.png#pic_center)


咱们在数据库中执行如下SQL语句(肯定是查询不到数据的):



select * from user where username = ‘abc’ or 1=1 – ’


![在这里插入图片描述](https://img-blog.csdnimg.cn/414afbd53d754a9ea3fdcfee3945eb29.png#pic_center)


如果把`PreparedStatement`加的那根`"/"`去掉呢?我们执行SQL试试:



select * from user where username = ‘abc’ or 1=1 – ’


![在这里插入图片描述](https://img-blog.csdnimg.cn/0734d7283e334b1fa8ccd6becfabdc47.png#pic_center)


小结:PreparedStatement之所以能够解决SQL注入的问题是因为PreparedStatement对占位符中的传递参数进行了特殊校验,如果发现一些特殊字符将会进行转义,达到不参与SQL语句生成的目的;


#### 1.1.5 PreparedStatement 的预编译


PreparedStatement 提供了量大功能,第一个就是占位符参数处理,另一个就是SQL语句的预编译了;那什么是预编译呢?


##### 1)预编译简介


通常我们发送一条SQL语句给MySQL服务器时,MySQL服务器每次都需要对这条SQL语句进行**校验、解析**等操作。但是有很多情况下,我们的一条SQL语句可能需要反复的执行,而SQL语句也只可能传递的参数不一样,类似于这样的SQL语句如果每次都需要进行校验、解析等操作,未免太过于浪费性能了,因此MySQL提出了SQL语句的预编译。



> 
> **Tips:预编译的功能是MySQL的,PreparedStatement 只是开启MySQL的预编译功能;**
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/a0aaa8d237bc43fd9a44a5248dcb9db2.png#pic_center)


所谓预编译就是将一些灵活的参数值以占位符?的形式给代替掉,我们把参数值给抽取出来,把SQL语句进行模板化。让MySQL服务器执行相同的SQL语句时,不需要在校验、解析SQL语句上面花费重复的时间,因此就是来**提高我们的查询速度的;**


##### 2)开启预编译


**PreparedStatement的预编译功能默认是关闭的**,要让其生效,必须在JDBC连接的URL设置`useServerPrepStmts=true`,让其打开。


* 示例:



jdbc:mysql://localhost:3306/test?&useServerPrepStmts=true


* 预编译性能测试:



package com.dfbz.demo;

import org.junit.Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
* @author lscl
* @version 1.0
* @intro: 预编译性能测试
*/
public class Demo13_PreparedStatement_预编译 {

/\*\*

* 预编译性能测试
* @throws SQLException
*/
@Test
public void test1() throws SQLException {

    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useServerPrepStmts=true", "root", "admin");
    
    long startTime = System.currentTimeMillis();

    PreparedStatement ps =  connection.prepareStatement("select \* from user where id = ?");
<think>嗯,我现在需要了解如何使用PreparedStatement来给数据表插入信息,并且要有代码实现。首先,我得回忆一下JDBC的基本流程。记得JDBC操作数据库一般分为几个步骤:加载驱动、建立连接、创建语句对象、执行SQL、处理结果、关闭资源。 PreparedStatement和普通的Statement有什么区别呢?对了,PreparedStatement是预编译的,可以防止SQL注入,并且执行效率更高,特别是多次执行同一语句时。所以插入数据应该用PreparedStatement更好。 那具体步骤应该是怎样的呢?首先,需要连接数据库,可能需要Class.forName加载驱动,不过新版本的JDBC好像不需要显式加载了。然后通过DriverManager获取Connection对象。接下来,要写插入的SQL语句,这里应该用INSERT INTO语句。使用PreparedStatement的话,SQL里的参数用问号占位符代替,比如INSERT INTO table (col1, col2) VALUES (?, ?)。 然后,创建PreparedStatement对象,通过connection.prepareStatement(sql)。之后,设置参数,用setXxx方法,比如setString(1, value)对应第一个问号的位置。设置完所有参数后,执行更新,应该用executeUpdate()方法,这个方法返回影响的行数,可以用来判断是否成功。 最后,记得关闭资源,PreparedStatement和Connection都要关闭,应该在finally块里或者用try-with-resources来自动关闭,避免资源泄露。 那代码的大致结构应该是这样的:先导入必要的包,比如java.sql包里的那些类。然后主函数里建立连接,创建PreparedStatement,设置参数,执行,处理结果,关闭资源。需要注意的是异常处理,要捕获SQLException。 比如,假设有一个用户表,有id、name、email三个字段,插入数据的SQL就是INSERT INTO users (id, name, email) VALUES (?, ?, ?)。然后给每个问号设置对应的值,比如setInt(1, 1),setString(2, "张三"),setString(3, "zhangsan@example.com")。 另外,关于连接数据库的URL、用户名和密码,这些需要根据实际的数据库配置来填写,比如MySQL的jdbc:mysql://localhost:3306/mydb,以及用户名和密码。 需要注意的是,使用try-with-resources可以自动关闭Connection和PreparedStatement,这样代码更简洁,也不容易忘记关闭资源。Java 7及以上支持这个特性。 那可能的异常情况有哪些呢?比如数据库连接失败,SQL语法错误,参数类型不匹配等,这些都需要在catch块里处理,打印错误信息或者抛出运行时异常。 总结一下步骤:加载驱动(可能省略)、获取连接、准备SQL、创建PreparedStatement、设置参数、执行更新、处理结果、关闭资源。代码结构大致如下: try (Connection conn = DriverManager.getConnection(url, user, password); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, id); pstmt.setString(2, name); pstmt.setString(3, email); int rows = pstmt.executeUpdate(); System.out.println("插入了 " + rows + " 行数据。"); } catch (SQLException e) { e.printStackTrace(); } 这样应该就可以实现插入数据的功能了。需要确保数据库驱动在类路径中,比如MySQL的connector JAR包。还有,处理自增主键的话,可能需要指定生成主键的方式,但这里可能不需要,用户的问题只是普通的插入。 有没有可能漏掉什么步骤?比如自动提交的问题,默认Connection是自动提交的,所以每次executeUpdate会立即提交。如果是批量操作可能需要手动管理事务,但这里单次插入不需要。 总之,这样的代码应该可以正确使用PreparedStatement插入数据到数据库表中。</think>以下是使用 `PreparedStatement` 向数据库表插入数据的详细步骤及代码实现: --- ### **1. 准备工作** - 确保已导入JDBC驱动包(如MySQL的`mysql-connector-java`)。 - 数据库连接信息:URL、用户名、密码。 --- ### **2. 代码实现** ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; public class InsertExample { public static void main(String[] args) { // 数据库连接信息 String url = "jdbc:mysql://localhost:3306/mydb"; // 替换为实际数据库URL String user = "root"; // 替换为数据库用户名 String password = "123456"; // 替换为数据库密码 // SQL模板(使用占位符?) String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)"; // 使用try-with-resources自动关闭资源 try (Connection conn = DriverManager.getConnection(url, user, password); PreparedStatement pstmt = conn.prepareStatement(sql)) { // 设置参数(索引从1开始) pstmt.setInt(1, 1001); // 替换为实际id pstmt.setString(2, "张三"); // 替换为实际姓名 pstmt.setString(3, "zhangsan@example.com"); // 替换为实际邮箱 // 执行插入操作 int affectedRows = pstmt.executeUpdate(); // 判断是否成功 if (affectedRows > 0) { System.out.println("数据插入成功!影响行数:" + affectedRows); } else { System.out.println("插入失败。"); } } catch (SQLException e) { e.printStackTrace(); } } } ``` --- ### **3. 关键点说明** 1. **防止SQL注入** `PreparedStatement` 通过预编译和参数绑定,避免用户输入被解析SQL代码。 2. **参数设置方法** - 使用 `setXxx(int index, Xxx value)` 方法设置参数,如: - `setInt(1, 1001)`:第1个`?`设为整数。 - `setString(2, "张三")`:第2个`?`设为字符串。 3. **执行更新** - `executeUpdate()` 返回受影响的行数,用于判断操作是否生效。 4. **资源管理** - `try-with-resources` 自动关闭 `Connection` 和 `PreparedStatement`,避免内存泄漏。 --- ### **4. 常见问题** - **驱动加载问题**: 若使用旧版本JDBC,需手动加载驱动: ```java Class.forName("com.mysql.cj.jdbc.Driver"); ``` - **自增主键处理**: 若主键自增,需在SQL中排除该字段,并通过参数指定生成方式: ```java String sql = "INSERT INTO users (name, email) VALUES (?, ?)"; PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); ``` --- 通过上述步骤,即可安全高效地向数据库插入数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值