JDBC原生方式连接数据库示例-PreparedStatement方式

 

1、连接数据库时的配置文件

##数据库驱动
driver=com.mysql.jdbc.Driver
##MySQL连接信息
url=jdbc:mysql://127.0.0.1:3306/RUNOOB?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT
##用户名
username=root
##密码
password=admin0001112

2、JDBC原生方式连接数据库工具类

package com.wind.ssm.utils;

import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * @Date: 2020/7/3 17:10
 * @Description: 原生JDBC连接数据库
 */

/**
 * 事务:一组操作,要么都成功,要么都失败
 * ACID原则:
 * (1)原子性:一组操作,要么全都执行完成,要么都不完成。
 * (2)一致性:这里是说最终一致性,总数保持不变。
 * (3)隔离性:多个事务之间相互隔离,互不干扰(多个进程互不干扰)。
 * (4)持久性:事务一旦提交之后,就持久化到数据库了,不能够再去修改。
 * 隔离性的级别会带来一些问题:
 * (1)脏读:一个事务读取到了另一个事务还没有提交的数据
 * (2)不可重复度:在同一个事务内,重复读取表中的某个数据,前后两次读取到的数据不一致(数据被更新了,是update操作)。
 * (3)幻读:在一个事务内,读取到了另一个事务插入的数据,导致前后两次读出来的结果不一致(记录数增加了或者减少了,是insert或delete操作)。
 */

@Component
public class JdbcUtils {

    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;

