Java JPA外键参考未保存

时间:2019-06-24 16:04:55

标签: java spring-boot jpa h2

我正在学习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 + "}";
    }
}

我的用户实体如下

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方法从h2表返回一个User对象。

任何建议

1 个答案:

答案 0 :(得分:1)

  

实际上是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的源代码,他们已经清楚地提到了

  
      
  • 如果联接用于元素集合,
  •   
  • 连接列名称形成为
  •   
  • 以下内容的并置:实体名称; “ _”;
  •   
  • 所引用的主键列的名称。
  •   

编辑

第二部分,您可以直接使用JPA的功能。

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