Apache Camel - How to override Json response? - spring

How can we override Json response ex: set header and override rest json response fields in Apache Camel?
I've below working code:
import java.net.ConnectException;
import javax.jms.JMSException;
import org.apache.camel.Exchange;
import org.apache.camel.LoggingLevel;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.rest.RestBindingMode;
import org.springframework.stereotype.Component;
import com.decodedbytes.beans.InboundNameAddress;
#Component
public class NewRestRoute extends RouteBuilder {
#SuppressWarnings("unchecked")
#Override
public void configure() throws Exception {
onException(JMSException.class, ConnectException.class)
.routeId("jmsExceptionRouteId")
.handled(true)
.log(LoggingLevel.INFO, "JMS Exception has occurred; handling gracefully");
restConfiguration()
.component("jetty")
.host("0.0.0.0")
.port(8080)
.bindingMode(RestBindingMode.json)
.enableCORS(true);
rest("masterclass")
.id("restRouter")
.produces("application/json")
.post("nameAddress").type(InboundNameAddress.class) //>>> replace with your POJO here
.to("direct:process")
;
from("direct:process").routeId("processMessageRouteId")
.process(exchange -> {}) //>>> replace exchange lambda function with your processor class here
.log(LoggingLevel.INFO, String.valueOf(simple("${body}")))
.log(LoggingLevel.INFO, "## Sending to DB EP")
.to("direct:toDB")
.to("direct:toActiveMQ")
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200))
.transform().simple("Message Processed and result generated with Body: ${body}")
.end();
from("direct:toDB")
.routeId("toDBId")
.log(LoggingLevel.INFO, "### Sending to ActiveMQ EP")
.to("jpa:"+InboundNameAddress.class.getName());
from("direct:toActiveMQ")
.routeId("toActiveMQId")
.log(LoggingLevel.INFO, ">>> in ActiveMQ EP")
.to("activemq:queue:nameaddressqueue?exchangePattern=InOnly");
}
}
InboundMessageAddress.java
#Entity
#Data
#Table(name = "NAME_ADDRESS")
#NamedQuery(name = "fetchAllRows", query="select x from InboundNameAddress x")
public class InboundNameAddress implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#Column(name = "house_number")
private String houseNumber;
private String city;
private String province;
#Column(name = "postal_code")
private String postalCode;
}
and help me with the

Related

Passing JSON in body spring and constructing object with foreign key

