英文同步版链接
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 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;
}
@Override
public User getUserById(Long id) {
return this.entityManager.find(User.class, id);
}
@Override
public List getAllUsers() {
List userList = this.entityManager.createNamedQuery(
"getAllUsersQuery", User.class).getResultList();
return userList;
}
@Override
public Long addUser(User user) {
this.entityManager.persist(user);
return user.getId();
}
@Override
public 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
需要进行一些改动:
java:/MySqlDS
因为之前JPA原生
属性在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
文件内容为:
notebookRoot
com.zxuqian
0.0.2
../notebookRoot/pom.xml
4.0.0
notebookServiceClient
src
**/*.java
maven-compiler-plugin
com.zxuqian
notebookDomain
org.jboss.spec.javax.ejb
jboss-ejb-api_3.2_spec
provided
此模块也继承自 notebookRoot 模块, 并且依赖于 notebookDomain 模块和jboss ejb模块.
创建名为 IUserService
的接口,并写入如下代码:
public interface IUserService {
User getUserById(Long id);
List 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
文件内容为:
notebookRoot
com.zxuqian
0.0.2
../notebookRoot/pom.xml
4.0.0
ejb
notebookService
src
**/*.java
maven-compiler-plugin
maven-ejb-plugin
org.jboss.spec.javax.ejb
jboss-ejb-api_3.2_spec
provided
org.hibernate.javax.persistence
hibernate-jpa-2.1-api
provided
com.zxuqian
notebookDomain
com.zxuqian
notebookServiceClient
这里的不同之处是
的值为 ejb, 意为此模块将被打包成 EJB 格式。 如果EJB版本小于3.0,则需要在模块的 src/META-INF 文件夹下添加 ejb-jar.xml
文件,如果EJB版本大于3.0,则可以省略。这里提供 ejb-jar.xml
文件内容,其实只是一个空的定义文件:
在此模块下创建名为 UserServiceBean
的Java类,实现 IUserServiceLocal
接口并添加 @Stateful
注解。@Stataful
意思为此EJB在客户端与其交互中,保存所有状态。另一种EJB类型为@Stateless
,即在每次客户端请求时,从bean池里取出一个新的bean,状态不会保存。此类使用 @PersistenceContext
注解注入了一个EntityManager
对象,注入即是让容器管理此对象的创建与销毁。@PostConstruct
是生命周期注解,意思是在对象创建之后调用此方法,即 init()
方法,此方法初始化 UserDao
类,并传递 EntityManager
对象。
@Stateful
public class UserServiceBean implements IUserServiceLocal {
@PersistenceContext
private EntityManager entityManager;
private IUserDao userDao;
@PostConstruct
private void init() {
this.userDao = new UserDao(entityManager);
}
public User getUserById(Long id) {
return this.userDao.getUserById(id);
}
public List 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());
@EJB
private IUserServiceLocal userService;
private List 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 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
对象。 -
user
即 notebookDomain 模块中的User
实体,用来接收用户从页面中输入的数据。 -
register()
方法保存用户数据到数据库中,并调用getAllUsers()
方法跳转到user_list
页面来显示所有用户的用户名。 -
getAllUsers()
用来查询所有用户,并跳转到user_list
页面来显示所有用户的用户名。
再创建一些JSF页面。首先创建 register.xhtml 页面,位于 WebContent 目录下:
这里使用EL表达式引用后端bean的属性和方法。
标签中的action
属性调用了 UserBackBean
中的 register()
方法。
再创建页面 user_list.xhtml
:
Qiantu - A simple Notebook
- #{user.username}
这里使用了 JSTL 标签库,提供了一系列的标签方法对数据进行迭代,访问和保存。
用来循环访问一个集合或数组中的元素。 items
属性指定要循环的集合或数组的变量名,var
属性可自定义每个元素的变量名。在
标签体里,定义 标签显示每个用户的用户名。
在 index.xhtml
页面, 在 标签之前添加如下标签:
Register
List All Users
会生成对应的html 标签,并跳转到
value
属性定义的页面。
必须定义在
标签里。
和
的不同之处在于
可以在页面跳转前,在后端bean中做一些操作,这里调用了 UserBackBean
类中的 getAllUsers()
来从数据库中查询所有用户并初始化 users
变量, 以供 user_list.xhtml
页面使用。
配置 Maven
因为新增了两个模块,所以需要修改 notebookRoot 模块的 pom.xml
文件。
这里添加了新的
标签,可以提供统一管理插件的版本和通用配置的功能,这样可以在子模块中省略插件的版本号,并且继承一些插件的配置。
maven-compiler-plugin
3.1
1.8
maven-war-plugin
2.3
maven-ejb-plugin
2.3
3.2
org.apache.maven.plugins
maven-ear-plugin
2.10
另外,新创建的模块添加到了
标签中:
com.zxuqian
notebookService
${project.version}
ejb
com.zxuqian
notebookServiceClient
${project.version}
这里的 notebookService 定义了
标签,值为 ejb
, 即需要和 notebookService 模块中定义的
的值相同,否则会找不到此依赖。
另外 notebookDomain 模块中的 pom.xml
也需要一些注意:
src
**/*.java
maven-compiler-plugin
3.5.1
1.8
标签必须定义哪些目录包含资源文件,即除了源代码之外的相关配置文件。 这里定义 src 文件夹下除了以 .java 结尾的都是资源文件。这样才能把 persistence.xml
打包进最终生成的Jar文件中。
其他模块对
也做了简单改动,即引用新创建的EJB模块,具体代码可查看github上的项目源代码,这里不再赘述。
测试
在 notebookRoot 模块上运行 install
之后, 使用 wildfly:run 部署 notebookEAR 模块。 打开浏览器输入如下URL:
http://localhost:8080/notebook
点击 Register 链接,
添加一些数据并点击 Submit 按钮,然后就可以看到新创建的用户列表: