美文网首页
中间件技术——实验二:EJB调用和练习

中间件技术——实验二:EJB调用和练习

作者: 东郭_先森 | 来源:发表于2019-04-10 11:02 被阅读0次

    一、实验内容

    1. 实验目的
      理解EJB,利用wildfly服务器容器进行远程调用。(笔者实现的是服务器端和客户端都在同一台电脑上)
    2. 实验准备操作
      a.下载安装java jdk 1.6或以上(笔者用的版本是1.8.0_201);
      b.下载安装Maven(笔者用的版本是3.6.0)
      c.熟悉IDEA或eclipse等开发环境(笔者用的版本是IDEA 2018.3.5)。
      d.下载并安装Wildfly服务器(网上的教程大多是10.0.0版本,最新的版本是16.0.0,笔者用的版本是16.0.0)。
      e.下载并安装Mysql数据库
      f.windows10家庭版
    3. 实验内容
      a.建立有状态的Java Bean,实现以下功能:
      b.操作用户(录入人员)登陆后,显示本次登陆的次数和上一次登陆的时间;
      c.操作用户登录后,可进行校友的检索、修改、删除、统计等功能;
      d.5分钟如果没有操作,则自动登出系统;
      e.操作用户退出时,显示用户连接的时间长度,并把此次登陆记录到数据库。

    二、实验过程

    1.代码打包如下:链接: https://pan.baidu.com/s/1mxV0BWVY3QjMjz3L5anSZQ 提取码: es83
    2.主体代码:

    • server MyRemote.java
    package org.jboss.as.quickstarts.ejb.remote.stateful;
    
    import java.util.List;
    
    public interface MyRemote {
        boolean login(String username, String password);
        boolean mysql_init();
        int update(String sql);
        List query(String sql);
    }
    
    • server Bean.java
    package org.jboss.as.quickstarts.ejb.remote.stateful;
    
    import javax.ejb.Remote;
    import javax.ejb.Stateful;
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import javax.sql.DataSource;
    import java.sql.*;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    
    @Stateful
    @Remote(MyRemote.class)
    public class Bean implements MyRemote {
        //不知道为什么,我用resultset返回query的结果到client时,就会报错,缺少什么resultset的jdk,于是我把resultset转换成list返回
        private List convertList(ResultSet rs) throws SQLException{
            List list = new ArrayList();
            ResultSetMetaData md = rs.getMetaData();//获取键名
            int columnCount = md.getColumnCount();//获取行的数量
            while (rs.next()) {
                Map rowData = new HashMap();//声明Map
                for (int i = 1; i <= columnCount; i++) {
                    rowData.put(md.getColumnName(i), rs.getObject(i));//获取键名及值
                }
                list.add(rowData);
            }
            return list;
        }
        //登录
        public boolean login(String username, String password){
            if(username.equals("cyd") && password.equals("cyd"))
                return true;
            else
                return false;
        }
        //数据库初始化
        public boolean mysql_init(){
            boolean flag = false;
            try {
                Context context = new InitialContext();
                DataSource dataSource = (DataSource) context.lookup("java:jboss/datasources/ExampleDS");
                Connection connection = dataSource.getConnection();
                Statement statement = connection.createStatement();
                statement.executeUpdate("create table admin(login_time TIMESTAMP (23) not null);");//一定不能大于23,不然会报错,建表失败
                //姓名、性别、生日、入学年份、毕业年份、工作城市/地区、工作单位、职务、手机、邮箱、微信
                statement.executeUpdate("create table alumni(name char (20) not null, gender char (10), birthday char (20), " +
                        "entrance int (4), graduation int (4), location char (20), company char (20), job char (20)," +
                        "phone char(11) not null, mail char (20), wechat char (20));");
                statement.executeUpdate("insert into alumni(name,gender,birthday,entrance,graduation,location,company,job,phone,mail,wechat)" +
                        "values('cyd','male','19971012','2016','2020','xiamen','xmu','student','15659833304','767937571@qq.com','cyd767937571')");
                statement.executeUpdate("insert into alumni(name,gender,birthday,entrance,graduation,location,company,job,phone,mail,wechat)" +
                        "values('cxx','female','19941209','2014','2018','xiamen','liantong','manager','15659833303','7685946828@qq.com','wechat')");
                flag = true;
                connection.close();
                statement.close();
            } catch (NamingException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return flag;
        }
        //insert, delete, update用这个,返回的是更新的行数
        public int update(String sql){
            int ins = 0;
            try {
                Context context = new InitialContext();
                DataSource dataSource = (DataSource) context.lookup("java:jboss/datasources/ExampleDS");
                Connection connection = dataSource.getConnection();
                Statement statement = connection.createStatement();
                ins = statement.executeUpdate(sql);
                connection.close();
                statement.close();
            } catch (NamingException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return ins;
        }
        //select用这个,返回的是本来是结果集,现在被我转换成List
        public List query(String sql){
            List list = new ArrayList();
            try {
                Context context = new InitialContext();
                DataSource dataSource = (DataSource) context.lookup("java:jboss/datasources/ExampleDS");
                Connection connection = dataSource.getConnection();
                Statement statement = connection.createStatement();
                ResultSet rs = statement.executeQuery(sql);
                list = convertList(rs);
                connection.close();
                statement.close();
            } catch (NamingException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return list;
        }
    }
    
    • client RemoteEJBClient.java
    package org.jboss.as.quickstarts.ejb.remote.client;
    import org.jboss.as.quickstarts.ejb.remote.stateful.MyRemote;
    
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    
    import java.sql.SQLException;
    import java.sql.Timestamp;
    import java.util.*;
    
    public class RemoteEJBClient {
    
        public static void main(String[] args) throws Exception {
    
            // Invoke a stateful bean
            invokeStatefulBean();
        }
    
    
        /**
         * Looks up a stateful bean and invokes on it
         *
         * @throws NamingException
         */
        private static void invokeStatefulBean() throws NamingException{
            final MyRemote statefulRemoteCounter = lookupRemoteStatefulCounter();
            statefulRemoteCounter.mysql_init();
            boolean flag = true;
            while(flag){
                System.out.println("1:login");
                System.out.println("others:exit");
                Scanner input_login = new Scanner(System.in);
                switch (input_login.nextInt()) {
                    case 1:
                        Scanner input_info = new Scanner(System.in);
                        System.out.println("username:");
                        String username = input_info.nextLine();
                        System.out.println("password:");
                        String password = input_info.nextLine();
                        if (statefulRemoteCounter.login(username, password)) {
                            Timestamp login_time = new Timestamp(System.currentTimeMillis());
                            List list_time = statefulRemoteCounter.query("select login_time from admin;");
                            if (list_time.isEmpty()) {
                                System.out.println("You have never logged in!");
                            } else {
                                int login_count = 1;
                                Timestamp timestamp = new Timestamp(System.currentTimeMillis());
                                for (Object list : list_time) {
                                    login_count++;
                                    HashMap hm = (HashMap) list;
                                    for (Object key : hm.keySet()) {
                                        timestamp = (Timestamp) hm.get(key);
                                    }
                                }
                                System.out.println("You have logged in to the system " + login_count + " times.");
                                System.out.println("The last time you logged into the system was " + timestamp + ".");
                            }
                            int result_int = statefulRemoteCounter.update(
                                    "insert into admin(login_time)values('" + login_time + "');");
    //                        if (result_int > 0) {
    //                            System.out.println("insert data successfully!");
    //                        }
                            System.out.println("login successfully");
                            boolean flag2 = true;
                            Timestamp init_time = new Timestamp(System.currentTimeMillis());
                            while(flag2) {
                                System.out.println("1:select");
                                System.out.println("2:update");
                                System.out.println("3:delete");
                                System.out.println("4:total");
                                System.out.println("5:insert");
                                System.out.println("others:logout");
                                Scanner input_operation = new Scanner(System.in);
                                int input_num = input_operation.nextInt();
                                Timestamp current_time = new Timestamp(System.currentTimeMillis());
                                float difftime = (current_time.getTime()-init_time.getTime())/1000;
                                if(difftime > 300){
                                    System.out.println("You have logged out of the system because it has not been operated for a long time!");
                                    current_time = new Timestamp(System.currentTimeMillis());
                                    difftime = (current_time.getTime()-login_time.getTime())/1000;
                                    System.out.println("Your login time is "+difftime+" seconds.");
                                    break;
                                }
                                init_time = current_time;
                                switch (input_num){
                                    case 1:
                                        System.out.println("input the content you want to select(name,gender,birthday,entrance,graduation,location,company,job,phone,mail,wechat,*):");
                                        Scanner input_content1 = new Scanner(System.in);
                                        String content1 = input_content1.nextLine();
                                        System.out.println("input the someone you want to show(where name = 'caiyuedong' or nothing):");
                                        Scanner input_value1 = new Scanner(System.in);
                                        String value1 = input_value1.nextLine();
                                        List select_result1=statefulRemoteCounter.query("select "+content1+" from alumni "+value1+";");
                                        if(select_result1.isEmpty()){
                                            System.out.println("Check no such person!");
                                        }else {
                                            for (Object sr : select_result1) {
                                                System.out.println(sr);
                                            }
                                        }
                                        break;
                                    case 2:
                                        System.out.println("input the name you select:");
                                        Scanner input_name2 = new Scanner(System.in);
                                        String name2 = input_name2.nextLine();
                                        List select_result2=statefulRemoteCounter.query("select * from alumni where name ='"+name2+"';");
                                        if(select_result2.isEmpty()){
                                            System.out.println("Check no such person!");
                                        }else {
                                            for (Object sr : select_result2) {
                                                System.out.println(sr);
                                            }
                                            System.out.println("input the content you want to update(name,gender,birthday,entrance,graduation,location,company,job,phone,mail,wechat):");
                                            Scanner input_content2 = new Scanner(System.in);
                                            String content2 = input_content2.nextLine();
                                            System.out.println("input the value you want to update:");
                                            Scanner input_value2 = new Scanner(System.in);
                                            String value2 = input_value2.nextLine();
                                            int update_result = statefulRemoteCounter.update("update alumni set "+content2+"='"+value2+"' WHERE name='"+name2+"';");
                                            if(update_result>0){
                                                System.out.println("Update successfully");
                                            }else {
                                                System.out.println("Update unsuccessfully");
                                            }
                                        }
                                        break;
                                    case 3:
                                        System.out.println("input the content you want to delete(name,gender,birthday,entrance,graduation,location,company,job,phone,mail,wechat):");
                                        Scanner input_content3 = new Scanner(System.in);
                                        String content3 = input_content3.nextLine();
                                        System.out.println("input the value you want to delete:");
                                        Scanner input_value3 = new Scanner(System.in);
                                        String value3 = input_value3.nextLine();
                                        System.out.println("Are you sure?Y/N");
                                        Scanner input_sure3 = new Scanner(System.in);
                                        String sure3 = input_sure3.nextLine();
                                        if(sure3.equals("Y")){
                                            statefulRemoteCounter.update("delete from alumni where "+content3+" = '"+value3+"';");
                                            System.out.println("delete successfully");
                                        }
                                        break;
                                    case 4:
                                        int count = 0;
                                        System.out.println("input the content you want to count(name,gender,birthday,entrance,graduation,location,company,job,phone,mail,wechat):");
                                        Scanner input_content4 = new Scanner(System.in);
                                        String content4 = input_content4.nextLine();
                                        System.out.println("input the value you want to count:");
                                        Scanner input_value4 = new Scanner(System.in);
                                        String value4 = input_value4.nextLine();
                                        List select_result4=statefulRemoteCounter.query("select * from alumni where "+content4+"='"+value4+"';");
                                        if(select_result4.isEmpty()){
                                            System.out.println("no person!");
                                        }else {
                                            for (Object sr : select_result4) {
                                                System.out.println(sr);
                                                count++;
                                            }
                                            System.out.println("There are "+count+" people totally");
                                        }
                                        break;
                                    case 5:
                                        System.out.println("input the value you want to insert(name,gender,birthday,entrance,graduation,location,company,job,phone,mail,wechat):");
                                        System.out.println("such as 'cyd','male','19971012','2016','2020','xiamen','xmu','student','15659833304','767937571@qq.com','cyd767937571'");
                                        Scanner input_name5 = new Scanner(System.in);
                                        String value5 = input_name5.nextLine();
                                        System.out.println(value5);
                                        int insert_result = statefulRemoteCounter.update("insert into alumni(name,gender,birthday,entrance,graduation,location,company,job,phone,mail,wechat)" +
                                                "values("+value5+")");
                                        if(insert_result>0)
                                            System.out.println("insert successfully");
                                        break;
                                        default:
                                            current_time = new Timestamp(System.currentTimeMillis());
                                            difftime = (current_time.getTime()-login_time.getTime())/1000;
                                            System.out.println("Your login time is "+difftime+" seconds.");
                                            flag2 = false;
                                            break;
                                }
                            }
                        } else {
                            System.out.println("login unsuccessfully");
                        }
                        break;
                        default:
                            flag = false;
                            break;
                }
            }
        }
    
    
        /**
         * Looks up and returns the proxy to remote stateful counter bean
         *
         * @return
         * @throws NamingException
         */
        private static MyRemote lookupRemoteStatefulCounter() throws NamingException {
            final Hashtable<String, String> jndiProperties = new Hashtable<>();
            jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
            final Context context = new InitialContext(jndiProperties);
    
            // The JNDI lookup name for a stateful session bean has the syntax of:
            // ejb:<appName>/<moduleName>/<distinctName>/<beanName>!<viewClassName>?stateful
            //
            // <appName> The application name is the name of the EAR that the EJB is deployed in
            // (without the .ear). If the EJB JAR is not deployed in an EAR then this is
            // blank. The app name can also be specified in the EAR's application.xml
            //
            // <moduleName> By the default the module name is the name of the EJB JAR file (without the
            // .jar suffix). The module name might be overridden in the ejb-jar.xml
            //
            // <distinctName> : WildFly allows each deployment to have an (optional) distinct name.
            // This example does not use this so leave it blank.
            //
            // <beanName> : The name of the session been to be invoked.
            //
            // <viewClassName>: The fully qualified classname of the remote interface. Must include
            // the whole package name.
    
            // let's do the lookup
            return (MyRemote) context.lookup("ejb:/wildfly-ejb-remote-server-side/Bean!"
                + MyRemote.class.getName() + "?stateful");
        }
    }
    

    3.我的实验过程
    我的代码是在一个demo(链接: https://pan.baidu.com/s/1OAkiqpxF2e9W7uOynX1Y4A 提取码: qezh)的基础上修改的。实验思路很清晰,用server连接数据库,然后在client端传送操作指令到server与数据库交互。刚开始的时候,打算使用JDBC连接数据库,但是每次都会报以下错误
    java.lang.ClassNotFoundException: jdbc:mysql://127.0.0.1:3306/ejb_remote
    在网上查找了很多相关的教程,例如在server的文件右键中的“Open Module Setting”添加jar包依赖,修改pom.xml文件等,都没办法解决这个问题,(有关于以上两个方法见后面的分享),于是我改用JNDI连接Mysql,(有关于JDBC和JDNI的优劣等见后面的分享),核心代码如下:

    Context context = new InitialContext();
    DataSource dataSource = (DataSource) context.lookup("java:jboss/datasources/ExampleDS");//连接默认的数据库
    Connection connection = dataSource.getConnection();//创建连接
    Statement statement = connection.createStatement();//用来执行sql语句
    statement.executeUpdate("create table admin(login_time TIMESTAMP (23) not null);");//.executeUpdate返回的是受影响的行数,executeQuery返回的ResultSet,即操作的结果集合
    connect.close;
    statement.close;
    

    接下来是最坑的阶段了,我以为wildfly要自己手动配置,于是我从官网上https://wildfly.org/downloads/下载了wildfly16.0.0,但是DataSource的配置一直配置不好,于是我更改版本,换成wildfly10.0.0版本(链接: https://pan.baidu.com/s/12DtYy022icKQaHlnSFllcQ 提取码: ijxb ),重新配置数据源,(配置数数据源的方法见后面的分享),这次成功了(科学的终点是玄学?有时候我也搞不懂,按照教程来,有时候可以,有时候不行),到这个时候我才发现,原来我第一次运行demo的时候,这个demo会自动下载并部署好一个wildfly16.0.0,以后的每次运行都是重新加载wildfly16.0.0,可以理解成重置,同时默认连接好Mysql,即数据源java:jboss/datasources/ExampleDS,每次重启server的时候,Mysql都会清空,所以要重新建表,初始化表。运行server后,会生成一个target文件夹,里面有一些配置,包括wildfly的文件夹。


    所以其实如果知道这个东西之后,实验的内容就很简单了,直接在MyRemote.java里面设置接口,在Bean.java里面实现函数,从Client里面调用函数,处理好逻辑和熟悉SQL语句的话根本不难。以下是一些运行结果的截图


    三、分享及我遇见的各种bug(满满的都是干货)

    1.wildfly安装与环境配置,以及一些操作细节(上述的代码无需安装和配置wildfly)

    • 安装:直接解压缩包到目录即可,注意路径中尽量不要出现中文或者空格,(其实很多的地方都会这么说明,避免因为编码问题出现bug)
    • 配置环境变量:我的电脑->右键属性->高级系统设置->高级->环境变量->系统变量,点击下面的“新建”按钮,输入变量名为:JBOSS_HOME,变量值为:D:\wildfly-16.0.0.Final,这个变量值更改为自己的安装路径,然后再找到path,双击或者点击下面的”编辑“,点击”新建“,输入%JBOSS_HOME%\bin,一路确定退出就行了。
    • tip1:如果有安装多个wildfly,需要更换不同的wildfly的版本,直接在上述的JBOSS_HOME修改成其他版本相对应的路径即可。
    • tip2:打开wildfly的目录,进入bin,打开add-user.bat,添加管理员账户,一路按照提示操作就行,然后打开同目录下的standalone.bat,不要关闭,在浏览器中( chrome天下第一)输入127.0.0.1:8080,如果成功进入如下界面,则证明安装成功。



      然后可以点击Administration Console,进入管理界面,如果出现下图情况,
      重新打开add-user.bat,添加一下账户就行了,如果还是不行的话,重启电脑一下,然后打开standalone.bat,记住这个standalone.bat不能关闭,再进入这个网站就行了。如果顺利的话会出现这个

      输入设置好的账户密码登录之后可以看见这个



      以下是配置数据源的地方

      点击“view”进入以下界面

      点击”disable“之后就可以编辑相关信息了,最后在connection里面点击test connection,如果显示

      就证明配置数据源成功了。

    • tip3:配置数据源(单独拎出来说明一下)
      在配置数据源的地方,点击”add“,选择”MYSQL DATASOURCE“,设置name和jndi name等等,直到以下这步



      要将connection url里面3306/后面的名称改成你在mysql里要使用的database的名字。

    • tip4:https://dev.mysql.com/doc/index-connectors.htmlmysql和java连接的jar包,添加到项目的目录中,然后在项目的pom.xml文件内,找到dependencies,模拟
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    

    或者直接在下图添加


    • tip5:jboss7之后就改名为wildfly了,所以如果搜wildfly找不到攻略的话,可以尝试搜一下jboss。

    2.jdbc和jdni

    JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序.
    JNDI全名为Java Naming and Directory Interface.JNDI主要提供应用程序所需要资源上命名目录服务。在Java EE环境中,JNDI扮演了一个很重要的角色,它提供了一个接口让用户在不知道资源所在位置的情形下,取得该资源服务。就好比网络磁盘驱动器的功能一样。如果有人事先将另一台机器上的磁盘驱动器接到用户的机器上,用户在使用的时候根本就分辨不出现在的驱动器是存在本端,还是在另一端的机器上,用户只需取得资源来用,根本就不知道资源在什么地方。
    以上概念来自百度百科。

    • jdbc的连接操作简单,核心代码如下
    String driver = "com.mysql.jdbc.Driver";
    String url = "jdbc:mysql://localhost:3306/mysqldb";
    String username = "root";
    String password = "";
    Connection conn = null;
    try {
         Class.forName(driver); //classLoader,加载对应驱动
         conn = (Connection) DriverManager.getConnection(url, username, password);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
    }
    
    • jdni的核心代码
    Context context = new InitialContext();
    DataSource dataSource = (DataSource) context.lookup("java:jboss/datasources/ExampleDS");//连接默认的数据库
    Connection connection = dataSource.getConnection();//创建连接
    Statement statement = connection.createStatement();//用来执行sql语句
    statement.executeUpdate("create table admin(login_time TIMESTAMP (23) not null);");//.executeUpdate返回的是受影响的行数,executeQuery返回的ResultSet,即操作的结果集合
    connect.close;
    statement.close;
    
    • 对比
      我觉得这个博客的内容说的比较详细,相比我个人的理解更加全面https://www.cnblogs.com/panjun-Donet/articles/1182204.html
      但是总而言之,jdbc需要先找到数据库的驱动,(Class.forNAME),修改数据库时需要重新修改代码,jdni只需要修改服务器配置。jndi的适用范围更广,鲁棒性更强。

    3.运行的代码解读
    server端的运行结果,挑选几条特别关心
    [org.jboss.as] (MSC service thread 1-2) WFLYSRV0049: WildFly Full 16.0.0.Final (WildFly Core 8.0.0.Final) starting ——启动的是默认配置的wildfly16
    [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-7) WFLYJCA0001: Bound data source [java:jboss/datasources/ExampleDS] ——绑定的数据源是默认配置的ExampleDS
    [org.jboss.as.server] (management-handler-thread - 1) WFLYSRV0010: Deployed "wildfly-ejb-remote-server-side.jar" (runtime-name : "wildfly-ejb-remote-server-side.jar") ——到这步说明部署成功,可以开始运行client端,在这之前运行client会报错,大概意思是连接被拒绝。

    4.总结
    兜兜转转,配环境那么久到最后发现demo已经帮你搞定一切了,只需要直接编写逻辑代码就行,有点难受,不过实际配环境这么久,查了很多官方文档和看了很多博客,也学习到了很多东西,至少我对wildfly、maven、jdni的运用的了解加深了很多。

    以上有小部分内容引用了其他博客的内容,都有附上链接,以及一些代码等也有放在百度网盘里了,如果有侵权的地方,我会立即删除。以上,终于完成这个作业了,舒服。

    相关文章

      网友评论

          本文标题:中间件技术——实验二:EJB调用和练习

          本文链接:https://www.haomeiwen.com/subject/thfzbqtx.html