We work on Spring Boot and oracle LDAP (Oid) with Spring LDAP module. The connection to LDAP with spring is ok but when we want to create LDAP group with ldapTemplate.create() method we got this error from Jackson:
please help us that what's wrong here!thanks
Type definition error: [simple type, class javax.naming.Name]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of javax.naming.Name (no Creators, like default construct, exist)
//Group Entry
#Getter
#Setter
#Entry(objectClasses = {"top", "groupOfUniqueNames"}, base = "cn=Groups")
public final class Group {
private static final String BASE_DN = "dc=eis,dc=msc,dc=ir";
#Id
private Name dn;
#Attribute(name="cn")
#DnAttribute("cn")
private String name;
#Attribute(name = "displayName")
private String description;
#Attribute(name = "owner")
private String owner;
#Attribute(name="uniqueMember")
private Set members;
public Group() {
}
public Group(String name, Set members) {
Name dn = LdapNameBuilder.newInstance(BASE_DN)
.add("ou", "groups")
.add("cn", name)
.build();
this.dn = dn;
this.name = name;
this.members = members;
}
public Group(Name dn, String name, String description, String owner, Set members) {
this.dn = dn;
this.name = name;
this.description = description;
this.owner = owner;
this.members = members;
}
public void addMember(Name member) {
if (this.members == null){
this.members = new HashSet<>();
}
members.add(member);
}
public void removeMember(Name member) {
members.remove(member);
}
#Override
public String toString() {
return "Group{" +
"dn=" + dn +
", name='" + name + '\'' +
", members=" + members +
'}';
}
//Service
#Override
public int createOIDGroupByJob(Group group) {
try {
ldapTemplate.create(group);
return 1;
}catch (Exception e){
return 0;
}
}
#PostMapping("/api/ldap/group")
public ResponseEntity<?> add(#RequestBody Group group) {
int retVal = ldapService.createOIDGroupByJob(group);
if (retVal==1) {
return new ResponseEntity<>(HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.EXPECTATION_FAILED);
}
}
//in postman
{
"dn": {
"rdns": [
{
"value": "Groups",
"type": "cn"
},
{
"value": "some data",
"type": "cn"
}
]
},
"fullName": "some data",
"lastName": "some data"
}
Finally!! I solved the problem!the problem is the jackson want to Deserialized an interface!! (javax.naming.Name) and it is not woking!
I found the solution is that use #JsonCreator on constructor like this:
//User Ldap Entry
public User(Name dn, String name, String lastName, String group) {
this.dn = dn;
this.name = name;
this.lastName = lastName;
this.group = group;
}
#JsonCreator
public User(#JsonProperty("dn") #JsonDeserialize(as=LdapName.class) final Name dn) {
this.dn = dn;
}
By the above sample, in #JsonDeserialize(as=LdapName.class) I pass LdapName.class that is the one of Name implemention! it works fine:)
Related
Basically, I am trying to establish a relationship between my two tables using spring boots.
And the relationship which I had used was the #onetoone and #onetomany relationship.
But after building the relationship and creating the table in MySQL whenever I run the program my foreign key is not updating.
The relationship is one user can have many contacts. I have tried unidirectional as well as bidirectional mapping but it is not working.
I want in contact table there will be a separate column for the foreign key. Based on that key I will show all contacts for that particular user.
This is my contact entity...
package com.example.jpa.contactEntities;
#Entity
#Table(name = "Contact")
public class ContactEntities {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long c_id;
private String c_name;
private String second_c_name;
private String c_work;
private String c_emali;
private String c_phone;
private String c_image;
#Column(length = 5000)
private String c_description;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "contact_id")
private UserEntities userEntities;
public ContactEntities() {
super();
}
public ContactEntities(long c_id, String c_name, String second_c_name, String c_work, String c_emali,
String c_phone, String c_image, String c_description, UserEntities userEntities) {
super();
this.c_id = c_id;
this.c_name = c_name;
this.second_c_name = second_c_name;
this.c_work = c_work;
this.c_emali = c_emali;
this.c_phone = c_phone;
this.c_image = c_image;
this.c_description = c_description;
this.userEntities = userEntities;
}
public long getC_id() {
return c_id;
}
public void setC_id(int c_id) {
this.c_id = c_id;
}
public String getC_name() {
return c_name;
}
public void setC_name(String c_name) {
this.c_name = c_name;
}
public String getSecond_c_name() {
return second_c_name;
}
public void setSecond_c_name(String second_c_name) {
this.second_c_name = second_c_name;
}
public String getC_work() {
return c_work;
}
public void setC_work(String c_work) {
this.c_work = c_work;
}
public String getC_emali() {
return c_emali;
}
public void setC_emali(String c_emali) {
this.c_emali = c_emali;
}
public String getC_phone() {
return c_phone;
}
public void setC_phone(String c_phone) {
this.c_phone = c_phone;
}
public String getC_image() {
return c_image;
}
public void setC_image(String c_image) {
this.c_image = c_image;
}
public String getC_description() {
return c_description;
}
public void setC_description(String c_description) {
this.c_description = c_description;
}
public UserEntities getUserEntities() {
return userEntities;
}
public void setUserEntities(UserEntities userEntities) {
this.userEntities = userEntities;
}
#Override
public String toString() {
return "ContactEntities [c_id=" + c_id + ", c_name=" + c_name + ", second_c_name=" + second_c_name + ", c_work="
+ c_work + ", c_emali=" + c_emali + ", c_phone=" + c_phone + ", c_image=" + c_image + ", c_description="
+ c_description + ", userEntities=" + userEntities + "]";
}
}
this is my user entity...
package com.example.jpa.userEntities;
#Entity
#Table(name = "UserEntities")
public class UserEntities {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long userId;
#NotBlank
#Size(min = 2, max = 20)
private String userName;
#NotBlank
#Column(unique = true)
#Email(regexp = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$")
private String userEmail;
#NotNull(message = "password should not be blank")
private String userPass;
private boolean enable;
private String role;
#Column(length = 500)
private String userAbout;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "userEntities", orphanRemoval = true)
private List<ContactEntities> contactList = new ArrayList<>();
public UserEntities() {
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserEmail() {
return userEmail;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
public String getUserPass() {
return userPass;
}
public void setUserPass(String userPass) {
this.userPass = userPass;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
public String getRoll() {
return role;
}
public void setRoll(String role) {
this.role = role;
}
public String getUserAbout() {
return userAbout;
}
public void setUserAbout(String userAbout) {
this.userAbout = userAbout;
}
public List<ContactEntities> getContactList() {
return contactList;
}
public void setContactList(List<ContactEntities> contactList) {
this.contactList = contactList;
}
#Override
public String toString() {
return "UserEntities [userId=" + userId + ", userName=" + userName + ", userEmail=" + userEmail + ", userPass="
+ userPass + ", enable=" + enable + ", role=" + role + ", userAbout=" + userAbout + ", contactList="
+ contactList + "]";
}
}
Repository of Contact
package com.example.jpa.repo;
import java.util.List;
import com.example.jpa.contactEntities.ContactEntities;
public interface ContactRepo extends JpaRepository<ContactEntities, Integer> {
#Query("from ContactEntities as c where c.userEntities.userId=:u_Id")
public List<ContactEntities> findContactsByUser(#Param("u_Id") long l);
}
Repository of User
package com.example.jpa.repo;
import com.example.jpa.userEntities.UserEntities;
#EnableJpaRepositories
public interface UserRepository extends JpaRepository<UserEntities, Integer> {
#Query("select u from UserEntities u where u.userEmail=:userEmail")
public UserEntities getUserByUserName(#Param("userEmail") String userEmail);
}
User controller
package com.example.jpa.controller;
#Controller
#RequestMapping("/user")
public class UserController {
#Autowired
private UserRepository userRepository;
#Autowired
private ContactRepo contactRepo;
#ModelAttribute
public void addCommonData(Model model, Principal principal) {
String username = principal.getName();
System.out.println("UserName:-" + username);
UserEntities userEntities = this.userRepository.getUserByUserName(username);
System.out.println("User:- " + userEntities);
model.addAttribute("userEntities", userEntities);
}
//dash board home
#RequestMapping("/index")
public String dashboard(Model model, Principal principal) {
return "normal/user_dashboard";
}
// open add form handler
#GetMapping("/add-contact")
public String openAddContactForm(Model model) {
model.addAttribute("title", "Add contact");
model.addAttribute("contactEntitie", new ContactEntities());
return "normal/add_contact";
}
// processing and contact form
#PostMapping("/upload")
public String processContact(#ModelAttribute ContactEntities contactEntitie,
#RequestParam("userImage") MultipartFile multipartFile, Principal principal, Model model,
HttpSession session) {
try {
model.addAttribute("contactEntitie", new ContactEntities());
String name = principal.getName();
UserEntities userEntities = userRepository.getUserByUserName(name);
userEntities.getContactList().add(contactEntitie);
// processing and uploading file....
if (multipartFile.isEmpty()) {
System.out.println("File is empty");
} else {
// upload the the file and update
contactEntitie.setC_image(multipartFile.getOriginalFilename());
File saveFile = new ClassPathResource("static/img").getFile();
// bring the folder path...
Path path = Paths
.get(saveFile.getAbsolutePath() + File.separator + multipartFile.getOriginalFilename());
Files.copy(multipartFile.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);
System.out.println("Image is uploaded");
}
userRepository.save(userEntities);
System.out.println("Datas are :" + contactEntitie);
// message success
session.setAttribute("message", new Messages("Your Contact is added !!! Add more...", "success"));
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
e.printStackTrace();
// error message
session.setAttribute("message", new Messages("Something went wrong !!! Try Again", "danger"));
}
return "normal/add_contact";
}
// show Contact handler
#GetMapping("/show-contacts")
public String showContact(Model model, Principal principal) {
model.addAttribute("title", "Show Contacts");
String userName = principal.getName();
UserEntities userEntities = userRepository.getUserByUserName(userName);
List<ContactEntities> contactList = contactRepo.findContactsByUser(userEntities.getUserId());
model.addAttribute("contactList", contactList);
return "normal/show_contacts";
}
}
All configuration class
User Details configuration
package com.example.jpa.Myconfiguration;
public class UserDetailsServiceImple implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// fetching data from DB
UserEntities userEntities = userRepository.getUserByUserName(username);
if (userEntities == null) {
throw new UsernameNotFoundException("Could not found user !!!");
}
CustomUserDetails customUserDetails = new CustomUserDetails(userEntities);
return customUserDetails;
}
}
package com.example.jpa.Myconfiguration;
public class CustomUserDetails implements UserDetails {
private UserEntities userEntities;
public CustomUserDetails(UserEntities userEntities) {
super();
this.userEntities = userEntities;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(userEntities.getRoll());
return List.of(simpleGrantedAuthority);
}
#Override
public String getPassword() {
return userEntities.getUserPass();
}
#Override
public String getUsername() {
return userEntities.getUserEmail();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
Application property:-
#Database configuration
spring.datasource.url=jdbc:mysql://localhost:3306/smartcontact
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.NonRegisteringDriver
spring.jpa.properties.hibernate.dilact=org.hibernate.dialect.Mysql8Dialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=false
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
spring.servlet.multipart.enabled=true
spring.servlet.multipart.file-size-threshold=2KB
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
I am new to Spring boot and Spring Data JPA . So here i am trying to implement a sample project where a employee has list of workers , while adding a new worker has employee details also to indicate that he works for particular employee. I am able to update the worker table and also fetch the details perfectly . Am trying to update Employee table as well so that while fetching a particular employee i want the list of workers associated with him also to be fetched . But that is not happening , i haven't used any query so far as it seems simple updation and i thought just save and setters would help to do so .
Employee.Java
#Entity
public class Employee {
#Id
private int empId;
private String empName;
private String location;
#OneToMany
private List<Worker> workers;
public Employee(){
}
public Employee(int empId, String empName, String location) {
super();
this.empId = empId;
this.empName = empName;
this.location = location;
}
public List<Worker> getWorkers() {
return workers;
}
public void setWorkers(List<Worker> workers) {
this.workers = workers;
}
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public void setWorkers(Worker worker) {
this.workers.add(worker);
}
#Override
public String toString() {
return "Employee [empId=" + empId + ", empName=" + empName + ", location=" + location + ", workers=" + workers
+ "]";
}
/*#Override
public String toString() {
return "Employee [empId=" + empId + ", empName=" + empName + ", location=" + location + "]";
}*/
Worker.Java
#Entity
public class Worker {
#Id
private int id;
private String name;
#ManyToOne
#JoinColumn(name="empId")
private Employee employee;
public Worker(int id, String name , int empId) {
super();
this.id = id;
this.name = name;
this.employee = new Employee(empId,"","");
}
public Worker() {
// TODO Auto-generated constructor stub
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
WorkerController.Java
#RestController
public class WorkerController {
#Autowired
WorkerRepository workerService;
#Autowired
EmployeeRepository employeeService;
#GetMapping("/employees/{id}/workers")
public List<Worker> getAllWorker(#PathVariable("id") int empId){
return workerService.findByEmployeeEmpId(empId);
}
#PostMapping("/employees/{id}/workers")
public String addNewEmployee(#RequestParam("name") String name ,
#RequestParam("workerId") int id , #PathVariable("id") int empId){
Worker worker = new Worker();
List<Worker> workers = new ArrayList<Worker>();
worker.setId(id);
worker.setName(name);
worker.setEmployee(new Employee(empId,"",""));
workerService.save(worker);
workers.add(worker);
employeeService.findById(empId).get().setWorkers(workers);
Employee emp = new Employee();
emp = employeeService.findById(empId).get();
return "Successfully added";
}
}
After adding worker , i retrieve the following as output
[
{
"id": 108,
"name": "vijay",
"employee": {
"empId": 99,
"empName": "darsha",
"location": "mumbai",
"workers": []
}
},
{
"id": 110,
"name": "suraj",
"employee": {
"empId": 99,
"empName": "darsha",
"location": "mumbai",
"workers": []
}
}
]
but while retrieving i could see the employee table is not updated. can someone guide me .
{
"empId": 99,
"empName": "darsha",
"location": "mumbai",
"workers": []
}
You says:
"...while fetching a particular employee i want the list of workers
associated with him also to be fetched"
then you have to write a OneToMany Relationship on employee's side. What you do is you tries to fetch for each Worker one Employee which is of course working and is there in your JSON result.
Here is a OneToMany Example on Emplyees class side:
#OneToMany(mappedBy="employee", fetch=FetchType.EAGER, cascade=CascadeType.ALL)
private List<Worker> worker = new ArrayList<>();
Note: mappedBy have to refer to the variable Emplyee employee in your Worker Class.
Please try different fetch types also.
Why you are not getting your #OneToMany attributes is due fetch type is lazy by default for all #OneToMany associations like list, set.
To overcome this problem. Change the default lazy fetch to eager fetch.
Modify your entity mapping to this.
#OneToMany(mappedBy="employee", fetch=FetchType.EAGER, cascade=CascadeType.ALL) private List<Worker> worker = new ArrayList<>();
while fetching you may get recursive mapping. To avoid this just add #JsonIgnoreProperties to your employee field in worker class.
Example:
#JsonIgnoreProperties("employee")
#ManyToOne
#JoinColumn(name="empId")
private Employee employee;
In my spring boot project I created a Repository interface (which extends CRUDRepository) and an Entity class of the Table in my DB.
This is my Repo:
#Repository
public interface AuthPaymentDao extends CrudRepository<TFraudCard,String> {
#Query("SELECT t FROM TFraudCard t where t.tokenNumber = (?1)")
TFraudCard findByTokenNumber(String tokenNumber);
}
This is my Entity Class (TOKEN_NUMBER is the primary Key in the TFRAUDCARD TABLE):
#Entity
#Table(name = "TFRAUDCARD")
public class TFraudCard {
#Id
#Column(name="TOKEN_NUMBER")
private String tokenNumber;
#Column(name="TRANSACTIONNUMBER")
private int transactionNumber;
#Column(name="CARDNUMBER")
private int cardNumber;
#Column(name="DATEADDED", insertable = false, updatable = false, nullable = false)
private Timestamp dateAdded;
#Column(name="CALLINGENTITY", nullable = false)
private String callingEntity;
#Column(name="ACCOUNTID")
private String accountId;
#Column(name="ROUTINGNUMBER")
private String routingNumber;
#Column(name="BANKACCOUNTNUMBER")
private String bankAccountNumber;
#Column(name="COMMENTS")
private String comments;
#Column(name="USERID")
private String userId;
#Column(name="REMOVEDATE")
private Timestamp removeDate;
public String getTokenNumber() {
return tokenNumber;
}
public void setTokenNumber(String tokenNumber) {
this.tokenNumber = tokenNumber;
}
public int getTransactionNumber() {
return transactionNumber;
}
public void setTransactionNumber(int transactionNumber) {
this.transactionNumber = transactionNumber;
}
public int getCardNumber() {
return cardNumber;
}
public void setCardNumber(int cardNumber) {
this.cardNumber = cardNumber;
}
public Timestamp getDateAdded() {
return dateAdded;
}
public void setDateAdded(Timestamp dateAdded) {
this.dateAdded = dateAdded;
}
public String getCallingEntity() {
return callingEntity;
}
public void setCallingEntity(String callingEntity) {
this.callingEntity = callingEntity;
}
public String getAccountId() {
return accountId;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public String getRoutingNumber() {
return routingNumber;
}
public void setRoutingNumber(String routingNumber) {
this.routingNumber = routingNumber;
}
public String getBankAccountNumber() {
return bankAccountNumber;
}
public void setBankAccountNumber(String bankAccountNumber) {
this.bankAccountNumber = bankAccountNumber;
}
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public Timestamp getRemoveDate() {
return removeDate;
}
public void setRemoveDate(Timestamp removeDate) {
this.removeDate = removeDate;
}
public TFraudCard() {
super();
}
public TFraudCard(String tokenNumber, int transactionNumber, int cardNumber, Timestamp dateAdded,
String callingEntity, String accountId, String routingNumber, String bankAccountNumber, String comments,
String userId, Timestamp removeDate) {
super();
this.tokenNumber = tokenNumber;
this.transactionNumber = transactionNumber;
this.cardNumber = cardNumber;
this.dateAdded = dateAdded;
this.callingEntity = callingEntity;
this.accountId = accountId;
this.routingNumber = routingNumber;
this.bankAccountNumber = bankAccountNumber;
this.comments = comments;
this.userId = userId;
this.removeDate = removeDate;
}
#Override
public String toString() {
return "TFraudCard [tokenNumber=" + tokenNumber + ", transactionNumber=" + transactionNumber + ", cardNumber="
+ cardNumber + ", dateAdded=" + dateAdded + ", callingEntity=" + callingEntity + ", accountId="
+ accountId + ", routingNumber=" + routingNumber + ", bankAccountNumber=" + bankAccountNumber
+ ", comments=" + comments + ", userId=" + userId + ", removeDate=" + removeDate + "]";
}
}
My Service Class:
Autowiring the DAO instance inside my Service Class:
Implementing the DAO instance inside a Method in the Service Class:
private void fraudCheck(PaymentDetail paymentDetail) throws RegularPaymentBusinessException {
logger.info("INSIDE FRAUD CHECK METHOD");
String pmtInd=paymentDetail.getPmtInd();
logger.info("pmtInd: " + pmtInd);
String tokenizedCardNum=paymentDetail.getTokenizedCardNum();
logger.info("tokenizedCardNum: " + tokenizedCardNum);
if(pmtInd.equalsIgnoreCase(VepsConstants.GIFT_CARD_IDENTIFIER) || pmtInd.equalsIgnoreCase(VepsConstants.CREDIT_CARD_IDENTIFIER) || pmtInd.equalsIgnoreCase(VepsConstants.DEBIT_CARD_IDENTIFIER)) {
logger.info("INSIDE CARD CHECK");
TFraudCard fraudCard = authPaymentDao.findByTokenNumber(tokenizedCardNum);
logger.info("fraudCard Details: " + fraudCard.toString());
if(fraudCard!=null) {
logger.info("INSIDE EXCEPTION FLOW FOR CARD FRAUD CHECK");
throw new RegularPaymentBusinessException(VepsConstants._9966, VepsConstants._9966_MESSAGE, VepsConstants.FAILURE);
}
}
}
Even though I pass the same token Number (tokenizedCardNumber) in my method as the data in the TOKEN_NUMBER column of my TFRAUDCARD table I still get a NullPointerException when I try to print a toString() of the Entity Object.
Here is the NullPointerException on my cloudFoundry logs (Click on it to see zoomed image) :
I'm providing the DB details in my dev properties file:
I have gone over every scenario in my head for why it breaks but I still can't come up with an answer. I'm using my variable marked with #Id i.e. the Primary Key for my find() method in the Repository.
I'm also adding a #Query annotation just to be even more specific.
It still does not work.
I'm using Spring Boot 1.5.3, Spring Data REST, HATEOAS.
I've a simple entity model:
#Entity
public class User extends AbstractEntity implements UserDetails {
private static final long serialVersionUID = 5745401123028683585L;
public static final PasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder();
#NotNull(message = "The name of the user cannot be blank")
#Column(nullable = false)
private String name;
/** CONTACT INFORMATION **/
private String landlinePhone;
private String mobilePhone;
#NotNull(message = "The username cannot be blank")
#Column(nullable = false, unique = true)
private String username;
#Email(message = "The email address is not valid")
private String email;
#JsonIgnore
private String password;
#Column(nullable = false)
private String timeZone = "Europe/Rome";
#JsonIgnore
private LocalDateTime lastPasswordResetDate;
#Column(nullable = false, columnDefinition = "BOOLEAN default true")
private boolean enabled = true;
#Type(type = "json")
#Column(columnDefinition = "json")
private Roles[] roles = new Roles[] {};
and my enum Roles is:
public enum Roles {
ROLE_ADMIN, ROLE_USER, ROLE_MANAGER, ROLE_TECH;
#JsonCreator
public static Roles create(String value) {
if (value == null) {
throw new IllegalArgumentException();
}
for (Roles v : values()) {
if (value.equals(v.toString())) {
return v;
}
}
throw new IllegalArgumentException();
}
}
I'm creating a client in Angular 4. Spring Data REST is great and expose repository easily return my model HATEOAS compliant:
{
"_embedded": {
"users": [
{
"name": "Administrator",
"username": "admin",
"roles": [
"Amministratore"
],
"activeWorkSession": "",
"_links": {
"self": {
"href": "http://localhost:8080/api/v1/users/1"
},
"user": {
"href": "http://localhost:8080/api/v1/users/1{?projection}",
"templated": true
}
}
},
Like you can see I'm also translating via rest-messages.properties the value of my enums. Great!
My Angular page now needs the complete lists of roles (enums). I've some question:
understand the better way for the server to return the list of roles
how to return this list
My first attemp was to create a RepositoryRestController in order to take advantage of what Spring Data REST offers.
#RepositoryRestController
#RequestMapping(path = "/api/v1")
public class UserController {
#Autowired
private EntityLinks entityLinks;
#RequestMapping(method = RequestMethod.GET, path = "/users/roles", produces = "application/json")
public Resource<Roles> findRoles() {
Resource<Roles> resource = new Resource<>(Roles.ROLE_ADMIN);
return resource;
}
Unfortunately, for some reason, the call to this methods return a 404 error. I debugged and the resource is created correctly, so I guess the problem is somewhere in the JSON conversion.
how to return this list?
#RepositoryRestController
#RequestMapping("/roles")
public class RoleController {
#GetMapping
public ResponseEntity<?> getAllRoles() {
List<Resource<Roles>> content = new ArrayList<>();
content.addAll(Arrays.asList(
new Resource<>(Roles.ROLE1 /*, Optional Links */),
new Resource<>(Roles.ROLE2 /*, Optional Links */)));
return ResponseEntity.ok(new Resources<>(content /*, Optional Links */));
}
}
I was playing around with this and have found a couple of ways to do it.
Assume you have a front end form that wants to display a combo box containing priorities for a single Todo such as High, Medium, Low. The form needs to know the primary key or id which is the enum value in this instance and the value should be the readable formatted value the combo box should display.
If you wish to customize the json response in 1 place only such as a single endpoint then I found this useful. The secret sauce is using the value object PriorityValue to allow you to rename the json field through #Relation.
public enum Priority {
HIGH("High"),
NORMAL("Normal"),
LOW("Low");
private final String description;
Priority(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public static List<Priority> orderedValues = new ArrayList<>();
static {
orderedValues.addAll(Arrays.asList(Priority.values()));
}
}
#RepositoryRestController
#RequestMapping(value="/")
public class PriorityController {
#Relation(collectionRelation = "priorities")
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
private class PriorityValue {
private String id;
private String value;
public PriorityValue(String id,
String value) {
this.id = id;
this.value = value;
}
}
#GetMapping(value = "/api/priorities", produces = MediaTypes.HAL_JSON_VALUE)
public ResponseEntity<Resources<PriorityValue>> getPriorities() {
List<PriorityValue> priorities = Priority.orderedValues.stream()
.map(p -> new PriorityValue(p.name(), p.getDescription()))
.collect(Collectors.toList());
Resources<PriorityValue> resources = new Resources<>(priorities);
resources.add(linkTo(methodOn(PriorityController.class).getPriorities()).withSelfRel());
return ResponseEntity.ok(resources);
}
}
Another approach is to use a custom JsonSerializer. The only issue using this is everywhere a Priority enum is serialized you will end up using this format which may not be what you want.
#JsonSerialize(using = PrioritySerializer.class)
#Relation(collectionRelation = "priorities")
public enum Priority {
HIGH("High"),
NORMAL("Normal"),
LOW("Low");
private final String description;
Priority(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public static List<Priority> orderedValues = new ArrayList<>();
static {
orderedValues.addAll(Arrays.asList(Priority.values()));
}
}
#RepositoryRestController
#RequestMapping(value="/api")
public class PriorityController {
#GetMapping(value = "/priorities", produces = MediaTypes.HAL_JSON_VALUE)
public ResponseEntity<Resources<Priority>> getPriorities() {
Resources<Priority> resources = new Resources<>(Priority.orderedValues);
resources.add(linkTo(methodOn(PriorityController.class).getPriorities()).withSelfRel());
return ResponseEntity.ok(resources);
}
}
public class PrioritySerializer extends JsonSerializer<Priority> {
#Override
public void serialize(Priority priority,
JsonGenerator generator,
SerializerProvider serializerProvider)
throws IOException, JsonProcessingException {
generator.writeStartObject();
generator.writeFieldName("id");
generator.writeString(priority.name());
generator.writeFieldName("value");
generator.writeString(priority.getDescription());
generator.writeEndObject();
}
}
The final json response from http://localhost:8080/api/priorities
{
"_embedded": {
"priorities": [
{
"id": "HIGH",
"value": "High"
},
{
"id": "NORMAL",
"value": "Normal"
},
{
"id": "LOW",
"value": "Low"
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/api/priorities"
}
}
}
I'm trying to query a mongo repository to return data that is within a specified geo circle. I'm using the following code:
Page<Img> findByLocationWithin(Circle circle, Pageable pageable);
and then in my controller I'm using:
Distance distance = new Distance(7.5, Metrics.MILES);
Circle circle = new Circle(location, distance);
Page<Img> results = imgRepository.findByLocationWithin(circle, pageable);
However it definitely doesn't use a radius of 7.5 miles as if I create the circle a few hundred metres away from where the data is located, it returns nothing. I've checked the logs in mongo and it says that the following code is being performed:
"location" : {
"$within" : {
"$center" : [
[
30.198,
-1.695
],
0.0018924144710663706
]
}
}
This means it's not using $geoWithin or $centerSphere. How can I fix these problems?
I had the same problem with spring and Couchbase... but the query is not the problem... Because Spring convert the distance in the geometric values.
In my case I also returned null, but my problem was solved, in the model class, the attribute that specifies the coordinate [x, y] must be of type Library Point org.springframework.data.geo.Point;
package com.webServices.rutas.model;
import org.springframework.data.couchbase.core.mapping.id.GeneratedValue;
import org.springframework.data.couchbase.core.mapping.id.GenerationStrategy;
import org.springframework.data.geo.Point;
import com.couchbase.client.java.repository.annotation.Field;
import com.couchbase.client.java.repository.annotation.Id;
public class Parada {
#Id #GeneratedValue(strategy = GenerationStrategy.UNIQUE)
private String id;
#Field
private String type;
#Field
private String nombre;
#Field
private String urlFoto;
#Field
private Point coordenada;
public Parada(String nombre, String urlFoto, Point coordenada) {
super();
this.type = "parada";
this.nombre = nombre;
this.urlFoto = urlFoto;
this.coordenada = coordenada;
}
public Parada() {
super();
this.type = "parada";
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getUrlFoto() {
return urlFoto;
}
public void setUrlFoto(String urlFoto) {
this.urlFoto = urlFoto;
}
public Point getCoordenada() {
return coordenada;
}
public void setCoordenada(Point coordenada) {
this.coordenada = coordenada;
}
#Override
public String toString() {
return "Parada [id=" + id + ", type=" + type + ", nombre=" + nombre + ", urlFoto=" + urlFoto + ", coordenada="
+ coordenada + "]";
}
}
In the service:
public Iterable<Parada> getParadasCercanasRadio(Punto punto){
Point cuadro = new Point(-2.2,-80.9);
Circle circle = new Circle(cuadro,new Distance(300000, Metrics.KILOMETERS));
return paradaRepository.findByCoordenadaWithin(circle);
}
In the Repository:
#Dimensional(designDocument = "paradas", spatialViewName = "paradas")
Iterable<Parada> findByCoordenadaWithin(Circle p);
P.D. Sorry for my English.