I'm currently learning spring boot and encounter an error message
method count(JobViewWrapper) is already defined
yes it's because it has 2 of the same method name , but the method has 2 separate function , the first one is to count all the job with deleted flag 1.
the second one (count-active), is to count all the job with deleted flag 1 and is active flag 1.
So i needed this 2 method, is there a workaround to do it?
#PostMapping(value = "/count")
public long count(#RequestBody(required = false) JobViewWrapper wrapper) {
System.out.println("into controller count");
if (wrapper == null) {
wrapper = new JobViewWrapper();
}
System.out.println("Prepare to count service");
return JobService.countLazyView();
}
#PostMapping(value = "/count-active")
public long count(#RequestBody(required = false) JobViewWrapper wrapper) {
System.out.println("into controller count");
if (wrapper == null) {
wrapper = new JobViewWrapper();
}
System.out.println("Prepare to count service");
return JobService.countLazyViewIsActive();
}
my service
public long countLazyView() {
return lowonganKerjaRepo.countLazyView();
}
public long countLazyViewIsActive() {
return lowonganKerjaRepo.countLazyViewIsActive();
}
If you really want to overload method count you must select one of the three options available:
provide different number of parameters:
#PostMapping(value = "/count")
public long count(#RequestBody(required = false) JobViewWrapper wrapper) {
// ...
return JobService.countLazyView();
}
#PostMapping(value = "/count-active")
public long count() {
return JobService.countLazyViewIsActive();
}
provide different types of parameters:
#PostMapping(value = "/count")
public long count(#RequestBody(required = false) JobViewWrapper wrapper) {
// ...
return JobService.countLazyView();
}
#PostMapping(value = "/count-active")
public long count(#RequestBody(required = false) ActiveJobViewWrapper wrapper) {
return JobService.countLazyViewIsActive();
}
provide different order of parameters (seems to be not applicable in this case).
If none of these options can be selectable, you'll have options:
Provide different names for these methods count() and countActive
Replace these methods with one method having additional parameter (API call may be changed to /count?active=true):
#PostMapping(value = "/count")
public long count(
#RequestParam(name = "active", required = false, defaultValue = "false") Boolean active,
#RequestBody(required = false) JobViewWrapper wrapper) {
// ...
return active ? JobService.countLazyViewIsActive() : JobService.countLazyView();
}
Related
I wish to perform strict validation on a boolean parameter, seen below as "withDetails."
#ApiResponses(value = {#ApiResponse(responseCode = "200", description = "Success")})
#GetMapping(value = "/order", produces = "application/json")
public ResponseEntity<ResponseClass> getOrders( String id,
#RequestParam(value = "withDetails", defaultValue = "true")
Boolean withDetails){
ResponseClass responseClass = this.service.getResponse(id, withDetails);
return ResponseEntity.ok(responseClass);
}
Spring accepts non-boolean substitutions for this value, such as 0/1. This is apparently by design, as described here:
https://stackoverflow.com/a/25123000/11994829
However, I want to strictly only allow "true/false" in requests. Can I do that while still defining the parameter as Boolean?
Try to create your own Converter:
#Component
public class BooleanConverter implements Converter<String, Boolean> {
#Override
public Boolean convert(String bool) {
if ("true".equals(bool)) {
return true;
}
if ("false".equals(bool)) {
return false;
}
throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
}
}
In spring Boolean parameters have a value of 1/0 if you want to catch the value
boolean type make parameter as String String withDetails and convert it .valueOf into boolean type (true\false)
or you to make your own converter convert 1 as true and 0 as false
I heard that Mockito framework is really good for testing purposes.
Can anyone help me to write a good JUnit for below code.
#Component
public class DataIntegrityValidatorForUpdate extends EmployeeCommonValidator implements Validator {
#Autowired
private EmployeeRepository employeeRepository;
#Override
public boolean supports(Class<?> paramClass) {
return Employee.class.equals(paramClass);
}
#Override
public void validate(Object targetObject, Errors errors) {
if (!(targetObject instanceof Employee))
return;
Employee employee = (Employee) targetObject;
Long employeeId = employee.getId();
String tenantId = employee.getTenantId();
// FIXME employee.getId == null or empty then throw error employee id is
// required
if (isEmpty(employeeId)) {
errors.rejectValue("employee.id", "no value", "provide employee id for update");
return;
}
if (!employeeRepository.checkIfEmployeeExists(employeeId, tenantId)) {
// FIXME throw error employee id does not exist
errors.rejectValue("employee.id", "no value present", "employee id doesn't exist");
return;
}
validateEmployee(employee, errors);
validateAssignments(employee.getAssignments(), employeeId, tenantId, errors);
public void validateEmployee(Employee employee, Errors errors) {
// FIXME call common validator.validateEmployee
super.validateEmployee(employee, errors);
// FIXME : check only for different employees
if (checkIfColumnValueIsSameInDB("egeis_employee", "code", employee.getCode(), employee.getId(), employee.getTenantId())) {
errors.rejectValue("employee.code", "invalid", "Employee Code cannot be changed.");
}
if ((employee.getPassportNo() != null) && duplicateExists("egeis_employee", "passportNo", employee.getPassportNo(), employee.getId(), employee.getTenantId())) {
errors.rejectValue("employee.passportNo", "concurrent", "passportNo already exists");
}
if ((employee.getGpfNo() != null) && duplicateExists("egeis_employee", "gpfNo", employee.getGpfNo(), employee.getId(), employee.getTenantId())) {
errors.rejectValue("employee.gpfNo", "concurrent", "gpfNo already exists");
}
}
private void validateAssignments(List<Assignment> assignments, Long employeeId, String tenantId, Errors errors) {
validateIdsForAssignment(assignments, employeeId, tenantId, errors);
for (int index = 0; index < assignments.size(); index++) {
// validateDocumentsForNewAssignment(assignments.get(index),
// errors, index);
}
}
private void validateIdsForAssignment(List<Assignment> assignments, Long employeeId, String tenantId, Errors errors) {
Map<Long, Integer> idsMap = new HashMap<>();
for (int index = 0; index < assignments.size(); index++) {
if (assignments.get(index).getId() != null) // FIXME check if long
// gets default value of
// 0L
idsMap.put(assignments.get(index).getId(), index);
}
if (!idsMap.isEmpty())
validateEntityId(idsMap, EntityType.ASSIGNMENT, employeeId, tenantId, errors);
}
public Boolean duplicateExists(String table, String column, String value, Long id, String tenantId) {
Long idFromDb = employeeRepository.getId(table, column, value, tenantId);
if (idFromDb == 0 || id.equals(idFromDb))
return false;
return true;
}
public Boolean checkIfColumnValueIsSameInDB(String table, String column, String value, Long id, String tenantId) {
Long idFromDb = employeeRepository.getId(table, column, value, tenantId);
if (id.equals(idFromDb))
return false;
return true;
}
}
Given your production code, writing tests is pretty simple; like:
public class DataIntegrityValidatorForUpdateTest {
#Mock
Errors mockedErrors;
#Mock
EmployeeRepository mockedRepository;
#InjectMocks
DataIntegrityValidatorForUpdate underTest;
#Before
public void setup() {
initMocks();
}
#Test
public void testNotAnEmployee() {
underTest.validate("not an employee", mockedErrors);
verifyZeroInteractions(mockedRepository, mockedErrors);
}
What this does:
it mocks the two objects you need to control in order to test this class
it uses the #InjectMocks annotation to instantiate an instance of the class you intend to test
it then tests one aspect of your method - the method is invoked with parameters that should cause the method to return and do nothing
The above is meant as inspiration. Now you have to enhance it, to cover all the possible paths that your method can take. Most of your test methods would look similar; but you will have to learn how to use Mockito to verify that certain calls on your mock objects came in.
But please do not expect that we do that for you. It is your project, and you have to study the documentation and tutorials to understand what you need to do.
Beyond that: from a clean code perspective, I would suggest you to refactor the whole thing. I would rather throw exceptions to indicate failed validations; instead of using an in/out parameter like errors.
Working with Spring data jpa and specifications, I have a requirement to implement a filter/search feature in spring mvc. The backend receives an object (ReportTemplateBean) which is basically a bean with some fields that represents the filters in the front end.
public class ReportTemplateBean implements Serializable {
private static final long serialVersionUID = -3915391620260021813L;
private Long id;
private String property;
private String city;
private String state;
private String zipCode;
private String propertyStatus;
private String realEstateRep;
//more code
We have the Controller
#RequestMapping(value = "/search", method = RequestMethod.GET)
#ResponseBody
public ReportBean search(#AuthenticationPrincipal ActiveUser activeUser,
#ModelAttribute("templateForm") ReportTemplateBean template,
Pageable pageable) throws GenericException {
LOGGER.info("Pulling report requested");
ReportBean report = reportService.searchProperties(template,
pageable.getPageNumber(), pageable.getPageSize());
return report;
}
The service
#Override
#Transactional(readOnly = true, timeout = 20)
public ReportBean searchProperties(ReportTemplateBean template,
Integer pageNumber, Integer pageSize) throws GenericException,
TransactionTimedOutException {
LOGGER.info("searchProperties({})", template);
try {
// pageNumber = (pageNumber == null ? 0 : pageNumber);
// pageSize = (pageSize == null ? 10 : pageSize);
ReportTemplate t = reportTemplateMapper.beanToEntity(template);
List<PropertyBean> beans = new ArrayList<PropertyBean>();
PropertySpecification spec = new PropertySpecification(t);
Page<Property> properties = propertyRepository.findAll(spec,
new PageRequest(pageNumber, pageSize, Sort.Direction.ASC,
"name"));
And then it builds the query dynamically, but using a long IF chain that I don't like it. This is the Specification.
#SuppressWarnings("unchecked")
#Override
public Predicate toPredicate(Root<Property> root, CriteriaQuery<?> query,
CriteriaBuilder cb) {
Path<String> propertyName = root.get(Property_.name);
Path<String> city = root.get(Property_.city);
Path<String> state = root.get(Property_.state);
Path<String> zipCode = root.get(Property_.zipCode);
final List<Predicate> orPredicates = new ArrayList<Predicate>();
final List<Predicate> andPredicates = new ArrayList<Predicate>();
if (template.getProperty() != null
&& template.getProperty().length() > 0) {
andPredicates.add(cb.equal(propertyName, template.getProperty()));
}
if (template.getCity() != null && template.getCity().length() > 0) {
andPredicates.add(cb.equal(city, template.getCity()));
}
if (template.getState() != null && template.getState().length() > 0) {
andPredicates.add(cb.equal(state, template.getState()));
}
if (template.getZipCode() != null && template.getZipCode().length() > 0) {
andPredicates.add(cb.equal(zipCode, template.getZipCode()));
}
if (template.getRealEstateRep() != null) {
Join<Property, User> pu = null;
if (query.getResultType().getName().equals("java.lang.Long")) {
pu = (Join<Property, User>) root.fetch(Property_.createdBy);
} else {
pu = root.join(Property_.createdBy);
}
Path<Long> userId = pu.get(User_.id);
andPredicates.add(cb.equal(userId, template.getRealEstateRep()));
}
if (template.getProjectType() != null
&& template.getProjectType().length() > 0) {
Join<Property, Project> pp = null;
if (query.getResultType().getName().equals("java.lang.Long")) {
pp = root.join(Property_.projects);
} else {
pp = (Join<Property, Project>) root.fetch(Property_.projects);
}
Path<String> projectType = pp.get(Project_.projectName);
andPredicates.add(cb.equal(projectType, template.getProjectType()));
}
//more IF's
return query.getRestriction();
}
As you can notice the Specification seems ugly and besides that SONAR complains about The Cyclomatic Complexity of this method (which is good).
So question is, How can I refactor the Specification (IF's) to be more OO code?.
Thanks in advance.
UPDATE- I would like to use/implement something like the new feature in Spring Data JPA (Query by Example) It seems that if you pass a bean the ExampleMatcher class will ignore the null value in the bean fields which is almost what I am looking for. Ignore null and empty values.
I write my solution to give you another option, but as i say in the comment i do not used Specification, and i am curious to see if anyone knows another way to do dynamic queries in spring jpa.
You could write your own query with the #Query annotation inside a #Repository interface.
In your case (assuming ReportTemplateBean is your Entity and its primary key is of Long type) it would be like:
#Repository
public interface ReportTemplateRepo extends JpaRepository<ReportTemplateBean, Long>{
#Query("SELECT rb FROM ReportBeanTemplate rb JOIN ExampleTable et WHERE et.idTemplate = rb.id AND (:id is null OR :id = rb.id) AND (:city is null OR :city = rb.city) AND (:state is null OR :state = rb.state)")
public List<ReportTemplateBean> findTemplates(#Param("id") Long id, #Param("city") String city, #Param("state") String state);
}
You can add all the parameters you want, pass it as null when you call the method.
Example of method invocation(in you service class):
#Autowire
ReportTemplateRepo templateRepo;
public void invocation(ReportTemplateBean template){
List<ReportTemplateBean> templateRepo.findTemplates(
template.getId(), template.getCity(), template.getState());
}
This is the only way i found to do this kind of query.
suppose I have the following simple rest defined:
#RequestMapping("/user/data")
#ResponseBody
public String getUserDetails(#RequestParam int id) {
...
}
Is there a way to read the path string problematically by another part of the code (i.e. from a different method altogether) ?
Something like:
String restPath = SomeReflection.getPath("getuserdetails"); // received value: "/user/data"
WDYT?
thanks!
Solved!
here's the implementation I needed:
public String getUrlFromRestMethod(Class controllerClass, String methodName) {
try {
Method method = controllerClass.getMethod(methodName);
if (method != null && method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMappingAnnotation = method.getAnnotation(RequestMapping.class);
return requestMappingAnnotation.toString();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();//TODO
}
return null;
}
If you mean that you wanna access that value programmatically even from another class, then maybe you can start working out your solution from this example:
//get all methods defined in ClassA
Method[] methods = ClassA.class.getMethods();
for (Method m : methods) {
//pick only the ones annotated with "#RequestMapping"
if (m.isAnnotationPresent(RequestMapping.class)) {
RequestMapping ta = m.getAnnotation(RequestMapping.class);
System.out.println(ta.value()[0].toString());
}
}
I would suggest you add a HttpServletRequest request in your method, and then from there go request.getServletPath()
ei
public String getUserDetails(HttpServletRequest request, #RequestParam int id) {
Or if this is done in Spring http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-ann-requestmapping
#RequestMapping(path = "/{day}", method = RequestMethod.GET)
public Map<String, Appointment> getForDay(#PathVariable #DateTimeFormat(iso=ISO.DATE) Date day, Model model) {
return appointmentBook.getAppointmentsForDay(day);
}
#RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(#PathVariable String ownerId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
model.addAttribute("owner", owner);
return "displayOwner";
}
maybe u can call its value.
Apologies if this has already been asked/answered a thousand times (I did check first).
I'm not a Java programmer by trade and have been tasked with extending an existing Java SOAP service. I'm keen to avoid copy/pasting existing code that I know works, but was wondering what the best options available in java are.
Essentially I have this method already:
public String methodThatIDontWantToCopyPaste(
#WebParam(name = "a", partName = "a") String paramA,
#WebParam(name = "b", partName = "b") int paramB,
#WebParam(name = "c", partName = "c") int paramC) {
// Validate parameters
if (paramA.isEmpty() ||
0 == paramB ||
0 == paramC) {
return "Invalid request";
}
String response = "";
try {
// Parmaeters OK, build client
/*
lots of generic implementation here
...
XYZ client = ...
...
*/
response = client.specificMethodToHandleABC(paramA, paramB, paramC);
} catch (SOAPException | IOException ex) {
// handling omitted
}
return response;
}
I want to add several new/similar methods, but each will have:
A different set of parameters
A different block of code to validate the parameters (the above code is trivial, but some will be more detailed
A different implementation of the line:
response = client.specificMethodToHandleABC(a, b, c);
i.e. where a different method will be called, with a different set of arguments
I'd normally go for a callback in my usual programming language, but it's a proprietary language and not as powerful as Java, so I wanted to know what the best option was?
In a similar setting, I used callbacks/anonymous classes by calling a method out of every endpoint and passing a callback for every variable part into the method, like the following
public String methodA(final int param1, final String param2) throws Exception {
return this.call(new Callable<Boolean>() {
#Override
public Boolean call() throws Exception {
return param1 != 0 && param2 != null;
}
});
}
public String methodB(final String param1, final String param2) throws Exception {
return this.call(new Callable<Boolean>() {
#Override
public Boolean call() throws Exception {
return param1 != null && param2 != null;
}
});
}
private String call(Callable<Boolean> validationCallable) throws Exception {
// static code similar to all methods
assert validationCallable.call().equals(Boolean.TRUE);
// static code similar to all methods
return ""; // probably result based on another callable
}