情況如下:Work物件,用戶可以做的一些作業。用戶還可以在作品上簽字。所以一個非常基本的作業物件:
class Work (
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) var workId: Int,
var userId: Int,
...
var flags: Int,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "signTargetUserId")
var signTargetUser: User?,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "signedUserId")
var signedUser: User?
)
有User幾件事,這個和那個......:
class User(
@Id
val userId: Int,
val username: String,
val name: String,
val email: String,
@JsonIgnore
val password: String,
val flags: Int,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "roleId")
var role: Role
)
Telling JPA about the user-role relationship is very handy, I hope we agree in that. When I get the user, I get the role too. The work-user relationship is also very nice, I like it. I get a work object, I know who's supposed to sign off on it (signTargetUser), and I also know this users role. Now, there's a controller to retrieve one or many work objects, and naturally the return type of these are Work or List<Work>. There has to be a function dealing with posting new Work entities. I have read it somewhere, that in a REST API it's nice if the entities travelling to and from are the same. So what structure does one have send to post a work? The same as one gets when querying one. Na then:
fun createWork(@RequestBody work: Work, authentication: Authentication): ResponseEntity<Any> {
This is very handy, I really like that, except that it doesn't work at all. Now the request body must be a valid Work object that sounds sweet with the validation for like a ms, but then it doesn't. It requires 2 User objects, one at signTargetUser and one at signedUser. To make it worse, these users must have Role objects hanging on them. To make it even worse, the User objects must have a non-null password property. Obviously I don't even know that, this is now way out of hand. All I want to do is insert a Work object, knowing the signTargetUserId if any.
I see a solution in 2 steps, but i don't like it at all:
- First I need to create another class (
WorkIncoming) only to describe the structure of the object coming in in the post, but this time without the ManyToOne relationships. But then when you read back the work you created, you'd get a different structure.WorkIncomingin andWorkout. Not to mention the semi-useless new class that would mostly be a bad repetition of the first one. If I add or change a field, do i need to change 2 files? Seriously? - Obviously the original repository is dealing with
Workclasses, so it won't be able to handleWorkIncomingtype: new repository then. Again, total code duplication, maintenance blah blah. I'm not even sure now how the JPA would feel about 2 entities referencing the same table.
那么這里真正的解決方案是什么?真實的人是怎么做到的?我在這里沒有想法。我剛才描述的這個解決方案技術含量非常低,不可能!
uj5u.com熱心網友回復:
最常見的方法是使用 DTO 物件進行請求和回應。應該使用包含所有作業欄位的 DTO 名稱 - workId、型別、標志等以及 signTargetUserId。DTO 欄位名稱應與物體名稱相同,以便于映射。
class WorkDTO (
var userId: Int,
...
var flags: Int,
var signTargetUserId: Int
)
之后,您使用 signTargetUserId 從 userRepository 獲取用戶物件。
User user = this.usersRepository.findById(signTargetUserId);
這樣,您將不需要 Role 物件,因為它已經存在于用戶物件中。
然后在您的服務中,您可以使用許多映射器庫,如 ModelMapper、MapStruct、JMapper、Orika、Dozer 等,將您的 DTO 映射到作業物體。記得將上面創建的用戶物件也傳遞給映射器
只是一個示例(此示例使用模型映射器,但您可以使用您選擇的映射器):
public Work convertDtoToEntity( WorkDTO workDto, User user) {
this.modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
Work work = modelMapper.map(workDto, Work.class);
work.setSignTargetUser(user);
return work;
}
然后簡單地使用 workRepository 的 save 方法。
另外,回答您的第二個問題,永遠不應該為 DTO 類創建存盤庫。
PS:這只是撰寫服務的一種方式。您可能會發現其他一些方式更符合您的編碼風格。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/435143.html
