Springboot: What is the best way to search for a List of an entity using more than one of its characteristics - spring-boot

I am developing a web application in spring-boot, where a user can search for users using a search field. The users being searched (which depends on the value typed into the input field) will be queried according to their username, first name and last-name. This is my UserModel :
#Component
#Entity
#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;
}
}
this is my method for querying the usermodel in my UserRepository :
#Repository
public interface UserRepository extends JpaRepository<User,Long> {
Page<User> findUsersByUserNameContainingOrFirstNameContainingOrLastNameContaining(String UserName, String FirstName, String LastName, Pageable pageable);
}
My question: Is there a better way or more efficient way to achieve querying the user entity ?

As mentioned in the comments, what you're looking for is a fuzzy search. This is not something you can easily do within a database, but there are separate search engines that you can use:
Apache Solr (platform based on Apache Lucene)
ElasticSearch
Hibernate Search (Hibernate integration with Apache Lucene)
...
When using such solution, you'll have to index your entities into your search engine as well. Spring Data can help you with that since there is also a library for Solr.
First of all you need a new class that represents how your entity will look like in Solr. Be aware that you want to "flatten" everything if you would have nested relations:
#Document
public class UserDocument {
#Id
#Indexed("id")
private String id;
#Indexed("firstName")
private String firstName;
#Indexed("lastName")
private String lastName;
#Indexed("userName")
private String userName;
// ...
}
After that, you can write a repository like you're used to with Spring Data:
public interface UserDocumentRepository extends SolrCrudRepository<UserDocument, String> {
#Query("userName:?0 OR firstName:?0 OR lastName:?0")
List<UserDocument> findAll(String searchTerm);
}
After that, you can do something like this:
public User create(User input) {
// Create user in database
documentRepository.save(new UserDocument(input.getFirstName(), input.getLastName(), input.getUserName());
}
And you can query for fuzzy searches by using the repository as well:
documentRepository.findAll("vickz~3");
This will use the query that I just wrote, and will look first firstnames, lastnames or usernames containing vickz. The ~3 at the end is a special syntax to indicate that the name can be 3 characters different from the one I just used (= edit distance).
However, this will return the UserDocument Solr entities. If you want to get the entities, you'll have to look them up as well, which can be done by their username:
List<String> usernames = documentRepository
.findAll("vickz~3")
.stream()
.map(UserDocument::getUserName)
.collect(Collectors.toList());
repository.findByUsername(usernames); // Look in database for users matching those usernames

Related

How should I Map foreign keys in entity classes to model class (pojo) objects

Model Class for User:
#Component
public class UserModel {
private Integer userId;
private Integer roleId;
private String firstName;
private String email;
private String password;
public Integer getUserId() {
return userId;
}
public Integer getRoleId() {
return roleId;
}
public String getFirstName() {
return firstName;
}
public String getEmail() {
return email;
}
public String getPassword() {
return password;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public void setName(String firstName) {
this.firstName = firstName;
}
public void setEmail(String email) {
this.email = email;
}
public void setPassword(String password) {
this.password = password;
}
}
Model Class for Role of the User:
#Component
public class RolesModel {
private Integer roleId;
private String roleName;
public Integer getRoleId() {
return roleId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
Entity Class for User:
#Entity
#Table(name = "userBloodBank")
public class User {
#Id
#GeneratedValue (strategy = GenerationType.AUTO)
private Integer userId;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="roleId")
private Roles roles;
#NotNull
#Column
#Pattern(regexp="[A-Z][a-zA-Z]*",message="Only enter words starting with captital letter")
private String firstName;
#NotNull
#Column
#Email
#Pattern(regexp = "^[a-zA-Z0-9+_.-]+#[a-zA-Z0-9.-]+$", message = "Enter a proper email ID")
private String email;
#NotNull
#Column
#Pattern(regexp= "^(?=.*[0-9])"
+ "(?=.*[a-z])(?=.*[A-Z])"
+ "(?=.*[##$%^&+=])"
+ "(?=\\S+$).{8,20}$", message="Enter minimum 8 characters and maximum 20 characters")
private String password;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public Roles getRoles() {
return roles;
}
public void setRoles(Roles roles) {
this.roles = roles;
}
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;
}
}
Entity Class for Role:
#Entity
#Table(name="Roles")
public class Roles {
#Id
#GeneratedValue (strategy = GenerationType.AUTO)
private Integer roleId;
#NotNull
#Column
#Pattern(regexp="(Recipient|Donor|BloodBankAdmin|Admin)", message = "Choose among Recipient|Donor|BloodBankAdmin|Admin")
private String roleName;
public Integer getRoleId() {
return roleId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
In the above case Role Id has oneToOne relationship and how will I map this to model object of role and user? Help me with the code to create the function "convertModelToEntity()" and "convertEntityToModel".
You can add below code in your User-entity class to map entity with Model.
public static UserModel convertEntityToModel(User user) {
UserModel userModel = new UserModel();
userModel.setUserId(user.getUserId());
userModel.setRoleId(user.getRoles().getRoleId());
userModel.setFirstName(user.getFirstName());
userModel.setEmail(user.getEmail());
userModel.setPassword(user.getPassword());
return userModel;
}
This will return User-model.
I think you should add the Roles-model(or list of Roles-model) in User Model.

Spring Boot test #After not deleting rows before next test

I have the following #Before and #After in my Spring Boot integration tests:
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
.addFilter(springSecurityFilterChain).build();
user = userRepository.save(
new User("Joe", "Bloggs", "joe#example.com", "joe", passwordEncoder.encode("secret")));
currency = currencyRepository.save(
new Currency("GBP", "£%01.2f"));
fund = fundRepository.save(
new Fund("Nationwide", (double) 100, currency));
}
#After
public void teardown() {
userRepository.delete(user);
currencyRepository.delete(currency);
fundRepository.delete(fund);
}
However, it doesn't seem that currencies are being deleted after each test and my tests are failing in error:
...
[ERROR] testGetFunds_whenNoToken_thenUnauthorized(biz.martyn.budget.FundsControllerTest) Time elapsed: 3.268 s <<< ERROR!
org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find biz.martyn.budget.currency.Currency with id 437; nested exception is javax.persistence.EntityNotFoundException: Unable to find biz.martyn.budget.currency.Currency with id 437
Caused by: javax.persistence.EntityNotFoundException: Unable to find biz.martyn.budget.currency.Currency with id 437
...
After, if I query the test database, I see that rows haven't been deleted:
mysql> select * from currencies;
+----+---------------------+---------------------+---------------+------+---------------------+
| id | created_at | deleted_at | format | name | updated_at |
+----+---------------------+---------------------+---------------+------+---------------------+
...
| 437 | 2020-01-02 13:51:24 | 2020-01-02 13:51:23 | £%01.2f | GBP | 2020-01-02 13:51:24 |
...
+----+---------------------+---------------------+---------------+------+---------------------+
5 rows in set (0.00 sec)
There should only be one unique entry for name but I guess as delete is not happening it is pulling duplicates for "GBP". My repository for currencies:
Currency.java
#Entity(name = "currencies")
#SQLDelete(sql = "UPDATE currencies SET deleted_at = now() WHERE id = ?")
#Where(clause = "deleted_at is null")
public class Currency {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected Integer id;
#Column(unique = true, nullable = false)
private String name;
#Column(nullable = false)
private String format;
#Column(name = "created_at", updatable = false)
#CreationTimestamp
protected LocalDateTime createdAt;
#Column(name = "updated_at")
#UpdateTimestamp
protected LocalDateTime updatedAt;
#Column(name = "deleted_at")
protected LocalDateTime deletedAt;
protected Currency() {}
public Currency(String name, String format) {
this.name = name;
this.format = format;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public String getFormat() {
return format;
}
public void setFormat(final String format) {
this.format = format;
}
}
User.java
#Entity(name = "users")
public class User implements Serializable {
/**
*
*/
private static final long serialVersionUID = -8507204786382662588L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(nullable = false)
private String firstName;
#Column(nullable = false)
private String surname;
#Column(nullable = false, unique = true)
private String email;
#Column(nullable = false, unique = true)
private String username;
#Column(nullable = false)
#JsonIgnore
private String password;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name="user_id")
#JsonIgnore
private List<Fund> funds;
protected User() {}
public User(String firstName, String surname, String email, String username, String password) {
this.firstName = firstName;
this.surname = surname;
this.email = email;
this.username = username;
this.password = password;
}
public Long getId() {
return 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;
}
// standard getters and setters
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public List<Fund> getFunds() {
return funds;
}
public void addFund(Fund fund) {
funds.add(fund);
fund.setUser(this);
}
public void removeFund(Fund fund) {
funds.remove(fund);
fund.setUser(null);
}
// public Fund getFund(int id) {
// fundRepository.findByIdAndUserId(id)
// .orElseThrow(() -> new EntityNotFoundException("Fund ID not found: "+id));
// }
}
The delete method takes an object of Currency. Your Currency object has an ID which is auto-generated.
When you pass the same object that you passed to save to delete you did not set the ID that's the reason the delete operation never actually deletes the data you wanted to delete.
You can either use the object that is returned from save method or get a hold of the genearted-id and use deleteById method.
Here is an example for delete using the object.
#Before
public void setup() {
user = repository.save(user);
}
#After
public void tearDown() {
repository.delete(user);
}
Alternatively, you can use the same object to get the generated-id and use deleteById method.
If you take a look at SimpleJpaRepository which provides an implementation for JPA, you will find that if your ID is null then they are treated as a new entity and are never deleted.
/*
* (non-Javadoc)
* #see org.springframework.data.repository.CrudRepository#delete(java.lang.Object)
*/
#Override
#Transactional
#SuppressWarnings("unchecked")
public void delete(T entity) {
Assert.notNull(entity, "Entity must not be null!");
if (entityInformation.isNew(entity)) {
return;
}
// Other stuff
}

Spring Data JPA - findByAlias vs. findUserByAlias - error with both

I am using spring boot 2 with JPA and Spring Security.
I have a handler method that, depending upon a dropdown, will find all links either similar to a specific title or posted by a specific user. I know my database is set up properly.
I am getting a null pointer exception at this line:
Optional<User> user = userRepository.findUserByAlias("searchTerm");
I have tried changing the method to findByAlias(...) with the same result.
This is the code for my UserRepository:
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
Optional<User> findUserByAlias(String alias);
Optional<User> findByAlias(String alias);
}
and this is my handler method wherein the error occurs at this line:
Optional<User> user = userRepository.findUserByAlias("searchTerm");
#GetMapping("/search")
public String showSearchResults(#RequestParam("searchTerm") String searchTerm, #RequestParam("searchBy") String searchBy, Model model) {
System.out.println("INSIDE showSearchResults + searchTerm =" + searchTerm);
List<Link> searchResults;
if(searchBy.equals("user")) {
System.out.println("INSIDE IF EQUALS 'user'");
// get the user by alias
Optional<User> user = userRepository.findUserByAlias("searchTerm");
// if the user is present the find all links by the user id
if (user.isPresent()) {
searchResults = linkRepository.findAllByUser_Id(user.get().getId());
} else {
searchResults = null;
}
}
if(searchBy.equals("title")){
searchResults = linkRepository.findAllByTitleLike("%" + searchTerm + "%");
} else {
searchResults = null;
}
model.addAttribute("searchTerm", new SearchTerm());
model.addAttribute("searchResults", searchResults);
return "search-results";
}
and this is my User class:
#Entity
public class User implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long id;
#Column
private String email;
#Column
private String password;
#Column
private boolean enabled;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "users_roles",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id")
)
private Set<Role> roles = new HashSet<>();
#Column
private String firstName;
#Column
private String lastName;
#Column
private String fullName;
#Column
private String alias;
#Transient
private String confirmPassword;
public User(){
}
public User(String email, String password, boolean enabled,
String firstName, String lastName,
String fullName, String alias) {
this.email = email;
this.password = password;
this.enabled = enabled;
this.firstName = firstName;
this.lastName = lastName;
this.fullName = fullName;
this.alias = alias;
}
public void addRole(Role role){
roles.add(role);
}
public void addRoles(Set<Role> roles) {
roles.forEach(this::addRole);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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 boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
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 getFullName() {
return firstName + " " + lastName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public String getConfirmPassword() {
return confirmPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for(Role role : roles){
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
#Override
public String getUsername() {
return null;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
}
So I have two questions:
1.) First and most importantly - why am I getting null pointer exception? I'm at a loss as to how to go about debugging this.
2.) What is the difference between findByAlias and findUserByAlias?
Any advice would be much appreciated.
Thank you for your help,
Marc
How is your userRepository injected into your controller? It seems that is the most likely reason for the null pointer.

Repeated column in mapping entity

I am using in my project JPA embedded entities.
I have this embeddable class
#Embeddable
public class Address {
private String addressLine1;
private String addressLine2;
private String city;
private String state;
private String zipCode;
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 getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
}
And I am embedding this class in here
#Entity
public class Customer implements
DomainObject{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Version
private Integer version;
private String firstName;
private String lastName;
private String email;
private String phoneNumber;
#Embedded
private Address billingAddress;
#Embedded
private Address shippingAddress;
#OneToOne
private User user;
#Override
public Integer getId() {
return id;
}
#Override
public void setId(Integer id) {
this.id = id;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
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 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 User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Address getBillingAddress() {
return billingAddress;
}
public void setBillingAddress(Address billingAddress) {
this.billingAddress = billingAddress;
}
public Address getShippingAddress() {
return shippingAddress;
}
public void setShippingAddress(Address shippingAddress) {
this.shippingAddress = shippingAddress;
}
}
After running the project I am this is error
Repeated column in mapping entity:
theo.tziomakas.udemy.domain.Customer column: billing_address_line1 (should be mapped with insert = "false" update = "false"
You can clone my project if you want and check it yourself.
I have also tried this answer but didn't get anything.
UPDATE
I solved this error by using the AttributeOverride annotation.
#Embedded
#AttributeOverrides({
#AttributeOverride( name = "addressLine1",
column = #Column(name = "billing_address_line1")
),
#AttributeOverride( name = "addressLine2",
column = #Column(name = "billing_address_line2")
),
#AttributeOverride( name = "city",
column = #Column(name = "billing_city")
),
#AttributeOverride( name = "state",
column = #Column(name = "billing_state")
),
#AttributeOverride( name = "zipCode",
column = #Column(name = "billing_zip_code")
)
})
private Address billingAddress;
#Embedded
#AttributeOverrides({
#AttributeOverride( name = "addressLine1",
column = #Column(name = "shipping_address_line1")
),
#AttributeOverride( name = "addressLine2",
column = #Column(name = "shipping_address_line2")
),
#AttributeOverride( name = "city",
column = #Column(name = "shipping_city")
),
#AttributeOverride( name = "state",
column = #Column(name = "shipping_state")
),
#AttributeOverride( name = "zipCode",
column = #Column(name = "shipping_zip_code")
)
})
private Address shippingAddress;
So I cloned your repository and found that the problem arises because you have Embedded Address 2 times - shippingAddress and billingAddress in Customer.java
. I would recommend the using #AttributeOverride annotation to solve this issue. See this thread.
Change the Entity Fields like this.
#Embedded
#AttributeOverrides({
#AttributeOverride(name="addressLine1",column=#Column(name="billingAddressLine1")),
#AttributeOverride(name="addressLine2",column=#Column(name="billingAddressLine2")),
#AttributeOverride(name="city",column=#Column(name="billingCity")),
#AttributeOverride(name="state",column=#Column(name="billingState")),
#AttributeOverride(name="zipCode",column=#Column(name="billingZipCode")),
})
private Address billingAddress;
#Embedded
#AttributeOverrides({
#AttributeOverride(name="addressLine1",column=#Column(name="shippingAddressLine1")),
#AttributeOverride(name="addressLine2",column=#Column(name="shippingAddressLine2")),
#AttributeOverride(name="city",column=#Column(name="shippingCity")),
#AttributeOverride(name="state",column=#Column(name="shippingState")),
#AttributeOverride(name="zipCode",column=#Column(name="shippingZipCode")),
})
private Address shippingAddress;

Lazy fetch elements

I'm fetching Company along with productSLA using join fetch query, since the Company has userlist and it doesn't get initialized. Therefore at the time when i send response using responseentity.ok it throws lazy init exception. I don't want user list for that purpose is there any way i can send it to front end without getting lazy init exception some one suggested me to do this using dto.
I am using angular on front end. When i was using jsp i never faced this kind of problem.
#Entity
#Table(name = "USER_TABLE")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer userId;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL,fetch=FetchType.EAGER)
private List<Ticket> raisedTickets;
#NotNull
#Column(unique = true)
#Email(message = "Invalid Email")
private String email;
#NotNull
#Column
#Length(min = 4, max = 12, message = "First name must be between 4 to 12 character long")
private String firstName;
#NotNull
#Column
#Length(min = 4, max = 12, message = "Last name must be between 4 to 12 character long")
private String lastName;
#NotNull
#Column
#Length(min = 8, max = 100, message = "Password must be 4 to 12 character long")
private String password;
#NotNull
#Column
#Length(min = 3, max = 30, message = "Company Name must be between 3 to 12 character long")
private String companyName;
#Column(name = "USER_ROLE")
#Enumerated(EnumType.STRING)
private UserRolesEnum userRole;
#ManyToOne
#JoinColumn(name = "COMPANY_ID", nullable = false)
#NotNull
private Company company;
#OneToMany(mappedBy="user", cascade=CascadeType.ALL)
private List<ProductAssociated> productAssociatedList=new ArrayList<ProductAssociated>();
public User() {
}
public User(String firstName, String lastName, String email, String password, String companyName,
UserRolesEnum role) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.password = password;
this.companyName = companyName;
this.userRole = role;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer id) {
this.userId = id;
}
public List<Ticket> getRaisedTickets() {
return raisedTickets;
}
public void setRaisedTickets(List<Ticket> raisedTickets) {
this.raisedTickets = raisedTickets;
}
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 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 getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public UserRolesEnum getUserRole() {
return userRole;
}
public void setUserRole(UserRolesEnum userRole) {
this.userRole = userRole;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public List<ProductAssociated> getProductAssociatedList() {
return productAssociatedList;
}
public void setProductAssociatedList(List<ProductAssociated> productAssociatedList) {
this.productAssociatedList = productAssociatedList;
}
public void addProductAssociated(ProductAssociated productAssociated) {
productAssociatedList.add(productAssociated);
productAssociated.setUser(this);
}
#Entity
#Table(name="PRODUCT_SLA")
public class ProductSLA {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="SLA_ID")
private Integer slaId;
#NotNull
#Column(name="RESPONSE_TIME")
private int responseTime;
#Column(name="RESOLVE_TIME")
private int resolveTime;
#NotNull
#Column(name="PRIORITY")
#Enumerated(EnumType.STRING)
private PriorityEnum priority;
#ManyToOne
#JoinColumn(name="COMPANY_ID", nullable = false)
private Company company;
#ManyToOne
#JoinColumn(name="PRODUCT_ID", nullable = false)
private Product product;
public ProductSLA() {
super();
}
public ProductSLA(Integer slaId, int responseTime, int resolveTime, PriorityEnum priority) {
super();
this.slaId = slaId;
this.responseTime = responseTime;
this.resolveTime = resolveTime;
this.priority = priority;
}
public Integer getSlaId() {
return slaId;
}
public void setSlaId(Integer slaId) {
this.slaId = slaId;
}
public int getResponseTime() {
return responseTime;
}
public void setResponseTime(int responseTime) {
this.responseTime = responseTime;
}
public int getResolveTime() {
return resolveTime;
}
public void setResolveTime(int resolveTime) {
this.resolveTime = resolveTime;
}
public PriorityEnum getPriority() {
return priority;
}
public void setPriority(PriorityEnum priority) {
this.priority = priority;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
#Entity
#Table(name = "COMPANY_TABLE")
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "COMPANY_ID")
private Integer companyId;
#NotNull
#Column(name = "COMPANY_NAME", unique = true)
private String companyName;
#NotNull
#Column(name = "ADDRESS_LINE1")
private String addressLine1;
#Column(name = "ADDRESS_LINE2")
private String addressLine2;
#NotNull
#Column(name = "CITY")
private String city;
#NotNull
#Column(name="STATE_NAME")
private String state;
#NotNull
#Column(name = "COUNTRY")
private String country;
#NotNull
#Column(name = "PHONE")
private String phone;
#NotNull
#Column(name = "POSTAL_CODE")
private String postalCode;
#NotNull
#Column(name = "COMPANY_WEBSITE")
private String companyWebsite;
#OneToMany( mappedBy = "company", cascade = CascadeType.ALL)
private List<User> userList = new ArrayList<User>();
#OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
private List<ProductSLA> productSLAList = new ArrayList<ProductSLA>();
#OneToMany(mappedBy = "company", cascade = CascadeType.ALL, orphanRemoval=true)
private List<AccessLevel> accessLevelList=new ArrayList<AccessLevel>();
public Company() {
super();
}
public Company(Integer companyId, String companyName, String addressLine1, String addressLine2, String city,
String state, String country, String phone, String postalCode, String companyWebsite) {
super();
this.companyId = companyId;
this.companyName = companyName;
this.addressLine1 = addressLine1;
this.addressLine2 = addressLine2;
this.city = city;
this.state = state;
this.country = country;
this.phone = phone;
this.postalCode = postalCode;
this.companyWebsite = companyWebsite;
}
public Integer getCompanyId() {
return companyId;
}
public void setCompanyId(Integer companyId) {
this.companyId = companyId;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
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 getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getPostalCode() {
return postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public String getCompanyWebsite() {
return companyWebsite;
}
public void setCompanyWebsite(String companyWebsite) {
this.companyWebsite = companyWebsite;
}
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
public void addUser(User user) {
userList.add(user);
user.setCompany(this);
}
public List<ProductSLA> getProductSLAList() {
return productSLAList;
}
public void setProductSLAList(List<ProductSLA> productSLAList) {
this.productSLAList = productSLAList;
}
public void addProductSLA(ProductSLA productSLA) {
productSLAList.add(productSLA);
productSLA.setCompany(this);
}
public List<AccessLevel> getAccessLevelList() {
return accessLevelList;
}
public void setAccessLevelList(List<AccessLevel> accessLevelList) {
this.accessLevelList = accessLevelList;
}
public void addAccessLevel(AccessLevel accessLevel) {
accessLevelList.add(accessLevel);
accessLevel.setCompany(this);
}
}
edit
i found solutions but i am confused which one to use and how to use, because there are many solutions there.
Avoid Jackson serialization on non fetched lazy objects
Assuming your hibernate session is closed already in the controller (which is a fair assumption to make since we don't want to expose our hibernate sessions outside spring/hibernate layer) you will run into this type of problem if you try to access a collection which was not loaded when it was inside the session.
Alright!
I'm also assuming that you are returning a one or collection of "hibernate managed entities" as opposed to DTOs. What I'm sensing is that when that entity is converted into JSON all the getters are called by underlying framework unless they are marked 'ignore' (or something like that). OR may be your UI is calling userList at which point it's throwing exception since a proxy was returned by hibernate.
Regardless, it's better to return a DTO and populate it however way you like. There are various reasons why returning a DTO (or a collection of DTOs) is preferred over returning an entity.
To resolve this problem, i've used http message converter,
Application Configuration looks like this:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(jacksonMessageConverter());
super.configureMessageConverters(converters);
}
and
public MappingJackson2HttpMessageConverter jacksonMessageConverter(){
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate5Module());
messageConverter.setObjectMapper(mapper);
return messageConverter;
}
Dependency Required
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>2.8.7</version>
</dependency>
There is a dirty solution also for this,if you are willing to modify every getters:
jackson 2 object to json ignore lazy loading

Resources