    static {
        try {
            InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(inputStream);
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            //1.加载数据库驱动(只需要加载一次即可)
            Class.forName(driver);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //2.获取数据库连接对象
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }

    //3.释放数据库连接资源
    public static void release(ResultSet resultSet, Statement statement, Connection connection) {
        try {
            if (resultSet != null && !resultSet.isClosed()) {
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            if (statement != null && !statement.isClosed()) {
                statement.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            if (connection != null && !connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

3、测试PreparedStatement对象

package com.wind.ssm.java.test;

import com.wind.ssm.entity.StudentEntity;
import com.wind.ssm.utils.JdbcUtils;
import org.springframework.stereotype.Component;

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


/**
 * @Date: 2020/7/3 18:40
 * @Description:
 */
@Component
public class JdbcUtilsTest2 {

    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null; //是Statement的子类
        ResultSet resultSet = null;

        try {
            //1.获取数据库连接
            connection = JdbcUtils.getConnection();

            //2.【新增SQL】参数使用占位符而不是直接使用实际值hardcode
            String insertSql = "insert into RUN_Student(Name,ClassId,Status,Addtime,UpdateTime) values(?,?,?,?,?)";

            //3.预编译SQL,此时并不执行SQL
            preparedStatement = connection.prepareStatement(insertSql);

            //4.给SQL中的占位符设置具体值
            preparedStatement.setString(1, "南京市");
            preparedStatement.setInt(2, 2);
            preparedStatement.setInt(3, 1);
            preparedStatement.setDate(4, new java.sql.Date(new Date().getTime()));
            preparedStatement.setDate(5, new java.sql.Date(new Date().getTime()));

            //4.执行SQL,并且获取结果。"查"executeQuery(),"增删改"executeUpdate()。
            int insertRes = preparedStatement.executeUpdate();

            //5.处理结果
            if (insertRes > 0) {
                System.out.println("插入DB记录成功");
            }


            //【更新SQL】
            String updateSql = "update RUN_Student set name = ? where id = ?";
            preparedStatement = connection.prepareStatement(updateSql);
            preparedStatement.setString(1, "南京市玄武区");
            preparedStatement.setInt(2, 19);
            int updateRes = preparedStatement.executeUpdate();
            if (updateRes > 0) {
                System.out.println("更新DB记录成功");
            }

            //【删除SQL】
            String deleteSql = "delete from RUN_Student where id = ?";
            preparedStatement = connection.prepareStatement(deleteSql);
            preparedStatement.setInt(1, 19);
            int deleteRes = preparedStatement.executeUpdate();
            if (deleteRes > 0) {
                System.out.println("删除DB记录成功");
            }

            //【查询SQL】
            String selectSql = "select * from RUN_Student where id in (?,?)";
            preparedStatement = connection.prepareStatement(selectSql);
            preparedStatement.setInt(1, 1);
            preparedStatement.setInt(2, 2);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                StudentEntity entity = new StudentEntity();
                entity.setId(resultSet.getInt("Id"));
                entity.setName(resultSet.getString("Name"));
                entity.setClassId(resultSet.getInt("ClassId"));
                entity.setStatus(resultSet.getInt("Status"));
                entity.setAddTime(resultSet.getDate("AddTime"));
                entity.setUpdateTime(resultSet.getDate("UpdateTime"));
                System.out.println("查询DB记录成功=entity=" + entity.toString());
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.release(resultSet, preparedStatement, connection);
        }


    }

}

 

4、PreparedStatement对象与Statement对象的区别

本人的几点浅见,各位大大不喜勿喷(前面两篇博客已经做了对应的测试)

先说下这俩到底是干啥的吧。其实这俩干的活儿都一样,就是创建了一个执行对象,然后通过对象调用executeQuery方法来执行sql语句。说是CreateStatement和PrepareStatement的区别,但其实说的就是Statement和PrepareStatement的区别,相信大家在网上已经看到过不少这方面的资料和博客,我在此处提几点,大家看到过的,就当是重新记忆,没看到就当补充~下面开始谈谈它们的区别。

(1)最明显的区别,就是执行的sql语句格式不同(是否存在占位符)。我们放两段代码来看看他们的区别:
代码背景:我们有一个数据库,里面有一个user表,有username, userpwd两列。我们要查出这两列的数据。

这是使用CreateStatement方法创建了stmt 对象,再通过他查询的一部分语句片段。

String sql = "select * from users where  username= '"+username+"' and userpwd='"+userpwd+"'";  
stmt = conn.createStatement();  
rs = stmt.executeQuery(sql);  

而下面则是使用了PrepareStatement方法创建了pstmt 对象,再通过这个对象查询的一部分语句片段。

String sql = "select * from users where  username=? and userpwd=?";  
pstmt = conn.prepareStatement(sql);  
pstmt.setString(1, username);  
pstmt.setString(2, userpwd);  
rs = pstmt.executeQuery();  

相信写到这,大家很多人就能看出来了,原来PrepareStatement跟Statement的主要区别:就是把上面sql语句中的变量抽出来了。这就是我要说的第一大优点,PrepareStatement可以提高代码的可读性。什么?你没觉得这有什么可以提高可读性的?那好,咱来看看下面这两段代码,看完你再说话。

代码背景:我们有一个数据库,里面有一个book表,有bookid, bookname, bookauthor, booksort, bookprice五列。我们要向这个表中添加一部分数据。

【Statement版】

String sql = "insert into book (bookid,bookname,bookauthor,booksort,bookprice) values ('"+var1+"',  
                                '"+var2+"',"+var3+",'"+var4+","+var5+"')";  
stmt = conn.createStatement();  
rs = stmt.executeUpdate(sql);  

【ParperStatement版】

String sql = "insert into book (bookid,bookname,bookauthor,booksort,bookprice) values (?,?,?,?,?)";  
pstmt = conn.prepareStatement(sql);  
pstmt.setString(1,var1);  
pstmt.setString(2,var2);  
pstmt.setString(3,var3);  
pstmt.setString(4,var4);  
pstmt.setString(5,var5);  
pstmt.executeUpdate();  

怎么样,反正我打这行代码的时候,整个引号逗号就给我刺激懵了。
(2)下面说说第二点优点,ParperStatement提高了代码的灵活性和执行效率。
PrepareStatement接口是Statement接口的子接口,它继承了Statement接口的所有功能。它主要是拿来解决我们使用Statement对象多次执行同一个SQL语句的效率问题的。ParperStatement接口的机制是在数据库支持预编译的情况下 预先将SQL语句编译,当多次执行这条SQL语句时,可以直接执行编译好的SQL语句,这样就大大提高了程序的灵活性和执行效率。

(3)最后但也是最重要的一个大大的比Statement好的优点,那就是安全,可以防止SQL注入!
你说啥?这还关安全啥事儿,那我给你一行代码,你来给我说说这是干嘛的。

String sql = "select * from user where username= '"+varname+"' and userpwd='"+varpasswd+"'";  
stmt = conn.createStatement();  
rs = stmt.executeUpdate(sql);  

这是验证用户名密码的,对吧。但要是我们把 'or '1' = 1' 当作密码传进去,你猜猜会发生啥。

select * from user where username = 'user' and userpwd = '' or '1' = '1';  

发现了吧!这是个永真式,因为1永远等于1。所以不管怎样都能获取到权限。哇。这就坏咯!这还不是最坏的,你再看!

String sql = "select * from user where username= '"+varname+"' and userpwd='"+varpasswd+"'";  
stmt = conn.createStatement();  
rs = stmt.executeUpdate(sql);  


依旧是这行代码。这次我们把 'or '1' = 1';drop table book;  当成密码传进去。哇!又坏了!这次直接把表给删了。但是,你如果用PrepareStatement的话就不会出现这种问题,因为它是把入参直接当做字符串来处理的,你传入的这些数据根本不会跟原来的数据有任何的交集,也不会发生这些问题。文章写到这就结束了。

转自:https://blog.csdn.net/u011161786/article/details/48394751

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章