How to get rid with infinity recursion - spring-boot

i'm trying to solve infinity recursion issue with no success !
project: spring boot and postgresql
i have 3 entities : course, course outline and course schedule :
public class Course implements Serializable {
private static final long serialVersionUID = -6645577819394287204L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#OneToMany(mappedBy = "course", cascade = CascadeType.REMOVE)
#OrderBy("rank ASC")
private List<CourseOutline> outlines;
....
}
public class CourseOutline implements Serializable {
private static final long serialVersionUID = -6645577819394287204L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#ManyToOne
#JoinColumn(name = "course", nullable = false)
#JsonIgnore
private Course course;
private Integer rank;
#OneToMany(mappedBy = "outline", cascade = CascadeType.REMOVE)
#OrderBy("day DESC, started ASC")
private Set<CourseSchedule> schedules;
...
}
public class CourseSchedule implements Serializable {
private static final long serialVersionUID = -6645577819394287204L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#ManyToOne
#JoinColumn(name = "instructor", nullable = true)
#JsonManagedReference
private Person instructor;
#ManyToOne
#JoinColumn(name = "outline", nullable = false)
#JsonIgnore
private CourseOutline outline;
...
}
in REST API i make a call to retrieve a List of CourseOutline by course
using CourseOutline Repository :
List<CourseOutline> findAllByCourse(Course course);
but i get below error :
Could not write JSON: Infinite recursion (StackOverflowError); nested
exception is com.fasterxml.jackson.databind.JsonMappingException:
Infinite recursion (StackOverflowError) (through reference chain:
java.util.ArrayList[2]-myDomain.api.models.entities.CourseOutline[\"schedules\"])"

Related

JPA and Hibernate One To One Shared Primary Key Uni-directional Mapping in Spring Boot

I want to have one-to-one uni-directional mapping with 2 child entities using shared primary key. Below are model classes
public class Template implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "templatekey")
Integer templateKey;
#Column(name = "templateid", unique = true)
String templateId;
#OneToOne(cascade = CascadeType.ALL, optional = false)
#PrimaryKeyJoinColumn(name = "templatekey", referencedColumnName = "templatekey")
InstantOfferNoEsp instantOfferNoEsp;
#OneToOne(cascade = CascadeType.ALL, optional = false)
#PrimaryKeyJoinColumn(name = "templatekey", referencedColumnName = "templatekey")
Mobile mobile;
//constructor , setter and getters
}
Child 1 :
public class Mobile implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "templatekey")
Integer templateKey;
String mobileNewUrl;
//constructor , setter and getters
}
Child 2:
public class InstantOfferNoEsp {
#Id
#Column(name = "templatekey")
Integer templateKey;
String offerCodeType;
String headerUrl;
//constructor , setter and getters
}
I want templateKey as PK in all tables. and I am calling templateRepository.save(template); to save all entities at once but its not working and getting ids for this class must be manually assigned before calling save() error.
Any suggestions would be of great help. Thank you.
I was able to do what you want with bidirectional #OneToOne like below:
#Entity
public class Mobile {
#Id
Integer templateKey;
#OneToOne
#MapsId
#JoinColumn(name = "templatekey")
Template template;
// ...
}
#Entity
public class InstantOfferNoEsp {
#Id
Integer templateKey;
#OneToOne
#MapsId
#JoinColumn(name = "templatekey")
Template template;
// ...
}
#Entity
public class Template {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "templatekey")
Integer templateKey;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "template", optional = false)
InstantOfferNoEsp instantOfferNoEsp;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "template", optional = false)
Mobile mobile;
// ...
public void setMobile(Mobile mobile)
{
this.mobile = mobile;
this.mobile.setTemplate(this);
}
public void setInstantOfferNoEsp(InstantOfferNoEsp instantOfferNoEsp)
{
this.instantOfferNoEsp = instantOfferNoEsp;
this.instantOfferNoEsp.setTemplate(this);
}
}
and an example of saving:
Mobile mobile = new Mobile();
mobile.setMobileNewUrl("MOB1");
InstantOfferNoEsp instant = new InstantOfferNoEsp();
instant.setOfferCodeType("INST_OFF1");
Template template = new Template();
template.setTemplateId("TMP1");
template.setInstantOffer(instant);
template.setMobile(mobile);
entityManager.persist(template);
P.S. The following mapping works too, but only if we set Template.templateKey manually.
#Entity
public class Template
{
#Id
// #GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "templatekey")
Integer templateKey;
#OneToOne(cascade = CascadeType.ALL, optional = false)
#JoinColumn(name = "templatekey", insertable = false, updatable = false)
InstantOfferNoEsp instantOfferNoEsp;
#OneToOne(cascade = CascadeType.ALL, optional = false)
#JoinColumn(name = "templatekey", insertable = false, updatable = false)
Mobile mobile;
// ...
}
and an example of saving:
Mobile mobile = new Mobile();
mobile.setMobileNewUrl("MOB1");
InstantOfferNoEsp instant = new InstantOfferNoEsp();
instant.setOfferCodeType("INST_OFF1");
Template template = new Template();
template.setTemplateKey(20);
template.setTemplateId("TMP1");
template.setInstantOffer(instant);
template.setMobile(mobile);
entityManager.persist(template);
Also I would suggest your explicitly specify what generation strategy you want to use (do not use GenerationType.AUTO) and use corresponding object wrapper classes instead of primitive types for #Id fields.

