面试过程中,主考官问道:谈谈你对事物的理解,感觉当时回答的好没有连贯性。事后总结如下:
谈谈我对事物的理解,从如下几个方面进行剖析:
一、事物的特性
二、对事物产生的原因
三、如何解决事物
四、延伸
一、事物的特性
对于一个事物必须遵循ISO/IEC所定制的ACID原则。
A:原子性(atomicity),一个不可切割的整体,不能在分,例如开关要么开,要么关,中间没有操作;军人,要么战死,要么凯旋,绝不会投降。
C:一致性(consistency),所操作的东西必须一致,多个因素产生的因素必须守恒(如何能量守恒一样),例如你在银行转钱给别人了,别人就一定能收到钱。
I:隔离性(isolation),隔离性在多个事物进行同时操作的时候就会产生问题。
D:持久性(durability),一定事物完成,就会不会恢复,例如数据一旦commit,就写入到磁盘中,造成不可逆的情况。
遵循这样四种特性的的才能称之为一个事物。
二、事物会产生的问题
事物产生的原因是有事物的隔离这个特性产生的,多事物同时并发执行对同一个数据库的记录进行操作的时候,会产生如下问题:
a. 数据丢失
很好理解:第一次提交(跟新)的数据,被第二次提交的数据(跟新)覆盖了,导致数据丢失。
b. 不可以重复读
在读数据的时候,我读第一次跟新的数据没问题,我还是同样的操作,读数据的时候确发现不一样,中间被第二个人跟新了数据导致把第一次提交的数据给覆盖掉了。
c. 虚读
在读数据的时候,我读第一次提交的数据没问题,我还是同样的操作,读数据的时候确发现不一样,中间被第二个人提交了数据导致把第一次提交的数据给覆盖掉了。
d. 脏读
读取数据的时候,读取了之前还没有提交的数据。
三、如何解决事物产生的问题
数据库中有如下几个事物级别可以解决事物产生的这四个问题:
1. SERIALIZABLE
完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的。
2. REPEATABLE_READ
对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。
3. READ_COMMITTED
允许在并发事务已经提交后读取。可防止脏读,但幻读和不可重复读仍可发生。
4. READ_UNCOMMITED
允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读
四种事物级别由上往下依次降低,但是性能确越来越好。
Mysq的默认事物级别低,但是性能好,默认的是READ_COMMITTED.(MYSQL的数据引擎偶多种,如ISAM,MYISAM,HEAP,INNODB,BERKLEY.,myisam是不支持事物的,INNODB支持事物)
Oracle的默认级别是REPEATABLE_READ.
延伸一、数据库底层实现机制是如何解决的呢?
数据库底层可以加锁进行控制,如悲观锁,乐观锁。
悲观锁:使用数据库底层的机制,对需要更新的数据进行锁定,防止并发问题,实现方式加入for update,例如:
update studentsset id = 10 where id = 5 for update;
这样当前事物就会锁定改行,其他事物是不能进行操作改行的的。
乐观锁:通过版本控制来实现,解决事物更新丢失的问题。例如hibernate中的解决方案是:
<!-- 使用此属性解决丢失更新问题-->
<versionname="myVersion"/>
此时在成员变量中定义一个:privateInteger myVersion;//加入一版本属性,解决丢失更新问题
一旦数据跟新:此时myVersion自动自增。若另一个事务操作此myVersion不一样的数据记录则会抛出异常。
Mysql默认使用的是排他锁:一个事物获取了一个数据行的排他锁,其他事物就不能在获取该行的其他所(排他锁或者共享锁),即一个事物在读取一个数据行的时候,其他事物不能对该数据进行crud操作。设置排他锁的语句:select … for update
排他锁:一个事物获取了一个数据行的共享锁,其他事物能获取该行对应的共享锁,但不能获取排他锁,即一个事物在读取一个数据行的时候,其他事物也可以读,但是不能对该数据进行cud操作。设置共享锁的语句:select … lock in share mode.
延伸二、框架Spring对事物的解决方案
1. Spring对事物的支持
Spring事务管理高层抽象主要包括3个接口:
a.PlatformTransactionManager事务管理器
b.TransactionDefinition事务定义信息(隔离、传播、超时、只读)
c.TransactionStatus 事务具体运行状态
其中Spring对PlatformTransactionManager事物管理的接口实现有
Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现
a.org.springframework.jdbc.datasource.DataSourceTransactionManager
使用Spring JDBC或MyBatis 进行持久化数据时使用
b.org.springframework.orm.hibernate3.HibernateTransactionManager
使用Hibernate3.0版本进行持久化数据时使用
Spring提供事务传播行为类型说明:
PROPAGATION_REQUIRED 支持当前事务,如果不存在 就新建一个
PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常
PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行
2. Spring对事物的配置方式
Spring 支持两种方式事务管理
1.编程式的事务管理
在实际应用中很少使用
2.通过TransactionTemplate手动管理事务
声明式事务管理,开发中推荐使用(代码侵入性最小,Spring的声明式事务是通过AOP实现的)
编程事务管理:
步骤1:
配置DataSourceTransactionManager
<!-- 配置DataSourceTransactionManager-->
<bean id="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"ref="dataSource" />
</bean>
<!-- 配置TransactionTemplate-->
<bean id="transactionTemplate"class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager"ref="txManager" />
</bean>
<!-- service层中的类 使用TransactionTemplate-->
<bean id="userService"class="com.feng.service.UserService">
<property name="transactionTemplate"ref="transactionTemplate" />
<propertyname="userDao" ref="userDao" />
</bean>
注:在userService中必须含有transactionTemplate这个类。在可以在:
transactionTemplate.execute(newTransactionCallbackWithoutResult() {
@Override
protected voiddoInTransactionWithoutResult(TransactionStatus status) {
...在里面完成相应的数据逻辑单元
}
});
则该execute方法中有控制事务的功能。
在xml中申明事事务(常用):
<!-- 配置DataSourceTransactionManager只是针对JDBC或者myBatis -->
<bean id="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<propertyname="dataSource" ref="dataSource" />
</bean>
<!--事物通知 -->
<tx:advice id="myAdvice"transaction-manager="txManager">
<tx:attributes>
<!-- 配置哪些方法需要事物通知-->
<tx:methodname="save*" propagation="REQUIRED"/>
<tx:methodname="find*" read-only="true"/>
<!-- 除了上面的方法-->
<tx:methodname="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- AOP方式 进行代理 -->
<aop:config>
<aop:pointcutexpression="execution(* com.feng.service.*Service.*(..))"id="myPointcut"/>
<aop:advisoradvice-ref="myAdvice" pointcut-ref="myPointcut"/>
</aop:config>
也可以使用注解:
beans.xml中配置如下:
<!-- 配置DataSourceTransactionManager只是针对JDBC或者myBatis -->
<bean id="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<propertyname="dataSource" ref="dataSource" />
</bean>
<!-- 事务的注解驱动,支持事务注解 -->
<tx:annotation-driventransaction-manager="txManager"/>
需要事物控制的类:
packagecom.feng.service;
importjava.sql.SQLException;
importjavax.annotation.Resource;
importorg.springframework.stereotype.Service;
importorg.springframework.transaction.annotation.Transactional;
importcom.feng.dao.UserDao;
importcom.feng.domian.User;
@Service
@Transactional
publicclass UserService {
@Resource
private UserDao userDao;
public void test() {
User user = newUser("555", "333");
try {
userDao.save(user);
user.setId(1);
userDao.update(user);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Transactional(readOnly=true)
public void find() {
}
}
以上几种配置方式都可以达到对事物进行控制。