@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); } }
控制器类
@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方法时,抛出 异常 :
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' }
首先,您使用的是Spring Boot,然后使用Spring Boot,并让它自动为您配置东西。它将配置数据源,实体管理工厂,事务管理器等。
接下来,使用错误的事务管理器,使用JPA,因此应使用,JpaTransactionManager而不是HibernateTransactionManager已经为您配置的,您可以简单地为此删除Bean定义。
JpaTransactionManager
HibernateTransactionManager
其次,您hibernate.current_session_context_class搞砸了正确的TX集成,请删除它。
hibernate.current_session_context_class
考虑到所有这些因素后,基本上可以将您的Application课程简化为以下内容。
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); } }
接着添加application.properties在src/main/resources含有以下内容。
application.properties
src/main/resources
# 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。
另一个技巧不是使用普通的hibernateAPI,而是简单地使用JPA,这样您就可以删除Bean SessionFactory。只需将dao更改为使用EntityManager而不是即可SessionFactory。
SessionFactory
EntityManager
@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添加到组合中,并完全删除DAO,仅保留一个接口。您现在拥有的将被移到服务类(它属于IMHO)。
整个仓库
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); } }
现在,您还可以删除bean定义,以SessionFactory将您Application的main方法简化为一个方法。
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依赖项中删除依赖项,而改用启动程序。对于AspectJ,使用AOP启动器也是如此。另外,杰克逊1也不再受支持,因此添加该依赖项不会添加任何内容
spring-data-jpa
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") }