Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Java Новый топик    Ответить
 Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
maxim.popov
Member

Откуда:
Сообщений: 10
Добрый день.

Пишу обычное CRUD приложение. Не знаю как правильно написать пост запрос.

Само приложение следующее: есть две сущности Instructor и Course. Один инструктор может вести несколько курсов. Нужно обрабатывать пост запрос, в котором перечислены инструктора и курсы, которые они ведут.

Вот сами сущности:

Инструктор:
+

@Entity
@Table(name = "instructor")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Instructor {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", columnDefinition = "serial")
    private Long id;

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

    @JsonManagedReference
    @OneToMany(mappedBy = "instructor",
            cascade = {
                    CascadeType.DETACH,
                    CascadeType.MERGE,
                    CascadeType.PERSIST,
                    CascadeType.REFRESH})
    private List<Course> courses = new ArrayList<>();

    public Instructor(String name) {
        this.name = name;
    }
}


Курс:
+

@Entity
@Table(name = "course")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Course {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", columnDefinition = "serial")
    private Long id;

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

    @JsonBackReference
    @ManyToOne(cascade = {
            CascadeType.PERSIST,
            CascadeType.MERGE,
            CascadeType.REFRESH,
            CascadeType.DETACH})
    @JoinColumn(name = "instructor_id")
    private Instructor instructor;
}


Вот структура базы данных:
+

create table instructor
(
    id   serial      not null,
    name varchar(20) not null,
    primary key (id)
);

create table course
(
    id            serial       not null,
    title         varchar(128) not null,
    instructor_id int default null,
    primary key (id),
    foreign key (instructor_id) references instructor (id)
);


Вот рест контроллер:
+

@RestController
@RequestMapping("v1/instructor")
public class InstructorController {

    private final InstructorService instructorService;
    private final InstructorMapper instructorMapper;

    public InstructorController(InstructorService instructorService, InstructorMapper instructorMapper) {
        this.instructorService = instructorService;
        this.instructorMapper = instructorMapper;
    }

    @GetMapping
    public ResponseEntity<Iterable<Instructor>> getAll() {
        return new ResponseEntity<>(instructorService.getAll(), HttpStatus.OK);
    }

    @GetMapping("{id}")
    public ResponseEntity<Instructor> getInstructor(@PathVariable Long id) throws Exception {
        return instructorService.getById(id)
                .map(i -> new ResponseEntity<>(i, HttpStatus.OK))
                .orElseThrow(() -> new Exception("Not found instructor with id: " + id));
    }

    @PostMapping
    public ResponseEntity<Iterable<Instructor>> postInstructor(@RequestBody Iterable<InstructorDto> dto) {
        Iterable<Instructor> instructors = instructorMapper.iterableToIterable(dto);
        return new ResponseEntity<>(instructorService.saveAll(instructors), HttpStatus.CREATED);
    }
}


Делаю POST запрос вот с таким содержимым:
+

[
    {
        "name": "instructor3",
        "courses": [
            {
                "title": "course4"
            }
        ]
    },
    {
        "name": "instructor4",
        "courses": [
            {
                "title": "course5"
            },
            {
                "title": "course6"
            }
        ]
    }
]


Ответ от сервера приходит правильный, т.е. для каждого инструктора и курса назначаются id, в логах hibernate пишет, что происходит вставка в таблицы intructor и course.

Но сами курсы в базу данных записываются не полностью: сохраняет название курса, a instructor_id нет, хотя по идее должен.

Кто подскажет, почему такой пост запрос не пишет в базу данных информацию по курсам?

Полный код можно посмотреть вот здесь.
10 май 19, 17:10    [21882173]     Ответить | Цитировать Сообщить модератору
 Re: Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
artas
Member

Откуда: Киев сити
Сообщений: 1015
maxim.popov,

не использовал никогда маппер, но что-то мне подсказывает что просто по названию он не смапит, попробуйде в Джейсон инструктор_ид добавить. Либо, возможно смапит, если поле нейм в инструкторе сделать уникальным
11 май 19, 10:39    [21882383]     Ответить | Цитировать Сообщить модератору
 Re: Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
