Spring Boot #JsonIgnore - spring

Can I use the JsonIngore in a RestController Method? if I put the #JsonIgnore in my VO he will ignore this prop in all request, but I want to ignore only in some request: sample:
I have
public class Pedido{
private Long id;
private Date day;
private List<Item> items;
}
public class Item {
private Long id;
private String nome;
}
#RestController
#RequestMapping("/pedido")
public class PedidoController{
#GetMapping(value = "/")
public List<Pedido> findAll(){
//HERE I dont need to return the List<Item>
}
#GetMapping(value = "/{id}")
public Pedido findById(#PathVariable Long id){
//HERE I need to return the List<Item>
}
}

#JsonView is the right solution for you. Here is an example, and code snippet from the link as below
public class View {
interface Summary {}
}
public class User {
#JsonView(View.Summary.class)
private Long id;
#JsonView(View.Summary.class)
private String firstname;
#JsonView(View.Summary.class)
private String lastname;
private String email;
private String address;
}
#RestController
public class MessageController {
#Autowired
private MessageService messageService;
#JsonView(View.Summary.class)
#RequestMapping("/")
public List<Message> getAllMessages() {
return messageService.getAll();
}
#RequestMapping("/{id}")
public Message getMessage(#PathVariable Long id) {
return messageService.get(id);
}
}

Related

Error locating String field in Spring Boot

I'm trying to find a company by its CNPJ(Brazilian corporate tax payer registry number) in a DB (H2), but it's returning an error
{
"timestamp": "2022-03-30T19:30:23.823+00:00",
"status": 404,
"error": "Not Found",
"path": "/companies/cnpj/30101554000146"
}
I've tried other alternatives using:
http://localhost:8080/companies/cnpj/'30.101.554/0001-46', http://localhost:8080/companies/cnpj/"30.101.554/0001-46",
but the error persists. I implemented like this :
#Entity
#Table(name = "company")
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#CNPJ
private String cnpj;
//skipped
}
public interface CompanyRepository extends JpaRepository<Company,Long> {
Optional<Company> findByCnpj(String cnpj);
}
public class CompanyDTO {
private Long id;
private String name;
private String cnpj;
//skipped
}
#Service
#Transactionalpublic class CompanyService {
#Autowired
private CompanyRepository companyRepository;
#Transactional(readOnly = true)
public CompanyDTO findById(Long id) {
Company resultado = companyRepository.findById(id).get();
CompanyDTO dto = new CompanyDTO(resultado);
return dto;
}
#Transactional(readOnly = true)
public CompanyDTO findByCnpj(String cnpf) {
Optional<Company> resultado = companyRepository.findByCnpj(cnpf);
CompanyDTO dto = new CompanyDTO(resultado.get());
return dto;
}
}
#RestController
#RequestMapping(value = "/companies")public class CompanyController {
#Autowired
private CompanyService companyService;
#GetMapping(value = "/{id}")
public CompanyDTO findById(#PathVariable Long id) {
return companyService.findById(id);
}
#GetMapping(value = "/cnpj/{cnpj}")
public CompanyDTO findByCnpj(#PathVariable String cnpj) {
return companyService.findByCnpj(cnpj);
}
}
The expected output would be:
[
{"id": 1,
"nome": "Company 123",
"cnpj": "30.101.554/0001-46"
}
]
UPDATE:
I changed #GetMapping(value = "/cnpj/{cnpj}") to #GetMapping(value = "/cnpj/**") and:
#GetMapping(value = "/cnpj/**")
public CompanyDTO findByCnpj(HttpServletRequest request) {
return companyService.findByCnpj(request.getRequestURI().split(request.getContextPath() + "/cnpj/")[1]);
}
Works for me! Thanks
As explained here, pathParams with slashes can be realy tricky while using spring-boot. This article explains pretty well what to do to avoid getting an error 404 when your pathVariable has a slash.

Spring boot -Axon framework NoHandlerForCommandException: No Handler for command

