SpringSide 3.1.4.3是目前SpringSide的最新版本,也是完成度比較高的一個(gè)版本,用來(lái)做實(shí)際項(xiàng)目的開(kāi)發(fā)應(yīng)該絲毫不成問(wèn)題。這里寫(xiě)一下使用該版本開(kāi)發(fā)一個(gè)簡(jiǎn)單Web項(xiàng)目的全過(guò)程,當(dāng)然,最重要的是我自己的一些心得體會(huì)。我的文章很長(zhǎng),只有耐下性子細(xì)看,才能體會(huì)個(gè)中三味。
第一步、下載SpringSide 3.1.4.3 all-in-one版。這個(gè)過(guò)程太簡(jiǎn)單了,SpringSide的官方網(wǎng)站是www.springside.org.cn,去那里就可以下載了,all-in-one版當(dāng)然是懶人們的不二選擇。這里有一點(diǎn)很搞笑,該版本標(biāo)的是SpringSide 3.1.4.3,但是下載后解壓縮,解壓縮出來(lái)的文件是springside-3.1.4.2,這可能是江南白衣的一點(diǎn)小小的失誤,據(jù)我猜測(cè),3.1.4.3較3.1.4.1的進(jìn)步應(yīng)該是加入了jsp-api.jar這一個(gè)庫(kù),希望白衣這次不要為了更改這個(gè)版本號(hào)上的失誤而再推出一個(gè)新版本,如果真要推出新版本,怎么樣也應(yīng)該把我最近研究出來(lái)的多數(shù)據(jù)庫(kù)的配置加進(jìn)去。
第二步、安裝SpringSide。如果安裝過(guò)SpringSide以前的版本,最好把用戶(hù)目錄下的.m2文件夾刪掉,這個(gè)文件夾是Maven的本地倉(cāng)庫(kù)所在地,雖說(shuō)Maven可以有效保證庫(kù)文件不會(huì)發(fā)生版本沖突,但是刪除這個(gè)文件夾會(huì)使安裝過(guò)程加快,否則,SpringSide的安裝過(guò)程會(huì)不停詢(xún)問(wèn)你是否覆蓋某某文件。刪除.m2文件夾后,運(yùn)行springside-3.1.4.2目錄下的bin目錄中的quickstart.bat即可(前提條件是已經(jīng)安裝好了JDK5或以上版本,如果你的電腦中連JDK都沒(méi)有,就別來(lái)趟SpringSide的渾水了)。 等待這個(gè)文件運(yùn)行完,就可以看到SpringSide 3提供的三個(gè)示例項(xiàng)目mini-web、mini-service、showcase都運(yùn)行起來(lái)了,這時(shí)你可以細(xì)細(xì)體會(huì)一下SpringSide實(shí)現(xiàn)的各種特性。
仔細(xì)察看SpringSide的bin目錄,發(fā)現(xiàn)該版本提供的腳本更加明確和有用,如start-db.bat可以用來(lái)啟動(dòng)Derby數(shù)據(jù)庫(kù),start-selenium.bat用來(lái)啟動(dòng)selenium server,而start-tomcat.bat那就別說(shuō)了,地球人都知道。
如果要想使用SpringSide來(lái)生成項(xiàng)目,還有一點(diǎn)點(diǎn)小工作要做,就是把Maven的bin目錄加入到PATH環(huán)境變量中,如下圖:

