简体   繁体   English

休眠-实体集合中设置为null会在提交事务时自动保留

[英]Hibernate - Setting null in entity collection is automatically persisted at transaction commit

Due to architecture requirements we can't use our Hibernate entities as DTOs, so we use Dozer to transform those entities to POJOs. 由于体系结构要求,我们不能将Hibernate实体用作DTO,因此我们使用Dozer将这些实体转换为POJO。

Our typical service looks like this: 我们的典型服务如下所示:

@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;
    }
}

As you can see, before transforming TaskEntity to Task we set attachments to null. 如您所见,在将TaskEntity转换为Task之前,我们将附件设置为null。 This is because attachments is a lazy collection and we don't want to unnecessarily trigger the loading of those entities. 这是因为附件是一个惰性集合,并且我们不想不必要地触发这些实体的加载。

Before updating to Spring 4.1.1 this worked without any problem. 在更新到Spring 4.1.1之前,这没有任何问题。 However, recently we upgraded Spring from 3.2.7, leaving Hibernate at 3.6.10. 但是,最近我们从3.2.7升级了Spring,使Hibernate处于3.6.10。 Then, when executing this same code we noticed that Hibernate was executing this statement after loadTask execution: 然后,当执行相同的代码时,我们注意到Hibernate在loadTask执行之后正在执行以下语句:

update TaskAttachment set taskId = NULL where id= ?

That is to say, because of setting null in taskEntity.attachments, Hibernate deletes the foreign key in TaskAttachment. 也就是说,由于在taskEntity.attachments中将其设置为null,因此Hibernate会删除TaskAttachment中的外键。

Configuration Properties : 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 配置属性 :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 = java:comp / UserTransaction

Session Factory Config 会话工厂配置

<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>

The only ORM-related thing that we changed was that we stopped using HibernateTemplate in favour the SessionFactory.getCurrentSession(). 我们更改的唯一与ORM相关的事情是,我们不再使用HibernateTemplate来支持SessionFactory.getCurrentSession()。

Our former BaseDAO: 我们以前的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);
    }

Our current BaseDAO: 我们当前的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();
    }

UPDATE: It's not a Spring version related issue. 更新:这不是与Spring版本相关的问题。 I have checked that using HibernateTemplate.get() it doesn't persist the null, and with SessionFactory.getCurrentSession().get() it does, why? 我已经检查过使用HibernateTemplate.get()不会持久保留null,而使用SessionFactory.getCurrentSession()。get()可以,为什么?

@Transactional(readOnly=true) tells Spring that the operation will not be modifying the DB, in such a case it sets the connection to read-only and Hibernate will not update the entity. @Transactional(readOnly=true)告诉Spring该操作不会修改数据库,在这种情况下,它将连接设置为只读,而Hibernate不会更新该实体。 If you remove the readOnly=true you will see that even using HibernateTemplate.get() the change will be persisted. 如果删除readOnly=true您将看到即使使用HibernateTemplate.get() ,更改也将保留。

If you use SessionFactory.getCurrentSession() you're circumventing Spring's initialization part that sets the session as readonly and therefore the changes get persisted. 如果使用SessionFactory.getCurrentSession()则会避开Spring的初始化部分,该部分将会话设置为只读,因此更改将持久化。

Relying in readOnly=true to inhibit updates however is not a good practice because it is not necessarily supported by all DBs and ORMs. 但是,依靠readOnly=true禁止更新不是一个好习惯,因为并非所有DB和ORM都必须支持它。 The best course of action is using Session.evict() to detach the entity. 最好的做法是使用Session.evict()分离实体。 Anyway keep the readOnly=true because if the DB/ORM supports it then DB access could be optimized for read-only operations. 无论如何,请保持readOnly=true因为如果DB / ORM支持,则可以针对只读操作优化DB访问。

而不是将集合设置为null,而是更改实体中的层叠选项,以便在保存父对象时不保存子对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM