I'm creating a web application using Spring MVC, but all my POST request result in "The request sent by the client was syntactically incorrect". As an example, this is a search form:
<form id="projectsForm" action="#" th:action="#{/projects}" th:object="${projectsForm}" method="post">
<input type="hidden" th:field="*{page}" />
<div id="search">
<select id="expert" th:field="*{expert}">
<option value="">(select expert)</option>
<option th:each="expert : ${experts}"
th:value="${expert.id}"
th:text="${expert.firstName + ' ' + expert.lastName}"></option>
</select>
<select id="company" th:field="*{company}">
<option value="">(select company)</option>
<option th:each="company : ${companies}"
th:value="${company.id}"
th:text="${company.name}"></option>
</select>
<input type="text" id="query" th:field="*{query}" />
<button class="search" onclick="firstPage()">Search</button>
<button class="empty" onclick="empty()">Erase</button>
</div>
The form object class looks like this:
public class ProjectsForm {
private Expert expert;
private Company company;
private String query;
private Integer page = 0;
private Integer pages;
public Expert getExpert() {
return expert;
}
public void setExpert(Expert expert) {
this.expert = expert;
}
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getPages() {
return pages;
}
public void setPages(Integer pages) {
if (pages > 0 && page >= pages) {
page = pages - 1;
}
this.pages = pages;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
}
And this is the controller method:
#RequestMapping(value="/projects", method=RequestMethod.POST)
public String projectsPost(#ModelAttribute("projectsForm") ProjectsForm projectsForm, ModelMap model) {
sessionBean.setProjectsForm(projectsForm);
Page<Project> projectPage = projectService.findAll(projectsForm.getPage(), ProjectController.PAGESIZE, projectsForm.getExpert(), projectsForm.getCompany(), projectsForm.getQuery());
List<Project> projects = projectPage.getContent();
model.addAttribute("projects", projects);
projectsForm.setPage(projectPage.getNumber());
projectsForm.setPages(projectPage.getTotalPages());
model.addAttribute("projectsForm", projectsForm);
return "projects";
}
Chrome tells me the form data look like this:
page=0&expert=&company=&query=
Is there an obvious error, or is there any way I can diagnose this problem? Adding log4j.logger.org.springframework.web=DEBUG to log4j.properties didn't give me any more information. What also puzzles me is that the exact same code worked fine in a Spring Boot jar application.
Related
I am working on Java Spring project, and I have this code that allows me to edit specific Quote based on it's Id.
//Quote's details
#GetMapping("/profile/{id}")
public String blogDetailsId(#PathVariable(value="id") long id, Model model){
if(!quoteRepository.existsById(id)){
return "redirect:/profile";
}
Optional<Quote> post = quoteRepository.findById(id);
ArrayList<Quote> res = new ArrayList<>();
post.ifPresent(res::add);
model.addAttribute("post", res);
return "detail_quote";
}
#GetMapping("/profile/{id}/edit")
public String QuoteDetails(#PathVariable(value="id") long id, Model model) {
if(!quoteRepository.existsById(id)){
return "redirect:/profile";
}
Optional<Quote> post = quoteRepository.findById(id);
ArrayList<Quote> res = new ArrayList<>();
post.ifPresent(res::add);
model.addAttribute("post", res);
return "edit_quote";
}
//Save changes into database
#PostMapping("/profile/{id}/edit")
public String QuoteEdit(#PathVariable(value="id") long id, #RequestParam String quote, #RequestParam String author, #RequestParam int votes, Model model) {
Quote post = quoteRepository.findById(id).orElseThrow(); //orElseThrow is used to throw exception when ID is not found.
post.setAuthor(author);
post.setQuote(quote);
post.setVotes(votes);
quoteRepository.save(post);
return "redirect:/profile";
}
Model code:
#Entity
public class Quote {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id; //Generate unique ID for every quote automatically.
private String quote, author;
private int votes;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getQuote() {
return quote;
}
public void setQuote(String quote) {
this.quote = quote;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getVotes() {
return votes;
}
public void setVotes(int votes) {
this.votes = votes;
}
public Quote() {}
public Quote(String quote, String author, int votes){
this.author = author;
this.quote = quote;
this.votes = votes;
}
}
This works as expected, and I am able to change the details of any Quote. But when I try to change specifically number of Votes by pressing upvote button - it results in following error:
There was an unexpected error (type=Bad Request, status=400).
Required parameter 'votes' is not present.
org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'votes' for method parameter type int is not present
The code for Voting:
#PostMapping("/profile/{id}/upvote")
#ResponseBody
public String VoteUp(#PathVariable(value="id") long id, #RequestParam int votes, Model model) {
Quote post = quoteRepository.findById(id).orElseThrow();
post.setVotes(votes+1);
quoteRepository.save(post);
return "redirect:/profile";
}
The Button for Upvoting is stored inside 'detail_quote.html', which is functioning well, except for upvote button:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Quote's Details</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.3/dist/css/bootstrap.min.css" crossorigin="anonymous">
<body>
<header th:insert="blocks/header :: header"></header>
<div class = "container mt-5">
<div th:each="el : ${post}" class="alert alert-info mt-2">
<h3 th:text="${el.quote}"></h3>
<p th:text="${el.author}"></p>
<p><b>Votes: </b><span th:text="${el.votes}"></span></p>
<a th:href="'/profile/' + ${el.id} + '/edit'" class="btn btn-warning">Edit</a><br>
<form th:action="'/profile/' + ${el.id} + '/upvote'" method="post"><br>
<button class="btn btn-warning" type="submit">Upvote</button><br>
</form>
<form th:action="'/profile/' + ${el.id} + '/downvote'" method="post">
<button class="btn btn-warning" type="submit">Downvote</button>
</form>
<form th:action="'/profile/' + ${el.id} + '/remove'" method="post"><br>
<button class="btn btn-warning" type="submit">Remove Quote</button><br>
</form>
</div>
</div>
<div th:insert="blocks/footer :: footer"></div>
</body>
</html>
So if anyone can explain to me why does it work when I change everything and does not work when I'm just trying to increase votes by 1 - I will be very thankful.
According to your code sample i could not see the args votes defined in your thymeleaf template as post request , but it is truely defined in backEnd Application
#PostMapping("/profile/{id}/upvote")
#ResponseBody
public String VoteUp(#PathVariable(value="id") long id, #RequestParam int votes, Model model) {
Quote post = quoteRepository.findById(id).orElseThrow();
post.setVotes(votes+1);
quoteRepository.save(post);
return "redirect:/profile";
}
so that if you do a post request to url "/profile/{id}/upvote" and have no args with votes you will get error log
There was an unexpected error (type=Bad Request, status=400). Required parameter 'votes' is not present.
as suggestion ,i advice build the api as below sample
#PostMapping("/profile/{id}/upvote")
#ResponseBody
public String VoteUp(#PathVariable(value="id") long id, #RequestParam(required = false) Integer votes, Model model) {
Quote post = quoteRepository.findById(id).orElseThrow();
votes == null ? post.getVotes():votes;
post.setVotes(votes+1);
quoteRepository.save(post);
return "redirect:/profile";
}
i´m working on a simple Spring-Boot CRUD app, where I´m trying to use the CrudRepository for updating or creating new entity instances and save them, but I keep getting the (type=Bad Request, status=400) error, but I don´t really have any validation, I don´t know were the error could be, I´m using old version of spring-boot for old Java compatibility because of the server I´ll be deploying the app.
This is my Entity
#Entity
#Table(name = "AAA_TEST_DM_DATA")
public class DataDM implements Serializable{
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_TIENDA")
private Tienda tienda;
#Column(name = "NIVEL_NSE")
private String nse;
#Column(name = "GENERADOR_PRINCIPAL")
private String generadorUno;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "DATA_ID")
private Long id;
This is my Dao or repo where I extend the CrudRepository
public interface IDataDMDao extends CrudRepository <DataDM, Long> {
DataDM findByTienda(Tienda tienda);
}
Here is my service
public interface IDataDMService {
public List<DataDM> findAll();
public DataDM findOne(Long id);
public void save(DataDM dataDM);
public boolean exists(Tienda tienda);
public DataDM findDm(Tienda tienda);
}
Here is the service implementation
#Service
public class IDataDMServiceImp implements IDataDMService {
#Autowired
private IDataDMDao dataDMDao;
#Override
#Transactional(readOnly = true)
public List<DataDM> findAll(){
return (List<DataDM>) dataDMDao.findAll();
}
#Override
#Transactional(readOnly = true)
public DataDM findOne(Long id){
return dataDMDao.findOne(id);
}
#Override
#Transactional
public void save(DataDM data){
dataDMDao.save(data);
}
#Override
#Transactional(readOnly = true)
public boolean exists(Tienda tienda){
if (dataDMDao.findByTienda(tienda) == null){
return false;
} else {
return true;
}
}
#Override
#Transactional(readOnly = true)
public DataDM findDm(Tienda tienda){
return dataDMDao.findByTienda(tienda);
}
}
And here is the controller
#SessionAttributes("data")
public class DesController {
#Autowired
private ITiendaService tiendaService;
#Autowired
private IDataDMService dataService;
#Autowired
private ISesionTiendaService sesionService;
//LOOK AT ALL DATADM
#RequestMapping("/data")
public String dataList(Model model){
model.addAttribute("title", "Datos de tiendas");
model.addAttribute("dataList", dataService.findAll());
return "datas";
}
#RequestMapping("/captura")
public String form(Model model,
#RequestParam(value = "_paramsP_ID") String idsesion,
HttpServletRequest request){
Integer idses = Integer.parseInt(request.getParameter("_paramsP_ID"));
//get tiendaid based on idses
SesionTienda sesion = sesionService.findSesion(idses, "FLT_TIENDA");
Integer id = Integer.parseInt(sesion.getValor());
//get tienda based on given id, then checks if there is data for that tienda
Tienda tienda = tiendaService.findOne(id);
boolean check = dataService.exists(tienda);
DataDM data = null;
//if there is no data create new data entity for that tienda
if (check == false){
data = new DataDM();
data.setTienda(tienda);
//dataService.save(data);
model.addAttribute("data", data);
model.addAttribute("title", "Tiendas form");
return "form";
}
//if there is data, find it and pass it into the model
data = dataService.findDm(tienda);
model.addAttribute("data", data);
model.addAttribute("title", "Tiendas form");
return "form";
}
//Saves the DataDM entity from the form
#RequestMapping(value = "/form", method = RequestMethod.POST)
public String save(Model model, DataDM data, SessionStatus status)
{
dataService.save(data);
status.setComplete();
return "redirect:/success";
}
From a given parameter I get Tienda id, with that Id I want to see if the is a DataDM instance for that Tienda, if it exists update it with the form, if there is not an instance then create it and save it, everything works (findByTienda works) up until I click the save button from the form it gives me the error:
(type=Bad Request, status=400).
Validation failed for object='dataDM'. Error count: 1, but I don´t really have any validation to save the DataDM entity, I guess it has to be something with the save() method, but i have no idea what could be, can someone help me?
Edit: Adding the client code
<!DOCTYPE html>
<html lang="en" xmlns:th="/http:wwww.thymeleaf.org">
<head th:replace="layout/layout :: head"></head>
<body>
<header th:replace="layout/layout :: header"></header>
<div class="container">
<h3 th:text=" 'CAPTURA PARA TIENDA ' + ${data.tienda.id} + ' ' + ${data.tienda.nombreTienda} "></h3>
<div class="container">
<form th:action="#{/form}" th:object="${data}" method="post" class="d-flex align-content-start flex-wrap p-2">
<label for="sec-1" class="h4" th:text=" 'ALREDEDORES DE TIENDA' "></label>
<div class="d-flex align-content-start flex-wrap" id="sec-1" name="sec-1" >
<div class="form-group p-2">
<select th:field="*{nse}" id='nse' name="nse" class="form-control">
<option th:value=" '-' " th:text=" '-' "></option>
<option th:value=" 'AB' " th:text=" 'AB' "></option>
<option th:value=" 'C+' " th:text=" 'C+' "></option>
<option th:value=" 'C' " th:text=" 'C' "></option>
<option th:value=" 'C-' " th:text=" 'C-' "></option>
<option th:value=" 'D' " th:text=" 'D' "></option>
</select>
</div>
<div class="form-group p-2">
<input type="submit" value="Guardar Datos" class="btn btn-secondary btn-block" />
</div>
<input type="hidden" th:field="*{id}"/>
<input type="hidden" th:field="*{tienda}"/>
</form>
</div>
Bad Request means the server can't process the request (so your server code doesn't run), usually it means there is a client error, so you should check your client code...
EDIT:
save method expects JSON by default, so you can either send JSON but it involves adding JavaScript or you can tell your API method to consume a different type of data, like this :
#PostMapping(
path = "/save",
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
I want to replace some web page elements throught using parametrized fragments with ThymeLeaf + Spring Boot.
public class Admin {
private Integer id;
public Admin(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Override
public String toString() {
return "Admin{" +
"id=" + id +
'}';
}
}
#Controller
public class AdminController {
#GetMapping("/admin-reload")
public String reloadElementDiv(Model model) {
Admin adminObj = new Admin(3);
model.addAttribute("adminObj", adminObj);
model.addAttribute("id", adminObj.getId());
return "/fragments/adminForm";
}
}
resources/templates/fragments/adminForm.html
<div th:fragment="admin_form(adminObj, id)" >
<form th:object="${adminObj}">
<p th:text="${id}"></p>
</form>
</div>
resources/templates/index.html
<div th:fragment="admin_form(adminObj, id)">
</div>
one way:
<div th:replace="fragments/adminForm :: admin_form(adminObj, id)" th:with="adminObj='${adminObj}', id='${id}'">
<p th:text="${id}"></p>
</div>
or
another way:
<div th:replace="fragments/adminForm :: admin_form(adminObj, id)">
<p th:text="${id}"></p>
</div>
So, as a result I just have got only 'id' parameter on my index.html web page, but not its value id = 3, as I set in the controller class. Can anybody help me to get the value I need?
Good afternoon,
I am newbie to Spring MVC. I'm stuck with the following error while running my project "The request sent by the client was syntactically incorrect."
My project has two entities, Team and Country which have a ManyToOne relationship. Both these entities map tables created in mysql database.
I started the project with only the Team entity, and sucessfuly created my classes (DAO, controller, services, etc) and jsp to create new teams.
Now, I created the class Country to relate both entities and I added a dropdown list in the "add-team-form.jsp" to select the country of the new team. This page is correctly displayed (all countries appear in the dropdown list), however, when I click "submit" to create the new team, I get the error "The request sent by the client was syntactically incorrect."
Can you please help me to identify my error? I'm guessing it's in the "add-team-form.jsp".
1 - Entity Team:
#Entity
#Table(name="teams")
public class Team implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "name", length = 40, nullable = false)
private String name;
#Column(name = "rating", length = 6, nullable = false)
private Integer rating;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "id_country", nullable = false)
private Country country;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getRating() {
return rating;
}
public void setRating(Integer rating) {
this.rating = rating;
}
public Country getCountry() {
return country;
}
public void setCountry(Country country) {
this.country = country;
}
}
2 - Entity Country:
#Entity
#Table(name = "countries")
public class Country implements Serializable{
#Id
#Column(name= "id_country", length = 6)
private String idCountry;
#Column(name = "name", length = 255, nullable = false)
private String name;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "country")
private List<Team> teams;
public String getIdCountry() {
return idCountry;
}
public void setIdCountry(String idCountry) {
this.idCountry = idCountry;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
My Team DAO
#Repository
public class TeamDAOImpl implements TeamDAO {
#Autowired
private SessionFactory sessionFactory;
private Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
#Override
public void addTeam(Team team) {
getCurrentSession().save(team);
}
}
My Team Service
#Service
#Transactional
public class TeamServiceImpl implements TeamService {
#Autowired
private TeamDAO teamDAO;
public void addTeam(Team team) {
teamDAO.addTeam(team);
}
My Team Controller
#Controller
#RequestMapping(value="/team")
public class TeamController {
#Autowired
private TeamService teamService;
#Autowired
private FilterService filterService;
#RequestMapping(value="/add", method=RequestMethod.GET)
public ModelAndView addTeamPage() {
ModelAndView modelAndView = new ModelAndView("add-team-form");
modelAndView.addObject("team", new Team());
return modelAndView;
}
#RequestMapping(value="/add", method=RequestMethod.POST)
public ModelAndView addingTeam(#ModelAttribute Team team) {
ModelAndView modelAndView = new ModelAndView("home");
teamService.addTeam(team);
String message = "Team was successfully added.";
modelAndView.addObject("message", message);
return modelAndView;
}
#ModelAttribute("countryList")
public Map<String, String> getCountryList(){
Map<String, String> countryList = filterService.getCountries();
return countryList;
}
...
}
My "add-team-form.jsp"
<%#taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<?xml version="1.0" encoding="ISO-8859-1" ?>
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Add team page</title>
</head>
<body>
<h1>Add team page</h1>
<form:form method="POST"
modelAttribute="team"
action="${pageContext.request.contextPath}/team/add.html">
<table>
<tbody>
<tr>
<td>Name:</td>
<td><form:input path="name" /></td>
</tr>
<tr>
<td>Rating:</td>
<td><form:input path="rating" /></td>
</tr>
<tr>
<td><label>Country</label></td>
<td>
<form:select path="country.idCountry">
<form:options items="${countryList}" />
</form:select>
</td>
<tr>
<td><input type="submit" value="Add" /></td>
<td></td>
</tr>
</tbody>
</table>
</form:form>
</body>
</html>
There is no error showing in the console of eclipse, but here is the error im receiving from the browser:
HTTP Status 400 -
type Status report
message
description The request sent by the client was syntactically incorrect.
Apache Tomcat/7.0.47
There's a couple of problems I can see here - you are posting to add/team/add.html and not hitting your post handler. You don't need the action attribute as you're posting to the same endpoint;
<form:form method="POST" modelAttribute="team" >
Secondly your are injecting the countries as a map, so these are ID/display values which works great for key/value pairs and for binding a value to a string field. In this case, Spring is trying to bind your country ID (String) to the team.country(Country) field which will fail. To help Spring out you need a databinder; in your controller add;
#InitBinder
public void initBinder (WebDataBinder binder) {
binder.registerCustomEditor(Country.class, new CountryEditor());
}
and create the property editor class;
public class CountryEditor extends PropertyEditorSupport {
#Override
public void setValue(Object value) {
super.setValue(value);
}
public String getAsText() {
if (getValue() == null) return null;
return ((Country) getValue()).getName();
};
public void setAsText(String text) throws IllegalArgumentException {
if (text != null) {
Country country = // something like filterService.getCountryById(text);
setValue(country);
}
};
}
There's more information in the Spring documentation
The error you are receiving generally happens if a parameter is missing or is in a different format and cannot be converted to the expected type.Check the values being passed to the Team object.You can either log the request and response or set the log level to "DEBUG",this will display the exact error in logs.
In a spring mvc application that uses hibernate an jpa, I have a module for editing phone numbers which is not committing changes to the database when the user enters altered information for phone number and phone number type. I keep going over the code, but I cannot see where the problem is. Can someone show me what to change in the below?
Here are the relevant methods of PhoneNumberController.java:
#RequestMapping(value = "/patients/{patientId}/phonenumbers/{phonenumberId}/edit", method = RequestMethod.GET)
public String initUpdateForm(#PathVariable("phonenumberId") int phonenumberId, Map<String, Object> model) {
System.out.println("--------------------------------- made it into initUpdateForm() method");
PhoneNumber phonenumber = this.clinicService.findPhoneNumberById(phonenumberId);
model.put("phonenumber", phonenumber);
return "phonenumbers/createOrUpdatePhoneNumberForm";
}
#RequestMapping(value = "/patients/{patientId}/phonenumbers/{phonenumberId}/edit", method = {RequestMethod.PUT, RequestMethod.POST})
public String processUpdateForm(#ModelAttribute("phonenumber") PhoneNumber phonenumber, BindingResult result, SessionStatus status) {
// we're not using #Valid annotation here because it is easier to define such validation rule in Java
new PhoneNumberValidator().validate(phonenumber, result);
if (result.hasErrors()) {return "phonenumbers/createOrUpdatePhoneNumberForm";}
else {
this.clinicService.savePhoneNumber(phonenumber);
status.setComplete();
return "redirect:/patients?patientID={patientId}&type=phone";
}
}
Here is the PhoneNumber.java model:
#Entity
#Table(name = "patient_phone_numbers")
public class PhoneNumber {
#Id
#GeneratedValue
#Column(name="id")
private Integer id;
#ManyToOne
#JoinColumn(name = "client_id")
private Patient patient;
#Column(name="phonenumber")
private String number;
#ManyToOne
#JoinColumn(name = "type_id")
private PhoneNumberType type;
#Column(name = "preferred")
private boolean preferred;
#Column(name = "okmessages")
private boolean okmessages;
public Integer getId(){return id;}
public void setId(Integer i){id=i;}
protected void setPatient(Patient patient) {this.patient = patient;}
public Patient getPatient(){return this.patient;}
public String getNumber(){return number;}
public void setNumber(String pn){number=pn;}
public PhoneNumberType getType(){return this.type;}
public void setType(PhoneNumberType nt){this.type=nt;}
public boolean getPreferred(){return preferred;}
public void setPreferred(boolean p){preferred=p;}
public boolean getOkmessages(){return okmessages;}
public void setOkmessages(boolean m){okmessages=m;}
public boolean isNew() {return (this.id == null);}
}
And here is the createOrUpdatePhoneNumberForm.jsp:
<html lang="en">
<jsp:include page="../fragments/headTag.jsp"/>
<body>
<div class="container">
<jsp:include page="../fragments/bodyHeader.jsp"/>
<c:choose>
<c:when test="${phonenumber['new']}">
<c:set var="method" value="post"/>
</c:when>
<c:otherwise>
<c:set var="method" value="put"/>
</c:otherwise>
</c:choose>
<h2>
<c:if test="${phonenumber['new']}">New </c:if>
Phone Number
</h2>
<form:form modelAttribute="phonenumber" method="${method}" class="form-horizontal">
<div class="control-group" id="patient">
<label class="control-label">Patient </label>
<c:out value="${phonenumber.patient.firstName} ${phonenumber.patient.lastName}"/>
</div>
<petclinic:inputField label="PhoneNumber" name="number"/>
<div class="control-group">
<petclinic:selectField name="type" label="Type" names="${numtypes}" size="5"/>
</div>
Preferred number? <form:checkbox path="preferred"/><br>
OK to leave messages? <form:checkbox path="okmessages"/>
<td>
</td>
<div class="form-actions">
<c:choose>
<c:when test="${phonenumber['new']}">
<button type="submit">Add Phone Number</button>
</c:when>
<c:otherwise>
<button type="submit">Update Phone Number</button> <h3> Link to delete will go here.</h3>
</c:otherwise>
</c:choose>
</div>
</form:form>
<c:if test="${!phonenumber['new']}">
</c:if>
</div>
</body>
</html>
ClinicService.java is:
#Service
public class ClinicServiceImpl implements ClinicService {
private DocumentRepository documentRepository;
private PatientRepository patientRepository;
private AddressRepository addressRepository;
private PhoneNumberRepository phoneNumberRepository;
#Autowired
public ClinicServiceImpl(DocumentRepository documentRepository, PatientRepository patientRepository, AddressRepository addressRepository, PhoneNumberRepository phoneNumberRepository) {
this.documentRepository = documentRepository;
this.patientRepository = patientRepository;
this.addressRepository = addressRepository;
this.phoneNumberRepository = phoneNumberRepository;
}
#Override
#Transactional(readOnly = true)
public Collection<DocumentType> findDocumentTypes() throws DataAccessException {return documentRepository.findDocumentTypes();}
#Override
#Transactional(readOnly = true)
public Collection<Gender> findGenders() throws DataAccessException {return patientRepository.findGenders();}
#Override
#Transactional(readOnly = true)
public Collection<Race> findRaces() throws DataAccessException {return patientRepository.findRaces();}
#Override
#Transactional(readOnly = true)
public Patient findPatientById(int id) throws DataAccessException {return patientRepository.findById(id);}
#Override
#Transactional(readOnly = true)
public Collection<Patient> findPatientByLastName(String lastName) throws DataAccessException {return patientRepository.findByLastName(lastName);}
#Override
#Transactional
public void savePatient(Patient patient) throws DataAccessException {
System.out.println("-------------------------------------- inside clinicservice.savePatient()");
patientRepository.save(patient);}
#Override
#Transactional(readOnly = true)
public Document findDocumentById(int id) throws DataAccessException {
System.out.println("--------------- made it into clinicservice.findDocumentById() method");
return documentRepository.findById(id);}
#Override
#Transactional
public void saveDocument(Document doc) throws DataAccessException {documentRepository.save(doc);}
#Override
#Transactional
public void saveAddress(Address addr) throws DataAccessException {addressRepository.save(addr);}
#Override
#Transactional(readOnly=true)
public Address findAddressById(int id) throws DataAccessException {return addressRepository.findById(id);}
#Override
#Transactional(readOnly = true)
public Collection<State> findStates() throws DataAccessException {return addressRepository.findStates();}
#Override
#Transactional(readOnly = true)
public Collection<PhoneNumberType> findPhoneNumberTypes() throws DataAccessException {return phoneNumberRepository.findPhoneNumberTypes();}
#Override
#Transactional(readOnly = true)
public void savePhoneNumber(PhoneNumber pn) throws DataAccessException {
System.out.println("++++++++++++++++++++ inside savePhoneNumber(pn) : "+pn.getNumber()+" , "+pn.getType().getName());
phoneNumberRepository.save(pn);
}
#Override
#Transactional(readOnly=true)
public PhoneNumber findPhoneNumberById(int id) throws DataAccessException {return phoneNumberRepository.findById(id);}
}
JpaPhoneNumberRepository.java contains the following:
#PersistenceContext
private EntityManager em;
#Override
public void save(PhoneNumber phonenumber) {
System.out.println("------------------------------ inside save(phonenumber) : "+phonenumber.getNumber()+" , "+phonenumber.getType().getName());
if (phonenumber.getId() == null) {
System.out.println("phonenumber.getId() == null ");
this.em.persist(phonenumber);
}
else {
System.out.println("else");
this.em.merge(phonenumber);}
}
The correct new values for getNumber() and getType().getName() print out. And "else" prints out, but then the data is not updated in the database. Why not? (note that clinicservice calls this save() method of JpaPhoneNumberRepository.java.)
The problem is that you have ClinicServiceImpl > savePhoneNumber method annotated as #Transactional(readOnly = true). Change it to #Transactional
Why is the savePhoneNumber method in your ClinicService.java as #Transactional(readOnly=True)?
That is the cause of the problem