JPA one to many, fetch children with specific column value

I have two entities in one to many relationship in my spring-data-jpa project.
Parent entity -
#Entity
#Table(name = "code_group")
public class CodeGroup implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#NaturalId
#Column(nullable = false)
private String entityId;
#OneToMany
#JoinColumn(name = "codeGroupId", referencedColumnName = "entityId")
private List<SystemCode> systemCodes;
// .. getters setters
}
Child entity -
#Entity
#Table(name = "system_code")
public class SystemCode implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Column(nullable = false)
private String codeGroupId;
#Column(nullable = false)
#Enumerated(EnumType.STRING)
private ActiveOrInactive status;
// getters and setters
}
status column is of enum type, it can only have Active or Inactive value.
My existing code works fine. It is fetching code group with associated system codes. I want to filter system code with status='Active'. How to do this?
Try like this:
#OneToMany
#JoinColumn(name = "codeGroupId", referencedColumnName = "entityId")
#Where(clause = "status= 'Active'")
private List<SystemCode> systemCodes;
You can create such methods in your repo:
List<CodeGroup> getAllBySystemCodes_Status(ActiveOrInactive status);
default List<CodeGroup> getAllActive() {
return getAllBySystemCodes_Status(ActiveOrInactive.Active);
}
default List<CodeGroup> getAllInactive() {
return getAllBySystemCodes_Status(ActiveOrInactive.Inactive);
}

Spring JPA: How to insert data to join many tables with #ManytoMany relationship

I'm starting to learn Spring Java Framework . I created some Enity to join 2 Model like my Database. And now I want to insert to Join Table by JpaRepository. What i have to do?
This is my Code (Please fix help me me if something is not right)
Model Users_RoomId to define Composite Primary Key
#Embeddable
public class Users_RoomId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "ID_room", nullable = false)
private String idRoom;
#Column(name = "user_id", nullable = false)
private int idUser;
}
Model Users_Room to join 2 Model Users and Room
#Entity
#Table(name ="bookroom")
public class Users_Room {
#EmbeddedId
private Users_RoomId usersroomId;
#ManyToOne
#MapsId("idRoom")
private Room room;
#ManyToOne
#MapsId("idUser")
private Users users;
#Column(name = "Bookday")
private String bookday;
Model Users and Room I used annotation #OneToMany
Model Users
#Entity
#Table(name = "users")
public class Users implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id", nullable = false)
private int id;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "email")
private String email;
#Column(name = "pass")
private String pass;
#Column(name = "role")
private int role;
#OneToMany(mappedBy = "users")
private List<Users_Room> user;
Model Room
#Entity
#Table(name ="room")
public class Room implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID_room", nullable = false)
private String id;
#Column(name = "name_room", nullable = false)
private String name;
#Column(name = "Description")
private String describe;
#ManyToOne
#JoinColumn(name = "ID_status")
private Status status;
#Column(name = "room_image")
private String image;
public Room() {
super();
}
#ManyToOne
#JoinColumn(name = "ID_kind")
private KindRoom kind;
#OneToMany(mappedBy = "room")
private List<Users_Room> rooms;
This is my database
So I don't know how to insert a new bookroom with iduser,idroom and bookday with JPA repository.. It'necessary to write Query in JPARepository or We just need to use method save() to insert data
Thanks everyone
I had same problem and solved with following code. I used method save() to insert data. Following code is 'createRoom' method in 'RoomService.java'.
RoomService.java
private final RoomRepository roomRepository;
private final UserRoomRepository userRoomRepository;
private final UserRepository userRepository;
public RoomService(RoomRepository roomRepository, UserRoomRepository userRoomRepository, UserRepository userRepository) {
this.roomRepository = roomRepository;
this.userRoomRepository = userRoomRepository;
this.userRepository = userRepository;
}
#Transactional
public RoomDto createRoom(Long userId, Long chattingUserId) {
Room room = roomRepository.save(new Room());
room.addUserRoom(userRepository.findById(userId).orElseThrow(()->new NoSuchElementException("No User")));
room.addUserRoom(userRepository.findById(chattingUserId).orElseThrow(()->new NoSuchElementException("No User")));
userRoomRepository.save(new UserRoom(userRepository.findById(userId).orElseThrow(()->new NoSuchElementException("No User")),room));
userRoomRepository.save(new UserRoom(userRepository.findById(chattingUserId).orElseThrow(()->new NoSuchElementException("No User")),room));
RoomDto roomDto = RoomDto.of(room);
return roomDto;
}

