2017-11-12 8 views
1

저는 최근에 springBoot 프로젝트 작업을 시작했습니다. DB에서 데이터를 가져오고 수정 한 다음 DB에 다시 저장하는 샘플 프로그램을 작성했습니다.springboot 앱의 트랜잭션 관리

내가 직면 한 문제는 DB에서 데이터를 가져올 수 있지만 다시 저장할 때 아래 예외가 발생합니다.

org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is 
javax.persistence.TransactionRequiredException: no transaction is in progress at 
org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:413) at 
org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:246) 

다음은 나의 코드입니다. DataConfiguraiton.java

import java.util.Properties; 


import javax.sql.DataSource; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.boot.autoconfigure.domain.EntityScan; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.ComponentScan; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 
import org.springframework.jdbc.datasource.DriverManagerDataSource; 
import org.springframework.orm.jpa.JpaTransactionManager; 
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; 

import org.springframework.transaction.annotation.EnableTransactionManagement; 
/** 
* 
* This class contains code to configure database. 
* @author satish 
* 
*/ 
@Configuration 
@EntityScan(basePackages= {"com.tushar.common.model"}) 
@ComponentScan(basePackages= {"com.tushar.common.model"}) 
@EnableJpaRepositories(basePackages={"com"}) 
@EnableTransactionManagement 
public class DataConfiguration { 

    @Value("${spring.datasource.driver-class-name}") 
    private String driverClassName; 

    @Value("${spring.datasource.url}") 
    private String url; 

    @Value("${spring.datasource.username}") 
    private String username; 

    @Value("${spring.datasource.password}") 
    private String password; 

    @Value("${spring.datasource.dialect}") 
    private String dialect; 

    /** 
    * 
    * It will scan the package where our entities appears. 
    * @param dataSource 
    * @return 
    */ 
    @Bean 
    @Autowired 
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { 
     HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); 
     vendorAdapter.setGenerateDdl(true); 

     Properties jpaProperties = new Properties(); 
     jpaProperties.setProperty("hibernate.show_sql", "true"); 
     jpaProperties.setProperty("hibernate.dialect", dialect); 
     jpaProperties.setProperty("hibernate.hbm2ddl.auto", "update"); 

     LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = 
       new LocalContainerEntityManagerFactoryBean(); 
     localContainerEntityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter); 
     localContainerEntityManagerFactoryBean.setPackagesToScan(new String[] {"com.att.pricerd.discountmanagement.model"}); 
     localContainerEntityManagerFactoryBean.setDataSource(dataSource); 

     localContainerEntityManagerFactoryBean.setJpaProperties(jpaProperties); 



     return localContainerEntityManagerFactoryBean; 
    } 
    /** 
    * 
    * It will set the database properties to the data Source. 
    * @return 
    */ 
    @Bean 
    public DataSource dataSource(){ 
     DriverManagerDataSource dataSource = new DriverManagerDataSource(); 
     dataSource.setDriverClassName(driverClassName); 
     dataSource.setUrl(url); 
     dataSource.setUsername(username); 
     dataSource.setPassword(password); 
     return dataSource; 
    } 

    @Bean 
    @Autowired 
    public JpaTransactionManager transactionManager(LocalContainerEntityManagerFactoryBean emf) throws Exception { 
    JpaTransactionManager transactionManager = new JpaTransactionManager(); 
    transactionManager.setEntityManagerFactory(emf.getObject()); 
    transactionManager.setEntityManagerFactory(emf.getNativeEntityManagerFactory()); 
    return transactionManager; 
    } 

} 

저장소

import java.math.BigDecimal; 
import java.util.ArrayList; 
import java.util.List; 

import javax.persistence.EntityManager; 
import javax.persistence.PersistenceContext; 
import javax.persistence.criteria.CriteriaBuilder; 
import javax.persistence.criteria.CriteriaQuery; 
import javax.persistence.criteria.Root; 

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Component; 

@Component 
public class EmployeeDataManagementDaoImpl implements EmployeeDataManagementDao { 

    private static final Logger LOG = LoggerFactory.getLogger(EmployeeDataManagementDaoImpl.class); 

    @Autowired 
    private EmployeeDataManagmentUtil EmployeeDataManagmentUtil; 

    @Autowired 
    private SalesEmployeesRepository salesEmployeesRepository; 

    @Autowired 
    private EmployeeRepository employeeRepository; 

    @PersistenceContext 
    private EntityManager em; 

    @Override 
    public void addEmployee(EmployeeDetailsRequestVo EmployeeRequest) { 
     convertAndSaveSalesEmployee(EmployeeRequest); 
    } 

