Spring Boot JPA update after fething ID is not working - spring-boot

#Autowired
LessonService lsnService;
#PutMapping(path = "/{id}")
public ResponseEntity<Object> updateLesson(#PathVariable("id") Long id, #Valid #RequestBody LessonDto dto) {
try {
lsnService.findById(id);
dto.setId(id);
lsnService.save(dto);
return ResponseEntity.ok(dto);
}
catch (Exception e) {
ApiErrorMessage errorMessage = new ApiErrorMessage();
errorMessage.setStatusCode(400L);
errorMessage.setMessage(e.getMessage());
errorMessage.setDescription("The server cannot or will not process the request due to an apparent client error");
return ResponseEntity.badRequest().body(errorMessage);
}
}
Here's my problem. When I remove lsnService.findById(id);, update is working.
If I didn't add that code if a user update with unexisting ID, it will save another data.
Another problem is when I remove dto.setId(id);, both method from lsnService; findById(id); and save(dto); are working! But as you can see, repo must update the entity but it won't!!!
So, I tried to put #Transactional in saving. And I even try putting Thread.sleep(5000); 5 secs delay between those two services. Like this,
lsnService.findById(id);
Thread.sleep(5000);
dto.setId(id);
lsnService.save(dto);
But it doesn't work either!
#Autowired
private LessonJpaRepository repo;
#Override
public LessonDto findById(Long id) {
// TODO Auto-generated method stub
Lesson lesson = repo.getOne(id);
LessonDto dto = new LessonDto(lesson);
return dto;
}
#Override
public void save(LessonDto dto) {
// TODO Auto-generated method stub
repo.save(dto.getEntity());
System.out.println(dto.getId()+dto.getTitle()+dto.getStructure()+dto.getExplanation());
}
And then, I check output of that dto. It's all there! repo is not saving it! It's so strange to me. Got any ideas?
public class LessonDto {
private Long id;
#NotNull(message = "Title must not be null")
#NotBlank(message = "Title must not be blank")
#ValidLessonTitle(message = "Title must begin with uppercase character")
private String title;
#NotNull(message = "Structure must not be null")
#NotBlank(message = "Structure must not be blank")
private String structure;
#NotNull(message = "Explanation must not be null")
#NotBlank(message = "Explanation must not be blank")
private String explanation;
public LessonDto() {
}
public LessonDto(Lesson lesson) {
this.id=lesson.getId();
this.title=lesson.getTitle();
this.structure=lesson.getStructure();
this.explanation=lesson.getExplanation();
}
#java.beans.Transient
public Lesson getEntity() {
Lesson lesson = new Lesson();
lesson.setId(this.id);
lesson.setTitle(this.title);
lesson.setStructure(this.structure);
lesson.setExplanation(this.explanation);
return lesson;
}
//getters and setters
}
This is the entity
#Entity
public class Lesson implements Serializable {
/**
*
*/
private static final long serialVersionUID = 2239534946567783017L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "title")
private String title;
#Column(name = "structure")
private String structure;
#Column(name = "explanation")
private String explanation;
//getters and setters
}

There are 2 ways to make that work
Update instance that is retured by findById with values from DTO
Dont use findById as it fetches entity pointer (at least) to the cache and this might be the origin of problems. Try to use existsById instead

Related

how not to consider #NotBlank in some methods

I'm doing a restful app in Spring boot,jpa,mysql. I have annoted some of my model fields #NotBlank to print an error in the creation of an object if those fields are blank.
Now when i'm updating, I don't want to get that error if I don't set some fields in my json body.My goal is to update just the fields which are present.
So I want to know if there is a way not to consider an #NotBlank in my updating method.
This is the code source :
For the Entity
public class Note implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank(name)
private String title;
#NotBlank
private String content;
//Getters and Setters
}
The controller
#RestController
#RequestMapping("/api")
public class NoteController {
#Autowired
NoteRepository noteRepository;
// Create a new Note
#PostMapping("/notes")
public Note createNote(#Valid #RequestBody Note note) {
return noteRepository.save(note);
}
// Update a Note
#PutMapping("/notes/{id}")
public Note partialUpdateNote(#PathVariable(value = "id") Long noteId,
#RequestBody Note noteDetails) {
Note note = noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
//copyNonNullProperties(noteDetails, note);
if(note.getTitle()!= null) {
note.setTitle(noteDetails.getTitle());
}else {
note.setTitle(note.getTitle());
}
if(note.getContent()!= null) {
note.setContent(noteDetails.getContent());
}else {
note.setContent(note.getContent());
}
Note updatedNote = noteRepository.save(note);
return updatedNote;
}
// Delete a Note
#DeleteMapping("/notes/{id}")
public ResponseEntity<?> deleteNote(#PathVariable(value = "id") Long noteId) {
Note note = noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
noteRepository.delete(note);
return ResponseEntity.ok().build();
}
}
ResourceNotFoundException is the class responsible to throws errors.
You can use groups for that.
Add two interfaces CreateGroup and UpdateGroup.
Use them by this way:
#NotBlank(groups = CreateGroup.class)
#Null(groups = UpdateGroup.class)
private String title;
In the create endpoint
#Valid #ConvertGroup(from = Default.class, to = CreateGroup.class) Note note
In the update endpoint
#Valid #ConvertGroup(from = Default.class, to = UpdateGroup.class) Note note
Probably you don't need UpdateGroup. It is just to show a common approach.
Also for the nested objects inside Note something like
#ConvertGroup(from = CreateGroup.class, to = UpdateGroup.class)
can be used.

