диспетчер транзакций спящего режима spring-boot не откатывает транзакцию даже при исключении времени выполнения - PullRequest
0 голосов
/ 20 июня 2020

Я работаю над книжным магазином личного проекта JPA с весенней гибернацией. Я столкнулся с проблемой, что методы, аннотированные аннотацией @Transactional, не откатывают транзакции даже с исключениями времени выполнения. Я использую весеннюю загрузку версии 1.5.1, гибернацию версии 5, java версию 11, а DB - MySQL. Я генерирую исключение времени выполнения, вставляя книгу с таким же названием в таблицу книг. В методе addBook BookServiceImpl запись в хранилище должна в идеале откатиться от таблицы хранилища при сбое добавления книги в таблицу книг, но в настоящее время этого не происходит.

Ниже приведен код файла конфигурации bean-компонента:

package com.freetests4u;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

@Configuration
public class BeanConfig {

    @Autowired
    private EntityManagerFactory entityManagerFactory;

    @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 Module datatypeHibernateModule() {
      return new Hibernate5Module();
    }
    
    
    @Bean
    public PlatformTransactionManager hibernateTransactionManager() {
        HibernateTransactionManager transactionManager
          = new HibernateTransactionManager();
        transactionManager.setSessionFactory(getSessionFactory());
        return transactionManager;
    }
    
//  @Bean
//  public DataSource dataSource() {
//      return new MysqlDataSource(); // (1)
//  }
//
//  @Bean
//  public PlatformTransactionManager txManager() {
//      return new HibernateTransactionManager(); // (2)
//  }
    
}

Ниже приведен код файла модели BooK

package com.freetests4u.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="books")
public class Book {

    @Id
    @Column(name="id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;
    
    @Column(name="title")
    private String title;
    
    @Column(name="writer")
    private String writer;

    @Column(name="language")
    private String language;
    
    @Column(name="category")
    private String category;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getWriter() {
        return writer;
    }

    public void setWriter(String writer) {
        this.writer = writer;
    }

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }
    
    

}

Ниже указан код модели магазина

package com.freetests4u.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name="store")
public class Store {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id")
    int id;
    
    @Column(name="bookid")
    int bookId;
    
    @OneToOne
    @JoinColumn(name="bookid", referencedColumnName="id", insertable = false, updatable = false)
    Book book;
    
    @Column(name="bookcount")
    int bookCount;
    
    @Column(name="isdeleted")
    boolean isDeleted;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getBookId() {
        return bookId;
    }

    public void setBookId(int bookId) {
        this.bookId = bookId;
    }

    public Book getBook() {
        return book;
    }

    public void setBook(Book book) {
        this.book = book;
    }

    public int getBookCount() {
        return bookCount;
    }

    public void setBookCount(int bookCount) {
        this.bookCount = bookCount;
    }

    public boolean isDeleted() {
        return isDeleted;
    }

    public void setDeleted(boolean isDeleted) {
        this.isDeleted = isDeleted;
    }


    
}

Ниже приведен код BookDaoImpl

package com.freetests4u.dao.impl;

import java.util.List;


import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.transaction.annotation.Transactional;
import com.freetests4u.dao.BookDao;
import com.freetests4u.model.Book;


@Component
public class BookDaoImpl implements BookDao {

