Spring Hibernate集成测试返回意外结果

时间:2011-07-13 08:36:40

标签: java hibernate spring integration-testing dbunit

我在我的应用程序中使用Maven,Hibernate和Spring。我已经在自己的包中实现了实体类,DAO类和服务类。测试服务时遇到问题。当单元测试此特定服务正在调用的DAO方法时,预期结果。但是在测试使用此DAO方法的服务方法时,我得不到相同的结果。我认为这个问题与整合有关。服务使用@Transactional进行注释。数据源和服务在测试服务类中使用@Autowired进行注释。我附上了服务测试类的上下文配置。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:jee="http://www.springframework.org/schema/jee"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
    http://www.springframework.org/schema/jee 
    http://www.springframework.org/schema/jee/spring-jee-2.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-2.5.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

    <bean id="class1Dao" 
        class="org.project.datalayer.implementation.Class1DaoImplementation">
    </bean>

    <bean id="service" 
        class="org.project.services.implementation.Service1Implementation">
        <property name="class1Dao">
            <ref bean="class1Dao" />
        </property>
    </bean>    

    <bean id="dataSource"
        class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName"
          value="org.hsqldb.jdbcDriver" />
        <property name="url" value="jdbc:hsqldb:mem:project" />
        <property name="username" value="username" />
        <property name="password" value="password" />
    </bean>

  <bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter">
      <bean
        class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
  </bean>

  <context:annotation-config />

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

  <tx:annotation-driven />

</beans>

请求的示例代码:

实体

@MappedSuperclass
public abstract class ClassBase implements Serializable {
      @Id()
      @GeneratedValue(strategy = GenerationType.AUTO)
      @Column(name = "id")
      private Integer id;

      @Version
      private int version =  0;

      private Date created;

      private Date updated;

      /**
       * @return the id
       */
      public Integer getId() {
            return id;
      }

      @PrePersist
      @Column(name = "created", nullable = false)
      protected void onCreate() {
            this.created = new Date();
      }

      @PreUpdate
      @Column(name = "updated", nullable = false)
      protected void onUpdate() {
            this.updated = new Date();
      }

      /**
       * @return the version
       */
      public int getVersion() {
            return this.version;
      }

      /**
       * @param version the version to set
       */
      public void setVersion(int version) {
            this.version = version;
      }

      /**
       * @return the created
       */
      public Date getCreated() {
            return this.created;
      }

      /**
       * @return the updated
       */
      public Date getUpdated() {
            return this.updated;
      }
}

@Entity
@Table(name = "class1")
class Class1 extends ClassBase {
  @ManyToOne(cascade = CascadeType.ALL)
  @JoinColumn(name = "class1_id")
  private List<Class2> class2List = new ArrayList<Class2>();

  @OneToMany(mappedBy = "class1", cascade = CascadeType.ALL)
  private List<Class1Class4> class1Class4List = new ArrayList<Class1Class4>();

}

@Entity
@Table(name = "class1_class4")
class Class1Class4 extends ClassBase {
  @ManyToOne(optional = false, cascade = CascadeType.ALL)
  @JoinColumn(name="class4_id")
  private Class4 class4;

  @ManyToOne(optional = false, cascade = CascadeType.ALL)
  @JoinColumn(name="class1_id")
  private Class1 class1;

}

@Entity
@Table(name = "class2")
class Class2 extends ClassBase {
  @OneToMany(mappedBy = "class1", cascade = CascadeType.ALL)
  private Class1 class1;

  @OneToMany(mappedBy = "class2", cascade = CascadeType.ALL)
  private List<Class2Class3> class2Class3List = new ArrayList<Class2Class3>();
}

@Entity
@Table(name = "class2_class3")
class Class2Class3 extends ClassBase {
  @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "class2_id")
  private Class2 class2;

  @ManyToOne(cascade = CascadeType.ALL)
  @JoinColumn(name = "class3_id")
  private Class3 class3;    
}

@Entity
@Table(name = "class3")
class Class3 extends ClassBase {
  @OneToMany(mappedBy = "class3", cascade = CascadeType.ALL)
  private List<Class2Class3> class2Class3List = new ArrayList<Class2Class3>();

  @OneToOne(cascade = CascadeType.ALL)
      @JoinColumns({
        @JoinColumn(name = "class4_id",
            referencedColumnName = "id")
      })
  private Class4 class4;

  @ManyToOne(cascade = CascadeType.ALL)
  @ForeignKey(name = "fk_class3_to_parent", inverseName = "fk_parent_to_class3")
  private Class3 parent;

