核心提示:从EJB的入门知识我们已经了解到,在EJB 2.x中,EJB有3种类型的Bean,分别是会话Bean(Session Bean)、消息驱动Bean(Message-Driven Bean)和实体Bean(Entity Bean)。随着EJB 3的推出,EJB中的实体Bean逐渐被JPA规范所替代,JPA不仅能在EJB环境中使用,而
从EJB的入门知识我们已经了解到,在EJB 2.x中,EJB有3种类型的Bean,分别是会话Bean(Session Bean)、消息驱动Bean(Message-Driven Bean)和实体Bean(Entity Bean)。随着EJB 3的推出,EJB中的实体Bean逐渐被JPA规范所替代,JPA不仅能在EJB环境中使用,而且能在Java SE、Java EE环境中使用,相对于EJB 2.x中的实体Bean,它的使用范围更广。但这里我们仍然将其称做实体Bean。
与会话Bean和消息驱动Bean类似,新的实体Bean也是一个加了注释符(@Entity)的简单 Java对象(POJO),实体关系和O/R映射也是通过注释符来定义的,并提供几种不同的数据库操作规范。一旦被EntityManager访问,它就成为了一个持久化对象,并且成为了持久化上下文的一部分。此时我们就可以像使用Hibernate、iBATIS一样来使用实体对象了。
接下来我们将通过本节课和后面的两节课来详细讲解实体Bean的开发技术。
Ø 第一个实体Bean实例:通过开发第一个实体Bean,建立与数据库的连接,演示实体Bean的开发与调用过程。
Ø 实体管理器:执行数据库更新的方法。
Ø 生命周期:实体Bean的监听和回调。
Ø 关系实体映射:开发实体的方法。
Ø JPQL查询语言:执行数据库实体查询。
Ø 原生SQL查询:执行原生SQL语句。
它们之间的关系如图6-1所示,通过实体管理器操作实体Bean,来实现对数据库的更新、JPQL查询和原生SQL查询。实体管理器是工具,实体Bean是数据。
图6-1 实体Bean内容结构图
本节课将重点讲解前3个部分的内容,即:
Ø 建立与数据库的连接。
Ø 实体管理器。
Ø 实体Bean的生命周期。
通过本节课的学习,你将能够通过实体Bean的开发,实现对数据库的操作。
6.1 第一个实体Bean实例——建立与数据库的连接
下面首先来讲解实体Bean的调用过程,然后通过开发第一个实体Bean,演示该配置与开发的过程,包括以下内容:
Ø 配置数据源。
Ø 指定数据源。
Ø 开发第一个实体Bean——Student.java。
Ø 开发会话Bean进行调用——StudentDAORemote.java和StudentDAO.java。
Ø 打包并部署到JBoss服务器。
Ø 开发客户端进行测试——StudentDAOClient.java。
通过本节的学习,你将能够通过实体Bean的建立与MySQL数据库的连接,往数据表中插入一条记录。
6.1.1 实体Bean的工作原理
我们知道,实体Bean是作为持久化类被EJB容器管理的,要实现对该持久化类的调用,必须经过以下步骤。
(1)配置数据源连接。
(2)在配置文件persistence.xml中指定数据源。
(3)开发实体Bean。
(4)在会话Bean、Java SE或Java EE中调用实体Bean。
如图6-2所示。
实体Bean不仅可以被会话Bean调用,还可以被任何的Java类、JSP和Servlet调用,调用的目的是实现对数据库的操作。它的意义与Hibernate、iBATIS完全相同,就是作为系统的DAO层,实现对数据库的访问。
下面我们就按照从底层到上层的顺序,来演示创建与数据库的连接的过程。
6.1.2 配置数据源
JBoss有一个默认的数据源DefaultDS,它使用JBoss内置的HSQLDB数据库。其数据源对应的配置文件为D:\jboss-5.0.0.GA\server\default\deploy\hsqldb-ds.xml,其中包含如下5个设置:
<jndi-name>DefaultDS</jndi-name>
<connection-url>jdbc:hsqldb:${jboss.server.data.dir}${/}hypersonic${/}localDB</connection-url>
<driver-class>org.hsqldb.jdbcDriver</driver-class>
<user-name>sa</user-name>
<password></password>
其中的意义表示如下。
Ø jndi-name:指定JNDI命名。
Ø connection-url:数据库连接URL。
Ø driver-class:数据库驱动类。
Ø user-name:数据库登录用户名。
Ø password:数据库登录密码。
我们只需要通过引用JNDI命令DefaultDS来引用该数据源,引用的方法很简单,只需要在persistence.xml中指定该别名即可(见下一节)。
在实际的应用中,我们可能需要使用不同的数据库,如MySQL、Oracle、MSSQL Server等。各种数据库的数据源配置模版可以在D:\jboss-5.0.0.GA\docs\examples\jca目录中找到,默认名称为数据库名+ -ds.xml,例如下面的例子。
Ø mysql-ds.xml:MySQL数据源配置模板。
Ø oracle-ds.xml:Oracle数据源配置模板。
Ø mssql-ds.xml:MSSQL Server数据源配置模板。
在D:\jboss-5.0.0.GA\docs\examples\jca目录下共包含38种模板,表明它支持对38种数据源的连接。
要配置到某一个数据源的连接,需要进行如下的步骤。
(1)复制添加该数据库类型的配置模板到D:\jboss-5.0.0.GA\server\default\deploy目录,并设置正确的数据源参数。
(2)添加该数据库的驱动程序包到D:\jboss-5.0.0.GA\server\default\lib目录,并重启JBoss服务器加载该程序包。
在准备篇中,我们已经安装并配置了MySQL数据库,下面我们以MySQL数据库为例,来添加到该数据库的数据源,配置步骤如下。
(1)复制D:\jboss-5.0.0.GA\docs\examples\jca\mysql-ds.xml到JBoss部署目录D:\jboss-5.0.0.GA\ server\default\deploy中,并修改配置参数如下:
<jndi-name>MySqlDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/demo</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>root</user-name>
<password>123</password>
其中的意义表示如下。
Ø jndi-name:指定JNDI命名为MySqlDS。
Ø connection-url:指定连接本地的demo数据库。
Ø driver-class:数据库驱动类com.mysql.jdbc.Driver。
Ø user-name:数据库登录用户名为root。
Ø password:数据库登录密码为123。
如果JBoss服务器处于启动状态,则此时JBoss会自动监控到该文件,并加载进来,同时在控制台输入如下信息:
16:42:06,937 INFO [ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:service=DataSourceBinding,name=
MySqlDS' to JNDI name 'java:MySqlDS'
这就表明,新配置的数据源MySqlDS生效了。如果你随时修改该文件,则JBoss服务器也能够监控到你的修改,并重新加载该数据源,在控制台输入如下信息:
16:42:27,109 INFO [ConnectionFactoryBindingService] Unbound ConnectionManager 'jboss.jca:service=DataSourceBinding,name=
MySqlDS' from JNDI name 'java:MySqlDS'
16:42:27,328 INFO [ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:service=DataSourceBinding,name=
MySqlDS' to JNDI name 'java:MySqlDS'
(2)下载MySQL数据库的驱动。
Ø 下载页面:http://dev.mysql.com/downloads/connector/j/5.0.html。
Ø 下载文件:mysql-connector-java-5.0.8.tar.gz。
Ø 驱动包文件:mysql-connector-java-5.0.8-bin.jar。
将mysql-connector-java-5.0.8-bin.jar添加到D:\jboss-5.0.0.GA\server\default\lib目录,然后重启JBoss来加载驱动包即可。
提 示
对于其他数据库,如Oracle、SQL Server等,均可以按照类似的步骤配置相应的数据源。
6.1.3 指定数据源——persistence.xml
以上配置的数据源由JBoss服务器加载和管理,要使用这些数据源,还需要在我们的应用中指定引用哪一个数据源。引用的方法很简单,只需要在应用的/ejbModule/META-INF目录下添加一个配置文件persistence.xml即可,并在该文件中指定引用的数据源JNDI名称,同时可以设置该数据源的相关操作属性。
提 示
persistence.xml是一个XML文件,它的文档定义Schema可以从http://java.sun.com/xml/ ns/persistence/persistence_1_0.xsd上下载到。
下面是一个persistence.xml配置实例,代码如下所示:
程序6-1 配置文件persistence.xml
<?xml version="1.0" encoding="UTF-8" ?>
<persistence 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" version="1.0">
<persistence-unit name="demo">
<jta-data-source>java:/MySqlDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
</properties>
</persistence-unit>
</persistence>
其中包含3个配置元素,分别如下。
Ø persistence-unit元素:可以有一个或多个,每个persistence-unit元素定义了持久化内容名称、使用的数据源名称及Hibernate属性。其中的name属性用于设置持久化名称。
Ø jta-data-source元素:用于指定实体Bean使用的数据源名称MySqlDS,当指定数据源名称时java:/前缀不能缺少,并注意数据源名称的大小写。
Ø properties元素:用于指定Hibernate的各项属性,如果hibernate.hbm2ddl.auto的值设为create-drop,则在实体Bean发布及卸载时将自动创建及删除相应的数据库表。为了使以后实体Bean的改动能反应到数据表,建议使用update,这样实体Bean添加一个属性时能同时在数据表增加相应字段。
注 意
(1)properties元素属性在各个应用服务器使用的持久化产品中都不一样,如JBoss使用Hibernate,WebLogic10使用Kodo,GlassFish/Sun Application Server/Oralce使用Toplink。
(2)properties元素的可用属性及默认值可以在下面的文件中找到:D:\jboss-5.0.0.GA\ server\all\deployers\ejb3.deployer\META-INF\persistence.properties。
(3)JBoss服务器在启动或关闭时会引发实体Bean的发布及卸载。
6.1.4 开发第一个实体Bean——Student.java
实体Bean实际上对应了数据库中的表,它是数据库中表在Java类中的表现,通常为最普通的POJO类。EJB容器能够根据实体Bean自动在数据库中创建数据表,这就需要将实体类与数据表的结构进行对应,包括表名、字段名、字段长度、字段类型、主键等信息。
为了开发一个与数据库表对应的单表实体Bean,我们首先设计一个学生表student的数据结构,该表共包括7个字段,如表6-1所示。
表6-1 学生表student
序号
字段名
字段描述
字段类型
Java类型
1
studentid
学号(主键)
int
Integer
2
name
姓名
varchar(50)
String
3
sex
性别
bit(1)
boolean
4
age
年龄
smaillint(6)
Short
5
birthday
出生日期
datetime
Date
6
address
地址
varchar(100)
String
7
telephone
电话
varchar(20)
String
其中的字段类型对应了MySQL数据库中的字段类型,Java类型为相应的POJO类中的字段类型,即实体Bean中的变量类型。
要开发一个与该表对应的实体Bean很简单,只需要新建一个POJO类,添加7个与表的字段同名的变量,同时使用一些注释符来表示该实体Bean与数据表student的对应映射关系即可。
首先在Eclipse中新建一个EJB项目“EntityBeanTest”,并创建包com.ejb.entitybean,然后在该包下新建实体Bean类Student.java。首先我们来看看这个完整的实体类的代码,如下所示:
程序6-2 实体Bean类Student.java
package com.ejb.entitybean;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
@SuppressWarnings("serial")
@Entity
@Table(name = "Student")
public class Student implements Serializable {
private Integer studentid; //学号
private String name; //姓名
private boolean sex; //性别
private Short age; //年龄
private Date birthday; //出生日期
private String address; //地址
private String telephone //电话
@Id
@GeneratedValue
public Integer getStudentid() {
return studentid;
}
public void setStudentid(Integer studentid) {
this.studentid = studentid;
}
@Column(name = "name", length = 50)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(nullable = false)
public boolean getSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
@Column(nullable = false)
public Short getAge() {
return age;
}
public void setAge(Short age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Column(name = "address", length = 100)
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Column(name = "telephone", length = 20)
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
}
注 意
实体Bean通常需要实现Serializable接口,这样就可以有EJB客户端创建该对象,并将该对象传送到服务端,否则将引发java.io.InvalidClassException例外。
该类是一个Java POJO类,其中包含了7个变量,并为每个变量添加了getter/setter函数。为了将该POJO类表现为一个实体Bean,添加了一些注释符,来与数据表student进行对应,这些注释如下。
Ø @Entity注释指明这是一个实体Bean,每个实体Bean类映射数据库中的一个表。
Ø @Table注释的name属性指定映射的数据表名称,Student类映射的数据表为Student。
Ø @Column注释定义了映射到列的所有属性,如列名是否唯一,是否允许为空,是否允许更新等,其属性介绍如下。
Ø name:映射的列名。如映射Student表的name列,可以在name属性的getName()方法上面加入@Column(name = "name"),如果不指定映射列名,则容器会将属性名称作为默认的映射列名。
Ø unique:是否唯一。
Ø nullable:是否允许为空。
Ø length:对于字符型列,length属性指定列的最大字符长度。
Ø insertable:是否允许插入。
Ø updatable:是否允许更新。
Ø columnDefinition:定义建表时创建此列的DDL。
Ø secondaryTable:从表名。如果此列不建在主表上(默认建在主表上),则该属性定义该列所在的从表的名字。
Ø @Lob注释指定某一个字段为大的文本字段类型。
Ø @Id注释指定studentid属性为表的主键。
Ø @GeneratedValue注释定义了标识字段的生成方式,本例中的studentid的值由MySQL数据库自动生成,它可以有以下多种生成方式。
Ø TABLE:容器指定用底层的数据表确保唯一。
Ø SEQUENCE:使用数据库的SEQUENCE 列来保证唯一。
Ø IDENTITY:使用数据库的INDENTIT列来保证唯一。
Ø AUTO:由容器挑选一个合适的方式来保证唯一。
Ø NONE:容器不负责主键的生成,由调用程序来完成。
例如:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer getId() {
return this.id;
}
这样就开发完实体Bean了,它除了在POJO上添加了一些注释外,与普通的POJO类没有任何区别。
6.1.5 开发会话Bean进行调用——StudentDAORemote.java和StudentDAO.java
由实体Bean的工作原理可知,它可以被EJB的会话Bean调用,也可以被任何的Java类或JSP调用。在EJB应用程序中,我们通常会在会话Bean中调用实体Bean,来实现对数据库的操作。
下面我们来开发一个远程的会话Bean组件,通过调用实体Bean类Student.java,来实现往数据表student中插入一条记录。
首先在项目EntityBeanTest中新建一个包com.ejb.dao,然后按照会话Bean的开发方法,在该包中新建一个远程的会话Bean组件StudentDAO,新建完后会产生一个远程接口类StudentDAORemote.java和实现类 StudentDAO.java。
1)远程接口类StudentDAORemote.java该类是一个远程接口类,通过注释符@Remote进行标识。为了执行插入student功能,首先在该接口中添加一个函数insert(),用来执行插入某一个Student类型的数据。如下所示:
程序6-3 远程接口类StudentDAORemote.java
package com.ejb.dao;
import java.util.List;
import javax.ejb.Remote;
import com.ejb.entitybean.Student;
@Remote
public interface StudentDAORemote {
public boolean insert(Student student);
}
2)实现类StudentDAO.java该类实现了远程接口StudentDAORemote.java,并通过@Stateless标识为无状态会话Bean。
首先它包含了一个EntityManager类型的变量em,EntityManager是实体管理器,顾名思义,它是实体Bean的管理容器。通过该对象,我们能够实现与数据库的各种交互,包括增、删、改、查等。
该实体变量em还通过注释@PersistenceContext来实现动态注入EntityManager对象,如果 persistence.xml文件中配置了多个不同的持久化内容,则还需要指定持久化名称注入EntityManager 对象,可以通过@PersistenceContext注释的unitName属性进行指定,例如:
@PersistenceContext(unitName="demo")
EntityManager em;
如果只有一个持久化内容配置,则不需要明确指定。
然后开发该类实现接口中的函数insert(),只需要调用em的persist()函数,就能够将Student对象持久化到数据库中,即往数据库中新增一条记录。
完整的实现代码如下:
程序6-4 实现类StudentDAO.java
package com.ejb.dao;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import com.ejb.entitybean.Student;
@Stateless
public class StudentDAO implements StudentDAORemote {
@PersistenceContext
protected EntityManager em;
public boolean insert(Student student) {
try {
em.persist(student);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
}
6.1.6 打包并部署到JBoss服务器
完成了以上的开发,我们就可以将当前应用发布到JBoss中,形成JNDI服务供外部访问了。发布的方法很简单,只需要导出该项目的JAR包到JBoss的部署目录D:\jboss-5.0.0.GA\server\default\ deploy即可。操作的方法是:用鼠标右键单击项目EJBTest,在弹出的快捷菜单中依次选择【Export】→【EJB JAR file】命令,指定导出文件为D:\jboss-5.0.0GA\server\default\deploy \EntityBeanTest.jar,该文件被自动发布到JBoss服务器中。JBoss服务器就会自动加载该服务,并在控制台输出如下信息:
14:59:29,578 INFO [Ejb3DependenciesDeployer] Encountered deployment AbstractVFSDeploymentContext@20142668
{vfszip:/D:/jboss-5.0.0.GA/server/default/deploy/EntityBeanTest.jar}
14:59:29,578 INFO [Ejb3DependenciesDeployer] Encountered deployment AbstractVFSDeploymentContext@20142668
{vfszip:/D:/jboss-5.0.0.GA/server/default/deploy/EntityBeanTest.jar}
14:59:29,703 INFO [JBossASKernel] Created KernelDeployment for: EntityBeanTest.jar
14:59:29,703 INFO [JBossASKernel] installing bean: jboss.j2ee:jar=EntityBeanTest.jar,name=StudentDAO,service=EJB3
14:59:29,703 INFO [JBossASKernel] with dependencies:
14:59:29,703 INFO [JBossASKernel] and demands:
14:59:29,703 INFO [JBossASKernel] jboss.ejb:service=EJBTimerService
14:59:29,703 INFO [JBossASKernel] persistence.unit:unitName=#demo
14:59:29,703 INFO [JBossASKernel] and supplies:
14:59:29,703 INFO [JBossASKernel] jndi:StudentDAO/remote
14:59:29,703 INFO [JBossASKernel] jndi:StudentDAO/remote-com.ejb.dao.StudentDAORemote
14:59:29,703 INFO [JBossASKernel] Class:com.ejb.dao.StudentDAORemote
14:59:29,703 INFO [JBossASKernel] Added bean(jboss.j2ee:jar=EntityBeanTest.jar,name=StudentDAO,service=EJB3) to
KernelDeployment of: EntityBeanTest.jar
14:59:29,750 INFO [PersistenceUnitDeployment] Starting persistence unit persistence.unit:unitName=#demo
14:59:29,765 WARN [Ejb3Configuration] Persistence provider caller does not implement the EJB3 spec correctly.
PersistenceUnitInfo.getNewTempClassLoader() is null.
14:59:29,765 INFO [AnnotationBinder] Binding entity from annotated class: com.ejb.entitybean.Student
14:59:29,765 INFO [EntityBinder] Bind entity com.ejb.entitybean.Student on table Student
14:59:29,812 INFO [HibernateSearchEventListenerRegister] Unable to find org.hibernate.search.event.FullTextIndexEventListener on
the classpath. Hibernate Search is not enabled.
14:59:29,812 INFO [ConnectionProviderFactory] Initializing connection provider:
org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider
14:59:29,812 INFO [InjectedDataSourceConnectionProvider] Using provided datasource
14:59:29,812 INFO [SettingsFactory] RDBMS: MySQL, version: 5.0.45-community-nt
14:59:29,812 INFO [SettingsFactory] JDBC driver: MySQL-AB JDBC Driver, version: mysql-connector-java-5.0.8 (
Revision: ${svn.Revision} )
14:59:29,812 INFO [Dialect] Using dialect: org.hibernate.dialect.MySQLDialect
14:59:29,828 INFO [TransactionFactoryFactory] Transaction strategy: org.hibernate.ejb.transaction.JoinableCMTTransactionFactory
14:59:29,828 INFO [TransactionManagerLookupFactory] instantiating TransactionManagerLookup:
org.hibernate.transaction.JBossTransactionManagerLookup
14:59:29,828 INFO [TransactionManagerLookupFactory] instantiated TransactionManagerLookup
14:59:29,828 INFO [SettingsFactory] Automatic flush during beforeCompletion(): disabled
14:59:29,828 INFO [SettingsFactory] Automatic session close at end of transaction: disabled
14:59:29,828 INFO [SettingsFactory] JDBC batch size: 15
14:59:29,828 INFO [SettingsFactory] JDBC batch updates for versioned data: disabled
14:59:29,828 INFO [SettingsFactory] Scrollable result sets: enabled
14:59:29,828 INFO [SettingsFactory] JDBC3 getGeneratedKeys(): enabled
14:59:29,828 INFO [SettingsFactory] Connection release mode: auto
14:59:29,828 INFO [SettingsFactory] Maximum outer join fetch depth: 2
14:59:29,828 INFO [SettingsFactory] Default batch fetch size: 1
14:59:29,828 INFO [SettingsFactory] Generate SQL with comments: disabled
14:59:29,828 INFO [SettingsFactory] Order SQL updates by primary key: disabled
14:59:29,828 INFO [SettingsFactory] Order SQL inserts for batching: disabled
14:59:29,828 INFO [SettingsFactory] Query translator: org.hibernate.hql.ast.ASTQueryTranslatorFactory
14:59:29,828 INFO [ASTQueryTranslatorFactory] Using ASTQueryTranslatorFactory
14:59:29,828 INFO [SettingsFactory] Query language substitutions: {}
14:59:29,828 INFO [SettingsFactory] JPA-QL strict compliance: enabled
14:59:29,828 INFO [SettingsFactory] Second-level cache: enabled
14:59:29,828 INFO [SettingsFactory] Query cache: disabled
14:59:29,828 INFO [SettingsFactory] Cache region factory : org.hibernate.cache.impl.bridge.RegionFactoryCacheProviderBridge
14:59:29,828 INFO [RegionFactoryCacheProviderBridge] Cache provider: org.hibernate.cache.HashtableCacheProvider
14:59:29,828 INFO [SettingsFactory] Optimize cache for minimal puts: disabled
14:59:29,828 INFO [SettingsFactory] Cache region prefix: persistence.unit:unitName=#demo
14:59:29,828 INFO [SettingsFactory] Structured second-level cache entries: disabled
14:59:29,828 INFO [SettingsFactory] Statistics: disabled
14:59:29,828 INFO [SettingsFactory] Deleted entity synthetic identifier rollback: disabled
14:59:29,828 INFO [SettingsFactory] Default entity-mode: pojo
14:59:29,828 INFO [SettingsFactory] Named query checking : enabled
14:59:29,828 INFO [SessionFactoryImpl] building session factory
14:59:29,859 INFO [SessionFactoryObjectFactory] Factory name: persistence.unit:unitName=#demo
14:59:29,859 INFO [NamingHelper] JNDI InitialContext properties:{java.naming.factory.initial=
org.jnp.interfaces.NamingContextFactory, java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces}
14:59:29,875 INFO [SessionFactoryObjectFactory] Bound factory to JNDI name: persistence.unit:unitName=#demo
14:59:29,875 WARN [SessionFactoryObjectFactory] InitialContext did not implement EventContext
14:59:29,875 INFO [SchemaExport] Running hbm2ddl schema export
14:59:29,875 INFO [SchemaExport] exporting generated schema to database
14:59:29,953 INFO [SchemaExport] schema export complete
14:59:29,953 INFO [NamingHelper] JNDI InitialContext properties:{java.naming.factory.initial=
org.jnp.interfaces.NamingContextFactory, java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces}
14:59:30,140 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EntityBeanTest.jar,name=StudentDAO,service=EJB3
14:59:30,140 INFO [EJBContainer] STARTED EJB: com.ejb.dao.StudentDAO ejbName: StudentDAO
14:59:30,218 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI:
StudentDAO/remote - EJB3.x Default Remote Business Interface
StudentDAO/remote-com.ejb.dao.StudentDAORemote - EJB3.x Remote Business Interface
从以上的信息可以看出,该发布过程执行了以下动作:
Ø 加载persistence.xml文件。
Ø 创建数据库表student,此时查看MySQL数据库就会看到新建的表student。
Ø 发布JNDI服务StudentDAO/remote。
这样我们就可以通过客户端访问JNDI服务StudentDAO/remote了。
6.1.7 开发客户端进行测试——StudentDAOClient.java
以上我们开发了实体Bean和会话Bean组件,并发布到了JBoss服务器中,下面我们来开发一个Java客户端程序,在该客户端创建一个实体对象,然后调用会话Bean组件插入该对象,往数据表中插入一条记录。
首先将实体类Student.java和接口类StudentDAORemote.java复制添加到测试项目EJBTestJava的对应包中,然后新建测试类StudentDAOClient.java,并开发如下代码:
程序6-5 测试类StudentDAOClient.java
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import com.ejb.dao.StudentDAORemote;
import com.ejb.entitybean.Student;
public class StudentDAOClient {
public static void main(String[] args) throws NamingException {
Properties props = new Properties();
props.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
props.setProperty("java.naming.provider.url", "localhost:1099");
props.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
try {
InitialContext ctx = new InitialContext(props);
StudentDAORemote studentDAO = (StudentDAORemote) ctx
.lookup("StudentDAO/remote");
Student student = new Student();
student.setName("刘中兵");
student.setSex(true);
student.setAge((short)25);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
student.setBirthday(format.parse("1981-05-04"));
student.setTelephone("12345678");
student.setAddress("北京");
studentDAO.insert(student);
System.out.println("已经插入一个学生记录!");
} catch (NamingException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
}
}
该类通过访问远程JNDI服务StudentDAO/remote,取得了远程会话Bean实例studentDAO。然后创建了一个Student实体对象,调用insert()函数插入该对象。运行该程序后,会在控制台中输出如下信息:
已经插入一个学生记录!
这就表明客户端的Java类已正确地将Student对象提交到远程会话Bean组件中了。此时再查看服务端JBoss的控制台,由于是第一次访问该实体Bean,因此JBoss会加载它并输出如下信息:
15:11:43,078 WARN [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a
ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
15:11:43,078 WARN [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a
ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
15:11:43,078 WARN [InterceptorRegistry] applicable interceptors is non-existent for public java.util.List
com.ejb.dao.StudentDAO.list(int,int)
15:11:43,093 WARN [InterceptorRegistry] applicable interceptors is non-existent for public boolean
com.ejb.dao.StudentDAO.deleteById(int)
15:11:43,109 WARN [InterceptorRegistry] applicable interceptors is non-existent for public com.ejb.entitybean.Student
com.ejb.dao.StudentDAO.selectById(int)
15:11:43,125 WARN [InterceptorRegistry] applicable interceptors is non-existent for public boolean
com.ejb.dao.StudentDAO.insert(com.ejb.entitybean.Student)
15:11:43,125 WARN [InterceptorRegistry] applicable interceptors is non-existent for public boolean
com.ejb.dao.StudentDAO.update(com.ejb.entitybean.Student)
15:11:43,140 WARN [InterceptorRegistry] applicable interceptors is non-existent for public java.lang.String
com.ejb.dao.StudentDAO.getNameById(int)
15:11:43,156 WARN [InterceptorRegistry] applicable interceptors is non-existent for public java.util.List
com.ejb.dao.StudentDAO.list(int,int)
15:11:43,171 WARN [InterceptorRegistry] applicable interceptors is non-existent for public boolean
com.ejb.dao.StudentDAO.deleteById(int)
15:11:43,171 WARN [InterceptorRegistry] applicable interceptors is non-existent for public com.ejb.entitybean.Student
com.ejb.dao.StudentDAO.selectById(int)
15:11:43,187 WARN [InterceptorRegistry] applicable interceptors is non-existent for public boolean
com.ejb.dao.StudentDAO.insert(com.ejb.entitybean.Student)
15:11:43,203 WARN [InterceptorRegistry] applicable interceptors is non-existent for public boolean
com.ejb.dao.StudentDAO.update(com.ejb.entitybean.Student)
15:11:43,218 WARN [InterceptorRegistry] applicable interceptors is non-existent for public java.lang.String
com.ejb.dao.StudentDAO.getNameById(int)
当以后再对该实体Bean进行访问时,就不会再输出以上的信息了,这是因为经过第一次加载后的实体Bean对象已经被实体管理器EntityManager缓存了,就会直接从缓存中取得该实体对象。