2016-03-15 4 views
3

Из того, что я читал, это исключение происходит, когда вы пытаетесь начать транзакцию до совершения предыдущего. Однако я не понимаю, почему я получаю это исключение в моем случае.«org.hibernate.TransactionException: вложенные транзакции не поддерживаются», но я не вложил транзакции

У меня есть веб-приложение, с помощью следующего сервлета:

@RestController 
public class Hello { 

/***** DAO's *******/ 
@Autowired 
CompteDAO compteDAO; 


@RequestMapping("/") 
public String index() { 

    String response = ""; 

    /***** COMPTE TEST *****/ 

    response = response + "======== COMPTE TEST ========= \n"; 

    response = response + "Compte list : \n"; 


    ArrayList<Compte> comptes = (ArrayList<Compte>) compteDAO.getAllComptes(); 

    for(int i = 0; i < comptes.size(); i++) { 
     response = response + comptes.get(i).getNomUtilisateur() + "\n"; 
    } 

    response = response + "\n" + "Compte name = ";  
    return response; 
} 

Это мой Compte объект:

public class Compte { 

    public int id; 
    public String nomUtilisateur; 
    public String motDePasse; 
    public int typeCompte; 
    public String courriel; 
    public String cleAPI; 
    public boolean visibleLorsDeLaCreation; 
    public int joursDisponibilite; 
    public int heuresDisponibilite; 


    public Compte(){ 

    } 

    public Compte(String nomUtilisateur, String motDePasse, int typeCompte, String courriel, String cleAPI, 
      boolean visibleLorsDeLaCreation, int joursDisponibilite, int heuresDisponibilite) { 
     this.nomUtilisateur = nomUtilisateur; 
     this.motDePasse = motDePasse; 
     this.typeCompte = typeCompte; 
     this.courriel = courriel; 
     this.cleAPI = cleAPI; 
     this.visibleLorsDeLaCreation = visibleLorsDeLaCreation; 
     this.joursDisponibilite = joursDisponibilite; 
     this.heuresDisponibilite = heuresDisponibilite; 
    } 
    /** Getters and setters omitted **/ 
} 

Это интерфейс моего DAO:

public interface CompteDAO { 

    public List<Compte> getAllComptes();  
} 

И это его реализация:

@Repository 
public class CompteDaoImpl implements CompteDAO { 

@Autowired 
public SessionFactory sessionFactory; 

public CompteDaoImpl() { 
} 


@Override 
public List<Compte> getAllComptes() { 
    Session currentSession = sessionFactory.getCurrentSession(); 
    List<Compte> comptes; 
    comptes = new ArrayList<Compte>(currentSession.createCriteria(Compte.class).list()); 
    return comptes; 
} 

Кроме того, это моя конфигурация Spring:

@Configuration 
@EnableTransactionManagement 
@PropertySource({ "classpath:application.properties" }) 
@ComponentScan({ "ca.etsmtl.gti525, ca.etsmtl.gti525.pojo" }) 
public class PersistenceConfig 
{ 
    @Autowired 
    private Environment env; 

    @Bean 
    public LocalSessionFactoryBean sessionFactory() { 
    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean(); 
    sessionFactory.setDataSource(restDataSource()); 
    sessionFactory.setPackagesToScan(new String[] { "ca.etsmtl.gti525" }); 
    sessionFactory.setHibernateProperties(hibernateProperties()); 
    sessionFactory.setMappingResources(new String[] { "mapping/Compte.hbm.xml" }); 

    return sessionFactory; 
    } 

@Bean 
    public DataSource restDataSource() { 
    BasicDataSource dataSource = new BasicDataSource(); 
    dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName")); 
    dataSource.setUrl(env.getProperty("jdbc.url")); 
    dataSource.setUsername(env.getProperty("jdbc.user")); 
    dataSource.setPassword(env.getProperty("jdbc.pass")); 

    return dataSource; 
} 

    @Bean 
    public CompteDaoImpl compteDAO() { 
     CompteDaoImpl compteDAO = new CompteDaoImpl(); 
     return compteDAO; 
    } 

