Pages

Wednesday, November 5, 2014

JPA embedded objects mappings tutorial

Embedded object is a part of its entity and cannot exist without it.
Usually embedded object does not have any matching table in database so that it is mapped to the table that corresponds to its entity.

Let's look at mapping of such objects.

This post is available in Russian.

Music artist and its recording label are related to defined cities and countries, e.g. they have addresses.

Create jpa_embedded_objects_mappings_tutorial database with artists and labels tables.
artists table has artist_city and artist_country columns that store data about artists addresses.
Similarly, labels table has label_city and label_country columns that store data about labels addresses.
Add data about artists and labels:
CREATE DATABASE jpa_embedded_objects_mappings_tutorial;
USE jpa_embedded_objects_mappings_tutorial;

CREATE TABLE artists (
artist_id INT PRIMARY KEY,
artist_name VARCHAR(50),
artist_city VARCHAR(50),
artist_country VARCHAR(50)
);

CREATE TABLE labels (
label_id INT PRIMARY KEY,
label_name VARCHAR(50),
label_city VARCHAR(50),
label_country VARCHAR(50)
);

INSERT INTO artists VALUES (1, 'Franz Ferdinand', 'Glasgow', 'Scotland');
INSERT INTO labels VALUES (1, 'Domino', 'London', 'England');

Tables columns that store data about cities and countries have the same data type and purpose - to represent addresses. Data from these columns can be kept in embedded object that will be used in entities.

Create Address class with city and country properties.
Add @Embeddable annotation for Address class to use its instances as embedded objects.
Add @Column annotation for city property. name annotation element must reference table column that stores data about artist city, e.g. artist_city.
Similarly, add @Column annotation for country property, but name annotation element must reference table column that stores data about artist country, e.g. artist_country:
package com.jpa.embedded.objects.mappings.tutorial.domain;

import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
public class Address {

    @Column(name = "artist_city")
    private String city;

    @Column(name = "artist_country")
    private String country;

    public Address() {

    }

    public Address(String city, String country) {
        this.city = city;
        this.country = country;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    @Override
    public String toString() {
        return "Address{" +
                "city='" + city + '\'' +
                ", country='" + country + '\'' +
                '}';
    }
}

Create Artist class with id, name and address properties.
address property references to embedded object, so that add @Embedded annotation for it:
package com.jpa.embedded.objects.mappings.tutorial.domain;

import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "artists")
public class Artist {

    @Id
    @Column(name = "artist_id")
    private int id;

    @Column(name = "artist_name")
    private String name;

    @Embedded
    private Address address;

    public Artist() {

    }

    public Artist(int id, String name, Address address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Artist{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
}

Create ArtistService class:
package com.jpa.embedded.objects.mappings.tutorial.service;

import com.jpa.embedded.objects.mappings.tutorial.domain.Address;
import com.jpa.embedded.objects.mappings.tutorial.domain.Artist;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.List;

public class ArtistService {

    private EntityManager em;

    public ArtistService(EntityManager em) {
        this.em = em;
    }

    public Artist createArtist(int id, String name, Address address) {
        Artist artist = new Artist(id, name, address);
        em.persist(artist);
        return artist;
    }

    public void removeArtist(int id) {
        Artist artist = em.find(Artist.class, id);

        if (artist != null) {
            em.remove(artist);
        }
    }

    public Artist changeArtistName(int id, String name) {
        Artist artist = em.find(Artist.class, id);

        if (artist != null) {
            artist.setName(name);
        }

        return artist;
    }

    public Artist findArtist(int id) {
        return em.find(Artist.class, id);
    }

    public List<Artist> findAllArtists() {
        TypedQuery<Artist> query = em.createQuery("SELECT a FROM Artist a", Artist.class);
        return query.getResultList();
    }
}

Add Artist class to persistence unit in persistence.xml file:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

    <persistence-unit name="JpaEmbeddedObjectsMappingsTutorial" transaction-type="RESOURCE_LOCAL">
        <class>com.jpa.embedded.objects.mappings.tutorial.domain.Artist</class>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url"
                      value="jdbc:mysql://localhost:3306/jpa_embedded_objects_mappings_tutorial"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="root"/>
        </properties>
    </persistence-unit>

</persistence>

Add code that creates ArtistService object, finds Artist entity and prints artist data to main() method of JpaEmbeddedObjectsMappingsTutorial class:
package com.jpa.embedded.objects.mappings.tutorial;

import com.jpa.embedded.objects.mappings.tutorial.domain.Artist;
import com.jpa.embedded.objects.mappings.tutorial.service.ArtistService;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class JpaEmbeddedObjectsMappingsTutorial {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("JpaEmbeddedObjectsMappingsTutorial");
        EntityManager em = emf.createEntityManager();
        ArtistService artistService = new ArtistService(em);

        System.out.println("--- Find artist ---");
        Artist artist = artistService.findArtist(1);
        System.out.println(String.format("Found: %s", artist));
    }
}

Execute main() method:

Application has printed data about artist and its address.

Next, we will reuse Address class with Label entity.

Create Label class with id, name and address properties.
address property also references to embedded object, so that add @Embedded annotation for it.
Currently city and country properties of Address class are mapped to artist_city and artist_country table columns, but we need to map them to label_city and label_country columns. To make it:
  • add @AttributeOverrides annotation for address property
  • add @AttributeOverride annotation which name element references to city property of Address class and column element references to column that stores data about label city, e.g. label_city
  • add @AttributeOverride annotation which name element references to country property of Address class and column element references to column that stores data about label country, e.g. label_country
package com.jpa.embedded.objects.mappings.tutorial.domain;

import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "labels")
public class Label {

