blob: a8c8c672223dcd33211ae4e7f2a73e5759df2a94 [file] [log] [blame]
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">&quot;yyyyMMdd&quot;</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">&quot;A date argument must be in format '&quot;</SPAN>
+ DEFAULT_DATE_FORMAT + <SPAN class="code-quote">&quot;': &quot;</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 &quot;newObject&quot; 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 &quot;main&quot; 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">&quot;Pablo Picasso&quot;</SPAN>);
picasso.setDateOfBirthString(<SPAN class="code-quote">&quot;18811025&quot;</SPAN>);</PRE>
</DIV></DIV>
<P>Note that at this point &quot;picasso&quot; object is only stored in memory and is not saved in the database. Let's continue by adding a Metropolitan Museum &quot;Gallery&quot; object and a few Picasso &quot;Paintings&quot;:</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">&quot;Metropolitan Museum of Art&quot;</SPAN>);
Painting girl = context.newObject(Painting.class);
girl.setName(<SPAN class="code-quote">&quot;Girl Reading at a Table&quot;</SPAN>);
Painting stein = context.newObject(Painting.class);
stein.setName(<SPAN class="code-quote">&quot;Gertrude Stein&quot;</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-&gt;DATE_OF_BIRTH:'1881-10-25 00:00:00.0', 2-&gt;ID:200,
3-&gt;NAME:'Pablo Picasso']
INFO: === updated 1 row.
INFO: INSERT INTO GALLERY (ID, NAME) VALUES (?, ?)
INFO: [batch bind: 1-&gt;ID:200, 2-&gt;NAME:'Metropolitan Museum of Art']
INFO: === updated 1 row.
INFO: INSERT INTO PAINTING (ARTIST_ID, GALLERY_ID, ID, NAME) VALUES (?, ?, ?, ?)
INFO: [batch bind: 1-&gt;ARTIST_ID:200, 2-&gt;GALLERY_ID:200, 3-&gt;ID:200,
4-&gt;NAME:'Girl Reading at a Table']
INFO: [batch bind: 1-&gt;ARTIST_ID:200, 2-&gt;GALLERY_ID:200, 3-&gt;ID:201,
4-&gt;NAME:'Gertrude Stein']
INFO: === updated 2 rows.
INFO: +++ transaction committed.
</PRE>
</DIV></DIV>
<P>So first Cayenne creates the needed tables (remember, we used &quot;CreateIfNoSchemaStrategy&quot;). 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>