Ссылка на внешний ключ Java JPA не сохраняется - PullRequest
1 голос
/ 24 июня 2019

Я изучаю SpringBoot и пытаюсь создать простой API.Использование H2 в качестве слоя данных.

Моя схема таблицы выглядит следующим образом

DROP TABLE IF EXISTS Users;
DROP TABLE IF EXISTS Cards;
CREATE TABLE Users(USER_ID INT PRIMARY KEY,
              NAME VARCHAR(255) NOT NULL,
              USERNAME VARCHAR(255) NOT NULL,
              PASSWORD VARCHAR(255) NOT NULL,
              IS_ADMIN BOOL);

insert into Users values(10001,'Administrator', 'admin', 'admin', true);

CREATE TABLE Cards(CARD_ID INT PRIMARY KEY,
              NUMBER VARCHAR(20) NOT NULL,
              EXPIRY VARCHAR(5) NOT NULL,
              CVV INT,
              USER INT,
              foreign key (USER) references Users(USER_ID)
);

Моя сущность CreditCard выглядит следующим образом.

package sprintboot;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

@Data
@Entity
@Table(name = "CARDS")
@ApiModel(description = "the Credit Card object")
public class CreditCard {

    @ApiModelProperty(notes = "The DB ID of card")
    private @Column(name = "CARD_ID") @Id @GeneratedValue Long card_id;

    @NotBlank(message = "Credit card number is mandatory")
    @ApiModelProperty(notes = "The number of credit card", required = true)
    private @Column(name = "NUMBER") String number;

    @NotBlank(message = "Credit card expiry is mandatory")
    @ApiModelProperty(notes = "The expiry of credit card", required = true)
    private @Column(name = "EXPIRY") String expiry;

    @NotNull(message = "Credit card cvv is mandatory")
    @ApiModelProperty(notes = "The cvv of credit card", required = true)
    private @Column(name = "CVV") Integer cvv;

    @ApiModelProperty(notes = "The owner user of credit card")
    @ManyToOne
    @JoinColumn
    private User user;

    public CreditCard() {}

    public CreditCard(String number, String expiry, Integer cvv, User user) {
        this.number = number;
        this.expiry = expiry;
        this.cvv = cvv;
        this.user = user;
    }

    @Override
    public String toString() {
        return "{number: " + this.number + ", expiry: " + this.expiry + ", cvv: " + this.cvv + ", user: " + this.user + "}";
    }
}

Моя сущность User выглядит следующим образом

package springboot;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import java.util.List;

@Data
@Entity
@Table(name = "USERS")
@ApiModel(description = "the USER object")
public class User {

    @ApiModelProperty(notes = "The id of User")
    private @Column(name = "USER_ID") @Id @GeneratedValue Long user_id;

    @ApiModelProperty(notes = "The name of User")
    private @Column(name = "NAME") String name;

    @ApiModelProperty(notes = "The username of User", required = true)
    @NotBlank(message = "Username is mandatory")
    private @Column(name = "USERNAME") String username;

    @ApiModelProperty(notes = "The password of User", required = true)
    @ValidPassword(message = "Password is mandatory")
    private @Column(name = "PASSWORD") String password;

    @ApiModelProperty(notes = "The admin status of User")
    private @Column(name = "IS_ADMIN") boolean isAdmin;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<CreditCard> cards;

    public User() {}

    public User(String name, String username, String password, boolean isAdmin) {
        this.name = name;
        this.username = username;
        this.password = password;
        this.isAdmin = isAdmin;
    }

    @Override
    public String toString() {
        return "{id: " + this.getUser_id() + ", username: " + this.getUsername() + ", isAdmin: " + this.isAdmin() + "}";
    }
}

Таким образом, кредитная карта будет содержать пользователя.Пользователь может содержать много кредитных карт.У меня проблема в том, что когда я пытаюсь сохранить запись кредитной карты.Выдает следующее сообщение об ошибке:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.In
validDataAccessResourceUsageException: could not prepare statement; SQL [insert into cards (cvv, expiry, number, user_user_id, card_id) values (?, ?, ?, ?, ?)]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement] with root

причина

org.h2.jdbc.JdbcSQLSyntaxErrorException: Column "USER_USER_ID" not found; SQL statement:
insert into cards (cvv, expiry, number, user_user_id, card_id) values (?, ?, ?, ?, ?) [42122-199]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:451) ~[h2-1.4.199.jar:1.4.199]

Я пытался найти несколько учебных пособий и следовать им, но не смог заставить их работать.Может кто-нибудь указать, в чем я не прав.

Метод сохранения объекта кредитной карты заключается в следующем.

public CreditCard upsertCC(
        CreditCard card,
        String jwtToken
) throws UnauthorizedException {
    User user = this.getUserFromJWTToken(jwtToken);
    log.info(user.toString());
    card.setUser(user);
    this.ccRepo.save(card);
    this.ccRepo.flush();
    card.setUser(null);
    return card;
}

Где метод getUserFromJWTToken возвращает объект User из таблицы h2.

Любые предложения

1 Ответ

1 голос
/ 24 июня 2019

Исключением на самом деле является Column "USER_USER_ID" not found;

Если вы видите запрос, упоминается insert into cards (cvv, expiry, number, user_user_id, card_id) values (?, ?, ?, ?, ?), user_user_id , который не является именем столбца

Итак, проблема в том, что вы не упомянули имя столбца как user в CreditCard сущности. Попробуйте ниже это будет работать

ИЗМЕНЕНИЕ, ЧТОБЫ СДЕЛАТЬ, CreditCard.class

    @ApiModelProperty(notes = "The owner user of credit card")
    @ManyToOne
    @JoinColumn(name = "user") // Added the name attribute
    private User user;

Если вы посмотрите на исходный код @JoinColumn's name attribute, он четко упомянул

  • Если объединение для коллекции элементов,
  • имя столбца соединения формируется как
  • объединение следующего: название объекта; "_";
  • имя столбца ссылочного первичного ключа.

EDIT

Для второй части вы можете напрямую использовать функциональность JPA.

List<CreditCard> findByNumberContainingAndUser(String number, User user);
...