我的话:
这几天在做一个小项目,我负责先实现底层的DAO与Service,用的是Spring+Hibernate,使用HibernateTemplate实现简单的增删改查功能。按理说应该很简单,但是我用JUnit测试的时候却发现很诡异的问题:1、save方法发出了sql,但数据库却没有相应记录;2、load方法获取不到对象,只有一个代理,调用任何属性就会说LazyInitialization
我们知道所谓的懒加载的原因是session被关闭了。在web环境下我们可以在web.xml里面加入OpenSessionInView的过滤器,那在junit下应该怎么办呢?
最后在网上找到了一篇文章,里面阐述了如何在各种层面上使用lazy。根据其最后一部分的指导,终于在junit里面把我的service跑通了,开心~~
全文摘录如下,原文地址是:http://www.jroller.com/kbaum/entry/orm_lazy_initialization_with_dao。如果上国外网比较慢,也可以看这里的转载:http://blog.csdn.net/wczwcg/archive/2008/11/01/3202535.aspx
Lazy Initialization and the DAO pattern with Hibernate and Spring
10:42PM Apr 06, 2005 in category Java by Karl Baum
Hibernate and Lazy Initialization
Hibernate object relational mapping offers both lazy and non-lazy modes of object initialization. Non-lazy initialization retrieves an object and all of its related objects at load time. This can result in hundreds if not thousands of select statements when retrieving one entity. The problem is compounded when bi-directional relationships are used, often causing entire databases to be loaded during the initial request. Of course one could tediously examine each object relationship and manually remove those most costly, but in the end, we may be losing the ease of use benefit sought in using the ORM tool.
The obvious solution is to employ the lazy loading mechanism provided by hibernate. This initialization strategy only loads an object's one-to-many and many-to-many relationships when these fields are accessed. The scenario is practically transparent to the developer and a minimum amount of database requests are made, resulting in major performance gains. One drawback to this technique is that lazy loading requires the Hibernate session to remain open while the data object is in use. This causes a major problem when trying to abstract the persistence layer via the Data Access Object pattern. In order to fully abstract the persistence mechanism, all database logic, including opening and closing sessions, must not be performed in the application layer. Most often, this logic is concealed behind the DAO implementation classes which implement interface stubs. The quick and dirty solution is to forget the DAO pattern and include database connection logic in the application layer. This works for small applications but in large systems this can prove to be a major design flaw, hindering application extensibility.
Being Lazy in the Web Layer
Fortunately for us, the Spring Framework has developed an out of box web solution for using the DAO pattern in combination with Hibernate lazy loading. For anyone not familiar with using the Spring Framework in combination with Hibernate, I will not go into the details here, but I encourage you to read Hibernate Data Access with the Spring Framework. In the case of a web application, Spring comes with both the OpenSessionInViewFilter and the OpenSessionInViewInterceptor. One can use either one interchangeably as both serve the same function. The only difference between the two is the interceptor runs within the Spring container and is configured within the web application context while the Filter runs in front of Spring and is configured within the web.xml. Regardless of which one is used, they both open the hibernate session during the request binding this session to the current thread. Once bound to the thread, the open hibernate session can transparently be used within the DAO implementation classes. The session will remain open for the view allowing lazy access to the database value objects. Once the view logic is complete, the hibernate session is closed either in the Filter doFilter method or the Interceptor postHandle method. Below is an example of the configuration of each component:
Interceptor Configuration
<beans>
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="openSessionInViewInterceptor"/>
</list>
</property>
<property name="mappings">
...
</bean>
...
<bean name="openSessionInViewInterceptor"
class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>
</beans>
Filter Configuration
<web-app>
...
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate.support.OpenSessionInViewFilter
</filter-class>
</filter>
...
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*.spring</url-pattern>
</filter-mapping>
...
</web-app>
Implementing the Hibernate DAO's to use the open session is simple. In fact, if you are already using the Spring Framework to implement your Hibernate DAO's, most likely you will not have to change a thing. The DAO's must access Hibernate through the convenient HibernateTemplate utility, which makes database access a piece of cake. Below is an example DAO.
Example DAO
public class HibernateProductDAO extends HibernateDaoSupport implements ProductDAO {
public Product getProduct(Integer productId) {
return (Product)getHibernateTemplate().load(Product.class, productId);
}
public Integer saveProduct(Product product) {
return (Integer) getHibernateTemplate().save(product);
}
public void updateProduct(Product product) {
getHibernateTemplate().update(product);
}
}
Being Lazy in the Business Layer
Even outside the view, the Spring Framework makes it easy to use lazy load initialization, through the AOP interceptor HibernateInterceptor. The hibernate interceptor transparently intercepts calls to any business object configured in the Spring application context, opening a hibernate session before the call, and closing the session afterward. Let's run through a quick example. Suppose we have an interface BusinessObject:
public interface BusinessObject {
public void doSomethingThatInvolvesDaos();
}
The class BusinessObjectImpl implements BusinessObject:
public class BusinessObjectImpl implements BusinessObject {
public void doSomethingThatInvolvesDaos() {
// lots of logic that calls
// DAO classes Which access
// data objects lazily
}
}
Through some configurations in the Spring application context, we can instruct the HibernateInterceptor to intercept calls to the BusinessObjectImpl allowing it's methods to lazily access data objects. Take a look at the fragment below:
<beans>
<bean id="hibernateInterceptor" class="org.springframework.orm.hibernate.HibernateInterceptor">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="businessObjectTarget" class="com.acompany.BusinessObjectImpl">
<property name="someDAO"><ref bean="someDAO"/></property>
</bean>
<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"><ref bean="businessObjectTarget"/></property>
<property name="proxyInterfaces">
<value>com.acompany.BusinessObject</value>
</property>
<property name="interceptorNames">
<list>
<value>hibernateInterceptor</value>
</list>
</property>
</bean>
</beans>
When the businessObject bean is referenced, the HibernateInterceptor opens a hibernate session and passes the call onto the BusinessObjectImpl. When the BusinessObjectImpl has finished executing, the HibernateInterceptor transparently closes the session. The application code has no knowledge of any persistence logic, yet it is still able to lazily access data objects.
Being Lazy in your Unit Tests
Last but not least, we'll need the ability to test our lazy application from J-Unit. This is easily done by overriding the setUp and tearDown methods of the TestCase class. I prefer to keep this code in a convenient abstract TestCase class for all of my tests to extend.
public abstract class MyLazyTestCase extends TestCase {
public void setUp() throws Exception {
super.setUp();
SessionFactory sessionFactory = (SessionFactory) getBean("sessionFactory");
Session s = sessionFactory.openSession();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));
}
protected Object getBean(String beanName) {
//Code to get objects from Spring application context
}
public void tearDown() throws Exception {
super.tearDown();
SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
Session s = holder.getSession();
s.flush();
TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory);
}
}
分享到:
相关推荐
标题 "JUnit 报 initializationError" 描述了一个在使用 JUnit 测试框架时常见的问题,即在执行测试时遇到 `initializationError`。这通常意味着在测试初始化阶段出现了错误,可能是由于测试类的静态初始化块、构造...
在Java编程环境中,JUnit是广泛使用的单元测试框架,它使得...对于hamcrest库的缺失,简单地下载并导入相应的jar包即可解决问题。同时,了解每个依赖的作用和使用方法,可以帮助我们更好地理解和调试可能出现的错误。
NULL 博文链接:https://yuhuiblog695685688425687986842568269.iteye.com/blog/2414402
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage,包含依赖包:junit-jupiter-5.5.1.jar,junit-jupiter-engine-5.5.1.jar,junit-jupiter-params-5.5.1.jar,junit-platform-launcher-1.5.1.jar,junit-...
org.junit.internal.runners.InitializationError.class org.junit.internal.runners.JUnit38ClassRunner.class org.junit.internal.runners.JUnit4ClassRunner.class org.junit.internal.runners.MethodRoadie....
这个名为"junit5.jar"的文件正是JUnit 5的核心库,它包含了执行单元测试所需的所有核心组件,包括JUnit Platform、JUnit Jupiter和JUnit Vintage。本文将全面解析JUnit 5的关键特性和使用方法。 首先,JUnit ...
junit4.1junit4.1junit4.1junit4.1junit4.1
TestNG和JUnit是两种广泛使用的Java测试框架,它们在软件质量保证和持续集成流程中扮演着重要角色。本文将深入探讨这两个测试框架的特性和差异,以及为什么TestNG被誉为下一代测试框架。 TestNG是由Cedric Beust...
JUnit API JUnit API JUnit API JUnit API JUnit API
实验过程中的问题解决和调试经验,对培养学生的实践能力和解决问题的能力大有裨益,也激发了他们对软件测试的兴趣和深入学习的欲望。 总之,JUnit作为Java开发中的重要工具,不仅简化了单元测试的实现,还增强了...
《JUnit Recipes》则更注重实战,提供了一系列的测试“食谱”,即解决特定测试问题的步骤和技巧。这本书覆盖了如何处理异步测试、模拟对象(Mocks)和桩对象(Stubs)的创建、测试性能监控、以及如何与持续集成工具...
"JUnit in Action 3rd Edition" JUnit是一种流行的Java单元测试框架,由Kent Beck和Eric Gamma于1997年创立。JUnit在软件测试领域中扮演着重要的角色,帮助开发者编写高质量的代码。下面是关于JUnit的重要知识点: ...
`junit5-r5.4.0.zip`可能是JUnit5的一个特定版本,版本号为5.4.0,它提供了一个稳定的API和修复了一些已知问题。开发者可以通过这个版本了解JUnit5在不同阶段的发展和功能变化。 `demo-junit-5-master.zip`则是...
JUnit是Java编程语言中最常用的单元测试框架之一,用于编写和运行可重复的自动化测试用例。Junit5.7.2版本是这个框架的一个稳定版本,提供了许多改进和新特性,使得测试更加高效且易于维护。这个离线jar文件包含了...
本文将深入探讨关于"junit-4.12.rar"包及其依赖包,以及如何解决在使用JUnit 4进行单元测试时遇到的"method initializationerror not found"错误。 首先,我们来了解JUnit 4.12版本。这是JUnit的一个稳定版本,发布...
Junit5是Java开发中最常用的单元测试框架之一,它的出现为开发者提供了更加高效、灵活的测试体验。相较于之前的版本,Junit5引入了许多新的特性和改进,使得测试代码的编写和维护变得更加简单。本整合包包含了Junit5...
JUnit是Java编程语言中最常用的单元测试框架之一,它允许开发者编写可执行的测试用例来验证代码的功能。这个“junit工具jar包”是JUnit 4.8.1版本,一个稳定且广泛使用的版本,提供了丰富的断言方法和测试注解,便于...
### Eclipse 下 JUnit 的详细配置 #### 一、引言 JUnit 是一款广泛使用的 Java 单元测试框架,它能够帮助开发者确保代码的质量与稳定性。Eclipse 作为一款流行的 Java 开发工具,已经内置了对 JUnit 的支持。本文...
JUnit是Java编程语言中最常用的单元测试框架之一,它允许开发者编写可执行的测试用例来验证代码的功能。单元测试是对程序中的最小可测试部分——通常是一个函数或方法——进行检查,确保其按照预期工作。JUnit作为...
这对于验证方法在不同输入情况下的行为非常有用。 为了更精细地控制测试执行,Junit4.12提供了测试套件(Test Suites)和分类(Categories)。测试套件允许将多个测试类组合在一起,一次性运行。分类则可以将测试...