`
Ben.Sin
  • 浏览: 236240 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

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

    博客分类:
  • Java
阅读更多

上一篇介绍了如何使用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了,首先我们看看页面的样子

registe-form

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

Register Successfully

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

 

查看数据库资料

User 01

可以看到用户注册成功

 

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

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

 

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

  • 大小: 57.8 KB
  • 大小: 52.9 KB
  • 大小: 10.4 KB
分享到:
评论
1 楼 aa100044535 2011-04-19  
持续关注,学习中

相关推荐

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

    标题 "Spring3 MVC REST + JPA2 (Hibernate 3.6.1) 构建投票系统 - 2.Spring MVC REST" 提供了我们要探讨的核心技术栈:Spring 3 MVC、RESTful API 和 JPA2(这里指的是 Hibernate 3.6.1 实现)。在这个项目中,开发...

    spring3.x权威开发指南:实施Java EE 6 的利器

    1.3.2 基于SVN库持续构建Spring源码 1.4 小结 2 控制反转容器 2.1 DI及Spring DI概述 2.1.1 面向Java ME/Java SE的BeanFactory 2.1.2 面向Java EE的ApplicationContext 2.2 多种依赖注入方式 2.2.1 设值注入 ...

    Spring攻略(第二版 中文高清版).part2

    11.11.2 解决方案 472 11.11.3 工作原理 472 11.12 使用GORM查询 475 11.12.1 问题 475 11.12.2 解决方案 475 11.12.3 工作原理 475 11.13 创建自定义标记 477 11.13.1 问题 477 11.13.2 解决方案...

    Spring攻略(第二版 中文高清版).part1

    11.11.2 解决方案 472 11.11.3 工作原理 472 11.12 使用GORM查询 475 11.12.1 问题 475 11.12.2 解决方案 475 11.12.3 工作原理 475 11.13 创建自定义标记 477 11.13.1 问题 477 11.13.2 解决方案...

Global site tag (gtag.js) - Google Analytics