【问题标题】:Hibernate - Setting null in entity collection is automatically persisted at transaction commitHibernate - 在实体集合中设置 null 会在事务提交时自动持久化
【发布时间】:2015-06-19 09:53:10
【问题描述】:

由于架构要求,我们不能将 Hibernate 实体用作 DTO,因此我们使用 Dozer 将这些实体转换为 POJO。

我们的典型服务如下所示:

@Transactional(readOnly=true)
@Override
public Task loadTask(int taskId){
    TaskEntity taskE = taskDAO.load(taskId);
    if (taskE != null){
        taskE.setAttachments(null)
        Task task = objectMapper.convert(taskE, Task.class);
        return task;
    }else{
        return null;
    }
}

如您所见,在将 TaskEntity 转换为 Task 之前,我们将附件设置为 null。这是因为附件是一个惰性集合,我们不想不必要地触发这些实体的加载。

在更新到 Spring 4.1.1 之前,这没有任何问题。然而,最近我们从 3.2.7 升级了 Spring,将 Hibernate 留在了 3.6.10。然后,在执行同样的代码时,我们注意到 Hibernate 在 loadTask 执行之后执行了这条语句:

update TaskAttachment set taskId = NULL where id= ?

也就是说,由于taskEntity.attachments中设置了null,Hibernate删除了TaskAttachment中的外键。

配置属性: spring.transactionManager_class=org.springframework.transaction.jta.WebSphereUowTransactionManager hibernate.transaction.manager_lookup_class=org.hibernate.transaction.WebSphereExtendedJTATransactionLookup hibernate.current_session_context_class=jta hibernate.transaction.factory_class=org.hibernate.transaction.JTATransactionFactory jta.UserTransaction=java:comp/UserTransaction

会话工厂配置

<bean id="mainSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="jtaTransactionManager" ref="jtaTransactionManager" />
    <property name="dataSource" ref="mainDataSource" />
    <property name="packagesToScan" ref="packages-mainSessionFactory" />
    <property name="hibernateProperties" ref="properties-mainSessionFactory" />
</bean>

我们唯一更改的与 ORM 相关的事情是我们停止使用 HibernateTemplate 以支持 SessionFactory.getCurrentSession()。

我们以前的 BaseDAO:

public abstract class BaseDAO<EntityType extends BaseEntity<IdType>, IdType extends Serializable> extends HibernateDaoSupport

    public BaseDAO(HibernateTemplate hibernateTemplate, Class<EntityType> clazz){
        super();
        super.setHibernateTemplate(hibernateTemplate);
        this.clazz= clazz;
    }

    public EntityType load(IdType id){
        return getHibernateTemplate().get(clazz, id);
    }

我们当前的 BaseDAO:

@SuppressWarnings("unchecked")
public abstract class BaseDAO<EntityType extends BaseEntity<IdType>, IdType extends Serializable> implements IBaseDAO<EntityType, IdType>{

    private Class<EntityType> clazz;

    private SessionFactory sessionFactory;

    public BaseDAO(SessionFactory sessionFactory, Class<EntityType> clazz){
        super();
        this.clazz= clazz;
        this.sessionFactory=sessionFactory;
    }   

    public EntityType load(IdType id){
        return (EntityType)getSession().get(clazz, id);
    }

    protected Session getSession(){
        return sessionFactory.getCurrentSession();
    }

更新:这不是 Spring 版本相关的问题。我已经检查过使用 HibernateTemplate.get() 它不会持久化 null,而使用 SessionFactory.getCurrentSession().get() 它会持久化,为什么? p>

【问题讨论】:

  • 您遇到的问题是什么?
  • 对不起,我在整理之前不小心发了,给我10分钟
  • 这是我在升级之前所期望的。您正在更改事务中的托管实体。这意味着休眠将跟踪所有更改,并在提交/结束时将它们保存在数据库中。如果从另一个@Transactional 方法调用此方法,至少会出现这种情况。 (能否也添加从哪个版本升级到哪个版本)?
  • 而您更新了框架,还是您还更改了配置、类等?
  • 这当然是有道理的,让我感兴趣的是为什么它之前没有坚持这些操作,而现在却坚持了...... ???

标签: java spring hibernate dozer


【解决方案1】:

@Transactional(readOnly=true) 告诉 Spring 该操作不会修改数据库,在这种情况下,它将连接设置为只读并且 Hibernate 不会更新实体。如果您删除readOnly=true,您将看到即使使用HibernateTemplate.get(),更改也会保留。

如果您使用 SessionFactory.getCurrentSession(),您将绕过 Spring 的初始化部分,该部分将会话设置为只读,因此更改会保持不变。

然而,依靠readOnly=true 来禁止更新并不是一个好习惯,因为它不一定得到所有 DB 和 ORM 的支持。最好的做法是使用Session.evict() 分离实体。无论如何,请保留readOnly=true,因为如果 DB/ORM 支持它,那么可以针对只读操作优化 DB 访问。

【讨论】:

  • 你就是那个男人!删除readOnly=true set-null 行为也发生在 HibernateTemplate 中。您建议做什么,在 BaseDAO.load() 方法中使用 Session.evict(),在服务方法中...
  • 我的建议是保留readOnly=true,因为它为事务提供了其他优化,但在调用load() 之后立即在loadTask() 中使用Session.evict()(不在load() 内部,因为可能您将其用于其他目的)。除非你真的需要它,否则也恢复到HibernateTemplate.get() 而不是getCurrentSession(),尽管evict() 两种用法都应该工作。
【解决方案2】:

不要将您的集合设置为 null...更改实体中的级联选项,以便在保存父级时不会保存子级。

【讨论】:

  • 他们将集合设置为 null 以防止 Dozer 在将值传输到 DTO 时深入到集合实体中,这将触发延迟加载。更改级联类型对此无济于事。
猜你喜欢
  • 2010-12-30
  • 1970-01-01
  • 2012-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-29
  • 2015-11-15
  • 2020-09-18
相关资源
最近更新 更多