JPA @JoinTable с составными (2 столбца) первичными ключами

В приложении с весенней загрузкой у меня есть следующее определение сущности:

@Data
@Entity
@Table(name = "users")
public class User {

    @Id
    @Column(nullable = false, name = "username", length = 100)
    private String username;

    @JoinTable(name = "userrole", 
        joinColumns = { @JoinColumn(name = "username") },
        inverseJoinColumns = { @JoinColumn(name = "role") }
    )
    @OneToMany(
        cascade = CascadeType.ALL, 
        orphanRemoval = true
    )
    private List<Role> roles;`

Я использую Spring-data-jpa, Hibernate с H2 в качестве базы данных. Проблема в том, что spring-data-jpa, hibernate всегда генерирует/создает «роль пользователя» таблицы соединений (DDL) с одним первичным ключом столбца. например 'имя пользователя'. Таким образом, если такие записи, как {'username', 'user_role'} и {'username', 'admin_role'}, вставляются в таблицу соединения ("userrole"), следующая вставка завершается с ошибкой из-за "дубликата" основной ключ.

Я пытался использовать оба столбца в приведенном выше определении, а также следующий вариант:

    @OneToMany(
        cascade = CascadeType.ALL, 
        orphanRemoval = true
    )
    @JoinColumns({
           @JoinColumn(name = "username"),
           @JoinColumn(name = "role") })
    private List<Role> roles;`

Но что они привели к таким же или более серьезным проблемам, например. а в последнем даже создание таблицы не удается, потому что только один столбец используется в качестве первичного ключа для соединения. Роль — это просто еще одна таблица с двумя столбцами «роль» и «описание», в основном каталог ролей.

Как указать JPA, что @JoinTable должен использовать столбцы «имя пользователя» и «роль» в качестве составных первичных ключей?

редактировать: Я попытался использовать независимую таблицу/объект, как было предложено, спасибо @Kamil Bęben

@Data
@Entity
@Table(name = "users")
public class User {

    @Id
    @Column(nullable = false, name = "username", length = 100)
    private String username;

    @OneToMany(
        fetch = FetchType.EAGER,
        cascade = CascadeType.ALL, 
        mappedBy = "username",
        orphanRemoval = true
    )
    @ElementCollection
    private List<UserRole> roles;

UserRole определяется как таковой

@Data
@NoArgsConstructor
@Entity
@Table(name = "userrole")
public class UserRole {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "userrole_seq")
    Long id;    
    
    @Column(nullable = false, name = "username", length = 100)
    private String username;

    @Column(nullable = false, name = "role", length = 50)
    private String role;

репозиторий для этой таблицы соединения ролей пользователей определяется как

@Repository
public interface UserRoleRepository extends CrudRepository<UserRole, Long> {

    UserRole findByUsernameAndRole(String username, String role);
    
    List<UserRole> findByUsername(String username); 
        
    List<UserRole> findByRole(String role); 
    
}

По общему признанию, некрасиво, но это работает. И каким-то образом казалось, что он использует правильный метод findByUsername() для извлечения ролей, относящихся к пользователю, вероятно, связанных с предложением «mappedBy». 'черная магия'! Есть еще много того, что мне все еще нужно найти в JPA, Spring, Spring-data

редактировать2: дальнейшее обновление: оригинальный @JoinTable также работает. Но что отношения должны быть указаны как @ManyToMany

    @ManyToMany(
            fetch = FetchType.EAGER,
            cascade = CascadeType.MERGE
    )
    @JoinTable(name = "usersroles", 
        joinColumns = { @JoinColumn(name = "username") },
        inverseJoinColumns = { @JoinColumn(name = "role") }
    )
    private List<Role> roles = new ArrayList<Role>();

Это создает 2 первичных ключа столбца, как и ожидалось, для таблицы «пользователи-роли».

Спасибо @Roman

Почему roles аннотирован @OneToMany? Разве так не должно быть @ManyToMany?

Roman 08.01.2023 20:52

да, на самом деле это \@ManyToMany, но из родительского класса User это будет больше похоже на \@OneToMany, поскольку у пользователя будет несколько ролей.

user4433437 09.01.2023 04:07
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
2
85
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если роль имеет только два столбца, например, user_id и роль, способ сопоставления этого в jpa будет следующим:

@ElementCollection
@CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"))
@Column(name = "role")
List<String> roles = new ArrayList<>();

В противном случае jpa действительно требует, чтобы идентификатор каждой сущности и столбцы соединения были отдельными столбцами, поэтому сущность Role должна была бы иметь такие столбцы, как id, user_id и role_name. Может выглядеть так.:

class Role {
  @Id
  Long id;
  @ManyToOne
  @JoinColumn(name = "user_id", referencedColumnName = "id");
  User user;
  String roleName;
  // Other fields
}

И в сущности пользователя

@OneToMany(mappedBy = "user") // user is Field's name, not a column
List<Role> roles = new ArrayList<>();

Читать дальше

Спасибо ! Я попробовал метод \@ElementCollection, и он работает. Я думаю, что явное определение таблицы соединения ролей пользователя как объекта, вероятно, является лучшей практикой! Я бы работал над другим, который заключается в том, чтобы таблица соединения ролей пользователей была определена как автономная сущность.

user4433437 09.01.2023 07:02

Другие вопросы по теме

Похожие вопросы