neo4j RelationshipEntity not created

I'm having issues getting a neo4j RelationshipEntity persisted with Spring Boot. I'm using spring-boot-starter-data-neo4j (2.1.0.RELEASE), and the neo4j docker image tagged 3.4.9.
I have a simple NodeEntity, which contains a collection for the RelationshipEntity:
#NodeEntity
public class Book {
#Id
#GeneratedValue
private Long id;
private String name;
public Book() {}
public Book(String name) {
this.name = name;
}
#Relationship(type = "PURCHASED_WITH", direction = "OUTGOING")
private Set<BookPurchase> purchases = new HashSet<>();
// getters and setters follow
}
I have another NodeEntity, which also contains a collection for the relationship entity:
#NodeEntity
public class CreditCard {
#Id
#GeneratedValue
private Long id;
private String number;
#DateString(value = "yyyy-MM-dd")
private Date expiryDate;
public CreditCard() {}
public CreditCard(String number, Date expiryDate) {
this.number = number;
this.expiryDate = expiryDate;
}
#Relationship(type = "PURCHASED_WITH", direction = "INCOMING")
private Set<BookPurchase> purchases = new HashSet<BookPurchase>();
// getters and setters follow
}
I have the RelationshipEntity, which adds references to both NodeEntity classes in the constructor:
#RelationshipEntity(type = "PURCHASED_WITH")
public class BookPurchase {
#Id
#GeneratedValue
private long id;
#DateString("yyyy-MM-dd")
Date purchaseDate;
#StartNode
private Book book;
#EndNode
private CreditCard card;
public BookPurchase(){}
public BookPurchase(CreditCard card, Book book, Date purchaseDate) {
this.card = card;
this.book = book;
this.purchaseDate = purchaseDate;
this.card.getPurchases().add(this);
this.book.getPurchases().add(this);
}
// getters and setters follow
}
And finally I have the Spring controller tying everything together:
#RestController
public class ExamplesController {
#Autowired
CreditCardRepository creditCardRepository;
#PostMapping(value="/purchases")
public String createPurchases() {
CreditCard card = new CreditCard("11111", new GregorianCalendar(2018, Calendar.FEBRUARY, 12).getTime());
Book book1 = new Book("of mice and men");
BookPurchase purchase1 = new BookPurchase(card,book1,new GregorianCalendar(2018, Calendar.MARCH, 15).getTime());
creditCardRepository.save(card);
return "Successfully created entities";
}
}
Whenever I try to curl -X POST http://localhost:8080/purchases, I just see the following in the neo4j browser - the RelationshipEntity is not persisted, only the nodes.
Can anyone assist?
Thanks to Gerrit Meier for answering this one. My RelationshipEntity was using the primitive long instead of the object/wrapper Long. Complete details here: https://community.neo4j.com/t/neo4j-relationshipentity-not-persisted/3039

How to code Spring JPA onetomany relation

I am new to Spring programming and trying the below example with one to many relationship between BID and BIDITEM classes. I am not sure whether the BIDITEM data is saved, as when I tried to retrieve the BID, I am getting only BID data and not BIDITEM data. Do we need to have a repository even for BIDITEM class. I can see that complete BID JSON string, along with BIDITEM is received in create method.
Could you please go through it, and let me know what is wrong with it.
#Entity
#Table(name = "bid")
public class Bid {
#Id
private String title;
#Column
private long startDate;
#Column
private long endDate;
#OneToMany(mappedBy = "myBid", cascade = {CascadeType.ALL})
private List<BidItem> bidItems = new ArrayList<BidItem>();
//Constructor, getter and setter methods go here
}
#Entity
#Table(name="biditem")
public class BidItem
{
#Id
private String item;
#Column
private String desc;
#Column
private double minAmt;
#ManyToOne
#JoinColumn(name = "title")
private Bid myBid;
//Constructor, getter and setter methods go here
}
public interface BidRepository extends CrudRepository<Bid, String> {
//Tried even JpaRepository
}
public class BidService {
ObjectMapper mapper = new ObjectMapper();
#Autowired
private BidRepository bidRepo;
public Bid create(String bidJson) throws JsonParseException, JsonMappingException, IOException
{
Bid bid = mapper.readValue(bidJson, Bid.class);
// bidJson string has below string
// {"bidItems":[{"item":"item1","desc":"item1","minAmt":"999"}],
// "title":"bid1","startDate":"D1","endDate":"D5"}
Bid savedBid = bidRepo.save(bid);
return savedBid;
}
public Bid findByID(String title)
{
Bid bid = bidRepo.findOne(title);
return bid;
}
}

