文章目录
一、Java连接mysql数据库1.1 流程1.2 一个测试连接的java程序 二、优化:创建一个工具类2.1 存在的问题2.2 创建配置文件和工具类2.3 测试使用工具类进行CRUD操作 三、SQL注入问题3.1 SQL注入示例 四、使用PreparedStatement防止SQL注入五、JDBC操作事务
一、Java连接mysql数据库
1.1 流程
java连接mysql大致需要这六步:
导入驱动包:这里我使用的是mysql-connector-java-8.0.17.jar(点击下载
),这个包连接mysql5.6,5.7,8.0版本都没问题。Class.forName("com.mysql.cj.jdbc.Driver");
url和账户名密码JDBC连接串:jdbc:mysql://<地址>:<端口>/<数据库>获取连接:
DriverManager.getConnection(url, user, password);
执行sql的Statement对象:connection.createStatement();
执行SQL释放连接 1.2 一个测试连接的java程序
JdbcFirstDemo.java
package com.peng.less01;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class JdbcFirstDemo { public static void main(String[] args) throws ClassNotFoundException, SQLException { // 1.导入驱动类 Class.forName("com.mysql.cj.jdbc.Driver"); // 2.用户信息和url String url = "jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8&useSSL=false"; String user = "root"; String password = "12345678"; // 3.获取连接 Connection conn = DriverManager.getConnection(url, user, password); // 4.执行SQL的对象 Statement sta = conn.createStatement(); // 5.执行SQL String sql = "select * from account"; ResultSet rs = sta.executeQuery(sql); while (rs.next()){ System.out.println("id= " + rs.getObject("id")); System.out.println("name= " + rs.getObject("name")); System.out.println("money= " + rs.getObject("money")); System.out.println("========================================="); } // 6.释放连接 rs.close(); sta.close(); conn.close(); }}
这里使用的是VScode,connector包放到了lib目录下,代码放在了src/com/peng/less01下。
执行结果如下,显示了shop.account表下面的三条记录。
二、优化:创建一个工具类
2.1 存在的问题
1、JDBC连接串,用户名,密码等都存在于代码中,需要进行解耦
2、创建连接,释放连接这些操作都是固定的,没必要每次都重复写这些代码(创建函数解决)
2.2 创建配置文件和工具类
src/db.properties 文件中记录driver,url,user,password
driver=com.mysql.cj.jdbc.Driverurl=jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8&useSSL=falseuser=rootpassword=12345678
src/com/peng/less02/utils/JdbcUtils.java 工具类:读取properties配置文件
自动读取配置信息,加载mysql驱动。
package com.peng.less02.utils;import java.io.InputStream;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Properties;// 这是一个工具类,用来减少重复操作public class JdbcUtils { private static String driver = null; private static String url = null; private static String user = null; private static String password = null; static{ try{ // 读取db.properties InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties"); Properties properties = new Properties(); properties.load(in); driver = properties.getProperty("driver"); url = properties.getProperty("url"); user = properties.getProperty("user"); password = properties.getProperty("password"); // 加载驱动 Class.forName(driver); }catch (Exception e){ e.printStackTrace(); } } // 创建连接 public static Connection getConnection() throws SQLException{ return DriverManager.getConnection(url,user,password); } // 释放连接 public static void releaseConnection(Connection conn, Statement sta, ResultSet rs){ if (rs != null){ try { rs.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (sta != null){ try { sta.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (conn != null){ try { conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }}
2.3 测试使用工具类进行CRUD操作
测试插入操作
src/com/peng/less02/TestInsert.java
package com.peng.less02;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import com.peng.less02.utils.JdbcUtils;public class TestInsert { public static void main(String[] args) { Connection conn = null; Statement sta = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); sta = conn.createStatement(); String sql = "insert into account(`id`,`name`,`money`) values(4,'药水哥',4698888)"; int i = sta.executeUpdate(sql); if (i > 0){ System.out.println("插入成功!"); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ JdbcUtils.releaseConnection(conn, sta, rs); } } }
测试更新操作
src/com/peng/less02/TestUpdate.java
package com.peng.less02;import java.sql.*;import com.peng.less02.utils.JdbcUtils;public class TestUpdate { public static void main(String[] args) { Connection conn = null; Statement sta = null; try { conn = JdbcUtils.getConnection(); sta = conn.createStatement(); String sql = "update account set `money`=238888 where id=3"; int i = sta.executeUpdate(sql); if (i > 0){ System.out.println("更新成功"); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ JdbcUtils.releaseConnection(conn, sta, null); } } }
测试读取操作
package com.peng.less02;import java.sql.*;import com.peng.less02.utils.JdbcUtils;public class TestSelect { public static void main(String[] args) { Connection conn = null; Statement sta = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); sta = conn.createStatement(); String sql = "select * from account"; rs = sta.executeQuery(sql); while (rs.next()){ System.out.println("id= " + rs.getInt("id")); System.out.println("name= " + rs.getString("name")); System.out.println("money= " + rs.getObject("money")); System.out.println("======================================================"); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ JdbcUtils.releaseConnection(conn, sta, rs); } }}
三、SQL注入问题
这里写一个登录认证程序,如果账号,密码输入正确则允许登录。为了测试,这里的允许登录改为输出账号密码。还使用前面写的JdbcUtils工具类
可以看到,这里我输入的用户名/密码是:’ or '1=1 ’ or '1=1
然后输出了user表中的所有数据,这明显是不合法的。存在sql注入的问题
问题就在于这段代码,sql是拼接而成的。
String sql = "select * from user where `username`='" + username + "' and `password`='" + password + "'";
拼接的sql为:select * from user where `username`='' or '1=1' and `password`='' or '1=1';
3.1 SQL注入示例
SqlInjection.java
package com.peng.less02;import com.peng.less02.utils.JdbcUtils;import java.sql.*;public class SqlInjection { public static void main(String[] args) { login("' or '1=1", "' or '1=1"); } // 登录功能 public static void login(String username, String password){ Connection conn = null; Statement sta = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); sta = conn.createStatement(); //这段代码中sql是变量拼接而成的 String sql = "select * from user where `username`='" + username + "' and `password`='" + password + "'"; rs = sta.executeQuery(sql); //输入正确的账号密码就能够登录,这里为了测试就(输入正确输出账号密码) while (rs.next()){ System.out.println("id= " + rs.getInt("id")); System.out.println("username= " + rs.getString("username")); System.out.println("password= " + rs.getObject("password")); System.out.println("======================================================"); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ JdbcUtils.releaseConnection(conn, sta, rs); } }}
四、使用PreparedStatement防止SQL注入
使用PreparedStatement,防止SQL注入,同时对于执行多次的SQL更加高效PreparedStatement,对sql进行了预编译
,传入的参数只会被当作字符串来处理,而不会像前面对sql进行了拼接。流程如下:
先编写sql --> 进行预编译 --> 增加sql参数值 --> 执行sql
SqlInjection.java
package com.peng.less03;import com.peng.less02.utils.JdbcUtils;import java.sql.*;public class SqlInjection { public static void main(String[] args) { // login("'' or 1=1", "'' or 1=1"); login("张三", "zhangsan"); } // 登录功能 public static void login(String username, String password){ Connection conn = null; PreparedStatement psta = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); //这里使用 ? 占位符来代替 String sql = "select * from user where `username`=? and `password`=?"; //使用PreparedStatement,防止SQL注入,同时对于执行多次的SQL更加高效 psta = conn.prepareStatement(sql); psta.setString(1,username); psta.setObject(2,password); rs = psta.executeQuery(); //输入正确的账号密码就能够登录,这里为了测试就(输入正确输出账号密码) while (rs.next()){ System.out.println("id= " + rs.getInt("id")); System.out.println("username= " + rs.getString("username")); System.out.println("password= " + rs.getObject("password")); System.out.println("======================================================"); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ JdbcUtils.releaseConnection(conn, psta, rs); } }}
五、JDBC操作事务
和用sql执行事务类似,创建连接后setAutoCommit(false)
,最后commit()
,失败则rollback()
。
注意:rollback()部分可以不用写,事务失败会自动回滚。
TestTransaction.java
package com.peng.less04;import java.sql.*;// 仍然使用前面写的JdbcUtils工具类import com.peng.less02.utils.JdbcUtils;public class TestTransaction { public static void main(String[] args) { Connection conn = null; PreparedStatement pst = null; try { conn = JdbcUtils.getConnection(); // 关闭自动提交,开启事务 conn.setAutoCommit(false); //模拟一个转账事务,A向大帅哥转账100块 String sql1 = "update account set `money`=money - 100 where `name`='A'"; pst = conn.prepareStatement(sql1); pst.executeUpdate(); // int x = 1/0; String sql2 = "update account set `money`=money + 100 where `name`='大帅哥'"; conn.prepareStatement(sql2).executeUpdate(); conn.commit(); System.out.println("Success!"); } catch (SQLException e) { // TODO Auto-generated catch block try { // rollback()可以不用写,失败的话会自动回滚 conn.rollback(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } e.printStackTrace(); }finally{ JdbcUtils.releaseConnection(conn, pst, null); } }}