Spring data cannot select book entity without me printing it in console - java

I have controller for selection book entity:
#GetMapping("/{id}")
public String bookPage(#PathVariable int id, Model model){
if(bookService.isExist(id)) {
//System.out.println(bookService.getById(id));
model.addAttribute("book", bookService.getById(id));
return "books/book";
}
return "books/noSuchBook";
}
If I add attribute "book" without printing it first in console then I get blank webpage.
Here is my html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title th:text="${book.getName() + ': ' + book.getAuthor()}"></title>
</head>
<body>
<p th:text="${book.getId() + ', ' + book.getName()}"></p>
</body>
</html>
If I call System.out.println(bookService.getById(id)); before adding it, it works. Do I need set Eager type somewhere or what?

I suspect that bookService.getById(id) delegates to JpaRepository.getById() which in fact returns a proxy with id, not a real entity. When you pass it to System.out.println() then toString() method is called forcing Hibernate to fetch the data from the database and store it in its 1st level cache, so subsequent calls to getById() deal with already loaded entity.
I think you need to use JpaRepository.findById() eagerly getting the entity, not its proxy.

I'm not sure if it's the best practice but I've resolved my problem by creating my own method in interface BookRepo and setting #Fetch(FetchMode.JOIN); on it.
#Fetch(FetchMode.JOIN)
Book findBookById(int id);

Related

Spring Boot + JMustache 404 not found error for .html page from /resources/templates folder

