Title: Tutorial DataObjects
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.objectstyle.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:
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:
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:
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)).
picasso.addToPaintings(girl); picasso.addToPaintings(stein); girl.setGallery(metropolitan); stein.setGallery(metropolitan);
Now lets save all five new objects:
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.objectstyle.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