API Rest - OneToMany / ManyTone with JPA and Hibernate - spring

I have the following relationship with this two entities
· Sport has Many SportTranslation
· SportTranslation belongsTo Sport
· Is Bidirectional
When I try to access from SportTranslation to Sport I recieve the following error.
java.lang.StackOverflowError: null
at java.util.AbstractCollection.toString(AbstractCollection.java:454) ~[na:1.8.0_191]
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:510) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
The error is not clear and the tables are not empty.
· I have the classical following Spring MVC architecture
The controller
#RestController
#RequestMapping("/api/public/sports")
public class SportController implements ISportsController {
Logger logger = LoggerFactory.getLogger(SportController.class);
#Autowired
private SportsMethods sportMethods;
#GetMapping(value = "/")
#Override
public ResponseEntity<List<SportDTO>> getSports(Pageable pageable) {
logger.info("--- Retrieve name of class --- : " +this.getClass().getSimpleName() + " --- Method name --- : " + new Object(){}.getClass().getEnclosingMethod().getName());
List<SportDTO> sportsMethodsList = sportMethods.retreiveListSports(pageable);
logger.info("--- Final list ---" + sportsMethodsList);
return new ResponseEntity<List<SportDTO>>(sportsMethodsList,
(sportsMethodsList == null || sportsMethodsList.isEmpty()) ? HttpStatus.NO_CONTENT : HttpStatus.OK);
}
}
· Class Methods
#Component
public class SportsMethods {
#Autowired
private SportService sportService;
public List<SportDTO> retreiveListSports(Pageable pageable) {
List<SportDTO> sportList = sportService.retreiveListSports();
return sportList;
}
}
· The service
#Component
public class SportService implements ISportService {
Logger logger = LoggerFactory.getLogger(SportService.class);
#Autowired
private SportRepository sportRepository;
#Autowired
private SportAdapter sportAdapter;
#Override
public List<SportDTO> retreiveListSports() {
List<Sport> sportList = sportRepository.findAll();
logger.info("--- Retrieve List Of Sports---"+sportList);
List<SportDTO > sportListDTO = sportAdapter.convertListSport2ListSportDTO(sportList);
logger.info("--- Retrieve a List Of SportsDTO" +sportListDTO);
return sportListDTO;
}
}
· The two adapters (SportAdatper , SportTranslationAdapter)
· SportAdapter
#Component
public class SportAdapter {
Logger logger = LoggerFactory.getLogger(SportAdapter.class);
#Autowired
private SportTranslationAdapter sportTranslationAdapter;
public List<SportDTO> convertListSport2ListSportDTO(List<Sport> sportList) {
logger.info("--- Sport List adapter" + sportList);
List<SportDTO> sportDTOList = new ArrayList<SportDTO>();
for (Sport sport : sportList) {
SportDTO sportDTO = new SportDTO();
sportDTO.setId(sport.getId());
sportDTO.setClave(sport.getClave());
sportDTO.setSportTranslationDTO(sportTranslationAdapter.convertSportTranslation2SportTranslationDTO(sport.getSportTranslation()));
sportDTOList.add(sportDTO);
}
logger.info("--- Sport DTO List adapter" + sportDTOList);
return sportDTOList;
}
· SportTranslationAdapter
#Component
public class SportTranslationAdapter {
Logger logger = LoggerFactory.getLogger(SportTranslationAdapter.class);
public List<SportTranslationDTO> convertSportTranslation2SportTranslationDTO(List<SportTranslation> list) {
List<SportTranslationDTO> sportTranslationDTOList = new ArrayList<SportTranslationDTO>();
for (SportTranslation sportTranslation : list) {
SportTranslationDTO sportTranslationDTO = new SportTranslationDTO();
sportTranslationDTO.setSportId(sportTranslation.getSport().getId());
sportTranslationDTO.setName(sportTranslation.getName());
sportTranslationDTOList.add(sportTranslationDTO);
}
return sportTranslationDTOList;
}
}
· Entities Sport and SportTranslation
· Sport
#Entity
#Table(name = "sport")
public class Sport implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sport_id")
private Long id;
#Column(name = "clave")
private String clave;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "sport_id")
private List<SportTranslation> sportTranslation;
·SportTranslation
#Entity
#Table(name = "sportstranslation")
public class SportTranslation implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sportstranslation_id")
private long idSportTranslation;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "sport_id", nullable = true)
private Sport sport;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "language_id", nullable = true)
private Language languageId;
#Column(name = "name")
private String name;
· Language is working correctly ,but this is not the problem the problem is with Sport.
Could anyone helps to me?

I would suggest your entity association to updated as below:
#Entity
#Table(name = "sport")
public class Sport implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sport_id")
private Long id;
#Column(name = "clave")
private String clave;
#OneToMany(mappedBy = "sport", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private List<SportTranslation> sportTranslation;
#Entity
#Table(name = "sportstranslation")
public class SportTranslation implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sportstranslation_id")
private long idSportTranslation;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "sport_id", nullable = true)
private Sport sport;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "language_id", nullable = true)
private Language languageId;
#Column(name = "name")
private String name;
Also, annotate your service method in which you are fetching data with #Transactional.

Related

(Do not display relationship values)

