Spring class RequestBody and MongoRepository - java

I have class:
class TestClass {
#Id
private ObjectId id;
private ObjectId parentId;
private String name;
private String describe;
private String privateData;
public TestClass(ObjectId parentId, String name, String describe, String privateData) {
this.parrentId = parrentId;
this.name = name;
this.describe = describe;
this.privateDate = privateData;
}
// get/set methods...
}
Can I use this class in MongoRepository and #RequestBody? Is it safe? parrentId and privateData is private properties and RequestBody does not have to fill them.
mongorepository:
public interface TestClassRepository extends MongoRepository<TestClass, String> {
public TestClass findById(ObjectId id);
}
post method:
#RequestMapping(value="/testclass", method=RequestMethod.POST)
public void create(#RequestBody TestClass testClass) {
testClass.setParentId(...);
repo.insert(testClass);
}
For example:
{"name": "test", "describe": "test", "id": "54d5261a8314fe3c650d5b1d", "parentId": "54d5261a8314fe3c650d5b1d", "privateData": "WrongPrivateData"}
How can I do that it was impossible to set properties id, parentId, privateDate?
Or need I create new class for RequestBody? I don't want duplicate code.

It should be better and safe to use separate models for DAO and VO layers(view). If your models currently looks the same, it doesn't mean that they will stay the same in future. You can use the Dozer Mapping framework for mappings between your models. It's easy,fast and safe.
If you need to skip some field from mongotemplate mapping use #Transient annotation.
P.S. You don't need findById method, because mongotemplate already have find method which uses key as param. TestClass should have an empty constructor.

Related

JPA #Convert annotation on a method

The JPA #Convert annotation says it's applicable to a method (as well as field and type).
What is an example of a situation where it is useful?
#Convert allows us to map JDBC types to Java classes.
Let's consider the code block below:-
public class UserName implements Serializable {
private String name;
private String surname;
// getters and setters
}
#Entity(name = "UserTable")
public class User {
private UserName userName;
//...
}
Now we need to create a converter that transforms the PersonName attribute to a database column and vice-versa.
Now we can annotate our converter class with #Converter and implement the AttributeConverter interface. Parametrize the interface with the types of the class and the database column, in that order:
#Converter
public class UserNameConverter implements
AttributeConverter<UserName, String> {
private static final String SEPARATOR = ", ";
#Override
public String convertToDatabaseColumn(UserName userName) {
if (userName == null) {
return null;
}
....
}
}
In order to use the converter, we need to add the #Convert annotation to the attribute and specify the converter class we want to use:
#Entity(name = "PersonTable")
public class User {
#Convert(converter = UserNameConverter.class)
private UserName userName;
// ...
}
For more details you can refer below:- jpa-convert

What's the best way to add a new Object using the HTTP POST call through Spring?

