Spring Boot Jpa @ElementCollection

0

In this article, we will explore how to map collections of objects that are not entities.

When the source entity has a collection containing instances of the target entity type it is called a multivalued relationship. For example, a Movie and its cast consisting of one or more actors.

There is another case of collection of elements where the element is not an entity but instead is a java type. For example, a Movie and the Genres it belongs to.

Here the element is Genre and the reference entity is Movie.

In case of the first Movie-Actor relationship, we will use @OneToMany annotation whereas in case of an element collection we need to use @ElementCollection annotation.

The collection data will be stored in a table whose name defaults to the name of the referencing entity(movie), appended with an underscore and the name of the entity attribute (genres) that contains the element collection. In our case it will be movie_genres. The join column again is derived if not specified explicitly, by default it is the name of the referencing entity (movie), appended with an underscore and the name of the primary key column (id) of the entity table.

@ElementCollection(targetClass=Genre.class)

Every collection table must have a join column that refers to the containing entity table.

@CollectionTable(name="movie_genres", joinColumns= {@JoinColumn(name="movie_id")})

The collection element fetched based on the column specified in @Column annotation which is nothing but a column in the collection table.


Genre:

package com.javarticles.springboot.movies.domain;

public enum Genre {
COMEDY,
SCIFI,
ACTION,
ROMANCE,
THRILLER,
HORROR,
MYSTERY,
CRIME,
ANIMATION,
ADVENTURE,
FANTASY,
DRAMA,
SUPERHERO
}

Movie:

package com.javarticles.springboot.movies.domain;

import javax.persistence.*;

import org.springframework.data.jpa.domain.AbstractPersistable;

import java.util.Set;

@Entity
public class Movie extends AbstractPersistable<Long> {
    private String name;
    
    private Integer year;

    @ElementCollection
    @Column(name="genre", nullable=false)
    @CollectionTable(name="movie_genres", joinColumns= {@JoinColumn(name="movie_id")})
    private Set<Genre> genres;

    public String getName() {
        return name;
    }

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

    public Integer getYear() {
        return year;
    }

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

    public Set<Genre> getGenres() {
        return genres;
    }

    public void setGenres(Set<Genre> genres) {
        this.genres = genres;
    }    
}

MovieRepository:

package com.javarticles.springboot.movies.repositories;

import org.springframework.data.jpa.repository.JpaRepository;

import com.javarticles.springboot.movies.domain.Movie;

public interface MovieRepository extends JpaRepository<Movie, Long> {
    Movie findByName(String name);
}

SpringbootMoviesJpaApplication:

package com.javarticles.springboot.movies;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class SpringbootMoviesJpaApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringbootMoviesJpaApplication.class, args);
	}
}

SpringbootMoviesJpaApplicationTests:

package com.javarticles.springboot.movies.springbootMoviesJpa;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;
import java.util.HashSet;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;

import com.javarticles.springboot.movies.domain.Genre;
import com.javarticles.springboot.movies.domain.Movie;
import com.javarticles.springboot.movies.repositories.MovieRepository;

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class SpringbootMoviesJpaApplicationTests {
    @Autowired
    private MovieRepository movieRepository;

    @Test
    public void createMovie() {
        String name = "Murder on the Orient Express";
        Movie movie = new Movie();
        movie.setName(name);
        movie.setYear(2001);
        movie.setGenres(new HashSet<>(Arrays.asList(Genre.DRAMA, Genre.MYSTERY)));
        movieRepository.save(movie);

        Movie movieFound = movieRepository.findByName(name);

        assertThat(movieFound, equalTo(movie));
        assertThat(movieFound.getGenres().size(), equalTo(2));
        assertTrue(movieFound.getGenres().contains(Genre.DRAMA));
        assertTrue(movieFound.getGenres().contains(Genre.MYSTERY));
    }

}
Hibernate: insert into movie (name, year, id) values (?, ?, ?)
Hibernate: insert into movie_genres (movie_id, genre) values (?, ?)
Hibernate: insert into movie_genres (movie_id, genre) values (?, ?)

Download the source code here

This was an example about JPA @ElementCollection using spring boot.

You can download the source code here: springbootJpaElementCollection.zip
Share.

Comments are closed.