Creating a WebObjects Database Application

One of the most powerful features of WebObjects is its ability to provide access to databases. To do so, it uses a framework called the Enterprise Objects Framework. This chapter introduces you to the Enterprise Objects Framework by showing you how to create a simple database application. The steps you take in creating this application demonstrate the principles you’ll use in every other application you develop with the WebObjects and Enterprise Objects frameworks.

The application you’ll create in this tutorial is called Movies. It makes use of a sample database, the Movies database, that contains information about movies. In this tutorial we’ll use the OpenBase Lite database that comes with WebObjects. If you wish to use another database, you need to set up the Movies database as described in the Post-Installation Instructions. Also, if you aren’t familiar with Project Builder and WebObjects Builder, read the first tutorials in this book, Creating a WebObjects Database Application and Enhancing Your Application, which introduce basic concepts and procedures you should know before you go on.

In this tutorial, you will

Along the way, you’ll learn basic Enterprise Objects Framework concepts you can use to design your own database applications.

The Movies Application

The Movies application has two pages, each of which allows you to access information from the database in different ways:

Enterprise Objects and the Movies Database

Enterprise Objects Framework manages the interaction between the database and objects in the Movies application. Its primary responsibility is to fetch data from relational databases into enterprise objects. An enterprise object, like any other object, couples data with methods for operating on that data. In addition, an enterprise object has properties that map to stored data. Enterprise object classes typically correspond to database tables. An enterprise object instance corresponds to a single row or record in a database table.

The Movies application centers around three kinds of enterprise objects: Movies, MovieRoles, and Talents. A movie has many roles, and talents (or actors) play those roles.

The Movie, MovieRole, and Talent enterprise objects in the Movies application correspond to tables in a relational database. For example, the Talent enterprise object corresponds to the TALENT table in the database, that has LAST_NAME and FIRST_NAME columns. The Talent enterprise object class in turn has lastName and firstName instance variables. In an application, Talent objects are instantiated using the data from a corresponding database row, as shown in the following figure:

../art/dbtoeo.gif

Enterprise Objects and Relationships

Relational databases model not just individual entities, but entities’ relationships to one another. For example, a movie has zero, one, or more roles. This is modeled in the database by both the MOVIE table and MOVIE_ROLE table having a MOVIE_ID column. In the MOVIE table, MOVIE_ID is a primary key, while in MOVIE_ROLE it’s a foreignkey.

A primary key is a column or combination of columns whose values are guaranteed to uniquely identify each row in that table. For example, each row in the MOVIE table has a different value in the MOVIE_ID column, which uniquely identifies that row. Two movies could have the same name but still be distinguished from each other by their MOVIE_IDs.

A foreign key matches the value of a primary key in another table. The purpose of a foreign key is to identify a relationship from a source table to a destination table. In the following diagram, notice that the value in the MOVIE_ID column for both MOVIE_ROLE rows is 501. This matches the value in the MOVIE_ID column of the “Alien” MOVIE row. In other words, “Ripley” and “Ash” are both roles in the movie “Alien.”

../art/dbrel.gif

Suppose you fetch a Movie object. Enterprise Objects Framework takes the value for the movie’s MOVIE_ID attribute and looks up movie roles with the corresponding MOVIE_ID foreign key. The framework then assembles a network of enterprise objects that connects a Movie object with its MovieRole objects. As shown below, a Movie object has an array of its MovieRoles, and the MovieRoles each have a Movie.

../art/eogrph.gif

Defining the Model

A model associates database columns with instance variables of objects. It also specifies relationships between objects in terms of database join criteria. You create model files using the EOModeler application. The wizard can assist you in creating a model using the schema information from an existing database as a starting point. You can then use EOModeler to modify the model.

Creating a New Model File

  1. Launch EOModeler.

    The EOModeler application is located in the WebObjects application group.

  2. Choose Model > New.

    The wizard’s adaptor selection panel appears. An adaptor is a mechanism that connects your application to a database. WebObjecject provides a JDBC adaptor that enables you to access any JDBC-compliant database. Make sure that JDBC is selected in the selection list.

  3. Click Next.

    ../art/eomnewmodelwizardjdbc.gif

    This panel lets you specify connection information to your database. The only information required for the tutorial is the URL. On the URL field type “jdbc:openbase://localhost/WOMovies” without the quotes.

  4. Click OK.

Choosing What to Include in Your Model

In this next wizard page, you can specify the degree to which the wizard configures your model.

../art/eomnewmodelwizardwhat.gif

The basic model the wizard creates contains entities, attributes, and relationships. An entity is the part of the database-to-object mapping that associates a database table with an enterprise object class. For example, the Movie entity maps rows from the MOVIE table to Movie objects. Similarly, an attribute associates a database column with an instance variable. For example, the title attribute in the Movie entity maps the TITLE column of the MOVIE table to the title instance variable of Movie objects.

A relationship is a link between two entities that’s based on attributes of the entities. For example, the Movie entity has a relationship to the MovieRole entity based on the entities’ movieId attributes (although the attributes in this example have the same name in both entities, they don’t have to). This relationship makes it possible to find all of a Movie’s MovieRoles.

How complete the basic model is depends on the completeness of the schema information inside your database server. For example, the wizard includes relationships in your model only if the server’s schema information specifies foreign key definitions.

Using the options in this page, you can supplement the basic model with additional information. (Note that the wizard doesn’t modify the underlying database.)

  1. Check the “Assign primary keys to all entities” box.

    Enterprise Objects Framework uses primary keys to uniquely identify enterprise objects and to map them to the appropriate database row. Therefore, you must assign a primary key to each entity you use in your application. The wizard automatically assigns primary keys to the model if it finds primary key information in the database’s schema information.

    Checking this box causes the wizard to prompt you to choose primary keys that aren’t defined in the database’s schema information. If your database doesn’t define them, the wizard later prompts you to choose primary keys.

  2. Check the “Ask about relationships” box.

    If there are foreign key definitions in the database’s schema information, the wizard includes the corresponding relationships in the basic model. However, a definition in the schema information doesn’t provide enough information for the wizard to set all of a relationship’s options. Checking this box causes the wizard to prompt you to provide the additional information it needs to complete the relationship configurations.

  3. Uncheck the “Ask about stored procedures” box.

    Checking this box causes the wizard to read stored procedures from the database’s schema information, display them, and allow you to choose which to include in your model. Because the Movies application doesn’t require the use of any stored procedures, don’t check this box.

  4. Uncheck the “Use custom enterprise objects” box.

    An entity maps a table to enterprise objects by storing the name of a database table (MOVIE, for example) and the name of the corresponding enterprise object class (a Java class, Movie, for example). When deciding what class to map a table to, you have two choices: EOGenericRecord or a custom class. EOGenericRecord is a class whose instances store key-value pairs that correspond to an entity’s properties and the data associated with each property.

    If you don’t check the “Use custom enterprise objects” box, the wizard maps all your database tables to EOGenericRecord. If you do check this box, the wizard maps all your database tables to custom classes. The wizard assumes that each entity is to be represented by a custom class with the same name. For example, a table named MOVIE has an entity named Movie, whose corresponding custom class is also named Movie.

    Use a custom enterprise object class only when you need to add business logic; otherwise use EOGenericRecord. The Movies application uses EOGenericRecord for the Movie entity and custom classes for the Talent and MovieRole entities. Later on, you’ll use EOModeler to specify the custom classes.

  5. Click Next.

Choosing the Tables to Include

  1. In the wizard panel, select MOVIE, MOVIE_ROLE, and TALENT in the Tables browser.

    ../art/eomnewmodelwizardtables.gif

    The wizard creates entities only for the tables you select. Since the Movies application doesn’t interact with any of the other tables (for example, DIRECTOR, PLOT_SUMMARY, STUDIO, and TALENT_PHOTO), you don’t need to include them in the model.

    Click on Select None. Then select the MOVIE, MOVIE_ROLE, and TALENT tables. You will need to use the Control key to make your selections.

  2. Click Next.

  3. Click Finish

    ../art/eomnewmodelwizardfinished.gif

Saving the Model

Once the wizard is finished gathering schema information, it’s ready to create the model.

  1. Choose Model > Save.

    Navigate to a directory where you want to save the model.

  2. Type “Movies” in the File name field.

  3. Click Save.

  4. Close EOModeler.

../art/eomnewmodel.gif

Designing the Main Page

Every WebObjects application has at least one component—usually named Main—that represents the first page the application displays. In Movies, the Main component represents the MovieSearch page.

To design the Main component, you’ll use the WebObjects Application Wizard. The wizard performs all the setup that’s necessary to fetch database records and display them in a web page. Specifying different wizard options yields different pages: The MovieSearch page is an example of one of the many different layouts you can generate with the wizard.

Starting the WebObjects Application Wizard

  1. In Project Builder, choose Project > New.

  2. In the New Project panel, select Java WebObjects Application from the Project Type pop-up list.

  3. Click Browse.

  4. Navigate to a directory where you want to create your new project.

  5. Type Movies in the “File name” field.

  6. Click Save.

  7. In the New Project panel, click OK.

    This starts the WebObjects Application Wizard.

  8. Choose Wizard under Available Assistance.

    With this option, the wizard guides you through the creation of a Main component for your application. When you finish, you can immediately build and run your application without performing any additional steps and without adding any code.

  9. Click Next.

  10. For the “J2EE Integration” window, leave the defaults and click Next.

  11. For the “Web Services” window, leave the defaults and click Next.

  12. In the “Choose EOAdapters” window, make sure JDBC is selected, and click Next.

Specifying a Model File

../art/pbnewprojectwizardmodel.gif
  1. Choose “Open existing model”.

  2. Click Browse.

  3. Navigate to the directory where you saved the model.

  4. Select Movies.eomodeld.

  5. Click Open.

  6. Click Next.

Choosing an Entity

In this page, the wizard asks you to choose the entity around which the Main component will be centered. Your Main component centers around the Movie entity.

  1. Select the Movie entity.

  2. Click Next.

Choosing a Layout

The wizard provides several page layout options for formatting objects fetched from the database.

  1. Choose Selected Record.

  2. Choose Matching Records.

    ../art/pbnewprojectwizardlayout.gif

    Based on your specifications, the wizard shows you a preview of the page it will generate. To see how the wizard’s preview corresponds with the actual page the wizard will create, the finished page is shown below.

    ../art/runmain.gif

    There are three parts to this page: the query part (at the top of the page), which contains fields in which users provide search criteria; the repetition part (in the middle of the page), which contains a list of matching records fetched from the database; and the editing part (at the bottom of the page), which allows you to make changes to the selected record.

  3. In the wizard panel, click Next.

Choosing Attributes to Display

The next step is to choose which of the Movie entity’s attributes to display in the editing part at the bottom of the page.

  1. Move attributes from the Don’t Include list to the Include list.

    ../art/pbnewprojectwizardatts.gif

    The order in which you add the attributes determines the order in which they appear on the page, so add them in the following order: title, category, dateReleased, and revenue.

    Don’t add any of the remaining attributes (for example, trailerName, studioId, rated, and posterName)—they aren’t used in this tutorial.

  2. Click Next.

Choosing an Attribute to Display as a Hyperlink

You now need to specify the attribute used in the repetition part of the page to identify each record. This attribute will be displayed as a hyperlink. Clicking the hyperlink displays the corresponding record in the detail part of the page.

  1. Add the title attribute to the Include browser.

  2. Click Next.

Choosing Attributes to Query On

Specify the attributes to display in the query part of the page. The wizard creates search criteria fields for each of the attributes you choose.

  1. Add the title and category attributes to the Include browser.

  2. Click Finish.

When the wizard finishes, your new project is displayed in Project Builder. The wizard has produced all the files and resources for a fully functional, one-page application. All you need to do before running your Movies application is build it.

Running Movies

Figure 3-1  Build and run the application as you did in the previous tutorials.
Build and run the application as you did in the previous tutorials.

Experiment with the application by entering different search criteria and inserting, updating, and deleting movies. For example:

  1. Search for all movies beginning with the letter “A”.

    Type A in the title field, and click Match.

  2. Change the attributes of one of the movies and click “Save to database.”

    When you’re done, perform another search to verify that your change was saved.

  3. Create a new movie.

    Click Insert/New to create a new, empty movie. Fill out all the fields, and click “Save to database.” Search for your movie to verify that it was saved successfully.

  4. Delete your movie.

    With your movie selected, click Delete and then click “Save to database.” When you’re done, search for the movie again to verify that it’s been deleted.

Examining Your Project

Whenever you create a new project, Project Builder populates the project with ready-made files and directories. What it includes depends on the choices you make in the wizard, so this project has a set of files different from those of the GuestBook project.

Like GuestBook, the Movies project contains a Main component (Main.wo). It also includes some files that the GuestBook doesn’t have: a model file, and images used by the Main component.

In Project Builder, navigate to the Movie project’s Resources category. This is where the model, named Movies.eomodeld, is located. Later in this tutorial you’ll use EOModeler to open the model and enhance it.

../art/projreso.gif

Navigate to the Web Server Resources category. This is where your project’s images are located: DBWizardUpdate.gif, DBWizardDelete.gif, and DBWizardInsert.gif, for the “Save to database,” “Delete”, and “Insert/New” buttons, respectively.

