Can I use `#Cacheable` in a Controller? - spring-boot

I want to cache my database access but I have no Repositories. This is how I'm doing (please don't ask why. This is not the point here):
#RequestMapping(value = "/database", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
public List<User> testDatabaseCache( #RequestParam("username") String userName ) {
Object[] params = new Object[] { userName };
String sql = "select * from public.users where user_name = ?";
List<User> users = jdbcTemplate.query(sql, params, new UserMapper() );
log.info("Database hit: " + userName);
return users;
}
So... since I have no repository to annotate as cacheable, what can I do?

Related

ServiceResponse mocked which gives a null value and not expected this null

I'm writing j-unit Test-cases for my services and in which i couldn't mock service Response properly, Which is giving me a null. can somebody help me in this issue.
public ResponseEntity<Void> lockGet(
#ApiParam(value = "Unique identifier for this request.", required = true) #RequestHeader(value = "service-id", required = true) String serviceId,
#ApiParam(value = "Logged in userid.", required = true) #RequestHeader(value = "user-id", required = true) String userId,
#ApiParam(value = "Unique messageid.", required = true) #RequestHeader(value = "message-id", required = true) String messageId,
#RequestHeader(value = "access-token", required = true) String accessToken,
#ApiParam(value = "Unique id of the doamin of the entity", required = true) #RequestParam(value = "lockDomainId", required = true) Long lockDomainId,
#ApiParam(value = "Unique id of the entity to be fetched", required = true) #RequestParam(value = "lockEntityId", required = true) Long lockEntityId,
HttpServletRequest request, HttpServletResponse response) {
ResponseEntity<Void> result = null;
if (request.getAttribute("user-id") != null)
userId = (String) request.getAttribute("user-id");
String logContext = "||" + lockDomainId + "|" + lockEntityId + "||";
ThreadContext.put("context", logContext);
long t1 = System.currentTimeMillis();
LOG.info("Method Entry: lockGet" + logContext);
ServiceRequest serviceRequest = AppUtils.mapGetRequestHeaderToServiceRequest(serviceId, userId, lockDomainId,
lockEntityId);
try {
ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
// set all the response headers got from serviceResponse
HeaderUtils.setResponseHeaders(serviceResponse.getResponseHeaders(), response);
result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
} catch (Exception ex) {
LOG.error("Error in lockGet", ex);
result = new ResponseEntity<Void>(HttpStatus.INTERNAL_SERVER_ERROR);
}
ThreadContext.put("responseTime", String.valueOf(System.currentTimeMillis() - t1));
LOG.info("Method Exit: lockGet");
return result;
}
#Test
public void testLockGetForError() {
request.setAttribute("user-id","TestUser");
ServiceRequest serviceRequest = new ServiceRequest();
serviceRequest.setUserId("TestUser");
ServiceResponse serviceResponse = new ServiceResponse();
LockService service = Mockito.mock(LockService.class);
when(service.getLock(serviceRequest)).thenReturn(serviceResponse);
// ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
ResponseEntity<Void> result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
ResponseEntity<Void> lockGet = lockApiController.lockGet("1234", "TestUser", "TestMessage", "TestTkn", 12345L, 12345L, request, response);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, lockGet.getStatusCode());
}
I tried in different scenario's which couldn't fix this issue. Can someone help me out. Thanks in advance.
From the code that you have put , the issue that i see is that you are actually mocking the LockService object but when calling the lockApiController.lockGet method the code is not actually working with the mocked LockService since lockApiController has an LockService object of it's own.
One way to solve this issue is to inject the mocked LockService
object into the lockApiController object using #Spy. This way
when the getLock() is called it will be actually called on the
mocked object and will return the mock response provided.
So in your test :
#Test
public void testLockGetForError() {
LockService service = Mockito.mock(LockService.class);
LockApiController lockApiController = Mockito.spy(new LockApiController(service));
request.setAttribute("user-id","TestUser");
ServiceRequest serviceRequest = new ServiceRequest();
serviceRequest.setUserId("TestUser");
ServiceResponse serviceResponse = new ServiceResponse();
when(service.getLock(serviceRequest)).thenReturn(serviceResponse);
// ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
ResponseEntity<Void> result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
ResponseEntity<Void> lockGet = lockApiController.lockGet("1234", "TestUser", "TestMessage", "TestTkn", 12345L, 12345L, request, response);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, lockGet.getStatusCode());
}
So you can try passing the mocked LockService object to the spy object.
Another way is to try using the #InjectMocks to inject the mocked
object into the LockApiController.
#InjectMocks marks a field on which injection should be performed. Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection – in this order. If any of the given injection strategy fail, then Mockito won’t report failure.
For example:
#Mock
Map<String, String> wordMap;
#InjectMocks
MyDictionary dic = new MyDictionary();
#Test
public void whenUseInjectMocksAnnotation_thenCorrect() {
Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");
assertEquals("aMeaning", dic.getMeaning("aWord"));
}
For the class:
public class MyDictionary {
Map<String, String> wordMap;
public MyDictionary() {
wordMap = new HashMap<String, String>();
}
public void add(final String word, final String meaning) {
wordMap.put(word, meaning);
}
public String getMeaning(final String word) {
return wordMap.get(word);
}
}
For both of these to work , you must be having a constructor or appropriate setters to set the mock object to the LockApiController class.
Reference : https://howtodoinjava.com/mockito/mockito-annotations/

