一、JDBC
1. 概念:Java DataBase Connectivity Java数据库连接,Java语言操作数据库。
2. 本质:其实是java官方SUN公司定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(即JDBC接口)编程,真正执行的代码是驱动jar包中的实现类,当调用接口方法时真正执行的是驱动jar包中实现类对应的实现方法。
定义了操作所有关系型数据库的规则(接口)。(由SUN公司程序员编写,定义了一套接口。)每一个数据库厂商写了不同的实现类,不同的实现类来操作它们自己。每一个实现类都实现了相同的接口。实现类的名字叫数据库驱动。
3. 快速入门:
(1)步骤:
第一步:导入驱动jar包。下载地址:https://dev.mysql.com/downloads/connector/j/,解压后,mysql-connector-java-5.1.49-bin.jar文件是驱动文件,导入到工程内,src文件是开源的源代码文件。
复制jar包到项目新建的libs文件夹目录下
右键 ----->add as library
第二步:编写代码,注册驱动
idea处理异常的快捷键 Alt+Enter
第三步:获取数据库的连接对象,Connection,就是本地java代码和数据库的桥梁对象。
idea返回函数生成值对象的快捷键 Ctrl+Alt+V
第四步:定义SQL
第五步:通过Connection对象,获取执行SQL语句的对象,Statement
第六步:执行SQL,接收返回结果
第七步:处理结果
System.out.println()在idea中的快捷键为sout回车。
第八步:释放资源
代码示例:
1 package cn.itcast.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.Statement; 6 7 /** 8 * JDBC快速入门 9 */ 10 public class JDBCDemo1 { 11 public static void main(String[] args) throws Exception { 12 13 //1.导入驱动jar包 14 15 //2.注册驱动 16 Class.forName("com.mysql.jdbc.Driver"); 17 18 //3.获取数据库的连接对象 Connection 19 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "root"); 20 21 //4.定义SQL语句 22 String sql = "update account set balance = 1000 where id = 1 or id = 2"; 23 24 //5.获取执行SQL的对象 statement 25 Statement stmt = conn.createStatement(); 26 27 //6.执行SQL 28 int count = stmt.executeUpdate(sql); 29 30 //7.处理结果 31 System.out.println(count); 32 33 //8.释放资源 34 stmt.close(); 35 conn.close(); 36 37 } 38 39 }
4.详解各个对象
(1)DriverManager:驱动管理对象
功能:
功能一 :注册驱动:告诉程序该使用哪一个数据库驱动jar包。因为都是面向接口编程,所以需要知道面向哪一个jar包的类,因此需要jar包。
解释:在DriverManager中有方法为:static void registerDriver(Driver driver); 含义为注册与给定的驱动程序 DriverManager
写代码时使用:Class.forName("com.mysql.jdbc.Driver");
把类加载进内存,后期也没有用到Driver类,说明有的代码在加载的时候就被加载到了内存,存在静态代码块。
通过查看源码发现,在com.mysql.jdbc.Driver中存在静态代码块:
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
在MySQL版本5后,在jar包中,有一个META-INF文件夹下面有一个services目录,下面有一个java.sql.Driver文件,文件中有一句话为com.mysql.jdbc.Driver。
说明在此文件中已经帮助我们注册了MySQL的驱动类,所以不写注册驱动的代码也是可以的,会自动扫描该文件,将驱动注册上。
功能二:获取数据库连接
方法:static Connection getConnection(String url, String user, String password);
参数:
url:指定连接的路径
不同的数据库写法有差异,此处介绍MySQL
语法:jdbc:mysql://ip地址(域名): 端口号/数据库名称
例子:jdbc:mysql://localhost:3306/db1
细节:如果连接的是本地MySQL服务器,并且MySQL服务器默认端口是3306,则url可以省略ip地址和端口号,具体简写为:jdbc:mysql:///数据库名称
user:用户名
password:密码
(2)Connection:数据库连接对象 —— 代表了当前代码和数据库连接的桥梁
功能:
功能一:获取执行SQL的对象
Statement createStatement()
PreparedStatement prepareStatement(String sql)
功能二:管理事务
开启事务:void setAutoCommit(boolean autoCommit) : 调用该方法设置参数为false,即开启事务
提交事务:void commit()
回滚事务:void rollback()
(3)Statement:执行SQL的对象
功能:执行SQL
boolean execute(String sql) : 可以执行任意的SQL 了解
int executeUpdate(String sql):执行DML(insert、update、delete)语句、DDL(create、alter、drop)语句。一般executeUpdate来执行DML语句
返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功。
ResultSet executeQuery(String sql) :执行DQL语句(select语句)
案例:JDBC增删改表中数据
添加一条记录:
1 package cn.itcast.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.SQLException; 6 import java.sql.Statement; 7 8 /** 9 * accout表 添加一条记录 insert语句 10 */ 11 public class JDBCDemo2 { 12 public static void main(String[] args){ 13 //提升Connection和Statement作用域 14 Connection conn = null; 15 Statement stmt = null; 16 //1. 添加jar包 17 //2. 注册驱动 18 try { 19 Class.forName("com.mysql.jdbc.Driver"); 20 //3. 定义SQL 21 String sql = "insert into account values(3,'王五',3000)"; 22 //4.获取数据库连接对象:Connection对象 23 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "root"); 24 //5.获取执行SQL的对象:Statement对象 25 stmt = conn.createStatement(); 26 //6.执行SQL语句 27 int count = stmt.executeUpdate(sql);//返回值为影响的行数 28 //7.处理结果 29 System.out.println(count); 30 if(count == 1){ 31 System.out.println("操作成功"); 32 } else{ 33 System.out.println("操作失败"); 34 } 35 } catch (ClassNotFoundException e) { 36 e.printStackTrace(); 37 } catch (SQLException e) { 38 e.printStackTrace(); 39 } finally { 40 //8.释放资源:倒着释放,因为statement是由connection获取的。 41 if(stmt != null){ 42 try { 43 stmt.close(); 44 } catch (SQLException e) { 45 e.printStackTrace(); 46 } 47 } 48 49 if(conn != null){ 50 try { 51 conn.close(); 52 } catch (SQLException e) { 53 e.printStackTrace(); 54 } 55 } 56 } 57 58 } 59 }
修改一条记录:
1 package cn.itcast.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.SQLException; 6 import java.sql.Statement; 7 8 public class JDBCDemo3 { 9 public static void main(String[] args) { 10 11 Connection conn = null; 12 Statement stmt = null; 13 14 //1.添加jar包 15 16 17 try { 18 //2.注册驱动 19 Class.forName("com.mysql.jdbc.Driver"); 20 //3.定义sql 21 String sql = "update account set balance = 1500 where name = '王五'"; 22 //4.获取数据库连接对象Connection 23 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2","root","root"); 24 //5.获取执行SQL对象 Statement 25 stmt = conn.createStatement(); 26 //6.执行SQL 27 int count = stmt.executeUpdate(sql); 28 //7.处理结果 29 System.out.println(count); 30 if (count == 1){ 31 System.out.println("操作成功"); 32 } else { 33 System.out.println("操作失败"); 34 } 35 } catch (ClassNotFoundException e) { 36 e.printStackTrace(); 37 } catch (SQLException e) { 38 e.printStackTrace(); 39 } finally { 40 //8.释放资源 41 if(stmt != null){ 42 try { 43 stmt.close(); 44 } catch (SQLException e) { 45 e.printStackTrace(); 46 } 47 } 48 49 if(conn != null){ 50 try { 51 conn.close(); 52 } catch (SQLException e) { 53 e.printStackTrace(); 54 } 55 } 56 } 57 } 58 }
删除一条记录:
1 package cn.itcast.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.SQLException; 6 import java.sql.Statement; 7 8 /** 9 * account表删除一套记录 10 */ 11 public class JDBCDemo4 { 12 public static void main(String[] args) { 13 14 Connection conn = null; 15 Statement stmt = null; 16 17 //1.导入jar包 18 19 try { 20 //2.注册驱动 21 Class.forName("com.mysql.jdbc.Driver"); 22 //3.获取数据库连接对象Connection 23 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2","root","root"); 24 //4.获取执行SQL对象 Statement 25 stmt = conn.createStatement(); 26 //5.定义SQL 27 String sql = "delete from account where id = 3"; 28 //6.执行SQL 29 int count = stmt.executeUpdate(sql); 30 //7.处理结果 31 System.out.println(count); 32 if(count == 1){ 33 System.out.println("删除操作成功"); 34 } else { 35 System.out.println("删除操作失败"); 36 } 37 } catch (ClassNotFoundException e) { 38 e.printStackTrace(); 39 } catch (SQLException e) { 40 e.printStackTrace(); 41 } finally { 42 //8.释放资源 43 if(stmt != null){ 44 try { 45 stmt.close(); 46 } catch (SQLException e) { 47 e.printStackTrace(); 48 } 49 } 50 if(conn != null){ 51 try { 52 conn.close(); 53 } catch (SQLException e) { 54 e.printStackTrace(); 55 } 56 } 57 } 58 59 } 60 }
DDL语句:
1 package cn.itcast.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.SQLException; 6 import java.sql.Statement; 7 8 /** 9 * 执行DDL语句 10 */ 11 public class JDBCDemo5 { 12 public static void main(String[] args) { 13 14 Connection conn = null; 15 Statement stmt = null; 16 17 //1.导入jar包 18 19 try { 20 //2.注册驱动 21 Class.forName("com.mysql.jdbc.Driver"); 22 //3.获取数据库连接对象Connection 23 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2","root","root"); 24 //4.获取执行SQL对象 Statement 25 stmt = conn.createStatement(); 26 //5.定义SQL 27 String sql = "create table student(id int,name varchar(20))"; 28 //6.执行SQL 29 int count = stmt.executeUpdate(sql); 30 //7.处理结果 31 System.out.println(count); 32 } catch (ClassNotFoundException e) { 33 e.printStackTrace(); 34 } catch (SQLException e) { 35 e.printStackTrace(); 36 } finally { 37 //8.释放资源 38 if(stmt != null){ 39 try { 40 stmt.close(); 41 } catch (SQLException e) { 42 e.printStackTrace(); 43 } 44 } 45 if(conn != null){ 46 try { 47 conn.close(); 48 } catch (SQLException e) { 49 e.printStackTrace(); 50 } 51 } 52 } 53 54 } 55 }
(4)ResultSet:结果集对象,封装查询结果
方法:
boolean next():游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是则返回false,如果不是则返回true。
getXXX(参数):获取数据,XXX代表数据类型,如getInt(),getString(),getDouble()等,参数有两种情况,第一种情况接收int类型的值,代表列的编号,如getString(2),编号从1开始。第二种情况接收String类型的值,列的名称,如getDouble("balance")
案例1
1 package cn.itcast.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.sql.Statement; 8 9 /** 10 * 练习ResultSet 11 */ 12 public class JDBCDemo6 { 13 public static void main(String[] args) { 14 Connection conn = null; 15 Statement stmt = null; 16 ResultSet rs = null; 17 //1.导入jar包 18 19 try { 20 //2.注册驱动 21 Class.forName("com.mysql.jdbc.Driver"); 22 //3.获取数据库连接对象Connection 23 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2","root","root"); 24 //4.获取执行SQL对象Statement 25 stmt = conn.createStatement(); 26 //5.定义SQL 27 String sql = "select * from account"; 28 //6.执行SQL 29 rs = stmt.executeQuery(sql); 30 //7.处理结果 31 rs.next(); 32 int id = rs.getInt(1); 33 String name = rs.getString("name"); 34 double balance = rs.getDouble("balance"); 35 System.out.println(id +" " + name + " " + balance); 36 rs.next(); 37 id = rs.getInt(1); 38 name = rs.getString("name"); 39 balance = rs.getDouble("balance"); 40 System.out.println(id +" " + name + " " + balance); 41 } catch (ClassNotFoundException e) { 42 e.printStackTrace(); 43 } catch (SQLException e) { 44 e.printStackTrace(); 45 } finally{ 46 if(rs != null){ 47 try { 48 rs.close(); 49 } catch (SQLException e) { 50 e.printStackTrace(); 51 } 52 } 53 if(stmt != null){ 54 try { 55 stmt.close(); 56 } catch (SQLException e) { 57 e.printStackTrace(); 58 } 59 } 60 if(conn != null){ 61 try { 62 conn .close(); 63 } catch (SQLException e) { 64 e.printStackTrace(); 65 } 66 } 67 } 68 69 } 70 }
注意:使用步骤,第一步游标向下移动一行,第二步判断是否有数据,第三步获取数据。
案例2
1 package cn.itcast.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.sql.Statement; 8 9 /** 10 * 练习ResultSet 11 */ 12 public class JDBCDemo7 { 13 public static void main(String[] args) { 14 Connection conn = null; 15 Statement stmt = null; 16 ResultSet rs = null; 17 //1.导入jar包 18 19 try { 20 //2.注册驱动 21 Class.forName("com.mysql.jdbc.Driver"); 22 //3.获取数据库连接对象Connection 23 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2","root","root"); 24 //4.获取执行SQL对象Statement 25 stmt = conn.createStatement(); 26 //5.定义SQL 27 String sql = "select * from account"; 28 //6.执行SQL 29 rs = stmt.executeQuery(sql); 30 //7.处理结果 31 while(rs.next()){//next()如果返回true则表示仍有数据。 32 int id = rs.getInt(1); 33 String name = rs.getString("name"); 34 double balance = rs.getDouble("balance"); 35 System.out.println(id +" " + name + " " + balance); 36 } 37 } catch (ClassNotFoundException e) { 38 e.printStackTrace(); 39 } catch (SQLException e) { 40 e.printStackTrace(); 41 } finally{ 42 if(rs != null){ 43 try { 44 rs.close(); 45 } catch (SQLException e) { 46 e.printStackTrace(); 47 } 48 } 49 if(stmt != null){ 50 try { 51 stmt.close(); 52 } catch (SQLException e) { 53 e.printStackTrace(); 54 } 55 } 56 if(conn != null){ 57 try { 58 conn .close(); 59 } catch (SQLException e) { 60 e.printStackTrace(); 61 } 62 } 63 } 64 65 } 66 }
(5)PreparedStatement:执行SQL的对象
SQL注入问题,表现为随便输入用户名,密码为:a' or 'a' = 'a 。 SQL为:select * from user where username = 'fhdshkkjfd' and password = 'a' or 'a' = 'a'
在拼接SQL时,有一些SQL的特殊关键字参与字符串的拼接,会造成安全性问题。
解决SQL注入的问题:使用preparedStatement对象来解决
预编译的SQL:参数使用?作为占位符
步骤:
1)导入驱动jar包
2)注册驱动
3)获取数据库连接对象Connection
4)定义SQL:主语SQL的参数使用?作为占位符。如:select * from user where username = ? password = ?;
5)获取执行SQL语句的对象PreparedStatement connection.prepareStatement(String sql)
6)给?赋值:方法为——setXxx(参数1,参数2); 参数1为?的位置编号,从1开始;参数2为?的值
7)执行SQL,接收返回结果,不需要传递SQL语句
8)处理结果
9)释放资源
示例代码
1 package cn.itcast.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.util.Scanner; 8 9 10 import cn.itcast.util.JDBCUtil; 11 12 public class JDBCDemo9 { 13 public static void main(String[] args) { 14 Scanner sc = new Scanner(System.in); 15 System.out.println("请输入用户名"); 16 String username = sc.nextLine(); 17 System.out.println("请输入密码"); 18 String password = sc.nextLine(); 19 20 boolean flag = new JDBCDemo9().login(username, password); 21 if (flag) { 22 System.out.println("登录成功"); 23 } else { 24 System.out.println("用户名密码错误"); 25 } 26 27 } 28 29 public boolean login(String username, String password){ 30 if(username == null || password == null){ 31 return false; 32 } 33 34 Connection conn = null; 35 PreparedStatement preStmt = null; 36 ResultSet rs = null; 37 38 try { 39 //1.注册驱动并获取连接 40 conn = JDBCUtil.getConnection(); 41 //2.定义SQL 42 String sql = "select * from user where name = ? and password = ?"; 43 //3.获取执行SQL的对象 PreparedStatement 44 preStmt = conn.prepareStatement(sql); 45 preStmt.setString(1,username); 46 preStmt.setString(2,password); 47 //4.执行查询,不需要传递SQL的方法 48 rs = preStmt.executeQuery(); 49 return rs.next();//如果有下一行则返回true 50 51 } catch (SQLException e) { 52 e.printStackTrace(); 53 } finally{ 54 JDBCUtil.close(rs,preStmt,conn); 55 } 56 return false; 57 } 58 }
5.抽取JDBC工具类 JDBCUtils
目的:简化书写
分析
抽取一个方法获取连接
需求:不想传递参数,还需要保证工具类的通用性。
解决方案:配置文件
定义jdbc.properties配置文件,将来只需要读取配置文件就可以拿到参数。
抽取一个方法释放资源
实例代码
1 package cn.itcast.util; 2 3 import java.io.FileReader; 4 import java.io.IOException; 5 import java.net.URL; 6 import java.sql.Connection; 7 import java.sql.DriverManager; 8 import java.sql.ResultSet; 9 import java.sql.SQLException; 10 import java.sql.Statement; 11 import java.util.Properties; 12 13 public class JDBCUtil { 14 private static String url; 15 private static String user; 16 private static String password; 17 private static String driver; 18 19 /** 20 * 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块 21 */ 22 static { 23 //读取资源文件,并获值 24 25 try { 26 //1.创建Properties集合类 27 Properties pro = new Properties(); 28 //获取src路径下的文件的方式 --> ClassLoader类加载器 29 ClassLoader classLoader = JDBCUtil.class.getClassLoader(); 30 //URL 统一资源定位符 31 URL res = classLoader.getResource("jdbc.properties");//传一个文件名,可以获得它的资源,以src为根目录 32 String path = res.getPath(); 33 34 //2.加载文件 35 pro.load(new FileReader(path)); 36 //3.获取数据,赋值 37 url = pro.getProperty("url"); 38 user = pro.getProperty("user"); 39 password = pro.getProperty("password"); 40 driver = pro.getProperty("driver"); 41 42 //4.注册驱动 43 Class.forName(driver); 44 } catch (IOException e) { 45 e.printStackTrace(); 46 } catch (ClassNotFoundException e) { 47 e.printStackTrace(); 48 } 49 } 50 51 /** 52 * 获取连接 53 * @return 连接对象 54 */ 55 public static Connection getConnection() throws SQLException { 56 return DriverManager.getConnection(url,user,password); 57 } 58 59 /** 60 * 释放资源 61 */ 62 public static void close(Statement stmt, Connection conn){ 63 if(stmt != null){ 64 try { 65 stmt.close(); 66 } catch (SQLException e) { 67 e.printStackTrace(); 68 } 69 } 70 if(conn != null){ 71 try { 72 conn.close(); 73 } catch (SQLException e) { 74 e.printStackTrace(); 75 } 76 } 77 } 78 79 public static void close(ResultSet rs, Statement stmt, Connection conn){ 80 if(rs != null){ 81 try { 82 rs.close(); 83 } catch (SQLException e) { 84 e.printStackTrace(); 85 } 86 } 87 if(stmt != null){ 88 try { 89 stmt.close(); 90 } catch (SQLException e) { 91 e.printStackTrace(); 92 } 93 } 94 if(conn != null){ 95 try { 96 conn.close(); 97 } catch (SQLException e) { 98 e.printStackTrace(); 99 } 100 } 101 } 102 }
6.JDBC控制事务
事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败
操作:开启事务,提交事务,回滚事务。
使用Connection对象来管理事务
开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
在执行SQL之前开启事务
提交事务:commit()
在所有SQL都执行完提交事务
回滚事务:rollback()
在catch中回滚事务
示例代码
1 package cn.itcast.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.SQLException; 6 7 import cn.itcast.util.JDBCUtil; 8 9 /** 10 * 事务操作 11 */ 12 public class JDBCDemo10 { 13 public static void main(String[] args) { 14 15 Connection conn = null; 16 PreparedStatement pstmt1 = null; 17 PreparedStatement pstmt2 = null; 18 try { 19 //1.获取数据库连接 20 conn = JDBCUtil.getConnection(); 21 22 //开启事务 23 conn.setAutoCommit(false); 24 25 26 //2.定义SQL 27 //2.1 张三减去500 28 String sql1 = "update account set balance = balance - ? where id = ?"; 29 //2.2 李四加上500 30 String sql2 = "update account set balance = balance + ? where id = ?"; 31 //3.获取SQL执行对象 32 pstmt1 = conn.prepareStatement(sql1); 33 pstmt2 = conn.prepareStatement(sql2); 34 //4.设置参数 35 pstmt1.setDouble(1,500); 36 pstmt1.setInt(2,1); 37 38 pstmt2.setDouble(1,500); 39 pstmt2.setInt(2,2); 40 //5.执行SQL 41 pstmt1.executeUpdate(); 42 43 //手动制造异常 44 int i = 3/0; 45 pstmt2.executeUpdate(); 46 47 //提交事务 48 conn.commit(); 49 } catch (Exception e) { 50 //事务的回滚 51 try { 52 if(conn !=null) 53 conn.rollback(); 54 } catch (SQLException e1) { 55 e1.printStackTrace(); 56 } 57 e.printStackTrace(); 58 } finally{ 59 if(pstmt1 != null){ 60 try { 61 pstmt1.close(); 62 } catch (SQLException e) { 63 e.printStackTrace(); 64 } 65 } 66 JDBCUtil.close(pstmt2,conn); 67 } 68 } 69 }