英文同步版链接

Github项目源码

EJB 全称为Enterprise Java Beans,封装了应用程序的业务逻辑并提供与容器服务交互的能力,如事务和安全。本教程在上次创建的JPA模块的基础上,继续添加EJB模块,并且实现真正与数据库交互。本次项目实现了添加用户到数据库,查询所有用户并显示在JSF页面上。

Project Structure

实现DAO(数据访问)层

首先在 notebookDomain 模块中添加一些代码,定义数据库操作的接口。在 com.zxuqian.notebook.dao 包中创建名为 IUserDao 的接口。代码如下:


public interface IUserDao {User getUserById(Long id);List<User> getAllUsers();Long addUser(User user);void deleteUser(User user);
}

接着,在 com.zxuqian.notebook.dao.impl 包下,定义此接口的实现类 UserDao。它包含一个只用一个参数的构造方法,接收 EntityManager 的实例对象。因为JPA模块并不属于容器管理,而EJB运行在容器的EJB组件中,所以我们需要从EJB中把容器注入的 EntityManager 对象传递给 UserDao


public class UserDao implements IUserDao, Serializable {private EntityManager entityManager;public UserDao(EntityManager entityManager) {this.entityManager = entityManager;}@Overridepublic User getUserById(Long id) {return this.entityManager.find(User.class, id);}@Overridepublic List<User> getAllUsers() {List<User> userList = this.entityManager.createNamedQuery("getAllUsersQuery", User.class).getResultList();return userList;}@Overridepublic Long addUser(User user) {this.entityManager.persist(user);return user.getId();}@Overridepublic void deleteUser(User user) {this.entityManager.remove(user);}
}

这个类中的方法分别进行了对 EntityManager API的调用,现作简单说明:

  • find() 用来根据指定 id 查询数据库,并返回相应的Java对象。
  • createNamedQuery() 执行预先定义好的命名查询语句,在本项目中,此名称定义在 User 类中,稍后进行介绍。
  • persist() 插入数据到数据库中。
  • remove() 根据传递过来的 User 对象,从数据库中删除指定记录。

命名查询使用 @NamedQuery 注解来定义相关查询, User 类如下所示:


