Objectify filter entities with parent key - java

I have written a piece of code that fetches entities from google datastore by filtering the entity with the supplied parent key. When I run the code I am getting java.lang.IllegalArgumentException.
I know the problem is with the way I am creating the parent key, can you please guide me how to effectively create a parent key for this use case?
I am getting the below exception in Myservice.java line 8
Method threw 'java.lang.IllegalArgumentException' exception
- Class hierarchy for class java.lang.Class has no #Entity annotation
Appengine v1.9.36,
Objectify v5.1.7,
JDK v1.7
Below is a sample code
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Cache;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
#Entity
#Cache
public class ParentEntity {
#Id
Long id;
String name;
String value;
public static Key<ParentEntity> getKey(Long id){
return Key.create(ParentEntity.class, id);
}
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 getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
Another entity class
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Cache;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Parent;
#Entity
#Cache
public class ChildEntity {
#Id
Long id;
#Parent Key<ParentEntity> application;
String city;
public static Key<ChildEntity> getKey(Long id){
return Key.create(Key.create(ParentEntity.class), ChildEntity.class, id);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Key<ParentEntity> getApplication() {
return application;
}
public void setApplication(Key<ParentEntity> application) {
this.application = application;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
ServiceLaver that uses Objectify to fetch entities
import java.util.List;
import com.googlecode.objectify.ObjectifyService;
public class MyService{
public List<ChildEntity> filterByID(Long id){
return ObjectifyService.ofy().load().type(ChildEntity.class)
.filterKey(ChildEntity.getKey(id)).first().now();
}
}

change Your ParentEntity's method :
public static Key<ParentEntity> getKey(Long id){
return Key.create(ParentEntity.class, id);
}
to:
public String getWebSafeKey(){
return Key.create(ParentEntity.class, id).getString();
}
Now when you insert a parent entity then in response it will give you websafe key of that parent entity. Use this websafe key whenever you wants to access this inserted parent entity.
After that change:
public List<ChildEntity> filterByID(Long id){
return ObjectifyService.ofy().load().type(ChildEntity.class)
.filterKey(ChildEntity.getKey(id)).first().now();
}
To:
public List<ChildEntity> filterByID(String parentWebSafeKey){
return ObjectifyService.ofy().load().type(ChildEntity.class)
.ancestor(Key.create(parentWebSafeKey)).first().now();
}
Don't forget to create relationship between ParentEntity and ChildEntity while creating child entity using:
child_entity.application = Ref.create(parent_entity_key);

Related

How to relate doctor and patient entities in spring boot jpa

I'm very new to spring boot. I am creating a health centre management system where I have 2 entities Doctor & Patient.
There are few rules that are followed
There can many doctors in a centre
There can be multiple patients too
A doctor can see multiple patients a day
But a patient can only have an appoinment with a single doctor at a time.
This is my Doctor entity:
package com.sb.projects.java.spring.medical_api.entities;
import com.sun.istack.NotNull;
import javax.persistence.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
#Entity
public class Doctors {
#Id
#NotNull
private int id;
private String name;
private String email;
private String degree;
private String specialization;
#OneToMany
private Set<Patients> patient = new HashSet<>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getDegree() {
return degree;
}
public void setDegree(String degree) {
this.degree = degree;
}
public String getSpecialization() {
return specialization;
}
public void setSpecialization(String specialization) {
this.specialization = specialization;
}
public Set<Patients> getPatient() {
return patient;
}
public void setPatient(Set<Patients> patient) {
this.patient = patient;
}
}
This is my Patient entity:
package com.sb.projects.java.spring.medical_api.entities;
import com.sun.istack.NotNull;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
#Entity
public class Patients {
#Id
#NotNull
private int id;
private String name;
private String email;
private String contact_no;
#ManyToOne
private Doctors doctor;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getContact_no() {
return contact_no;
}
public void setContact_no(String contact_no) {
this.contact_no = contact_no;
}
public Doctors getDoctor() {
return doctor;
}
public void setDoctor(Doctors doctor) {
this.doctor = doctor;
}
}
Here are the few problems that I'm facing
I'm not sure about the type of relationship that I'm setting between the Doctor and Patient Enity is correct
If the relationship is correct then I'm not sure about the setPatient setter function in Doctor entity, that my implentation of the setter function is the right way of doing the thing or not
If the all the above points are okay then what will be a perfect mockup json object which will be in a http POST request body to test the Doctor Entity
Thanks for your help in advance.
I think you can create a Doctor without taking any patient into account something like this:
POST http://locahost:8080/v1/doctors
{
"name": "doctorName",
"email": "somemail#xyz.com",
"degree": "xyz",
"specialization": "a"
}
When you want to add a patient to your doctor then you would just call another endpoint to create a visit between your doctor and your patient
POST http://localhost:8080/visits/{patientId}
body...
{
"doctorId": idOfDoctor,
}
With this you would attack the patient's db repository to create a relation between your patient and your doctor.
It sounds extrange to me to relate directly the doctor with the patients, i would do a middle relation like "VISITS" with the day of visit and the hour...

CRUD API Spring Boot Make a Copy of an entry

I am trying to make a copy of an instance which I am fetching from a CRUD Repository. I want to store the copy of an instance but with a different primary key. In the copy method which I have made in the service class, when I try to make a copy, it throws an error saying org.hibernate.HibernateException: identifier of an instance of SpringBootStarter.Topic.Topic was altered from <id> to <new_id> When I hit a GET request on postman after making the copy, I want to see both the original and the copy in the result (but the copy with a different primary key.)
Can somebody please help me?
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
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 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;
}
}
Below is the Controller Class
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
public class TopicController {
#Autowired
private TopicService topicService;
#GetMapping("/topics")
public List<Topic> getAllTopics(){
return topicService.getAllTopics();
}
#GetMapping("/topics/{id}")
public Topic getTopic(#PathVariable String id){
return topicService.getTopic(id);
}
#PostMapping("/topics")
public void addTopic(#RequestBody Topic topic){
topicService.addTopic(topic);
}
#PostMapping("topics/{id}/{new_id}")
public void copyTopic(#PathVariable String id, #PathVariable String new_id){
topicService.copyTopic(id, new_id); }
#PutMapping("/topics/{id}")
public void updateTopic(#RequestBody Topic topic, #PathVariable String id){
topicService.updateTopic(topic, id);
}
#DeleteMapping("/topics/{id}")
public void deleteTopic(#PathVariable String id){
topicService.deleteTopic(id);
}
}
Below is the Service Class
package SpringBootStarter.Topic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
#Service
public class TopicService {
#Autowired
private TopicRepository topicRepository;
public List<Topic> getAllTopics(){
List<Topic> topics = new ArrayList<>();
topicRepository.findAll().forEach(topics :: add);
return topics;
}
public Topic getTopic(String id){
Optional<Topic> optional = topicRepository.findById(id);
return optional.get();
}
public void addTopic(Topic topic){
topicRepository.save(topic);
}
public void copyTopic(String id, String new_id){
Topic topic = topicRepository.findById(id).get();
Topic topicCopy = topic;
topicCopy.setId(new_id);
addTopic(topicCopy);
}
public void updateTopic(Topic topic, String id){
topicRepository.save(topic);
}
public void deleteTopic(String id){
topicRepository.deleteById(id);
}
}
Below is the Topic Repository
import org.springframework.data.repository.CrudRepository;
public interface TopicRepository extends CrudRepository<Topic, String> {
}
The persistence context holds the lifecycle of all entities. When you fetch an entity it will be an attached entity within that transsaction. Because the reference of your object does not change, the persistence context will know that it's still the same object in the database which does not allow it's identifier to change.
If you want to create a new entry, you will have to create a new object using the new keyword and persist that one.
So instead of changing the identifier like so
public void copyTopic(String id, String new_id){
Topic topic = topicRepository.findById(id).get();
Topic topicCopy = topic;
topicCopy.setId(new_id);
addTopic(topicCopy);
}
Create a new Topic entity and persist it like so
public void copyTopic(String id, String new_id){
Topic topic = topicRepository.findById(id).get();
Topic topicCopy = new Topic(topic);
topicCopy.setId(new_id);
addTopic(topicCopy);
}
My advice is to read up on the basics of Hibernate because there are a lot of pitfalls when using an ORM. It's never a good idea to start using one without understanding the very basics.

#Id doesn't create primary key

In my Spring Boot database project, I wanted this 'id' field to be auto-generated, unique Primary Key, but it's not. When I check the H2 database GUI there the Primary Key is some obscure, hidden 3rd column, which doesn't even list when I query the table. I attached a screenshot of it.
Screenshot of H2 database GUI:
This is my #Entity class.
package com.schabby.springdb;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class Entry implements Serializable {
#GeneratedValue(strategy=GenerationType.AUTO)
#Id
private Long id;
private String text;
public Entry() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
Try this:
#Entity
public class Entry implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String text;
public Entry() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
Here IDENTITY indicates that the persistence provider must assign primary keys for the entity using the database identity column.
You can refer below reference to know more about different GenerationType.
Reference: https://docs.oracle.com/javaee/5/api/javax/persistence/GenerationType.html

Eclipse 'generate entities from tables' and 'many to one' relation

i am developing a simple Java EE application, that uses database. It has two tables (Admin and Session signature) connected with many to one relation.
When i used eclipse to generate entities from tables, my attribute that links both tables was generated like this:
//bi-directional many-to-one association to Admin
#ManyToOne
#JoinColumn(name="owner")
private Admin admin;
Problem is, my owner attribute is Integer in database, and it has been created as Admin type.
Now when i want to pass some Integer variable to input it to database i get error:
The method setAdmin(Admin) in the type Signaturesession is not applicable for arguments (int).
Or when i want to cast it to (Admin) like this (taking it from session):
(Admin)session.getAttribute("adminId")
I get Jboss Error:
javax.servlet.ServletException: java.lang.ClassCastException: java.lang.Integer cannot be cast to com.podpisy.entities.Admin
javax.faces.webapp.FacesServlet.service(FacesServlet.java:606)
secure.SecurityCheckFilter.doFilter(SecurityCheckFilter.java:100)
I am sure that this can be done easy, but i'm just really bad using Java.
Thanks for any help.
EDIT:
My Admin.java class:
package com.podpisy.entities;
import java.io.Serializable;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name="admins")
#NamedQuery(name="Admin.findAll", query="SELECT a FROM Admin a")
public class Admin implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private int id;
private String login;
private String password;
//bi-directional many-to-one association to Signature
#OneToMany(mappedBy="admin")
private List<Signature> signatures;
//bi-directional many-to-one association to Signaturesession
#OneToMany(mappedBy="admin")
private List<Signaturesession> signaturesessions;
public Admin() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getLogin() {
return this.login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public List<Signature> getSignatures() {
return this.signatures;
}
public void setSignatures(List<Signature> signatures) {
this.signatures = signatures;
}
public Signature addSignature(Signature signature) {
getSignatures().add(signature);
signature.setAdmin(this);
return signature;
}
public Signature removeSignature(Signature signature) {
getSignatures().remove(signature);
signature.setAdmin(null);
return signature;
}
public List<Signaturesession> getSignaturesessions() {
return this.signaturesessions;
}
public void setSignaturesessions(List<Signaturesession> signaturesessions) {
this.signaturesessions = signaturesessions;
}
public Signaturesession addSignaturesession(Signaturesession signaturesession) {
getSignaturesessions().add(signaturesession);
signaturesession.setAdmin(this);
return signaturesession;
}
public Signaturesession removeSignaturesession(Signaturesession signaturesession) {
getSignaturesessions().remove(signaturesession);
signaturesession.setAdmin(null);
return signaturesession;
}
}
My Signaturesession.class:
package com.podpisy.entities;
import java.io.Serializable;
import javax.persistence.*;
/**
* The persistent class for the signaturesession database table.
*
*/
#Entity
#NamedQuery(name="Signaturesession.findAll", query="SELECT s FROM Signaturesession s")
public class Signaturesession implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private int id;
private String device;
private String name;
private int signatures;
private int time;
private String type;
private int users;
//bi-directional many-to-one association to Admin
#ManyToOne
#JoinColumn(name="owner")
private Admin admin;
public Signaturesession() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getDevice() {
return this.device;
}
public void setDevice(String device) {
this.device = device;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getSignatures() {
return this.signatures;
}
public void setSignatures(int signatures) {
this.signatures = signatures;
}
public int getTime() {
return this.time;
}
public void setTime(int time) {
this.time = time;
}
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
public int getUsers() {
return this.users;
}
public void setUsers(int users) {
this.users = users;
}
public Admin getAdmin() {
return this.admin;
}
public void setAdmin(Admin admin) {
this.admin = admin;
}
}
You should pass an Admin object which surely has an int id field.
So you've to make something like this
Admin myAdmin=new Admin(id,.. other properties);
mySignaturesession.setAdmin(myAdmin);
EDIT
Above is valid if you want to associate and Admin to your SignatureSession object. Instead if you have an Admin ojbect in Session you just have to execute
Admin anAdmin=(Admin)session.getAttibute("adminId");
Admin myAdmin=new Admin(id,.. other properties);
or
Admin myAdmin=new Admin();
myAdmin.setId(anId);
But, i repeat, it depends from what you have in the Session and which objects you handle.
And, as you look to be using JPA, dont forget to do something like em.persist or em.merge on your objects.
Maybe you should get a little deeper on how JPA works.

JPA Many to Many cascade problem

If I create a Customer and Controller, then associate my Controller with a customer it saves fine.
If I then remove my controller it doesn't remove the relationship between them.
This causes an EntityNotFoundException when I load the Customer.
javax.persistence.EntityNotFoundException: Unable to find Controller with id 22
I'd like to know how to map this so that when a Controller is deleted the relationship is also deleted.
Database Tables
customer
controller
customer_controllers - mapping table.
The Controller's id is not getting removed from the customer_controllers mapping table.
#Entity
public class Customer implements Serializable{
private Integer id;
private Set<Controller> controllers;
#Id
#GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#ManyToMany(cascade={CascadeType.ALL})
public Set<Controller> getControllers()
{
return controllers;
}
public void setControllers(Set<Controller> controllers)
{
this.controllers = controllers;
}
}
#Entity
public class Controller implements Serializable{
private Integer id;
private String name;
private String abbreviation;
#Id
#GeneratedValue
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getAbbreviation()
{
return abbreviation;
}
public void setAbbreviation(String abbreviation)
{
this.abbreviation = abbreviation;
}
}
If you have a ManyToMany then you should map Controller to Customer with a
#ManyToMany(mappedBy="controllers")
or the other way around, depending on which side is the owning side.
As you have it now the relation is not fully defined and it will fail on events like "Cascade".
Have you checked the javadoc for #ManyToMany?
It includes the above example mappings.
you need to make the relationship bidirectional, so that the controller object is aware of its relationship to the customer. Yhis means that when the controller is deleted the record in the join table is also deleted.
This isn't the exact mapping but it gives you the idea.
#Entity
public class Controller implements Serializable{
private Integer id;
private String name;
private String abbreviation;
private Set<Customer> customers;
#Id
#GeneratedValue
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getAbbreviation()
{
return abbreviation;
}
public void setAbbreviation(String abbreviation)
{
this.abbreviation = abbreviation;
}
#ManyToMany(cascade={CascadeType.ALL})
public Set<Customer> getCustomers()
{
return customers;
}
public void setCustomers(Set<Customers> customers)
{
this.customers= customers;
}
}

Categories