Catalog
  1. 1. 事务
    1. 1.1. 概念
    2. 1.2. 操作
    3. 1.3. 事务的四大特性
    4. 1.4. 事务的隔离级别
  2. 2. JDBC
    1. 2.1. 概念
    2. 2.2. 步骤
    3. 2.3. 详解各个对象
      1. 2.3.1. DriverManager:驱动管理对象
      2. 2.3.2. Connection:数据连接对象
      3. 2.3.3. Statement:执行sql的对象
      4. 2.3.4. ResultSet:结果集对象
      5. 2.3.5. PreparedStatement:执行sql的对象
    4. 2.4. 抽取JBDBC工具类
  3. 3. 数据库连接池
    1. 3.1. 概念
    2. 3.2. 实现
    3. 3.3. Spring JDBC
事务和JDBC

事务

概念

如果一个包含多个步骤的业务操作,被事务管理,要么同时成功,要么同时失败。

操作

  1. 开启事务:start transaction
  2. 回滚:rollback
  3. 提交:commit

在MySQL数据库中事务默认自动提交

  • 一条DML(增删改)语句会自动提交一次事务。
  • 事务提交的两种方式:
    • 自动提交
    • 手动提交:需要先开启事务,再提交(Oracle数据库默认手动提交事务)
  • 修改事务的默认提交方式
    • 查看事务的默认提交方式:SELECT @@autocommit
    • 修改默认提交方式:set @@autocommit=0

事务的四大特性

  1. 原子性:是不可分割的最小操作单位,要么同时成功,要么同时失败。
  2. 持久性:当事务提交或回滚后,数据库会持久化的保存数据。
  3. 隔离性:多个事务之间。相互独立。
  4. 一致性:事务操作前后数据总量不变。

事务的隔离级别

概念:多个事务之间是相互独立的。但是多个事务操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题。

存在问题:

  • 脏读:一个事务,读取到另一个事务中没有提交的数据。
  • 不可重复读:在同一个事务中,两次读取到的数据不一样。
  • 幻读:一个事务操作(DML)数据表中所有的记录,另一个事务添加了一条数据,则第一个事务查询不到自己的修改。

隔离级别:

  • read uncommitted:读未提交
    • 产生的问题:脏读,不可重复读,幻读
  • read committed:读已提交(Oracle)
    • 产生的问题:不可重复读,幻读
  • repeatable read:可重复读(MySQL默认)
    • 产生的问题:幻读
  • serializable:串行化
    • 可以解决所有的问题

注意:隔离级别从小到大安全性越来越高,效率越来越低

数据库查询隔离级别: select @@tx_isolation;

数据库设置隔离级别:set global transaction isolation level 级别字符串;

JDBC

概念

Java DataBase Connectivity Java数据库连接,Java语言操作数据库

JDBC本质:Sun公司定义了一套操作所有关系型数据库的规则(接口)。各个数据库厂商实现这套接口,提供数据库驱动jar包,我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类

步骤

  • 导入驱动jar包
  • 注册驱动
  • 获取数据库连接对象Connection
  • 定义sql
  • 获取执行sql语句的对象Statement
  • 执行sql。接受返回结果
  • 处理结果
  • 释放资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) throws Exception {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取数据库连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/eesy", "root", "12345678");
//定义sql语句
String sql = "update account set money = 20 where id = 1";
//获取执行sql的对象Statement
Statement statement = conn.createStatement();
int count = statement.executeUpdate(sql);
System.out.println(count);
//释放资源
statement.close();
conn.close();
}

详解各个对象

DriverManager:驱动管理对象

功能:

  • 注册驱动:告诉程序该使用哪一个数据库jar包
    • static void registerDriver(Driver driver):注册与给定的驱动程序。
    • 我们使用Class.forName(“com.mysql.jdbc.Driver”)
    • 通过静态代码块,使类被加载时,方法就被执行
    • 5版本之后可以省略注册驱动的步骤
  • 获取数据库连接:
    • 参数:
      • URL: 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
      • user:用户名
      • password:密码
    • 如果连接的是本机的mysql服务器,并且mysql服务器默认端口是3306,则URL可以简写为:jdbc:mysql:///数据库名称

      Connection:数据连接对象

      功能:
  • 获取执行sql对象
    • Statement createStatement()
    • PreparedStatement prepareStatement(String sql)
  • 管理事务:
    • 开启事务:void setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务。
    • 提交事务:void commit()
    • 回滚事务:void rollback()

      Statement:执行sql的对象

  1. 执行sql
    • boolean execute(String sql):可以执行任意的sql
    • int executeUpdate(String sql):执行DML(insert、update、delete)语句,DDL语句(create,alter、drop)语句。
      • 返回值:影响的行数,可以通过函数判断是否执行成功
    • ResultSet executeQuery(String sql):执行DQL(select)语句。

      ResultSet:结果集对象

      用来封装查询的结果
  • next():游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true
  • getXxx(参数):获取数据,XXX代表数据类型,如 int getInt(),参数:
    • int:代表列的编号,从1开始。
    • String:代表列的名称
  • 使用步骤:
    1. 游标向下移动一行
    2. 判断是否有数据
    3. 获取数据

      PreparedStatement:执行sql的对象

      SQL注入问题:在拼接sql时,有一些SQL关键字参与字符串的拼接,会造成安全性问题。

例如用户随便输入密码,再加一句 OR ‘a’ = ‘a’,这就恒成立了

解决SQL注入问题:使用PreparedStatement对象来解决。

预编译的SQL:参数使用?作为占位符

  • setString(1,”root”);
  • setInt(2,12345678);

后期都会使用PrepareStatement对象,因为安全性和效率都更高。

抽取JBDBC工具类

目的:简化书写

分析:

  • 抽取一个方法获取连接对象,通过配置文件
  • 抽取一个方法释放资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;

static {
//读取资源文件
Properties pro = new Properties();
try {
//获取src路径下文件的方式,类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL resource = classLoader.getResource("jdbc.properties");
String path = resource.getPath();
// pro.load(new FileReader("src/jdbc.properties"));
pro.load(new FileReader(path));
url = pro.getProperty("url");
password = pro.getProperty("password");
user = pro.getProperty("user");
driver = pro.getProperty("driver");
Class.forName(driver);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}

public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,user,password);
}

public static void close(Statement s,Connection conn){
if(s!=null){
try{
s.close();
}catch(SQLException e){
e.printStackTrace();
}
}

if(conn!=null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}

public static void close(ResultSet rs,Statement s, Connection conn){

if(rs!=null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
}

if(s!=null){
try{
s.close();
}catch(SQLException e){
e.printStackTrace();
}
}

if(conn!=null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}

}

数据库连接池

概念

其实就是一个容器(集合),存放数据库连接的容器。

当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户访问完之后,会讲连接对象归还给容器。

好处:

  • 节约资源
  • 用户访问高效

实现

标准接口:DataSource javax.sql包下的

方法:

  • 获取连接:getConnection()
  • 归还连接:如果连接对象Connection是从连接池中获取的,那么调用Connection.close()是归还连接,而不是关闭连接。

一般我们不去实现它,有数据库厂商来实现

  • c3p0:较老
  • Druid:数据库连接池实现技术,由阿里巴巴提供
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class JDBCUtils {
private static DataSource ds;

static{
Properties pro = new Properties();
try {
pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
}
}

public static Connection getConnection() throws SQLException {
return ds.getConnection();
}

public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);
}

public static void close(Resultset rs, Statement stmt, Connection conn){

if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}


if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

/**
* 获取连接池
*/
public static DataSource getDataSource(){
return ds;
}
}

Spring JDBC

spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象来简化JDBC的框架。

步骤:

  • 导入jar包
  • 创建JDBCTemplate对象。依赖于数据源DataSource
    • JdbcTemplate template = new jdbcTemplate(ds);
  • 调用JdbcTemplate的方法来完成CRUD操作
    • update():执行增删改语句
    • queryForMap():查询结果,将结果集封装为Map集合
      • 查询的结果集长度只能是1,将列名作为key,将值作为value,封装为Map。
    • queryFoeList():查询结构将结果封装为List集合
    • query():查询结构,将结果封装为JavaBean对象
    • queryForObject():查询结果,将结果封装为对象
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      List<Emp> list = template.query(sql,new RowMapper<Emp>(){

      @Override
      public Emp mapRow(Result rs,int i)throws SQLException{
      Emp emp = new Emp();
      int id = rs.getInt("id");
      String name = rs.getString("name");
      emp.setId(id);
      emp.setName(name);
      return emp;
      }
      });
      上面是自己写的实现类,也可以使用提供好的实现类
      1
      List<Emp> list = template.query(sql,new BeanPropertyRowMapper<Emp>(Emp.class));
      上面的一个问题在于,如果我们定义的类的属性是基本数据类型,就无法接收null,可以将属性的类型设置为引用类型,比如Integer。
Author: zycode1561
Link: https://zycode1561.github.io/2020/01/30/%E4%BA%8B%E5%8A%A1%E5%92%8CJDBC/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
  • 支付宝

Comment