How to populate multiple beans in a request using Spring #MVC - java

I'm developing a project using Spring #MVC (with MVC annotations).
If all request parameters shall be populated to a single bean everything seems fine, but what about multiple POJOs?
I have searched the web and am aware of form-backing-objects, but how can I use them in #MVC (annotation-based)?
Another question: shall I construct a bean for each form? Doesn't it just look like Strut's ActionForms? Is there anyway to prevent creating these objects?
Is there a way, to put all beans in a Map and ask Spring binder to populate them? Something like:
map.put("department", new Department());
map.put("person", new Person());
so department.name and department.id bind into department bean, and person.name, person.sex and ... populate in the person bean? (So the controller method accepts a Map as its parameter).

Form backing objects are not mandatory, you can use #RequestParam annotation to obtain the form values directly. See Binding request parameters to method parameters with #RequestParam on Spring Manual.
I don't think Map is a supported by the default Spring MVC type converters, but you can register a custom converter. See Customizing WebDataBinder initialization.

If you give Person a reference of Department then it will be easy. In your app, if the person works in a department it will be logical to create a Has-A relationship in your Person class like this:
#Component
#Scope("prototype")
public class Person {
private String firstName;
private Department department;
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
You can create a Controller that gets a Person bean from the Context and renders a view.
#Controller
public class TestController implements ApplicationContextAware{
private ApplicationContext appContext;
#RequestMapping(value="/handleGet",method=RequestMethod.GET)
public String handleGet(ModelMap map){
map.addAttribute("person", appContext.getBean("person"));
return "test";
}
#RequestMapping(value="/handlePost",method=RequestMethod.POST)
public #ResponseBody String handlePost(#ModelAttribute("person") Person person){
return person.getDepartment().getDepartmentName();
}
#Override
public void setApplicationContext(ApplicationContext appContext)
throws BeansException {
this.appContext=appContext;
}
}
Then inside your JSP view you can write something like this:
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%# taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Test</title>
</head>
<body>
<sf:form commandName="person" action="/appname/handlePost.html" method="post">
<sf:input path="firstName"/>
<sf:input path="department.departmentName"/>
<sf:button name="Submit">Submit</sf:button>
</sf:form>
</body>
</html>

Related

How do I add a POJO to a list using Spring/Thymeleaf form?

I have a very simply object called Move:
public class Move {
private String move;
public String getMove() {
return move;
}
public void setMove(String move) {
this.move = move;
}
}
I also have a move repository(list of all moves):
#Component
public class MoveRepository {
private List<Move>allMoves;
public void addMove(Move move){
allMoves.add(move);
}
public MoveRepository() {
this.allMoves = new ArrayList<>();
}
public void setAllMoves(List<Move> allMoves) {
this.allMoves = allMoves;
}
public List<Move> getAllMoves(){
return allMoves;
}
}
Here is my controller:
#Controller
public class MoveController {
#Autowired
private MoveRepository moveRepository = new MoveRepository();
#GetMapping("/moveList")
public String listMoves (ModelMap modelMap){
List<Move> allMoves = moveRepository.getAllMoves();
modelMap.put("moves", allMoves);
return "moveList";
}
#GetMapping("/addMove")
public String addMoveForm(Model model) {
model.addAttribute("move", new Move());
return "addMove";
}
#PostMapping("/addMove")
public String addMoveSubmit(#ModelAttribute Move move) {
moveRepository.addMove(move); //Producing an error
return "moveAdded";
}
}
Basically, I want to add the move submitted using the form on the webpage "/addMove" to the list of moves allMoves in the Move Repository. However, it produces a 500 server error whenever I click the submit button on the webpage. If I delete the
moveRepository.addMove(move);
from my code then everything works fine, but then of course the move does not get added to the move repository.
I also have my html(using thymeleaf) code posted below for reference:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Add Move</title>
</head>
<body>
<h1>Add a move</h1>
<form action = "#" th:action="#{/addMove}" th:object="${move}" method = "post">
<p>Move: <input type = "text" th:field="*{move}"/></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
</body>
</html>
Welcome to SO.
Change
#Autowired
private MoveRepository moveRepository = new MoveRepository();
to
#Autowired
private MoveRepository moveRepository;
The idea is that Spring should handle instantiation of objects through dependency injection. Also, make sure that your annotations are picked up with component scanning (either in your XML or Java config).
Other tips:
It isn't so helpful to have the property named move in a class of
the same name. More descriptive naming would be much better for the
next person reading your code.
If your team allows it, try Project Lombok to
get rid of the boilerplate code. Then you can just do:
public class Move {
#Getter
#Setter
private String move;
}
or even better:
#Data
public class Move {
private String move;
}
Consider annotating your repository with #Repository if you're planning to persist to a database. This will also give you some exception handling features through Spring.

solr exception Expected mime type application/octet-stream but got text/html

Hi I am trying to index an entity in solr using spring boot data solr. But i am getting exception
Caused by: org.apache.solr.client.solrj.impl.HttpSolrClient$RemoteSolrException: Error from server at http://localhost:8983/solr/portal: Expected mime type application/octet-stream but got text/html. <html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Error 404 Not Found</title>
</head>
<body><h2>HTTP ERROR 404</h2>
<p>Problem accessing /solr/portal/publishers/update. Reason:
<pre> Not Found</pre></p><hr><i><small>Powered by Jetty://</small></i><hr/>
</body>
</html>
This is my publisher class
#SolrDocument
public class Publishers {
#Id
#Field
private String advID;
#Field
private String name;
#Field
private String domain;
#Field("cat")
private String categories;
//getters and setters
}
My repository interface
public interface BookRepository extends SolrCrudRepository<Publishers, String> {
List<Publishers> findByName(String name);
}
and my base URL is "http://localhost:8983/solr/portal". In my main class code is like below
#Resource
BookRepository repository;
#Override
public void run(String... strings) throws Exception {
this.repository.save(new Publishers("4", "Sony Playstation","none", null));
}
UPDATED
I have observed that the entity class i.e Publishers name is automatically appending to the URL. I don't know how to tell application not to append the name in solr URL while sending a call to solr.
can anyone help me. Thanks in advance
You should annotate your solr document as #SolrDocument(solrCoreName="YOU_CORE_NAME")
check you URL is up and correct.

JSF how to store, with Controller, in session without persist in DB

I'm trying to make a page where the user can insert one or more Providers, check all the Providers inserted, then confirm. ONLY when the user confirms, all the Providers inserted will be stored in my Database.
How can I do that? It seems that if I use my ManagedBean Controller, with List where I will store the Providers, I get a strange error
HTTP Status 500 -
root cause
java.lang.NullPointerException
it.myProject.model.Provider.equals(Provider.java:71)
org.apache.myfaces.shared.util.SelectItemsIterator.next(SelectItemsIterator.java:275)
org.apache.myfaces.shared.util.SelectItemsIterator.next(SelectItemsIterator.java:49)
org.apache.myfaces.shared.renderkit.RendererUtils.internalGetSelectItemList(RendererUtils.java:800)
org.apache.myfaces.shared.renderkit.RendererUtils.getSelectItemList(RendererUtils.java:764)
org.apache.myfaces.shared.renderkit.html.HtmlSelectableRendererBase.internalRenderSelect(HtmlSelectableRendererBase.java:74)
org.apache.myfaces.shared.renderkit.html.HtmlMenuRendererBase.renderMenu(HtmlMenuRendererBase.java:91)
org.apache.myfaces.shared.renderkit.html.HtmlMenuRendererBase.encodeEnd(HtmlMenuRendererBase.java:76)
javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:665)
javax.faces.component.UIComponentBase.encodeAll(UIComponentBase.java:545)
javax.faces.component.UIComponentBase.encodeAll(UIComponentBase.java:541)
javax.faces.component.UIComponentBase.encodeAll(UIComponentBase.java:541)
org.apache.myfaces.shared.view.JspViewDeclarationLanguageBase.actuallyRenderView(JspViewDeclarationLanguageBase.java:364)
org.apache.myfaces.shared.view.JspViewDeclarationLanguageBase.renderView(JspViewDeclarationLanguageBase.java:201)
org.apache.myfaces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:285)
javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:59)
javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:59)
org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:116)
org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:241)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:199)
here's the code of my ProviderController
#ManagedBean
public class ProviderController{
private Long id;
private String vat;
private String address;
private List<Product> products;
private Provider provider;
private List<Provider> providers;
public ProviderController() {
this.products= new LinkedList<>();
this.providers= new LinkedList<>();
}
//adds the Provider in the c:forEach of the view newProvider.jsp
public String addProvider() {
this.provider= new Provider(vat, address, products);
this.providers.add(this.provider);
return "newProvider";
}
-I omitted the facade because I have nothing to store in this case, I just want to add the provider in the list and THEN, with another method, add all the providers of the list in my db
-Provider is a Model that uses the annotation #Entity, so my doubt is that I can't use Provider if I don't use the EntityManager
If I'm right, what can I do to store simply in session my providers? I have to create another class Provider? I have to create another ProviderController?
Thank you very much for the answers.
Keeping apart from your hypothetical persistence layer, you can store the information in the session. However, you have to differ between the information that belongs to the session and the one that does to the current view.
For your case, I think you want to have the stored providers as a session variable, while the information you send in the form when adding a new one should be in view scope. That way you divide the logic of the application in a proper way. Here you've got more information about the view scope, introduced in JSF 2.
As you're already in JSF 2, you could choose between JSP and Facelets for your view technology. If it's a new project, I encourage you to go with facelets, as it is the standard for JSF 2:
ProviderController.java
/*
* Manages providers in the session
*/
#ManagedBean
#SessionScoped
public class ProviderController{
private List<Provider> providers;
private List<Product> products;
public ProviderController() {
this.products= new LinkedList<>();
this.providers= new LinkedList<>();
}
public List<Provider> getProviders(){
return providers;
}
}
providers.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head />
<h:body>
<!-- Display all the providers from the session -->
<ul>
<ui:repeat value="#{providerController.providers}" var="provider">
<li>#{provider.vat}-#{provider.address}</li>
</ui:repeat>
</ul>
</h:body>
</html>
ProviderAdd.java
/*
* Manages the view to add a new provider
*/
#ManagedBean
#ViewScoped
public class ProviderAdd{
private Long id;
private String vat;
private String address;
//Injects the session scoped bean
#ManagedProperty(value="#{providerController}")
private ProviderController providerController;
//Getter and setters
//Adds a new provider to the session and returns to the provider list
public String addProvider() {
this.provider= new Provider(this.vat, this.address, this.products);
providerController.getProviders.add(this.provider);
return "providers";
}
}
addProvider.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head />
<h:body>
<h2>VAT:</h2><h:inputText value="#{providerAdd.vat}" />
<h2>Address:</h2><h:inputText value="#{providerAdd.address}" />
<h:commandButton action="#{providerAdd.addProvider}" value="Add provider" />
</h:body>
</html>

CommandButton lose action when I use the PickList

I´m with a strange problem. I´m using the picklist component but it seems like when I use the picklist my commandbutton stop working: Here´s the code:
xhtml
<!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"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>PickList Test</title>
</h:head>
<h:body>
<h:form id="form">
<p:pickList value="#{pickListBean.employeeList}" var="employee" itemLabel="#{employee.employeeName}" itemValue="#{employee.employeeCode}" />
<p:commandButton value="Save" action="#{pickListBean.message}" style="margin-left: 12px;"/>
</h:form>
</h:body>
</html>
bean
#ManagedBean
#RequestScoped
public class PickListBean {
#EJB
private BussinessList bl = new BussinessList();
private DualListModel<Employee> employeeList;
private Employee employee;
/**
* Creates a new instance of PickListBean
*/
public PickListBean() {
List<Employee> source = new ArrayList<Employee>();
List<Employee> target = new ArrayList<Employee>();
source = bl.getEmployee();
employeeList = new DualListModel<Employee>(source, target);
}
public void message(){
System.out.println("CommandButton is working");
}
public DualListModel<Employee> getEmployeeList() {
return employeeList;
}
public void setEmployeeList(DualListModel<Employee> employeeList) {
this.employeeList = employeeList;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee= employee;
}
}
When I click in the commandbutton the message method is not called, but when I remove the picklist from my xhtml the commandbutton call the message method.
I´m using jsf 2.2, primefaces 4.0...
Lots of errors on this code
Before starting with the errors, I can see 2 seriously poor naming choices in the method named getEmployeePickList
It's not a PickList in the first place. its a DataList
the difference from EmployeeList is that it keeps instances of Employee instead of Functionario. The names you chose, give the impression that one of them is a PickList, while the other is an ArrayList, and they keep instances of the same Class
Now, your xhtml might missing something in the declarations. I tried your code, and even when i deleted the pickList, your commandbutton would not fire. Try starting with this (drop the xml declaration)
<!DOCTYPE html PUBLIC "-W3CDTD XHTML 1.0 TransitionalEN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
Also, the Employee Class is not a String, so your pickList will not work without a converter. See this for example
Finally, your PickList will search for a setEmployeePickList() method in your Class. I tried using your pickList working with String instead of Employee, to get past the converter issue, and naturally, I got this error:
javax.el.PropertyNotFoundException: test.xhtml #21,140 value="#{pickListBean.employeePickList}": Property 'employeePickList' not writable on type org.primefaces.model.DualListModel
The solution for this, would be , feeding the pickList with #{pickListBean.employeeList} instead of #{pickListBean.employeePickList}. You can populate your Employee List somewhere else, like in the PickListBean constructor, instead of re-creating in getEmployeePickList.

Pass non-library Java class type into JSP tagfile as attribute

I am trying to write a JSP 2.0 tagfile which will accept an object of a non-library Java type as an attribute.
For example:
package org.myapp.model: Question.java
public Class Question {
private String name;
private String type;
public getName() { return name; }
public getType() { return type; }
...
}
displayQuestion.tag
<%# tag body-content="empty" %>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# attribute name="question" required="true" type="org.myapp.model.Question"%>
<h2>
<div>Question ${question.name} is of type ${question.type}</div>
</h2>
When I deploy my webapp I get an error: 'Unknown attribute type (org.myapp.model.Question) for attribute question.' If I leave off the type it defaults to String, and of course question.name and question.type fail. What am I doing wrong?
Did you try adding import to the tag?
<%# tag body-content="empty" import="org.myapp.model.Question" %>

Categories

Resources