3 Replies Latest reply on Sep 29, 2004 10:41 AM by jamesstrachan

    Bean Managed Persistence and ejb method mappings.

    iaintoft

      Hi guys,

      I have some questions about EJB's, in particular about bean managed persistence (BMP). First some preamble:

      System: Windows XP SP1
      JBoss: jboss-3.2.5 - standard install using default server config.
      RDBMS: HyperSonicSQL shipped with jboss-3.2.5.

      I have developed the simplest of BMP 'Car' Entity beans: a java.lang.Integer primary key and a single field a String attribute 'name'. My questions are about remote object and home interface to bean class method mappings.

      I have noticed that if we have the following signature in our remote object interface (public interface CarRemote extends javax.ejb.EJBObject):

      public String zeus() throws RemoteException;


      Can map too (in the bean implementation class, public class CarBean implements javax.ejb.EntityBean):


      public String zeus() {
       System.out.println("zeus from remote object interface! :-)");
       return "zeus object interface return";
      }


      I appreciate this type of call is better suited to a stateless session bean but serves the purpose of understanding the mapping from interface to bean methods. I also have observed that with the following signature in our home remote interface (public interface CarHomeRemote extends javax.ejb.EJBHome)


      public String fooBar() throws RemoteException;


      Can map too (in the bean implementation class, public class CarBean implements javax.ejb.EntityBean):

      public String ejbHomeFooBar() {
       System.out.println("foobar from remote home interface! :-)");
       return "foo bar home interface returned";
      }


      Now these mappings seem fine and dandy but rather unhelpful for entity beans. Using container managed persistence we readily accept that the container will subclass our abstract bean class and provide implementations of accessor and mutator methods and trust EJBQL to help generate appropriate finder methods, however in BMP this is less clear.

      When we have a:

      public CarRemote findByPrimaryKey(Integer primaryKey) throws FinderException, RemoteException;


      In our home remote interface this gets mapped to:

      public Integer ejbFindByPrimaryKey (Integer primaryKey) throws FinderException { ...


      Question is how does the Integer return type in the bean get converted too a CarRemote reference returned in the home remote interface? The implementation of the find method appears to only confirm the existence of the entity by returning the primaryKey passed to the find method or throw a ObjectNotFoundException when it doesn't exist and will throw a SQLException or other EJBException (potentially chained exception) at all other times.

      The above question also extends too other finder methods for example:

      public Collection findByName(String name) throws FinderException, RemoteException;


      In our home remote interface this gets mapped to:

      public Integer ejbFindByName(String name) throws FinderException {


      Now from examples I have looked at this works similar to the primary key finder except this time returning a Collection/Enumeration of instances of the primary key class (in our case Integer) representing the found entities, when the client application receives this collection, it is a collection of CarRemote remote object references. What's going on here?

      Finally I appreciate why ejbStore() is called after a setter method. For example if we call a setter that is defined in the remote object interface and implemented in the bean class:

      public void setName(String name) {
       this.name = name;
      }


      Then ejbStore also gets called (again some container magic?) this is understandable in order that we maintain integrity between bean state and persistent state in the event of the container dying. However the same behavior is observed for:

      public String getName () {
       System.out.println ("getName()");
       return name;
      }


      Why do we need to call ejbStore after a getter method?

      In essence my question is where does the container magic come in finder methods and other standard ejbXxx methods? What does the container do and is there anyway to 'watch' this behavior?

      Thanks in advance,
      Iain Toft,
      PhD Candidate

        • 1. Re: Bean Managed Persistence and ejb method mappings.
          jamesstrachan

          Iain,

          Going through your topics in order :-

          Method names create, load, store, findByPrimaryKey and remove in the remote interface are remapped by the container to ejbCreate() etc. Presumably this is to make sure that the container can perform operations such as storing an in-memory copy, looking for a copy in memory etc.

          Similarly all methods starting with find such as findByName are remapped to ejbFindByName. They always return a Collection to the container which remaps each element of the Collection to the remote interface class. The exception is ejbFindByPrimaryKey which returns an object of the same class as the primary key. The container then loads an instance without telling you, and also returns a remote interface instance to the caller.

          Finally for this section, any method declared in the home interface that does not match the names above is remapped with a prefix of ejbHome. I would guess that the intention is to make it clear, when you look at the code for the bean, that this method is invoked by the container and cannot manipulate data belonging to any particular instance of the entity bean.

          The finder method that you quote is wrong. The remote interface is :-

           public Collectiion findByName( String name ) throws FinderException, RemoteException;
          


          and the method on the bean is :-
           public Collection ejbFindByName( String name ) throws FinderException;
          


          In this case, you would return a Collection of Integer objects to the container which remaps them to CarRemote objects.

          The remapping is done in the container and presumably allows existing remote objects to be used if available.

          The EJB specification mandates that ejbStore() is called after every method is executed because the container has no means of knowing whether the method changed data in the entity bean or not. So the specification played safe and insisted on a write. You can get round this using a 'dirty' flag as shown in the example below.

          Incautious use of EJB's, when combined with fine grained network I/O, can be a serious hit on performance. If you can imagine a more complex car entity with (say) eight attributes, and getters and setters for each attribute, just reading and writing the attributes could result in one network I/O and database hit to locate an instance, followed by sixteen network I/O's and database hits as the getters and setters were called.

          Similarly, a search that retrieved fifty cars by name, got the remote for each car and then read two attributes from each to fill a summary display could be performing 100 network I/O's and, without any optimisation, the same number of database writes.

          I attach a sample bean for a Sales Product that optimises the operation by :-


          using a value object to eliminate individual getters and setters;
          using a dirty flag so that ejbStore() only writes when it needs to;


          I also use a session bean to optimise finder methods by handling the remote objects within the server and building a collection of Value Objects to return to the client.

          //Package: Demonstration
          //Class: SalesProductBean
          //Version: 0.b
          //Copyright: Copyright (c) 2002
          //Author: James Strachan
          //Company: Strachan Software Components
          //Description: The EJB Component holding an instance of a Sales Product.
          //Amendment History:-
          //Version Date Author Modification
          //0.a 17/06/2002 JAS Conception
          //0.b 01/01/2003 JAS Convert to Log4J Error Reporting.
          
          package org.milton.demonstration.maintenance;
          
          // Classes required to return summary list of parameters.
          import java.util.Collection;
          import java.util.ArrayList;
          
          // Database imports
          import java.sql.Date;
          import java.sql.Connection;
          import java.sql.SQLException;
          import java.sql.PreparedStatement;
          import java.sql.ResultSet;
          import javax.sql.*;
          
          // EJB imports.
          import java.rmi.RemoteException;
          import javax.ejb.RemoveException;
          import javax.ejb.EJBException;
          import javax.ejb.CreateException;
          import javax.ejb.EntityBean;
          import javax.ejb.EntityContext;
          import javax.naming.InitialContext;
          import javax.naming.NamingException;
          import javax.ejb.ObjectNotFoundException;
          import javax.ejb.FinderException;
          import javax.rmi.PortableRemoteObject;
          
          
          import java.rmi.RemoteException;
          import javax.naming.Context;
          import java.io.*;
          
          // Money classes.
          import org.milton.services.money.MoneyControlBean;
          import org.milton.services.money.MoneyControlHome;
          import org.milton.services.money.MoneyControlRemote;
          import org.milton.services.money.ImmutableMoney;
          
          // Error Log Helper.
          import org.apache.log4j.Logger;
          
          import org.milton.services.utilities.InvalidDataException;
          
          import org.milton.demonstration.common.DemoEJBNameFactory;
          import org.milton.services.common.EJBNameFactory;
          
          /**
           * This class defines an Entity Bean used to implement a Sales Product.
           * <p>
           * The class has specialised finder methods and therefore uses Bean Managed Persistence.
           * @author J.A.Strachan
           * @version 0.b
           */
          public class SalesProductBean implements EntityBean {
          
           private static final String DATASTORE_ADDRESS = DemoEJBNameFactory.DEMO_DATASTORE_ADDRESS;
          
           private EntityContext ctx;
          
           /**
           * The identifier for a Sales Product.
           * Must be public to be available for use by the EJB container.
           */
           public String productCode;
          
           /**
           * Data representing a Sales Product in a lightweight, behaviourless bean.
           */
           private SalesProductLightweight salesProduct;
          
           /**
           * Flag set when data changes
           */
           private boolean dirty;
          
           /**
           * Log4J Logger for error logging.
           */
           private static Logger logger;
          
           /**
           * Parameterless constructor as required by EJB.
           */
           public SalesProductBean() {
          
           if ( logger == null ) {
           logger = Logger.getLogger( this.getClass() );
           }
          
           }
          
          
           /**
           * Get the identifier for a Product Code.
           * @return String
           */
           public String getProductCode() {
           return productCode;
           }
          
           /**
           * Get the Value Object for a Product Code.
           * @return SalesProductLightweight
           */
           public SalesProductLightweight getSalesProduct() {
           return salesProduct;
           }
          
           /**
           * Set the identifier for a sequence.
           * @param salesProduct
           */
           public void setSalesProduct(SalesProductLightweight salesProduct) {
          
           this.salesProduct = salesProduct;
           productCode = salesProduct.getProductCode();
           dirty = true;
          
           }
          
           /**
           * Standard create method.
           * @param newSalesProduct
           * @return String sequenceId - primary key of created object.
           * @throws RemoteException
           * @throws CreateException
           */
           public String ejbCreate( SalesProductLightweight newSalesProduct )
           throws RemoteException, CreateException {
          
           // Set the primary key and the contained data.
           this.productCode = newSalesProduct.getProductCode();
           this.salesProduct = newSalesProduct;
          
           logger.info( "Create called : " + productCode );
          
           Connection conn = null;
           PreparedStatement statement = null;
           InitialContext context = null;
           DataSource dataSource;
          
           String sqlStatement = "INSERT into salesProducts ( productCode, " +
           "productCategory, title, author, leadTime, currencyCode, netPrice, vatCode ) " +
           "values ( ?, ?, ?, ?, ?, ?, ?, ? )";
          
           try {
          
           // Get a database connection.
           context = new InitialContext();
           dataSource = (DataSource) context.lookup( DATASTORE_ADDRESS );
           conn = dataSource.getConnection();
          
           // Prepare a statement and set parameters.
           statement = conn.prepareStatement( sqlStatement );
           statement.setString( 1, productCode );
           statement.setString( 2, salesProduct.getProductCategory() );
           statement.setString( 3, salesProduct.getTitle() );
           statement.setString( 4, salesProduct.getAuthor() );
           statement.setInt( 5, salesProduct.getLeadTime() );
           statement.setString( 6, salesProduct.getNetPrice().getCurrencyCode() );
           statement.setBigDecimal( 7, salesProduct.getNetPrice().getValue() );
           statement.setString( 8, salesProduct.getVatCode() );
          
           // Execute the statement.
           statement.execute();
           dirty = false;
           }
           // Error in lookup ?
           catch( NamingException ne ) {
           logger.error( ne.getMessage(), ne );
           throw new CreateException( ne.getMessage() );
           }
           // Error in SQL statement ?
           catch( SQLException sqe ) {
           try {
           // Check if error is caused by duplicate primary key.
           ejbFindByPrimaryKey( productCode );
           throw new CreateException( "Sales Product " + productCode + "already exists." );
           }
           catch ( Exception ignore ) {};
           logger.error( sqe.getMessage(), sqe );
           throw new CreateException( sqe.getMessage() );
           }
           finally {
           try {
           if ( statement != null ) {
           statement.close();
           }
           if ( conn != null ) {
           conn.close();
           }
           }
           catch ( SQLException e1 ) {}
           }
          
           return productCode;
          
           }
          
           /**
           * No work required here - but skeleton is required by EJB.
           * @param newSalesProduct
           */
           public void ejbPostCreate( SalesProductLightweight newSalesProduct ) {}
          
           /**
           * This standard EJB method locates a Sales Product using the Primary Key.
           * @param productCode - the Key of the requested Sales Product.
           * @return String productCode - the Product Code if the Sales Product was located.
           * @throws RemoteException
           * @throws ObjectNotFoundException
           * @throws FinderException
           */
           public String ejbFindByPrimaryKey( String productCode ) throws RemoteException,
           ObjectNotFoundException, FinderException {
          
           logger.info( "Find by primary key >" + productCode + "<" );
          
           Connection conn = null;
           PreparedStatement statement = null;
           InitialContext context = null;
           DataSource dataSource;
           ResultSet rs;
          
           String sqlStatement = "SELECT productCode from SalesProducts " +
           "where productCode = ?";
          
           try {
          
           // Get a database connection.
           context = new InitialContext();
           dataSource = (DataSource) context.lookup( DATASTORE_ADDRESS );
           conn = dataSource.getConnection();
          
           // Prepare and execute SQL statement.
           statement = conn.prepareStatement( sqlStatement );
           statement.setString( 1, productCode );
           statement.execute();
          
           // Check the result set. If there is one row, we have successfully located
           // the Parameter.
           rs = statement.getResultSet();
           if ( rs.next() ) {
           return productCode;
           }
           else {
           throw new ObjectNotFoundException( "Sales Product " + productCode + "not found." );
           }
           }
           // Lookup Service failed.
           catch( NamingException ne ) {
           logger.error( ne.getMessage(), ne );
           throw new ObjectNotFoundException( ne.getMessage() );
           }
           // SQL failed somewhere.
           catch( SQLException sqe ) {
           logger.error( sqe.getMessage(), sqe );
           throw new ObjectNotFoundException( sqe.getMessage() );
           }
           finally {
           try {
           if ( statement != null ) {
           statement.close();
           }
           if ( conn != null ) {
           conn.close();
           }
           }
           catch ( SQLException e1 ) {}
           }
          
           }
          
           /**
           * This standard EJB method loads a Sales Product. To fit EJB standards, the
           * primary key - productCode - is read from the Context.
           * <p>
           * @throws ObjectNotFoundException
           * @throws RemoteException
           */
           public void ejbLoad() throws RemoteException {
          
           Connection conn = null;
           PreparedStatement statement = null;
           InitialContext context = null;
           DataSource dataSource;
           ResultSet rs;
          
           MoneyControlRemote moneyControl = getMoneyRemote();
          
           String sqlStatement = "SELECT productCategory, title, author, " +
           "leadTime, currencyCode, netPrice, vatCode from salesProducts " +
           "where productCode = ?";
          
           productCode = (String) ctx.getPrimaryKey();
          
           logger.info( "Load called : " + productCode );
          
           try {
          
           // Get a database connection.
           context = new InitialContext();
           dataSource = (DataSource) context.lookup( DATASTORE_ADDRESS );
           conn = dataSource.getConnection();
          
           // Prepare and execute the statement.
           statement = conn.prepareStatement( sqlStatement );
           statement.setString( 1, productCode );
           statement.execute();
          
           // Get the result set - if there is a row, create an instance of a Parameter.
           rs = statement.getResultSet();
           if ( rs.next() ) {
           salesProduct = new SalesProductLightweight();
           salesProduct.setProductCode( productCode );
           salesProduct.setProductCategory( rs.getString(1).trim() );
           salesProduct.setTitle( rs.getString(2).trim() );
           salesProduct.setAuthor( rs.getString(3).trim() );
           salesProduct.setLeadTime( rs.getInt(4) );
           ImmutableMoney temp = moneyControl.createImmutableMoney( rs.getString(5).trim(),
           rs.getBigDecimal(6) );
           salesProduct.setNetPrice( temp );
           salesProduct.setVatCode( rs.getString(7).trim() );
           dirty = false;
           }
           else {
           throw new RemoteException( "Sales Product " + productCode + "not found." );
           }
           }
           // Lookup failed ?
           catch( InvalidDataException ive ) {
           logger.error( ive.getMessage(), ive );
           throw new RemoteException( ive.getMessage() );
           }
           // Lookup failed ?
           catch( NamingException ne ) {
           logger.error( ne.getMessage(), ne );
           throw new RemoteException( ne.getMessage() );
           }
           // Something nasty in the woodshed ?
           catch( SQLException sqe ) {
           logger.error( sqe.getMessage(), sqe );
           throw new RemoteException( sqe.getMessage() );
           }
           finally {
           try {
           if ( statement != null ) {
           statement.close();
           }
           if ( conn != null ) {
           conn.close();
           }
           }
           catch ( SQLException e1 ) {}
           }
          
           }
          
           /**
           * This standard EJB method physically deletes a Sales Product from the database.
           * The key of the Sales Product is obtained from the context.
           * @throws RemoteException
           * @throws RemoveException
           */
           public void ejbRemove() throws RemoteException, RemoveException {
          
           Connection conn = null;
           PreparedStatement statement = null;
           InitialContext context = null;
           DataSource dataSource;
          
           String sqlStatement = "DELETE from SalesProducts where productCode = ?";
           int rowCount;
          
           productCode = (String) ctx.getPrimaryKey();
          
           logger.info( "Remove called : " + productCode );
          
           try {
          
           context = new InitialContext();
           dataSource = (DataSource) context.lookup( DATASTORE_ADDRESS );
           conn = dataSource.getConnection();
          
           statement = conn.prepareStatement( sqlStatement );
           statement.setString( 1, productCode );
           rowCount = statement.executeUpdate();
           if ( rowCount != 1 ) {
           throw new RemoveException( "Sales product " + productCode + " already deleted." );
           }
           }
           catch( NamingException ne ) {
           logger.error( ne.getMessage(), ne );
           throw new RemoveException( ne.getMessage() );
           }
           catch( SQLException sqe ) {
           logger.error( sqe.getMessage(), sqe );
           throw new RemoveException( sqe.getMessage() );
           }
           finally {
           try {
           if ( statement != null ) {
           statement.close();
           }
           if ( conn != null ) {
           conn.close();
           }
           }
           catch ( SQLException e1 ) {}
           }
          
           }
          
           /**
           * Standard EJB method to store a Sales Product in persistent data.
           * @throws RemoteException
           */
           public void ejbStore() throws RemoteException {
          
           Connection conn = null;
           PreparedStatement statement = null;
           InitialContext context = null;
           DataSource dataSource;
          
           // Do nothing unless data has chhanged.
           if ( dirty == false ) {
           return;
           }
          
           String sqlStatement = "UPDATE SalesProducts set ProductCategory = ?, " +
           "author = ?, title = ?, leadTime = ?, currencyCode = ?, " +
           "netPrice = ?, vatCode = ? " +
           "WHERE productCode = ?";
          
           int rowCount;
          
           logger.info( "Store : " + productCode );
          
           try {
          
           // Get a database connection.
           context = new InitialContext();
           dataSource = (DataSource) context.lookup( DATASTORE_ADDRESS );
           conn = dataSource.getConnection();
          
           // Prepare statement and parameters.
           statement = conn.prepareStatement( sqlStatement );
           statement.setString( 1, salesProduct.getProductCategory() );
           statement.setString( 2, salesProduct.getTitle() );
           statement.setString( 3, salesProduct.getAuthor() );
           statement.setInt( 4, salesProduct.getLeadTime() );
           statement.setString( 5, salesProduct.getNetPrice().getCurrencyCode() );
           statement.setBigDecimal( 6, salesProduct.getNetPrice().getValue() );
           statement.setString( 7, salesProduct.getVatCode() );
           statement.setString( 8, productCode );
          
           // Execute and check results.
           rowCount = statement.executeUpdate();
           logger.debug( "rowCount = " + rowCount );
           if ( rowCount != 1 ) {
           logger.warn( "Sales Product " + productCode + "not found." );
           throw new RemoteException( "Sales Product " + productCode + "not found." );
           }
           }
           // Lookup failed.
           catch( NamingException ne ) {
           logger.error( ne.getMessage(), ne );
           throw new RemoteException( ne.getMessage() );
           }
           // Database error.
           catch( SQLException sqe ) {
           logger.error( sqe.getMessage(), sqe );
           throw new RemoteException( sqe.getMessage() );
           }
           finally {
           try {
           if ( statement != null ) {
           statement.close();
           }
           if ( conn != null ) {
           conn.close();
           }
           }
           catch ( SQLException e1 ) {}
           }
          
           }
          
          
          
           /**
           * This method is a customised EJB finder method that returns a list of all
           * Sales Products stored in persistent data. The data returned is contained
           * in a Collection of objects which are Primary Keys of the class. These
           * are then encapsulated as EJBObjects by the container.
           * <p>
           * The primary key for this class is a String so we build an ArrayList of
           * Strings.
           * @return Collection of String primary keys.
           * @throws RemoteException
           * @throws FinderException
           */
           public Collection ejbFindAll() throws RemoteException, FinderException {
          
           Connection conn = null;
           PreparedStatement statement = null;
           InitialContext context = null;
           DataSource dataSource;
           ResultSet rs;
           ArrayList result = new ArrayList();
          
           String sqlStatement = "SELECT productCode from SalesProducts " +
           "order by productCode";
          
           logger.info( "FindAll called");
          
           try {
          
           // Get a connection/
           context = new InitialContext();
           dataSource = (DataSource) context.lookup( DATASTORE_ADDRESS );
           conn = dataSource.getConnection();
          
           // Execute the statement.
           statement = conn.prepareStatement( sqlStatement );
           statement.execute();
           rs = statement.getResultSet();
          
           // Loop through the SQL result set, building the Collection to be returned.
           // Trim the strings so that the key definition is consistent.
           while( rs.next() ) {
           result.add( rs.getString(1).trim() );
           }
           }
           // Lookup Servicefailed ?
           catch( NamingException ne ) {
           logger.error( ne.getMessage(), ne );
           throw new RemoteException( ne.getMessage() );
           }
           // Database service failed ?
           catch( SQLException sqe ) {
           logger.error( sqe.getMessage(), sqe );
           throw new RemoteException( sqe.getMessage() );
           }
           finally {
           try {
           if ( statement != null ) {
           statement.close();
           }
           if ( conn != null ) {
           conn.close();
           }
           }
           catch ( SQLException e1 ) {}
           }
          
           return result;
          
           }
          
          
           /**
           * This method is a customised EJB finder method that returns a list of all
           * Sales Products in a Product Category.
           * @see comments on ejbFindAll
           * @param productCategory
           * @return Collection of String primary keys.
           * @throws RemoteException
           * @throws FinderException
           */
           public Collection ejbFindByCategory( String productCategory )
           throws RemoteException, FinderException {
          
           Connection conn = null;
           PreparedStatement statement = null;
           InitialContext context = null;
           DataSource dataSource;
           ResultSet rs;
           ArrayList result = new ArrayList();
          
           String sqlStatement = "SELECT productCode from SalesProducts " +
           "where productCategory = ? " +
           "order by productCode";
          
           logger.info( "FindByCategory called");
          
           try {
          
           // Get a connection/
           context = new InitialContext();
           dataSource = (DataSource) context.lookup( DATASTORE_ADDRESS );
           conn = dataSource.getConnection();
          
           // Execute the statement.
           statement = conn.prepareStatement( sqlStatement );
           statement.setString( 1, productCategory );
           statement.execute();
           rs = statement.getResultSet();
          
           // Loop through the SQL result set, building the Collection to be returned.
           // Trim the strings so that the key definition is consistent.
           while( rs.next() ) {
           result.add( rs.getString(1).trim() );
           }
           }
           // Lookup Servicefailed ?
           catch( NamingException ne ) {
           logger.error( ne.getMessage(), ne );
           throw new RemoteException( ne.getMessage() );
           }
           // Database service failed ?
           catch( SQLException sqe ) {
           logger.error( sqe.getMessage(), sqe );
           throw new RemoteException( sqe.getMessage() );
           }
           finally {
           try {
           if ( statement != null ) {
           statement.close();
           }
           if ( conn != null ) {
           conn.close();
           }
           }
           catch ( SQLException e1 ) {}
           }
          
           return result;
          
           }
          
           /**
           * Get the remote interface to the MoneyControlBean in the Application Server.
           * @return MoneyControlRemote
           * @throws RemoteException
           */
           private MoneyControlRemote getMoneyRemote() throws RemoteException {
          
           MoneyControlHome home;
           MoneyControlRemote remote = null;
          
          
           try {
           Context context = new InitialContext();
           home = (MoneyControlHome) PortableRemoteObject.narrow(
           context.lookup(EJBNameFactory.buildName(EJBNameFactory.MONEY_CONTROLS)),
           MoneyControlHome.class );
           remote = home.findByPrimaryKey( MoneyControlBean.SINGLETON_NAME );
           }
           catch ( Exception e ) {
           logger.error( "Finding money control", e );
           throw new RemoteException( "Finding Money Control", e );
           }
          
           return remote;
          
           }
          
           public void setEntityContext(EntityContext ctx) throws RemoteException {
          
           this.ctx = ctx;
          
           }
          
           public void unsetEntityContext() throws RemoteException {
          
           ctx = null;
          
           }
          
           public void ejbActivate() throws RemoteException {
           }
          
           public void ejbPassivate() throws RemoteException {
           }
          
          }
          


          • 2. Re: Bean Managed Persistence and ejb method mappings.
            iaintoft

            Thank you for the comprehensive response James. I appreciate the findByName signature was incorrect, it seems copy and paste coding is dangerous in forum posts too!

            The part which really intrigues me is 'In this case, you would return a Collection of Integer objects to the container which remaps them to CarRemote objects' I suppose I have to take it as read. I had intended to subclass EnitityContext and for each method override with super(..); and out.println(); calls to see how the various ejbXxx calls employ the bean context.

            It's my understanding that in a deployment descriptor (jboss.xml) for a BMP entity bean we may specify a commit option <commit-option>A</commit-option> with options A,B,C which determines how aggresively the container persists bean data? A value object is always a good idea and one that I subscribe too.

            Thanks again
            Iain

            • 3. Re: Bean Managed Persistence and ejb method mappings.
              jamesstrachan

              Iain,

              Pleased to help.

              The commit-option of A, B or C controls reading from the database (not writing).

              If you go the Wiki page and searcvh for "commit option", you will find a page that explains this.

              James