Я работаю над книжным магазином личного проекта 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());
}
}
Было бы здорово, если бы вы могли указать, что не так в приведенном выше коде. Спасибо