How to pass #EmbeddedId in POST json in spring-boot data rest - java

I have built a REST API using Spring Boot Data REST. I'm using an embeddedId and have also implemented a BackendIdConverter.
Below is my Embeddable class
#Embeddable
public class EmployeeIdentity implements Serializable {
#NotNull
#Size(max = 20)
private String employeeId;
#NotNull
#Size(max = 20)
private String companyId;
public EmployeeIdentity() {}
public EmployeeIdentity(String employeeId, String companyId) {
this.employeeId = employeeId;
this.companyId = companyId;
}
public String getEmployeeId() {
return employeeId;
}
public void setEmployeeId(String employeeId) {
this.employeeId = employeeId;
}
public String getCompanyId() {
return companyId;
}
public void setCompanyId(String companyId) {
this.companyId = companyId;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EmployeeIdentity that = (EmployeeIdentity) o;
if (!employeeId.equals(that.employeeId)) return false;
return companyId.equals(that.companyId);
}
#Override
public int hashCode() {
int result = employeeId.hashCode();
result = 31 * result + companyId.hashCode();
return result;
}
}
Here's my Employee model
#Entity
#Table(name = "employees")
public class Employee {
#EmbeddedId
private EmployeeIdentity id;
#NotNull
#Size(max = 60)
private String name;
#NaturalId
#NotNull
#Email
#Size(max = 60)
private String email;
#Size(max = 15)
#Column(name = "phone_number", unique = true)
private String phoneNumber;
public Employee() {}
public Employee(EmployeeIdentity id, String name, String email, String phoneNumber) {
this.id = id;
this.name = name;
this.email = email;
this.phoneNumber = phoneNumber;
}
public EmployeeIdentity getId() {
return id;
}
public void setId(EmployeeIdentity id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
And to have resource links generated properly using my embedded id instead of a qualified class name
#Component
public class EmployeeIdentityIdConverter implements BackendIdConverter {
#Override
public Serializable fromRequestId(String id, Class<?> aClass) {
String[] parts = id.split("_");
return new EmployeeIdentity(parts[0], parts[1]);
}
#Override
public String toRequestId(Serializable source, Class<?> aClass) {
EmployeeIdentity id = (EmployeeIdentity) source;
return String.format("%s_%s", id.getEmployeeId(), id.getCompanyId());
}
#Override
public boolean supports(Class<?> type) {
return Employee.class.equals(type);
}
}
And here's my repository code
#RepositoryRestResource(collectionResourceRel = "employees", path = "employees")
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, EmployeeIdentity> {
}
This works fine with GET requests but I need to be able to POST. The first thing I noticed that when I do a POST with the json
{
"id": {
"employeeId": "E-267",
"companyId": "D-432"
},
"name": "Spider Man",
"email": "spman#somedomain.com",
"phoneNumber": "+91-476253455"
}
This doesn't work. EmployeeIdentityIdConverter#fromRequestId throws a null pointer exception because the string parameter is null. So I added a null check and return default EmployeeIdentity when id is null. As described by this answer https://stackoverflow.com/a/41061029/4801462
Modified EmployeeIdentityIdConverter#fromRequestId
#Override
public Serializable fromRequestId(String id, Class<?> aClass) {
if (id == null) {
return new EmployeeIdentity();
}
String[] parts = id.split("_");
return new EmployeeIdentity(parts[0], parts[1]);
}
But this raised another problem. My implementations for hashCode and equals now through null pointer exceptions since the default constructor was used and the employeeId and companyId are null.
In an attempt to fix this, I gave default values to employeeId and companyId
**Modified Employee#Employee() constructor*
public Employee() {
this.employeeId = "";
this.companyId = "";
}
NOTE
I am not even sure of what I was doing above. I was just trying to fix the small problems as they occurred.
By the way if you guessed this didn't work then you're right. While I didn't get an error and the request was successful, I didn't get the behavior I expected. A new entry was created with empty employeeId and companyId.
How do make POST to REST API whose model uses #EmbeddedId with spring boot data rest?

Here is an other solution. (Still not perfect though.)
Expose the id for your Employee class:
#Configuration
protected class MyRepositoryRestConfigurer implements RepositoryRestConfigurer {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.exposeIdsFor(ThemeMessage.class);
}
}
Add the following line to your converter (during POST requests the id will be null):
#Override
public Serializable fromRequestId(String id, Class<?> aClass) {
if(id==null) {
return null;
}
String[] parts = id.split("_");
return new EmployeeIdentity(parts[0], parts[1]);
}
The following POST request then will work:
{
"id": {
"employeeId": "E-267",
"companyId": "D-432"
},
"name": "Spider Man",
"email": "spman#somedomain.com",
"phoneNumber": "+91-476253455"
}
However, the id field will be exposed in all of the responses. But maybe it's not a real problem, because when you use a composite id it usually means that the id is not only an abstract identifier, but its parts have meaningful content which should appear in the entity body.
Actually, I'm thinking of adding these lines to my own code too .... :)

I had a similar problem and I couldn't find a solution for creating new entities via the POST /entities endpoint.
However, you can also create a new entity via PUT /entities/{newId} endpoint. And the converter works fine for these endpoints.
I also completely denied the POST endpoint avoiding the 500 responses:
#PostMapping(value = "/themeMessages")
public ResponseEntity<Void> postThemeMessage() {
return new ResponseEntity<>(HttpStatus.METHOD_NOT_ALLOWED);
}

Related

Unexpected route name in spring

I have 3 controllers on my spring app. Each one of them have different name then I set up for example I have
#RestController
#RequestMapping("/operator")
public class OperativesController {
and when run it on localhost and check available routes this is my output
{
"_links" : {
"operativeses" : {
"href" : "http://localhost:8080/operativeses"
}
}
}
I would appreciate if someone would tell me that where this name is coming from?
PS. yesterday everything was all right.
Not sure if that is helpful for you but below you can find my properties
logging.level.org.hibernate=INFO
spring.jpa.show-sql=true
hibernate.dialect = org.hibernate.dialect.SQLServer2016Dialect
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.hibernate.ddl-auto=none
EDIT
I just debug this spring app and find out that, my path are created from "Entities". So I have entity names Operatives and spring picking up it as URL, so it automatically creating path like operativeses (why? I do not know) because I change name of this entity to 'Operativesasd' and in the result I have path 'Operativesasds' (notice s on the end)
So my question now is, Why my spring app creating path base on entities even if I have controllers?
my entity looks like
#Entity
#Table(name="operatives", schema = "lm")
public class Operatives implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name", length=150)
private String name;
#Column(name = "level", length=7)
private String level;
#Column(name="is_qa", length=50)
private String isQa;
#Column(name="is_active", length=5)
private String isActive;
#Column(name="is_admin", length=50)
private String isAdmin;
public Operatives() {
super();
}
public int getId() {
return id;
}
public void setId(int aId) {
id = aId;
}
public String getName() {
return name;
}
public void setName(String aName) {
name = aName;
}
public String getLevel() {
return level;
}
public void setLevel(String aLevel) {
level = aLevel;
}
public String getIsQa() {
return isQa;
}
public void setIsQa(String aIsQa) {
isQa = aIsQa;
}
public String getIsActive() {
return isActive;
}
public void setIsActive(String aIsActive) {
isActive = aIsActive;
}
public String getIsAdmin() {
return isAdmin;
}
public void setIsAdmin(String aIsAdmin) {
isAdmin = aIsAdmin;
}
}

Spring #OneToMany problems

Doing a project with parcel service. I created OrderItem API and Dispatcher API. Now, I want to connect then by relations. The idea is: dispatcher can have many orderItems. OrderItem can only have one dispatcher. If you delete dispatcher, his order items also has to go out.
I have already created a little bit, but I'm so messed up here and can't finish this thing logically. Would someone give me some ideas on how I should attack this problem.
Do I need to put relations both sides or only to one of them?
When do I need to create constructors with arguments? Because in entity class you have to have no arg constructors...?
OrderItem class:
#Entity
public class OrderItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotBlank(message = "Order weight is required")
private String weight;
#NotBlank(message = "Order dimensions are required")
private String dimensions;
#NotBlank(message = "Order origin is required")
private String origin;
#NotBlank(message = "Order destination is required")
private String destination;
#NotNull(message = "Order comment cannot be null")
private String comment;
#ManyToOne
private Dispatcher dispatcher;
public OrderItem() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getWeight() {
return weight;
}
public void setWeight(String weight) {
this.weight = weight;
}
public String getDimensions() {
return dimensions;
}
public void setDimensions(String dimensions) {
this.dimensions = dimensions;
}
public String getOrigin() {
return origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
public String getDestination() {
return destination;
}
public void setDestination(String destination) {
this.destination = destination;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public Dispatcher getDispatcher() {
return dispatcher;
}
public void setDispatcher(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
}
OrderController class:
#RestController
#RequestMapping("/order")
public class OrderController {
#Autowired
OrderService service;
#Autowired
private MapValidationErrorService mapValidationErrorService;
#GetMapping("/{dispatcherId}/orders")
public List<OrderItem> getAllOrderItems(#PathVariable int dispatcherId) {
return service.getAllOrderItems(dispatcherId);
}
#PostMapping("/{dispatcherId}/orders")
public ResponseEntity<?> saveOrder(#Valid #RequestBody OrderItem orderItem, #PathVariable int dispatcherId, BindingResult result) {
ResponseEntity<?> errorMap = mapValidationErrorService.MapValidationService(result);
if (errorMap != null) {
return errorMap;
}
orderItem.setDispatcher(new Dispatcher(dispatcherId, "", "", ""));
service.insertOrUpdate(orderItem);
return new ResponseEntity<String>("Order was created successfully", HttpStatus.CREATED);
}
#PutMapping("/update")
public ResponseEntity<?> updateOrder(#Valid #RequestBody OrderItem orderItem, BindingResult result) {
ResponseEntity<?> errorMap = mapValidationErrorService.MapValidationService(result);
if (errorMap != null) {
return errorMap;
}
service.insertOrUpdate(orderItem);
return new ResponseEntity<String>("Order was updated successfully", HttpStatus.OK);
}
#GetMapping("/all")
public Iterable<OrderItem> getAllOrders() {
return service.findAllOrders();
}
#DeleteMapping("/{orderId}")
public ResponseEntity<String> deleteOrder(#PathVariable int orderId) {
if (service.findById(orderId) == null) {
throw new CustomErrorException("Order doesn't exist, check order id");
}
service.deleteOrder(orderId);
return new ResponseEntity<String>("Order with ID " + orderId + " was deleted", HttpStatus.OK);
}
#GetMapping("/{orderId}")
public ResponseEntity<OrderItem> getOrderById(#PathVariable int orderId) {
OrderItem item = service.findById(orderId);
if (service.findById(orderId) == null) {
throw new CustomErrorException("Order id not found - " + orderId);
}
return new ResponseEntity<OrderItem>(item, HttpStatus.OK);
}
}
Dispatcher class:
#Entity
public class Dispatcher {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotBlank(message = "Dispatcher first name is required")
private String firstName;
#NotBlank(message = "Dispatcher last name is required")
private String lastName;
#NotBlank(message = "Dispatcher email name is required")
private String email;
#NotBlank(message = "Dispatcher email is required")
private String password;
#NotBlank(message = "Dispatcher phone number is required")
private String phoneNumber;
public Dispatcher() {
}
public Dispatcher(int id, String firstName, String lastName, String email) {
super();
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
DispatcherController class:
#RestController
#RequestMapping("/dispatcher")
public class DispatcherController {
#Autowired
DispatcherService service;
#Autowired
private MapValidationErrorService mapValidationErrorService;
#PostMapping("/save")
public ResponseEntity<?> saveDispatcher(#Valid #RequestBody Dispatcher dispatcher, BindingResult result) {
ResponseEntity<?> errorMap = mapValidationErrorService.MapValidationService(result);
if (errorMap != null) {
return errorMap;
}
service.insertOrUpdate(dispatcher);
return new ResponseEntity<String>("Dispatcher was created successfully", HttpStatus.CREATED);
}
#GetMapping("/all")
public Iterable<Dispatcher> getAllDispatchers() {
return service.findAllDispatchers();
}
#GetMapping("/{dispatcherId}")
public ResponseEntity<?> getDispatcherById(#PathVariable int dispatcherId) {
Dispatcher dispatcher = service.findById(dispatcherId);
if (service.findById(dispatcherId) == null) {
throw new CustomErrorException("Dispatcher id not found - " + dispatcherId);
}
return new ResponseEntity<Dispatcher>(dispatcher, HttpStatus.OK);
}
#DeleteMapping("/{dispatcherId}")
public ResponseEntity<?> deleteDispatcher(#PathVariable int dispatcherId) {
if (service.findById(dispatcherId) == null) {
throw new CustomErrorException("Dispatcher doesn't exist, check dispatcher id");
}
service.deleteDispatcher(dispatcherId);
return new ResponseEntity<String>("Order with ID " + dispatcherId + " was deleted", HttpStatus.OK);
}
#PutMapping("/update")
public ResponseEntity<?> updateDispatcher(#Valid #RequestBody Dispatcher dispatcher, BindingResult result) {
ResponseEntity<?> errorMap = mapValidationErrorService.MapValidationService(result);
if (errorMap != null) {
return errorMap;
}
service.insertOrUpdate(dispatcher);
return new ResponseEntity<String>("Dispatcher was updated successfully", HttpStatus.OK);
}
}
I think you have defined the relationship incorrectly. And yes you need to have no-args constructor. This helps hibernate to map the values from database to java objects when retrieving data from the database
Assuming you are going for a uni-directional mapping,
#Entity
public class OrderItem {
#ManyToOne( cascade = CascadeType.ALL )
#JoinColumn(name = <foriegn_key_column in orderItem table i.e. id>)
private Dispatcher dispatcher;
}
#Entity
public class Dispatcher {
private List<OrderItem > orders;
}
The main difference is that bidirectional relationship gives you access in both directions. so that you can access the other side without any queries. It works for cascade actions too.
The bidirectional #OneToMany generates better DML because the #ManyToOne owns the relationship.
Unidirectional #ManyToOneor bidirectional #OneToMany are more efficient than unidirectional #OneToMany.
Before JPA 2.0 this unidirection #OneToMany used a join table to manage the association between parent and child rows. So higher cost in read (3 tables join) and write (3 tables insertion).
Since JPA 2.0 for unidirectional #OneToMany you should use it in correlation with #JoinColumn
With the #JoinColumn the #OneToMany association controls the child table FK.. and so no need for extra junction table.
But performance wise there is no better than bidirectional associations.
Pros of unidirectional #OneToMany -> simplicity.
For your second question : NoArg is required only by the persistence framework (Hibernate for e.g). But you can (and should) use your own constructors to create consistent objects.

Spring Boot - Hierarchical entities data is not returning from a rest API

I have an API method implemented in spring boot for Courses. It fetches the course by topic Id. The Course class is implemented as:
#Entity
public class Course {
#Id
private String id;
private String name;
private String description;
#ManyToOne
private Topic topic;
public Course() {
}
public Course(String id, String name, String description, String topicId) {
super();
this.id = id;
this.name = name;
this.description = description;
this.topic = new Topic(topicId, "", "");
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public void setTopic(Topic t) {
this.topic = t;
}
}
And the API method is implemented as:
#RequestMapping(method=RequestMethod.GET, value="/topics/{topicId}/courses")
public RestMessage getAllCourses(#PathVariable String topicId) {
try {
List<Course> course = courseService.getAllCourses(topicId);
message = new RestMessage(course,StatusCodeEnum.OK);
return message;
} catch (Exception e) {
message = new RestMessage(e.getMessage(),StatusCodeEnum.INTERNAL_SERVER_ERROR);
message.setException(e);
return message;
}
}
The method implementation is simple, it tries to get all the courses based on the topic id and return it as a RestMessage Object. I'm using postman for the testing and in the response I am getting the list of Course but the Topic entity data is discarded.
The api response is as:
{
"data": [
{
"id": "java-streams",
"name": "Java Streams",
"description": "Java Stream learning"
}
],
"httpStatus": "OK",
"statusCode": 200,
"exception": null
}
And the RestMessage Class is defined as:
public class RestMessage {
private Object data;
private StatusCodeEnum httpStatus;
private int statusCode;
private Exception ex;
public RestMessage() {
}
public RestMessage(Object d, StatusCodeEnum c) {
data = d;
httpStatus = c;
statusCode = c.val();
}
public void setData(Object d) {
data =d;
}
public void setHttpStatus(StatusCodeEnum c) {
httpStatus = c;
}
public void setStatusCode(int c) {
statusCode = c;
}
public void setException(Exception e) {
ex = e;
}
public void setStatusCode(StatusCodeEnum c) {
httpStatus = c;
}
public Object getData() {
return data;
}
public StatusCodeEnum getHttpStatus() {
return httpStatus;
}
public Exception getException() {
return ex;
}
public int getStatusCode() {
return statusCode;
}
}
However, I have tried to debug the API endpoint and before returning the RestMessage object I have data in the required shape but after getting the json response the Topic object is truncated for all the courses.
The debug data image is attached:
I wonder what I am doing wrong in this case?
The field topic from Course doesn't have a getter, that's why is ignored by JSON serializer.
You can use Lombok annotation to automatically generate getters and setters.
Just add #Dataon class definition and you will have generated getters and setters, constructor without parameters. You will have everything for what you can said that is POJO class.
Check this link: Project lombok

Dropwizard/Jersey is giving "Unable to process JSON" message on GET request

I'm trying to make my first simple project with Dropwizard. I have a MySQL-database, and the idea is to get the data (companies) from there and represent it as JSON. I have followed the Getting started page by Dropwizard and this tutorial to get connected to database with Hibernate.
The idea is that URL "/companies" serves all the companies as JSON, and it is working fine.
URL "/companies/{id}" is supposed to give a single company with given id, but every request gives code 400 and message "Unable to process JSON". The details field in the response says
"No serializer found for class
jersey.repackaged.com.google.common.base.Present and no properties
discovered to create BeanSerializer (to avoid exception, disable
SerializationFeature.FAIL_ON_EMPTY_BEANS) )"
If I give an id of company that does not exist in database, the class in mentioned message changes to
jersey.repackaged.com.google.common.base.Absent
The company class is here:
public class Company {
#ApiModelProperty(required = true)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "name")
private String name;
#Column(name = "address")
private String address;
#Column(name = "zipcode")
private String zipCode;
#Column(name = "email")
private String eMail;
#Column(name = "mobile")
private String mobile;
public Company() {
}
public Company (String name, String address, String zipCode, String eMail, String mobile) {
this.name = name;
this.address = address;
this.zipCode = zipCode;
this.eMail = eMail;
this.mobile = mobile;
}
#JsonProperty
public long getId() {
return id;
}
#JsonProperty
public void setId(long id) {
this.id = id;
}
#JsonProperty
public String getName() {
return name;
}
#JsonProperty
public void setName(String name) {
this.name = name;
}
#JsonProperty
public String getAddress() {
return address;
}
#JsonProperty
public void setAddress(String address) {
this.address = address;
}
#JsonProperty
public String getZipCode() {
return zipCode;
}
#JsonProperty
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
#JsonProperty
public String geteMail() {
return eMail;
}
#JsonProperty
public void seteMail(String eMail) {
this.eMail = eMail;
}
#JsonProperty
public String getMobile() {
return mobile;
}
#JsonProperty
public void setMobile(String mobile) {
this.mobile = mobile;
}
}
DAO is here:
public class CompanyDAO extends AbstractDAO<Company> {
public CompanyDAO(SessionFactory sessionFactory) {
super(sessionFactory);
}
public List<Company> findAll() {
return list(namedQuery("com.webapp.project.core.Company.findAll"));
}
public Optional<Company> findById(long id) {
return Optional.fromNullable(get(id));
}
}
Application class:
public class HelloWorldApplication extends Application<HelloWorldConfiguration> {
public static void main(String[] args) throws Exception {
new HelloWorldApplication().run(args);
}
#Override
public String getName() {
return "hello-world";
}
/**
* Hibernate bundle.
*/
private final HibernateBundle<HelloWorldConfiguration> hibernateBundle
= new HibernateBundle<HelloWorldConfiguration>(
Company.class
) {
#Override
public DataSourceFactory getDataSourceFactory(
HelloWorldConfiguration configuration
) {
return configuration.getDataSourceFactory();
}
};
#Override
public void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) {
bootstrap.addBundle(new SwaggerBundle<HelloWorldConfiguration>() {
#Override
protected SwaggerBundleConfiguration getSwaggerBundleConfiguration(HelloWorldConfiguration sampleConfiguration) {
return sampleConfiguration.getSwaggerBundleConfiguration();
}
});
bootstrap.addBundle(hibernateBundle);
}
#Override
public void run(HelloWorldConfiguration configuration,
Environment environment) {
final CompanyDAO companyDAO = new CompanyDAO(hibernateBundle.getSessionFactory());
environment.jersey().register(new CompaniesResource(companyDAO));
environment.jersey().register(new JsonProcessingExceptionMapper(true));
}
}
Configuration class:
public class HelloWorldConfiguration extends Configuration {
#Valid
#NotNull
private DataSourceFactory database = new DataSourceFactory();
#NotNull
private SwaggerBundleConfiguration swaggerBundleConfiguration;
#JsonProperty("swagger")
public void setSwaggerBundleConfiguration (SwaggerBundleConfiguration conf) {
this.swaggerBundleConfiguration = conf;
}
#JsonProperty("swagger")
public SwaggerBundleConfiguration getSwaggerBundleConfiguration () {
return swaggerBundleConfiguration;
}
#JsonProperty("database")
public void setDataSourceFactory(DataSourceFactory factory) {
this.database = factory;
}
#JsonProperty("database")
public DataSourceFactory getDataSourceFactory() {
return database;
}
}
Resource class:
#Path("/companies")
#Api("Companies")
#Produces(MediaType.APPLICATION_JSON)
public class CompaniesResource {
private CompanyDAO companyDAO;
public CompaniesResource(CompanyDAO companyDAO) {
this.companyDAO = companyDAO;
}
#GET
#ApiOperation(
value = "Gives list of all companies",
response = Company.class,
code = HttpServletResponse.SC_OK
)
#UnitOfWork
public List<Company> findAll () {
return companyDAO.findAll();
}
#GET
#Path("/{id}")
#UnitOfWork
public Optional<Company> getById(#PathParam("id") LongParam id) {
return companyDAO.findById(id.get());
}
}
I would be happy for any responses!
I was getting the error Unable to Process JSON.
Troubleshooted more than 4 hours until I found the problem.
The error is caused because of Enum getter.
If you are using Enum fields/getters/setters in your POJO, Jackson will fail to map your JSON to Java Object, and it will crash, leading to the mentioned error.
Looks like your json marshaller is not able to marshall google's Optional class. Try to return Company from the controller, and not Optional:
#GET
#Path("/{id}")
#UnitOfWork
public Company getById(#PathParam("id") LongParam id) {
return companyDAO.findById(id.get()).get();
}

DTOs and entities which implements the same interface

I have the next maven projects:
project-model : I have JPA entities
project-rest : Spring data, spring rest based on spring boot
project-client : Jersey clients to consume the rest services
project-web : Only jsf web application
project-desktop : Java Fx desktop application
project-android : Mobile application which consumes my Rest web services.
I'm planing to remove the JPA entities from the project-model and place there only DTO's pojos and interfaces and place my JPA entities in the rest project in order to remove the jpa dependencies from the project-model. This is because I don't want to have JPA dependencies in the project-android, project-web and project-desktop.
I was thinking to follow the next schema:
#JsonSerialize(as=CountryDto.class)
#JsonDeserialize(as=CountryDto.class)
public interface ICountry extends Serializable
{}
#Entity
#Table(name = "COUNTRY")
#JsonSerialize(as=Country.class)
#JsonDeserialize(as=Country.class)
public class Country implements ICountry
{}
public class CountryDto implements ICountry
{}
And if I need to convert from Entities to DTO's use mapstruct or Selma.
But I'm not sure if this is the best practice because I have problems in my code like the next:
#JsonSerialize(as=CityDto.class)
#JsonDeserialize(as=CityDto.class)
public interface ICity extends Serializable
{
public Integer getIdCity();
public void setIdCity(Integer idCity);
public String getName();
public void setName(String name);
public ICountry getCountryId();
public void setCountryId(ICountry countryId);
}
public class CityDto implements ICity
{
private static final long serialVersionUID = -6960160473351421716L;
private Integer idCity;
private String name;
private CountryDto countryId;
public CityDto()
{
// TODO Auto-generated constructor stub
}
public CityDto(Integer idCity, String name, CountryDto countryId)
{
super();
this.idCity = idCity;
this.name = name;
this.countryId = countryId;
}
public CityDto(Integer idCity, String name)
{
super();
this.idCity = idCity;
this.name = name;
}
#Override
public Integer getIdCity()
{
return idCity;
}
#Override
public void setIdCity(Integer idCity)
{
this.idCity = idCity;
}
#Override
public String getName()
{
return name;
}
#Override
public void setName(String name)
{
this.name = name;
}
#Override
public ICountry getCountryId()
{
return countryId;
}
#Override
public void setCountryId(ICountry countryId)
{
this.countryId = (CountryDto) countryId;
}
}
#Entity
#Table(name = "CITY")
#JsonSerialize(as=City.class)
#JsonDeserialize(as=City.class)
public class City implements ICity
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID_CITY")
private Integer idCity;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 100)
#Column(name = "NAME")
private String name;
#JoinColumn(name = "COUNTRY_ID", referencedColumnName = "ID_COUNTRY")
#ManyToOne(optional = false)
private Country countryId;
private static final long serialVersionUID = 1L;
public City()
{
}
public City(Integer idCity)
{
this.idCity = idCity;
}
public City(Integer idCity, String name)
{
this.idCity = idCity;
this.name = name;
}
#Override
public Integer getIdCity()
{
return idCity;
}
#Override
public void setIdCity(Integer idCity)
{
this.idCity = idCity;
}
#Override
public String getName()
{
return name;
}
#Override
public void setName(String name)
{
this.name = name;
}
#Override
public ICountry getCountryId()
{
return countryId;
}
#Override
public void setCountryId(ICountry countryId)
{
this.countryId = (Country) countryId;
}
#Override
public int hashCode()
{
int hash = 0;
hash += (idCity != null ? idCity.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object)
{
// TODO: Warning - this method won't work in the case the id fields are
// not set
if (!(object instanceof City))
{
return false;
}
City other = (City) object;
if ((this.idCity == null && other.idCity != null) || (this.idCity != null && !this.idCity.equals(other.idCity)))
{
return false;
}
return true;
}
#Override
public String toString()
{
return "com.neology.ebreeder.model.entities.City[ idCity=" + idCity + " ]";
}
}
And as You can see in the entity I have getters and setters using the shared interface, and I think that It could provoke problems, I thought to override the getters using the entity but I can't override the setters.
I cant do this:
#Override
public Country getCountryId()
{
return countryId;
}
But I can't do this :
#Override
public void setCountryId(Country countryId)
{
this.countryId = (Country) countryId;
}
Do you see a better solution or could you give me your point of view :)
thanks
Based on past experience, I do not think it is a good idea to use an interface that is shared between the DTO model and the JPA model.
You are in essence tightly coupling your DTO model to your JPA model with this approach.
I would rather have them loosely coupled and use a separate framework to copy between these two models. This will need to be powered by a meta model (could be derived from JPA) to walk and copy the data from one model to another based on the getters and setters.

Categories