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.
Related
I'm getting an error where the entity class's variable is not found while the variable is clearly there and the entity class is found
#RestController
public class LanguageController {
#Autowired
private LanguageService service;
#RequestMapping("/language")
public List<Language> allLanguages() {
return service.getAllLanguages();
}
#RequestMapping("/language/{id}")
public Optional<Language> allLanguagesById(#PathVariable String id) {
return service.getLanguagesById(id);
}
#RequestMapping(value = "/language", method = RequestMethod.POST)
public void addTopic(#RequestBody Language topic) {
service.addLanguage(topic);
}
// addLanguage used to save/add depending if obj already exists
// Will need more variable in obj for it to truly update
#RequestMapping(value = "/language/{id}", method = RequestMethod.PUT)
public void updateTopic(#RequestBody Language lang) {
service.addLanguage(lang);
}
#RequestMapping(value = "/language/{id}", method = RequestMethod.DELETE)
public void deleteTopics(#PathVariable String id) {
service.deleteLanguage(id);
}
}
entity class
package com.Alex.language;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import com.Alex.language.Language;
// JPA to create columns according it
// Act as table labels
#Entity
public class Topics {
// Mark primary key
#Id
private String id;
private String name;
private String description;
// Many topics to one language
#ManyToOne
private Language lang;
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 Topics() {
}
public Topics(String id, String name, String description, String langId) {
super();
this.id = id;
this.name = name;
this.description = description;
this.lang = new Language(langId);
}
public Language getLang() {
return lang;
}
public void setLang(Language lang) {
this.lang = lang;
}
}
package com.Alex.language;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class Language {
#Id
private String langId;
public String getLangId() {
return langId;
}
public void setLangId(String langId) {
this.langId = langId;
}
public Language(String langId) {
super();
this.langId = langId;
}
public Language() {
}
}
Controller class
package com.Alex.language;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.Alex.language.Language;
// Used to create RESTful web services
#RestController
public class TopicsController {
// As the database is embedded, will have to add data before able to see in Postman
#Autowired
private TopicService service;
#RequestMapping("/language/{id}/topics")
public List<Topics> getAllTopics(#PathVariable String id) {
return service.getAllTopicsByLanguageId(id);
}
// #PathVariable get variable from #RequestMapping {id}
#RequestMapping("/language/{langId}/topics/{id}")
public Optional<Topics> getSpecifiedTopic(#PathVariable String id) {
return service.getTopic(id);
}
// #RequestBody to convert JSON object to Java object to be used
#RequestMapping(value = "/language/{langId}/topics", method = RequestMethod.POST)
public void addTopic(#RequestBody Topics topic, #PathVariable String langId) {
topic.setLang(new Language(langId));
service.addTopic(topic);
}
#RequestMapping(value = "/language/{langId}/topics/{id}", method = RequestMethod.PUT)
public void updateTopic(#PathVariable String langId, #RequestBody Topics topic) {
topic.setLang(new Language(langId));
service.updateTopic(topic);
}
#RequestMapping(value = "/language/{langId}/topics/{id}", method = RequestMethod.DELETE)
public void deleteTopics(#PathVariable String id) {
service.deleteTopic(id);
}
}
So, I get the following error message when I type in the url something like this in Postman. localhost:8080/language/java/topics ( POST )
message=Unable to find com.Alex.language.Language with id langId; nested exception is javax.persistence.EntityNotFoundException: Unable to find com.Alex.language.Language with id langId, path=/language/java/topics}]
EDIT: Remove " " between langId which makes a literal String. Silly me, this is one of the mistakes I make most commonly :/
Solving that, I'm getting " Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'langId' in value "/language/${langId}/topics/{id}". I believe there is some configuration problem in main or so.
I am new to spring boot, I was trying to create a simple REST service using Spring-boot JPA. First I used simple #Repository which works fine, I can write custom Query method for the properties defined in my class. But when I use #RepositoryRestResource I am not able to create custom Query methods. Basic finding on primary key and other operations are happening but I can not create any custom Query.
package com.example.demo.Model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
#Entity
public class Book {
#Id
#GeneratedValue
int id;
int isbn;
String name;
String author;
double price;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getIsbn() {
return isbn;
}
public void setIsbn(int isbn) {
this.isbn = isbn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
This is Repository
package com.example.demo.repo;
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 org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.stereotype.Repository;
import com.example.demo.Model.Book;
#RepositoryRestResource(collectionResourceRel = "books", path = "books")
public interface BookRepo extends JpaRepository<Book, Integer> {
//I want this query to work same as it does for primary Key id.
public Book findByName(#Param("name") String name);
//Same for this
public Book findByIsbn(#Param("isbn") String isbn);
}
Since I am not mapping any url for searching by name, I am trying to search like this localhost:8080/books/?name=java Is it correct?
For above url it simply behaves like localhost:8080/books and ignore subsequent part and provides all books details.
I just want to have two classes and perform all basic rest operation and create custom Query.
Thank you in advance.
you need add the therm "search" and the method name in yout uri, like this:
localhost:8080/books/search/findByName?name=java
I'm using CrudRepository for database operations in my project. When I update an persisted data with save method whis is implemented by Spring, other fields are being overrided. Such as, I'm sending only firstName for updating but lastName is being converted to empty field.
Simply, I'm calling the save method with entity like this:
memberRepository.save(memberForUpdate);
I'm sending this JSON to update method:
{"id":2,"firstName": "Rubens","lastName": "Barichello"}
Spring converts this JSON to Member at the rest api endpoint. Member.java is:
package com.ilkaygunel.entities;
import java.time.LocalDateTime;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
#Entity
#JsonInclude(Include.NON_NULL)
public class Member {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String firstName;
private String lastName;
private String email;
private boolean enabled;
private String password;
private String memberLanguageCode;
private String activationToken;
private LocalDateTime activationTokenExpDate;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id")
private MemberRoles roleOfMember;
#Override
public String toString() {
return String.format("Member [id=%d, firstName='%s', lastName='%s', email='%s']", id, firstName, lastName,
email);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public MemberRoles getRoleOfMember() {
return roleOfMember;
}
public void setRoleOfMember(MemberRoles roleOfMember) {
this.roleOfMember = roleOfMember;
}
public String getActivationToken() {
return activationToken;
}
public void setActivationToken(String activationToken) {
this.activationToken = activationToken;
}
public LocalDateTime getActivationTokenExpDate() {
return activationTokenExpDate;
}
public void setActivationTokenExpDate(LocalDateTime activationTokenExpDate) {
this.activationTokenExpDate = activationTokenExpDate;
}
public String getMemberLanguageCode() {
return memberLanguageCode;
}
public void setMemberLanguageCode(String memberLanguageCode) {
this.memberLanguageCode = memberLanguageCode;
}
}
How can I prevent overriding other fields and provide updating only sended fields?
The save method itself behaves like saveorUpdate, so the problem here is with your code.
What you are doing is only set some values values any not other values. So when you send partial json to spring it sets other values to their default values. Now the problem is here, null is valid value in database. So the CrudRepository is updating the all the values along with null value as it considers you want to update to the database with the values in the object. Consider your scenario here:
Json sent to Spring :{"id":2,"firstName": "Rubens","lastName": "Barichello"}
Object generated by Spring:
private long id=2;
private String firstName="Rubens";
private String lastName="Barichello";
private String email=null;
private boolean enabled=false;
private String password=null;
private String memberLanguageCode=null;
private String activationToken=null;
private LocalDateTime activationTokenExpDate=null;
So what you should do is get the object with findById(ID id) method, set the values and then call the save(S entity) method.
Eg.
memberfromspring;
memberforupdate= memberRepository.findById(memberfromspring.getId());
memberforupdate.setFirstName(memberfromspring.getFirstName());
memberforupdate.setLastName(memberfromspring.getLastName());
memberRepository.save(memberForUpdate);
The reason for this is in my Selenium tests, I am mocking the REST services to return POJOs with hardcoded values, which represents my dummy data. One of the pages requires a list of objects who has heaps of fields and children Java objects (think Person has List, List, etc.).
A quick way I did was generate a JSON string from one of the REST services that pulls from the database. So now, I have a JSON string which I saved as a file and can load into my Selenium test as my hardcoded data. However, I want to maintain this in the Java code rather than a separate file, the data.json file.
Is there a way to generate Java code, which is basically lines and lines of setters where the values come from the JSON? I am trying to avoid having to hand-code each setter for each fields....
Example json file (in reality it has more fields and more children...):
{
"personEntity":{
"name":"xxx",
"dob":"2000-01-01",
"address":[
{
"id":"1",
"line1":"123"
},
{
"id":"2",
"line1":"zzz"
}
],
"phones":[
{
"id":"1",
"number":"999-999-999"
}
]
}
}
Desired Java code that is auto-generated:
Person p = new Person();
p.setName("xxx");
p.setDob("2000-01-01");
Address a1 = new Address();
a1.setId(1);
a1.setLine1("123")
p.addAddress(a1);
// and so on for the other fields
NOTE:
The POJOs are already existing and are NOT needed to be auto-generated. The only auto-generated code I am looking for is the sample above such as p.setName("xxx") and so on for the other fields.
Do your mean JSON -> JAVA Bean?
You can use this website json2javapojo
then you can use JSON utils to parse.
package ;
public class Address {
private String id;
private String line1;
public void setId(String id){
this.id = id;
}
public String getId(){
return this.id;
}
public void setLine1(String line1){
this.line1 = line1;
}
public String getLine1(){
return this.line1;
}
}
package ;
public class Phones {
private String id;
private String number;
public void setId(String id){
this.id = id;
}
public String getId(){
return this.id;
}
public void setNumber(String number){
this.number = number;
}
public String getNumber(){
return this.number;
}
}
package ;
import java.util.List;
public class PersonEntity {
private String name;
private String dob;
private List<Address> address ;
private List<Phones> phones ;
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setDob(String dob){
this.dob = dob;
}
public String getDob(){
return this.dob;
}
public void setAddress(List<Address> address){
this.address = address;
}
public List<Address> getAddress(){
return this.address;
}
public void setPhones(List<Phones> phones){
this.phones = phones;
}
public List<Phones> getPhones(){
return this.phones;
}
}
package ;
public class Root {
private PersonEntity personEntity;
public void setPersonEntity(PersonEntity personEntity){
this.personEntity = personEntity;
}
public PersonEntity getPersonEntity(){
return this.personEntity;
}
}
You need to de-serialize the returned JSON to java object using any of the parsers like GSON, Jackson, JSON simple etc.
Some online tools available to do your job very simple.
You can use jsonschema2pojo
-----------------------------------com.example.Address.java-----------------------
package com.example;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"id",
"line1"
})
public class Address {
#JsonProperty("id")
private String id;
#JsonProperty("line1")
private String line1;
#JsonProperty("id")
public String getId() {
return id;
}
#JsonProperty("id")
public void setId(String id) {
this.id = id;
}
#JsonProperty("line1")
public String getLine1() {
return line1;
}
#JsonProperty("line1")
public void setLine1(String line1) {
this.line1 = line1;
}
}
and so on....
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);