🥽 视频链接及资源下载
视频:【尚硅谷JDBC核心技术视频教程(康师傅带你一站式搞定jdbc)】
资源下载:【百度网盘】
提取码:1234
🥽 JDBC简介
JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准Java类库,(java.sql,javax.sql)使用这个类库可以以一种标准的方法、方便地访问数据库资源
JDBC驱动为对JDBC中定义的接口的实现。不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。
JDBC接口(API)包括两个层次:
- 面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。
- 面向数据库的API:Java Driver API,供开发商开发数据库驱动程序用。
JDBC API 是一系列的接口,它使得应用程序能够进行数据库连接,执行SQL语句,并且得到返回结果。Java程序员只需要面向这套接口编程即可。
🥽 JDBC程序访问数据库步骤
- 导入对应的包和驱动
- 创建连接数据库的驱动对象
- 加载注册连接数据库的驱动
- 通过驱动获取连接数据库的对象
- 通过连接数据库的对象获取执行SQL语句的对象
- 执行SQL语句得到结果集对象
- 关闭对应的对象和连接
🥽 获取数据库连接
🌊 导入jar包
压缩包下载到本地后解压,在IDE项目根目录下新建lib目录
把下载的压缩包解压后的jar包复制到lib目录下
复制完成后鼠标右键jar包,添加为库
🌊 Driver 接口
java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现
在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现
- Oracle的驱动:oracle.jdbc.driver.OracleDriver
- mySql的驱动: com.mysql.jdbc.Driver
- …
🌊 JDBC URL
JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。
JDBC URL的标准由三部分组成,各部分间用冒号分隔。
- jdbc:子协议:子名称
- 协议:JDBC URL中的协议总是jdbc
- 子协议:子协议用于标识一个数据库驱动程序
- 子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息。包含主机名(对应服务端的ip地址),端口号,数据库名
- 对于 Oracle 数据库连接,采用如下形式:
jdbc:oracle:thin:@localhost:1521:atguigu - 对于 SQLServer 数据库连接,采用如下形式:
jdbc:microsoft:sqlserver//localhost:1433; DatabaseName=sid - 对于 MYSQL 数据库连接,采用如下形式:
jdbc:mysql://localhost:3306/atguigu - …
🌊 方式1:
如有报错后面的解决方法可能会有帮助
@Test public void testConnection1() throws Exception { // 获取 Driver 驱动对象 Driver driver = new com.mysql.jdbc.Driver(); // 配置获取连接数据对象需要的信息 // url:http://localhost:8080/gmall/keyboard.jpg // jdbc:mysql:协议 // localhost:ip地址 // 3306:默认mysql的端口号 // test:test数据库 // 字符集与数据库不一致 ?characterEncoding=utf8 设置字符集 String url = "jdbc:mysql://localhost:3306/test?characterEncoding=utf8"; // 将用户名和密码封装到Properties对象中 Properties info = new Properties(); info.setProperty("user", "root"); info.setProperty("password", "123456"); // 通过驱动对象根据配置信息获取连接数据库的对象 Connection connection = driver.connect(url, info); // 打印连接对象,查看是否连接成功 System.out.println(connection); }
🌊 方式2:
对方式一的迭代:在如下的程序中不出现第三方的api,使得程序具有更好的可移植性
@Test public void testConnection2() throws Exception { // 获取 Driver 驱动对象 // 使用反射 // 获取连接数据库驱动的类 Class clazz = Class.forName("com.mysql.jdbc.Driver"); // 通过连接驱动的类创建连接驱动Driver对象 Driver driver = (Driver) clazz.newInstance(); // 数据库的url String url = "jdbc:mysql://localhost:3306/test?characterEncoding=utf8"; // 连接数据库需要的用户信息配置对象 Properties info = new Properties(); info.setProperty("user", "root"); info.setProperty("password", "123456"); // 获取连接数据库的对象 Connection connection = driver.connect(url, info); System.out.println(connection); }
🌊 方式3:
使用DriverManager替换Driver
DriverManager 类是驱动程序管理器类,负责管理驱动程序
- DriverManager.registerDriver(com.mysql.jdbc.Driver);
- 通常不用显式调用 DriverManager 类的 registerDriver() 方法来注册驱动程序类的实
例,因为 Driver 接口的驱动程序类都包含了静态代码块,在这个静态代码块中,会
调用 DriverManager.registerDriver() 方法来注册自身的一个实例
@Test public void testConnection3() throws Exception { // 获取 Driver 驱动对象 // 使用反射 Class clazz = Class.forName("com.mysql.jdbc.Driver"); Driver driver = (Driver) clazz.newInstance(); // 连接数据库的配置信息 String url = "jdbc:mysql://localhost:3306/test?characterEncoding=utf8"; String user = "root"; String password = "285013"; // 通过 DriverManager 注册驱动 DriverManager.registerDriver(driver); // 通过 DriverManager 获取连接对象 Connection connection = DriverManager.getConnection(url, user, password); System.out.println(connection); }
🌊 方式4:
可以只是加载驱动,不用显示的注册驱动,因为在加载驱动时候就会进行驱动的注册。
@Test public void testConnection4() throws Exception { // 连接需要的三个基本信息 String url = "jdbc:mysql://localhost:3306/test?characterEncoding=utf8"; String user = "root"; String password = "123456"; // 使用反射加载驱动 Class.forName("com.mysql.jdbc.Driver"); // 相较于方式3,省略了如下操作 // Driver driver = (Driver) clazz.newInstance(); // 通过 DriverManager 注册驱动 // DriverManager.registerDriver(driver); // 通过驱动管理获取数据库的连接 Connection connection = DriverManager.getConnection(url, user, password); System.out.println(connection); }
可以省略的原因:
在mysql的Driver实现类中,声明了如下的操作:
static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }
🌊 方式5(final版):
将数据库连接需要的4个基本信息声明在配置文件中,通过读取配置文件的方式,获取连接
此种方式的好处:
- 实现了数据与代码的分离。实现了解耦
- 如果需要修改配置文件信息,可以避免程序重新打包。
配置文件:
配置文件在src下,等号两边不能有空格!!!!!!
user=root password=123456 url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8 driverClass=com.mysql.jdbc.Driver
@Test public void testConnection5() throws Exception { // 通过反射获取读取配置文件的输入流 InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties"); // 配置信息对象 Properties properties = new Properties(); properties.load(is); // 加载配置信息 // 得到配置信息 String user = properties.getProperty("user"); String password = properties.getProperty("password"); String url = properties.getProperty("url"); String driverClass = properties.getProperty("driverClass"); // 加载驱动 Class.forName(driverClass); // 获取连接 Connection connection = DriverManager.getConnection(url, user, password); System.out.println(connection); }
🌊 连接报错:
💦 Cause: java.sql.SQLException: Unknown initial character set index ‘255’ received from server.
Cause: java.sql.SQLException: Unknown initial character set index '255' received from server.
🥽 使用Statement操作数据表的弊端【了解】
使用Statement的弊端:
- 需要拼写sql语句
- 存在SQL注入的问题
如何避免出现sql注入:只要用 PreparedStatement(从Statement扩展而来) 取代 Statement
SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令
User 类:
public class User { private String user; private String password; public User() { } public User(String user, String password) { super(); this.user = user; this.password = password; } @Override public String toString() { return "User [user=" + user + ", password=" + password + "]"; } public String getUser() { return user; } public void setUser(String user) { this.user = user; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
StatementTest 测试类:
public class StatementTest { // 使用Statement的弊端:需要拼写sql语句,并且存在SQL注入的问题 //如何避免出现sql注入:只要用 PreparedStatement(从Statement扩展而来) 取代 Statement @Test public void testLogin() { Scanner scanner = new Scanner(System.in); System.out.print("请输入用户名:"); String user = scanner.nextLine(); System.out.print("请输入密码:"); String password = scanner.nextLine(); //SELECT user,password FROM user_table WHERE user = '1' or ' AND password = '=1 or '1' = '1' String sql = "SELECT user,password FROM user_table WHERE user = '"+ user +"' AND password = '"+ password +"'"; User returnUser = get(sql, User.class); if(returnUser != null){ System.out.println("登录成功"); }else{ System.out.println("用户名不存在或密码错误"); } } // 使用Statement实现对数据表的查询操作 public <T> T get(String sql, Class<T> clazz) { T t = null; Connection conn = null; Statement st = null; ResultSet rs = null; try { // 1.加载配置文件 InputStream is = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties"); Properties pros = new Properties(); pros.load(is); // 2.读取配置信息 String user = pros.getProperty("user"); String password = pros.getProperty("password"); String url = pros.getProperty("url"); String driverClass = pros.getProperty("driverClass"); // 3.加载驱动 Class.forName(driverClass); // 4.获取连接 conn = DriverManager.getConnection(url, user, password); st = conn.createStatement(); rs = st.executeQuery(sql); // 获取结果集的元数据 ResultSetMetaData rsmd = rs.getMetaData(); // 获取结果集的列数 int columnCount = rsmd.getColumnCount(); if (rs.next()) { t = clazz.newInstance(); for (int i = 0; i < columnCount; i++) { // //1. 获取列的名称 // String columnName = rsmd.getColumnName(i+1); // 1. 获取列的别名 String columnName = rsmd.getColumnLabel(i + 1); // 2. 根据列名获取对应数据表中的数据 Object columnVal = rs.getObject(columnName); // 3. 将数据表中得到的数据,封装进对象 Field field = clazz.getDeclaredField(columnName); field.setAccessible(true); field.set(t, columnVal); } return t; } } catch (Exception e) { e.printStackTrace(); } finally { // 关闭资源 if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (st != null) { try { st.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } return null; } }
当输入的用户名为:1' or
密码为:=1 or '1' = '1
就会造成SQL注入问题
因为此时进行字符串拼接完成后,需要执行的SQL语句变为:
SELECT user,password FROM user_table WHERE user = '1' or ' AND password = '=1 or '1' = '1'
判断是否成立的条件有原来的两个条件相与变为三个条件相或:
user = '1' or ' AND password = '=1 or '1' = '1'
其中 '1' = '1'
一定成立,此时不管账号密码是否正确一定可以登录系统。
以上就是SQL注入问题。
🥽 PreparedStatement
🌊 PreparedStatement 实现表数据的添加
package preparedstatement; import org.junit.Test; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Properties; public class PreparedStatementUpdate { // 向customers表中添加一条数据 @Test public void testInsert() throws Exception { // 通过类加载器获取输入流 InputStream is = PreparedStatementUpdate.class.getClassLoader().getResourceAsStream("jdbc.properties"); // 创建数据库配置信息对象 Properties properties = new Properties(); properties.load(is); // 加载配置信息 // 得到配置信息 String user = properties.getProperty("user"); String password = properties.getProperty("password"); String url = properties.getProperty("url"); String driverClass = properties.getProperty("driverClass"); // 加载驱动 Class.forName(driverClass); // 获取数据库的连接对象 Connection conn = DriverManager.getConnection(url, user, password); // 预编译SQL语句,返回PreparedStatement实例对象 // sql语句 // ? 为占位符 String sql = "INSERT INTO customers(name, email, birth) VALUES (?, ?, ?);"; // 通过连接对象获取 PreparedStatement 对象 PreparedStatement ps = conn.prepareStatement(sql); // 填充占位符 第一个参数为填充占位符的索引(从1开始), 第二个参数为填充到占位符的数据 ps.setString(1, "ZS"); ps.setString(2, "zs@qq.com"); ps.setString(3, "1000-10-10"); // 执行操作 ps.execute(); // 资源的关闭 ps.close(); conn.close(); } }
SELECT * FROM customers WHERE name = 'ZS';
🌊 数据库连接与关闭操作的封装
package utils; import preparedstatement.PreparedStatementUpdate; import java.io.InputStream; import java.sql.*; import java.util.Properties; /** * 操作数据库的工具类 */ public class JDBCUtils { /** * 获取数据库的连接 * @return * @throws Exception */ public static Connection getConnection() throws Exception { // 通过类加载器获取输入流 InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties"); // 创建数据库配置信息对象 Properties properties = new Properties(); properties.load(is); // 加载配置信息 // 得到配置信息 String user = properties.getProperty("user"); String password = properties.getProperty("password"); String url = properties.getProperty("url"); String driverClass = properties.getProperty("driverClass"); // 加载驱动 Class.forName(driverClass); // 获取数据库的连接对象 Connection conn = DriverManager.getConnection(url, user, password); // 返回连接对象 return conn; } /** * 关闭资源操作 * @param connection * @param ps */ public static void closeResource(Connection connection, Statement ps) { // 如果存在Statement或Statement的子类对象,进行关闭操作 if (ps != null) { try { ps.close(); } catch (SQLException e) { throw new RuntimeException(e); } } // 进行数据库的关闭 if (connection != null) { try { connection.close(); } catch (SQLException e) { throw new RuntimeException(e); } } } }