文章目录
1、jdbc简介
发展历史:
2、如何使用jdbc
可分为5步
//1. 加载数据库驱动 (可以省略的. 在JavaEE的web项目中,jar包不可以省略.)
// mysql 6以下驱动:com.mysql.jdbc.Driver
// mysql 6及以上驱动:com.mysql.cj.jdbc.Driver
Class.forName("com.mysql.jdbc.Driver");
//2. 通过驱动管理器, 获取JDBC的连接对象
// 连接地址格式:
// 主协议:子协议://ip地址:端口号/数据库名称
// mysql: jdbc:mysql://localhost:3306/jdbc
// oracle: jdbc:oracle:thin:@ip地址:1521/ORCL
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8", "root", "123");
//3. 通过连接对象,创建SQL执行对象 (SQL执行环境)
Statement state = conn.createStatement();
//4. 通过SQL执行对象 执行SQL
state.execute("create table person(id int,nickname varchar(32))");
//5. 释放连接
state.close();
conn.close();
jdbc常用的类型与方法
1. DriverManager : 驱动管理器
2. Connection : 数据库连接对象
//前两个就是连接的
3. Statement : SQL执行对象
常用方法:
- excute(执行所有sql都可以用)
返回值:Boolean
- excuteUpdate(用于执行DML与DDL语句)
返回值:int(表示对数据库影响的行数)
- excuteQuery(用于执行DQL语句)
返回值:ResultSet(set集合)
4、ResultSet : 结果集对象 (指的是一个select语句的查询结果)
常用方法:
1、控制游标移动的常用方法(注意是行的移动):
- boolean next() ****
作用: 控制游标向下一行移动.
-下面4个作为了解(都返回的布尔):
privious() 控制游标向上一行移动
absolute(int 行号) 控制游标向指定行移动
beforeFirst()控制游标移动到第一行
afterLast() 控制游标移动到最后一行
2、获取游标指向行的字段值的常用方法:
- XXX getXXX(String 列名) ***
根据字段名, 得到此字段的值
- XXX getXXX(int 字段的索引) *
根据字段的索引, 得到字段的值 , 索引从1开始
工厂方法设计模式
( 静态工厂方法模式 )
3、SQL注入问题 (重)
问题描述
进行用户登录时, 输入不存在的帐号 和 如下的密码:
1' or '1'='1
结果显示登录成功.
因为用户输入的密码, 与我们的查询语句拼接后, 使得我们的查询语句产生了歧义:
原查询语句:
select * from xzk_user where username='' and password='密码'
拼接后:
select * from xzk_user where username='hahahaheiheihei' and password='1' or
'1'='1'
如何解决
我们可以将SQL语句与参数分离,将参数作为SQL的特殊部分进行预处理.
即通过PreparedStatement 预编译的SQL执行环境解决问题
内部实现原理:
1. 将未拼接参数的SQL语句, 作为SQL指令, 先传递给数据库 进行编译.
2. 再将参数传递给数据库, 此时传递的参数不会再作为指令执行, 只会被当作文本存在.
操作流程与Statement基本一致:
1. 如何得到一个PreparedStatement 对象
PreparedStatement state = conn.prepareStatement("预编译的SQL语句");
2. 预编译的SQL语句如何编写
需要填充参数的位置, 使用?代替即可! 例如:
select id from xzk_user where username=? and password=?
3. 参数如何填充
state.setXXX(int index,XXX value);
setXXX中XXX指的是数据类型,
参数1: index : SQL语句中?的索引值 , 从1开始
参数2: value : 填充的参数值.
4. 如何执行填充完毕参数的SQL
- boolean execute();
- int executeUpdate();
- ResultSet executeQuery();
4、事务
概述: 对数据进行操作的多条sql语句放在一起形成的一个完整的逻辑单元叫做事务
特性: 事务是一个整体. 要么一起成功, 要么一起失败.
事务在mysql中, 是默认自动提交的 .
如何操作事务
1、通过命令行
- 开启事务: start transaction;
- 回滚 : rollback; --此次事务中所有的sql操作, 放弃.
- 提交 : commit; --此次事务中所有的sql操作, 作为一个整体, 提交.
2、通过java
JDBC事务通过连接对象开启, 回滚 ,提交. 只针对当前连接对象生效.
- 开启事务: conn.setAutoCommit(false);
- 回滚事务: conn.rollback();
- 提交事务: conn.commit();
关于事务的面试题
1. 请描述事务的四大特征 :
<1>. 原子性: 事务是一个整体 , 不可分割 , 要么同时成功, 要么同时失败.
<2>. 持久性: 当事务提交或回滚后, 数据库会持久化的保存数据.
<3>. 隔离性: 多个事务之间, 隔离开, 相互独立.
<4>. 一致性: 事务操作的前后 , 数据总量不变 (例如: 转账时: 孟亮给帅兵转账是
一个事务, 转账完毕后. 两人余额的和不变.)
2. 请描述什么是脏读, 幻读, 不可重复读 ?
- 脏读: 读取到了一个事务 未提交的数据.
- 不可重复读: 一个事务中, 两次连续的读取 ,结果不一致(中间被其它事务更改了).
- 幻读: 一个事务A在执行DML语句时, 另一个事务B也在执行DML语句 , B修改了A修改过的
数据, 导致A在查询时就像发生了幻觉一样(A更改的内容A看不到了.)
3. 请描述事务的隔离级别
//三种级别锁: 页级,表级,行级(共享锁,排它锁).
1. 读未提交 : read uncommitted; (可能产生:脏读, 不可重复读, 幻读)
2. 读已提交 : read committed; (可能产生: 不可重复度, 幻读)
3. 可重复读 : repeatable read;(mysql默认值) (可能产生: 幻读)
4. 串行化 : serializable;
- 查看数据库当前的隔离级别: select @@tx_isolation; (了解)
- 数据库设置隔离级别: set global transaction isolation level 级别字符串;
(了解)
总结:实际上可以理解为(假设有a,b事务):
1、脏读就是a没提交表的数据就自动变化影响了b,
2、不可重复读就是a提交了数据表的的数据就自动变化影响了b
3、幻读就是a提交了数据,表虽然自动变化了,但是我重复读不会发现问题,但是一旦b对表进行了操作,此时数据就会出现问题
5、批处理
概念:
将多条语句, 放到一起批量处理 .
批处理的原理: 将多条SQL语句, 转换为一个SQL指令. 显著的提高大量SQL语句执行时的数据库性能.
使用流程
//Statement对象使用流程:
1. 得到Statement对象
Statement state = conn.createStatement();
2. 将一条SQL语句, 加入到批处理中.
state.addBatch(String sql);
3. 执行批处理
state.executeBatch();
4. 清空批处理
state.clearBatch();
//PreparedStatement对象使用流程:
1. 得到PreparedStatement对象
PreparedStatement state = conn.prepareStatement("预编译的SQL");
2. 填充预编译的参数
state.setXXX(1,填充参数);
3. 将一条填充完毕参数的SQL, 加入到批处理中.
state.addBatch();
4. 执行批处理
state.executeBatch();
5. 清空批处理
state.clearBatch();
6、连接池(DataSource)概念(重!)
概念
连接池用于缓存连接!
当我们需要使用连接时, 可以不用再创建连接 ! 可以直接从连接池中获取连接.
当连接池中存在空闲连接时, 会将空闲连接给到程序使用.
当连接池中不存在空闲连接时, 且连接池未满时 , 则创建连接提供给程序使用 ,并在程序使用完毕后,缓存连接.
当连接池中不存在空闲连接时, 且连接池已满时 , 则排队等候空闲连接的出现.注意:
使用连接池中的连接对象操作数据库时, 操作完毕依然需要释放连接(调用close()).
连接池中的连接在设计时, 使用了动态代理设计模式+装饰者设计模式 . 我们调用它的close方法,
代理没有关闭这个连接, 而是将连接重新放入了池中.
Properties 作为配置文件
这个是后面连接池的使用需要这个文件
Properties类 是Java中的Map集合的实现类.
.properties文件 用于通过文件描述一组键值对!
.properties文件 ,可以快速的转换为Properties类的对象.
文件中内容的格式:
文件内容都是字符串 , 键与值之间通过等号连接 , 多个键值对之间换行分割.
例如:
url=xxx
user=xxx
password=xxx
如何将文件 转换为 集合:
步骤:
1. 创建Properties对象
Properties ppt = new Properties();
2. 创建一个字节输入流 , 指向.properties文件
InputStream is = new FileInputStream("文件地址");
3. 将字节输入流, 传递给properties对象, 进行加载.
ppt.load(is)
配置例子
#获取连接数据库的地址
url=jdbc:mysql://localhost:3306/自己的数据库名?useUnicode=true&characterEncoding=utf-8
username=自己的
password=自己的
#获取驱动类
driverClassName=com.mysql.jdbc.Driver
#最大初始化数
initialSize=5
#最大连接数
maxActive=10
#最小连接数
minIdle=5
#最大等待时间(比如数据库正在被访问,访问3s访问不到就不访问了)
maxWait=3000
DBCP连接池
1、使用步骤
1. 引入相关的jar文件
- druid-1.0.9.jar
2. 将配置文件引入
3. 将配置文件, 转换为Properties对象
Properties ppt = new Properties();
ppt.load(配置文件的输入流);
4. 通过连接池的工厂类(DruidDataSourceFactory)的创建连接池的方法(createDataSource())
DataSource ds = DruidDataSourceFactory.createDataSource(ppt);
5. 从连接池中 获取连接对象
Connection conn = ds.getConnection();
2、DBCPUtil工具类(简化获取连接的方式)
注意:需要自己创建,下面只有类的方法和属性
private static DataSource ds = null;
/**
* 通过静态代码块创建一次连接池就行
* 先加载Properties,然后通过德鲁伊工厂创造连接池
*/
static{
Properties ppt = new Properties();
try {
ppt.load(DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties"));
ds = DruidDataSourceFactory.createDataSource(ppt);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 用于从DBCP连接池中 获取一个连接
* @return DBCP连接池中的一个连接对象.
*/
public static Connection getConnection(){
try {
return ds.getConnection();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
/**
* 释放资源不止一个,要全部释放
* 有顺序,最后获取的最先关闭
* @param conn 要释放的连接资源
* @param state 要释放的执行环境资源
* @param result 要释放的结果集资源
*/
public static void close(Connection conn, ResultSet rs, Statement state){
try {
rs.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
try {
state.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
try {
conn.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
}
德鲁伊(Druid)连接池
1、使用步骤(跟上面的差不多,除了创建连接池的工厂类和要导入的jar包不一样)
1. 引入相关的jar文件
- druid-1.0.9.jar
2. 将配置文件引入
3. 将配置文件, 转换为Properties对象
Properties ppt = new Properties();
ppt.load(配置文件的输入流);
4. 通过连接池的工厂类(DruidDataSourceFactory)的创建连接池的方法(createDataSource())
DataSource ds = DruidDataSourceFactory.createDataSource(ppt);
5. 从连接池中 获取连接对象
Connection conn = ds.getConnection();
2、DruidUtil工具类(简化获取连接的方式)(和上面的创建一样)
注意:需要自己创建,下面只有类的方法和属性
private static DataSource ds = null;
/**
* 通过静态代码块创建一次连接池就行
* 先加载Properties,然后通过德鲁伊工厂创造连接池
*/
static{
Properties ppt = new Properties();
try {
ppt.load(DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties"));
ds = DruidDataSourceFactory.createDataSource(ppt);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//创建连接
public static Connection getConnection(){
try {
return ds.getConnection();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
/**
* 释放资源不止一个,要全部释放
* 有顺序,最后获取的最先关闭
* @param conn
*/
public static void close(Connection conn, ResultSet rs, Statement state){
try {
rs.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
try {
state.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
try {
conn.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
}
public static void close(Connection conn, ResultSet rs, Statement state){
try {
rs.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
try {
state.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
try {
conn.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
}