Customizing HATEOS URI

I have a Spring Boot Data Rest project I'm working on.
Specifically, I have the following dependencies:
dependencies {
compile 'org.springframework.boot:spring-boot-starter-data-jpa:1.1.9.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-data-rest:1.1.9.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-actuator:1.1.9.RELEASE'
}
I have three entities:
#Entity
#Table(name = "prefecture", uniqueConstraints={#UniqueConstraint(columnNames = {"code", "name"})})
public class Prefecture implements Serializable {
private static final long serialVersionUID = -4664664252005282494L;
#Id
#Column(name = "code")
private Integer code;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "prefecture")
private List<City> cities;
...
#Entity
#Table(name = "city", uniqueConstraints = {#UniqueConstraint(columnNames = {"code", "name"})})
public class City implements Serializable {
private static final long serialVersionUID = 1077260811602686775L;
#Id
#Column(name = "code")
private Integer code;
#ManyToOne
#JoinColumn(name = "prefecture_code", referencedColumnName = "code")
private Prefecture prefecture;
#Column(name = "name", unique = true)
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "city")
private List<TownArea> townAreas;
...
#Entity
#Table(name = "town_area", uniqueConstraints={#UniqueConstraint(columnNames = {"name"})})
public class TownArea implements Serializable {
private static final long serialVersionUID = -4908446167092081914L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#ManyToOne
#JoinColumn(name = "city_code", referencedColumnName = "code")
private City city;
#Column(name = "prefecture_code")
private Integer prefectureCode;
#Column(name = "postal_code")
private String postalCode;
...
And three repositories:
#RepositoryRestResource(collectionResourceRel = "cities", path = "cities")
public interface CityRepository extends PagingAndSortingRepository<City, Integer> {}
#RepositoryRestResource(collectionResourceRel = "prefectures", path = "prefectures")
public interface PrefectureRepository extends PagingAndSortingRepository<Prefecture, Integer> {}
#RepositoryRestResource(collectionResourceRel = "town_areas", path = "town_areas")
public interface TownAreaRepository extends PagingAndSortingRepository<TownArea, Integer> {}
Given this, when I run my application locally I have a set of URLs like this:
http://localhost:8080/prefectures
http://localhost:8080/prefectures/1
http://localhost:8080/prefectures/1/cities
http://localhost:8080/cities/2/townareas
http://localhost:8080/townareas/3
However, I would like to configure the following URLs:
http://localhost:8080/prefectures/1/cities/2/
http://localhost:8080/prefectures/1/cities/2/townareas
http://localhost:8080/prefectures/1/cities/2/townareas/3
Is there a way I can customize the uris to accomplish this?

Hibernate 4 Entity Mapping with Spring

I've got 2 entity classes as follows:
#Entity
#Table(name = "USER_ACCT")
public class UserAccount implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="USER_ACCT_ID_SEQ")
#SequenceGenerator(name="USER_ACCT_ID_SEQ", sequenceName="USER_ACCT_ID_SEQ")
#Column(name = "USER_ACCT_ID")
protected Long id;
#Basic(optional = false)
#Column(length = 50, unique = true)
private String username;
#Basic(optional = false)
#Column(length = 128, nullable = false)
private String password;
#ManyToMany(targetEntity = UserPermission.class)
#JoinTable(name = "USER_ACCT_PERM",
joinColumns = #JoinColumn(name = "USER_ACCT_ID"),
inverseJoinColumns = #JoinColumn(name = "USER_PERM_ID", referencedColumnName = "USER_PERM_ID"))
private Set<UserPermission> permissions;
// getters and setters...
}
AND
#Entity
#Table(name = "USER_PERM")
public class UserPermission implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="USER_PERM_ID_SEQ")
#SequenceGenerator(name="USER_PERM_ID_SEQ", sequenceName="USER_PERM_ID_SEQ")
#Column(name = "USER_PERM_ID")
protected Long id;
#Column(name = "PERM", unique = true, length = 255, nullable = false)
private String authority;
// getters and setters...
}
Now when I create an object of UserAccount and try to save it using the session factory it gives me the following error:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.rpm.domain.UserPermission
Shouldn't the UserPermission class be saved by itself??
This is the code that is used to persist:
#Transactional
public void addUser(){
Set<UserPermission> permissions = new HashSet<UserPermission>();
permissions.add(new UserPermission("ROLE_ADMIN"););
sessionFactory.getCurrentSession().save(new UserAccount("admin", "d033e22ae348aeb5660fc2140aec35850c4da997", permissions));
}
Not unless you specify the cascade options.

Resources