The biggest difference between the GuestBook and Movies projects is their Main components. Whereas the Main component you created for the GuestBook project was empty, the Main component for the Movies project contains a fully functional user interface. Also, the Main.java class already contains code that supplies the component with behavior. In the next sections, you’ll examine the Movies project’s Main.wo component and its Main.java class.

Examining the Variables

  1. Double-click Main.wo in Project Builder’s Web Components category to open the Main component in WebObjects Builder.

    There are four variables in the object browser: the application and session variables that are available in all components and two others, movie and movieDisplayGroup.

    The movie variable is an enterprise object that represents a row fetched from the MOVIE table. movieDisplayGroup is a display group—an object that interacts with a database, indirectly through classes in the Enterprise Objects Framework. Display groups are used to fetch, insert, update, and delete enterprise objects that are associated with a single entity. The entity of movieDisplayGroup is Movie, which you specified in the wizard’s “Choose an entity” page.

  2. In Project Builder, look at the class file Main.java to see how movie is declared.

    The movie declaration (shown below) declares movie to be an EOEnterpriseObject—a Java interface that describes the general behavior that all enterprise objects must have.

        /** @TypeInfo Movie */
        protected EOEnterpriseObject movie;

    At runtime, movie is a EOGenericRecord object. Recall that EOGenericRecord is used to represent enterprise objects unless you specify a custom class. Since you didn’t check the “Use custom enterprise objects” box in the wizard’s “Choose what to include in your model” page, your application uses EOGenericRecord for all its entities.

    The comment (/** @TypeInfo Movie */) is used by WebObjects Builder to identify movie’s entity (Movie). Knowing the entity allows WebObjects Builder to display movie’s attributes (category, dateReleased, and so on). You can see movie’s attributes if you select the movie variable in the WebObjects Builder’s object browser.

  3. In Project Builder, examine the movieDisplayGroup declaration in Main.java.

    The declaration (shown below) declares movieDisplayGroup to be a WODisplayGroup.

        protected WODisplayGroup movieDisplayGroup;

    Also note the comment explaining how movieDisplayGroup is initialized. The Main.java class doesn’t have any code to create and initialize the display group. Instead, it’s instantiated from an archive file, Main.woo, that’s stored in the Main.wo component. You shouldn’t edit woo files by hand; they’re maintained by WebObjects Builder. The woo file archiving mechanism is described in more detail later in Specifying a Sort Order.

Examining the Bindings

Now examine the bindings of your Main component in WebObjects Builder.

../art/mainwo.gif

Remember that you can use WebObjects Builder’s Inspector to see the bindings for an element’s attributes. Simply select the element to inspect, and click the

../art/inspector.gif

button to open the Inspector.

Bindings in the Query Part

In the query part of the component, movieDisplayGroup.queryMatch.title is bound to the title text field. There are similar bindings to the category text fields. The queryMatch bindings allow users to specify search criteria to use when movieDisplayGroup next fetches movies. The Match button is bound to movieDisplayGroup.qualifyDataSource, that actually performs the fetch.

For example, to display all comedies, a user types “Comedy” in the Category text field, and clicks the Match button. movieDisplayGroup then refetches, selecting only movies whose category values are set to Comedy.

../art/queryprt.gif

Bindings in the Repetition Part

In the repetition part of the component where matching movies are listed, movieDisplayGroup.displayedObjects is bound to a repetition. More specifically, displayedObjects is bound to the repetition’s list attribute, providing an array of movies for the repetition to iterate over.

The movie variable is bound to the repetition’s item attribute to hold each movie in turn, and movie.title is bound to the string element inside the repetition. These bindings produce a list of movie titles.

../art/repprt.gif

The repetition’s string element is enclosed in a hyperlink. By clicking a movie title, the user selects the corresponding movie.

  1. Inspect the hyperlink.

    Its action attribute is bound to the action method selectObject.

    ../art/linkinsp.gif
  2. Look in the Main.java class to see how the selectObject method is implemented.

    The method (shown below) simply sets the selected object of movieDisplayGroup to the movie the user clicked.

        public void selectObject() {        movieDisplayGroup.selectObject(movie);    }

Bindings in the Editing Part

The text fields in the editing part are all bound to attributes of the movieDisplayGroup object’s selectedObject—the movie on which the user clicked. Typing new values into these fields updates the Movie enterprise object. To actually save the updated values to the database, the user must click the “Save to database” button.

../art/editprt.gif
  1. Inspect the middle image button.

    Its action attribute is bound to the action method saveChanges.

  2. Look in the Main.java class to see how saveChanges is implemented.

    The method (shown below with comments omitted) simply saves any changes that have been made to movieDisplayGroup’s objects to the database.

        public void saveChanges() throws Exception {
            try {
                this.session().defaultEditingContext().saveChanges();
            }
            catch (Exception exception) {
                NSLog.err.appendln("Cannot save changes ");
                throw exception;
            }
        }

    this.session() returns a Session object that represents a connection to the application by a single user. A Session object provides access to an EOEditingContext object. The expression

        this.session().defaultEditingContext().saveChanges();

    sends a saveChanges message to the Session’s defaultEditingContext. This default EOEditingContext object manages graphs of objects fetched from the database, and all changes to the database are saved through it. For more information, see the EOEditingContext class specification in the WebObjects API Reference.

    An EOEditingContext’s saveChanges method uses other Enterprise Objects Framework objects to analyze its network of enterprise objects (Movie objects referenced by the application) for changes and then to perform a set of corresponding operations in the database. If an error occurs during this process, saveChanges throws an exception. The Main.javasaveChanges method simply raises the exception, having the effect of returning a diagnostic page. You could return an error page that explains the reason for the save failure instead, but the application in this tutorial uses the default behavior.

  3. Inspect the first and third image buttons to see what their action attributes are bound to.

    They are bound to the movieDisplayGroup.insert and movieDisplayGroup.delete methods respectively. The WODisplayGroup insert method creates a new enterprise object, then inserts it into the display group’s list of objects just past the current selection. The WODisplayGroup delete method deletes the display group’s selected object. These changes happen only in memory—not in the database. To actually insert a new row in the database (or delete a row), the user must click the “Save to database” button, invoking saveChanges on the session’s EOEditingContext. The editing context analyzes the enterprise objects in memory, determines if any objects have been added, updated, or deleted, and then executes database operations to sync the database with the application.

Refining Main.wo

You may have noticed that your application doesn’t list fetched movies in any particular order. Also, when you insert a new movie, it appears in the list of movies as a blank line.

../art/blankmov.gif

In this section you’ll tidy up the user interface to fix these things and a few others. Specifically, you’ll