  @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
  private Set<Class3> descendantList = new HashSet<Class3>();

  ...
}

@Entity
@Table(name = "class4")
class Class4 extends ClassBase {
  @OneToOne(mappedBy="class4")
  private Class3 class3;

  @OneToMany(mappedBy = "class4", cascade = CascadeType.ALL)
  private List<Class1Class4> class1Class4List = new ArrayList<Class1Class4>();
}

DAO

public class Class1DaoImplementation extends BaseDaoTemplate<Class1> implements Class1Dao {
    public Class1DaoImplementation() {
        super(Class1.class);
    }       

    public Class getPublicByPrimaryKey(Integer id) {
        EntityManager em = getEntityManager();
                Query q = 
                    em.createQuery("SELECT distinct(p) FROM Class1 p"
                        + " INNER JOIN p.class5 ps"
                        + " INNER JOIN FETCH p.class2 it"
                        + " INNER JOIN FETCH it.class2Class3List itfs"                      
                        + " INNER JOIN FETCH itfs.class3 f"                 
                        + " LEFT JOIN FETCH f.parent fp" // PROBLEM: null when running integration test
                        + " LEFT JOIN FETCH f.descendants fd"
                        + " WHERE p.id = :class1Id"
                            + " AND ps.id = 5"
                        + " ORDER BY p.id"
                        );
                q.setParameter("class1Id", id);

                return (Class1) q.getSingleResult();
      }
}

问题可能与方法getPublicByPrimaryKey的查询的复杂性有关吗?但是,如果方法在单元测试期间工作正常,为什么在运行集成测试时它的工作方式不同?

测试数据:

<?xml version='1.0' encoding='UTF-8'?>
<dataset>
    <!--ELEMENT CLASS3 EMPTY-->  
    <!--ATTLIST CLASS3  
        PARAM1 CDATA #REQUIRED
        ID CDATA #REQUIRED  
        PARAM2 CDATA #REQUIRED  
        PARENT_ID CDATA #IMPLIED  
    -->  
]> 
<class3 param1="0" id="1" param2="5" />
<class3 param1="0" id="2" param2="5" />
<class3 param1="0" id="3" param2="5" parent_id="1" />
<class3 param1="0" id="4" param2="5" parent_id="1" />
<class3 param1="0" id="5" param2="5" parent_id="2" />

加载测试数据:     连接连接=         DataSourceUtils.getConnection(数据源);

ArrayList<String> dbunitFilePaths = new ArrayList<String>();
dbunitFilePaths.add("/class3.xml");

ListIterator<String> dbunitFilePathsIterator = dbunitFilePaths.listIterator();

try {

    IDatabaseConnection dbUnitConnection =
        new DatabaseConnection(connection); 

    String dbunitFilePath;
    ClassPathResource xml;
    IDataSet[] dataSets = new IDataSet[dbunitFilePaths.size()];

    for(int i = 0; i < dbunitFilePaths.size(); i++) {
        dbunitFilePath = dbunitFilePathsIterator.next();

        xml = new ClassPathResource(dbunitFilePath);

        dataSets[i] = new FlatXmlDataSetBuilder().build(xml.getInputStream());

    }

    CompositeDataSet compositeDataSet = new CompositeDataSet(dataSets);

    DatabaseOperation.CLEAN_INSERT.execute(dbUnitConnection,
        compositeDataSet);

} finally {
    DataSourceUtils.releaseConnection(connection, dataSource);
}

如果我像这样编辑文件加载方法......

    ...

    ListIterator<String> dbunitFilePathsIterator = dbunitFilePaths.listIterator();
    FlatXmlDataSetBuilder flatXmlDataSetBuilder;

    try {

    ...

    xml = new ClassPathResource(dbunitFilePath);

    flatXmlDataSetBuilder = new FlatXmlDataSetBuilder().setColumnSensing(true);

    dataSets[i] = flatXmlDataSetBuilder.build(xml.getInputStream());

    }

    ...

...然后我得到以下消息,为所有BUT第一个测试方法:

    java.sql.SQLException: Integrity constraint violation FK_CLASS3_TO_PARENT table: CLASS3

1 个答案:

答案 0 :(得分:0)

您是如何设置数据的?通过直接SQL插入?从文件加载?或者,使用Hibernate(在setup()等中)?

如果你是通过Hibernate设置数据,你在安装后是否刷新了?或者,您是否在设置过程中设置了f.parent的引用,即使您已在父obj中设置了descendantList? Hibernate的第一级缓存可能会保留您通过Hibernate创建的空父Class3实例并返回给您。