CRUD JDBC template problem with edit operation - spring-mvc

I have problem with CRUD edit operation. When i click Edit which is written in this way in jsp file
Edit
i got error
HTTP Status 404 – Not Found
Type Status Report
Message /Firstaidkit/editMedicines
Description The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
EditController
#WebServlet(value = "/editMedicines")
public class MedicinesEditController extends HttpServlet {
private static final long serialVersionUID = 1L;
#RequestMapping(method = RequestMethod.GET)
public ModelAndView editMedicines(HttpServletRequest request) {
int medicinesId = Integer.parseInt(request.getParameter("id"));
Medicines medicines = GenericDAO.get(medicinesId);
ModelAndView model = new ModelAndView("editform");
model.addObject("medicines", medicines);
return model;
}
}
GenericDAO
public interface GenericDAO <T, PK extends Serializable> {
//CRUD
T create(T newObject);
T read(PK primaryKey);
public void update(Medicines medicines);
public void delete(T id);
List<T> getAll();
public static Medicines get(int medicinesId) {
return null;
}
}
MedicinesDAOImpl
private final static String UPDATE_MEDICINES =
"UPDATE medicines SET name=:name, drugform=:drugform, quantity=:quantity, expiration_date=:expiration_date, description=:description WHERE id_medicines=:id_medicines;";
#Override
public void update(Medicines medicines) {
jdbcTemplate.update(UPDATE_MEDICINES, medicines.getName(), medicines.getDrugForm(),
medicines.getQuantity(), medicines.getExpirationDate(), medicines.getId());
}
}
editform.jsp
<form class="form-signin" method="post" action="editMedicines">

I believe the problem is that the URL your anchor is linked to does not exist. You should have an annotation dictating the path on your MedicinesEditController at a class level. Assuming you want the path of this endpoint to be /Firstaidkit/editMedicines, the following should work:
#Path(value = "/Firstaidkit") // possibly a different but similar annotation
public class MedicinesEditController extends HttpServlet {
#RequestMapping(value = "/editMedicines", method = RequestMethod.GET)
public ModelAndView editMedicines(HttpServletRequest request) {
int medicinesId = Integer.parseInt(request.getParameter("id"));
Medicines medicines = GenericDAO.get(medicinesId);
ModelAndView model = new ModelAndView("editform");
model.addObject("medicines", medicines);
return model;
}
}
Otherwise, you need to alter the link of the anchor to reference the root of the application, followed by your endpoint of /editMedicines. This can be accomplished by using the following anchor, as described here:
Edit
Edit: Try the following
MedicinesEditController
#RequestMapping(value = "/editMedicines")
public class MedicinesEditController extends HttpServlet {
private static final long serialVersionUID = 1L;
#GetMapping
public ModelAndView editMedicines(HttpServletRequest request) {
int medicinesId = Integer.parseInt(request.getParameter("id"));
Medicines medicines = GenericDAO.get(medicinesId);
ModelAndView model = new ModelAndView("editform");
model.addObject("medicines", medicines);
return model;
}
}
editform.jsp
<form class="form-signin" method="GET" action="editMedicines">
anchor
(note: try these variations, as the link changes depending on where you are when you click it. View this stack to help determine the correct link)
Edit
Edit
Edit

Related

No mapping found for HTTP request with URI [/api/transactions] in DispatcherServlet with name ''

