Spring boot API - How to ensure no concurrency issues - java

I'm still in the process of learning Java / spring and I think I'm getting better. Now at this point I'm able to build a rest api BUT I'm at a lost at how to ensure I've no concurrency issues . I've read many topics regarding making the API stateless or making my POJO's immutable but I'm sure if in my case below I need to. And if I did, I'm actually unsure how my code can function by making everything final in my POJO.
If someone could help me learn here I'd be VERY grateful. Thank you for your time
Below i have a POJO called User:
#Getter
#Setter
#Document(collection = "UserProfiles")
public class User {
#Id
#JsonIgnore
private String _id;
#JsonView({ProfileViews.Intro.class, ProfileViews.Full.class})
private String userId;
#JsonView({ProfileViews.Intro.class, ProfileViews.Full.class})
private String name;
#JsonView({ProfileViews.Intro.class, ProfileViews.Full.class})
private String displayName;
#DBRef
#JsonView({ProfileViews.Full.class})
private UserInterests personalInterests;
#DBRef
#JsonIgnore
private ProfileFollows profileFollowDetails;
}
#Getter
#Setter
#Document(collection = "ProfileFollows")
public class ProfileFollows {
#Id
//Id of The Mongo Document
private String id;
//The Id of the User Profile who owns the document
private String userId;
//A list containing the Ids of the Users who have followed the Profile belonging to userId
private List<String> profileFollowedByUserIds;
//A list containing the Ids of the Profiles the current user has followed
private List<String> profileFollowingByUserList;
}
And here is my Service layer where I create and update the user
#Service
public class UserService {
#Autowired
UserDal userDal;
public User createNewUserAccount(String userId, String userName) {
//check If userId already in DB
if (checkIfUserIdExits(userId)) {
throw new UserAlreadyExistsException("Cannot create User with Id { " + userId + " }, a user with this Id already " +
"exists");
}
//Create a Empty / Base New User Object
User newUser = new User();
UserInterests userInterests = new UserInterests();
userInterests.setUserId(userId);
userInterests.setPersonalInterestsExtras(null);
userInterests.setCreatedDate(Instant.now());
userInterests.setLastUpdatedAt(Instant.now());
userInterestsDAL.save(userInterests);
newUser.setPersonalInterests(userInterests);
ProfileFollows userProfileFollows = new ProfileFollows();
userProfileFollows.setUserId(userId);
userProfileFollows.setProfileFollowedByUserIds(new ArrayList<>());
userProfileFollows.setProfileFollowingByUserList(new ArrayList<>());
newUser.setProfileFollowDetails(profileFollowsDAL.save(userProfileFollows));
newUser.setUserId(userId);
newUser.setDisplayName(generateUserDisplayName(userName));
newUser.setCreatedDate(Instant.now());
newUser.setLastUpdatedAt(Instant.now());
//save the new User Profile to the DB
return userDal.save(newUser);
}
Here is my UserDAL:
public interface UserDal {
/**
* Method to check if a user exists with a given user Id
* #param Id -- Id of user to look up where id is a string
* #return
*/
Boolean existsById(String Id);
/**
* Method to save a user to the DB
* #param user -- User object to save to the DB
* #return
*/
User save(User user);
}
My User Repository / DALImpl:
#Repository
public class UserDALImpl implements UserDal {
private final MongoTemplate mongoTemplate;
#Autowired
public UserDALImpl(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
#Override
public User save(User user) {
return mongoTemplate.save(user);
}
And lastly my controller:
#RestController
#RequestMapping("/profile")
public class CreateProfileController {
#Autowired
public CreateProfileController() {
}
#Autowired
UserService userService;
#ApiOperation(value = "Allows for the creation of a user Profile")
#PostMapping("/create")
public User createUserProfile(#RequestParam(name = "userId") String userId,
#RequestParam(name = "displayName", required = true, defaultValue = "AnonymousDev") String displayName) {
if (userId.equals("")) throw new BadRequestException("UserId cannot be blank");
if (userService.checkIfUserIdExits(userId)) {
throw new UserAlreadyExistsException("Unable to create user with Id { " + userId + " }, the " +
"userId already exists");
}
return userService.createNewUserAccount(userId, displayName);
}
}

Related

Spring JPA: How to get the custom json response

I need to list all the permission available in the system, and on each permission i need to show to particular user which permission is active on him ...
here is table relations:
All Record from Table: authority_master(select * from authority_master)
All Record from Table: users_authority_relation (select * from users_authority_relation;)
All Record from Table: userdetails (select*from userdetails)
Expected JSON Output : if i want to know which and all permission active on each user(basically whose record exists in table "user_authority_relation" table ,(here i want to list all the permissions available in table "authority_master" and on that "isActive" json key is True only if that particular authority exists in table "user_authority_relation"
Basically i need to select userdetails table and join with users_authority_relation , this will give result only who has permission But it will not list all the avaialble permission. i am confused on this how to get like below expected json result
If you are allowed to make multiple database call, you can do this
Get list of Authority authList from database.
Get list of users
For each user do the step 4-5
loop authList and check if the current user's authority list contains the element. if yes set isActive true otherwise false.
Set the authList as currents users permission
As i am also in spring boot learning phase and i found your problem interesting so i attempted to solve your problem
Your entity beans might look like this
user_authority_relation bean
#Table(name="user_authority_relation")
#Data
public class UserAuthRelation implements Serializable {
#Id
#GeneratedValue
Long user_auth_id;
#ManyToOne
#JoinColumn(name="userid")
UserDetails user;
#JoinColumn(name="authorityid")
#OneToOne
AuthorityMaster authority;
}
UserDetailsBean.java
#Entity
#Table(name="userdetails")
#Data
public class UserDetails implements Serializable {
#Id
#GeneratedValue
Long userid;
String name;
#OneToMany(mappedBy="user")
List<UserAuthRelation> userAuths;
}
AuthorityMaster.java
#Entity
#Table(name="authority_master")
#Data
public class AuthorityMaster implements Serializable {
#Id
#GeneratedValue
Long authorityid;
String authority;
String description;
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AuthorityMaster other = (AuthorityMaster) obj;
if (authorityid == null) {
if (other.authorityid != null)
return false;
} else if (!authorityid.equals(other.authorityid))
return false;
return true;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((authorityid == null) ? 0 : authorityid.hashCode());
return result;
}
}
To get desired response, we need to make some classes which can help to create custom response bean which may look like this
#Data
public class CustomResponseJson implements Serializable {
List<UserResponseJson> usersList;
}
#Data
public class UserResponseJson {
Long userId;
List<PermissionObject> permissions;
}
#Data
public class PermissionObject {
String authority;
Long authorityid;
Boolean isactive;
}
Rest Controller
#RestController
public class UsersController {
#Autowired
UserDetailRepository userRepo;
#Autowired
AuthorityRepository authRepo;
#GetMapping("/fetchUserPermissons")
#ResponseBody
public CustomResponseJson fetchUserPermission() {
//find all available users
List<UserDetails> users = userRepo.findAll();
//find all available authorities
List<AuthorityMaster> auths = authRepo.findAll();
//initilizing custom reponse bean
CustomResponseJson res =new CustomResponseJson();
if(users!=null && !users.isEmpty()){
//list of all users in json response
List<UserResponseJson> userJsonReponse = new ArrayList<UserResponseJson>();
for(UserDetails user : users){
UserResponseJson userjson = new UserResponseJson();
userjson.setUserId(user.getUserid());
//prepare list of all authority availed and not availed to user
List<PermissionObject> permissions = new ArrayList<PermissionObject>();
if(user.getUserAuths()!=null && user.getUserAuths().size()>0){
List<AuthorityMaster> tempList = new ArrayList<AuthorityMaster>();
for(UserAuthRelation rel : user.getUserAuths()){
tempList.add(rel.getAuthority());
PermissionObject permObj = new PermissionObject();
permObj.setAuthority(rel.getAuthority().getAuthority());
permObj.setAuthorityid(rel.getAuthority().getAuthorityid());
permObj.setIsactive(true);
permissions.add(permObj);
}
//to find authority which is not assigned to user
List<AuthorityMaster> remainedAuths = auths.stream()
.filter(e -> !tempList.contains(e))
.collect (Collectors.toList());
for(AuthorityMaster auth:remainedAuths){
PermissionObject permObj = new PermissionObject();
permObj.setAuthority(auth.getAuthority());
permObj.setAuthorityid(auth.getAuthorityid());
permObj.setIsactive(false);
permissions.add(permObj);
}
}
userjson.setPermissions(permissions);
userJsonReponse.add(userjson);
}
res.setUsersList(userJsonReponse);
}
return res;
}
}
OUTPUT
You should create one response DTO which can hold all data from different resources(databases, external services)
My Controller response is:
#Data
#AllArgsConstructor
#NoArgsConstructor
public class AxeleStatisticReportDTO implements Serializable {
private static final long serialVersionUID = 1L;
private List<EmailLogDTO> emailAndCount = new ArrayList<>();
private List<IpLogDTO> ipRequestsCount = new ArrayList<>();
}

Spring Boot DBRef is null after saving

I have user and role models. Thats look like this:
User:
#Document("user")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class User {
#Id
private String id;
private String name;
private String username;
private String password;
#DBRef
private Collection<Role> roles = new ArrayList<>();
Role:
#Document("role")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Role {
#Id
private String id;
private String name;
}
And I have besides others those three methods to save user, save role and add role to user, like this:
#Override
public User saveUser(User user) {
log.info("Saving new user {} to the database", user.getName());
return userRepository.save(user);
}
#Override
public Role saveRole(Role role) {
log.info("Saving new role {} to the database", role.getName());
return roleRepository.save(role);
}
#Override
public void addRoleToUser(String username, String roleName) {
log.info("Adding role {} to user {}", roleName, username);
User user = userRepository.findByUsername(username);
Role role = roleRepository.findByName(roleName);
user.getRoles().add(role);
}
Also I have a CommandLineRunner to insert those data
#Bean
CommandLineRunner run(UserService userService) {
return args -> {
userService.saveRole(new Role(null, "ROLE_USER"));
userService.saveRole(new Role(null, "ROLE_ADMIN"));
userService.saveUser(new User(null,"Stefan", "stefanadmin", "stefanadmin", new ArrayList<>()));
userService.saveUser(new User(null,"Marko", "markoadmin", "markoadmin", new ArrayList<>()));
userService.saveUser(new User(null,"Jovan", "jovanobicanuser", "jovanobicanuser", new ArrayList<>()));
userService.addRoleToUser("stefanadmin", "ROLE_ADMIN");
userService.addRoleToUser("stefanadmin", "ROLE_USER");
userService.addRoleToUser("markoadmin", "ROLE_ADMIN");
userService.addRoleToUser("jovanobicanuser", "ROLE_USER");
};
}
And everything works fine except array column roles, its empty.
Its like this in JSON when I try to return all users:
{"id":"6331bda42c1fa17e41079c99","name":"Jovan","username":"jovanobicanuser","password":"jovanobicanuser","roles":[]}]
I'm following some tutorial for this stuff, guy is using MySql while I'm using Mongo, so I think problem is somewhere how I'm trying to connect those two tables.
What can cause problem like this, what I'm missing?

java.util.NoSuchElementException: No value present exception thrown when tried to check whether the emailID is registered or not in POSTGRES database

I am new to Spring boot, I have tried to check with unregistered emailID whether the given email id is available or not in the Postgres database. But unfortunately got this NoSuchElementException. I have attached an exception screenshot here for your reference. Can you guys help me to come out of this problem?.
TipTopController.java
#RestController
#RequestMapping(path = "/user")
public class TipTopController {
#Autowired
private UserService userService;
#PostMapping(path = "/registration")
public ResponseEntity<UserRegistrationResponse> registration(#RequestBody UserRegistrationModel userRegistrationModel){
UserRegistrationResponse userRegistrationResponse = userService.doUserRegistration(userRegistrationModel);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("description","User created successfully");
return ResponseEntity.status(HttpStatus.OK)
.headers(httpHeaders)
.body(userRegistrationResponse);
}
}
UserService.java
#Service
public class UserService {
private static final Logger logger = LogManager.getLogger(UserService.class);
#Autowired
private UserRepository userRepository;
#Transactional
public UserRegistrationResponse doUserRegistration(UserRegistrationModel userRegistrationModel){
UserRegistrationResponse userRegistrationResponse = new UserRegistrationResponse();
try{
logger.info("Entering into try block");
Optional<UserInformationRepo> userInformationRepo=userRepository.findById(userRegistrationModel.getEmail());
logger.info("Get value is "+userInformationRepo.get().getEmail());
logger.info("Username is "+userInformationRepo.get().getFirstName()+" "+userInformationRepo.get().getLastName());
if(userInformationRepo.isPresent()){
userRegistrationResponse.setStatus(false);
userRegistrationResponse.setMessage("This email id { "+userRegistrationModel.getEmail()+" } is taken by another user already");
}
else{
UserInformationRepo userInformationRepoSave = new UserInformationRepo();
userInformationRepoSave.setEmail(userRegistrationModel.getEmail());
userInformationRepoSave.setFirstName(userRegistrationModel.getFirstName());
userInformationRepoSave.setEmail(userRegistrationModel.getLastName());
userInformationRepoSave.setEmail(userRegistrationModel.getPassword());
userRepository.save(userInformationRepoSave);
userRegistrationResponse.setStatus(true);
userRegistrationResponse.setMessage("New user account created with your " + userRegistrationModel.getEmail() + " emailId");
}
}
catch (Exception e){
logger.error(e.getMessage()+"*****"+e);
System.out.println("Exception occurred");
}
return userRegistrationResponse;
}
}
UserInformationRepo.java
#Entity
#Table(name = "USER_INFORMATION")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class UserInformationRepo {
#Id
#Column(name = "EMAIL")
private String email;
#Column(name="FIRSTNAME")
private String firstName;
#Column(name="LASTNAME")
private String lastName;
#Column(name="PASSWORD")
private String password;
}
UserRepository.java
#Repository
public interface UserRepository extends JpaRepository<UserInformationRepo, String> {
}
This is the exception I got while tried to hit the registration API from POSTMAN tool
Are you sure that com.ajithan.tiptop.tiptopapplication.domain.UserInformationRepo is a correct fully qualified class name?
Are you sure that the entity UserInformationRepo has an appropriate constructor for this:
UserInformationRepo(UI.EMAIL,UI.FIRSTNAME,UI.LASTNAME,UI.PASSWORD)
You should not use a native query for this. You can use JPA query in this way:
#Transactional
#Query(value = "select new com.ajithan.tiptop.tiptopapplication.domain.UserInformationRepo(UI.email,UI.firstName,UI.lastName,UI.password) from UserInformationRepo UI where UI.email = :email")
UserInformationRepo checkUserExists(#Param("email") String email);
Why you can't just use the standard method?
Optional<UserInformationRepo> findById(String id);
You can avoid to use your custom methods checkUserExists and saveNewUser and start to use the standard JpaRepository methods findById and save in this way:
#Transactional
public UserRegistrationResponse doUserRegistration(UserRegistrationModel userRegistrationModel)
{
UserRegistrationResponse userRegistrationResponse = new UserRegistrationResponse(); // this was missed !!!
try{
logger.info("Entering into try block");
Optional<UserInformationRepo> userInformationRepo = userRepository.findById(userRegistrationModel.getEmail());
System.out.println(userInformationRepo.getEmail());
if(userInformationRepo.isPresent()){
userRegistrationResponse.setStatus(false);
userRegistrationResponse.setMessage("This email id { "+userRegistrationModel.getEmail()+" } is taken by another user already");
}
else{
UserInformationRepo userInfo = new UserInformationRepo();
userInfo.setEmail(userRegistrationModel.getEmail());
userInfo.setFirstName(userRegistrationModel.getFirstName())
userInfo.setLastName(userRegistrationModel.getLastName());
userInfo.setPassword(userRegistrationModel.getPassword());
userRepository.save(userInfo);
userRegistrationResponse.setStatus(true);
userRegistrationResponse.setMessage("New user account created with your " + userRegistrationModel.getEmail() + " emailId");
}
}
catch (Exception e){
logger.error(e.getMessage()+"*****"+e);
System.out.println("Exception occurred");
}
return userRegistrationResponse;
}

Repository doesn't save data to H2 in-memory db

Here is my controller method:
// CREATE A USER
#PostMapping("/register")
public String createUser(
#RequestBody User user
) {
if (userService.userExists(user)) {
return "User already exists";
}
userService.saveUser(user);
return "Good job!";
}
UserServiceBean
#Service
public class UserServiceBean {
private UserRepository userRepository;
#Autowired
public UserServiceBean(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User saveUser(User user) {
return userRepository.save(user);
}
public boolean userExists(User user) {
if (userRepository.findByUsername(user.getUsername()) == null) {
return false;
}
return true;
}
And my interface repository:
UserRepository
public interface UserRepository extends CrudRepository<User, Long> {
// TODO: 29.01.17 Create a query to find all todos for logged in user
#Query("select td from User u inner join u.toDoItems td where u = :user")
public Iterable<ToDoItem> findAllToDosForLoggedInUser(#Param("user") User user);
public User findByUsername(String username);
}
And here is my User Entity (getters and setters ommited)
#Entity
#Table (name = "USERS")
public class User extends BaseEntity {
#Column(name = "USERNAME")
private String username;
// TODO: 28.01.17 Find a way to store hashed and salted pws in DB
#Column(name = "PASSWORD")
private String password;
#Column(name = "EMAIL")
private String email;
// user can have many ToDoItems
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private Set<ToDoItem> toDoItems;
// JPA demands empty constructor
public User() {}
public User(String username, String password, String email) {
this.username = username;
this.password = password;
this.email = email;
}
When I shoot JSON at my localhost:8080/register:
{
"username":"ss",
"password":"mkyong.com",
"email":"asdasd#wp.pl"
}
I get response Good job! so it works fine. But when I check my DB at localhost:8080/console it just has Test Table and new User is not added.
I've got my hibernate ddl setup in application.properties set:
# Console to H2 database to check data
spring.h2.console.enabled=true
spring.h2.console.path=/console
spring.jpa.hibernate.ddl-auto=create-drop
So, how do I update my code that it creates table USERS and save created user into that db? I'm going to change my db later on, just using H2 to check if my controllers work fine but it shouldn't matter here.
EDIT:
Here is my RepositoryConfiguration.java:
#Configuration
#EnableAutoConfiguration
#EntityScan(basePackages = {"com.doublemc.domain"})
#EnableJpaRepositories(basePackages = {"com.doublemc.repositories"})
#EnableTransactionManagement
public class RepositoryConfiguration {
}
EDIT2:
When I want to register the same User again (using same JSON) then it gives me "User already exists" resposne so it is already in the db... Why can't I see it then? Maybe I've got H2 somewhere else? Not in the basic /console or different port? How can I check this?
I think you're missing the transactional part of your service. Did you define a transaction manager in your spring context ?
If so, you need to add the annotation #Transactional into your service. For example :
#Service
public class UserServiceBean {
#Transactional
public User saveUser(User user) {
return userRepository.save(user);
}
}
I had to add:
spring.datasource.url=jdbc:h2:~/test
spring.datasource.driver-class-name=org.h2.Driver
to application.properties and it works great now. I just thought I don't need it becasue Spring will auto-configure it for me but apparently it doesn't.

Relationships question in hibernate

I'm learning Hibernate and Play framework (also add Java into account...). I'm having problems saving this kind of entity
#Entity
#Table(name="users")
public class User extends Model {
#Required
public String username;
#Column(name="user_displayname",nullable=true)
public String displayname;
#Password
public String user_password;
#Email
#Column(name="user_email",nullable=false,unique=true)
public String user_email;
public String user_salt;
public Date user_joindate;
#ManyToOne
#JoinTable(name="users_meta")
public UserMeta userdata;
#Required
public boolean user_isActive;
#OneToOne(targetEntity=UserPhotos.class,cascade=CascadeType.ALL)
#JoinColumn(name="id",referencedColumnName="userID")
public UserPhotos userPhoto;
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name="links_rol2user")
public List<Rol> rol;
public User (String username, models.Pass password, String user_email) {
this.username = username;
this.user_password = password.getHashedPassword();
this.user_salt = password.getUserHash();
this.user_email = user_email;
this.user_joindate = new Date();
this.user_isActive = false;
}
This is my code when I'm registering a user
// check if the validation has errors
if(validation.hasErrors()) {
params.flash(); // add http parameters to the flash scope
validation.keep(); // keep the errors for the next request
register();
} else {
Cache.delete(uuid);
Pass pass = new Pass(password,new Date().toString());
User newUser = new User(firstName, pass, email);
UserMeta utest = new UserMeta(newUser.id);
utest.setUserTownID(pueblos);
newUser.setUserMeta(utest);
newUser.save();
Logger.info("NewUser ID : %s", newUser.getId());
// UserMeta userInfo = new UserMeta(newUser.getId());
// userInfo.setUserTownID(pueblos);
// userInfo.save();
// TODO salvar foto a null
// Confirmation left
Cache.set("thankyou", "alright!", "3mn");
thankyou();
}
I'm trying to save the userMeta, it does creates a new record when I set the userMeta object into newUser (not visible right now), but it doesn't insert the new ID created in newUser.
What kind of relation do I need? before I tweaked the code as it is now, it was a OneToOne relationship, worked quite well, but now when I was completing the register functions it kinda hit me that I needed to save userMeta object too..
If you need more info let me know, I don't know if I explained it well or not, just trying to get the hang of how Hibernate do relations, etc.
Adding UserMeta:
*/
#Entity
#Table(name="users_meta")
public class UserMeta extends Model {
#Lob
#Column(name="userBio")
public String userBio;
#Column(name="userPhotoID",nullable=true)
public Long userPhotoID = null;
#Column(name="userRoleID", nullable=false)
public Long userRoleID = 2L;
#Lob
public String userDescription;
#Column(name="userViews", nullable=false)
public Long userViews = 0L;
#Column(name="userFavoriteCount", nullable=false)
public Long userFavoriteCount = 0L;
#Column(name="userTotalComments", nullable=false)
public Long userTotalComments = 0L;
#Column(name="userTotalUploadedVideos", nullable=false)
public Long userTotalUploadedVideos = 0L;
public Long userTownID;
public Long userID;
public UserMeta() {}
public UserMeta(Long userid) {
this.userBio = "El usuario no ha escrito nada todavia!";
this.userDescription = "El usuario todavia no se ha describido!";
this.userID = userid;
}
public Long getUserTownID() {
return userTownID;
}
public void setUserTownID(Long userTownID) {
this.userTownID = userTownID;
}
}
// pass model
public class Pass {
protected String hashed;
protected String userHash;
public Pass(String passwordToHash, String salt) {
StringBuffer passSalt = new StringBuffer(passwordToHash);
this.userHash = DigestUtils.md5Hex(salt);
passSalt.append(this.userHash);
passSalt.append(Play.configuration.getProperty("application.passwordSalt"));
this.hashed = DigestUtils.sha512Hex(passSalt.toString());
}
public String getHashedPassword() {
return this.hashed;
}
public String getUserHash() {
return this.userHash;
}
}
There seems to be a lot going on there! But from what I can tell, you problem is with the id that you are passing into the UserMeta.
As you are extending Model, the id is being generated by the Model class. However, this is not set until after the entity is saved to the database (as the id is auto-generated by the database).
Therefore, because you are passing the id into the UserMeta before the User object is saved, the value of id will be null.
If you can save the User object before you create your UserMeta object, your code should work.

Categories

Resources