Trying to bind request parameters to nested object with spring controller using the dot notation and I keep getting a bad request error - spring

I have searched and everything seems to say as long as you use spring 4+ I should be able to use dot notation to bind request parameters to a pojo.
This is what my request looks like:
And this is what my controller looks like:
And my dto:
I even tried adding #RequestParam("p.page") int page in the controller to make sure my endpoint was getting hit and it does. Am I missing something obvious or am I not allowed to use dot notation to populate a pojo with a spring controller?
And the parent class:
public class JhmPageableDto
{
private String query;
private int page;
private int size;
private String sort;
private boolean sortAsc;
public String getQuery()
{
return query;
}
public void setQuery(String query)
{
this.query = query;
}
public int getPage()
{
return page;
}
public void setPage(int page)
{
this.page = page;
}
public int getSize()
{
return size;
}
public void setSize(int size)
{
this.size = size;
}
public String getSort()
{
return sort;
}
public void setSort(String sort)
{
this.sort = sort;
}
public boolean isSortAsc()
{
return sortAsc;
}
public void setSortAsc(boolean sortAsc)
{
this.sortAsc = sortAsc;
}
}

Related

Spring Data JPA JpaRepository only uses No Arg Constructor

I have this simple REST API that i created with Spring Boot.
In this app, I have a a POJO called Expense with 4 fields. I have a no Argument constructor and another constructor that takes only two inputs. One String value "item" and one Integer value "amount". The date is set using the LocalData.now() method and the id is set automatically in a MySql db running in the server.
Here's my Entity class
#Entity
public class Expense {
#Id
#GeneratedValue (strategy = GenerationType.AUTO)
private Integer id;
private String date;
private String item;
private Integer amount;
//No Arg Construction required by JPA
public Expense() {
}
public Expense(String item, Integer amount) {
this.date = LocalDate.now().toString();
this.item = item;
this.amount = amount;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public Integer getAmount() {
return amount;
}
public void setAmount(Integer amount) {
this.amount = amount;
}
}
I have another class with RestController annotation where i have set a method to post Expense object with a post method using Request Mapping annotation.
#RestController
public class ExpController {
private ExpService expService;
private ExpenseRepo expenseRepo;
#Autowired
public ExpController(ExpService expService, ExpenseRepo expenseRepo) {
this.expService = expService;
this.expenseRepo = expenseRepo;
}
#RequestMapping(path = "/addExp", method=RequestMethod.POST)
public void addExp(Expense expense){
expenseRepo.save(expense);
}
}
Now finally i am using PostMan to make the HTTP Post Request. I have made a simple Json Format text to send Item and Amount
{
"item":"Bread",
"amount": 75
}
After I make the post request, all i can see is that a new Entry is created but all values are set to null.
I have done some experimentation and found out that the expenseRepo.save(expense) method is only using the default no Arg constructor to save the data. But it's not using the second constructor that takes the two parameters that I am passing through Postman
How to solve this issue. Please help
Change your controller method like this
#RequestMapping(path = "/addExp", method=RequestMethod.POST)
public void addExp(#RequestBody Expense expense){
expenseRepo.save(expense);
}
You need to use #RequestBody

Dynamic MongoDB collection in spring boot

I want to create a MongoDB collection for each month dynamically.
Example: viewLog_01_2018, viewLog_02_2018
#Document(collection = "#{viewLogRepositoryImpl.getCollectionName()}")
#CompoundIndexes({
#CompoundIndex(def = "{'viewer':1, 'viewed':1}", name = "viewerViewedIndex",unique=true)
})
public class ViewLog {
private Integer viewer;
private Integer viewed;
private Date time;
public Integer getViewer() {
return viewer;
}
public void setViewer(Integer viewer) {
this.viewer = viewer;
}
public Integer getViewed() {
return viewed;
}
public void setViewed(Integer viewed) {
this.viewed = viewed;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
}
The implementation for the collection name is as follows:
#Repository
public class ViewLogRepositoryImpl implements ViewLogRepositoryCustom {
private String collectionName;
public ViewLogRepositoryImpl() {
CommonUtility common = new CommonUtility();
Pair<Integer, Integer> pair = common.getStartingEndingDateOfMonth();
setCollectionName("viewLog_"+pair.getFirst()+"_"+pair.getSecond());
}
#Override
public String getCollectionName() {
return collectionName;
}
#Override
public void setCollectionName(String collectionName) {
this.collectionName = collectionName;
}
}
On my each request, to save a document, I am setting the collection name as:
#Autowired
ViewLogRepository viewLogRepository;
public boolean createLog(int viewer, int viewed,String viewed_mmm, Date time){
CommonUtility common = new CommonUtility();
Pair<Integer, Integer> pair = common.getStartingEndingDateOfMonth();
viewLogRepository.setCollectionName("viewLog_"+pair.getFirst()+"_"+pair.getSecond());
ViewLog viewLog = new ViewLog();
viewLog.setViewer(viewer);
viewLog.setViewed(viewed);
viewLog.setTime(time);
ViewLog viewLog2 = viewLogRepository.save(viewLog);
return true;
}
The problem I am facing is that I when for the first time I up my service the mongo collection that is created has the unique attribute for the fields 'viewer' and 'viewed' but for any subsequent collection that is created dynamically, the document does not have the unique constraint and multiple entries of same viewer-viewed combination are able to be inserted.
Any help will be very much appreciated.

How can I inject #PathVariable into a field?

Let's say I have following controller.
#RestController()
#RequestMapping("/my/{path}")
public class MyController {
public void some1(#PathVariable("path") String path) {
}
public void some2(#PathVariable("path") String path) {
}
public void some3(#PathVariable("path") String path) {
}
}
Now I want the path be injected into a field.
// I usually do this with JAX-RS
#RequestScope // I added this!
#RestController()
#RequestMapping("/my/{path}")
public class MyController {
public void some1() {
}
public void some2() {
}
public void some3() {
}
// single declaration for all methods
// I know ElementType.FIELD is not one of #PathVariable's target
// Is there any equivalent way to do this with Spring?
#PathVariable("path")
String path
}
Not compiles.
How can I do this?
request url : /a/b/c
#RequestMapping(value = "/a/{some}/c")
public void some(#PathVariable("some") String some) {
}
#PathVariable Annotation which indicates that a method parameter should be bound to a URI template variable.
Examples :
#ResponseBody
RequestMapping(value="/myapp/{id}")
public String method(#PathVariable("id") int id){
return "id="+id;
}
#ResponseBody
#RequestMapping(value="/myapp/{id:[\\d]+}/{name}")
public String method(#PathVariable("id") long id, #PathVariable("name") String name){
return "id= "+id+" and name="+name;
}
Refer more for below links :
Spring mvc #PathVariable
https://www.journaldev.com/3358/spring-requestmapping-requestparam-pathvariable-example

managedbean live cycle listener

I use a managed bean with a view scoped because I need it ( for a p:datatable with lazy and selection modes ), but i'd like to do some things into this managed bean at the render response phase, is there a convenient way to do that ?
I need a view scoped to save a lazyDataModel, but I noticed that the rowCount method is called several times, each time a request ( Select count ) is executed.
So I deciced to save the result of the request. But if I add a data and refresh the datatable with ajax, the row count still contain the same result because of the scope. I use a boolean to know if the rowcount method has been already executed and I can set this boolean to false each time I add or remove a data but if I could do that a the render response phase it would be more convenient for me.
#ManagedBean
#ViewScoped
public class ListBean {
protected MyLazyDataModel myLazyDataModel = new MyLazyDataModel();
public MyLazyDataModel getMyLazyDataModel() {
return myLazyDataModel;
}
public void reloadList() {
this.reloadList = true;
}
protected class MyLazyDataModel extends LazyDataModel{
private int rowCount ;
#Override
public List load(int first, int pageSize, String sortField,
SortOrder sortOrder, Map<String, Object> filters) {
...
}
#Override
public Object getRowKey(Type object) {
...
}
#Override
public Type getRowData(String rowKey) {
...
}
#Override
public int getRowCount() {
if( reloadList ) {
this.rowCount = getDao().getRowCount().intValue();
reloadList = false;
}
return rowCount;
}
}
}
Thanks.

Is there something like #PostPostRequest?

I often want to refine posted data before use it, for example
public class Song() {
public String[] tags;
public String csvTags;
public void setTagsWithCsv() {
// this one should be more complicated for handling real data
this.tags = csvTags.split(",");
}
}
In this case, I have to call setTagsWithCsv method inside the method of the controller class.
#RequestMapping(value = "/song/create", method = POST)
public String createSong(Song song) {
song.setTagsWithCsv();
songService.create(song); // some code like this will come here
...
}
Is there any way to call the method with an annotation like '#PostConstruct'? The method should be called after a post request.
Maybe you just provided a bad example, but If your Song is in a form of POJO, you do it on a call to setCsvTags
public class Song {
private String[] tags;
private String csvTags;
public void setCsvTags(String csvTags) {
this.csvTags = csvTags;
this.tags = csvTags.split(",");
}
public void setTags(String[] tags) {
this.tags == tags;
String newCsvTags = Arrays.toString(tags);
this.csvTags = newCsvTags.substring(1, newCsvTags.length() - 1); // get rid of []
}
}
or make a method, without keeping explicit tags array
public class Song {
private String csvTags;
public void getTags() {
return csvTags.split(",");
}
}
Otherwise, there is no standard way of doing this, you can play with request interception before reaching your Controller, but I think it would be just a waste of time.

Resources