I'm learning the Spring Framework and I'm struggling with the Rest services with spring, in particular for the POST call that it's supposed to add a new object to the database.
I've seen a lot of different implementations through the web, but I don't know how to pick the best.
Let's take for example a film class:
#Entity
public class Film {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String title;
private String description;
//Constructor, Getter and Setter Omitted.
}
Assuming the repository extends the JpaRepository<Film,Long>, this would be the Controller class:
#RestController
public class FilmController {
#Autowired
FilmRepository filmRepository;
//Implementation #1
#PostMapping("/film")
public Film addNew(#RequestBody Map<String,String> body){
String title = body.get("title");
String description = body.get("description");
return filmRepository.save(new Film(title,description));
}
//Implementation #2
#PostMapping("/film")
public Film addNew(String title, String description){
Film film = new Film(title,description);
System.out.println(film.getTitle() + " " + film.getDescription());
return filmRepository.save(film);
}
//Implementation #3
#PostMapping("/film")
public Film addNew(#RequestBody Film newFilm){
return filmRepository.save(newFilm);
}
}
Why some implementations have as parameter a Map<String, String> ? Is that a body mapped to a key/value pair ?
Also bear in mind that I managed to implement correctly just the implementation #2, the first and the third gave me a
415 error:"Unsupported Media Type" org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;boundary=--------------------------901298977805450214809889;charset=UTF-8' not supported]
(Despite I followed the official Spring tutorial) on REST services.
I also read something about the creation of DTO classes where I can define attributes without exposing the object to the controller, how can be implemented such solution?
Implementation 3 is the best practice, but you should create a lightweight DTO class (maybe FilmDto) to avoid exposing the internal structure of your entity, please see LocalDTO, Martin Fowler.
You may use ModelMapper to map FilmDto to Film, and make sure there are proper getters and setters in both classes, if the getters and setters have the same names in both classes, then ModelMapper will do the conversion smoothly:
public class FilmDto {
private long id;
private String title;
private String description;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
and you controller:
#RestController
#RequestMapping("/api")
public class FilmController {
private final FilmRepository filmRepository;
private ModelMapper modelMapper = new ModelMapper();
#Autowired
public FilmController(FilmRepository filmRepository) {
this.filmRepository = filmRepository;
}
//Implementation #3
#PostMapping("/film")
public ResponseEntity<FilmDto> addNew(#RequestBody FilmDto filmDto){
Film newFilm = modelMapper.map(filmDto, Film.class);
newFilm = filmRepository.save(newFilm);
filmDto.setId(newFilm.getId());//you may use modelMapper here
return ResponseEntity.ok(filmDto);
}
}
you can test using postman by passing the film as below:
{
"title": "some title",
"description": "some description"
}
and the body should be of type "raw", "JSON".
Why some implementations have as parameter a Map<String, String> ?
some implementations use map<key,value> because they need the properties that map interface provide such as non-duplicate key value or the classes that implement map interface such as TreeMap and LinkedHashMap.
about your implementation of the class FilmController i think its not necessary to use map<String,String> for posting your domain in the data base simply you can have this implementation
#RestController
public class FilmController {
#Autowired
FilmRepository filmRepository;
#PostMapping("/film")
public ResponseEntity addNew(#RequestBody Film film){
return ResponseEntity.ok(filmRepository.save(film));

Ignore json POJO fields not mapped in object in spring boot

I have pojo which has many fields. I have set value some field. But when i create json, whole pojo create a json . here is the code i have used:
Pojo
public class BasicInfoModel {
private String client_id="";
private String father_name="";
private String mother_name="";
private String gendar="";
//Getter and setter
}
Repository code
public BasicInfoModel getBasicInfo() {
BasicInfoModel lm = new BasicInfoModel();
lm.setFather_name("Enamul Haque");
return lm;
}
Controller code
#RequestMapping(value = "/get_donar_basic_info", method = RequestMethod.POST, produces ="application/json")
public #ResponseBody BasicInfoModel getBasicinfo(){
return repository.getBasicInfo();
}
But my json is resposne like bellow:
{
"client_id": "",
"father_name": "Enamul Haque",
"mother_name": "",
"gendar": ""
}
I have set value to father_name but i have seen that i have found all the value of pojo fields. I want get only set value of father_name and ignor other value which is not set in repository.
My json look like bellow: I will display only father_name.how to display bellow like json from above code?
{
"father_name": "Enamul Haque"
}
Please help me..
Json include non null values to ignore null fields when serializing a java class
#JsonInclude(Include.NON_NULL)
Jackson allows controlling this behavior at either the class level:
#JsonInclude(Include.NON_NULL)
public class BasicInfoModel { ... }
at the field level:
public class BasicInfoModel {
private String client_id="";
#JsonInclude(Include.NON_NULL)
private String father_name="";
private String mother_name="";
private String gendar="";
//Getter and setter
}
from jackson 2.0 use here use
#JsonInclude(JsonInclude.Include.NON_NULL)
You can also ignore the empty values
#JsonInclude(JsonInclude.Include.NON_EMPTY)
Add the #JsonIgnoreProperties("fieldname") annotation to your POJO.
Or you can use #JsonIgnore before the name of the field you want to ignore while deserializing JSON. Example:
#JsonIgnore
#JsonProperty(value = "client_id")
#RequestMapping(value = "/get_donar_basic_info", method = RequestMethod.POST, produces ="application/json")
public #ResponseBody BasicInfoModel getBasicinfo(){
return repository.getBasicInfo();
}
You can ignore field at class level by using #JsonIgnoreProperties annotation and specifying the fields by name:
#JsonIgnoreProperties(value = { "client_id" })
public class BasicInfoModel {
private String client_id="";
private String father_name="";
private String mother_name="";
private String gendar="";
//Getter and setter
}
Or you can use #JsonIgnore annotation directly on the field.
public class BasicInfoModel {
#JsonIgnore
private String client_id="";
private String father_name="";
private String mother_name="";
private String gendar="";
//Getter and setter
}
You can read here more about this.

PUT request with JSON payload sent from Postman, nested object not parsed

I didn't have this problem before, with other POJOs, I'm not sure what's different this time, but I can't get this working and I could not find an exact solution for this.
I have this POJO called Component (with some Hibernate annotations):
#Entity
#Table(name="component", uniqueConstraints={#UniqueConstraint(
columnNames = {"name", "component_type"})})
public class Component {
#Column(name="id")
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#Column(name="name")
private String name;
#Column(name="component_type")
private String componentType;
#Column(name="serial_number")
private int serialNumber;
#Column(name="active_since")
private String activeSince;
#Embedded
private ComponentWearoutModel wearout;
public Component() {
}
public Component(String name, String componentType, int serialNumber, String activeSince,
ComponentWearoutModel wearout) {
this.name = name;
this.componentType = componentType;
this.serialNumber = serialNumber;
this.activeSince = activeSince;
this.wearout = wearout;
}
public ComponentWearoutModel getModel() {
return wearout;
}
public void setModel(ComponentWearoutModel wearout) {
this.wearout = wearout;
}
//more getters and setters
}
ComponentWearoutModel:
#Embeddable
public class ComponentWearoutModel {
private String componentType; //dont mind the stupid duplicate attribute
private Integer componentLifeExpectancy;
private Float componentWearOutLevel;
private Float actionThreshold;
public ComponentWearoutModel() {
}
public ComponentWearoutModel(String componentType, int componentLifeExpectancy, float componentWearOutLevel,
float actionThreshold) {
this.componentType = componentType;
this.componentLifeExpectancy = componentLifeExpectancy;
this.componentWearOutLevel = componentWearOutLevel;
this.actionThreshold = actionThreshold;
}
//getters and setters
}
The sample payload I use:
{
"name": "component name",
"componentType": "airfilter2",
"serialNumber": 573224,
"activeSince": "2016-04-10 17:38:41",
"wearout":
{
"componentType": "airfilter",
"componentLifeExpectancy": 1000,
"componentWearOutLevel": 0.24,
"actionThreshold": 0.2
}
}
And finally the resource class:
#Path("myresource")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
public class MyResource {
DatabaseManager dm = DatabaseManager.getInstance();
#PUT
#Path("Component")
public Response storeComponent(Component component){
System.out.println("reached");
System.out.println(component.getComponentType()); //okay
System.out.println(component.getModel().getComponentType()); //nullpointerexception
ComponentWearoutModel model = new ComponentWearoutModel("type", 1000, 1f, 0.2f);
component.setModel(model); //this way it's saved in the db just fine
dm.save(component);
return Response.status(Status.OK).entity(component).build();
}
}
Without the prints, only the fields which are not part of the ComponentWearoutModel class are stored in the database table, the other columns are null. So when I try to print one of them, I get an exception, I just dont understand why. If I create a ComponentWearoutModel in the resource method and add it to the component, everything is fine in the database.
UPDATE:
so my mistake was that I named the ComponentWearoutModel attribute as "wearout" in the Component.class, but the autogenerated getters and setter were called getModel/setModel and moxy could not parse my payload because of this. Solution: change the attribute name to "model" in Component class and in payload too.
Please ensure that the attribute names you are using in the POJO are same as what are being sent in the json string.
Since there are no jackson etc annotations being used in your POJO to tell it the corresponding json mapping, the underlying code will directly use the names given in json string. If you are using the string "model", the convertor code will look for a "setModel" method in your POJO.
In the above example, either call everything "model", or "wearable".

