I am creating a project using spring MVC.
I am validating fields using jsr303.
It is working fine It showing errors of fields in console but not in jsp page
Can any one let me know what wrong I am doing here ?
My controller method is
#RequestMapping(value="/addCampaign", method = RequestMethod.POST)
public String processForm(#ModelAttribute(value="Campaign") #Valid CampaignEntity campaignObj,BindingResult result, ModelMap model, Principal principal) {
validator.validate(campaignObj, result);
if(result.hasErrors()){
System.out.println(result.getErrorCount());
System.out.println(result.toString());
CampaignEntity campaignBean = new CampaignEntity();
Map<String,String> agencies = new LinkedHashMap<String,String>();
agencies.put("1", "United Stated");
agencies.put("2", "China");
agencies.put("3", "Singapore");
agencies.put("4", "Malaysia");
model.addAttribute("agencies", agencies);
model.addAttribute("publishers", agencies);
model.addAttribute("Campaign", campaignBean);
return "addCampaign";
}else{
return campaign(model, principal);
}
}
In jsp I am showing error using tag
<form:errors path="*" cssClass="error" />
my entity class is
package com.nativeunlock.dto;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.*;
import javax.validation.constraints.Digits;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.NotEmpty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
#SuppressWarnings("serial")
#NamedQueries({
#NamedQuery(
name = CampaignEntity.GET_CAMPAIGNS_QUERY,
query = "from CampaignEntity campaign"
),
#NamedQuery(
name = CampaignEntity.DELETE_CAMPAIGNS_QUERY,
query = "DELETE FROM CampaignEntity campaign WHERE campaign.campaign_id = :campaign_id"
)
})
#Entity
#Table(name = "campaign")
public class CampaignEntity implements Serializable {
public static final String GET_CAMPAIGNS_QUERY ="getCampaignList";
public static final String DELETE_CAMPAIGNS_QUERY ="deleteCampaignList";
#Id
#Getter
#Setter
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "campaign_id", unique = true, nullable = false)
private int campaign_id;
#Getter
#Setter
#Column(name = "name", unique = true)
#NotEmpty(message="Name field is mandatory.")
private String name;
#Getter
#Setter
#Column(name = "no_of_views")
#NotNull(message="No. of Views field is mandatory.")
private int no_of_views;
#Getter
#Setter
#Column(name = "video_url")
#NotEmpty(message="Video URL field is mandatory.")
private String video_url;
#Getter
#Setter
#Column(name = "start_date")
#NotEmpty(message="Start Date field is mandatory.")
private String start_date;
#Getter
#Setter
#Column(name = "end_date")
#NotEmpty(message="End Date field is mandatory.")
private String end_date;
#Getter
#Setter
#Column(name = "click_to_play")
#NotNull(message="Click to play field is mandatory.")
private int click_to_play;
#Getter
#Setter
#Column(name = "frequency")
#NotNull(message="Frequency field is mandatory.")
private int frequency;
#Getter
#Setter
#Column(name = "priority")
#Digits(fraction = 0, integer = 100)
#NotNull(message="Priority field is mandatory.")
private int priority;
#Getter
#Setter
#Column(name = "divice")
#NotNull(message="Divice field is mandatory.")
private int divice;
#Getter
#Setter
#Column(name = "operating_system")
#NotNull(message="Operating system field is mandatory.")
private int operating_system;
#Getter
#Setter
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "publisher_id")
#NotEmpty(message="Publisher field is mandatory.")
private PublisherEntity publishers;
#Getter
#Setter
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "agency_id")
#NotEmpty(message="Agency field is mandatory.")
private AgencyEntity agencies;
}
my validator class is
package com.nativeunlock.Validator;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import com.nativeunlock.dto.CampaignEntity;
#Component
public class CampaignValidator implements Validator{
#Override
public boolean supports(Class c) {
return CampaignEntity.class.isAssignableFrom(c);
}
#Override
public void validate(Object command, Errors errors) {
CampaignEntity campaignBean = (CampaignEntity)command;
/*if(!regBean.getPassword().equals(regBean.getRePassword()))
errors.rejectValue("rePassword","password.notmatch");*/
}
}
The problem is that by adding a model attribute model.addAttribute("Campaign", campaignBean); you're removing your binding errors from the model.
Binding errors are bound to the validated instance. If you set a breakpoint to a first line inside your method and inspect a model, you will see a validated object with the key Campaign, but also a binding error object with the key org.springframework.validation.BindingResult.Campaign
When you move further, and reach a point where you're adding a new instance to the model under a 'Campaign' key, notice that the binding errors will get removed from the model, since the instance they're bound is no longer existing, and that is why you don't see them in your JSP.
Remove the model.addAttribute("Campaign", campaignBean); or change the key, and you should see your binding erros.
Also not that you don't need the manual invocation validator.validate(campaignObj, result); the #Valid will call the validation for you
Related
I have the following code, where I need to populate a set with multiple objects that are instances of the same class (User). The problem is, I only get the first object when I log.
user = User.builder()
.id(1L)
.username("2397047")
.nickname("test1")
.build();
anotherUser = User.builder()
.id(2L)
.username("23971948")
.nickname("test2")
.build();
Set<User> userSet = new HashSet<>();
userSet.add(user);
userSet.add(anotherUser);
System.out.println("User set from test " + userSet);
This code produces the following output
User set from test [User(id=1, nickname=test1, username= 2397047, password=null, roles=null, groups=null)]
Why am I unable to get the entire collection?
This is my User class
package com.chama.chamaservice.user;
import com.chama.chamaservice.BaseEntity;
import com.chama.chamaservice.Views;
import com.chama.chamaservice.config.ApplicationUserRole;
import com.chama.chamaservice.group.Group;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.*;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
#Builder
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#ToString
public class User extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(Views.Public.class)
private Long id;
#JsonView(Views.Public.class)
private String nickname;
#JsonView(Views.Public.class)
private String username; // <- Unique user's phone number
private String password;
#ElementCollection(targetClass = ApplicationUserRole.class)
#CollectionTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"))
#Enumerated(EnumType.STRING)
#Column(name = "role")
private Set<ApplicationUserRole> roles;
#JsonIgnore
#ManyToMany(mappedBy = "groupMembers", fetch = FetchType.LAZY, targetEntity = Group.class)
private Set<Group> groups = new HashSet<>();
}
In the User class, #Data annotation which will implement #Getter, #Setter, #ToString method.
It will print all values in the Set.
Found an answer, although it may not be the optimal solution. I annotated the User class with #EqualsAndHashCode
I am in the process of adding a DTO layer to a restful api. Before, the program used entity (Recipe and Ingredient) directly and now I added a new DTO layer in between (RecipeDTO IngredientDTO). However, the moment I made the change I started getting Null values from #RequestBody. Each recipe contains a list of Ingredients and it is the list of ingredients that are returning null values, the recipe by itself is returning fine.
The controller looks like this
package com.example.recipes.controller;
import com.example.recipes.DTO.RecipeDTO;
import com.example.recipes.Service.RecipeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/*...*/
#PostMapping(path = "/post")
public void postRecipes(#RequestBody RecipeDTO recipeDTO){
recipeService.postRecipes(recipeDTO);
}
/*...*/
Recipe Entity
package com.example.recipes.Entity;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.List;
#Data
#Entity
#Table(name = "recipe", schema = "public")
#AllArgsConstructor
#NoArgsConstructor
public class Recipe {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#Column(name = "id", updatable = false, nullable = false)
private long id;
#Column(name = "name")
private String name;
#Column(name = "instructions")
private String instructions;
#OneToMany(mappedBy = "recipe")
private List<Ingredient> ingredients;
#JsonProperty("date_added")
private String dateAdded = String.valueOf(LocalDateTime.now());
#JsonProperty("last_edited")
private String lastEdited = String.valueOf(LocalDateTime.now());
}
RecipeDTO
package com.example.recipes.DTO;
import lombok.*;
import javax.persistence.OneToMany;
import java.time.LocalDateTime;
import java.util.List;
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#ToString
public class RecipeDTO {
private long id;
private String name;
private String instructions;
private List<IngredientDTO> ingredientsDTO;
private String dateAdded = String.valueOf(LocalDateTime.now());
private String lastEdited = String.valueOf(LocalDateTime.now());
public RecipeDTO(long id, String name, String instructions, String dateAdded, String lastEdited) {
this.id = id;
this.name = name;
this.instructions = instructions;
this.dateAdded = dateAdded;
this.lastEdited = lastEdited;
}
}
Ingredient Entity
package com.example.recipes.Entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.persistence.*;
#Data
#Entity
#Table(name = "Ingredient")
#NoArgsConstructor
public class Ingredient {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#JsonProperty("ingredient_id")
private long ingredient_ID;
#JsonProperty("ingredient_name")
private String ingredientName;
#Column(name = "amount")
private int amount;
#Column(name = "unit")
private String unit;
#ManyToOne
#JoinColumn(name = "recipe_id")
#ToString.Exclude
#JsonIgnore
private Recipe recipe;
}
IngredientDTO
package com.example.recipes.DTO;
import lombok.*;
#Data
#AllArgsConstructor
#NoArgsConstructor
public class IngredientDTO {
private long ingredientID;
private String ingredientName;
private int amount;
private String unit;
}
the json i sent
{
"name":"unique2",
"ingredients":[
{
"ingredient_name":"Atlantic",
"amount":13,
"unit":"ton"
},
{
"ingredient_name":"Pacific",
"amount":15,
"unit":"boatload"
},
{
"ingredient_name":"Indian",
"amount":38,
"unit":"trucload"
}
],
"instructions":"easy on the salt"
}
and the #requestbody the ingredientsDTO is null
this is recipe: RecipeDTO(id=0, name=unique2, instructions=easy on the salt, ingredientsDTO=null, dateAdded=2022-08-08T15:04:10.678748100, lastEdited=2022-08-08T15:04:10.678748100)
Edit: I have just tried copying the code from the entity classes and pasting them in the DTO classes and it still returning null...
package com.example.recipes.DTO;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.List;
#Data
#Entity
#Table(name = "recipe", schema = "public")
#AllArgsConstructor
#NoArgsConstructor
public class RecipeDTO {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#Column(name = "id", updatable = false, nullable = false)
private long id;
#Column(name = "name")
private String name;
#Column(name = "instructions")
private String instructions;
#OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredientDTOs;
#JsonProperty("date_added")
private String dateAdded = String.valueOf(LocalDateTime.now());
#JsonProperty("last_edited")
private String lastEdited = String.valueOf(LocalDateTime.now());
public RecipeDTO(long id, String name, String instructions, String dateAdded, String lastEdited) {
this.id = id;
this.name = name;
this.instructions = instructions;
this.dateAdded = dateAdded;
this.lastEdited = lastEdited;
}
}
package com.example.recipes.DTO;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.persistence.*;
#Data
#Entity
#Table(name = "Ingredient")
#NoArgsConstructor
public class IngredientDTO {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#JsonProperty("ingredient_id")
private long ingredientID;
#JsonProperty("ingredient_name")
private String ingredientName;
#Column(name = "amount")
private int amount;
#Column(name = "unit")
private String unit;
#ManyToOne
#JoinColumn(name = "recipe_id")
#ToString.Exclude
#JsonIgnore
private RecipeDTO recipeDTO;
public IngredientDTO(long ingredientID, String ingredientName, int amount, String unit) {
this.ingredientID = ingredientID;
this.ingredientName = ingredientName;
this.amount = amount;
this.unit = unit;
}
}
#RequestBody
this is recipe: RecipeDTO(id=0, name=unique2, instructions=easy on the salt, ingredientDTOs=null, dateAdded=2022-08-08T15:24:19.325806500, lastEdited=2022-08-08T15:24:19.325806500)
these are the ingredients: null
this is ingredientDTO: null
this is ingredientDTO: null
Edit2: I tried posting only the ingredientDTO and the #RequestBody was able to pick it up just fine
//this is fine
public void testRecipePost(#RequestBody IngredientDTO ingredientDTO) {
System.out.println("ingredientDTO: " + ingredientDTO);
}
You can replace
#OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredientDTOs;
to
#OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredients;
Or adding
#JsonProperty("ingredients")
Example:
#JsonProperty("ingredients")
#OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredientDTOs;
The reason for null is because Jackson doesn't know how to deserialise your fields properly with different names.
In the json, the name is ingredients but, in the DTO, it is ingredientsDTO. Those 2 need to match.
You request
{
"name":"unique2",
"ingredients":[...]
here the name of array you are passing in Json is different in the entity you are using.
Your DTO
#OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredientDTOs;
The name of fields in JSON request and Entity must match.
Change the name of field private List<IngredientDTO> ingredientDTOs; to ingredients.
How to put in order hibernate and database sequence generation?
My entity:
package kz.open.sankaz.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import javax.persistence.*;
#Entity
#Table(name = "SEC_ROLE")
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class SecRole extends AbstractEntity implements GrantedAuthority {
#Id
#GeneratedValue(generator = "SEC_ROLE_SEQ", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(allocationSize = 1, sequenceName = "SEC_ROLE_ID_SEQ", name = "SEC_ROLE_ID")
private Long id;
#Column(name = "NAME", nullable = false, unique = true)
private String name;
#Override
public String getAuthority() {
return getName();
}
}
I wrote that "allocationSize" is 1. But Hibernate generates wrong query:
Hibernate: create sequence public.sec_role_seq start 1 increment 50
It makes a problem not only while you are inserting a new data, it also makes problem when you are running database migration queries. For example, I wrote the next line:
create sequence public.sec_role_seq start 1 increment 1;
and Hibernate conflicts with it:
The increment size of the [SEC_ROLE_SEQ] sequence is set to [50] in the entity mapping while the associated database sequence increment size is [1]
How to solve it? Please, help!
I solved it changing generator in the following way:
#Entity
#Table(name = "SEC_ROLE")
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class SecRole extends AbstractEntity implements GrantedAuthority {
#Id
#GeneratedValue(generator = "SEC_ROLE_SEQ", strategy = GenerationType.SEQUENCE)
#GenericGenerator(
name = "SEC_ROLE_SEQ",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
#Parameter(name = "sequence_name", value = "SEC_ROLE_SEQ"),
#Parameter(name = "initial_value", value = "1"),
#Parameter(name = "increment_size", value = "1")
}
)
private Long id;
#Column(name = "NAME", nullable = false, unique = true)
private String name;
#Override
public String getAuthority() {
return getName();
}
}
I am making a Spring web service to learn more about it and I am currently mapping the database. I have a table that has a composite ID, where one of the ID's is a foreign key to another table (ManytoOne).
Creditors
Creditor_Invoices
ID
Creditor_ID
name
Invoice_ID
As anywhere you buy something they use their own way of making ID's it has a composite ID like this.
My Current code:
Serializable class CInvoiceId:
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import java.io.Serializable;
import java.util.Objects;
#Embeddable
public class CInvoiceId implements Serializable {
#ManyToOne
#JoinColumn(name = "creditors_id", nullable = false)
private Creditor cInvoiceCreditorId;
#Column(name = "invoice_id", nullable = false)
private String cInvoiceId;
public CInvoiceId(Creditor creditor, String cInvoiceId){
this.cInvoiceCreditorId = creditor;
this.cInvoiceId = cInvoiceId;
}
//Setters, Getters, Equals and Hash
}
My Creditor class
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name = "creditors")
public class Creditor {
#Id
#GeneratedValue
#Column(name = "id")
private int creditorId;
#Column(name = "name",nullable = false)
private String creditorName;
#OneToMany(mappedBy = "cInvoiceCreditorId")
private List<CInvoice> cInvoices;
}
My CInvoice class:
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;
#Entity
#Table(name = "c_invoices")
public class CInvoice {
#EmbeddedId
private CInvoiceId cInvoiceID;
}
When I start it to try and test it I get the error that it can not find the mapped by from the creditor class, but I don't know what I should map it to as the ID is now made in the CInvoiceId class. What should it be?
Regards
Dany
You can use "derived identities" to map these classes:
Creditor:
#Entity
#Table(name = "creditors")
public class Creditor {
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "name",nullable = false)
private String name;
#OneToMany(mappedBy = "creditor")
private List<CInvoice> invoices;
}
CInvoiceId:
#Embeddable
public class CInvoiceId implements Serializable {
#Column(name = "invoice_id", nullable = false)
private String invoiceID;
private int creditorID; // corresponds to PK type of Creditor
// ...
}
CInvoice:
#Entity
#Table(name = "c_invoices")
public class CInvoice {
#EmbeddedId
private CInvoiceId id;
#MapsId("creditorID") // maps creditorID attribute of embedded id
#ManyToOne
#JoinColumn(name = "creditors_id", nullable = false)
Creditor creditor;
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.
I have problem with adding users role during registration request
This is my DB diagram:
And I have the following classes: first is entity role:
package application.model;
import lombok.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.persistence.*;
import java.util.List;
#NamedQueries({
#NamedQuery(name = User.GET_USERS, query = User.QUERY_GET_USERS),
})
#Getter
#Setter
#NoArgsConstructor
#Entity
#Table(name = "users")
public class User {
public static final String GET_USERS = "User.get_users";
public static final String QUERY_GET_USERS = "select u from User u";
#Id
#NotNull
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id")
public int id;
#NotNull
#NotEmpty
#Column(name="firstname")
private String firstname;
#NotNull
#Column(name="lastname")
private String lastname;
#NotNull
#Column(name="email")
private String email;
#NotNull
#Column(name="password")
private String password;
#JoinTable
#OneToMany
private List<Role> roles;
}
second entity is Role:
package application.model;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
#Getter
#Setter
#NoArgsConstructor
#Entity
#Table(name = "role")
public class Role {
#Id
#NotNull
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public int id;
#NotNull
#Column(name="name")
private String name;
}
So, i have 3 roles in my application, when I register new user with post request:
{
"firstname": "imasdie5",
"lastname": "nazasdwisko5",
"email": "masdil",
"password": "pass",
"roles": [
{ "id":2 }
]
}
First user is registered correctly, but when i send second request with same roles.id=2, i have:
ERROR: duplicate key value violates unique constraint "uk_d9najy24fium4vkivgwjuf0hw"
Detail: Key (roles_id)=(2) already exists.
In dbeaver table users_role have constraint uk_d9najy24fium4vkivgwjuf0hw with type UNIQUE_KEY, so that's the problem, but how to change type to non-unique? Many users may have same role, so it is necessary for me
All tables are generated with hibernate.
Table users_role is not entity in my application, maybe it should be entity?
Do You have any advice what I should change to add one role for many users?
Try to use #ManyToMany annotation
#JoinTable
#ManyToMany
private List<Role> roles;