11

So, there is that behavior with innodb that can cause problem if some tables lack of primary key.

So with Hibernate, I am looking for a key to specifies a primary key on a @ElementCollection table with a Set as the underling data structure.

I found a way to have a primary key with a map, but it is kind of weird because I do not need a map.

I also found an answer related to @Embeddable, but I do not need that kind of complexities. I am using a Set or Set as the data structure in my entities.

Any idea how to achieve that?

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
plcstpierre
  • 171
  • 1
  • 2
  • 11

3 Answers3

18

If you use a Set and make the element Column be not null, then hibernate will make a primary key with the join column and element column.

Example:

@Column(name = "STRINGS", nullable = false)
@ElementCollection
private Set<String> strings;
nickb
  • 59,313
  • 13
  • 108
  • 143
Dave L.
  • 43,907
  • 11
  • 63
  • 62
  • 1
    Good simple solution. Worked for me. – Manuel M Nov 24 '16 at 10:40
  • 2
    Not null has to be set using @Column(nullable = false) and no using @NotNull annotation. @Column(nullable = false) is used for indicating database schema details. See [@NotNull vs @Column(nullable = false)](https://stackoverflow.com/a/7439544/1997088) for more details – Black Jul 17 '17 at 18:25
4

@ElementCollection cannot take a primary key, because an Embeddable types cannot have an identifier.

You can add an @OrderColumn to optimize the generates SQL statements.

If you need a primary key, then you should turn the @ElementCollection into a @OneToMany association.

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
0

Tested on Spring Boot / Data JPA 2.5.2, hibernate-core 5.4.32:

@Entity
@Table(name = "user")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Data
@NoArgsConstructor
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ElementCollection(fetch = FetchType.EAGER, targetClass = Role.class)
    @Enumerated(EnumType.STRING)
    @CollectionTable(
        name = "user_role",
        joinColumns = @JoinColumn(name = "user_id")
    )
    @Column(name = "role", nullable = false)
    private Set<Role> roles; // public enum Role {ADMIN,USER}
}

produces MySQL table:

CREATE TABLE `user_role` (
  `user_id` bigint(20) NOT NULL,
  `role` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`user_id`,`role`),
  CONSTRAINT `FK_random_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
)
kinjelom
  • 6,105
  • 3
  • 35
  • 61