You can also put the query part of the page in a table and capitalize Main.wo’s text field labels—for example, use “Title” instead of “title” and “Date Released” instead of “dateReleased.”

Specifying a Sort Order

You can change your application to sort movies alphabetically without writing any code. Display groups manage sorting behavior, and WebObjects Builder provides a Display Group Options panel for configuring this and other characteristics of display groups.

  1. Double-click the movieDisplayGroup variable in the object browser.

    The Display Group Options panel opens for configuring movieDisplayGroup.

    ../art/dgoption.gif
  2. Select the title attribute in the Sorting pop-up list.

  3. Select Ascending.

  4. Click OK.

WebObjects Builder stores your settings in an archive that specifies how to create and configure movieDisplayGroup at runtime. The archive is stored inside your Main component in a file named Main.woo. You can’t see the file from Project Builder because you’re not meant to edit it directly, but WebObjects Builder’s object browser shows you which of your component’s variables are initialized from the archive (or woo file) so you don’t have to view its contents directly.

../art/mdgvar.gif

Specifying Default Values for New Enterprise Objects

When new enterprise objects are created in your application, it’s common to assign default values to some of their properties. For example, in your Movies application it makes sense to assign a default value for the title attribute so a new movie won’t be displayed in the list of movies as a blank line.

You could write an action method for the Insert/New button instead of binding it directly to the display group insert action method. In the custom action, you would create a new Movie object, assign default values to it, and then insert the new object into the display group. However, there are two additional ways to specify default values for new enterprise objects, without making explicit assignments:

  • Assign default values in the enterprise object class.

  • Specify default values using a display group.

For a particular situation, one of the approaches is usually better than the other. If the default values are intrinsic to the enterprise object, assign them in the enterprise object class. For example, consider a Member class with a memberSince property. It’s likely that you would automatically assign the current date to memberSince instead of forcing a user to supply a value. You’ll see how to use this technique in Adding Behavior to Your Enterprise Objects.

On the other hand, if the default values are specific to an application or to a particular user interface, explicitly initialize the object in code or specify the default values using a display group. In the Movies application, the need for default values is motivated by Main’s user interface: you need to provide a default value so users can tell when a newly inserted record is selected. In another situation, you might not want a new movie to have a default title; you might instead want a new movie’s title to be blank.

The Movies application specifies default values for newly created Movie objects using the display group, movieDisplayGroup.

  1. Open Main.java in Project Builder.

  2. Change the Main class’ constructor so that it looks like the following:

        public Main(WOContext context) {
            super(context);
            NSMutableDictionary defaultValues = new NSMutableDictionary();
            defaultValues.setObjectForKey("New Movie Title", "title");
            movieDisplayGroup.setInsertedObjectDefaultValues(defaultValues);
        }

    This method assigns the value “New Movie Title” as the default value for a new movie’s title attribute. When movieDisplayGroup inserts a new movie (as it does when a user clicks the Insert/New button), it creates a new movie and assigns this default value to that movie.

Setting a Date Format

To change the way that dates are displayed, you assign a date format to the element that displays the dates.

  1. Using WebObjects Builder, inspect the dateReleased text field, which is near the bottom of the Main component window.

    Notice that the text field has a dateformat attribute that is bound to the string “%m/%d/%Y”. This binding tells the text field that it’s displaying dates and describes how to format them. The %m conversion specifier stands for month as a decimal number, %d stands for day of the month, and %Y stands for year with century.

  2. Click the combo box on the right side of the binding column. Choose ”%d %B %Y”.

    This date format displays dates as 14 Jan 2005. The %b conversion specifier stands for abbreviated month name, and %Y stands for year with century. You can create your own date formats with any of the conversion specifiers defined for dates. For more information, see the NSTimestamp class specification in the WebObjects API Reference.

    ../art/datefld.gif

Setting a Number Format

In addition to a dateformat attribute, text field elements also have a numberformat attribute.

  1. Inspect the revenue text field.

    The revenue text field’s numberformat attribute has no binding.

  2. Enter “$###,##0.00” as the value of the numberFormat attribute (including the quotes).

    Using this number format, the Movies application formats the number 1750000 as $1,750,000.00. For more information on creating number formats, see the NSNumberFormatter class specification in the WebObjects API Reference.

Optional Exercise

You can tidy up the user interface even further by putting the query part of the page in a table to match the editing part of the page. Also, you should consider capitalizing Main.wo’s text field labels.

To put the query part of the page in a table, follow these steps:

  1. Put the cursor inside the form element before the “title” text field (in the Query By Example segment).

  2. ../art/table.gif

    Click the button in the toolbar to add a table.

    The table panel appears.

    ../art/tablepanel.gif
  3. Enter 2 in the Rows field and 2 in the Columns field.

  4. Enter 0 for in the Border field to remove the appearance of a border.

  5. Uncheck “First row cells are header cells.” The first row text will not appear in bold.

  6. Click OK. The table appears in your page.

  7. Type the labels Title: and Category: in the cells in the first column.

    The table doesn’t resize to accommodate new cell content until you’re done typing; that is, until you move the cursor out of the edited cell.

  8. Cut and paste the query text fields into their corresponding table cells.

    Just click a text field to select it. When a text field is selected, it appears shaded with a box around it. Choose Cut from the Edit menu, double-click the cell to select its text, and choose Paste from the Edit menu.

  9. Delete the old query field labels.

When you’re done, the query part should look like this:

../art/fintable.gif

Now edit the text labels in the editing part of the page and put any other finishing touches on the page that you want. The finished component might look something like this:

../art/finpage.gif

Adding the MovieDetails Page

The MovieDetails page shows you the detailed information about a movie you select in the Main page. For this to work, the Main page has to tell the MovieDetails page which movie the user selected. The MovieDetails page keeps track of the selected movie in its own instance variable. In this section, you’ll

In the sections following this one, you’ll extend the MovieDetails page to display movie roles and the starring actors.

Creating the MovieDetails Component

  1. In Project Builder, choose File > New in Project.

  2. In the New File panel, click the Web Components suitcase.

  3. Type MovieDetails in the Name field.

  4. Click OK.

  5. In the wizard panel, choose None for available assistance.

  6. Click Finish.

  7. Open the new component, MovieDetails.wo, in WebObjects Builder.

Storing the Selected Movie

Now, in the MovieDetails component, create a variable that holds the application’s selected movie. Later on, you’ll add code to the Main.java class that assigns Main’s selected movie to this variable.

  1. Choose Add Key from the pull-down list.

    ../art/addvar.gif
  2. Name the variable selectedMovie.

  3. Set the variable’s type to Movie.

    Movie isn’t actually a class; it’s an entity. It’s listed in the combo box as a type along with entries for all the entities in your model. When you choose an entity as the type for your variable, WebObjects Builder recognizes that the variable is an enterprise object. Using information in the model, WebObjects Builder can determine the entity’s corresponding enterprise object class and the properties of that class.

  4. Check the “An instance variable” box.

  5. Check the “A method returning the value” box.

  6. Check the “A method setting the value” box.

  7. Click Add.

