Pages

Wednesday, August 7, 2013

JPA enumerated types mappings tutorial

Enumerated types mappings have own features and can be implemented in several ways in JPA.
Let's look at them in details.

This post is available in Russian.

Create jpa_enumerated_types_mappings_tutorial database with albums table.
albums table has album_id, album_name, album_year and album_type columns.
album_type column stores values that correspond to album types.
If album type is "LP", then column value is "0". If type is "SINGLE", then value is "1".
Add artist albums data:
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);

Ordinal mapping


If every enum value corresponds to specific ordinal number in table column, then such enum mapping is ordinal.

Create AlbumType enum class with possible "LP" and "SINGLE" values:
package com.jpa.enumerated.types.mappings.tutorial.domain;

public enum AlbumType {
    LP,
    SINGLE
}

Create Album class with id, name, year and type properties. type property has AlbumType type.
To create ordinal enum mapping add @Column annotation to type property. @Column annotation name element must reference table column, which stores album type values, e.g. 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 +
                '}';
    }
}

Create AlbumService class:
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();
    }
}

Add Album 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="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>

Add code that creates AlbumService object, finds Album entities and prints artist albums data to main() method of JpaEnumeratedTypesMappingsTutorial class:
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));
        }
    }
}

Execute main() method:

Application has printed artist albums data. Albums types correspond to album_type column values.

Ordinal mapping has a large drawback. If enum class changes then its properties might not correspond to database values anymore.
Add new enum value - "NONE":
package com.jpa.enumerated.types.mappings.tutorial.domain;

public enum AlbumType {
    NONE,
    LP,
    SINGLE
}

Now "0" column value corresponds to "NONE" enum type, "1" - to "LP" and so on.

Execute main() method:

Application has printed artist albums data, but albums types are incorrect.
This problem can be solved with...

String mapping


If every enum value corresponds to specific string in table column, then it's a string enum mapping. Enum types order is not important anymore.

Change album_type column type from INT to VARCHAR.
Update column data according to initial conformity, so that "0" value is replaced by "LP" and "1" - by "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';

To create string enum mapping add @Enumerated annotation with "EnumType.STRING" value to type property:
@Column(name = "album_type")
@Enumerated(EnumType.STRING)
private AlbumType type;

Execute main() method:

Application has printed artist albums data with correct albums types, which means we have successfully created ordinal and string enum mappings during application development.

Folder structure:




Application source code is available at https://code.google.com/p/jpa-enumerated-types-mappings-tutorial/.

Recommended posts:

No comments:

Post a Comment