@i_yan

Spring JPA, как обновить данные в модальном окне?

У меня есть две страницы, но которых вывожу информацию о турах: на главной и по адресу /admin/tourList.
На первой странице через модальное окно я могу спокойно редактировать поля и сохранять.
<a th:href="@{findOne/(id=${tour.id})}" class="btn btn-primary eBtn"
                       sec:authorize="hasAuthority('ADMIN')">Редактировать тур</a>

$(document).ready(function () {
    $('.nBtn, .card-deck .eBtn').on('click', function (event) {
        event.preventDefault();
        var href = $(this).attr('href');
        var text = $(this).text();
        if (text=='Редактировать тур') {
            $.get(href, function (tour, status) {
                $('.myForm #id').val(tour.id);
                $('.myForm #title').val(tour.title);
                $('.myForm #description').val(tour.description);
                $('.myForm #startTime').val(tour.startTime);
                $('.myForm #endTime').val(tour.endTime);
                $('.myForm #price').val(tour.price);
            });
            $('.myForm #exampleModal').modal();
        } else {
            $('.myForm #id').val('');
            $('.myForm #title').val('');
            $('.myForm #description').val('');
            $('.myForm #startTime').val('');
            $('.myForm #endTime').val('');
            $('.myForm #price').val('');
            $('.myForm #exampleModal').modal();
        }
    });
});

@Controller
public class TourController {
    @Autowired
    private TourRepository tourRepository;
    @PostMapping("/save")
    public String save(@ModelAttribute(name = "tour") Tour tour) {
        tourRepository.save(tour);
        return "redirect:/";
    }
    @PostMapping("/admin/saveTour")
    public String saveTourInModal(@ModelAttribute(name = "tour") Tour tour) {
        tourRepository.save(tour);
        return "redirect:/admin/tourList";
    }
    @GetMapping("/")
    public String mainPageShowTours(Model model) {
        model.addAttribute("tours",tourRepository.findAll());
        return "home/main";
    }
 @GetMapping("/findOne")
    @ResponseBody
    public Tour findOne(Integer id) {
        return tourRepository.findById(id).get();
    }
}

Для такой же функции, только на отдельной странице всех туров в виде таблице, я создал ссылку
<a th:href="@{findOne/(id=${tour.id})}" class="btn btn-primary qBtn">Edit</a>
уже на другое идентичное окно, только с др. id
<div class="myForm3" th:fragment="modalForChangeTour">
    <form th:action="@{/admin/saveTour}" method="post">
        <div class="modal fade" id="exampleModal3" tabindex="-1" role="dialog"
             aria-labelledby="exampleModalLabel3"
             aria-hidden="true">
            <div class="modal-dialog" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title" id="exampleModalLabel3">Update tour or Create3</h5>
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <div class="modal-body">
                        <div class="form-group">
                            <label for="id3" class="col-form-label">ID:</label>
                            <input type="text" class="form-control" id="id3" name="id3" value=""/>
                        </div>
                        <div class="form-group">
                            <label for="title3" class="col-form-label">Title:</label>
                            <input type="text" class="form-control" id="title3" name="title3" value=""/>
                        </div>
                        <div class="form-group">
                            <label for="description3" class="col-form-label">description:</label>
                            <input type="text" class="form-control" id="description3" name="description3" value=""/>
                        </div>
                        <div class="form-group">
                            <label for="startTime3" class="col-form-label">startTime:</label>
                            <input type="text" class="form-control" id="startTime3" name="startTime3" value=""/>
                        </div>
                        <div class="form-group">
                            <label for="endTime3" class="col-form-label">endTime:</label>
                            <input type="text" class="form-control" id="endTime3" name="endTime3" value=""/>
                        </div>
                        <div class="form-group">
                            <label for="price3" class="col-form-label">price:</label>
                            <input type="text" class="form-control" id="price3" name="price3" value=""/>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                        <input type="submit" class="btn btn-primary" value="Save or Create"/>
                    </div>
                </div>
            </div>
        </div>
    </form>
</div>

и JQuery
$(document).ready(function () {
        $('table .qBtn').on('click', function (event) {
            event.preventDefault();
            var href = $(this).attr('href');
            var text = $(this).text();
            if (text=='Edit') {
                $.get(href, function (tour, status) {
                    $('.myForm3 #id3').val(tour.id);
                    $('.myForm3 #title3').val(tour.title);
                    $('.myForm3 #description3').val(tour.description);
                    $('.myForm3 #startTime3').val(tour.startTime);
                    $('.myForm3 #endTime3').val(tour.endTime);
                    $('.myForm3 #price3').val(tour.price);
                });
                $('.myForm3 #exampleModal3').modal();
            } else {
                $('.myForm3 #id3').val('');
                $('.myForm3 #title3').val('');
                $('.myForm3 #description3').val('');
                $('.myForm3 #startTime3').val('');
                $('.myForm3 #endTime3').val('');
                $('.myForm3 #price3').val('');
                $('.myForm3 #exampleModal3').modal();
            }
        });       
    });

