【问题标题】:Spring Boot foreign key entity nullSpring Boot 外键实体 null
【发布时间】:2020-01-24 16:54:42
【问题描述】:

Spring Boot 版本2.1.6

我有User 类:

@Data
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    Long userID;
    String eMail;
    public User()
    {

    }
}

还有一个LoginCredential 类:

@Data
@Entity
public class LoginCredential {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    Long userID;
    String eMail;
    String passwordHash;
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id", referencedColumnName = "id")
    User user;
    public LoginCredential()
    {

    }
}

当我创建LoginCredential 的实例时,它不应该创建User 的实例吗?

因为我运行这个命令:curl -X POST -H "Content-Type: application/json" -d "{ \"email\": \"a\", \"passwordHash\": \"b\" }" http://localhost:8080/login

我得到了LoginCredential 的实例但没有User

回应:

{"userID":1,"passwordHash":"b","user":null,"email":"a"}

然后我运行这个命令:curl -X POST -H "Content-Type: application/json" -d "{ \"email\": \"c\", \"passwordHash\": \"d\" ,\"user\":{}}" http://localhost:8080/login

而我得到的是什么都没有,不是同一个 ID。

回应:

{"userID":2,"passwordHash":"d","user":{"userID":3,"email":null},"email":"c"}

我错过了什么吗?如何解决?

LoginCredentialController 部分:

@PostMapping("/login")
LoginCredential newLoginCredential(@RequestBody LoginCredential newLoginCredential)
{   
    return repository.save(newLoginCredential);
}

我的pom.xml

我的application.properties

【问题讨论】:

  • 发布您的 curl 请求响应
  • 你问过Shouldn't it create an instance of User when I create an instance of LoginCredential? 这取决于你在 /login 控制器方法上做了什么。也请在此处发布。
  • 已编辑,请参阅@rimonmostafiz
  • 两个结果都完全符合预期 - 第一个请求不包含 User 所以你没有收到,第二个请求包含一个空的 User 所以你得到一个,但请考虑这个事实LoginCredential#userIDUser#userID 无关。
  • @Smutje 请参阅this,我设置了起点。看到他使用了**创建具有相同父母身份的孩子**的东西。我从 git 和教程中尝试了他的代码。不起作用。而且他的 tut 甚至与 git 代码都不匹配。

标签: spring hibernate spring-boot spring-data-jpa foreign-keys


【解决方案1】:
@Data
@Entity
public class LoginCredential implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    Long credentailID;
    String eMail;
    String passwordHash;
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id", referencedColumnName = "id", nullable = false)
    @OneToOne(fetch = FetchType.EAGER) //<-- EAGER is default, but try setting it explicitly
    User user;
    public LoginCredential()
    {

    }
}



@PostMapping("/login")
LoginCredential newLoginCredential(@RequestBody LoginCredential newLoginCredential)
{   
    LoginCredential saveResult = repository.save(newLoginCredential);
    //load it again after saving 
    return repository.findbyId(saveResult.getuserId);
}

或更好:

   @PostMapping("/login")
    ResponseEntity newLoginCredential(@RequestBody LoginCredential newLoginCredential)
    {   
      try{
        LoginCredential saveResult = repository.save(newLoginCredential);
        //load it again after saving 
        LoginCredential loaded = repository.findbyId(saveResult.getuserId);
//if you set a breakpoint here, you should already see if it is working, if its ok here, then it might have to do with serilalization ( jackson )...
            return new ResponseEntity<>(loaded,HttpStatus.OK);
          }
           catch (Exception e){
                log.error(e.getMessage());
                return       ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
            }
        }

而且,您对 jackson 有依赖关系吗? -> 它负责将对象序列化为 json ...

<dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>

这在你的属性中:

spring.jackson.serialization.indent_output=true
spring.jackson.default-property-inclusion=ALWAYS

希望对你有帮助,干杯

【讨论】:

  • 类也用EntityData注解
  • 那我该怎么办?我无法得到你的答案
  • 首先有很多错误,在你的代码中我修复了它们。但它给了我this,只是以一种有风格的方式。但是仔细看那里id/userID是不同的。关键不工作。 @womd
  • 好的,谢谢修复,应该只是基本说明,但这不是你想要的吗? - 您获得带有子用户的 UserCredentials ....? .. 样式输出来自 .intent_output=True ,但关键是:spring.jackson.default-property-inclusion=ALWAYS ...当密钥不匹配时 - 手动验证您的数据库 - 你确定它没有保存吗方式?
  • 感谢您的大力帮助,我们快到了。我现在有孩子在父母。但我希望他们的userID/id 相同。
【解决方案2】:

这里是解决办法,我后来问了另一个question,发现了一个answer

你有三个问题

  1. 缺少 cascade 选项来触发从 LoginCredential 保存的 User 实体创建。
  2. User 上缺少@MapsId 注释以使它们共享相同的ID,否则LoginCredential 及其创建的User 将具有不同的ID 值,因为它们的@GeneratedValue(strategy = GenerationType.AUTO) 在它们的@Id 列上具有@GeneratedValue(strategy = GenerationType.AUTO) /li>
  3. 不设置关系的双方...

要解决所有问题,您需要将实体更改为以下(我还删除了一些无用的注释和值);

@Data
@Entity
public class User {

    @Id
    Long userID;

    @JsonBackReference
    @MapsId
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "userID", referencedColumnName = "userID")
    @ToString.Exclude
    private LoginCredential loginCredential;
}

@Data
@Entity
public class LoginCredential {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long userID;
    String eMail;
    String passwordHash;

    @JsonManagedReference
    @OneToOne(mappedBy = "loginCredential", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
    private User user;
}

还需要在最终确定端点之前设置关系的双方;

Optional.ofNullable(loginCredential.getUser())
        .ifPresent(user -> user.setLoginCredential(loginCredential));
loginCredentialRepo.save(loginCredential);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-21
    • 1970-01-01
    • 2021-05-23
    • 2017-08-21
    • 2021-09-03
    • 1970-01-01
    • 2020-07-04
    相关资源
    最近更新 更多