| Title: Tutorial Persistent Objects |
| |
| <h2>Tutorial Persistent Objects</h2> |
| |
| <P>In this section we'll learn about persistent objects, how to customize them and how to create and save them in DB.</P> |
| |
| <H3>Inspecting and Customizing Persistent Objects</H3> |
| |
| <P>Persistent classes in Cayenne implement a <A href="dataobjects.html" title="DataObjects">DataObject interface</A>. If you inspect any of the classes <A href="tutorial-java-classes.html" title="Tutorial Java Classes">generated earlier</A> in this tutorial (e.g. <TT>org.example.cayenne.persistent.Artist</TT>), you'll see that it extends a class with the name that starts with underscore (<TT>org.example.cayenne.persistent.auto._Artist</TT>), which in turn extends from <TT>org.apache.cayenne.CayenneDataObject</TT>. Splitting each persistent class into user-customizable subclass (Xyz) and a generated superclass (_Xyz) is a useful technique to avoid overwriting the custom code when refreshing classes from the mapping model.</P> |
| |
| <P>Let's for instance add a utility method to the Artist class that sets Artist date of birth, taking a string argument for the date. It will be preserved even if the model changes later:</P> |
| <DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent"> |
| <PRE class="code-java"><SPAN class="code-keyword">package</SPAN> org.example.cayenne.persistent; |
| |
| <SPAN class="code-keyword">import</SPAN> java.text.ParseException; |
| <SPAN class="code-keyword">import</SPAN> java.text.SimpleDateFormat; |
| <SPAN class="code-keyword">import</SPAN> java.util.Date; |
| |
| <SPAN class="code-keyword">import</SPAN> org.example.cayenne.persistent.auto._Artist; |
| |
| <SPAN class="code-keyword">public</SPAN> class Artist <SPAN class="code-keyword">extends</SPAN> _Artist { |
| |
| <SPAN class="code-keyword">static</SPAN> <SPAN class="code-keyword">final</SPAN> <SPAN class="code-object">String</SPAN> DEFAULT_DATE_FORMAT = <SPAN class="code-quote">"yyyyMMdd"</SPAN>; |
| |
| /** |
| * Sets date of birth using a string in format yyyyMMdd. |
| */ |
| <SPAN class="code-keyword">public</SPAN> void setDateOfBirthString(<SPAN class="code-object">String</SPAN> yearMonthDay) { |
| <SPAN class="code-keyword">if</SPAN> (yearMonthDay == <SPAN class="code-keyword">null</SPAN>) { |
| setDateOfBirth(<SPAN class="code-keyword">null</SPAN>); |
| } <SPAN class="code-keyword">else</SPAN> { |
| |
| Date date; |
| <SPAN class="code-keyword">try</SPAN> { |
| date = <SPAN class="code-keyword">new</SPAN> SimpleDateFormat(DEFAULT_DATE_FORMAT) |
| .parse(yearMonthDay); |
| } <SPAN class="code-keyword">catch</SPAN> (ParseException e) { |
| <SPAN class="code-keyword">throw</SPAN> <SPAN class="code-keyword">new</SPAN> IllegalArgumentException( |
| <SPAN class="code-quote">"A date argument must be in format '"</SPAN> |
| + DEFAULT_DATE_FORMAT + <SPAN class="code-quote">"': "</SPAN> + yearMonthDay); |
| } |
| |
| setDateOfBirth(date); |
| } |
| } |
| }</PRE> |
| </DIV></DIV> |
| |
| |
| <H3><A name="TutorialPersistentObjects-CreateNewObjects"></A>Create New Objects</H3> |
| |
| <P>Now we'll create a bunch of objects and save them to the database. An object is created and registered with ObjectContext using "newObject" method. Objects <B>must</B> be registered with DataContext to be persisted and to allow setting relationships with other objects. Add this code to the "main" method of the Main class:</P> |
| |
| <DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent"> |
| <PRE class="code-java">Artist picasso = context.newObject(Artist.class); |
| picasso.setName(<SPAN class="code-quote">"Pablo Picasso"</SPAN>); |
| picasso.setDateOfBirthString(<SPAN class="code-quote">"18811025"</SPAN>);</PRE> |
| </DIV></DIV> |
| |
| <P>Note that at this point "picasso" object is only stored in memory and is not saved in the database. Let's continue by adding a Metropolitan Museum "Gallery" object and a few Picasso "Paintings":</P> |
| |
| <DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent"> |
| <PRE class="code-java">Gallery metropolitan = context.newObject(Gallery.class); |
| metropolitan.setName(<SPAN class="code-quote">"Metropolitan Museum of Art"</SPAN>); |
| |
| Painting girl = context.newObject(Painting.class); |
| girl.setName(<SPAN class="code-quote">"Girl Reading at a Table"</SPAN>); |
| |
| Painting stein = context.newObject(Painting.class); |
| stein.setName(<SPAN class="code-quote">"Gertrude Stein"</SPAN>);</PRE> |
| </DIV></DIV> |
| |
| <P>Now we can link the objects together, establishing relationships. Note that in each case below relationships are automatically estabslished in both directions (e.g. <TT>picasso.addToPaintings(girl)</TT> has exactly the same effect as <TT>girl.setToArtist(picasso)</TT>).</P> |
| |
| <DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent"> |
| <PRE class="code-java">picasso.addToPaintings(girl); |
| picasso.addToPaintings(stein); |
| |
| girl.setGallery(metropolitan); |
| stein.setGallery(metropolitan);</PRE> |
| </DIV></DIV> |
| |
| <P>Now lets save all five new objects, in a single method call:</P> |
| |
| <DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent"> |
| <PRE class="code-java">context.commitChanges();</PRE> |
| </DIV></DIV> |
| |
| <P>Now you can run the application again as described <A href="tutorial-objectcontext.html" title="Tutorial ObjectContext">in the previous section</A>. The new output will show a few actual DB operations:</P> |
| |
| <DIV class="preformatted panel" style="border-width: 1px;"><DIV class="preformattedContent panelContent"> |
| <PRE>Dec 20, 2009 11:11:37 PM org.apache.cayenne.conf.RuntimeLoadDelegate startedLoading |
| INFO: started configuration loading. |
| ... |
| INFO: Opening connection: jdbc:derby:memory:testdb;create=true |
| Login: null |
| Password: ******* |
| INFO: +++ Connecting: SUCCESS. |
| INFO: --- transaction started. |
| INFO: Detected and installed adapter: org.apache.cayenne.dba.derby.DerbyAdapter |
| INFO: --- will run 3 queries. |
| INFO: No schema detected, will create mapped tables |
| INFO: CREATE TABLE ARTIST (DATE_OF_BIRTH DATE, ID INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| NAME VARCHAR (200), PRIMARY KEY (ID)) |
| INFO: CREATE TABLE GALLERY (ID INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY, NAME VARCHAR (200), |
| PRIMARY KEY (ID)) |
| INFO: CREATE TABLE PAINTING (ARTIST_ID INTEGER, GALLERY_ID INTEGER, ID INTEGER NOT NULL |
| GENERATED BY DEFAULT AS IDENTITY, NAME VARCHAR (200), PRIMARY KEY (ID)) |
| INFO: ALTER TABLE PAINTING ADD FOREIGN KEY (ARTIST_ID) REFERENCES ARTIST (ID) |
| INFO: ALTER TABLE PAINTING ADD FOREIGN KEY (GALLERY_ID) REFERENCES GALLERY (ID) |
| INFO: CREATE TABLE AUTO_PK_SUPPORT ( TABLE_NAME CHAR(100) NOT NULL, NEXT_ID BIGINT NOT NULL, |
| PRIMARY KEY(TABLE_NAME)) |
| INFO: DELETE FROM AUTO_PK_SUPPORT WHERE TABLE_NAME IN ('ARTIST', 'GALLERY', 'PAINTING') |
| INFO: INSERT INTO AUTO_PK_SUPPORT (TABLE_NAME, NEXT_ID) VALUES ('ARTIST', 200) |
| INFO: INSERT INTO AUTO_PK_SUPPORT (TABLE_NAME, NEXT_ID) VALUES ('GALLERY', 200) |
| INFO: INSERT INTO AUTO_PK_SUPPORT (TABLE_NAME, NEXT_ID) VALUES ('PAINTING', 200) |
| INFO: SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = ? FOR UPDATE [bind: 1:'ARTIST'] |
| INFO: SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = ? FOR UPDATE [bind: 1:'GALLERY'] |
| INFO: SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = ? FOR UPDATE [bind: 1:'PAINTING'] |
| INFO: --- will run 3 queries. |
| INFO: INSERT INTO ARTIST (DATE_OF_BIRTH, ID, NAME) VALUES (?, ?, ?) |
| INFO: [batch bind: 1->DATE_OF_BIRTH:'1881-10-25 00:00:00.0', 2->ID:200, |
| 3->NAME:'Pablo Picasso'] |
| INFO: === updated 1 row. |
| INFO: INSERT INTO GALLERY (ID, NAME) VALUES (?, ?) |
| INFO: [batch bind: 1->ID:200, 2->NAME:'Metropolitan Museum of Art'] |
| INFO: === updated 1 row. |
| INFO: INSERT INTO PAINTING (ARTIST_ID, GALLERY_ID, ID, NAME) VALUES (?, ?, ?, ?) |
| INFO: [batch bind: 1->ARTIST_ID:200, 2->GALLERY_ID:200, 3->ID:200, |
| 4->NAME:'Girl Reading at a Table'] |
| INFO: [batch bind: 1->ARTIST_ID:200, 2->GALLERY_ID:200, 3->ID:201, |
| 4->NAME:'Gertrude Stein'] |
| INFO: === updated 2 rows. |
| INFO: +++ transaction committed. |
| </PRE> |
| </DIV></DIV> |
| |
| <P>So first Cayenne creates the needed tables (remember, we used "CreateIfNoSchemaStrategy"). Then it runs a number of inserts, generating primary keys on the fly. Not bad for just a few lines of code.</P> |
| |
| <HR> |
| <P><B>Next Step: <A href="tutorial-selectquery.html" title="Tutorial SelectQuery">Tutorial SelectQuery</A></B></P> |
| <HR> |