1

Тема: Питання щодо створення DTO

Привіт.
В мене є сутність і відповідна до неї табличка в БД (скажімо, classA) зі своїми специфічними полями і всякими контактними полями.

Мені потрібно контактні дані винести в окремий клас Contact і зробити окрему табличку (в 1 сутності багато контактів). Це вже зроблено, але я тепер не знаю як отримувати дані на вебі. Наприклад, раніше при створені цього об'єкта користувач просто заповнював ці поля (емейл, адреса, телефон), вони разом з іншими параметрами приходили в пост-запиті і аналогічні параметри були в конструкторі dto. Тепер потрібно щоб на вебі була кнопочка "додати ще один контакт", яка знову створить цих 3 інпути.

Тобто, мені не відомо скільки параметрів прийде пост-запитом після заповнення форми. Була думка в контрукторі dto об'єкта мого класу приймати var-arg стрінгів і потім вже перетворювати їх в Contact, але, зрозуміло, що це не підходить, бо повинна бути можливість заповнити наприклад додаткове поле з тел і залишити пустим адресу.

Скажіть хтось, як це правильно робиться?

2

Re: Питання щодо створення DTO

Було би непогано трохи коду побачити, то може б і не джавісти подивилися б. А так - ну можна спробувати застосувати "перевантаження конструкторів" або шаблон-будівник - https://en.wikipedia.org/wiki/Builder_pattern, або, дивлячись, як там у вас воно реалізовано, в пост-запит пихати порожні рядки (тобто, якщо та адреса порожня, то переслати порожній рядок, а не просто не пересилати нічого.)

3

Re: Питання щодо створення DTO

Було би непогано трохи коду побачити

Спрощено все якось так:
ClassA:

Прихований текст
@Entity
@Table(name = "classA")
public class ClassA implements Serializable {
    private Integer classAId;
    //some other fields
    private String email;
    private String phone;
    private String address;

    public ClassA() {
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "classA_id")
    public Integer getclassAId() {
        return classAId;
    }
    public void setclassAId(Integer classAId) {
        this.classAId = classAId;
    }
    //some other getters and setters

    @Column(name = "email")
    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Column(name = "phone")
    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Column(name = "address")
    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

DTO:

Прихований текст
public class ClassAPageDto {
    private Integer classAId;
    private String email;
    private String phone;
    private String address;

    public ClassAPageDto(Integer classAId, //other fields
                         String email, String phone, String address) {
        this.classAId = classAId;
        //other
        this.email = email;
        this.phone = phone;
        this.address = address;
    }

Controller:

Прихований текст
@RestController
@RequestMapping(value = "/restful/4example")
public class ClassAController {
    @Autowired
    ClassAService classAService;

    @RequestMapping(value = "/", method = RequestMethod.POST)
    public ResponseEntity<ClassAPageDTO>> createClassA(@RequestBody ClassAPageDTO classAPageDTO) {
        Resource<ClassAPageDTO> classAResource;
        ClassA classA = ClassAPageDTOMapper.getInstance().getClassAEntityFromDto(ClassAPageDTO);
        try {
            classAService.saveClassA(classA);
            ClassAPageDTO classAPageDto = classAPageDtoMapper.getInstance().mapClassAEntityToDto(classA.getClassAId(), classA);
            classAResource = addResourceLinkToclassA(classAPageDto);
        } catch (EntityNotFoundException e) {
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return new ResponseEntity<>(classAResource, HttpStatus.OK);
    }


    private Resource<classAPageDTO> addResourceLinkToclassA(classAPageDTO classA) throws EntityNotFoundException {
        //some logic
        return  Resource<classAPageDTO>;
    }
}

Entity to DTO Mapper:

Прихований текст
public class ClassAPageDtoMapper {
    private static ClassAPageDtoMapper classAPageDtoMapper = new ClassAPageDtoMapper();

    private ClassAPageDtoMapper(){
    }
    public  static ClassAPageDtoMapper getInstance(){
        return classAPageDtoMapper;
    }
    
    public ClassAPageDTO mapClassAEntityToDto(Integer classAId, ClassA classA){
        ClassAPageDTO classAPageDTO = new ClassAPageDTO();
        if (classA != null) {
            classAPageDTO.setClassAId(classA.getClassAId());
            //other
            classAPageDTO.setEmail(classA.getEmail());
            classAPageDTO.setPhone(classA.getPhone());
            classAPageDTO.setAddress(classA.getAddress());
        }
        return classAPageDTO;
    }

    public ClassA getclassAEntityFromDto(ClassAService classAService, ClassAPageDTO classAPageDTO) {
        if (classAPageDTO == null) logger.debug("empty request");
        ClassA classA;
        Integer classAId = classAPageDTO.getclassAId();
        if (classAId !=null)
            classA = classAService.findOneClassAById(classAPageDTO.getClassAId());
        else classA = new ClassA();
        //other
        classA.setEmail(classAPageDTO.getEmail());
        classA.setPhone(classAPageDTO.getPhone());
        classA.setAddress(classAPageDTO.getAddress());
        if (classAId==null) classAService.saveClassA(classA);
        return classA;
    }
}
Подякували: leofun011

4

Re: Питання щодо створення DTO

Якщо я добре зрозумів, то Ви хочете щоб у людини було кілька можливих контактів. Це дуже легко мапається JPA.

Спрочатку створюємо клас, що буде репрезентувати контакт:

import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
public class Contact {

    @Column(name = "email")
    private String email;

    @Column(name = "phone")
    private String phone;

    @Column(name = "address")
    private String address;

    public Contact(String email, String phone, String address) {
        this.email = email;
        this.phone = phone;
        this.address = address;
    }

    public static Contact of(String email, String phone, String address) {
        return new Contact(email, phone, address);
    }
}

А тепер його вбудовуємо у КлассА:

@Entity
public class ClassA {

    @Id
    @Column(name = "classa_id")
    private long classaId;

    @ElementCollection
    @CollectionTable(name = "classa_contacts", joinColumns = @JoinColumn(name = "classa_id")) // контакти будуть у окремій таблиці classa_contacts
    private List<Contact> contacts;

    public List<Contact> contacts() {
        return contacts;
    }
}

DTO:

public class ContactSpecification {
    @NotNull
    @Size(min = 1)
    public String email;

    @NotNull
    @Size(min = 1)
    public String phone;

    @Size(min = 1)
    public String address;
}

Endpoint (вибачаюся, зі спрінговими анотаціями не дуже знайом. Але я думаю буде зрозуміло.):

@Resource
@Path("/v1/classas")
public class ClassAResource {

    private ClassARepository classARepository;

    @Path("/{classaId}/contacts")
    @POST
    public Response addContact(@PathParam("classaId") @NotNull  long classaId,
                               @Valid ContactSpecification spec) {

        ClassA clz = classARepository.findOne(classaId).orElseThrow(NotFoundException::new);

        clz.contacts().add(Contact.of(spec.email, spec.phone, spec.address));
        
        return Response.ok().build();
    }
}