@NamedQuery(name = "getAllUsersQuery", query = "from User u")
public class User implements Serializable {

这里的查询语言是JPA定义的,叫做 JPQL ,与SQL语法类似。 这里的语句意为查询所有用户。如果查询所有列,可省略 Select 子句和列名。

Persistence.xml 需要进行一些改动:


<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"><persistence-unit name="notebookDomain" transaction-type="JTA"><jta-data-source>java:/MySqlDS</jta-data-source><properties><!-- Have to define dialect and use hibernate ddl generation strategy --><property name="hibernate.hbm2ddl.auto" value="create-drop"/><property name="hibernate.dialect" value="MySQL5" /></properties></persistence-unit>
</persistence>

因为之前JPA原生 <property> 属性在Wildfly实现中并不总是生效,所以这里换成了Wildfly默认的JPA实现,即Hibernate。把属性改成了Hibernate专有的。hibernate.hbm2ddl.auto 定义是否自动生成表,create-and-drop 意为如果表存在,则删除后再创建。hibernate.dialect 用来指定数据库厂商,以根据不同的数据库生成厂商相关的SQL语句。

创建EJB模块

EJB Service client 模块

EJB可以用接口来定义此Bean是本地还是远程的。本地bean只能在部署应用的同一容器中访问,而远程bean可以被集群中的服务器所访问。

现在,创建一个maven模块,名为 notebookServiceClient

pom.xml 文件内容为:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>notebookRoot</artifactId><groupId>com.zxuqian</groupId><version>0.0.2</version><relativePath>../notebookRoot/pom.xml</relativePath></parent><modelVersion>4.0.0</modelVersion><artifactId>notebookServiceClient</artifactId><build><resources><resource><directory>src</directory><excludes><exclude>**/*.java</exclude></excludes></resource></resources><plugins><plugin><artifactId>maven-compiler-plugin</artifactId></plugin></plugins></build><dependencies><dependency><groupId>com.zxuqian</groupId><artifactId>notebookDomain</artifactId></dependency><dependency><groupId>org.jboss.spec.javax.ejb</groupId><artifactId>jboss-ejb-api_3.2_spec</artifactId><scope>provided</scope></dependency></dependencies></project>

此模块也继承自 notebookRoot 模块, 并且依赖于 notebookDomain 模块和jboss ejb模块.

创建名为 IUserService 的接口,并写入如下代码:


public interface IUserService {User getUserById(Long id);List<User> getAllUsers();Long addUser(User user);void deleteUser(User user);
}

这些方法定义了数据库CRUD操作,根据用户ID查询用户,查询所有用户,添加一个用户,删除一个用户。

再创建另一个名为 IUserServiceLocal 的接口,继承自 IUserService, 并添加 @Local 注解,表明它为本地bean接口。


@Local
public interface IUserServiceLocal extends IUserService {}

创建一个远程bean接口, IUserServiceRemote


@Remote
public interface IUserServiceRemote extends IUserService {}

这两个接口都使用父接口的方法,所以类体留空即可。

EJB Service 模块

创建Maven模块 notebookService。 此模块是 notebookServiceClient 的具体实现。 pom.xml文件内容为:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>notebookRoot</artifactId><groupId>com.zxuqian</groupId><version>0.0.2</version><relativePath>../notebookRoot/pom.xml</relativePath></parent><modelVersion>4.0.0</modelVersion><packaging>ejb</packaging><artifactId>notebookService</artifactId><build><resources><resource><directory>src</directory><excludes><exclude>**/*.java</exclude></excludes></resource></resources><plugins><plugin><artifactId>maven-compiler-plugin</artifactId></plugin><plugin><artifactId>maven-ejb-plugin</artifactId></plugin></plugins></build><dependencies><dependency><groupId>org.jboss.spec.javax.ejb</groupId><artifactId>jboss-ejb-api_3.2_spec</artifactId><scope>provided</scope></dependency><dependency><groupId>org.hibernate.javax.persistence</groupId><artifactId>hibernate-jpa-2.1-api</artifactId><scope>provided</scope></dependency><dependency><groupId>com.zxuqian</groupId><artifactId>notebookDomain</artifactId></dependency><dependency><groupId>com.zxuqian</groupId><artifactId>notebookServiceClient</artifactId></dependency></dependencies></project>

这里的不同之处是 <packaging> 的值为 ejb, 意为此模块将被打包成 EJB 格式。 如果EJB版本小于3.0,则需要在模块的 src/META-INF 文件夹下添加 ejb-jar.xml 文件,如果EJB版本大于3.0,则可以省略。这里提供 ejb-jar.xml 文件内容,其实只是一个空的定义文件:


<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"version="3.1">
</ejb-jar>

在此模块下创建名为 UserServiceBean 的Java类,实现 IUserServiceLocal 接口并添加 @Stateful 注解。@Stataful 意思为此EJB在客户端与其交互中,保存所有状态。另一种EJB类型为@Stateless,即在每次客户端请求时,从bean池里取出一个新的bean,状态不会保存。此类使用 @PersistenceContext 注解注入了一个EntityManager 对象,注入即是让容器管理此对象的创建与销毁。@PostConstruct 是生命周期注解,意思是在对象创建之后调用此方法,即 init() 方法,此方法初始化 UserDao 类,并传递 EntityManager 对象。


@Stateful
public class UserServiceBean implements IUserServiceLocal {@PersistenceContextprivate EntityManager entityManager;private IUserDao userDao;@PostConstructprivate void init() {this.userDao = new UserDao(entityManager);}public User getUserById(Long id) {return this.userDao.getUserById(id);}public List<User> getAllUsers() {return this.userDao.getAllUsers();}public Long addUser(User user) {return this.userDao.addUser(user);}public void deleteUser(User user) {this.userDao.deleteUser(user);}
}

JSF调用EJB服务

现在可以使用JSF来调用EJB服务了。在之前的 notebook 模块中,创建一个名为 UserBackBean 的Java类:


public class UserBackBean implements Serializable {private Logger logger = Logger.getLogger(UserBackBean.class.getCanonicalName());@EJBprivate IUserServiceLocal userService;private List<User> users;private User user;public UserBackBean() {this.user = new User();}public User getUser() {return user;}public void setUser(User user) {this.user = user;}public List<User> getUsers() {return users;}public void setUsers(User user) {this.users.add(user);}public String register() {this.userService.addUser(this.user);return this.getAllUsers();}public String getAllUsers() {this.users = this.userService.getAllUsers();return "user_list";}
}

此类作为JSF页面的后端bean,提供属性和方法供页面使用,利用EL表达式。

