1.jdbc入门原理:
面向接口编程,就是面向抽象编程。
由于 每个数据库厂家是=底层原理不同,所以如果没有这个接口,程序员需要对每个数据库都写一套程序。
所以sun公司定义了一个接口,用于连接数据库与Java。
关系图如下:
接口类似一个库文件,各个数据库厂家写java程序实现自己的数据库。也就是为jdbc接口写实现类。这些实现类要去官网下载,一个jar包。这些实现类也叫驱动。
程序员也是面向这个接口编程,直接调用。
2.jdbc连接六步
- 注册驱动(告诉java要处理的是什么厂家的数据库)
- 获取连接(表示jvm的进程和数据库进程直接的通道打开了,这属于进程之间的通信,重量级的使用完一定要关闭)
- 获取数据库操作对象(专门执行sql语句)
- 执行sql语句(DQL,DML……)
- 处理查询结果集(只有第四步执行的是查询语句,才有这一步)
- 释放资源(使用完资源以后一定要关闭资源,java和数据库属于进程间的通信,开启以后一定要关闭)
3.第一步——注册驱动
第一种方法(原理方法,但是不常用):
直接使用一个方法
//驱动程序管理器.注册驱动程序(驱动接口)
DriverManager.registerDriver(new com.mysql.jdbc.Driver);
这里,括号里面的Driver是一个代表驱动的接口程序,然后各个公司来实现它。
他在java.sql.Driver
然后我们传的是数据库厂家做的实现类
也就是com.mysql,jdbc,Driver 与接口同名的实现类
第一步结束,也就是告诉java我们连接的是哪个数据库。
第二种方法(常用,因为里面有一个静态代码块执行上面那个语句,类加载,静态代码块就执行了)
//既然只要类加载就可以,那就用反射把类加载了
Class.forName("com.mysql.jdbc.Driver");
为什么这种方式常用?因为参数是字符串,字符串可以写在xxx.properties文件中。
4.第二部——获取连接
怎么获取连接呢?是通过DriverManager下的一个方法getConnection(url,use,password)
这是个静态方法,直接用就可以,返回的是一个Connect对象,说明连接成功建立
其中,url:统一资源定位符(网络中某个资源的绝对路径)
包括:协议+ip+端口+数据库名(协议是通信标准,ip代表主机是哪个,端口代表软件是哪个,然后再指定这个软件的哪个资源(数据库名字))
user是用户名,password是密码
如下
String url = "jdbc:mysql://127.0.0.1:3306/abc";
String user = "root";
String password = "333";
Connect conn = DriveerManager.getConnection(url,user.password);
5.第三步——获取数据库操作对象
就是Statement,专门用于执行sql语句,它由刚才建立的连接,的一个方法创建
Statement stmt = conn.createStatement();
6.第四步——执行sql语句
先把sql写成字符串,再执行
//增删改,返回数量
//直接用操作对象的这个方法执行,返回的int数据是指几条语句受到影响
String sql = "insert into abc(xx,xxx,xx) values(xxx,xx,xx)";
int count = stmt.exeuteUpdate(sql); //这个是对于增删改的语句
//查询语句,返回结果集
String sql = "select empno,ename,sal from emp";
ResultSet rs = stmt.executeQuery(sql); //一般rs的定义在try外部
由于select的结果需要显示,并不是操作了就完事了,所以需要在处理一下
7.第五步——处理查询结果集
rs是一个游标,一开始指着第0行,我们通过next往下走,通过getString()或者getInt()或者getDouble()获取本行第几列的元素
boolean flag = rs.next(); //这里是向下走一行
String empno = rs.getString(1); //获取本行第一列
//或者
String empno = rs.getString(“name”); //获取本行name那一列(这里注意,列名称不是表的列名称,是查询结果的列名称)
int id = rs.getInt("id") //取数字
打印表的话,就是用个while循环
8.第六步——释放资源
先关小的,再关大的
rs.close
stmt.close();
conn.close();
分开try,catch SQLExeception。前面那些也都要放在try里,这个释放要卸载finally里,无论怎么样都要释放
一般rs的定义在try外部,不然finally里作用域关不了,同样连接和操作对象也是这样。
9.SQL注入
1.注入问题现象及原因
现象:随便输一个用户名,然后密码输一个
'xxxx' or '1' = '1'
就能直接破解进入数据库
原因:用户输入的信息中,含有sql语句的关键字,并且这些关键字参与sql语句的编译过程
因为我们的sql语句在字符串里
String sql = "selet * from tbale where user = " + user + "and password = " + password;
我们像这样把语句拼接起来,然后对比where是否正确,但是我们如果用上述的注入,就会变成
String sql = "selet * from tbale where user = " + user + "and password = 'xxxx' or '1' = '1';
这样就是一个或条件,无论前面的密码有没有,都会返回true
2.注入问题解决——PerparedStatement
只要用户提供的信息不参与sql语句的编译过程,问题就解决了
即使用户提供的信息中含有sql关键字,不参与编译就不起作用
要想不参与,就要用java.sql.PerparedStatement
PerparedStatement是预编译的数据库操作对象,原理是预先对sql语句框架进行编译,然后再给sql传值
//先写sql语句,再获取这个语句对应的数据库操作对象
//对于参数,用?这个占位符占位即可
String sql = "selet * from tbale where user = ? and password = ?";
//传给数据库操作对象,这里perpare没有d,因为是动作,p也不大写,因为是方法不是类。
PerparedStatement ps = conn.perpareStatement(sql);
//然后传参数,第一个问号对应着下标1(jdbc都是从1开始)
ps.setString(1,name);
ps.setString(2,password);
//然后再获取查寻结果集,这里要记住不能再传了,因为再传还要再编译
rs = ps.executeQuery();
与普通的操作的不同点
- statement:先获取操作对象statement--------->再写sql语句,参数用加号拼接--------->再把sql语句传入查询结果集
- PerparedStatement:先写sql语句,有参数的部分用?作为占位符--------->传入预编译数据库操作对象进行预编译--------->再传参数,通过下标一个一个传--------->最后进行处理查询结果集,这里不需要传入sql语句,sql已经在操作对象中了
PerparedStatement更快,因为他相同功能语句样子都一样, mysql中相同的语句只用编译一次,所以编译一次可以执行n次。但是statement每次因为都要拼接,所以每次都要编译、
PerparedStatement输入参数的时候,也就是ps.setString(1,name);这一步会进行类型的安全检查,所以更安全。
综上:PerparedStatement没有sql注入、性能更好、更安全。
(有注入的需求,就必须用statement了:也就是需要进行拼接,比如升序降序,我们要传dsc和asc,进行sql拼接,而papare的只能传字符串的还有其他的类型,不能拼接编译)
10.jdbc事务
- jdbc中事务是自动提交的,也就是一条dml就自动提交一条
- 实际业务开发中,为墨门更需要多条一起执行
//关闭自动提交
conn.setAutoCommit(false);
//手动提交,代表事务结束
conn.commit();
//捕捉到一次,手动回滚,事务没完成,那么就回滚到事务开始时候的状态
conn.rollback
11.工具类
- 工具类的构造方法都是私有的,因为都是静态方法不用实例化
把注册驱动方法放在工具类的静态代码块里,使用工具类时候自动执行,并且只会执行一次。
把建立连接的封装到一个静态方法里。
把关闭的都封装的一个静态方法。
12.悲观锁和乐观锁
行级锁(悲观锁):
- 在查询的时候,在后边加一个for update,就是上锁,这个表里所有符合job = dd 的记录,其他事务都不能修改了
- 自己操作完了会开锁,别人才能改他
select xx,xx,xx from emp where job = 'dddd' for update
乐观锁:几个事务可以一起操作,但是每一条记录都会有一个版本号,每次事务都会检查版本号,如果拿到数据的时候的版本号和提交数据的时候的版本号发生了变化,说明有其他的事务在这期间修改了,那么就回滚放弃这次提交。