    /** 
    * Fetch data from DB, update the inital and maximum Employee and 
    * save it back to DB. 
    * @throws DataNotFoundException 
    * 
    */ 
    @Override 
    public void changeEmployee(List<Employee> Employees) throws EmployeeManagementException { 
     for (Employee employee : Employees) { 

      List<Employee> EmployeesDB; 
      try { 
       EmployeesDB = getEmployeeFromDB(employee); 
      } catch (DataNotFoundException e) { 
       List<String> errorMessage = new ArrayList<>(); 
       errorMessage.add(e.getMessage()); 
       LOG.error(e.getMessage(),e); 
       throw new EmployeeManagementException(errorMessage); 
      } 

      for (Employee employee : EmployeesDB) { 
       if (employee.getMaxEmployee() != null) { 
        Employee.setMaxEmployee(employee.getMaxEmployee()); 
       } 
       if (employee.getInitialEmployee() != null) { 
        Employee.setInitialEmployee(employee.getInitialEmployee()); 
       } 
       employeeRepository.saveAndFlush(Employee); 
      } 
     } 

    } 

    /** 
    * This method is used to get the Employee details from DB. 
    * 
    * @param employee 
    * @return List<Employee> 
    * @throws DataNotFoundException 
    */ 
    private List<Employee> getEmployeeFromDB(Employee employee) 
      throws DataNotFoundException { 
     List<Employee> EmployeesDB = findByAllEmployeesFilters(employee.getempId(), 
       employee.getyearsExp(), employee.getdeptLevInd(), employee.getsalary(), 
       employee.getaddress(), employee.getCountryCd(), employee.getpinCode()); 

     if (EmployeesDB.isEmpty()) { 
      String errCode = ""; // error code for data not found, yet to be 
            // decided. 
      LOG.error("ERROR CODE :: {}", errCode); 
      throw new DataNotFoundException(errCode); 
     } 
     return EmployeesDB; 
    } 

    /** 
    * This method will update the end date Employee 
    * @param List<Employee> 
    */ 
    @Override 
    public void inactivateEmployee(List<Employee> Employees) 
      throws EmployeeManagementException { 
     for (Employee employee : Employees) { 

      List<Employee> employeesDB; 
      try { 
       employeesDB = getEmployeeFromDB(employee); 
      } catch (DataNotFoundException e) { 
       List<String> errorMessage = new ArrayList<>(); 
       errorMessage.add(e.getMessage()); 
       LOG.error(e.getMessage(),e); 
       throw new EmployeeManagementException(errorMessage); 
      } 
      for (Employee employee : EmployeesDB) { 
       if (employee.getEmployeeEndDate() != null) { 
        employee.setEmployeeEndDate(employee.getEmployeeEndDate()); 
       } 
       // 
       employeeRepository.saveAndFlush(Employee); 
      } 
     } 
    } 

    /** 
    * 
    * @param empId 
    * @param yearsExp 
    * @param bigDecimal 
    * @param salary 
    * @param regionId 
    * @param countryCd 
    * @param pinCode 
    * @return 
    */ 
    private List<Employee> findByAllEmployeeFilters(BigDecimal empId, BigDecimal yearsExp, 
      BigDecimal bigDecimal, BigDecimal salary, String regionId, String countryCd, String pinCode) { 
     CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder(); 
     CriteriaQuery<Employee> criteriaQuery = criteriaBuilder 
       .createQuery(Employee.class); 
     Root<Employee> root = criteriaQuery.from(Employee.class); 

     criteriaQuery.where(criteriaBuilder.equal(root.get("empId"), empId), 
       criteriaBuilder.equal(root.get("deptLevInd"), bigDecimal), 
       criteriaBuilder.equal(root.get("yearsExp"), yearsExp), criteriaBuilder.equal(root.get("salary"), salary), 
       criteriaBuilder.equal(root.get("address"), regionId), 
       criteriaBuilder.equal(root.get("countryCd"), countryCd), 
       criteriaBuilder.equal(root.get("pinCode"), pinCode)); 


     return em.createQuery(criteriaQuery).getResultList(); 
    } 

} 

getEmployeeFromDB 나에게 DB하지만 saveAndFlush의 값을 얻을 수

@Repository 
public interface EmployeeRepository extends JpaRepository<Employee,Long> { 

} 

DAO 클래스 나에게 예외를주고있다.

+1

[@Transactional] (https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/annotation/Propagation.html#REQUIRED) 메소드로 inactivateEmployee() 주석을 주려고 했습니까?)? – Bedla

+0

그러나 같은 방법으로 결과 집합을 반환하는 getEmployeeFromDB 호출이 발생합니다. 왜 명시 적으로 @Transactional 메서드를 표시해야합니까? –

+0

트랜잭션은 데이터를 조작하는 메소드에서만 필요합니다. 메소드 getEmployeeFromDB 만 선택하기 때문에 Spring JPA는 트랜잭션 실행을 강제하지 않습니다. – Bedla

답변

0

실상 Bedla는 rigth입니다. @Transactional을 사용해야합니다. 그 메소드를 추가하고자하는 것은 changeEmployee가 Service 클래스에서 선언되어야한다는 것이다. 올바른 방법은 EmployeeService 클래스를 만들고이 메서드를 changeEmployee으로 옮기고 @Transactional을 사용하여 서비스에 주석을 답니다. Generaly dao는 db에서로드하고 저장할 메소드를 업데이트/저장하는 메소드 만 찾아야합니다.