Saturday, June 22, 2013

JPA-маппинг отношений "один-к-одному"

Многие сущности имеют отношения с другими сущностями.
Отношения бывают 4 типов:
  • One-to-One (один-к-одному)
  • One-to-Many (один-ко-многим)
  • Many-to-One (многие-к-одному)
  • Many-to-Many (многие-ко-многим)

Рассмотрим маппинг отношений типа "один-к-одному".

Возьмем за основу приложение, созданное в посте "JPA-маппинг сущности и ее свойств".

Музыкальный исполнитель может иметь только один официальный веб-сайт, а веб-сайт может относиться только к одному исполнителю. Опишем эти отношения в приложении.

Создадим базу данных jpa_one_to_one_mappings_tutorial и таблицы artists и websites.
Колонка artist_id таблицы websites является внешним ключом и ссылается на одноименную колонку таблицы artists.
Добавим данные об исполнителе и его веб-сайте:
CREATE DATABASE jpa_one_to_one_mappings_tutorial;
USE jpa_one_to_one_mappings_tutorial;

CREATE TABLE artists (
artist_id INT PRIMARY KEY,
artist_name VARCHAR(30)
);

CREATE TABLE websites (
website_id INT PRIMARY KEY,
website_url VARCHAR(50),
artist_id INT UNIQUE,
FOREIGN KEY (artist_id) REFERENCES artists (artist_id)
);

INSERT INTO artists VALUES (1, 'Franz Ferdinand');
INSERT INTO websites VALUES (1, 'www.franzferdinand.com', 1);

Однонаправленный маппинг


Если сущность Website будет иметь ссылку на сущность Artist, но Artist не будет ссылаться на Website, то отношения будут однонаправленными.

Создадим класс сущности Website с полями id, url и artist.
Для однонаправленного маппинга вида "один-к-одному" добавим полю artist аннотации @OneToOne и @JoinColumn с указанием имени колонки таблицы websites, которая является внешним ключом, т.е. artist_id:
package com.jpa.one.to.one.mappings.tutorial.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name = "websites")
public class Website {

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

    @Column(name = "website_url")
    private String url;

    @OneToOne
    @JoinColumn(name = "artist_id")
    Artist artist;

    public Website() {

    }

    public Website(int id, String url) {
        this.id = id;
        this.url = url;
    }

    public int getId() {
        return id;
    }

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

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Artist getArtist() {
        return artist;
    }

    public void setArtist(Artist artist) {
        this.artist = artist;
    }

    @Override
    public String toString() {
        return "Website{" +
                "id=" + id +
                ", url='" + url + '\'' +               
                '}';
    }
}

Создадим класс WebsiteService:
package com.jpa.one.to.one.mappings.tutorial.service;

import com.jpa.one.to.one.mappings.tutorial.domain.Website;

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

public class WebsiteService {

    private EntityManager em;

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

    public Website createWebsite(int id, String url) {
        Website artist = new Website(id, url);
        em.persist(artist);

        return artist;
    }

    public void removeWebsite(int id) {
        Website website = em.find(Website.class, id);

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

    public Website changeWebsiteUrl(int id, String url) {
        Website website = em.find(Website.class, id);

        if (website != null) {
            website.setUrl(url);
        }

        return website;
    }

    public Website findWebsite(int id) {
        return em.find(Website.class, id);
    }

    public List<Website> findAllWebsites() {
        TypedQuery<Website> query = em.createQuery("SELECT w FROM Website w", Website.class);
        return query.getResultList();
    }
}

Добавим класс Website в persistence unit в файле persistence.xml:
<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="JpaOneToOneMappingsTutorial" transaction-type="RESOURCE_LOCAL">
        <class>com.jpa.one.to.one.mappings.tutorial.domain.Artist</class>
        <class>com.jpa.one.to.one.mappings.tutorial.domain.Website</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_one_to_one_mappings_tutorial"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="root"/>
        </properties>
    </persistence-unit>

</persistence>

В методе main() класса JpaOneToOneMappingsTutorial создается объект типа WebsiteService, вызывается метод для получения сущности Website и выводится информация о веб-сайте и связанном с ним исполнителе:
package com.jpa.one.to.one.mappings.tutorial;

import com.jpa.one.to.one.mappings.tutorial.domain.Website;
import com.jpa.one.to.one.mappings.tutorial.service.WebsiteService;

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

public class JpaOneToOneMappingsTutorial {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("JpaOneToOneMappingsTutorial");
        EntityManager em = emf.createEntityManager();
        WebsiteService websiteService = new WebsiteService(em);

        System.out.println("--- Find website ---");
        Website website = websiteService.findWebsite(1);
        System.out.println(String.format("Found website: %s", website));
        System.out.println(String.format("Website artist: %s\n", website.getArtist()));
    }
}

Запустим метод main():

Приложение вывело информацию о веб-сайте и связанном с ним исполнителе.

Двунаправленный маппинг


Если сущности Website и Artist будут иметь ссылки друг на друга, то отношения будут двунаправленными.

Для этого:
  • создадим поле website в классе Artist
  • добавим полю website аннотацию @OneToOne
  • добавим элемент аннотации mappedBy, который будет указывать на имя поля класса Website, которое имеет аннотации @OneToOne и @JoinColumn, т.е. artist
package com.jpa.one.to.one.mappings.tutorial.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
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;

    @OneToOne(mappedBy = "artist")
    private Website website;

    public Artist() {

    }

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

    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 Website getWebsite() {
        return website;
    }

    public void setWebsite(Website website) {
        this.website = website;
    }

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

Добавим в метод main() класса JpaOneToOneMappingsTutorial создание объекта типа ArtistService, вызов метода для получения сущности Artist и вывод информации об исполнителе и его сайте:
package com.jpa.one.to.one.mappings.tutorial;

import com.jpa.one.to.one.mappings.tutorial.domain.Artist;
import com.jpa.one.to.one.mappings.tutorial.domain.Website;
import com.jpa.one.to.one.mappings.tutorial.service.ArtistService;
import com.jpa.one.to.one.mappings.tutorial.service.WebsiteService;

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

public class JpaOneToOneMappingsTutorial {

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

        System.out.println("--- Find website ---");
        Website website = websiteService.findWebsite(1);
        System.out.println(String.format("Found website: %s", website));
        System.out.println(String.format("Website artist: %s\n", website.getArtist()));

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

Запустим метод main():

Приложение вывело информацию об исполнителе и его сайте, а значит мы успешно сделали однонаправленный и двунаправленный маппинги типа "один-к-одному".

Структура проекта:




Исходный код созданного приложения доступен по ссылке https://code.google.com/p/jpa-one-to-one-mappings-tutorial/.

Рекомендуемые посты:

No comments:

Post a Comment