标签:
文档版本 | 开发工具 | 测试平台 | 工程名字 | 日期 | 作者 | 备注 |
---|---|---|---|---|---|---|
V1.0 | 2016.05.13 | lutianfei | none |
在开发中,有事务的存在,可以保证数据完整性。
例如:A——B转帐,对应于如下两条sql语句
Start transaction
...
commit
方式1:
方式2:
测试:将autocommit的值设置为off
java.sql.Connection
接口中有几个方法是用于可以操作事务 create table account(
id int primary key auto_increment,
name varchar(20),
money double
);
insert into account values(null,‘aaa‘,1000);
insert into account values(null,‘bbb‘,1000);
insert into account values(null,‘ccc‘,1000);
原子性
是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 隔离性
是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。持久性
是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响1.脏读 一个事务读取到另一个事务的未提交数据。
2.不可重复读
3.虚读(幻读)
4.丢失更新
数据库共定义了四种隔离级别:
Serializable
:可避免脏读、不可重复读、虚读情况的发生。(串行化)Repeatable read
:可避免脏读、不可重复读情况的发生。不可以避免虚读Read committed
:可避免脏读情况发生(读已提交)Read uncommitted
:最低级别,以上情况均无法保证。(读未提交)安全性:serializable > repeatable read > read committed > read uncommitted
性能 :serializable < repeatable read < read committed < read uncommitted
结论: 实际开发中,通常不会选择 serializable 和 read uncommitted ,
怎样设置事务的隔离级别?
1.mysql中设置
select @@tx_isolation
查询当前事务隔离级别set session transaction isolation level
设置事务隔离级别2.jdbc中设置
java.sql.Connection
接口中提供的方法 setTransactionIsolation
(int level) throws SQLExceptionTRANSACTION_READ_UNCOMMITTED
,指示可以发生脏读、不可重复读、虚读。TRANSACTION_READ_COMMITTED
:指示不可以发生脏读,但是不可重复读、虚读可以发生。TRANSACTION_REPEATABLE_READ
:指示不可以发生脏读、不可重复读,但是虚读可以发生。TRANSACTION_SERIALIZABLE
:指示不可以发生脏读、不可重复读和虚读的常量。TRANSACTION_NONE
,一般不使用,因为它指定了不受支持的事务。) 1.脏读
2.解决脏读问题
3.解决不可重复读
4.设置事务隔离级别 Serializable ,它可以解决所有问题
问题:service调用了dao中两个方法完成了一个业务操作,如果其中一个方法执行失败怎样办?
问题:怎样进行事务控制?
问题:进行事务操作需要使用Connection对象,怎样保证,在service中与dao中所使用的是同一个Connection.
注意:Connecton对象使用完成后,在service层的finally中关闭
关于程序问题
问题:
public interface AccountDao {
public void accountOut(String accountOut, double money) throws Exception;
public void accountIn(String accountIn, double money) throws Exception;
}
ThreadLocal
可以理解成是一个Map集合,Map<Thread,Object>
get方法是从ThreadLocal中获取数据,它是根据当前线程对象来获取值。
如果我们是在同一个线程中,只要在任意的一个位置存储了数据,在其它位置上,就可以获取到这个数据。
ThreadLocal
private static final ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
getConnection()
方法中操作 if (con == null) {
// 2.获取连接
con = DriverManager.getConnection(URL, USERNAME, PASSWORD);
tl.set(con); //将con装入到ThreadLocal中。
}
1.悲观锁
悲观锁
: 假设丢失更新一定会发生 ,利用数据库内部锁机制,管理事务提供的锁机制
2.乐观锁( Optimistic Locking )
乐观锁
假设丢失更新不会发生 ,采用程序中添加版本字段解决丢失更新问题 create table product (
id int,
name varchar(20),
updatetime timestamp
);
insert into product values(1,‘冰箱‘,null);
update product set name=‘洗衣机‘ where id = 1;
连接池
:就是创建一个容器,用于装入多个Connection对象,在使用连接对象时,从容器中获取一个Connection, 使用完成后,在将这个Connection重新装入到容器中。这个容器就是连接池
。(DataSource
)也叫做数据源
.作用:我们可以通过连接池获取连接对象。
优点:
应用程序直接获取链接
应用程序直接获取链接的缺点
编写连接池需实现javax.sql.DataSource接口
。DataSource接口中定义了两个重载的getConnection方法:
实现DataSource接口,并实现连接池功能的步骤:
原来由jdbcUtil创建连接,现在由dataSource创建连接,为实现不和具体数据为绑定,因此datasource也应采用配置文件的方法获得连接。
LinkedList<Connection>
4.创建一个 public void readd(Connection) 这个方法是将使用完成后的Connection对象重新装入到List集合中.
代码问题:
1.连接池的创建是有标准的.
javax.sql
包下定义了一个接口 DataSource
javax.sql.DataSource接口
,2.我们操作时,要使用标准,怎样可以让 con.close()它不是销毁,而是将其重新装入到连接池.
结论:Connection对象如果是从连接池中获取到的,那么它的close方法的行为已经改变了,不在是销毁,而是重新装入到连接池。
1.连接池必须实现javax.sql.DataSource接口。
动态代理增强close示例
proxyConn = (Connection) Proxy.newProxyInstance(this.getClass()
.getClassLoader(), conn.getClass().getInterfaces(),
new InvocationHandler() {
//此处为内部类,当close方法被调用时将conn还回池中,其它方法直接执行
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if (method.getName().equals("close")) {
pool.addLast(conn);
return null;
}
return method.invoke(conn, args);
}
});
//使用装饰进行增强
public class Demo2 {
public static void main(String[] args) {
Car car=new Bmw();
//给车增强
CarDerector cd=new CarDerector(car);
cd.run();
}
}
interface Car {
void run();
}
class Bmw implements Car {
public void run() {
System.out.println("bmw run....");
}
}
class Benz implements Car {
public void run() {
System.out.println("benz run....");
}
}
// 使用装饰来完成
class CarDerector implements Car {
private Car car;
public CarDerector(Car car) {
this.car = car;
}
public void run() {
System.out.println("添加导航");
car.run();
}
}
// 1.手动配置
@Test
public void test1() throws SQLException {
BasicDataSource bds = new BasicDataSource();
// 需要设置连接数据库最基本四个条件
bds.setDriverClassName("com.mysql.jdbc.Driver");
bds.setUrl("jdbc:mysql:///day18");
bds.setUsername("root");
bds.setPassword("abc");
// 得到一个Connection
Connection con = bds.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对象重新装入到连接池.
}
// 2.自动配置
@Test
public void test2() throws Exception {
Properties props = new Properties();
// props.setProperty("driverClassName", "com.mysql.jdbc.Driver");
// props.setProperty("url", "jdbc:mysql:///day18");
// props.setProperty("username", "root");
// props.setProperty("password", "abc");
FileInputStream fis = new FileInputStream(
"D:\\java1110\\workspace\\day18_2\\src\\dbcp.properties");
props.load(fis);
DataSource ds = BasicDataSourceFactory.createDataSource(props);
// 得到一个Connection
Connection con = ds.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对象重新装入到连接池.
}
目前使用它的开源项目有Hibernate,Spring等。
c3p0与dbcp区别
1.导包
1.手动使用
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql:///day18");
cpds.setUser("root");
cpds.setPassword("abc");
c3p0.properties
or c3p0-config.xml
并且放置在classpath路径下(对于web应用就是classes目录)那么c3p0会自动查找。src
下就可以。<?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);
}
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对象,只需通过名称检索即可。
<context>
元素 <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>
在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);
}
}
标签:
原文地址:http://blog.csdn.net/lutianfeiml/article/details/51424084