I am trying to create a basic spring API.
I have a Post class that have an attribute User user as foreign key.
package com.example.demo.model;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import jakarta.persistence.*;
import java.util.Objects;
#Entity
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, updatable = false)
private Long id;
private String title;
private String body;
#ManyToOne
private User user;
public Post() {
}
public Post(String title, String body) {
this.title = title;
this.body = body;
}
// Getters and Settes ...
}
Here is the User class
package com.example.demo.model;
import jakarta.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
#Entity
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, updatable = false)
private Long id;
private String name;
private Integer age;
private String email;
#OneToMany(mappedBy = "user")
private List<Post> posts = new ArrayList<Post>();
#ManyToMany
#JoinTable(name = "user_task",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "task_id"))
private List<Task> tasks = new ArrayList<Task>();
public User() {}
public User(String name, Integer age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
// Getters and Settes ...
}
and here is my Post Controller
package com.example.demo.controller;
import com.example.demo.model.Post;
import com.example.demo.service.PostService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
#RequestMapping("/post")
public class PostController {
private final PostService postService;
#Autowired
public PostController(PostService postService) {
this.postService = postService;
}
#GetMapping("/all")
public List<Post> getAllPosts (){
System.out.println("3");
return postService.getAllPosts();
}
#GetMapping("/{id}")
public Post getPost(#PathVariable Long id){
System.out.println("2");
return postService.getPost(id);
}
#PostMapping("/create")
public Post createPost(#RequestBody Post post){
return postService.createPost(post);
}
}
So in the /create endpoint i am passing a json object in the body. Here is an exemple:
{
"title": "Post1",
"body": "Post1 Body",
"user": "1"
}
the user: 1 is the foreign key to user who owns the post.
Here is the full error:
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.example.demo.model.User` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('1')]
I need to insert the json object into the Post table with the foreign key

Why am I getting null for the date when I create a Todo entity?

What is wrong with my to-do application? I want the user to be able to add a todo and have it be saved in my MySQL database with the time it was created, but I don't know what I'm doing wrong.
I am new to learning Springboot and would appreciate any suggestions or advice.
Todo Entity:
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import javax.persistence.*;
import java.util.Date;
#Entity(name = "Todo")
#NoArgsConstructor
#Table(name = "todos")
public class Todo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name="description")
private String description;
#Column(name="target_date")
#CreationTimestamp
private Date targetDate;
public Todo(String description) {
this.description = description;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getTargetDate() {
return targetDate;
}
public void setTargetDate(Date targetDate) {
this.targetDate = targetDate;
}
#Override
public String toString() {
return "Todo{" +
"id=" + id +
", description='" + description + '\'' +
", targetDate=" + targetDate +
'}';
}
}
Adding a Todo with Spring Data JPA
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import javax.transaction.Transactional;
import java.util.List;
#Repository
#Component
public interface TodoRepository extends JpaRepository<Todo, Integer> {
#Modifying
#Query(value = "INSERT INTO todos (description) VALUES (:description)", nativeQuery=true)
#Transactional
void addTodo(#Param("description") String description);
}
TodoController
#RestController
#RequestMapping(value = "/api/v1/todos")
#AllArgsConstructor
public class TodoController {
#Autowired
private ITodoService todoService;
#PostMapping(value = "/add-todo")
public String addTodo(#RequestParam String description) {
Todo todo = new Todo();
todo.setDescription(description);
todoService.addTodo(todo);
return todo.toString();
}
after getting a post request, the target_date is getting NULL in MySQL
I assume you can solve it by using persist():
#Autowired EntityManager entityManager;
#PostMapping(value = "/add-todo")
public String addTodo(#RequestParam String description) {
Todo todo = new Todo();
todo.setDescription(description);
entityManager.persist(todo);
return todo.toString();
}

I18n for custom error messages into JPA entity

I looking to understand how to internationalize JPA entity error message. I understand how its work into a controller using autowired MessageSource but in my case I want to do this into a JPA entity. I'm not intresting about using the same way as the controller issue because I think is not optimized to autowired the full MessageSource on this entity. If someone have a simple example to show me how its work with a simple entity like mine. My project using spring-boot 2.2 ; JPA ; and thymeleaf.
The entity I using:
package com.bananasplit.weblab2.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
#Entity
#Table(name = "todo")
public class Todo {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name", nullable = false)
#NotEmpty
#Size(min=2, max=30) // error message is already internationalized here with spring-boot
private String name;
#Column(name = "category", nullable = false)
#NotEmpty
#Pattern(regexp="(WORK|PERSONAL|SPECIAL)",
message="Category must be WORK or PERSONNAL or SPECIAL.") // here is the message I want to internationalize
private String category;
public Todo() {}
public Todo(String name, String category) {
this.name = name;
this.category = category;
}
#Override
public String toString() {
return String.format(
"Todo[id=%d, name='%s', category='%s']",
id, name, category);
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
}
By default Spring boot uses this ValidationMessages.properties but you can override by adding this file in resources.
#Size(min=2, max=30, message="{empty.todo.name")
private String name;
In ValidationMessages.properties file
empty.todo.name = Cannot be blank
If you want to manage which package messages should be scanned by Spring then should follow this link

How to know the name of the resource from an Entity class, to build a Hateoas link to that resource?

Suppose I have two resources Person and Article
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long person_id;
private String firstName;
private String lastName;
#OneToMany(mappedBy="person", cascade=CascadeType.ALL)
private List<Article> articles = new ArrayList<>();
}
#Entity
#Table(name="article")
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String title;
private String details;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="person_id")
private Person person;
}
I now want to add HATEOAS support to the response of the controller for which I am using org.springframework.hateoas.ResourceAssembler
public class PersonResourceAssembler implements ResourceAssembler<Person, Resource<Person>> {
private EntityLinks entityLinks;
public UserJobResourceAssembler(EntityLinks entityLinks) {
this.entityLinks = entityLinks;
}
#Override
public Resource<Person> toResource(Person entity) {
Resource<UserJob> resource = new Resource<>(entity);
resource.add(
entityLinks.linkFor(Person.class).withSelfRel()),
entityLinks.linkFor(...logic...).withRel("articles")) //here I am hardcoding the relation link name i.e "article"
);
return resource;
}
}
So, in above code the "article" is hardcoded for the link name, but I don't want to do it this way. I want it do in the way Spring-Data-REST handles it i.e for every relationship it auto detects the name of the variable used inside the Entity class e.g articles will be picked from Person and person will be picked from Article.
I have no idea how Spring-Data-REST handles it, but are there any readymade/custom solutions for this requirement?
You can use the reflection API to introspect the entity and find associations. Something like:
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.persistence.OneToMany;
public class AssociationUtility {
public static List<Field> getAssociatedFields(Object entity) {
Stream<Field> fields = Arrays.stream(entity.getClass().getDeclaredFields());
return fields.filter(field -> field.getAnnotation(OneToMany.class)
!= null).collect(Collectors.toList());
}
public static void main(String[] args) {
List<Field> fields = getAssociatedFields(new Customer());
fields.stream().forEach(f -> System.out.println("Make a link for Class: "
+ ((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0]
+ " with rel: " + f.getName()));
System.exit(0);
}
}

Hibernate Query to join two table using Jparepository

Hi all i have a small issue with joining two tables using jparepository using #query but i am getting error. please help me with this.
UserAddress.java
package com.surya_spring.example.Model;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
#Entity
#Table(name = "user_address")
//#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class UserAddress implements Serializable {
/**
*
*/
private static final long serialVersionUID = -3570928575182329616L;
/*#ManyToMany(cascade = {CascadeType.ALL},fetch=FetchType.EAGER,mappedBy = "userAddress",targetEntity=UserData.class)*/
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="user_id")
private UserData userdata;
#Id
#Column(name = "addr_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long addrid;
#Column(name = "dr_no")
#NotNull
private String doorNo;
#Column(name = "strt_name")
#NotNull
private String streetName;
#Column(name = "city")
#NotNull
private String city;
#Column(name = "country")
#NotNull
private String country;
/*#OneToOne(cascade=CascadeType.ALL)
#Column(name="user_id")*/
public UserData getUserdata() {
return userdata;
}
public void setUserdata(UserData userdata) {
this.userdata = userdata;
}
public Long getAddrid() {
return addrid;
}
public void setAddrid(Long addrid) {
this.addrid = addrid;
}
public String getDoorNo() {
return doorNo;
}
public void setDoorNo(String doorNo) {
this.doorNo = doorNo;
}
public String getStreetName() {
return streetName;
}
public void setStreetName(String streetName) {
this.streetName = streetName;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
UserData.java
package com.surya_spring.example.Model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.NonNull;
#Entity
#Table(name = "user_data")
public class UserData implements Serializable{
/**
* Serialization ID
*/
private static final long serialVersionUID = 8133309714576433031L;
/*#ManyToMany(targetEntity=UserAddress.class ,cascade= {CascadeType.ALL },fetch=FetchType.EAGER)
#JoinTable(name="userdata",joinColumns= #JoinColumn(name="userid"),inverseJoinColumns = #JoinColumn(name="userid"))
*/
#Id
#Column(name = "user_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long userId;
#Column(name = "user_name")
#NonNull
private String userName;
#Column(name = "user_email")
#NonNull
private String userEmail;
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;
}
}
Repository:
package com.surya_spring.example.Repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import com.surya_spring.example.Model.UserData;
public interface UserDataRepository extends JpaRepository<UserData, Long>{
#Query(" FROM UserData where userId= :id")
public List<UserData> findBySearchTerm(#Param("id") Long id);
}
any one let me know the query to join this both the table to get city name from user_address where user_id=? joining user_data table
If you want to get the city for a user you can do:
#Query("SELECT ua.city FROM UserAddress ua WHERE ua.userdata.userId = ?1")
String findCityByUserId(Long userId);
Note that your entity names are used (like in your java classes) and not the table names in database! You do not have to do the join by yourself as you can use the properties of your domain models to access the related data

Resources