您的位置:首页 > 编程语言 > Java开发

javaweb-事务和连接池

2018-02-05 22:59 274 查看
一、事务

什么是事务?

事务,一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元。这些单元要么全都成功,要么全都不成功。

做一件事情,这个一件事情中有多个组成单元,这个多个组成单元要不同时成功,要不同时失败。A账户转给B账户钱,将A账户转出钱的操作与B账户转入钱的操作绑定到一个事务中,要不这两个动作同时成功,代表这次转账成功,要不就两个动作同时失败,代表这次转账失败。

事务在开发中的作用


下面来举例说明什么是事务,如下所示:

现实生活中的银行转账业务,张三要给李四转账1000元,而在程序员眼中两条SQL语句就可以搞定,如下:

l 给张三的账户减去1000元;

l 给李四的账户加上1000元;

如果在转账的业务中,成功的将张三的账户减去1000元,而在给李四的账户加1000元的时候,程序出现了问题,李四的账户没有加上1000元,而张三的账户却减掉了1000元,在现实生活中,这种情况肯定是不允许存在的。当我们将这个转账业务放在一个事务中,就不会出现以上情况了。

事务中有多个操作,这些操作要么全部成功,要么全部失败,也就是说给张三的账户减去1000元如果成功了,那么给李四的账户加上1000元的操作也必须是成功的,否则给张三减去1000元,以及给李四加上1000元都必须是失败的


mysql的事务控制

MySQL下如何开启事务
方式一:
      start  transaction   开启事务
      rollback    事务回滚(将数据恢复到事务开始时的状态)
      commit    事务提交(对事务中进行操作,进行确认操作,事务在提交后,数据就不可再进行恢复)
方式二:
     show variables like '%commit%';   可以查看当前autocommit 值
     在MySQL数据库中它的默认值是 "on" 代表自动事务。
     自动事务的意义就是:执行任意一条SQL语句都会自动提交事务。
     测试:将autocommit的值设置成off
           1. set autocommit=off  
           2. 必须手动commit才可以将事务提交
           注意:MySQL 默认autocommit=on   oracle默认的autocommit=off
                     如果设置autocommit 为 off,意味着以后每条SQL 都会处于一个事务中,相当于每条SQL执行前 都执行                      start transaction

mysql默认事务是自动提交的,一条sql是一个事务

当手动开启事务后,数据库默认的事务的自动提交暂时失效手动开启事务:start transaction

提交事务:commit

提交事务到开启事务之间的所有的sql语句都生效

回滚事务:rollback

从回滚事务到开启事务之间的所有的sql操作都无效


jdbc的API的事务控制

通过Connection对象可以控制事务

jdbc中想控制事务其实是控制jdbc的更新数据库的API方法---executeUpdate

开启事务:connection.setAutoCommit(false);

提交事务:connection.commit();

回滚事务:connection.rollback();

官方的介绍事务特性

事务的四大特性简称ACID(Atomicity Consistency Isolation Durability),分别是:

l 原子性:原子性对应的英文是Atomicity,即表示事务中所有操作是不可再分割的原子单位。事务中所有操作要么全部执行成功,要么全部执行失败;

l 一致性:一致性对应的英文是Consistency,事务执行后,数据库状态与其它业务规则保持一致。例如转账业务,无论事务执行成功与否,参与转账的两个账号余额之和应该是不变的;

l 隔离性:隔离性对应的英文是Isolation,是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰;

l 持久性:持久性对应的英文是Durability,指的是一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据。

不同的事务,其一致性的表现形式是不同的,事务的其他三大特性其实都是为了事务的一致性服务的。


事务的四大特性概括

原子性:数据库的操作的最小的单位就是事务

一致性:一个事务中的多个操作的结果数据是一致的,同时成功和同时失败

隔离性:多个事务之间的操作互不影响

持久性:当一个事务提交后,更新操作才持久化到磁盘上

事务的隔离级别与问题

不考虑隔离性产生的问题介绍


n 脏读 一个事务读取到了另一个事务未提交数据.

n 不可重复读 一个事务内,两次读取到的数据不一致.(update)

n 虚读(幻读) 两次读取的数据不一致(insert)

事务的四种隔离级别介绍


数据库内部定义了四种隔离级别,用于解决三种隔离问题

u 1 Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)

u 2 Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)不可以避免虚读

u 3 Read committed:可避免脏读情况发生(读已提交)

u 4 Read uncommitted:最低级别,以上情况均无法保证。(读未提交)

mysql数据库默认的事务隔离级别-----repeatable read级别.

oracle数据默认的事务隔离级别 ----read committed

设置事务隔离级别


n mysql中设置

数据库默认有事务的隔离级别,mysql 中查看与修改事务的隔离级别