Unable to update Data to DB : org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations

i was trying to update database tables by using following Hibernate Query Language
#RequestMapping(value = "/update",method = RequestMethod.POST)
public #ResponseBody String update(#RequestParam(value = "score1",required = true) String score1,
#RequestParam(value = "score2",required = true) String score2,
#RequestParam(value = "score3",required = true) String score3,
#RequestParam(value = "score4",required = true) String score4,
#RequestParam(value = "id",required = true)String id,
Model model)
{
SessionFactory sessionFactory=new Configuration().configure("hibernate.cfg.xml")
.addAnnotatedClass(User.class)
.addAnnotatedClass(UserDetail.class)
.addAnnotatedClass(UserScores.class).buildSessionFactory();
Session session=sessionFactory.getCurrentSession();
try
{
System.out.println("ID is"+id);
session.beginTransaction();
session.createQuery("update UserScores u set " +
"u.score1=:score1," +
"u.score2=:score2," +
"u.score3=:score3," +
"u.score4=:score4 where u.ID=:id")
.setParameter("score1",score1)
.setParameter("score2",score2)
.setParameter("score3",score3)
.setParameter("score4",score4)
.setParameter("id",id);
session.getTransaction().commit();
session.close();
}
catch (Exception e)
{
System.out.println(e);
}
return score1+score2+score3+score4;
}
after executing this code, it doesnt give any error , but the data is not updated in the database
what is the problem in executing this code
Its working, i tried it in another way
#RequestMapping(value = "/update",method = RequestMethod.POST)
public #ResponseBody String update(#RequestParam(value = "score1",required = true) String score1,
#RequestParam(value = "score2",required = true) String score2,
#RequestParam(value = "score3",required = true) String score3,
#RequestParam(value = "score4",required = true) String score4,
#RequestParam(value = "id",required = true)String id,
Model model)
{
SessionFactory sessionFactory=new Configuration().configure("hibernate.cfg.xml")
.addAnnotatedClass(User.class)
.addAnnotatedClass(UserDetail.class)
.addAnnotatedClass(UserScores.class).buildSessionFactory();
Session session=sessionFactory.getCurrentSession();
try
{
session.beginTransaction();
System.out.println("ID is"+id);
UserScores userScores=session.get(UserScores.class,Integer.parseInt(id));
userScores.setScore1((Double.parseDouble(score1)));
userScores.setScore2((Double.parseDouble(score2)));
userScores.setScore3((Double.parseDouble(score3)));
userScores.setScore4((Double.parseDouble(score4)));
session.update(userScores);
session.getTransaction().commit();
session.close();
}
catch (Exception e)
{
System.out.println(e);
}
The query that you are creating needs to be executed using query.executeUpdate()
session.beginTransaction();
Query query = session.createQuery("update UserScores u set " +
"u.score1=:score1," +
"u.score2=:score2," +
"u.score3=:score3," +
"u.score4=:score4 where u.ID=:id");
query.setParameter("score1",score1)
query.setParameter("score2",score2)
query.setParameter("score3",score3)
query.setParameter("score4",score4)
query.setParameter("id",id);
query.executeUpdate();
session.getTransaction().commit();
session.close();
An alternative way is to make changes to the persistent entities. In this case the updates will be automatically propogated.

Login and get information user with token in spring (no OAuth)

I am implementing a project RESTful API, it should login (username / password) and returns a token, I want to use token to retrieve user information.
I follow the instructions:
https://github.com/virgo47/restful-spring-security
But: I do not know how to use it in my function, you can help me with?
#RequestMapping(value = "/login", method = RequestMethod.POST)
public #ResponseBody ResponseObject<Object> login(
#RequestParam(value = "username", required = true) String username,
#RequestParam(value = "password", required = true) String password,
#RequestHeader(value = "token") String token,
HttpSession session) {
//TODO
return new ResponseObject<Object>(1, "Success", data);
}
#RequestMapping(value = "/info", method = RequestMethod.GET)
public #ResponseBody ResponseObject<User> getInfo(#RequestHeader(value = "token", required = true) String token,
HttpSession session) {
//TODO
return null;
}
Why would you want to do that ? Why not just get the logged in user from the SecurityContext as follows
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String test() {
System.out.println(" *** MainRestController.test");
// Spring Security dependency is unwanted in controller, typically some
// #Component (UserContext) hides it.
// Not that we don't use Spring Security annotations anyway...
return "SecurityContext: "
+ SecurityContextHolder.getContext().getAuthentication()
.getName();
}
If you insist on doing it, you can do the following.
UserDetails userDetails =
(UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
tokenManager.getUserTokens(userDetails)

Spring MVC variable resets for no reason

For some reason when I execute a GET request to a certain URI the variable that I need to access in that method loses its memory or points to null.
I have a form where a user can update his personal information. But when he enters a duplicate, it redirects him to a page that lets him know
I have : private static volatile User currentUser;
This field is set when a user logs in and the server performs a GET request to a REST API, which I programmed myself, and returns the User containing his info. This works as expected and the user info is displayed on his home screen.
Code for the above:
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(#ModelAttribute Credentials credentials,
RedirectAttributes redirect) {
RestTemplate restTemplate = new RestTemplate();
RoleInfo roleInfo = restTemplate.postForObject(
"http://localhost:9090/users/login", credentials,
RoleInfo.class);
if (roleInfo != null) {
if (roleInfo.isAdmin()) {
redirect.addFlashAttribute("credentials", credentials);
return "redirect:/adminHome";
} else {
redirect.addFlashAttribute("credentials", credentials);
return "redirect:/getBasicUser";
}
} else {
return "login_fail";
}
}
#RequestMapping(value = "/getBasicUser", method = RequestMethod.GET)
public <T> String getBasicUser(#ModelAttribute Credentials credentials,
Model model, RedirectAttributes redirect) {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:9090/users/getBasicUser?username="
+ credentials.getUsername();
ResponseEntity<User> responseEntity = restTemplate.exchange(
url,
HttpMethod.GET,
new HttpEntity<T>(createHeaders(credentials.getUsername(),
credentials.getPassword())), User.class);
User user;
user = responseEntity.getBody();
currentUser = user;
System.out.println("current user: " + currentUser.getUsername());
if (user != null) {
userName = credentials.getUsername();
passWord = credentials.getPassword();
redirect.addFlashAttribute("credentials", credentials);
redirect.addFlashAttribute("user", user);
return "redirect:/basicHome";
} else {
return "register_fail";
}
}
So on "basicHome" he can view his information. Also on that page is a link to a form where he can edit the information:
#RequestMapping(value = "/edit", method = RequestMethod.GET)
public String getEditProfilePage(Model model) {
model.addAttribute("currentUser", currentUser);
System.out.println("current use firstname: " + currentUser.getFirstname());
model.addAttribute("user", new User());
return "edit_profile";
}
If an edit is successful he is returned back to his home page with the updated information.
The problem comes when he enters invalid info. He should be redirected back to the "/edit" URI and the currentUserfield should still hold his information but is actually null.
Here is the "/edit" PUT function:
#RequestMapping(value = "/edit", method = RequestMethod.PUT)
public <T> String editProfile(#ModelAttribute("user") User user,
#ModelAttribute("credentials") Credentials credentials,
RedirectAttributes redirect) {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:9090/users/update?username=" + userName;
HttpHeaders headers = createHeaders(userName,
passWord);
#SuppressWarnings({ "unchecked", "rawtypes" })
HttpEntity<T> entity = new HttpEntity(user, headers);
ResponseEntity<User> responseEntity = restTemplate.exchange(url,
HttpMethod.PUT, entity, User.class);
User returnedUser = responseEntity.getBody();
currentUser = returnedUser;
if (returnedUser != null) {
redirect.addFlashAttribute("user", returnedUser);
redirect.addFlashAttribute("credentials", credentials);
return "redirect:/basicHome";
} else {
return "redirect:/editFail";
}
}
I figured out what I had to do. I basically made "user" a session object in: #SessionAttributes("user")

Type cast issue for [Ljava.lang.Object

public List<Client> findClientByAssociateUser(String userId) {
logger.info("Enter find list of clients by this user");
org.hibernate.Query query = sessionFactory.getCurrentSession()
.createQuery("SELECT c.id, c.clientName, c.billingAddress,c.contactNumber"
+ " from Client c, User ud"
+ " WHERE ud.id = c.userId and ud.id = :id")
.setString("id", userId);
List<Client> result = (List<Client>) query.list();
logger.info("Exit find list of clients");
return result;
}
public ModelAndView userDetails(#PathVariable String id, HttpServletRequest request) {
ModelAndView mvc = new ModelAndView();
List<Client> clientList = userRepository.findClientByAssociateUser(id.toString());
mvc.addObject("clientList", clientList);
for (Client client : clientList) {
System.out.println("Client Name{" + client.getClientName());
}
mvc.setViewName(MANAGEUSER_PREFIX + "details");
return mvc;
}
I am getting:
Ljava.lang.Object; cannot be cast to Client
The return type in the query would be List<Object[] >.
Because your query says
SELECT c.id, c.clientName, c.billingAddress,c.c......
change
List<Client> result = (List<Client>) query.list();
and then process according to that
to
List<Object[]> result = (List<Object[]>) query.list();
or change the query to
SELECT c from Client c......

Resources