概念: Java DataBase Connectivity Java 数据库连接, Java语言操作数据库
JDBC 规范定义接口,具体的实现由各大数据库厂商来实现。
JDBC本质: 其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。 各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
JDBC 是 Java 访问数据库的标准规范,真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。所以我们只需要会调用 JDBC 接口中的方法即可,数据库驱动由数据库厂商提供。
下载mysql 连接数据库的驱动,下载地址:https://www.mysql.com/products/connector/
解压:
下面是一些字节码文件:
1、导入驱动jar包
复制 mysql-connector-java-5.1.47-bin.jar 到项目的 libs 目录下右键–>Add As Library 2、加载和注册驱动 3、获取数据库连接对象 Connection 4、定义sql 5、获取执行sql语句的对象 Statement 6、执行sql,接受返回结果 7、处理结果 8、释放资源注意: 使用时,导的包为 import java.sql.DriverManager;,是接口;而实际用的是 libs 文件夹下面的实现类。
疑问:为什么这样可以注册驱动?
public class Demo1 { public static void main(String[] args) throws ClassNotFoundException { //抛出类找不到的异常,注册数据库驱动 Class.forName("com.mysql.jdbc.Driver"); } }com.mysql.jdbc.Driver 源代码:
// Driver 接口,所有数据库厂商必须实现的接口,表示这是一个驱动类。 public class Driver implements java.sql.Driver { public Driver() throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); //注册数据库驱动 } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } } }注意: mysql5之后的驱动jar包可以省略注册驱动的步骤。Class.forName这句话可以省略。
原因:该jar包下面有了下面的配置文件,不写,会自动读取配置注册
类中的方法:
DriverManager 类中的静态方法描述Connection getConnection (String url, String user, String password)通过连接字符串,用户名,密码来得到数据库的连接对象Connection getConnection (String url, Properties info)通过连接字符串,属性对象来得到连接对象使用 JDBC 连接数据库的四个参数:
JDBC 连接数据库的四个参数说明用户名登录的用户名密码登录的密码连接字符串 URL不同的数据库 URL 是不同的,mysql 的写法:jdbc:mysql://localhost:3306/数据库[?参数名=参数值]驱动类的字符串名com.mysql.jdbc.Driver连接数据库的 URL 地址格式: 协议名:子协议://服务器名或 IP 地址:端口号/数据库名?参数=参数值
MySQL 写法: 乱码的处理: 如果数据库出现乱码,可以指定参数: ?characterEncoding=utf8,表示让数据库以 UTF-8 编码来处理数据。 jdbc:mysql://localhost:3306/数据库?characterEncoding=utf8
例子:
package cn.ys.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; /** * 得到连接对象 */ public class Demo2 { public static void main(String[] args) throws SQLException { String url = "jdbc:mysql://localhost:3306/day24"; //1) 使用用户名、密码、URL 得到连接对象 Connection connection = DriverManager.getConnection(url, "root", "root"); //com.mysql.jdbc.JDBC4Connection@68de145 System.out.println(connection); } }Connection 作用: Connection 接口,具体的实现类由数据库的厂商实现,代表一个连接对象。
Connection 方法:
Connection 接口中的方法描述Statement createStatement()创建一条 SQL 语句对象功能:
获取执行sql 的对象 Statement createStatement()PreparedStatement prepareStatement(String sql) 管理事务: 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务提交事务:commit()回滚事务:rollback()JDBC 访问数据库的步骤:
注册和加载驱动(可以省略)获取连接Connection 获取 Statement 对象使用 Statement 对象执行 SQL 语句返回结果集释放资源Statement 作用: 代表一条语句对象,用于发送 SQL 语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象。
Statement 接口中的方法:
boolean execute(String sql) :可以执行任意的sql(了解 )int executeUpdate(String sql) 执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功,返回值>0的则执行成功,反之,则失败。 ResultSet executeQuery(String sql) 执行DQL(select)语句,执行查询的操作。参数:SQL 语句。返回值:查询的结果集。释放资源:
需要释放的对象:ResultSet 结果集,Statement 语句,Connection 连接释放原则:先开的后关,后开的先关。ResultSet Statement Connection放在哪个代码块中:finally 块例子:
account表 添加一条记录account表 修改记录account表 删除一条记录DDL(create,alter、drop)语句 package cn.ys.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * account表 添加一条记录 insert 语句 */ public class JDBCDemo2 { public static void main(String[] args) { Statement stmt = null; Connection conn = null; try { //1. 注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2. 定义sql String sql = "insert into account values(null,'王五',3000)"; //3.获取Connection对象 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/javaee", "yangshuo", "123456"); //4.获取执行sql的对象 Statement stmt = conn.createStatement(); //5.执行sql int count = stmt.executeUpdate(sql);//影响的行数 //6.处理结果 System.out.println(count); if(count > 0){ System.out.println("添加成功!"); }else{ System.out.println("添加失败!"); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally { //stmt.close(); //7. 释放资源 //避免空指针异常 if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } } package cn.ys.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * account表 修改记录 */ public class JDBCDemo3 { public static void main(String[] args) { Connection conn = null; Statement stmt = null; try { //1. 注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2.获取连接对象 conn = DriverManager.getConnection("jdbc:mysql:///javaee", "yangshuo", "123456"); //3.定义sql String sql = "update account set balance = 1500 where id = 3"; //4.获取执行sql对象 stmt = conn.createStatement(); //5.执行sql int count = stmt.executeUpdate(sql); //6.处理结果 System.out.println(count); if(count > 0){ System.out.println("修改成功!"); }else{ System.out.println("修改失败"); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { 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(); } } } } } package cn.ys.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * account表 删除一条记录 */ public class JDBCDemo4 { public static void main(String[] args) { Connection conn = null; Statement stmt = null; try { //1. 注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2.获取连接对象 conn = DriverManager.getConnection("jdbc:mysql:///javaee", "yangshuo", "123456"); //3.定义sql String sql = "delete from account where id = 3"; //4.获取执行sql对象 stmt = conn.createStatement(); //5.执行sql int count = stmt.executeUpdate(sql); //6.处理结果 System.out.println(count); if(count > 0){ System.out.println("删除成功!"); }else{ System.out.println("删除失败"); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { 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(); } } } } } package cn.ys.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * 执行DDL语句 */ public class JDBCDemo5 { public static void main(String[] args) { Connection conn = null; Statement stmt = null; try { //1. 注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2.获取连接对象 conn = DriverManager.getConnection("jdbc:mysql:///javaee", "yangshuo", "123456"); //3.定义sql String sql = "create table student (id int , name varchar(20))"; //4.获取执行sql对象 stmt = conn.createStatement(); //5.执行sql int count = stmt.executeUpdate(sql); //6.处理结果 System.out.println(count); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { 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(); } } } } }作用: 封装数据库查询的结果集,对结果集进行遍历,取出每一条记录。 接口中的方法:
ResultSet 接口中的方法描述boolean next()游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true数据类型 getXxx()1) 通过字段名,参数是 String 类型。返回不同的类型;2) 通过列号,参数是整数,从 1 开始。返回不同的类型补充: getXxx(参数):获取数据
Xxx:代表数据类型 如: int getInt() , String getString()参数: 1. int:代表列的编号,从1开始 如: getString(1) 2. String:代表列名称。 如: getDouble(“balance”)注意: 使用步骤:
游标向下移动一行判断是否有数据获取数据常用数据类型转换表: 注:java.sql.Date、Time、Timestamp(时间戳),三个共同父类是:java.util.Date
package cn.ys.jdbc; import java.sql.*; /** * 执行DQL语句 */ public class JDBCDemo6 { public static void main(String[] args) { Connection conn = null; Statement stmt = null; ResultSet rs = null; try { //1. 注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2.获取连接对象 conn = DriverManager.getConnection("jdbc:mysql:///javaee", "yangshuo", "123456"); //3.定义sql String sql = "select * from account"; //4.获取执行sql对象 stmt = conn.createStatement(); //5.执行sql rs = stmt.executeQuery(sql); //6.处理结果 //循环判断游标是否是最后一行末尾。 while(rs.next()){ //获取数据 //6.2 获取数据 int id = rs.getInt(1); String name = rs.getString("name"); double balance = rs.getDouble(3); System.out.println(id + "---" + name + "---" + balance); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { //7.释放资源 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(); } } } } }关于 ResultSet 接口中的注意事项:
如果光标在第一行之前,使用 rs.getXX()获取列值,报错:Before start of result set如果光标在最后一行之后,使用 rs.getXX()获取列值,报错:After end of result set使用完毕以后要关闭结果集 ResultSet,再关闭 Statement,再关闭 Connection在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
输入用户随便,输入密码:a’ or ‘a’ = 'asql:select * from user where username = ‘fhdsjkf’ and password = ‘a’ or ‘a’ = ‘a’PreparedStatement 是 Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语句。
如果一个功能经常要用到,我们建议把这个功能做成一个工具类,可以在不同的地方重用。
上面写的代码中出现了很多重复的代码,可以把这些公共代码抽取出来。
jdbc.properties:
url=jdbc:mysql://localhost:3306/javaee user=yangshuo password=123456 driver=com.mysql.jdbc.Driver package cn.ys.utils; import java.io.FileReader; import java.io.IOException; import java.net.URL; import java.sql.*; import java.util.Properties; /** * JDBC工具类 */ public class JDBCUtils { private static String url; private static String user; private static String password; private static String driver; /** * 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块 */ static{ //读取资源文件,获取值。 try { //1. 创建Properties集合类。 Properties pro = new Properties(); //获取src路径下的文件的方式--->ClassLoader 类加载器 ClassLoader classLoader = JDBCUtils.class.getClassLoader(); URL res = classLoader.getResource("jdbc.properties"); String path = res.getPath(); //2. 加载文件 pro.load(new FileReader(path)); //3. 获取数据,赋值 url = pro.getProperty("url"); user = pro.getProperty("user"); password = pro.getProperty("password"); driver = pro.getProperty("driver"); //4. 注册驱动 Class.forName(driver); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 获取连接 * @return 连接对象 */ public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } /** * 释放资源 * @param stmt * @param conn */ public static void close(Statement stmt,Connection conn){ if( stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if( conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } /** * 释放资源 * @param stmt * @param conn */ public static void close(ResultSet rs,Statement stmt, Connection conn){ 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(); } } } } package cn.ys.jdbc; import cn.ys.entity.Emp; import cn.ys.utils.JDBCUtils; import java.sql.*; import java.util.ArrayList; import java.util.List; /** * * 定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回。 */ public class JDBCDemo8 { public static void main(String[] args) { List<Emp> list = new JDBCDemo8().findAll(); System.out.println(list); System.out.println(list.size()); } /** * 使用JDBC工具类,查询所有emp对象 * @return */ public List<Emp> findAll(){ Connection conn = null; Statement stmt = null; ResultSet rs = null; List<Emp> list = null; try { //2.获取连接 conn = JDBCUtils.getConnection(); //3.定义sql String sql = "select * from emp"; //4.获取执行sql的对象 stmt = conn.createStatement(); //5.执行sql rs = stmt.executeQuery(sql); //6.遍历结果集,封装对象,装载集合 Emp emp = null; list = new ArrayList<Emp>(); while(rs.next()){ //获取数据 int id = rs.getInt("id"); String ename = rs.getString("ename"); int job_id = rs.getInt("job_id"); int mgr = rs.getInt("mgr"); Date joindate = rs.getDate("joindate"); double salary = rs.getDouble("salary"); double bonus = rs.getDouble("bonus"); int dept_id = rs.getInt("dept_id"); // 创建emp对象,并赋值 emp = new Emp(); emp.setId(id); emp.setEname(ename); emp.setJob_id(job_id); emp.setMgr(mgr); emp.setJoindate(joindate); emp.setSalary(salary); emp.setBonus(bonus); emp.setDept_id(dept_id); //装载集合 list.add(emp); } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.close(rs,stmt,conn); } return list; } }注意: 上面的 JDBCUtils 工具类,重载了释放资源的方法。 因为:查询比增加和修改 要多释放 一个结果集
之前我们是使用 MySQL 的命令来操作事务。接下来我们使用 JDBC 来操作银行转账的事务。
一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
操作:
开启事务提交事务回滚事务定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回。
定义Emp类定义方法 public List findAll(){}实现方法 select * from emp;Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发