简体   繁体   English

spring-boot hibernate 事务管理器即使在运行时异常时也不回滚事务

[英]spring-boot hibernate transaction manager not rolling back transaction even on runtime exception

I'm working on a spring-boot hibernate JPA personal project book-store.我正在开发 spring-boot hibernate JPA 个人项目书店。 I'm facing a problem that the methods annotated with @Transactional annotation not rolling back transactions even with runtime exceptions.我面临一个问题,即使用 @Transactional 注释注释的方法即使出现运行时异常也不会回滚事务。 I'm using spring-boot version 1.5.1, hibernate version 5, java version 11 & DB is MySQL.我正在使用 spring-boot 版本 1.5.1、hibernate 版本 5、java 版本 11 和 DB1DC4BB673AZ.4AFA4979 I'm generating run time exception by inserting the book with the same title in books table.我通过在书籍表中插入具有相同标题的书籍来生成运行时异常。 In BookServiceImpl's addBook method the store-entry should ideally rollback from the store table when adding the book to books table fails, but, it is currently not happening.在 BookServiceImpl 的 addBook 方法中,理想情况下,商店条目应该在将书籍添加到书籍表失败时从商店表回滚,但目前没有发生。

Below is bean config file code:下面是 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)
//  }
    
}

Below is BooK model file code下面是 Book model 文件代码

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

}

Below is store Model code下面是商店 Model 代码

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


    
}

Below is BookDaoImpl code下面是 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();
        
    }

}

Below is StoreDaoImpl code下面是 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();
//      }
    }
    
}

Below is BookServiceImpl code下面是 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());
    }

}

It will be great if you can point out what's wrong in the above code.如果您能指出上述代码中的问题,那就太好了。 Thanks谢谢

You shouldn't use Session session = sessionFactory.openSession();你不应该使用Session session = sessionFactory.openSession(); - this will create a brand new Session each time. - 这将每次创建一个全新的 Session。 Instead you simply want to inject the current Session and use that.相反,您只需注入当前的 Session 并使用它。

Btw., I'm not sure whether your Configuration is necessary.顺便说一句,我不确定您的配置是否是必要的。 Usually Spring Boot Autoconfiguration takes care of everything if you include the right starter (for Spring Boot 2 that's spring-boot-starter-data-jpa).如果您包含正确的启动器,通常 Spring 启动自动配置会处理所有事情(对于 Spring 启动 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
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM