Fork me on GitHub

Programming Design Notes

Java EE 6 - GlassFish V3 + MySQL 5.1 配合 EJB + JPA + Servlet

| Comments

現時的專業系統設計者會主張將系統分為 3 層或 4 層的系統架構

3 層的系統架構:
  • 客戶層 (Client tier)
  • 商業邏輯層 (Business tier)
  • 資料層 (Enterprise Information Systems(EIS) tier)
而 4 層則是在 客戶層 (Client tier) 和 商業邏輯層 (Business tier) 之間加插一個網絡服務層 (Web tier)

4 層的系統架構:
  • 客戶層 (Client tier)
  • 網絡服務層 (Web tier)
  • 商業邏輯層 (Business tier)
  • 資料層 (Enterprise Information Systems(EIS) tier)
給大家一張圖片比較好理解


上圖左邊是 3 層架構
右邊則是 4 層架構
Web-Based Client 大多數是指瀏覽器 

其實在上圖的例子不是只有瀏覽器才會使用到網絡服務層 (Web tier)
就好像下圖一樣


因為網絡服務層 (Web tier) 包含 Web service
Web service 可以經由各種不同的應用程式去使用

現在開始試作 4 層的系統架構
開發軟件: Eclipse IDE for Java EE Developer
服務器: GlassFish v3
資料庫: MySQL 5.1

安裝好以上的工具後便開始進行設定


打開 MySQL Command Line Client
輸入密碼
然後輸入以下的 SQL

CREATE  TABLE IF NOT EXISTS `test`.`User` (

`UID` INT NOT NULL AUTO_INCREMENT ,

`name` VARCHAR(45) NOT NULL ,

`password` VARCHAR(45) NOT NULL ,

PRIMARY KEY (`UID`) )

ENGINE = InnoDB

DEFAULT CHARACTER SET = utf8;




**在安裝 MySQL 後 test 這個 Database 會自動加入**

在 Eclipse 按 File -> New -> Enterprise Application Project


  • Project name: TestEAR
  • Target runtime: GlassFish v3 Java EE 6
  • EAR version: 5.0
  • Configuration: Default Configuration for GlassFish v3 Java EE 6

按下 Finish



打開 File -> New -> JPA Project


  • Project name: TestJPA
  • Configuration: Default Configuration for GlassFish v3 Java EE 6
  • EAR membership: Add project to an EAR
  • EAR project name: TestEAR

按下 Finish



打開 File -> New -> EJB Project


  • Project name: TestEJB
  • EJB Module version: 3.0 (還沒有 3.1 可供選擇)
  • Configuration: Default Configuration for GlassFish v3 Java EE 6
  • EAR membership: Add project to an EAR
  • EAR project name: TestEAR
按下 Next


再按 Next

不要選取 Create an EJB client JAR module to hold the client interface and class
按下 Finish


打開 File -> New -> Dynamic Web Project


  • Project name: TestWAR
  • Dynamic Web module version: 2.5 (還沒有 3.0 可供選擇)
  • Configuration: Default Configuration for GlassFish v3 Java EE 6
  • EAR membership: Add project to an EAR
  • EAR project name: TestEAR
按下 Finish




設換到 JPA 的版面


在 Data Source Explorer 的 Database connection 按下滑鼠右鍵
按下 New



  • 選擇 MySQL
  • Name: Test Connection

按下 Next



現在是沒有 Driver 可供選擇的
按下右邊第二個按鈕


選擇 MySQL 5.1


在上方的標籤選擇 Jar List


按下 Edit JAR/Zip… 選擇 MySQL JDBC Driver
沒有 Driver 的可在這裡下載: http://dev.mysql.com/downloads/connector/j/
按下 OK 回到剛才的頁面

  • Database: test
  • URL: jdbc:mysql://localhost:3306/test
  • User name: root
  • password: (不用教吧)
確定資料正確後可以按一下 Test Connection 測試一下連接有沒有問題



正確連接會看見以下畫面


按下 Finish 完成

在 TestJPA project 新增一個 Entities
選取 New -> Entities From Table



  • Connection: Test Connection
  • Schema: test
  • Table: user
按下 Next


再按下 Next
  • Key generator: auto
  • Collection properties type: java.util.List
  • Package: com.blogspot.lawpronotes.jpa
按下 Finish


在 src 的 資料夾會出現 User.java


原始碼如下
package com.blogspot.lawpronotes.jpa;

import java.io.Serializable;
import javax.persistence.*;

/**
* The persistent class for the user database table.
*
*/
@Entity
@Table(name = "User")
@NamedQueries( { @NamedQuery(name = "User.findAll", query = "SELECT u FROM User u") })
public class User implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@Column(name = "UID")
private int uid;

private String name;

private String password;

public User() {
}

public int getUid() {
return this.uid;
}

public void setUid(int uid) {
this.uid = uid;
}

public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

public String getPassword() {
return this.password;
}

public void setPassword(String password) {
this.password = password;
}

}

現在設定 EJB
到 TestEJB -> Properties


找尋 Java EE Module Dependencies
將 TestJPA 打勾起來
按下 OK


在 TestEJB 新增一個 Session Bean (EJB 3.x)

  • Java package: com.blogspot.lawpronotes.ejb
  • Class name: UserController
  • Status type: Stateless
  • 不要將 Remote 打勾 (在同一個 JVM 環境下不需要用到 Remote)
  • 將 Local 打勾並輸入: com.blogspot.lawpronotes.ejb.UserControllerLocal
按下 Next


  • Bean name: UserController
  • Mapped name: ejb/UserController
  • Transaction type: Container
按下 Finish


在 TestEJB project 內會多出 2 個 file


將 UserControllerLocal.java 換成以下程式碼
package com.blogspot.lawpronotes.ejb;

import javax.ejb.Local;

@Local
public interface UserControllerLocal {
public void addUser(String userName, String password) throws Exception;

public java.util.List<com.blogspot.lawpronotes.jpa.User> getAllUser()
throws Exception;
}


再將 UserController.java 換成以下程式碼
package com.blogspot.lawpronotes.ejb;

import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import com.blogspot.lawpronotes.jpa.User;

/**
* Session Bean implementation class UserController
*/
@Stateless(mappedName = "ejb/UserController")
public class UserController implements UserControllerLocal {

@PersistenceContext
private EntityManager entityManager;

/**
* Default constructor.
*/
public UserController() {
// TODO Auto-generated constructor stub
}

@Override
public void addUser(String userName, String password) throws Exception {
// TODO Auto-generated method stub
User user = new User();
user.setName(userName);
user.setPassword(password);
entityManager.persist(user);
entityManager.flush();
}

@SuppressWarnings("unchecked")
@Override
public List<User> getAllUser() throws Exception {
// TODO Auto-generated method stub
List<User> userList = null;
Query query = entityManager.createNamedQuery("User.findAll");
userList = (List<User>) query.getResultList();
return userList;
}
}

在 TestEJB -> ejbModule -> META-INF 新增一個 XML 檔案


File name: persistence.xml
按下 Finish


貼上以下內容
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="TestJPA">
<jta-data-source>jdbc/test</jta-data-source>
<class>com.blogspot.lawpronotes.jpa.User</class>
</persistence-unit>
</persistence>

其中的 jta-data-source 一定要跟 GlassFish JDBC Resources 的 JNDI name 要一致
<jta-data-source>jdbc/test</jta-data-source>

進入 TestWAR -> Properties



找尋 Java EE Module Dependencies
將 TestJPA 和 TestEJB 打勾起來
按下 OK



在 TestWAR 新增一個 Servlet



  • Java package: com.blogspot.lawpronotes.servlet
  • Class name: IndexServlet
按下 Finish



將 IndexServlet.java 換成以下內容
package com.blogspot.lawpronotes.servlet;

import java.io.IOException;
import java.util.List;

import javax.ejb.EJB;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.blogspot.lawpronotes.ejb.UserControllerLocal;
import com.blogspot.lawpronotes.jpa.User;

/**
* Servlet implementation class IndexServlet
*/
@WebServlet(name = "Index", urlPatterns = { "/index" })
public class IndexServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

@EJB
private UserControllerLocal userController;

/**
* @see HttpServlet#HttpServlet()
*/
public IndexServlet() {
super();
// TODO Auto-generated constructor stub
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
try {
List<User> userList = userController.getAllUser();
request.setAttribute("userList", userList);
RequestDispatcher dispatcher = request
.getRequestDispatcher("/WEB-INF/pages/index.jsp");
dispatcher.forward(request, response);
} catch (Exception ex) {
ex.printStackTrace();
}
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
try {
String userName = request.getParameter("userName");
String password = request.getParameter("password");
userController.addUser(userName, password);
List<User> userList = userController.getAllUser();
request.setAttribute("userList", userList);
RequestDispatcher dispatcher = request
.getRequestDispatcher("/WEB-INF/pages/index.jsp");
dispatcher.forward(request, response);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

將 TestWAR -> Web Content 的 index.jsp 移除掉


在 TestWAR -> Web Content -> WEB-INF 新增一個資料夾


Folder name: pages
按下 Finish


在 pages 資料夾新增一個 JSP


File name: index.jsp
按下 Finish


貼上以下程式碼
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Sample</title>
</head>
<body>
<c:if test="${userList.size() > 0}">
<table>
<thead>
<tr>
<td>User Name</td>
<td>Password</td>
</tr>
</thead>
<tbody>
<c:forEach var="user" items="${userList}">
<tr>
<td>${user.getName()}</td>
<td>${user.getPassword()}</td>
</tr>
</c:forEach>
</tbody>
</table>
</c:if>
<form method="post">
<label>User Name:</label><input type="text" name="userName"/><br/>
<label>Password:</label><input type="password" name="password"/><br/>
<input type="submit"/>
</form>
</body>
</html>

最後要修改一下 web.xml
到 TestWAR -> Web Content -> WEB-INF -> web.xml
貼上以下內容
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>TestWAR</display-name>
<welcome-file-list>
<welcome-file>index</welcome-file>
</welcome-file-list>
</web-app>

現在完成了!
立即去測試一下
到 TestEAR -> Run As - Run on Server


選擇 GlassFish v3 Java EE 6 at localhost
按下 Finish
Eclipse 便會將整個 Project 打包放到 GlassFish Server 上


在瀏覽器輸入網址: http://localhost:8080/TestWAR/
將會看見以下畫面
其實這是一個加入 User 的表格 (好像太陽春了點….)


  • User Name: Lawrence
  • Password: 123456
按下提交


結果會顯示如以下畫面


現在講解一下流程:
  1. 按下提交
  2. 瀏覽器將表格內容送回 IndexServlet
  3. IndexServlet 接收內容後將 userName 和 password 送到 EJB Session bean - UserController 的 addUser function
  4. UserController 利用 EntityManager 將 userName 和 password 加入到 user table 內
  5. IndexServlet 將所有 user 找出來
  6. IndexServlet 將 user list 送到 index.jsp 顯示
大概就是這樣

完整範例: EJB_JPA_Servlet.zip

相關書籍: Beginning Java™ EE 6 Platform with GlassFish™ 3: From Novice to ProfessionalPro JPA 2: Mastering the Java™ Persistence API (Expert's Voice in Java Technology)EJB 3 in Action