Интеграционный тест Spring Hibernate возвращает неожиданный результат - PullRequest
1 голос
/ 13 июля 2011

Я использую 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 (DataSource);

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());

    }

    ...

... Затем я получаю следующее сообщение для всех, НО первый метод тестирования:

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

1 Ответ

0 голосов
/ 13 июля 2011

Как вы настроили данные? Путем прямой вставки SQL? Загружен из файлов? Или, используя Hibernate (в setup () и т. Д.)?

Если вы настраиваете данные с помощью Hibernate, сбрасывали ли вы после установки? Или вы установили ссылку на f.parent во время вашей установки, даже если вы установили переменную lowerndantList в родительском объекте obj? Вполне возможно, что кэш первого уровня Hibernate хранит тот пустой родительский экземпляр Class3, который вы создали с помощью Hibernate, и возвращает его вам.

...