Гость
Форумы / Java [игнор отключен] [закрыт для гостей] / Spring boot & hibernate: помогите написать правильно @PostMapping / 15 сообщений из 15, страница 1 из 1
10.05.2019, 17:10
    #39811896
maxim.popov
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
Добрый день.

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

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

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

Инструктор:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
@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;
    }
}



Курс:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
@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;
}



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

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
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)
);



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

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
@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 запрос вот с таким содержимым:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
[
    {
        "name": "instructor3",
        "courses": [
            {
                "title": "course4"
            }
        ]
    },
    {
        "name": "instructor4",
        "courses": [
            {
                "title": "course5"
            },
            {
                "title": "course6"
            }
        ]
    }
]



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

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

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

Полный код можно посмотреть вот здесь .
...
Рейтинг: 0 / 0
11.05.2019, 10:39
    #39812005
artas
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
maxim.popov,

не использовал никогда маппер, но что-то мне подсказывает что просто по названию он не смапит, попробуйде в Джейсон инструктор_ид добавить. Либо, возможно смапит, если поле нейм в инструкторе сделать уникальным
...
Рейтинг: 0 / 0
11.05.2019, 13:48
    #39812056
maxim.popov
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
artas,

От сервера ответ приходи правильный: и для курсов, и для инструктора назначаются правильные id. Только в таблицу с курсами id инструкторов не пишутся. Почему, я понять не могу.
...
Рейтинг: 0 / 0
11.05.2019, 16:39
    #39812078
maxim.popov
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
Сейчас переписал @PostMapping вот так:

Код: java
1.
2.
3.
4.
5.
6.
7.
    @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);
    }



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

Кто подскажет, коректно и допустимо так писать контроллеры, или это натуральный говнокод?
...
Рейтинг: 0 / 0
11.05.2019, 17:26
    #39812083
Андрей Панфилов
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
...
Рейтинг: 0 / 0
11.05.2019, 19:58
    #39812107
maxim.popov
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
Андрей Панфилов,

Так как тогда правильно написать @PostMapping для двунаправленной связи @OneToMany?
...
Рейтинг: 0 / 0
11.05.2019, 20:27
    #39812111
Андрей Панфилов
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
maxim.popov,

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

и априори ни одна из них из коробки консистентность двунаправленных связей не поддерживает: webmvc и mapstruct вообще про эту концепцию ничего не знают и там нужно руками делать, т.е. либо писать код в webmvc как вы сделали, либо @AfterMapping в mapstruct, а в хибернейте оно по-умолчанию выключено (включается либо при компиляции либо в рантайме через указание org.hibernate.jpa.AvailableSettings#ENHANCER_ENABLE_ASSOCIATION_MANAGEMENT в свойствах JPA у EMF) - в каком месте это делать решать вам, но сама по себе идея делать двунаправленные связи изначально плохая, со стороны хибера оно полезно только в двух случаях:
- кеширование данных на уровне сессии
- возможность писать более сложные запросы на JPQL и производных
а так оно только вредит
...
Рейтинг: 0 / 0
12.05.2019, 10:29
    #39812167
maxim.popov
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
Андрей Панфилов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 указал в первом посте.

Как бы вы решали данную задачу?
...
Рейтинг: 0 / 0
12.05.2019, 11:12
    #39812171
Андрей Панфилов
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
maxim.popovКак бы вы решали данную задачу?Делать в хибере или mapstruct не особо правильно, потому как будет проблема с безопасностью: как только ваш mapstruct научится обновлять сущности, получится так, что через v1/instructor можно будет у существующих курсов перебивать инструктора - это неправильно. Наиболее предпочтительный вариант - это когда мы с инструктором работаем через один endpoint, а с курсами через другой, т.е. в вашем случае получится два запроса к серверу, если так нельзя, то лично у меня был бы еще один слой, независящий от http.
...
Рейтинг: 0 / 0
12.05.2019, 11:28
    #39812174
maxim.popov
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
Андрей Панфиловmaxim.popovКак бы вы решали данную задачу?Делать в хибере или mapstruct не особо правильно, потому как будет проблема с безопасностью: как только ваш mapstruct научится обновлять сущности, получится так, что через v1/instructor можно будет у существующих курсов перебивать инструктора - это неправильно. Наиболее предпочтительный вариант - это когда мы с инструктором работаем через один endpoint, а с курсами через другой, т.е. в вашем случае получится два запроса к серверу, если так нельзя, то лично у меня был бы еще один слой, независящий от http.

Андрей, а можете подробнее рассказать про способ решения с еще одним слоем, который не зависит от http. Не очень понимаю, как это реализовать.
...
Рейтинг: 0 / 0
12.05.2019, 11:45
    #39812178
Андрей Панфилов
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
maxim.popov,

ну вот есть у вас уже InstructorService, пусть он из DTO преобразует и сохраняет как нужно
...
Рейтинг: 0 / 0
12.05.2019, 12:33
    #39812191
maxim.popov
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
Андрей Панфилов,

Всё, понял.

Вот этот кусок:
Код: java
1.
2.
3.
4.
...
instructors.forEach(
                instructor -> instructor.getCourses().forEach(course -> course.setInstructor(instructor)));
...



перенести на уровень сервиса.
...
Рейтинг: 0 / 0
12.05.2019, 12:52
    #39812197
alex55555
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
maxim.popovВсё, понял.

Вот этот кусок:
Код: java
1.
2.
3.
4.
...
instructors.forEach(
                instructor -> instructor.getCourses().forEach(course -> course.setInstructor(instructor)));
...


Ну раз понял, тогда можно бы ответит на вопрос - а что делает данный код?
...
Рейтинг: 0 / 0
12.05.2019, 13:23
    #39812205
maxim.popov
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
alex55555maxim.popovВсё, понял.

Вот этот кусок:
Код: java
1.
2.
3.
4.
...
instructors.forEach(
                instructor -> instructor.getCourses().forEach(course -> course.setInstructor(instructor)));
...


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

Назначает каждому курсу соответствующего инструктора.
...
Рейтинг: 0 / 0
13.05.2019, 12:14
    #39812452
alex55555
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring boot & hibernate: помогите написать правильно @PostMapping
maxim.popovНазначает каждому курсу соответствующего инструктора.
А откуда берётся информация для назначения?

ЗЫ. Интересно, сколько вопросов понадобится, что бы чел. понял очевидное?
...
Рейтинг: 0 / 0
Форумы / Java [игнор отключен] [закрыт для гостей] / Spring boot & hibernate: помогите написать правильно @PostMapping / 15 сообщений из 15, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]