Persistent classes in Cayenne implement a DataObject interface. If you inspect any of classes generated earlier in this tutorial (e.g. cayenne.tutorial.Artist), you'll see that it extends a class with the name that starts with underscore (cayenne.tutorial.auto._Artist), which in turn extends from org.apache.cayenne.CayenneDataObject. Splitting each persistent class into user-customizable subclass (Xyz) and a generated superclass (_Xyz) is a useful technique to avoid overwriting of the custom code when refreshing classes from the mapping model.

Let's add a utility method to the Artist class that sets Artist date of birth, taking a string argument for the date:

Artist.java
package cayenne.tutorial;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import cayenne.tutorial.auto._Artist;

public class Artist extends _Artist {

    static final String DEFAULT_DATE_FORMAT = "yyyyMMdd";

    /**
     * Sets date of birth using a string in format yyyyMMdd.
     */
    public void setDateOfBirthString(String yearMonthDay) {
        if (yearMonthDay == null) {
            setDateOfBirth(null);
        }
        else {
            
            Date date;
            try {
                date = new SimpleDateFormat(DEFAULT_DATE_FORMAT).parse(yearMonthDay);
            }
            catch (ParseException e) {
                throw new IllegalArgumentException("A date argument must be in format '"
                        + DEFAULT_DATE_FORMAT
                        + "': "
                        + yearMonthDay);
            }

            setDateOfBirth(date);
        }
    }
}

We'll continue by creating a bunch of new objects and saving them to the database.

An object is created and registered with DataContext using "newObject" method. (Note that objects must be registered with DataContext to be persisted and to allow relationships with other objects.) Add this code to the "main" method:

Main.java
Artist picasso = (Artist) context.newObject(Artist.class);
picasso.setName("Pablo Picasso");
picasso.setDateOfBirthString("18811025");

Note that at this point "picasso" object is only stored in memory and is not saved in teh database. Let's continue by adding a Metropolitan Museum gallery object and a few Picasso paintings:

Main.java
Gallery metropolitan = (Gallery) context.newObject(Gallery.class);
metropolitan.setName("Metropolitan Museum of Art"); 

Painting girl = (Painting) context.newObject(Painting.class);
girl.setName("Girl Reading at a Table");
        
Painting stein = (Painting) context.newObject(Painting.class);
stein.setName("Gertrude Stein");

Now we can link the objects together, establishing relationships. Note that in each case below relationships are automatically estabslished in both directions (e.g. picasso.addToPaintings(girl) has exactly the same effect as girl.setToArtist(picasso)).

Main.java
picasso.addToPaintings(girl);
picasso.addToPaintings(stein);
        
girl.setGallery(metropolitan);
stein.setGallery(metropolitan);

Now lets save all five new objects:

Main.java
context.commitChanges();

Now you can run the application again as described here. The new output will look like this:

INFO  QueryLogger: Created connection pool: jdbc:derby:/Users/andrus/Desktop/testdb;create=true
	Driver class: org.apache.derby.jdbc.EmbeddedDriver
	Min. connections in the pool: 1
	Max. connections in the pool: 1
INFO  QueryLogger: Opening connection: jdbc:derby:/Users/andrus/Desktop/testdb;create=true
	Login: null
	Password: *******
INFO  QueryLogger: +++ Connecting: SUCCESS.
INFO  QueryLogger: Detected and installed adapter: org.apache.cayenne.dba.derby.DerbyAdapter
INFO  QueryLogger: SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = ? FOR UPDATE [bind: 'GALLERY']
INFO  QueryLogger: --- transaction started.
INFO  QueryLogger: SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = ? FOR UPDATE [bind: 'ARTIST']
INFO  QueryLogger: SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = ? FOR UPDATE [bind: 'PAINTING']
INFO  QueryLogger: --- will run 3 queries.
INFO  QueryLogger: INSERT INTO GALLERY (ID, NAME) VALUES (?, ?)
INFO  QueryLogger: [bind: 200, 'Metropolitan Museum of Art']
INFO  QueryLogger: === updated 1 row.
INFO  QueryLogger: INSERT INTO ARTIST (DATE_OF_BIRTH, ID, NAME) VALUES (?, ?, ?)
INFO  QueryLogger: [bind: '1881-10-25 00:00:00.0', 200, 'Pablo Picasso']
INFO  QueryLogger: === updated 1 row.
INFO  QueryLogger: INSERT INTO PAINTING (ARTIST_ID, GALLERY_ID, ID, NAME) VALUES (?, ?, ?, ?)
INFO  QueryLogger: [bind: 200, 200, 200, 'Girl Reading at a Table']
INFO  QueryLogger: === updated 1 row.
INFO  QueryLogger: [bind: 200, 200, 201, 'Gertrude Stein']
INFO  QueryLogger: === updated 1 row.
INFO  QueryLogger: +++ transaction committed.

The first few SELECT's are done by Cayenne primary key generator (note that we did not assign any explicit PKs to the objects - Cayenne does this automatically). Following them are the statements generated to save the objects that we created.


Next Step: Tutorial Mapping Query