MySQL day05 JDBC、SQL注入、PreparedStatement类、连接池、工具类

JDBC

什么是JDBC

Java Data Base Connectivity:Java数据库连接
JDBC是Java访问数据库的标准规范

JDBC的作用

通过JDBC可以让Java程序操作数据库

JDBC四个核心对象

这几个类都是在java.sql包中
DriverManager: 用于注册驱动
Connection: 表示数据库的连接
Statement: 执行SQL语句的对象
ResultSet: 结果集或一张虚拟表

在这里插入图片描述
JDBC的使用步骤

1、注册驱动
2、获取数据库连接
3、获取执SQL语句对象
4、执行SQL语句并返回结果
5、(处理结果 )
6、释放资源

1、注册驱动
在这里插入图片描述

public class Demo02 {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        // 向JDBC注册MySQL驱动.表示我们将来通过JDBC操作MySQL数据库
        // 我们自己注册一次驱动, new Driver()触发Driver类中的静态代码块执行,也会注册一次(驱动重复注册)
        //DriverManager.registerDriver(new Driver());

        // 我们只要触发com.mysql.jdbc包中的Driver类的静态代码块执行即可注册驱动
        // 标准方式
        Class.forName("com.mysql.jdbc.Driver");
    }
}

在这里插入图片描述

注意:

1、DriverManager.registerDriver(new Driver())
会执行2次驱动注册,如果只是走静态代码块,可以使用 Class.forName(“com.mysql.jdbc.Driver”);
2、获取class类的三种方式的区别可以参考反射获取三种class类的区别

2、获取Connection连接

在这里插入图片描述
3、获取Statement对象
在这里插入图片描述

参数说明

String url:连接数据库的URL,用于说明连接数据库的位置
String user:数据库的账号
String password:数据库的密码
连接数据库的URL地址格式:协议名:子协议://服务器名或IP地址:端口号/数据库名

MySQL写法:jdbc:mysql://localhost:3306/day20
如果是本地服务器,端口号是默认的3306,则可以简写:jdbc:mysql:///day20

增删改等语句使用executeUpdate()
在这里插入图片描述

public class Demo03 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");

        // 2.获取连接
        Connection conn = DriverManager.getConnection("jdbc:mysql:///day20", "root", "root");
        System.out.println("conn = " + conn); // com.mysql.jdbc.JDBC4Connection@1e7c7811

        // 3.获取Statement执行对象
        Statement stmt = conn.createStatement();
        System.out.println("stmt = " + stmt); // com.mysql.jdbc.StatementImpl@77ec78b9

        // 4.执行增删改的SQL语句
        String sql = "DELETE FROM category WHERE cid=1;";
        int row = stmt.executeUpdate(sql);

        System.out.println("影响的行数: " + row);

        // 5.释放资源
        stmt.close();
        conn.close();
    }
}

查询语句使用executeQuery()
在这里插入图片描述

ResultSet的原理

ResultSet用于保存执行查询SQL语句的结果。我们不能一次性取出所有的数据,需要一行一行的取出。
ResultSet内部有一个指针,记录获取到哪行数据 next()方法
1.会将游标向下移动一行
2.如果有数据返回true,没有数据返回false

在这里插入图片描述

public class Demo04 {
	public static void main(String[] args) throws Exception {
		// 1.注册驱动
		Class.forName("com.mysql.jdbc.Driver");
		// 2.获取连接
		Connection conn = DriverManager.getConnection("jdbc:mysql:///day20", "root", "root");
		// 3.获取Statement
		Statement stmt = conn.createStatement();
		// 4.执行查询的SQL语句
		String sql = "SELECT * FROM category;";
		ResultSet rs = stmt.executeQuery(sql);
		// 5.处理结果
		while (rs.next()) {
			// 有数据,获取数据
			int cid = rs.getInt("cid");
			String cname = rs.getString("cname");
			System.out.println(cid + " == " + cname);
		}

		// 6.释放资源
		rs.close();
		stmt.close();
		conn.close();
	}
}

JDBC事务

使用步骤

1、注册驱动
2、获取连接
3、开启事务
4、获取到Statement
5、Statement执行SQL
6、提交或回滚事务
7、关闭资源

在这里插入图片描述

