JDBC
1. jdbc概述
-
什么是 jdbc? 为什么要学习 jdbc?
- JDBC(Java DataBase Connectivity) :Java数据库连接技术:具体讲就是通过Java连接广泛的数据库,并对表中数据执行增、删、改、查等操作的技术
- jdbc 是java和数据库的连接的 桥梁。是所有持久层框架底层设计的核心。
2. 前期准备
-
创建数据库,在数据库中创建一个
Account
账目表,并插入三条记录,然后利用jdbc连接数据库并打印在控制台上。 -
开发步骤:
- 准备数据库
jdbc_db
,并创建表Account
,如下:
-- 创建数据库 create database jdbc_db charset utf8; -- 创建表数据 create table account( id int primary key auto_increment, acc_name varchar(20), money decimal (10.2) ); --插入数据 insert into account values(null,"张三",1000); insert into account values(null,"李四",1000); insert into account values(null,"王五",1000);
- 准备数据库
-
创建 JAVA工程
- 首先下载 java与数据库的 连接驱动,因为咱们使用的是
mysql 8.0.30
版本,以前的5.7
版本就无法使用,学习者需要注意版本。 - MySQL官网连接网址 然后下载,下载zip压缩即可。
- 首先下载 java与数据库的 连接驱动,因为咱们使用的是
- 将驱动程序,放到该工程中。
- 新建目录 lib ,复制驱动到该目录中。
- 将驱动添加到项目环境中。
- 选中驱动 右键 > add as library。确认即可。
- 选中驱动 右键 > add as library。确认即可。
3. JAVA 访问数据库
3.1 Jdbc 快速入门案例
-
查询表,并将结果显示到控制台。
- 需要六个步骤,如下图:
- 数据库驱动,
com.mysql.cj.jdbc.Driver
- url
jdbc:mysql://localhost:3306/数据库?characterEncoding=utf-8
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* java 连接数据库快速入门案例
* @Author Old Wu
*/
public class Jdbc_Connection {
public static void main(String[] args) throws Exception{
//1.注册数据库驱动,com.mysql.cj.jdbc.Driver 是新版本,之前5.x com.mysql.jdbc.Driver,需要注意。
Class.forName("com.mysql.cj.jdbc.Driver"); //为了测试直接抛 throws Exception
//2.获取数据库连接
/*
url参数用来确定连接的数据库信息: 数据库机器ip 端口号port 数据库名db_name 连接的参数,比如编解码集
url格式:jdbc:mysql://ip:port/db_name?k=v参数
2.2 要使用 import java.sql.Connection;
*/
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc_db?characterEncoding=utf-8",
"root", "root");
// 3.获取传输器
Statement statement = con.createStatement();
//4.发送sql到服务器,等待返回结果。
String select = "select * from account"; // sql 查询语句
ResultSet res = statement.executeQuery(select);//发送查询结果,返回结果集
//5.处理结果
//遍历结果集
while (res.next()){ //判断结果集中是否有数据,有返回true,无则false。
int id = res.getInt("id");
String acc_name = res.getString("acc_name");
BigDecimal money = res.getBigDecimal("money");
System.out.println("输出结果: "+id+" : "+acc_name+" : "+money);
}
// 6.释放资源
// 6.1 原则,先获取的后释放,后获取的先释放
res.close();
statement.close();
con.close();
System.out.println("sql执行完成");
}
}
3.2 jdbc入门中参数解释
-
注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
- 这句语句并没有返回值,它是否有意义?
- 相当于加载该类时,静态方法就注册了驱动。jdbc就加载mysql驱动,并管理驱动。
-
获取数据库连接
Connection conn = DriverManager.getConnection(url,username,password);
- url 连接
jdbc:mysql://localhost:3306/jdbc_db?characterEncoding=utf-8"
; characterEncoding=utf-8
防止中文乱码,别写错(写错不会提示,就不生效)。
- url 连接
- 传输器
Statement statement = conn.createStatement();
- 该方法相当于获取向数据库发送sql语句的传输对象,
Statement
,该对象上提供了发送sql方法。
- 该方法相当于获取向数据库发送sql语句的传输对象,
1.executeQuery(String sql) :用于向数据库发送查询类型的sql语句,返回是一个ResultSet对象中。
-- 相当于返回的是一个表格,是一个结果集,需要使用ResultSet去处理。
2.executeUpdate(String sql): 用于向数据库发送更新(增加,删除,修改)类型sql语句,返回一个int值,表示的是影响的是记录行数。
-
结果集对象 ResultSet
- 用户封装查询语句,非常重要的一个对象。上面提供了获取数据的遍历方法。
next()
遍历数据行的方法,指向数据行索引,并移动下一行,返回结果true 则箭头指向行有数据,false则无。- 提供了获取列的方法如:
getInt().... getString()
等。
-
释放结果
- 资源一定要按照顺序关闭,越晚获取的越早关闭。
- 建议
try{}catch(){}finall{...}
,以免造成上面异常资源没关出现。
res.close();
statement.close();
conn.close();
推荐使用 finall{
}
if (statement!=null) { //如果不为空就关闭。
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
4. Jdbc 的增删改查
4.1 新增
- 往
account表
中插入一条记录,姓名 “赵六”,money 为3500的记录;
/**
* jdbc 相关操作,增删改查
* @Author Old Wu
*/
public class Jdbc_Test {
@Test
public void insert_Test(){
//还是6个步骤;并在最后需要处理异常
// 7.在finall处理关闭资源
Statement statement =null;
Connection connection =null;
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.创建数据库连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc_db?characterEncoding=utf-8",
"root", "root");
//3.获取传输器
statement = connection.createStatement();
//4,发送sql等待返回结果,这里面使用单引号和双引号一样,但是双引号需要转义。
String sql = "insert into account values(4,'赵六','3500')";
int i = statement.executeUpdate(sql);
//5,处理结果
System.out.println("影响条数"+i);
//System.out.println("验证");
// statement.close(); //在finally去处理。
} catch (Exception e) {
e.printStackTrace();
}finally {
//6关闭资源,标准写法
if (statement!=null) { //如果不为空就关闭。
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
4.2 修改
- 将
account
表中,名为张三的记录中money修改为 2000;- 会发现每次都需要写重复代码,注册,连接,关资源等,如何处理? 将重复代码封装成工具类
JdbcUtils.class
,以减少代码臃肿。 - 代码片段,如下:
- 会发现每次都需要写重复代码,注册,连接,关资源等,如何处理? 将重复代码封装成工具类
mian 方法同上 新增。
//2.修改方法
@Test
public void update_Test(){
// 将方法提取出,finally 无法找到变量
Connection conn = null;
Statement statement=null;
try {
//获取连接方法
conn = JdbcUtils.getConn();
statement = conn.createStatement();
//发送sql给数据库,等待返回数据
String sql ="update account set money=2000 where acc_name ='张三'";
int i = statement.executeUpdate(sql);
System.out.println("影响结果 "+i);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(conn,statement,null); // 没有就null
}
}
- 封装的JdbcUtils.class,工具类。
/**
* 该工具为jdbc 封装连接工具
* @Author lpx
*/
public class JdbcUtils {
private JdbcUtils(){} //私有构造方法
/**
* 连接数据库
* @return
*/
public static Connection getConn(){
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc_db?characterEncoding=utf-8",
"root", "root");
return connection;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 关闭资源方法。
* @param resultSet
* @param statement
* @param connection
*/
public static void close(ResultSet resultSet,Statement statement,Connection connection){
if (resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement!=null) { //如果不为空就关闭。
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
4.3 查询
- 将
account
表中,查询姓名为 张三的记录。- 查询,需要
ResultSet
对象。 - 代码片段,如下:
- 查询,需要
//3.查询方法
@Test
public void select_Test(){
Connection conn=null;
Statement statement=null;
ResultSet resultSet = null;
try {
conn = JdbcUtils.getConn();
statement = conn.createStatement();
resultSet = statement.executeQuery("select * from account where acc_name ='张三'");
//遍历方法查询结果
while (resultSet.next()){
int id = resultSet.getInt("id");
String acc_name = resultSet.getString("acc_name");
String money = resultSet.getString("money");
System.out.println("查询结果 :"+ id +":"+acc_name+":"+money);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(conn,statement,resultSet);
}
}
4.4 删除
- 将
account
表中,id 为4的记录。- 代码片段,如下:
//4.删除方法
@Test
public void delete_Test(){
Connection conn =null;
Statement statement =null;
try {
conn = JdbcUtils.getConn();
statement = conn.createStatement();
int i = statement.executeUpdate("delete from account where id = 4");
System.out.println("影响条数"+i);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.close(conn,statement,null);
}
}
5. PreparedStatement
- 上面案例中使用的是传输器
Statement
传输对象,而在开发中我们用的更多的传输器对象是PreparedStatement
传输对象,该对象是Statement
子接口,更安全,并且更能提高程序的执行效率。
5.1 模拟登录案例
- 通过模拟登录案例,引发出一个重要的安全内容,
SQL 注入
问题。- 在之前准备创建表名
user
,在数据库jdbc_db
中,并插入数据。 - 新建类名
LoginUser.class
。模拟登录环境与数据库对接数据。
- 在之前准备创建表名
-- 在数据库jdbc_db,中创建表user
create table user(
id int primary key auto_increment,
username varchar(20),
password varchar(20)
);
-- 插入相关数据
insert into user values(null,"张三","123");
insert into user values(null,"李四","1234");
/**
* 注意提前创建表,和插入数据。
* 模拟登录案例。
*/
public class LoginUser {
public static void main(String[] args) {
//1.提示用户输入用户名和密码
System.out.println("请输入用户名:");
String userName = new Scanner(System.in).next();
System.out.println("请输入密码: ");
String passWord = new Scanner(System.in).next();
//2.拿到了用户名和密码之后需要和数据库中的用户名和密码进行比较
login(userName,passWord);
}
/**
* 通过jdbc连接数据库进行校验输入用户名和密码
* @param userName
* @param passWord
*/
private static void login(String userName, String passWord) {
//1.如何连接数据库?6个步骤。
Connection conn =null;
Statement statement =null;
ResultSet resultSet =null;
try {
conn = JdbcUtils.getConn();
statement = conn.createStatement();
//2 需要注意实际上使用参数传递过来,进行拼串输入,用户名和密码 '"+userName+"'
resultSet = statement.executeQuery("select * from user where username='"+userName+"' and passwor d= '"+passWord+"'");
//处理返回的结果集,使用if就是看是否判断成功,即可。
if (resultSet.next()){
System.out.println("用户名与密码正确,登录成功");
}else{
System.out.println("用户名与密码错误,登录失败");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
//关闭资源
JdbcUtils.close(resultSet,statement,conn);
}
}
}
输出结果:
5.2 安全问题: SQL 注入
- 什么叫SQL注入? 什么情况下会引发这样的问题!? 该怎样去解决!?
- 使用
PreparedStatement
比Statement
安全的方式就解决。 - 注意以下,其中2种注入方式:
- 使用
5.3 如何解决Sql注入问题
- sql注入在2010年的时候,那时候技术不完善,但是随着现在技术的发展,早就有如何解决这样的问题了。
- 使用 正则表达式去校验,如输入框中输入的
or # 或者其它 字符
- 使用
PreparedStatement
比Statement
安全的方式就解决。 - 提示: 传递的参数需要使用
?
占位符去处理。
- 使用 正则表达式去校验,如输入框中输入的
/**
* 改造登录案例
使用PreparedStatement 替换 Statement解决注入问题。
*/
public class LoginUser2 {
public static void main(String[] args) {
System.out.println("请输入用户名:");
String userName = new Scanner(System.in).next();
System.out.println("请输入密码:");
String passWord = new Scanner(System.in).next();
//登录方法,通过jadb与数据库进行连接判断
login(userName,passWord);
}
/**
* 处理用户输入用户名和密码,通过jdbc连接判断数据库中,是否正确。
* @param userName
* @param passWord
*/
private static void login(String userName, String passWord) {
// 为了finally里能找到变量
Connection conn = null;
PreparedStatement ps =null;
ResultSet resultSet =null;
try {
conn = JdbcUtils.getConn();
//使用preparedStatement 传递的参数需要使用占位符去处理 ?
/*1.
String sql = "select * from user where username=? and password=?";
ps = conn.prepareStatement(sql);
相当于把 查询sql的骨架发送给数据库。
*/
String sql = "select * from user where username=? and password=?";
ps = conn.prepareStatement(sql);
//2.设置占位符的参数,可以设置相应的类型。
ps.setString(1,userName);
ps.setString(2,passWord);
//3. 将拼接好的 sql发送到数据库。
resultSet = ps.executeQuery();
//4处理返回结果
if (resultSet.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
JdbcUtils.close(resultSet,ps,conn);//ps使用多态进行处理。
}
}
}