9. The Complete Mappings
Prev | Chapter 13. Mapping Metadata | Next

## 9. The Complete Mappings

We began this chapter with the goal of mapping the following object model:

That goal has now been met. In the course of explaining JPA's object-relational mapping metadata, we slowly built the requisite schema and mappings for the complete model. First, the database schema:

And finally, the complete entity mappings. We have trimmed the mappings to take advantage of JPA defaults where possible.

Example 13.17. Full Entity Mappings

    package org.mag;

    @Entity
    @IdClass(Magazine.MagazineId.class)
    @Table(name="MAG")
    @DiscriminatorValue("Mag")
    public class Magazine {

        @Column(length=9)
        @Id private String isbn;
        @Id private String title;

        @Column(name="VERS")
        @Version private int version;

        private String name;
        private double price;

        @Column(name="COPIES")
        private int copiesSold;

        @OneToOne(fetch=FetchType.LAZY,
            cascade={CascadeType.PERSIST,CascadeType.REMOVE})
        @JoinColumn(name="COVER_ID")
        private Article coverArticle;

        @OneToMany(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
        @OrderBy
        @JoinTable(name="MAG_ARTS",
            joinColumns={
                @JoinColumn(name="MAG_ISBN", referencedColumnName="ISBN"),
                @JoinColumn(name="MAG_TITLE", referencedColumnName="TITLE")
            },
            inverseJoinColumns=@JoinColumn(name="ART_ID"))
        private Collection<Article> articles;

        @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
        @JoinColumn(name="PUB_ID")
        private Company publisher;

        @Transient private byte[] data;

        ...

        public static class MagazineId {
            ...
        }
    }

    @Entity
    @Table(name="ART", uniqueConstraints=@Unique(columnNames="TITLE"))
    @SequenceGenerator(name="ArticleSeq", sequenceName="ART_SEQ")
    public class Article {

        @Id
        @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ArticleSeq")
        private long id;

        @Column(name="VERS")
        @Version private int version;

        private String title;
        private byte[] content;

        @ManyToMany(cascade=CascadeType.PERSIST)
        @OrderBy("lastName, firstName")
        @JoinTable(name="ART_AUTHS",
            joinColumns=@JoinColumn(name="ART_ID"),
            inverseJoinColumns=@JoinColumn(name="AUTH_ID"))
        private Collection<Author> authors;

        ...
    }

    package org.mag.pub;

    @Entity
    @Table(name="COMP")
    public class Company {

        @Column(name="CID")
        @Id private long id;

        @Column(name="VERS")
        @Version private int version;

        private String name;

        @Column(name="REV")
        private double revenue;

        @Embedded
        @AttributeOverrides({
            @AttributeOverride(name="street", column=@Column(name="STRT")),
            @AttributeOverride(name="city", column=@Column(name="ACITY"))
        })
        private Address address;

        @OneToMany(mappedBy="publisher", cascade=CascadeType.PERSIST)
        private Collection<Magazine> mags;

        @OneToMany(cascade=CascadeType.PERSIST,CascadeType.REMOVE)
        @JoinTable(name="COMP_SUBS",
            joinColumns=@JoinColumn(name="COMP_ID"),
            inverseJoinColumns=@JoinColumn(name="SUB_ID"))
        private Collection<Subscription> subscriptions;

        ...
    }

    @Entity
    @Table(name="AUTH")
    public class Author {

        @Id
        @GeneratedValue(strategy=GenerationType.TABLE, generator="AuthorGen")
        @TableGenerator(name="AuthorGen", tableName="AUTH_GEN", pkColumnName="PK",
            valueColumnName="AID")
        @Column(name="AID", columnDefinition="INTEGER64")
        private long id;

        @Column(name="VERS")
        @Version private int version;

        @Column(name="FNAME")
        private String firstName;

        @Column(name="LNAME")
        private String lastName;

        private Address address;

        @ManyToMany(mappedBy="authors", cascade=CascadeType.PERSIST)
        private Collection<Article> arts;

        ...
    }

    @Embeddable
    public class Address {

        private String street;
        private String city;
        @Column(columnDefinition="CHAR(2)")
        private String state;
        private String zip;
    }

    package org.mag.subscribe;

    @MappedSuperclass
    public abstract class Document {

        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        private long id;

        @Column(name="VERS")
        @Version private int version;

        ...
    }

    @Entity
    @Table(schema="CNTRCT")
    @Inheritance(strategy=InheritanceType.JOINED)
    @DiscriminatorColumn(name="CTYPE")
    public class Contract
        extends Document {

        @Lob
        private String terms;

        ...
    }

    @Entity
    @Table(name="SUB", schema="CNTRCT")
    @DiscriminatorColumn(name="KIND", discriminatorType=DiscriminatorType.INTEGER)
    @DiscriminatorValue("1")
    public class Subscription {

        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        private long id;

        @Column(name="VERS")
        @Version private int version;

        @Column(name="START")
        private Date startDate;

        @Column(name="PAY")
        private double payment;

        @OneToMany(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
        @MapKey(name="num")
        @JoinTable(name="SUB_ITEMS", schema="CNTRCT",
            joinColumns=@JoinColumn(name="SUB_ID"),
            inverseJoinColumns=@JoinColumn(name="ITEM_ID"))
        private Map<Long,LineItem> items;

        ...

        @Entity
        @Table(name="LINE_ITEM", schema="CNTRCT")
        public static class LineItem
            extends Contract {

            @Column(name="COMM")
            private String comments;

            private double price;
            private long num;

            @ManyToOne
            @JoinColumns({
                 @JoinColumn(name="MAG_ISBN", referencedColumnName="ISBN"),
                 @JoinColumn(name="MAG_TITLE", referencedColumnName="TITLE")
            })
            private Magazine magazine;
            ...
        }
    }

    @Entity(name="Lifetime")
    @DiscriminatorValue("2")
    public class LifetimeSubscription
        extends Subscription {

        @Basic(fetch=FetchType.LAZY)
        @Column(name="ELITE")
        private boolean getEliteClub() { ... }
        public void setEliteClub(boolean elite) { ... }

        ...
    }

    @Entity(name="Trial")
    @DiscriminatorValue("3")
    public class TrialSubscription
        extends Subscription {

        @Column(name="END")
        public Date getEndDate() { ... }
        public void setEndDate(Date end) { ... }

        ...
    }

The same metadata expressed in XML form:

    <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
        version="1.0">
        <mapped-superclass class="org.mag.subscribe.Document">
            <attributes>
                <id name="id">
                    <generated-value strategy="IDENTITY"/>
                </id>
                <version name="version">
                    <column name="VERS"/>
                </version>
            </attributes>
        </mapped-superclass>
        <entity class="org.mag.Magazine">
            <table name="MAG"/>
            <id-class="org.mag.Magazine.MagazineId"/>
            <discriminator-value>Mag</discriminator-value>
            <attributes>
                <id name="isbn">
                    <column length="9"/>
                </id>
                <id name="title"/>
                <basic name="name"/>
                <basic name="price"/>
                <basic name="copiesSold">
                    <column name="COPIES"/>
                </basic>
                <version name="version">
                    <column name="VERS"/>
                </version>
                <many-to-one name="publisher" fetch="LAZY">
                    <join-column name="PUB_ID"/>
                    <cascade>
                        <cascade-persist/>
                    </cascade>
                </many-to-one>
                <one-to-many name="articles">
                    <order-by/>
                    <join-table name="MAG_ARTS">
                        <join-column name="MAG_ISBN" referenced-column-name="ISBN"/>
                        <join-column name="MAG_TITLE" referenced-column-name="TITLE"/>
                        <inverse-join-column name="ART_ID"/>
                    </join-table>
                    <cascade>
                        <cascade-persist/>
                        <cascade-remove/>
                    </cascade>
                </one-to-many>
                <one-to-one name="coverArticle" fetch="LAZY">
                    <join-column name="COVER_ID"/>
                    <cascade>
                        <cascade-persist/>
                        <cascade-remove/>
                    </cascade>
                </one-to-one>
                <transient name="data"/>
            </attributes>
        </entity>
        <entity class="org.mag.Article">
            <table name="ART">
                <unique-constraint>
                   <column-name>TITLE</column-name>
                </unique-constraint>
            </table>
            <sequence-generator name="ArticleSeq", sequenceName="ART_SEQ"/>
            <attributes>
                <id name="id">
                    <generated-value strategy="SEQUENCE" generator="ArticleSeq"/>
                </id>
                <basic name="title"/>
                <basic name="content"/>
                <version name="version">
                    <column name="VERS"/>
                </version>
                <many-to-many name="articles">
                    <order-by>lastName, firstName</order-by>
                    <join-table name="ART_AUTHS">
                        <join-column name="ART_ID" referenced-column-name="ID"/>
                        <inverse-join-column name="AUTH_ID" referenced-column-name="AID"/>
                    </join-table>
                </many-to-many>
            </attributes>
        </entity>
        <entity class="org.mag.pub.Company">
            <table name="COMP"/>
            <attributes>
                <id name="id">
                    <column name="CID"/>
                </id>
                <basic name="name"/>
                <basic name="revenue">
                    <column name="REV"/>
                </basic>
                <version name="version">
                    <column name="VERS"/>
                </version>
                <one-to-many name="mags" mapped-by="publisher">
                    <cascade>
                        <cascade-persist/>
                    </cascade>
                </one-to-many>
                <one-to-many name="subscriptions">
                    <join-table name="COMP_SUBS">
                        <join-column name="COMP_ID"/>
                        <inverse-join-column name="SUB_ID"/>
                    </join-table>
                    <cascade>
                        <cascade-persist/>
                        <cascade-remove/>
                    </cascade>
                </one-to-many>
                <embedded name="address">
                    <attribute-override name="street">
                       <column name="STRT"/>
                    </attribute-override>
                    <attribute-override name="city">
                       <column name="ACITY"/>
                    </attribute-override>
                </embedded>
            </attributes>
        </entity>
        <entity class="org.mag.pub.Author">
            <table name="AUTH"/>
            <attributes>
                <id name="id">
                    <column name="AID" column-definition="INTEGER64"/>
                    <generated-value strategy="TABLE" generator="AuthorGen"/>
                    <table-generator name="AuthorGen" table="AUTH_GEN"
                        pk-column-name="PK" value-column-name="AID"/>
                </id>
                <basic name="firstName">
                    <column name="FNAME"/>
                </basic>
                <basic name="lastName">
                    <column name="LNAME"/>
                </basic>
                <version name="version">
                    <column name="VERS"/>
                </version>
                <many-to-many name="arts" mapped-by="authors">
                    <cascade>
                        <cascade-persist/>
                    </cascade>
                </many-to-many>
                <embedded name="address"/>
            </attributes>
        </entity>
        <entity class="org.mag.subcribe.Contract">
            <table schema="CNTRCT"/>
            <inheritance strategy="JOINED"/>
            <discriminator-column name="CTYPE"/>
            <attributes>
                <basic name="terms">
                    <lob/>
                </basic>
            </attributes>
        </entity>
        <entity class="org.mag.subcribe.Subscription">
            <table name="SUB" schema="CNTRCT"/>
            <inheritance strategy="SINGLE_TABLE"/>
            <discriminator-value>1</discriminator-value>
            <discriminator-column name="KIND" discriminator-type="INTEGER"/>
            <attributes>
                <id name="id">
                    <generated-value strategy="IDENTITY"/>
                </id>
                <basic name="payment">
                    <column name="PAY"/>
                </basic>
                <basic name="startDate">
                    <column name="START"/>
                </basic>
                <version name="version">
                    <column name="VERS"/>
                </version>
                <one-to-many name="items">
                    <map-key name="num">
                    <join-table name="SUB_ITEMS" schema="CNTRCT">
                        <join-column name="SUB_ID"/>
                        <inverse-join-column name="ITEM_ID"/>
                    </join-table>
                    <cascade>
                        <cascade-persist/>
                        <cascade-remove/>
                    </cascade>
                </one-to-many>
            </attributes>
        </entity>
        <entity class="org.mag.subscribe.Subscription.LineItem">
            <table name="LINE_ITEM" schema="CNTRCT"/>
            <attributes>
                <basic name="comments">
                    <column name="COMM"/>
                </basic>
                <basic name="price"/>
                <basic name="num"/>
                <many-to-one name="magazine">
                    <join-column name="MAG_ISBN" referenced-column-name="ISBN"/>
                    <join-column name="MAG_TITLE" referenced-column-name="TITLE"/>
                </many-to-one>
            </attributes>
        </entity>
        <entity class="org.mag.subscribe.LifetimeSubscription" name="Lifetime">
            <discriminator-value>2</discriminator-value>
            <attributes>
                <basic name="eliteClub" fetch="LAZY">
                    <column name="ELITE"/>
                </basic>
            </attributes>
        </entity>
        <entity class="org.mag.subscribe.TrialSubscription" name="Trial">
            <discriminator-value>3</discriminator-value>
            <attributes>
                <basic name="endDate">
                    <column name="END"/>
                </basic>
            </attributes>
        </entity>
        <embeddable class="org.mag.pub.Address">
            <attributes>
                <basic name="street"/>
                <basic name="city"/>
                <basic name="state">
                    <column column-definition="CHAR(2)"/>
                </basic>
                <basic name="zip"/>
            </attributes>
        </embeddable>
    </entity-mappings>

Prev | Up | Next
8. Field Mapping | Home | Chapter 14. Conclusion