- 浏览: 1843746 次
- 性别:
- 来自: 深圳
文章分类
- 全部博客 (665)
- 闲话 (17)
- ruby (1)
- javascript (40)
- linux (7)
- android (22)
- 开发过程 (11)
- 哥也读读源代码 (13)
- JVM (1)
- ant (2)
- Hibernate (3)
- jboss (3)
- web service (17)
- https (4)
- java基础 (17)
- spring (7)
- servlet (3)
- 杂记 (39)
- struts2 (10)
- logback (4)
- 多线程 (2)
- 系统诊断 (9)
- UI (4)
- json (2)
- Java EE (7)
- eclipse相关 (4)
- JMS (1)
- maven (19)
- 版本管理 (7)
- sso (1)
- ci (1)
- 设计 (18)
- 戒烟 (4)
- http (9)
- 计划 (4)
- HTML5 (3)
- chrome extensions (5)
- tomcat源码阅读 (4)
- httpd (5)
- MongoDB (3)
- node (2)
最新评论
-
levin_china:
勾选了,还是找不到
用spring annotation声明的bean,当打包在jar中时,无法被扫描到 -
GGGGeek:
我用的maven-3.5.0,还没有遇到这种情况,使用jar ...
用spring annotation声明的bean,当打包在jar中时,无法被扫描到 -
GGGGeek:
受益匪浅,从组织项目结构,到技术细节,讲的很到位,只是博主不再 ...
一个多maven项目聚合的实例 -
Aaron-Joe-William:
<?xml version="1.0" ...
hibernate逆向工程 -
li272355201:
http://archive.apache.org/dist/ ...
tomcat源码阅读(一)——环境搭建
论坛上有另外一篇更全面的帖子,jinnianshilongnian写的:http://www.iteye.com/topic/1120924
本文的环境是:
spring-framework-3.1.0
hibernate-4.1.6
junit-4.10
这里大部分是参考我以前熟悉的配置方法,只是把hibernate3升级到hibernate4,其实差不了很多,只要注意几个要点:
1、以前集成hibernate3和spring的时候,spring的ORM包里提供了HibernateSupport和HibernateTemplate这两个辅助类,我用的是HibernateTemplate。不过在Hibernate4里,spring不再提供这种辅助类,用的是hibernate4的原生API
2、集成hibernate4之后,最小事务级别必须是Required,如果是以下的级别,或者没有开启事务的话,无法得到当前的Session
执行这行代码,会抛出No Session found for current thread
对于运行时,这个可能不是很大的问题,因为在Service层一般都会开启事务,只要保证级别高于Required就可以了。可是由于在Dao层是不会开启事务的,所以针对Dao层进行单元测试就有困难了。
解决的办法是,或者在Dao层的单元测试类上,开启事务。或者专门准备一个for unit test的配置文件,在Dao层就开启事务。我采用的是前者
首先是目录结构,这里暂时还没有集成struts2、spring-mvc等web框架,也尚未包含js、css、jsp等目录
这里除了servlet规范规定的web.xml必须放在WEB-INF下之外,其他的所有配置文件,都放在src根目录下。这样做的好处是,后续所有需要引用配置文件的地方,都可以统一用classpath:前缀找到配置文件。之前试过有的文件放在WEB-INF下,有的放在src根目录下,所以在引用的地方会不太统一,比较麻烦。
当然无论配置文件怎么放,只要恰当使用classpath:和file:前缀,都是能找到的,只是个人选择的问题。另外,由于现在配置文件还比较少,所以直接扔到src根目录下没什么问题,如果配置文件增多了,可以再进行划分
接下来是web.xml
这里没有什么要特别注意的,只是声明了beans.xml的路径。这里的servlet是配置cxf的,与hibernate没有关系。因为目标是要搭一个完整的开发框架,所以把cxf也事先放上了
接下来是spring的配置文件beans.xml
这里有几点要注意的:
这里把jdbc驱动的参数,放到了专门的配置文件里,改动起来会比较方便。另外数据库连接池在实际生产环境可以考虑切换一下,比如听说阿里巴巴出的druid就挺不错,jboss和WAS自带的连接池也是不错的
这里的sessionFactory改成org.springframework.orm.hibernate4.LocalSessionFactoryBean,如果ORM映射采用的不是配置文件,是用注解的话,以前hibernate3有一个AnnotationSessionFactoryBean,在hibernate4里没看到
这里ORM映射用的是配置文件,其实用注解也差不多
这一行:
可以避免启动容器时报的一个错误:
Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException
这个错误其实是无所谓的,不过还是不要报错好看一点
这里是开启事务,用的是注解,比用配置文件简单一点。
用配置文件的好处,是事务声明比较集中,不需要在每个Service层接口上单独声明。缺点是Service中的方法,命名规范需要事先约定好,否则事务就不能生效
用注解的好处,是Service中的方法命名不需要特别规定,缺点是没有做到集中声明,如果在某个Service层的接口忘记声明事务,那么事务就无法生效
两种方法各有好处,我个人更喜欢用注解
然后是DAO层的结构
首先有一个通用的DAO接口,然后有一个通用的DAO抽象实现类。每个具体业务DAO接口,继承通用DAO接口,具体业务DAO实现,继承通用DAO抽象实现类
因为只是示例,这里的方法不是很多,只包含了基本的增删改查方法
这个抽象类实现了IGenericDAO的所有方法,具体业务DAO的实现类,就不需要重复实现这些方法了。
这里因为session.get()和session.load()方法,都需要传入一个Class类型的参数,所以定义了entityClass字段,在具体业务类的构造方法中传入,下面会看到。另外有一个办法是用反射的方法,来获取entityClass字段,就不需要在具体子类的构造方法中再传入了。不过我个人觉得传入也不是很麻烦,就没有这么做
这个类除了实现了IGenericDAO里定义的public方法之外,还提供了protected的queryForObject()和queryForList()方法,可以为具体子类提供一些便利
这个通用DAO还不是很完善,主要是还可以补充更多的方法,以及考虑分页。为了简化的需要,这里省略了
这是具体业务DAO的接口,除了通用的方法之外,增加了一个按照name查询的方法,所以就要单独定义此方法
这是具体业务DAO的实现类,实现了接口里的queryByName()方法,并且在构造参数中传入了User.class,用于初始化GenericDAO里的entityClass字段
此外,这个类需要用@Repository注解,声明为spring bean
DAO层里是不能声明事务的,也不能自行捕获异常,如果有特殊需求必须捕获的话,也要在处理之后,重新抛出来。否则Service层的事务就失效了
接下来是Service层
只要在接口上用@Transactional注解,此接口内的所有方法就自动声明为事务了,方法即是事务的边界。
注意事务是在接口上声明的,一般不在实现类上声明
后面的propagation参数,至少要到REQUIRED,否则No Session found for current thread,我也不知道这算不算一个BUG,还是spring认为是一个强制要求
这个Service的实现类就很简单了,不需要重复声明事务,但是需要用@Service注解将自身声明为一个spring bean(因为可能还会注入上层),另外用@Autowired注解,将之前声明的DAO注入
接下来说明一下单元测试的方法,在想做单元测试的类上,用右键菜单New-->JUnit Test Case
这里要注意Source folder选到test,不然就会生成到src目录下了,然后可以视情况勾选setUp()
生成的单元测试类
注解为@Test的方法,会被认为是单元测试方法被执行,注解为@Before的方法,会在每个单元测试方法执行之前被执行
这里是把要单元测试的目标类注入进来
下面重点介绍一下类上面的几个注解:
加上@RunWith注解之后,单元测试类会在spring容器里执行,这会带来很多便利。
@ContextConfiguration注解,可以指定要加载的spring配置文件路径。如果对spring配置文件进行了恰当的拆分,就可以在不同的单元测试类里,仅加载必要的配置文件
这行注解是最关键的,前面已经提到,因为在DAO层是没有声明事务的,所以如果直接执行的话,就会抛出No Session found for current thread
所以需要加上这句注解,在执行单元测试时,开启事务,就可以规避这个问题。同时也不会影响到实际的事务
此外还引入了一个额外的好处,就是加上了这个注解之后,单元测试对数据库的改动会被自动回滚,避免不同单元测试方法之间的耦合。这个特性在实际跑单元测试里是很方便的
实际运行一下这个单元测试类,可以在控制台看到以下输出:
2012-8-16 19:37:42 org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction
信息: Began transaction (1): transaction manager [org.springframework.orm.hibernate4.HibernateTransactionManager@183d59c]; rollback [true]
1903 [main] WARN o.h.hql.internal.ast.HqlSqlWalker - [DEPRECATION] Encountered positional parameter near line 1, column 60. Positional parameter are considered deprecated; use named parameters or JPA-style positional parameters instead.
Hibernate:
select
book0_.ID as ID0_,
book0_.NAME as NAME0_,
book0_.ISBN as ISBN0_
from
developframeworkschema.book book0_
where
book0_.ISBN=?
2012-8-16 19:37:42 org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction
信息: Rolled back transaction after test execution for test context
每个方法开始之前,都会开启一个新事务,在执行完毕之后,该事务都会被回滚
其中还有一行警告信息:[DEPRECATION] Encountered positional parameter near line 1, column 60. Positional parameter are considered deprecated; use named parameters or JPA-style positional parameters instead.
这是因为在GenericDAO中采用了hibernate4不推荐的写法:
hibernate4的建议,是把
改成
鉴于自动回滚这个特性很方便,对Service层组件进行单元测试的时候,也推荐加上@Transactional注解
对于spring3和hibernate4的集成,本文就简单介绍到这里,欢迎补充
这位同学你好!这怎么是误人不浅呢,如果你开发的时候,没有区分开发库和生产库,那么单元测试的代码本来就不应该实际操作数据啊,否则删掉了测试的数据怎么办呢?
当然如果你测试和开发本来就是用的2个库,那就去掉@Transactional注解就行了呀
不好意思,我没有进行过项目开发,正在学习中,我当时的想法比较简单,主要想通过junit将更新和删除操作响应到数据库,因为@Transactional ,试了各种方法才发现是@Transactional导致了更新和删除不能同步到数据库,费了很大的劲才在jUnit下删除了数据库的东西,当时可能有点激动。感谢您的分享,您的分享给了我很大的帮助,我认真地学习了您的这篇文章,受益匪浅,请原谅我的粗鲁。
客气了,没啥粗鲁~~多交流而已
这位同学你好!这怎么是误人不浅呢,如果你开发的时候,没有区分开发库和生产库,那么单元测试的代码本来就不应该实际操作数据啊,否则删掉了测试的数据怎么办呢?
当然如果你测试和开发本来就是用的2个库,那就去掉@Transactional注解就行了呀
不好意思,我没有进行过项目开发,正在学习中,我当时的想法比较简单,主要想通过junit将更新和删除操作响应到数据库,因为@Transactional ,试了各种方法才发现是@Transactional导致了更新和删除不能同步到数据库,费了很大的劲才在jUnit下删除了数据库的东西,当时可能有点激动。感谢您的分享,您的分享给了我很大的帮助,我认真地学习了您的这篇文章,受益匪浅,请原谅我的粗鲁。
这位同学你好!这怎么是误人不浅呢,如果你开发的时候,没有区分开发库和生产库,那么单元测试的代码本来就不应该实际操作数据啊,否则删掉了测试的数据怎么办呢?
当然如果你测试和开发本来就是用的2个库,那就去掉@Transactional注解就行了呀
那就好,没有白写,呵呵
本文的环境是:
spring-framework-3.1.0
hibernate-4.1.6
junit-4.10
这里大部分是参考我以前熟悉的配置方法,只是把hibernate3升级到hibernate4,其实差不了很多,只要注意几个要点:
1、以前集成hibernate3和spring的时候,spring的ORM包里提供了HibernateSupport和HibernateTemplate这两个辅助类,我用的是HibernateTemplate。不过在Hibernate4里,spring不再提供这种辅助类,用的是hibernate4的原生API
2、集成hibernate4之后,最小事务级别必须是Required,如果是以下的级别,或者没有开启事务的话,无法得到当前的Session
sessionFactory.getCurrentSession();
执行这行代码,会抛出No Session found for current thread
对于运行时,这个可能不是很大的问题,因为在Service层一般都会开启事务,只要保证级别高于Required就可以了。可是由于在Dao层是不会开启事务的,所以针对Dao层进行单元测试就有困难了。
解决的办法是,或者在Dao层的单元测试类上,开启事务。或者专门准备一个for unit test的配置文件,在Dao层就开启事务。我采用的是前者
首先是目录结构,这里暂时还没有集成struts2、spring-mvc等web框架,也尚未包含js、css、jsp等目录
这里除了servlet规范规定的web.xml必须放在WEB-INF下之外,其他的所有配置文件,都放在src根目录下。这样做的好处是,后续所有需要引用配置文件的地方,都可以统一用classpath:前缀找到配置文件。之前试过有的文件放在WEB-INF下,有的放在src根目录下,所以在引用的地方会不太统一,比较麻烦。
当然无论配置文件怎么放,只要恰当使用classpath:和file:前缀,都是能找到的,只是个人选择的问题。另外,由于现在配置文件还比较少,所以直接扔到src根目录下没什么问题,如果配置文件增多了,可以再进行划分
接下来是web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>DevelopFramework</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:beans.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>CXFServlet</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXFServlet</servlet-name> <url-pattern>/webservice/*</url-pattern> </servlet-mapping> </web-app>
这里没有什么要特别注意的,只是声明了beans.xml的路径。这里的servlet是配置cxf的,与hibernate没有关系。因为目标是要搭一个完整的开发框架,所以把cxf也事先放上了
接下来是spring的配置文件beans.xml
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <context:component-scan base-package="com.huawei.inoc.framework" /> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:jdbc.properties</value> </list> </property> </bean> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${driverClass}" /> <property name="jdbcUrl" value="${jdbcUrl}" /> <property name="user" value="${user}" /> <property name="password" value="${password}" /> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mappingLocations" value="classpath:/com/huawei/inoc/framework/model/**/*.hbm.xml" /> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.jdbc.fetch_size">50</prop> <prop key="hibernate.jdbc.batch_size">25</prop> <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop> </props> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> <jaxws:endpoint id="helloWorld" implementor="#helloWorldWebserviceImpl" address="/HelloWorld" /> <jaxws:client id="client" serviceClass="com.huawei.inoc.dummy.webservice.IDemoSupport" address="http://localhost:8080/Dummy/webservice/getDate" /> </beans>
这里有几点要注意的:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${driverClass}" /> <property name="jdbcUrl" value="${jdbcUrl}" /> <property name="user" value="${user}" /> <property name="password" value="${password}" /> </bean>
这里把jdbc驱动的参数,放到了专门的配置文件里,改动起来会比较方便。另外数据库连接池在实际生产环境可以考虑切换一下,比如听说阿里巴巴出的druid就挺不错,jboss和WAS自带的连接池也是不错的
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mappingLocations" value="classpath:/com/huawei/inoc/framework/model/**/*.hbm.xml" /> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.jdbc.fetch_size">50</prop> <prop key="hibernate.jdbc.batch_size">25</prop> <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop> </props> </property> </bean>
这里的sessionFactory改成org.springframework.orm.hibernate4.LocalSessionFactoryBean,如果ORM映射采用的不是配置文件,是用注解的话,以前hibernate3有一个AnnotationSessionFactoryBean,在hibernate4里没看到
这里ORM映射用的是配置文件,其实用注解也差不多
这一行:
<prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
可以避免启动容器时报的一个错误:
Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException
这个错误其实是无所谓的,不过还是不要报错好看一点
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" />
这里是开启事务,用的是注解,比用配置文件简单一点。
用配置文件的好处,是事务声明比较集中,不需要在每个Service层接口上单独声明。缺点是Service中的方法,命名规范需要事先约定好,否则事务就不能生效
用注解的好处,是Service中的方法命名不需要特别规定,缺点是没有做到集中声明,如果在某个Service层的接口忘记声明事务,那么事务就无法生效
两种方法各有好处,我个人更喜欢用注解
然后是DAO层的结构
首先有一个通用的DAO接口,然后有一个通用的DAO抽象实现类。每个具体业务DAO接口,继承通用DAO接口,具体业务DAO实现,继承通用DAO抽象实现类
public interface IGenericDAO<T> { void insert(T t); void delete(T t); void update(T t); T queryById(String id); List<T> queryAll(); }
因为只是示例,这里的方法不是很多,只包含了基本的增删改查方法
public abstract class GenericDAO<T> implements IGenericDAO<T> { private Class<T> entityClass; public GenericDAO(Class<T> clazz) { this.entityClass = clazz; } @Autowired private SessionFactory sessionFactory; @Override public void insert(T t) { sessionFactory.getCurrentSession().save(t); } @Override public void delete(T t) { sessionFactory.getCurrentSession().delete(t); } @Override public void update(T t) { sessionFactory.getCurrentSession().update(t); } @SuppressWarnings("unchecked") @Override public T queryById(String id) { return (T) sessionFactory.getCurrentSession().get(entityClass, id); } @Override public List<T> queryAll() { String hql = "from " + entityClass.getSimpleName(); return queryForList(hql, null); } @SuppressWarnings("unchecked") protected T queryForObject(String hql, Object[] params) { Query query = sessionFactory.getCurrentSession().createQuery(hql); setQueryParams(query, params); return (T) query.uniqueResult(); } @SuppressWarnings("unchecked") protected T queryForTopObject(String hql, Object[] params) { Query query = sessionFactory.getCurrentSession().createQuery(hql); setQueryParams(query, params); return (T) query.setFirstResult(0).setMaxResults(1).uniqueResult(); } @SuppressWarnings("unchecked") protected List<T> queryForList(String hql, Object[] params) { Query query = sessionFactory.getCurrentSession().createQuery(hql); setQueryParams(query, params); return query.list(); } @SuppressWarnings("unchecked") protected List<T> queryForList(final String hql, final Object[] params, final int recordNum) { Query query = sessionFactory.getCurrentSession().createQuery(hql); setQueryParams(query, params); return query.setFirstResult(0).setMaxResults(recordNum).list(); } private void setQueryParams(Query query, Object[] params) { if (null == params) { return; } for (int i = 0; i < params.length; i++) { query.setParameter(i, params[i]); } } }
这个抽象类实现了IGenericDAO的所有方法,具体业务DAO的实现类,就不需要重复实现这些方法了。
这里因为session.get()和session.load()方法,都需要传入一个Class类型的参数,所以定义了entityClass字段,在具体业务类的构造方法中传入,下面会看到。另外有一个办法是用反射的方法,来获取entityClass字段,就不需要在具体子类的构造方法中再传入了。不过我个人觉得传入也不是很麻烦,就没有这么做
这个类除了实现了IGenericDAO里定义的public方法之外,还提供了protected的queryForObject()和queryForList()方法,可以为具体子类提供一些便利
这个通用DAO还不是很完善,主要是还可以补充更多的方法,以及考虑分页。为了简化的需要,这里省略了
public interface IUserDAO extends IGenericDAO<User> { public User queryByName(String userName); }
这是具体业务DAO的接口,除了通用的方法之外,增加了一个按照name查询的方法,所以就要单独定义此方法
@Repository public class UserDAO extends GenericDAO<User> implements IUserDAO { public UserDAO() { super(User.class); } @Override public User queryByName(String userName) { String hql = "from User u where u.name = ?"; return queryForObject(hql, new Object[] { userName }); } }
这是具体业务DAO的实现类,实现了接口里的queryByName()方法,并且在构造参数中传入了User.class,用于初始化GenericDAO里的entityClass字段
此外,这个类需要用@Repository注解,声明为spring bean
DAO层里是不能声明事务的,也不能自行捕获异常,如果有特殊需求必须捕获的话,也要在处理之后,重新抛出来。否则Service层的事务就失效了
接下来是Service层
@Transactional(propagation = Propagation.REQUIRED, readOnly = false) public interface IBookService { void addBook(Book book); }
只要在接口上用@Transactional注解,此接口内的所有方法就自动声明为事务了,方法即是事务的边界。
注意事务是在接口上声明的,一般不在实现类上声明
后面的propagation参数,至少要到REQUIRED,否则No Session found for current thread,我也不知道这算不算一个BUG,还是spring认为是一个强制要求
@Service public class BookService implements IBookService { @Autowired private IBookDAO bookDAO; @Override public void addBook(Book book) { bookDAO.insert(book); } }
这个Service的实现类就很简单了,不需要重复声明事务,但是需要用@Service注解将自身声明为一个spring bean(因为可能还会注入上层),另外用@Autowired注解,将之前声明的DAO注入
接下来说明一下单元测试的方法,在想做单元测试的类上,用右键菜单New-->JUnit Test Case
这里要注意Source folder选到test,不然就会生成到src目录下了,然后可以视情况勾选setUp()
生成的单元测试类
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:beans.xml") @Transactional public class BookDAOTest { @Autowired private BookDAO bookDAO; @Test public void testQueryByIsbn() { String isbn = "123"; Book result = bookDAO.queryByIsbn(isbn); String name = result.getName(); assertEquals("thinking in java", name); } @Test public void testInsert() { Book book = new Book(); book.setName("bai ye xing"); book.setIsbn("be bought yesterday"); bookDAO.insert(book); } @Test public void testDelete() { String id = "test_1"; Book target = bookDAO.queryById(id); bookDAO.delete(target); } @Test public void testUpdate() { String id = "test_1"; Book target = bookDAO.queryById(id); target.setName("i am changeid"); bookDAO.update(target); } @Test public void testQueryById() { String id = "test_1"; Book target = bookDAO.queryById(id); String name = target.getName(); assertEquals("thinking in java", name); } @Test public void testQueryAll() { List<Book> books = bookDAO.queryAll(); assertEquals(3, books.size()); } }
注解为@Test的方法,会被认为是单元测试方法被执行,注解为@Before的方法,会在每个单元测试方法执行之前被执行
@Autowired private BookDAO bookDAO;
这里是把要单元测试的目标类注入进来
下面重点介绍一下类上面的几个注解:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:beans.xml")
加上@RunWith注解之后,单元测试类会在spring容器里执行,这会带来很多便利。
@ContextConfiguration注解,可以指定要加载的spring配置文件路径。如果对spring配置文件进行了恰当的拆分,就可以在不同的单元测试类里,仅加载必要的配置文件
@Transactional
这行注解是最关键的,前面已经提到,因为在DAO层是没有声明事务的,所以如果直接执行的话,就会抛出No Session found for current thread
所以需要加上这句注解,在执行单元测试时,开启事务,就可以规避这个问题。同时也不会影响到实际的事务
此外还引入了一个额外的好处,就是加上了这个注解之后,单元测试对数据库的改动会被自动回滚,避免不同单元测试方法之间的耦合。这个特性在实际跑单元测试里是很方便的
实际运行一下这个单元测试类,可以在控制台看到以下输出:
2012-8-16 19:37:42 org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction
信息: Began transaction (1): transaction manager [org.springframework.orm.hibernate4.HibernateTransactionManager@183d59c]; rollback [true]
1903 [main] WARN o.h.hql.internal.ast.HqlSqlWalker - [DEPRECATION] Encountered positional parameter near line 1, column 60. Positional parameter are considered deprecated; use named parameters or JPA-style positional parameters instead.
Hibernate:
select
book0_.ID as ID0_,
book0_.NAME as NAME0_,
book0_.ISBN as ISBN0_
from
developframeworkschema.book book0_
where
book0_.ISBN=?
2012-8-16 19:37:42 org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction
信息: Rolled back transaction after test execution for test context
每个方法开始之前,都会开启一个新事务,在执行完毕之后,该事务都会被回滚
其中还有一行警告信息:[DEPRECATION] Encountered positional parameter near line 1, column 60. Positional parameter are considered deprecated; use named parameters or JPA-style positional parameters instead.
这是因为在GenericDAO中采用了hibernate4不推荐的写法:
private void setQueryParams(Query query, Object[] params) { if (null == params) { return; } for (int i = 0; i < params.length; i++) { query.setParameter(i, params[i]); } }
hibernate4的建议,是把
String hql = "from User u where u.name = ?"; Query query = sessionFactory.getCurrentSession().createQuery(hql); query.setParameter(0, name);
改成
String hql = "from User u where u.name = :name"; Query query = this.getSession().createQuery(hql); query.setParameter("name", name);
鉴于自动回滚这个特性很方便,对Service层组件进行单元测试的时候,也推荐加上@Transactional注解
对于spring3和hibernate4的集成,本文就简单介绍到这里,欢迎补充
评论
14 楼
kyfxbl
2016-02-14
Croesus青烟 写道
kyfxbl 写道
Croesus青烟 写道
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:beans.xml")
@Transactional
public class BookDAOTest {
}
您的测试类,增加了@Transactional,将导致Hibernate只能查询数据库,更新和删除操作,将无法响应到数据库,虽然有sql语句生成,误人不浅/-\
@ContextConfiguration(locations = "classpath:beans.xml")
@Transactional
public class BookDAOTest {
}
您的测试类,增加了@Transactional,将导致Hibernate只能查询数据库,更新和删除操作,将无法响应到数据库,虽然有sql语句生成,误人不浅/-\
这位同学你好!这怎么是误人不浅呢,如果你开发的时候,没有区分开发库和生产库,那么单元测试的代码本来就不应该实际操作数据啊,否则删掉了测试的数据怎么办呢?
当然如果你测试和开发本来就是用的2个库,那就去掉@Transactional注解就行了呀
不好意思,我没有进行过项目开发,正在学习中,我当时的想法比较简单,主要想通过junit将更新和删除操作响应到数据库,因为@Transactional ,试了各种方法才发现是@Transactional导致了更新和删除不能同步到数据库,费了很大的劲才在jUnit下删除了数据库的东西,当时可能有点激动。感谢您的分享,您的分享给了我很大的帮助,我认真地学习了您的这篇文章,受益匪浅,请原谅我的粗鲁。
客气了,没啥粗鲁~~多交流而已
13 楼
Croesus青烟
2016-01-10
kyfxbl 写道
Croesus青烟 写道
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:beans.xml")
@Transactional
public class BookDAOTest {
}
您的测试类,增加了@Transactional,将导致Hibernate只能查询数据库,更新和删除操作,将无法响应到数据库,虽然有sql语句生成,误人不浅/-\
@ContextConfiguration(locations = "classpath:beans.xml")
@Transactional
public class BookDAOTest {
}
您的测试类,增加了@Transactional,将导致Hibernate只能查询数据库,更新和删除操作,将无法响应到数据库,虽然有sql语句生成,误人不浅/-\
这位同学你好!这怎么是误人不浅呢,如果你开发的时候,没有区分开发库和生产库,那么单元测试的代码本来就不应该实际操作数据啊,否则删掉了测试的数据怎么办呢?
当然如果你测试和开发本来就是用的2个库,那就去掉@Transactional注解就行了呀
不好意思,我没有进行过项目开发,正在学习中,我当时的想法比较简单,主要想通过junit将更新和删除操作响应到数据库,因为@Transactional ,试了各种方法才发现是@Transactional导致了更新和删除不能同步到数据库,费了很大的劲才在jUnit下删除了数据库的东西,当时可能有点激动。感谢您的分享,您的分享给了我很大的帮助,我认真地学习了您的这篇文章,受益匪浅,请原谅我的粗鲁。
12 楼
kyfxbl
2015-12-27
Croesus青烟 写道
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:beans.xml")
@Transactional
public class BookDAOTest {
}
您的测试类,增加了@Transactional,将导致Hibernate只能查询数据库,更新和删除操作,将无法响应到数据库,虽然有sql语句生成,误人不浅/-\
@ContextConfiguration(locations = "classpath:beans.xml")
@Transactional
public class BookDAOTest {
}
您的测试类,增加了@Transactional,将导致Hibernate只能查询数据库,更新和删除操作,将无法响应到数据库,虽然有sql语句生成,误人不浅/-\
这位同学你好!这怎么是误人不浅呢,如果你开发的时候,没有区分开发库和生产库,那么单元测试的代码本来就不应该实际操作数据啊,否则删掉了测试的数据怎么办呢?
当然如果你测试和开发本来就是用的2个库,那就去掉@Transactional注解就行了呀
11 楼
Croesus青烟
2015-12-09
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:beans.xml")
@Transactional
public class BookDAOTest {
}
您的测试类,增加了@Transactional,将导致Hibernate只能查询数据库,更新和删除操作,将无法响应到数据库,虽然有sql语句生成,误人不浅/-\
@ContextConfiguration(locations = "classpath:beans.xml")
@Transactional
public class BookDAOTest {
}
您的测试类,增加了@Transactional,将导致Hibernate只能查询数据库,更新和删除操作,将无法响应到数据库,虽然有sql语句生成,误人不浅/-\
10 楼
Croesus青烟
2015-12-07
按照博主的思路给自己的项目进行了junit测试,很成功,十分感谢!
9 楼
天极网络
2015-06-18
getHibernateTemplet()再也没有爱了
8 楼
sunxiaoyes
2014-12-27
你好,我不想用userdao继承basedao去获得pojo类型,我想直接在basedao中取到类型该如何操作?试了很多种方法都报错java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType,还望回复
7 楼
kyfxbl
2013-07-26
happylouis 写道
你的文章给了我极大的帮助
那就好,没有白写,呵呵
6 楼
happylouis
2013-07-26
你的文章给了我极大的帮助
5 楼
searchjack
2013-06-04
@Autowired
private SessionFactory sessionFactory;
Hibernate 4 ,
为什么得不行 sessionFactory 呢 ?
private SessionFactory sessionFactory;
Hibernate 4 ,
为什么得不行 sessionFactory 呢 ?
4 楼
strive708
2013-04-16
我也是看了jinnianshilongnian的文章,然后想要自己用在自己的项目中,不过我是用的XML文件进行的配置,不是用的注解,到现在已经调试了好几天 了,就是无法获取具体类的类名,每次在生成HQL时都是from Object where 这样的语句,请教楼主这个问题出在什么地方?
3 楼
jay61439476
2013-02-25
LZ 能提供下打包的项目吗?
2 楼
leftcoffee
2013-01-10
如果是查询分表的数据 你这个封装怎么使用呢? userDao<User> 这里应该泛型是接口Iuser
?
?
1 楼
891379133
2012-08-24
老大行个好 .发个该示例的源码zip .
发表评论
-
自定义ClassLoader,让spring加载外部的配置文件和类
2012-11-20 20:50 10645今天同事遇到一个需求: 在外部以jar包的形式存放若干个插件 ... -
用spring annotation声明的bean,当打包在jar中时,无法被扫描到
2012-09-10 20:56 20574发现一个问题,十分蛋疼。 我们项目是由N个工程组成的,外围工 ... -
spring配置中classpath和classpath*的区别
2012-09-10 20:45 8270在spring配置文件里,可以用classpath:前缀,来从 ... -
spring3.0设置定时任务
2012-07-27 10:47 2422今天做个小需求,需要 ... -
集成spring和quartz
2012-06-19 18:00 1458quartz是个好东西,今天用的版本是quartz-1.7.3 ... -
Spring MVC初体验之出师不利
2012-01-20 16:09 1748对struts2感觉日渐不满,主要是比较复杂的界面,Actio ...
相关推荐
4. **JUnit测试**:在你的测试类上添加`@RunWith(SpringRunner.class)`注解,表明这个测试类将由Spring Test Runner运行。然后,使用`@SpringBootTest`或`@ContextConfiguration`注解指定Spring配置文件的位置。你...
标题中的“hibernate和junit的jar”指的是两个重要的Java开发工具——Hibernate...在实际项目中,可能还需要结合其他工具和库,如Spring框架来进一步管理和集成Hibernate,以及使用Maven或Gradle等构建工具来管理依赖。
3. Spring4对Hibernate4的事务管理支持 4. 使用JUnit进行测试驱动开发 5. Eclipse集成开发环境的使用 理解并掌握这些知识点,对于任何Java后端开发者来说都至关重要,因为它们构成了现代企业级应用的基础架构。通过...
在IT行业中,集成Spring和Hibernate是常见的开发模式,用于构建高效、可维护的企业级应用程序。Spring作为一个全面的轻量级框架,提供了依赖注入、AOP(面向切面编程)、MVC(模型-视图-控制器)等核心功能,而...
在MyEclipse这样的集成开发环境中,集成Spring和Hibernate可以构建高效且易于维护的Java Web应用。 在"Spring集成Hibernate Myeclipse下"的场景中,我们首先需要配置MyEclipse环境,确保安装了Spring和Hibernate的...
本文将深入探讨如何使用Spring4和Hibernate4实现数据库的增删改查操作,以及在这个过程中可能涉及的关键知识点。 首先,Spring4是一个全面的Java应用程序框架,它提供了一种方式来管理组件(或称为bean)之间的依赖...
SpringMVC、Spring4和Hibernate4是Java Web开发中的三个核心框架,它们分别负责不同的职责:SpringMVC作为模型-视图-控制器架构的一部分,处理Web应用的请求和响应;Spring4提供依赖注入和面向切面编程,管理应用的...
在本整合实例中,我们将探讨如何将Spring MVC 5、Hibernate 5、JUnit 4 和 Log4j2 集成在一起,以构建一个高效、可测试和日志记录完善的Java Web应用程序。以下是对这些技术及其整合过程的详细说明: **Spring MVC ...
这个压缩包文件“spring4-hibernate3-demo”很可能是包含了一个示例项目的源代码,用于演示这种集成。 首先,我们需要理解Spring 4和Hibernate 3的基本概念。Spring是一个全面的开源应用框架,它提供了依赖注入、...
4. **SessionFactory 创建**:使用 Spring 的 LocalSessionFactoryBean 创建 Hibernate 的 SessionFactory。配置文件中需要包含实体类的扫描路径、数据库连接信息、Hibernate 配置属性等。 5. **DAO 层集成**:在 ...
在与Spring和Hibernate这两个流行框架集成时,JUnit发挥着至关重要的作用,确保了业务逻辑和数据访问层的正确性。 首先,让我们深入了解一下Spring框架。Spring是一个全面的企业级应用开发框架,它提供了依赖注入...
总的来说,"Spring3+Hibernate4+Maven+jUnit 多库多数据源实现"是一个综合性的技术实践,它涉及到多个关键组件的集成,通过灵活的配置和编程,可以有效地处理企业级应用中复杂的数据库管理需求。这样的配置不仅提高...
本项目涉及的技术栈包括Hibernate、Spring、Ant以及JUnit,这些都是Java开发中至关重要的工具和框架。下面将详细阐述这些技术及其在项目中的应用。 1. **Hibernate**: Hibernate是一个优秀的对象关系映射(ORM)...
3. 集成Hibernate:配置Hibernate的SessionFactory,指定数据库连接信息、实体类映射文件等。然后,通过Spring的HibernateTemplate或SessionFactoryBean,将Hibernate集成到Spring中,以便在Controller中方便地进行...
标题"Spring整合Hibernate.jar"意味着我们将讨论如何将这两个强大的框架集成在一起,以便在Spring管理的环境中使用Hibernate进行数据库操作。这通常涉及到以下步骤和知识点: 1. **引入依赖**:首先,你需要在项目...
Spring4与Hibernate4的整合主要涉及以下几个关键点: 1. **依赖管理**:首先,我们需要在项目的pom.xml或build.gradle文件中添加Spring和Hibernate的依赖库。确保版本号对应,以避免兼容性问题。 2. **配置...
在IT行业中,Spring和Hibernate是两个非常重要的框架,它们分别在应用层和数据持久化层发挥着关键作用。Spring是一个全面的Java企业级应用开发框架,而Hibernate则是一个优秀的对象关系映射(ORM)工具,它简化了...
3. 配置Hibernate:在Spring配置中集成Hibernate,配置SessionFactory、DataSource等,还可以使用Spring的TransactionManager进行事务管理。 4. 创建DAO层:使用Spring的JdbcTemplate或Hibernate的Session接口进行...
Struts、Spring 和 Hibernate 是Java Web开发中的三大框架,它们的组合通常被称为SSH(Struts、Spring、Hibernate)。这个“struts+spring+hibernate开发 注册小实例”是为初学者设计的一个教程,目的是帮助他们快速...
首先,SpringMVC4整合Hibernate4的主要目标是将Spring的依赖注入(DI)和控制反转(IOC)特性与Hibernate的数据持久化能力结合起来,提供一个高效、灵活的Web应用解决方案。 1. **项目结构**:在Maven项目中,我们...