Usually, when we write a Many-To-Many relationship it is between 2 entities. Sometimes, it could happen that we have an entity which has a field which is a set of enum. In this case, we need to approach the problem differently and Spring comes to help.
Let’s see an example where we have two classes: Person and Skill (e.g. swimming, running, etc).
Many-To-Many between entities example
The following code shows what the situation is when Person and Skill are both entities.
@Entity public class Person { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "person_skill", joinColumns = { @JoinColumn(name = "person_id")}, inverseJoinColumns = { @JoinColumn(name = "skill_id")}) private Set<Skill> skillSet; } @Entity public class Skill { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; }
To use this mapping, you would have 3 tables. You can create them with the following SQL code.
CREATE TABLE `person` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `name` VARCHAR(45) NOT NULL, PRIMARY KEY (`id`)); CREATE TABLE `skill` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `name` VARCHAR(45) NOT NULL, PRIMARY KEY (`id`)); CREATE TABLE `person_skill` ( `person_id` BIGINT(20) NOT NULL, `skill_id` BIGINT(20) NOT NULL, PRIMARY KEY (`person_id`, `skill_id`), INDEX `skill_fk_idx` (`skill_id` ASC), CONSTRAINT `person_fk` FOREIGN KEY (`person_id`) REFERENCES `person` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `skill_fk` FOREIGN KEY (`skill_id`) REFERENCES `skill` (`id`) ON DELETE CASCADE ON UPDATE CASCADE);
Many-To-Many between entity and enum example
The following code shows what the situation is when Person is an entity and Skill is an enum.
@Entity public class Person { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @ElementCollection(targetClass = Skill.class) @CollectionTable(name = "person_skill", joinColumns = @JoinColumn(name = "person_id")) @Enumerated(EnumType.STRING) @Column(name = "skill_name") private Set<Skill> skillSet; } public enum Skill { RUNNING, SWIMMING }
To use this mapping, you would have only 2 tables, because Skill is not an entity and would not have its own table. You can create them with the following SQL code.
CREATE TABLE `person` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `name` VARCHAR(45) NOT NULL, PRIMARY KEY (`id`)); CREATE TABLE `person_skill` ( `person_id` BIGINT(20) NOT NULL, `skill_name` VARCHAR(45) NOT NULL, PRIMARY KEY (`person_id`, `skill_name`), CONSTRAINT `person_fk` FOREIGN KEY (`person_id`) REFERENCES `person` (`id`) ON DELETE CASCADE ON UPDATE CASCADE);
As you can see here, you don’t have the table skill because Skill is not an entity but only an enum; you only have to create the table which represents the relationship between Person and Skill. Furthermore, since we added the annotation @Enumerated(EnumType.STRING) Spring Data will save the name of the entity in the database (look, we put a VARCHAR column). If you prefer, you could use EnumType.ORDINAL and Spring Data will save the ordinal value of the entity (1,2,3,etc..), so change the column to accept a numeric value.
That’s it!