Spring3 MVC REST + JPA2 (Hibernate 3.6.1) 构建投票系统 - 3. JPA2(Hibernate实现)

上一篇介绍了如何使用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来解除事务

  1. propagation=Propagation.NOT_SUPPORTED 表示与事务无关
  2. 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了,首先我们看看页面的样子

Spring3 MVC REST + JPA2 (Hibernate 3.6.1) 构建投票系统 - 3. JPA2(Hibernate实现)_第1张图片

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

Spring3 MVC REST + JPA2 (Hibernate 3.6.1) 构建投票系统 - 3. JPA2(Hibernate实现)_第2张图片

成功页面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>
 

 

查看数据库资料

Spring3 MVC REST + JPA2 (Hibernate 3.6.1) 构建投票系统 - 3. JPA2(Hibernate实现)_第3张图片

可以看到用户注册成功

 

View -> Controller -> Service -> DB这一流程基本上就完成了。用户登录就不详细了,具体可以参考本章的源代码

下一章将介绍如何实现投票

 

上一篇:Spring3 MVC REST + JPA2 (Hibernate 3.6.1)构建投票系统 - 2.Spring MVC REST

你可能感兴趣的:(spring,mvc,Hibernate,REST,jpa)