Getting data by a particular key - java

I am a beginner in java spring boot and I want to find a country by id but it's giving me this error when I test it in Postman meanwhile a country with the said id 4 exist in MySQL daabase. I don't know what went wrong "timestamp": "2022-05-11T16:54:40.909+00:00",
"status": 404,
"error": "Not Found",
"path": "/country/findById/4"
Here is my Entity class
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class)
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String code;
private String capital;
private String description;
private String nationality;
private String continent;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getCapital() {
return capital;
}
public void setCapital(String capital) {
this.capital = capital;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getNationality() {
return nationality;
}
public void setNationality(String nationality) {
this.nationality = nationality;
}
public String getContinent() {
return continent;
}
public void setContinent(String continent) {
this.continent = continent;
}
public List<State> getStates() {
return states;
}
public void setStates(List<State> states) {
this.states = states;
}
#OneToMany(mappedBy = "country")
private List<State> states;
}
Here is my Repository
#Repository
public interface CountryRepository extends JpaRepository<Country,Integer> {
}
Here is my Service
#Service
public class CountryService {
#Autowired
private CountryRepository countryRepository;
public List<Country> getAllCountry() {
return countryRepository.findAll();
}
public void saveCountryInfo(Country country){
countryRepository.save(country);
}
public Optional<Country> getCountryById(Integer id){
return countryRepository.findById(id);
}
}
Here is my Controller
#Controller
public class CountryController {
#Autowired
private CountryService countryService;
#GetMapping("/countries")
public String getCountry(Model model){
List<Country> countryList = countryService.getAllCountry();
model.addAttribute("countries",countryList);
return "country";
}
#PostMapping("/countries/addNew")
public String saveInfo(Country country){
countryService.saveCountryInfo(country);
return "redirect:/countries";
}
#GetMapping("/country/findById")
#ResponseBody
public Optional<Country> getCountryById(Integer id){
return countryService.getCountryById(id);
}
}
But this is the error from postman
"timestamp": "2022-05-11T16:54:40.909+00:00",
"status": 404,
"error": "Not Found",
"path": "/country/findById/4"

In your controller, you are not accepting the id as path variable
#GetMapping("/country/findById/{id}")
#ResponseBody
public Optional<Country> getCountryById(#PathVariable("id") Integer id){ //Bind PathVariable id to id
return countryService.getCountryById(id);
}
{xyz} this works as placeholder/variable
#PathVariable("xyz") Integer id This is how Spring will bind that variable to your parameter

Related

In java/spring-boot what is the cleanest way to return an empty json object if an item is not found

