Hibernate事物的一次填坑之旅

先说说事情经过吧。

在进行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();
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,827评论 19 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 47,118评论 6 342
  • Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库...
    兰缘小妖阅读 4,937评论 1 18
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 32,350评论 18 399
  • 晚饭过后,给宝贝批作业,发现刚学的方向的知识,还是掌握不好,(昨天晚上就针对性的讲过了)今天还是犯同样的...
    杜欣阳妈妈阅读 1,090评论 0 0

友情链接更多精彩内容