I want to convert an old application to a new one in which i make HTTP calls (GET/POST/DELETE). I have a model package where i have my POJO classes and i made a DTO package where i have Dto classes. When i make a simple GET method to take a String message as a response i dont have any problem but when i use dependency injection and i want to use GET or POST methods to see some details about my app i get in postman this message: inStream parameter is null
OwnerResource.java
#Path("ownerResource")
public class OwnerResource {
#GET
public Response ping() { // When i use only this method i dont have any problem and i can see the message
return Response
.ok("pang")
.build();
}
// When i use the below code i have problem in HTTP calls
#Inject
private OwnerService ownerService;
#GET
#Path("owner/{ownerId}")
#Produces("application/json")
public RestApiResult<PropertyOwnerDto> getOwner(#PathParam("ownerId") int ownerId) {
return ownerService.getOwner(ownerId);
}
#POST
#Path("owner")
#Produces("application/json")
#Consumes("application/json")
public void createNewOwner(PropertyOwnerDto owner) {
ownerService.createPropertyOwner(owner);
}
}
PropertyOwner.java
#Data
#NoArgsConstructor
#Entity(name = "propertyowner")
#Table
public class PropertyOwner extends PersistentClass {
// id is in PersistenceClass
private int vat;
private String name;
private String surname;
private String address;
private String phoneNumber;
private String email;
private String username;
private String password;
#OneToMany(mappedBy = "owner", orphanRemoval = true)
private List<Property> properties;
public PropertyOwner(int vat, String name, String surname, String address, String phoneNumber, String email, String username, String password) {
this.vat = vat;
this.name = name;
this.surname = surname;
this.address = address;
this.phoneNumber = phoneNumber;
this.email = email;
this.username = username;
this.password = password;
}
}
PropertyOwnerDto.java
#Data#Getter#Setter
public class PropertyOwnerDto {
private int vat;
private String name;
private String surname;
private String address;
private String phoneNumber;
private String email;
private String username;
private String password;
public PropertyOwnerDto() {
}
public PropertyOwnerDto(PropertyOwner propertyOwner) {
this.vat = propertyOwner.getVat();
this.name = propertyOwner.getName();
this.surname = propertyOwner.getSurname();
this.address = propertyOwner.getAddress();
this.phoneNumber = propertyOwner.getPhoneNumber();
this.email = propertyOwner.getEmail();
this.username = propertyOwner.getUsername();
this.password = propertyOwner.getPassword();
}
public PropertyOwner asOwner() {
PropertyOwner propertyOwner = new PropertyOwner();
propertyOwner.setVat(vat);
propertyOwner.setName(name);
propertyOwner.setSurname(surname);
propertyOwner.setAddress(address);
propertyOwner.setPhoneNumber(phoneNumber);
propertyOwner.setEmail(email);
propertyOwner.setUsername(username);
propertyOwner.setPassword(password);
return propertyOwner;
}
OwnerService.java
public interface OwnerService {
void createPropertyOwner(PropertyOwnerDto ownerDto);
RestApiResult<PropertyOwnerDto> getOwner(int ownerId);
}
OwnerServiceImpl.java
public class OwnerServiceImpl implements OwnerService {
private final Properties sqlCommands = new Properties();
{
final ClassLoader loader = getClass().getClassLoader();
try ( InputStream config = loader.getResourceAsStream("sql.properties")) {
sqlCommands.load(config);
} catch (IOException e) {
throw new IOError(e);
}
}
private static final Logger logger = LogManager.getLogger(OwnerServiceImpl.class);
#Inject
private PropertyOwnerRepository propertyOwnerRepository;
#Inject
private PropertyRepository propertyRepository;
#Inject
private RepairRepository repairRepository;
// SOME OTHER CODE HERE //
#Override
public RestApiResult<PropertyOwnerDto> getOwner(int ownerId) {
PropertyOwnerDto ownerDto = new PropertyOwnerDto(propertyOwnerRepository.findById(ownerId));
return new RestApiResult<PropertyOwnerDto>(ownerDto, 0, "successful");
}
#Override
public void registerNewPropertyDto(PropertyDto propertyDto) {
propertyRepository.create(propertyDto.asProperty());
}
}
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
bean-discovery-mode="all">
</beans>
I use WILDFLY as application server.
Related
I am trying to set up a testing environment using an expectedPojo, but I continue getting the error that the constructor is undefined. From the error, I see it is listing the LocalTimeDate in the expectedPojo as String which may be the cause, but I am unfamiliar with testing with a LocalDateTime entry.
public class UserPojo {
private int id;
private String name;
private String email;
private String phoneNumber;
private String username;
private String password;
private boolean darkModePreference;
private LocalDateTime registerDate;
private int roleId;
public UserPojo() {
}
public UserPojo(int id, String name, String email, String phoneNumber, String username, String password,
boolean darkModePreference, LocalDateTime registerDate, int roleId) {
super();
this.id = id;
this.name = name;
this.email = email;
this.phoneNumber = phoneNumber;
this.username = username;
this.password = password;
this.darkModePreference = darkModePreference;
this.registerDate = registerDate;
this.roleId = roleId;
}
#ExtendWith(MockitoExtension.class)
public class LoginAndRegisterTest {
#Mock
UserDao userDao;
#InjectMocks
UserServiceImpl userService;
private UserPojo expectedPojo;
private UserEntity dummyEntity;
#BeforeEach
public void setup() {
LocalDateTime now;
expectedPojo = new UserPojo(1, "Goldendeep", "golden#coolkids.com", "333-343-3434", "golden", "kaur11", false, "2022-06-25T22:37:24.894", 2);
dummyEntity = new UserEntity(1, "Goldendeep", "golden#coolkids.com", "333-343-3434", "golden", "kaur11", false, "2022-06-25T22:37:24.894", 2);
}
While creating the object of UserPojo class you are passing the second-to-last argument as String instead an argument of LocalDateTime type is expected, which is causing the issue. Either provide the argument of LocalDateTime type or modify the constructor definition to accept String argument.
I'm fairly new to Spring, and coding in general. I want to build an application from which a user can update existing records in a database from a CSV file. I'm using spring batch to do so, but when I execute the test, I get the famous:
java.lang.IllegalStateException: Existing transaction detected in JobRepository. Please fix this and try again (e.g. remove #Transactional annotations from client).
If I remove the #Transactional, I get this error instead:
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations [UPDATE com.example.testApp.Models.Person p SET p.address = p.address, p.country = p.country, p.cellphone = p.cellphone, p.city = p.city, p.phone = p.cellphone, p.email = p.email, p.age = p.age WHERE p.pplId IN (:ids_0, :ids_1, :ids_2, :ids_3, :ids_4, :ids_5, :ids_6, :ids_7, :ids_8, :ids_9, :ids_10)]
I've looked around some other solutions here, but they don't seem to work for what I want to do. I'm aware that hibernate supports batch updates, but again, I'm not sure how to make it work with what I already have.
Here's my test class (obviously with just the method I've been having problems with):
#SpringBootTest
public class PersonTests {
#Autowired
private PeopleRepository peopleRepository;
#Autowired
JobLauncher jobLauncher;
#Autowired
Job peopleInsertJob; //Separate job for inserting from a CSV file
#Autowired
Job peopleUpdateJob;
#Order(4) //executes after the insert from CSV test, for obvious reasons.
#Test
public void updateRecordsFromCsvFile() throws Exception {
Map<String, JobParameter> maps = new HashMap<>();
maps.put("time", new JobParameter(System.currentTimeMillis()));
JobParameters parameters = new JobParameters(maps);
JobExecution jobExecution = jobLauncher.run(peopleUpdateJob, parameters);
assertEquals("COMPLETED", jobExecution.getStatus().name());
}
}
Here's my Batch configuration file for the update:
#Bean
public Job peopleUpdateJob(JobBuilderFactory jobBuilderFactory,
StepBuilderFactory stepBuilderFactory,
ItemReader<Person> itemReaderForUpdate,
ItemProcessor<Person, Person> itemProcessor,
#Qualifier("DBPeopleUpdater") ItemWriter<Person> itemUpdater)
{
Step step = stepBuilderFactory.get("ETL-file-load")
.<People, People>chunk(100)
.reader(itemReaderForUpdate)
.processor(itemProcessor)
.writer(itemUpdater)
.build();
return jobBuilderFactory.get("ETL-Load")
.incrementer(new RunIdIncrementer())
.start(step)
.build();
}
#Bean
public FlatFileItemReader itemReaderForUpdate() throws Exception
{
FlatFileItemReader<Person> flatFileItemReader = new FlatFileItemReader<>();
flatFileItemReader.setResource(new FileSystemResource("src/main/resources/CSV/TestFilePeople-UpdateBatch.csv"));
flatFileItemReader.setName("PeopleCSV-Update-Reader");
flatFileItemReader.setLinesToSkip(1);
flatFileItemReader.setLineMapper(lineMapper());
return flatFileItemReader;
}
#Bean
public LineMapper<Person> lineMapper()
{
DefaultLineMapper<Person> defaultLineMapper = new DefaultLineMapper<>();
DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
lineTokenizer.setDelimiter(",");
lineTokenizer.setStrict(false);
lineTokenizer.setNames("Id","lastname", "lastname2", "firstname",
"midname","phone", "cellphone", "email", "status", "address", "city", "region", "country");
BeanWrapperFieldSetMapper<Person> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
fieldSetMapper.setTargetType(Person.class);
defaultLineMapper.setLineTokenizer(lineTokenizer);
defaultLineMapper.setFieldSetMapper(fieldSetMapper);
return defaultLineMapper;
}
This is the ItemWriter I'm using for the update:
#Component
public class DBPeopleUpdater implements ItemWriter<Person> {
#Autowired
PeopleRepository peopleRepository;
#Override
public void write(List<? extends Person> list) throws Exception
{
List<String> pplIds = new ArrayList<>();
for(People p : list)
{
pplIds.add(p.getpplId());
}
peopleRepository.updatePersonsByIdIsIn(pplIds);
}
}
This is the method that I'm using from my PeopleRepository. It's the only method I have. My repository is extends CrudRepository:
#Modifying
#Query("UPDATE Person p SET p.address= p.address, p.country = p.country," +
"p.cellphone = p.cellphone, p.city = p.city, p.phone = p.cellphone," +
"p.email = p.email, p.status = p.status WHERE p.pplId IN :ids")
List<Person>updatePersonsByPplIdIsIn(List<String> ids);
I'm mapping my Person class as an entity that connects to a PostgreSQL database table. I don't have any local sql files in my project. I have no other tables in the database.
If it's not possible to solve this using spring batch, if you could guide me towards jpa/hibernate resources I could use to understand how to solve this problem, I would really appreciate it.
EDIT
Here's the code for the entity
#Entity
#Table(name = "people", schema = "example_schema")
public class Person {
#NonNull
#Id
#Column(name = "ppl_id")
private String pplId;
#Column(name = "ppl_last_name")
private String lastname;
#Column(name = "ppl_sec_last_name")
private String lastname2;
#Column(name = "ppl_first_name")
private String firstname;
#Column(name = "ppl_mid_name")
private String midname;
#Column(name = "ppl_phone_no")
private String phone;
#Column(name = "ppl_cell_no")
private String cellphone;
#Column(name = "ppl_corp_email")
private String email;
#Column(name="ppl_status")
private String status;
#Column(name="ppl_address")
private String address;
#Column(name="ppl_city")
private String city;
#Column(name="ppl_region")
private String region;
#Column(name="ppl_country")
private String country;
//Default constructor
public Person()
{
super();
}
public Person(#NonNull String pplId, String lastname, String lastname2, String firstname, String midname, String phone, String cellphone, String email, String status, String address, String city, String region, String country) {
this.pplId = pplId;
this.lastname = lastname;
this.lastname2 = lastname2;
this.firstname = firstname;
this.midname = midname;
this.phone = phone;
this.cellphone = cellphone;
this.email = email;
this.status = status;
this.address = address;
this.city = city;
this.region = region;
this.country = country;
}
public String getPplId() {
return pplId;
}
public void setWrkId(String pplId) {
this.pplId = pplId;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getLastname2() {
return lastname2;
}
public void setLastname2(String lastname2) {
this.lastname2 = lastname2;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getMidname() {
return midname;
}
public void setMidname(String midname) {
this.midname = midname;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getCellphone() {
return cellphone;
}
public void setCellphone(String cellphone) {
this.cellphone = cellphone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
I am aware of ResultBinding when trying to validate a form (get request), i am also aware of #Valid when trying to validate a request body but i have no knowledge of validating a multipartForm Request parameter. I have a multipart form with a request parameter #RequestParam("model") String userJson of a json string representation of my User.class. when i convert the json to an object,
User user = new Gson().fromJson(userJson, User.class);
how can i check if the parameters in the user class meets requirements, such as (email having an '#' or first name not having numeral values) ?
This is my code, the controller :
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public ResponseEntity<ResponseModel> SignUp(#RequestParam("file") MultipartFile file, #RequestParam("model") String userJson, RedirectAttributes redirectAttributes){
User tempSavedUser = new User();
try {
if (file.isEmpty()) {
redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
responseModel.setIsSuccessful(false).setResponseMessage("Please select an image to upload");
return new ResponseEntity<>(responseModel, HttpStatus.CREATED); // return response to client.
}
User user = new Gson().fromJson(userJson, User.class);
tempSavedUser = this.defaultUserDAOService.saveUser(user);
responseModel.setIsSuccessful(true);
responseModel.setResponseMessage("Registration was successful, Please check your mail for an account activation link");
return new ResponseEntity<ResponseModel>(responseModel, HttpStatus.CREATED);
}
this is the user class to:
#Table(name = "Users")
public class User extends DefaultEntity {
#Column(name = "FirstName")
#NotNull(message = "Enter a FirstName")
private String firstName;
#Column(name = "LastName")
#NotBlank(message = "Enter a LastName")
private String lastName;
#Column(unique = true,name = "UserName")
#NotBlank(message = "Enter a UserName")
private String userName;
#Column(unique = true, name = "Email")
#NotBlank(message = "Please enter an Email address")
#Email(message = "Enter a valid Email")
private String email;
#Column(name = "Password")
#NotBlank(message = "Enter a Password")
private String password;
#Enumerated(EnumType.STRING)
#Column(name = "Gender")
private Gender gender;
#Column(name = "Address")
#NotBlank(message = "Please enter your Home Address")
private String address;
#Column(name = "Country")
#NotBlank(message = "Please enter your Country")
private String country;
#Column(name = "Picture")
private String picture;
#Column(unique = true, name = "PhoneNumber") //make this accept only numbers
private String phoneNumber;
#Column(name = "Bio")
private String bio;
#Enumerated(EnumType.STRING)
#Column(name = "OnlineStatus")
private OnlineStatus onlineStatus;
#Enumerated(EnumType.STRING)
#Column(name = "UserType")
private UserType userType;
#Column(name = "Money")
private double money;
//#MapsId()
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "playerstats")
private PlayerStats playerStats;
//columnDefinition = "tinyint default false"
#Column(name = "locked",columnDefinition = "BOOL default false")
private Boolean locked;
#Transient
private MultipartFile file;
public String getFirstName() {
return firstName;
}
public User setFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public String getLastName() {
return lastName;
}
public User setLastName(String lastName) {
this.lastName = lastName;
return this;
}
public String getUserName() {
return userName;
}
public User setUserName(String userName) {
this.userName = userName;
return this;
}
public String getEmail() {
return email;
}
public User setEmail(String email) {
this.email = email;
return this;
}
public String getPassword() {
return password;
}
public User setPassword(String password) {
this.password = password;
return this;
}
public Enum.Gender getGender() {
return gender;
}
public User setGender(Enum.Gender gender) {
this.gender = gender;
return this;
}
public String getAddress() {
return address;
}
public User setAddress(String address) {
this.address = address;
return this;
}
public String getCountry() {
return country;
}
public User setCountry(String country) {
this.country = country;
return this;
}
public String getPicture() {
return picture;
}
public User setPicture(String picture) {
this.picture = picture;
return this;
}
public String getBio() {
return bio;
}
public User setBio(String bio) {
this.bio = bio;
return this;
}
public Enum.OnlineStatus getOnlineStatus() {
return onlineStatus;
}
public User setOnlineStatus(Enum.OnlineStatus onlineStatus) {
this.onlineStatus = onlineStatus;
return this;
}
public Enum.UserType getUserType() {
return userType;
}
public User setUserType(Enum.UserType userType) {
this.userType = userType;
return this;
}
public double getMoney() {
return money;
}
public User setMoney(double money) {
this.money = money;
return this;
}
public String getPhoneNumber() {
return phoneNumber;
}
public User setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
return this;
}
public MultipartFile getFile() {
return file;
}
public User setFile(MultipartFile file) {
this.file = file;
return this;
}
public PlayerStats getPlayerStats() {
return playerStats;
}
public User setPlayerStats(PlayerStats playerStats) {
this.playerStats = playerStats;
return this;
}
public Boolean getLocked() {
return locked;
}
public void setLocked(Boolean locked) {
this.locked = locked;
}
}
Inorder to validate a multipart form with spring you can take take this approach :
1. Add Dependencies
include the commons-fileupload which is used to upload a MultipartFile.
include the validation-api to create a Validator.
As:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
2.Create a Web Config:
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = {
"com.test"
})
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource rb = new ResourceBundleMessageSource();
rb.setBasenames(new String[] {
"validation"
});
return rb;
}
#Bean(name = "multipartResolver")
public CommonsMultipartResolver getResolver() {
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
//set max upload size per file is 20mb
commonsMultipartResolver.setMaxUploadSizePerFile(20 * 1024 * 1024);
return commonsMultipartResolver;
}
}
3.Create WebInitializer Class which extends from AbstractAnnotationConfigDispatcherServletInitializer
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class << ? > [] getRootConfigClasses() {
return new Class[] {
WebConfig.class
};
}
#Override
protected Class << ? > [] getServletConfigClasses() {
// TODO Auto-generated method stub
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] {
"/"
};
}
}
4. Create Your Model
public class Model {
private User user;
private CommonsMultipartFile[] files;
//getter and setter
}
5.Create a Validator to validate you file
#Component
public class ModelValidator implements Validator {
public boolean supports(Class << ? > clazz) {
return Model.class.isAssignableFrom(clazz);
}
public void validate(Object target, Errors errors) {
Model model = (Model) target;
CommonsMultipartFile[] commonsMultipartFiles = model.getFiles();
for (CommonsMultipartFile multipartFile: commonsMultipartFiles) {
if (multipartFile.getSize() == 0) {
errors.rejectValue("files", "myapplication.missing.file");
}
}
}
}
6. Add validation error message in side application.properties under resources folder
myapplication.missing.file=No file choose
7. Develop a controller
#Controller
#RequestMapping(value = "/")
public class Controller {
#Autowired
ModelValidator modelValidator;
#RequestMapping(value = "/page", method = RequestMethod.GET)
public ModelAndView page() {
ModelAndView model = new ModelAndView("page");
model.addObject("model", new Model());
return model;
}
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public ModelAndView upload(#ModelAttribute("model") Model model, BindingResult result) throws IOException {
// :::: VALIDATION HAPPENS HERE ::::
fileValidator.validate(model, result);
if (result.hasErrors()) {
return new ModelAndView("page");
}
return new ModelAndView("success", "fileNames", processUpload(model));
}
private List < String > processUpload(Model model) throws IOException {
List < String > Models = new ArrayList < String > ();
CommonsMultipartFile[] commonsMultipartFiles = model.getFiles();
for (CommonsMultipartFile multipartFile: commonsMultipartFiles) {
FileCopyUtils.copy(multipartFile.getBytes(), new File("C:\\upload\\" + multipartFile.getOriginalFilename()));
fileNames.add(multipartFile.getOriginalFilename());
}
return fileNames;
}
}
More Information:
https://memorynotfound.com/spring-mvc-file-upload-example-validator/
http://websystique.com/springmvc/spring-mvc-4-fileupload-download-hibernate-example/
http://javainsimpleway.com/spring-mvc-file-upload-with-validation/
I am implementing a sample Spring MVC Form with Form Validation. I have a complex type Address as bean property for Student form bean. And I have added form validation #NotEmpty for Address bean properties. But the same is not reflecting in the UI. But form validation works for other primitive types of Student form bean.
So, Validation works perfectly for Student form bean but not for nested complex types like Address within Student form bean.
I am trying understand the reason and a fix.
Spring version 4.0+.
Hibernate Validator api:5.2.4
Student POJO:
package com.xyz.form.beans;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
import com.xyz.validators.DateNotEmpty;
import com.xyz.validators.ListNotEmpty;
public class Student {
#Size(min = 2, max = 30)
private String firstName;
#Size(min = 2, max = 30)
private String lastName;
#NotEmpty
private String gender;
#DateNotEmpty
#Past
private Date DOB;
private String email;
private String mobileNumber;
#ListNotEmpty
private List<String> courses;
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Date getDOB() {
return DOB;
}
public void setDOB(Date dOB) {
DOB = dOB;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobileNumber() {
return mobileNumber;
}
public void setMobileNumber(String mobileNumber) {
this.mobileNumber = mobileNumber;
}
public List<String> getCourses() {
return courses;
}
public void setCourses(List<String> courses) {
this.courses = courses;
}
}
Address POJO:
package com.xyz.form.beans;
import org.hibernate.validator.constraints.NotEmpty;
import com.xyz.validators.LongNotEmpty;
public class Address {
#NotEmpty
private String houseNo;
#NotEmpty
private String street;
#NotEmpty
private String area;
#NotEmpty
private String city;
#LongNotEmpty
private Long pin;
public String getHouseNo() {
return houseNo;
}
public void setHouseNo(String houseNo) {
this.houseNo = houseNo;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Long getPin() {
return pin;
}
public void setPin(Long pin) {
this.pin = pin;
}
}
Student Controller:
#RequestMapping(value = "/newStudentDetails.do", method = RequestMethod.POST)
public ModelAndView newStudentDetails(
#Valid #ModelAttribute("student") com.xyz.form.beans.Student studentFormBean,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return new ModelAndView("newStudentPage");
}
Student studentDto = new Student();
studentDto.setFirstName(studentFormBean.getFirstName());
studentDto.setLastName(studentFormBean.getLastName());
studentDto.setGender(studentFormBean.getGender());
studentDto.setDOB(new Date(studentFormBean.getDOB().getTime()));
studentDto.setEmail(studentFormBean.getEmail());
studentDto.setMobileNumber(studentFormBean.getMobileNumber());
StringBuilder sb = new StringBuilder();
sb.append(studentFormBean.getAddress().getHouseNo() + ", ");
sb.append(studentFormBean.getAddress().getStreet() + ", ");
sb.append(studentFormBean.getAddress().getArea() + ", ");
sb.append(studentFormBean.getAddress().getCity() + "-");
sb.append(studentFormBean.getAddress().getPin());
studentDto.setAddress(sb.toString());
studentDto.setCourses(studentFormBean.getCourses());
studentDao.createStudent(studentDto);
ModelAndView mav = new ModelAndView("newStudentSuccess");
return mav;
}
Thanks,
Viswanath
You need to annotate your complex types with #Valid.
This is the reference (which references here)
Hi lets try #ModelAttribute("student") #Valid com.xyz.form.beans.Student studentFormBean in place of #Valid #ModelAttribute("student")
For nested complex types, you have to activate the direct field access. Just like below:
#org.springframework.web.bind.annotation.ControllerAdvice
public class ControllerAdvice {
#InitBinder
public void initBinder(WebDataBinder webDataBinder) {
webDataBinder.initDirectFieldAccess();
}
I'm trying to use JPA for the first time in a project. Most of my entities are working fine, but I am having trouble with one which is part of a Joined Inheritance Strategy.The entities are also being serialised by Jackson so they also have Json annotations.
The parent "User" class:
(Edit: added "Type" field)
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include= JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonTypeName("user")
#JsonSubTypes({
#JsonSubTypes.Type(name="customer", value=Customer.class),
#JsonSubTypes.Type(name="employee", value=Employee.class)})
#Entity(name = "User")
#Table(name="user")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name="type",discriminatorType = DiscriminatorType.INTEGER)
#NamedQuery(name="User.all",query = "select u from User u")
public abstract class User {
#Id
private String username;
#Column(name = "type",nullable = false)
private int type;
public User(){
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public abstract Set<Order> getOrders();
}
A Child "Employee"
#JsonTypeName("employee")
#Entity(name="Employee")
#Table(name="employee")
#PrimaryKeyJoinColumn(name = "username",referencedColumnName = "username")
#DiscriminatorValue("1")
#NamedQuery(name = "Employee.all",query = "select e from Employee e")
public class Employee extends User implements Serializable{
private String username;
private String firstName;
private String lastName;
private String email;
#Convert(converter = LocalDatePersistenceConverter.class)
private LocalDate dateStarted;
#Convert(converter = LocalDatePersistenceConverter.class)
private LocalDate dateEnded;
#OneToMany(mappedBy = "employee",targetEntity = Order.class,fetch = FetchType.EAGER,cascade = CascadeType.PERSIST)
#JsonIgnore
private Set<Order> orders = new HashSet<>();
public Employee() {
}
#Override
public Set<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
public void addOrder(Order order){
orders.add(order);
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setEmail(String email) {
this.email = email;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getEmail() {
return email;
}
public String getDateStarted() {
if(dateStarted != null)
return dateStarted.toString();
else return null;
}
public void setDateStarted(LocalDate dateStarted) {
this.dateStarted = dateStarted;
}
public String getDateEnded() {
if(dateEnded != null)
return dateEnded.toString();
else return null;
}
public void setDateEnded(LocalDate dateEnded) {
this.dateEnded = dateEnded;
}
#Override
public String toString(){
return getUsername();
}
}
And a child "Customer":
(Edit: removed #Id field)
#JsonTypeName("customer")
#Entity(name="Customer")
#Table(name="customer")
#PrimaryKeyJoinColumn(name = "username",referencedColumnName = "username")
#DiscriminatorValue("2")
#NamedQueries({
#NamedQuery(name="Customer.all",query = "select c from Customer c")
})
public class Customer extends User implements Serializable{
public enum VIP_TYPE {NORMAL,SILVER,GOLD,DIAMOND}
#Transient
private static final int SILVER_THRESHOLD = 1000;
#Transient
private static final int GOLD_THRESHOLD = 2000;
#Transient
private static final int DIAMOND_THRESHOLD = 3000;
private String firstName;
private String lastName;
private String email;
private String address;
private String postcode;
private String mobileNumber;
private String homeNumber;
#Convert(converter = VipTypeConverter.class)
private VIP_TYPE vipGroup;
private String discount;
#OneToMany(mappedBy = "customer",targetEntity = Order.class,fetch=FetchType.EAGER,cascade = CascadeType.ALL)
#JsonIgnore
private Set<Order> orders = new HashSet<>();
public Customer() {
}
#Override
public Set<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
public void addOrder(final Order order){
orders.add(order);
updateVipGroup();
}
private void updateVipGroup() {
int sum = orders.stream().map(Order::getPayment).distinct().mapToInt(p->p.getAmmount()).sum();
if(sum > DIAMOND_THRESHOLD){
vipGroup = VIP_TYPE.DIAMOND;
return;
}
if(sum > GOLD_THRESHOLD){
vipGroup = VIP_TYPE.GOLD;
return;
}
if(sum > SILVER_THRESHOLD){
vipGroup = VIP_TYPE.SILVER;
return;
}
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setEmail(String email) {
this.email = email;
}
public void setAddress(String address) {
this.address = address;
}
public void setDiscount(String discount) {
this.discount = discount;
}
public void setVipGroup(VIP_TYPE vipGroup) {
this.vipGroup = vipGroup;
}
public void setHomeNumber(String homeNumber) {
this.homeNumber = homeNumber;
}
public void setMobileNumber(String mobileNumber) {
this.mobileNumber = mobileNumber;
}
public void setPostcode(String postcode) {
this.postcode = postcode;
}
public String getDiscount() {
return discount;
}
public VIP_TYPE getVipGroup() {
return vipGroup;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getEmail() {
return email;
}
public String getAddress() {
return address;
}
public String getPostcode() {
return postcode;
}
public String getMobileNumber() {
return mobileNumber;
}
public String getHomeNumber() {
return homeNumber;
}
}
Persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="local" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/cod</jta-data-source>
<class>com.technicalpioneers.cod.user.Customer</class>
<class>com.technicalpioneers.cod.user.Employee</class>
<class>com.technicalpioneers.cod.user.User</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
</persistence-unit>
</persistence>
Everything to do with "employee" works file, I can use the named query Employee.all to find all the employees in the database.
However, If I try to retrieve any customers I get errors. If I try to run the named query Customer.all I get:
java.lang.IllegalArgumentException: NamedQuery of name: Customer.all not found.
If I try to use EntityManager's find() method to find a particular customer I get:
javax.servlet.ServletException: Exception [EclipseLink-43] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Missing class for indicator field value [2] of type [class java.lang.Integer].
Descriptor: RelationalDescriptor(com.technicalpioneers.cod.user.User --> [DatabaseTable(user)])
I don't understand why the Customer entity is not being found by JPA. I've checked the user table and the "type" column is there with correct numbers, and #DescriminatorValue is set correctly. It's almost like the annotations are being ignored?
Have done many clean rebuilds and redeploys too. Any help would be very much appreciated!
I found this eventually. https://bugs.eclipse.org/bugs/show_bug.cgi?id=429992
It turns out EclipseLink will silently ignore entities with lambda expressions! Very annoying for it to not be at least mentioned in logs!
Thanks to everyone who took the time!