Tuesday, July 23, 2013

JPA-маппинг перечисляемых типов

В JPA маппинг перечисляемых типов имеет свои особенности и может быть сделан несколькими способами.
Рассмотрим, как он реализуется.

Создадим базу данных jpa_enumerated_types_mappings_tutorial с таблицей albums.
Таблица albums имеет колонки album_id, album_name, album_year и album_type.
Колонка album_type хранит значения, которые соответствуют типу альбома.
Если альбом имеет тип "LP", то значение колонки равно "0". Если тип - "SINGLE", то значение равно "1".
Добавим данные об альбомах исполнителя:
CREATE DATABASE jpa_enumerated_types_mappings_tutorial;
USE jpa_enumerated_types_mappings_tutorial;

CREATE TABLE albums (
album_id INT PRIMARY KEY,
album_name VARCHAR(50),
album_year INT,
album_type INT
);

INSERT INTO albums VALUES (1, 'Franz Ferdinand', 2004, 0);
INSERT INTO albums VALUES (2, 'Darts of Pleasure', 2003, 1);
INSERT INTO albums VALUES (3, 'Take Me Out', 2004, 1);
INSERT INTO albums VALUES (4, 'The Dark of the Matinee', 2004, 1);
INSERT INTO albums VALUES (5, 'Michael', 2004, 1);
INSERT INTO albums VALUES (6, 'This Fire', 2004, 1);

Маппинг с использованием порядковых номеров


При маппинге с использованием порядковых номеров каждому значению перечисляемого типа соответствует определенный порядковый номер в колонке таблицы базы данных.

Создадим класс перечисляемого типа AlbumType с возможными значениями "LP" и "SINGLE":
package com.jpa.enumerated.types.mappings.tutorial.domain;

public enum AlbumType {
    LP,
    SINGLE
}

Создадим класс сущности Album с полями id, name, year и type. Поле type имеет тип AlbumType.
Для маппинга с использованием порядковых номеров добавим полю type аннотацию @Column с указанием колонки таблицы albums, которая хранит эти номера, т.е. album_type:
package com.jpa.enumerated.types.mappings.tutorial.domain;

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

@Entity
@Table(name = "albums")
public class Album {

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

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

    @Column(name = "album_year")
    private int year;

    @Column(name = "album_type")
    private AlbumType type;

    public Album() {

    }

    public Album(int id, String name, int year, AlbumType type) {
        this.id = id;
        this.name = name;
        this.year = year;
        this.type = type;
    }

    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 int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public AlbumType getType() {
        return type;
    }

    public void setType(AlbumType type) {
        this.type = type;
    }

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

Создадим класс AlbumService:
package com.jpa.enumerated.types.mappings.tutorial.service;

import com.jpa.enumerated.types.mappings.tutorial.domain.Album;
import com.jpa.enumerated.types.mappings.tutorial.domain.AlbumType;

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

public class AlbumService {

    private EntityManager em;

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

    public Album createAlbum(int id, String name, int year, AlbumType type) {
        Album album = new Album(id, name, year, type);
        em.persist(album);

        return album;
    }

    public void removeAlbum(int id) {
        Album album = em.find(Album.class, id);

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

    public Album changeAlbumName(int id, String name) {
        Album album = em.find(Album.class, id);

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

        return album;
    }

    public Album findAlbum(int id) {
        return em.find(Album.class, id);
    }

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

Добавим класс Album в 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="JpaEnumeratedTypesMappingsTutorial" transaction-type="RESOURCE_LOCAL">
        <class>com.jpa.enumerated.types.mappings.tutorial.domain.Album</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_enumerated_types_mappings_tutorial"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="root"/>
        </properties>
    </persistence-unit>

</persistence>

В методе main() класса JpaEnumeratedTypesMappingsTutorial создается объект типа AlbumService, вызывается метод для получения сущностей типа Album и выводится информация об альбомах исполнителя:
package com.jpa.enumerated.types.mappings.tutorial;

import com.jpa.enumerated.types.mappings.tutorial.domain.Album;
import com.jpa.enumerated.types.mappings.tutorial.service.AlbumService;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.util.List;

public class JpaEnumeratedTypesMappingsTutorial {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("JpaEnumeratedTypesMappingsTutorial");
        EntityManager em = emf.createEntityManager();
        AlbumService albumService = new AlbumService(em);

        System.out.println("--- Finding albums ---");
        List<Album> albums = albumService.findAllAlbums();
        for (Album album : albums) {
            System.out.println(String.format("Found album: %s", album));
        }
    }
}

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

Приложение вывело информацию об альбомах исполнителя. Тип каждого альбома соответствует значениям в базе данных.

Маппинг с использованием порядковых номеров имеет серьезный недостаток. Если класс перечисляемого типа изменяется, то его значения перестают соответствовать значениям в базе данных.
Добавим новое значение "NONE" в класс перечисляемого типа:
package com.jpa.enumerated.types.mappings.tutorial.domain;

public enum AlbumType {
    NONE,
    LP,
    SINGLE
}

Теперь значение "0" в колонке order_type будет соответствовать типу "NONE", "1" - "LP" и т.д.

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

Приложение вывело информацию об альбомах исполнителя, но тип каждого альбома неверен.
Эту проблему может решить...

Маппинг с использованием строк


При маппинге с использованием строк каждому значению перечисляемого типа соответствует строковое значение в колонке таблицы базы данных. Порядок значений перечисляемого типа уже не важен.

Изменим тип колонки album_type с INT на VARCHAR.
Обновим данные колонки, чтобы было исходное соответствие, т.е. значение "0" изменится на "LP", а "1" - на "SINGLE":
ALTER TABLE albums CHANGE album_type album_type VARCHAR(10);
UPDATE albums SET album_type = 'LP' WHERE album_type = '0';
UPDATE albums SET album_type = 'SINGLE' WHERE album_type = '1';

Для маппинга с использованием строк добавим полю type аннотацию @Enumerated со значением "EnumType.STRING":
@Column(name = "album_type")
@Enumerated(EnumType.STRING)
private AlbumType type;

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

Приложение вывело информацию об альбомах исполнителя с правильными типами альбомов, а значит мы успешно сделали различные типы маппингов перечисляемых типов.

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




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

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

No comments:

Post a Comment