SpringBoot24-spingboot数据访问-声明式事务
来源:互联网 发布:优化9蒙古入侵 编辑:程序博客网 时间:2024/06/11 14:34
一,spring事务的机制
所有的数据访问技术都有事务处理机制,这些技术提供了API用来开启事务,提交事务来完成数据操作,或者在发生错误的时候回滚数据。
而spring的事务机制是用统一的机制来处理不同数据访问技术的事务处理。spring的事务机制提供了一个PlatformTransactionManager接口,不同的数据访问技术的事务使用不同的接口实现,如下:
数据访问技术及实现:
JDBC ->DataSourceTransactionManager
JPA -> JpaTransactionManager
Hibernate ->HibernateTransactionManager
JDO -> JdoTransactionManager
分布式事务 -> JtaTransactionManager
在程序中定义事务管理器的代码如下:
@Bean public PlatformTransactionManager transactionManager(){ JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager; }
二,声明式事务
Spring支持声明式事务,即使用注解来选择使用事务的方法,它使用@Transactional注解在方法上表明该方法需要事务支持。这是一个基于AOP的实现操作,被注解的方法在被调用时,Spring开启一个新的事务,当方法无异常运行结束后,Spring会提交这个事务。
@Transactional public void saveSomething(Integer id,String name){ //数据库操作 }
在此需要特别注意的是,此@Transactional注解来自org.springframework.transaction.annotation包,而不是javax.transaction
Spring提供了一个@EnableTransactionManagement注解在此配置类上来开启声明式事务的支持。使用了@EnableTransactionManagement后,Spring容器会自动扫描注解了@Transactional的方法和类。@EnableTransactionManagement的使用方式如下:
package com.example.springboot7transaction.config;import org.springframework.context.annotation.Configuration;import org.springframework.transaction.annotation.EnableTransactionManagement;/** * create by jack 2017/10/8 */@Configuration@EnableTransactionManagementpublic class AppConfig {}
三,注解事务行为
@Transactional有如下的属性来定制事务行为。
propagation,propagation定义了事务的生命周期,主要有下面选项:
REQUIRED:方法a调用时没有事务新建一个事务,当方法a调用另外一个方法b的时候,方法b将使用相同的事务;如果方法b发生异常需要回滚的时候,整个事务数据回滚。
REQUIRED_NEW:对于方法a和b,在方法调用的时候无论是否有事务都开启一个新的事务;这样如果方法b有异常不会导致方法a的数据回滚。
NESTED:和REQUIRED_NEW类似,但支持JDBC,不支持JPA或hibernate。
SUPPORTS:方法调用时有事务就用事务,没事务就不用事务。
NOT_SUPPORTS:强制方法不在事务中执行,若有事务,在方法调用到结束阶段事务都将会被挂起。
NEVER:强制方法不在事务中执行,若有事务则抛出异常。
MANDATORY:强制方法在事务中执行,若无事务则抛出异常
默认:REQUIRED
isolation,isolation(隔离)决定了事务的完整性,处理在多事务对相同数据的处理机制,主要包含下面的隔离级别(当然不可以随便设置,这样看当前数据库是否支持):
READ_UNCOMMITTED:对于在a事务里修改了一条记录但没有提交事务,在b事务可以读取到修改后的记录。可导致脏读,不可重复以及幻读。
READ_COMMITTED:只有当在事务a里修改了一条记录且提交事务后,b事务才可以读取到提交的记录;阻止脏读,但可导致不可重复读和幻读。
REPEATABLE_READ:不仅能实现READ_COMMITTED的功能,而且还能阻止当a事务读取了一条记录,b事务将不允许修改这条记录;阻止脏读和不可重复读,当可出现幻读。
SERIALIZABLE:此级别下事务是顺序执行的,可避免上述级别的缺陷,但开销较大。
DEFAULT:使用当前数据库的默认隔离级别,如ORACLE,SQL Server是READ_COMMITTED;Mysql是REPEATABLE_READ
默认值:DEFAULT
timeout,timeout指事务过期时间,默认当前数据库的事务过期时间,默认值TIMEOUT_DEFAULT。
readOnly,指定当前事务是否是只读事务,默认false。
rollbackFor,指定哪个或者哪些异常可以引起事务回滚,默认为Throwable的子类。
noRollbackFor,指定哪个或者哪些异常不可以引起事务回滚,默认为Throwable的子类
四,类级别使用@Transactional
@Transactional不仅可以注解在此方法上,也可以注解在类上。当注解在类上的时候意味着此类级别的所有public方法都是开启事务的。如果类级别和方法级别同时使用了@Transactional注解,则使用方法级别注解覆盖类级别注解。
五,Spring Data JPA的事务支持
Spring Data JPA对所有的默认方法都开启了事务支持,且查询类事务默认启用readOnly=true属性。
这些我们在SimpleJpaRepository的源码可以看到,下面来看看
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package org.springframework.data.jpa.repository.support;import java.io.Serializable;import java.util.ArrayList;import java.util.Collections;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Map.Entry;import javax.persistence.EntityManager;import javax.persistence.LockModeType;import javax.persistence.NoResultException;import javax.persistence.Query;import javax.persistence.TypedQuery;import javax.persistence.criteria.CriteriaBuilder;import javax.persistence.criteria.CriteriaQuery;import javax.persistence.criteria.Expression;import javax.persistence.criteria.ParameterExpression;import javax.persistence.criteria.Path;import javax.persistence.criteria.Predicate;import javax.persistence.criteria.Root;import org.springframework.dao.EmptyResultDataAccessException;import org.springframework.data.domain.Example;import org.springframework.data.domain.Page;import org.springframework.data.domain.PageImpl;import org.springframework.data.domain.Pageable;import org.springframework.data.domain.Sort;import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder;import org.springframework.data.jpa.domain.Specification;import org.springframework.data.jpa.provider.PersistenceProvider;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.JpaSpecificationExecutor;import org.springframework.data.jpa.repository.query.Jpa21Utils;import org.springframework.data.jpa.repository.query.JpaEntityGraph;import org.springframework.data.jpa.repository.query.QueryUtils;import org.springframework.data.repository.support.PageableExecutionUtils;import org.springframework.data.repository.support.PageableExecutionUtils.TotalSupplier;import org.springframework.stereotype.Repository;import org.springframework.transaction.annotation.Transactional;import org.springframework.util.Assert;@Repository@Transactional( readOnly = true)public class SimpleJpaRepository<T, ID extends Serializable> implements JpaRepository<T, ID>, JpaSpecificationExecutor<T> { private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null!"; private final JpaEntityInformation<T, ?> entityInformation; private final EntityManager em; private final PersistenceProvider provider; private CrudMethodMetadata metadata; public SimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) { Assert.notNull(entityInformation, "JpaEntityInformation must not be null!"); Assert.notNull(entityManager, "EntityManager must not be null!"); this.entityInformation = entityInformation; this.em = entityManager; this.provider = PersistenceProvider.fromEntityManager(entityManager); } public SimpleJpaRepository(Class<T> domainClass, EntityManager em) { this(JpaEntityInformationSupport.getEntityInformation(domainClass, em), em); } public void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata) { this.metadata = crudMethodMetadata; } protected CrudMethodMetadata getRepositoryMethodMetadata() { return this.metadata; } protected Class<T> getDomainClass() { return this.entityInformation.getJavaType(); } private String getDeleteAllQueryString() { return QueryUtils.getQueryString("delete from %s x", this.entityInformation.getEntityName()); } private String getCountQueryString() { String countQuery = String.format("select count(%s) from %s x", this.provider.getCountQueryPlaceholder(), "%s"); return QueryUtils.getQueryString(countQuery, this.entityInformation.getEntityName()); } @Transactional public void delete(ID id) { Assert.notNull(id, "The given id must not be null!"); T entity = this.findOne(id); if (entity == null) { throw new EmptyResultDataAccessException(String.format("No %s entity with id %s exists!", this.entityInformation.getJavaType(), id), 1); } else { this.delete(entity); } } @Transactional public void delete(T entity) { Assert.notNull(entity, "The entity must not be null!"); this.em.remove(this.em.contains(entity) ? entity : this.em.merge(entity)); } @Transactional public void delete(Iterable<? extends T> entities) { Assert.notNull(entities, "The given Iterable of entities not be null!"); Iterator var2 = entities.iterator(); while(var2.hasNext()) { T entity = var2.next(); this.delete(entity); } } @Transactional public void deleteInBatch(Iterable<T> entities) { Assert.notNull(entities, "The given Iterable of entities not be null!"); if (entities.iterator().hasNext()) { QueryUtils.applyAndBind(QueryUtils.getQueryString("delete from %s x", this.entityInformation.getEntityName()), entities, this.em).executeUpdate(); } } @Transactional public void deleteAll() { Iterator var1 = this.findAll().iterator(); while(var1.hasNext()) { T element = var1.next(); this.delete(element); } } @Transactional public void deleteAllInBatch() { this.em.createQuery(this.getDeleteAllQueryString()).executeUpdate(); } public T findOne(ID id) { Assert.notNull(id, "The given id must not be null!"); Class<T> domainType = this.getDomainClass(); if (this.metadata == null) { return this.em.find(domainType, id); } else { LockModeType type = this.metadata.getLockModeType(); Map<String, Object> hints = this.getQueryHints(); return type == null ? this.em.find(domainType, id, hints) : this.em.find(domainType, id, type, hints); } } protected Map<String, Object> getQueryHints() { if (this.metadata.getEntityGraph() == null) { return this.metadata.getQueryHints(); } else { Map<String, Object> hints = new HashMap(); hints.putAll(this.metadata.getQueryHints()); hints.putAll(Jpa21Utils.tryGetFetchGraphHints(this.em, this.getEntityGraph(), this.getDomainClass())); return hints; } } private JpaEntityGraph getEntityGraph() { String fallbackName = this.entityInformation.getEntityName() + "." + this.metadata.getMethod().getName(); return new JpaEntityGraph(this.metadata.getEntityGraph(), fallbackName); } public T getOne(ID id) { Assert.notNull(id, "The given id must not be null!"); return this.em.getReference(this.getDomainClass(), id); } public boolean exists(ID id) { Assert.notNull(id, "The given id must not be null!"); if (this.entityInformation.getIdAttribute() == null) { return this.findOne(id) != null; } else { String placeholder = this.provider.getCountQueryPlaceholder(); String entityName = this.entityInformation.getEntityName(); Iterable<String> idAttributeNames = this.entityInformation.getIdAttributeNames(); String existsQuery = QueryUtils.getExistsQueryString(entityName, placeholder, idAttributeNames); TypedQuery<Long> query = this.em.createQuery(existsQuery, Long.class); if (!this.entityInformation.hasCompositeId()) { query.setParameter((String)idAttributeNames.iterator().next(), id); return ((Long)query.getSingleResult()).longValue() == 1L; } else { Iterator var7 = idAttributeNames.iterator(); while(var7.hasNext()) { String idAttributeName = (String)var7.next(); Object idAttributeValue = this.entityInformation.getCompositeIdAttributeValue(id, idAttributeName); boolean complexIdParameterValueDiscovered = idAttributeValue != null && !query.getParameter(idAttributeName).getParameterType().isAssignableFrom(idAttributeValue.getClass()); if (complexIdParameterValueDiscovered) { return this.findOne(id) != null; } query.setParameter(idAttributeName, idAttributeValue); } return ((Long)query.getSingleResult()).longValue() == 1L; } } } public List<T> findAll() { return this.getQuery((Specification)null, (Sort)((Sort)null)).getResultList(); } public List<T> findAll(Iterable<ID> ids) { if (ids != null && ids.iterator().hasNext()) { if (!this.entityInformation.hasCompositeId()) { SimpleJpaRepository.ByIdsSpecification<T> specification = new SimpleJpaRepository.ByIdsSpecification(this.entityInformation); TypedQuery<T> query = this.getQuery(specification, (Sort)((Sort)null)); return query.setParameter(specification.parameter, ids).getResultList(); } else { List<T> results = new ArrayList(); Iterator var3 = ids.iterator(); while(var3.hasNext()) { ID id = (Serializable)var3.next(); results.add(this.findOne(id)); } return results; } } else { return Collections.emptyList(); } } public List<T> findAll(Sort sort) { return this.getQuery((Specification)null, (Sort)sort).getResultList(); } public Page<T> findAll(Pageable pageable) { return (Page)(null == pageable ? new PageImpl(this.findAll()) : this.findAll((Specification)null, pageable)); } public T findOne(Specification<T> spec) { try { return this.getQuery(spec, (Sort)null).getSingleResult(); } catch (NoResultException var3) { return null; } } public List<T> findAll(Specification<T> spec) { return this.getQuery(spec, (Sort)null).getResultList(); } public Page<T> findAll(Specification<T> spec, Pageable pageable) { TypedQuery<T> query = this.getQuery(spec, pageable); return (Page)(pageable == null ? new PageImpl(query.getResultList()) : this.readPage(query, this.getDomainClass(), pageable, spec)); } public List<T> findAll(Specification<T> spec, Sort sort) { return this.getQuery(spec, sort).getResultList(); } public <S extends T> S findOne(Example<S> example) { try { return this.getQuery(new SimpleJpaRepository.ExampleSpecification(example), example.getProbeType(), (Sort)((Sort)null)).getSingleResult(); } catch (NoResultException var3) { return null; } } public <S extends T> long count(Example<S> example) { return executeCountQuery(this.getCountQuery(new SimpleJpaRepository.ExampleSpecification(example), example.getProbeType())).longValue(); } public <S extends T> boolean exists(Example<S> example) { return !this.getQuery(new SimpleJpaRepository.ExampleSpecification(example), example.getProbeType(), (Sort)((Sort)null)).getResultList().isEmpty(); } public <S extends T> List<S> findAll(Example<S> example) { return this.getQuery(new SimpleJpaRepository.ExampleSpecification(example), example.getProbeType(), (Sort)((Sort)null)).getResultList(); } public <S extends T> List<S> findAll(Example<S> example, Sort sort) { return this.getQuery(new SimpleJpaRepository.ExampleSpecification(example), example.getProbeType(), (Sort)sort).getResultList(); } public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) { SimpleJpaRepository.ExampleSpecification<S> spec = new SimpleJpaRepository.ExampleSpecification(example); Class<S> probeType = example.getProbeType(); TypedQuery<S> query = this.getQuery(new SimpleJpaRepository.ExampleSpecification(example), probeType, (Pageable)pageable); return (Page)(pageable == null ? new PageImpl(query.getResultList()) : this.readPage(query, probeType, pageable, spec)); } public long count() { return ((Long)this.em.createQuery(this.getCountQueryString(), Long.class).getSingleResult()).longValue(); } public long count(Specification<T> spec) { return executeCountQuery(this.getCountQuery(spec, this.getDomainClass())).longValue(); } @Transactional public <S extends T> S save(S entity) { if (this.entityInformation.isNew(entity)) { this.em.persist(entity); return entity; } else { return this.em.merge(entity); } } @Transactional public <S extends T> S saveAndFlush(S entity) { S result = this.save(entity); this.flush(); return result; } @Transactional public <S extends T> List<S> save(Iterable<S> entities) { List<S> result = new ArrayList(); if (entities == null) { return result; } else { Iterator var3 = entities.iterator(); while(var3.hasNext()) { S entity = var3.next(); result.add(this.save(entity)); } return result; } } @Transactional public void flush() { this.em.flush(); } /** @deprecated */ @Deprecated protected Page<T> readPage(TypedQuery<T> query, Pageable pageable, Specification<T> spec) { return this.readPage(query, this.getDomainClass(), pageable, spec); } protected <S extends T> Page<S> readPage(TypedQuery<S> query, final Class<S> domainClass, Pageable pageable, final Specification<S> spec) { query.setFirstResult(pageable.getOffset()); query.setMaxResults(pageable.getPageSize()); return PageableExecutionUtils.getPage(query.getResultList(), pageable, new TotalSupplier() { public long get() { return SimpleJpaRepository.executeCountQuery(SimpleJpaRepository.this.getCountQuery(spec, domainClass)).longValue(); } }); } protected TypedQuery<T> getQuery(Specification<T> spec, Pageable pageable) { Sort sort = pageable == null ? null : pageable.getSort(); return this.getQuery(spec, this.getDomainClass(), sort); } protected <S extends T> TypedQuery<S> getQuery(Specification<S> spec, Class<S> domainClass, Pageable pageable) { Sort sort = pageable == null ? null : pageable.getSort(); return this.getQuery(spec, domainClass, sort); } protected TypedQuery<T> getQuery(Specification<T> spec, Sort sort) { return this.getQuery(spec, this.getDomainClass(), sort); } protected <S extends T> TypedQuery<S> getQuery(Specification<S> spec, Class<S> domainClass, Sort sort) { CriteriaBuilder builder = this.em.getCriteriaBuilder(); CriteriaQuery<S> query = builder.createQuery(domainClass); Root<S> root = this.applySpecificationToCriteria(spec, domainClass, query); query.select(root); if (sort != null) { query.orderBy(QueryUtils.toOrders(sort, root, builder)); } return this.applyRepositoryMethodMetadata(this.em.createQuery(query)); } /** @deprecated */ @Deprecated protected TypedQuery<Long> getCountQuery(Specification<T> spec) { return this.getCountQuery(spec, this.getDomainClass()); } protected <S extends T> TypedQuery<Long> getCountQuery(Specification<S> spec, Class<S> domainClass) { CriteriaBuilder builder = this.em.getCriteriaBuilder(); CriteriaQuery<Long> query = builder.createQuery(Long.class); Root<S> root = this.applySpecificationToCriteria(spec, domainClass, query); if (query.isDistinct()) { query.select(builder.countDistinct(root)); } else { query.select(builder.count(root)); } query.orderBy(Collections.emptyList()); return this.em.createQuery(query); } private <S, U extends T> Root<U> applySpecificationToCriteria(Specification<U> spec, Class<U> domainClass, CriteriaQuery<S> query) { Assert.notNull(domainClass, "Domain class must not be null!"); Assert.notNull(query, "CriteriaQuery must not be null!"); Root<U> root = query.from(domainClass); if (spec == null) { return root; } else { CriteriaBuilder builder = this.em.getCriteriaBuilder(); Predicate predicate = spec.toPredicate(root, query, builder); if (predicate != null) { query.where(predicate); } return root; } } private <S> TypedQuery<S> applyRepositoryMethodMetadata(TypedQuery<S> query) { if (this.metadata == null) { return query; } else { LockModeType type = this.metadata.getLockModeType(); TypedQuery<S> toReturn = type == null ? query : query.setLockMode(type); this.applyQueryHints(toReturn); return toReturn; } } private void applyQueryHints(Query query) { Iterator var2 = this.getQueryHints().entrySet().iterator(); while(var2.hasNext()) { Entry<String, Object> hint = (Entry)var2.next(); query.setHint((String)hint.getKey(), hint.getValue()); } } private static Long executeCountQuery(TypedQuery<Long> query) { Assert.notNull(query, "TypedQuery must not be null!"); List<Long> totals = query.getResultList(); Long total = 0L; Long element; for(Iterator var3 = totals.iterator(); var3.hasNext(); total = total.longValue() + (element == null ? 0L : element.longValue())) { element = (Long)var3.next(); } return total; } private static class ExampleSpecification<T> implements Specification<T> { private final Example<T> example; public ExampleSpecification(Example<T> example) { Assert.notNull(example, "Example must not be null!"); this.example = example; } public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) { return QueryByExamplePredicateBuilder.getPredicate(root, cb, this.example); } } private static final class ByIdsSpecification<T> implements Specification<T> { private final JpaEntityInformation<T, ?> entityInformation; ParameterExpression<Iterable> parameter; public ByIdsSpecification(JpaEntityInformation<T, ?> entityInformation) { this.entityInformation = entityInformation; } public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) { Path<?> path = root.get(this.entityInformation.getIdAttribute()); this.parameter = cb.parameter(Iterable.class); return path.in(new Expression[]{this.parameter}); } }}
从源码可以看出SimpleJpaRepository在类级别定义了@Transactional(readOnly=true),而在和save,delete相关的操作重写了@Transactional属性,此时的readOnly属性是false,其余查询操作readOnly仍然为true。
六,Spring Boot的事务支持
1,自动配置的事务管理器
在使用JDBC作为数据访问技术的时候Spring Boot为我们定义了PlatformTransactionManager的实现DataSourceTransactionManager的Bean;配置见org.springframework.boot.autoconfig.jdbc.DataSourceTransactionManagerAutoConfiguration类中的定义:
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package org.springframework.boot.autoconfigure.jdbc;import javax.sql.DataSource;import org.springframework.beans.factory.ObjectProvider;import org.springframework.boot.autoconfigure.AutoConfigureOrder;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import org.springframework.transaction.PlatformTransactionManager;@Configuration@ConditionalOnClass({JdbcTemplate.class, PlatformTransactionManager.class})@AutoConfigureOrder(2147483647)@EnableConfigurationProperties({DataSourceProperties.class})public class DataSourceTransactionManagerAutoConfiguration { public DataSourceTransactionManagerAutoConfiguration() { } @Configuration @ConditionalOnSingleCandidate(DataSource.class) static class DataSourceTransactionManagerConfiguration { private final DataSource dataSource; private final TransactionManagerCustomizers transactionManagerCustomizers; DataSourceTransactionManagerConfiguration(DataSource dataSource, ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) { this.dataSource = dataSource; this.transactionManagerCustomizers = (TransactionManagerCustomizers)transactionManagerCustomizers.getIfAvailable(); } @Bean @ConditionalOnMissingBean({PlatformTransactionManager.class}) public DataSourceTransactionManager transactionManager(DataSourceProperties properties) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(this.dataSource); if (this.transactionManagerCustomizers != null) { this.transactionManagerCustomizers.customize(transactionManager); } return transactionManager; } }}
在是用JPA作为数据访问技术的时候,Spring Boot为我们定义了一个PlatformTransactionManager的实现JpaTransactionManager的Bean;配置见org.springframework.boot.autoconfig.orm.jpa.JpaBaseConfiguration.class类中的定义:
@Bean @ConditionalOnMissingBean({PlatformTransactionManager.class}) public PlatformTransactionManager transactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); if (this.transactionManagerCustomizers != null) { this.transactionManagerCustomizers.customize(transactionManager); } return transactionManager; }
2,自动开启注解事务的支持
Spring Boot专门用于配置事务的类为:org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,此配置类依赖于JpaBaseConfiguratin和DataSourceTransactionManagerAutoConfiguration。
而在DataSourceTransactionManagerAutoConfiguration配置里还开启了对声名事务的支持,代码如下:
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package org.springframework.boot.autoconfigure.jdbc;import javax.sql.DataSource;import org.springframework.beans.factory.ObjectProvider;import org.springframework.boot.autoconfigure.AutoConfigureOrder;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import org.springframework.transaction.PlatformTransactionManager;@Configuration@ConditionalOnClass({JdbcTemplate.class, PlatformTransactionManager.class})@AutoConfigureOrder(2147483647)@EnableConfigurationProperties({DataSourceProperties.class})public class DataSourceTransactionManagerAutoConfiguration { public DataSourceTransactionManagerAutoConfiguration() { } @Configuration @ConditionalOnSingleCandidate(DataSource.class) static class DataSourceTransactionManagerConfiguration { private final DataSource dataSource; private final TransactionManagerCustomizers transactionManagerCustomizers; DataSourceTransactionManagerConfiguration(DataSource dataSource, ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) { this.dataSource = dataSource; this.transactionManagerCustomizers = (TransactionManagerCustomizers)transactionManagerCustomizers.getIfAvailable(); } @Bean @ConditionalOnMissingBean({PlatformTransactionManager.class}) public DataSourceTransactionManager transactionManager(DataSourceProperties properties) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(this.dataSource); if (this.transactionManagerCustomizers != null) { this.transactionManagerCustomizers.customize(transactionManager); } return transactionManager; } }}
所以在Spring Boot中,无须显示开启使用@EnableTransactionManagement注解。
七,实战
在实际使用中,使用Spring Boot默认的配置就能满足我们绝大多数需求,下面的实战中演示如何使用@Transactional使用异常导致数据回滚和使用异常让数据不回滚。
1,新建spring boot项目,依赖JPA和web,添加mysql的驱动,修改application.yml属性:
pom.xml的代码如下:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>springboot7transaction</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>springboot7transaction</name><description>Demo project for Spring Boot</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.7.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--mysql连接驱动--><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.39</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
application.yml里面的配置如下:
server: port: 9090spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/jack username: root password: root jpa: hibernate: #hibernate\u63D0\u4F9B\u4E86\u6839\u636E\u5B9E\u4F53\u7C7B\u81EA\u52A8\u7EF4\u62A4\u6570\u636E\u5E93\u8868\u7ED3\u6784\u7684\u529F\u80FD\uFF0C\u53EF\u901A\u8FC7spring.jpa.hibernate.ddl-auto\u6765\u914D\u7F6E\uFF0C\u6709\u4E0B\u5217\u53EF\u9009\u9879 #create\uFF1A\u542F\u52A8\u65F6\u5220\u9664\u4E0A\u4E00\u6B21\u751F\u6210\u7684\u8868\uFF0C\u5E76\u6839\u636E\u5B9E\u4F53\u7C7B\u751F\u6210\u8868\uFF0C\u8868\u4E2D\u6570\u636E\u4F1A\u88AB\u6E05\u7A7A #create-drop\uFF1A\u542F\u52A8\u65F6\u6839\u636E\u5B9E\u4F53\u7C7B\u751F\u6210\u8868\uFF0CsessionFacotry\u5173\u95ED\u65F6\u8868\u4F1A\u88AB\u5220\u9664 #update\uFF1A\u542F\u52A8\u65F6\u6839\u636E\u5B9E\u4F53\u7C7B\u751F\u6210\u8868\uFF0C\u5F53\u5B9E\u4F53\u7C7B\u5C5E\u6027\u53D8\u52A8\u7684\u65F6\u5019\uFF0C\u8868\u7ED3\u6784\u4E5F\u4F1A\u66F4\u65B0\uFF0C\u5728\u521D\u671F\u5F00\u53D1\u9636\u6BB5\u4F7F\u7528\u6B64\u9879 #validate\uFF1B\u542F\u52A8\u65F6\u9A8C\u8BC1\u5B9E\u4F53\u7C7B\u548C\u6570\u636E\u8868\u662F\u5426\u4E00\u81F4\uFF0C\u5728\u6211\u4EEC\u6570\u636E\u7ED3\u6784\u7A33\u5B9A\u65F6\u91C7\u7528\u6B64\u9009\u9879 #none\uFF1A\u4E0D\u91C7\u53D6\u4EFB\u4F55\u63AA\u65BD ddl-auto: update #show-sql\u7528\u6765\u8BBE\u7F6Ehibernate\u64CD\u4F5C\u7684\u65F6\u5019\u5728\u63A7\u5236\u53F0\u663E\u793A\u5176\u771F\u5B9E\u7684sql\u8BED\u53E5 show-sql: true properties: hibernate: dialect: org.hibernate.dialect.MySQL5Dialect jackson: serialization: #\u8BA9\u63A7\u5236\u5668\u8F93\u51FA\u7684json\u5B57\u7B26\u4E32\u683C\u5F0F\u66F4\u7F8E\u89C2 indent_output: true
2,实体类
package com.example.springboot7transaction.entity;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;/** * create by jack 2017/10/3 *///@Entity注解指明这是一个和数据库表映射的实体类@Entitypublic class Person { /** * 主键id * @Id注解指明这个属性映射为数据库的主键 * @GeneratedValue定义主键生成的方式,下面采用的是mysql的自增属性 */ @Id @GeneratedValue(strategy= GenerationType.AUTO) private Integer id; /** * 姓名 */ private String name; /** * 年龄 */ private Integer age; /** * 地址 */ private String address; public Person() { super(); } public Person(Integer id, String name, Integer age, String address) { super(); this.id = id; this.name = name; this.age = age; this.address = address; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; }}
3,实体类Repository
package com.example.springboot7transaction.dao;import com.example.springboot7transaction.entity.Person;import org.springframework.data.jpa.repository.JpaRepository;/** * create by jack 2017/10/8 * 实体类 */public interface PersonRepository extends JpaRepository<Person,Integer>{}
4,业务服务Service
1)服务接口
package com.example.springboot7transaction.service;import com.example.springboot7transaction.entity.Person;/** * create by jack 2017/10/8 * 服务接口 */public interface DemoService { Person savePersonWithRollBack(Person person); Person savePersonWithoutRollBack(Person person);}
2)服务实现
package com.example.springboot7transaction.impl;import com.example.springboot7transaction.dao.PersonRepository;import com.example.springboot7transaction.entity.Person;import com.example.springboot7transaction.service.DemoService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;/** * create by jack 2017/10/8 */@Servicepublic class DemoServiceImpl implements DemoService{ /** * 直接注入我们的PersonRepository */ @Autowired private PersonRepository personRepository; /** * 使用@Transactional注解的rollbackFor属性,指定特定异常时,数据回滚 * @param person * @return */ @Transactional(rollbackFor = {IllegalArgumentException.class}) @Override public Person savePersonWithRollBack(Person person) { Person p = personRepository.save(person); if (person.getName().equals("jack7")) { //抛出异常 throw new IllegalArgumentException("jack7已存在,数据将回滚"); } return p; } /** * 使用@Transactional注解的noRollbackFor属性,指定特定异常时,数据不回滚 * @param person * @return */ @Transactional(noRollbackFor = {IllegalArgumentException.class}) @Override public Person savePersonWithoutRollBack(Person person) { Person p = personRepository.save(person); if ("jack7".equals(person.getName())) { //抛出异常 throw new IllegalArgumentException("jack7已存在,数据将不会回滚"); } return p; }}
5,控制器
package com.example.springboot7transaction.controller;import com.example.springboot7transaction.entity.Person;import com.example.springboot7transaction.service.DemoService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * create by jack 2017/10/8 */@RestController@RequestMapping("transaction")public class TransactionController { @Autowired private DemoService demoService; /** * 测试回滚情况 * @param person * @return */ @RequestMapping("/rollback") public Person rollback(Person person){ return demoService.savePersonWithRollBack(person); } /** * 测试不回滚情况 * @param person * @return */ @RequestMapping("/norollback") public Person noRollback(Person person){ return demoService.savePersonWithoutRollBack(person); }}
6,运行测试
1)回滚
在postman输入http://localhost:9090/transaction/rollback?name=jack7&age=99,测试回滚情况,输出如下:
java.lang.IllegalArgumentException: jack7已存在,数据将回滚at com.example.springboot7transaction.impl.DemoServiceImpl.savePersonWithRollBack(DemoServiceImpl.java:32) ~[classes/:na]at com.example.springboot7transaction.impl.DemoServiceImpl$$FastClassBySpringCGLIB$$23f3bcdb.invoke(<generated>) ~[classes/:na]at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]at com.example.springboot7transaction.impl.DemoServiceImpl$$EnhancerBySpringCGLIB$$c5ef9eec.savePersonWithRollBack(<generated>) ~[classes/:na]at com.example.springboot7transaction.controller.TransactionController.rollback(TransactionController.java:25) ~[classes/:na]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_111]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_111]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_111]at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_111]at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1457) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.20.jar:8.5.20]at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_111]at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_111]at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.20.jar:8.5.20]at java.lang.Thread.run(Thread.java:745) [na:1.8.0_111]
查看数据库,发现没有进行数据的插入,数据进行插入的时候进行回滚了。
2)不回滚
访问http://localhost:9090/transaction/norollback?name=jack7&age=99,虽然我们也抛出了异常,如下图所示,但是数据并没有进行回滚,且数据还新增了一条记录,如下:
java.lang.IllegalArgumentException: jack7已存在,数据将不会回滚at com.example.springboot7transaction.impl.DemoServiceImpl.savePersonWithoutRollBack(DemoServiceImpl.java:48) ~[classes/:na]at com.example.springboot7transaction.impl.DemoServiceImpl$$FastClassBySpringCGLIB$$23f3bcdb.invoke(<generated>) ~[classes/:na]at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]at com.example.springboot7transaction.impl.DemoServiceImpl$$EnhancerBySpringCGLIB$$c5ef9eec.savePersonWithoutRollBack(<generated>) ~[classes/:na]at com.example.springboot7transaction.controller.TransactionController.noRollback(TransactionController.java:35) ~[classes/:na]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_111]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_111]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_111]at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_111]at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1457) [tomcat-embed-core-8.5.20.jar:8.5.20]at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.20.jar:8.5.20]at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_111]at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_111]at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.20.jar:8.5.20]at java.lang.Thread.run(Thread.java:745) [na:1.8.0_111]
源码地址:https://github.com/wj903829182/SpringCloudTwo/tree/master/springboot7transaction
- SpringBoot24-spingboot数据访问-声明式事务
- SpringBoot25-spingboot数据访问-数据缓存Cache
- SpringBoot22-spingboot数据访问-Spring Data JPA
- SpringBoot23-spingboot数据访问-Spring Data REST
- SpringBoot26-spingboot数据访问-NoSQL(MongDB)
- SpringBoot27-spingboot数据访问-NoSQL(Redis)
- spingBoot
- spring声明式事务
- 声明式事务 @Transaction
- Spring---声明式事务
- 采用声明式事务
- Spring声明式事务
- Spring声明式事务
- 声明式事务2.0
- spring 声明式事务
- 声明式事务 spring
- 声明式事务
- 声明式事务
- leetcode Product of Array Except Self 生成除自己之外的数组
- [CODE【VS】]江哥的DP题d
- 1. STM32F10X-架构
- linux 命令小结
- python 爬虫爬取所有上市公司公告信息(一)
- SpringBoot24-spingboot数据访问-声明式事务
- 新建一个Maven Web项目jsp出现错误(关于Tomcat服务器)
- Java并发编程 07 Executors框架
- 通过 TensorFlow 进行的机器学习 如何帮助可口可乐实现移动购买凭证
- 人人可以机器学习!谷歌教你零编程基础玩转机器学习 ‖ github+教程
- 腾讯、阿里、百度、网易等18家中秋月饼设计盘点!(完整版)
- 渗透测试 | 无线渗透 | 6-破解
- 【python】如何在某.py文件中调用其他.py内的函数
- Error:Connection timed out: connect. If you are behind an HTTP proxy ...