EJB调用和练习
1、引言
2、实验环境
- 操作系统:Windows 10
- 实验工具:IntelliJ IDEA
- 环境:Java jdk 1.8
3、实验过程
3.1 掌握EJB调用原理
3.1.1 EJB 的基本原理
EJB(Enterprise Java Bean)是JavaEE中面向服务的体系架构的解决方案,可以将功能封装在服务器端,以服务的形式对外发布,客户端在无需知道方法细节的情况下来远程调用方法,大大降低了模块间的耦合性,提高了系统的安全性和可维护性。
EJB实际上是服务器端运行的一个对象,只不过该对象所对应的类并不被客户端所知,该对象对外发布的是一个服务名称,并提供一个可以被客户端调用的接口。通俗点说,EJB就是一个可以被客户端调用,但是并不让客户端知道源代码的类的对象。
因此,EJB并不是普通的Java Bean,普通的JavaBean是一个符合某种规范的Java类文件,只能作为一个类被调用,只有调用的时候才运行,是一个进程内组件。而EJB并不是一个单独的文件,其组成包括:
-
类文件:实现基本方法的类,封装了需要实现的商务逻辑,数据逻辑或消息处理逻辑,具有一定的编程规范,代码不能被客户端得知。
-
接口文件:接口是EJB组件模型的一部分,里面提供的方法一般和需要被远程调用的方法一致,一般情况下,要求类文件必须和接口中的定义保持一致性。
-
必要的情况下,编写一些配置文件,用于描述EJB部署过程中的一些信息。
EJB可以作为一个服务被调用,可以单独运行,是一个进程级组件。EJB中还提供了一些安全管理、事务控制功能,使得我们调用EJB时,不需要太多地束缚于这些问题的编码。
EJB 定义了四种类型的组件:
-
Session Bean:会话Bean,封装业务逻辑,负责完成某个操作。根据生命周期的不同,又可以分为:
(1) Stateless Session Bean: 无状态会话Bean,不存储用户相关信息,一般说来,在服务器端,一个Bean对象可能为很多客户服务,如下图所示:
(2) Stateful Session Bean: 有状态会话Bean,可以存储用户相关信息,在服务器端,一个Bean对象只为一个客户服务,如下图所示:
-
Entity Bean:实体Bean,类似Hibernate,封装数据库中的数据,代表底层数据的持久化对象,把表中的列映射到对象的成员,主键在实体Bean中具有唯一性,一个实体Bean对象对应表中的一行,这将在下一章讲解。
-
Message Driven Bean:消息驱动Bean,是一种异步的无状态组件,和无状态会话组件具有相似性,是JMS消息的消费者,可以和JMS配合起来使用。
3.1.2 EJB运行原理
在EJB中,常用的的组件有:客户端、接口(远程接口或者本地接口)、EJB实现类、JNDI名称等。它们之间的关系如下图所示:
对于一个业务操作,其执行步骤为:
首先,服务器端将EJB发布为一个JNDI名称,并提供一个接口文件。不过,值得注意的是,如果客户端和EJB运行在同一个容器内,可以提供的是本地(Local)接口,如果运行在不同的Java虚拟机内,提供的是远程(Remote)接口。接下来步骤如下:
-
客户端向服务器发起连接,在服务器上寻找相应的JNDI名称,如果找到,返回一个对象。
-
客户端将该对象强制转换为接口类型。
-
客户端调用接口中的方法,实际上调用了服务器端EJB内的方法。
因此,利用EJB编程,有以下几个步骤:
-
编写EJB实现类。
-
编写接口。
-
部署到服务器中,设定JNDI名称。
-
编写客户端,并将接口拷贝给客户端,将JNDI名称公布,客户端调用EJB。
3.2 实验实现过程
3.2.1 校友信息类创建
包含校友信息成员变量以及相应的getter和setter方法;
3.2.2 接口创建
创建接口类,包括基本的增删改查方法;
3.2.3 创建接口的实现类
创建实现类,并用Stateful注解,表明这是一个有状态的会话bean;
连接数据库并创建数据库表ALUMNI来存储校友信息;
以下为实现类实现的接口方法:以select方法为例展示数据库访问操作;
3.2.4 以假乱真的校友信息生成器
校友各个信息的自动生成:
public String getRandomName() {
String[] firstNameArray = { "李", "王", "张", "刘", "陈", "杨", "赵", "黄", "周", "吴", "徐", "孙", "胡", "朱", "高", "欧阳", "太史", "端木", "上官", "司马" };// 20个姓,其中5个复姓
String[] lastNameArray = { "伟", "勇", "军", "磊", "涛", "斌", "强", "鹏", "杰", "峰", "超", "波", "辉", "刚", "健", "明", "亮", "俊", "飞", "凯", "浩", "华",
"平", "鑫", "毅", "林", "洋", "宇", "敏", "宁", "建", "兵", "旭", "雷", "锋", "彬", "龙", "翔", "阳", "剑", "静", "敏", "燕", "艳", "丽", "娟", "莉", "芳", "萍", "玲", "娜", "丹", "洁", "红", "颖", "琳", "霞", "婷", "慧",
"莹", "晶", "华", "倩", "英", "佳", "梅", "雪", "蕾", "琴", "璐", "伟", "云", "蓉", "青", "薇", "欣", "琼", "宁", "平", "媛" };// 80个常用于名字的单字
int firstPos = (int) (20 * random());
StringBuilder name = new StringBuilder(firstNameArray[firstPos]);
int lastLen = (int) (2 * random()) + 1;
for (int i = 0; i < lastLen; i++) {
int lastPos = (int) (80 * random());
name.append(lastNameArray[lastPos]);
}
return name.toString();
}
public String getRandomGender() {
String[] genderArray = { "男", "女"};
int genderPos = (int) (2 * random()) ;
StringBuilder gender = new StringBuilder(genderArray[genderPos]);
return gender.toString();
}
public String getRandomBirthday(int year){
int byear=year-18;
int month=(int) (12 * random())+1; //生成[1,12]的整数;月
int day;
if(month==2)
{
day=(int) (28 * random())+1;
}
else
{
day=(int) (30 * random())+1;
}
return byear+"-"+month+"-"+day;
}
public String getRandomWechat() {
char[] numArray = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
int wechatLen = (int) (5 * random()) + 7; // wechat长度可能为7~11位
StringBuilder wechat = new StringBuilder();
for (int i = 0; i < wechatLen; i++) {
int wechatPos = (int) (10 * random());
wechat.append(numArray[wechatPos]);
}
return wechat.toString();
}
public String getRandomEnterYear() {
int enter = (int) (24 * random()) + 1990;
return String.valueOf(enter);
}
public String getRandomGraduateYear(int year) {
int gyear = year+4;
return String.valueOf(gyear);
}
public String getRandomPhone() {
String[] telFirst="184,135,136,137,138,139,150,151,152,157,158,159,130,131,132,155,156,133,153".split(",");
int index=(int) (telFirst.length * random());
String first=telFirst[index];
String second=String.valueOf((int) (8888 * random())+10000).substring(1);
String thrid=String.valueOf((int) (9100 * random())+10000).substring(1);
return first+second+thrid;
}
public String getRandomEmail() {
int length = (int) (10 * random())+6;
String base ="abcdefghijklmnopqrstuvwxyz0123456789";
String[] email_suffix = {"@gmail.com","@yahoo.com","@msn.com","@hotmail.com","@aol.com","@ask.com","@live.com","@qq.com","@0355.net","@163.com",
"@163.net","@263.net","@3721.net","@yeah.net","@googlemail.com","@126.com","@sina.com","@sohu.com","@yahoo.com.cn"};
StringBuffer email = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = (int) (random() * base.length());
email.append(base.charAt(number));
}
email.append(email_suffix[(int) (random() * email_suffix.length)]);
return email.toString();
}
public String getRandomWorkPlace() {
String[] road="长城南路,流亭立交桥,虹桥广场,华城路,康城街,正阳路,和阳广场,中城路,江城大厦,顺城路,安城街,山城广场,春城街,国城路,泰城街,德阳路,明阳大厦,春阳路,艳阳街,秋阳路,硕阳街,青威高速,瑞阳街,丰海路,双元大厦,天津路,保定街,安徽路,河北大厦,黄岛路,北京街,莘县路,台西纬四街,台西纬二路,西陵峡二街,西陵峡三路,台西纬三广场,台西纬五路,明月峡大厦,青铜峡路,台西二街,观音峡广场,瞿塘峡街,台西四街,三门峡路,寿张街,嘉祥路,南村广场,范县路,西康街,云南路,滋阳街,邹县广场,濮县街,磁山路,汶水街,西藏路,信号山支路,延安一街,信号山路,兴安支街,福山支广场,红岛支大厦,金口二街,海阳路,龙口街,恒山路,鱼山广场,掖县路,福山大厦,红岛路,常州街,大学广场,龙华街,齐河路,莱阳街,黄县路,张店大厦,祚山路,苏州街,华山路,伏龙街,江苏广场,龙江街,王村路,琴屿大厦,齐东路,京山广场,龙山路,牟平街,延安三路,延吉街,南京广场,东海东大厦,银川西路,海口街,山东路,太平角三大厦,漳州路一路,漳州街二街,新湛二路,三明北街,新湛支路,湛山五街,泰州三广场,东海一大厦,天台二路,闽江二广场,闽江一大厦,屏东支路,大尧三路,晓望支街,秀湛二路,逍遥三大厦,澄海路,莆田街,海游路,镇江街,石岛广场,宜兴大厦,旌德路,汇泉广场,宁国路,泉州街,如东路,奉化街,鹊山广场,莲岛大厦,华严路,嘉义街,古田路,南平广场,秀湛路,长汀街,湛山路,徐州大厦,丰县广场,汕头街,新竹路,黄海街,安庆路,基隆广场,韶关路,云霄大厦,新安路,仙居街,屏东广场,晓望街,海门路,珠海街,上杭路,永嘉大厦,冠县支广场,小港一大厦,市场一路,小港二街,清平路,广东广场,新疆路,博平街,港通路,小港沿,福建广场,高唐街,茌平路,港青街,高密路,阳谷广场,平阴路,夏津大厦,邱县路,渤海街,恩县广场,旅顺街,长清街,长安路,惠民街,禹城街,临清路,东阿街,吴淞路,大港沿,辽宁路,棣纬二大厦,大港纬一路,无棣三街,黄台支广场,大港三街,无棣一路,贮水山大厦,泰山支路,大港一广场,无棣四路,大连支街,大港二路,锦州支街,德平广场,高苑大厦,长山路,乐陵街,临邑路,嫩江广场,合江路,大连街,博兴路,蒲台大厦,黄台广场,城阳街,临淄路,安邱街,临朐路,青城广场,商河路,热河大厦,包头路,无棣街,铁山广场,锦州街,桓台路,兴安大厦,邹平路,胶东广场,章丘路,丹东街,华阳路,青海街,泰山广场,顺兴街,利津路,阳明广场,人和路,郭口大厦,营口路,昌邑街,标山路,云溪广场,太清路".split(",");
int index=(int) (road.length * random());
String first=road[index];
String second=String.valueOf((int)(random()*11+100))+"号";
return first+second;
}
public String getRandomCity(){
String citys[] = {"北京","广东","山东","江苏","河南","上海","河北","浙江","香港","山西","陕西","湖南","重庆","福建","天津","云南","四川","广西","安徽","海南","江西","湖北","山西","辽宁","内蒙古"};
int index=(int) (citys.length * random());
return citys[index];
}
public String getRandomJob(){
String jobs[] = {"大学老师","C++程序员","Java程序员","PHP程序员","前端程序员","运维","中学老师","分析师","经理","工程师","科学家","医生","总经理","设计师","商人"};
int index=(int) (jobs.length * random());
return jobs[index];
}
生成校友信息:
public void setRandomStudent(){
this.studentName = getRandomName();
this.gender=getRandomGender();
this.enterYear = getRandomEnterYear();
int year=Integer.parseInt(enterYear);
this.birthday=getRandomBirthday(year);
this.graduateYear=getRandomGraduateYear(year);
this.city=getRandomCity();
this.email=getRandomEmail();
this.job=getRandomJob();
this.phone=getRandomPhone();
this.workplace=getRandomWorkPlace();
this.wechat=getRandomWechat();
}
生成校友信息存入数据库:
@Override
public void initStudent(){
try {
SchoolFellow schoolFellow =new SchoolFellow();
for(int i=0;i<5;i++)
{
schoolFellow.setRandomStudent();
String sql="insert into ALUMNI(studentName,gender,birthday,enterYear,graduateYear,city,workplace,job,phone,email,wechat) " +
"values('"+ schoolFellow.getStudentName()+"','"+ schoolFellow.getGender()+"','"+ schoolFellow.getBirthday()+"','"+ schoolFellow.getEnterYear() +"','"
+ schoolFellow.getGraduateYear()+"','"+ schoolFellow.getCity()+"','"+ schoolFellow.getWorkplace()+"','"+ schoolFellow.getJob()+"','"+ schoolFellow.getPhone()
+"','"+ schoolFellow.getEmail()+"','"+ schoolFellow.getWechat()+"')";
stmt.execute(sql);
}
System.out.println("init students successful");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
3.2.5 运行结果
服务器端:
登录:
删除:
插入:
修改:
第二次登录显示登录次数和上次登录时间:
4、总结
1、端口冲突问题:刚开始服务器端运行时会出现端口冲突,但是修改wildfly配置文件standalone.xml修改端口是没有用的,所以后面就把和它冲突的其他服务进程关闭了。
2、要理解清楚EJB调用的原理,这样对于在实验中遇到的问题才能准确定位和解决。