I am trying to learn how to build an application using Spring MVC and Hibernate. Currently I am stuck on inserting checkbox values into MySQL database.
My Database Table Structure is like following:
id name interest
When I fill up my form and hit submit I get this error message :
root cause
java.sql.SQLException: Incorrect string value: '\xAC\xED\x00\x05ur...' for column 'interest' at row 1
com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1084)
com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4232)
I am trying to insert values in my table so that after insertion it looks like following:
id name interest
1 Steve PHP
2 Steve Java
3 Wuagh C#
4 Wuagh PHP
Could you please tell me how to achieve this? And If possible could you also tell me how can I achieve this as well ?
id name interest
1 Steve PHP, Java
2 Wuagh C#, PHP
Please see my codes below
My Form:
<c:url var="action" value="/register" ></c:url>
<form:form action="${action}" modelAttribute="subscriber" method="POST" >
<div>
<label>Name</label>
<form:input path="name"/>
<form:errors path="name" cssClass="error"/>
</div>
<div>
<label>Interests</label>
<form:checkboxes path="interest" items="${records.interests}"/>
</div>
<input type="submit" value="Submit">
</form:form>
Controller:
package com.spring.org;
#Controller
public class HomeController {
#Autowired
private SubscriberService subService;
#RequestMapping(value="/register", method= RequestMethod.GET)
public ModelAndView RegistrationForm(#ModelAttribute Subscriber subscriber, BindingResult result)
{
HashMap<Integer, String> interest = new HashMap<Integer, String>();
interest.put(1,"Java");
interest.put(2,"PHP");
interest.put(3, "C#");
return new ModelAndView("regForm", "records", interest);
}
#RequestMapping(value="/register", method= RequestMethod.POST)
public ModelAndView RegistrationFormSubmit(#ModelAttribute("subscriber") #Valid Subscriber subscriber, BindingResult result)
{
if (result.hasErrors()) {
return new ModelAndView("regForm");
}
else
{
subService.addSubscriber(subscriber);
return new ModelAndView("redirect:/showList");
}
}
}
Model - Subscriber
#Entity
#Table(name = "PERSON", schema = "java2")
public class Subscriber {
#Id
#Column(name="ID")
#GeneratedValue
private int id;
#NotEmpty(message = "Please enter your Name.")
private String name;
private String[] interest;
public String getName() {return name;}
public void setName(String name) { this.name = name; }
public String[] getInterest() { return interest; }
public void setInterest(String[] interest) { this.interest = interest; }
}
SubscribeService Implementation :
#Service
public class SubscriberServiceImpl implements SubscriberService{
#Autowired
private SubscriberDao subsDao ;
#Override
public void addSubscriber(Subscriber subscriber) {
subsDao.addSubscriber(subscriber);
}
}
SubscriberDao Implementation :
#Repository
public class SubscriberDaoImpl implements SubscriberDao {
#Autowired
private SessionFactory sessionFactory ;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
#Override
public void addSubscriber(Subscriber subscriber) {
getSessionFactory().openSession().save(subscriber);
}
}
Related
I'm trying to add data to my database and reload the same page using spring boot and thymeleaf but when I save data I face this error
org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'org.closure.gcp.entities.QuestionEntity'; nested exception
is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Integer] for value 'adsf'; nested exception is java.lang.NumberFormatException: For input string: "adsf"
controller code :
#Controller
#RequestMapping(path = "/Questions")
public class QuestionView {
#Autowired
QuestionRepo questionRepo;
#RequestMapping(path = "/")
public String index(#ModelAttribute("question") QuestionEntity question, Model model)
{
List<QuestionEntity> list = questionRepo.findAll();
model.addAttribute("questions", list);
return "Questions";
}
#RequestMapping(value="/add", method=RequestMethod.POST)
public String addQuestion(Model model,#ModelAttribute("question") QuestionEntity question) {
questionRepo.save((QuestionEntity)model.getAttribute("question"));
List<QuestionEntity> list = questionRepo.findAll();
model.addAttribute("questions", list);
return "Questions";
}
}
thymeleaf page :
<html>
<header>
<title>Questions</title>
</header>
<body>
<h2>hello questions</h2>
<hr>
<tr th:each="q: ${questions}">
<td th:text="${q.question}"></td>
<br>
<td th:text="${q.question_type}"></td>
<hr>
</tr>
<!-- <form th:action="#{/add}" th:object="${question}" method="post"> -->
<form action="./add" th:object="${question}" method="POST">
<input type="text" th:field="*{question}" />
<br >
<input type="text" th:field="*{question_type}" />
<br >
<input type="submit" value="save" >
</form>
</body>
</html>
#Entity
#Table(name="question")
public class QuestionEntity {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Integer id;
#Column(nullable=false)
private String question;
#Column(nullable=false)
private String question_type;
#ManyToOne(optional = true)
private InterestEntity interest;
#ManyToOne(optional = true)
private LevelEntity level;
#Column(nullable = true)
private String sup_file;
#Column(nullable = false)
private int pionts;
#ManyToMany
private List<ContestEntity> contest;
#OneToMany(mappedBy ="question")
private List<AnswerEntity> answers;
// getters and setters
}
notice when I try to open another page in "/add" it works
I found this to solve
I just made a model class and use it instead of entity
and I used just one method to handle index and add requests
#Controller
#RequestMapping(path = "/Questions")
public class QuestionView {
#Autowired
QuestionRepo questionRepo;
#RequestMapping(path = {"/",""},method = {RequestMethod.POST,RequestMethod.GET})
public String index(#ModelAttribute("question") QuestionModel question, Model model,HttpServletRequest request)
{
if(request.getMethod().equals("POST"))
{
questionRepo.save(new QuestionEntity().question(question.getQuestion()).question_type(question.getQuestion_type()));
}
List<QuestionEntity> list = questionRepo.findAll();
model.addAttribute("questions", list);
return "Questions";
}
}
It is better to have 2 separate methods, one for GET and one for POST and to use redirect after the POST (see https://en.wikipedia.org/wiki/Post/Redirect/Get). This is how I would code this based on your separate QuestionModel class:
#Controller
#RequestMapping(path = "/Questions")
public class QuestionView {
#Autowired
QuestionRepo questionRepo;
#GetMapping
public String index(Model model)
{
List<QuestionEntity> list = questionRepo.findAll();
model.addAttribute("questions", list);
model.addAttribute("question", new QuestionModel());
return "Questions";
}
#PostMapping("/add")
public String addQuestion(#Valid #ModelAttribute("question") QuestionModel question, BindingResult bindingResult, Model model) {
if(bindingResult.hasErrors()) {
return "Questions";
}
questionRepo.save(new QuestionEntity().question(question.getQuestion()).question_type(question.getQuestion_type()));
return "redirect:/Questions";
}
}
Main points:
Use separate methods for GET and POST
Add the #Valid annotation to the #ModelAttribute in the POST method so any validation annotations on QuestionModel are checked (Because you probably want to make sure the question has at least some text in it for example).
Use BindingResult as parameter to check if there are validation errors.
Use "redirect:" to force a new GET after the POST to help avoid double submissions if a user would refresh the browser.
I have this model:
Order.java
#Entity
#Table(name = "`order`")
public class Order {
private Long id;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "order")
private List<OrderProduct> orderProducts;
#Override
public int hashCode() {
return new Long(id).hashCode();
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof Order)) {
return false;
}
return this.id == ((Order) obj).getId();
}
/* getters & setters */
}
OrderProduct.java
#Entity
#Table(name = "order_product")
public class OrderProduct {
private Long id;
private String name;
private Long quantity;
#ManyToOne
#JoinColumn(name = "order_id")
private Order order;
/* getters & setters */
}
And this controller:
OrderController.java
#Controller
#SessionAttributes({"products"})
public class OrderController {
#Autowired
private OrderService orderService;
#Autowired
private ProductService productService;
#Autowired
private SecurityService securityService;
#Autowired
private UserService userService;
#RequestMapping(value = "/order/add", method = RequestMethod.GET)
public String addOrder(Model model) {
Order order = new Order();
order.setOrderProducts(new AutoPopulatingList<>(OrderProduct.class));
model.addAttribute("orderForm", order);
model.addAttribute("products", productService.findAll());
return "order/add";
}
#RequestMapping(value = "/order/add", method = RequestMethod.POST)
public String addOrder(#ModelAttribute("orderForm") Order orderForm, BindingResult bindingResult, Model model) {
orderForm.setUser(userService.findByUsername(securityService.findLoggedInUsername()));
for (OrderProduct orderProduct : orderForm.getOrderProducts()) {
orderProduct.setOrder(orderForm);
}
orderService.save(orderForm);
return "redirect:/order/view";
}
}
And I want to create a form where a user can choose few products, give a quantity of them and submit his order. I was trying to write JSP code like that:
<form:form method="POST" modelAttribute="orderForm">
<spring:bind path="orderProducts">
<tr>
<td>
<c:forEach items="${products}" var="product">
<form:checkbox path="orderProducts[${product.id}].name"
value="${product.name}"
label="${product.name}"/>
<form:input type="text" path="orderProducts[${product.id}].quantity" placeholder="Quantity"/>
</c:forEach>
</td>
</tr>
</spring:bind>
<button class="btn btn-lg btn-primary btn-block" type="submit">Submit</button>
</form:form>
...but unfortunately it's probably trying to create as many orderProducts as I have in products list and as a result the orderProducts, which a service try to save, doesn't have properly setted values and at the end there is created an order without the orderProducts. So my question is how can I properly send the orderProducts via JSP code to the controller which finally will create the right order with its items?
I am suspecting this ${product.id}. As this is an index value, it should be like
orderProducts[0].name orderProducts[0].quantity
orderProducts[1].name orderProducts[1].quantity
orderProducts[2].name orderProducts[2].quantity
I solved it by removing an orderProducts from an orderForm which don't have a quantity properly setted in my OrderController but probably it isn't the best solution.
I have problem with validation in thymeleaf. My case is to save Employee with Position and Role-s. Those two "fields" cause LazyInitializationException when validation has errors. If validation passed Employee will be save to DB and everything is ok. Please give me some advice, what I am doing wrong or how can I fix it.
Please look for my code below:
EmployeeController:
#Controller
public class EmployeeController extends BaseCrudController {
// (........)
#RequestMapping(value = urlFragment + "/create", method = RequestMethod.GET)
public String createEmployee(Model model) {
prepareEmployeeForm(model);
return "crud/employee/create";
}
#RequestMapping(value = urlFragment + "/create", method = RequestMethod.POST)
public String processNewEmployee(Model model, #ModelAttribute("employeeForm") #Valid EmployeeForm employeeForm, BindingResult result) {
if (!result.hasErrors()) {
User user = employeeFormService.getUserFromEmployeeForm(employeeForm);
try {
userService.merge(user);
model.addAttribute("success", true);
prepareEmployeeForm(model);
} catch (Exception e) {
model.addAttribute("error", true);
}
} else {
initCollections(employeeForm, model);
}
return "crud/employee/create";
}
private void initCollections(EmployeeForm employeeForm, Model model)
{
employeeForm.setAllAvailableRoles(roleRepository.findAll());
employeeForm.setAllAvailablePositions(positionRepository.findByEnabledTrueOrderByNameAsc());
model.addAttribute("employeeForm", employeeForm);
}
private void prepareEmployeeForm(Model model) {
EmployeeForm employee = new EmployeeForm();
employee.setAllAvailablePositions(positionRepository.findByEnabledTrueOrderByNameAsc());
employee.setAllAvailableRoles(roleRepository.findAll());
model.addAttribute("employeeForm", employee);
}
}
EmployeeForm:
public class EmployeeForm extends BaseForm {
#Length(min = 2, max = 45)
private String firstName = "";
// (........)
private Position position;
private Collection<Role> roles;
private Collection<Position> allAvailablePositions;
private Collection<Role> allAvailableRoles;
public EmployeeForm() {
}
public Position getPosition() {
return position;
}
public void setPosition(Position position) {
this.position = position;
}
public Collection<Role> getRoles() {
return roles;
}
public void setRoles(Collection<Role> roles) {
this.roles = roles;
}
public Collection<Position> getAllAvailablePositions() {
return allAvailablePositions;
}
public void setAllAvailablePositions(Collection<Position> allAvailablePositions) {
this.allAvailablePositions = allAvailablePositions;
}
public Collection<Role> getAllAvailableRoles() {
return allAvailableRoles;
}
public void setAllAvailableRoles(Collection<Role> allAvailableRoles) {
this.allAvailableRoles = allAvailableRoles;
}
}
employeeForm.html
<form action="#" th:action="#{/panel/employee/create}" th:object="${employeeForm}" method="post">
<!--(......)-->
<div class="row">
<div class="col-md-6">
<label th:text="#{position}">Position</label>
<!--(Line 57 cause LazyInitializationException)--><select th:field="*{position}" class="form-control">
<option th:each="positionQ : *{allAvailablePositions}"
th:value="${{positionQ}}"
th:text="${positionQ.name}">Position name
</option>
</select>
</div>
<div class="col-md-6">
<label th:text="#{permissions}">Permissions</label>
<th:block th:each="role : *{allAvailableRoles}">
<p>
<input type="checkbox" th:id="${{role}}" th:value="${{role}}" th:field="*{roles}"/>
<label th:for="${{role}}"
th:text="#{${role.name}}">Role name</label>
</p>
</th:block>
</div>
</div>
</form>
Trace:
HTTP Status 500 - Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringSelectFieldAttrProcessor' (crud/employee/employeeForm:57)
root cause:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringSelectFieldAttrProcessor' (crud/employee/employeeForm:57)
root cause
org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringSelectFieldAttrProcessor' (crud/employee/employeeForm:57)
root cause
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
I will be really glad for any help.
The problem is that your hibernate session is closed. The pattern open-session-in-view solves this issue. You can use spring-boot where it is the default or look at the configuration in fuwesta-sampe.
The cleaner approach is to ensure that the data are complete loaded before you close the session. This means a service layer should navigate to each entity or use eager fetching.
I am having difficulties binding the spring form value to a backing object.
The following are the related parts of the code.
This is from page.jsp
<form:form method="post" commandName="building" action="addBuilding">
<div>
<div>
<form:label path="buildingName">Building Name:</form:label>
<form:input path="buildingName" />
<form:errors path="buildingName"></form:errors>
</div>
<div>
<form:label path="buildingType">Building Type:</form:label>
<form:select path="buildingType">
<form:option value="none">--Select One--</form:option>
<form:options items="${buildingTypeList}" itemValue="id" itemLabel="typeName"/>
</form:select>
<form:errors path="buildingType"></form:errors>
</div>
</div>
</form:form>
Model classes I want to bind are as the following. I add these for the sake of completeness
#Entity
#Table(name="tablename")
class Building {
#Column
private buildingName;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "buildingType", referencedColumnName = "id", nullable = false)
private BuildingType buildingType;
//other fields, getters and setters etc.
}
#Entity
#Table(name="tablename")
class BuildingType {
#Id
#Column
private int id;
#Column
private String typeName;
//getters, setters
}
At this point I can see the building type name in the combo-box just fine (in a GET request). The problem happens when I post the form. Itemvalue from combo-box is int and I want to bind it to the buildingType field in the Building model. The code will explain it better I guess. Related controller functions:
#RequestMapping(value = "addBuilding", method = RequestMethod.GET)
public String addBuildingPage(Model model) {
Building building = new Building();
model.addAttribute("building", building);
List<BuildingType> buildingTypeList = buildingTypeDao.findAll();
model.addAttribute("buildingTypeList", buildingTypeList);
return "addBuilding";
}
#RequestMapping(value = "addBuilding", method = RequestMethod.POST)
public String submitNewBuilding(#ModelAttribute(value = "building") #Valid Building building,
BindingResult result, Model model) {
if (result.hasErrors()) {
return "addBuilding";
}
model.addAttribute("building", building);
return "addBuilding";
}
I get a cannot cast int to BuildingType exception, after some search I followed the blog post written here. So I decided to write a custom Formatter and use ConversionService.
This is the formatter class
#Component
public class BuildingTypeFormatter implements Formatter<BuildingType> {
#Autowired
private BuildingTypeDao buildingTypeDao;
#Override
public String print(BuildingType buildingType, Locale arg1) {
return buildingType.getName();
}
#Override
public BuildingTypeDBO parse(String id, Locale arg1) throws ParseException {
return buildingTypeDao.findOne(Long.parseLong(id));
}
}
And this is the spring configuration class. (I don't use xml configuration.)
#EnableWebMvc
#Configuration
#ComponentScan({ "my.packages" })
public class MvcConfig extends WebMvcConfigurerAdapter {
#Autowired
private BuildingTypeDBOFormatter formatter;
public MvcConfig() {
super();
}
#Bean(name = "conversionService")
public FormattingConversionServiceFactoryBean conversionService() {
FormattingConversionServiceFactoryBean bean = new FormattingConversionServiceFactoryBean();
Set<Formatter<?>> formatters = new HashSet<Formatter<?>>();
formatters.add(formatter);
bean.setFormatters(formatters);
return bean;
}
I think I need to register conversion service as explained in the blog post. Using and init binder in my controller like this.
#Autowired
ConversionService conversionService;
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setConversionService(conversionService);
}
The problem is I get the following exception when using setConversionException. And when I debug it I see that binder is initialized with a default conversionService.
java.lang.IllegalStateException: DataBinder is already initialized with ConversionService
at org.springframework.util.Assert.state(Assert.java:385)
at org.springframework.validation.DataBinder.setConversionService(DataBinder.java:562)
at my.package.controller.MyController.initBinder(MyController.java:138)
I came across with many answers suggesting setConversionService but it just doesn't work, how can I fix this? (PS: Sorry for the long post, but I think there may be couple of ways to fix this, so I preferred to post the whole thing.)
You can try add custom property editor in your controller
#InitBinder
public void initBinder(ServletRequestDataBinder binder) {
binder.registerCustomEditor(BuildingType.class, "buildingType", new PropertyEditorSupport() {
public void setAsText(String text) {
Long buildingTypeId = Long.parseLong(text);
BuildingType buildingType = (BuildingType) buildingTypeDao.findOne(buildingTypeId);
setValue(buildingType);
}
});
}
I am trying to implement 'forgot password' functionality using JSF SEAM in our index page, I am using a a4j:jsFunction to send the users email and card number via two 's
It seems to work fine when I just send the email (as a string), but when I added card number (int) it threw the following..
Caused by: javax.el.PropertyNotFoundException: /index.xhtml #256,138 assignTo="#{forgotPasswordActions.cardnumber}": The class 'org.javassist.tmp.java.lang.Object_$$_javassist_seam_5' does not have the property 'cardnumber'.
The backing bean looks like this...
#Stateless
#Name("forgotPasswordActions")
public class ForgotPasswordActionsBean implements ForgotPasswordActions, Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Logger private Log log;
#In private EmailService emailService;
#In private UserDAO userDAO;
#In private MessagePoster messagePoster;
#In private Map<String, String> messages;
private User user;
private String address;
private String email;
private int cardnumber;
#Override
public void resetPassword(){
new RunAsOperation(true) {
public void execute() {
if(StringUtils.isNotEmpty(email)){
user = userDAO.findByEmail(email);
}
else{
messagePoster.postPopupInfoMessage(messages.get("inputEmpty"));
}
if(user!=null && cardnumber == user.getCardId()){
String newPassword = generateRandomPassword();
log.debug("updating password...");
user.setPassword(newPassword);
user = userDAO.makePersistent(user);
address = user.getEmail();
log.debug("password changed to: "+newPassword);
Map<String, Object> emailInfo = new HashMap<String, Object>();
emailInfo.put("name", user.getFirstname());
emailInfo.put("newPassword", newPassword);
emailService.sendToAddress(Email.user_password_reset, address, emailInfo);
messagePoster.postPopupInfoMessage(messages.get("pwReset")+" "+user.getEmail());
}
else{
messagePoster.postPopupInfoMessage(messages.get("resetFailed"));
}
}
}.run();
}
//---------------------- Setters
#Override
public void setEmail(String email) {
this.email = email;
}
#Override
public void setCardno(int cardnumber) {
this.cardnumber = cardnumber;
}
}
and the JSF / HTML
<div id="forgotPasswordDialog" title="Forgot Password">
<div class="textBox">
<input id="emailLookupval" type="text" />
<input id="cardNoval" type="text" />
<button onclick="resetPassword(jQuery('#emailLookupval').val(),jQuery('#cardNoval').val())" type="button">Reset</button>
<a4j:form id="forgotPassword">
<a4j:jsFunction name="resetPassword"
action="#{forgotPasswordActions.resetPassword}"
oncomplete="jQuery('#forgotPasswordDialog').dialog('open')">
<a4j:actionparam name="userEmail" assignTo="#{forgotPasswordActions.email}" />
<a4j:actionparam name="userCardno" assignTo="#{forgotPasswordActions.cardnumber}" />
</a4j:jsFunction>
</a4j:form>
</div>
</div>
I cant work out why it wont set this bean property?? Any help appreciated!
Your setter is called setCardno() while setCardnumber() is been expected by the view. The #{bean.property} does not relate to property names in the bean. It relates to getter/setter method names. There are 2 ways to fix this:
Rename the setter method:
public void setCardnumber(int cardnumber) {
this.cardnumber = cardnumber;
}
Or, rename the view property:
assignTo="#{forgotPasswordActions.cardno}"