第三步,使用SpringSide生成項(xiàng)目。運(yùn)行bin目錄下的new-project.bat即可,如下圖:
在創(chuàng)建項(xiàng)目的過(guò)程中,該腳本會(huì)提出一些問(wèn)題,其中g(shù)roupId指的是你的組織的名稱(chēng),由于該項(xiàng)目由我私人貢獻(xiàn),純屬示范用,所以我填了youxia.personal,因此,在第5個(gè)問(wèn)題上,我選擇了personal.you作為我項(xiàng)目中的package的名字,這也是符合國(guó)際慣例的;artifactId指的是項(xiàng)目的名字,這里為MultiDatasourceExample,名字有點(diǎn)長(zhǎng),從名字就可以看出來(lái)我要示范多個(gè)數(shù)據(jù)源的配置。
第四步、啟動(dòng)Eclipse,導(dǎo)入項(xiàng)目。 生成的項(xiàng)目位于SpringSide目錄下的tools\generator\generated-project目錄下,下面是Eclipse的截圖:
項(xiàng)目導(dǎo)入成功后,Eclispe資源管理器的截圖:
可以看到,該項(xiàng)目一經(jīng)導(dǎo)入,立即可用,一個(gè)煩人的紅叉都沒(méi)有,這也正說(shuō)明了該版本是SpringSide 3的一個(gè)革命性版本,從該版本開(kāi)始,SpringSide 3的易用性提高了不止一個(gè)檔次。
Eclipse推薦使用3.4及以上版本,因?yàn)樵谠摪姹局?,?duì)Tomcat服務(wù)器的管理更加方便,只需要在項(xiàng)目的快捷菜單中選擇Run On Server,即可自動(dòng)打開(kāi)Tomcat服務(wù)器并部署項(xiàng)目,如下圖:
這里有一點(diǎn)一定要注意,由于SpringSide生成的項(xiàng)目默認(rèn)使用的是Derby數(shù)據(jù)庫(kù),所以要想成功運(yùn)行項(xiàng)目,必須先啟動(dòng)Derby數(shù)據(jù)庫(kù),還記得前面提到的start-db.bat嗎?運(yùn)行它!然后運(yùn)行該項(xiàng)目的bin目錄下的init-db.jar,在數(shù)據(jù)庫(kù)中放入該項(xiàng)目的初始化數(shù)據(jù)。
然后就可以點(diǎn)Run On Server來(lái)啟動(dòng)項(xiàng)目了,讓大家見(jiàn)識(shí)一下Eclipse的嵌入式瀏覽器、Tomcat服務(wù)器視圖、Console視圖。真的是太方便了:
第五步、將數(shù)據(jù)庫(kù)遷移到MySQL中。在項(xiàng)目中,創(chuàng)建數(shù)據(jù)庫(kù)和初始化數(shù)據(jù)庫(kù)的語(yǔ)句都是以SQL文件存在的,如下圖:
但是該語(yǔ)句都是針對(duì)Derby的,如果要應(yīng)用于MySQL,還必須得要做一些修改才行,先修改schema.sql,如下:
drop table if exists RESOURCES_AUTHORITIES;
drop table if exists ROLES_AUTHORITIES;
drop table if exists USERS_ROLES;
drop table if exists RESOURCES;
drop table if exists AUTHORITIES;
drop table if exists USERS;
drop table if exists ROLES;
create table USERS (
ID integer primary key auto_increment,
LOGIN_NAME varchar(20) not null unique,
PASSWORD varchar(20),
NAME varchar(20),
EMAIL varchar(30)
);
create unique index USERS_LOGIN_NAME_INDEX on USERS(LOGIN_NAME);
create table ROLES (
ID integer primary key auto_increment,
NAME varchar(20) not null unique
);
create table USERS_ROLES (
USER_ID integer not null,
ROLE_ID integer not null,
FOREIGN KEY (ROLE_ID) references ROLES(ID),
FOREIGN KEY (USER_ID) references USERS(ID)
);
CREATE TABLE AUTHORITIES (
ID integer primary key auto_increment,
NAME varchar(20) not null,
DISPLAY_NAME varchar(20) not null
);
create table ROLES_AUTHORITIES (
ROLE_ID integer not null,
AUTHORITY_ID integer not null,
FOREIGN KEY (ROLE_ID) references ROLES(ID),
FOREIGN KEY (AUTHORITY_ID) references AUTHORITIES(ID)
);
CREATE TABLE RESOURCES (
ID integer primary key auto_increment,
RESOURCE_TYPE varchar(20) not null,
VALUE varchar(255) not null,
ORDER_NUM float not null
);
create table RESOURCES_AUTHORITIES (
AUTHORITY_ID integer not null,
RESOURCE_ID integer not null,
FOREIGN KEY (AUTHORITY_ID) references AUTHORITIES(ID),
FOREIGN KEY (RESOURCE_ID) references RESOURCES(ID)
); 該修改主要包含兩個(gè)地方,一個(gè)是在drop table后面加上了if exists,一個(gè)是把GENERATED ALWAYS as IDENTITY修改為auto_increment。而load-data.sql不需要修改。
然后,啟動(dòng)MySQL,在MySQL中使用上面的兩個(gè)sql文件創(chuàng)建數(shù)據(jù)庫(kù)和添加初始化數(shù)據(jù),如下圖:
然后更改數(shù)據(jù)庫(kù)連接,修改項(xiàng)目的application.properties文件,如下:
#jdbc settings
jdbc.url=jdbc:mysql://localhost:3306/MultiDatasourceExample?useUnicode=true&characterEncoding=utf8
jdbc.username=youxia
jdbc.password=******
#hibernate settings
hibernate.show_sql=false
hibernate.format_sql=false
hibernate.ehcache_config_file=/ehcache/ehcache-hibernate-local.xml 修改項(xiàng)目的applicationContext.xml文件,這里要修改兩個(gè)地方,一個(gè)為DriverClassName,一個(gè)為hibernate.dilect,如下:
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
default-lazy-init="true">
<description>Spring公共配置文件 </description>
<!-- 定義受環(huán)境影響易變的變量 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<!-- 標(biāo)準(zhǔn)配置 -->
<value>classpath*:/application.properties</value>
<!-- 本地開(kāi)發(fā)環(huán)境配置 -->
<value>classpath*:/application.local.properties</value>
<!-- 服務(wù)器生產(chǎn)環(huán)境配置 -->
<!-- <value>file:/var/myapp/application.server.properties</value> -->
</list>
</property>
</bean>
<!-- 使用annotation 自動(dòng)注冊(cè)bean,并保證@Required,@Autowired的屬性被注入 -->
<context:component-scan base-package="personal.youxia" />
<!-- 數(shù)據(jù)源配置,使用應(yīng)用內(nèi)的DBCP數(shù)據(jù)庫(kù)連接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- Connection Info -->
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- Connection Pooling Info -->
<property name="initialSize" value="5" />
<property name="maxActive" value="100" />
<property name="maxIdle" value="30" />
<property name="maxWait" value="1000" />
<property name="poolPreparedStatements" value="true" />
<property name="defaultAutoCommit" value="false" />
</bean>
<!-- 數(shù)據(jù)源配置,使用應(yīng)用服務(wù)器的數(shù)據(jù)庫(kù)連接池 -->
<!--<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/ExampleDB" />-->
<!-- Hibernate配置 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="namingStrategy">
<bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
</prop>
<prop key="hibernate.cache.provider_configuration_file_resource_path">${hibernate.ehcache_config_file}</prop>
</props>
</property>
<property name="packagesToScan" value="personal.youxia.entity.*" />
</bean>
<!-- 事務(wù)管理器配置,單數(shù)據(jù)源事務(wù) -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 事務(wù)管理器配置,多數(shù)據(jù)源JTA事務(wù)-->
<!--
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager or
WebLogicJtaTransactionManager" />
-->
<!-- 使用annotation定義事務(wù) -->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans> 由于SpringSide不提供Mysql的jdbc驅(qū)動(dòng),所以需要自己去MySQL的官方網(wǎng)站下載,將下載到的mysql-connector-5.*.jar復(fù)制到項(xiàng)目的WEB-INF中的lib目錄中。然后運(yùn)行項(xiàng)目,成功。至此,成功將項(xiàng)目遷移到MySQL中。
第六步、添加數(shù)據(jù)表、編寫(xiě)Entity類(lèi)、編寫(xiě)Dao類(lèi)、Manager類(lèi),并進(jìn)行單元測(cè)試。還是以前幾篇文章中提到的文章發(fā)布系統(tǒng)為例,每一篇文章對(duì)應(yīng)多篇評(píng)論,所以說(shuō)據(jù)庫(kù)中需創(chuàng)建articles和comments兩個(gè)數(shù)據(jù)表,如下:
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)
);
在編寫(xiě)Java代碼之前,我還要做一點(diǎn)小工作,什么工作呢?那就是要為我自己的項(xiàng)目創(chuàng)建一個(gè)單獨(dú)的源文件夾,因?yàn)閟rc\main\java這個(gè)文件夾已經(jīng)被江南白衣放入了太多的package,而且因?yàn)樯婕暗絪ecurity,所以層次也不明顯,操作起來(lái)不方便,找起代碼來(lái)也不夠快。下面是我創(chuàng)建了自己的源文件夾后的截圖:
在我自己的源文件夾中,只創(chuàng)建了四個(gè)package,剛好代表從底到上的四個(gè)層次,這樣,找起代碼來(lái)要方便得多。
先來(lái)Entity層,Article.java的代碼如下:
package personal.youxia.entity;
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;
@Entity
// 表名與類(lèi)名不相同時(shí)重新定義表名.
@Table(name = "articles")
// 默認(rèn)的緩存策略.
@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")
// 集合中對(duì)象id的緩存.
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public Set<Comment> getComments() {
return comments;
}
public void setComments(Set<Comment> comments) {
this.comments = comments;
}
} Comment.java如下:
package personal.youxia.entity.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import personal.youxia.entity.IdEntity;
@Entity
// 表名與類(lèi)名不相同時(shí)重新定義表名.
@Table(name = "comments")
// 默認(rèn)的緩存策略.
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Comment extends IdEntity {
private String content;
private Long articleId;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Column(name = "article_id")
public Long getArticleId() {
return articleId;
}
public void setArticleId(Long articleId) {
this.articleId = articleId;
}
}
編寫(xiě)Dao層代碼,ArticleDao.java如下:
package personal.youxia.dao;
import org.springside.modules.orm.hibernate.HibernateDao;
import personal.youxia.entity.Article;
public class ArticleDao extends HibernateDao<Article, Long> {
} CommentDao.java如下:
package personal.youxia.dao;
import org.springside.modules.orm.hibernate.HibernateDao;
import personal.youxia.entity.Comment;
public class CommentDao extends HibernateDao<Comment, Long> {
} 可以看出,以上代碼都從HibernateDao繼承,得益于泛型支持,基本不需要編寫(xiě)一行代碼。
編寫(xiě)B(tài)ussiness層代碼,這一層,白衣使用的包名為service,而類(lèi)名的后綴都是Manager,我就跟他學(xué)算了,懶得改了。
ArticleManager.java如下:
package personal.youxia.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springside.modules.orm.hibernate.HibernateDao;
import personal.youxia.dao.ArticleDao;
import personal.youxia.entity.Article;
public class ArticleManager extends EntityManager<Article, Long> {
@Autowired
private ArticleDao articleDao;
public void setArticleDao(ArticleDao articleDao) {
this.articleDao = articleDao;
}
@Override
protected HibernateDao<Article, Long> getEntityDao() {
// TODO Auto-generated method stub
return articleDao;
}
} CommentManager.java如下:
package personal.youxia.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springside.modules.orm.hibernate.HibernateDao;
import personal.youxia.dao.CommentDao;
import personal.youxia.entity.Comment;
public class CommentManager extends EntityManager<Comment, Long> {
@Autowired
private CommentDao commentDao;
public void setCommentDao(CommentDao commentDao) {
this.commentDao = commentDao;
}
@Override
protected HibernateDao<Comment, Long> getEntityDao() {
// TODO Auto-generated method stub
return commentDao;
}
} 以上代碼大同小異,都是從EntityManager繼承,并使用Spring的IoC特性,將Dao類(lèi)注入到Manager類(lèi)之中,并重載getEntityDao方法來(lái)使用該注入的Dao。這個(gè)時(shí)候,為了驗(yàn)證這些數(shù)據(jù)訪(fǎng)問(wèn)相關(guān)的層能否正常運(yùn)行,可以編寫(xiě)單元測(cè)試。 代碼如下:
package personal.youxia.test;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springside.modules.test.junit4.SpringTxTestCase;
import personal.youxia.entity.entities.Article;
import personal.youxia.entity.entities.Comment;
import personal.youxia.service.ArticleManager;
import personal.youxia.service.CommentManager;
public class DataAccessTest extends SpringTxTestCase {
@Autowired
private ArticleManager articleManager;
@Autowired
private CommentManager commentManager;
public void setArticleManager(ArticleManager articleManager) {
this.articleManager = articleManager;
}
@Test
public void addArticle() {
Comment comment = new Comment();
Article article = new Article();
article.setSubject("test");
article.setContent("test");
articleManager.save(article);
comment.setArticleId(article.getId());
commentManager.save(comment);
}
} 單元測(cè)試一運(yùn)行,發(fā)現(xiàn)了三個(gè)問(wèn)題,先是出現(xiàn)Manager類(lèi)沒(méi)有注入成功的錯(cuò)誤,經(jīng)檢查發(fā)現(xiàn)
所有的Manager類(lèi)都應(yīng)該使用@Service注解,再出現(xiàn)的錯(cuò)誤是提示Dao類(lèi)沒(méi)有注入成功,經(jīng)檢查發(fā)現(xiàn)
所有的Dao類(lèi)須使用@Repository注解,最后出現(xiàn)的錯(cuò)誤是找不到Entity類(lèi)的錯(cuò)誤,經(jīng)檢查發(fā)現(xiàn)
Entity類(lèi)不能位于personal.youxia.entity包中,必須位于其子包中,這是由applicationContext.xml文件中的配置決定的,更改包名為personal.youxia.entity.entities后,問(wèn)題解決。
下一步就應(yīng)該是編寫(xiě)Action和JSP了,由于文章太長(zhǎng),在Blogjava的編輯器中編輯已經(jīng)非常緩慢了,所以只有將該文章分為上中下三部分。且看下回分解!