Spring Boot - 为单个 API 调用组合嵌套资源



假设您有两个资源,用户和帐户。它们存储在单独的表中,但具有一对一的关系,所有 API 调用都应同时使用它们。例如,创建具有帐户的用户的 POST 请求将发送以下数据:

{ "name" : "Joe Bloggs", "account" : { "title" : "My Account" }}

/users而不是像users/1/account那样拥有具有单独路由的多个控制器。这是因为我需要 User 对象只是一个,无论它如何在内部存储。

假设我创建了这些实体类

@Table(name = "user")
public class User {
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@NotNull
Account account;
@Column(name = "name")
String name;
}
@Table(name = "account")
public class Account {
@OneToOne(cascade = CascadeType.ALL, optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
@NotNull
User user;
@Column(name = "title")
String title;
}

问题是当我在上面发出 POST 请求时,它会抛出错误,因为缺少user_id,因为这是连接所必需的,但我无法发送user_id,因为尚未创建用户。

有没有办法在单个 API 调用中创建两个实体?

由于它是双向关系,并且在这种情况下one-to-one是必需的,因此您应该保留用户实体,然后才保留帐户。这里还有一件事不清楚,那就是数据库模式。实体的 pk 是什么?我提议将user.id用作两个表的单个标识。如果是这样,实体将如下所示:User(id, name), Account(user_id, title)及其实体是:

@Table(name = "account")
@Entity
public class Account {
@Id
@Column(name = "user_id", insertable = false, updatable = false)
private Long id;
@OneToOne(mappedBy = "account", fetch = FetchType.LAZY, optional = false)
@MapsId
private User user;
@Column(name = "title")
private String title;
}
@Table(name = "user")
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;;
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@JoinColumn(name = "id", referencedColumnName = "user_id")
private Account account;
@Column(name = "name")
private String name;
}

在服务层,您必须一致地保存它们:

@Transactional
public void save(User userModel) {
Account account = user.getAccount();
user.setAccount(null);
userRepository.save(user);
account.setUser(user);
accountRepository.save(account);
}

它将在单个事务中完成。但是您必须先保存用户,因为user_idaccount表的PK@MapsId显示用户的 ID 用作帐户的标识

另一种情况是当帐户的id存储在user表中时:User(id, name, account_id), Account(id, title)和实体是:

@Table(name = "account")
@Entity
public class Account {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToOne(cascade = CascadeType.ALL, mappedBy = "account")
private User user;
@Column(name = "title")
private String title;
}
@Table(name = "user")
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(name = "account_id", insertable = false, updatable = false)
private Long accountId;
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@JoinColumn(name = "account_id", referencedColumnName = "id", unique = true)
private Account account;
@Column(name = "name")
private String name;
}

在这种情况下,Account实体将在User实体保存时隐式保留:

@Transactional
public void save(User userModel) {
userRepository.save(user);
}

将导致插入到两个表中。由于声明了级联和孤立项,因此删除足以为帐户引用设置null

user.setAccount(null);
userRepository.save(user);

最新更新