I keep getting the same error whenever I try to delete an entity using Morphia:
org.mongodb.morphia.query.ValidationException: The field '_id' could not be found in ...
while validating - _id; if you wish to continue please disable validation.
I don't really want to disable validation. I just want to delete the object.
Here is my delete method of the service I'm writing:
public void delete(ObjectId id) {
BaseMaterial baseMaterial = this.findOne(id);
WriteResult writeResult = this.repo.delete(baseMaterial);
}
and here is the findOne method in the same service (i.e. this.findOne)
public BaseMaterial findOne(ObjectId id) {
Query<BaseMaterial> query = repo.createQuery(BaseMaterial.class);
return Optional.ofNullable(query.field("id").equal(id).get())
.orElseThrow(() -> new DataRetrievalFailureException(
"Failed to fetch " + this.getClass().getName() + " with id " + id.toString()));
}
And here is my POJO:
#Entity("baseMaterial")
public class BaseMaterial {
#Id
#NotNull
protected ObjectId id;
#NotEmpty
private String name;
private String description;
public String get_id() {
return this.id.toString();
}
#JsonIgnore
public ObjectId getId() {
return id;
}
public void setId(ObjectId 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;
}
}
The findOne method of shown above works as expected so I really have no idea why this is happening.
Any ideas?
I don't see a field annotated with #Id...
Related
I have Entity with 3 fields: id, lastname and phoneNumber. I want to create method which works for update all fields or only one or two.
I use Hibernate and JPA Repository.
When I try to update all fields everything works well but when for example i want to update only lastname without changing of phoneNumber I have in output null insted of old phoneNumber.
Here is my method from Controller:
#PutMapping("/students/update/{id}")
public String updateStudentById(#ModelAttribute Student student, #ModelAttribute StudentDetails studentDetails,
String lastname, String phoneNumber,
#PathVariable Long id) {
Optional<Student> resultOptional = studentRepository.findById(id);
//Student result =resultOptional.get();
resultOptional.ifPresent((Student result) -> {
result.getStudentDetails().setPhoneNumber(studentDetails.getPhoneNumber()); result.getStudentDetails().setLastname(studentDetails.getLastname());
studentRepository.save(result);
});
return "Student updated";
}
The class for update:
#DynamicUpdate
#Entity
public class StudentDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="lastname")
private String lastname;
#Column(name="phone_number")
private String phoneNumber;
public StudentDetails() {
}
public StudentDetails(Long id, String lastname, String phoneNumber) {
this.id = id;
this.lastname = lastname;
this.phoneNumber = phoneNumber;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
The class which has relation with StudentDetails:
#Entity
#Table(name = "student")
#DynamicUpdate
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "email")
private String email;
//#OneToMany(mappedBy = "student")
#ManyToMany
#JoinTable(name="course_student",joinColumns = #JoinColumn(name="student_id"),
inverseJoinColumns = #JoinColumn(name="course_id"))
private List<Courses> courses;
#OneToOne(cascade = CascadeType.ALL)
// #JoinColumn(name="studen/_details_id") // with this we have dobule student_details column
private StudentDetails studentDetails;
public List<Courses> getCourses() {
return courses;
}
public void setCourses(List<Courses> courses) {
this.courses = courses;
}
public StudentDetails getStudentDetails() {
return studentDetails;
}
public void setStudentDetails(StudentDetails studentDetails) {
this.studentDetails = studentDetails;
}
// Methods for StudentViewController
public String getLastname(){
return studentDetails.getLastname();
}
public String getPhoneNumber(){
return studentDetails.getPhoneNumber();
}
public Student() {
}
public Student(String name, String email, StudentDetails studentDetails) {
// this.id = id;
this.name = name;
this.email = email;
this.studentDetails = studentDetails;
}
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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
'}';
}
}
I was looking for solution and I added #DynamicUpdate but still it doesn't work.
Your code works properly. When you only provide lastName parameter in your request, then the phoneNumber parameter will be mapped to null so you override the phoneNumer property in your entity with this null value.
Change the code in the following way:
resultOptional.ifPresent((Student result) -> {
if(studentDetails.getPhoneNumber()!=null) {
result.getStudentDetails().setPhoneNumber(studentDetails.getPhoneNumber());
}
if(studentDetails.getLastname()!=null) {
result.getStudentDetails().setLastname(studentDetails.getLastname());
}
studentRepository.save(result);
});
Unfortunately it raises an other problem: How will you delete these fields? (How can you set them explicitly to null? )
A possible solution if you check for the "" (empty string) and set the property to null if the parameter is empty string.
It will be a quite messy code anyway...
You should consider using the Spring Data Rest package. It automatically creates all of the standard REST endpoints for your entities and handles all of these PUT/PATCH/POST/DELETE issues out of the box.
why don't you just set the params of your request in you setters?
resultOptional.ifPresent((Student result) -> {
result.getStudentDetails().setPhoneNumber(phoneNumber);
result.getStudentDetails().setLastname(lastname);
studentRepository.save(result);
});
You forget set #OneToOne mapping in StudentDetails - StudentDetails also need field of type Student which will be annotated #OneToOne.
Also you have to ensure, that all of entity fields will be filled - read more about fetch types.
I have a method to verify a recipient of an email.
In my code .map(Recipient::getId) produces the error :
Non static method cannot be reference from a static context.
private Long verifyRecipient(Long recipientId) throws NotFoundException {
return Optional.ofNullable(recipientRepository.findById(recipientId))
.map(Recipient::getId)
.orElseThrow(()-> new NotFoundException("recipient with ID" + recipientId +
" was not found"));
}
Recipient class:
#Entity
public class Recipient {
#Id
#GeneratedValue
private Long id;
#NotBlank
private String name;
#NotBlank
#Email
#Column(unique = true)
private String emailAddress;
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 getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
}
I am using SpringBoot and H2 in memory database.
So I also have a RecipientRepository interface:
public interface RecipientRepository extends JpaRepository<Recipient, Long> {}
Defination of the findById() method:
Optional<T> findById(ID var1);
The method findById() already returns an Optional<T>, so you don't need to wrap the result with additional Optional.ofNullable() in this situation.
Actually, the line:
Optional.ofNullable(recipientRepository.findById(recipientId));
returns Optional<Optional<Recipient>>, which is redundant.
Instead, you can just write:
private Long verifyRecipient(Long recipientId) throws NotFoundException {
return recipientRepository.findById(recipientId)
.map(Recipient::getId)
.orElseThrow(() ->
new NotFoundException("Recipient with ID " + recipientId + " was not found"));
}
I'm using Spring mvc and Hibernate with AliasToBeanResultTransformer. When I get data from jsp, I received an exception from SQL.
Model class:
#Entity
#Table(name="CARS")
public class Car {
#Id
#Column(name="ID", nullable=false, unique=true)
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#Column(name="NAME", length=50)
private String name;
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;
}
}
This method get data from service:
public List<Car> getAllCars() {
Session session = HibernateUtils.getSession(sessionFactory);
String sqlQuery = GetSqlUtils.getSqlQueryString(CarRepositoryImpl.class, SQL_DIR + GET_ALL_CARS);
List<Car> list = null;
try {
Query query = session.createSQLQuery(sqlQuery);
list = query.setResultTransformer(new AliasToBeanResultTransformer(Car.class)).list();
} catch (HibernateException e) {
logger.error("error at CarRepositoryImpl.getAllCars: " + e.getMessage());
} finally {
HibernateUtils.closeCurrentSession(session);
}
return list;
}
SQL:
SELECT CAR.ID AS ID, CAR.NAME AS NAME
FROM
CARS CAR
Log message:
CarRepositoryImpl - error at CarRepositoryImpl.getAllCars: Could not find setter for ID on class com.tct.web.model.Car
How to fix this error ? thank so much !
Your fields are declared in lowercase and you use uppercase aliases.
Try to change them so they are exactly as the field names:
SELECT CAR.ID AS id, CAR.NAME AS name
change int to Integer for id
private Integer id;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
I am learning google app engine with datastore for my next project. I have made a sample app for the same.
Here are the code for entities:
#Entity
public class Quote {
#Id
private Long id;
#Parent #Load
private Ref<Author> author;
public Quote() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Author getAuther() {
return author.get();
}
public void setAuther(Author author) {
this.author = Ref.create(author);
}
}
#Entity
public class Author {
#Id
private Long id;
String name;
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;
}
}
and I am inserting a Quote using this API
#ApiMethod(
name = "insert",
path = "quote",
httpMethod = ApiMethod.HttpMethod.POST)
public Quote insert(Quote quote) {
ofy().save().entity(quote).now();
return ofy().load().entity(quote).now();
}
When I try to insert a new quote, I get my author.get() as null. I am stuck in this problem from a long time and I am not able to continue learning.
Thanks.
I was not inserting Auther before inserting Quote. You can either hide it within the Entity model or you can do it separately in an API call.
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;
}
}