Само модальное окно вызывается и заполняется данными объекта. После нажатия на кнопку сохранения, у меня стало выводить исключение org.hibernate.exception.ConstraintViolationException: could not execute statement, и что поля бд title и description не могут быть null. В бд данные не записывались. Я снял ограничения с этих полей в бд, и теперь у меня после измения полей в модальном окне сохраняются пустые записи.
При изменении объекта через модальное окно на главной странице вывод с Hibernate:
Hibernate: select tour0_.code as code1_3_0_, tour0_.description as descript2_3_0_, tour0_.end_time as end_time3_3_0_, tour0_.image as image4_3_0_, tour0_.price as price5_3_0_, tour0_.start_time as start_ti6_3_0_, tour0_.title as title7_3_0_ from tour tour0_ where tour0_.code=?
Hibernate: select tour0_.code as code1_3_0_, tour0_.description as descript2_3_0_, tour0_.end_time as end_time3_3_0_, tour0_.image as image4_3_0_, tour0_.price as price5_3_0_, tour0_.start_time as start_ti6_3_0_, tour0_.title as title7_3_0_ from tour tour0_ where tour0_.code=?
Hibernate: update tour set description=?, end_time=?, image=?, price=?, start_time=?, title=? where code=?
Hibernate: select tour0_.code as code1_3_, tour0_.description as descript2_3_, tour0_.end_time as end_time3_3_, tour0_.image as image4_3_, tour0_.price as price5_3_, tour0_.start_time as start_ti6_3_, tour0_.title as title7_3_ from tour tour0_


А со страницы /admin/tourList
Hibernate: select tour0_.code as code1_3_, tour0_.description as descript2_3_, tour0_.end_time as end_time3_3_, tour0_.image as image4_3_, tour0_.price as price5_3_, tour0_.start_time as start_ti6_3_, tour0_.title as title7_3_ from tour tour0_
Hibernate: select tour0_.code as code1_3_0_, tour0_.description as descript2_3_0_, tour0_.end_time as end_time3_3_0_, tour0_.image as image4_3_0_, tour0_.price as price5_3_0_, tour0_.start_time as start_ti6_3_0_, tour0_.title as title7_3_0_ from tour tour0_ where tour0_.code=?
Hibernate: insert into tour (description, end_time, image, price, start_time, title) values (?, ?, ?, ?, ?, ?)
Hibernate: select tour0_.code as code1_3_, tour0_.description as descript2_3_, tour0_.end_time as end_time3_3_, tour0_.image as image4_3_, tour0_.price as price5_3_, tour0_.start_time as start_ti6_3_, tour0_.title as title7_3_ from tour tour0_

Не могу понять уже долго, почему так происходит, ведь аналогично делаю, но во втором случае запись не обновляется. Возможно, что-то не так в контроллере?
  • Вопрос задан
  • 198 просмотров
Решения вопроса 1
@i_yan Автор вопроса
В конечном итоге сделал кнопку, ссылающую на первое модальное окно, js файл приленил уже в списке туров в админ панеле. Переделал контроллер, чтобы он перенаправлял на /admin/tourList.. Т.е. теперь при изменении полей на гл.странице или админке, будет перенаправлять на общую таблицу, что в принципе и нужно было (т.к. на главной со временем кнопку и уберу)
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
azerphoenix
@azerphoenix Куратор тега Spring
Java Software Engineer
th:href="@{findOne/(id=${tour.id})}"
Глянув на этот участок кода могу сказать следующее - не передавайте саму сущность на фронт. Вместо этого используйте паттерн DTO. Т.е. для создания / обновления используйте DTO, который затем в сервисном слое маппите в entity и сохраняете в БД,
Для маппинга можно использовать разные либы.
Например, интерфейс Converter<S, T> или же либу MapStruct, ModelMapper и др.
Также можно использовать BeanUtils.copyProperties() например, для обновления сущности.

@ResponseBody
    public Tour findOne(Integer id) {
        return tourRepository.findById(id).get();
    }

Вместо get() лучше использовать например, orElseThrow() и выбросить исключение, которое впоследствиее можно при помощи ExceptionHandler обработать и вернуть корректный http ответ. Например, когда объект не найден (404)

@Autowired
    private TourRepository tourRepository;
    @PostMapping("/save")
    public String save(@ModelAttribute(name = "tour") Tour tour) {
        tourRepository.save(tour);
        return "redirect:/";
    }
    @PostMapping("/admin/saveTour")
    public String saveTourInModal(@ModelAttribute(name = "tour") Tour tour) {
        tourRepository.save(tour);
        return "redirect:/admin/tourList";
    }

Нет смысла в двух одинаковых методах внутри одного контроллера. Есть смысл вынести эндпоинты отвечающие за админку в отдельный контроллер.

Само модальное окно вызывается и заполняется данными объекта. После нажатия на кнопку сохранения, у меня стало выводить исключение org.hibernate.exception.ConstraintViolationException: could not execute statement, и что поля бд title и description не могут быть null.

Вопрос в том, почему они были null, если вы делаете сабмит формы. Попробуйте глянуть в консоли браузера во вкладке "Сеть", какие именно данные отправляются в бэк. Также в режиме дебага проверьте, что именно вы принимаете и сохраняете в БД.

Обратите внимание, что на одной странице вы делаете update
Hibernate: update tour set description=?, end_time=?, image=?, price=?, start_time=?, title=? where code=?

А на другой странице insert
Hibernate: insert into tour (description, end_time, image, price, start_time, title) values (?, ?, ?, ?, ?, ?)


Для более точного ответа нужен исходник проекта, ибо я не вижу кода сущности
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы