How to set the Boolean value as false in Java Spring boot - java

I am practicing Java and Spring Boot.
Actually my idea is if we send json delete request means it should change details in the database as inactive instead of deleting that data.
for Example if I want to delete the student record. Base on student ID as 1 means it should change the student status as inactive instead of deleting that record.
In spring boot controller I have a delete method.
Codes are below for your understanding:
Controller:
#DeleteMapping("v1/student/delete/{id}")
public ResponseEntity<String> deleteStudentDetails(#PathVariable("id") Integer getId) {
studentService.deleteStudentdetails(getId);
return new ResponseEntity<>("STUDENT RECORD HAS BEEN DELETED !!!", HttpStatus.OK);
}
studentService = is a service class which sending the information from controller to service.
deleteStudentdetails = is a method in service class.
Service class method
public void deleteStudentdetails(Integer getId) {
Optional<StudentDetails> studentIdDetails = studentRepo.findById(getId); // getting info from DB
StudentDetails studentIsdetail = studentIdDetails.get();
if (studentIsdetail.getActive() == false) {
throw new RuntimeException("Student is Already inActive!!");
}
studentIsdetail.setActive(false); // Here I want to change the Active Status from True to false
}
Here I am changing the values in the Database by retrieving the student ID which is already existing in the DB.
STUDENT_ID ACTIVE BOARD_ID
60 TRUE STATEBOARD
116 TRUE STATEBOARD //here want to change the status as False
120 FALSE STATEBOARD

You never save studentIsdetail into the database after modifying.
So just add studentRepo.save(studentIsdetail); after calling studentIsdetail.setActive(false); and it should work:
public void deleteStudentdetails(Integer getId) {
Optional<StudentDetails> studentIdDetails = studentRepo.findById(getId); // getting info from DB
StudentDetails studentIsdetail = studentIdDetails.get();
if (!studentIsdetail.getActive()) {
throw new RuntimeException("Student is Already inActive!!");
}
studentIsdetail.setActive(false);
studentRepo.save(studentIsdetail);
}
And I recommend that you check if you get a result to avoid exceptions:
public void deleteStudentdetails(Integer getId) {
Optional<StudentDetails> studentIdDetails = studentRepo.findById(getId); // getting info from DB
if (studentIsdetail.isEmpty()) {
// handle e.g. throw exception or just return (with return:)
return;
}
StudentDetails studentIsdetail = studentIdDetails.get();
if (!studentIsdetail.getActive()) {
throw new RuntimeException("Student is Already inActive!!");
}
studentIsdetail.setActive(false);
studentRepo.save(studentIsdetail);
}
For this you can also use Optional#ifPresent:
public void deleteStudentdetails(Integer getId) {
Optional<StudentDetails> studentIdDetails = studentRepo.findById(getId); // getting info from DB
studentIdDetails.ifPresent((studentIsdetail) -> {
if (!studentIsdetail.getActive()) {
throw new RuntimeException("Student is Already inActive!!");
}
studentIsdetail.setActive(false);
studentRepo.save(studentIsdetail);
});
}

Related

ArrayLIst in an object is not updating during Integration test

I have one service layer class which takes in a userId and add the userId to an arraylist in the Lobby object.I need to perform Integration test for this functionality. But somehow the code is not returning the updated arraylist to the test class. In service layer its working fine but when asserting the arraylist updated size, its failing.
Test module -
#BeforeEach
public void setupLobby(){
testUser = new User();
testUser.setPassword("testName");
testUser.setUsername("testUsername");
testUser = userService.createUser(testUser);
lobbyTest = new Lobby();
lobbyTest.setName("testLobby");
lobbyTest.setHostPlayerId(testUser.getId());
lobbyId = lobbyService.createLobby(lobbyTest);
}
#Test
public void addUserToLobby(){
System.out.println("before ->"+lobbyTest.getPlayerIds().size());
User newUser = new User();
newUser.setUsername("user2");
newUser.setPassword("password");
newUser = userService.createUser(newUser);
System.out.println("new user ->"+newUser.getId());
lobbyService.addPlayerToLobby(lobbyTest.getId(),newUser.getId());
System.out.println("after->"+lobbyTest.getPlayerIds().size());
assertEquals(lobbyTest.getPlayerIds().size(),2);
}
Service method which I need to test
public void addPlayerToLobby(long id, long userId){
Lobby lobby = getLobby(id);
System.out.println("service ->"+lobby.getId()+" "+lobby.getPlayerIds().size());
if(lobby.getStatus()==1){
throw new LobbyException("Game is in progress. You can't join lobby in the middle of the game. Please try later");
}
//Checking if the user exists before adding the user to lobby
try {
userRepository.findById(userId);
} catch (Exception e) {
throw new LobbyException(String.format("User with id: %d doesn't exist", userId));
}
String baseErrorMessage = "The lobby cannot have more than 7 player. Please join different lobby";
System.out.println("service2->"+lobby.getId()+" "+lobby.getPlayerIds().size());
//Size of lobby is limited to maximum of 7 players.
if(lobby.getPlayerIds().size()>7){
throw new LobbyException(baseErrorMessage);
}
//Player should be unique in the lobby
if(lobby.getPlayerIds().contains(userId)){
baseErrorMessage = "Player already exists in the lobby";
throw new LobbyException(baseErrorMessage);
}
lobby.getPlayerIds().add(userId);
saveOrUpdate(lobby);
System.out.println("service3->"+lobby.getId()+" "+lobby.getPlayerIds().size());
}
public Lobby getLobby(Long id) {
Optional<Lobby> optionalLobby = lobbyRepository.findById(id);
if (!optionalLobby.isPresent()) {
throw new LobbyException(String.format("Could not find lobby with id %d.", id));
}
return optionalLobby.get();
}
public void saveOrUpdate(Lobby updateLobby){
lobbyRepository.save(updateLobby);
lobbyRepository.flush();
System.out.println("service method ->"+updateLobby.getId()+" "+updateLobby.getPlayerIds().size());
}
For my testing I have put some print statement which clearly shows that its updating the arraylist in service layer but its not replicated in integration test method.
before ->1
new user ->3
service ->2 1
service2->2 1
service method ->2 2
service3->2 2
after->1
I am unable to get the issue. If there is some reference issue with the object?
I'd need to see the full source code to be sure, but it seems the Lobby object returned at:
Lobby lobby = getLobby(id);
is not the same object as the Lobby you set up in your test.
You can confirm that by checking the object signature (toString()), their Hex codes are likely different.

Update row if references is exist, otherwise delete using spring boot

Need guideline -
How to do hard delete when no reference is available and do soft delete when reference is available, this operation should be performed in a single method itself.
E.g.
I have 1 master table and 3 transactional tables and the master reference is available in all 3 transactional tables.
Now while deleting master row - I have to do the following: If master reference is available then update the master table row and if no master ref. is available delete the row.
I tried following so far.
Service Implementation -
public response doHardOrSoftDelete(Employee emp) {
boolean flag = iMasterDao.isDataExist(emp);
if(flag) {
boolean result = iMasterDao.doSoftDelete(emp);
} else {
boolean result = iMasterDao.doHardDelete(emp);
}
}
Second Approach:
As we know that while deleting a record if the reference is available then it throws ConstraintViolationException so simply we can catch it and check that caught exception is of type ConstraintViolationException or not, if yes then call doSoftDelete() method and return the response. So here you don't need to write method or anything to check the references. But I'm not sure whether it is the right approach or not. Just help me with it.
Here is what I tried again -
public Response deleteEmployee(Employee emp) {
Response response = null;
try{
String status= iMasterDao.deleteEmployeeDetails(emp);
if(status.equals("SUCCESS")) {
response = new Response();
response.setStatus("Success");
response.setStatusCode("200");
response.setResult("True");
response.setReason("Record deleted successfully");
return response;
}else {
response = new Response();
response.setStatus("Fail");
response.setStatusCode("200");
response.setResult("False");
}
}catch(Exception e){
response = new Response();
Throwable t =e.getCause();
while ((t != null) && !(t instanceof ConstraintViolationException)) {
t = t.getCause();
}
if(t instanceof ConstraintViolationException){
boolean flag = iMasterDao.setEmployeeIsDeactive(emp);
if(flag) {
response.setStatus("Success");
response.setStatusCode("200");
response.setResult("True");
response.setReason("Record deleted successfully");
}else{
response.setStatus("Fail");
response.setStatusCode("200");
response.setResult("False");
}
}else {
response.setStatus("Fail");
response.setStatusCode("500");
response.setResult("False");
response.setReason("# EXCEPTION : " + e.getMessage());
}
}
return response;
}
Dao Implementation -
public boolean isDataExist(Employee emp) {
boolean flag = false;
List<Object[]> tbl1 = session.createQuery("FROM Table1 where emp_id=:id")
.setParameter("id",emp.getId())
.getResultList();
if(!tbl1.isEmpty() && tbl1.size() > 0) {
flag = true;
}
List<Object[]> tbl2 = session.createQuery("FROM Table2 where emp_id=:id")
.setParameter("id",emp.getId())
.getResultList();
if(!tbl2.isEmpty() && tbl2.size() > 0) {
flag = true;
}
List<Object[]> tbl3 = session.createQuery("FROM Table3 where emp_id=:id")
.setParameter("id",emp.getId())
.getResultList();
if(!tbl3.isEmpty() && tbl3.size() > 0) {
flag = true;
}
return flag;
}
public boolean doSoftDelete(Employee emp) {
empDet = session.get(Employee.class, emp.getId());
empDet .setIsActive("N");
session.update(empDet);
}
public boolean doHardDelete(Employee emp) {
empDet = session.get(Employee.class, emp.getId());
session.delete(empDet);
}
No matter how many transactional tables will be added with master tbl reference, my code should do the operations(soft/hard delete) accordingly.
In my case, every time new transactional tables get added with a master reference I've do the checks, so Simply I want to skip the isDataExist() method and do the deletions accordingly, how can I do it in a better way?
Please help me with the right approach to do the same.
There's a lot of repeated code in the body of isDataExist() method which is both hard to maintain and hard to extend (if you have to add 3 more tables the code will double in size).
On top of that the logic is not optimal as it will go over all tables even if the result from the first one is enough to return true.
Here is a simplified version (please note that I haven't tested the code and there could be errors, but it should be enough to explain the concept):
public boolean isDataExist(Employee emp) {
List<String> tableNames = List.of("Table1", "Table2", "Table3");
for (String tableName : tableNames) {
if (existsInTable(tableName, emp.getId())) {
return true;
}
}
return false;
}
private boolean existsInTable(String tableName, Long employeeId) {
String query = String.format("SELECT count(*) FROM %s WHERE emp_id=:id", tableName);
long count = (long)session
.createQuery(query)
.setParameter("id", employeeId)
.getSingleResult();
return count > 0;
}
isDataExist() contains a list of all table names and iterates over these until the first successful encounter of the required Employee id in which case it returns true. If not found in any table the method returns false.
private boolean existsInTable(String tableName, Long employeeId) is a helper method that does the actual search for employeeId in the specified tableName.
I changed the query to just return the count (0 or more) instead of a the actual entity objects as these are not required and there's no point to fetch them.
EDIT in response to the "Second approach"
Is the Second Approach meeting the requirements?
If so, then it is a "right approach" to the problem. :)
I would refactor the deleteEmployeeDetails method to either return a boolean (if just two possible outcomes are expected) or to return a custom Enum as using a String here doesn't seem appropriate.
There is repeated code in deleteEmployeeDetails and this is never a good thing. You should separate the logic which decides the type of the response from the code that builds it, thus making your code easier to follow, debug and extend when required.
Let me know if you need a code example of the ideas above.
EDIT #2
Here is the sample code as requested.
First we define a Status enum which should be used as return type from MasterDao's methods:
public enum Status {
DELETE_SUCCESS("Success", "200", "True", "Record deleted successfully"),
DELETE_FAIL("Fail", "200", "False", ""),
DEACTIVATE_SUCCESS("Success", "200", "True", "Record deactivated successfully"),
DEACTIVATE_FAIL("Fail", "200", "False", ""),
ERROR("Fail", "500", "False", "");
private String status;
private String statusCode;
private String result;
private String reason;
Status(String status, String statusCode, String result, String reason) {
this.status = status;
this.statusCode = statusCode;
this.result = result;
this.reason = reason;
}
// Getters
}
MasterDao methods changed to return Status instead of String or boolean:
public Status deleteEmployeeDetails(Employee employee) {
return Status.DELETE_SUCCESS; // or Status.DELETE_FAIL
}
public Status deactivateEmployee(Employee employee) {
return Status.DEACTIVATE_SUCCESS; // or Status.DEACTIVATE_FAIL
}
Here is the new deleteEmployee() method:
public Response deleteEmployee(Employee employee) {
Status status;
String reason = null;
try {
status = masterDao.deleteEmployeeDetails(employee);
} catch (Exception e) {
if (isConstraintViolationException(e)) {
status = masterDao.deactivateEmployee(employee);
} else {
status = Status.ERROR;
reason = "# EXCEPTION : " + e.getMessage();
}
}
return buildResponse(status, reason);
}
It uses two simple utility methods (you can make these static or export to utility class as they do not depend on the internal state).
First checks if the root cause of the thrown exception is ConstraintViolationException:
private boolean isConstraintViolationException(Throwable throwable) {
Throwable root = throwable;
while (root != null && !(root instanceof ConstraintViolationException)) {
root = root.getCause();
}
return root != null;
}
And the second one builds the Response out of the Status and a reason:
private Response buildResponse(Status status, String reason) {
Response response = new Response();
response.setStatus(status.getStatus());
response.setStatusCode(status.getStatusCode());
response.setResult(status.getResult());
if (reason != null) {
response.setReason(reason);
} else {
response.setReason(status.getReason());
}
return response;
}
If you do not like to have the Status enum loaded with default Response messages, you could strip it from the extra info:
public enum Status {
DELETE_SUCCESS, DELETE_FAIL, DEACTIVATE_SUCCESS, DEACTIVATE_FAIL, ERROR;
}
And use switch or if-else statements in buildResponse(Status status, String reason) method to build the response based on the Status type.

