Alexey是一个在使用Java,TestNG 和Selenium的自动化WEB应用程序中有丰富经验的测试开发者。他如此的喜欢QA以至于在下班后他为初级QA工程师提供培训课程。
在这篇文章中我想介绍一个Spring MVC + Hibernate + Maven例子。这组技术主要涉及一些基础知识,我想在每一个必要的地方详细解释它。本篇话题范围以外的更多资源,我会提供链接方便你阅读。在文章的最后,我将发布一个GitHub的链接。
目标
示例web应用程序是基于Spring MVC, Hibernate, Maven的,界面是基于HTML的。这个应用程序将提供所有的CRUD操作:增删改查。和往常一样,我将使用Mysql作为我的数据库。这个应用程序将 把足球俱乐部相关的实体来作为示例,所以这个教程将会涉及运动领域。
准备
我需要在数据库中创建一个表,下面就是创建它的代码:
1
2
3
4
5
6
|
CREATE
TABLE
`teams` (
`id`
int
(6)
NOT
NULL
AUTO_INCREMENT,
`
name
`
varchar
(40)
NOT
NULL
,
`rating`
int
(6)
NOT
NULL
,
PRIMARY
KEY
(`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6
DEFAULT
CHARSET=utf8;
|
这个表将在应用程序中用下面的类来表示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
@Entity
@Table
(name=
"teams"
)
public
class
Team {
@Id
@GeneratedValue
private
Integer id;
private
String name;
private
Integer rating;
public
Integer getId() {
return
id;
}
public
void
setId(Integer id) {
this
.id = id;
}
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
}
public
Integer getRating() {
return
rating;
}
public
void
setRating(Integer rating) {
this
.rating = rating;
}
}
|
然后我需要在IDE(我使用Eclipse)里面创建一个Maven项目。我将略过创建的详细过程,你可以查看它在我的关于Maven项目的创建的文章里,这是pom.xml文件的链接。首先最重要的一点就是WebAppConfig.java 文件,所以我开始吧:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
@Configuration
@ComponentScan
(
"com.sprhib"
)
@EnableWebMvc
@EnableTransactionManagement
@PropertySource
(
"classpath:application.properties"
)
public
class
WebAppConfig {
private
static
final
String PROPERTY_NAME_DATABASE_DRIVER =
"db.driver"
;
private
static
final
String PROPERTY_NAME_DATABASE_PASSWORD =
"db.password"
;
private
static
final
String PROPERTY_NAME_DATABASE_URL =
"db.url"
;
private
static
final
String PROPERTY_NAME_DATABASE_USERNAME =
"db.username"
;
private
static
final
String PROPERTY_NAME_HIBERNATE_DIALECT =
"hibernate.dialect"
;
private
static
final
String PROPERTY_NAME_HIBERNATE_SHOW_SQL =
"hibernate.show_sql"
;
private
static
final
String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN =
"entitymanager.packages.to.scan"
;
@Resource
private
Environment env;
@Bean
public
DataSource dataSource() {
DriverManagerDataSource dataSource =
new
DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
return
dataSource;
}
@Bean
public
LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactoryBean =
new
LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setPackagesToScan(env.getRequiredProperty(
PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
sessionFactoryBean.setHibernateProperties(hibProperties());
return
sessionFactoryBean;
}
private
Properties hibProperties() {
Properties properties =
new
Properties();
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
return
properties;
}
@Bean
public
HibernateTransactionManager transactionManager() {
HibernateTransactionManager transactionManager =
new
HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return
transactionManager;
}
@Bean
public
UrlBasedViewResolver setupViewResolver() {
UrlBasedViewResolver resolver =
new
UrlBasedViewResolver();
resolver.setPrefix(
"/WEB-INF/pages/"
);
resolver.setSuffix(
".jsp"
);
resolver.setViewClass(JstlView.
class
);
return
resolver;
}
}
|
在这个文件开头,你看见了注解@EnableTransactionManagement,它可以使Spring的注解驱动事务管理器生效工作。注 解@PropertySource(“classpath:application.properties”)定位属性文件所在的资源文件夹。注意着三个 beans:transactionManager, sessionFactory, dataSource,这些beans提供了事务管理。更多信息可以阅读我的关于Hibernate功能的文章。
1
2
3
4
5
6
7
8
9
10
|
#DB properties:
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql:
//localhost:3306/hibnatedb
db.username=hibuser
db.password=root
#Hibernate Configuration:
hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
hibernate.show_sql=
true
entitymanager.packages.to.scan=com.sprhib.model
|
以上是我和项目有关的所有准备,接下来我将向你展示DAO层和Service层。
DAO层和Service层
下面是DAOs和Services接口和实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
public
interface
TeamDAO {
public
void
addTeam(Team team);
public
void
updateTeam(Team team);
public
Team getTeam(
int
id);
public
void
deleteTeam(
int
id);
public
List
getTeams();
}
@Repository
public
class
TeamDAOImpl
implements
TeamDAO {
@Autowired
private
SessionFactory sessionFactory;
private
Session getCurrentSession() {
return
sessionFactory.getCurrentSession();
}
public
void
addTeam(Team team) {
getCurrentSession().save(team);
}
public
void
updateTeam(Team team) {
Team teamToUpdate = getTeam(team.getId());
teamToUpdate.setName(team.getName());
teamToUpdate.setRating(team.getRating());
getCurrentSession().update(teamToUpdate);
}
public
Team getTeam(
int
id) {
Team team = (Team) getCurrentSession().get(Team.
class
, id);
return
team;
}
public
void
deleteTeam(
int
id) {
Team team = getTeam(id);
if
(team !=
null
)
getCurrentSession().delete(team);
}
@SuppressWarnings
(
"unchecked"
)
public
List
getTeams() {
return
getCurrentSession().createQuery(
"from Team"
).list();
}
}
|
注解 @Repository 表明被注解的类是一个DAO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
public
interface
TeamService {
public
void
addTeam(Team team);
public
void
updateTeam(Team team);
public
Team getTeam(
int
id);
public
void
deleteTeam(
int
id);
public
List
getTeams();
}
@Service
@Transactional
public
class
TeamServiceImpl
implements
TeamService {
@Autowired
private
TeamDAO teamDAO;
public
void
addTeam(Team team) {
teamDAO.addTeam(team);
}
public
void
updateTeam(Team team) {
teamDAO.updateTeam(team);
}
public
Team getTeam(
int
id) {
return
teamDAO.getTeam(id);
}
public
void
deleteTeam(
int
id) {
teamDAO.deleteTeam(id);
}
public
List
getTeams() {
return
teamDAO.getTeams();
}
}
|
注解@Service表明备注解的类是一个“Service”。注解@Transactional在一个方法或者是类上声明一个事务。
控制器和JSPs
现在我就要涵盖所有的 CRUD操作,这一章会有点长。我将从最基础的控制器开始,它负责主页:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Controller
public
class
LinkController {
@RequestMapping
(value=
"/"
)
public
ModelAndView mainPage() {
return
new
ModelAndView(
"home"
);
}
@RequestMapping
(value=
"/index"
)
public
ModelAndView indexPage() {
return
new
ModelAndView(
"home"
);
}
}
|
它挺简单的,这是JSP文件:
1
2
3
4
5
6
7
8
|
...
<h1>Home page</h1>
<p>
${message}
<a href=
"${pageContext.request.contextPath}/team/add.html"
>Add
new
team</a>
<a href=
"${pageContext.request.contextPath}/team/list.html"
>Team list</a>
</p>
...
|
下面是这里是核心控制器,主要应用程序的控制器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
@Controller
public
class
TeamController {
@Autowired
private
TeamService teamService;
@RequestMapping
(value=
"/team/add"
)
public
ModelAndView addTeamPage() {
ModelAndView modelAndView =
new
ModelAndView(
"add-team-form"
);
modelAndView.addObject(
"team"
,
new
Team());
return
modelAndView;
}
@RequestMapping
(value=
"/team/add/process"
)
public
ModelAndView addingTeam(
@ModelAttribute
Team team) {
ModelAndView modelAndView =
new
ModelAndView(
"home"
);
teamService.addTeam(team);
String message =
"Team was successfully added."
;
modelAndView.addObject(
"message"
, message);
return
modelAndView;
}
@RequestMapping
(value=
"/team/list"
)
public
ModelAndView listOfTeams() {
ModelAndView modelAndView =
new
ModelAndView(
"list-of-teams"
);
List
teams = teamService.getTeams();
modelAndView.addObject(
"teams"
, teams);
return
modelAndView;
}
@RequestMapping
(value=
"/team/edit/{id}"
, method=RequestMethod.GET)
public
ModelAndView editTeamPage(
@PathVariable
Integer id) {
ModelAndView modelAndView =
new
ModelAndView(
"edit-team-form"
);
Team team = teamService.getTeam(id);
modelAndView.addObject(
"team"
,team);
return
modelAndView;
}
@RequestMapping
(value=
"/team/edit/{id}"
, method=RequestMethod.POST)
public
ModelAndView edditingTeam(
@ModelAttribute
Team team,
@PathVariable
Integer id) {
ModelAndView modelAndView =
new
ModelAndView(
"home"
);
teamService.updateTeam(team);
String message =
"Team was successfully edited."
;
modelAndView.addObject(
"message"
, message);
return
modelAndView;
}
@RequestMapping
(value=
"/team/delete/{id}"
, method=RequestMethod.GET)
public
ModelAndView deleteTeam(
@PathVariable
Integer id) {
ModelAndView modelAndView =
new
ModelAndView(
"home"
);
teamService.deleteTeam(id);
String message =
"Team was successfully deleted."
;
modelAndView.addObject(
"message"
, message);
return
modelAndView;
}
}
|
基本上所有的方法和请求映射都是很清晰的。请注意editTeamPage() 和edditingTeam() 方法的@RequestMapping,对于不同的method属性包含不同的值。
现在我们来看看JSP页面:
“Add new team” 页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
...
<h1>Add team page</h1>
<p>Here you can add a
new
team.</p>
<form:form method=
"POST"
commandname=
"team"
action=
"${pageContext.request.contextPath}/team/add/process.html"
>
<table>
<tbody>
<tr>
<td>Name:</td>
<td><form:input path=
"name"
></form:input></td>
</tr>
<tr>
<td>Rating:</td>
<td><form:input path=
"rating"
></form:input></td>
</tr>
<tr>
<td><input value=
"Add"
type=
"submit"
></td>
<td></td>
</tr>
</tbody>
</table>
</form:form>
<p><a href=
"${pageContext.request.contextPath}/index.html"
>Home page</a></p>
...
|
“List of teams” 页面:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
...
<h1>List of teams</h1>
<p>Here you can see the list of the teams, edit them, remove or update.</p>
<c:foreach var=
"team"
items=
"${teams}"
>
</c:foreach><table border=
"1px"
cellpadding=
"0"
cellspacing=
"0"
>
<thead>
<tr>
<th width=
"10%"
>id</th><th width=
"15%"
>name</th><th width=
"10%"
>rating</th><th width=
"10%"
>actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>${team.id}</td>
<td>${team.name}</td>
<td>${team.rating}</td>
<td>
<a href=
"${pageContext.request.contextPath}/team/edit/${team.id}.html"
>Edit</a>
<a href=
"${pageContext.request.contextPath}/team/delete/${team.id}.html"
>Delete</a>
</td>
</tr>
</tbody>
</table>
<p><a href=
"${pageContext.request.contextPath}/index.html"
>Home page</a></p>
...
|
“Edit team” 页面:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
...
<h1>Edit team page</h1>
<p>Here you can edit the existing team.</p>
<p>${message}</p>
<form:form method=
"POST"
commandname=
"team"
action=
"${pageContext.request.contextPath}/team/edit/${team.id}.html"
>
<table>
<tbody>
<tr>
<td>Name:</td>
<td><form:input path=
"name"
></form:input></td>
</tr>
<tr>
<td>Rating:</td>
<td><form:input path=
"rating"
></form:input></td>
</tr>
<tr>
<td><input value=
"Edit"
type=
"submit"
></td>
<td></td>
</tr>
</tbody>
</table>
</form:form>
<p><a href=
"${pageContext.request.contextPath}/index.html"
>Home page</a></p>
...
|
“List of teams” 页面的效果:
总结
几个技术的整合通常不是一件简单的事情,所有如果要想成功就需要有耐心。在这篇文章中没有囊括所有的资源,你可以访问GitHub去查看哪些你感兴趣的类。