事务注释在 Spring Boot 中不起作用
Posted
技术标签:
【中文标题】事务注释在 Spring Boot 中不起作用【英文标题】:Transactional annotation not working in Spring Boot 【发布时间】:2015-09-01 10:09:12 【问题描述】:@Transactional 在 Spring Boot 中不起作用。
Application.java:
@EnableTransactionManagement(proxyTargetClass=true)
@SpringBootApplication(exclude = ErrorMvcAutoConfiguration.class)
public class Application
@Autowired
private EntityManagerFactory entityManagerFactory;
public static void main(String[] args)
System.out.println("--------------------------- Start Application ---------------------------");
ApplicationContext ctx = SpringApplication.run(Application.class, args);
@Bean
public SessionFactory getSessionFactory()
if (entityManagerFactory.unwrap(SessionFactory.class) == null)
throw new NullPointerException("factory is not a hibernate factory");
return entityManagerFactory.unwrap(SessionFactory.class);
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory()
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] "com.buhryn.interviewer.models" );
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
@Bean
public DataSource dataSource()
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl("jdbc:postgresql://localhost:5432/interviewer");
dataSource.setUsername("postgres");
dataSource.setPassword("postgres");
return dataSource;
@Bean
@Autowired
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory)
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation()
return new PersistenceExceptionTranslationPostProcessor();
Properties additionalProperties()
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
properties.setProperty("hibernate.show_sql", "false");
properties.setProperty("hibernate.format_sql", "false");
properties.setProperty("hibernate.hbm2ddl.auto", "create");
properties.setProperty("hibernate.current_session_context_class", "org.hibernate.context.internal.ThreadLocalSessionContext");
return properties;
CandidateDao.java
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
public class CandidateDao implements ICandidateDao
@Autowired
SessionFactory sessionFactory;
protected Session getCurrentSession()
return sessionFactory.getCurrentSession();
@Override
@Transactional
public CandidateModel create(CandidateDto candidate)
CandidateModel candidateModel = new CandidateModel(candidate.getFirstName(), candidate.getLastName(), candidate.getEmail(), candidate.getPhone());
getCurrentSession().save(candidateModel);
return candidateModel;
@Override
public CandidateModel show(Long id)
return new CandidateModel(
"new",
"new",
"new",
"new");
@Override
public CandidateModel update(Long id, CandidateDto candidate)
return new CandidateModel(
"updated",
candidate.getLastName(),
candidate.getEmail(),
candidate.getPhone());
@Override
public void delete(Long id)
服务类
@Service
public class CandidateService implements ICandidateService
@Autowired
ICandidateDao candidateDao;
@Override
public CandidateModel create(CandidateDto candidate)
return candidateDao.create(candidate);
@Override
public CandidateModel show(Long id)
return candidateDao.show(id);
@Override
public CandidateModel update(Long id, CandidateDto candidate)
return candidateDao.update(id, candidate);
@Override
public void delete(Long id)
candidateDao.delete(id);
Controller.class
@RestController
@RequestMapping(value = "/api/candidates")
public class CandidateController
@Autowired
ICandidateService candidateService;
@RequestMapping(value="/id", method = RequestMethod.GET)
public CandidateModel show(@PathVariable("id") Long id)
return candidateService.show(id);
@RequestMapping(method = RequestMethod.POST)
public CandidateModel create(@Valid @RequestBody CandidateDto candidate, BindingResult result)
RequestValidator.validate(result);
return candidateService.create(candidate);
@RequestMapping(value="/id", method = RequestMethod.PUT)
public CandidateModel update(@PathVariable("id") Long id, @Valid @RequestBody CandidateDto candidate, BindingResult result)
RequestValidator.validate(result);
return candidateService.update(id, candidate);
@RequestMapping(value="/id", method = RequestMethod.DELETE)
public void delete(@PathVariable("id") Long id)
candidateService.delete(id);
当我在 DAO 系统中调用 create 方法时抛出 exception:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: save is not valid without active transaction; nested exception is org.hibernate.HibernateException: save is not valid without active transaction
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)
javax.servlet.http.HttpServlet.service(HttpServlet.java:644)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.java:291)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:102)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration$MetricsFilter.doFilterInternal(MetricFilterAutoConfiguration.java:90)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
我的 Gradle 文件:
buildscript
repositories
mavenCentral()
dependencies
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.3.RELEASE")
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'spring-boot'
jar
baseName = 'interviewer'
version = '0.1.0'
repositories
mavenCentral()
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-actuator")
compile("org.codehaus.jackson:jackson-mapper-asl:1.9.13")
compile("com.google.code.gson:gson:2.3.1")
compile("org.springframework.data:spring-data-jpa:1.8.0.RELEASE")
compile("org.hibernate:hibernate-entitymanager:4.3.10.Final")
compile("postgresql:postgresql:9.1-901-1.jdbc4")
compile("org.aspectj:aspectjweaver:1.8.6")
testCompile("org.springframework.boot:spring-boot-starter-test")
task wrapper(type: Wrapper)
gradleVersion = '2.3'
并链接到 git 存储库:https://github.com/Yurii-Buhryn/interviewer
【问题讨论】:
您在哪里/如何调用@Transactional
方法?您可以将那段代码添加到问题中吗?
@mhlz 添加以及 git 存储库的路径:github.com/Yurii-Buhryn/interviewer
你已经在你的 gradle 文件中定义了 spring-data-jpa,但是你手动实现了你的 dao 层,你为什么要这样做?您不需要定义 sessionFactory、entityFactory 和所有这些,只需在您的 ICandidateDao
中扩展 JpaRepository<CandidateModel, Long>
,用 @Repository
注释它,删除您的 CandidateDao
实现并完成它
您正在使用 JPA,因此应该使用 JpaTransactionManager
而不是 HibernateTransactionManager
。如果你可以简单地用 JPA 来做,为什么还要使用普通的休眠?你只是让事情变得更复杂。此外,您正在使用 Spring Boot,然后使用 Spring Boot 并让它为您自动配置东西,而不是您解决这个问题并手动配置所有内容。
【参考方案1】:
首先您使用 Spring Boot,然后使用 Spring Boot 并让它为您自动配置。它将配置数据源、实体管理器、事务管理器等。
接下来您使用了错误的事务管理器,您使用的是 JPA,因此您应该使用 JpaTransactionManager
而不是 HibernateTransactionManager
,因为它已经为您配置好了,您可以简单地删除它的 bean 定义。
第二次你的hibernate.current_session_context_class
搞砸了正确的 tx 集成,删除它。
使用自动配置
当您考虑到所有这些时,您基本上可以将您的 Application
类减少到以下内容。
@SpringBootApplication(exclude = ErrorMvcAutoConfiguration.class)
@EntityScan("com.buhryn.interviewer.models")
public class Application
public static void main(String[] args)
System.out.println("--------------------------- Start Application ---------------------------");
ApplicationContext ctx = SpringApplication.run(Application.class, args);
@Bean
public SessionFactory sessionFactory(EntityManagerFactory emf)
if (emf.unwrap(SessionFactory.class) == null)
throw new NullPointerException("factory is not a hibernate factory");
return emf.unwrap(SessionFactory.class);
接下来在src/main/resources
中添加一个application.properties
,其中包含以下内容。
# DataSource configuration
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.datasource.url=jdbc:postgresql://localhost:5432/interviewer
# General JPA properties
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.show-sql=false
# Hibernate Specific properties
spring.jpa.properties.hibernate.format_sql=false
spring.jpa.hibernate.ddl-auto=create
这将正确配置数据源和 JPA。
使用 JPA 代替普通的 Hibernate
另一个技巧,而不是使用普通的休眠 API,只需使用 JPA,这样您也可以删除 SessionFactory
的 bean。只需将您的 dao 更改为使用 EntityManager
而不是 SessionFactory
。
@Repository
public class CandidateDao implements ICandidateDao
@PersistenceContext
private EntityManager em;
@Override
@Transactional
public CandidateModel create(CandidateDto candidate)
CandidateModel candidateModel = new CandidateModel(candidate.getFirstName(), candidate.getLastName(), candidate.getEmail(), candidate.getPhone());
return em.persist(candidateModel);
@Override
public CandidateModel show(Long id)
return new CandidateModel(
"new",
"new",
"new",
"new");
@Override
public CandidateModel update(Long id, CandidateDto candidate)
return new CandidateModel(
"updated",
candidate.getLastName(),
candidate.getEmail(),
candidate.getPhone());
@Override
public void delete(Long id)
添加 Spring Data JPA
如果您真的想从中受益,请将 Spring Data JPA 添加到组合中并完全删除您的 DAO 并只留下一个接口。您现在拥有的内容将被移至服务类(恕我直言)。
整个仓库
public interface ICandidateDao extends JpaRepository<CandidateModel, Long>
修改后的服务(现在也是事务性的,所有业务逻辑都在服务中)。
@Service
@Transactional
public class CandidateService implements ICandidateService
@Autowired
ICandidateDao candidateDao;
@Override
public CandidateModel create(CandidateDto candidate)
CandidateModel candidateModel = new CandidateModel(candidate.getFirstName(), candidate.getLastName(), candidate.getEmail(), candidate.getPhone());
return candidateDao.save(candidate);
@Override
public CandidateModel show(Long id)
return candidateDao.findOne(id);
@Override
public CandidateModel update(Long id, CandidateDto candidate)
CandidateModel cm = candidateDao.findOne(id);
// Update values.
return candidateDao.save(cm);
@Override
public void delete(Long id)
candidateDao.delete(id);
现在您还可以删除 SessionFactory
的 bean 定义,将您的 Application
减少到只是一个 main
方法。
@SpringBootApplication(exclude = ErrorMvcAutoConfiguration.class)
@EntityScan("com.buhryn.interviewer.models")
public class Application
public static void main(String[] args)
System.out.println("--------------------------- Start Application ---------------------------");
ApplicationContext ctx = SpringApplication.run(Application.class, args);
所以我强烈建议使用框架而不是试图绕过框架。因为这将真正简化您的开发人员的生活。
依赖关系
作为最后一点,我建议从您的依赖项中删除 spring-data-jpa
依赖项并改用 starter。 AspectJ 也是如此,为此使用 AOP 启动器。此外,不再支持 jackson 1,因此添加该依赖项不会添加任何内容
dependencies
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-actuator")
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("org.springframework.boot:spring-boot-starter-aop")
compile("com.google.code.gson:gson:2.3.1")
compile("org.hibernate:hibernate-entitymanager:4.3.10.Final")
compile("postgresql:postgresql:9.1-901-1.jdbc4")
testCompile("org.springframework.boot:spring-boot-starter-test")
【讨论】:
一般来说,您不希望在 DAO/Repository 层上使用 @Transactional,而是让它从 Service 层进行管理。 我遵循了你的所有指示,但对我来说还不起作用):我认为 @Transactional 被忽略了,因为我使用了 throw new Exception();模拟错误。Exception
不仅仅回滚RuntimeException
s(这在参考指南和所述类的javadocs中都有解释)。
我也尝试过使用,抛出新的 RuntimeException();明确地说,我使用了 rollbackFor = RuntimeException.class, Exception.class (先一个然后两个)并且不起作用。
如前所述,您还没有像问题中那样做。您自己正在捕获异常,因此正在破坏事务管理。它看不到错误,因此将提交而不是回滚。以上是关于事务注释在 Spring Boot 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章
注释 CrossOrigin 在 Spring Boot 中不起作用
@Transactional 注释 rollbackFor 值在 Spring Boot 中不起作用
Spring ComponentScan excludeFilters 注释在 Spring Boot Test 上下文中不起作用
JUnit 在带有 @Autowired 注释的 Spring Boot 中不起作用