一、需求
模拟登录,控制台输入账号和密码,判断是否登陆成功成功!
/**
* TODO:
* 1.明确jdbc的使用流程 和 详解讲解内部设计api方法
* 2.发现问题,引出preparedstatement
*
* TODO
* 输入密码账号
* 进行数据库信息查询
* 反馈登录成功还是登录失败
*
* TODO:
* 1.键盘输入事件,收集账号和密码信息
* 2.注册驱动
* 3.获取链接
* 4.创建statement
* 5.发送查询SQL语句,并获取返回结果
* 6.结果判断,显示登陆成功还是失败
* 7.关闭资源
*/
二、注册驱动
方案一:调用静态方法,但是会注册两个
DriverManager.registerDriver(new com.cj.jdbc.Driver())
注意:8+ ——> com.mysql.cj.jdbc.Driver
5+ ——> com.mysql.jdbc.Driver
问题:在DriverManager.registerDriver方法中,会注册一次驱动: registerDriver(driver, null);
在Driver类中的静态代码块中,也会注册一次驱动:DriverManager.registerDriver(new Driver());
这样就造成了一个性能消耗的问题
如下图,registerDriver
方法中进行了一次驱动注册
Driver类
中的静态代码块也会调用注册驱动
也就是说,我们其实会注册两个驱动,这个就属于性能消耗了。
大家思考一个问题,我们一定要用到这个驱动类,那只要用到了这个驱动类,就一定会触发静态代码块,也就是说我们不要去触发 DriverManager.registerDriver()
方法了
解决问题:只想注册一次
只触发静态代码块即可!Driver
触发静态代码块:
类加载机制:类加载的时刻,会触发静态代码块!
加载 [class文件 -> jvm虚拟机的class对象]
链接 [验证(检查文件类型) -> 准备(静态变量默认值) -> 解析(触发静态代码块)]
初始化 (给静态属性赋真实值)
触发类加载:
1.new 关键字
2.调用静态方法
3.调用静态属性
4.接口 1.8以后新特性 加default默认实现
5.反射
6.子类触发父类
7.程序的入口main
方案二:通过反射机制,触发类加载,触发静态代码块的调用
// new Driver();//只会触发一次,但这样写非常不雅,代码就很固定化
// 因为当前导的是mysql新版本的驱动,但如果有一天换成了oracle数据库,这个类就要改成oracle的类,就很不方便
Class.forName("com.mysql.cj.jdbc.Driver"); // 通过反射机制,触发类加载,触发静态代码块的调用
// 由于这里使用的是字符串,以后字符串 -> 提取到外部的配置文件 -> 可以在不改代码的情况下,完成数据库驱动的切换! -> 这样就很灵活
三、获取数据库连接重载方法对比
发现,DriverManager.getConnection()
是一个重载方法,即同名不同参,有三种参数传递形式,允许开发者,用不同的形式传入数据库连接的核心参数!
PS:这三种只是参数传递的形式不同,但最终选择哪种都无所谓。
核心属性:
1.数据库软件所在的主机的ip地址:127.0.0.1,或者本机的主机名:localhost
2.数据库软件所在的主机的端口号:3306
3.连接的具体库:itcast
4.连接的账号:root
5.连接的密码:123456
6.可选的信息:稍后说
三个参数
String url 数据库软件所在的信息,连接的具体库,以及其他可选信息
语法:jdbc:数据库管理软件名称[mysql,oracle]://ip地址|主机名:port端口号/数据库名?key=value&key=value 以 key-value形式填入可选信息!
必须以jdbc开头,它代表这个是连接JDBC的一种路径协议
示例 : jdbc:mysql://127.0.0.1:3306/itcast
jdbc:mysql://localhost:3306/itcast
本机的省略写法:(强调:必须是本机,并且端口号是3306方可省略 使用///)
PS:如果没省略却使用了///则一定会报错。而且既然要省略,ip地址和prot必须都省略,不能只省略一个。
如果你的数据库软件安装到本机,可以进行一些省略
jdbc:mysql:///itcast
String user 数据库的账号 root
String password 数据库的密码 123456
示例
Connection connection = DriverManager.getConnection("jdbc:mysql:///itcast", "root", "123456");
两个参数(更麻烦一些,推荐三个参数)
String url :次url和三个参数的url的作用一样!
Properties info:存储账号和密码
Properties 类似于 Map 只不过key = value 都是字符串形式的
key user:账号信息
key password:密码信息
我们可以来看一下源码的参数介绍
示例
Properties info = new Properties();
info.put("user", "root");
info.put("password", "123456");
Connection connection2 = DriverManager.getConnection("jdbc:mysql://localhost:3306/itcast", info);
一个参数
String url:数据库ip,端口号,具体的数据库 可选信息(账号密码)
jdbc:mysql/localhost:3306/itcast?user=root&password=123456;
携带固定的参数名 user password 传递账号和密码信息![规定!]
url的路径属性可选信息:
url?user=账号&password=密码
8.0.27版本驱动,下面都是一些可选属性!
8.0.25以后,自动识别时区!serverTimezone=Asia/Shanghai 不用添加!8.0.25之前版本,下面一句话还是要加的!
8版本以后,默认使用的就是utf-8格式,useUnicode=true&characterEncoding=utf8&useSSL=true 都可以省略了!
时区:会影响数据库插入时间类型的时间、编码格式是不是UFT-8、忽略SSL格式验证
我们使用的是8.0.27版本,下面这些其实都可以省略了
serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true
示例
Connection connection3 = DriverManager.getConnection("jdbc:mysql://localhost:3306/itcast?user=root&password=123456");
四、发送SQL语句的方法
statement发送SQL语句有很多种方法
我们需要根据不同的SQL类型进行原则。
executeUpdate
:返回值是一个int类型。
executeQuery
:返回值是一个ResultSet。
到底使用哪种方式呢?我们来看一下源码的介绍就明白了。
1)executeUpdate
总结:
SQL分类:DDL(容器创建,修改,删除) DML(增删改) DQL(查询) DCL(权限控制) TPL(事务控制语句)
参数:sql 非DQL
返回结果:int
情况1:DML 返回影响的行数,例如;删除了三条数据 return 3;插入了两条 return 2;
情况2:非DML return 0;
2)executeQuery
总结:
参数:sql DQL
返回:resultSet 结果封装对象
ResultSet resultSet = executeQuery(sql);
3)总结
DQL选择 executeQuery
,非DQL选择 executeUpdate
。
五、查询结果集解析 resultSet
我们之前在图形化界面中查询出来的结果如下图,是有行有列的。
但是到了Java代码中,它没有办法使用这种可视化帮我们展示,它只能用Java的面向对象的思维,将查询结果封装成了resultSet对象,我们应该理解,内部一定也是有行和有列的!和可视化工具是一样的!只不过是同样的结果,不同的体现形式而已。
1)获取行
resultSet -> 逐行获取数据,行 -> 行的列的数据
我们可以来看看 resultSet
的源码
最初resultSet的 cursor(光标)
会指向第一行的前一行,next()
方法即将 cursor
移向下一行
若下一行有数据,返回 true
,若下一行没数据,则返回 false
总结:
想要进行数据解析,我们需要进行两件事情:1.移动游标指定获取数据行 2.获取指定数据行的列数据即可
游标移动问题
resultSet内部包含一个游标,指定当前行数据!
默认游标指定的是第一行数据之前!
我们可以调用next()方法向后移动一行游标!
如果我们有很多行数据,我们可以使用while(next){获取每一行数据}
boolean = next() true:有更多行数据,并且向下移动一行
false:没有更多行数据,不移动!
来看一下 next()
的源码
其实还有一个方法叫 previos
,可以将光标往前移。
TODO:移动光标的方法有很多,只需要记next即可,配合while循环获取全部数据!
2)获取列
resultSet.get类型(String columnLabel | int columnIndex);
columnLabel
:列名,如果有别名,写别名
columnIndex
:列的下标获取,从左向右,从1开始
六、代码实现
package com.atguigu.api.statement;
import java.sql.*;
import java.util.Scanner;
public class StatementUserLoginPart {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
//1.获取用户输入信息
Scanner scanner = new Scanner(System.in);
System.out.println("请输入账号");
String account = scanner.nextLine();
System.out.println("请输入密码");
String password = scanner.nextLine();
// 1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");//通过反射机制,触发类加载,触发静态代码块的调用
// 2.获取数据库链接
Connection connection = DriverManager.getConnection("jdbc:mysql:///itcast", "root", "123456");
//3.创建发送SQL语句的statement对象
//statement 可以发送SQL语句到数据库,并且可以返回结果!
Statement statement = connection.createStatement();
//4.发送SQL语句(1.编写SQL语句 2.发送SQL语句)
String sql = "select * from employee where account = '" + account + "' and password = '" + password + "';";
ResultSet resultSet = statement.executeQuery(sql);
statement.executeUpdate(sql);
//5.查询结果集解析 resultSet
/*while(resultSet.next()) {
int id = resultSet.getInt(1);
int salary = resultSet.getInt("salary");
System.out.println(id + "--" + salary);
}*/
//但是当前需求下,没有必要使用while循环去遍历结果集,我们只需要移动一次光标,只要有数据,就代表登录成功
if (resultSet.next()) {
System.out.println("登陆成功");
} else {
System.out.println("登录失败");
}
//关闭资源,规则:先开的流最后关闭
connection.close();
statement.close();
resultSet.close();
}
}