  • IUserServiceLocal 使用 @EJB 注解,不但声明了它是EJB组件,而且它的生命周期由容器管理,所以不用手动初始化它。
  • users 成员变量保存从数据库查询出来的所有 User 对象。
  • usernotebookDomain 模块中的 User 实体,用来接收用户从页面中输入的数据。
  • register() 方法保存用户数据到数据库中,并调用 getAllUsers() 方法跳转到 user_list 页面来显示所有用户的用户名。
  • getAllUsers() 用来查询所有用户,并跳转到 user_list 页面来显示所有用户的用户名。

再创建一些JSF页面。首先创建 register.xhtml 页面,位于 WebContent 目录下:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"xmlns:h="http://xmlns.jcp.org/jsf/html"xmlns:jsf="http://xmlns.jcp.org/jsf"xmlns:f="http://xmlns.jcp.org/jsf/core"><h:form><label for="username">Username: </label><h:inputText id="usernmae" value="#{userBackBean.user.username}" /><br /><label for="password">Password: </label><h:inputSecret id="password" value="#{userBackBean.user.password}" /><br /><label for="date_of_birth">Date of birth: </label><input type="date" jsf:id="date_of_birth" value="#{userBackBean.user.dateOfBirth}"><f:convertDateTime pattern="yyyy-MM-dd"/></input><br /><label for="email">Email: </label><input type="email" jsf:id="email" value="#{userBackBean.user.email}" /><br /><label for="phone">Phone number: </label><h:inputText  value="#{userBackBean.user.phone}" /><br /><h:commandButton value="Submit" action="#{userBackBean.register}" /></h:form>
</html>

这里使用EL表达式引用后端bean的属性和方法。<h:commandButton> 标签中的action 属性调用了 UserBackBean 中的 register() 方法。

再创建页面 user_list.xhtml


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"xmlns:h="http://xmlns.jcp.org/jsf/html"xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"><h:head><title>Qiantu - A simple Notebook</title></h:head><h:body><ul><c:forEach items="#{userBackBean.users}" var="user"><li>#{user.username}</li></c:forEach></ul></h:body>
</html>

这里使用了 JSTL 标签库,提供了一系列的标签方法对数据进行迭代,访问和保存。 <c:forEach> 用来循环访问一个集合或数组中的元素。 items 属性指定要循环的集合或数组的变量名,var 属性可自定义每个元素的变量名。在 <c:forEach> 标签体里,定义 <li> 标签显示每个用户的用户名。

index.xhtml 页面, 在 </h:body> 标签之前添加如下标签:

<p><h:outputLink value="register.xhtml">Register</h:outputLink></p><h:form><p><h:commandLink action="#{userBackBean.getAllUsers}">List All Users</h:commandLink></p></h:form>

<h:outputLink> 会生成对应的html <a> 标签,并跳转到 value 属性定义的页面。

<h:commandLink> 必须定义在 <h:form> 标签里。 <h:outputLink><h:commandLink> 的不同之处在于 <h:commandLink> 可以在页面跳转前,在后端bean中做一些操作,这里调用了 UserBackBean 类中的 getAllUsers() 来从数据库中查询所有用户并初始化 users 变量, 以供 user_list.xhtml 页面使用。

配置 Maven

因为新增了两个模块,所以需要修改 notebookRoot 模块的 pom.xml 文件。

这里添加了新的 <pluginManagement> 标签,可以提供统一管理插件的版本和通用配置的功能,这样可以在子模块中省略插件的版本号,并且继承一些插件的配置。


<build><pluginManagement><plugins><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.1</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin><plugin><artifactId>maven-war-plugin</artifactId><version>2.3</version></plugin><plugin><artifactId>maven-ejb-plugin</artifactId><version>2.3</version><configuration><ejbVersion>3.2</ejbVersion></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-ear-plugin</artifactId><version>2.10</version></plugin></plugins></pluginManagement>
</build>

另外,新创建的模块添加到了 <dependencyManagement> 标签中:


<dependency><groupId>com.zxuqian</groupId><artifactId>notebookService</artifactId><version>${project.version}</version><type>ejb</type>
</dependency>
<dependency><groupId>com.zxuqian</groupId><artifactId>notebookServiceClient</artifactId><version>${project.version}</version>
</dependency>

这里的 notebookService 定义了 <type>标签,值为 ejb, 即需要和 notebookService 模块中定义的 <packaging> 的值相同,否则会找不到此依赖。

另外 notebookDomain 模块中的 pom.xml 也需要一些注意:


<build><resources><resource><directory>src</directory><excludes><exclude>**/*.java</exclude></excludes></resource></resources><plugins><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.5.1</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins></build>

<resources> 标签必须定义哪些目录包含资源文件,即除了源代码之外的相关配置文件。 这里定义 src 文件夹下除了以 .java 结尾的都是资源文件。这样才能把 persistence.xml 打包进最终生成的Jar文件中。

其他模块对<dependency>也做了简单改动,即引用新创建的EJB模块,具体代码可查看github上的项目源代码,这里不再赘述。

测试

notebookRoot 模块上运行 install 之后, 使用 wildfly:run 部署 notebookEAR 模块。 打开浏览器输入如下URL:

http://localhost:8080/notebook

点击 Register 链接,

添加一些数据并点击 Submit 按钮,然后就可以看到新创建的用户列表:

Java EE开发系列教程 - 使用EJB组件与JPA层交互相关推荐

  1. 第2课:JSP常用开发模式 (黄菊华Java网站开发系列教程)

    JSP常用开发模式 单纯的JSP编程 在 JSP 编程模式下,通过应用 JSP 中的脚本标志,可以直接在 JSP 页面中实现各种功能.虽然这种模式很容易实现,但是,其缺点也非常明显.因为将大部分的 J ...

  2. Java 比特币开发系列教程汇总

    用Java创建一个机器人 机器人接受比特币并立即退还用户 用Java创建比特币钱包 用Java实现市场价闪兑比特币 用Java在去中心化交易所限价买卖比特币 用Java在去中心化交易所自由挂单买卖任意 ...

  3. 第11课:JSP指令 Include指令 Taglib指令 (JSP教程 JSP入门实战教程 黄菊华Java网站开发系列教程)

    Include指令 JSP include 指令用于通知 JSP 引擎在翻译当前 JSP 页面时,将其他文件中的内容合并进当前 JSP 页面转换成的 Servlet 源文件中,这种在源文件级别进行引入 ...

  4. Java Web开发技术教程入门-JavaBean组件与Servlet

    补更:阅战阅勇第7/8/9Days笔记 昨天我们了解了JDBC技术的一些日常操作,对于数据库而言,不仅仅的只有"增,删,改,查".博主觉得最重要的是SQL语句的优化,一个" ...

  5. java ee开发环境_设置Java EE 6开发环境

    java ee开发环境 本教程简要说明了如何设置典型的环境来开发基于Java EE 6的应用程序. 除了可以正常工作的Windows XP客户端具有足够的CPU能力和内存外,本教程没有其他先决条件. ...

  6. 人员雇佣 网络流_雇用Java EE开发人员的一些面试问题

    人员雇佣 网络流 互联网上充斥着Java开发人员的面试问题. 这些问题的主要问题是,它们仅证明候选人具有良好的记忆力,并记住所有语法,结构,常量等.对他/她的逻辑推理没有真正的评估. 我在下面列举一些 ...

  7. 雇用Java EE开发人员的一些面试问题

    互联网上充斥着Java开发人员的面试问题. 这些问题的主要问题是,它们仅证明候选人具有良好的记忆力,并且记住所有语法,结构,常量等.对他/她的逻辑推理没有真正的评估. 我在下面列举了一些面试问题的示例 ...

  8. JAVA EE开发环境准备

    Java EE开发环境 开发环境 IDEA MySQL Navicat Postman 其它 notepad++ Everything Github 占空,想到再补充 这是一个系列博客,从这篇开始,我 ...

  9. unity2d游戏开发系列教程:四、一个2D游戏所需要的主要功能(游戏框架)

    目录 unity2d游戏开发系列教程:一.环境安装 unity2d游戏开发系列教程:二.新建工程并熟悉Unity编辑器常用功能 unity2d游戏开发系列教程:三.场景布置,增加怪物和机关 原文下载 ...

  10. unity2d游戏开发系列教程:三、场景布置,增加怪物和机关

    目录 unity2d游戏开发系列教程:一.环境安装 unity2d游戏开发系列教程:二.新建工程并熟悉Unity编辑器常用功能 第一节.场景草地布置 先查看一下资源文件里都有什么,一会就要用到的 打开 ...

最新文章

  1. 【RAC】使用一条“ps”命令获取Linux环境下全部RAC集群进程信息
  2. 交流潮流matlab程序,大神们,求个电力系统潮流计算的matlab程序。
  3. DPDK报文转发(四)
  4. 查看控制文件的内容(oracle)
  5. Zero-shot Learning / One-shot Learning / Few-shot Learning
  6. 从特斯拉到计算机视觉之「图像语义分割」
  7. ( )不是对网络模型进行分层的目标。
  8. duilib学习领悟(4)
  9. Scala 基础(8)—— 占位符_和部分应用函数
  10. feign和ajax,SpringCloud-feign 声明式服务调用
  11. 神州云科DCN存储管理IP_干货!DCN校园网大二层解决方案详解
  12. LOJ116 有源汇有上下界最大流(上下界网络流)
  13. 程序员考核的五大死因
  14. 大括号之谜:C++的列表初始化语法解析
  15. Js中Date的应用
  16. c语言实验分支程序设计二,C语言程序实验报告分支结构的程序设计(0页).doc
  17. [转载] 将整数k转换成实数python表达式_Python程序设计课后习题答案-第一单元
  18. .net中对时间的操作
  19. 谷歌浏览器官网默认只能下载在线安装包(只有几兆大小)若想下载完整的离线安装包,可以用以下官方链接
  20. 如何生成网站地图(Sitemap)

热门文章

  1. win10下使用pycharm实现基于pyg的cora+citeseer+pubmed数据集的JKNET模型测试
  2. C#服务端的微信小游戏——多人在线角色扮演(十二)
  3. 可以免费自学编程的12个网站
  4. nios程序烧写到epcs方法
  5. Google 因果推断的CausalImpact 贝叶斯结构时间序列模型(二十二)
  6. 谷歌Android UI设计技巧
  7. 车载视频监控基于JTT808/1078管理平台商业开源
  8. vue项目pc端和移动端适配
  9. web开发中实现页面记忆的几种方式
  10. JAVA怎么打开pkg_PKG文件在电脑上怎么打开!(普通PC)