    @Bean 
    public SessionManagerFilter sessionManagerFilter() { 
     SessionManagerFilter sessionManagerFilter = new SessionManagerFilter(); 
     return sessionManagerFilter; 
    } 

    @Bean 
    @Autowired 
    public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) { 
    HibernateTransactionManager txManager = new HibernateTransactionManager(); 
    txManager.setSessionFactory(sessionFactory); 

    return txManager; 
    } 

    @Bean 
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() { 
    return new PersistenceExceptionTranslationPostProcessor(); 
    } 

    Properties hibernateProperties() { 
    return new Properties() { 
     { 
      setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); 
      setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); 
      setProperty("hibernate.globally_quoted_identifiers", "true"); 
     } 
    }; 
    } 
} 

Ассоциированный application.properties файл содержит следующие строки:

# jdbc.X 
jdbc.driverClassName=com.mysql.jdbc.Driver 
jdbc.url=jdbc:mysql://localhost/gti525 
jdbc.user=root 
jdbc.pass=vente 

# hibernate.X 
hibernate.dialect=org.hibernate.dialect.MySQLDialect 
hibernate.show_sql=false 
hibernate.hbm2ddl.auto=create-drop 
hibernate.current_session_context_class=thread 

Наконец, у меня есть последовавший сервлет фильтр, где я начинаю транзакция:

@Transactional 
public class SessionManagerFilter implements Filter { 

    @Autowired 
    SessionFactory sessionFactory; 


    @Override 
    public void destroy() { 

    } 

    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
      throws IOException, ServletException { 
     try { 
       Session currentSession = sessionFactory.getCurrentSession(); 
       currentSession.beginTransaction(); 

       // Call the next filter (continue request processing) 
       chain.doFilter(request, response); 

       // Commit and cleanup 
       sessionFactory.getCurrentSession().getTransaction().commit(); 

      } catch (StaleObjectStateException staleEx) { 
       // Rollback, close everything, possibly compensate for any permanent changes 
       // during the conversation, and finally restart business conversation. Maybe 
       // give the user of the application a chance to merge some of his work with 
       // fresh data... what you do here depends on your applications design. 
       throw staleEx; 
      } catch (Throwable ex) { 
       // Rollback only 
       ex.printStackTrace(); 
       try { 
        if (sessionFactory.getCurrentSession().getTransaction().isActive()) { 
         sessionFactory.getCurrentSession().getTransaction().rollback(); 
        } 
       } catch (Throwable rbEx) { 

       } 

       throw new ServletException(ex); 
      } 
    } 

    @Override 
    public void init(FilterConfig arg0) throws ServletException { 

    } 

} 

Что отображается в файле web.xml lik е это:

