What causes unability to fetch entities in this code? - spring

So i'm developing a REST API for my Spring appplication. I have to store all data in H2 database and i'm trying to find a correct way to do so. I'm new to JPA and databases and general and need help understanding the causes of errors here.
First, i have these entities.
Position.java:
package com.example.offerserver.offerservice.task1;
#Entity
#Table(name = "position_table")
public class Position {
public Position() {
}
public Position(UUID id, String name, Integer salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
#Id
private UUID id;
#Column(name = "name")
private String name;
#Column(name = "salary")
private Integer salary;
//getters and setters
Stuff.java:
package com.example.offerserver.offerservice.task1;
#Entity
#Table(name = "stuff_table")
public class Stuff {
public Stuff(){};
public Stuff(UUID id,
String surname,
String name,
String patronymic,
boolean sex,
LocalDate birthDate,
Double salaryMultiplier,
Position position) {
this.id = id;
this.surname = surname;
this.name = name;
this.patronymic = patronymic;
this.sex = sex;
this.birthDate = birthDate;
this.salaryMultiplier = salaryMultiplier;
this.position = position;
}
#Id
private UUID id;
#Column(name="surname")
private String surname;
#Column(name="name")
private String name;
#Column(name="patronymic")
private String patronymic;
#Column(name="sex")
private boolean sex;
#Column(name="birth_date")
private LocalDate birthDate;
#Column(name="salary_multiplier")
private Double salaryMultiplier;
#OneToOne(fetch = FetchType.LAZY)
private Position position;
And JPA repositories:
package com.example.offerserver.repository;
#Repository
public interface StuffRepository extends JpaRepository<Stuff, String> {
}
package com.example.offerserver.repository;
#Repository
public interface PositionRepository extends JpaRepository<Position, UUID> {
}
And i have this request:
package com.example.offerserver.controller;
#Controller
#RequestMapping("/api/v1/stuff")
public class StuffListController {
#Autowired
StuffRepository repository;
#GetMapping("")
public ResponseEntity<List<Stuff>> getStuffList(){
List<Stuff> stuff = repository.findAll();
return new ResponseEntity<>(stuff, HttpStatus.OK);
Sending this request i'm getting this error:
Could not write JSON: Unable to find com.example.offerserver.offerservice.task1.Position with id f0bd15f2-74c1-4979-854b-ffbe44b3da59; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unable to find com.example.offerserver.offerservice.task1.Position with id f0bd15f2-74c1-4979-854b-ffbe44b3da59 (through reference chain: java.util.ArrayList[0]->com.example.offerserver.offerservice.task1.Stuff["position"]->com.example.offerserver.offerservice.task1.Position$HibernateProxy$E63ZeIxs["id"])
org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Unable to find com.example.offerserver.offerservice.task1.Position with id f0bd15f2-74c1-4979-854b-ffbe44b3da59; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unable to find com.example.offerserver.offerservice.task1.Position with id f0bd15f2-74c1-4979-854b-ffbe44b3da59 (through reference chain: java.util.ArrayList[0]->com.example.offerserver.offerservice.task1.Stuff["position"]->com.example.offerserver.offerservice.task1.Position$HibernateProxy$E63ZeIxs["id"])
In debug every instance of stuff in the list is initialized without its "position" field, throwing an error:
Method threw 'javax.persistence.EntityNotFoundException' exception. Cannot evaluate com.example.offerserver.offerservice.task1.Position$HibernateProxy$2ZiRYZbP.toString()
This is how position repository is initialized on launch:
public static List<Position> POSITIONS = List.of(
new Position(UUID.randomUUID(), "Junior Java Backend Developer", 60000),
new Position(UUID.randomUUID(), "Middle Machine Learning Engineer", 120000),
new Position(UUID.randomUUID(), "Senior DevOps Engineer", 200000),
new Position(UUID.randomUUID(), "Senior DevOps Engineer", 150000),
new Position(UUID.randomUUID(), "Intern System Engineer", 20000)
);
positionRepository.saveAll(POSITIONS);
Stuff repository as well. Position field for every stuff instance is randomly chosen from a POSITIONS list.

Related

MapStruct - mapping method from iterable to non-iterable

I have been working with MapStruct some days now and haven't yet achieved what i need.
As part of the exercises with Spring, I am writing a small app that will display information about the movies (title, description, director, etc.) and additionally the movie category.
Therefore, I created an additional Entity called Category, so that (e.g. an admin) could add or remove individual category names.
Movie Entity:
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
private String director;
private int year;
#ManyToMany
#Column(nullable = false)
private List<Category> category;
private LocalDate createdAt;
}
Category Entity
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String categoryName;
private LocalDate createdAt;
}
I packed it all into MapStruct and DTOs.
MovieDTORequest.java
public class MovieDTORequest {
private String title;
private String content;
private String director;
private List<Category> category;
private int year;
}
MovieDTOResponse.java
public class MovieDTOResponse {
private String title;
private String content;
private String director;
private String categoryName;
private int year;
private LocalDate createdAt;
}
And MovieMapper.java
#Mapper(componentModel = "spring")
public interface MovieMapper {
#Mapping(target = "categoryName", source = "category")
MovieDTOResponse movieToMovieDTO(Movie movie);
#Mapping(target = "id", source = "title")
#Mapping(target = "createdAt", constant = "")
Movie movieRequestToMovie(MovieDTORequest request);
#Mapping(target = "id", source = "title")
#Mapping(target = "createdAt", constant = "")
void updateMovie(MovieDTORequest request, #MappingTarget Movie target);
String map(List<Category> value);
}
However, I have a problem with Mapper. First, I got the error:
"Can't map property "List<Category> category" to "String categoryName". Consider to declare/implement a mapping method: "String map(List<Category> value)"
and when I wrote it in Mapper, I have one more error:
Can't generate mapping method from iterable type from java stdlib to non-iterable type.
I am asking for help, because I am already lost.
You should define default implementation for String map(List<Category> value) inside MovieMapper interface, what would Mapstruct use to map property List<Category> category to String categoryName. For example:
#Mapper(componentModel = "spring")
public interface MovieMapper {
#Mapping(target = "categoryName", source = "category")
MovieDTOResponse movieToMovieDTO(Movie movie);
default String map(List<Category> value){
//TODO: Implement your own logic that determines categoryName
return "Movie Categories";
}
}

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

Return type of JPA Repository 'getOne(id)' Method

I have the following Spring boot service for an object of type Report -
#Service
public class ReportService {
#Autowired
private ReportRepository reportRepository;
#Autowired
private UserRepository userRepository;
/*get all reports */
public List<Report> getAllReports(){
return reportRepository.findAll();
}
/*get a single report */
public Report getReport(Long id){
return reportRepository.getOne(id);
}
//other similar methods....
}
The problem arises while retrieving a single Report. If a report ID is send which doesn't exist, the following error is generated...
DefaultHandlerExceptionResolver : Failed to write HTTP message:
org.springframework.http.converter.HttpMessageNotWritableException: Could not
write JSON: Unable to find com.interact.restapis.model.Report with id 16;
nested exception is com.fasterxml.jackson.databind.JsonMappingException:
Unable to find com.interact.restapis.model.Report with id 16 (through
reference chain:
com.interact.restapis.model.Report_$$_jvst83c_1["fromUserId"])
Below is the code for my Report Controller
#RestController
public class ReportController {
#Autowired
private ReportService reportService;
//Get all reports
#GetMapping("/interactions")
public List<Report> getAllReports() {
return reportService.getAllReports();
}
//Get single report
#GetMapping("/interactions/{id}")
public ResponseEntity<Report> getReport(#PathVariable Long id) {
if(reportService.getReport(id) == null)
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
return new ResponseEntity<>(reportService.getReport(id), HttpStatus.OK);
}
#PostMapping("/interactions")
public ResponseEntity<Report> addReport(#RequestBody Report report) {
Report report1 = reportService.addReport(report);
if(report1 == null)
return new ResponseEntity<>(report, HttpStatus.NOT_FOUND);
return new ResponseEntity<>(report1, HttpStatus.OK);
}
//Other request methods...
}
Below is the code for my Report Model class -
#Entity
#Table (name = "report")
#EntityListeners(AuditingEntityListener.class)
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Report {
#Id
#Column (name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "from_user_id")
private Long fromUserId;
#Column(name = "to_user_id")
private Long toUserId;
#Column(name = "to_user_email")
private String toUserEmail;
#Column(name = "from_user_email")
private String fromUserEmail;
#Temporal(TemporalType.DATE)
#JsonFormat(pattern = "dd-MM-yyyy hh:mm:ss")
#CreatedDate
private Date createdAt;
#Column(nullable = false)
private String observation;
#Column(nullable = false)
private String context;
private String recommendation;
#Column(nullable = false)
private String eventName;
#Temporal(TemporalType.DATE)
#JsonFormat(pattern = "dd-MM-yyyy hh:mm:ss")
#Column(nullable = false)
private Date eventDate;
private boolean isAnonymous;
#Temporal(TemporalType.DATE)
#JsonFormat(pattern = "dd-MM-yyyy hh:mm:ss")
private Date acknowledgementDate;
#OneToMany(cascade = CascadeType.ALL, targetEntity = Action.class)
#JoinColumn(name = "report_id")
private List<Action> actionList;
#Value("${some.key:0}")
private int rating; //Range 0 to 4
private int type;
/*
Getter and setter methods...
*/
}
I want to know if reportRepository.getOne(Long id) returns null so that I can actually check if a particular report doesn't exist in the database. If not, how else can I implement the above?
The JpaRepository.getOne with throw EntityNotFoundException if it couldn't find a record with the given id.
You can use CrudRepository.findById (JpaRepository is a subclass of CrudRepository) which will return an Optional<Report> which can be empty if there are no record for the given id. You can use Optional.isPresent() to check whether it a Report is available or not and take actions accordingly.
Create a method in your ReportRepository.
It will return Report by matched id else return null.
public Optional<Report> findById(Long id);
Note: findById(Long id); should match with the property name in your Report entity.
I am assuming your Report entity is as follows:
public class Entity{
private Long id;
...
}

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;
}
}

converting URI to entity with custom controller in spring data rest?

i have an jpa entity like this.
#Entity
#Table(name = "location")
#Data
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "LOCATION_ID", unique = true)
#NotEmpty(message = "Please Enter Location ID")
private String name;
#Column(name = "LOCATION_DESCRIPTION")
#NotEmpty(message = "Please Enter Location Description")
private String description;
#ManyToOne
#NotNull(message = "Please Choose a Building")
Building building;
#Version
Long version;
}
and the repository like this.
public interface LocationRepository extends PagingAndSortingRepository<Location, Long> {
Location findByName(#Param("name") String name);
}
i am using spring data rest i am able to create location with rest api by providing the following payload
{
"name":"adminxxxxx","description":"adminxxx" , "building": "http://localhost:8080/buildings/2"
}
now i am trying to write my custom controller which will persist the entity. this is my custom controller
#ExposesResourceFor(Location.class)
#RepositoryRestController
#BasePathAwareController
public class LocationController {
#Autowired
LocationRepository locationDao;
#Autowired
LocationResourceAssembler resourceAssembler;
#Value("${buildings.error.messages.uniqueconstraintviolation}")
String uniqueConstrainMessage;
static final String TAG = LocationController.class.getSimpleName();
#RequestMapping(value="locations",method = org.springframework.web.bind.annotation.RequestMethod.POST)
public ResponseEntity<?> save(#RequestBody #Valid Location location) {
try {
location = locationDao.save(location);
LocationResource b = resourceAssembler.toResource(location);
return ResponseEntity.ok().body(b);
} catch (DataIntegrityViolationException e) {
if (locationAlreadyExists(location.getName()))
throw new LocationAlreadyExistException(uniqueConstrainMessage, location);
else
throw new RuntimeException("Some Error Occured");
}
}
i am getting this error
exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.alamdar.model.Building: no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/buildings/2')
at [Source: java.io.PushbackInputStream#5d468b16; line: 3, column: 60] (through reference chain: com.alamdar.model.Location["building"])</div></body></html>
can anyone please help?
I am not sure why you are writing a custom controller however the issue would appear to be that you do not have a default no args constructor so Jackson cannot instantiate an instance.
This is because you are using Lombok's #Data annotation:
https://projectlombok.org/features/Data.html
You should also annotate you class with #NoArgsConstructor to have a default no-args constructor generated:
#Entity
#Table(name = "location")
#Data
#NoArgsConstructor
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "LOCATION_ID", unique = true)
#NotEmpty(message = "Please Enter Location ID")
private String name;
#Column(name = "LOCATION_DESCRIPTION")
#NotEmpty(message = "Please Enter Location Description")
private String description;
#ManyToOne
#NotNull(message = "Please Choose a Building")
Building building;
#Version
Long version;
}

Resources