[英]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.