How can get the object in thymeleaf select option? - spring

Here in autocomplete I get the Product name as expected.
I want to do some calculation based on the product selected. But in doCalculation function i'm getting id instead of 'price'. So calculation not working as expected.
Suppose if i change String idExpression = "#{price}"; then calculation works as expected but Order not saved. Since getting error as below
Failed to convert property value of type [java.lang.String] to required type [com.myapp.domain.Product] for property product; nested exception is
org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [#javax.persistence.OneToOne
#io.springlets.format.EntityFormat com.myapp.domain.Product] for value 2500; nested exception is java.lang.IllegalStateException: Parsers are not allowed to return null: io.springlets.format.EntityParser#2201ba1c
So I want to get the price for calculation at the same time the save feature should not be broken. For now either 1st or 2nd is working for me.
ProductsCollectionThymeleafController.java
#GetMapping(produces = MediaType.APPLICATION_JSON_VALUE, name = "select2", value = "/s2")
#ResponseBody
public ResponseEntity<Select2DataSupport<Product>> select2(GlobalSearch search, Pageable pageable,
Locale locale) {
Page<Product> products = getProductService().findAll(search, pageable);
String idExpression = "#{id}";
Select2DataSupport<Product> select2Data =
new Select2DataWithConversion<Product>(products, idExpression, getConversionService());
return ResponseEntity.ok(select2Data);
}
OrderCollectionThymeleafController.java
#PostMapping(name = "create")
public ModelAndView create(#Valid #ModelAttribute Order order, BindingResult result,
Model model) {
if (result.hasErrors()) {
populateForm(model);
return new ModelAndView("/order/create");
}
Order newOrder = getOrderService().save(order);
UriComponents showURI = getItemLink().to(OrderItemThymeleafLinkFactory.SHOW)
.with("order", newOrder.getId()).toUri();
return new ModelAndView("redirect:" + showURI.toUriString());
}
orderview.html
<form class="form-horizontal validate" method="POST" data-th-object="${order}" data-th-action="#{${collectionLink.to('create').with('order', order.id)}}">
<fieldset id="containerFields">
<div class="form-group has-error has-feedback" data-z="3c00987d" id="servicio-product-field" data-th-classappend="${#fields.hasErrors('product')}? 'has-error has-feedback'" data-th-class="form-group" data-th-with="collectionLink=${#linkBuilder.of('ProductsCollectionThymeleafController')}">
<label for="product" class="col-md-3 control-label" data-th-text="#{label_servicio_product}">Product</label>
<div class="col-md-6">
<!-- Select2 -->
<select data-th-field="*{product}" onChange="doCalculation()" class="form-control dropdown-select-ajax" data-allow-clear="true" data-data-ajax--url="${collectionLink.to('select2')}" data-ajax--cache="true" data-ajax--delay="250" data-ajax--data-type="json" data-data-placeholder="#{info_select_an_option}">
<option data-th-unless="*{product} == null" data-th-value="*{product.id}" data-th-text="*{{product}}" selected="selected">Product</option>
</select>
<span data-th-classappend="${#fields.hasErrors('product')}? 'glyphicon glyphicon-remove form-control-feedback'" class="glyphicon glyphicon-remove form-control-feedback" data-th-if="${#fields.hasErrors('product')}" aria-hidden="true"></span>
<span id="product-error" class="help-block" data-th-if="${#fields.hasErrors('product')}" data-th-errors="*{product}">Error message.</span>
</div>
</div>
<script>
function doCalculation() {
var price = document.getElementById("product").value;
alert("price: " + price);
// Do some calculation
}
doCalculation();
</script>
</fieldset>
</form>
Product.java
#RooJavaBean
#RooToString
#RooJpaEntity
#RooEquals(isJpaEntity = true)
#Entity
#EntityFormat
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String productName;
#Min(1L)
#NumberFormat
private Integer price;
#OneToOne(fetch = FetchType.LAZY)
#EntityFormat
private Order order;
public static final String ITERABLE_TO_ADD_CANT_BE_NULL_MESSAGE = "The given Iterable of items to add can't be null!";
public static final String ITERABLE_TO_REMOVE_CANT_BE_NULL_MESSAGE = "The given Iterable of items to add can't be null!";
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getPrice() {
return this.price;
}
public void setPrice(Integer price) {
this.price = price;
}
public String getProductName() {
return this.productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public Order getOrder() {
return this.order;
}
public void setOrder(Order order) {
this.order= order;
}
}
Order.java
#RooJavaBean
#RooToString
#RooJpaEntity
#RooEquals(isJpaEntity = true)
#Entity
#EntityFormat
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Version
private Integer version;
#OneToOne(cascade = { javax.persistence.CascadeType.MERGE,
javax.persistence.CascadeType.PERSIST }, fetch = FetchType.LAZY, mappedBy = "order")
#RooJpaRelation(type = JpaRelationType.AGGREGATION)
#EntityFormat
private Product product;
public static final String ITERABLE_TO_ADD_CANT_BE_NULL_MESSAGE = "The given Iterable of items to add can't be null!";
public static final String ITERABLE_TO_REMOVE_CANT_BE_NULL_MESSAGE = "The given Iterable of items to add can't be null!";
/**
* This `equals` implementation is specific for JPA entities and uses the
* entity identifier for it, following the article in
* https://vladmihalcea.com/2016/06/06/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
*
* #param obj
* #return Boolean
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
// instanceof is false if the instance is null
if (!(obj instanceof Order)) {
return false;
}
return getId() != null && Objects.equals(getId(), ((Order) obj).getId());
}
/**
* This `hashCode` implementation is specific for JPA entities and uses a
* fixed `int` value to be able to identify the entity in collections after
* a new id is assigned to the entity, following the article in
* https://vladmihalcea.com/2016/06/06/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
*
* #return Integer
*/
public int hashCode() {
return 31;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getVersion() {
return this.version;
}
public void setVersion(Integer version) {
this.version = version;
}
public Product getProduct() {
return this.product;
}
public void setProduct(Product product) {
this.product = product;
}
public void addToProduct(Product product) {
if (product == null) {
removeFromProduct();
} else {
this.product = product;
product.setOrder(this);
}
}
public void removeFromProduct() {
if (this.product != null) {
product.setOrder(null);
}
this.product = null;
}
}