I am getting NoHandlerForCommandException: No Handler for command while trying to use Axon framework with Spring boot.
Below are my Java files :
The Rest controller ->
#RestController
#RequestMapping("/product")
public class ProductController {
#Autowired
private CommandGateway gateway;
#PostMapping
public ResponseEntity createProduct(#RequestBody CreateProductModel model) {
CreateProductCommand command=CreateProductCommand.builder()
.price("$123")
.productId(UUID.randomUUID().toString())
.product("Shoe")
.build();
String s=gateway.sendAndWait(command);
return new ResponseEntity<String>(HttpStatus.CREATED);
}
The ProductCreatedEvent object ->
import lombok.Data;
#Data
public class ProductCreatedEvent {
#TargetAggregateIdentifier
private String productId;
private String product;
private String price ;
}
The command class CreateProductCommand ->
#Builder
#Data
public class CreateProductCommand {
#TargetAggregateIdentifier
private final String productId;
private final String product;
private final String price ;
}
The Aggregate class ->
#Aggregate
public class ProductAggregate {
#AggregateIdentifier
private String productId;
private String product;
private String price ;
public ProductAggregate() {
}
#CommandHandler
public ProductAggregate(CreateProductCommand command) {
//TODO: Validation logic can be handled here
ProductCreatedEvent event=new ProductCreatedEvent();
BeanUtils.copyProperties(command, event);
AggregateLifecycle.apply(event);
}
#EventSourcingHandler
public void on(ProductCreatedEvent event) {
this.price=event.getPrice();
this.productId=event.getProductId();
this.product=event.getProduct();
}
}

I don't know why the double values are displayed in postman. Is the my code correct?

This is my Book class:
#Entity
#Table(name="book")
public class Book {
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
#ManyToOne(targetEntity=Category.class,cascade=CascadeType.ALL,fetch=FetchType.LAZY)
#JoinColumn(name="CategoryId")
public Category category;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(length=10)
private int book_id;
#Column(length=128)
private String title;
#Column(length=64)
private String author;
#Column(length=200)
private String description;
#Column(length=10)
private int ISBN;
#Column(length=10)
private float price;
private Date published_Date;
#Lob
#Column
#Basic(fetch = FetchType.LAZY)
private byte[] icon;
//getter and setter
}
This is my Category class:
#Entity
#Table(name="category1")
public class Category {
#Id
#Column(length=12)
#GeneratedValue(strategy=GenerationType.AUTO)
public int CategoryId;
#Column(length=50)
public String CategoryName;
//#JsonBackReference
#OneToMany(mappedBy="category")
private List<Book> books = new ArrayList<Book>();
//getter and setter
}
The relationship between them is one to many.
This is my Category Service class
#Service
#Transactional
public class AdminServiceImpl implements AdminService {
#Autowired
private CategoryDao dao;
#Autowired
private BookDao dao1;
#Override
public List<Category> getAllCategory(){
return dao.findAll();
}
}
My Controller class
#RestController
#RequestMapping("/bookstore")
public class CategoryController {
#Autowired
private AdminService service;
#GetMapping("/GetAllCategory")
private ResponseEntity<List<Category>> getAllCategory() {
List<Category> catlist = service.getAllCategory();
return new ResponseEntity<List<Category>>(catlist, new HttpHeaders(), HttpStatus.OK);
}
}
My category table already has data.When i try to display them it is showing double values.
Displaying values using Postman
The Category table in the Database: Database table
Jackson's ObjectMapper uses the Java bean pattern and it expects the following
public class Foo {
public Object bar;
public Object getBar() {...}
public void setBar(Object bar) {...}
}
The getters and setters start with get and set, respectively, followed by the corresponding field name with its first letter capitalized.
Change
CategoryId to categoryId (first letter lowercase)
and
CategoryName to categoryName

null values inserted while auditing

My AuditListener
public class EmployeeAuditListeners {
#PrePersist
public void prePersist(Employee employee){
perform(employee,Action.INSERTED);
}
#PreUpdate
public void preUpdate(Employee employee){
perform(employee,Action.UPDATED);
}
#PreRemove
public void preRemove(Employee employee){
perform(employee,Action.DELETED);
}
#Transactional
public void perform(Employee emp, Action action){
EntityManager em = BeanUtil.getBean(EntityManager.class);
CommonLogs commonLogs = new CommonLogs();
commonLogs.setQuery("new query");
em.persist(commonLogs);
}
}
and My Auditable.class
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class Auditable<U> {
#CreatedBy
protected U createdBy;
#CreatedDate
#Temporal(TemporalType.TIMESTAMP)
protected Date createdDate;
#LastModifiedBy
protected U lastModifiedBy;
#LastModifiedDate
#Temporal(TemporalType.TIMESTAMP)
protected Date lastModifiedDate;
}
My CommonLogs.class
#Entity
#EntityListeners(AuditingEntityListener.class)
public class CommonLogs extends Auditable<String> {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String query;
public CommonLogs() {
}
public CommonLogs(String query) {
this.query = query;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
}
My Employee.java class
#Entity
#EntityListeners(EmployeeAuditListeners.class)
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String address;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
and I have a simple Rest Controller
#RestController
#RequestMapping("/api")
public class EmployeeController {
#Autowired
private EmployeeRepository employeeRepository;
#PostMapping("/employees")
public Employee createEmployee(#RequestBody Employee employee){
return employeeRepository.save(employee);
}
}
I want to log it on my table (common_logs) every time i perform some crud operations on my Employee Entity.
the above given example is working to some extent as it successfully stores employee and invokes EmployeeAuditListeners.
but now while saving CommongLog entity i expect it's parent class Auditable to automatically insert createdBy, createdDate etc. for now only query and id is inserted on common_logs table and remaining columns are null.
You can review the documentation for Auditing in here.
To enable the automatic Auditing, you must add the annotation #EnableJpaAuditing in your Application class:
#SpringBootApplication
#EnableJpaAuditing
class Application {
static void main(String[] args) {
SpringApplication.run(Application.class, args)
}
}
If you want the fields #CreatedBy and #LastModifiedBy too, you will also need to implement the AuditorAware<T> interface. For example:
class SpringSecurityAuditorAware implements AuditorAware<User> {
public User getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return null;
}
return ((MyUserDetails) authentication.getPrincipal()).getUser();
}
}