Navigating from Main to MovieDetails

To get to the MovieDetails page from the Main page, users use a hyperlink. Clicking the hyperlink should set MovieDetail’s selectedMovie variable and then open the MovieDetails page.

  1. Add a hyperlink at the bottom of the Main component.

  2. Replace the text “Hyperlink” with “Movie Details”.

    ../art/mnhylink.gif
  3. Choose Add Action from the pull-down list.

  4. In the Add Action panel, type showDetails in the Name field.

  5. Select MovieDetails from the “Page returned” combo box.

  6. Click Add.

  7. Bind the showDetails action to the hyperlink’s action attribute.

  8. In Project Builder, modify the showDetails action in Main.java to look like the following:

        public MovieDetails showDetails() {
            MovieDetails nextPage =
        (MovieDetails)pageWithName("MovieDetails");
     
            // Initialize your component here
            EOEnterpriseObject selection =
                (EOEnterpriseObject)movieDisplayGroup.selectedObject();
            nextPage.setSelectedMovie(selection);
     
            return nextPage;
        }

    This method creates the MovieDetails page and then invokes its setSelectedMovie method with the movie that’s selected in the Main page. The display group method selectedObject returns its selected object, which, in the Main component, is set when a user clicks a movie title hyperlink.

Designing MovieDetails’ User Interface

Now lay out the user interface for MovieDetails.

  1. Create a top-level heading with the text Movie Details.

    Recall that to create a top-level heading, you type the text of the heading, select the text, click the ../art/heading.gif button to add a heading element around the text, and then use the Inspector to set the heading’s level, as you did in Using the Inspector.

  2. Below the heading, add a string element.

  3. With the string element selected, add a heading.

    This adds a new level-1 heading element around the string. The MovieDetails page will show the title of the selected movie in this heading.

  4. Click <H1> in the path view. The Inspector now displays the Heading Level.

  5. Click 3 in the Heading Inspector.

  6. Add labels and string elements to display the selected movie’s category, date released, and revenue.

  7. Bold the labels.

  8. Bind selectedMovie.title to the value attribute of the first string element (the one in the heading).

  9. Similarly, create bindings for the Category, Date Released, and Revenue strings.

  10. At the bottom of the page, add a horizontal rule.

Adding Date and Number Formats

String elements have dateformat and numberformat attributes just like text field elements. Create bindings for the Date Released and Revenue strings so that dateReleased and revenue values are displayed the way they are in the Main page.

  1. Add the date format "%d %B %Y" to the Date Released string. You can select the format from the combo box in the Inspector’s binding column.

  2. Add the number format "$###,##0.00" to the Revenue string. You can select the format from the combo box in the Inspector’s binding column.

Navigating from MovieDetails to Main

Now add a hyperlink to the MovieDetails page so users can navigate back to the Main page from MovieDetails.

  1. Add a hyperlink to the bottom of the page.

  2. Label it Movie Search.

    ../art/dehylink.gif
  3. Bind the hyperlink’s pageName attribute to the text (including the quotes) "Main". You can select “Main” from the combo box in the inspector’s binding column.

    Recall that the pageName attribute is a mechanism for navigating to another page without writing code. By setting the attribute to “Main”, you’re telling the application to open the MovieSearch page when the hyperlink is clicked.

Running Movies

Be sure that all your project’s files are saved (including the components in WebObjects Builder), and build and run your application. In the Main page, select a movie and click the Movie Details link. The MovieDetails page should display all the movie’s information.

Refining Your Model

The model created for you by the wizard is just a starting point. For most applications, you need to do some additional work to your model to make it useful in your application. To refine your model so that it can be used in the Movies application, you’ll ultimately need to do all of the following:

These steps are described in more detail throughout the rest of this tutorial.

Opening Your Model

  1. In Project Builder, click the Resources category.

  2. Select Movies.eomodeld.

  3. Double-click the model icon.

    ../art/pbopnmdl.gif

Project Builder opens your model file in EOModeler, launching EOModeler first if it isn’t already running. EOModeler displays your model in the Model Editor. It lists the entities for the tables you specified in the wizard—Movie, MovieRole, and Talent.

../art/mdleditr.gif

Removing Foreign Keys as Class Properties

By default, the wizard makes all of an entity’s attributes, except primary keys, class properties. When an attribute is a class property, it means that the property is a part of your enterprise object, usually as an instance variable.

You should mark as class properties only those attributes whose values are meaningful in the objects that are created when you fetch from the database. Attributes that are essentially database artifacts, such as primary and foreign keys, shouldn’t be marked as class properties unless the key has meaning to the user and must be displayed in the user interface.

Eliminating primary and foreign keys as class properties has no adverse effect on how Enterprise Objects Framework manages enterprise objects in your application.

  1. In the left frame (or tree view), click the Movie entity.

    The right frame switches from a view of the entities in the model to a view of Movie’s attributes.

    A ../art/key.gif symbol in the first column means that the attribute is a primary key for the selected entity. A ../art/diamond.gif symbol in the second column means that the attribute is a class property.

  2. ../art/diamond.gif

    Click in the Class Property column to remove the symbol for the studioId attribute, which is a foreign key. The wizard didn’t make movieId a class property because it is a primary key.

../art/rmclsprp.gif

Adding Relationships to Your Model

