先说说事情经过吧。
在进行Spring与Hibernate的整合的时候出现了一个奇怪的现象,当实体类主键类型为GenerationType.IDENTITY时,使用Spring的事物在方法中抛出异常的时候无法回滚已经保存的数据,当使用非GenerationType.IDENTITY的时候可以进行回滚,很神奇(最后发现自己还是要好好再学习呀)。
先来原来的代码吧:
Spring的原来的Java配置类
@Configuration
@ComponentScan(basePackages = {"cn.sharek.test"})//进行自动装配
@EnableTransactionManagement
public class SpringRootConfig {
/**
* 连接池
*/
@Bean
public ComboPooledDataSource dataSource() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/coqup");
dataSource.setUser("root");
dataSource.setPassword("123456");
dataSource.setMaxPoolSize(40);
dataSource.setMinPoolSize(5);
dataSource.setMaxStatements(100);
dataSource.setAcquireIncrement(5);
dataSource.setAutoCommitOnClose(true);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return dataSource;
}
/**
* Session工厂
*/
@Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource) {
//创建SessionFactory工厂
LocalSessionFactoryBean sfb = new LocalSessionFactoryBean();
sfb.setDataSource(dataSource);
sfb.setPackagesToScan("cn.sharek.bsg.coq.model.database");
//创建属性配置
Properties properties = new Properties();
//设置数据库方言
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");//原来的
// properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
//每次运行时重新建表
properties.setProperty("hibernate.hbm2ddl.auto", "create");
//显示SQL语句
// properties.setProperty("hibernate.show_sql", "true");
//格式化显示的SQL的语句
// properties.setProperty("hibernate.format_sql", "true");
sfb.setHibernateProperties(properties);
return sfb;
}
/**
* Hibernate事物管理
*/
@Bean
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory);
return transactionManager;
}
/**
* 配置异常转换类
*
* @return
*/
@Bean
public BeanPostProcessor persistenceTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
实体类
@Entity
public class Test implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String descr;
@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o, "id");
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this, "id");
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDescr() {
return descr;
}
public void setDescr(String descr) {
this.descr = descr;
}
}
Service业务
@Service//注解为业务层
@Transactional
public class ConductHeatNumFileServiceImpl implements ConductFileService {
//数据库DML操作
private DMLDao dmlDao;
/**
* 注入数据库DML操作实现
*
* @param dmlDao
*/
@Autowired
public void setDmlDao(DMLDao dmlDao) {
this.dmlDao = dmlDao;
}
@Override
public void addEntitesByFile(File file) {
for(int i =0;i<10;i++){
Test test = new Test();
test.setDescr(""+i);
dmlDao.save(test);
System.out.println(test.getId());
}
}
}
数据库操作
@Repository
public class DMLDaoImpl implements DMLDao {
private SessionFactory sessionFactory;
public DMLDaoImpl(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* 保存对象
*
* @param entity 实体对象
*/
@Override
public void save(Object entity) throws DataAccessException {
Session session = getSession();
session.save(entity);
}
/**
* 修改对象
*
* @param entity 实体对象
*/
@Override
public void update(Object entity) throws DataAccessException {
Session session = getSession();
session.update(entity);
}
/**
* 删除对象
*
* @param entity 实体对象
*/
@Override
public void delete(Object entity) throws DataAccessException {
Session session = getSession();
session.delete(entity);
}
@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* 通过注入的SessionFactory工厂获取线程中的session;
*
* @return 当前线程中的Session
*/
private Session getSession() {
return sessionFactory.getCurrentSession();
}
}
先说说为什么会出现那种情况。因为通过Hibernate的创建表的时候数据库表的Engine属性为MyISAM,在该类型下每一条SQL语句即是一个事物。所以当你在保存主键类型为GenerationType.IDENTITY的实体类型数据的时候会立即执行SQL语句因此就会导致数据立即保存在数据库当中而无法通过Spring的事物管理进行数据的回退操作。
解决办法也是很简单,将Hibernate的方言设置为org.hibernate.dialect.MySQL5InnoDBDialect则数据库在创建表的时候将表的Engine属性设置为InnoDB,即可消除这种错误。
新的Spring Java配置类
@Configuration
@ComponentScan(basePackages = {"cn.sharek.test"})//进行自动装配
@EnableTransactionManagement
public class SpringRootConfig {
/**
* 连接池
*/
@Bean
public ComboPooledDataSource dataSource() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/coqup");
dataSource.setUser("root");
dataSource.setPassword("123456");
dataSource.setMaxPoolSize(40);
dataSource.setMinPoolSize(5);
dataSource.setMaxStatements(100);
dataSource.setAcquireIncrement(5);
dataSource.setAutoCommitOnClose(true);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return dataSource;
}
/**
* Session工厂
*/
@Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource) {
//创建SessionFactory工厂
LocalSessionFactoryBean sfb = new LocalSessionFactoryBean();
sfb.setDataSource(dataSource);
sfb.setPackagesToScan("cn.sharek.bsg.coq.model.database");
//创建属性配置
Properties properties = new Properties();
//设置数据库方言
// properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");//原来的
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
//每次运行时重新建表
properties.setProperty("hibernate.hbm2ddl.auto", "create");
//显示SQL语句
// properties.setProperty("hibernate.show_sql", "true");
//格式化显示的SQL的语句
// properties.setProperty("hibernate.format_sql", "true");
sfb.setHibernateProperties(properties);
return sfb;
}
/**
* Hibernate事物管理
*/
@Bean
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory);
return transactionManager;
}
/**
* 配置异常转换类
*
* @return
*/
@Bean
public BeanPostProcessor persistenceTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}