how to Fix spring boot one to many bidirectional infinity loop?

i am try to create a one to many bidirectional mapping using spring boot and spring data jpa please look the below entity
Employer Entity
#Entity
public class Employer
{
private Long id;
private String employerName;
private List<Employee> employees;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getEmployerName()
{
return employerName;
}
public void setEmployerName(String employerName)
{
this.employerName = employerName;
}
#OneToMany(cascade=CascadeType.ALL, mappedBy="employer")
public List<Employee> getEmployees()
{
return employees;
}
public void setEmployees(List<Employee> employees)
{
this.employees = employees;
}
}
Employee Entity
#Entity
public class Employee
{
private Long id;
private String employeeName;
private Employer employer;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getEmployeeName()
{
return employeeName;
}
public void setEmployeeName(String employeeName)
{
this.employeeName = employeeName;
}
#ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
public Employer getEmployer()
{
return employer;
}
public void setEmployer(Employer employer)
{
this.employer = employer;
}
}
Employer Repo
public interface EmployerServices extends JpaRepository<Employer, Long> {
}
Employee Repo
public interface EmployeeServices extends JpaRepository<Employee, Long> {
}
REST Controller is
#RestController
public class Controller {
#Autowired EmployeeServices employeeServices;
#Autowired EmployerServices employerServices;
#GetMapping("/getempr")
public Object getempr(){
return employerServices.findOne(1L);
}
}
now the problem begin start see my out put
its look like a infighting loop and my server throwing error getOutputStream() has already been called for this response.
I used #JsonBackReference & #JsonManagedReference
annotation but the problem is its working like one to many
{
"id":1,
"employerName":"employer",
"employees":[
{"id":1,"employeeName":"emp1"},
{"id":2,"employeeName":"emp2"}
]
}
if I am trying to get in the concern of many to one like all employee with employer. the output is
[
{
"id":1,
"employeeName":"emp1"
},
{
"id":2,
"employeeName":"emp2"}
]
its not showing me the employer details.
please suggets me guys what i am doing wrong. thanks in advance!!
Instead of using #JsonBackReferenceand #JsonManagedReference try to use annotation #JsonIgnoreProperties:
#JsonIgnoreProperties("employer")
private List<Employee> employees;
#JsonIgnoreProperties("employees")
private Employer employer;
It prevents Jackson from rendering a specified properties of associated objects.
with the JSON its a problem with bi-directional mapping. Use the below properties.
#JsonIgnoreProperties("employer")
#JsonIgnoreProperties("employees")
please keep fetching type as eager.
hope this will work.
You can solve your issue with two modification with annotations.
Employer.class
#Entity
public class Employer {
private Long id;
private String employerName;
#OneToMany(cascade = CascadeType.ALL,
mappedBy = "employer",
orphanRemoval = true)
private List<Employee> employees;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmployerName() {
return employerName;
}
public void setEmployerName(String employerName) {
this.employerName = employerName;
}
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}
Employee.class
#Entity
public class Employee {
private Long id;
private String employeeName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "employer_id")
private Employer employer;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public Employer getEmployer() {
return employer;
}
public void setEmployer(Employer employer) {
this.employer = employer;
}
}
For more information please visit this link.
Change your getEmployer Method like this:
#ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
public Employer getEmployer()
{
return employer;
}
use
#JsonProperty(access = Access.WRITE_ONLY)
private List<Employee> employees;
So that it will ignore employees while printing to JSON in the response (and thus prevents the looping), but will still consider the JSON data (employee list) you pass in the request body so that it is available for persistence.

Resources