public class Demo05 {
	public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        // 1.注册驱动
        try {
            Class.forName("com.mysql.jdbc.Driver");
            // 2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql:///day20", "root", "root");
            // 3.开启事务
            conn.setAutoCommit(false);
            // 4.获取到Statement
            stmt = conn.createStatement();
            // 5.Statement执行SQL
            // 张三-500
            stmt.executeUpdate("UPDATE account SET balance=balance-500 WHERE id=1;");
            int a = 10 / 0;
            // 李四+500
            stmt.executeUpdate("UPDATE account SET balance=balance+500 WHERE id=2;");
            // 6.提交事务
            System.out.println("成功, 提交事务!");
            conn.commit();
        } catch (Exception e) {
            if (conn != null) {
                try {
                    // 6.回滚事务
                    System.out.println("失败, 回滚事务!");
                    conn.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            // 7.关闭资源
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

JDBC 工具类

编写JDBC工具类步骤

将固定字符串定义为常量
在静态代码块中注册驱动
提供一个获取连接的方法static Connection getConneciton();
定义关闭资源的方法close(Connection conn, Statement stmt, ResultSet rs)
重载关闭方法close(Connection conn, Statement stmt)

public class JDBCUtils {
    // 将固定字符串定义为常量
    private static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver";
    private static final String URL = "jdbc:mysql:///day20";
    private static final String USER = "root";
    private static final String PASSWORD = "root";
    // 在静态代码块中注册驱动
    static {
        try {
            Class.forName(DRIVER_CLASS_NAME);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    // 提供一个获取连接的方法`static Connection getConneciton();`
    // getConnection返回连接给别人用,出现问题.告诉别人
    public static Connection getConnection() throws SQLException {
        Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
        return conn;
    }

    // 定义关闭资源的方法`close(Connection conn, Statement stmt, ResultSet rs)`
    // 将来别人调用close方法把对象给我们使用,有问题就自己处理
    public static void close(Connection conn, Statement stmt, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

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

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

    // 重载关闭方法`close(Connection conn, Statement stmt)`
    public static void close(Connection conn, Statement stmt) {
        close(conn, stmt, null);
    }
}

SQL注入攻击(用户登录)

先看代码(使用上面的JDBC工具类)

public class Demo07 {
	public static void main(String[] args) throws SQLException {
		// 1.使用数据库保存用户的账号和密码
		// 2.让用户输入账号和密码
		Scanner sc = new Scanner(System.in);
		System.out.println("请输入账号:");
		String user = sc.next();
		System.out.println("请输入密码:");
		String password = sc.next();

		// 3.使用SQL根据用户的账号和密码去数据库查询数据
		Connection conn = JDBCUtils.getConnection();
		Statement stmt = conn.createStatement();

		String sql = "SELECT * FROM user WHERE name='" + user + "' AND password='" + password + "';";
		System.out.println("sql = " + sql);
		ResultSet rs = stmt.executeQuery(sql);
		if (rs.next()) {
			// 4.如果查询到数据,说明登录成功
			System.out.println("欢迎您, " + user);
		} else {
			// 5.如果查询不到数据,说明登录失败
			System.out.println("登录失败!");
		}

		JDBCUtils.close(conn, stmt, rs);
	}
}

运行结果
在这里插入图片描述

SQL注入攻击的原理:

1、 Statement对象在执行sql语句时,将密码的一部分内容当做查询条件来执行了。
sql = SELECT * FROM user WHERE name=‘asdfdfg’ AND password=‘a’or’1’=‘1’;
1=1 的结果是true,原句就变成了sql = SELECT * FROM user WHERE true;
2、数据库是不安全的

PreparedStatement(改进用户登录)

使用上面的JDBC工具类

public class Demo09 {
	public static void main(String[] args) throws SQLException {
		// 1.使用数据库保存用户的账号和密码
		// 2.让用户输入账号和密码
		Scanner sc = new Scanner(System.in);
		System.out.println("请输入账号:");
		String user = sc.next();
		System.out.println("请输入密码:");
		String password = sc.next();

		// 3.使用SQL根据用户的账号和密码去数据库查询数据
		Connection conn = JDBCUtils.getConnection();

		String sql = "SELECT * FROM user WHERE name=? AND password=?;";
		System.out.println("sql = " + sql);
		PreparedStatement pstmt = conn.prepareStatement(sql);
		// 给?赋值, parameterIndex: 指定第几个?, String x: ?的具体值
		pstmt.setString(1, user);
		pstmt.setString(2, password);

		ResultSet rs = pstmt.executeQuery();
		if (rs.next()) {
			// 4.如果查询到数据,说明登录成功
			System.out.println("欢迎您, " + user);
		} else {
			// 5.如果查询不到数据,说明登录失败
			System.out.println("登录失败!");
		}

		JDBCUtils.close(conn, pstmt, rs);
	}
}

运行结果
在这里插入图片描述

PreparedStatement预编译执行者对象

在执行sql语句之前,将sql语句进行提前编译。明确sql语句的格式后就不会改变了。剩余的内容都会认为是参数!
SQL语句中的参数使用?作为占位符

PreparedStatement实现查询(保存到集合中)

使用上面的JDBC工具类

public class Employee {
    private int id;
    private String NAME;
    private int age;
    private String address;
    //默认无参,满参,set,get
}
public class Demo10 {
    public static void main(String[] args) throws SQLException {
        // 定义Employee类
        // 执行SQL语句,得到ResultSet
        Connection conn = JDBCUtils.getConnection();
        String sql = "SELECT * FROM employee WHERE id<=?;";
        PreparedStatement pstmt = conn.prepareStatement(sql);
        pstmt.setInt(1, 4);
        ResultSet rs = pstmt.executeQuery();
        // 创建一个集合用于保存所有的员工对象
        ArrayList<Employee> list = new ArrayList<>();

        // 每次循环将一条记录存的数据放到一个员工对象中
        while (rs.next()) {
            int id = rs.getInt("id");
            String name = rs.getString("name");
            int age = rs.getInt("age");
            String address = rs.getString("address");
            Employee emp = new Employee(id, name, age, address);
            // 把员工对象放到集合中
            list.add(emp);
        }

        // 关闭资源
        JDBCUtils.close(conn, pstmt, rs);

        // 遍历集合操作数据
        for (Employee emp : list) {
            System.out.println(emp);
        }
    }
}

连接池

连接池的概念:

连接池就是一个容器,连接池中保存了一些数据库连接,这些连接是可以重复使用的。

连接池的原理:

启动连接池,连接池就会初始化一些连接
当用户需要使用数据库连接,直接从连接池中取出
当用户使用完连接,会将连接重新放回连接池中

CP30连接池

地址、账户、密码都在xml文件都进行修改

在这里插入图片描述

CP30配置文件

<c3p0-config>
  <!-- 使用默认的配置读取连接池对象 -->
  <default-config>
  	<!--  数据库连接参数 -->
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/day20</property>
    <property name="user">root</property>
    <property name="password">root</property>
    
    <!-- 连接池参数 -->
    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">10</property>
    <property name="checkoutTimeout">3000</property>
  </default-config>

  <named-config name="nba"> 
    <!--  连接参数 -->
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/day19</property>
    <property name="user">root</property>
    <property name="password">root</property>
    
    <!-- 连接池参数 -->
    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">6666</property>
    <property name="checkoutTimeout">1000</property>
  </named-config>
</c3p0-config>
public class Demo11 {
	public static void main(String[] args) throws SQLException {
		// 导入jar包c3p0-0.9.5.2.jar和mchange-commons-java-0.2.12.jar
		// 复制配置文件c3p0-config.xml放在src目录下,配置对应参数
		// 创建连接池对象ComboPooledDataSource
		ComboPooledDataSource ds = new ComboPooledDataSource();
		// 从连接池中获取连接对象

		Connection conn = ds.getConnection();
		// 使用连接对象操作数据库
		String sql = "DELETE FROM employee WHERE id=4;";
		PreparedStatement pstmt = conn.prepareStatement(sql);
		int row = pstmt.executeUpdate();
		System.out.println("row = " + row);

		// 关闭资源,将连接还回连接池中
		JDBCUtils.close(conn, pstmt);
	}
}

DRUID连接池

地址、账户、密码都在.properties文件都进行修改

在这里插入图片描述

public class Demo12 {
	public static void main(String[] args) throws Exception {
		// 导入druid-1.0.0.jar的jar包
		// 复制druid.properties文件到src下,并设置对应参数
		// 加载properties文件的内容到Properties对象中
		Properties pp = new Properties();
		pp.load(new FileReader("study_day20\\src\\druid.properties"));
		// 创建Druid连接池,使用配置文件中的参数
		DataSource ds = DruidDataSourceFactory.createDataSource(pp);
		//从连接池中获取connection
		Connection conn = ds.getConnection();
		// 使用连接对象操作数据库
		String sql = "DELETE FROM employee WHERE id=4;";
		PreparedStatement pstmt = conn.prepareStatement(sql);
		int row = pstmt.executeUpdate();
		System.out.println("row = " + row);

		// 关闭资源,将连接还回连接池中
		JDBCUtils.close(conn, pstmt);
	}
}

Druid properties文件

url=jdbc:mysql://localhost:3306/day20
username=root
password=root
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
maxWait=2000

连接池工具类

连接池工具类步骤

声明静态连接池成员变量
创建连接池对象
定义得到连接对象的方法
定义关闭资源的方法

public class DataSourceUtils {
    // 声明静态连接池成员变量
    private static DataSource ds;
    // 创建连接池对象
    static {
        try {
            Properties pp = new Properties();
            pp.load(new FileReader("study_day20\\src\\druid.properties"));
            ds = DruidDataSourceFactory.createDataSource(pp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 定义得到连接对象的方法
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }

    // 定义关闭资源的方法
    public static void close(Connection conn, Statement stmt, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

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

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

    public static void close(Connection conn, Statement stmt) {
        close(conn, stmt, null);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

halulu.me

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值