您的位置:首页 > 其它

事务、连接池、开源数据源(DBCP、C3P0)

2014-03-13 23:19 316 查看
事务的概念

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

例如:A——B转帐,对应于如下两条sql语句

update account set money=money-100 where name=‘a’;

update account set money=money+100 where name=‘b’;

数据库开启事务命令DTL

start transaction 开启事务

Rollback 回滚事务

Commit 提交事务

一、事务

a、数据库中的DTL:Database Transaction Language

开启事务:start transaction

提交事务:commit

回滚事务:rollback

数据库:MySQL,一般情况下,事务是自动开启和关闭的。

手工控制事务:使用DTL

b、JDBC中控制事务的执行

开启事务:start transaction Connection.setAutocommit(false)

提交事务:commit Connection.commit()

回滚事务:rollback Connection.rollback()

c、事务的特性:(理论)

A原子性:比喻处于同一个事务中的多条语句是不可分割的单位

C一致性:比如:A1000 B1000 执行事务前:A+B=2000 执行事务后A+B=2000

I隔离性:事务执行过程中不应该被其他线程的事务所干扰。

D持久性:事务结束后,操作的数据应该被永久性的保存起来(磁盘上)

********************************************************************************************

专题:隔离性(很重要)

前提:多线程并发操作数据库中的数据

不考虑事务隔离性,会出现以下问题:

1、脏读:A线程读取到了B线程未提交的数据

2、不可重复读:A线程读到了B线程提交的更新(update)后的数据

3、虚读:A线程读到了B线程提交的插入(insert)后的数据

a、MySQL控制事务的隔离级别

READ UNCOMMITTED:脏读、不可重复读、虚读都有可能发生

READ COMMITTED:防止脏读的发生,不可重复读、虚读都有可能发生(Oracle默认的级别)

REPEATABLE READ:防止脏读、不可重复读,虚读有可能发生(MySQL默认的级别)

SERIALIZABLE:防止脏读、不可重复读、虚读

MySQL:

查看当前事务的隔离级别:select @@tx_isolation;

更改当前线程事务的隔离级别:set transaction isolation level 四个级别之一;

b、JDBC控制事务的隔离级别

Connection:TRANSACTION_READ_UNCOMMITTED

Connection:TRANSACTION_READ_COMMITTED

Connection:TRANSACTION_REPEATABLE_READ

Connection:TRANSACTION_SERIALIZABLE

Connection.setTransactionIsolation(int level):开启事务之前调用。

conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);

//开启事务

conn.setAutoCommit(false);

二、数据库连接池

1、标准的连接池要实现javax.sql.DataSource接口

2、编程核心:

当用户调用Connection.close()(实际上是调用的数据库驱动中的对应实现方法),把当前用到的连接换回原来的连接池中,已达到复用的目的。

不能去该数据库的驱动的实现。改写已知类的某个或某些方法的原有行为。

解决方案:

a、继承(行不通):MyConnection extends ?(不能与具体数据库有关) com.mysql.jdbc.Connection

b、包装(装饰):

1、编写一个类,实现与被包装类相同的接口 public class MyConnection implements Connection{}

2、定义一个成员变量,引用被包装的对象的实例 private Connection conn;

3、定义构造方法,传入被包装的对象的实例 public MyConnection(Connection conn){this.conn = conn;}

4、对于不需要改变的方法,调用原有对象的对应方法 public void commit(){conn.commit();}

5、对于需要改变的方法,你该就完了。 public void close(){//把连接放回池中}

变化:

1、编写一个类,继承已经实现了被包装对象接口的类

2、定义一个成员变量,引用被包装的对象的实例

3、定义构造方法,传入被包装的对象的实例

4、对于需要改变的方法,覆盖就完了

c、动态代理:

实现拦截功能:方法执行前,方法执行后,方法遇到异常,方法中的finally,从而实现AOP(Aspect-Oriented Programming面向切面)编程

1、基于接口的动态代理:借助JDK中的reflect.Proxy

2、基于子类的动态代理:借助第三方的开源项目。CGLIB

使用前提:被代理对象的类

a、必须是public的,有默认的构造方法

b、不能是final的

三、开源数据源的使用

1、DBCP(常用)

Commons-dbcp.jar:连接池的实现

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

dbcpconfig.properties配置文件如下:

#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day16
username=root
password=sorry

#<!-- 初始化连接 -->
initialSize=10

#最大连接数量
maxActive=50

#<!-- 最大空闲连接 -->
maxIdle=20

#<!-- 最小空闲连接 -->
minIdle=5

#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000

#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=utf8

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=REPEATABLE_READ
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

public class DBCPUtil {
private static DataSource dataSource;
static{
try {
InputStream inStream = DBCPUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties props = new Properties();
props.load(inStream);
dataSource = BasicDataSourceFactory.createDataSource(props);
} catch (Exception e) {
throw new ExceptionInInitializerError("初始化数据库配置文件失败");
}
}
public static Connection getConnection() throws SQLException{
return dataSource.getConnection();
}
}


2、C3P0

发行包\lib\全考

c3p0-config.xml配置文件:文件名是固定的写法

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///day16</property>
<property name="user">root</property>
<property name="password">sorry</property>
<property name="initialPoolSize">15</property>
<property name="maxPoolSize">20</property>
<property name="minPoolSize">5</property>
<property name="maxStatements">200</property>
</default-config>
<!--
<named-config name="intergalactoApp">
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</named-config>
-->
</c3p0-config>


import java.sql.Connection;
import java.sql.SQLException;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Util {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();

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


3、用服务器提供的数据源的配置(具体参考服务器的文档)

以Tomcat为例(用的就是DBCP)

a、拷贝数据库驱动jar到Tomcat\lib目录下

b、在你应用的META-INF目录下建立一个context.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>

<Context>

<!-- 配置一个资源。name=大名字/小名字

auth:Container,说明该资源由容器创建

-->

<Resource name="jdbc/day16" auth="Container" type="javax.sql.DataSource"

maxActive="100" maxIdle="30" maxWait="10000" username="root"

password="sorry" driverClassName="com.mysql.jdbc.Driver"

url="jdbc:mysql://localhost:3306/day16" />

</Context>

c、重新启动Tomcat,服务器就按照配置创建好了数据源,在JNDI容器中。

JNDI:JavaEE技术之一。Java Naming and Directory Interface

(就是一个Map<String,Object>,Map的key是一个String,是一个目录, value就是绑定的对象。 类似Window系统的注册表)

API存在JDK的javax.naming.*包中。

d、在Tomcat管理的web应用中才能使用,不能在main方法中运行,不是同一个虚拟机。

Context initContext = new InitialContext();

DataSource ds = (DataSource)initContext.lookup("java:/comp/env/jdbc/day16");

Connection conn = ds.getConnection();

import java.sql.Connection;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

public class JndiDsTest {
//和Tomcat不是一个虚拟机
public static void main(String[] args) throws Exception {
Context initContext = new InitialContext();
DataSource ds = (DataSource)initContext.lookup("java:/comp/env/jdbc/day16");
Connection conn = ds.getConnection();
System.out.println(conn);
}

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