<?xml version="1.0" encoding="UTF-8"?> 
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> 
    <display-name>sitevente2</display-name> 

    <filter> 
     <filter-name>SessionManagerFilter</filter-name> 
     <filter-class>ca.etsmtl.gti525.SessionManagerFilter</filter-class> 
     <!-- <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> --> 
    </filter> 

    <filter-mapping> 
     <filter-name>SessionManagerFilter</filter-name> 
     <url-pattern>/*</url-pattern> 
    </filter-mapping> 


</web-app> 

Когда метод BeginTransaction() вызывается в doFilter в SessionManagerFilter (в) метод, я получаю следующее Трассировка стека:

org.hibernate.TransactionException: nested transactions not supported 
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(AbstractTransactionImpl.java:154) 
at org.hibernate.internal.SessionImpl.beginTransaction(SessionImpl.java:1435) 
at ca.etsmtl.gti525.SessionManagerFilter.doFilter(SessionManagerFilter.java:41) 
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) 
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) 
at java.lang.reflect.Method.invoke(Unknown Source) 
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) 
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) 
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281) 
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) 
at com.sun.proxy.$Proxy73.doFilter(Unknown Source) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:87) 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121) 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
at org.springframework.boot.context.web.ErrorPageFilter.doFilter(ErrorPageFilter.java:120) 
at org.springframework.boot.context.web.ErrorPageFilter.access$000(ErrorPageFilter.java:61) 
at org.springframework.boot.context.web.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:95) 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
at org.springframework.boot.context.web.ErrorPageFilter.doFilter(ErrorPageFilter.java:113) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212) 
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) 
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) 
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141) 
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) 
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616) 
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) 
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522) 
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095) 
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672) 
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500) 
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) 
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 
at java.lang.Thread.run(Unknown Source) 
2016-03-14 22:14:32.120 ERROR 7284 --- [nio-8080-exec-2]  o.s.t.i.TransactionInterceptor   : Application exception overridden by commit exception 

Я не знаю, что я делаю неправильно , Должно быть что-то о структуре, которую я не понимаю. Я буду очень признателен, если вы могли бы дать мне руку.

Спасибо! :)

+0

В какой момент вы видите исключение? любая трассировка стека? – Sanj

+0

Я только что редактировал вопрос. Я забыл трассировку стека извините! –

+0

Можете ли вы попробовать установить свойство класса сеанса контекста следующим образом: setProperty ("hibernate.current_session_context_class", env.getProperty ("hibernate.current_session_context_class")); – Sanj

ответ

0

В вашем приложении используются две системы управления транзакциями.

Подробно:

  1. Использование @Transactional в SessionManagerFilter
  2. currentSession.beginTransaction();

Я никогда не тестировал @Transactional в фильтре, так как обычно я начинаю свою транзакцию в @RestController, но это зависит от вас.

+0

Я прокомментировал строку currentSession.beginTransaction(), и она дает мне исключение нулевого указателя в этой строке в том же классе: Session currentSession = sessionFactory.getCurrentSession(); –

+0

Итак, если я использую @Transactional, транзакция будет создана сама по себе? –

+0

Мне кажется, что ваш следующий либо плохой учебник, либо вы смешиваете разные друг с другом. (At) EnableTransactionManagement активирует управление весенними транзакциями. Который будет вызван методом (at) Transactiontal. Это в основном означает, что каждый метод invokation, отслеживаемый Spring (например, это не внутренние вызовы класса), запускает транзакцию в зависимости от распространения (по умолчанию запускается одно, если ее нет). Но я не уверен, что будет обрабатываться Spring в фильтре. –

1

Хорошо, что у вас есть код, как указано выше. Кажется, вы сбиваете с толку сеанс Transactional с сеансом HTTP. Это две разные вещи. A Transactional сеанс - это вызов базы данных. Вы переносите вызовы базы данных внутри транзакции, чтобы, если что-то пошло не так, вся вещь откат. A HTTP сеанс - это не то, что вы должны использовать, если вы собираетесь использовать архитектуру RESTful, потому что сеанс HTTP подразумевает, что вы сохраняете состояние в своем приложении. Операции REST должны быть идемпотентными (независимыми и повторяемыми).С архитектурой RESTful вы сможете выполнять один и тот же вызов тысячи раз и получать тот же результат, а не выполнять какую-либо другую часть вашего приложения.

Весной обычно имеется 3 отдельных слоя. Ваши контроллеры управляют HTTP-запросами и ответами для конкретных объектов, поэтому у вас есть CompteController и контроллеры для других объектов. Эти контроллеры связаны с управлением RESTful этими объектами. Запросы GET, POST, PUT и DELETE обрабатываются контроллерами. Обычно контроллеры выполняют это путем вызова классов обслуживания. Классы обслуживания используются для управления транзакциями, управления объектами и инкапсуляции бизнес-логики. Классы DAO предназначены исключительно для доступа к базе данных.

Вы получаете свое исключение, потому что пытаетесь использовать сеанс HTTP вместе с транзакционным сеансом. Это неверно по многим причинам и вызовет множество проблем, потому что это не так, как должно использоваться @Transcational.

Я бы реструктурировать свой код следующим образом:

Контроллер:

@RestController 
public class CompteController { 

    @Autowired 
    private CompteService compteService; 

    @RequestMapping(value="/", method=RequestMethod.GET) 
    public List<Compte> getAllCompte() throws Exception { 
     /** Because you've used `@RestController` you've indicated that you want your object return in JSON format by this method. **/ 
     return compteService.getAllCompte(); 
    } 
    /** methods here to handle GET, POST, PUT and DELETE requests**/ 

} 

