1 数据库驱动
驱动:声卡、显卡、数据库
我们的程序会通过数据库驱动来和数据库打交道!
2 JDBC
SUN公司为了简化开发人员的(对数据库的统一)操作,提供了一个(Java操作数据库)的规范,俗称JDBC
这些规范的实现由具体的厂商去做~
对于开发人员来说,我们只需要掌握JDBC接口的操作即可!
Idea中内置了java.sql 、javax.sql包可供我们使用!
除此之外,还需要下载一个数据库驱动包:下载地址链接
3 第一个JDBC程序
1、首先准备要连接的数据库(代码如下)
CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;
USE jdbcStudy;
CREATE TABLE `users`(
id INT PRIMARY KEY,
NAME VARCHAR(40),
PASSWORD VARCHAR(40),
email VARCHAR(60),
birthday DATE
);
INSERT INTO `users`(id,NAME,PASSWORD,email,birthday)
VALUES(1,'zhansan','123456','zs@sina.com','1980-12-04'),
(2,'lisi','123456','lisi@sina.com','1981-12-04'),
(3,'wangwu','123456','wangwu@sina.com','1979-12-04')
2、导入数据库驱动
在新建的工程下创建lib目录,并将下载的数据库驱动.jar
复制到lib目录下,如图:
右键单击lib目录,选择ADD as Library...
导入完成后就和上图一样了!
随后在src目录下创建自己的类,可以参考我的创建,其中demut是我的账户名,可以自行修改:
3、编写测试代码
package com.demut.demo;
// 我的第一个JDBC程序
import java.sql.*;
public class jdbcFirstDemo {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver"); // 固定写法,加载驱动
// 2.用户信息和url
//?useUnicode=true&characterEncoding=utf8&useSSL=true
//支持中文编码&设置中文集&设置安全连接
String url = "jdbc:mysql://localhost:3306/jdbcStudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
String username = "root";
String password = "123456";
// 3.连接数据库, 使用DriverManager.getConnection()连接。
Connection connection = DriverManager.getConnection(url,username,password);
// 4.创建执行SQL的对象 Statement为执行sql的对象类
Statement statement = connection.createStatement();
// 5.执行SQL对象去执行SQL
String sql = "SELECT * FROM users";
ResultSet resultSet = statement.executeQuery(sql);//返回的结果集,结果集中封装了查询出来的所有结果
while (resultSet.next()){
System.out.println("id = " + resultSet.getObject("id"));
System.out.println("name = " + resultSet.getObject("name"));
System.out.println("pwd = " + resultSet.getObject("password"));
System.out.println("email = " + resultSet.getObject("email"));
System.out.println("birth = " + resultSet.getObject("birthday"));
System.out.println("==========================");
}
// 6.释放连接
resultSet.close();
statement.close();
connection.close();
}
}
/*运行结果
id = 1
name = zhansan
password = 123456
email = zs@sina.com
birthday = 1980-12-04
--------------------------
id = 2
name = lisi
password = 123456
email = lisi@sina.com
birthday = 1981-12-04
--------------------------
id = 3
name = wangwu
password = 123456
email = wangwu@sina.com
birthday = 1979-12-04
--------------------------
*/
步骤总结:
1、加载驱动
2、连接数据库DriverManager
3、获取执行SQL的对象 Statement
4、获得返回的结果集 resultSet
5、释放连接
以上代码中用到的对象详解:
DriverManager
//DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
Class.forName("com.mysql.cj.jdbc.Driver");//加载驱动
Connection connection = DriverManager.getConnection(url, username, password);
//connection代表数据库
//数据库设置自动提交
//事务提交
//事务回滚
connection.setAutoCommit();
connection.rollback();
connection.commit();
URL
String url = "jdbc:mysql://localhost:3306/jdbcStudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
//mysql -- 3306
//jdbc:mysql://主机地址:端口号/数据库名?参数1&参数2&参数3
//oralce -- 1521
//jdbc:oracle:thin:@localhost:1521:sid
Statement 执行SQL的对象
String sql = "SELECT * FROM users"; //编写SQL
statement.executeQuery(); //查询操作,返回ResultSet
statement.execute(); // 执行任何SQL
statement.executeUpdate(); // 更新、插入、删除都是用这个,返回一个受影响的行数
ResultSet 查询的结果集:封装了所有的查询结果
获得指定的数据类型
resultSet.getObject(); //在不知道列类型时使用
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();
遍历指针:
resultSet.beforeFirst(); //指针移动到最前面
resultSet.afterLast(); //移动到最后面
resultSet.next(); //移动到下一个数据
resultSet.previous(); //移动到前一行
resultSet.absolute(row); //移动到指定行
释放资源!
resultSet.close();
statement.close();
connection.close();
4 JDBC常用语句的封装与测试!
4.1 封装
测试了以上程序后,相信心中一定会有So tm What?
的感觉~好在上述代码其实是可以封装的,以下为封装文件说明:
-
db.properties
文件:内含driver、url、username、password信息。注意:将该文件直接创建在src目录下!
-
JdbcUtils
类:内含加载数据库驱动
、获取了解
、释放连接
方法
目录展示:
源码:
db.properties
文件:
driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/jdbcStudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username = root
password = 123456
JdbcUtils
类:
package com.demut.demo02.utils;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
//加载数据库驱动
static {
try {
//获取文件流
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(in); //加载流
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
//驱动只需要加载一次
Class.forName(driver);
}catch (Exception e){
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
//释放连接资源
public static void release(Connection conn, Statement st, ResultSet rs){
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();
}
}
}
}
4.2 测试
以下为数据库增删改查的测试:(增删改均类似,查请特殊关注!)
TestInsert类:
package com.demut.demo02;
import com.demut.demo02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestInsert {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection(); //获取数据库连接
st = conn.createStatement(); //获得SQL的执行对象
String sql = "INSERT INTO users(id,`name`,`password`,`email`,`birthday`)\n" +
"VALUES (5, 'demut','123456','12312312@qq.com','20200101')";
int i = st.executeUpdate(sql); //返回受影响的行数
if (i>0) {
System.out.println("插入成功!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
TestDelete类:
package com.demut.demo02;
import com.demut.demo02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestDelete {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection(); //获取数据库连接
st = conn.createStatement(); //获得SQL的执行对象
String sql = "DELETE FROM users WHERE id = 4";
int i = st.executeUpdate(sql); //返回受影响的行数
if (i>0) {
System.out.println("删除成功!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
TestUpdate类:
package com.demut.demo02;
import com.demut.demo02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestUpdate {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection(); //获取数据库连接
st = conn.createStatement(); //获得SQL的执行对象
String sql = "UPDATE users SET `name` = 'DEMUT',`email` = 'jeverdemut@126.com' WHERE id = 5";
int i = st.executeUpdate(sql); //返回受影响的行数
if (i>0) {
System.out.println("更新成功!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
TestSelect类:
package com.demut.demo02;
import com.demut.demo02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestSelect {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
//SQL
String sql = "SELECT * FROM users";
rs = st.executeQuery(sql);//查询完毕返回一个结果集
while (rs.next()){
System.out.println(rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn, st, rs);
}
}
}
5 SQL 注入问题
sql存在漏洞,会被攻击导致数据泄露! SQL会被拼接(由于OR的存在)
此处使用案例说明:
编写一个login方法如下:
//登陆业务
public static void login(String username, String password){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
String sql = "SELECT * FROM users WHERE `name` = '"+username+"' AND `password` = '"+password+"'";
rs = st.executeQuery(sql);
while (rs.next()){
System.out.println(rs.getString("name"));
System.out.println(rs.getString("password"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn, st, rs);
}
}
首先使用数据库内已知的一条数据测试:
public static void main(String[] args) {
login("DEMUT","123456");
}
/*结果:
DEMUT
123456
*/
测试成功!语句被成功输出!
现在我们对SQL进行测试,盗取数据库中用户数据~
public static void main(String[] args) {
//login("DEMUT","123456");
login("' or '1=1","' or '1=1"); //此处半个单引号是为了与sql语句拼接!
}
/*结果:
zhansan
123456
lisi
123456
wangwu
123456
DEMUT
123456
*/
SQL注入说明:由于我们输入的是账户名与密码名均为‘ ’ or '1==1'
语句,所以在执行SQL语句时,Statement默认输入的值全为true,则将数据库中所有的用户信息查询了出来,造成信息泄露。SQL注入已经危害了很多网站,几乎所有数据库都会存在注入问题,为了规避这种现象,我们今后使用PreparedStatement
对象操作!
6 PreparedStatement对象
PreparedStatement可以防止SQL注入,效率更高!
与Statement不同的是,PreparedStatement采用了预编译,且使用?占位符代替参数,方便输入且更加安全!
使用步骤:
- 编写SQL语句
- 预编译
- 设置参数
- 执行
插入语句测试:
package com.demut.demo03;
import com.demut.demo02.utils.JdbcUtils;
import java.sql.*;
import java.util.Date;
public class TestInsert {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
//区别
//使用?占位符代替参数
String sql = "INSERT INTO users(id,`name`,`password`,`email`,`birthday`) VALUES (?,?,?,?,?)";
pst = conn.prepareStatement(sql); //预编译SQL,先写sql,不执行
//手动给参数赋值:
pst.setInt(1,4); //此处的1表示第一个问好
pst.setString(2,"Jever");
pst.setString(3,"111111");
pst.setString(4,"123456@qq.com");
//注意: sql.Date 数据库 java.sql.Date()
// util.Date Java new Date().getTime() 获得时间戳
pst.setDate(5,new java.sql.Date(new Date().getTime()));
int i = pst.executeUpdate();
if (i > 0) {
System.out.println("插入成功!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,pst,rs);
}
}
}
/*结果:
插入成功!
*/
删除语句测试:
package com.demut.demo03;
import com.demut.demo02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
public class TestDelete {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
//区别
//使用?占位符代替参数
String sql = "DELETE FROM users WHERE id=?";
pst = conn.prepareStatement(sql); //预编译SQL,先写sql,不执行
//手动给参数赋值:
pst.setInt(1,5); //此处的1表示第一个问好
int i = pst.executeUpdate();
if (i > 0) {
System.out.println("删除成功!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,pst,rs);
}
}
}
/*结果:
删除成功!
*/
更新语句测试:
package com.demut.demo03;
import com.demut.demo02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
public class TestUpdate {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
//区别
//使用?占位符代替参数
String sql = "update users set `name` = ? where id=?;";
pst = conn.prepareStatement(sql); //预编译SQL,先写sql,不执行
//手动给参数赋值:
pst.setString(1,"LiHua");
pst.setInt(2,1);
int i = pst.executeUpdate();
if (i > 0) {
System.out.println("更新成功!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,pst,rs);
}
}
}
/*结果:
更新成功!
*/
查询语句测试:
package com.demut.demo03;
import com.demut.demo02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestSelect {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql = "SELECT * FROM users WHERE id = ?"; //编写SQL
pst = conn.prepareStatement(sql); //预编译
pst.setInt(1,1); //传递参数
rs = pst.executeQuery(); //执行
if (rs.next()) {
System.out.println(rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn, pst, rs);
}
}
}
/*结果
LiHua
*/
我们再来测试一下SQL注入问题:
本文第5部分中分别使用已有的数据与注入数据进行了测试,此处同上述情况,由于使用的是PreparedStatement对象,语句略有变化;同时在main函数内,两种测试情况笔者均已注释,据情况解注释即可!
源码:
package com.demut.demo03;
import com.demut.demo02.utils.JdbcUtils;
import java.sql.*;
public class SQL注入 {
public static void main(String[] args) {
//login("LiHua","123456");
//login("' or '1=1","' or '1=1"); //此处半个单引号是为了与sql语句拼接!
}
//登陆业务
public static void login(String username, String password){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql = "SELECT * FROM users WHERE `name`=? AND `password`=?";
st = conn.prepareStatement(sql);
st.setString(1,username);
st.setString(2,password);
rs = st.executeQuery(); //查询完毕,返回结果集
while (rs.next()){
System.out.println(rs.getString("name"));
System.out.println(rs.getString("password"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn, st, rs);
}
}
}
/*测试结果:
情形一:(使用LiHua,123456)
LiHua
123456
情形二:(使用注入语句)
(无输出!)
*/
说明:PreparedStatement防止SQL注入的本质是:其把传递进来的参数当做字符,如果其中存在转义字符,比如 '
会被直接转义
写在最后
Be wise in the way you act toward outsiders; make the most of every opportunity. Let your conversation be always full of grace, seasoned with salt, so that you may know how to answer everyone. (Colossians 4)