I have the following models,
public class Shift {
private UUID id;
private UUID unit;
private List employees;
private Timestamp startTime;
private Timestamp endTime;
...
}
public class Unit {
private UUID id;
private String name;
...
}
following route,
path("/shift", () -> {
get("", ShiftController.fetchShifts);
});
following controller,
public static Route fetchShifts = (Request req, Response res) -> {
Map map = new HashMap<>();
map.put("shifts", shiftDao.findAllByOrderByUnitAscStartTimeAsc());
map.put("units", unitDao.findAllByOrderByName().stream().collect(Collectors.toMap(Unit::getId, u -> u)));
return render(req, map, "shifts");
};
following template,
<table>
<tbody>
<tr th:each="s : ${shifts}">
<td th:text="*{units[__${s.unit}__].name}">unit</td>
</tr>
</tbody>
</table>
which gives me,
ERROR org.thymeleaf.TemplateEngine - [THYMELEAF][qtp1905797065-18] Exception processing template "shifts": Exception evaluating OGNL expression: "units[dd002ece-10c7-11e7-9009-93b58da4760f].name"
...
org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating OGNL expression: "units[dd002ece-10c7-11e7-9009-93b58da4760f].name"
...
Caused by: ognl.ExpressionSyntaxException: Malformed OGNL expression: units[dd002ece-10c7-11e7-9009-93b58da4760f].name [ognl.ParseException: Encountered " "c7 ""
...
and for the death of me I can't figure out the problem. What I want is to iterate through all the shifts and find out the name of the unit for each shift. For this I create a map units in the controller with the ids of units and objects representing them. However, I'm unsuccessful in implementing the map in the template. *{units.get(__${s.unit}__).name} in the template gives similar errors.
It should look like this:
<table>
<tbody>
<tr th:each="s: ${shifts}">
<td th:text="${units.get(s.unit).name}" />
</tr>
</tbody>
</table>
You have a few problems with your thymeleaf.
As the error message states, units[dd002ece-10c7-11e7-9009-93b58da4760f].name is not a valid expression. As far as I know, you can only use the ${map[index]} expression with numbers (which look like map[0]) and strings (which look like map['test']). Your expression is neither -- to the parser, you have a string missing the containing quotes.
Second, you're misusing __${}__ expressions. You should really only need to ever use __${}__ when you are defining a th:field expression. In most other cases, you should be able to do everything without them.
Related
In Apache OfBiz application, I have such code in controller:
public static String runRequest(HttpServletRequest request, HttpServletResponse response) {
Map<String, Long> typesToCount = getTypesToCount();
request.setAttribute("types", typesToCount);
return HttpFinals.RETURN_SUCCESS;
}
And in freemarker template it's processed/iterated like so:
<table
<#list requestAttributes.types as key, value>
<tr>
<td>${key}</td>
<td>${value}</td>
</tr>
</#list>
</table>
On rendered html page I'm getting both actual map's string key's and map's methods names (put, remove, add etc.).
As for values they are not rendered at all the with following error:
FreeMarker template error: For "${...}" content: Expected a string or something automatically convertible to string (number, date or boolean), or "template output" , but this has evaluated to a method+sequence (wrapper: f.e.b.SimpleMethodModel)
I'm using freemarker 2.3.28
Map.entrySet() method returns a collection (Set<Map.Entry<K, V>>) of the mappings contained in this map. So we can iterate over key-value pair using getKey() and getValue() methods of Map.Entry<K, V>. This method is most common and should be used if you need both map keys and values in the loop.
Try this code to iterate through the values in FTL
<table>
<#list requestAttributes.entrySet() as requestAttribute>
<tr>
<td>${requestAttribute.getKey()}</td>
<td>${requestAttribute.getValue()}</td>
</tr>
</#list>
</table>
Basically, I managed to iterate through the map only after wrapping it in SimpleMapModel like so:
public static String runRequest(HttpServletRequest request, HttpServletResponse response) {
Map<String, Long> typesToCount = getTypesToCount();
request.setAttribute("types", new SimpleMapModel(typesToCount, new DefaultObjectWrapper())));
return HttpFinals.RETURN_SUCCESS;
}
and int ftl template:
<#list requestAttributes.types?keys as key>
<tr>
<td>${key}</td>
<td>${requestAttributes.types[key]}</td>
</tr>
</#list>
That works like that if FreeMarker is configured to use a pure BeansWrapper (as opposed to DefaultObjectWrapper) for its object_wrapper setting, and the BeansWrapper.simpleMapWrapper property is left on its default value, false. Needless to say, it's a quite problematic configuration to work with. Hopefully it isn't the default of OfBiz. Although for old frameworks this can happen, as many many years ago this was a way of working around some limitations of FreeMarker, since this way you can just use the Java API of Map. Later, the introduction of the ?api built-in has made this hack needless.
I am working on hibernate search with the elastic search.I am having a requirement where I need to fetch parent document immediately followed by child documents.
To maintain parent and child relation I am having a table with id ,and parentId column.Here parentId is the id of another record.e.g
Note:For Better understanding run the belowsnippet
<html>
<body>
<table border="1" >
<tr>
<th>ID</th>
<th>Name</th>
<th>ParentId</th>
</tr>
<tr>
<td>1</td>
<td>Samule</td>
<td>0</td>
</tr>
<tr>
<td>2</td>
<td>jhon</td>
<td>1</td>
</tr>
<tr>
<td>3</td>
<td>peeter</td>
<td>2</td>
</tr>
</table>
</body>
</html>
expected output :
for ex: while searching name equals to Samule it needs to fetch in the below order
1
2
Here the first record is the parent record and the second record is the child record.Is there any way to do in elastic search with hibernate search?
The only way I see this possibly happen is if your hierarchy has a known maximum depth. For instance if you know you will only ever have two levels of parents, never three.
If this is your case, you can have a model like this:
#Indexed
public class Document {
#DocumentId
private int id;
#Field
private String text;
#IndexedEmbedded(depth = /* YOUR MAX DEPTH HERE */)
private Document parent;
#Field(name = "path_sort", analyze = Analyze.NO)
public String getPath() {
return parent == null ? id : parent.getPath() + "." + id;
}
}
And perform your search like this:
FullTextSession fts = ... ;
QueryBuilder qb = fts.getSearchFactory()
.buildQueryBuilder()
.forEntity( Document.class )
.get();
Query query = qb.keyword().onFields(
"text",
"parent.text",
"parent.parent.text"
/* and so on, on as many levels as necessary */
)
.matching( "some string" )
.createQuery();
Sort sort = qb.sort().byField( "path_sort" ).createSort();
List<Document> result = fts.createFullTextQuery( query, Document.class )
.setSort( sort )
.list();
As the codebase is already using Spring Data JPA, I would like to create a Sort
object, which will be based upon the presence (or lack thereof) of a particular element
being present in a Collection, collection itself which is one of the element in the
primary table. The Sort object's property would need to be dynamic, as a user might want to sort
records once one way and the next time another way.
Explicitly, if multiple PrimaryEntity objects have a SecondaryEntity with 'type' set to a particular
value, I would then want to sort them based upon the corresponding 'notes' field in the corresponding
SecondaryEntity. Also, while I would want to retrieve all SecondaryEntity objects, I would want the
sorting to be based solely upon the SecondaryEntity records where 'type' is equal to, say, 'Important'.
The classes look like the following (I also redefined 'equals' & 'hashCode' for SecondaryEntity):
public class PrimaryEntity
{
#OneToMany(mappedBy = "primary", cascade = CascadeType.ALL)
#MapKey(name = "type")
private Map<String, SecondaryEntity> myMap = new HashMap<>();
#Column(name = "name")
private String name;
}
public class SecondaryEntity
{
#Column(name = "type", length = 200)
private String type;
#Column(name = "notes", length = 2000)
private String notes;
#ManyToOne
#JoinColumn(name = "primary_id", referencedColumnName = "id")
private PrimaryEntity primary;
}
I would then want to create a Sort with a syntax similar to he following:
Sort sort = new Sort("myMap[important].notes")
Finally, while I am striving to sort the PrimaryEntity records as per above, it does not matter to
me how, for a given PrimaryEntity, its SecondaryEntity records are displayed.
For example,
<html>
<head>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
</style>
</head>
<body>
<table>
<tr>
<th>Name</th>
<th>Type</th>
<th>Notes</th>
</tr>
<tr>
<td>Second primary</td>
<td>Important</td>
<td>1</td>
</tr>
<tr>
<td>Second primary</td>
<td>Other</td>
<td>2</td>
</tr>
<tr>
<td>Second primary</td>
<td>Miscellaneous</td>
<td></td>
</tr>
<tr>
<td>Third primary</td>
<td>Important</td>
<td>2</td>
</tr>
<tr>
<td>First primary</td>
<td>Important</td>
<td>3</td>
</tr>
</table>
</body>
</html>
Thank you.
You can optionally override findAll(Sort sort) method in the SecondaryRepo with #EntityGraph annotation:
public interface SecondaryRepo extends JpaRepository<Secondary, Long> {
#EntityGraph(attributePaths = {"primary"})
#Override
List<Secondary> findAll(Sort sort);
}
to fetch associated Primaries eagerly.
Then just define Sort with type and note and fetch Secondaries (that will contain also their Primaries) like this:
Sort sort = new Sort(new Sort.Order("type"), new Sort.Order("note"));
List<Secondary> list = secondaryRepo.findAll(sort);
See example and test.
This question already has an answer here:
${employee.id} from List in JSP throws java.lang.NumberFormatException: For input string: "id"
(1 answer)
Closed 6 years ago.
I am trying to delete a department from the database in addDepartment.jsp page in my spring mvc application. I do get this error when I try to delete
org.apache.jasper.JasperException: An exception occurred processing JSP page /WEB-INF/views/addDepartment.jsp at line 21
18: </tr>
19: <tr>
20: <td><form:label path="departmentName">Department Name:</form:label></td>
21: <td><form:input path="departmentName" value="${department.departmentName}"/></td>
22: </tr>
23:
24: <tr>
This is the line 21 of the addDepartment.jsp file
21: <td><form:input path="departmentName" value="${department.departmentName}"/></td>
this is the controller for the view that handles the delete function
#RequestMapping(value = "/deleteDepartment", method = RequestMethod.GET)
public ModelAndView deleteCategory(#ModelAttribute("command") Department department,
BindingResult result) {
departmentService.deleteDepartment(department.getDepartmentId());
Map<String, Object> model = new HashMap<String, Object>();
model.put("department", departmentService.getDepartments());
return new ModelAndView("addDepartment", model);
}
this is the root cause stacktrace from the error
java.lang.NumberFormatException: For input string: "departmentName"
java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
java.lang.Integer.parseInt(Integer.java:580)
java.lang.Integer.parseInt(Integer.java:615)
this is the view of the code that handles the delete from the addDepartment.jsp file
<c:forEach items="${departments}" var="department">
<tr>
<td><c:out value="${department.departmentId}"/></td>
<td><c:out value="${department.departmentName}"/></td>
<td align="center">Edit | Delete</td>
</tr>
</c:forEach>
Why am I not been able to delete a department name?
Correct me if I am wrong but the exception is not related to the deleteDepartment code but the addDepartment.jsp page, so how have you connected those two together?
Also from the numberFormatException it seems to me as if java is trying to cast the departmentName value into an Integer. Which of course does not make sense because I suppose that you will be feeding a string to this field.
I am noty entirely sure about where the problem is from the code that you have shared but I sense some kind of mixing up selecting the "department" by departmentId or by departnmentName, in order to delete it. Thus the casting from a String value to a number value that throws the exception occurs.
My code lists the data that I would like to see, but when I hit submit it fails to populate the backing form with exception below. How can I make the bindings work? The exception I get is of Mismatch type, trying to insert a String in List when expecting Objects. Which makes sense.
Example
<tr>
<td><form:select id="myTypes" path="myTypes" multiple="false">
<form:option value="NONE" label="--- Select ---" />
<form:options items="${form.myTypes}" itemValue="id" itemLabel="label"/>
</form:select>
</td>
<td><form:errors path="myTypes" cssClass="error" /></td>
This is how form looks like
public class MyForm {
List<MyType> myTypes;
public List<MyType> getMyTypes() {
return myTypes;
}
public void setMyTypes(List<MyType> myTypes) {
this.myTypes = myTypes;
}
}
And of course MyType has id and label.
Link to above sample code and exception below
HTTP Status 500 - Request processing failed; nested exception is org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'Form' on field 'myTypes': rejected value [8768743658734587345]; codes [typeMismatch.Form.myTypes,typeMismatch.myTypes,typeMismatch.java.util.List,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [myForm.myTypes,myTypes]; arguments []; default message [myTypes]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.List' for property 'myTypes'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.x.x.MyTypeEntity] for property 'myTypes[0]': no matching editors or conversion strategy found]
Solution:
Make sure you are mapping to Single element and not to the list :)
It should be something like
<form:select path="someEntity[${status.index}].myType.id">
HTH
I think the problem is that Spring does not know how to convert the selected option value (which is a String posted towards your app as an HTTP parameter named "myTypes" when you submit the form) to a MyType object. You should configure a Formatter< MyType > and register it to the Spring FormatterRegistry (see Spring doc) to let Spring know how to convert the incoming String to a MyType object.
public class MyTypeFormatter implements Formatter<MyType> {
#Override
public MyType parse(String text, Locale locale) throws ParseException {
return myTypeService.getType(text); // for example
}
public String print(MyType t, Locale locale) {
return t.getId();// for example
};
}
By the way, if I may, since your dropdown list is not multiple, it means that you are going to select just one of the available MyType options. The path of the < form:select > should be named "myType" instead of "myTypes" and especially, it should refer to a MyType attribute within your Form object and not to a List< MyType > attribute. Maybe you should name your first list of available MyType objects "availableTypes" and create a second attribute named "selectedType" to bind the MyType object corresponding to the selected option on the GUI.