The Movies application uses two pairs of inverse relationships. The first pair defines the relationship between the Movie and MovieRole entities, while the second pair defines the relationship between the MovieRole and Talent entities. An Enterprise Objects Framework relationship is directed; that is, a relationship has a source and a destination. Generally, models define a relationship for each direction.

  1. Select the Movie entity.

    The right frame of the Model Editor shows the Movie’s relationships as well as its attributes.

    ../art/showrels.gif

    If your Movie entity doesn’t have a movieRoles relationship, it means that the database server’s schema information for your database didn’t have enough information for the wizard to create them. You need to create them by hand now. The next several steps explain how.

  2. Choose Property > Add Relationship.

    A new relationship named “Relationship” is added in the table view at the bottom of the Model Editor. The new relationship is already selected.

  3. ../art/inspector.gif

    With the relationship selected in the right frame of the Model Editor, click the button (in the toolbar) to inspect the relationship.

    ../art/relinsp.gif
  4. In the Inspector, select the To Many option.

  5. Select MovieRole as the destination entity.

  6. Select movieId in the Source Attributes list.

  7. Select movieId in the Destination Attributes list.

  8. Click Connect.

    EOModeler automatically renames the relationship based on the name of the destination entity. For example, after connecting a to-many relationship from Movie to MovieRole, EOModeler names the relationship “movieRoles”. To-one relationships are named with the singular form of the destination entity’s name. For example, EOModeler names the inverse to-one relationship (from MovieRole to Movie) “movie”.

    If the wizard created your relationship and used a name other than “movieRoles”, consider renaming the relationship. The rest of this tutorial assumes that your relationships are named using EOModeler’s naming convention.

  9. Repeat the steps above to create the following relationships (if they do not already exist):

    A to-one relationship named “movie” in the MovieRole entity where

    • The destination entity is Movie.

    • The source attribute is movieId.

    • The destination attribute is movieId.

      A to-one relationship named “talent” in the MovieRole entity where

    • The destination entity is Talent.

    • The source attribute is talentId.

    • The destination attribute is talentId.

      A to-many relationship named “movieRoles” in the Talent entity where

    • The destination entity is MovieRole.

    • The source attribute is talentId.

    • The destination attribute is talentId.

  10. Choose Tools > Diagram View to switch the Model Editor to Diagram View.

../art/popupbtn.gif

At this point your model has all the relationships it needs. The Diagram View gives you an overview of the entities in the model and their relationships to other entities.

../art/diagram.gif

You can also use the Diagram View to edit your model. Double-click an attribute or relationship to change its name. To create a relationship and its inverse, Control-drag from the relationship’s source attribute to its destination attribute.

Using the Advanced Relationship Inspector

There are several additional settings you use to configure a relationship’s referential integrity rules. For these, use the Advanced Relationship Inspector.

  1. Inspect Movie’s movieRoles relationship.

  2. In the Inspector, click the Advanced Relationship button.

    ../art/advIisp.gif
  3. Ensure that the delete rule is set to Cascade.

  4. Ensure that the Owns Destination box is checked.

  5. Check the Propagate Primary Key box.

    A relationship that propagates its primary key propagates its key value to newly inserted objects in the destination of the relationship. In this case, checking the Propagate Primary Key box means that if you create a new MovieRole and add it to a Movie’s list of MovieRoles, the Movie object automatically assigns its movieId value as the value for the new MovieRole’s movieId property.

    This option is usually used with relationships that own their destination. For more information on propagating primary keys, see Where Do Primary Keys Come From?.

  6. Ensure that Talent’s movieRoles relationship has its delete rule set to Deny.

  7. Ensure that Talent’s movieRoles relationship owns its destination.

  8. Set Talent’s movieRoles relationship to propagate its primary key.

  9. Choose Model > Save to save your model.

Where Do Primary Keys Come From?

Enterprise Objects Framework uses primary keys to identify enterprise objects in memory, and it works best if you never change an enterprise object’s primary key from its initial value. Consequently, applications usually generate and assign primary key values automatically instead of having users provide them. For example, the Movies application assigns a movieId value to a new movie when it’s created, and the value never changes afterward. The Movies interface doesn’t even display movieId values because they aren’t meaningful to users of the application.

Enterprise Objects Framework provides several mechanisms for generating and assigning unique values to primary key attributes. By default, Enterprise Objects Framework uses a native database mechanism to assign primary key values.

The Movies application generates primary key values for Movie and Talent objects using the default mechanism, but MovieRole is a special case because:

  • MovieRole’s primary key is compound. The default behavior of generating a primary key value using a native database mechanism works only on simple (not compound) primary keys.

  • A MovieRole’s primary key attributes, movieId and talentId, must match the corresponding attributes in the MovieRole’s Movie and Talent objects. The default mechanism generates new, unique values.

Instead of the default mechanism, Enterprise Objects Framework uses primary key propagation to assign primary keys to MovieRole objects. By configuring the Movie’s movieRoles relationship to propagate primary key, the Framework knows to assign a new MovieRole’s movieId to the same value as the movieId of the MovieRole’s Movie. Similarly, a new MovieRole’s talentId is set to the same value as the talentId of the MovieRole’s Talent.

Setting Up a Master-Detail Configuration

So far your Movies application fetches, inserts, updates, and deletes only Movie objects. Considered alone, a Movie object isn’t as interesting as it is when it’s related to actors and roles. In this section, you’ll add MovieRole and Talent objects to the Movies application.

The relationships defined in your model now come into play. Using Movie’s movieRoles relationship, you can display the MovieRoles for the selected Movie. In this type of configuration, called master-detail, a master display group holds enterprise objects for the source of a relationship, while a detail display group holds records for the destination. As individual records are selected in the master display group, the detail display group gets a new set of enterprise objects to correspond to the selection in the master.

In the Movies application, the master-detail configuration is built around Movie’s movieRoles relationship. The configuration is split across two pages in the application. The master, movieDisplayGroup, is in the Main component, while the detail is in MovieDetails.

In this section, you’ll

Creating a Detail Display Group

You can create a detail display group several different ways. You can write a declaration for it in Project Builder, or you can use WebObjects Builder’s Add Variable/Method command. But the easiest way to create a detail display group is by dragging a relationship from EOModeler into your component, as described below.

  1. In EOModeler’s tree view, expand the Movie entity.

    ../art/expatree.gif
  2. Drag the Movie’s toMovieRole relationship from the tree view into the MovieDetails component’s object browser.

    ../art/dragent.gif

    An Add Display Group panel opens.

    ../art/adddg.gif
  3. In the Add Display Group panel, change the name to movieRoleDisplayGroup.

  4. Click Add and Configure.

    The Display Group Options panel opens so you can immediately configure the newly created display group.

    ../art/dgoptions.gif

    Ensure that the “Has detail data source” box is checked. This means that movieRoleDisplayGroup gets its objects from a EODetailDataSource object.

    All display groups use some kind of data source to fetch their objects. A data source is an object that exists primarily as a simple means for a WODisplayGroup to access a store of objects. It’s through a data source that a display group fetches, inserts, updates, and deletes database records.

    An EODetailDataSource is a subclass of EODataSource that’s intended for use in master-detail configurations. A detail data source keeps track of a master object and a detail key. The master object is typically the selected object in a master display group, but a master display group isn’t strictly required. The detail key is the name of the relationship on which the master-detail configuration is based. When a detail display group asks its data source to fetch, the EODetailDataSource simply gets the destination objects from the master object as follows:

        detailObjects = masterObject.valueForKey(detailKey);

    In your master-detail configuration, the master object is the selected Movie, and the detail key is movieRoles. When movieRoleDisplayGroup asks its data source for its MovieRole objects, the detail WODisplayGroup returns the objects in the selected Movie’s movieRoles array of MovieRoles. Similarly, when MovieRole objects are inserted or deleted in movieRoleDisplayGroup, they are added and removed from the master object’s movieRoles array.

  5. Set the display group to sort alphabetically by roleName.

  6. Check the “Fetches on load” box.

    When “Fetches on load” is selected, the display group fetches its objects as soon as the component is loaded into the application. You want this feature in the MovieDetails page so that users are immediately presented with the selected movie’s roles. In contrast, the Main page does not fetch on load; it shouldn’t present a list of movies until the user has entered search criteria and clicked Match.

  7. Click OK.

  8. In Project Builder, modify MovieDetail’s setSelectedMovie method to look like the following:

        public void setSelectedMovie(EOEnterpriseObject newSelectedMovie) {
            selectedMovie = newSelectedMovie;
            movieRoleDisplayGroup.setMasterObject(newSelectedMovie);
        }

    With this addition, whenever a user navigates to the MovieDetails page, setSelectedMovie updates the master object of the movieRoleDisplayGroup so it displays the corresponding MovieRole objects.

