Spring JPA - Ignore a field only in persistence - spring

I have a field called password which can be received by endpoint. But it cannot be sent back in response or persisted in Database
The class is as follows -
public class ShortURL {
#Pattern(regexp="^(https?|ftp|file)://[-a-zA-Z0-9+&##/%?=~_|!:,.;]*[-a-zA-Z0-9+&##/%=~_|]")
private String url;
#Size(min=8,max=16)
#Transient
private String password = null;
private boolean isPasswordProtected = false;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isPasswordProtected() {
return isPasswordProtected;
}
public void setPasswordProtected(boolean isPasswordProtected) {
this.isPasswordProtected = isPasswordProtected;
}
public ShortURL(
#Pattern(regexp = "^(https?|ftp|file)://[-a-zA-Z0-9+&##/%?=~_|!:,.;]*[-a-zA-Z0-9+&##/%=~_|]") String url,
#Size(min = 8, max = 16) String password, boolean isPasswordProtected) {
super();
this.url = url;
this.password = password;
this.isPasswordProtected = isPasswordProtected;
}
#Transient works properly. But adding the #JsonIgnore after #Transient causes problems -
Type definition error: [simple type, class java.lang.String];
nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
No fallback setter/field defined for creator property 'password'"
How do I achieve my intentions?

Depends on your Jackson version.
Before version 1.9, you could add #JsonIgnore to the getter of password and add #JsonProperty to the setter of the password field.
Recent versions of Jackson provide READ_ONLY and WRITE_ONLY annotation arguments for #JsonProperty, something like this:
#JsonProperty(access = Access.READ_ONLY)
private String password;

Yes you can use #JsonIgnore to let jackson ignore it during sending the user response but. There are certain best practices you should follow.
Never expose the Entities directly to the endpoint instead its better to have a wrapper i.e DTO that translates your entity to the required response.
For eg. in your case
public class ShortURL {
#Pattern(regexp="^(https?|ftp|file)://[-a-zA-Z0-9+&##/%?=~_|!:,.;]*[-a-zA-Z0-9+&##/%=~_|]")
private String url;
#Size(min=8,max=16)
private String password;
private boolean isPasswordProtected;
}
//here is the dto in which you can create a parameterised constructor and
accordingly invoke it based on the fields you want to set.
public class ShortURLDTO {
private String url;
public ShortURLDTO(String url){
this.url=url
}
}

Related

How to validate a field based on other field value in bean (pojo) class in Spring Boot using annotations

I have created a request class having some fields with getters & setters. Now I want to validate each & every field. So with this validation I need to check if the value for field1 is A then fields2 should be mandatory and if value for field1 is B then field3 should be mandatory and field2 will be optional. Consider the below pojo class.
public class CreateADTSpaceRequestDTO implements Serializable{
private static final long serialVersionUID = 5654993652896223164L;
#NotEmpty(message = "taskUId cannot be null/empty")
#JsonProperty(value = "taskUId")
private String taskUId;
#NotEmpty(message = "clientName cannot be null/empty")
#JsonProperty(value = "clientName")
private String clientName;
#NotEmpty(message = "SpaceType cannot be null/empty")
#JsonProperty(value = "spaceType")
private String spaceType;
public String getTaskUId() {
return taskUId;
}
public void setTaskUId(String taskUId) {
this.taskUId = taskUId;
}
public String getClientName() {
return clientName;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public String getSpaceType() {
return spaceType;
}
public void setSpaceType(String spaceType) {
this.spaceType = spaceType;
}
}
In the above class we have a field called clientName, so based on client name value I want to validate spaceType field.
For ex. if clientName = A then spaceType is mandatory and if clientName = B then spaceType is optional.
Please help me with your comments how we can have this kind of validation using annotations or using regex or any other way.

Java-Hashed password value is null

Hello I have spent several hours trying to figure out why my hashed password is null. I am trying to use Spring BCryptPasswordEncoder. I have read several documentations, looked at many different examples, and watched videos on how to use this encryptor but I just can't get it to work. Looking at my MYSQL database I know the password field is populated, but the field for hashPassword remains null. Before anyone flips out I know that the plain password should not be stored in the database, but at the moment this is how I know that the password parameter is not null. As far as I know this is not a duplicate question and if so please direct me to right place, because everything I have seen for the hashing category has not been helpful. Below is a snippet of the code I am using if you need more detail please let me know.
#Entity
public class User {
#Id
#GeneratedValue
private int uid;
#NotNull
#Column(unique = true )
private String username;
#NotNull
private String password;
#Column
private String hashedPassword;
private static final BCryptPasswordEncoder encodedPassword = new BCryptPasswordEncoder();
public User(String username, String password) {
this.username = username;
this.password = password;
this.hashedPassword = hashIt(this.password);
}
public User() { }
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getHashedPassword() {
return hashedPassword;
}
public void setHashedPassword(String hashedPassword) {
this.hashedPassword = hashedPassword;
}
public String hashIt(String password) {
return encodedPassword.encode(password);
}
public boolean isCorrectPassword(String password){
return encodedPassword.matches(password,hashedPassword);
}
}

#JsonProperty/#JsonIgnore not working as expected on write-only property

As suggested here I've tried to use #JsonIgnore and #JsonProperty (from import com.fasterxml.jackson.annotation.*;) on the password and passwordSalt fields of my AppUser.
#Entity
#Table(name = "app_user")
public class AppUser extends AbstractTimestampEntity {
// ..
#JsonIgnore
public String getPassword() {
return password;
}
#JsonProperty
public void setPassword(String password) {
this.password = password;
}
#JsonIgnore
public String getPasswordSalt() {
return passwordSalt;
}
#JsonProperty
public void setPasswordSalt(String passwordSalt) {
this.passwordSalt = passwordSalt;
}
}
However, for some reason the two fields are always null. As I try to "register" a user, the JSON I am sending is not getting de-serialized completely. password is set to null:
What I want to achieve is to be able to receive the user's password in order to store it, but at the same time make sure that the stored (encrypted) password is not being serialized and sent back to the client as well.
What am I doing wrong here?
With Jackson version 2.9.0 the following works for me:
#NoArgsConstructor
#AllArgsConstructor
public class Credentials {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#JsonProperty(access = Access.WRITE_ONLY)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
…and a running test example…
#Test
public void jsonIgnoreSerialization() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
String serialized = objectMapper.writeValueAsString(new Credentials("user", "pass"));
assertThat(serialized).isEqualTo("{\"username\":\"user\"}");
Credentials credentials = objectMapper.readValue("{\"username\":\"user\",\"password\":\"pass\"}", Credentials.class);
assertThat(credentials.getUsername()).isEqualTo("user");
assertThat(credentials.getPassword()).isEqualTo("pass");
}
For the sake of simplicity the constructors are the ones from Lombok and the assertions come from AssertJ.
Can you give this a try?

why I can't use string as id

I am trying to create a user model with a CrudRepository:
#Entity
public class User {
#Id
#GeneratedValue
private String username;
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
public interface UserRepository extends CrudRepository<User, String> {
}
However I got an 500 error every time I call findOne():
#Controller
public class UserController {
#Autowired
private UserRepository users;
#Override
#RequestMapping(value="/register", method=RequestMethod.POST)
public #ResponseBody User register(#RequestBody User userToRegister) {
String username = userToRegister.getUsername();
User user = users.findOne(id);
if (user != null) {
return null;
}
User registeredUser = users.save(userToRegister);
return registeredUser;
}
}
However if I just switch to an long type id instead of username itself then everything works. I think it's common to use string as id. So how to make this work?
I use the embedded hsql database. I didn't wrote any sql code.
The problem is that String username; is annotated with both #Id and #GeneratedValue. #Id means that is should be a primary key, fine it can be a String. But #GeneratedValue means that you want the system to automatically generate a new key when you create a new record. That's easy when the primary key is integer, all databases have a notion of sequence (even if the syntax is not always the same). But if you want String automatically generated keys, you will have do define your own custom generator.
Or if you have no reason for the #GeneratedValue annotation, simply remove it as suggested by Bohuslav Burghardt
Use column annotation like below by putting nullable False.
#Id
#GeneratedValue
#Column(name = "username", nullable = false)
private String username;

Returning returned model object to json String using spring data jpa with hibernate

I am using spring data jpa with hibernate
This is my dao interface
#Repository
public interface IUserDAO extends JpaRepository<User, Integer>{
User findByUsername( final String username );
}
This is my User class
Entity
#Table(name="USER")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="ID", nullable = false)
private int id;
#Column(name="USERNAME", nullable = false)
private String username;
#Column(name="NAME", nullable = false)
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
This is my UserImplClass
This is my UserImplClass{
#Autowired
private IUserDAO iUserDAO;
public String findUserByUserName(String username) {
User user =iUserDAO.findByUsername(username);
Convert user to json object from framework level automatically
// i can add my one implemenation of converting user to json here ,but i want to achieve it from framework so that my code is not scattered on every service level
return "jsonStringOfUserObject"
}
Is it possible with spring data jpa with hibernate so that i do not have to write code for converting java object to json string in every service level?
I am using spring ,therefore i want to achieve it from spring .
You have two options to do what you want:
1) If you plan on returning this Object as an HTTP Response, and you use Spring MVC with Controllers you can annotate your controller method as follows:
public #ResponseBody User getUser(){
return userImplClass.findUserByUserName("yourusername");
}
2) If you want the UserImplClass itself to return a JSON String (which I do't recommend, but I leave you the decision), you can use Jackson Object Mapper to do it for you (you can inject it if you declare it as a bean on your configuration xml, or create a new instance of it, I personally prefer injecting it with #Autowired)
public String findUserByUserName(String username) {
User user =iUserDAO.findByUsername(username);
ObjectMapper mapper = new ObjectMapper(); // no need to do this if you inject via #Autowired
return mapper.writeValueAsString(user);
}

Resources