So I'm trying to just follow instructions for simple Spring Boot project using devtools+mustache+data-jpa. I'm just copy-pasting the whole thing and it doesn't work, even thought tutorial says "Just press the button and it works". Full source code is here, some listings I will provide in the end.
All I want to do is to redirect to index.html from localhost:8080/ and insert simple value into the template.
But instead:
1. Something redirects me from / to /apex/f?p=4950:1 for some reason
2. If I change mapping to #GetMapping("/home") and try localhost:8080/home I get 404
After enabling logging I found out that PathResourceResolver doesn't scan the /resources/templates directory. And if I add dependency on Thymeleaf, it finds it.
So the question is where is the problem? Should I add some config file? Or Mustache isn't working like that?
IndexController.java
#Controller
public class IndexController {
#GetMapping("/")
public ModelAndView home() {
Map<String, String> model = new HashMap<>();
model.put( "name", "Alex" );
return new ModelAndView( "index", model );
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Welcome to Spring, {{ name }}</h1>
</body>
</html>
Dependencies
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-mustache')
compile('org.springframework.boot:spring-boot-starter-web')
runtime('org.springframework.boot:spring-boot-devtools')
runtime('com.h2database:h2')
testCompile('org.springframework.boot:spring-boot-starter-test')
Structure
Log
In order for given demo app to work, please add following to the main/resources/application.properties
spring.mustache.prefix=classpath:/templates/
spring.mustache.suffix=.html
This will tell Spring where to look for Mustache views and what extension those are supposed to have.

Generate HTML email from a Template

I am working on a functionality where the application needs to generate user specific emails. This will be setup or configured on the user level using a email template which essentially contains a SQL query, column model, data type, subject, header, footer etc. The template serves as the dataset and layout for the email.
Now using this XML template I need to generate the HTML email. The application will read the XML, execute the SQL query and then match the resultset to the column model. Beyond this; is there any framework or API that can help generate the HTML response (nicely formatted css table) from Java objects or it has to be cooked using raw HTML tags (, etc.)?
I was also researching to see if BIRT or Jasper can provide HTML response but it doesn't seem like they are meant for that. If anyone has experience building a solution for such a use case please let me know.
Take a look at Thymeleaf. It's a HTML template engine.
It's as simple as this:
ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
resolver.setTemplateMode("HTML5");
resolver.setSuffix(".html");
TemplateEngine templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(resolver);
final Context context = new Context(Locale.CANADA);
String name = "John Doe";
context.setVariable("name", name);
// add more objects from your ResultSet
final String html = templateEngine.process("myhtml", context);
with a myhtml.html file:
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-3.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>My first template with Thymeleaf</title>
</head>
<body>
<p th:text="${name}">A Random Name</p>
</body>
</html>
Here the placeholder ${name} will replace the value A Random Name in the <p> element by the value you inserted in the context.
As for your requirement of reading and generating a table, Thymeleaf provides constructs to loop as many times as is required (ie. as long as you have data remaining). Example:
<tr th:each="prod : ${allProducts}">
will iterate through allProducts, assigning each object to the variable prod at each iteration. Take a look at the tutorials and the docs for more.
Notice, you have to write the HTML yourself.
Take a look at this answer for generating HTML report through Jasper
You can use XSLT to transform your XML to HTML. The result of your SQL query would have to be inserted as XML beforehand.

Linking JSF inputText with backing bean's field without showing its value

I have backing bean like this:
#ManagedBean
#SessionScoped
public class TestBean {
private String testString;
public String getTestString() {
return testString;
}
public void setTestString(String testString) {
this.testString = testString;
}
}
And my xhtml page pretty simple too:
<?xml version="1.0" encoding="UTF-8"?>
<!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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<h:head></h:head>
<h:body>
<h:form>
<h:inputText value="#{testBean.testString}"/>
<h:commandButton action="#{testController.testAction}"/>
</h:form>
</h:body>
</html>
Everything I want - to render my h:inputText element without value (empty).
I'm new to JSF, so, could you help me?
With best regards!
UPD!
It's simplified code, I'm using testString in other places and testString have value, which I want to hide! And I want to keep this value.
Provided that it's really a request/view scoped bean, you're likely victim of browser's builtin autocomplete/autofill feature. You can turn it off by adding autocomplete="off" to the input component in question.
<h:inputText ... autocomplete="off" />
Note again that it's not JSF who has filled the inputs, but the webbrowser itself. Clear the browser cache and you'll see that the browser won't do it anymore. Depending on browser make/version you can also reconfigure it to autocomplete a bit less eagerly.
Update: as per your question update, your bean turns out to be session scoped. This is not the normal scope for request/view based forms. A session scoped bean instance is shared across all browser windows/tabs (read: all requests/views) in the same HTTP session. You usually store only the logged-in user and its preferences (language, etc) in the session. You will only get a brand new instance when you shutdown and restart the entire browser, or use a different browser/machine.
Change it to be request or view scoped. In this particular simple example, the request scope should suffice:
#ManagedBean
#RequestScoped
See also:
How to choose the right bean scope?
Update 2 based on the comment,
Oh, you right, it's better for me to use #RequestScoped. But it doesn't resolve my problem - I want to keep this value, but I don;t want to show it in textInput. This value is important in context of request-response cycle.
the concrete functional requirement is now much more clear (in future questions, please pay attention to that while preparing the question, I had no idea that you was initially asking it like that). In that case, use a view scoped bean with 2 properties like this:
#ManagedBean
#ViewScoped
public class TestBean {
private String testString;
private String savedTestString;
public void testAction() {
savedTestString = testString;
testString = null;
}
// ...
}
You can alternatively also store it in the database or a property of an injected managed bean which is in turn actually in the session scope, for example.
You should bind the input text to some other field in your backing bean. And if you want to use that field for yourtestString, copy the entered value to testString in the testAction method.
<h:form>
<h:inputText value="#{testBean.copyTestString}"/>
<h:commandButton action="#{testController.testAction}"/>
</h:form>
public String testAction()
{
testString = copyTestString;
return "destinationPage";
}
Some Browsers ignore autocomplete - it can help to put autocomplete in form tag:
<h:form autocomplete="off">

Component attributes doesn't set after "reRender" on AJAX request

Currently i'm working on some complex web front-end and implement it using:
JSF 1.2
Facelets 1.1.15
RichFaces 3.3.3.Final
I have created a custom JSF component which enables validation of inputText fields using pure JavaScript. This component have only one attribute: type. This attribute is responsible for validation algorithm which will be applied at time when user presses a keyboard key.
At restoreView phase when initial view is created this attribute is set by JSF (actually by Facelets). This means that i have a component class with setter and getter for attribute 'type'. And a 'type' setter called with value specified in xhtml document.
Component object is recreated each time at restoreView phase if i specify them in reRender attribute. But when it is recreated my required attribute type is not set.
It's simply creates new component objects... and it's all. May be i don't understand something and this is normal behavior, but how to get attribute values in this case?
Code:
Simple test page:
<?xml version="1.0" encoding="utf-8"?>
<!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:u="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a="http://richfaces.org/a4j"
xmlns:r="http://richfaces.org/rich"
xmlns:v="http://nobodyhere.ru/jsf/validation">
<head>
<title>Test Page</title>
</head>
<body>
<h:form id="testForm">
<h:inputText id="textInput" value="test">
<v:keyValidator type="time"/>
</h:inputText>
<a:commandButton value="Make AJAX request" reRender="testForm"/>
</h:form>
</body>
</html>
Component class:
public class KeyValidator extends UIComponentBase
{
public KeyValidator()
{
System.out.println("new KeyValidator");
}
public KeyValidatorType getValidatorType()
{
return type;
}
public String getType()
{
return getValidatorType().toString();
}
public void setType(String type)
{
this.type = KeyValidatorType.valueOf(type.toUpperCase());
}
#Override
public String getFamily()
{
return KeyValidator.class.getName();
}
private KeyValidatorType type;
}
When i press "Make AJAX request" button my component is recreated. But attribute 'type' is not set in component.
The main problem starts at renderView phase in component renderer when encodeBegin is called it tries to get this attribute and of course it gets null instead of correct value.
So, the more precise question probably:
How to get attribute values of component on AJAX request at renderView phase?
Any help will be greatly appreciated.
You must override saveState and restoreState in component to save and restore needed attributes.
Good Luck!

Java: help me convert a working .jsp to some XML notation

I've got a .jsp file that is working fine. It's maybe a bit special in that it is calling a factory (ArticlesFactory) that returns a singleton (but that is a detail) of class Articles (it does so by automatically fetching shared Google Docs that are transformed to html and then stored into ".../text/en" but that is a detail too).
The following is working fine: it does exactly what I need, it fetches the articles automatically and I can access my Articles instance fine.
<%# page pageEncoding="UTF-8" contentType="text/html;charset=utf-8" %>
<%# page import="com.domain.projectname.*"%>
<!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" dir="ltr" lang="en">
<head></head>
<body>
<% Articles articles = ArticlesFactory.create( getServletContext().getRealPath( "text/en" )); %>
We have <%= articles.getNbItems()%>
</body>
</html>
However, I must transform it to some notation I don't know nor understand, I'm not even sure what the name for that is and obviously I've got some issue.
I don't know if it's a namespace issue or if there's a problem with the ArticlesFactory factory's static factory method creating the Articles singleton:
<?xml version="1.0" encoding="UTF-8"?>
<jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:c="urn:jsptld:http://java.sun.com/jsp/jstl/core">
<jsp:directive.page import="com.domain.project.ArticlesFactory"/>
<jsp:directive.page contentType="text/html; charset=UTF-8" />
We have ${variable.nbItems} <!-- What to put here !? -->
</jsp:root>
I tried many things and couldn't figure it out.
Basically I need to:
- call the static create method from the ArticlesFactory class
- by passing it the result of getServletContext().getRealPath( "text/en" ))
(which should give back an Articles instance)
then I want to put the result of getNbItems() in a variable that I want to display
Note that I don't want to have to call getServletContext from any servlet/dispatcher: I want to do it just like in the first working example (ie directly from inside the .jsp).
You're basically looking for "JSP in XML syntax". Most is already explained in this (old) tutorial. You yet have to replace <% %> by <jsp:scriptlet> and <%= %> by <jsp:expression>.
The xmlns:c namespace is by the way unnecessary here, unless you'd like to use any of the JSTL core tags.
The Expression Language (those ${} things) which is explained in this (also old) tutorial is by the way a separate subject. It only acts on objects in the page, request, session or application scope. In scriptlets however, variables are only defined in local scopes (methodlocal actually), those aren't available in EL. You would need to do the following in the scriptlet to make it available in EL:
pageContext.setAttribute("articles", articles); // Put in page scope (recommended).
request.setAttribute("articles", articles); // Or in request scope. Also accessible by any include files.
session.setAttribute("articles", articles); // Or in session scope. Accessible by all requests in same session.
application.setAttribute("articles", articles); // Or in application scope. Accessible by all sessions.
This way it's available by ${articles} in EL.

Categories