Adding a Repetition

Now you’ll extend the user interface of the MovieDetails component to display the actors in the selected movie. Because different movies have different numbers of roles, you need the dynamism of a repetition element.

  1. In the MovieDetails component window, add the bolded text Starring: beneath the Revenue line.

  2. Below the Starring label, add a repetition.

  3. Replace the “Repetition” text with three string elements.

    The strings should all be on the same line, so don’t type carriage returns between them.

  4. Type a space between the first two strings and the word “ as ” (with a space before and after) between the last two.

  5. Add a carriage return after the last string.

Configuring a Repetition

Now configure MovieDetails’ repetition in a way similar to the way Main’s repetition is configured. First you need to create a new variable to bind to the repetition’s item attribute.

  1. Use the Add Key command to add a new variable, movieRole, whose type is set to the MovieRole entity.

    Don’t create set and get methods for movieRole. You won’t need accessor methods because the variable is used only within the MovieDetails component and shouldn’t be visible to any other classes.

  2. Bind movieRoleDisplayGroup.displayedObjects to the repetition’s list attribute.

  3. Bind movieRole to the repetition’s item attribute.

  4. Bind movieRole.toTalent.firstName to the value attribute of the first string in the repetition.

  5. Bind movieRole.toTalent.lastName to the value attribute of the second string.

  6. Bind movieRole.roleName to the value attribute of the last string.

When you’re done, the repetition bindings should look like the following:

../art/boundrep.gif

Running Movies

Be sure that all your project’s files are saved (including the components in WebObjects Builder and the model in EOModeler), and build and run your application. In the Main page, select a movie and click the Movie Details link. Now, in addition to displaying all the movie’s information, the Movie Details page should also display the movie’s roles and actors.

Updating Objects in the Detail Display Group

In this section, you’ll add the ability to insert, update, and delete movie roles. The MovieDetails page will then look something like this:

../art/detail.gif

Many of the features in this page are similar to features in the Main page, but in this section you perform by hand the tasks the wizard performed for you to create Main. Already you’ve learned how to create a WODisplayGroup variable and how to bind it to dynamic elements. In this section you’ll

Managing a WODisplayGroup’s Selection

Remember how clicking a movie title in the Main page selects the corresponding Movie object in movieDisplayGroup. MovieDetails has a similar behavior for selecting a MovieRole object in movieRoleDisplayGroup.

First you need to add a hyperlink element around the repetition’s role name string so that users can select a particular MovieRole. When a user clicks one of the movie role hyperlinks, the application should select the corresponding MovieRole object in the movieRoleDisplayGroup.

  1. Select the repetition’s role name string element.

  2. Click the Add WOHyperlink button in the toolbar to add a hyperlink element around the string.

    Now you need to create an action method to invoke when the hyperlink is clicked.

  3. Use the Add Action command in the pull-down list to add an action named selectObject, returning null.

  4. Bind the selectObject method to the hyperlink’s action attribute.

  5. Now write the code for selectObject in MovieDetails.java. Modify the selectObject action to look like the following:

        public WOComponent selectObject() {
            movieRoleDisplayGroup.selectObject(movieRole);
            return null;
        }

Adding a Form

Now lay out the user interface used to view and edit the selected MovieRole. When you’re done, it should look like the following:

../art/browui.gif
  1. Add another horizontal rule after the repetition.

  2. ../art/woform.gif

    Use the button to add a WOForm element between the two horizontal rules.

  3. ../art/wobrowser.gif

    While the Form text is highlighted, click the button to replace the text with a WOBrowser element.

  4. Beneath the browser (within the bounds of the new form), type the bolded text Role Name:.

  5. Add a text field.

  6. Bind the text field’s value attribute to movieRoleDisplayGroup.selectedObject.roleName.

Adding a Talent Display Group

The browser you just created is going to display a list of Talent objects. Like a repetition element, a browser has list and item attributes. As the browser moves through its list, the browser sets item to the object at the current index. The Movies application uses a display group to provide the browser with a list of Talent objects, so now you need to create the new display group and a variable to bind to the browser’s item attribute.

  1. Use the Add Key command to create two new instance variables:

    • talentDisplayGroup, whose type is WODisplayGroup

    • talent, whose type is Talent

      You don’t need to add set and get methods for the variables.

  2. Using the Display Group Options panel, assign the talentDisplayGroup object’sentity to Talent.

    Remember that to open the Display Group Options panel, simply double-click the talentDisplayGroup variable in the object browser. The ../art/dash.gif icon initially displayed next to the variable indicates that initialization parameters have not yet been set.

  3. Configure talentDisplayGroup to sort its objects alphabetically (ascending) by lastName.

  4. Configure it to fetch on load and click OK.

    After you configure talentDisplayGroup, the object browser shows a ../art/check.gif icon next to the variable.

The Movies application uses a display group to provide Talent objects, but you could fetch the Talent objects from the database without one. Display groups provide a simple way to fetch, insert, update, and delete enterprise objects without writing much, if any, code. To get finer-grained control over these operations, you can work directly with an EOEditingContext object. An editing context can do everything a display group does and much more, but you have to write more code to use one. For more information, see the EOEditingContext class specification in the WebObjects API Reference.

Configuring the Browser

