JDBC(Java Data Base Connectivity,Java数据库连接)是一种用于执行SQL语句的Java API,为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成
JDBC 规范定义接口,其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
JDBC 是 Java 访问数据库的标准规范,真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。所以我们只需要会调用 JDBC 接口中的方法即可,数据库驱动由数据库厂商提供
使用 JDBC 的好处
程序员如果要开发访问数据库的程序,只需要会调用 JDBC 接口中的方法即可,不用关注类是如何实现的。使用同一套 Java 代码,进行少量的修改就可以访问其他 JDBC 支持的数据库有了JDBC,程序员只需用JDBC API写一个程序,就可以访问所有数据库。将Java语言和JDBC结合起来使程序员不必为不同的平台编写不同的应用程序,只须写一遍程序就可以让它在任何平台上运行,这也是Java语言“编写一次,处处运行”的优势。方法一:
点击file->Project Structure。点击左边的Modules->dependencies->右上角的+号->选择第一个选项。选择jdbc包的路径,点击ok就行。方法二:直接在jar包所在lib目录右击,显示 Add as Library点击即可
static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager 。
写代码使用: Class.forName("com.mysql.jdbc.Driver");
通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }获取数据库连接
方法:static Connection getConnection(String url, String user, String password)
参数
url:指定连接的路径
语法:jdbc:mysql://ip地址(域名):端口号/数据库名称例子:jdbc:mysql://localhost:3306/db3细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称user:用户名
password:密码
Connection 接口,具体的实现类由数据库的厂商实现,代表一个连接对象
功能
获取执行sql 的对象 Statement createStatement() //创建一条 SQL 语句对象PreparedStatement prepareStatement(String sql) 管理事务 setAutoCommit(boolean autoCommit) :开启事务调用该方法设置参数为false,即开启事务commit() :提交事务rollback() :回滚事务代表一条语句对象,用于发送 SQL 语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象。
作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录
ResultSet对象是executeQuery()方法的返回值,它被称为结果集,它代表符合SQL语句条件的所有行,并且它通过一套getXXX方法(这些get方法可以访问当前行中的不同列)提供了对这些行中数据的访问。
ResultSet里的数据一行一行排列,每行有多个字段,且有一个记录指针,指针所指的数据行叫做当前数据行,我们只能来操作当前的数据行。我们如果想要取得某一条记录,就要使用ResultSet的next()方法 ,如果我们想要得到ResultSet里的所有记录,就应该使用while循环。
ResultSet对象自动维护指向当前数据行的游标。每调用一次next()方法,游标向下移动一行。
初始状态下记录指针指向第一条记录的前面,通过next()方法指向第一条记录。循环完毕后指向最后一条记录的后面。
boolean next():游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false, 如果不是则返回true
getXxx(参数):获取数据
Xxx:代表数据类型 如: int getInt() , String getString()参数: 通过列号:int:代表列的编号,从1开始 如: getString(1)通过字段名:String:代表列名称。 如: getDouble(“balance”)关于 ResultSet 接口中的注意事项:
如果光标在第一行之前,使用 rs.getXX()获取列值,报错:Before start of result set
如果光标在最后一行之后,使用 rs.getXX()获取列值,报错:After end of result set
使用完毕以后要关闭结果集 ResultSet,再关闭 Statement,再关闭 Connection
java.sql.Date、Time、Timestamp(时间戳),三个共同父类是:java.util.Date
java.sql.Date和java.util.Date的区别
sql.Date继承util.Date
sql下的Date只有年月日 util下的Date既有年月日,又有时分秒
sql下的Date有无参构造器,util下的Date没有无参构造器
PreparedStatement 是 Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语句
安全性更高,可以防止SQL注入
prepareStatement()会先将 SQL 语句发送给数据库预编译。PreparedStatement 会引用着预编译后的结果。可以多次传入不同的参数给 PreparedStatement 对象并执行。减少 SQL 编译次数,提高效率。
提高了程序的可读性
注意:后期都会使用PreparedStatement来完成增删改查的所有操作
SQL注入问题
在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
输入用户随便,输入密码:a’ or ‘a’ = 'asql:select * from user where username = ‘fhdsjkf’ and password = ‘a’ or ‘a’ = ‘a’解决sql注入问题:使用PreparedStatement对象来解决
1. 导入驱动jar包
mysql-connector-java-5.1.37-bin.jar
复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下右键–>Add As Library2. 加载和注册驱动(反射, DriverManager)
加载JDBC驱动是通过调用方法java.lang.Class.forName(),下面列出常用的几种数据库驱动程序加载语句的形式 :
Class.forName(“oracle.JDBC.driver.OracleDriver”);//使用Oracle的JDBC驱动程序 Class.forName(“com.microsoft.JDBC.sqlserver.SQLServerDriver”);//使用SQL Server的JDBC驱动程序 Class.forName(“com.ibm.db2.JDBC.app.DB2Driver”);//使用DB2的JDBC驱动程序 Class.forName("com.mysql.JDBC.Driver");//使用MySql的JDBC驱动程序3. 获取数据库连接对象 Connection
与数据库建立连接的方法是调用DriverManager.getConnection(String url, String user, String password )方法
Connection conn=null; String url="jdbc:mysql://127.0.0.1:3306/db_demo?useUnicode=true&characterEncoding=utf8&useSSL=false"; String user=“root"; String password=“root"; conn = DriverManager.getConnection(url, user, password);连接数据库的 URL 地址格式:
协议名:子协议://服务器名或 IP 地址:端口号/数据库名?参数=参数值
注意:
服务器名或IP地址 写为 127.0.0.1 和localhost代表连接本机,也可填写其他主机的ip地址,进行远程访问,前提是已经授权了远程访问
MySQL授权远程访问代码如下
grant all privileges on *.* to root@'%' identified by 'root' with grant option; flush privileges; *.* 中第一个*代表所有的database 第二个*代表所有database下的所有表格root@’%’ :代表任意ip均可连接本机器 root@ ‘192.168.10.1’ 代表ip为192.168.10.1的用户可连接本机器‘root’ ''中间代表数据库的连接密码with grant option :让连接用户拥有授权的权限MySQL 中可以简写:jdbc:mysql:///数据库名
前提:必须是本地服务器,端口号是 3306注意
如果数据库出现乱码,可以指定参数: ?characterEncoding=utf8,表示让数据库以 UTF-8 编码来处理数据。jdbc:mysql://localhost:3306/数据库?characterEncoding=utf8useUnicode=true :使用unicode编码 characterEncoding=utf8 :字符编码设为utf-8 useSSL=false :不使用ssl协议,不写容易出警告4. 定义sql
5. 获取执行sql语句的对象 Statement
Statement对象用于将 SQL 语句发送到数据库中,或者理解为执行sql语句
//第一种 可能出现sql注入问题 Statement stmt = conn.createStatement(); //第二种 可以解决sql注入问题 PreparedStatement pstmt =conn.prepareStatement(sql); pstmt.setString(1, username); pstmt.setString(2, pwd);6. 发送并执行SQL语句, 得到结果(查询会得到ResultSet, 增删改会得到整数, 表示影响的行数)
//发送SQL语句, 得到结果ResultSet ResultSet rs = stmt.executeQuery(sql); // 发送执行, 得到结果, 代表受影响的行数, 0表示操作无效 int rows = stmt.executeUpdate(sql);7. 处理结果
8. 释放资源(ResultSet, Statement, Connection)
需要释放的对象:ResultSet 结果集,Statement 语句,Connection 连接释放原则:先开的后关,后开的先关。ResultSet–> Statement–> Connection放在哪个代码块中:finally 块注意
作为一种好的编程风格,应在不需要Statement对象和Connection对象时显式地关闭它们。关闭Statement对象和Connection对象的语法形式为:public void close() throws SQLException
用户不必关闭ResultSet。当它的 Statement 关闭、重新执行或用于从多结果序列中获取下一个结果时,该ResultSet将被自动关闭。
注意:要按先ResultSet结果集,后Statement,最后Connection的顺序关闭资源,因为Statement和ResultSet是需要连接是才可以使用的,所以在使用结束之后有可能其他的Statement还需要连接,所以不能先关闭Connection。
建表和数据库
-- 创建database create database db_demo charset=utf8; -- 切换数据库 use db_demo; -- 创建表格 -- Integer是int的别名,相当于int(11) -- comment '' 注释 -- date:年月日 datetime:年月日时分秒 create table emp ( empno Integer primary key comment '编号', ename varchar(20) not null comment '姓名', job varchar(20) comment '职位', mgr Integer comment '领导编号', hiredate date comment '入职日期', sal double(7,2) comment '薪水', comm double(7,2) comment '提成', deptno Integer ); -- 录入测试数据 insert into emp values (1111, 'KING', 'PRESIDENT', null, '1990-02-06', 5000, null, 10), (1112, 'SMITH', 'CLERK', 1111, '1999-12-26', 2000, null, 10), (1113, '张三', 'CLERK', 1112, '1999-12-16', 1000, 200, 10); select * from emp; -- 截断表格, 相当于先删除表, 再重新创建 truncate table tb_user;jdbc代码
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * 创建一张学生表 */ public class Demo4DDL { public static void main(String[] args) { //1. 创建连接 Connection conn = null; Statement statement = null; try { conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day24", "root", "root"); //2. 通过连接对象得到语句对象 statement = conn.createStatement(); //3. 通过语句对象发送 SQL 语句给服务器 //4. 执行 SQL statement.executeUpdate("create table student (id int PRIMARY key auto_increment, " + "name varchar(20) not null, gender boolean, birthday date)"); //5. 返回影响行数(DDL 没有返回值) System.out.println("创建表成功"); } catch (SQLException e) { e.printStackTrace(); } //6. 释放资源 finally { //关闭之前要先判断 if (statement != null) { try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } 8 / 21 } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }缺陷:存在SQL注入风险
当我们输入以下密码,我们发现我们账号和密码都不对竟然登录成功了
问题分析:
我们让用户输入的密码和 SQL 语句进行字符串拼接。用户输入的内容作为了 SQL 语句语法的一部分,改变了原有 SQL 真正的意义,以上问题称为 SQL 注入。要解决 SQL 注入就不能让用户输入的密码和我们的 SQL 语句进行简单的字符串拼接。
import java.sql.*; import java.util.Scanner; public class Login { public static void main(String[] args) { String user = "root"; String password = "12345"; String url = "jdbc:mysql://127.0.0.1:3306/db_demo?useUnicode=true&characterEncoding=utf8&useSSL=false"; Connection conn = null; Statement stmt = null; ResultSet rs = null; Scanner sc = new Scanner(System.in); System.out.println("请输入用户名:"); String uname = sc.nextLine(); System.out.println("请输入密码:"); String pwds = sc.nextLine(); String sql = "select count(*) from tb_user where username='" + uname + "' and password='" + pwds + "'"; try { Class.forName("com.mysql.jdbc.Driver"); // [2] 获取连接对象Connection conn = DriverManager.getConnection(url, user, password); // [3] 基于连接对象获取发送器Statement stmt = conn.createStatement(); // [4] 发送SQL语句, 得到结果ResultSet rs = stmt.executeQuery(sql); if (rs.next()) { int count=rs.getInt(1); if(count==0){ System.out.println(sql); System.out.println("用户名密码错误,请重新登录!"); }else{ System.out.println(sql); System.out.println("登录成功!"); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { // 关闭资源 try { if (null != stmt) { rs.close(); } } catch (SQLException e) { e.printStackTrace(); } try { if (null != stmt) { stmt.close(); } } catch (SQLException e) { e.printStackTrace(); } try { if (null != conn) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } }为了解决sql注入问题,需要采用PreparedStatement。
public class Login { public static void main(String[] args) { // 创建扫描对象Scanner Scanner sc = new Scanner(System.in); // 接收用户输入的用户名和密码 System.out.println("请输入用户名和密码进行登录: "); String username = sc.nextLine(); String pwd = sc.nextLine(); // 声明相关信息 String url = "jdbc:mysql://localhost:3306/db_demo?useUnicode=true&characterEncoding=utf8&useSSL=false"; String user = "root"; String password = "root"; Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; // SQL语句中的占位符, ? String sql = "select count(*) from tb_user where username=? and password=?"; System.out.println("sql = " + sql); try { // 注册驱动 Class.forName("com.mysql.jdbc.Driver"); // 获取连接 conn = DriverManager.getConnection(url, user, password); // 发送器 pstmt = conn.prepareStatement(sql); // 为sql绑定参数 pstmt.setString(1, username); pstmt.setString(2, pwd); // 执行得到结果集 // rs = stmt.executeQuery(sql); rs = pstmt.executeQuery();// 不要将sql传递了 // 处理结果集 if(rs.next()) { int count = rs.getInt(1); if(count == 0) { System.out.println("用户名密码错误, 请重新登录!"); } else { System.out.println("登录成功!"); } } } catch (Exception e) { e.printStackTrace(); } finally { // 关闭资源 try { if(rs != null){ rs.close(); } } catch (SQLException e) { e.printStackTrace(); } try { if(pstmt != null){ pstmt.close(); } } catch (SQLException e) { e.printStackTrace(); } try { if(conn != null){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } }db.Properties
# mysql连接相关信息 db.driver=com.mysql.jdbc.Driver db.user=root db.password=12345 db.url=jdbc:mysql://localhost:3306/db_oa?useUnicode=true&characterEncoding=UTF8&useSSL=falseDBUtil.java
import java.io.IOException; import java.sql.*; import java.util.Properties; /** * JDBC操作工具类, 提供注册驱动, 连接, 发送器, 动态绑定参数, 关闭资源等方法 * jdbc连接参数的提取, 使用Properties进行优化(软编码) */ public class DBUtil { private static String driver; private static String url; private static String user; private static String password; static { // 借助静态代码块保证配置文件只读取一次就行 // 创建Properties对象 Properties prop = new Properties(); try { // 加载配置文件, 调用load()方法 // 类加载器加载资源时, 去固定的类路径下查找资源, 因此, 资源文件必须放到src目录才行 prop.load(DBUtil.class.getClassLoader().getResourceAsStream("db.properties")); // 从配置文件中获取数据为成员变量赋值 driver = prop.getProperty("db.driver").trim(); url = prop.getProperty("db.url").trim(); user = prop.getProperty("db.user").trim(); password = prop.getProperty("db.password").trim(); // 加载驱动 Class.forName(driver); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 统一关闭资源 * * @param rs * @param stmt * @param conn */ public static void close(ResultSet rs, Statement stmt, Connection conn) { try { if(rs != null){ rs.close(); } } catch (SQLException e) { e.printStackTrace(); } try { if(stmt != null){ stmt.close(); } } catch (SQLException e) { e.printStackTrace(); } try { if(conn != null){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } /** * 动态绑定参数 * * @param pstmt * @param params */ public static void bindParam(PreparedStatement pstmt, Object... params) { try { for (int i = 0; i < params.length; i++) { pstmt.setObject(i + 1, params[i]); } } catch (SQLException e) { e.printStackTrace(); } } /** * 预处理发送器 * * @param conn * @param sql * @return */ public static PreparedStatement getPstmt(Connection conn, String sql) { PreparedStatement pstmt = null; try { pstmt = conn.prepareStatement(sql); } catch (SQLException e) { e.printStackTrace(); } return pstmt; } /** * 获取发送器的方法 * * @param conn * @return */ public static Statement getStmt(Connection conn) { Statement stmt = null; try { stmt = conn.createStatement(); } catch (SQLException e) { e.printStackTrace(); } return stmt; } /** * 获取数据库连接的方法 * * @return */ public static Connection getConn() { Connection conn = null; try { conn = DriverManager.getConnection(url, user, password); } catch (SQLException e) { e.printStackTrace(); } return conn; } }