0%

事务_连接池_JNDI

MySQL事务

事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。

如果有两条sql语句执行,要么两条sql语句都执行成功,要么两头都不执行。

例:A给B转账

1
2
update account set money=money-100 where name='a';
update account set money=money+100 where name='b';

数据库开启事务命令

  • mysql支持事务
  • mysql默认自动提交事务,每条语句都处在单独的事务中。
  • 事务操作
    • start transaction 开启事务
    • Rollback 回滚事务
    • commit 提交事务

JDBC使用事务

当jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据提交在它上面发送的sql语句。若想关闭这个默认提交方式,让多条sql在同一个事物中执行,可以用connection对象中一些方法来实现上面提到的三个事务操作。

  • Connection.setAutoCommit(false); start transaction
  • Connection.rollback(); rollback
  • Connection.commit(); commit
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
public class Demo01 {

public static void main(String[] args){
// 通过java代码进行转账
// 获取连接
Connection conn = null;
Statement stmt = null;
try {
conn = DBUtils.getConnection();
stmt = conn.createStatement();

// 设置事务的隔离级别
conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);

// 开启事务
conn.setAutoCommit(false);

// 转账a账号转100给b账号
stmt.executeUpdate("update account set money=money-100 where name ='a'");
int i = 1/0; // 制造异常
stmt.executeUpdate("update account set money=money+100 where name ='b'");

// 提交事务
conn.commit();
} catch (Exception e) {
e.printStackTrace();
// 如果遇到错误就进行回滚
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
// 关闭资源
DBUtils.close(conn, null, stmt);
}
}
}

事物的特性

原子性:指事物是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

一致性:事务必须使数据库从一个一致性状态变换成另一个一致性状态。转帐前和转账后的总金额不变。

隔离性:事物的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事物,不能被其他食物的操作数据所干扰,多个并发事物之间要相互隔离。

持久性:指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

事物的隔离级别

数据库并发操作可能会引起下面的问题:

脏读:指一个事务读取了另外一个事务未提交的数据。

不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同。一个事务取到了另一个事务提交后的数据。(update)

虚读(幻读):指在一个事务内读取到了别的事物插入的数据,导致前后读取不一致。(insert)

数据库通过设置事物的隔离级别来防止以上情况的发生:

  1. READ UNCOMMITED[读未提交]:上述三种情况都有可能发生。

  2. READ COMMITED[读已提交]:可以避免脏读。(oracle默认)

  3. REPEATABLE READ[可重复度]:避免脏读,不可重复度。虚读有可能发生。(MySQL默认)

  4. SERIALIZABLE:避免上面三种情况。不能多线程操作。

    数据越安全,性能越低。

MySQL中:

  • 查看当前事务隔离级别:SELECT @@TX_ISOLATION;
  • 更改当前的事务隔离级别:SET TRANSACTION ISOLATION LEVEL 上面四个级别之一。
  • 设置隔离级别必须在事务之前。

隔离级别演示

通过两个终端访问数据库来模拟多线程。

数据库连接池

当连接数据库的连接数很多时,为每一个用户单独创建连接就变得效率低下了。所以我们有了连接池,在连接池中事先放好很多连接,用户请求连接数据库时,从连接池中拿到连接,用完再放回连接池。

解决了建立数据库连接耗费资源和时间很多的问题,提高性能。

常用的数据库连接池

DBCP(Database Connection Pool)数据库连接池,是java数据库连接池的一种,由Apache开发,通过数据库连接池,可以让程序自动管理数据库连接的释放和断开。

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3和JDBC2的规范,目前使用它的开源项目有Hibernate和Spring。

使用DBCP连接数据库

导入commons-DBCP和commons-pool两个包。

使用db.properties配置数据库和连接池的设置。

使用C3P0连接数据库

添加C3P0.jar包。

编写配置文件c3p0-config.xml,放在classpath或classes目录中。

JNDI

Java Naming and Directory interface是一个应用程序设计的API。为开发人员提供查找和访问各种命名和服务的通用、统一的接口。类似于JDBC都是构建在抽象层上。现在JNDI已经成为了J2EE的标准之一,所有的J2EE容器都必须提供一个JNDI服务。

Tomcat中配置JNDI数据源

开发JavaWeb应用,必须使用一个JavaWeb服务器,JavaWeb服务器都内置数据源。这种配置就是JNDI。

Tomcat中内置了数据源DBCP,使用数据源只需要配置服务器即可。

配置数据源的步骤

  1. 拷贝数据库连接驱动的jar到tomcat的lib目录下。

    • 出现java.lang.AbstractMethodError请使用更高版本的数据库驱动连接。
  2. 配置数据源的XML文件

    1. 如果把配置信息写在tomcat下conf目录的context.xml中,那么多有应用都能使用此数据源。

    2. 如果是配置在应用的META-INF中创建context.xml,编写数据源,那么是由当前应用可以使用。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      <?xml version="1.0" encoding="UTF-8"?>
      <Context>
      <Resource name="jdbc/db1"
      auth="Container"
      type="javax.sql.DataSource"
      username="root"
      password="123456"
      driverClassName="com.mysql.jdbc.Driver"
      url="jdbc:mysql//localhost:3306/db1"
      maxTotal="8"
      maxIdle="4"
      />
      </Context>
  3. 使用连接池

    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
    public class DBUtils {  
    private static DataSource ds;

    static {
    try {
    // 读取数据源context.xml
    Context context = new InitialContext();
    ds = (DataSource)context.lookup("java:comp/env/jdbc/dbdemo");
    } catch (NamingException e) {
    e.printStackTrace();
    }
    }

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

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

多个数据源

一个项目中可能要访问多个数据库。

声明多个<Resource/>即可。

数据源:可以理解为数据库连接池。有3中实现方式:

  1. DBCP。
  2. C3P0,一般与hibernate一起使用。
  3. JNDI,tomcat内置的dbcp。