什么是JDBC?
JDBC(Java DataBase Connectivity) Java数据库连接,其实就是利用Java语言/程序连接并访问数据库的一门技术
如何通过JDBC程序访问数据库?
(1)准备数据, 创建jt_db库, 创建account表
drop database if exists jt_db;
create database jt_db charset utf8;
use jt_db;
create table account(
id int primary key auto_increment,
name varchar(50),
money double
);
insert into account values(null, 'tom', 1000);
insert into account values(null, 'andy', 1000);
insert into account values(null, 'tony', 1000);
(2)创建JAVA工程:
(3)导入jar包——mysql驱动包:
(4)创建类并实现JDBC程序(六个步骤)
//1.注册数据库驱动
//2.获取数据库连接
//3.获取传输器
//4.发送SQL到服务器执行并返回执行结果
//5.处理结果
//6.释放资源
代码实现:
public static void main(String[] args) throws Exception {
//1.注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8",
"root", "root");
//3.获取传输器
Statement stat = conn.createStatement();
//4.发送SQL到服务器执行并返回执行结果
String sql = "select * from account";
ResultSet rs = stat.executeQuery( sql );
//5.处理结果
while( rs.next() ) {
int id = rs.getInt("id");
String name = rs.getString("name");
double money = rs.getDouble("money");
System.out.println(id+" : "+name+" : "+money);
}
//6.释放资源
rs.close();
stat.close();
conn.close();
System.out.println("TestJdbc.main()....");
}
执行结果:
1、注册数据库驱动
Class.forName(“com.mysql.jdbc.Driver”);
所谓的注册驱动,就是让JDBC程序加载mysql驱动程序,并管理驱动
驱动程序实现了JDBC API定义的接口以及和数据库服务器交互的功能,加载驱动是为了方便使用这些功能。
2、获取连接之数据库URL
DriverManager.getConnection() 用于获取数据连接,返回的Connection连接对象是JDBC程序连接数据库至关重要的一个对象。
DriverManager.getConnection(
"jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8",
"root", "root");
参数2和参数3为所连接数据库的用户名和密码
参数1为连接数据库的url地址,下面是其写法:
当连接本地数据库,并且端口为3306,可以简写为如下形式:(前面的三个斜杠不能省略)
jdbc:mysql:///jt_db
3、Statement传输器对象
Statement stat = conn.createStatement();
//该方法返回用于向数据库服务器发送sql语句的Statement传输器对象
该对象上提供了发送sql的方法:
executeQuery(String sql) --
//用于向数据库发送查询类型的sql语句,返回一个ResultSet对象中
executeUpdate(String sql) --
//用于向数据库发送更新(增加、删除、修改)类型的sql语句,返回一个int值,表示影响的记录行数
4、ResultSet结果集对象
ResultSet对象用于封装sql语句查询的结果,也是一个非常重要的对象。该对象上提供了遍历数据及获取数据的方法。
(1)遍历数据行的方法
next() – 使指向数据行的箭头向下移动一行,并返回一个布尔类型的结果,true表示箭头指向了一行数据,false表示箭头没有指向任何数据(后面也没有数据了)
(2)获取数据的方法
getInt(int columnIndex)
getInt(String columnLable)
getString(int columnIndex)
getString(String columnLable)
getDouble(int columnIndex)
getDouble(String columnLable)
getObject(int columnIndex)
getObject(String columnLable)
5、释放资源
rs.close();
stat.close();
conn.close();
此处释放资源必须按照一定的顺序释放,越晚获取的越先关闭。所以先关闭
rs对象,再关闭stat对象,最后关闭conn对象。
另外,为了避免上面的程序抛出异常,释放资源的代码不会执行,应该把释放资源的代码放在finally块中.
try{
...
}catch(Exception e){
...
}finally{
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
rs = null;
}
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
stat = null;
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
conn = null;
}
}
}
PreparedStatement
在开发中我们用的更多的传输器对象是PreparedStatement对象,PreparedStatement是Statement的子接口,比Statement更加安全,并且能够提高程序执行的效率。
模拟用户登录案例
(1)准备数据
use jt_db;
create table user(
id int primary key auto_increment,
username varchar(50),
password varchar(50)
);
insert into user values(null,'张三','123');
insert into user values(null,'李四','234');
(2)创建LoginUser 类,提供 main 方法 和 login 方法。
public static void main(String[] args) {
/* 1、提示用户登录,提示用户输入用户名并接收用户名
* 2、提示用户输入密码并接收密码
* 3、根据用户名和密码查询用户信息
*/
// 1、提示用户登录,提示用户输入用户名并接收用户名
Scanner sc = new Scanner(System.in);
System.out.println( "请登录:" );
System.out.println( "请输入用户名:" );
String user = sc.nextLine();
// 2、提示用户输入密码并接收密码
System.out.println( "请输入密码:" );
String pwd = sc.nextLine();
// 3、根据用户名和密码查询用户信息
login( user, pwd );
}
/**
* 根据用户名和密码查询用户信息
* @param user 用户名
* @param pwd 密码
*/
private static void login(String user, String pwd) {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
//1.注册驱动并获取连接
conn = JdbcUtil.getConn();
//2.获取传输器,执行sql并返回执行结果
stat = conn.createStatement();
String sql = "select * from user where username='"+user+"' and password='"+pwd+"'";
rs = stat.executeQuery(sql);
System.out.println( sql );
//3.处理结果
if( rs.next() ) { //有数据 -- 用户名密码都正确
System.out.println("恭喜您登录成功!");
}else { //没数据 -- 用户名或密码不正确
System.out.println("登录失败, 用户名或密码不正确!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.释放资源
JdbcUtil.close(conn, stat, rs);
}
}
请登录:
请输入用户名:
张飞'#'
请输入密码:
select * from user where username='张飞'#'' and password=''
恭喜您登录成功了!
或输入:
请登录:
请输入用户名:
张飞' or '1=1
请输入密码:
select * from user where username='张飞' or '1=1' and password=''
恭喜您登录成功了!
通过上面的案例,我们发现在执行时,不输入密码只输入用户名也可以登陆成功。这就是SQL注入攻击。
SQL注入攻击产生的原因: 由于后台执行的SQL语句是拼接而来的:
select * from user where username='"+user+"' and password='"+pwd+"'
其中的参数是用户提交过来的,如果用户在提交参数时,在参数中掺杂了一些SQL关键字(比如or)或者特殊符号(#、-- 、’ 等),就可能会导致SQL语句语义的变化,从而执行一些意外的操作(用户名或密码不正确也能登录成功)!
防止SQL注入攻击
如何防止SQL注入攻击?
(1)使用正则表达式对用户提交的参数进行校验。如果参数中有(# – ’ or等)这些符号就直接结束程序,通知用户输入的参数不合法
(2)使用PreparedStatement对象来替代Statement对象。
下面通过第二种方式解决SQL注入攻击:添加loginByPreparedSatement方法,在方法中,使用PreparedStatement来代替Statement作为传输器对象使用,代码示例:
/**
* 根据用户名和密码查询用户信息
* @param user 用户名
* @param pwd 密码
*/
private static void login(String user, String pwd) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.注册驱动并获取连接
conn = JdbcUtil.getConn();
//2.获取传输器,执行sql并返回执行结果
String sql = "select * from user where username=? and password=?";
ps = conn.prepareStatement( sql );
//设置SQL语句中的参数
ps.setString( 1 , user );
ps.setString( 2 , pwd );
//执行SQL语句
rs = ps.executeQuery();//这里不要再传输SQL语句
//3.处理结果
if( rs.next() ) { //有数据 -- 用户名密码都正确
System.out.println("恭喜您登录成功!");
}else { //没数据 -- 用户名或密码不正确
System.out.println("登录失败, 用户名或密码不正确!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.释放资源
JdbcUtil.close(conn, ps, rs);
}
}
再次执行程序,按照上面的操作登录。此时,已经成功的防止了SQL注入攻击问题了。
PreparedStatement对象是如何防止SQL注入攻击的:
使用PreparedStatement对象是先将SQL语句的骨架发送给服务器编译并确定下来,
编译之后SQL语句的骨架和语义就不会再被改变了,
再将SQL语句中的参数发送给服务器,
即使参数中再包含SQL关键字或者特殊符号,
也不会导致SQL语句的骨架或语义被改变,
只会被当作普通的文本来处理!