Below is my Java/spring-boot controller code for /api/speaker/123 request handler:
#GetMapping
#RequestMapping("{id}")
public Speaker get(#PathVariable Long id) {
return speakerRepository.getOne(id);
}
If the id 123 is not found for the incoming request /api/speaker/123, then how to quickly code to return an empty object/json to the browser? In Nodejs/JavaScript world, I could simply do below one line. Any such clean code without having to add if...else then, more code to look for null/empty, then do more...and then return!
return speakerRepository.getOne(id) || {};
Edited:
// Speaker.java
#Entity(name = "speakers")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Speaker {
public Long getSpeaker_id() {
return speaker_id;
}
public void setSpeaker_id(Long speaker_id) {
this.speaker_id = speaker_id;
}
public String getFirst_name() {
return first_name;
}
public void setFirst_name(String first_name) {
this.first_name = first_name;
}
public String getLast_name() {
return last_name;
}
public void setLast_name(String last_name) {
this.last_name = last_name;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public String getSpeaker_bio() {
return speaker_bio;
}
public void setSpeaker_bio(String speaker_bio) {
this.speaker_bio = speaker_bio;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long speaker_id;
private String first_name;
private String last_name;
private String title;
private String company;
private String speaker_bio;
public List<Session> getSessions() {
return sessions;
}
#ManyToMany(mappedBy = "speakers")
#JsonIgnore
private List<Session> sessions;
public Speaker(){
}
}
// SpeakerRepository.java
public interface SpeakerRepository extends JpaRepository<Speaker, Long> {
}
#GetMapping
#RequestMapping("{id}")
public Speaker get(#PathVariable Long id) {
return speakerRepository.getOne(id);
}
instead use below code:
#JsonSerialize
public class EmptyJsonResponse {}
#GetMapping
#RequestMapping("{id}")
public ResponseEntity<?> get(#PathVariable Long id) {
Speaker speaker = speakerRepository.getOne(id);
if(speaker != null){
return new ResponseEntity<Speaker>(speaker, HttpStatus.OK)
}else{
return new ResponseEntity<EmptyJsonResponse>(new EmptyJsonResponse(), HttpStatus.OK)
}
}

How to read JSON array values in Spring controller

I would like to iterate Products and get the list of name,code and price and set in my Model class. Any help would be really appreciated - how can I iterate this. When I use obj.get("Products") - it just printing as string - got stuck to iterate.
{
"id": "skd3303ll333",
"Products": [{
"name": "apple",
"code": "iphone-393",
"price": "1939"
},
{
"name": "ipad",
"code": "ipad-3939",
"price": "900"
}
]
}
#PostMapping(path="/create", consumes=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> create(#RequestBody Map<String, Object> obj ) {
System.out.println("Products :" + obj.get("Products"));
}
There are two ways to do this,
1) By type casting (personally i will not prefer this)
List<Map<Object,Object>> productslist = (List<Map<Object, Object>>) obj.get("products");
for(Map entry: productslist) {
for(Object s: entry.keySet()) {
System.out.println(s.toString());
System.out.println(entry.get(s).toString());
}
}
2) Mapping directly to Model class, for this approach you need Jackson library in buildpath
#JsonIgnoreProperties(unknown =true)
public class Customer {
#JsonProperty("id")
private String id;
#JsonProperty("products")
private List<Products> products;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<Products> getProducts() {
return products;
}
public void setProducts(List<Products> products) {
this.products = products;
}
}
#JsonIgnoreProperties(unknown =true)
class Products{
#JsonProperty("name")
private String name;
#JsonProperty("code")
private String code;
#JsonProperty("price")
private String price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
}
Controller
public ResponseEntity<Object> create(#RequestBody Customer obj ) {
You need POJO structure with two classes:
public class Product {
private String name;
private String code;
private int price;
}
public class ProductsGroup {
private long id;
private List<Product> products;
// getters/setters
}
And change your method signature to:
#PostMapping(path="/create", consumes=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ProductsGroup> create(#RequestBody ProductsGroup productGroup)
{
System.out.println("Products :" + productGroup.getProducts());
}
You are trying to process the json using a Map<String, Object> obj, which could be possible in some way, but mostly what you want to do is define a single or multiple POJO classes. These represent the json.
public class IdWrapper {
private String id;
#JsonProperty("Products")
private List<Product> products;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<Product> getProducts() {
return products;
}
}
public class Product {
private String name;
private String code;
private String price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
}
And in you controller like this:
#RestController
#RequestMapping("test")
public class DemoController {
#PostMapping()
public void test(#RequestBody IdWrapper productsWrapper) {
System.out.println();
}
}

POST a list of items spring boot

#RestController
public class TopicController {
#Autowired
private TopicService topicService;
#RequestMapping(value="/topics", method= RequestMethod.GET)
public List<Topic> getAllTopics(){
return topicService.getAllTopics();
}
#RequestMapping(value="/topics/{id}", method= RequestMethod.GET)
public Topic getTopic(#PathVariable String id){
return topicService.getTopic(id);
}
#RequestMapping(value="/topics", method= RequestMethod.POST)
public void addTopic(#RequestBody Topic topic){
topicService.addTopic(topic);
}
#RequestMapping(value="/topics/{id}", method= RequestMethod.PUT)
public void updateTopic(#RequestBody Topic topic, #PathVariable String id){
topicService.updateTopic(id, topic);
}
#RequestMapping(value="/topics/{id}", method= RequestMethod.DELETE)
public void deleteTopic(#PathVariable String id){
topicService.deleteTopic(id);
}
}
Controller class
#Service
public class TopicService {
#Autowired
private TopicRepository topicRepo;
public List<Topic> getAllTopics(){
return (List<Topic>)topicRepo.findAll();
}
public Topic getTopic(String id){
return topicRepo.findOne(id);
}
public void addTopic(Topic topic){
//topics.add(topic);
topicRepo.save(topic);
}
public void updateTopic(String id, Topic topic) {
topicRepo.save(topic);
}
public void deleteTopic(String id) {
//topics.removeIf(t -> t.getId().equals(id));
//topics.removeIf((Topic t) -> t.getId().equals(id));
topicRepo.delete(id);
}
}
Service Class
#Repository
public interface TopicRepository extends CrudRepository<Topic, String>{
//List<Course> findByTopic_Id(String topicid);
}
Repository Class
#Entity
public class Topic {
#Id
#Column(name="TOPIC_ID")
private String id;
#Column(name="NAME")
private String name;
#Column(name="DESCRIPTION")
private String description;
#OneToMany(mappedBy="topic", fetch = FetchType.EAGER)
#JsonManagedReference
private List<Course> course = new ArrayList<Course>();
//no - argument constructor. Needed for hibernate
public Topic(){};
public Topic(String id, String name, String description, List<Course> course){
super();
this.id = id;
this.name = name;
this.description = description;
this.course = course;
}
public Topic(String id, String name, String description){
super();
this.id = id;
this.name = name;
this.description = description;
}
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 List<Course> getCourse() {
return course;
}
public void setCourse(List<Course> course) {
this.course = course;
}
}
Topic Class
#Entity
public class Course{
#Id
#Column(name="COURSE_ID")
private String id;
private String name;
private String description;
//There could be many courses related to 1 topic
#ManyToOne
#JoinColumn(name = "TOPIC_ID")
#JsonBackReference
private Topic topic;
public Course(){};
public Course(String id, String name, String description){
super();
this.id = id;
this.name = name;
this.description = description;
}
public Topic getTopic() {
return topic;
}
public void setTopic(Topic topic) {
this.topic = topic;
}
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;
}
}
Course Class
I am trying to use Postman to post a Topic class which contains many Courses into my sql database.
In Postman, I did the POST using JSON like this
{
"id": "700",
"name": "How to countt",
"description": "Counting numbersssss",
"course": [
{
"id": "1",
"name": "php",
"description": "gooddddyyyy stuff"
},
{
"id": "2",
"name": "phpp",
"description": "gooddddyyyy stuffp"
}
]
}
However, when i do the corresponding get all topics, my response was
{
"id": "700",
"name": "How to countt",
"description": "Counting numbersssss",
"course": []
}
It is not picking up the courses that i posted. One Topic can have many Courses. How do i fix this? Thank you
You never set the owning side of the bidirectional association: Course.topic.
And there is no cascade set on Topic.courses.
So, not only saving a topic won't save its courses, but even if it did, the courses would not belong to their topic.

