복붙노트

[SPRING] 스프링 주석에서 트랜잭션 주석이 작동하지 않음

SPRING

스프링 주석에서 트랜잭션 주석이 작동하지 않음

@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 메소드를 호출하면 예외가 발생합니다.

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

해결법

  1. ==============================

    1.먼저 Spring Boot를 사용하고 Spring Boot를 사용하여 자동 설정을하십시오. 데이터 소스, entitymanagerfactory, 트랜잭션 관리자 등을 구성 할 것입니다.

    먼저 Spring Boot를 사용하고 Spring Boot를 사용하여 자동 설정을하십시오. 데이터 소스, entitymanagerfactory, 트랜잭션 관리자 등을 구성 할 것입니다.

    다음으로는 잘못된 트랜잭션 관리자를 사용하고 있습니다. JPA를 사용하고 있기 때문에 HibernateTransactionManager 대신 JpaTransactionManager를 사용해야합니다. 이미 설정되어 있으므로 간단히 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가 올바르게 구성됩니다.

    평범한 hibernate API를 사용하는 대신에 또 다른 팁은 단순히 JPA를 사용하여 SessionFactory의 빈을 제거 할 수있다. SessionFactory 대신에 EntityManager를 사용하기 위해 dao를 변경하기 만하면됩니다.

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

    이제 SessionFactory에 대한 Bean 정의를 제거하여 응용 프로그램을 기본 메소드로 축소 할 수 있습니다.

    @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 스타터를 사용한다. 또한 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")
    }
    
  2. from https://stackoverflow.com/questions/30870146/transactional-annotation-not-working-in-spring-boot by cc-by-sa and MIT license