hibernate的错误 different object with the same identifier value was already associated with the session

本文详细解析了在使用Hibernate框架时遇到的主键冲突问题,特别是当保存具有自增长主键的Teacher对象时出现的不同对象拥有相同标识符值的异常。通过对Hibernate事务机制的理解,分析了问题产生的原因,并给出了具体的解决方案。

hibernate的错误 different object with the same identifier value was already associated with the session

1、今天在service层,循环保存导入的教室信息即Teacher对象,

然后调用Dao层的保存方法saveTeachers(Teacher teacher),用hibernate的

sessionFactory.getCurrentSession().save(teacher);
org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session 

意思大概是说主键不唯一,即在事务的最后执行数据永久化时,session缓存里面有多个(>1)主键一样的对象。但,Teacher实体类对应的teacher表的id是自增长的主键,为什么还会有重复同一标识呢?

2、通过查找资料,慢慢理解hibernate的事务了,事务流程大概是这样的

 

      应用使用session.save()保存对象,这个时候Session将这个对象放入entityEntries,用来标记对象已经和当前的会话建立了关联,由于应用对对对象做了保存的操作,Session还要在insertions中登记应用的这个插入行为(行为包括:对象引用、对象id、Session、持久化处理类)。即,其实调用session.save(class)之后,hibernate并不会立即提交数据库,而是先将要保存,更新,删除放进了缓存中(为什么要这样做呢?我想应该是,一方面数据永久化,实际上是将数据保存到硬盘等存储介质,学过操作系统的人都知道,外存的读取和存放的消耗是主存的几个数量级别的消耗,先放到缓存中,然后一次性写入到存储介质,减少了读写消耗;另一方面,应该是为了高效率实现事务滚回,众所皆知,事务具有原子性,要么都成功完成,要么都失败,那么如果一个事务要调用多个dao层实现数据的增删改查,如果hibernate是一条修改语句就立刻修改数据库的数据,一条删除语句就删除了,那如果删除语句删除失败,那又要去硬盘修改回去数据,那么这样会大大增大cpu的资源消耗,但如果这些一开始是放到缓存中进行标记,最后如果事务完成,则自动将缓存标记的操作写入到数据库,这样就能提高资源利用率了,这就是hibernate自带的一级缓存功能),等整个事务操作完成后,事务提示,需要将所有缓存flush入数据库,Session启动一个事务,并按照insert ,update,...,delete的顺序提交所有之前登记的操作(注意:所有insert执行完毕后才会执行update,这里的特殊处理也可能会将你的程序搞得一团遭,如需要控制操作的顺序,需要使用flush)

 

3、解释完事务流程,那么回答开始提出的问题,Teacher实体类对应的teacher表的id是自增长的主键,为什么还会有重复同一标识呢?因为每次调用sessionFactory.getCurrentSession().save(teacher)的时候,hibernate吧teacher实例对象保存到了缓存中,那么第二次循环保存第二个teacher实例时,因为在数据库设置的teacher主键是自增长的id,所以save时,我并没有给这两个teacher实例的id赋值,那么这两个实例在缓存中就没有唯一标识,这样就导致最后hibernate想要缓存中的实例写入到数据库时,没法区分这些实例了,也就报了这个错,我想,teacher表的自增长是只有在写入到数据库的时候,数据库会自动给赋值。

4、最后解决这个问题的方式是,给Teacher的实体类的主键加一个注解@GeneratedValue(strategy = GenerationType.IDENTITY)  -->用IDENTITY告诉hibernate这个主键是自增长型,在调用save()的时候就在缓存中自动赋值

 
    @Id
    @Column(name = "id", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public int getId() {
        return id;
    }

@GeneratedValue的使用方式是

 

JPA通用策略生成器 
通过annotation来映射hibernate实体的,基于annotation的hibernate主键标识为@Id, 
其生成规则由@GeneratedValue设定的.这里的@id和@GeneratedValue都是JPA的标准用法, 
JPA提供四种标准用法,
JPA提供的四种标准用法为TABLE,SEQUENCE,IDENTITY,AUTO. 
TABLE:使用一个特定的数据库表格来保存主键。 
SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。 
IDENTITY:主键由数据库自动生成(主要是自动增长型) 
AUTO:主键由程序控制
 

总结,一开始以为是跟网上的那种update的报错一样,使用session.evict(),session.save(),然后用session.flush(),的方法,但没用,因为我的问题的根本原因是没有主键,而update是因为实例的更换了,具体原因自己去看他们的博客。

解决问题的过程中,特别要感谢这几篇博客

1、session和getCurrentSession,openSession的区别

2、update()出现这个问题的原因分析

3、session.clear()和session.evict()的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值