Return List type in JPA

I have DTO structure like :
public class ADto{
private String name;
private String id;
private List<BDto> bdtos;
//Created constructor using fields
}
public class BDto{
private String id;
private String code;
private List<CDto> cdtos;
//Created constructor using fields
}
public class CDto{
private String mKey;
private String mVal;
//Created constructor using fields
}
Used Spring MVC for fetching the data.
Below query is working perfectly fine and binding the data :
#org.springframework.data.jpa.repository.Query("select new pkg.ADto(id,name) from AEntity a where a.id=?1")
public ADto getAData(Long id);
How can I fetch the data for the list which is in turn composed of further list using the above method?
If you want to return DTOs instead on enitites, you need to provide mapping between DTOs and entities. With JPQL query, the only option is to provide that mapping in constructor of the resulting object. Therefore, you need to add a constructor to ADto, which accepts BEntities, and map all nested entities to dtos in that constructor. Or in more object oriented way, the new constructor will accept AEntity as the only argument.
This is how it could look like:
getAData() method in the repository (JPQL is slightly modified by adding a.bEntities to result):
#org.springframework.data.jpa.repository.Query("select new pkg.ADto(id,name, a.bEntities) from AEntity a where a.id=?1")
public ADto getAData(Long id);
New constructor in ADto:
public class ADto{
private String name;
private String id;
private List<BDto> bdtos;
public ADto(String id, String name, List<BEntity> bEntities) {
this.id = id; this.name = name;
this.bdtos = new ArrayList<>();
for (BEntity b : bEntities) {
BDto bdto = new BDto(b.id, b.code, b.cEntities);
/* you need to pass cEntities and map them again in the BDto
* constructor, or you may do the apping in ADto constructor
* and only pass mapped values to BDto constructor */
}
}
}
You have to enable eager fetch:
#OneToMany(mappedBy = "adto", fetch = FetchType.EAGER)
private List<BDto> bdtos;
Then you can fetch it like this i.e.:
ADto findById(Long id); // literally!

Categories

Resources