I have two entity with name of the article and article Category.
and they have one-to-many relationships.
I use #JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class,property = "id")
but I cant see data of article category(category_id) in spring data rest.
ArticleCategory.class
#Entity
#Table(name = "article_category")
#Getter
#Setter
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "articleCategory", fetch = FetchType.LAZY)
private Set<Article> articles = new HashSet<>();
}
Article.class
#Entity
#Table(name = "article")
#Getter
#Setter
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Article implements Serializable {
public Article() {
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", nullable = false)
private ArticleCategory articleCategory;
#Column(name = "title")
private String title;
#Column(name = "image_url")
private String image_url;
#Column(name = "short_description")
private String short_description;
#Column(name = "text")
private String text;
#Column(name = "keywords", nullable = true)
private String keywords;
#Column(name = "visit", nullable = false)
private int visit;
#Column(name = "code", nullable = false)
private UUID code;
#Column(name = "date_created")
#CreationTimestamp
private Date dateCreated;
#Column(name = "date_updated", nullable = false)
#UpdateTimestamp
private Date dateUpdated;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
public Article(String title, String image_url, String short_description, String text, String keywords, int visit, UUID code) {
this.title = title;
this.image_url = image_url;
this.short_description = short_description;
this.text = text;
this.keywords = keywords;
this.visit = visit;
this.code = code;
}
}
Article Repository
#CrossOrigin("http://localhost:4200")
#RepositoryRestResource(collectionResourceRel = "article", path = "article")
public interface ArticleRepository extends JpaRepository<Article,Long> {
Article findByCode(UUID uuid);
}
And this is output of spring data rest
enter image description here
That is exactly because you used #JsonManagedReference and #JsonBackReference. Keep in mind the following when using them:
#JsonManagedReference is the forward part of the relationship and is the one that gets serialized normally.
#JsonBackReference is the back part of the relationship and it will be omitted from serialization.
The serialized Article object does not contain a reference to the ArticleCategory object.
If you want to have any ArticleCategory data when serializing Article you can either use #JsonIdentityInfo so that one of the properties is serialized (in this case I've chosen id for both):
#Entity
#Table(name = "article")
#Getter
#Setter
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Article implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", nullable = false)
private ArticleCategory articleCategory;
}
#Entity
#Table(name = "article_category")
#Getter
#Setter
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#OneToMany(cascade = CascadeType.ALL,mappedBy = "articleCategory" ,fetch = FetchType.LAZY)
private Set<Article> articles=new HashSet<>();
}
If you are only interested in categoryId another possibility would be to use #JsonIgnore on private Set<Article> articles property so that it is not serialized:
#Entity
#Table(name = "article_category")
#Getter
#Setter
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#JsonIgnore
#OneToMany(cascade = CascadeType.ALL,mappedBy = "articleCategory" ,fetch = FetchType.LAZY)
private Set<Article> articles=new HashSet<>();
}
If none of those suits your needs you might need to implement your own custom serializer. You can read more about all those options at https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion.
I solved the problem using the controller
And that's why #JsonManageRefrence and #JsonBackRefrence do not work
I replaced the lazy load with the eager load in both entity
#ManyToOne(fetch = FetchType.Eager)
#JoinColumn(name = "user_id")
#JsonManageRefrence
private User user;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "articleCategory",
fetch = FetchType.Eager)
#JsonBackRefrence
private Set<Article> articles = new HashSet<>();
and then add a controller
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#RestController
#RequestMapping("/getAllArticle")
public class MyController {
private ArticleRepository articleRepository;
// you must do constructor injection
#GetMapping("/getAllArticle")
public List<Article> allArticle()
{
return articleRepository.findAll();
}
}

Fetching all entities with many to many relationships in repository layer : Hibernate with JPA

in my current project, I am using Hibernate with JPA.
I have entities as follows:
Entity A:
#Entity
#Table(name = "a")
public class A implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
#Column(name = "name")
private String name;
#OneToMany
private Set<Association> associations = new HashSet<>();
}
Entity B:
#Entity
#Table(name = "b")
public class B implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
#Column(name = "description")
private String description;
#OneToMany
private Set<Association> associations = new HashSet<>();
}
Entity Association:
#Entity
#Table(name = "association")
public class Association implements Serializable {
private static final long serialVersionUID = 1L;
#ManyToOne
#JoinColumn(name = "a_id", referencedColumnName = "id")
A a;
#ManyToOne
#JoinColumn(name = "b_id", referencedColumnName = "id")
B b;
...other columns..
}
Now I would like to implement repository layer for A as follows:
#Repository
public interface ARepository extends JpaRepository<A, Long> {
//Here I would like to write the function/query get all the As with many-to-many relationships.
}

Get Jackson to ignore some fields at controller level in Spring Boot

I have the following class:
#Entity
#Table(name = "Positions")
#NamedQuery(name = "Position.findAll", query = "SELECT p FROM Position p")
public class Position implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id")
private Long id;
private String title;
private String description;
#ElementCollection
#CollectionTable(name = "qualifications", joinColumns = #JoinColumn(name = "position_id"))
#Column(name = "required_qualifications")
private List<String> requiredQualifications;
#ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinTable(name = "positions_competencies", joinColumns = #JoinColumn(name = "position_id"), inverseJoinColumns = #JoinColumn(name = "Competence_id"))
private List<Competence> competencies;
#ManyToOne
#JoinColumn(name = "department_id")
private Department department;
I'm using spring boot with spring data.
I want to ignore some of the fields in the getAllPositions method, but not in the getPositionById method, so #JsonIgnore won't work for me. What is the best way to do this in Spring Boot 2.0.2?
Here is the controller:
#Autowired
private PositionRepository positionRepository;
#GetMapping(path = "/positions")
public Iterable<Position> getAllPositions() {
return positionRepository.findAll();
}
#GetMapping(path = "/positions/{id}")
public Position getPositionById(#PathVariable Long id) {
return positionRepository.findById(id).get();
}

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?

Resources