上一篇介绍了如何使用Spring MVC搭建REST的web应用,今天主要介绍如何使用JPA2.0实现数据库操作
JPA2.0只是一种规范,实现的框架有多种,包括几个较为常用的:
当然还有更多其它的实现框架,但这些不是本文讨论的范围。
以前用得最多的iBatis框架,后来因为工作的关系开始学习使用Hibernate,所以这次就是用Hibernate框架来完成JPA的应用。
闲话少说,我们来关注一下如何使用JPA来完成我们的后台业务逻辑。我们先从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来实现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来解除事务
有了接口我们就可以开始编写实现类,具体如下
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,具体的还是由业务来声明
上一篇已经知道如何使用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