Spring Hibernate 集成测试返回意外结果
我在我的应用程序中使用 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>
请求的示例代码:
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();
}
}
该问题是否与 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
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, dataSource);
}
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
你是如何设置数据的?通过直接SQL插入?从文件加载?或者,使用 Hibernate(在 setup() 等中)?
如果您通过 Hibernate 设置数据,设置后是否刷新?或者,您是否在设置过程中设置了 f.parent 的引用,即使您已经在父对象中设置了后代列表? Hibernate 的一级缓存可能会保留您通过 Hibernate 创建的空父 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.