Create your browser’s bindings. The steps are similar to those for creating bindings for a repetition.

  1. Bind talentDisplayGroup.displayedObjects to the browser’s list attribute.

  2. Bind talent to the browser’s item attribute.

  3. Bind talent.lastName to the browser’s value attribute.

    The value attribute tells the browser what string to display. For each item in its list, the browser evaluates the item’s value.

    The browser in the MovieDetails page should display the actors’ full names, but there isn’t an attribute for full name. In the next section, you’ll create a custom Talent class that implements a fullName method, but for now just use talent.lastName as the value attribute.

    A browser also has a selections attribute that should be bound to an array of objects. A browser’s selection can be zero, one, or many objects; but in the Talent browser, the selection should refer to a single object. Consequently, you need to add two methods to manage the browser’s selection: one to return an array containing the selected Talent and one to set the selected Talent from an array object.

  4. Add the method talentSelection to the MovieDetails.java class as follows:

        public NSArray talentSelection() {
            EOEnterpriseObject aTalent;
            EOEnterpriseObject aMovieRole =
                (EOEnterpriseObject)movieRoleDisplayGroup.selectedObject();
     
            if (aMovieRole == null){
                return null;
            }
            aTalent = (EOEnterpriseObject)aMovieRole.valueForKey("talent");
            if (aTalent == null){
                return null;
            } else {
                return new NSArray(aTalent);
            }
        }

    Because the browser expects an array for its selections attribute, this method packages the selected MovieRole’s talent object in an array. If the selected MovieRole object is null, talentSelection simply returns null to indicate that the browser shouldn’t set a selection.

  5. Add the method setTalentSelection as follows:

        public void setTalentSelection(NSArray talentArray){
            if (talentArray.count() > 0){
                EOEnterpriseObject aMovieRole =
                    (EOEnterpriseObject)movieRoleDisplayGroup.selectedObject();
                EOEnterpriseObject selectedTalent =
                    (EOEnterpriseObject)talentArray.objectAtIndex(0);
     
                aMovieRole.addObjectToBothSidesOfRelationshipWithKey(
                    selectedTalent, "talent");
            }
        }

    Again because the browser uses an array for its selections attribute, the setTalentSelection method must take an array as its argument. If the size of talentArray is nonzero, then this method sets the selected MovieRole’s talent to the first object in the array. Note that by default, a user can’t select more than one actor in a browser.

    With the addition of these methods, WebObjects Builder now displays talentSelection in MovieDetail’s object browser.

  6. Save MovieDetails.java.

  7. Bind talentSelection to the browser’s selections attribute.

Adding Insert, Save, and Delete Buttons

Now add the buttons that let users insert, save, and delete MovieRoles. When you’re done, it should look like the following:

../art/ibuttons.gif
  1. Inside the form, add three image buttons below the Role Name text field.

  2. Inspect the first active image element.

  3. Bind the filename attribute to the text (including the quotes) "DBWizardInsert.gif".

  4. Follow the same procedure to set the second image’s filename attribute to the text (including the quotes) "DBWizardUpdate.gif".

  5. Set the last image’s filename attribute to the text (including the quotes) "DBWizardDelete.gif".

    The WODisplayGroup class defines the actions insert and delete. You’ll bind to the Insert/New and Delete buttons. It doesn’t, however, provide a save method. You’ll have to provide that yourself.

  6. Copy the saveChanges method from the Main.java class and paste it into the MovieDetails.java class:

        public void saveChanges() throws Exception {
            try {
                this.session().defaultEditingContext().saveChanges();
            }
            catch (Exception exception) {
                NSLog.err.appendln("Cannot save changes ");
                throw exception;
            }
        }
  7. Save MovieDetails.java.

  8. Bind movieRoleDisplayGroup.insert to the Insert/New image’s action attribute.

  9. Bind the saveChanges method to the “Save to database” image’s action attribute.

  10. Bind movieRoleDisplayGroup.delete to the Delete image’s action attribute.

  11. Save MovieDetails.wo.

Adding Behavior to Your Enterprise Objects

Right now, the Movies application maps all its entities to the EOGenericRecord class. As the preceding sections illustrate, you can go quite far in an application using just this default enterprise object class, but now you need to add some custom classes to the Movies application.

In this section, you’ll learn how to:

You’ll create custom classes for the Talent and MovieRole entities. In the Talent class, you’ll write a fullName method that concatenates a Talent’s first and last names. You’ll use the method to populate MovieDetail’s browser element. In the MovieRole class, you’ll provide default values for newly inserted MovieRoles so they don’t show up in the list of movie roles as a blank line.

Specifying Custom Enterprise Object Classes

Unless you specify otherwise, EOModeler maps entities to the EOGenericRecord class. When you want to use a custom class instead, you need to specify that custom class in the model.

  1. In EOModeler, inspect the Talent entity.

  2. In the Entity Inspector for Talent, type Talent in the Class field.

    ../art/entinsp.gif
  3. Set the MovieRole entity’s class to MovieRole.

Now you can generate the source files for your Talent and MovieRole classes.

Generating Custom Enterprise Object Classes

You can easily create a custom class to hold your business logic: EOModeler provides a command to generate enterprise object classes.

  1. In EOModeler, select the Talent entity.

  2. Choose Property > Generate Java Files.

    A Choose Class Name panel opens. If you opened the model file from Project Builder, the Choose Class Name panel displays the project as the destination directory and Talent.java as the default filename.

  3. Ensure that the Movies project directory is selected.

  4. Click Save.

    A panel opens, asking if you want to insert the file in your project.

  5. Click Yes.

    EOModeler creates the source file Talent.java and adds it to your project.

  6. Follow the same procedure for MovieRole.

Adding Custom Behavior to Talent

Now add the fullName method to Talent and bind it to the browser.

  1. Open Talent.java in Project Builder.

    The class file and implements set and get methods for all of Talent’s class properties (firstName and lastName).

  2. Add the method, fullName, as follows.

        public String fullName(){
            return firstName() + " " + lastName();
        }

    After you save, fullName appears in the object browser of WebObjects Builder as a property of Talent.

  3. Bind talent.fullName to the browser’s displayString attribute and unbind the value attribute.

Providing Default Values in MovieRole

As discussed in Specifying Default Values for New Enterprise Objects, there are two main ways to specify default values for new enterprise objects without making explicit assignments:

  • Assign default values in the enterprise object class.

  • Specify default values using a display group.

For the Movie class, you specified default values using a display group. This approach is also the more appropriate choice for the MovieRole class, but you’ll use the other approach for MovieRole just to see how its done.

  1. Open MovieRole.java in Project Builder.

  2. Add the method, awakeFromInsertionInEditingContext, as follows

        public void awakeFromInsertion(EOEditingContext context){
            super.awakeFromInsertion(context);
            setRoleName("New Role");
        }

    This method is automatically invoked right after your enterprise object class creates a new MovieRole and inserts it into an editing context, which happens when you use a display group to insert.

Running Movies

Be sure that all your project’s files are saved (including your model file), and build and run your application. Now when a user clicks the Insert/New button on the MovieDetails page, a new MovieRole is inserted, with “New Role” already displayed as the role name.