Spring Hibernate integration test returns unexpected result
I am using Maven, Hibernate and Spring in my application. I have implemented entity classes, DAO classes and service classes in packages of their own. I have problem when testing a service. When unit testing a DAO method that this specific service is calling the result is expected. But when testing a service method that is using this DAO method I don't get the same result. I think this problem is related to integration. Service is annoted with @Transactional. Data source and service are annotated with @Autowired in testing class of service. I attached context configuration of the service testing class.
<?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>
Sample code as requested:
Entities
@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();
}
}
Could the problem be related to the complexity of the query of the method getPublicByPrimaryKey? But if the method is working fine during unit tests why it is working differently while running integration tests?
Test data:
<?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" />
Loading of test data: Connection connection = 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, dataSourc开发者_JAVA百科e);
}
If I edit the file loading method like this...
...
ListIterator<String> dbunitFilePathsIterator = dbunitFilePaths.listIterator();
FlatXmlDataSetBuilder flatXmlDataSetBuilder;
try {
...
xml = new ClassPathResource(dbunitFilePath);
flatXmlDataSetBuilder = new FlatXmlDataSetBuilder().setColumnSensing(true);
dataSets[i] = flatXmlDataSetBuilder.build(xml.getInputStream());
}
...
...I then get the following message for the all BUT the first test method:
java.sql.SQLException: Integrity constraint violation FK_CLASS3_TO_PARENT table: CLASS3
How did you setup the data? By direct SQL insert? Loaded from files? Or, using Hibernate (in setup() etc )?
If you are setting up the data by Hibernate, did you flush after setup? Or, have you set the reference of f.parent during your setup, even you have setup the descendantList in the parent obj? It is possible that Hibernate's first level cache is keeping that empty-parent Class3 instance which you created through Hibernate and return to you.
精彩评论