    @Id
    @Column(name = "label_id")
    private int id;

    @Column(name = "label_name")
    private String name;

    @Embedded
    @AttributeOverrides({
            @AttributeOverride(name = "city", column = @Column(name = "label_city")),
            @AttributeOverride(name = "country", column = @Column(name = "label_country"))
    })
    private Address address;

    public Label() {

    }

    public Label(int id, String name, Address address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Label{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
}

Create LabelService class:
package com.jpa.embedded.objects.mappings.tutorial.service;

import com.jpa.embedded.objects.mappings.tutorial.domain.Address;
import com.jpa.embedded.objects.mappings.tutorial.domain.Label;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.List;

public class LabelService {

    private EntityManager em;

    public LabelService(EntityManager em) {
        this.em = em;
    }

    public Label createLabel(int id, String name, Address address) {
        Label label = new Label(id, name, address);
        em.persist(label);
        return label;
    }

    public void removeLabel(int id) {
        Label label = em.find(Label.class, id);

        if (label != null) {
            em.remove(label);
        }
    }

    public Label changeLabelName(int id, String name) {
        Label label = em.find(Label.class, id);

        if (label != null) {
            label.setName(name);
        }

        return label;
    }

    public Label findLabel(int id) {
        return em.find(Label.class, id);
    }

    public List<Label> findAllLabels() {
        TypedQuery<Label> query = em.createQuery("SELECT l FROM Label l", Label.class);
        return query.getResultList();
    }
}

Add Label class to persistence unit in persistence.xml file:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

    <persistence-unit name="JpaEmbeddedObjectsMappingsTutorial" transaction-type="RESOURCE_LOCAL">
        <class>com.jpa.embedded.objects.mappings.tutorial.domain.Artist</class>
        <class>com.jpa.embedded.objects.mappings.tutorial.domain.Label</class>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url"
                      value="jdbc:mysql://localhost:3306/jpa_embedded_objects_mappings_tutorial"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="root"/>
        </properties>
    </persistence-unit>

</persistence>

Add code that creates LabelService object, finds Label entity and prints label data to main() method of JpaEmbeddedObjectsMappingsTutorial class:
package com.jpa.embedded.objects.mappings.tutorial;

import com.jpa.embedded.objects.mappings.tutorial.domain.Artist;
import com.jpa.embedded.objects.mappings.tutorial.domain.Label;
import com.jpa.embedded.objects.mappings.tutorial.service.ArtistService;
import com.jpa.embedded.objects.mappings.tutorial.service.LabelService;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class JpaEmbeddedObjectsMappingsTutorial {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("JpaEmbeddedObjectsMappingsTutorial");
        EntityManager em = emf.createEntityManager();
        ArtistService artistService = new ArtistService(em);
        LabelService labelService = new LabelService(em);

        System.out.println("--- Find artist ---");
        Artist artist = artistService.findArtist(1);
        System.out.println(String.format("Found: %s\n", artist));

        System.out.println("--- Find label ---");
        Label label = labelService.findLabel(1);
        System.out.println(String.format("Found: %s", label));
    }
}

Execute main() method:

Application has printed data about label and its address, which means we have successfully used embedded objects during application development.

Folder structure:




Application source code is available at http://code.google.com/p/jpa-embedded-objects-mappings-tutorial/.

Recommended posts:

No comments:

Post a Comment