JDBC的概念:
JDBC就是使用java语言操作关系数据库的一套API,全称为(java DataBase Connectivity)-----java数据库连接.
JDBC的本质:
是由sun公司定义的一套操作所有关系型数据库的规则,即接口,各个数据库厂商去实现这套接口,提供数据库驱动jar包,我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类
JDBC好处:
各数据库厂商使用相同的接口,java代码不需要针对不同数据库分别开发
可随时替换底层数据库,访问数据库的java代码基本不变
同一套java代码,操作不同的关系型数据库:
使用JDBC的步骤:
a:创建工程,导入驱动jar包
创建新工程,对工程进行配置:
创建新模块:
用于存放jar包:
将下载好的jar包直接复制到lib下:
注意,jar一定要放在src下面,别放错啦
编写代码:
package MyJDBC;
import java.sql.*;
public class myjdbc {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
String url="jdbc:mysql://localhost:3306/已创建好的数据库名";
String username="root";
String password="你的数据库密码";
Connection connection= DriverManager.getConnection(url,username,password);
//定义SQL
String sql="update person set age=19 where id=2";
//获取执行SQL的对象Statement
Statement statement=connection.createStatement();
//执行sql
int count_row=statement.executeUpdate(sql);
//释放资源
statement.close();
connection.close();
}
}
执行完代码之后,在mysql中查询数据的变化情况:
注意:数据库以及表的创建等工作,一定要提前在mysql中完成
注意:
注册驱动 Class.forName("com.mysql.cj.jdbc.Driver");//mysql5以上的版本,该行代码可以不写,也不会报错
原因是下述文件已经为我们写了该驱动,因此可以自动加载驱动类:
JDBC API:
DriverManager:
注册驱动 Class.forName("com.mysql.cj.jdbc.Driver");
查看Driver源码:
获取连接:
//url:连接路径,user:用户名,password:密码
static Connection getConnection(String url,String user,String password);
url:
语法:
jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2...
举例:
//localhost:表示本机 3306:是mysql服务器默认的端口号 person:数据库名称
jdbc:mysql://localhost:3306/person
注意:
如果连接的是本机mysql服务器,并且mysql服务器默认端口为3306,则URL可以简写为:jdbc:mysql:///数据库名称?参数键值对
配置useSSL=false参数,表示禁用安全连接方式,解决警告提示
Connection:
获取执行SQL的对象:
普通执行SQL对象:
Statement createStatement();
预编译SQL的执行SQL对象:
防止SQL注入
PreparedStatement prepareStatement(sql)
执行存储过程的对象:
CallableStatement prepareCall(sql)
事务管理:
MySQL事务管理:
开启事务:begin;/start transaction;
提交事务:commit;
回滚事务:rollback;
mysql默认自动提交事务
JDBC事务管理:Connection接口中定义了三个对应的方法
开启事务:setAutoCommit(boolean autoCommit):true为自动提交事务;false为手动提交事务,即为开启事务
提交事务:commit()
回滚事务:roolback();
package MyJDBC;
import java.sql.*;
public class myjdbc {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//获取连接
String url="jdbc:mysql://localhost:3306/MyJDBC";
String username="root";
String password="xxx";
Connection connection= DriverManager.getConnection(url,username,password);
//定义SQL
String sql1="update person set age=24 where id=1";
String sql2="update person set age=22 where id=2";
//获取执行SQL的对象Statement
Statement statement=connection.createStatement();
//执行sql
try {
//开启事务
connection.setAutoCommit(false);
int count_row1=statement.executeUpdate(sql1);
System.out.println(count_row1);
int count_row2=statement.executeUpdate(sql2);
System.out.println(count_row2);
//提交事务
connection.commit();//未出现异常则提交事务否则事务回滚
} catch (Exception throwables ) {
//回滚事务
connection.rollback();
throwables.printStackTrace();
}
//释放资源
statement.close();
connection.close();
}
}
ide输出:
mysql数据库中:
数据发生更新!
当发生异常时:
idea输出:
mysql中的数据情况:
数据并没有更新!
Statement:
statement的作用:
执行SQL语句
int executeUpdate(sql):执行DML,DDL语句
//返回值:
(1)DML语句影响的行数
(2)DDL语句执行后,执行成功也可能返回0
注:
DML数据操纵语言:对数据库中的数据进行一些简单操作,如insert、delete、update、select
等。DML操作是可以手动控制事务的开启、提交和回滚的
DDL数据定义语言:对数据库中的某些对象(例如database、table)进行管理,如create、alter和drop
,DDL操作是隐性提交的,不能rollback
执行DML语句:
包括增删改,这里我们以修改作为例子进行讲解
:
举例:
package MyJDBC;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class myjdbc_test {
//单元测试:必须导入junit,快速导入的方法:Alt+回车,点击junit4,注意必须在联网环境下
@Test
public void test_demo() throws ClassNotFoundException, SQLException {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
String url="jdbc:mysql://localhost:3306/myjdbc";
String username="root";
String password="xxx";
Connection connection= DriverManager.getConnection(url,username,password);
//定义SQL
String sql="update person set age=18 where id=2";
//获取执行SQL的对象Statement
Statement statement=connection.createStatement();
//执行sql
int count_row=statement.executeUpdate(sql);
System.out.println(count_row);
//释放资源
statement.close();
connection.close();
}
}
输出:
在mysql中查询数据:
当所修改的数据不存在时:
虽然这样能够正确的修改结果,但并不是最优方案,因为输出的影响行数对于用户来说,没有任何意义,因此,我们需要将输出影响行数的内容修改为提示信息。
修改如下:
将输出影响行数的代码修改为如下代码:
if(count_row>0){
System.out.println("修改成功");
}else{
System.out.println("修改失败");
}
当要修改的数据不存在时,就会提示修改失败的信息:
执行DDL语句:
将myjdbc_test做如下修改:
在mysql中进行查询:
删除刚创建的数据库:
package MyJDBC;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class myjdbc_test {
//Alt+回车,注意必须在联网环境下
@Test
public void test_demo() throws ClassNotFoundException, SQLException {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
String url="jdbc:mysql://localhost:3306/?";
String username="root";
String password="xxx";
Connection connection= DriverManager.getConnection(url,username,password);
//定义SQL
String sql="drop database myjdbc1";
//获取执行SQL的对象Statement
Statement statement=connection.createStatement();
//执行sql
int count_row=statement.executeUpdate(sql);
System.out.println(count_row);
//释放资源
statement.close();
connection.close();
}
}
idea输出:
虽然返回值为0,但是在mysql中进行查询,我们会发现该数据库已经成功被删除,因为DDL语句执行后,执行成功也可能返回0
,正是由于这个原因,所以我们不能像上面执行DML语句一样,通过返回值来判断是否成功。
判断DDL语句是否执行成功的方法很简单,只要未报错即认为执行成功;
举例:
ResultSet:
ResultSet[结果集对象]作用:
ResultSet executeQuery(sql):执行DQL语句[数据查询语句]
//返回值:ResultSet结果集对象
获取查询结果:
boolean next():
1:将光标从当前位置向前移动一行
2:判断当前行是否为有效行
//返回值:
true:有效行,当前行有数据
false:无效行,当前行没有数据
如下所示:
光标需要从上到下依次判断当前行是否有效,因此,判断数据是否有效的过程是一个循环的过程:
while(rs.next()){
//获取数据
rs.getXXX(参数);
}
xxx getXXX(参数):获取数据
xxx:数据类型,如int getInt(参数):String getString(参数)
参数:int:列的编号,从1开始 String:列的名称
举例:
package MyJDBC;
import org.junit.Test;
import java.sql.*;
public class myjdbc_test {
//Alt+回车,注意必须在联网环境下
@Test
public void test_demo() throws ClassNotFoundException, SQLException {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
String url="jdbc:mysql://localhost:3306/myjdbc";
String username="root";
String password="xxx";
Connection connection= DriverManager.getConnection(url,username,password);
//定义SQL语句
String sql="select * from person";
//获取statement对象
Statement statement=connection.createStatement();
//执行SQL
ResultSet resultSet=statement.executeQuery(sql);
//处理结果,遍历resultSet中的所有数据
//光标移动到下一行,并且判断当前行是否有数据
while(resultSet.next()){
//第一种写法:
int id=resultSet.getInt(1);
int age=resultSet.getInt(2);
String name=resultSet.getString(3);
//第二种写法:
//int id=resultSet.getInt("id");
//int age=resultSet.getInt("age");
//String name=resultSet.getString("name");
System.out.println(id);
System.out.println(age);
System.out.println(name);
System.out.println("--------------------------");
}
//关闭资源
resultSet.close();
statement.close();
connection.close();
}
}
输出:
1
24
张三
--------------------------
2
18
李军
--------------------------
3
24
张伟
--------------------------
ResultSet简单应用:
要求:查询person账户表数据,封装为Person对象中,并且存储到ArrayList集合当中
步骤:
a:定义实体类person
package pdjo;
public class Person {
//注意:这里的字段要和数据库中的表的字段对应
private int id;
private int age;
private String name;
public void setId(int id) {
this.id = id;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
b:查询数据,封装到person对象中,将person对象存入ArrayList集合中
package pdjo;
import org.junit.Test;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class myjdbc_test {
@Test
public void test_demo() throws ClassNotFoundException, SQLException {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
String url="jdbc:mysql://localhost:3306/myjdbc";
String username="root";
String password="xxx";
Connection connection= DriverManager.getConnection(url,username,password);
//定义SQL语句
String sql="select * from person";
//获取statement对象
Statement statement=connection.createStatement();
//执行SQL
ResultSet resultSet=statement.executeQuery(sql);
//创建集合
List<Person> personList=new ArrayList<>();
//处理结果,遍历resultSet中的所有数据
//光标移动到下一行,并且判断当前行是否有数据
while(resultSet.next()){
//创建对象操作
Person person=new Person();
int id=resultSet.getInt("id");
int age=resultSet.getInt("age");
String name=resultSet.getString("name");
//赋值操作
person.setId(id);
person.setAge(age);
person.setName(name);
//将对象放入集合
personList.add(person);
}
//输出集合中的数据
System.out.println(personList);
//关闭资源
resultSet.close();
statement.close();
connection.close();
}
}
输出:
[Person{id=1, age=17, name='张三'}, Person{id=2, age=21, name='李军'}, Person{id=3, age=24, name='张伟'}, Person{id=3, age=19, name='lucy'}]
PreparedStatement:
PreparedStatement作用:
预编译SQL语句并执行:预防SQL注入问题
SQL注入:SQL注入是通过操作输入来修改事先定义好的SQL语句,用以到达执行代码对服务器进行攻击的方法
恶意的SQL语句会入侵数据库,甚至对数据库产生破坏:
String sql="select * from tb_name where name='"+varname+"' and password='"+varpassword+"'";
对于上述SQL语句,如果我们将' or 1=1 or '
或者'or'1'='1
作为varpassword传进来,那么无论用户名是什么,都会登录成功
举例:
package MyJDBC;
import pdjo.Person;
import java.sql.*;
public class myjdbc {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//获取连接
String url="jdbc:mysql://localhost:3306/MyJDBC";
String username="root";
String password="xxxx";
Connection connection= DriverManager.getConnection(url,username,password);
//接收用户输入:用户名和密码
String name="张三";
String pwd="'or'1'='1";
//定义SQL
String SQL="select * from user_info where name='"+name+"' and password='"+pwd+"'";
//获取Statement对象
Statement Statement= connection.createStatement();
ResultSet resultSet=Statement.executeQuery(SQL);
//判断是否登录成功
if(resultSet.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//关闭资源
resultSet.close();
Statement.close();
connection.close();
}
}
输出:
原因是:
String SQL="select * from user_info where name='任意' and password=''or'1'='1'";
上述这条SQL语句'1'='1'
一定成立,因此可以通过任何的验证,但是有些数据库是不会让这种恶意的SQL语句执行成功的,但也有很多数据库可以让这些语句得到执行,而如果使用预编译语句,传入的任何内容就不会和原来的语句发生任何匹配关系
,只要安全使用预编译语句,就不需要对传入的数据做任何过滤。
通过PreparedStatement对象解决注入问题:
package MyJDBC;
import pdjo.Person;
import java.sql.*;
public class myjdbc {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//获取连接
String url="jdbc:mysql://localhost:3306/MyJDBC";
String username="root";
String password="xxx";
Connection connection= DriverManager.getConnection(url,username,password);
//接收用户输入:用户名和密码
String name="lisa";
String pwd="'or'1'='1";
//定义SQL
String SQL="select * from user_info where name='?' and password='?'";
//获取Statement对象
PreparedStatement preparedStatement= connection.prepareStatement(SQL);
ResultSet resultSet=preparedStatement.executeQuery();
//判断是否登录成功
if(resultSet.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//关闭资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
此时的密码依然是上述发生SQL注入问题的时的密码,但当对象为PreparedStatement,登录失败:
那么PreparedStatement对象是如何解决这个注入问题呢?
将特殊字符进行转义操作
转义前:
转义后:
1:获取PreparedStatement对象:
//sql语句中的参数值,使用?占位符替代
String SQL="select * from user where username=? and password=?";
//通过Connection对象获取,并传入对应的SQL语句
PreparedStatement pstmt=conn.preparedStatement(sql);
2:设置参数值
PreparedStatement对象:
setXXX(参数1,参数2,参数3…):给?赋值
XXX:
数据类型:如setInt(参数1,参数2)
参数:
参数1:?的位置编号,从1开始
参数2:?的值
执行SQL:
executeQuery();不需要再传递SQL
package MyJDBC;
import pdjo.Person;
import java.sql.*;
public class myjdbc {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//获取连接
String url="jdbc:mysql://localhost:3306/MyJDBC";
String username="root";
String password="xxx";
Connection connection= DriverManager.getConnection(url,username,password);
//定义SQL
String SQL="insert into person(id,age,name) values(?,?,?)";
//获取Statement对象
PreparedStatement preparedStatement= connection.prepareStatement(SQL);
//设置?的值:在设置时,数据类型一定要和数据库中表的字段类型一致
preparedStatement.setInt(1,3);
preparedStatement.setInt(2,19);
preparedStatement.setString(3,"lucy");
preparedStatement.executeUpdate();
preparedStatement.close();
connection.close();
}
}
在数据库查询数据:
PreparedStatement原理:
PreparedStatement好处
:
预编译SQL,性能高,并且可以防止SQL注入,将敏感字符进行转义
PreparedStatement预编译功能开启:userServerPreStmts=true
配置mysql执行日志(重启MySQL服务器后生效):
在mysql中的ini文件
中编写如下代码:
log-output=FILE
general-log=1
general_log_file="D:\mysql.log"
slow-query-log=1
slow_query_log_file="D:\mysql_slow.log"
long_query_time=2
执行SQL语句,查看 D:\mysql.log 日志,即可发现SQL语句中的单引号都被加上了转义字符
PreparedStatement原理:
1:在获取PreparedStatement对象时,将SQL语句发送给mysql服务器进行检查,编译[耗时]
2:执行时就不用再进行这些步骤了,速度较快
3:如果SQL模板一样,则只需要进行一次检查,编译
举例:
未使用预编译
select * from tb_user where username='zhangsan';
select * from tb_user where username-'lisa';
使用编译
select * from tb_user where username=?;
setString(1,'zhangsan');
setString(1,'lisa');
第一种未使用预编译,那么程序每次运行都会经历检查SQL语法,编译SQL,执行SQL这三个步骤
,但是使用预编译,前两个步骤只执行一次,此后只需要执行第三个步骤即可。