我有Hibernate缓存或映射错误吗?

时间:2013-04-06 23:15:52

标签: java hibernate spring-data

我正在使用以下框架:

  • Hibernate 4.2.0.Final
  • Spring 3.2.2.RELEASE
  • Spring Data Jpa 1.1.0.RELEASE
  • HSQL 2.2.9
  • TestNG 6.1.1

我有2个实体:

实体A:

@Entity
@Table( name = "A" )
public class A {

    @Id
    @GeneratedValue( strategy = GenerationType.IDENTITY )
    private Long id;

    @OneToMany( fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "a" )
    private Set<B> b = new HashSet<B>();

    //... getter and setter

}

和实体B:

@Entity
@Table( name = "B" )
public class B {

    @Id
    @GeneratedValue( strategy = GenerationType.IDENTITY )
    private Long id;

    @ManyToOne
    @JoinColumn( name = "a_id" )
    private A a;

    //... getter and setter

}

我为他们制作了Spring Data JPAR存储库:

@Repository
public interface ARepository
        extends JpaRepository<A, Long> {

}

@Repository
public interface BRepository
        extends JpaRepository<B, Long> {

}

然后我写了一个测试,看看映射是否有效:

@TransactionConfiguration( defaultRollback = true )
@ContextConfiguration
public class ARepositoryTest
        extends AbstractTransactionalTestNGSpringContextTests {

    @Inject
    @Setter
    private ARepository aRepository;

    @Inject
    @Setter
    private BRepository bRepository;


    @Test( groups = { "integration" } )
    public void testSaveWithFeed() {

        A a = new A();
        aRepository.saveAndFlush( a );

        B b = new B();
        b.setA( a );
        bRepository.saveAndFlush( b );

        A findOne = aRepository.findOne( a.getId() );
        Assert.assertEquals( 1, findOne.getB().size() );

    }
}

但测试失败了:

Hibernate: insert into A (id) values (default)
Hibernate: insert into B (id, a_id) values (default, ?)
FAILED: testSaveWithA
java.lang.AssertionError: expected [1] but found [0]

我不明白为什么。映射中是否缺少某些内容,或者我是否必须清除hibernate缓存? 我看到没有新的select-query所以hibernate正在缓存A对象 - 但它不应该更新从A到Bs缓存的引用吗?

此处的附加信息是我的persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
        http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
    xmlns="http://java.sun.com/xml/ns/persistence">    
    <persistence-unit name="local" transaction-type="RESOURCE_LOCAL" >
    </persistence-unit>
</persistence>

我的ARepositoryTest-context.xml

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:jpa="http://www.springframework.org/schema/data/jpa"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:jdbc="http://www.springframework.org/schema/jdbc"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/data/jpa 
     http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
     http://www.springframework.org/schema/jdbc
 http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd"> 

    <context:annotation-config /> 
    <context:component-scan base-package="my.package"/>

    <import resource="classpath:/SpringBeans.xml"/>

</beans>

和我的SpringBeans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:jpa="http://www.springframework.org/schema/data/jpa"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:jdbc="http://www.springframework.org/schema/jdbc"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/data/jpa 
     http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
     http://www.springframework.org/schema/jdbc
 http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd"> 

    <context:annotation-config /> 

    <jpa:repositories base-package="my.package.dao" />

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
        </property>
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
      <property name="persistenceUnitName" value="local"/>
      <property name="dataSource" ref="dataSource" />
      <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true"/>
                <property name="generateDdl" value="true" />
                <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect"/>
                <property name="database" value="HSQL" />               
            </bean>
        </property>
        <property name="jpaPropertyMap">
            <map>
                <entry key="hibernate.show_sql" value="true" />
            </map>
        </property>
    </bean>

    <bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
        <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
        <property name="url" value="jdbc:hsqldb:file:${user.home}/data;shutdown=true" />
        <property name="username" value="sa" />
        <property name="password" value="" />
    </bean>

    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/> 

</beans>

编辑:添加版本号和xml文件


编辑:为JB Nizet回答 如果我加入A类

public void addB( B b ) {

    this.b.add( b );
}

并调整测试用例

@Test( groups = { "integration" } )
public void testSaveWithA() {

    A a = new A();
    aRepository.save( a );

    B b = new B();
    a.addB( b );
    aRepository.saveAndFlush( a );

    A findOne = aRepository.findOne( a.getId() );
    Assert.assertEquals( findOne.getB().size(), 1 );

}
测试通过了 但是在hsql文件中,A和B之间没有链接:

INSERT INTO A VALUES(1)
INSERT INTO B VALUES(1,NULL)

因此,它无法在其他交易中正确选择它。

如果我在A中扩展新的mMethod

public void addB( B b ) {

    this.b.add( b );
    b.setA( this );
}

刷新或提交时发生异常

java.lang.StackOverflowError
    at java.util.HashMap$KeyIterator.<init>(HashMap.java:926)
    at java.util.HashMap$KeyIterator.<init>(HashMap.java:926)
    at java.util.HashMap.newKeyIterator(HashMap.java:940)
    at java.util.HashMap$KeySet.iterator(HashMap.java:974)
    at java.util.HashSet.iterator(HashSet.java:170)
    at java.util.AbstractSet.hashCode(AbstractSet.java:122)
    at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:429)
    at my.package.A.hashCode(A.java:17)
    at my.package.B.hashCode(B.java:13)
    at java.util.AbstractSet.hashCode(AbstractSet.java:126)
    at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:429)
    at my.package.A.hashCode(A.java:17)
    at my.package.B.hashCode(B.java:13)
    ...

Linenumbers A.java:17和B.java:13是放置龙目岛的@Data注释产生我的getter和setter的地方。

通过使用Lomboks @EqualsAndHashCode(exclude = {&#34; b&#34;})删除对hashCode和equals /的依赖关系来解决

1 个答案:

答案 0 :(得分:2)

您的测试使用单个会话在单个事务中运行。因此,当您执行aRepository.findOne(a.getId())时,Hibernate会返回已在其第一级缓存中的A。因为你忘了将B添加到A中的B组,所以这个集合仍然是空的。

您有责任保持对象图的一致性。如果您执行b.setA(a),则还应执行a.getBs().add(b)。最好的方法是将这两个操作封装到A中的方法addB(B b)中,该方法将B添加到集合中并初始化B.a