Transactional annotation not working in Spring Boot

匿名 (未验证) 提交于 2019-12-03 01:23:02

问题:

@Transactional not working in 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 Class

@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);     } }

When I call create method in DAO system throw 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)

My Gradle file :

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' }

And link to git repository : https://github.com/Yurii-Buhryn/interviewer

回答1:

First you are using Spring Boot then use Spring Boot and let that auto configure things for you. It will configure a datasource, entitymanagerfactory, transaction manager etc.

Next you are using the wrong transaction manager, you are using JPA so you should use the JpaTransactionManager instead of the HibernateTransactionManager as that is already configured for you you can simply remove the bean definition for that.

Second your hibernate.current_session_context_class is messing up proper tx integration remove it.

Use auto-config

When you take all this into account you can basically reduce your Application class to the following.

@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);     } }

Next add an application.properties in src/main/resources containing the following.

# 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

This will configure the datasource and JPA correctly.

Use JPA instead of plain Hibernate

Another tip instead of using the plain hibernate API simply use JPA that way you could remove the bean for the SessionFactory as well. Simply change your dao to use an EntityManager instead of a 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) {      } }

Adding Spring Data JPA

And if you really want to benefit add Spring Data JPA into the mix and remove your DAO completely and leave only an interface. What you have now would be moved to a service class (where it belongs IMHO).

The whole repository

public interface ICandidateDao extends JpaRepository {}

The modified service (which is now also transactional as it should and all business logic is in the service).

@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);     } }

Now you can also remove the bean definition for the SessionFactory reducing your Application to just a main method.

@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);     } }

So I would strongly suggest to work with the framework instead of trying to work around the framework. As that will really simplify your developer live.

Dependencies

As a final note I would suggest removing the spring-data-jpa dependency from your dependencies and use the starter instead. The same goes for AspectJ use the AOP starter for that. Also jackson 1 isn't supported anymore so adding that dependency doesn't add anything

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") }


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!