spring DTO到实体的转换:在服务或控制器中,对于多对一拥有方,如果服务接受DTO

a11xaf1n  于 2023-06-04  发布在  Spring
关注(0)|答案(1)|浏览(197)

假设我有一个简单的应用程序,其中包含这样的实体:

Dog.java

@Entity(name = "dogs")
public class Dog  {
    @Id
    @SequenceGenerator(name = "dog_sequence",
                            sequenceName = "dog_sequence",
                            allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
                            generator = "dog_sequence")
    private Long id;
  
    @Column(unique = true)
    private String name;

    // getters, setters, constructors
}

Command.java

@Entity(name = "commands")
public class Command  {
    @Id
    @SequenceGenerator(name = "command_sequence",
                            sequenceName = "command_sequence",
                            allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
                            generator = "command_sequence")
    private Long id;
  
    @Column(unique = true)
    private String name;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "dog_id", nullable = false)
    private Dog dog;

    // getters, setters, constructors
}

所以一只狗可以知道几个命令,每个命令对一只狗来说都是唯一的(只属于一只狗)。
我有一个视图(网站页面),允许管理员添加新的命令,每只狗,也改变狗的名字。
完成后,视图将发送一个POST请求到/api/dog/edit,JSON如下所示:

{
  "commands": ["some command name", "another command name"],
  "id": 1,
  "name": "Updated Dog Name"
}

现在,由于Dog实体不包含List<Command>字段,控制器无法将所有JSONMap到Dog实体,因此我创建了一个DogDto

DogDto.java

public class DogDto  {
  private Long id;
  private String name;
  private List<CommandDto> commands;  
  

  // getters, setters, constructors
}

CommandDto

CommandDto.java

public class CommandDto  {
  private Long id;
  private String name;
  private DogDto dog;

  // getters, setters, constructors
}

现在我的控制器可以将从视图传递的JSONMap到DogDto

@RestController
@RequestMapping(path = "api/dog")
public class DogController {
    ...

    @PostMapping("edit")
    @CrossOrigin
    public ResponseEntity<DogDto> editDog(@RequestBody DogDto dogDto)  {
      ...
      Dog dog = mapper.map(dogDto, Dog.class);
      List<Command> commands = new ArrayList<>();
      for (CommandDto commandDto: dogDto.getCommands())  {
        Command command = mapper.map(commandDto, Command.class);
        commands.add(command);
      }
      ...
    }
}

现在我可以将DogDtoMap到DogCommand实体。但是什么才是坚持下去的最好方法呢?目前我的DogService

public boolean editDog(Dog dog) {...}

方法接受一个狗,尝试通过ID查找它:

Dog foundDog = this.dogRepository.findById(dog.getId());

如果找到了狗,则更新其名称字段并执行this.dogRepository.save(foundDog);
但是我也需要保存命令。Dog实体不包含命令列表。因此,我需要将editDog方法签名更改为boolean editDog(DogDto dog),或者在控制器中这样做:

for (Command command: commands)  {
  command.setDog(dogFoundById);
  this.commandService.createCommand(command);
}

我不知道该怎么办。
我也一直在想,如果我有一个更复杂的结构,有更多的嵌套实体:

public class C  {
  ...
  @ManyToOne
  private B b;
}

最佳设计决策是否会有所不同?

fruv7luv

fruv7luv1#

一种方法是使用业务对象。
我们总是希望能够自由地更改域对象,而不会影响视图对象。这意味着域实体可以随着业务需求开始而发展,而不会改变API的使用者。
这里的约束是控制器总是与API的客户端交换DTO,并且存储库调用总是处理域对象。当在对象上执行业务逻辑时,您通常不希望选择其中一个。两种选择都不合适。

相关问题