Java multiple join query - java

I am trying to create a REST service in Java with Spring Boot.
I have 2 tables Topic and Course and i want to retrieve NAME and DESCRIPTION from Topic and PRICE from Course.
The connection between these 2 tables is made with TOPIC_ID from Course.
Result MUST be a JSON.
[
{
"id": "course1",
"name": "name course1",
"description": "course1"
},
{
"id": "course2",
"name": "course2 name",
"description": "course2"
},
{
"id": "course3",
"name": "course3 name",
"description": "course3"
}
]
The query is below.
I know it's possible with DTO and JPA but I need to write a lot of code for a simple database query.
Thank you.
package com.example.course;
import com.example.topic.Topic;
import javax.persistence.*;
#Entity
#Table(name = "topic", schema = "topic")
public class Topic {
#Id
private String id;
private String name;
private String description;
public Topic() {
}
public Topic(String id, String name, String description) {
this.id = id;
this.name = name;
this.description = description;
}
public String getDescription() {
return description;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setDescription(String description) {
this.description = description;
}
}
package com.example.topic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "course", schema = "topic")
public class Course {
#Id
#Column(name = "ID")
private String id;
#Column(name = "NAME")
private String name;
#Column(name = "DESCRIPTION")
private String description;
#Column(name="PRICE")
private Integer price;
#ManyToOne
#JoinColumn(name="TOPIC_ID", nullable=false)
private Topic topic;
public Course() {
}
public Course(String id, String name, String description, String topicId) {
this.id = id;
this.name = name;
this.description = description;
this.topic = new Topic(topicId, "", "");
}
public String getDescription() {
return description;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setDescription(String description) {
this.description = description;
}
public Topic getTopic() {
return topic;
}
public void setTopic(Topic topic) {
this.topic = topic;
}
public void setPrice(Integer price) { this.price = price; }
public Integer getPrice() { return price; }
}
package com.example.dto;
public class TopicDescDTO {
private String id;
private String name;
private String description;
public TopicDescDTO(String id, String name, String description) {
this.id = id;
this.name = name;
this.description = description;
}
public TopicDescDTO() {
}
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;
}
}
select t.name, t.description, c.price as course_price
from topic.course c
inner join topic.topic t on t.id = c.topic_id

Take advantage of the projection interface offered by Spring Data JPA:
public interface TopicDescDTO{
Integer getPrice();
String getName();
String getDescription();
}
and then just land a proper method in one of your repositories:
#Query(select t.name, t.description, c.price
from Course c
inner join c.topic t)
List<TopicDescDTO> getTopicDescs();
Spring will do the mapping for you.

Related

Why this code can't POST anything using Rest API?

Do you see why this code doesn't work? I can't add any topic to my list through function addTopic (I get a error message attached at the end).
private List<Topic> topics = new ArrayList<>(Arrays.asList(
new Topic("java", "back", "java description"),
new Topic("html", "front", "html description"),
));
public void addTopic(Topic topic) {
topics.add(topic);
}
#PostMapping("/topics")
public void addTopic(#RequestBody Topic topic) {
topicService.addTopic(topic);
}
public class Topic {
private String id;
private String name;
private String description;
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;
}
}
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.http.converter.HttpMessageNotReadableException",
#PostMapping(path = "/***", consumes = "application/json", produces = "application/json")
public void addTopic(#RequestBody Topic topic) {
//code
}

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.

File in Database Object POJO (Springboot)

Say I have a SQL DO named "Team" that has a name, description, id, and profile picture.
It would look something like this:
#Entity
#Table(name = "Team")
public class TeamDO implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="team_id")
private Long id;
private String description;
private String name;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="user_id", nullable = false)
private Users users;
public TeamDO() {}
public TeamDO(Users user) {
this.setUsers(user);
}
#Override
public String toString() {
return String.format(
"TeamDO[id=%d, inital='%s', description='%s', name='%s']"
, getId()
, getUsers().getInitialName()
, getDescription()
, getName()
);
}
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 Users getUsers() {
return users;
}
public void setUsers(Users users) {
this.users = users;
}
}
What kind of variable stores a file such as a jpg? Or would my POJO have a String profilePictureURL which would be used to fetch the image (logo) for each team?
It's preferred to store the file in a hosting service of some sort and save only the URL in the DB but you can also save it as a byte array.

Trouble with one to many relationship hibernate