u set session transaction isolation level 设置事务隔离级别

u select @@tx_isolation 查询当前事务隔离级别

n jdbc中设置事务隔离级别

在java.sql.Connection接口中提供

u setTransactionIsolation(int level) ;

参数可以取 Connection 常量之一:

Connection.TRANSACTION_READ_UNCOMMITTED、

Connection.TRANSACTION_READ_COMMITTED、

Connection.TRANSACTION_REPEATABLE_READ

Connection.TRANSACTION_SERIALIZABLE。

(注意,不能使用 Connection.TRANSACTION_NONE,因为它指定了不受支持的事务。)

 


事务的丢失更新问题(Lost Update)

两个或多个事务更新同一行,但这些事务彼此之间都不知道其他事务进行的修改,因此第二个更改覆盖了第一个修改。

如何解决事务的丢失更新问题?

解决事务的丢失更新可以采用两种方式



方式一:悲观锁


       悲观锁(假设丢失更新一定会发生)——利用数据库内部锁机制,管理事务提供的锁机制

      (1)共享锁

                select  * from table lock  in  share mode  (读锁、共享锁)

      (2)排它锁

                select  * from table for update  (写锁、排它锁)

 悲观锁详解:  

       MySQL锁机制分为表级锁(例如  事务隔离级别中的Serializable)和行级锁。

共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。

      排它锁又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他锁并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。

  其实共享锁就是多个事务只能读数据不能改数据。对于排它锁而言,当排它锁锁住一行数据后 并不是说其他事务不能读取和修改这行数据,排它锁指的是一个事务在一行数据上加上排它锁后,其他事务不能在其上面加任何其他的锁。MySQL引擎默认的修改数据语句(update、insert、delete)都会自动给涉及到的数据加上排它锁,select 语句默认不会加任何锁类型,如果加排它锁可以使用select ... for update语句,加共享锁可以使用select
... lock in share mode语句。所以加过排它锁的数据行在其他事务中是不能修改数据的,也不能通过 for  update 和lock in share mode锁的方式查询数据,但是可以直接通过select ...  from ... 查询数据,因为普通查询默认没有任何锁机制。

注意:事务控制必须在service层


连接池

连接池
:就是创建一个容器,用于装入多个Connection对象,在使用连接对象时,从容器中获取一个Connection, 使用完成后,在将这个Connection重新装入到容器中。这个容器就是
连接池
。(
DataSource
)也叫做
数据源
.

作用:我们可以通过连接池获取连接对象。

优点:
节省创建连接与释放连接造成的性能消耗 —- 连接池中连接起到复用的作用 ,提高程序性能

应用程序直接获取链接 



应用程序直接获取链接的缺点
用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。




数据库连接池编写原理分析

编写连接池需实现
javax.sql.DataSource接口
。DataSource接口中定义了两个重载的getConnection方法:
Connection getConnection()
Connection getConnection(String username, String password)

实现DataSource接口,并实现连接池功能的步骤:
在DataSource构造函数中批量创建与数据库的连接,并把创建的连接保存到一个集合对象中
实现getConnection方法,让getConnection方法每次调用时,从集合对象中取一个Connection返回给用户。
当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到连接池的集合对象中,而不要把conn还给数据库。

原来由jdbcUtil创建连接,现在由dataSource创建连接,为实现不和具体数据为绑定,因此datasource也应采用配置文件的方法获得连接。


自定义连接池

1.创建一个MyDataSource类,在这个类中创建一个
LinkedList<Connection>

2.在其构造方法中初始化List集合,并向其中装入5个Connection对象。
3.创建一个public Connection getConnection();从List集合中获取一个连接对象返回.

4.创建一个 public void readd(Connection) 这个方法是将使用完成后的Connection对象重新装入到List集合中.

代码问题:

1.连接池的创建是有标准的.
javax.sql
包下定义了一个接口 
DataSource

所有的连接池必须实现
javax.sql.DataSource接口


2.我们操作时,要使用标准,怎样可以让 con.close()它不是销毁,而是将其重新装入到连接池.
要解决这个问题,其本质就是将Connection中的close()方法的行为改变。
怎样可以改变一个方法的行为(对方法功能进行增强) 
1.继承
2.装饰模式 
1.装饰类与被装饰类要实现同一个接口或继承同一个父类
2.在装饰类中持有一个被装饰类引用
3.对方法进行功能增强。

3.动态代理 
可以对行为增强
Proxy.newProxyInstance(ClassLoacer ,Class[],InvocationHandler);

结论:Connection对象如果是从连接池中获取到的,那么它的close方法的行为已经改变了,不在是销毁,而是重新装入到连接池。

