Looping in entities hibernate - java

I have two entities and when I want to get grade.getSubcompetence(). Error happens when i save entity, because subcompetence data null. When i debug i see error:
'java.lang.StackOverflowError' exception. Cannot evaluate _$$_jvstea5_f.toString()
grade entity:
#Entity
#Data
#NoArgsConstructor
#ToString(exclude = {"subcompetence"})
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
#Table(name = "GRADE")
public class Grade {
#JsonBackReference
#ManyToOne(
fetch = FetchType.LAZY,
optional = false)
#JoinColumn(name = "SUBCOMPETENCE_ID", nullable = false)
private Subcompetence subcompetence;
#JsonBackReference
#ManyToOne(
fetch = FetchType.LAZY,
optional = false)
#JoinColumn(name = "LEVEL_ID", nullable = false)
private Level level;
}
subcompetence entity:
#Entity
#Data
#NoArgsConstructor
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
#Table(name = "SUBCOMPETENCE")
public class Subcompetence {
#JsonManagedReference
#OneToMany(
mappedBy = "subcompetence",
fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private List<Grade> grades;
}

try adding #EqualsAndHashCode(exclude="grades") in your Subcompetence class

Add add #EqualsAndHashCode(callSuper=false) and JsonIgnore like below
#OneToMany(
mappedBy = "subcompetence",
fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#JsonIgnore
private List<Grade> grades;

Related

How Do relationship JPA OneToOne Nullable with none CascadeType

Stack Java 11 , spring boot 2.7, jpa
I try to creat relationship OneToOne Nullable and with no update or insert cascade.
When i call saveEntity i want it to save only the id and only check if the id exists.
the tables are in different databases
#Data
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "USER_ROLE", uniqueConstraints = {#UniqueConstraint(columnNames = {"USR_ID"})})
#EntityListeners(AuditingEntityListener.class)
#Builder
public class UserRoleEntity {
#Id
#Column(name = "USR_ID")
private String id;
#OneToOne(fetch = FetchType.EAGER, targetEntity = UserAEntity.class, cascade = {
CascadeType.DETACH, CascadeType.MERGE
})
#JoinColumn(name = "USR_ID", referencedColumnName = "USA_ID", nullable = true, insertable = false, updatable = false)
private UserAEntity userAEntity;
...
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "T_USER_ATTRIBUTE", uniqueConstraints = {#UniqueConstraint(columnNames = {"USA_ID"})})
#EntityListeners(AuditingEntityListener.class)
#Builder
public class UserAEntity {
#Id
#Column(name = "USA_ID")
private String id;
...
}
[one Save UserRoleEntity ]
If remove (fetch = FetchType.EAGER, targetEntity = UserAEntity.class, cascade = { CascadeType.DETACH, CascadeType.MERGE })
i have null error on creation but works with Update.
If i leave it works but the userAEntity changes are saved in the database

Cannot create entity where two of the properties are another entity's Id and ArrayList of Ids of another entity

I'm new at Spring Boot's JPA concept so need your help in deciding how to import the ID of another entity and ArrayList of Ids of another entity. I want to create a board, providing an account's Id and ArrayList of Ids of accounts.
Following are my Account and Board entities:
#Entity(name = "Account")
#Table(name = "account", uniqueConstraints = {#UniqueConstraint(name = "account_email_unique", columnNames = "email")})
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "account_id")
private Integer accountId;
#JsonIgnore
#OneToMany(targetEntity = Board.class, mappedBy = "boardOwnerId")
private Set<Board> boardSet = new HashSet<>();
#JsonIgnore
#ManyToMany(mappedBy = "boardMembers")
private Set<Board> boards = new HashSet<>();
#Entity(name = "Board")
#Table(name = "board", uniqueConstraints = {#UniqueConstraint(name = "board_name_unique", columnNames = "name")})
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Board {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "board_id")
private Integer boardId;
#ManyToOne(targetEntity = Account.class, cascade = CascadeType.ALL)
#JoinColumn(name = "account_id", referencedColumnName = "account_id")
private Account boardOwnerId;
#ManyToMany
#JoinTable(name = "board_member", joinColumns = #JoinColumn(name = "board_id"), inverseJoinColumns =
#JoinColumn(name = "account_id"))
private Set<Account> boardMembers = new HashSet<>();
#Repository
public interface BoardRepository extends JpaRepository<Board, Integer> {
}
#RestController
#RequestMapping("/boards")
public class BoardController {
private final BoardService boardService;
#Autowired
public BoardController(BoardService boardService) {
this.boardService = boardService;
}
#PostMapping("/create-board")
ResponseEntity<BoardDtoResponse> createBoard(#Valid #RequestBody BoardDto boardDto) {
return new ResponseEntity<>(boardService.createBoard(boardDto), HttpStatus.CREATED);
}
}
#Service
public class BoardServiceImpl implements BoardService {
private final BoardRepository boardRepository;
private final ModelMapper modelMapper;
#Autowired
public BoardServiceImpl(BoardRepository boardRepository) {
this.boardRepository = boardRepository;
modelMapper = new ModelMapper();
}
#Override
public BoardDtoResponse createBoard(BoardDto boardDto) {
Board boardToSave = modelMapper.map(boardDto, Board.class);
Board newBoard = boardRepository.save(boardToSave);
return modelMapper.map(newBoard, BoardDtoResponse.class);
}
}
I can successfully create an account, but when I want to create a board and pass boardOwnerId and membersIds, it creates a board, but boardOwnerId and membersIds are set to null.
Here is the request via Postman:
Thanks in advance for your time!
As far as I have seen, you should change the mapping between the two entities for both mappings. Let me explain:
For the mapping of the board owner (#OneToMany) try to maintain only that one annotation and remove the property with #ManyToOne from Board entity. In addition, change the properties values of the #OneToMany annotation and add a #JoinColumn with next values:
#Entity(name = "Account")
#Table(name = "account", uniqueConstraints = {#UniqueConstraint(name = "account_email_unique", columnNames = "email")})
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "account_id")
private Integer accountId;
#JsonIgnore
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
#JoinColumn(name = "boardOwnerId")
private Set<Board> boardSet = new HashSet<>();
...
#Entity(name = "Board")
#Table(name = "board", uniqueConstraints = {#UniqueConstraint(name = "board_name_unique", columnNames = "name")})
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Board {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "board_id")
private Integer boardId;
...
This is known as a One To Many unidirectional mapping (https://www.bezkoder.com/jpa-one-to-many-unidirectional/).
On the other hand you could try to maintain only the #ManyToOne annotation on Board entity, but remove the property with #OneToMany annotation from Account entity with next properties values:
#Entity(name = "Account")
#Table(name = "account", uniqueConstraints = {#UniqueConstraint(name = "account_email_unique", columnNames = "email")})
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "account_id")
private Integer accountId;
...
#Entity(name = "Board")
#Table(name = "board", uniqueConstraints = {#UniqueConstraint(name = "board_name_unique", columnNames = "name")})
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Board {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "board_id")
private Integer boardId;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "account_id", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
private Account boardOwnerId;
...
This is known as the default One To Many mapping (https://www.bezkoder.com/jpa-one-to-many/).
In any case, you see you only have to implement one of the two types of annotations for a One To Many mapping.
And last, for the #ManyToMany mappings, try the next implementation (adding fetch and cascade properties values):
#Entity(name = "Account")
#Table(name = "account", uniqueConstraints = {#UniqueConstraint(name = "account_email_unique", columnNames = "email")})
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Account {
...
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "boardMembers")
#JsonIgnore
private Set<Board> boards = new HashSet<>();
#Entity(name = "Board")
#Table(name = "board", uniqueConstraints = {#UniqueConstraint(name = "board_name_unique", columnNames = "name")})
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Board {
...
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "board_member", joinColumns = #JoinColumn(name = "board_id"), inverseJoinColumns =
#JoinColumn(name = "account_id"))
private Set<Account> boardMembers = new HashSet<>();
You can find this implementation design here: https://www.bezkoder.com/jpa-many-to-many/
The problem was that the entity was not mapping properly with dto. The solution is explicit mapping plus the answer of Gescof.
Here I found information about explicit mapping: ModelMapper mapping the wrong id
Changed code in the service class:
#Service
public class BoardServiceImpl implements BoardService {
private final BoardRepository boardRepository;
private final ModelMapper modelMapper;
#Autowired
public BoardServiceImpl(BoardRepository boardRepository) {
this.boardRepository = boardRepository;
modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.LOOSE);
}
#Override
public BoardDtoResponse createBoard(BoardDto boardDto) {
Board boardToSave = modelMapper.map(boardDto, Board.class);
Board newBoard = boardRepository.save(boardToSave);
return modelMapper.map(newBoard, BoardDtoResponse.class);
}
}
Changed code in the entity classes:
#Entity(name = "Account")
#Table(name = "account", uniqueConstraints = {#UniqueConstraint(name = "account_email_unique", columnNames = "email")})
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "account_id")
private Integer accountId;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "boardMembers")
private Set<Board> boards = new HashSet<>();
#Entity(name = "Board")
#Table(name = "board", uniqueConstraints = {#UniqueConstraint(name = "board_name_unique", columnNames = "name")})
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Board {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "board_id")
private Integer boardId;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "account_id", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
private Account boardOwnerId;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "board_member", joinColumns = #JoinColumn(name = "board_id"), inverseJoinColumns =
#JoinColumn(name = "account_id"))
private Set<Account> boardMembers = new HashSet<>();

How to map relationship with multiple target entities

I don't understand how to map bVersionId.
#Data
#Table(name = "prefix_class_a")
ClassA{
//...
private Long bVersionId;
//...
}
This tables already have ManyToOne relationship.
#Data
#Table(name = "prefix_class_b")
ClassB{
//...
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "classB")
private Collection<ClassBVersion> versions = new ArrayList<>();
//...
}
#Data
#Table(name = "prefix_class_b_version")
ClassBVersion{
//...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "prefix_class_b_id")
private ClassB classB;
//...
}
what relation should be used? And how?

Spring data JPA clone entity with LAZY relationships

I have Entity:
#Data
#Entity
#Table(name = "USERS")
public class User{
#Id
#Column(name = "GUID", nullable = false)
private String guid;
#OneToMany(mappedBy = "user", cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = LAZY)
private List<Role> roles;
#OneToMany(mappedBy = "user", fetch = LAZY, cascade = {CascadeType.ALL})
private List<Person> persons;
And I need clone this entity. I do it like this:
usersRepository.detach(user);
But I can not get roles and persons becaus it LAZY. I use hack:
user.getRoles().size();
user.getPersons().size();
usersRepository.detach(user);
But I do not like it. Can I make it easier?

Hibernate #OneToMany #ManyToOne mapping

everyone i have a question with using annotation #OneToMany/#ManyToOne; is it possible to create one user model with two sets of subjects in this model (conducted for the teacher and attending the student) instead of creating separate student and teacher models? I wrote such a code but when I want to get data about item and user, hibernate crashes the "Stack overflow" error.I will add that I use H2 Database.
User Entity:
#Entity
public class User{
#OneToMany(
mappedBy = "student",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
private Set<Item> items = new HashSet<>();
#OneToMany(mappedBy = "teacher",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
private Set<Item> carriedItems= new HashSet<>();
}
//id and other data
Item entity:
#Entity
public class Item{
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "student_id", nullable = false)
private User student;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "teacher_id", nullable = false)
private User teacher;
}
//id and other data
Thanks for help #Leviand
Based on your comments, looks like your code and logic needs some rework.
First, use instead of Item pojo two dedicated classes:
#Entity
#Table(name = Studend) // I'm guessing a table name
public class Student{
#JoinColumn(name = "student_id", nullable = false)
private User user;
}
#Entity
#Table(name = Teacher) // I'm guessing a table name
public class Teacher{
#JoinColumn(name = "teacher_id", nullable = false)
private User user;
}
Then since an User can only be connected to a single Teacher or Student, refact that in something like:
#Entity
#Table(name = User) // I'm guessing a table name
public class User{
#OneToOne(
mappedBy = "student",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
private Student student;
#OneToMany(mappedBy = "teacher",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
private Teacher teacher;
}
Hope this helps :)

Categories