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

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()的区别



猜你喜欢

转载自blog.csdn.net/qq_35923749/article/details/79369421