hello i'm trying to learn one to many mapping but i really having trouble with hibernate. I was able to persist to database but when trying to apply one to many relationship it doesn't persist to DB and also doesn't display the relationship when viewing the response body in postman. I really need help been on this problem since yesterday morning. I have looked on tutorials on youtube and on internet but every tutorial seem basic and when applying same idea no success. I have an entity person and another entity organization. A person can belong up to one organization but different persons can belong to the same organization. So my approach was using a one to many relationship.
Below is my entity of Organization:
#Entity
#Table(name="organization")
public class Organization {
#Id
#Column(name="org_Id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name, description;
#OneToMany(/*fetch = FetchType.EAGER, cascade = CascadeType.ALL*/)
#JoinTable(joinColumns = #JoinColumn(name="org_Id"),
inverseJoinColumns = #JoinColumn(name="person_Id"))
// #JsonIgnore
//#JoinColumn(name="org_Id")
private Collection<Person> personCollection = new ArrayList<Person>();
public Collection<Person> getPersonCollection() {
return personCollection;
}
public void setPersonCollection(Collection<Person> personCollection) {
this.personCollection = personCollection;
}
private Address address;
public Organization() {}
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 Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
And below is my Person entity:#Entity
#Table(name = "Person")
public class Person {
#Id
#Column(name="person_Id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name="Email",unique = true)
private String email;
#Column(name="FirstName")
private String first_name;
#Column(name="LastName")
private String last_name;
#Column(name="Description")
private String description;
//#Embedded
private Address address;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonIgnore
private Organization organization;
public Organization getOrganization() {
return organization;
}
public void setOrganization(Organization organization) {
this.organization = organization;
}
public Person() {}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
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 getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
when trying to create person. Below is my create method:
public Person createPerson(String f_name, String l_name, String email, String city, String state,
String zipCode, String street, String description, Long id) {
Person person = null;
//f_name, l_name, email are required parameters if empty return null and throws an exception..
if(f_name.isEmpty() || l_name.isEmpty() || email.isEmpty()) {
return person;
}
else {
Session session = null;
Transaction transaction = null;
try {
session = sessionFactory.openSession();
transaction = session.beginTransaction();
person = new Person();
person.setFirst_name(f_name);
person.setLast_name(l_name);
person.setEmail(email);
person.setDescription(description);
Address address = new Address();
address.setStreet(street);
address.setZipCode(zipCode);
address.setState(state);
address.setCity(city);
person.setAddress(address);
/* checks to see if id of organization exist if so add to list if not don't do anything.*/
if(id!=null) {
Organization organization = session.get(Organization.class, id);
if (organization != null) {
/* adds id of organization to person table and vice versa.*/
person.setOrganization(organization);
organization.getPersonCollection().add(person);
} else {
//do nothing
}
}
session.save(person);
transaction.commit();
} catch (HibernateException ex) {
if (transaction != null)
transaction.rollback();
ex.printStackTrace();
} finally {
if (session != null)
session.close();
}
return person;
}
}
I am able to create both person and organization and persist to database. But when i try to add an organization to a person Row in database i cannot add the relationship(verified when i tried looking up database itself) and also no response as i get a lazy initialization collection error as well. Please has anyone encountered this problem
I just executed the code snippet you gave in hibernate with the following simplified structure which works perfectly fine. You should start from here and modify as per your needs.
Entity
#Table(name="organization")
public class Organization {
#Id
#Column(name="org_Id")
private long id;
private String name, description;
#OneToMany(cascade = CascadeType.ALL)
private Collection<Person> personCollection = new ArrayList<Person>();
public Collection<Person> getPersonCollection() {
return personCollection;
}
public void setPersonCollection(Collection<Person> personCollection) {
this.personCollection = personCollection;
}
public Organization() {}
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;
}
}
and
#Entity
#Table(name = "Person")
public class Person {
#Id
#Column(name="person_Id")
private long id;
#Column(name="Email",unique = true)
private String email;
#Column(name="FirstName")
private String first_name;
#Column(name="LastName")
private String last_name;
#Column(name="Description")
private String description;
#ManyToOne()
private Organization organization;
public Organization getOrganization() {
return organization;
}
public void setOrganization(Organization organization) {
this.organization = organization;
}
public Person() {}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
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 getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
EDIT : The CascadeType.ALL from Person side of the relationship has been moved to the Organization side of the relationship.Because you want when organization is deleted Person should also get deleted, but not the other way round.

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