在不调用save()方法的情况下持久化对象

时间:2014-02-14 12:29:18

标签: java spring hibernate

首先,我从DB获得持久性对象(实例A),然后我值设置为字段。我再次再次调用来自DB的持久对象(实例B)。出乎意料的是,加载的对象(实例B)具有我设置为实例A的值。即使我没有调用任何save()方法。

意外行为背后的原因是什么?我该如何解决?

以下是测试失败:

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( locations = { "/webapp-beans.xml" } )
@Transactional
public class HibernatePersistentTest extends TestCase
{
    @Autowired
    CompanyDAO companyDAO;

    @Test
    public void persisting_setAValueAndOnlyCallGet_fieldValueDoesNotChange()
    {
        // GIVEN
        // persist new Company
        Company company = new Company();
        company.setIsDeleted( YesNoType.NO );
        company.setName( "A" );
        companyDAO.getHibernateTemplate().saveOrUpdate( company );

        // assert "object persisted properly"
        Company companyA = (Company) companyDAO.getHibernateTemplate().get( Company.class, company.getId() );
        assertEquals( "Field value changed", "A", companyA.getName() );

        // WHEN set name B and call get()
        company.setName( "B" );
        Company companyB = (Company) companyDAO.getHibernateTemplate().get( Company.class, company.getId() );

        // THEN name should be still A
        assertEquals( "Field value changed", "A", companyB.getName() );
    }
}

我的休眠配置:

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass"> <value>oracle.jdbc.driver.OracleDriver</value></property>
    <property name="jdbcUrl"><value>${jdbc.url}</value></property>
    <property name="user"><value>${jdbc.encusername}</value></property>
    <property name="password"><value>${jdbc.encpassword}</value></property>
    <property name="initialPoolSize"><value>10</value></property>
    <property name="minPoolSize"><value>10</value></property>
    <property name="maxPoolSize"><value>100</value></property>
    <property name="maxStatements"><value>0</value></property>
    <property name="preferredTestQuery"><value>SELECT 1 FROM DUAL</value></property>
    <property name="idleConnectionTestPeriod"><value>300</value></property>
    <property name="checkoutTimeout"><value>10000</value></property>
</bean>

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
            <prop key="hibernate.use_outer_join">true</prop>
            <prop key="hibernate.jdbc.batch_size">0</prop>
            <prop key="hibernate.jdbc.use_streams_for_binary">true</prop>
            <prop key="hibernate.connnection.charSet">UTF8</prop>
            <prop key="hibernate.connection.defaultNChar">true</prop>
            <prop key="hibernate.bytecode.use_reflection_optimizer">true</prop>
            <prop key="hibernate.transaction.flush_before_completion">true</prop>
            <prop key="hibernate.default_batch_fetch_size">16</prop>
            <prop key="hibernate.cache.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop>
            <prop key="hibernate.cache.use_query_cache">true</prop>
            <prop key="hibernate.show_sql">false</prop>
            <prop key="hibernate.format_sql">false</prop>
            <prop key="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</prop>
            <prop key="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop>
            <prop key="current_session_context_class">thread</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
            <prop key="hibernate.default_schema">${schema}</prop>
        </props>
    </property>
    <property name="packagesToScan">
        <list merge="true">
            <value>com/pegasus/data/entity</value>
        </list>
    </property>
</bean>

2 个答案:

答案 0 :(得分:4)

这是完全预期的行为。

在给定会话中,只存在给定实体的一个实例。它存储在与会话关联的第一级缓存中。您在此会话中执行的任何返回具有相同ID的相同类型实体的查询将返回存储在缓存中的此唯一实体实例。您对此实体所做的任何更改都将自动保持持久性(除非您更改刷新模式)。

我的猜测是你的测试是事务性的,因此测试中的所有内容都在一个会话中执行,因此你看到的行为。

如果您的测试不是事务性的,并且每个DAO调用都是在新事务中进行的,那么您应该会看到您期望的行为。

答案 1 :(得分:0)

这种行为就像hibernate的行为一样:两个get() - 使用相同id的调用应返回相同的对象 - 实例 - 因此之前的更改仍将反映在该实例中。

JPA规范要求此行为:

  

其主键的值唯一标识实体实例   在持久化上下文和EntityManager操作中   在第3章“实体操作”中描述。