    @Autowired
    private SessionFactory sessionFactory;
    
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public boolean addBook(Book book) {
        // TODO Auto-generated method stub
        
          TransactionStatus status=null;
          try {
             status = TransactionAspectSupport.currentTransactionStatus();
          } catch (NoTransactionException e) {
              System.err.println(e);
          }
          System.out.println("from addBook: "+status!=null? "active transaction": "no transaction");

        
        Session session = sessionFactory.openSession();
//      Transaction trans = session.beginTransaction();
        
//      try {
            session.save(book);
//          trans.commit();
            return true;
//      }
//      catch(Exception e) {
//          e.printStackTrace();
//          trans.rollback();
//          return false;
//      }
//      finally {
//      session.close();
//      }
        
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Book> getBookList(int limit, int offset) {
        // TODO Auto-generated method stub
                
        int finalLimit = limit!=0?limit:10;
        int finalOffset = offset!=0?offset:0;
                
        return (List<Book>) sessionFactory.openSession().createCriteria(Book.class)
//              .createAlias("Book.id", "BookId")
                .addOrder(Order.desc("id"))
                .setFirstResult(finalOffset)
                .setMaxResults(finalLimit)
                .list();
    }

    @Override
    public Book getBook(int id) {
        // TODO Auto-generated method stub
        
        String hqlQuery = "from Book Where id=:id";
        return (Book) sessionFactory.openSession().createQuery(hqlQuery).setParameter("id", id).uniqueResult();
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Book> getBook(String name) {
        // TODO Auto-generated method stub
        
        String hqlQuery = "from Book Where title=:title";
        return (List<Book>) sessionFactory.openSession().createQuery(hqlQuery).setParameter("title", name).list();
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Book> getBooks(String writer, String language, String category, int limit, int offset) {
        // TODO Auto-generated method stub
                
        Book b = new Book();        
        b.setWriter(writer);
        b.setLanguage(language);
        b.setCategory(category);
        
        boolean flag =false;
        
        String hqlQuery = " from Book WHERE ";
        
        if(writer!=null && !writer.isEmpty()) {
            hqlQuery = hqlQuery + "writer=:writer ";
            flag=true;
        }
        
        if(language!=null && !language.isEmpty()) {
            hqlQuery = flag==true?  hqlQuery + "AND language=:language ":hqlQuery + "language=:language";
            flag=true;
        }

        if(category!=null && !category.isEmpty()) {
            hqlQuery = flag==true?  hqlQuery + "AND category=:category ":hqlQuery + "category=:category ";
            flag=true;
        }
        
        hqlQuery = hqlQuery + " Order By id desc";
        
        return (List<Book>) sessionFactory.openSession().createQuery(hqlQuery).setProperties(b)
                .setFirstResult(offset)
                .setMaxResults(limit)
                .list();
        
    }

}

Ниже приведен код StoreDaoImpl

package com.freetests4u.dao.impl;

import org.hibernate.Transaction;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.freetests4u.dao.StoreDao;
import com.freetests4u.dto.StoreAction;
import com.freetests4u.model.Store;

@Component
public class StoreDaoImpl implements StoreDao{

    @Autowired
    SessionFactory sessionFactory;
    
    @Override
    public void updateStore(int bookId, StoreAction action) {
        // TODO Auto-generated method stub
        
        Session session = sessionFactory.openSession();
        
        try {
            String hql ="";
            if(action==StoreAction.INCREMENT) {
            hql = "UPDATE Store s SET s.count=s.count+1";
            }
            else if(action==StoreAction.DECREMENT){
                hql = "UPDATE Store s SET s.count=s.count-1";
            }
            
            int count = session.createQuery(hql).executeUpdate();
            System.out.println("updated count: "+count);
            
        }
        catch(Exception e) {
            e.printStackTrace();
            throw e;
        }
        finally {
            session.close();
        }
    }

    @Override
    public Store getBookCountByBookId(int bookId) {
        // TODO Auto-generated method stub
        
        Session session = sessionFactory.openSession();
        
        try {
            String hql ="from Store where bookId=:bookId";
            
            return (Store) session.createQuery(hql)
                    .setParameter("bookId", bookId)
                    .uniqueResult();
        }
        catch(Exception e) {
            e.printStackTrace();
            throw e;
        }
        finally {
            session.close();
        }
        
    }
    
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void createStoreEntry(int bookId) {
        // TODO Auto-generated method stub
        Session session = sessionFactory.openSession();
//      Transaction tr = session.beginTransaction();
        
          TransactionStatus status=null;
          try {
             status = TransactionAspectSupport.currentTransactionStatus();
          } catch (NoTransactionException e) {
              System.err.println(e);
          }
          System.out.println("from createStoreEntry: "+status!=null? "active transaction": "no transaction");

        
//      try {
            Store store = new Store();
            store.setBookId(bookId);
            store.setBookCount(0);
            store.setDeleted(false);
            session.save(store);
//          tr.commit();
//      }
//      catch(Exception e) {
//          e.printStackTrace();
//          throw e;
//      }
//      finally {
//          session.close();
//      }
    }
    
}

Ниже приведен код BookServiceImpl

package com.freetests4u.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.transaction.annotation.Transactional;
import com.freetests4u.dao.BookDao;
import com.freetests4u.dao.StoreDao;
import com.freetests4u.dto.BookSearchRequest;
import com.freetests4u.exceptions.DuplicateBookEntryException;
import com.freetests4u.model.Book;
import com.freetests4u.service.BookService;

@Service
public class BookServiceImpl implements BookService{
    
    @Autowired
    BookDao bookDao;
    
    @Autowired
    StoreDao storeDao;

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void addBook(Book b) throws DuplicateBookEntryException {
        // TODO Auto-generated method stub
        
//      List<Book> old = bookDao.getBook(b.getTitle().toLowerCase());
//      if(old.size()>0) {
//          throw new DuplicateBookEntryException("Book with same name already exists");
//      }       
        
//      Sessio  
        
          TransactionStatus status=null;
          try {
             status = TransactionAspectSupport.currentTransactionStatus();
          } catch (NoTransactionException e) {
              System.err.println(e);
          }
          System.out.println(status!=null? "active transaction": "no transaction");

          try {
        storeDao.createStoreEntry(13);
        bookDao.addBook(b);
          }
          catch(Exception e) {
              System.out.println("throwing exception");
              e.printStackTrace();
              throw e;
          }
    }

    @Override
    public Book getBook(int id) {
        // TODO Auto-generated method stub
        return bookDao.getBook(id);
    }

    @Override
    public List<Book> getBooks(int limit, int offset) {
        // TODO Auto-generated method stub
        
        return bookDao.getBookList(limit, offset);
    }

    @Override
    public List<Book> getBook(String name) {
        // TODO Auto-generated method stub
        return bookDao.getBook(name);
    }

    @Override
    public List<Book> getBooks(BookSearchRequest br) {
        // TODO Auto-generated method stub
        return bookDao.getBooks(br.getWriter(), br.getLanguage(), br.getCategory(), br.getLimit(), br.getOffset());
    }

}

Было бы здорово, если бы вы могли указать, что не так в приведенном выше коде. Спасибо

1 Ответ

0 голосов
/ 21 июня 2020

Вы не должны использовать Session session = sessionFactory.openSession(); - это будет создавать новый сеанс каждый раз. Вместо этого вы просто хотите внедрить текущий сеанс и использовать его.

Кстати, я не уверен, нужна ли ваша конфигурация. Обычно автоконфигурация Spring Boot позаботится обо всем, если вы включите правильный стартер (для Spring Boot 2 это spring-boot-starter-data-jpa).

@Component
public class BookDaoImpl implements BookDao {

    @Autowired
    private Session session; // Might want to use JPA EntityManager instead
    
    @Transactional(rollbackFor = MyException.class) // propagation = Propagation.REQUIRED is default anyway
    @Override
    public boolean Book addBook(Book book) {
        // No need to handle transaction manually
        session.save(book);
        return true; // Maybe return something more useful
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...