1 基础理论
2、JDBC步骤
操作步骤
1. 导入jar包 2. 找到驱动 Driver 3. 获取连接 Connection 4. 获取操作 Statement 5. 查询结果 ResultSet 6. 释放资源
案例代码
1、准备数据
-- 1. 创建数据库 DROP DATABASE IF EXISTS mydb08; CREATE DATABASE IF NOT EXISTS mydb08; USE mydb08; -- 2. 创建表 DROP TABLE IF EXISTS student; CREATE TABLE IF NOT EXISTS student( sid INT PRIMARY KEY AUTO_INCREMENT, sname VARCHAR(20) NOT NULL, sage INT ); -- 3. 插入数据 INSERT INTO student VALUES (NULL,'定浩',18); INSERT INTO student VALUES (NULL,'郭龙',19); INSERT INTO student VALUES (NULL,'黄杰',17); -- 4. 查询结果 SELECT * FROM student;
2、代码实现
//JDBC的快速入门 public class Demo { //启动的顺序: C->S->R public static void main(String[] args) throws Exception { String username = "root"; String password = "root"; String url = "jdbc:mysql://localhost:3306/mydb08"; String sql = "SELECT * FROM student"; //---------------------- //找到驱动的对象 Class.forName("com.mysql.jdbc.Driver"); //通过驱动管理者,获取到连接 Connection Connection conn = DriverManager.getConnection(url, username, password); //通过连接 conn 获取到操作的对象 Statement Statement stat = conn.createStatement(); //执行SQL语句,查询数据库 ResultSet resu = stat.executeQuery(sql); //循环遍历结果集,获取到结果集当中的数据 while (resu.next()) { //判断是否还存在下一行的数据,如果存在,则进入循环 //获取到指定的数据, 通过列名获取到指定的数据值 int id = resu.getInt("sid"); String name = resu.getString("sname"); int age = resu.getInt("sage"); System.out.println(id + "\t" + name + "\t" + age); } //释放资源,关闭的顺序是相反的。R->S->C resu.close(); stat.close(); conn.close(); } }
二 JDBC相关API
1 、DriverManager类
底层代码 DriverManager.getConnection(url,username,password)
public static Connection getConnection(String url,String user, String password) throws SQLException { //1. 创建了 Properties的对象 info java.util.Properties info = new java.util.Properties(); //2. 判断用户名是否为空, 如果不为空,则存放到properties当中 if (user != null) { info.put("user", user); } //3. 判断密码是否为空, 如果不为空,则存放到properties当中 if (password != null) { info.put("password", password); } //4. 调用自己的方法 getConnection(三个参数方法) return (getConnection(url, info, Reflection.getCallerClass())); }
发现问题:
底层会将 username 和 password 进行 Properties 的封装
底层实现
//构造方法,私有化修饰了 private DriverManager(){} //静态代码块,只要类加载的时候,都会加载静态代码块,加载静态代码块的之后,才会调用 getConnection()方法 static { loadInitialDrivers(); println("JDBC DriverManager initialized"); } //调用下面的方法 private static void loadInitialDrivers() { String drivers; try { drivers = AccessController.doPrivileged(new PrivilegedAction<String>() { public String run() { return System.getProperty("jdbc.drivers"); } }); } catch (Exception ex) { drivers = null; } AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator(); try{ while(driversIterator.hasNext()) { driversIterator.next(); } } catch(Throwable t) { } return null; } }); //这里,什么时候驱动为null呢?就是上面出现异常之后为空。 println("DriverManager.initialize: jdbc.drivers = " + drivers); if (drivers == null || drivers.equals("")) { return; } String[] driversList = drivers.split(":"); println("number of Drivers:" + driversList.length); //底层在遍历驱动 for (String aDriver : driversList) { try { println("DriverManager.Initialize: loading " + aDriver); //核心代码:底层会加载所有的驱动,通过ClassLoader去加载驱动 Class.forName(aDriver, true,ClassLoader.getSystemClassLoader()); } catch (Exception ex) { println("DriverManager.Initialize: load failed: " + ex); } } }
注意事项:
Class.forName("com.mysql.jdbc.Driver");
可以省略不写的,在高版本的JDBC当中,找驱动的底层会自动完成。
底层代码当中,会有扫描驱动的操作。(底层在遍历驱动)
Class.forName(aDriver, true,ClassLoader.getSystemClassLoader());
2、 Connection 接口
1、核心方法
1. Statement createStatement() //获取到操作数据的对象 Statement 接口对象 2. PreparedStatement prepareStatement(String sql) //预置操作数据的对象 PreparedStatement 接口对象 ------------------- 3. void setAutoCommit(boolean autoCommit) //是否开启自动提交事务,如果传递值是 false 表示手动开启事务,默认true 4. void commit() //提交事务的操作,多组SQL语句执行,没有问题的情况下,则提交 5. void rollback() //回滚事务的操作,当SQL语句执行过程当中,出现异常的时候,则回滚
2、事务处理
//目标:学习JDBC当中的事务处理方式 @SuppressWarnings("all") public class Test01 { public static void main(String[] args) { String username = "root"; String password = "root"; String url = "jdbc:mysql://localhost:3306/mydb08"; String sql = "INSERT INTO student VALUES (3,'佳佳',18)"; Connection conn = null; Statement stat = null; //---------------------- try { //通过驱动管理者,获取到连接 Connection conn = DriverManager.getConnection(url, username, password); //****[1]开启事务手动提交**** conn.setAutoCommit(false); //************************ //获取到操作数据的对象 stat = conn.createStatement(); //执行SQL语句 //执行增删改 采用的方法是 executeUpdate 返回的结果是影响的行数 int count = stat.executeUpdate(sql); //****[2]执行成功,则提交事务**** conn.commit(); //************************ System.out.println(count > 0 ? "执行成功" : "执行失败"); } catch (SQLException e) { e.printStackTrace(); //****[3]如果出现了异常,需要事务的回滚**** try { conn.rollback(); System.out.println("事务回滚执行了..."); } catch (SQLException e1) { e1.printStackTrace(); } //************************ }finally { //进行资源的释放 if (stat!=null){ try { stat.close(); System.out.println("资源释放stat..."); } catch (SQLException e) { e.printStackTrace(); } } if (conn!=null){ try { conn.close(); System.out.println("资源释放conn..."); } catch (SQLException e) { e.printStackTrace(); } } } } }
3、 Statement 接口
1、核心方法
1. void addBatch(String sql) //可以用于批量执行SQL语句 2. int[] executeBatch() //与上面的方法搭配使用 3. int executeUpdate(String sql) //执行更新操作。增、删、改 的SQL语句,需要使用此方法,返回的是影响行数 4. ResultSet executeQuery(String sql) //执行查询的方法。返回的是结果集 ResultSet
2、方法演示
//目标:学习Statement的常用方法 @SuppressWarnings("all") public class Test02 { String username = "root"; String password = "root"; String url = "jdbc:mysql://localhost:3306/mydb08"; Connection conn = null; Statement stat = null; ResultSet resu = null; @Before public void start() throws Exception { //通过驱动管理者,获取到连接 Connection conn = DriverManager.getConnection(url, username, password); //通过conn去获取到操作数据库的对象 stat stat = conn.createStatement(); } @Test public void testAddBatch() throws Exception{ String sql1 = "INSERT INTO student VALUES (NULL,'佳佳1',18)"; String sql2 = "INSERT INTO student VALUES (NULL,'佳佳2',18)"; //将SQL语句批量的添加进去 stat.addBatch(sql1); stat.addBatch(sql2); //执行操作 int[] array = stat.executeBatch(); System.out.println(Arrays.toString(array)); } @Test public void testExecuteUpdate() throws Exception { String sql = "INSERT INTO student VALUES (NULL,'佳佳',18)"; //executeUpdate 执行的是增删改 int count = stat.executeUpdate(sql); System.out.println("count = " + count); Assert.assertEquals(1, count); } @Test public void testExecuteQuery() throws Exception { String sql = "SELECT * FROM student"; //执行的是查询 resu = stat.executeQuery(sql); //循环查找 while (resu.next()) { int id = resu.getInt("sid"); String name = resu.getString("sname"); int age = resu.getInt("sage"); System.out.println(id + "\t" + name + "\t" + age); } } @After public void end() throws Exception { //释放资源,关闭的顺序是相反的。R->S->C if (resu != null) { resu.close(); } if (stat != null) { stat.close(); } if (conn != null) { conn.close(); } } }
4、 ResultSet接口
1、核心方法
1. boolean next() //判断是否还存在下一行的数据,可以作用在while循环判断和if判断语句当中 2. int getInt(int columnIndex) //获取到int类型的数据,参数是查询结果集的第几个索引 3. int getInt(String columnLabel) //获取到int类型的数据,参数是需要查询的列的名称 4. String getString(int columnIndex) //获取到String类型的数据,参数是查询结果集的第几个索引 5. String getString(String columnLabel) //获取到String类型的数据,参数是需要查询的列的名称
2、方法演示
//目标:学习ResultSet的常用方法 @SuppressWarnings("all") public class Test03 { String username = "root"; String password = "root"; String url = "jdbc:mysql://localhost:3306/mydb08"; Connection conn = null; Statement stat = null; ResultSet resu = null; @Before public void start() throws Exception { //通过驱动管理者,获取到连接 Connection conn = DriverManager.getConnection(url, username, password); //通过conn去获取到操作数据库的对象 stat stat = conn.createStatement(); } @Test public void testNext() throws Exception{ String sql = "SELECT * FROM student"; ResultSet resu = stat.executeQuery(sql); boolean flag1 = resu.next(); System.out.println("flag1 = " + flag1); boolean flag2 = resu.next(); System.out.println("flag2 = " + flag2); boolean flag3 = resu.next(); System.out.println("flag3 = " + flag3); boolean flag4 = resu.next(); System.out.println("flag4 = " + flag4); } @Test public void testGetInt() throws Exception{ String sql = "SELECT sname,sid FROM student"; ResultSet resu = stat.executeQuery(sql); while (resu.next()) { int id1 = resu.getInt("sid"); //System.out.println(id1); int id2 = resu.getInt(2); System.out.println(id1+","+id2); } } @Test public void testGetString() throws Exception{ String sql = "SELECT sid,sname FROM student"; ResultSet resu = stat.executeQuery(sql); while (resu.next()) { String name1 = resu.getString("sname"); System.out.println("name1 = " + name1); String name2 = resu.getString(2); System.out.println("name2 = " + name2); } } @After public void end() throws Exception { //释放资源,关闭的顺序是相反的。R->S->C if (resu != null) { resu.close(); } if (stat != null) { stat.close(); } if (conn != null) { conn.close(); } } }
三 SQL注入问题
1、 数据准备
1、数据库准备
-- 1. 创建新表 DROP TABLE IF EXISTS t_user; CREATE TABLE IF NOT EXISTS t_user( uid INT PRIMARY KEY AUTO_INCREMENT, uname VARCHAR(20), upass VARCHAR(20) ); -- 2. 插入数据 INSERT INTO t_user VALUES (NULL,'zhangsan','333'); INSERT INTO t_user VALUES (NULL,'lisi','444'); -- 3. 查询数据 SELECT * FROM t_user; /* 需求:做一个登录的效果,如果输入用户名和密码,只有都是正确的才能登录成功 */ SELECT * FROM t_user WHERE uname = 'zhangsan' AND upass = '333'; SELECT * FROM t_user WHERE uname = 'zhangsan' OR '1=1' AND upass = '随便写都可以的';
2、演示问题
//目标: 演示SQL注入的问题 @SuppressWarnings("all") public class Test01 { public static void main(String[] args) throws Exception { Scanner sc = new Scanner(System.in); System.out.println("请输入用户名: zhangsan' OR '1=1 "); String nameStr = sc.nextLine(); System.out.println("请输入密码:"); String passStr = sc.nextLine(); String username = "root"; String password = "root"; String url = "jdbc:mysql://localhost:3306/mydb08"; String sql = "SELECT * FROM t_user WHERE uname = '" + nameStr + "' AND upass = " + passStr; //通过驱动管理者,获取到连接 Connection Connection conn = DriverManager.getConnection(url, username, password); //通过连接 conn 获取到操作的对象 Statement Statement stat = conn.createStatement(); //执行SQL语句,查询数据库 ResultSet resu = stat.executeQuery(sql); //判断是否存在数据呢? String message = "Sorry 登录失败,请检查账号和密码"; //只要存在下一条的记录,则表示登录成功 if (resu.next()) { message = "登录成功:" + nameStr; } System.out.println("message = " + message); //释放资源 resu.close(); stat.close(); conn.close(); } }
2、解决问题
1、需要使用API
//1. 当我们获取到conn的对象之后,需要获取到预置语句的对象PreparedStatement PreparedStatement prepareStatement(String sql) //2. 提前定义好SQL语句,采用占位符,占据位置。最后去设置占位符的值 void setInt(int parameterIndex, int x) //参数1: 第几个问号, 参数2: 需要插入的值 void setString(int parameterIndex, String x) //参数1: 第几个问号,参数2: 需要插入的值 //3. 执行SQL的操作 int executeUpdate() //执行 增、删、改操作 ResultSet executeQuery() //执行 查询操作
2、解决问题
//目标: 解决SQL注入的问题 @SuppressWarnings("all") public class Test02 { public static void main(String[] args) throws Exception { Scanner sc = new Scanner(System.in); System.out.println("请输入用户名: zhangsan' OR '1=1"); String nameStr = sc.nextLine(); System.out.println("请输入密码:"); String passStr = sc.nextLine(); String username = "root"; String password = "root"; String url = "jdbc:mysql://localhost:3306/mydb08"; //预置的SQL语句,里面的变量由问号(英文) 去代替 String sql = "SELECT * FROM t_user WHERE uname = ? AND upass = ?"; //通过驱动管理者,获取到连接 Connection Connection conn = DriverManager.getConnection(url, username, password); //********************* //通过连接 conn 获取到操作的对象 PreparedStatement PreparedStatement stat = conn.prepareStatement(sql); //设置参数值 stat.setString(1,nameStr); stat.setString(2,passStr); //执行SQL语句,查询数据库 ResultSet resu = stat.executeQuery(); //********************* //判断是否存在数据呢? String message = "Sorry 登录失败,请检查账号和密码"; //只要存在下一条的记录,则表示登录成功 if (resu.next()) { message = "登录成功:" + nameStr; } System.out.println("message = " + message); //释放资源 resu.close(); stat.close(); conn.close(); } }
四 JDBC工具类
1、配置文件
位置: src/jdbc.properties
url=jdbc:mysql://localhost:3306/mydb08 username=root password=root
2、 工具类
//定义JDBC的工具类 @SuppressWarnings("all") public class JDBCUtils { private static String url = null; private static String username = null; private static String password = null; private static Connection conn = null; private static PreparedStatement stat = null; private static ResultSet resu = null; /** * 静态代码块,只加载一次。 */ static{ try { //在反射章节讲过的类加载器,专门加载src下面的文件 properties,加载成为流对象 InputStream is = ClassLoader.getSystemResourceAsStream("jdbc.properties"); Properties pp = new Properties(); pp.load(is); is.close(); url = pp.getProperty("url"); username = pp.getProperty("username"); password = pp.getProperty("password"); } catch (IOException e) { e.printStackTrace(); } } /** * 自定义的方法,用于获取数据库的连接对象 Connection * * @return */ public static Connection getConnection() { try { conn = DriverManager.getConnection(url, username, password); } catch (SQLException e) { e.printStackTrace(); } return conn; } /** * 自定义的方法,用于操作 增删改数据 */ public static int exeUpdate(String sql, Map<Integer, Object> map) { //INSERT INTO 表名称 VALUES (NULL,?,?,?); //map.put(1,1001); //map.put(2,"张三"); //map.put(3,23); int lineNumber = -1; try { PreparedStatement stat = getConnection().prepareStatement(sql); //询问map集合到底有多少个数据 for (int i = 0; map != null && i < map.size(); i++) { //得到索引值 int index = i + 1; stat.setObject(index, map.get(index)); } //执行SQL语句 lineNumber = stat.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } return lineNumber; } /** * 自定义的方法,用于操作 */ public static ResultSet exeQuery(String sql) { //调用下面的重载方法 return exeQuery(sql,null); } /** * 自定义的方法,用于操作 查询数据 */ public static ResultSet exeQuery(String sql, Map<Integer, Object> map) { //SELECT * FROM 表名 WHERE 列名1=? AND 列名2 = ?; try { PreparedStatement stat = getConnection().prepareStatement(sql); //询问map集合到底有多少个数据 for (int i = 0; map != null && i < map.size(); i++) { //得到索引值 int index = i + 1; stat.setObject(index, map.get(index)); } //具体的查询操作 resu = stat.executeQuery(); } catch (SQLException e) { e.printStackTrace(); } return resu; } /*** * 释放资源 */ public static void close() { //判断 if (resu != null) { try { resu.close(); } catch (SQLException e) { e.printStackTrace(); } } //判断 if (stat != null) { try { stat.close(); } catch (SQLException e) { e.printStackTrace(); } } //判断 if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
3、 测试类
//测试类 @SuppressWarnings("all") public class Test { public static void main(String[] args) throws Exception { //测试添加数据的操作 String sql1 = "INSERT INTO t_user VALUES (NULL,'zhaoliu',666)"; int lineNumber = JDBCUtils.exeUpdate(sql1, null); System.out.println("lineNumber = " + lineNumber); System.out.println(lineNumber > 0 ? "成功" : "失败"); //2.释放资源 JDBCUtils.close(); System.out.println("------------"); //测试查询所有的操作 String sql2 = "SELECT * FROM t_user"; ResultSet resu1 = JDBCUtils.exeQuery(sql2); while (resu1.next()){ int uid = resu1.getInt("uid"); String uname = resu1.getString("uname"); String upass = resu1.getString("upass"); System.out.println(uid+","+uname+","+upass); } //2.释放资源 JDBCUtils.close(); } }