By default, the Select2DataWithConversion data type only returns the identifier that will be set as value attribute of the option element and the representation of the object (in your case the product name) as the text attribute of the option element.
That is the minimum info that the select2 component needs to be constructed.
https://select2.org/data-sources/formats
However, as you described in your answer, it's really common to need more info in your Select2 component. For that reason, we overloaded the constructor of Select2DataWithConversion including a boolean parameter to return the entire information of the object.
Check this overloaded constructor here:
https://github.com/DISID/springlets/blob/master/springlets-data/springlets-data-commons/src/main/java/io/springlets/data/web/select2/Select2DataWithConversion.java#L76
So, you just need to change your ProductsCollectionThymeleafController.java to use it like:
Select2DataSupport<Product> select2Data = new Select2DataWithConversion<Product>(products, idExpression, getConversionService(), true);
Now that yor select2 component is going to receive extra information, you need to store it in a data-* attribute of your select2 option during the option creation. To do that use the templateSelection function that offers the select2 component.
https://select2.org/programmatic-control/retrieving-selections#using-a-jquery-selector
Now, your doCalculation should obtain the selected option and after that, the data-price attribute.
<script>
function doCalculation() {
var price = $('#product').find(':selected').data('price');
alert("price: " + price);
//Do some calculation
}
doCalculation();
</script>
And that's all!
EDIT: I've just create the following project where you could find your desired behaviour: https://github.com/jcagarcia/proofs/tree/master/select2-with-extra-info
Just check the necessary changes in the following commit: https://github.com/jcagarcia/proofs/commit/105c18f7ad0da4d1e2089fbf71d4f27ccdb60689
Hope it helps,

Related

How do I insert values of elements that are part of the EmbeddedId in JPA?

