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;
}
Related
I'm trying to set security roles in my Spring Boot application.
If I use in memory users the roles work. My code looks as folows:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// add users for in memory authentication
UserBuilder users = User.withDefaultPasswordEncoder();
auth.inMemoryAuthentication()
.withUser(users.username("paul").password("test123").roles("MEMBER", "ADMIN"))
.withUser(users.username("sandra").password("test123").roles("MEMBER", "ADMIN"))
.withUser(users.username("matthew").password("test123").roles("MEMBER"));
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/users/list").hasAnyRole("MEMBER", "ADMIN")
.antMatchers("/events/list").hasAnyRole("MEMBER", "ADMIN")
.antMatchers("/events/showFormForAdd").hasRole("ADMIN")
.antMatchers("/events/listEventAttendeeDetails*").hasRole("ADMIN")
.antMatchers("/resources/**").permitAll()
.and()
.formLogin()
.loginPage("/showMyLoginPage")
.loginProcessingUrl("/authenticateTheUser")
.permitAll();
}
If I use database authentication for configure() the user is not authorised to access any pages. My database method looks like this:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
As The roles work for in memory and not database I thing the configure(HttpSecurity http) must work fine. I suspect there is some issue getting my roles. My User and Authority(roles) models are as follows:
#Entity
#Table(name="users")
public class User {
public static final PasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder();
#OneToMany(mappedBy="user",
cascade={CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH,
CascadeType.REMOVE})
private List<Authority> authorities;
// define fields
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
#Column(name="gender")
private String gender;
#DateTimeFormat(pattern="yyyy-MM-dd")
#Column(name="birth_date")
private LocalDate birthDate;
#Column(name="address_line1")
private String addressLine1;
#Column(name="address_line2")
private String addressLine2;
#Column(name="town")
private String town;
#Column(name="county")
private String county;
#Column(name="country")
private String country;
#Column(name="postcode")
private String postcode;
#Column(name="email")
private String email;
#Column(name="phone")
private String phone;
#Column(name="mobile")
private String mobile;
#Column(name="password")
private #JsonIgnore String password;
#Column(name="enabled")
private int enabled;
// define constructors
public User() {
}
public User(List<Authority> authorities, int id, String firstName, String lastName, String gender, LocalDate birthDate,
String addressLine1, String addressLine2, String town, String county, String country, String postcode,
String email, String phone, String mobile, String password, int enabled) {
this.authorities = authorities;
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.gender = gender;
this.birthDate = birthDate;
this.addressLine1 = addressLine1;
this.addressLine2 = addressLine2;
this.town = town;
this.county = county;
this.country = country;
this.postcode = postcode;
this.email = email;
this.phone = phone;
this.mobile = mobile;
this.password = password;
this.enabled = enabled;
}
public List<Authority> getAuthorities() {
return authorities;
}
public void setAuthorities(List<Authority> authorities) {
this.authorities = authorities;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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 LocalDate getBirthDate() {
return birthDate;
}
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
public String getAddressLine1() {
return addressLine1;
}
public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}
public String getAddressLine2() {
return addressLine2;
}
public void setAddressLine2(String addressLine2) {
this.addressLine2 = addressLine2;
}
public String getTown() {
return town;
}
public void setTown(String town) {
this.town = town;
}
public String getCounty() {
return county;
}
public void setCounty(String county) {
this.county = county;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getPostcode() {
return postcode;
}
public void setPostcode(String postcode) {
this.postcode = postcode;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = PASSWORD_ENCODER.encode(password);
}
public int getEnabled() {
return enabled;
}
public void setEnabled(int enabled) {
this.enabled = enabled;
}
}
#Entity
#Table(name="authorities")
public class Authority {
#ManyToOne(cascade= {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
#JoinColumn(name="email")
private User user;
#Id
#Column(name="id")
private String email;
#Column(name="authority")
private String authority;
public Authority() {
}
public Authority(User user, String email, String authority) {
this.user = user;
this.email = email;
this.authority = authority;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
}
This is the service I'm using to authenticate:
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired private UserRepository userRepository = null;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
org.springframework.security.core.userdetails.User user = null;
try {
Optional<User> optional = userRepository.findByEmail(username);
HashSet<GrantedAuthority> set = new HashSet<>();
/*
* Add SimpleGrantedAuthority to set as appropriate
*/
user = new org.springframework.security.core.userdetails.User(username, optional.get().getPassword(), set);
} catch (UsernameNotFoundException exception) {
throw exception;
} catch (Exception exception) {
throw new UsernameNotFoundException(username);
}
return user;
}
}
The Authorities are inserted into the DB as follows:
INSERT INTO `authorities`
VALUES
('john','ROLE_MEMBER'),
('mary','ROLE_MEMBER'),
('mary','ROLE_MANAGER'),
('susan','ROLE_MEMBER'),
('susan','ROLE_ADMIN');
I put some logging around UserDetailsServiceImpl. As you can see, no authorities are granted.
2020-10-21 17:02:17.612 INFO 20363 --- [nio-8080-exec-4] c.p.clubmanager.aspect.LoggingAspect : =====> in #Before: Calling method: UserDetailsServiceImpl.loadUserByUsername(..)
2020-10-21 17:02:17.613 INFO 20363 --- [nio-8080-exec-4] c.p.clubmanager.aspect.LoggingAspect : =====> argument: Paul_carron#hotmail.com
2020-10-21 17:02:17.619 INFO 20363 --- [nio-8080-exec-4] c.p.clubmanager.aspect.LoggingAspect : =====> in #AfterReturning: from method: UserDetailsServiceImpl.loadUserByUsername(..)
2020-10-21 17:02:17.619 INFO 20363 --- [nio-8080-exec-4] c.p.clubmanager.aspect.LoggingAspect : =====> result: org.springframework.security.core.userdetails.User#eefb770b: Username: Paul_carron#hotmail.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; **Not granted any authorities**
Is there anything I've done wrong, or may be missing in terms of getting my roles?
In UserDetailsServiceImpl it seems that you are setting Roles/Authority passing only new HashSet<>(). You can try this:
try {
Optional<User> optional = userRepository.findByEmail(username);
List<SimpleGrantedAuthority> authorities = set = new ArrayList<>();
if(optional.isPresent()) {
authorities = optional.get().getAuthorities().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName()))
.collect(Collectors.toList()))
}
user = new org.springframework.security.core.userdetails.User(username, optional.get().getPassword(), authorities);
} catch (UsernameNotFoundException exception) {
throw exception;
} catch (Exception exception) {
throw new UsernameNotFoundException(username);
}
This will be quite a bit of code as I don't know what will be important. I was trying to recreated the basic UI Alejandro made in my tutorial session with him a few months ago, substituting a table in my database for the one he used. The errors I'm getting all seem related to overriding Vaadin Flow functions. I know that replaces the behavior of the Super method. IntelliJ opens the relevant Super method when I click on the errors, which I'm assuming it wants me to edit to solve the problem, but I have no idea how to do that.
I was going to paste a link to the code but the forum told me to just place it here.
Customer.java
package com.dbproject.storeui;
import java.time.LocalDate;
public class Customer {
private Long id;
private String lastname;
private String firstname;
private String email;
private String password;
private String phone;
private String street;
private String city;
private String st;
private int zip;
private LocalDate dob;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getSt() {
return st;
}
public void setSt(String st) {
this.st = st;
}
public int getZip() {
return zip;
}
public void setZip(int zip) {
this.zip = zip;
}
public LocalDate getDob() {
return dob;
}
public void setDob(LocalDate dob) {
this.dob = dob;
}
}
CustomerRepository.java
package com.dbproject.storeui;
import org.apache.ibatis.annotations.*;
import java.util.List;
#Mapper
public interface CustomerMapper {
#Select("SELECT * FROM customer ORDER BY id")
List<Customer> findAll();
#Update("UPDATE customer" +
"SET lastname=#{lastname}, firstname=#{firstname}, email=#{email}, password=#{password}, phone=#{phone}, street=#{street}, city=#{city}, st=${st}, zip=#{zip}, dob=#{dob}" +
"WHERE id=#{id}")
void update(Customer customer);
#Insert("INSERT INTO customer(lastname, firstname, email, password, phone, street, city, st, zip, dob) VALUES(#{lastname}, #{firstname}, #{email}, #{password}, #{phone}, #{street}, #{city}, #{st}, #{zip}, #{dob})")
#Options(useGeneratedKeys = true, keyProperty = "id")
void create(Customer customer);
}
CustomerView.java (the UI class)
package com.dbproject.storeui;
import com.vaadin.flow.component.Composite;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.router.Route;
#Route("")
public class CustomerView extends Composite<VerticalLayout> {
private final CustomerMapper customerMapper;
private Grid<Customer> grid = new Grid<>();
private TextField lastname = new TextField("Last Name");
private TextField firstname = new TextField("First Name");
private Button save = new Button("Save", VaadinIcon.CHECK.create());
private Button create = new Button("New", VaadinIcon.PLUS.create());
private VerticalLayout form = new VerticalLayout(lastname, firstname, save);
private Binder<Customer> binder = new Binder<>(Customer.class);
private Customer customer;
public CustomerView(CustomerMapper customerMapper) {
this.customerMapper = customerMapper;
grid.addColumn(Customer::getLastname).setHeader("Last Name");
grid.addColumn(Customer::getFirstname).setHeader("First Name");
grid.addSelectionListener(event -> setCustomer(grid.asSingleSelect().getValue()));
updateGrid();
save.addClickListener(event -> saveClicked());
create.addClickListener(event -> createClicked());
getContent().add(grid, create, form);
binder.bindInstanceFields(this);
binder.setBean(null);
}
private void createClicked() {
grid.asSingleSelect().clear();
setCustomer(new Customer());
}
private void saveClicked() {
binder.readBean(customer);
if (customer.getId() == null) {
customerMapper.create(customer);
} else {
customerMapper.update(customer);
}
updateGrid();
Notification.show("Saved!");
}
private void setCustomer(Customer customer) {
this.customer = customer;
form.setEnabled(customer != null);
binder.setBean(customer);
}
private void updateGrid() {
grid.setItems(customerMapper.findAll());
}
}
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 have a problem with Hibernate. Im struggling with this since yesterday, it seems very easy but I have no idea why it is not working...
I have entity Login.java:
package offersmanager.model.entity;
import org.json.JSONObject;
import javax.persistence.*;
#Entity
public class Login {
#Id
#GeneratedValue
private Integer id;
#Column(nullable = false, unique = true)
String username;
#Column(nullable = false)
String password;
public Login(){
}
public Login(String username, String password){
this.username = username;
this.password = password;
}
public Login(JSONObject jsonObject) {
this.id = (Integer) jsonObject.get("id");
this.username = (String) jsonObject.get("username");
this.password = (String) jsonObject.get("password");
}
public JSONObject toJsonObject() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", this.id);
jsonObject.put("username", this.username);
jsonObject.put("password", this.password);
return jsonObject;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
And entity TourOffice.java:
package offersmanager.model.entity;
import org.json.JSONObject;
import javax.persistence.*;
#Entity
public class TourOffice {
#Id
#GeneratedValue
private Integer id;
#Column(nullable = false)
String officeName;
#Column(nullable = false)
String eMail;
#Column(nullable = false)
String phoneNumber;
#Column(nullable = false)
String city;
#Column(nullable = false)
String zipCode;
#Column(nullable = false)
String address;
#OneToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "login_id")
Login login;
public TourOffice(){
}
public TourOffice(String officeName, String eMail, String phoneNumber, String city, String zipCode, String address) {
this.officeName = officeName;
this.eMail = eMail;
this.phoneNumber = phoneNumber;
this.city = city;
this.zipCode = zipCode;
this.address = address;
}
public TourOffice(JSONObject jsonObject) {
this.id = (Integer) jsonObject.get("id");
this.officeName = (String) jsonObject.get("officeName");
this.eMail = (String) jsonObject.get("eMail");
this.phoneNumber = (String) jsonObject.get("phoneNumber");
this.city = (String) jsonObject.get("city");
this.zipCode = (String) jsonObject.get("zipCode");
this.address = (String) jsonObject.get("address");
this.login = (new Login((JSONObject) jsonObject.get("login")));
}
public JSONObject toJsonObject() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", this.id);
jsonObject.put("officeName", this.officeName);
jsonObject.put("eMail", this.eMail);
jsonObject.put("phoneNumber", this.phoneNumber);
jsonObject.put("city", this.city);
jsonObject.put("zipCode", this.zipCode);
jsonObject.put("address", this.address);
jsonObject.put("login", this.login == null? null : login.toJsonObject());
return jsonObject;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOfficeName() {
return officeName;
}
public void setOfficeName(String officeName) {
this.officeName = officeName;
}
public String geteMail() {
return eMail;
}
public void seteMail(String eMail) {
this.eMail = eMail;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Login getLogin() {
return login;
}
public void setLogin(Login login) {
this.login = login;
}
}
These entities are connected with #OneToOne relation.
What I'm trying to do is to find the name of my office (officeName) with field of Login class (username).
This is my function in TourOfficeDAO.java:
public TourOffice findOfficeNameByLogin(String username) {
Criteria name = createCriteria();
name.add(Restrictions.eq("login.username", username));
return (TourOffice) name.uniqueResult();
}
It goes through TourOfficeService to my rest controller where this method is invoked. But it doesn't matter cause exeception is thrown in DAO:
could not resolve property: login.username of:
offersmanager.model.entity.TourOffice; nested exception is
org.hibernate.QueryException: could not resolve property:
login.username of: offersmanager.model.entity.TourOffice
It can't find "login.username" and have no idea why... everything seems good.
I looked for similiar topics but I haven't still managed to make this works. Any help would be appreciated.
EDIT 1:
This is my abstract class DAO.java where is the function createCriteria()
public abstract class DAO<MODEL> implements Serializable {
public abstract Class<MODEL> getEntityClass();
#Autowired
protected SessionFactory sessionFactory;
protected Session getSession(){
return sessionFactory.getCurrentSession();
}
protected Query createQuery(String query){
return getSession().createQuery(query);
}
protected SQLQuery createSQLQuery(String query){
return getSession().createSQLQuery(query);
}
protected Criteria createCriteria(){
return getSession().createCriteria(getEntityClass());
}
#SuppressWarnings("unchecked")
public MODEL findById(Integer id) {
return (MODEL) getSession().get(getEntityClass(), id);
}
public void save(MODEL entity) {
getSession().save(entity);
getSession().flush();
}
public void update(MODEL entity) {
getSession().update(entity);
getSession().flush();
}
public void saveOrUpdate(MODEL entity) {
getSession().saveOrUpdate(entity);
getSession().flush();
}
public void delete(MODEL entity) {
getSession().delete(entity);
getSession().flush();
}
public List<MODEL> list(){
Criteria criteria = createCriteria();
#SuppressWarnings("unchecked")
List<MODEL> list = criteria.list();
return list;
}
}
I think you need first to create an alias like that:
public TourOffice findOfficeNameByLogin(String username) {
Criteria name = createCriteria();
name.createAlias("login", "login");
name.add(Restrictions.eq("login.username", username));
return (TourOffice) name.uniqueResult();
}
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!