Service Layer

public interface CompteService { 

    public Compte getCompte(int id); 

    public List<Compte> getAllCompte(); 

    /** Other methods to create, update, manipulate and delete Compte objects **/ 
} 

Внедрение Service

@Service 
public class CompteServiceImpl implements CompteService { 

    @Autowired 
    private CompteDAO compteDao; 

    @Override 
    @Transctional 
    public Compte getCompte(int id) { 
     return compteDao.getCompte(id); 
    } 
    @Override 
    @Transcational 
    public List<Compte> getAllCompte() { 
     return compteDao.getAllCompte(); 
    } 

}

В аннотации @Transactional здесь будет найден HibernateTransactionManager, который отвечает за создание, открытие, обслуживание и закрытие соединения с базой данных. Это происходит вокруг метода, который вы комментируете @Transactional. Если вы поместите @Transactional в класс, то все методы класса будут завернуты в транзакцию. Если исключение выбрано, то HibernateTransactionManager откатит транзакцию и распространит исключение обратно на контроллер.

DAO:

public interface CompteDAO { 

    public Compte findCompte(int id); 

    public List<Compte> findAllCompte(); 

} 

DAO Реализация:

@Repository 
public class CompteDAOImpl implements CompteDAO { 

    @Autowired 
    private SessionFactory sessionFactory; 

    @Override 
    public List<Compte> getAllComptes() { 
     Session currentSession = sessionFactory.getCurrentSession(); 
     List<Compte> comptes = currentSession.createCriteria(Compte.class).list() 
     return comptes; 
    } 
} 

Вам не нужно иметь, чтобы объявить CompteDAOImpl как @Bean, потому что вы используете @ComponentScan, которые подберут классы аннотированные с @Controller, @RestController, @Service, @Repository, @Component и некоторые другие. Эти классы будут доступны для автопостановки в другие классы.

Вышеупомянутое разделение кода позволяет легко протестировать ваш DAO, службу и контроллер изолированно. Он также отделяет логику вашего приложения от выборки объектов из базы данных. Если вы когда-нибудь думаете, что вам нужно явно управлять своим подключением к базе данных, то что-то пошло не так.

Если вы специально хотите поймать StaleObjectStateException и сделать что-то другое, когда это произойдет, то вы можете сделать следующее:

@ControllerAdvice 
public class ExceptionHandlerController { 

     protected static final Logger logger = LogManager.getLogger(ExceptionHandlerController.class); 

    @ExceptionHandler(StaleObjectStateException.class) 
    public ResponseEntity<String> handleStaleObjectStateException(StaleObjectStateException e){ 
     logger.error("A stale object state exception has been thrown", e); 
     /** Will return a HTTP 500 if you throw this exception **/ 
     return new ReponseEntity<String(HttpStatus.INTERNAL_SERVER_ERROR); 
    } 
} 

Дальнейшее чтение на Hibernate и Spring (Раскрытие информации: Q & элементов а я создал на тема):

+0

Запустите транзакцию на уровне контроллера. Этот стиль не гарантирует, что вызов отдыха либо совершается целиком, либо ничего. Поэтому он ведет себя иначе, чем версия вопроса. –

+0

Как так? Если возникает исключение, то пусть Spring откатит транзакцию и обработает исключение либо внутри этого контроллера, либо с помощью специального обработчика обработки исключений, аннотированного с помощью '@ ControllerAdvice' – JamesENL

+0

. Я узнал, что разработчики, как правило, выполняют несколько задач в контроллере. Это включает вызов различных методов обслуживания. Так, например, я вызываю метод delete x, который работает отлично, и метод add y, который генерирует исключение. В этой ситуации вы можете завершить испорченные данные. –