I have a case where I need to execute an insert statement via createNativeQuery. I have an entity list I'm looping through in order to set the properties accordingly from another bean class, and then persist that data to the oracle database.
The problem I am facing is persisting the data that is part of the embeddedId (item, loc, weekstart, type, forecastId, insertTS). I need to persist that data for the new records to be inserted into the database. When I try to set the values from the POJO bean to my set method for the properties of my entity bean, nothing happens. Below is my code for setting the values of the properties from the POJO bean to my entity bean, along with my persistence method and the insert query being executed:
Validation class where validation occurs beforehand (missing to get the point) that includes the setting of my entity properties from the POJO bean:
List <InsertPromoData> insertPromos = new ArrayList<InsertPromoData>();
promo.forEach(record -> {
if (record.getErrorList().size() == 0) {
rowsSuccessful++;
Util.writeSuccessToFile(templateCd, successFile, record, successFields);
try {
InsertPromoData insertData = new InsertPromoData();
insertData.getId().setItem(record.getItem());
insertData.getId().setLoc(record.getLoc());
insertData.getId().setWeekStart(record.getWeek_Start_Date());
insertData.setNumberOfWeeks(record.getNumber_Of_Weeks());
insertData.getId().setType(record.getType());
insertData.getId().setForecastId(record.getForecast_ID());
insertData.setQty(record.getUnits());
insertPromos.add(insertData);
}
catch (Exception e) {
logger.error("Error with setting insertPromolist from promo list values and the error is " + e.getMessage());
}
}
else {
if (rowsFailure == 0) {
Util.writeHeaderToFile(templateCd, errorFile);
}
rowsFailure++;
Util.writeErrorToFile(templateCd, errorFile, record, record.getErrorList());
}
});
errorFile.close();
successFile.close();
OracleImpl.insertPromoData(insertPromos);
POJO bean (promo is the variable representing this list of beans in validation class above):
public class PromoBean extends ErrorListBean
{
public String Item;
public String Loc;
public String Week_Start_Date;
public String Units;
public String Forecast_ID;
public String Type;
public String Number_Of_Weeks;
public String getItem() {
return Item;
}
public void setItem(String item) {
Item = item;
}
public String getLoc() {
return Loc;
}
public void setLoc(String loc) {
Loc = loc;
}
public String getWeek_Start_Date() {
return Week_Start_Date;
}
public void setWeek_Start_Date(String week_Start_Date) {
Week_Start_Date = week_Start_Date;
}
public String getNumber_Of_Weeks() {
return Number_Of_Weeks;
}
public void setNumber_Of_Weeks(String number_Of_Weeks) {
Number_Of_Weeks = number_Of_Weeks;
}
public String getType() {
return Type;
}
public void setType(String type) {
Type = type;
}
public String getForecast_ID() {
return Forecast_ID;
}
public void setForecast_ID(String forecast_ID) {
Forecast_ID = forecast_ID;
}
public String getUnits() {
return Units;
}
public void setUnits(String units) {
Units = units;
}
}
Embeddable class representing the composite primary key of the table:
#Embeddable
public class PromoID implements Serializable {
#Column(name = "ITEM")
private String item;
#Column(name = "LOC")
private String loc;
#Column(name = "WK_START")
private String weekStart;
#Column(name = "TYPE")
private String type;
#Column(name = "FCSTID")
private String forecastId;
#Column(name = "U_TIMESTAMP")
private String insertTS;
public PromoID() {
}
public PromoID (String item, String loc, String weekStart, String type, String forecastId, String insertTS) {
this.item = item;
this.loc = loc;
this.weekStart = weekStart;
this.type = type;
this.forecastId = forecastId;
this.insertTS = insertTS;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public String getWeekStart() {
return weekStart;
}
public void setWeekStart(String weekStart) {
this.weekStart = weekStart;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getForecastId() {
return forecastId;
}
public void setForecastId(String forecastId) {
this.forecastId = forecastId;
}
public String getInsertTS() {
return insertTS;
}
public void setInsertTS(String insertTS) {
this.insertTS = insertTS;
}
//hashcode and equals methods
Persistence Bean:
#Entity
#Table(name = "U_USER_PROMO")
public class InsertPromoData {
#EmbeddedId
private PromoID id;
#Column(name="NUMBER_OF_WEEKS")
String numberOfWeeks;
#Column(name="QTY")
String qty;
#Id
#AttributeOverrides(
{
#AttributeOverride(name = "item",column = #Column(name="ITEM")),
#AttributeOverride(name = "loc", column = #Column(name="LOC")),
#AttributeOverride(name = "weekStart", column = #Column(name="WK_START")),
#AttributeOverride(name = "type", column = #Column(name="TYPE")),
#AttributeOverride(name = "forecastId", column = #Column(name="FCSTID"))
}
)
public PromoID getId() {
return id;
}
public void setId(PromoID id) {
this.id = id;
}
public String getNumberOfWeeks() {
return numberOfWeeks;
}
public void setNumberOfWeeks(String numberOfWeeks) {
this.numberOfWeeks = numberOfWeeks;
}
public String getQty() {
return qty;
}
public void setQty(String qty) {
this.qty = qty;
}
}
DAO class method to execute the update (entitymanagerfactory emf already initialized):
public static void insertPromoData(List<InsertPromoData> insertData) {
logger.debug("Execution of method insertPromoData in Dao started");
System.out.println("Size of the insertData list is " + insertData.size());
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
System.out.println("Beginning transaction for insertPromoData");
Query query = em.createNativeQuery(env.getProperty("insertPromoUploadData"));
for (InsertPromoData promoData : insertData) {
query.setParameter("item", promoData.getId().getItem());
query.setParameter("location", promoData.getId().getLoc());
query.setParameter("wkStart", promoData.getId().getWeekStart());
query.setParameter("numberOfWeeks", promoData.getNumberOfWeeks());
query.setParameter("type", promoData.getId().getType());
query.setParameter("fcstId", promoData.getId().getForecastId());
query.setParameter("quantity", promoData.getQty());
query.executeUpdate();
}
em.getTransaction().commit();
}
catch(Exception e) {
logger.error("Exception in beginning transaction");
e.printStackTrace();
}
finally {
em.clear();
em.close();
}
logger.debug("Execution of method insertPromoData in Dao ended");
}
Query in properties file:
insertPromoUploadData = INSERT INTO {h-schema}U_USER_PROMO (ITEM, LOC, WK_START, NUMBER_OF_WEEKS, TYPE, FCSTID, QTY, U_TIMESTAMP) VALUES (:item, :location, TO_DATE(:wkStart,'MM DD YYYY'), :numberOfWeeks, :type, :fcstId, :quantity, SYSDATE)
My list size from my DAO class is returning as 0 once I begin the transaction and not sure why it is empty. Is there a reason that it is empty? I'm trying to persist each of the fields to the database (including the composite key fields) via insert query. Any help appreciated.
After looking into this for hours, I finally came to the conclusion that the simplest way to executeUpdate() without running into issues due to my current #EmbeddedId/#Embeddable logic was to change it to use #IdClass for my composite PK class, and annotate the fields from the PK in my entity with #Id. This allowed my data to be persisted to the database. Another slight difference was adding the insertTS in my entity class and annotating with #Id and generating getters/setters. This was necessary for JPA to recognize all the properties being referenced that I am wanting to persist, though I am persisting insertTS using SYSDATE function from the oracle DB instead of utilizing the get/set methods and setting to the current time from the java side.
I am sure there is a way to use #EmbeddedId/#Embeddable logic and be able to persist the fields that are part of the EmbeddedId, however, this I found to be a more simplistic way of doing it without further complexity in the code.

Bidirectional OneToMany-ManyToOne Relationship referencing unsaved transient instance (Spring MVC - Thymeleaf)

new here. I'm new to Spring and Thymeleaf, I'm trying to learn by following a video and I don't know why I get the following exception (org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : org.launchcode.codingevents.models.Event.eventCategory -> org.launchcode.codingevents.models.EventCategory) when I try to creat an Event giving it an EventCategory in the Thymeleaf form. I tried cascading from one side, then from the other and then from both, but it didn't work.
I'll be immensely grateful with whoever helps me out.
Here's my code.
#MappedSuperclass
public abstract class AbstractEntity {
#Id
#GeneratedValue
private int id;
public int getId() {
return id;
}
#Override
public int hashCode() {
return Objects.hash(id);
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
AbstractEntity entity = (AbstractEntity) obj;
return this.id == entity.id;
}
#Entity
public class Event extends AbstractEntity {
#NotBlank(message = "Name is required")
#Size(min = 3, max = 50, message = "Name must be between 3 and 50 characters")
private String name;
#Size(max = 500, message = "Description too long!")
private String description;
#NotBlank(message = "Email is required")
#Email(message = "Invalid email. Try again")
private String contactEmail;
#ManyToOne
#NotNull(message = "Category is required")
private EventCategory eventCategory;
public Event() {
}
public Event(String name, String description, String contactEmail, EventCategory eventCategory) {
this.name = name;
this.description = description;
this.contactEmail = contactEmail;
this.eventCategory = eventCategory;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getContactEmail() {
return contactEmail;
}
public void setContactEmail(String contactEmail) {
this.contactEmail = contactEmail;
}
public EventCategory getEventCategory() {
return eventCategory;
}
public void setEventCategory(EventCategory eventCategory) {
this.eventCategory = eventCategory;
}
#Override
public String toString() {
return name;
}
#Entity
public class EventCategory extends AbstractEntity implements Serializable {
#Size(min = 3, message = "Name must be at least 3 characters long")
private String name;
#OneToMany(mappedBy = "eventCategory")
private final List<Event> events = new ArrayList<>();
public EventCategory() {
}
public EventCategory(#Size(min = 3, message = "Name must be at least 3 characters long") String name) {
this.name = name;
}
public List<Event> getEvents() {
return events;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return name;
#Controller
#RequestMapping("events")
public class EventController {
#Autowired
private EventRepository eventRepository;
#Autowired
private EventCategoryRepository eventCategoryRepository;
#GetMapping
public String displayAllEvents(#RequestParam(required = false) Integer categoryId, Model model) {
if (categoryId == null) {
model.addAttribute("title", "All Events");
model.addAttribute("events", eventRepository.findAll());
} else {
Optional<EventCategory> result = eventCategoryRepository.findById(categoryId);
if (!result.isPresent()) {
model.addAttribute("title", "Invalid Category Id: " + categoryId);
} else {
EventCategory category = result.get();
model.addAttribute("title", "Events in Category: " + category.getName());
model.addAttribute("events", category.getEvents());
}
}
return "events/index";
}
// Lives at /events/create
#GetMapping("create")
public String displayCreateEventForm(Model model) {
model.addAttribute("title", "Create Event");
model.addAttribute(new Event());
model.addAttribute("categories", eventCategoryRepository.findAll());
return "events/create";
}
// lives at /events/create
#PostMapping("create")
public String processCreateEventForm(#Valid #ModelAttribute("newEvent") Event newEvent, Errors errors, Model model) {
if (errors.hasErrors()) {
model.addAttribute("title", "Create Event");
return "events/create";
}
model.addAttribute("events", eventRepository.findAll());
eventRepository.save(newEvent);
return "redirect:";
}
// lives at /events/delete
#GetMapping("delete")
public String displayDeleteEventForm(Model model) {
model.addAttribute("title", "Delete Events");
model.addAttribute("events", eventRepository.findAll());
return "events/delete";
}
// lives at /events/delete
#PostMapping("delete")
public String processDeleteEventForm(#RequestParam(required = false) int[] eventIds) {
if (eventIds != null) {
for (int id : eventIds) {
eventRepository.deleteById(id);
}
}
return "redirect:";
}
}
Create Event
<nav th:replace="fragments :: navigation"></nav>
<form method="post" th:action="#{/events/create}" th:object="${event}">
<div class="form-group">
<label>Name
<input class="form-control" th:field="${event.name}">
</label>
<p class="error" th:errors="${event.name}"></p>
</div>
<div class="form-group">
<label>Description
<input class="form-control" th:field="${event.description}">
</label>
<p class="error" th:errors="${event.description}"></p>
</div>
<div class="form-group">
<label>Contact Email
<input class="form-control" th:field="${event.contactEmail}">
</label>
<p class="error" th:errors="${event.contactEmail}"></p>
</div>
<div class="form-group">
<label>Category
<select th:field="${event.eventCategory}">
<option th:each="eventCategory : ${categories}" th:value="${eventCategory.id}"
th:text="${eventCategory.name}">
</option>
</select>
<p class="error" th:errors="${event.eventCategory}"></p>
</label>
</div>
<div th:replace="fragments :: create-button"></div>
</form>
As per your code you are only trying to save Event entity and ignoring EventCategory.
You need to set Event to EventCategory as well as EventCategory to Event and make the cascade save.
First add cascade property in Event entity as below.
#ManyToOne(cascade = CascadeType.ALL)
#NotNull(message = "Category is required")
private EventCategory eventCategory;
Then in the Controller make the following changes.
#PostMapping("create")
public String processCreateEventForm(#Valid #ModelAttribute("newEvent") Event newEvent, Errors errors, Model model) {
if (errors.hasErrors()) {
model.addAttribute("title", "Create Event");
return "events/create";
}
model.addAttribute("events", eventRepository.findAll());
EventCategory eventCategory = newEvent.getEventCategory();
eventCategory.setEvent(newEvent);
eventRepository.save(newEvent);
return "redirect:";
}

org.springframework.web.bind.MissingServletRequestParameterException: Required int parameter

Hi i am new for WebServices and In my My-Sql Database I have student table with some columns those are "user_id", and "name" and "marks"
I want to update one row based on userId for this i wrote below code but i am getting exception like below can some one help me please
Controller [com.ensis.sample.controller.SampleController]
Method [public com.ensis.sample.model.StatusObject com.ensis.sample.controller.SampleController.updateStudentListById(int)]
org.springframework.web.bind.MissingServletRequestParameterException: Required int parameter 'userId' is not present
controller:-
#RequestMapping(value="/update",method=RequestMethod.POST,produces={"application/json"})
#ResponseBody
public StatusObject updateStudentListById(#RequestParam int userId){
return userService.updateStudentDetailsById(userId);
}
UserService:-
#Transactional
public StatusObject updateStudentDetailsById(int id){
Users users = usersdao.updateStudentDetailsById(id);
if(users!=null){
users.setName("Sample");
users.setMarks(99.99);
}
StatusObject statusObject = new StatusObject();
boolean status = usersdao.updateUser(users);
if(status==true){
statusObject.setStatus(false);
statusObject.setMessage("Success");
return statusObject;
}else{
statusObject.setStatus(true);
statusObject.setMessage("Failure");
return statusObject;
}
}
UserDao:-
public Users updateStudentDetailsById(int userId){
System.out.println("UserId is=====>"+userId);
String hql = "FROM Users s WHERE " + "s.user_id = :userId";
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery(hql);
query.setParameter("user_id", userId);
List<?>list = query.list();
Iterator<?>itr = list.iterator();
if(itr.hasNext()){
Users users = (Users)itr.next();
return users;
}
session.flush();
session.clear();
return null;
}
Users:-
#Entity
#Table(name = "student")
public class Users {
#Id
private int user_id;
private String name;
private int rank;
private double marks;
public int getUser_id() {
return user_id;
}
public void setUser_id(int user_id) {
this.user_id = user_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
public double getMarks() {
return marks;
}
public void setMarks(double marks) {
this.marks = marks;
}
#Krish, when you are posting something, you usually use Spring's #RequestBodyas seen below:
#RequestMapping(value="/update",method=RequestMethod.POST,produces={"application/json"})
#ResponseBody
public StatusObject updateStudentListById(#RequestBody User user){
return userService.updateStudentDetailsById(userId);
}
You need to pass the JSON object to this controller method. Spring will deserialize the JSON for you.
When you say #RequestParam, it expects to find the request parameters like
/update?userId=1
PS: It is not good practice to send just the ID to update a resource.
Are you using it as a RestController.The excecption is coming from the controller as it expects a parameter from the client.Please verify if you are passing the userID in the pathParam.

Spring Mvc display session in jsp

I have created a simple shopping cart project using spring framework. I've created a button add to cart so that when clicked it will create session of that item. I have following codes
<button href="/addcart/1">Add to button</button>
This is my controller
#RequestMapping("/addcart/{id}")
public ModelAndView goCart(#PathVariable("id")int id,HttpServletRequest request, HttpSession session) {
List<CartItem> cart = new ArrayList<CartItem>();
cart.add(new CartItem(productService.findProductCart(id),1));
session.setAttribute("cart", cart);
ModelAndView model = new ModelAndView();
model.setViewName("cart");
return model;
}
this is my CartItem class
public class CartItem {
private List<Product> product;
private int quantity;
public List<Product> getProduct() {
return product;
}
public void setProduct(List<Product> product) {
this.product = product;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public CartItem(List<Product> product, int quantity) {
super();
this.product = product;
this.quantity = quantity;
}
public CartItem() {
super();
}
}
this is my Product class
public class Product {
private int pid;
private String productName;
private int unitPrice;
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public int getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(int unitPrice) {
this.unitPrice = unitPrice;
}
}
I've a productServiceImpl class of this productDaoImpl
#Override
public List<Product> findProductCart(int id) {
List<Product> cartProduct = new ArrayList<Product>();
String sql = "select * from product where pid= " + id;
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
cartProduct = jdbcTemplate.query(sql, new ProductRowMapper());
return cartProduct;
}
I've view in jsp with following code
<c:forEach var="item" items="${sessionScope.cart}">
<li>${item.quantity}</li>
<li>${item}</li>
</c:forEach>
this give
1
com.sparktronix.mvc.domain.CartItem#344e01
How to display session value productname and unitprice? Any suggestion are welcomed. Thanks in advance
You are almost there! You just have to get the product object from sessionScope and iterate once again like below.
<c:forEach var="item" items="${sessionScope.cart}">
<li>${item.quantity}</li>
<c:forEach items = "${item.product}" var="product">
<li>${product.productName}</li>
<li>${product.unitPrice}</li>
</c:forEach>
</c:forEach>
Hope this helps!

Spring Boot Adding Model to the View with Thymeleaf and MVC

Ok, so I'm trying to put an attribute of an object from the model to the view as a list using thymeleaf, spring boot and jpa, I've been reading over the code for hours and I can't seem to spot my problem, also in the same application I have a very similar function working so I sort of know how to do it, but I just cannot seem to figure out this one. I keep getting an error Property or field 'question' cannot be found on null. I have no idea where I'm going wrong. The object I'm have is called QuestionAnswerSet, and I have a question string and an answer string in the database, that I can submit through the app, so it's not a problem with the database. Also everything is good with my pom file because as I said earlier I have done a very similar function.
Here's my controller.
#Controller
public class QuestionAnswerSetController
{
private QuestionAnswerSetRepository questionAnswerSetRepo;
#RequestMapping("sets")
public String sets (ModelMap model)
{
List<QuestionAnswerSet> questionAnswerSets = questionAnswerSetRepo.findAll();
model.put("questionAnswerSets", questionAnswerSets);
return "sets";
}
#RequestMapping(value="editSet/{questionAnswerSetId}", method=RequestMethod.GET)
public String editSetGet (#PathVariable Long questionAnswerSetId, ModelMap model)
{
return "editCourse";
}
#RequestMapping(value="createSet", method=RequestMethod.GET)
public String createSetGet (ModelMap model)
{
QuestionAnswerSet questionAnswerSet = new QuestionAnswerSet();
model.put("questionAnswerSet", questionAnswerSet);
return "createSet";
}
#RequestMapping(value="createSet", method=RequestMethod.POST)
public String createSetPost (#ModelAttribute QuestionAnswerSet questionAnswerSet, ModelMap model)
{
questionAnswerSetRepo.save(questionAnswerSet);
return "redirect:/sets";
}
#Autowired
public void setQuestionAnserSetRepo(QuestionAnswerSetRepository questionAnserSetRepo) {
this.questionAnswerSetRepo = questionAnserSetRepo;
}
}
Here's my html
<div th:each="Set : ${questionAnswerSets}" th:object="${questionAnswerSet}">
<span th:text="${questionAnswerSet.question}"></span>
</div>
<div th:if="${#lists.isEmpty(questionAnswerSets)}">
There is no sets to display.
</div>
Here's my repository, it's pretty standard, just though I would include it
public interface QuestionAnswerSetRepository extends JpaRepository<QuestionAnswerSet, Long> {
}
And here's my QuestionAnswerSet.java object, which is what I'm trying to return as a list
#Entity
public class QuestionAnswerSet {
private Long id;
private String question;
private String answer;
private User user;
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
public String getAnswer() {
return answer;
}
public void setAnswer(String answer) {
this.answer = answer;
}
#ManyToOne
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
And Here's the error in my console
org.springframework.expression.spel.SpelEvaluationException: EL1007E:(pos 0): Property or field 'question' cannot be found on null
Yup, that should be pretty straightforward, here is the exception :
Property or field 'question' cannot be found on null
Spring EL tries to evaluate the below :
<div th:each="Set : ${questionAnswerSets}" th:object="${questionAnswerSet}">
<span th:text="${questionAnswerSet.question}"></span>
</div>
And it is unable to find questionAnswerSet ,which is null hence the error.
Use something like this :
<div th:each="questionAnswerSet : ${questionAnswerSets}">
<span th:text="${questionAnswerSet.question}"></span>
</div>
Refer Doc :
http://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#using-theach

Resources