maxim.popov
Member

Откуда:
Сообщений: 10
artas,

От сервера ответ приходи правильный: и для курсов, и для инструктора назначаются правильные id. Только в таблицу с курсами id инструкторов не пишутся. Почему, я понять не могу.
11 май 19, 13:48    [21882467]     Ответить | Цитировать Сообщить модератору
 Re: Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
maxim.popov
Member

Откуда:
Сообщений: 10
Сейчас переписал @PostMapping вот так:

    @PostMapping
    public ResponseEntity<Iterable<Instructor>> postInstructor(@RequestBody Iterable<InstructorDto> dto) {
        Iterable<Instructor> instructors = instructorMapper.iterableToIterable(dto);
        instructors.forEach(
                instructor -> instructor.getCourses().forEach(course -> course.setInstructor(instructor)));
        return new ResponseEntity<>(instructorService.saveAll(instructors), HttpStatus.CREATED);
    }


Т.е. "ручками" выставляю инструктора для каждого курса.

Кто подскажет, коректно и допустимо так писать контроллеры, или это натуральный говнокод?
11 май 19, 16:39    [21882509]     Ответить | Цитировать Сообщить модератору
 Re: Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
Андрей Панфилов
Member

Откуда: Москва > Melbourne
Сообщений: 3256
maxim.popov,

http://devdoc.net/javaweb/hibernate/Hibernate-5.1.0/userGuide/en-US/html/ch03.html#d5e249
11 май 19, 17:26    [21882521]     Ответить | Цитировать Сообщить модератору
 Re: Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
maxim.popov
Member

Откуда:
Сообщений: 10
Андрей Панфилов,

Так как тогда правильно написать @PostMapping для двунаправленной связи @OneToMany?
11 май 19, 19:58    [21882551]     Ответить | Цитировать Сообщить модератору
 Re: Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
Андрей Панфилов
Member

Откуда: Москва > Melbourne
Сообщений: 3256
maxim.popov,

я ваш вопрос не понимаю. Вы используете три технологии:
- webmvc
- mapstruct
- hibernate