Why the same id is incremented when I save an object in the repository?

Whenever I call save() method the same ID is shared between three different entities and I don't know why ?
#Entity
public class Department {
#Id
#GeneratedValue
private Long departmentId;
private String name;
public Department(Long departmentId) {
this.departmentId = departmentId;
}
public Department() {
}
public Department(String name) {
this.name = name;
}
public Long getDepartmentId() {
return departmentId;
}
public void setDepartmentId(Long departmentId) {
this.departmentId = departmentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Entity
public class Location {
#Id
#GeneratedValue
private Long locationId;
private String name;
public Location(Long locationId) {
this.locationId = locationId;
}
public Location() {
}
public Location(String name) {
this.name = name;
}
public Long getLocationId() {
return locationId;
}
public void setLocationId(Long locationId) {
this.locationId = locationId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And this is my Controller:
#RestController
public class SettingsController {
#Autowired
private LocationRepository locationRepository;
#Autowired
private DepartmentRepository departmentRepository;
#Autowired
private RoleRepository roleRepository;
#RequestMapping(value = "/api/locations", method = RequestMethod.POST)
public ResponseEntity addLocation(#RequestBody DataForm dataForm) {
if (dataForm == null) {
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
locationRepository.save(new Location(dataForm.getName()));
return new ResponseEntity(HttpStatus.CREATED);
}
#RequestMapping(value = "/api/roles", method = RequestMethod.POST)
public ResponseEntity addRole(#RequestBody DataForm dataForm) {
if (dataForm == null) {
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
roleRepository.save(new Role(dataForm.getName()));
return new ResponseEntity(HttpStatus.CREATED);
}
#RequestMapping(value = "/api/departments", method = RequestMethod.POST)
public ResponseEntity addDepartment(#RequestBody DataForm dataForm) {
if (dataForm == null) {
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
departmentRepository.save(new Department(dataForm.getName()));
return new ResponseEntity(HttpStatus.CREATED);
}
}
This should happen only if the id would be static, but It's not. If I create two new Location() objects, when I will create a new Department() the Id of the department will be 3. Why ?
Since you didn't specify the strategy for #GeneratedValue, I guess that Hibernate uses the same sequence for all your entities.
You can set something like
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="department_seq_gen")
#SequenceGenerator(name="department_seq_gen", sequenceName="DEPARTMENT_SEQ")
on Department entity, and something similar on Location entity (just use location_seq_gen and LOCATION_SEQ).

Objectify Java Reference

#Entity
public class Category {
#Id
private Long id;
private String name;
private String description;
#Load
private List<Ref<Subcategory>> subcategories = new ArrayList<Ref<Subcategory>>();
#Load
private Ref<Image> image;
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 getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<Subcategory> getSubcategories() {
List<Subcategory> scs = new ArrayList<Subcategory>();
for (Ref<Subcategory> sc : this.subcategories) {
scs.add(sc.get());
}
return scs;
}
public void setSubcategory(Subcategory subcategory) {
this.subcategories.add(Ref.create(subcategory));
}
public Image getImage() {
if(image != null) {
return image.get();
}
return null;
}
public void setImage(Image image) {
this.image = Ref.create(image);
}
}
#Entity
public class Subcategory {
#Id
private Long id;
private String name;
private String description;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class CategoryDTO {
private Long id;
#NotNull
private String name;
private String description;
private List<Subcategory> subcategories = new ArrayList<Subcategory>();
private Long imageId;
public CategoryDTO() {
}
public CategoryDTO(Category category) {
this.id = category.getId();
this.name = category.getName();
this.description = category.getDescription();
this.subcategories = category.getSubcategories();
if (category.getImage() != null) {
this.imageId = category.getImage().getId();
}
}
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 getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<Subcategory> getSubcategories() {
return subcategories;
}
public void setSubcategories(List<Subcategory> subcategories) {
this.subcategories = subcategories;
}
public Long getImageId() {
return imageId;
}
public void setImageId(Long imageId) {
this.imageId = imageId;
}
}
CategoryDAO
public class CategoryDAO {
private static final Logger log = Logger.getLogger(CategoryService.class.getName());
public static QueryResultIterator<Category> getCategories() {
QueryResultIterator<Category> categories = ofy().load().type(Category.class).iterator();
return categories;
}
}
public class SubcategoryDAO {
public static Subcategory createSubcategory(Long categoryId, Subcategory data) {
// save sub category
Subcategory subcategory = new Subcategory();
if (data.getName() != null) {
subcategory.setName(data.getName());
}
if (data.getDescription() != null) {
subcategory.setDescription(data.getDescription());
}
ofy().save().entity(subcategory).now();
Category category =
ofy().load().type(Category.class).id(categoryId).get();
category.setSubcategory(subcategory);
ofy().save().entity(category).now();
return subcategory;
}
}
CategoryService
#Path("/categories")
public class CategoryService {
#GET
#Produces(MediaType.APPLICATION_JSON)
public String getCategories() {
try {
List<CategoryDTO> categories = new ArrayList<CategoryDTO>();
QueryResultIterator<Category> cats = CategoryDAO.getCategories();
while (cats.hasNext()) {
categories.add(new CategoryDTO(cats.next()));
}
Map<String, List<CategoryDTO>> map = new HashMap<String, List<CategoryDTO>>();
map.put("categories", categories);
return Helper.prepareResponse(map);
} catch (Exception e) {
LogService.getLogger().severe(e.getMessage());
throw new WebApplicationException(500);
}
}
}
Problem:-
When i hit getCategories service, it is showing unexpected behaviour.Instead of showing all the subcategories, it is showing random no of different subcategories every time.
For example say,
first i save a category "c"
then i save subcategories "sa", "sb" and "sc"
On hitting getCategry service,
Expected Behaviour -
{
"status": 200,
"categories" : [{
"name":a,
"subcategories": [
{
"name":"sa"
},
{
"name":"sb"
},
{
"name":"sc"
}
]
}]
}
Outputs i get is something like this -
{
"status": 200,
"categories" : [{
"name":a,
"subcategories": [
{
"name":"sa"
},
{
"name":"sc"
}
]
}]
}
or
{
"status": 200,
"categories" : [{
"name":a,
"subcategories": [{
"name":"sb"
}]
}]
}
To summarize this question, you're performing a query (give list of all categories) and getting back inconsistent results.
This is the system working as advertised. Read this: https://cloud.google.com/appengine/docs/java/datastore/structuring_for_strong_consistency
Eventual consistency is something that you learn to live with and work around when you need it. There is no way to force a query to be strongly consistent without changing the structure of your data - say, put it under a single entity group - but that has repercussions as well. There is no free lunch if you want a globally replicated, infinitely-scalable database.
In addition to eventual consistency, the datastore has no defined ordering behavior if you do not specify a sort order in your query. So that might add to your confusion.
Welcome to the wonderful world of eventual consistency. When I encountered something like this, using ObjectifyService.begin() instead of ObjectifyService.ofy() resolved it. Unlike ofy(), begin() gets you fresh data every time.

Categories

Resources