Error while querying for a column in database using spring

I am trying to query an entire column data for eg:
SELECT USER_USERNAME FROM xxxx WHERE USER_USERNAME=?
I'm getting error
org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
My Dao
#Override
public String getAllUsers(UserRegistration uname) {
System.out.println(uname.getUserName());
return template.queryForObject(GET_USER_USERNAME, new Object[] { uname.getUserName() },
new BeanPropertyRowMapper<String>(String.class));
}
I'm injecting the properties through xml file.
my controller
#RequestMapping(method = RequestMethod.POST,value = "/checkUserName", headers = "Accept=application/json")
public org.weber.nag.model.UserRegistration checkUserName(#RequestBody org.weber.nag.model.UserRegistration userReg) {
userDao.getAllUsers(userReg);
return userReg;
}
So from the above when i am trying to pass the username from postman it takes the values to controller and from there I'm passing it to my dao to compare whether the name exits or not.The name successfully reaches my dao but I get an error.
So I tried to catch the exception
#Override
public String getAllUsers(UserRegistration uname) {
System.out.println(uname.getUserName());
try {
return template.queryForObject(GET_USER_USERNAME, new Object[] { uname.getUserName() },
new BeanPropertyRowMapper<String>(String.class));
} catch (EmptyResultDataAccessException e) {
System.out.println("uname already exists");
return "user exists";
}
}
But every time it prints
"uname already exists"
irrespective of the username given whether it is there in db or not.
In JdbcTemplate , queryForInt, queryForLong, queryForObject all such methods expects that executed query will return one and only one row.
If you get no rows that will result in EmptyResultDataAccessException.
From the javadoc of EmptyResultDataAccessException
Data access exception thrown when a result was expected to have at
least one row (or element) but zero rows (or elements) were actually
returned.
Make sure the query you are using should return only one row.
If at all it is not possible then use query method instead of queryForObject.
Tip: To debug this, run the same query in an SQL IDE directly.
#Override
public String getAllUsers(UserRegistration uname) {
try {
template.queryForObject(GET_USER_USERNAME, new Object[] { uname.getUserName() },
new BeanPropertyRowMapper<String>(String.class));
System.out.println("uname exists");
return "user name is NOT available.";
} catch (EmptyResultDataAccessException e) {
System.out.println("uname do not exists");
}
return "user is available";
}

Play framework and ionic for mobile I need Security without cookies but token

I have an issue with using mobile app that I created in Ionic and back-end APIs that I coded in play framework, my issue simply is I need way to handle security matter for calling APIs that need to be secured, mean user must be logged in to do actions, as example guest can view group but can join only if logged in.
My issue that I believe that cookies is not supported for Mobile, i have code checking session that stored in cookies, its working for website, but it will not work for mobile, right?
Currently I'm trying to send a token that generated in back-end and returned with login response and stored in localStorage in Ionic, but my issue is that I can't sent token to be validated with request.
Front End:
I have the following http interceptor :
angular
.module('app.core')
.factory('sessionInjector', sessionInjector);
/** #ngInject */
function sessionInjector($q, sessionService, $rootScope) {
return {
request: function (config) {
$rootScope.$broadcast('loading:show');
if (!sessionService.isAnonymous())
config.headers['x-session-token'] = sessionService.getToken();
}
return config;
}
}
Back-End:
Controller:
#Security.Authenticated(Secure.class)
public Result joinOrganization() {
// Do some business
}
Secure.java :
#Override
public String getUsername(Http.Context ctx) {
// Is this correct way? I get null here
String str = ctx.request().getHeader("x-session-token");
String userId = ctx.session().get("usedId");
if (userId == null) {
return null;
}
User user = Play.application().injector().instanceOf(UserService.class).findUserById(Integer.parseInt(userId));
if (user != null && user.isActive) {
return user.id;
} else {
return null;
}
}
#Override
public Result onUnauthorized(Http.Context ctx) {
return unauthorized(results);
}
Note: Tokens stored in database:
Entity:
#Entity
#Table(name = "AUTHTOKEN")
public class AuthToken extends BaseModel {
#OneToOne(targetEntity = User.class, cascade = CascadeType.REFRESH, optional = false)
public User user;
#Column(nullable = false)
public String token;
#Column
public long expiration;
public AuthToken() {
}
}
For cookies working, but need to remove cookies and use tokens, or use them together cookies for website, tokens for mobile .
Mobile is no different to otherwise in regards of cookies. There are restrictions if AJAX, or cross-domain requests are used, and specially with Apple stuff, but they apply to non-mobile too. If your cookies work on a PC/Mac, they should do on a mobile device just as well. It's more of a form factor than anything else...
I found solution and it was complicated because there are many issues starting from that ngResource does not apply request interceptor its an issue opened from long time.
Second issue was how to send the token with ngResource, its simply with adding headers, the another issue here is getting dynamically the token, this "dynamically" means because the localStorage in memory getting lost when refresh so you need to get back it, this can be done with service, and function call for getting the token, something like this :
$resource('/user/:userId/card/:cardId', {userId:123, cardId:'#id'}, {
charge: {method:'POST', params:{charge:true}, headers = {
'x-session-token': function () {
return sessionService.getToken()
}}
});
Inside sessionService :
// this to recreate the cache in memory once the user refresh, it will keep the data if exisit but will point to it again in memory
if (CacheFactory.get('profileCache') == undefined) {
//if there is no cache already then create new one and assign it to profileCache
CacheFactory.createCache('profileCache');
}
function getCurrentSession() {
var profileCache = CacheFactory.get('profileCache');
if (profileCache !== undefined && profileCache.get('Token') !== undefined) {
return profileCache.get('Token');
}
return null;
}
function getToken() {
var currentSession = getCurrentSession();
if (currentSession != null && currentSession != '') {
return currentSession.token;
}
return null;
}
And then this method will work inside Secure.java
protected User getUser(Http.Context ctx) {
String token = ctx.request().getHeader("x-session-token");
if (token != null) {
return securityService.validateToken(token);
}
return null;
}

Removing/deleting from google datastore using endpoints, illegal arguement exception, delete with non-zero content length not supported

I'm trying to delete objects from the datastore (using cloud endpoints)
I know the connection is valid because I'm pulling/inserting objects with no problem
However when I try to delete using various approaches I get the same exception
java.lang.illegalArgumentException:DELETE with non-zero content length is not supported
approach 1(using the raw datastore service and the key I stored when inserting the item):
#ApiMethod(name = "removeRPurchase")
public RPurchase removeRPurchase(RPurchase purchase) {
NamespaceManager.set(purchase.getAccount());
DatastoreService d=DatastoreServiceFactory.getDatastoreService();
Key k=KeyFactory.stringToKey(purchase.getKeyrep());
try {
d.delete(k);
} catch (Exception e) {
e.printStackTrace();
purchase=null;
}
return purchase;
}
Approach 2
#ApiMethod(name = "removeRPurchase")
public RPurchase removeRPurchase(RPurchase purchase) {
NamespaceManager.set(purchase.getAccount());
Key k=KeyFactory.stringToKey(purchase.getKeyrep());
EntityManager mgr = getEntityManager();
RPurchase removed=null;
try {
RPurchase rpurchase = mgr.find(RPurchase.class, k);
mgr.remove(rpurchase);
removed=rpurchase;
} finally {
mgr.close();
}
return removed;
}
Ive also tried various variations with the entity manager and the Id, but all with the same exception
The object that i've passed in does contain the namespace in the account, and it does contain the 'KeytoString' of the key associated with the object
the endpoint is called as it should in an AsyncTask endpoint.removeRPurchase(p).execute();
Any help suggestions are appreciated
Make your API method a POST method like this:
#ApiMethod(name = "removeRPurchase" path = "remove_r_purchase", httpMethod = ApiMethod.HttpMethod.POST)
public RPurchase removeRPurchase(RPurchase purchase) {
NamespaceManager.set(purchase.getAccount());
DatastoreService d=DatastoreServiceFactory.getDatastoreService();
Key k=KeyFactory.stringToKey(purchase.getKeyrep());
try {
d.delete(k);
} catch (Exception e) {
e.printStackTrace();
purchase=null;
}
return purchase;
}
I had the same problem because I was using httpMethod = ApiMethod.HttpMethod.DELETE. The error it gives is correct. Simply change it to a POST and do whatever you want inside that API method like delete entities, return entities, etc.
How about trying out the following :
#ApiMethod(
name = "removeRPurchase",
httpMethod = HttpMethod.DELETE
)
public void removeRPurchase(#Named("id") String id) {
//Now take the id and plugin in your datastore code to retrieve / delete
}

Categories