1.连接池必须实现javax.sql.DataSource接口。

2.要通过连接池获取连接对象 DataSource接口中有一个 getConnection方法.
3.将Connection重新装入到连接池 使用Connection的close()方法。


开源连接池

现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
也有一些开源组织提供了数据源的独立实现: 

DBCP 数据库连接池
C3P0 数据库连接池
Apache Tomcat内置的连接池(apache dbcp)

实际应用时不需要编写连接数据库代码,直接从数据源获得数据库的连接。程序员编程时也应尽量使用这些数据源的实现,以提升程序的数据库访问性能。


DBCP数据源(了解)

DBCP 是 Apache 软件基金组织下的开源连接池实现
使用DBCP数据源,应用程序应在系统中增加如下两个 jar 文件: 

Commons-dbcp-1.4.jar:连接池的实现
Commons-pool-1.5.6.jar:连接池实现的依赖库

Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。


C3P0数据源(必会)

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。

目前使用它的开源项目有Hibernate,Spring等。

c3p0与dbcp区别
dbcp没有自动回收空闲连接的功能
c3p0有自动回收空闲连接功能

1.导包
c3p0-0.9.1.2.jar

1.手动使用

ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql:///day18");
cpds.setUser("root");
cpds.setPassword("abc");
1
2
3
4
5
2.自动(使用配置文件) 

c3p0的配置文件可以是properties也可以是xml。
c3p0的配置文件如果名称叫做 
c3p0.properties
 or 
c3p0-config.xml
 并且放置在classpath路径下(对于web应用就是classes目录)那么c3p0会自动查找。

注意:我们其时只需要将配置文件放置在
src
下就可以。

使用: 

ComboPooledDataSource cpds = new ComboPooledDataSource();
它会在指定的目录下查找指定名称的配置文件,并将其中内容加载。

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///day18</property>
<property name="user">root</property>
<property name="password">abc</property>
</default-config>

</c3p0-config>

@Test
public void test2() throws PropertyVetoException, SQLException {
ComboPooledDataSource cpds = new ComboPooledDataSource();

// 得到一个Connection
Connection con = cpds.getConnection();

ResultSet rs = con.createStatement().executeQuery(
"select * from account");

while (rs.next()) {

System.out.println(rs.getInt("id") + "  " + rs.getString("name"));
}

rs.close();
con.close(); // 将Connection对象重新装入到连接池.

// String path = this.getClass().getResource("/").getPath();
// System.out.println(path);
}
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


配置Tomcat数据源

tomcat服务器内置连接池 (使用Apache DBCP)
配置tomcat 内置连接池,通过JNDI方式 去访问tomcat的内置连接池

JNDI技术简介


JNDI(Java Naming and Directory Interface)
,Java**命名和目录接口,它对应于**J2SE中的
javax.naming包
。是JavaEE一项技术,允许将一个Java对象绑定到一个JNDI容器(tomcat)中,并且为对象指定一个名称,通过javax.naming
包 Context 对JNDI 容器中绑定的对象进行查找,通过指定名称找到绑定Java对象。


这套API的主要作用在于:它可以把Java对象放在一个容器中(支持JNDI容器 Tomcat),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需通过名称检索即可。

其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。

配置操作步骤

1、配置使用tomcat 内置连接池 配置
<context>
 元素

context元素有三种常见配置位置 

1) tomcat/conf/context.xml 所有虚拟主机,所有工程都可以访问该连接池
2) tomcat/conf/Catalina/localhost/context.xml 当前虚拟主机(localhost)下所有工程都可以使用该连接池
3) 当前工程/META-INF/context.xml 只有当前工程可以访问该连接池

<Context>
<Resource name="jdbc/EmployeeDB" auth="Container"
type="javax.sql.DataSource" username="root" password="abc"
driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql:///day14"
maxActive="8" maxIdle="4"/>
</Context>
1
2
3
4
5
6
必须先将mysql驱动jar包 复制tomcat/lib下

在tomcat启动服务器时,创建连接池对象,绑定 jdbc/EmployeeDB 指定名称上

2、通过运行在JNDI容器内部的程序(Servlet/JSP)去访问tomcat内置连接池

public class DataSourceServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

try {
Context context = new InitialContext();
Context envCtx = (Context) context.lookup("java:comp/env"); // 固定路径
DataSource datasource = (DataSource) envCtx
.lookup("jdbc/EmployeeDB");//自己定义的数据库名字

Connection con = datasource.getConnection();
ResultSet rs = con.createStatement().executeQuery(
"select * from account");

while (rs.next()) {
System.out.println(rs.getInt("id") + "  "
+ rs.getString("name"));
}

} catch (Exception e) {
e.printStackTrace();
}

}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: