Is it possible to use entity inheritance in Olingo? - spring

I did some research but didn't find anything about entity inheritance in Olingo.
It is possible in Odata Complex Type Inheritance in OData v4 with ASP.NET Web API.
I have only one REST controller which only accepts Vehicle and want to send Car instances with POST request.
Super class is below.
#Entity(name = "Vehicle")
#Table(schema = "fs", name = "\"Vehicle\"")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Vehicle {
#Id
#GeneratedValue
private Long id;
}
And subclass is below.
public class Car extends Vehicle{
public String carStr;
}
I've tried Inheritance with Jackson.
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = Car.class, name = "car"),
#JsonSubTypes.Type(value = Truck.class, name = "truck")
})
Added snippet above to Vehicle class but it didn't added any type column in database and not accepting type in POST requests.

Related

Best way to handle negative scenario in spring boot

So I am working on a spring-boot application for a zoo. while adding animals I can assign some room and mark some rooms as favorites, My sample JSON looks like below
{
"id":null,
"title":null,
"located":null,
"type":null,
"preference":null,
"favoriteRooms":null,
"room":{
"id":null,
"title":"Green",
"size":25,
"createdOn":null,
"favorites":null,
"animals":null
}
}
now I want to make sure the room should be valid while adding animals, if there is no room available I want to throw an error. currently, I am using CascadingType.MERGE but it throws hibernate exception I want to do some valid addition for room and favorite room what is the best way to do this?
my entity class looks like below
#Entity
#Data
#NoArgsConstructor
#FieldDefaults(level = AccessLevel.PRIVATE)
public class Animal {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_animal_id")
#SequenceGenerator(name = "seq_animal_id")
Long id;
#NotEmpty(message = "title should be given")
String title;
#CreatedDate
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss")
LocalDateTime located;
String type;
Long preference;
#OneToMany(mappedBy = "animal")
Set <Favorite> favoriteRooms;
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name = "room_id")
Room room;
}
I'm not sure if I really understand what you're asking for but you can handle this in your controller or better in ServiceImplementation which handles the business logic.
Using #Valid the constraints will be checked which you have defined in your models:
example:
#Autowired
RoomRepository roomRepository;
#PostMapping
public ResponseEntity<?> insertAnimal(#Valid #RequestBody Animal animal) {
Optional<Room> optionalRoom = roomRepository.findById(animal.getRoom.getRoomId());
if (optionalRoom.isPresent()) {
Room room = optionalRoom.get();
// your logic to check if the room is valid
}
else {
return new ResponseEntity<String>("Your message here", HttpStatus.NOT_FOUND);
}
}

Issue in persisting nested comments using Spring Hibernate

I am trying to create a simple CRUD application using Spring Boot with User, UserEntity, Post, Comment entities.
-> UserEntity is super class of Comment and Post.
-> Each comment has a ManyToOne relationship to a UserEntity (which can be a Post or another Comment)
UserEntity
   |
   #ManyToOne
   createdBy - refers to user table (id)
   |
--------------------
|        |
|        |
Post    Comment
        |
        #ManytoOne
          UserEntity - refers to PK(entity_id) of user_entity table as comment can be on post or reply to another comment
On trying to save a comment on post from the CommentService class,
//Controller
#PostMapping(path = "api/v1/addComment")
public void addComment(#RequestBody Comment comment){ commentService.addCommentOnPost(comment); }
//Service
public void addCommentOnEntity(Comment comment){ commentRepos.save(comment); }
the foreign key in comment table (parent_entity_id) referring to entity_id in user_entity table is not getting updated. The value is blank.
On the other hand UserEntity has a manytoone relationship with User -- createdBy -- which is updating foriegn key user_id in user_entity table properly
Can someone guide me what could be wrong, I have been trying since yesterday night but no luck. Have checked some other answers but could not get an answer for this case.
User.java
#Entity
#Table(name="[user]")
public class User {
#Id
#SequenceGenerator(name="student_sequence",
sequenceName = "student_sequence",
allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "student_sequence")
private long id;
private String name;
private String email;
private int age;
private LocalDate DOB;
//Setters and Getters and default constructor
}
UserEntity.java
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class UserEntity {
#Id
#SequenceGenerator(sequenceName = "entity_sequence", name="entity_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "post_sequence")
private long entityId;
private char entityType;
private LocalDate createdOn;
private LocalDate modifiedOn;
#ManyToOne
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User createdBy;
//Setters and Getters and default constructor
}
Post.java
#Entity
public class Post extends UserEntity{
private String postHeading;
private String postBody;
//Setters and Getters and default constructor
}
Comment.java
#Entity
public class Comment extends UserEntity{
private String comment;
#ManyToOne
#JoinColumn(name="parent_entity_id", referencedColumnName = "entityId")
private UserEntity parentEntity;
//Setters and Getters and default constructor
}
and their repositories
#NoRepositoryBean
public interface UserEntityBaseRepos<T extends UserEntity> extends JpaRepository<T, Long>{
Optional<List<T>> findByCreatedBy_Id(Long user_id);
Optional<List<T>> findByEntityId(Long entity_id);
}
#Repository
public interface UserRespository extends JpaRepository<User, Long> {
Optional<User> findUserByEmail(String email);
Optional<User> findUserByName(String name);
}
#Repository
public interface PostRepos extends UserEntityBaseRepos<Post>, JpaRepository<Post, Long> {
}
#Repository
public interface CommentRepos extends UserEntityBaseRepos<Comment>, JpaRepository<Comment, Long> {
}
Json for postComment service
{
"entityType" : "C",
"createdOn" : "2020-02-05",
"createdBy" : {
"id" : 1
},
"comment": "I am the comment",
"parentEntity" : {
"entityId" : 1
}
}
//User with id = 1 and UserEntity(Post) with entityId = 1 available in database.
Here createdBy.id (user id) is getting updated in the user_entity table, but userEntity.entityId is not getting updated in the comment table
You have very complex entity relationships, it seems to me...
Anyway, I found that you added a generator property to the UserEntity entity with a post_sequence value, but I can't find any relationship to the Post entity in your database. This is probably the reason of the breakdown. You have to connect UserEntity to Post as shown on your diagram or change the generator value.
I was able to solve the problem. The issue was in the following piece of code in Comment concrete class
#ManyToOne
#JoinColumn(name="parent_entity_id", referencedColumnName = "entityId")
private UserEntity parentEntity;
and this Json input
"parentEntity" : {
"entityId" : 1
}
It seems the parentEntity in json input was not being parsed. This was solved on placing JsonProperty("parentEntity") above parentEntity in the Json input was being parsed correctly.
However there was another issue. The parentEntity was not being deserialized to UserEntity as UserEntity is an abstract class. I had to use JacksonPolymorphicDeserialization by introducing a new field parentType("P" for post, "C" for comment) along with some Annotations like below to deserialize parentEntity to corresponding concrete class object.
public class Comment extends UserEntity{
private String comment;
#Transient
#JsonProperty("parentType")
private char parentType;
#ManyToOne
#JoinColumn(name="parent_entity_id", referencedColumnName = "entity_id", foreignKey = #ForeignKey(value=ConstraintMode.CONSTRAINT))
#JsonProperty("parentEntity")
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME , property = "parentType", include = JsonTypeInfo.As.EXTERNAL_PROPERTY)
#JsonSubTypes(value = {
#JsonSubTypes.Type(value = Comment.class, name = "C"),
#JsonSubTypes.Type(value = Post.class, name = "P")
})
private UserEntity parentEntity;
reference - Jackson Polymorphic Deserialization via field. I am not really sure how this works. Will try to make sense of it and update the answer.
If anyone knows a better way to deserialize json, do mention it in the comments or as a new answer.

How to return custom class from PagingAndSortingRepository in Spring Boot

I have a model that looks like this:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Sale {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(targetEntity = User.class)
private User customer;
#OneToOne(targetEntity = Product.class)
private Product product;
}
And I have a paging-and-sorting repository, like so:
public interface SaleRepository extends PagingAndSortingRepository<Sale, Long> {}
But I would like to the "findAll" method of the repository in order to return a custom class with certain fields (instead of all the data of the nested entities), while still maintain paging and sorting abilities. The list of objects I would like to return would contain something like:
saleId
customerId
customerName
productId
ProductName
prouctPrice
But I'm not sure how to actually do this.
My controller method accepts sorting and paging parameters and then does this:
Page<Sale> saleList = saleRepository.findAll(pageable);
return saleList;
You can create either and Interface or a DTO and use this as the result of a find declared in your repository:
Page<YourDTO> findAllAsDtos(Pageable pageable);
Read more about projections here: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

Spring Jpa Entity - EntityManager.getReference

I have a Spring Boot application using Spring JPA, and what I'm trying to do is to save a new entity that has some foreign keys by just providing the IDs of those child entities. So, like:
#Table(name = "PERSON")
public class Person {
#Column(name = "PET_GUID")
public Pet pet;
}
Using this, I'd like to be able to have my PersonRepository that implements CrudRepository save a Person by just providing the guid of the Pet. Using straight up hibernate I can do that using EntityManager.getReference. I know that I can inject an EntityManager into my Entity or Repository and do something that way, but is there an easier way? I tried just doing person.setPet(new Pet(myPetsGuid)), but I get a "foreign key not found" when doing that, so that does not seem to work.
First, you should add #ManyToOne relation to the pet property:
#Entity
#Table(name = "PERSON")
public class Person {
//...
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "pet_guid")
privat Pet pet;
}
It says to Hibernate to use a foreign key to the Pet entity (and its table).
Second, you should use the method getOne of your PersonRepository to get a reference to the Pet entity, for example:
#Service
public class PersonService {
private final PetRepository petRepo;
private final PersonRepository personRepo;
//...
#NonNull
#Transactional
public Person create(#NonNull final PersonDto personDto) {
Person person = new Person();
//...
UUID petId = personDto.getPetId();
Pet pet = petRepo.getOne(perId);
person.setPet(pet);
//...
return person.save(person);
}
}

How to use different EntityGraph as annotation on findAll and findOne repository methods

I have a Spring project which uses Hibernate to interact with an underlying MySQL db. Please note EHCACHE is used as Hibernate second level cache.
I am trying to load different dependent objects by using different #EntityGraph annotations on findAll and fineOne method. I first load the entity using findAll method and then load the same object using findOne method - to my surprise it simply neglects the #EntityGraph specified on findOne method and instead returns data using the EntityGraph specified on findAll.
To explain the problem better I am copying the entity class and repository class below:
#Entity
#Table(name = "COURSE")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName="course")
#NamedEntityGraphs({
#NamedEntityGraph(name = "categories", attributeNodes = #NamedAttributeNode("categories")),
#NamedEntityGraph(name = "batches", attributeNodes = #NamedAttributeNode("batches"))
})
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#id")
public class Course implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "course")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Batch> batches = new HashSet<>();
#ManyToMany
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "COURSE_CATEGORY",
joinColumns = #JoinColumn(name="courses_id", referencedColumnName="ID"),
inverseJoinColumns = #JoinColumn(name="categorys_id", referencedColumnName="ID"))
private Set<Category> categories = new HashSet<>();
// GETTERS AND SETTERS
}
The repository class is:
public interface CourseRepository extends JpaRepository<Course,Long> {
#EntityGraph(value = "categories", type = EntityGraphType.FETCH)
Page<Course> findAll(Pageable pageable);
#EntityGraph(value = "batches", type = EntityGraphType.FETCH)
Course findByIdAndPublished(Long id);
}
And after load up the calls happen in this format:
courseRepository.findAll() => This loads and returns all Course objects with categories loaded
courseRepository.findOne(1) => This returns Course with id 1 but again categories are loaded and not batches (Please note as per the EntityGraph I was expecting batches to be loaded and not categories)
Thanks in advance for helping out,
Ankit

Resources