你可以使用 BMP 来解决多表的问题!

解决方案 »

  1.   

    to lanlansky :
    能不能给我一个简单的例子:如何做一个BMP ? 或者发一个程序给我看看?
    我的邮件[email protected]
    谢谢
      

  2.   

    Bean 管理的持续性  
    bean 管理的持续 (BMP) Enterprise Bean 负责将其状态与数据库同步,就象容器管理的持续一样。bean 使用数据库 API(通常是 JDBC)来读取其字段并将字段写入数据库,但容器会告诉它何时执行每个同步操作,并会自动管理 bean 的事务。bean 管理的持续可以让 bean 开发人员灵活地执行对于容器来说太过于复杂的持续操作,或者使用容器不支持的数据源 -- 例如,定制或旧的数据库。 在本屏面中,您将把 CustomerBean 类修改成 BMP bean,而不是 CMP bean。这个修改根本不会影响远程或本地接口。实际上,不会直接修改原始 CustomerBean。事实上,您将通过扩展 bean 并覆盖适当方法来将它更改成 bean 管理的持续。以下是一个类定义,这个类将扩展 Customer bean 类以使它成为 BMP 实体。大多数情况下,不必扩展 bean 以使它成为 BMP,只要直接将 bean 实现成 BMP。执行这种策略(扩展 CMP bean)是出于两个原因:它允许 bean 可以是 CMP 或 BMP;它可以很方便地消减显示所需要的全部代码。以下是随着本屏的继续将要添加的 BMP 类的定义: 
    public class CustomerBean_BMP extends CustomerBean {
        public void ejbLoad() {
            // override implementation
        }
        public void ejbStore() {
            // override implementation
        }
        public void ejbCreate() {
            // override implementation
        }
        public void ejbRemove() {
            // override implementation
        }
        private Connection getConnection() {
            // new helper method
        }
    }对于 BMP bean,它与 CMP bean 的差别在于容器和 bean 使用 ejbLoad() 和 ejbStore() 方法的方式不同。在 BMP 中,ejbLoad() 和 ejbStore() 方法分别包含了用于从数据库读取 bean 的数据和将更改写入数据库的代码。当 EJB 服务器决定要读写数据时,会自动在 bean 上调用这些方法。 CustomerBean_BMP bean 管理它自己的持续。换句话说,ejbLoad() 和 ejbStore() 方法必须包括数据库访问逻辑,只有这样,当 EJB 告诉 bean 要装入和存储数据时,它才能这么做。容器会在适当时自动执行 ejbLoad() 和 ejbStore() 方法。 通常在事务开始时,就在容器将商业方法委托给 bean 之前,容器会调用 ejbLoad() 方法。以下代码显示了如何使用 JDBC 实现 ejbLoad() 方法: 
    import java.sql.Connection;public class CustomerBean_BMP extends CustomerBean {  public void ejbLoad() {
        Connection con;
        try {
          Integer primaryKey = (Integer)ejbContext.getPrimaryKey();
          con = this.getConnection();
          Statement sqlStmt = 
          con.createStatement("SELECT * FROM Customer " +
                              " WHERE customerID = " +
                              primaryKey.intValue());
          ResultSet results = sqlStmt.executeQuery();
          if (results.next()) {
            // get the name information from the customer table
            myName = new Name();
            myName.first = results.getString("FIRST_NAME");
            myName.last = results.getString("LAST_NAME");
            myName.middle = results.getString("MIDDLE_NAME");
            // get the address information from the customer table
            myAddress = new Address();
            myAddress.street = results.getString("STREET");
            myAddress.city = results.getString("CITY");
            myAddress.state = results.getString("STATE");
            myAddress.zip = results.getInt("ZIP");
            // get the credit card information from the customer table
            myCreditCard = new CreditCard();
            myCreditCard.number = results.getString("CREDIT_NUMBER");
            myCreditCard.expDate = results.getString("CREDIT_DATE");
            myCreditCard.type = results.getString("CREDIT_TYPE");
            myAddress.name = results.getInt("CREDIT_NAME");
          }
        }
        catch (SQLException sqle) {
          throw new EJBException(sqle);
        }
        finally {
          if (con!=null)
            con.close();
        }
      }
    }在 ejbLoad() 方法中,使用对 bean 的 EntityContext 的 ejbContext() 引用来获取实例的主键。这种方法确保对数据库使用了正确的索引。显然,CustomerBean_BMP 需要使用前面所述的继承的 setEntityContext() 和 unsetEntityContext() 方法。 在事务结束时,就在容器试图将所有更改提交给数据库之前,容器会对 bean 调用 ejbStore() 方法。 
    import java.sql.Connection;public class CustomerBean_BMP extends CustomerBean {  public void ejbLoad() {
        ... read data from database
      }  public void ejbStore() {
        Connection con;
        try {
          Integer primaryKey = (Integer)ejbContext.getPrimaryKey();
          con = this.getConnection();
          PreparedStatement sqlPrep = 
          con.prepareStatement(
             "UPDATE customer set " +
             "last_name = ?, first_name = ?, middle_name = ?, " +
             "street = ?, city = ?, state = ?, zip = ?, " +
             "card_number = ?, card_date = ?, " +
             "card_name = ?, card_name = ?, " +
             "WHERE id = ?"
          );
          sqlPrep.setString(1,myName.last);
          sqlPrep.setString(2,myName.first);
          sqlPrep.setString(3,myName.middle);
          sqlPrep.setString(4,myAddress.street);
          sqlPrep.setString(5,myAddress.city);
          sqlPrep.setString(6,myAddress.state);
          sqlPrep.setString(7,myAddress.zip);
          sqlPrep.setInt(8, myCreditCard.number);
          sqlPrep.setString(9, myCreditCard.expDate); 
          sqlPrep.setString(10, myCreditCard.type);
          sqlPrep.setString(11, myCreditCard.name);
          sqlPrep.setInt(12,primaryKey.intValue());
          sqlPrep.executeUpdate();
        }
        catch (SQLException sqle) {
          throw new EJBException(sqle);
        }
        finally {
          if (con!=null)
            con.close();
        }
      }
    }在 ejbLoad() 和 ejbStore() 方法中,bean 都会使用 JDBC 将其自己的状态与数据库同步。如果仔细研究过代码,您也许会发现 bean 从神秘的 this.getConnection() 方法调用中获取它的数据库连接,而此方法尚未实现。getConnection() 方法并不是标准的 EJB 方法;它只是一个私有辅助方法,实现它是为了隐藏获取数据库连接的方法。以下是 getConnection() 方法的定义: 
    import java.sql.Connection;public class CustomerBean_BMP extends CustomerBean {  public void ejbLoad() {
        ... read data from database
      }  public void ejbStore() {
        ... write data to database
      }  private Connection getConnection() throws SQLException {     InitialContext jndiContext = new InitialContext();
         DataSource source = (DataSource)
         jndiContext.lookup("java:comp/env/jdbc/myDatabase");
         return source.getConnection();
      }
    }通过使用缺省 JNDI 上下文(称作 JNDI 环境命名上下文 (ENC)),从容器中获取数据库连接。ENC 通过标准连接库,javax.sql.DataSource 类型,提供对事务性、合用、JDBC 连接的访问权。 在 BMP 中,容器调用 ejbLoad() 和 ejbStore() 方法使 bean 实例与数据库中的数据同步。为了将实体插入数据库或除去数据库中的实体,使用类似的数据库访问来实现 ejbCreate() 和 ejbRemove() 方法。ejbCreate() 方法将新记录插入数据库,而 ejbRemove() 方法从数据库中删除实体的数据。容器调用 BMP 实体的 ejbCreate() 方法和 ejbRemove() 方法以响应对本地和远程接口中它们相应方法的调用。这些方法的实现显示如下: 
    public void ejbCreate(Integer id) {
        this.customerID = id.intValue();
        Connection con;
        try {
          con = this.getConnection();
          Statement sqlStmt = 
          con.createStatement("INSERT INTO customer id VALUES (" +
                              customerID + ")");
          sqlStmt.executeUpdate();
          return id;
        }
        catch(SQLException sqle) {
          throw new EJBException(sqle);
        }
        finally {
          if (con!=null)
            con.close();
        }
      }  public void ejbRemove() {
        Integer primaryKey = (Integer)ejbContext.getPrimaryKey();
        Connection con;
        try {
          con = this.getConnection();
          Statement sqlStmt = 
          con.createStatement("DELETE FROM customer WHERE id = "
                              primaryKey.intValue());
          sqlStmt.executeUpdate();
        }
        catch(SQLException sqle) {
          throw new EJBException(sqle);
        }
        finally {
          if (con!=null)
            con.close();
        }
      }在 BMP 中,bean 类负责实现在本地接口中定义的查找方法。对于本地接口中定义的每个查找方法,在 bean 类中必须有相应的 ejbFind() 方法。ejbFind() 方法定位数据库中相应的 bean 记录,并将它们的主键返回给容器。容器将主键转换成 bean 引用,并将它们返回给客户机。以下是 CustomerBean_BMP 类中 ejbFindByPrimaryKey() 方法实现的示例,它对应于 CustomerHome 接口中定义的 findByPrimaryKey(): 
    public Integer ejbFindByPrimaryKey(Integer primaryKey)
                   throws ObjectNotFoundException {
        Connection con;
        try {
            con = this.getConnection();
            Statement sqlStmt = 
            con.createStatement("SELECT * FROM Customer " +
                                " WHERE customerID = " +
                                primaryKey.intValue());
                                
            ResultSet results = sqlStmt.executeQuery();
            if (results.next())
                return primaryKey;
            else
                throw ObjectNotFoundException();
        }
        catch (SQLException sqle) {
            throw new EJBException(sqle);
        }
        finally {
            if (con!=null)
            con.close();
        }
    }以上这个单一实体查找方法会返回一个主键,如果没有找到匹配的记录,则它会掷出 ObjectNotFoundException。多实体查找方法返回主键的集合(java.util.Enumeration 或 java.util.Collection)。容器将主键集合转换成远程引用的集合,然后将此集合返回给客户机。以下是如何在 CustomerBean_BMP 类中实现多实体 ejbFindByZipCode() 方法的示例,该方法对应于 CustomerHome 接口中定义的 findByZipCode() 方法: 
    public Enumeration ejbFindByZipCode(int zipCode)  {
        Connection con;
        try {
            con = this.getConnection();
            Statement sqlStmt =
            con.createStatement("SELECT id FROM Customer " +
                                " WHERE zip = " +zipCode);
                                
            ResultSet results = sqlStmt.executeQuery();
            Vector keys = new Vector();
            while(results.next()){
                int id = results.getInt("id");
                keys.addElement(new Integer(id));
            }
            return keys.elements();
        }
        catch (SQLException sqle) {
            throw new EJBException(sqle);
        }
        finally {
            if (con!=null)
            con.close();
        }
    }如果没有找到匹配的 bean 记录,则会将空集合返回给容器,然后容器将空集合返回给客户机。 实现了所有这些方法并对 bean 的部署描述信息进行了一些较小更改之后,就可以将 CustomerBean_BMP 部署成 BMP 实体了。