文章目录
JDBC介绍
JDBC全称为Java Database Connectivity
,直译过来就是Java数据库连接。它由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现。我们要连接数据库,就要去相应的数据库官网下载驱动,驱动都是以jar包的形式存在,jar包当中有很多.class文件,这些class文件就是JDBC接口的实现。本文针对MySQL数据库的连接实现。
JDBC六部曲
- 注册驱动(作用:告诉Java程序,即将要连接的是哪个品牌数据库)。
- 获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后要关闭通道)
- 获取数据库操作对象(专门执行sql语句的对象)。
- 执行sql语句。
- 处理查询结果集(只有第五步进行的是select语句的时候,才会到这一步)。
- 释放资源(使用完资源后一定要关闭资源)。
数据库连接完整代码如下:
import java.sql.*; public class JDBCTest06 { public static void main(String[] args) { Connection conn = null; Statement state = null; Driver driver = null; ResultSet rs = null; try { /* 注册驱动 在此处,Driver类型的对象driver和后面的com.mysql.cj.jdbc.Driver()并不是一个,它们是不同包下的。 */ driver = new com.mysql.cj.jdbc.Driver(); DriverManager.registerDriver(driver); /* 第二步:获取连接 这里的url是由以下部分组成的: 协议:jdbc:mysql:// 数据库服务端ip地址:因为这里使用的是本机,所以写的是localhost 数据库端口号:MySQL的专用端口号:3306 数据库账号:root 密码:111111 */ conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/study2","root","111111"); //获取数据库操作对象 state = conn.createStatement(); /* 执行sql语句 若执行DML语句(update,insert,delete),写法如下: state.executeUpdate("insert into user (id,name) value ("1", "张三")"); 下面演示的是执行DQL语句(select) */ rs = state.executeQuery("select *from user"); /* 处理查询结果 如果rs中还有没有访问的记录,next()方法则会返回true 如果访问的字段是varChar类型,则使用getString(),若访问字段是int类型,则使用getInt(),若访问字段是bigint,则使用getDouble(),其余同理。 注意这里getStrng()以及同类型方法的传参,可以传一个字段名,也可以传要查询字段的下标(从1开始) */ while(rs.next()){ System.out.println(rs.getString(1) + " " + rs.getString(2)); } } catch (SQLException e) { e.printStackTrace(); }finally{ //释放资源,要从小到大释放 if(rs != null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if(state != null){ try { state.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
在注册驱动时,我们现在并不会这样写,因为com.mysql.cj.jdbc.Driver类的源码中有如下静态代码块:
SQL注入
我们在这里简单的利用数据库实现一个账号的登录。
public static boolean Login(String loginName, String passWord){ Connection conn = null; Statement state = null; ResultSet st = null; try { //注册驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //获取连接 conn = DriverManager.getConnection("jdbc:mysql://localhost/study2","root","111111"); //获取数据库操作对象 state = conn.createStatement(); //执行查询语句 st = state.executeQuery("select *from login where loginName ='" + loginName + "'and loginPwd = '" + passWord + "'"); if(st.next()){ return true; } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally { if (state != null) { try { state.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } ; } } return false; }
在这里我们借助了SQL语句的拼接来实现了一个账号登录功能,其中loginName,和passWordd都是由用户来输入的。如果用户正常输入,例如:输入loginName为’‘111
’‘,输入passWord为’‘12345678
’',拼接之后的SQL语句如下:
select * form login where loginName = '111' and passWord = '12345678'
只要用户输入的名字和密码是正确的,就会有查询结果,登录成功。
如果用户在输入时输入了带有sql关键字的’非法信息’并且该非法信息串改了sql语句的原先的含义,导致未输入正确的信息也同样登录成功,例如:输入loginName为’‘111
’',输入passWord为 ‘‘1' or ' 1' = '1
’’,拼接之后的SQL语句如下:
select * from login where loginName = '111' and passWord = '1' or '1' = '1';
用户如果这样输入就改变的SQL语句的原意,导致在不知道账号密码的情况下仍然登录成功,这就是SQL注入
。
PerparedStatement
上面使用Statement对象执行sql语句会造成SQL注入问题,Statement的子类PreparedStatement对象可以预编译SQL语句,从而避免了这种问题。
使用PreparedStatement来实现一个简单的登入如下:
public static boolean login(String loginName, String passWord){ ResultSet rs = null; Connection conn = null; //预编译的数据库操作对象 PreparedStatement ps = null; try { //注册驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //连接数据库 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/study2","root","111111"); //获取预编译的数据库操作对象 ps = conn.prepareStatement("select * from login where loginName = ? and loginPwd = ?"); //给占位符传值 ps.setString(1,map.get("loginName")); ps.setString(2,map.get("passWord")); //执行sql语句 rs = ps.executeQuery(); //处理查询结果 if(rs.next()){ return true; } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally{ if(ps != null){ try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } return false; }
在该方法中我们通过调用数据库连接对象的prepareStatement(sql)方法获取的是预编译的数据库操作对象,在获取预编译的数据库操作对象时需要传入SQL语句的框子,其中它用占位符?来代替sql语句要接收的值。然后我们再用setString(int parameterIndex, String x) 方法给占位符‘ ?’传值。
Statement 与 PreparedStatement比较
- Statement存在sql注入问题,PreparedStatement不存在sql注入问题。
- Statement每一次执行都需要对sql语句进行编译一次,效率较低,PreparedStatement只需要编译一次就可以执行N次,效率较高。
- PreparedStatement会在编译阶段做类型检查,但是Statement不会。
- PreparedStatement并不支持sql语句的拼接,Statement更加灵活。
JDBC的事务机制
- JDBC中事务是自动提交的。只要执行一个DML语句,则会自动提交一次。
- 若conn为数据库连接对象,调用以下方法可以进行相关事务操作。
//关闭自动提交 conn.setAutoCommit(false); //回滚事务 conn.rollback(); //提交事务 conn.commit();