- 浏览: 26098 次
- 性别:
- 来自: 河北
-
最新评论
-
hsjshijiazhuang:
关注。。。
SpringSide 3 中的数据库访问层和单元测试用例 -
fantasybei:
-__-!!
使用Java Servlet动态生成图片 -
ericjoe:
建议放到问答板块,获取最佳答案
棘手问题,求帮忙 -
geminiyellow:
不知道哪个31
搭错车 -
lihuiqi_cn:
少女杀手 写道哈哈,差不多啦,我上次去城隍庙玩,回来做过站了, ...
搭错车
在SpringSide 3 中,数据库访问层使用的是Hibernate,Hibernate是一个很优秀的ORM框架,是大家耳熟能详的东西了。关于Hibernate的内容,很多人是写了又写,我想我是很难写出新意了。不过我的思路是这样的,我从实际开发的过程出发,写出在SpringSide 3中使用Hibernate的步骤,在这些步骤中,探讨SpringSide 3对Hibernate的封装,探讨数据持久层的单元测试,探讨二级缓存和性能优化。 我创建一个虚拟的应用场景来做示范,假设我们开发的是一个简单的文章发布系统,实现对文章简单的增删查改功能。同时为了演示多个表之间的关联查询,假设每篇文章有多篇评论。这时,我们需要在数据库中创建两个表,如下:
create table articles(
id int primary key auto_increment, subject varchar ( 20 ) not null , content text ); create table comments( id int primary key auto_increment, content varchar ( 255 ), article_id int not null , foreign key (article_id) references articles(id) );
在MySQL的客户端直接执行上面的SQL语句就可以创建这两个表了。这里需要额外提一下的是我使用的数据库是MySQL,而不是默认的Derby,要把SpringSide创建的项目的数据库更换为MySQL并不难,只需要如下几个步骤: 下一步,编写Entity类:
package cn.puretext.entity.web;
import java.util.LinkedHashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.OneToMany; import javax.persistence.OrderBy; import javax.persistence.Table; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; import cn.puretext.entity.IdEntity; @Entity // 表名与类名不相同时重新定义表名. @Table(name = " articles " ) // 默认的缓存策略. @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) public class Article extends IdEntity { private String subject; private String content; private Set < Comment > comments = new LinkedHashSet < Comment > (); public String getSubject() { return subject; } public void setSubject(String subject) { this .subject = subject; } public String getContent() { return content; } public void setContent(String content) { this .content = content; } @OneToMany(cascade = { CascadeType.ALL }) @JoinColumn(name = " article_id " ) // Fecth策略定义 @Fetch(FetchMode.SUBSELECT) // 集合按id排序. @OrderBy( " id " ) // 集合中对象id的缓存. @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) public Set < Comment > getComments() { return comments; } public void setComments(Set < Comment > comments) { this .comments = comments; } }
package cn.puretext.entity.web;
import javax.persistence.Entity; import javax.persistence.Table; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import cn.puretext.entity.IdEntity; @Entity // 表名与类名不相同时重新定义表名. @Table(name = " comments " ) // 默认的缓存策略. @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) public class Comment extends IdEntity { private String content; public String getContent() { return content; } public void setContent(String content) { this .content = content; } }
下一步,编写DAO类:
package cn.puretext.dao;
import org.springframework.stereotype.Repository; import org.springside.modules.orm.hibernate.HibernateDao; import cn.puretext.entity.web.Article; @Repository public class ArticleDao extends HibernateDao < Article, Long > { }
写到这里,我又忍不住要评论一下江南白衣在项目架构方面的一些习惯了,他的层次太多,这应该是他在实际项目中锤炼出来的经验,但是和教科书上的就不大一样了,教科书上的三层就是三层,而白衣可以把它扩展到4层甚至5层,白衣的层次可以总结成 Entity->DAO->Service(Manager)->Action->View,其中Service这一层命名还不统一,包名是Service,类名中用的是Manager。我觉得这个大家可以探讨探讨,也许白衣认为DAO里面不应该包含业务逻辑,只应该包含CRUD 和分页操作,而Action里面也不应该包含业务逻辑,所以就单独抽出一层来了吧,所以这一层应该称为Bussiness层比较合适,而白衣也认为,有时候DAO层和Bussiness层可以合并在一起。另外,我认为白衣在项目中搞的package也太多了一点,在IDE里面不方便,所以我的实际项目中,我会对包重新进行整理。 2、在DAO类中可以使用Hibernate的原生API。我们来总结一下在Hibernate中通常采用的查询方式:一是使用HQL语言,它的过程基本上是先获取Session,然后创建Query对象,最后通过Query对象执行HQL语句;二是使用条件查询,它的过程基本上是先获取 Session,然后创建Creteria对象,然后执行Creteria对象的list()方法。而在Dao类中,我们可以很简单的通过 sessionFactory.getCurrentSession()来获得Session对象,进而很方便的使用到HQL或者Creteria。 3、在SpringSide 2中,我们可以对数据表中的数据不做物理删除,该特性得益于白衣提供的@Undeletable注解和HibernateEntityExtendDao 类,在SpringSide 3中,该特性没有了。现在回想起来,我觉得该特性也没有什么存在的必要。 后面再继续探讨分页查询和性能优化。现在的任务是赶紧确认一下这Entity层和Dao层能否正常工作,完成该任务的最佳途径,当然是单元测试了。 在SpringSide 3中,编写单元测试非常方便,只需要继承白衣提供的SpringContextTestCase类或者SpringTxTestCase类即可,事实上,只有继承SpringTxTestCase类才能正常工作,因为我们的项目的配置无法让我们工作在非事务的环境下。继承这个类有什么用处呢?它的用处就是可以读取项目中的applicationContext.xml文件,自动建立数据源、Dao对象,并把Dao对象注入到测试用例中,所以,测试类的代码非常简洁,如下:
package cn.puretext.unit.service;
import java.util.List; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springside.modules.orm.Page; import org.springside.modules.test.junit4.SpringTxTestCase; import cn.puretext.dao.ArticleDao; import cn.puretext.entity.web.Article; public class DaoTest extends SpringTxTestCase { @Autowired private ArticleDao articleDao; public ArticleDao getArticleDao() { return articleDao; } public void setArticleDao(ArticleDao articleDao) { this .articleDao = articleDao; } @Test public void addArticle() { Article article = new Article(); article.setSubject( " article test " ); article.setContent( " article test " ); articleDao.save(article); } } 因为该单元测试工作在事务环境下,所以运行单元测试不会改变数据库中的数据。白衣提供的这两个类事实上只是在Spring 2.5的测试框架上做了一点点改进。关于Spring 2.5测试框架的详细介绍,大家可以到“IBM DeveloperWorks 中国”上去看这一篇文章: 但是白衣自己的做法却完全不同,在白衣写的单元测试中,他偏偏用的是EasyMock,关于EasyMock的使用方法,大家可以到“IMB DeveloperWorks 中国”上去看这一篇文章: 再让大家看一下截图,我特地把测试类的代码、JUnit著名的绿条和Hibernate输出的SQL语句放到了一起,如下: 代码比较简单,只是为了证明上面写的Entity和Dao能够正常运行。在下面的内容里,随着我们的探讨,测试代码的内容会逐渐增加。 上文的内容演示了SpringSide 3中Hibernate的使用过程和单元测试,也提到了SpringSide 3提供的CRUD封装,这些都很简单。在SpringSide 3对Hibernate的封装中,还有一个重点,那就是分页查询。 分页查询有HibernateDao类实现,要配合Page类来使用。Page类一般用来设置查询条件,并返回查询结果,举例说明,如果对Articles表中的数据进行分页显示,每一页10条记录,那么查询第二页应该怎么办呢?代码如下:
@Test
public void pageQuery() { Page < Article > page = new Page < Article > (); page.setPageSize( 10 ); page.setPageNo( 2 ); page = articleDao.getAll(page); List < Article > articles = page.getResult(); }
Page只是一个辅助类,真正的查询操作是在HibernateDao类中完成的,具体代码如下:
/**
* 按Criteria分页查询. * * @param page 分页参数. * @param criterions 数量可变的Criterion. * * @return 分页查询结果.附带结果列表及所有查询时的参数. */ @SuppressWarnings( " unchecked " ) public Page < T > find( final Page < T > page, final Criterion ![]() Assert.notNull(page, " page不能为空 " ); Criteria c = createCriteria(criterions); if (page.isAutoCount()) { int totalCount = countCriteriaResult(c); page.setTotalCount(totalCount); } setPageParameter(c, page); List result = c.list(); page.setResult(result); return page; }
下面跟踪到setPageParameter方法中,其代码如下:
protected Criteria setPageParameter( final Criteria c, final Page < T > page) {
// hibernate的firstResult的序号从0开始 c.setFirstResult(page.getFirst() - 1 ); c.setMaxResults(page.getPageSize()); /* 以下代码省略 */ }
讲到这里,我得提一下我的技术背景:在使用MySQL之前,我有很长一段时间使用的是MS SQL Server 2000。为什么要提这个问题呢?那是因为站在SQL Server 2000的角度,处理分页问题是比较困难的。在SQL Server 2000中,如果要获取指定条数的记录,只能使用top关键字,也就是说要获取10条数据,就应该使用select * top 10 from articles,那么怎么定位到第二页呢?就必须知道第二页的第一条数据的ID是多少,然后用这样的语句select * top 10 from articles where id >= ?,那怎么知道第二页的第一条记录的ID是多少呢?免不了又要多一次查询如select id top 20 from articles order by id desc。 所以在SQL Server 2000中,要实现分页查询比较困难,不是思考起来困难,而是提高效率困难,必须得避免多次查询。解决的办法当然有,要么使用存储过程,要么在前面的select语句中加入子查询。但是不管采取哪种办法,SQL语句写起来都不简单。 在MySQL中,该问题就简单多了,MySQL不提供top,但提供limit,更重要的是limit接受两个参数,而不是像top只接受一个参数。limit后面的参数可以是{[offset,] row_count | row_count OFFSET offset},其中的offset就代表了第2页的第一条数据所在的位置,大家请注意,这里说的是位置,而不是SQL Server 2000中的ID,这两者是有区别的,因为ID可能不连续,而位置肯定是连续的,所以位置是可以通过简单的数学计算来获得的,这样,MySQL就只需要生成一个简单的SQL语句select * from articles limit 10,10。 下面是Hibernate自己生成的SQL语句: 为了和SQL Server 2000对比,我把配置文件中的Dialect改为org.hibernate.dialect.SQLServerDialect,得到的SQL语句如下: 因为我没有把数据库迁移到SQL Server,所以该语句一运行就出错了,不过从该语句中的top 20也可以看出,要么该语句的作用是为了得到第二页的第一条记录的ID,然后后面再跟一条SQL语句,只不过因为出现错误,所以后面的语句没有显示出来,要么是直接取出20条记录,并抛弃10条,只留下第二页的数据。总之,和我之前预想的一样,性能得不到保证。 通过搜索引擎我还查出,Oracle也不支持limit语句,所以说,我们不能完全相信Hibernate,必要的时候,还是得靠自己写存储过程。 Fetch策略也是影响性能的一个方面,Fetch策略主要是针对Entity中的集合数据,正如白衣所说,很多人多只知道使用默认的Lazy策略,我就是这很多人中的一个,以前我还因为Lazy策略出现过问题,什么问题呢,那就是我先获取一个Entity的数据,然后把在Entity保存到 HttpSession中,然后在使用该对象中的集合数据时,就报错了,为什么呢,因为这个时候Hibernate的Session早就关闭了,所以出错。 关于Fetch策略的选择,SpringSide的文档和Hibernate的文档上面都写得很清楚,我就不罗嗦了,至于在代码中怎么设置Fetch策略,代码的注释很清楚,一看就会。 最后谈一谈二级缓存,Session中的缓存是一级缓存,ehcache提供二级缓存,关于二级缓存的配置,主要涉及到两个地方,一个是xml配置文件,另一个是Entity类中的注解,xml配置文件中配置的是ehcache的属性,而Entity中的注解设置了隔离级别,具体内容请参阅 SpringSide 3 的文档。 |
发表评论
-
Oracle、SQL和DB2分页查询写法介绍
2013-01-21 09:54 0DB2分页查询和Oracle、SQL中的分页查询语句写法都不太 ... -
表单提交:ognl.OgnlException: target is null for setProperty(null, "id", [Ljava.lang.
2011-05-17 10:28 3185表单提交:ognl.OgnlException: target ... -
hibernate annoation (三 id生成器)
2011-04-27 14:25 1259id生成: hibernate内不可使用的id生成器可见代码 ... -
Jquery插件--easyUI属性汇总
2011-04-26 14:35 1412找了个时间看了下EasyUI ... -
崩溃了!
2010-11-17 17:19 766最近在研究jms的东西,第一次接触这个有点困难,希望了解的人多 ... -
一种不等概率随机数产生办法
2010-09-28 14:17 1904一种不等概率随机数产生办法 Java实用工具库Java.ut ... -
支付接口
2010-09-02 08:46 781查支付宝接口查到的文件。 -
使用Java Servlet动态生成图片
2009-12-08 09:16 767在Web应用中,经常需要动态生成图片,比如实时股市行情,各种统 ... -
棘手问题,求帮忙
2009-10-24 11:31 802做项目时遇到的问题,求高人指点。 1.传unicode编码 ... -
公司框架
2009-08-15 09:25 903在看公司框架时碰到了 ... -
java的反射机制
2009-02-27 17:20 890public class Person { // 私有的属性 ... -
Hibernate 连接池配置
2009-02-24 19:07 2455Hibernate 连接池配置 <!-- jdbc - ...
相关推荐
在SpringSide3中,你会了解到如何通过XML配置或注解方式实现依赖注入,以及如何使用ApplicationContext管理bean。 三、面向切面编程 AOP允许开发者将关注点分离,如日志、事务等,从而提高代码的可读性和可维护性。...
- **测试支持**:内置了JUnit和Mockito等测试工具,鼓励开发者进行单元测试和集成测试,保证代码质量。 4. **开发实践**: 使用SpringSide 3时,开发者可以通过其提供的模板快速创建项目结构,然后根据需求添加和...
2. **注释与文档**:springside3的源码中包含了丰富的Javadoc注释,便于开发者理解代码功能和使用方法。同时,项目还提供了详细的API文档,有助于快速上手。 三、springside3的jar包使用 1. **依赖管理**:...
- **项目结构**:GameSky 示例项目展示了 SpringSide3 的实际应用场景,包括 MVC 结构、服务层设计、数据库交互等。 - **业务逻辑**:GameSky 可能是一个游戏天空平台,涉及用户管理、游戏信息展示、游戏排行榜等...
4. **单元测试支持**:内置了对JUnit和Mockito的支持,使得单元测试变得简单易行,确保了代码质量。 5. **最佳实践**:SpringSide3-core集成了许多最佳实践,如日志管理、异常处理、国际化等,为开发者提供了一套...
- **Action层:** Springside3中Action层的设计主要依赖于Struts2的Convention插件,该插件能够自动识别和配置Action,使得Action的配置变得更加简洁。 - **示例代码:** ```java @Namespace("/security") public...
JUnit和Mockito等工具被用来编写和执行测试用例,保证代码质量。同时,SpringSide支持与Jenkins等持续集成工具的集成,实现自动化构建和部署。 九、示例项目 在"springside"这个压缩包文件中,包含了各种示例项目的...
6. **数据访问层**:通过集成Hibernate或MyBatis,springside简化了数据库操作,提供了ORM(对象关系映射)功能,使得开发者可以更专注于业务逻辑。 7. **安全控制**:springside内置了Spring Security模块,用于...
6. **测试框架集成**:项目包含了对JUnit和Mockito等测试框架的集成,源码中包含了大量单元测试,这为我们提供了良好的测试实践示例,有助于理解如何编写高效的测试代码。 7. **Maven构建**:springside3-core-...
1. 单元测试:SpringSide 强调测试驱动开发,每个模块都有相应的单元测试,使用了 JUnit 和 Mockito 进行测试。 2. MVC 设计模式:在 Web 模块中,模型(Model)、视图(View)和控制器(Controller)之间分工明确,...
5. **单元测试**:SpringSide强调TDD(测试驱动开发),因此在项目中会有详尽的单元测试,这不仅有助于保证代码质量,也提供了学习如何编写测试用例的机会。 6. **最佳实践**:SpringSide遵循Pragmatic编程原则,...
4. **src/test/java** 和 **src/test/resources**:这两个目录分别用于存放测试用例的源代码和相关资源。SpringSide Quickstart会使用JUnit进行单元测试,你将在这里看到测试类,它们通常会继承自SpringJUnit4Class...
这包括依赖注入(DI)、AOP(面向切面编程)、MVC(模型-视图-控制器)架构模式以及Spring的数据访问层支持,如JdbcTemplate和HibernateTemplate。 5. **SSH整合** "ssh3.3"这个文件名暗示了SpringSide 3.3可能...
6. **单元测试与集成测试**:SpringSide 3.0 强调测试驱动开发,提供了JUnit和Mockito等工具进行单元测试,以及Spring Test进行集成测试,确保代码质量。 7. **国际化支持**:SpringSide 3.0 支持多语言环境,通过...
源码中包含了这些测试用例,有助于理解如何在Spring项目中进行高质量的测试。 8. **Maven构建系统**:springside-3.1.3.1-src使用Maven作为构建工具,展示了如何利用Maven的依赖管理和生命周期管理功能,进行项目的...
学习如何编写测试用例,可以确保代码的质量和可维护性。 - **RESTful API 设计**:SpringSide 通常会遵循 RESTful 架构风格设计 Web API,这包括了 HTTP 方法的选择、URL 路径的设计以及 JSON 数据的序列化和反序列...
"TestSpringSide" 中的测试类可以教会我们如何编写单元测试和集成测试,确保代码质量。 总的来说,SpringSide 3.3.4 实例为我们提供了一个全面的学习平台,通过分析 "TestSpringSide" 文件,我们可以掌握 ...
3. **数据访问层**:支持多种ORM框架,如Hibernate和MyBatis,提供统一的DAO接口,降低了数据访问层的复杂性。 4. **RESTful API**:通过Spring MVC实现RESTful服务,方便前后端分离的开发模式。 四、使用场景与...