上一篇介绍了如何使用Spring MVC搭建REST的web应用,今天主要介绍如何使用JPA2.0实现数据库操作
JPA2.0只是一种规范,实现的框架有多种,包括几个较为常用的:
- TopLink - Sun
- OpenJPA - Apache
- Hibernate - Jboss
当然还有更多其它的实现框架,但这些不是本文讨论的范围。
以前用得最多的iBatis框架,后来因为工作的关系开始学习使用Hibernate,所以这次就是用Hibernate框架来完成JPA的应用。
闲话少说,我们来关注一下如何使用JPA来完成我们的后台业务逻辑。我们先从Domain开始
- Domain
以下使用用户表User作为例子,他对应数据库表为
T_USER(*USER_OID, VERSION_NO, USER_NAME, PASSWORD, NICK_NAME, GENDER)
Java代码如下:
package ben.vote.domain;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Version;
import ben.vote.domain.type.Gender;
@Entity
@Table(name = "T_USER")
public class User implements Serializable{
private static final long serialVersionUID = 8787093968860546113L;
private Long userOid;
private Long versionNo;
private String userName;
private String nickName;
private String password;
private Gender gender;
@Id
@GeneratedValue
@Column(name="USER_OID", unique=true, nullable=false)
public Long getUserOid() {
return userOid;
}
public void setUserOid(Long userOid) {
this.userOid = userOid;
}
@Version
@Column(name="VERSION_NO")
public Long getVersionNo() {
return versionNo;
}
public void setVersionNo(Long versionNo) {
this.versionNo = versionNo;
}
@Column(name="USER_NAME", length=40, unique=true, nullable=false)
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Column(name="NICK_NAME", length=80, unique=true, nullable=false)
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
@Column(name="PASSWORD", length=80, nullable=false)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Embedded
@Column(name="GENDER", length=1)
public Gender getGender() {
return gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
}
这里只使用了一小部分的annotation,其他的annotation就不一一列举了,有需要大家查相关文档。
类User里面用到的Annontation很简单,而且也很好理解,除了其中两个需要稍作说明,其他就不一一描述了
@Version 声明这列用于版本控制,使用的是VersionNo乐观锁
@Embbeded 声明此列和枚举类Gender相关联
枚举类Gender有三种类型,分别是Male和Female,还有Other(我们要尊重性别选择),代码如下:
package ben.vote.domain.type;
public enum Gender {
M("Male"),
F("Female"),
O("Other");
private Gender(String value){
this.value = value;
}
private String value;
public String getValue(){
return value;
}
}
- Service
下一步我们构建Service来实现domain是数据库的交互。先定义一个接口ICURDService,具体如下
package ben.vote.service;
import java.util.List;
import java.util.Map;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Transactional
public interface ICRUDService<E> {
void create(E domain);
void delete(E domain);
E update(E domain);
@Transactional(propagation=Propagation.NOT_SUPPORTED, readOnly = true)
E query(Class<E> c, Long id);
@Transactional(propagation=Propagation.NOT_SUPPORTED, readOnly = true)
List<E> query(Class<E> c, Map<String, Object> params);
@Transactional(propagation=Propagation.NOT_SUPPORTED, readOnly = true)
List<E> queryAll(Class<E> c);
}
顾名思义,这个接口主要声明的服务是C reate, R etrieve, U pdate, D elete。
@Transactional 表示这个接口类将由Spring来管理事务
而由于查询类没有事务的必要,所以也是用annontation来解除事务
- propagation=Propagation.NOT_SUPPORTED 表示与事务无关
- readOnly表示只读
有了接口我们就可以开始编写实现类,具体如下
package ben.vote.service.impl;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.stereotype.Service;
import ben.vote.service.ICRUDService;
//@Service(value="crudService")
public class CRUDServiceImpl<E> implements ICRUDService<E>{
@PersistenceContext
protected EntityManager entityManager;
public void create(E entity) {
entityManager.persist(entity);
}
public void delete(E entity) {
entityManager.remove(entity);
}
public E query(Class<E> c, Long id) {
return entityManager.find(c, id);
}
@SuppressWarnings("unchecked")
public List<E> queryAll(Class<E> c) {
final String HQL = "from " + c.getSimpleName();
return entityManager.createQuery(HQL).getResultList();
}
@SuppressWarnings("unchecked")
public List<E> query(Class<E> c, Map<String, Object> params) {
if (null == params || params.isEmpty()) {
return queryAll(c);
} else {
StringBuilder queryBuilder = new StringBuilder()
.append("from ")
.append(c.getSimpleName())
.append(" o where ");
Set<String> keys = params.keySet();
//Where
int count = 0;
for (String key : keys){
if (count > 0) {
queryBuilder.append(" and ");
} else {
count ++;
}
queryBuilder.append("(").append(key).append(" = :").append(key).append(")");
}
Query query = entityManager.createQuery(queryBuilder.toString());
for (String key : keys){
query.setParameter(key, params.get(key));
}
return query.getResultList();
}
}
public E update(E entity) {
return entityManager.merge(entity);
}
}
可以看出,这个类写得简陋的来也相当的丑陋,见笑了。
@Service(name="crudService") 是我用来做Unit test用的,这里可以没有这个必要
到这里Service基本上完成了,和接口一样,实现类只保留了泛型,并没有指向具体的Domain,具体的还是由业务来声明
- Controller
上一篇已经知道如何使用Spring MVC构建Web的REST应用,这里就沿用上一篇的内容,添加一个用户Controller
package ben.vote.web;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import ben.vote.domain.User;
import ben.vote.domain.type.Gender;
import ben.vote.service.ICRUDService;
@Controller
public class UserController {
@Autowired private ICRUDService<User> userService;
/**
* Access GET request forward to register form
*
* @return
*/
@RequestMapping(value="/register", method=RequestMethod.GET)
public String register(){
return "/register/form";
}
/**
* Access POST request the register a new User
*
* @param request
* @param response
* @return
*/
@RequestMapping(value="/register", method=RequestMethod.POST)
public String register(HttpServletRequest request, HttpServletResponse response) {
String userName = request.getParameter("userName");
String password = request.getParameter("password");
String rePassword = request.getParameter("rePassword");
String nickName = request.getParameter("nickName");
String gender = request.getParameter("gender");
//Validation before save
boolean hasException = false;
if (userName == null || "".equals(userName.trim())){
request.setAttribute("userNameException", "Please fill in User Name!");
hasException = true;
} else if (isExistsUser("userName", userName)) {
request.setAttribute("userNameException", "User is exists!");
hasException = true;
}
//Password & re-password should not be null
if (password == null || "".equals(password.trim())){
request.setAttribute("passwordException", "Please fill in Password!");
hasException = true;
}
if (rePassword == null || "".equals(rePassword.trim())){
request.setAttribute("rePasswordException", "Please fill in Re-Password!");
hasException = true;
}
//Password and re-password should be the same
if (!password.equals(rePassword)){
request.setAttribute("rePasswordException", "Password are not equals!");
hasException = true;
}
if (nickName == null || "".equals(nickName.trim())){
request.setAttribute("nickNameException", "Please fill in Name!");
} else if (isExistsUser("nickName", nickName)) {
request.setAttribute("nickNameException", "Nick name is exists!");
hasException = true;
}
//Handle exception
if (hasException) {
request.setAttribute("userName", userName);
request.setAttribute("nickName", nickName);
request.setAttribute("gender", gender);
return register();
}
User user = new User();;
user.setUserName(userName);
user.setPassword(password.trim());
user.setNickName(nickName);
user.setGender(Gender.valueOf(gender));
//Save the user
userService.create(user);
//Put user into session
request.getSession().setAttribute("logonUser", user);
//Redirect to success page
return "redirect:/register/success";
}
/**
* Access GET request forward to register form
*
* @return
*/
@RequestMapping(value="/register/success", method=RequestMethod.GET)
public String registerSuccess() {
return "/register/success";
}
/**
* Validate the specific user is exists or not
*
* @param key the column name
* @param value the column value
* @return
*/
private boolean isExistsUser(String key, String value) {
Map<String, Object> params = new HashMap<String, Object>();
params.put(key, value);
List<User> userList = userService.query(User.class, params);
return (userList != null && !userList.isEmpty());
}
}
Controller准备好了之后,接下来就可以编写jsp了,首先我们看看页面的样子
Form 源代码如下
<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<html>
<head>
<%@include file="../include/INC.HEADER.jspf" %>
</head>
<body>
<div class="titlebar">
<span class="title">Vote</span>
<span class="sub-title"> - User Registration</span>
</div>
<div class="title-option">
<a href="<%=basePath%>/login">Login</a>
</div>
<hr/>
<div style="color: red">${registerException}</div>
<form action="register" method="POST">
<table>
<tr>
<td class="label">User Name *</td>
<td>
<input type="text" name="userName" value="${userName}"/>
<span style="color: red">${userNameException}</span>
</td>
</tr>
<tr>
<td class="label">Password *</td>
<td>
<input type="password" name="password" value="${password}"/>
<span style="color: red">${passwordException}</span>
</td>
</tr>
<tr>
<td class="label">Re-Password *</td>
<td>
<input type="password" name="rePassword" value="${rePassword}"/>
<span style="color: red">${rePasswordException}</span>
</td>
</tr>
<tr>
<td class="label">Name *</td>
<td>
<input type="text" name="userName" value="${userName}"/>
<span style="color: red">${userNameException}</span>
</td>
</tr>
<tr>
<td class="label">Gender</td>
<td>
<select name="gender" value="${gender}">
<option value="M">Male</option>
<option value="F">Female</option>
<option value="O">Other</option>
</select>
</td>
</tr>
<tr>
<td align="right"><input type="submit" name="submit" value="Submit"/></td>
<td><input type="reset" name="reset" text="Reset"/></td>
</tr>
</table>
</form>
</body>
</html>
填入注册资料后点击Submit
成功页面JSP script
<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<html>
<head>
<%@include file="../include/INC.HEADER.jspf" %>
</head>
<body>
<div class="titlebar">
<span class="title">Vote</span>
<span class="sub-title"> - User Registration</span>
</div>
<div class="title-option">
<a href="<%=basePath%>/login">Login</a>
</div>
<hr/>
<div>Thanks your register, ${logonUser.nickName}</div>
</body>
</html>
查看数据库资料
可以看到用户注册成功
View -> Controller -> Service -> DB这一流程基本上就完成了。用户登录就不详细了,具体可以参考本章的源代码
下一章将介绍如何实现投票
上一篇:Spring3 MVC REST + JPA2 (Hibernate 3.6.1)构建投票系统 - 2.Spring MVC REST