и априори ни одна из них из коробки консистентность двунаправленных связей не поддерживает: webmvc и mapstruct вообще про эту концепцию ничего не знают и там нужно руками делать, т.е. либо писать код в webmvc как вы сделали, либо @AfterMapping в mapstruct, а в хибернейте оно по-умолчанию выключено (включается либо при компиляции либо в рантайме через указание org.hibernate.jpa.AvailableSettings#ENHANCER_ENABLE_ASSOCIATION_MANAGEMENT в свойствах JPA у EMF) - в каком месте это делать решать вам, но сама по себе идея делать двунаправленные связи изначально плохая, со стороны хибера оно полезно только в двух случаях:
- кеширование данных на уровне сессии
- возможность писать более сложные запросы на JPQL и производных
а так оно только вредит
11 май 19, 20:27    [21882557]     Ответить | Цитировать Сообщить модератору
 Re: Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
maxim.popov
Member

Откуда:
Сообщений: 10
Андрей Панфилов
maxim.popov,

я ваш вопрос не понимаю. Вы используете три технологии:
- webmvc
- mapstruct
- hibernate

и априори ни одна из них из коробки консистентность двунаправленных связей не поддерживает: webmvc и mapstruct вообще про эту концепцию ничего не знают и там нужно руками делать, т.е. либо писать код в webmvc как вы сделали, либо @AfterMapping в mapstruct, а в хибернейте оно по-умолчанию выключено (включается либо при компиляции либо в рантайме через указание org.hibernate.jpa.AvailableSettings#ENHANCER_ENABLE_ASSOCIATION_MANAGEMENT в свойствах JPA у EMF) - в каком месте это делать решать вам, но сама по себе идея делать двунаправленные связи изначально плохая, со стороны хибера оно полезно только в двух случаях:
- кеширование данных на уровне сессии
- возможность писать более сложные запросы на JPQL и производных
а так оно только вредит


Вопрос очень простой.

Есть две сущности, между которыми двунаправленная связь oneToMany.

Нужно написать @PostMapping, который на вход получает json, и пишет сущности в базу данных. База данных postgres, используем orm hibernate.
Пример json`а и ссылку на git указал в первом посте.

Как бы вы решали данную задачу?
12 май 19, 10:29    [21882673]     Ответить | Цитировать Сообщить модератору
 Re: Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
Андрей Панфилов
Member

Откуда: Москва > Melbourne
Сообщений: 3256
maxim.popov
Как бы вы решали данную задачу?
Делать в хибере или mapstruct не особо правильно, потому как будет проблема с безопасностью: как только ваш mapstruct научится обновлять сущности, получится так, что через v1/instructor можно будет у существующих курсов перебивать инструктора - это неправильно. Наиболее предпочтительный вариант - это когда мы с инструктором работаем через один endpoint, а с курсами через другой, т.е. в вашем случае получится два запроса к серверу, если так нельзя, то лично у меня был бы еще один слой, независящий от http.
12 май 19, 11:12    [21882679]     Ответить | Цитировать Сообщить модератору
 Re: Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
maxim.popov
Member

Откуда:
Сообщений: 10
Андрей Панфилов
maxim.popov
Как бы вы решали данную задачу?
Делать в хибере или mapstruct не особо правильно, потому как будет проблема с безопасностью: как только ваш mapstruct научится обновлять сущности, получится так, что через v1/instructor можно будет у существующих курсов перебивать инструктора - это неправильно. Наиболее предпочтительный вариант - это когда мы с инструктором работаем через один endpoint, а с курсами через другой, т.е. в вашем случае получится два запроса к серверу, если так нельзя, то лично у меня был бы еще один слой, независящий от http.


Андрей, а можете подробнее рассказать про способ решения с еще одним слоем, который не зависит от http. Не очень понимаю, как это реализовать.
12 май 19, 11:28    [21882684]     Ответить | Цитировать Сообщить модератору
 Re: Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
Андрей Панфилов
Member

Откуда: Москва > Melbourne
Сообщений: 3256
maxim.popov,

ну вот есть у вас уже InstructorService, пусть он из DTO преобразует и сохраняет как нужно
12 май 19, 11:45    [21882689]     Ответить | Цитировать Сообщить модератору
 Re: Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
maxim.popov
Member

Откуда:
Сообщений: 10
Андрей Панфилов,

Всё, понял.

Вот этот кусок:
...
instructors.forEach(
                instructor -> instructor.getCourses().forEach(course -> course.setInstructor(instructor)));
...


перенести на уровень сервиса.
12 май 19, 12:33    [21882705]     Ответить | Цитировать Сообщить модератору
 Re: Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
alex55555
Member

Откуда:
Сообщений: 2129
maxim.popov
Всё, понял.

Вот этот кусок:
...
instructors.forEach(
                instructor -> instructor.getCourses().forEach(course -> course.setInstructor(instructor)));
...

Ну раз понял, тогда можно бы ответит на вопрос - а что делает данный код?
12 май 19, 12:52    [21882714]     Ответить | Цитировать Сообщить модератору
 Re: Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
maxim.popov
Member

Откуда:
Сообщений: 10
alex55555
maxim.popov
Всё, понял.

Вот этот кусок:
...
instructors.forEach(
                instructor -> instructor.getCourses().forEach(course -> course.setInstructor(instructor)));
...

Ну раз понял, тогда можно бы ответит на вопрос - а что делает данный код?


Назначает каждому курсу соответствующего инструктора.
12 май 19, 13:23    [21882727]     Ответить | Цитировать Сообщить модератору
 Re: Spring boot & hibernate: помогите написать правильно @PostMapping  [new]
alex55555
Member

Откуда:
Сообщений: 2129
maxim.popov
Назначает каждому курсу соответствующего инструктора.

А откуда берётся информация для назначения?

ЗЫ. Интересно, сколько вопросов понадобится, что бы чел. понял очевидное?
13 май 19, 12:14    [21883213]     Ответить | Цитировать Сообщить модератору
Все форумы / Java Ответить