Ignoring Nested properties in Jackson OnDemand

I am working on a spring boot application with Hibernate as ORM and Jackson as JSON serialiser .
I have three model objects and CRUD operations for all three models.
Class Student{
private Teacher teacher; // Teacher of the student — to be fetched eagerly
+Getter/Setter
}
class Teacher {
private List<Subject> subject; // List of subjects associated to that user— to be fetched eagerly
+Getter/Setter
}
class Subject {
private long subjectId
//Other subject properties
+ Getter/Setter
}
Whenever I trigger a get request for student info I get the teacher info which is correct where as I also receive Subject info as well which is unnecessary for me. In the same time when I request for Teacher info, I need Subject info should be associated to that for sure. If I use #JsonBackReference for subject I am losing it all the time. I am not sure how to achieve this.
Thanks in advance for your help!!
You can also annotate like this
Class Student{
#JsonIgnoreProperties("subject")
private Teacher teacher; // Teacher of the student — to be fetched eagerly
}
You can use JSON Views
From the spring blog:
public class View {
interface Summary {}
}
public class User {
#JsonView(View.Summary.class)
private Long id;
#JsonView(View.Summary.class)
private String firstname;
#JsonView(View.Summary.class)
private String lastname;
private String email;
private String address;
private String postalCode;
private String city;
private String country;
}
public class Message {
#JsonView(View.Summary.class)
private Long id;
#JsonView(View.Summary.class)
private LocalDate created;
#JsonView(View.Summary.class)
private String title;
#JsonView(View.Summary.class)
private User author;
private List<User> recipients;
private String body;
}
and in the controller
#RestController
public class MessageController {
#Autowired
private MessageService messageService;
#JsonView(View.Summary.class)
#RequestMapping("/")
public List<Message> getAllMessages() {
return messageService.getAll();
}
#RequestMapping("/{id}")
public Message getMessage(#PathVariable Long id) {
return messageService.get(id);
}
}
PS: No link to http://fasterxml.com/ as it's currently down.

Why the record is posted twice in the database?

Can you tell me, why the record is posted twice in the database. I think. this happens because I use save() method. But shouldn't I save the master-entity and dependent-entity separately?
Controller method:
#RequestMapping(value = "/addComment/{topicId}", method = RequestMethod.POST)
public String saveComment(#PathVariable int topicId, #ModelAttribute("newComment")Comment comment, BindingResult result, Model model){
Topic commentedTopic = topicService.findTopicByID(topicId);
commentedTopic.addComment(comment);
// TODO: Add a validator here
if (!comment.isValid() ){
return "//";
}
// Go to the "Show topic" page
commentService.saveComment(comment);
return "redirect:../details/" + topicService.saveTopic(commentedTopic);
}
Services:
#Service
#Transactional
public class CommentService {
#Autowired
private CommentRepository commentRepository;
public int saveComment(Comment comment){
return commentRepository.save(comment).getId();
}
}
#Service
#Transactional
public class TopicService {
#Autowired
private TopicRepository topicRepository;
public int saveTopic(Topic topic){
return topicRepository.save(topic).getId();
}
}
Model:
#Entity
#Table(name = "T_TOPIC")
public class Topic {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#ManyToOne
#JoinColumn(name="USER_ID")
private User author;
#Enumerated(EnumType.STRING)
private Tag topicTag;
private String name;
private String text;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)
private Collection<Comment> comments = new LinkedHashSet<Comment>();
}
#Entity
#Table(name = "T_COMMENT")
public class Comment
{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#ManyToOne
#JoinColumn(name="TOPIC_ID")
private Topic topic;
#ManyToOne
#JoinColumn(name="USER_ID")
private User author;
private String text;
private Date creationDate;
}
In this concrete case, you do not need to save the master and the client.
Saving the master or the client would be enough (with this concrete mapping)
But I think the main problem is that you do not have a good equals method in your Comment so your ORM Provider think that there are two different comments, and therefore store them twice.

Resources