1 JDBC本质
- JDBC:Java DataBase Connective
- 本质是一个sun公司制定的一套接口:java和数据库连接的接口
- 数据库厂家面对接口,编写了很多实现类,这些类就是驱动
以下是模拟JDBC本质的代码:一套连接java和数据库的接口
//接口指定者:sum公司 编写JDBC接口
interface JDBC{
//抽象方法:连接 返回类型 + 方法名();
void getConnection();
}
------------------------------------------------------------------
//实现者:MySQL厂家
public class MySQL implements JDBC{
//具体实现方法
public void getConnection(){
//MySQL底层原理
//...
System.out.println("MySQL");
}
}
------------------------------------------------------------------
//调用者1: 程序员 面向JDBC接口编程
public class JavaProgrammer1{
public static void main(String[] args){
//接口是抽象的,无法new出对象
//JDBC jdbc = new JDBC();
//但是java中存在多态机制,即父类型引用可以指向子类型
//父类型[接口]:JDBC, 子类[实现接口的类]:MySQL
JDBC jdbc = new MySQL();
//调用子类中的方法
jdbc.getConnection();
}
}
------------------------------------------------------------------
//调用者2:采用反射机制,创建对象
public class JavaProgrammer2{
public static void main(String[] args) throws Exception{
//配置文件源
ResourceBundle bundle = new ResourceBundle("jdbc");
//根据key获取value值
String className = budle.getString("className");
Class c = Class.forName(className);
//实例化对象
JDBC jdbc = (JDBC)c.newInstance();
jdbc.getConnection();
}
}
------------------------------------------------------------------
//配置文件:jdbc.properties
className=MySQL
2 JDBC编程(六步)
- 注册驱动
- 告诉Java程序,即将连接哪种品牌的数据库
- 获取连接
- 打开JVM和数据库的进程通道。注:使用完,需要关闭通道
- 获取操作对象
- 专门执行SQL语句的对象
- 执行SQL语句
- DQL、DML…
- 处理查询结果
- 只有当第四步由select语句,才有这一步查询结果集
- 释放资源
- 使用完资源,一定要关闭
3 JDBC编程时出现的问题:SQL注入
-
什么是SQL注入?
- 注入了不安全的信息
-
什么是不安全的信息?
- 所谓不安全的信息,指的是输入了带有SQL语句的关键字,使得原本SQL语句的意思发生改变,这样造成数据库的不安全
-
如何防止SQL注入?
-
在SQL语句注入前,对其进行预处理
- 所谓预处理,就是先制定SQL语句的框架。SQL语句中的那些从外部输入的信息,用占位符?替代
- 再调用PreparedStatement**,获取预准备操作对象**
- 通过PreparedStatement,调用setObject(1,userName)传入指定下标的参数,JDBC下标是从1开始
- 通过ps.executeQuery(),执行SQL语句,返回ResultSet结果集合
-
4 详细JDBC操作流程:防止SQL注入
-
注册驱动
-
告诉java程序,获取什么品牌的驱动
Class.forName("com.mysql.cj.jdbc.Driver");
-
-
获取连接
-
通过驱动管理(DriverManager),连接java和数据库进程通道
Connection conn = DriverManager.getConnection(url,user,password);
注意:在获取连接的时候,可能会出现以下异常
java.sql.SQLException: The server time zone value '�й���ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
原因:mysql的时区问题
方法一:【推荐】
在dos窗口,连接完数据库后,修改mysql的时区
set global time_zone = '+8:00';//修改Myqsl全局时区为北京时间,即我们的东8区 set time_zone = '+8:00';//修改当前会话时区
方法二:修改url
String url = "jdbc:mysql://localhost:3306/aa?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC";
-
-
编写SQL语句框架
-
利用占位符?表示后面传入的参数
String sql = "select * from tb_user where loginName = ? and loginPws = ?";
-
目的:防止SQL注入,因为输入的信息,可能含有SQL语句的关键字,从而可能会修改了本地SQL语句,造成本地数据库不安全。
-
在MySQL中,如果SQL语句一致,则只会编译一次,下一次执行,不会编译,直接输出结果。因而,设置了SQL语句框架,只会执行一次,后续修改,只要修改?占位符就行。提高执行效率
-
-
获取数据库预准备操作对象
-
通过JDBC通道,调用预准备操作对象(PrepareStatement)
PreparedStatement ps = conn.prepareStatement(sql);
-
-
输入占位符?形参 : JDBC下标从1开始
-
通过预准备操纵对象的setObject,传入SQL框架中的形参
ps.setObject(1,userName);
-
-
执行SQL语句,返回ResultSet结果集
ResultSet res = ps.executeQuery();
-
处理结果集查询结果
if(res.next()){ loginStatus = true;// 在之前打一个标记:loginStatus = false; }
-
释放资源
- 关闭JDBC连接通道: conn
- 释放预处理操作对象: ps
- 释放结果资源:res
5 执行SQL语句中:executeUpdate()和executeQuery()
executeUpdate():
- 发生在SQL语句的insert、delete、update
- 返回值:影响数据库的记录的条数
executeQuery():
- 发生在SQL语句的select语句中,查询语句
- 返回值:ResultSet结果集
6 悲观锁 和 乐观锁
- 悲观锁:
- 顾名思义,它总是假设最坏的情况,每次取数据时,其他线程会对数据进行修改,因而加锁(读锁,写锁,行锁)
- 缺点:会造成阻塞,其他线程会进行排队状态
- 乐观锁:
- 顾名思义,它总是假设最好的情况,数据不会发生并发问题,每次取数据时,其他线程不会对数据进行修改,因此不会上锁。但是,在更新时**,会判断其他线程在这之前有没有对数据进行修改**,一般通过版本号和CAS操作实现
- 写入操作频繁的时候,使用悲观锁,不希望别的线程修改
- 读取操作频繁的时候,使用乐观锁,不影响其他线程的查询
1、version方式:
一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
核心SQL代码:
update table set x=x+1, version=version+1 where id=#{id} and version=#{version};
2、CAS(定义见后)操作方式:
即compare and swap 或者 compare and set,涉及到三个操作数,数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。