网站建设调研提纲,网站设计的简称,网站建设公司海外,北京公司摇号需要哪些资格条件1、事务基础概念_四大特性 数据库中事务的四大特性#xff08;ACID#xff09;#xff0c;如果一个数据库声称支持事务的操作#xff0c;那么该数据库必须要具备以下四个特性#xff1a; ⑴ 原子性#xff08;Atomicity#xff09; 原子性#xff0c;是指事务包含的所有…1、事务基础概念_四大特性 数据库中事务的四大特性ACID如果一个数据库声称支持事务的操作那么该数据库必须要具备以下四个特性 ⑴ 原子性Atomicity 原子性是指事务包含的所有操作要么全部成功要么全部失败回滚。因此事务的操作如果成功就必须要完全持久化到数据库如果操作失败则不能对数据库有任何影响。 ⑵ 一致性Consistency 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态也就是说一个事务执行之前和执行之后都必须处于一致性状态。 举例假设用户甲和用户乙两者的钱加起来一共是10000那么不管甲和乙之间如何转账转几次账事务结束后两个用户的钱相加起来应该还是10000这即是事务的一致性。 ⑶ 隔离性Isolation 隔离性是当多个用户并发访问数据库时比如操作同一张表时数据库为每一个用户开启的事务不能被其他事务的操作所干扰多个并发事务之间要相互隔离。 即要达到这么一种效果对于任意两个并发的事务T1和T2在事务T1看来T2要么在T1开始之前就已经结束要么在T1结束之后才开始这样每个事务都感觉不到有其他事务在并发地执行。 在介绍数据库提供的各种隔离级别之前我们先看看如果不考虑事务的隔离性会发生的几种问题 第一、脏读 脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。 当一个事务正在多次修改某个数据而在这个事务中这多次的修改都还未提交这时一个并发的事务来访问该数据就会造成两个事务得到的数据不一致。例如用户A向用户B转账100元对应SQL命令如下 update account set moneymoney100 where name’B’; (此时A通知B)update account set moneymoney - 100 where name’A’; 当只执行第一条SQL时A通知B查看账户B发现确实钱已到账此时即发生了脏读而之后无论第二条SQL是否执行只要该事务不提交则所有操作都将回滚那么当B以后再次查看账户时就会发现钱其实并没有转。 第二、不可重复读 不可重复读是指在对于数据库中的某个数据一个事务范围内多次查询却返回了不同的数据值这是由于在查询间隔被另一个事务修改并提交了。 例如事务T1在读取某一数据而事务T2立马修改了这个数据并且提交事务给数据库事务T1再次读取该数据就得到了不同的结果发送了不可重复读。 不可重复读和脏读的区别是脏读是某一事务读取了另一个事务未提交的脏数据而不可重复读则是读取了前一事务提交的数据。 在某些情况下不可重复读并不是问题比如我们多次查询某个数据当然以最后查询得到的结果为主。但在另一些情况下就有可能发生问题例如对于同一个数据A和B依次查询就可能不同A和B就可能打起来了…… 第三、虚读(幻读) 幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作这时事务T2又对这个表中插入了一行数据项而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据会发现还有一行没有修改其实这行是从事务T2中添加的就好像产生幻觉一样这就是发生了幻读。 幻读和不可重复读都是读取了另一条已经提交的事务这点就脏读不同所不同的是不可重复读查询的都是同一个数据项而幻读针对的是一批数据整体比如数据的个数 关于事务的隔离性数据库提供了多种隔离级别mySQL数据库为我们提供的四种隔离级别 ① Serializable (串行化)可避免脏读、不可重复读、幻读的发生。 ② Repeatable read (可重复读)可避免脏读、不可重复读的发生。 ③ Read committed (读已提交)可避免脏读的发生。 ④ Read uncommitted (读未提交)最低级别任何情况都无法保证。 以上四种隔离级别最高的是Serializable级别最低的是Read uncommitted级别当然级别越高执行效率就越低。像Serializable这样的级别就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待所以平时选用何种隔离级别应该根据实际情况。在MySQL数据库中默认的隔离级别为Repeatable read (可重复读)。 在MySQL数据库中支持上面四种隔离级别默认的为Repeatable read (可重复读)而在Oracle数据库中只支持Serializable (串行化)级别和Read committed (读已提交)这两种级别其中默认的为Read committed级别。
查看mysql数据库事务的默认隔离级别
SELECT tx_isolation; ⑷ 持久性Durability 持久性是指一个事务一旦被提交了那么对数据库中的数据的改变就是永久性的即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。 例如我们在使用JDBC操作数据库时在提交事务方法后提示用户事务操作完成当我们程序执行完成直到看到提示后就可以认定事务以及正确提交即使这时候数据库出现了问题也必须要将我们的事务完全执行完成否则就会造成我们看到提示事务处理完毕但是数据库因为故障而没有执行事务的重大错误。 2、spring-data-jpa事务管理
1默认事务 Spring Data 提供了默认的事务处理方式即所有的查询均声明为只读事务。确保了单个请求过程数据的一致性。 对于自定义的方法如需改变 SpringData 提供的事务默认方式可以在方法上注解Transactional声明进行多个 Repository操作时也应该使它们在同一个事务中处理按照分层架构的思想这部分属于业务逻辑层因此需要在Service 层实现对多个 Repository的调用并在相应的方法上声明事务。 Repository概念按照最初提出者的介绍它是衔接数据映射层和域之间的一个纽带作用相当于一个在内存中的域对象集合。客户端对象把查询的一些实体进行组合并把它们提交给Repository。对象能够从Repository中移除或者添加就好比这些对象在一个Collection对象上就行数据操作同时映射层的代码会对应的从数据库中取出相应的数据。 从概念上讲Repository是把一个数据存储区的数据给封装成对象的集合并提供了对这些集合的操作。 广义上可以理解为我们常说的DAO 2dao层代码
Modifying
Query(valueUPDATE hr_employee_contract t SET t.deleteStatus1 WHERE t.id?1,nativeQuery true)
void delete(Long id);
说明Modifying注解
①在Query注解中编写JPQL实现DELETE和UPDATE操作的时候必须加上modifying注解以通知Spring Data 这是一个DELETE或UPDATE操作。
②UPDATE或者DELETE操作需要使用事务此时需要定义Service层在Service层的方法上添加事务操作。
③注意JPQL不支持INSERT操作。 3service层代码
使用Transactional手动开启事务管理
Transactional
Override
public void delete(Long id) {employeeContractDao.delete(id);
}
Transactional注解支持9个属性的设置其中使用较多的三个属性readOnly、propagation、isolation。其中propagation属性用来枚举事务的传播行为isolation用来设置事务隔离级别readOnly进行读写事务控制。 ① readOnly
从这一点设置的时间点开始时间点a到这个事务结束的过程中其他事务所提交的数据该事务将看不见查询中不会出现别人在时间点a之后提交的数据
应用场合
NO.1、如果你一次执行单条查询语句则没有必要启用事务支持数据库默认支持SQL执行期间的读一致性 NO.2、如果你一次执行多条查询语句例如统计查询报表查询在这种场景下多条查询SQL必须保证整体的读一致性否则在前条SQL查询之后后条SQL查询之前数据被其他用户改变则该次整体的统计查询将会出现读数据不一致的状态此时应该启用事务支持。 【注意是一次执行多次查询来统计某些信息这时为了保证数据整体的一致性要用只读事务】
怎样设置
对于只读查询可以指定事务类型为readonly即只读事务。 由于只读事务不存在数据的修改因此数据库将会为只读事务提供一些优化手段例如Oracle对于只读事务不启动回滚段不记录回滚log。
1在JDBC中指定只读事务的办法为 connection.setReadOnly(true);
2在Hibernate中指定只读事务的办法为 session.setFlushMode(FlushMode.NEVER); 此时Hibernate也会为只读事务提供Session方面的一些优化手段
3在Spring的Hibernate封装中指定只读事务的办法为 bean配置文件中prop属性增加“readOnly” 或者用注解方式Transactional(readOnlytrue) 【 if the transaction is marked as read-only, Spring will set the Hibernate Session’s flush mode to FLUSH_NEVER, and will set the JDBC transaction to read-only】也就是说在Spring中设置只读事务是利用上面两种方式 ② propagation
//支持当前事务如果当前没有事务就新建一个事务。Spring默认事务级别。
int PROPAGATION_REQUIRED 0; //支持当前事务如果当前没有事务就以非事务方式执行。
int PROPAGATION_SUPPORTS 1; //支持当前事务如果当前没有事务就抛出异常。
int PROPAGATION_MANDATORY 2; //新建事务如果当前存在事务把当前事务挂起。执行新事务后再激活当前事务。
int PROPAGATION_REQUIRES_NEW 3; //以非事务方式执行操作如果当前存在事务就把当前事务挂起。
int PROPAGATION_NOT_SUPPORTED 4; //以非事务方式执行如果当前存在事务则抛出异常。
int PROPAGATION_NEVER 5;//如果当前存在事务则在嵌套事务内执行。如果当前没有事务则进行与PROPAGATION_REQUIRED类似的操作。//嵌套时由外部事务决定子事务是否是commit还是rollback。//一般在外部事务是使用try{}catch(嵌套事务方法){}进行编码。
int PROPAGATION_NESTED 6;
案例分析1
Service
class A{AutowiredB b;Transactional(propagation Propagation.REQUIRED) void call(){try{b.call();} catch(Exception e){//doSomething....//不抛异常则A无法提交}//doSomething....}
}Service
class B{Transactional(propagation Propagation.REQUIRED)void call(){}
}
A和B共用事务如果B异常。A未使用try..catch..捕获则AB一起回滚。
如果B异常。A捕获但并未抛出。则A最终也无法提交因为B的事务已经被设置为rollback-only了。
案例分析2
Service
class A{AutowiredB b;Transactional(propagation Propagation.REQUIRED) void call(){try{b.call();} catch(Exception e){throw e; //或者不抛}//doSomething....}
}Service
class B{Transactional(propagation Propagation.REQUIRES_NEW)void call(){}
}
执行b.call()时A事务挂起此时如果B执行异常。被A捕获如果抛出异常则AB回滚如果A捕获未抛异常则A继续执行不回滚。
执行b.call()时A事务挂起此时如果B正常执行而在A中出现异常。则B不回滚A回滚。
案例分析3
Service
class A{AutowiredB b;Transactional(propagation Propagation.REQUIRED) void call(){try{b.call();} catch(Exception e){throw e; //或者不抛}//doSomething....}
}Service
class B{Transactional(propagation Propagation.NESTED)void call(){}
}
执行b.call()时A事务挂起B新起事务并设置SavePoint。如果B正常执行A出现异常则AB一起回滚。
如果B失败异常此时A如果捕获但未抛出后续A正常执行的话A可以提交而B已经回滚。
如果B失败异常此时A如果捕获且抛出则AB一起回滚。
以上案例我们可以得出第1种和第3种模式的区别第3种在嵌套模式下可以在内部异常下执行其它业务且外部正常提交而第1种不可以这么操作。 ③ isolation OverrideTransactional(isolation Isolation.READ_COMMITTED)public void test() {User user userMapper.getOne(1L);System.out.println(user.getName());userMapper.updateUser();try {Thread.sleep(10000); // 10 s} catch (InterruptedException e) {e.printStackTrace();}System.out.println(end);}
Transactional 默认值 3、spring事务详解 Spring 为事务管理提供了丰富的功能支持。 Spring 事务管理分为编码式和声明式的两种方式。编程式事务指的是通过编码方式实现事务声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。 声明式事务有两种方式一种是在配置文件xml中做相关的事务规则声明另一种是基于Transactional 注解的方式。注释配置是目前流行的使用方式此处重介绍基于Transactional 注解的事务管理。
1Transactional 注解管理事务的实现步骤
使用Transactional 注解管理事务的实现步骤分为两步。
第一步配置
在 xml 配置类或配置文件中添加如的事务配置信息。除了用配置文件的方式EnableTransactionManagement 注解也可以启用事务管理功能。这里以简单的 DataSourceTransactionManager 为例。
1、配置类 package com.myfutech.market.service.provider.config;import com.myfutech.common.spring.jpa.base.impl.BaseJpaRepositoryImpl;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;import javax.sql.DataSource;
import java.util.Properties;Configuration
ConditionalOnClass(HikariDataSource.class)
EnableConfigurationProperties(ConnectionPoolProperties.class)
EnableJpaRepositories(entityManagerFactoryRef marketServiceEntityManagerFactory,transactionManagerRef marketServiceTransactionManager, basePackagescom.myfutech.market.service.provider.dao,repositoryBaseClass BaseJpaRepositoryImpl.class)
public class JpaConfig {Value(${marketService.url})private String url;Value(${marketService.username})private String username;Value(${marketService.password})private String password;Autowiredprivate ConnectionPoolProperties properties;Bean(marketServiceTransactionManager)PlatformTransactionManager marketServiceTransactionManager() {return new JpaTransactionManager(marketServiceEntityManagerFactory().getObject());}Bean(marketServiceEntityManagerFactory)LocalContainerEntityManagerFactoryBean marketServiceEntityManagerFactory() {HibernateJpaVendorAdapter vendorAdapter new HibernateJpaVendorAdapter();vendorAdapter.setGenerateDdl(true);LocalContainerEntityManagerFactoryBean factoryBean new LocalContainerEntityManagerFactoryBean();Properties properties new Properties();properties.setProperty(hibernate.dialect, org.hibernate.dialect.MySQLDialect);properties.setProperty(hibernate.jdbc.batch_size, 20);properties.setProperty(current_session_context_class, jpa);properties.setProperty(hibernate.ejb.entitymanager_factory_name, marketServiceEntityManagerFactory);properties.setProperty(hibernate.hbm2ddl.auto, none);factoryBean.setJpaProperties(properties);factoryBean.setDataSource(marketServiceDataSource());factoryBean.setJpaVendorAdapter(vendorAdapter);factoryBean.setPackagesToScan(com.myfutech.market.service.provider.model);return factoryBean;}BeanJdbcTemplate initJdbcTemplate(){return new JdbcTemplate(marketServiceDataSource());}Bean(marketServiceDataSource)DataSource marketServiceDataSource() {HikariDataSource dataSource new HikariDataSource();dataSource.setUsername(username);dataSource.setPassword(password);dataSource.setJdbcUrl(url);dataSource.setDriverClassName(properties.getDriverClass());dataSource.addDataSourceProperty(cachePrepStmts, properties.isCachePrepStmts());dataSource.addDataSourceProperty(prepStmtCacheSize, properties.getPrepStmtCacheSize());dataSource.addDataSourceProperty(prepStmtCacheSqlLimit, properties.getPrepStmtCacheSqlLimit());dataSource.addDataSourceProperty(useServerPrepStmts, properties.isUseServerPrepStmts());dataSource.addDataSourceProperty(useLocalSessionState, properties.isUseLocalSessionState());dataSource.addDataSourceProperty(rewriteBatchedStatements, properties.isRewriteBatchedStatements());dataSource.addDataSourceProperty(cacheResultSetMetadata, properties.isCacheResultSetMetadata());dataSource.addDataSourceProperty(cacheServerConfiguration, properties.isCacheServerConfiguration());dataSource.addDataSourceProperty(elideSetAutoCommits, properties.isElideSetAutoCommits());dataSource.addDataSourceProperty(maintainTimeStats, properties.isMaintainTimeStats());return dataSource;}
}
2、或者在 xml 配置中的事务配置信息
清单1
tx:annotation-driven /bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManagerproperty namedataSource refdataSource /
/bean 第二步添加
将Transactional 注解添加到合适的方法上并设置合适的属性信息。Transactional 注解的属性信息如表 1 展示。
表 1. Transactional 注解的属性信息 ① Transactional 注解也可添加在类级别上。当把Transactional 注解放在类级别时表示所有该类的公共方法都配置相同的事务属性信息。如下面类EmployeeService 的所有方法都支持事务并且是只读。
② 当类级别配置了Transactional方法级别也配置了Transactional应用程序会以方法级别的事务属性信息来管理事务换言之方法级别的事务属性信息会覆盖类级别的相关配置信息。
Transactional 注解的类级别支持
清单 2
Transactional(propagation Propagation.SUPPORTS,readOnlytrue)
Service(value employeeService)
public class EmployeeService 到此您会发觉使用Transactional 注解管理事务的实现步骤很简单。但是如果对 Spring 中的 transaction 注解的事务管理理解的不够透彻就很容易出现错误比如事务应该回滚rollback而没有回滚事务的问题。接下来将首先分析 Spring 的注解方式的事务实现机制然后列出相关的注意事项以最终达到帮助开发人员准确而熟练的使用 Spring 的事务的目的。 2Spring 的注解方式的事务实现机制 在应用系统调用声明Transactional 的目标方法时Spring Framework 默认使用 AOP 代理在代码运行时生成一个代理对象根据Transactional 的属性配置信息这个代理对象决定该声明Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截在 TransactionInterceptor 拦截时会在在目标方法开始执行之前创建并加入事务并执行目标方法的逻辑, 最后根据执行情况是否出现异常利用抽象事务管理器(图 2 有相关介绍)AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务, 如图 1 所示。 图 1. Spring 事务实现机制 Spring AOP 代理有 CglibAopProxy 和 JdkDynamicAopProxy 两种图 1 是以 CglibAopProxy 为例对于 CglibAopProxy需要调用其内部类的 DynamicAdvisedInterceptor 的 intercept 方法。对于 JdkDynamicAopProxy需要调用其 invoke 方法。 正如上文提到的事务管理的框架是由抽象事务管理器 AbstractPlatformTransactionManager 来提供的而具体的底层事务处理实现由 PlatformTransactionManager 的具体实现类来实现如事务管理器 DataSourceTransactionManager。不同的事务管理器管理不同的数据资源 DataSource比如 DataSourceTransactionManager 管理 JDBC 的 Connection。
PlatformTransactionManagerAbstractPlatformTransactionManager 及具体实现类关系如图 2 所示。 图 2. TransactionManager 类结构 3注解方式的事务使用注意事项 当您对 Spring 的基于注解方式的实现步骤和事务内在实现机制有较好的理解之后就会更好的使用注解方式的事务管理避免当系统抛出异常数据不能回滚的问题。
正确的设置Transactional 的 propagation 属性
需要注意下面三种 propagation 可以不启动事务。本来期望目标方法进行事务管理但若是错误的配置这三种 propagation事务将不会发生回滚。
TransactionDefinition.PROPAGATION_SUPPORTS如果当前存在事务则加入该事务如果当前没有事务则以非事务的方式继续运行。 TransactionDefinition.PROPAGATION_NOT_SUPPORTED以非事务方式运行如果当前存在事务则把当前事务挂起。 TransactionDefinition.PROPAGATION_NEVER以非事务方式运行如果当前存在事务则抛出异常。
正确的设置Transactional 的 rollbackFor 属性
默认情况下如果在事务中抛出了未检查异常继承自 RuntimeException 的异常或者 Error则 Spring 将回滚事务除此之外Spring 不会回滚事务。
如果在事务中抛出其他类型的异常并期望 Spring 能够回滚事务可以指定 rollbackFor。例
Transactional(propagation Propagation.REQUIRED,rollbackFor MyException.class)
通过分析 Spring 源码可以知道若在目标方法中抛出的异常是 rollbackFor 指定的异常的子类事务同样会回滚。
清单 3. RollbackRuleAttribute 的 getDepth 方法
private int getDepth(Class? exceptionClass, int depth) {if (exceptionClass.getName().contains(this.exceptionName)) {// Found it!return depth;}// If weve gone as far as we can go and havent found it...if (exceptionClass Throwable.class) {return -1;}return getDepth(exceptionClass.getSuperclass(), depth 1);} Transactional 只能应用到 public 方法才有效
只有Transactional 注解应用到 public 方法才能进行事务管理。这是因为在使用 Spring AOP 代理时Spring 在调用在图 1 中的 TransactionInterceptor 在目标方法执行前后进行拦截之前DynamicAdvisedInterceptorCglibAopProxy 的内部类的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSourceSpring 通过这个类获取表 1. Transactional 注解的事务属性配置属性信息的 computeTransactionAttribute 方法。
清单 4. AbstractFallbackTransactionAttributeSource
protected TransactionAttribute computeTransactionAttribute(Method method,Class? targetClass) {// Dont allow no-public methods as required.if (allowPublicMethodsOnly() !Modifier.isPublic(method.getModifiers())) {return null;}
这个方法会检查目标方法的修饰符是不是 public若不是 public就不会获取Transactional 的属性配置信息最终会造成不会用 TransactionInterceptor 来拦截该目标方法进行事务管理。
避免 Spring 的 AOP 的自调用问题
在 Spring 的 AOP 代理下只有目标方法由外部调用目标方法才由 Spring 生成的代理对象来管理这会造成自调用问题。若同一类中的其他没有Transactional 注解的方法内部调用有Transactional 注解的方法有Transactional 注解的方法的事务被忽略不会发生回滚。见清单 5 举例代码展示。
清单 5.自调用问题举例 Servicepublic class OrderService {private void insert() {insertOrder();}Transactionalpublic void insertOrder() {//insert log info//insertOrder//updateAccount}}
insertOrder 尽管有Transactional 注解但它被内部方法 insert 调用事务被忽略出现异常事务不会发生回滚。
上面的两个问题Transactional 注解只应用到 public 方法和自调用问题是由于使用 Spring AOP 代理造成的。为解决这两个问题使用 AspectJ 取代 Spring AOP 代理。
需要将下面的 AspectJ 信息添加到 xml 配置信息中。
清单 6. AspectJ 的 xml 配置信息
tx:annotation-driven modeaspectj /bean idtransactionManager
classorg.springframework.jdbc.datasource.DataSourceTransactionManagerproperty namedataSource refdataSource /
/beanbean classorg.springframework.transaction.aspectj.AnnotationTransactionAspect factory-methodaspectOfproperty nametransactionManager reftransactionManager /
/bean
同时在 Maven 的 pom 文件中加入 spring-aspects 和 aspectjrt 的 dependency 以及 aspectj-maven-plugin。
清单 7. AspectJ 的 pom 配置信息
dependencygroupIdorg.springframework/groupIdartifactIdspring-aspects/artifactIdversion4.3.2.RELEASE/version
/dependency
dependencygroupIdorg.aspectj/groupIdartifactIdaspectjrt/artifactIdversion1.8.9/version
/dependency
plugingroupIdorg.codehaus.mojo/groupIdartifactIdaspectj-maven-plugin/artifactIdversion1.9/versionconfigurationshowWeaveInfotrue/showWeaveInfoaspectLibrariesaspectLibrarygroupIdorg.springframework/groupIdartifactIdspring-aspects/artifactId/aspectLibrary/aspectLibraries/configurationexecutionsexecutiongoalsgoalcompile/goalgoaltest-compile/goal/goals/execution/executions
/plugin