i am using java, spring mvc and mybatis.
for http patch, it is used to update partial resource, while put updates the entirely resource.
my code looks like
#RestController
#RequestMapping("/test")
public class Test {
#PutMapping
public void update(MyBean myBean) {
//update MyBean
}
mybatis code is:
<update id="updateMyBean">
update My_Bean
<set>
<if test="filed1 != null>field1 = #{field1},</if>
<if test="filed2 != null>field1 = #{field2},</if>
<if test="filed3 != null>field1 = #{field3},</if>
</set>
where id = #{id}
</update>
then how to implement patch in the spring mvc? how to implement patch in mybatis?
is it add another update method like following?
#PutMapping
public void update(MyBean myBean) {
//update MyBean
}
#PatchMapping
public void updateBeanPartial(MyBean myBean) {
//update MyBean
}
//they look like the same just annotations and/or method name are different
or
#PatchMapping
public void updateBeanPartial(Map myBeanMap) {
//update MyBean
}
//use Map as parameters, but in this case, we cannot do bean validation easily and cannot show what fields need to be sent in swagger
//or use specified fields of MyBean as parameter, but it will introduce many controller methods because MyBean can have many fields
and they use same mybatis update statement?
Thus, how to implement put and patch in the code?
or their difference only in the semantic not in the code?
Lets clarify some things first:
Update: If Bean (MyBean) has multiple fields, then you create 1 SQL statement to update all fields once in your batis.
Patch: If Bean (MyBean) has multiple fields, then you create 1 SQL Statement to patch (updating only some fields).
In your Batis mapper XML, would you need to define 2 functions:
Update User ,
Patch User ,
What you define in your batis mapper is your DAO statements.
What you define your Spring MVC is your Controller/Services, and what you want to achieve.
If you want 2 endpoints (PUT, PATCH), and you want them to do different things, then you need 2 different statements in batis.
Take note of <mapper namespace="org.com.BatisUserService"> which is what you will use to later on reference the implementations from your other classes such as #Controller, or #Service in your Spring MVC app.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.com.BatisUserService"> <---- Take note of this
<update id="updateUser"
parameterType="org.com.model.User">
UPDATE user SET
user_name=#{userName,jdbcType=VARCHAR},
user_password=#{userPassword,jdbcType=VARCHAR},
user_email=#{userEmail,jdbcType=VARCHAR}
WHERE
user_id=#{userId,jdbcType=INTEGER};
</update>
<update id="patchUser"
parameterType="org.com.model.User">
UPDATE user SET
user_name=#{userName,jdbcType=VARCHAR}
user_password=#{userPassword,jdbcType=VARCHAR},
WHERE
user_id=#{userId,jdbcType=INTEGER};
</update>
</mapper>
Then in your Controller, you would then call your Mapper org.com.BatisUserService which has the functions updateUser and patchUser defined:
#Controller
#RequestMapping(value = "/user")
public class UserController {
#Autowired
private BatisUserService batisUserService;
#RequestMapping(value = "/updateUser", method = RequestMethod.POST)
public User updateUser(User user) {
batisUserService.updateUser(user);
}
#RequestMapping(value = "/patchUser", method = RequestMethod.PATCH)
public User patchUser(User user) {
batisUserService.patchUser(user);
}
}
Related
I need to ignore the field when return the response from spring boot. Pls find below info,
I have one pojo called Student as below
Student {
id,
name,
lastName
}
i am getting a body for as PostRequest as below
{
id:"1",
name:"Test",
lname:"Test"
}
i want get all the data from frontEnd (id,name,Lname) But i just want to return the same pojo class without id as below,
{
name:"Test",
lName:"Test"
}
I have tried #JsonIgnore for column id, But it makes the id column as null(id=null -it is coming like this even when i send data to id field from postman) when i get the data from frontEnd.
I would like to use only one pojo to get the data with proper data(withoud getting id as Null), and need to send back the data by ignoring the id column.
Is there any way to achieve it instead of using another pojo?
You just need to use #JsonInclude(JsonInclude.Include.NON_NULL) at class level and it will be helpful for ignore all your null fields.
For example :
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Test {
// Fields
// Constructors
// Getters - setters
}
As of now you are using only one POJO it's not good practice because it's your main entity into your project, so good practice is always make DTO for the same.
This is possible via the #JsonView annotation that is part of Jackson. Spring can leverage it to define the views used on the controller.
You'd define your DTO class like this:
class User {
User(String internalId, String externalId, String name) {
this.internalId = internalId;
this.externalId = externalId;
this.name = name;
}
#JsonView(User.Views.Internal.class)
String internalId;
#JsonView(User.Views.Public.class)
String externalId;
#JsonView(User.Views.Public.class)
String name;
static class Views {
static class Public {
}
static class Internal extends Public {
}
}
}
The Views internal class acts as a marker to jackson, in order to tell it which fields to include in which configuration. It does not need to be an inner class, but that makes for a shorter code snippet to paste here. Since Internal extends Public, all fields marked with Public are also included when the Internal view is selected.
You can then define a controller like this:
#RestController
class UserController {
#GetMapping("/user/internal")
#JsonView(User.Views.Internal.class)
User getPublicUser() {
return new User("internal", "external", "john");
}
#GetMapping("/user/public")
#JsonView(User.Views.Public.class)
User getPrivateUser() {
return new User("internal", "external", "john");
}
}
Since Spring is aware of the JsonView annotations, the JSON returned by the /public endpoint will contain only externalId and name, and the /internal endpoint will additionally include the internalId field.
Note that fields with no annotation will not be included if you enable any view. This behaviour can be controlled by MapperFeature.DEFAULT_VIEW_INCLUSION, which was false in the default Spring ObjectMapper when I used this for the last time.
You can also annotate your #RequestBody parameters to controller methods with JsonView, to allow/disallow certain parameters on input objects, and then use a different set of parameters for output objects.
The following is just an exmaple for context not the actual implementation. Using Spring MVC, I have the following Model which has validation based on annotations.
#Entity
public class Customer {
#Id private int id;
#NotNull private String name;
}
And the following DTO used to map the data received in the request's body in the Controller's createNewCustomer function.
public class CustmerDTO{
private String name;
}
In my Controller I am using the modelMapper to convert the customerDTO to a new domain model Customer object. Based on the #NotNull annotation, if the name property of the recieved object (customerDTO) is empty the ConstraintViolationException is thrown.
public class CustomerController {
#Autowired private CustomerService customerService;
#Autowired private ModelMapper modelMapper;
#PostMapping(value = "/customer")
public Customer createNewCustomer (#RequestBody CustomerDTO customerDTO) {
try {
Customer newCustomer = modelMapper.map(customerDTO, Customer.class);
return customerService.saveCustomer(newCustomer);
}
catch (ConstraintViolationException e) {
throw new CustomerMissingInformation();
}
}
}
As you can see here I handle validation for the Customer in the Controller, which by definition is not a good habit, as Controllers in MVC are part of the presentation layer, and should have no clue about how to perform data validation, also I want to keep my controllers as light as possible.
Can I keep this design or is there any good approach to move my validation in the Service layer while keeping validation annotations and also allowing the Controller to be able to receive representation objects(DTOs) and convert them to domain models?
Looking more into this, I came to the following conclusion. Validation for persisting an object should happen as soon as possible and at property level, as you don't want your persistence layer or any mappers that you might be using deal with null when it is not expected. Everything that goes past property validation, shall be approached from a contextual validation perspective, and be passed to methods that hold logic for a given context.
Is this order valid to be filled, is this customer valid to check in to the hotel. So rather than have methods like isValid have methods like isValidForCheckIn.
I'm using Spring Data REST to build my application. It's been working very well so far, but I'd like to add some customizations to returned entity while still keeping the automatically generated links.
I'd like to do something like this:
#RepositoryRestController
public class SomeController {
#GetMapping("/entity/{id}")
public SomeEntity getEntity(#PathVariable int id)
SomeEntity entity = SpringDataREST.findById(id); //-> is there a way to do this?
Link randomLink = generateRandomLink();
entity.addLink(randomLink);
//do other stuff with entity
return entity;
}
}
Where SomeEntity class extends Spring HATEOAS ResourceSupport.
If you are using Spring Data REST you can use RepositoryEntityLinks to programmatically create links:
#Component
public class MyBean {
private final RepositoryEntityLinks entityLinks;
public MyBean(RepositoryEntityLinks entityLinks) {
this.entityLinks = entityLinks;
}
public Link someMethod(MyEntity entity) {
//...
return entityLinks.linkToSingleResource(entity)
}
}
Note - to use linkToSingleResource method, the MyEntity must implement Identifiable interface. Instead you can use method linkForSingleResource:
return entityLinks.linkForSingleResource(MyEntity.class, entity.getId())
I am new to mybatis. Previously I used Hibernate ORM framework.Now I want to use mybatis for development.
Below is my mapper class where I can write the actual query for application.
public interface UserMapper {
#Insert("INSERT into user(id,name,address) VALUES(#{uid}, #{uname}, #{uaddress})")
void insertUser(User user);
}
I am getting the User information in request from front end application.
Below is my Controller::
#RequestMapping(value = "/userdetails", method = RequestMethod.POST, headers = "Accept=application/json")
public #ResponseBody ResponseCodeModel userInfo(#RequestBody User user)
{
session.save(user);//In Hibernate
}
If I used Hibernate I can directly persist the user object in db using session.save(userObject);
But in Mybatis ,I need to map all the user parameter in mapper query.
If my table having 50 coloumn then I need to mention all the parameter in query like below::
public interface UserMapper {
#Insert("INSERT into user(id,name,address,....50 Coloumn name) VALUES(#{uid}, #{uname}, #{uaddress} ,....50 model attribute name)")
void insertUser(User user);
}
Is there any easier way to persist the Model Object in DB using myBatis.
Mybatis is SQL Mapper, not ORM. then you indeed have to map.
If you insert into all columns in the table then you may omit the columns name and specify values in right order, this is common SQL.
I am trying setting a generic class for my project. So far I get this code:
https://github.com/klebermo/blog.cms/blob/master/src/main/java/com/config/generic/controller/GenericController.java
In this moment, I am stuck with this method:
#RequestMapping(value="cadastra", method=RequestMethod.POST)
#ResponseBody
public String cadastra(#ModelAttribute("") E object, BindingResult result) {
if(service.cadastra(object))
return "yes";
else
return "not";
}
the parameter for the annotation ModelAtribute should be the name of an entity class from my project, which I have stored in the class member entity_class, but can't use because the annotation only accept constant values.
Also, I have this same problem with the annotation PreAutorize. I will use this annotation like that:
#PreAuthorize("hasPermission(#user, '<<permission_name>>')")
#RequestMapping(value="cadastra")
public ModelAndView cadastra() {
ModelAndView mav = new ModelAndView();
mav.setViewName("privado/"+this.entity_name+"/cadastra");
return mav;
}
and in my methods from my generic service too. The permission name follows this rule:
<<action_name>>_<<entity_name>>
and for each entity class I will have three permissions: cadastra (new_item), altera (change_item) and remove (remove_item).
Anyone can point a direction of how to solve this?
For the first question #ModelAttribute, what I've understood, you have several form calling the same Mapping method cadastra to verify if the informations are correct.
I think you can use a form parameter to tell which entity class it is, and use wrapper to wrap all of the entities that call this method.
create an Entity:
#Entity
public class Wrapper{
private Entity1 e1;
.....
in your jsp:
<form:form modelAttribute="wrapper" .../>
<form:hidden value="classname" .../>
<form:input path=e1.someProperty .../>
in your controller:
#RequestParam("classname") String classname;//used to get which entity it's
service.cadastra(wrapper.getEntityX())
for the second question I don't know much about #PreAuthorize annotation.