JDBC简介
1、什么是JDBC?
JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API(工具)。JDBC是Java访问数据库的标准规范。
规范:在java中的直接体现是接口
作用:为不同关系型数据库提供统一的访问,由一组用java语言编写的接口和工具类组成,由各大数据库厂商实现对应接口
2、连接数据库时要先加载驱动
什么是驱动?
两个设备要进行通信时,需要满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信。
Java和数据库要想进行链接,必须提前规定一些数据格式,格式由数据库厂商实现。
mysql连接工具下载地址:https://dev.mysql.com/downloads/connector/j/
3、JDBC是接口,而JDBC驱动才是接口的实现,没有驱动无法完成数据库连接!每个数据库厂商都有自己的驱动,用来连接自己公司的数据库。
JDBC连接详解
1.通过JDBC连接数据库需要五步
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class Demo01 {
public static void main(String[] args) throws Exception {
//1、加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2、创建连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_test?serverTimezone=GMT%2B8", "root", "root");
//3、编译sql语句 编译器
Statement statement = conn.createStatement();
String sql = "select * from test";
//4、发送sql 并接受结果
ResultSet eq = statement.executeQuery(sql);
//根据列名获取数据 迭代器
while(eq.next()) {
int sid = eq.getInt(1);
String name = eq.getString(2);
int chinese = eq.getInt("chinese");
int math = eq.getInt("math");
System.out.println(sid+"\t"+name+"\t"+chinese+"\t"+math);
}
//5、关闭资源
eq.close();
statement.close();
conn.close();
}
}
2、详解每一步
1、Class.forName("com.mysql.cj.jdbc.Driver"); //这里写下载的连接工具包中的驱动路径
通过反射,建立驱动的对象
2、Connection conn = DriverManager.getConnection(url,username,password); //获取连接
url="jdbc:mysql://localhost:3306/db_test?serverTimezone=GMT%2B8"
协议,有严格书写规范 主协议名:子协议名://主机名:端口号/数据库名 在连接工具到了6.0之后需要设置时区
serverTimezone是时区,如果设定serverTimezone=UTC,会比中国时间早8个小时,设定中国则为GMT%2B8
username = "root", password = "root" 数据库的用户名和密码
3、获取发送sql语句的对象
Statement statement = conn.createStatement();
4、发送sql,执行并接受结果
executeQuery(String sql),只能执行select语句,如果执行insert into,update,delete from 都会报错,会把SQL语句传递给mysql数据库去执行
executeUpdate(String sql),执行更新的SQL语句。只能执行insert into,update,delete from,如果执行select语句会报错,会把SQL语句传递给mysql数据库去执行
execute(String sql) 执行任意语句,执行select 返回true,执行insert into,update,delete from 返回false
5、释放资源
eq.close();statement.close();conn.close();
3、JDBC的增删改查

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Scanner;
public class Demo2 {
public static void main(String[] args) throws Exception{
Connection conn=null;
Scanner sc=new Scanner(System.in);
PreparedStatement ps =null;
ResultSet eq =null;
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///db_701?serverTimezone=GMT%2B8", "root", "ccc");
login(conn,sc,eq,ps);
}
public static void login(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception {
ps = conn.prepareStatement("select username,pwd from users where id='1'");
eq = ps.executeQuery();
int flag=1;
if(eq.next()) {
do{
System.out.println("请输入用户名:");
String s1 = sc.nextLine();
String s2 = eq.getString(1);
System.out.println(s2);
if(s1.equals(s2)) {
System.out.println("请输入密码:");
String pwd1 = sc.nextLine();
String pwd2 = eq.getString(2);
if(pwd1.equals(pwd2)) {
System.out.println("登录成功,欢迎使用");
flag=0;
}else {
System.out.println("用户名密码错误 ,请重新输入");
continue;
}
}else {
System.out.println("用户名不存在,请重新输入");
continue;
}
}while(flag==1);
test(conn,sc,eq,ps);
}
}
public static void test(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception{
System.out.println("请选择功能:1、添加 2、删除 3、修改 4、查询 0、退出");
int num =Integer.parseInt(sc.nextLine());
switch(num){
case 1:
add(conn,sc,eq,ps);
break;
case 2:
delete(conn,sc,eq,ps);
break;
case 3:
alter(conn,sc,eq,ps);
break;
case 4:
select(conn,sc,eq,ps);
break;
case 5 :
System.out.println("谢谢您的使用,再见");
after(conn,sc,eq,ps);
break;
}
}
public static void add(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception {
ps = conn.prepareStatement("insert into users values(null,?,?)");
System.out.println("请输入用户名:");
ps.setString(1, sc.nextLine());
int flag=1;
while(flag==1) {
System.out.println("请输入密码 :");
String pwd1 = sc.nextLine();
System.out.println("确认密码:");
String pwd2 = sc.nextLine();
if(pwd1.equals(pwd2)) {
ps.setString(2, pwd2);
flag=0;
}else {
System.out.println("两次密码输入不一致,请重新输入密码 ");
continue;
}
}
int count=ps.executeUpdate();
if(count>0) {
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}
test2(conn,sc,eq,ps);
}
public static void delete(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception{
ps = conn.prepareStatement("delete from users where username=? and pwd=?");
int flag=1;
while(flag==1) {
System.out.println("请输入想要删除的用户名:");
String s = sc.nextLine();
ps.setString(1, s);
System.out.println("请输入此用户的密码 :");
String pwd1 = sc.nextLine();
ps.setString(2, pwd1);
int count=ps.executeUpdate();
if(count>0) {
System.out.println("删除成功");
flag=0;
}else {
System.out.println("用户名密码错误,删除失败 1.继续删除 0.返回 ");
String s1 = sc.nextLine();
if(s1.equals("1")) {
continue;
}else {
flag=0;
System.out.println("正在返回上一级");
}
}
}
test2(conn,sc,eq,ps);
}
public static void alter(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception {
ps = conn.prepareStatement("update users set pwd=? where username=? and pwd=?");
int flag=1;
int flag1=1;
while(flag==1) {
System.out.println("请输入想要修改密码的用户名:");
String s1 = sc.nextLine();
ps.setString(2, s1);
System.out.println("请输入原密码 ");
String pwd = sc.nextLine();
ps.setString(3, pwd);
while(flag1==1) {
System.out.println("请输入修改的密码 :");
String pwd1 = sc.nextLine();
System.out.println("确认密码:");
String pwd2 = sc.nextLine();
if(pwd1.equals(pwd2)) {
ps.setString(1, pwd2);
flag1=0;
}else {
System.out.println("两次密码输入不一致,请重新输入 ");
continue;
}
}
int count=ps.executeUpdate();
if(count>0) {
System.out.println("修改成功");
flag=0;
}else {
System.out.println("修改失败");
System.out.println("用户名密码错误,修改失败 1.继续修改 0.返回 ");
String s3 = sc.nextLine();
if(s3.equals("1")) {
continue;
}else {
flag=0;
System.out.println("正在返回上一级");
}
}
}
test2(conn,sc,eq,ps);
}
public static void select(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception {
System.out.println("1.查询全部 2.指定用户名查询");
String s = sc.nextLine();
if(s.equals("2")) {
ps = conn.prepareStatement("select * from users where username=?");
System.out.println("请输入要查询的用户名:");
String s1 = sc.nextLine();
ps.setString(1, s1);
eq = ps.executeQuery();
}else if(s.equals("1")) {
ps = conn.prepareStatement("select * from users");
eq = ps.executeQuery();
}
if(eq.next()) {
do{
String username = eq.getString(2);
String pwd = eq.getString(3);
System.out.println(username+"\t"+pwd);
}while(eq.next()) ;
}else {
System.out.println("查询失败");
}
test2(conn,sc,eq,ps);
}
public static void test2(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception{
System.out.println("1.继续 0.退出");
String s3 = sc.nextLine();
if(s3.equals("1")) {
test(conn,sc,eq,ps);
}else {
System.out.println("谢谢您的使用,再见");
after(conn,sc,eq,ps);
}
}
public static void after(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception{
if(conn!=null) {
conn.close();
}
if(ps!=null) {
ps.close();
}
if(sc!=null) {
sc.close();
}
if(eq !=null) {
eq.close();
}
}
}
4、工具类的抽取
将重复的加载驱动和获取连接和关闭资源封装到工具类,方便使用

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCUtils {
public static final String driver = "com.mysql.cj.jdbc.Driver";
public static final String url = "jdbc:mysql://localhost:3306/db_test?serverTimezone=GMT%2B8";
public static final String user = "root";
public static final String password="root";
static {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConn() {
try {
return DriverManager.getConnection(url,user,password);
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
public static void closeAll(ResultSet rs,Statement st,Connection conn) {
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();
}
}
}
}
SQL注入问题
1、在上面的JDBC使用中,存在着一个安全问题,那就是SQL的注入问题
这个问题主要发生在编译对象身上,因为statement对象每次都是将字符串拼接完毕之后才发送给数据库执行,因此就产生了安全问题。
eg:用户登录 假设用户名密码必须是root 和root才能登录获得查看结果
sql语句为 String sql = "select * from users where username='"+username+"' and password='"+password+"'";
username 和 password需要用户输入
注入问题如下:
username = a' or 'a'='a;
password = a' or 'a'='a;
此时sql语句变为 select * from users where username = 'a' or 'a'='a' and password='a' or 'a'='a'
虽然用户名密码均不正确,但依然可以登录成功进行查看,这就是SQL注入问题
2、那么该如何解决呢?
使用预编译对象 preparedStatement
PreparedStatement与Statement的区别:
前者对sql语句进行了预编译
PreparedStatement将语句先送回数据库
使语句的逻辑已经确定为select * from users where username=? and password=?
此时用户如果输入同样的内容,却无法改变原有的逻辑,会产生语法错误,所以避免了注入问题
且预编译对象在使用同一条语句传不同内容时,也只需编译一次,大大提高了效率
3、PreparedStatement的预编译是数据库进行的,编译后的函数key是缓存在PreparedStatement中的,编译后的函数是缓存在数据库服务器中的。预编译前有检查sql语句语法是否正确的操作。只有数据库服务器支持预编译功能时,JDBC驱动才能够使用数据库的预编译功能,否则会报错。
jdbc:mysql://localhost:3306/db?useServerPrepStmts=true 可以设置数据库开启预编译
4、使用不同的PreparedStatement对象来执行相同的SQL语句时,还是会出现编译两次的现象,这是因为驱动没有缓存编译后的函数key,导致二次编译。如果希望缓存编译后函数的key,那么就要设置cachePrepStmts参数为true。
jdbc:mysql://localhost:3306/db?useServerPrepStmts=true&cachePrepStmts=true
5、PreparedStatement的预编译还有注意的问题,在数据库端存储的函数和在PreparedStatement中存储的key值,都是建立在数据库连接的基础上的,如果当前数据库连接断开了,数据库端的函数会清空,建立在连接上的PreparedStatement里面的函数key也会被清空,各个连接之间的预编译都是互相独立的。
6、PreparedStatement的使用
|
setInt(int index,int value) |
为?占位符,赋予int值 |
|
setString(int index,String value) |
为?占位符,赋予String值 |
|
setObject(int index,Object value) |
|
|
executeQuery() |
执行查询的SQL语句。 只能执行select语句, 如果执行insert into,update,delete from 都会报错 会把SQL语句传递给mysql数据库去执行
返回结果:ResultSet对象---一张二维表格
|
|
executeUpdate() |
执行更新的SQL语句。 只能执行insert into,update,delete from 如果执行select语句会报错 会把SQL语句传递给mysql数据库去执行
返回结果:int SQL语句执行后更新了几行数据
|
上面的代码练习中采用的就是PreparedStatement。
单元测试
1、导包 右击项目-->prooerties-->java build path -->libraries-->add library-->JUint-->next finish
2、给想要测试的方法加上注解(在上方加上 @Test)
3、可以在其他方法上加@Before 这个方法会在测试方法之前执行 用来加载资源
@After 这个方法会在测试方法之后执行 用来关闭资源
DAO模式
Database Access Object
把对数据库进行的JDBC操作(增、删、改、查) 都放在一个类中,用不同的方法分别来完成增、删、改、查。
一张数据库表做一个实体类,数据库的列是类的属性,数据库的一行数据是类的一个对象
