执行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, nam
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这三个步骤
,但是使用预编译,前两个步骤只执行一次,此后只需要执行第三个步骤即可。