JDBC深刻理解

 

JDBC深刻java

 

课程介绍

 1.预编译语句对象mysql

 2.SQL注入问题面试

 3.用户登陆sql

 4.事务数据库

 5.链接池tomcat

 6.得到主键问题安全

预编译语句对象服务器

2.1 回顾Statement执行SQL语句

2.1.1 操做流程

i. 加载驱动并发

ii. 得到链接ide

iii. 准备sql语句以及Statement对象

iv. 执行sql语句

v. 释放资源

2.1.2 带来的问题

i. 拼接sql语句容易出错,麻烦;

ii. 存在sql注入问题;

2.1.3 sql注入问题

 

1. 使用Statement模拟sql注入问题

wKiom1ht_V3SMafYAABVQ07_YUg042.png-wh_50 

2. 使用PreparedStatement解决sql注入问题

3. 为何PreparedStatement就解决了sql注入问题呢?

使用PrepareStatement 它的语句至关是一个模板,这个模板的基本结构咱们是不可以进行更改的(不能拚接字符串),因而,它很好的解决了sql注入的问题

 

 

 

2.2 使用PreparedStatement执行SQL语句

2.2.1 概念

PreparedStatement:Statement的子接口,表示预编译SQL语句对象.

什么是预编译SQL语句?

 

预编译语句PreparedStatement 是java.sql中的一个接口,它是Statement的子接口。

经过Statement对象执行SQL语句时,须要将SQL语句发送给数据库,由数据库首先进行编译后再执行。

预编译语句和Statement不一样,在建立PreparedStatement 对象时就指定了SQL语句,该语句当即发送给数据库进行编译。

当该编译语句被执行时,数据库直接运行编译后的SQL语句,而不须要像其余SQL语句那样首先将其编译,再执行。

2.2.2 PreparedStatement使用

Api示例:

 

 wKiom1ht_TeS_OuHAAAITOP8uis631.png-wh_50

 

使用PreparedStatement的操做流程-完成update方法

 wKioL1ht_UnADWHTAABzPYsDH2A942.png-wh_50

2.2.3 注意事项

i. 库连错了,表指定错了,列名指定错了,sql语句拼错了;

ii. 上面的sql语句中的 ? 表明就是一个任意类型的值,不需加 ' '

iii. PreparedStatement中是一个模板,而不是拼接字符串

iv. 调用executeUpdate方法的时候不须要传入sql参数了,模板中已经有了;

v. set方法指定的列编号是从1开始

2.2.4 PreparedStatement特色

i. 代码可读性/维护性更高.

ii. PreparedStatement的执行性能更高.

iii. 安全性更高,防止SQL注入问题.

wKioL1ht_Ivj20JPAAAuSL-JUS4333.png-wh_50 

2.2.5 Statement和PreparedStatement区别(面试题)

第一           

数据库在执行sql语句的时候若是使用PreparedStatement语句会有一点优点:

由于数据库会preparedStatement   语句进行预编译,预编译的sql能够复用

下次执行相同的sql语句时,数据库端不会再进行预编译了,而直接用数据库的缓冲区,

提升数据访问的效率(尽可能采用使用?号的方式传递参数).

第二  

极大地提升了安全性,防止sql注入。

第三

增长代码的可读性 可维护性

最后:在JDBC应用中,若是是稍有水平开发者,你就应该始终以PreparedStatement代替Statement.也就是说,

在任什么时候候都不要使用Statement

用户登陆功能

登陆界面wKioL1ht_AzCI131AAASnDqZAOc146.png-wh_50

有以下几种登陆方式:

--------------------------------------------------------------------------------------------

1. 第一种

i. 在外面的DAO层应该有这样的一条sql:

select * from student where username=? and password=?

wKiom1ht_EywXQOqAACJLj5kL6E512.png-wh_50 

ii. 劣势 不可以独立判断出是用户名仍是密码错误

iii. 优点 简单

安全性更高,思考为何?

2. 第二种

i. 在外面的DAO层应该有这样的一条sql:

select * from student where username=?

ii. 如何判断重复的?

1. 先使用户名从数据库中得到数据

a) 若是没有返回东西,提示用户名错误

b) 若是获得结果了,从结果中得到密码和前台传入的密码进行比较

c) 若是相等  登录成功

d) 若是不相等,提示密码错误wKioL1ht_F3CBK0YAAFI6rNlDDk858.png-wh_50

 

wKiom1ht_HTQC7p7AACLFdBlBZQ731.png-wh_50 

 

iii. 劣势 麻烦一点

iv. 优点 能够单独判断是用户名仍是密码错误

用户体验更好,安全性没那么好,由于直接告诉了用户是 用户名错误 仍是密码错误

 

 

 

