Error with JPA PagingAndSorting request parameter - spring

I have a Controller that pages and sorts all the news in my database:
#RequestMapping(value = "/viewstatus", method = RequestMethod.GET)
ModelAndView viewStatus(ModelAndView modelAndView, #RequestParam(name = "p", defaultValue = "1") int pageNumber) {
Page<StatusUpdate> page = statusUpdateService.getPage(pageNumber);
modelAndView.getModel().put("page", page);
modelAndView.setViewName("app.viewStatus");
return modelAndView;
}
With its call to the service that works fine:
public Page<StatusUpdate> getPage(int pageNumber) {
PageRequest request = new PageRequest(pageNumber-1, pageSize, Sort.Direction.DESC, "added");
return statusUpdateDao.findAll(request);
}
But now, I would like to do the same SortingAndPaging BUT with one parameter (SiteUser). Here is my object:
#Entity
#Table(name = "status_update")
public class StatusUpdate {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Size(min=5, max=255, message="{addstatus.title.size}")
#Column(name = "title")
private String title;
#Size(min=5, max=5000, message="{addstatus.text.size}")
#Column(name = "text")
private String text;
#Column(name = "added")
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(pattern="yyyy/MM/dd hh:mm:ss")
private Date added;
#OneToOne(targetEntity = SiteUser.class)
#JoinColumn(name="user_id")
private SiteUser siteUser;
#PrePersist
protected void onCreate() {
if (added == null) {
added = new Date();
}
}
public StatusUpdate() {
}
But when I do it, it gives me this error:
Exception: org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [3] did not match expected type [com.caveofprogramming.model.entity.SiteUser (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [3] did not match expected type [com.caveofprogramming.model.entity.SiteUser (n/a)]
Failed URL: http://192.168.160.128:8080/viewmystatus
Exception message: Parameter value [3] did not match expected type [com.caveofprogramming.model.entity.SiteUser (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [3] did not match expected type [com.caveofprogramming.model.entity.SiteUser (n/a)]
I tired to send a String, but it did not work. I had to change it to a NativeQuery but I does not work with PagingAndSorting, it only works as a List (which is a headache to work with). So if you can help me with the ERROR of PagingAndSorting that would be great.
Here is the Controller:
#RequestMapping(value = "/viewmystatus", method = RequestMethod.GET)
ModelAndView viewMyStatus(ModelAndView modelAndView, #RequestParam(name = "p", defaultValue = "1") int pageNumber) {
SiteUser user = getUser();
Long user_id= user.getId();
Page<StatusUpdate> page = statusUpdateService.findMyStatusUpdates(user_id, pageNumber);
for(StatusUpdate statusUpdate: page){
SiteUser siteUser= statusUpdate.getSiteUser();
modelAndView.getModel().put("siteuser", siteUser);
}
modelAndView.getModel().put("page", page);
modelAndView.setViewName("app.viewStatus");
return modelAndView;
}
Here is the service:
public Page<StatusUpdate> findMyStatusUpdates(Long user_id, int pageNumber) {
PageRequest request = new PageRequest(pageNumber-1, pageSize, Sort.Direction.DESC, "added");
return statusUpdateDao.findBySiteUser(user_id, request);
}
And the DAO:
#Repository
public interface StatusUpdateDao extends PagingAndSortingRepository<StatusUpdate, Long> {
StatusUpdate findFirstByOrderByAddedDesc();
Page<StatusUpdate> findBySiteUser(Long user_id, Pageable pageable);
}
Thanks for your help!

Use a SiteUser object:
public Page<StatusUpdate> findMyStatusUpdates(Long user_id, int pageNumber) {
PageRequest request = new PageRequest(pageNumber-1, pageSize, Sort.Direction.DESC, "added");
return statusUpdateDao.findBySiteUser(new SiteUser(user_id), request);
}
You could try query by nested properties as well, but I think you must change user_id by userId, as underscore is a reserved character. Have a look to the documentation:
Spring data JPA Property expressions
Have you changed your findBySiteUser method? The one you posted looks fine but it seems is complaining about the signature. Even when PageRequest implements Pageable the signature has to be declared, explicitly, using Pageable, but the error message you are getting says PageRequest
public abstract org.springframework.data.domain.Page com.caveofprogramming.model.repository.StatusUpdateDao.findB‌​ySiteUser(com.caveof‌​programming.model.en‌​tity.SiteUser,org.sp‌​ringframework.data.d‌​omain.PageRequest)
More info:
PageRequest parameter not recognized as Pageable in Paging query

Related

BaseCrudRepository findById returns null when the record exists

I've encountered a problem with spring where a call to the repository findById(id) returns null. The record in question exists in the postgresql database, and my call to return pages of records works correctly. This is implemented via Classes which extend BaseCrudService and BaseCrudRepository. In the case with the problem, the controller calls the BaseCrudService function directly. It is not overridden in the extended classes. I have implemented the same code for two similar classes and underlying tables and it works okay. I cannot find what is different with this one.
'id' in the table is a unique numeric.
Code in Controller which extends PageCrudController
#RestController
#RequestMapping(API_ENDPOINT)
public class WildlifeController extends PageCrudController<Wildlife, Long, WildlifeView> {
private static final Logger LOG = LoggerFactory.getLogger(WildlifeController.class);
private final WildlifeService service;
private final WildlifeValidation validation;
#Autowired
protected WildlifeController(final WildlifeMapper mapper, final WildlifeService service, final WildlifeValidation validation ) {
super(API_ENDPOINT, mapper, service);
this.service = service;
this.validation = validation;
}
#GetMapping
#PreAuthorize(HAS_AUTHORITY_READ)
public ResponseEntity<Page<WildlifePageView>> findPage(#SortDefault(sort = DEFAULT_SORT_FIELD) Pageable pageable ) {
LOG.debug("Find Wildlife by Pageable: {}", pageable);
// Return only results for authenticated user's aerodrome
Aerodrome aerodrome = Objects.requireNonNull(SecurityContext.getPrincipal()).getAerodrome();
Page<WildlifePageView> result = service.findPageByAerodrome(aerodrome, pageable, WildlifePageView.class);
return ResponseEntity.ok(result);
}
#GetMapping("/{id}")
#PreAuthorize(HAS_AUTHORITY_READ)
public ResponseEntity<WildlifeView> find(#PathVariable("id") Long id) {
LOG.debug("Find Wildlife by id: {}", id);
Wildlife result = service.find(id);
LOG.debug("Found Wildlife by id: {} = {}", id, result);
return Optional.ofNullable(result).map(this::responseOk)
.orElse(responseNotFound());
}
#PostMapping()
#PreAuthorize(HAS_AUTHORITY_WRITE)
public ResponseEntity<WildlifeView> create(#Valid #RequestBody WildlifeView view) {
LOG.debug("Create Wildlife by view: {}", view);
validation.validate(view);
return super.doCreate(view);
}
#PutMapping("/{id}")
#PreAuthorize(HAS_AUTHORITY_WRITE)
public ResponseEntity<WildlifeView> update(#PathVariable("id") Long id, #Valid #RequestBody WildlifeView view) {
LOG.debug("Update Wildlife '{}' by view: {}", id, view);
validation.validate(view);
return super.doUpdate(id, view);
}
#DeleteMapping("/{id}")
#PreAuthorize(HAS_AUTHORITY_WRITE)
public ResponseEntity<WildlifeView> delete(#PathVariable("id") Long id) {
LOG.debug("Delete Wildlife by ID: {}", id);
validation.validate(id);
return super.doDelete(id);
}
}
Code in BaseCrudService - I've added the debug statements.
#Transactional(readOnly = true)
public E find(final ID id) {
LOG.debug("Find with ID: {}", id);
LOG.debug("Find with ID: {} count {}", id, this.repository.count());
return repository.findById(id).orElse(null);
}
Found my problem. I had an an entity property marked nullable = false mapped to a nullable database table. The data set I was using had nulls in this column which prevents it from being read.
Broken:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "cntrl_msrs_tkn_1_id", nullable = false)
private LutTabWlcm controlMeasuresTaken1;
Fixed
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "cntrl_msrs_tkn_1_id")
private LutTabWlcm controlMeasuresTaken1;

Mockito Test for Spring NamedJDBC Template

I am trying to figure out mickito test for Named Jdbc Template but unable to do so. I did googling but did not find any accurate result. Below is example Code.
Student.class
#Data
public class Student {
private int id;
private String name;
private String address;
public Student(ResultSet rs) throws SQLException {
id = rs.getInt("id");
name = rs.getString("name");
address = rs.getString("address");
}
}
Student class takes ResultSet argument in constructor and mapped all column to variable .
StudentService.class
public class StudentService {
#Autowired
#Qualifier("namedJdbcTemplate")
NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public Student gerStudent(String id) {
Student student;
String selectStudent = "select id , name ,address from student where id=:id";
MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource();
mapSqlParameterSource.addValue(id, "id");
student = namedParameterJdbcTemplate.query(selectStudent, mapSqlParameterSource, resultSet -> {
Student response = new Student(resultSet);
return response;
});
return student;
}
}
Can anyone please help on Mockito Test for below line of code?
student = namedParameterJdbcTemplate.query(selectStudent, mapSqlParameterSource, resultSet -> {
Student response = new Student(resultSet);
return response;
});

Java: GroupSequenceProvider for Validation, object is null in getValidationGroups method

This is what I am trying to achieve:
I have an update request object and user is allowed to do Partial Updates. But I want to validate the field only if it is in the request body. Otherwise, it is OK to be null. To achieve this, I am using GroupSequenceProvider to let the Validator know what groups to validate. What am I doing wrong here? If there is a blunder, how do I fix it?
Documentation: https://docs.jboss.org/hibernate/validator/5.1/reference/en-US/html/chapter-groups.html#example-implementing-using-default-group-sequence-provider
#GroupSequenceProvider(UpdateUserRegistrationGroupSequenceProvider.class)
public class UpdateUserRegistrationRequestV1 {
#NotBlank(groups = {EmailExistsInRequest.class})
#Email(groups = {EmailExistsInRequest.class})
#SafeHtml(whitelistType = SafeHtml.WhiteListType.NONE, groups = {EmailExistsInRequest.class})
private String email;
#NotNull(groups = {PasswordExistsInRequest.class})
#Size(min = 8, max = 255, groups = {PasswordExistsInRequest.class})
private String password;
#NotNull(groups = {FirstNameExistsInRequest.class})
#Size(max = 255, groups = {FirstNameExistsInRequest.class})
#SafeHtml(whitelistType = SafeHtml.WhiteListType.NONE, groups = {FirstNameExistsInRequest.class})
private String firstName;
// THERE ARE GETTERS AND SETTERS BELOW
}
Group Sequence Provider Code:
public class UpdateUserRegistrationGroupSequenceProvider implements DefaultGroupSequenceProvider<UpdateUserRegistrationRequestV1> {
public interface EmailExistsInRequest {}
public interface PasswordExistsInRequest {}
public interface FirstNameExistsInRequest {}
#Override
public List<Class<?>> getValidationGroups(UpdateUserRegistrationRequestV1 updateUserRegistrationRequestV1) {
List<Class<?>> defaultGroupSequence = new ArrayList<Class<?>>();
defaultGroupSequence.add(Default.class);
defaultGroupSequence.add(UpdateUserRegistrationRequestV1.class);
if(StringUtils.hasText(updateUserRegistrationRequestV1.getEmail())) {
defaultGroupSequence.add(EmailExistsInRequest.class);
}
if(StringUtils.hasText(updateUserRegistrationRequestV1.getPassword())) {
defaultGroupSequence.add(PasswordExistsInRequest.class);
}
if(StringUtils.hasText(updateUserRegistrationRequestV1.getFirstName())) {
defaultGroupSequence.add(FirstNameExistsInRequest.class);
}
return defaultGroupSequence;
}
}
I am using Spring MVC, so this is how my controller method looks,
#RequestMapping(value = "/{userId}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.NO_CONTENT)
public void updateUser(#PathVariable("userId") Long userId,
#RequestBody #Valid UpdateUserRegistrationRequestV1 request) {
logger.info("Received update request = " + request + " for userId = " + userId);
registrationService.updateUser(userId, conversionService.convert(request, User.class));
}
Now the problem is, the parameter "updateUserRegistrationRequestV1" in the UpdateUserRegistrationGroupSequenceProvider.getValidationGroups method is null. This is the request object that I am sending in the request body and I am sending email field with it.
What am I doing wrong?
I too went through the same issue ,and hopefully solved it
You just have to check the object is null and put all your conditions inside it.
public List<Class<?>> getValidationGroups(Employee object) {
List<Class<?>> sequence = new ArrayList<>();
//first check if the object is null
if(object != null ){
if (!object.isDraft()) {
sequence.add(Second.class);
}
}
// Apply all validation rules from default group
sequence.add(Employee.class);
return sequence;
}

How to return a list of object as Json in Spring MVC

I'm trying to get a list of objects to render on a Spring 3 MVC app and would like to do this via Ajax.
So in my Spring class I have:
#RequestMapping(value = "/viewSearchEnquiriesAjax", method = RequestMethod.GET, headers = "Accept=application/json")
public #ResponseBody List<Enquiry> getEnquiriesBySearchAjax(#RequestParam String name) {
Search search = new Search();
search.setFirstName(name);
return searchEnquiries(search);
}
But I get a 500 (Internal Server Error) when this is run. This manifests itself when I'm debugging in the browser as 'GET http://localhost:8080/SpringMVC/viewSearchEnquiriesAjax?name=peter 500 (Internal Server Error)'
I can successfully return a single object with no error. Can the Spring Json mapper(Jackson) convert correctly? Am I missing something fundamental?
My javascript is as follows:
function doAjaxPost() {
// get the form values
var firstName = $('#firstName').val();
$.getJSON("/SpringMVC/viewSearchEnquiriesAjax", { name: firstName }, function(result) {
alert("Success");
});
}
My Enquiry object is an Entity:
#Entity
#Table(name = "enquiries")
public class Enquiry implements java.io.Serializable{
private static final long serialVersionUID = -5093725544297637792L;
protected Long id;
protected Date created = new Date();
...
...
public Enquiry() {
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", unique = true, nullable = false)
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
...
...
For Jackson you have to create a strongly typed list class because of type erasure:
public class EnquiryList extends ArrayList<Enquiry> {
}
Then return this list from your controller:
#RequestMapping(value = "/viewSearchEnquiriesAjax", method = RequestMethod.GET, headers = "Accept=application/json")
public #ResponseBody EnquiryList getEnquiriesBySearchAjax(#RequestParam String name) {
EnquiryList list = new EnquiryList();
...
return list;
}
Also check out this answer on a similar question.

Unable to fix: 'java.lang.String' to required type 'java.util.Collection'

I'm getting this error when I submit my form and cannot figure out why this is happening. I believe the taglib should be handling this. I've tried changing the value passed in my jsp to itemValue="id" but it has no affect.
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'content' on field 'stateCollection': rejected value [com.myapp.cmt.model.State[ id=3 ]]; codes [typeMismatch.content.stateCollection,typeMismatch.stateCollection,typeMismatch.java.util.Collection,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [content.stateCollection,stateCollection]; arguments []; default message [stateCollection]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Collection' for property 'stateCollection'; nested exception is java.lang.IllegalStateException:
Cannot convert value of type [java.lang.String] to required type [com.myapp.cmt.model.State] for property 'stateCollection[0]': no matching editors or conversion strategy found]
My jsp
<strong>State</strong><br/>
<form:checkboxes path="stateCollection" items="${states}" itemLabel="name"/>
My Content
public class Content implements Serializable {
.......
#JoinTable(name = "content_to_state", joinColumns = {
#JoinColumn(name = "content_id", referencedColumnName = "id")}, inverseJoinColumns = {
#JoinColumn(name = "state_id", referencedColumnName = "id")})
#ManyToMany
private Collection<State> stateCollection;
.....
#XmlTransient
public Collection<State> getStateCollection() {
return stateCollection;
}
public void setStateCollection(Collection<State> stateCollection) {
this.stateCollection = stateCollection;
}
.....
My Controller
...
#RequestMapping(value = "/{guid}/save", method = RequestMethod.POST)
public ModelAndView saveContent(#ModelAttribute("content") Content content, #PathVariable("guid") String guid) {
try {
// Save the modified object
contentService.save(content);
} catch (IllegalOrphanException ex) {
...
My content service
...
#Transactional
public void save(Content content) throws IllegalOrphanException, NonexistentEntityException, RollbackFailureException, Exception {
try {
utx.begin();
em.merge(content);
utx.commit();
} catch (Exception ex) {
} finally {
if (em != null) {
em.close();
}
}
}
...
Your title isn't correct. You have declared a Collection<State> your input is a String. Spring couldn't know how to make a State from a String, you have to tell it. Please see this question: Converting from String to custom Object for Spring MVC form Data binding?
I had the same problem. i'm using Spring, Hibernate.
I have one class with composite primary key and pass two parameters in request, my mistake was:
#Entity
#Table(name = "TAREAS")
public class Tarea implements Serializable {
private static final long serialVersionUID = 1L;
protected TareaPK clave;
private String descripcion;
.....
}
the controller:
#RequestMapping(value = "/tareas", params = {"clave", "tipot"}, method = RequestMethod.GET)
public String formularioTareaEditar(
#RequestParam(value = "clave") String clave,
#RequestParam(value = "tipot") String tipoTrabajo,
Model model) {
Tarea tarea = catalogoService.getTarea(tipoTrabajo, clave);
model.addAttribute(tarea);
return "tarea/editar";
}
#RequestMapping(value = "/tareas", params = {"clave", "tipot"}, method = RequestMethod.POST)
public String tareaEditar(#Valid #ModelAttribute Tarea tarea, BindingResult result) {
if (result.hasErrors()) {
return "tarea/editar";
} else {
catalogoService.edit(tarea);
return "redirect:/tareas";
}
}
So... when the info gets in the controller the parameter clave is considered as if the object TareaPK of the primary key.
i just change the name of the parameter in my controller.
#RequestMapping(value = "/tareas", params = {"txt_clave", "tipot"}, method = RequestMethod.GET)
public String formularioTareaEditar(...){
...
}

Resources