I thought this is a standard configuration. But I get a 404 back. Where else should I configure Spring Boot ?
#RestController
#RequestMapping("/api")
public class TransactionStatisticsController {
public static final Logger logger = LoggerFactory.getLogger(TransactionStatisticsController.class);
#RequestMapping(value = "/transactions",
method = RequestMethod.POST)
public ResponseEntity sendTransaction(#RequestBody Transaction request) {
logger.info( request.toString());
return new ResponseEntity(HttpStatus.OK);
}
}
This is my test.
#JsonTest
#SpringBootTest(classes = Application.class)
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
public class TransactionStatisticsRestTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private JacksonTester<Transaction> json;
private static Transaction transaction;
#BeforeClass
public static void createTransaction(){
BigDecimal amount = new BigDecimal(12.3343);
transaction = new Transaction(amount.toString(),
"2010-10-02T12:23:23Z");
}
#Test
public void getTransactionStatus() throws Exception {
final String transactionJson = json.write(transaction).getJson();
mockMvc
.perform(post("/api/transactions")
.content(transactionJson)
.contentType(APPLICATION_JSON_UTF8))
.andExpect(status().isOk());
}
public static byte[] convertObjectToJsonBytes(Object object) throws IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsBytes(transaction);
}
}
Request being made is
MockHttpServletRequest:
HTTP Method = POST
Request URI = /api/transactions
Parameters = {}
Headers = {Content-Type=[application/json;charset=UTF-8]}
Body = {"amount":"12.3343000000000007077005648170597851276397705078125","timestamp":"2010-10-02T12:23:23Z[UTC]"}
Session Attrs = {}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 404
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Update : I added a component scan pointing to a base package. I don't see that error now. Please see the comments where there is an answer.
As in the comment section ,there was only requirement was to bind a component scan base package location .
#Component scan -->Configures component scanning directives for use with #Configuration classes. Provides support parallel with Spring XML's element.
Either basePackageClasses() or basePackages() (or its alias value()) may be specified to define specific packages to scan. If specific packages are not defined, scanning will occur from the package of the class that declares this annotation.
Please share your project folder architecture. It might be possible that your controller package is out of the main class package. That's why it is showing 404.
This code :
#RestController
#RequestMapping("/api")
public class TransactionStatisticsController {
public static final Logger logger = LoggerFactory.getLogger(TransactionStatisticsController.class);
#RequestMapping(value = "/transactions",
method = RequestMethod.POST)
public ResponseEntity sendTransaction(#RequestBody Transaction request) {
logger.info( request.toString());
return new ResponseEntity(HttpStatus.OK);
}
}
This should be into your main package where
#SpringBootApplication
public class YourApplication {
public static void main(String[] args) {
SpringApplication.run(YourApplication.class, args);
}
}
this main class resides.
I hope, this will help.
Seems using #JsonTest does not even allow to load Application Context, results mapping is not loaded and its throw 404 so #JsonTest is not a replacement for #SpringBootTest, it is a way to easily test json serialization/de-serialization.
As per documentation:
you can use the #JsonTest annotation. #JsonTest auto-configures the
available supported JSON mapper, which can be one of the following
libraries:
Jackson ObjectMapper, any #JsonComponent beans and any Jackson Modules
Gson
Jsonb
If by using Gson and removing #JsonTest your test run fine..(add Gson Dependency in pom)
#SpringBootTest
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
public class DemoKj01ApplicationTests {
#Autowired
private MockMvc mockMvc;
private static Transaction transaction;
#BeforeClass
public static void createTransaction(){
BigDecimal amount = new BigDecimal(12.3343);
transaction = new Transaction(amount.toString(),
"2010-10-02T12:23:23Z");
}
#Test
public void getTransactionStatus() throws Exception {
//final String transactionJson = json.write(transaction).getJson();
Gson gson = new Gson();
String jsonRequest = gson.toJson(transaction);
mockMvc
.perform(post("/api/transactions")
.content(jsonRequest)
.contentType(APPLICATION_JSON_UTF8))
.andExpect(status().isOk());
}
It is beacause of the trailing slas in #RequestMapping(value = "/transactions/", method = RequestMethod.POST)
Remove it and it will be ok : value = "/transactions/" => value = "/transactions"

Unable to Mock the Database call handled by Spring JPA framework while writing test Case for Controller Class

Spring MVC converts the id from path to corresponding object by making call to JpaRepository's findById method. For example see getVersionTree() method.
public class Controller {
#NonNull
private final MyService service;
#NonNull
private final MyAssembler assembler;
#GetMapping(path = VERSION_TREE_MAPPING, produces = MediaTypes.HAL_JSON_UTF8_VALUE)
public HttpEntity<?> getVersionTree(#PathVariable("id") MappingDocument mappingDocument, Pageable pageable, PagedResourcesAssembler<VersionNode> pagedResourcesAssembler) {
Page<VersionNode> versionNodes = service.getVersionTreeFor(mappingDocument, pageable);
return new ResponseEntity<>(pagedResources, HttpStatus.OK);
}
While testing, SpringMVC throws " Failed to convert value of type 'java.lang.String' to required type 'com.rbc.dna.dtl.mappingdocument.MappingDocument'". I am mocking jpaRepository.findById() method. Test Code is as follows:
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
private MyRepository repository;
#Mock
MyController controller;
#MockBean
private MyServiceImpl serviceImpl;
#Test
public void testMethod() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).apply(SecurityMockMvcConfigurers.springSecurity()).build();
MappingDocument mappingDocumentl=MappingDocument.builder().id(17L).build();
Mockito.when(repository.findById(17L)).thenReturn(Optional.of(mappingDocumentl));
Mockito.when(serviceImpl.getVersionTreeFor(mappingDocument,pageable)).thenReturn(pagedResponse);
mockMvc.perform(MockMvcRequestBuilders.get("/abc/17/def").param("page","0").param("size","20").contentType(MediaTypes.HAL_JSON_UTF8_VALUE)
.with(authentication(getOauthTestAuthentication()))
.sessionAttr("scopedTarget.oauth2ClientContext", getOauth2ClientContext()))
.andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
}
Your rest Controller take #PathVariable("id") MappingDocument mappingDocument as variable in your path, but in mockMvc.perform you are passing a simple String/Number or something that doesn't bind with MappingDocument.
Try to replace
public HttpEntity<?> getVersionTree(#PathVariable("id") MappingDocument mappingDocument...
with
public HttpEntity<?> getVersionTree(#PathVariable("id") Long idMappingDocument, ...
If you want to keep your object in #PathVariable you need to change
#GetMapping(path = VERSION_TREE_MAPPING)
To parse an object as PathVariable you need to have path that represent property of your object.
For example if you have
class Person {
String name;
String address;
//getters and setters
}
and you need to define a controller as follow:
#GetMapping(path = "/person/{name}/{address}", produces = MediaTypes.HAL_JSON_UTF8_VALUE)
public HttpEntity<?> getVersionTree(Person person) {
Where {name} and {address} must bind Person properties

Adding server side dynamic validations in spring boot

I am using Spring Boot 2. I want to add certain dynamic validations at server side which should be executed in POST REST call. Can these validations be added in form of annotations at parameter level of POST method call?
Below code will help you to achieve server side validation.
Pojo class :
public class Data {
#NotNull
private final String someStringValue;
#Min(1)
private final int someIntValue;
#JsonCreator
public Data(#JsonProperty("someStringValue") String someStringValue, #JsonProperty("someIntValue") int someIntValue) {
this.someStringValue = someStringValue;
this.someIntValue = someIntValue;
}
public String getSomeStringValue() {
return someStringValue;
}
public int getSomeIntValue() {
return someIntValue;
}
For the validation we need a custom class containing the logic:
#Component
public class StringValueValidator {
public void validate(String language, Data data, Errors errors) {
if (!"de-DE".equals(language)) {
if (data.getSomeStringValue().length() > 140) {
errors.reject("someStringValue");
}
}
}
}
controller method :
#RequestMapping(value = "/validation", method = RequestMethod.POST)
public ResponseEntity<?> acceptData(#Valid #RequestBody Data data, Errors errors,
#RequestHeader(HttpHeaders.ACCEPT_LANGUAGE) String language) {
stringValueValidator.validate(language, data, errors);
if (errors.hasErrors()) {
return new ResponseEntity<>(createErrorString(errors), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(HttpStatus.OK);
}
Refer this : https://blog.codecentric.de/en/2017/11/dynamic-validation-spring-boot-validation/
Here is how you can achieve to defining validations in RequestBody you are receiving from client side.
Define your POJO you will receive from client side.
public class Employee implements Serializable {
private Long id;
#NotNull(message = "Name should not be null")
#NotBlank(message = "Name should be not empty")
#Size(min = 1, max = 255, message = "Name allowed max 255 characters")
private String name;
Now in your controller use #Valid annotation for #RequestBody like below.
public ResponseEntity<?> createEmployee(#Valid #RequestBody Employee employee)

Mapping field-errors with different validators

My usecase: a single html-form can triggered as a save or an update event. Depending on the event the validation is is performed by a different validator. This works so far with the following code. The only problem I have, that I want the field-errors to be mapped in all cases to "saveDto", so I can map them in my form.
Any hints welcome.
#Inject
private SaveValidator saveValidator;
#Inject
private UpdateValidator updateValidator;
#RequestMapping(value = EVENT_SAVE, method = RequestMethod.POST)
protected String doSave(#Valid #ModelAttribute("saveDto") final SaveDto saveDto,
final BindingResult bindingResult, final Model model, final HttpServletRequest request)
{
if (bindingResult.hasErrors())
{
// ...
}
}
#RequestMapping(value = EVENT_UPDATE, method = RequestMethod.POST)
protected String doUpdate(#Valid #ModelAttribute("updateDto") final SaveDto saveDto,
final BindingResult bindingResult, final Model model, final HttpServletRequest request)
{
if (bindingResult.hasErrors())
{
// ...
}
}
#InitBinder("saveDto")
protected void initSaveValidator(final WebDataBinder binder)
{
binder.addValidators(saveValidator);
}
#InitBinder("updateDto")
protected void initUpdateValidator(final WebDataBinder binder)
{
binder.addValidators(updateValidator);
}
Try the hibernate validation group feature along with #Validated annotation

Spring MVC - PropertyEditor not called during ModelAttribute type conversion

Using Spring 3.2.3, I'm trying to implement a simple CRUD controller that handles REST-ful URLs. It relies on a PropertyEditor to convert a path variable to a BusinessService entity by loading it from an application service. Code is as follows:
#Controller
public class BusinessServiceController {
#Autowired
private BusinessServiceService businessSvcService;
public BusinessServiceController() {
}
#InitBinder
public void initBinder(final WebDataBinder binder) {
binder.registerCustomEditor(BusinessService.class, new BusinessServicePropertyEditor(businessSvcService));
}
#RequestMapping(value = "/ui/account/business-services/{businessSvc}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ModelAndView update(#ModelAttribute("businessSvc") #Valid final BusinessService businessSvc, final BindingResult result,
final RedirectAttributes redirectAttribs) throws UnknownBusinessServiceException {
ModelAndView mav;
if (result.hasErrors()) {
mav = new ModelAndView("/business-service/edit");
}
else {
businessSvcService.updateBusinessService(XSecurity.principal().getId(), businessSvc);
mav = new ModelAndView("redirect:/ui/account/business-services");
redirectAttribs.addFlashAttribute("message", Message.info("businessService.updated", businessSvc.getTitle()));
}
return mav;
}
}
public class BusinessServicePropertyEditor extends PropertyEditorSupport {
private final BusinessServiceService businessSvcService;
public BusinessServicePropertyEditor(final BusinessServiceService businessSvcService) {
this.businessSvcService = businessSvcService;
}
#Override
public String getAsText() {
final BusinessService svc = (BusinessService) getValue();
return Long.toString(svc.getId());
}
#Override
public void setAsText(final String text) {
final BusinessService svc = businessSvcService.getBusinessService(Long.parseLong(text));
setValue(svc);
}
}
According to SPR-7608, starting from Spring 3.2, #ModelAttribute method argument resolution checks if a path variable by the same name exists (it does here), in which case it tries to convert that path variable's value to the target parameter type through registered Converters and PropertyEditors. This is not what I'm experiencing. When I inspect what ServletModelAttributeMethodProcessor does, it clearly uses the request DataBinder's ConversionService to perform type conversion, which does not consider registered PropertyEditors, and hence BusinessServicePropertyEditor#setAsText is never called.
Is this a configuration problem or an actual bug?
Thanks for your help!
Spring's ConversionService and Converters are replacement for standard Java Beans PropertyEditors.
You need to implement Converter instead of PropertyEditor if this feature is based purely on conversion service.
To register your custom converters in WebDataBinder you might use ConfigurableWebBindingInitializer or #InitBinder method.

Resources