事务

4.1 银行转帐案例分析

1. 需求:王麻子准备把1000元转给女友小花

2. 数据准备-帐户表accountid,name,balance

3. 步骤

1) 查询 王麻子 的帐户余额是否大于等于1000块钱.

       SELECT  *  FROM account WHERE name = '王麻子' AND balance >= 1000;

① 余额小于1000: 系统提示:,您的余额不足,把钱赚够了再转吧!!!

② 余额大于等于1000: 继续后面的操做

2)  王麻子 的帐户上减小1000块钱.

UPDATE account SET balance = balance - 1000  WHERE name = '王麻子';

3)  小花 的帐户上增长1000块钱.

UPDATE account SET balance = balance + 1000  WHERE name = '小花';

 

4) 那么问题来了:若在第二步和第三步操做之间,忽然停电!!!--->程序中断(使用异常模拟.)

5) 模拟代码:

wKiom1ht-_Sg8LJqAABb9ws8wOY690.png-wh_50 

4.2 事务概述和处理

4.2.1 什么是事务

把多个操做单元当作是一个总体,所有的操做单元成功都才视为成功(commit),如有一个操做单元失败则,视为整个过程失败(rollback)

为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:当每一个逻辑操做单元所有完成时,数据的一致性能够保持,而当这个单元中的一部分操做失败,整个事务应所有视为错误,全部从起始点之后的操做应所有回退到开始状态。

4.2.2 事务的ACID属性(面试可能问到)

1. 原子性Atomicity

指整个事务是不能够分割的工做单元。只有事务中全部的操做执行成功,才算整个事务成功,

事务中任何一个SQL语句执行失败,那么已经执行成功的SQL语句也必须撤销,数据库状态应该回到执行事务前的状态。 

2. 一致性Consistency

指数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。

例如对于银行转帐事务,无论事务成功仍是失败,应该保证事务结束后两个转帐帐户的存款总额是与转帐前一致的。

3. 隔离性Isolation:

指的是在并发环境中,当不一样的事务同时操纵相同的数据时,每一个事务都有各自的完整数据空间。

即一个事务内部的操做及使用的数据对并发的其余事务是隔离的,并发执行的各个事务之间不能互相干扰。

4. 持久性Durability

指的是只要事务成功结束,它对数据库所作的更新就必须永久保存下来。

即便发生系统崩溃,从新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。

4.2.3 事务的处理机制

try{

   //开启事务,设置为的手动提交

   操做1

   操做2

   操做3

 

   ......

   //提交事务:

}catch(Exception e){

   //回滚事务

}

4.2.4 JDBC事务细节

1. JDBC中事务是默认自动提交的.

  在执行DML语句的时候就已经提交事务了.

2. 事务只对DML语句有效,对于DQL(查询)没效果,查询不会涉及到修改数据.

  可是,之后在Spring设置的时候,每每把查询也放到事务中.

  注意:之后作开发,方式代码没有报错,可是数据插入/修改/删除不成功,首先判断事务是否提交.

3. 回滚事务以后再释放资源,释放锁机制(InnoDB:行锁).

4. MySQL,MyISAM不支持外键,不支持事务,InnoDB都支持.

  在开发中,外键能够不要,可是事务必须得要.

Spring中有专门的事务管理器(TransactionManager):

得到主键问题(了解)

5.1 得到主键的场景分析

新增一个产品 产品库存

1. 从前台网页中录入数据的时候

a) 录入产品

b) 录入库存

2. 后台新增数据到表 product product_stock

a) 先新增一条产品数据,数据库生成一个主键id

b) 再新增库存记录(product_stock引用了product表的外键)

须要得到新增的productid,应该怎么弄?

5.2 Statement拿到主键的方式

Statement中有方法:

int executeUpdate(String sql)

返回的是影响的行数

int executeUpdate(String sql, int autoGeneratedKeys)  

返回仍是影响的行数

参数 autoGeneratedKeys,表示能够设置是否能够去得到自动生成的主键

值: Statement.RETURN_GENERATED_KEYS

 

设置能够获取以后,如何获取?

ResultSet getGeneratedKeys() 获取因为执行此 Statement 对象而建立的全部自动生成的键

 

注意

wKioL1ht-87humQwAABxz_cYiLA811.png-wh_50 

分析:

1) 表中明明是有id

2) 之前使用executeQuery查询的时候,返回的ResultSet中就是数据库表中的一条一条的数据,就是经过上面的方式来得到

3) getGeneratedKeys() 调用完毕以后返回的对象中只有键

4) 怎么拿?调用ResultSet中经过索引来得到列数据的方法

 

wKioL1ht-93D3GCYAAB5kOKBkhQ232.png-wh_50 

 

 

 

 

5.3 PreparedStatement 拿到主键的方式

wKiom1ht-7nSexWhAABXwWNswXw650.png-wh_50 

 

Hibernate中能够自动返回,(底层已经封装OK)

链接池

6.1 什么是链接池

链接池:就是用来装链接对象connection的容器

链接池的技术比较多,java仅仅是提供了DataSource的接口(就是链接池),不提供任何实现.由各大服务器厂商来提供DataSource实现(Tomcat,WebLogic).

 

示例

假设一个场景,火车站排队买票:

  无论有没有人,都须要留一个两个窗口,--有个初始窗口

1. 初始容量(5

   火车站刚开始须要有几个窗口

2. 若是人变多的时候,窗口不够怎么办---须要增长窗口,可是这个窗口不是随便增长

 当人数比较多的时候,添加窗口;

   最大数量(10)

 

3. 若是人变少的时候,只有几个窗口 -- 这时候须要最小窗口

   最小数量(2)

 

4. 若是有人占着窗口不放,怎么办?

一我的若是占着位置又不买票,好比占了3分钟,强制驱离不要占用窗口

  最大链接时间(3)

 

5. 票已经卖完的时候,还有人等待怎么办?

  若是等待过长,断开链接,请求超时;

  请求超时

 

这就是链接池的理解;

 

6.2 为何使用数据库链接池

普通的JDBC数据库链接使用 DriverManager 来获取,每次向数据库创建链接的时候都要将 Connection 加载到内存中,再验证用户名和密码(得花费0.05s1s的时间)

须要数据库链接的时候,就向数据库要求一个,执行完成后再断开链接。这样的方式将会消耗大量的资源和时间。

数据库的链接资源并无获得很好的重复利用.若同时有几百人甚至几千人在线,频繁的进行数据库链接操做将占用不少的系统资源,严重的甚至会形成服务器的崩溃。

对于每一次数据库链接,使用完后都得断开。不然,若是程序出现异常而未能关闭,将会致使数据库系统中的内存泄漏,最终将致使重启数据库。

这种开发不能控制被建立的链接对象数,系统资源会被毫无顾及的分配出去,如链接过多,也可能致使内存泄漏,服务器崩溃.

 

使用链接池和不使用链接池在代码上的区别:

 

获取链接:

使用链接池以前:

使用DriverManager来获取Connection对象.

Connection  conn = DriverManager.getConnection(url,username,password);

使用链接池以后:

直接找链接池(DataSource对象),取出Connection便可.

如何建立DataSource对象,在建立DataSource的时候,就会设置链接数据库的url,user,password.

Connection conn = DataSource对象.getConnection();

接下来的代码和之前相同.

 

释放链接:

代码:connection对象.close();

使用链接池以前:

直接和数据库服务器创建链接关系,而断开也是和数据库服务器断开链接.

使用链接池以后:

直接和链接池创建链接关系,而断开也是把Connection对象还给链接池,供其余客户使用.

    没有真正的和数据库断开. 如此一来,一个Connection对象就获得了充分的利用!!!

 

6.3 经常使用链接池

DBCP 链接池

C3P0  链接池

 

Druid 链接池 : 如今用的最多,是阿里巴巴开源的链接池。

 

排第一 Druid 

排第二  tomcat 里面有个链接池

6.4 DBCP链接池实现

准备:

  拷贝jar:

  commons-dbcp-1.3.jar      commons-pool-1.5.6.jar .

 

查阅文档:commons-dbcp-1.3-src\doc\BasicDataSourceExample.java

---------------------------------------------------------------------------

解决DBCP的硬编码问题:

  应该把链接信息放到配置文件中去:  配置文件的名词能够任意.

---------------------------------------------------------------------------

dbcp.properties

#链接字符串

url=jdbc:mysql://localhost:3306/jdbcdemo

#用户名

username=root

#密码

password=admin

#驱动的类路径

driverClassName=com.mysql.jdbc.Driver

#链接池启动时的初始值

initialSize=1

#链接池的最大值

maxActive=50

#链接池的最大空闲数

maxIdle=20

---------------------------------------------------------------------------

如何把dbcp.properties中的配置信息,设置到程序中去:

wKiom1ht-0nB0scqAACb***cJTo576.png-wh_50wKioL1ht-1-QD3f9AABLxTTt-O0943.png-wh_50 

 

直接读取资源文件的方式:

wKiom1ht-xmAQR66AABXydUx6jw824.png-wh_50 

注意:db.properties里的属性名称必须和set的属性名字的对应上

相关文章
相关标签/搜索