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?
Related
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<>();
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;
I have an issue that even if I delete an element from #OneToMany relation set, it keeps in database. I tried Cascade.ALL, orphanRemoval = true etc. but still couldn't find the problem.
What am I doing wrong?
#Entity
#Table(name = "PARENT")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Parent extends JpaBaseEntity {
....
#OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL }, mappedBy = "parent", orphanRemoval = true)
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Child> children = new HashSet<>();
public Parent(ParentSO parentSO) {
.....
this.getChildren().clear();
this.getChildren().addAll(parentSO.getChildren(this));
.....
}
.....
}
#Entity
#Table(name = "CHILD")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Child extends JpaBaseEntity {
.....
#ManyToOne
#JoinColumn(name = "PARENT_ID", nullable = false)
private Parent parent;
.....
}
Parent parent = new Parent(parentSO);
subsDao.updateParent(parent);
I have a tricky problem to let hibernate order two list of the same entity. With some code it could be easier to understand what I wanna do.
#MappedSuperclass
public abstract class AbstractParent {
List<CommonObject> commonObjects;
public abstract List<CommonObject> getCommonObjects();
}
#Entity
#Table
public class Child1 extends AbstractParent {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name="child1_id", nullable = false)
#OrderColumn(name = "sort_index")
public List<CommonObject> getCommonObject() {
return this.commonObjects;
}
}
#Entity
#Table
public class Child2 extends AbstractParent {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name="child2_id", nullable = false)
#OrderColumn(name = "sort_index")
public List<CommonObject> getCommonObject() {
return this.commonObjects;
}
}
But because hibernate handle the mapping of the column "sort_index", it's mapped twice (for Child1 and Child2). So I get this error :
Caused by: org.hibernate.MappingException: Repeated column in mapping
for entity ... column: sort_index (should be mapped with
insert="false" update="false")
I know that I can resolve this problem if I put two different columns for sorting. But I would like to know if someone has a better solution to give me.
Thanks,
I added a test to replicate your issue on GitHub and it works after modifying the mappings to this:
#MappedSuperclass
public abstract class AbstractParent {
public abstract List<CommonObject> getCommonObjects();
}
#Entity(name = "Child1")
public class Child1 extends AbstractParent {
#Id
private Long id;
#OneToMany(targetEntity = CommonObject.class, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(name = "child_1_common_objects", joinColumns = #JoinColumn(name="child1_id", nullable = false))
#OrderColumn(name = "sort_index")
private List<CommonObject> commonObjects = new ArrayList<>();
public List<CommonObject> getCommonObjects() {
return this.commonObjects;
}
}
#Entity(name = "Child2")
public class Child2 extends AbstractParent {
#Id
private Long id;
#OneToMany(targetEntity = CommonObject.class, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(name = "child_2_common_objects", joinColumns = #JoinColumn(name="child2_id", nullable = false))
#OrderColumn(name = "sort_index")
private List<CommonObject> commonObjects = new ArrayList<>();
public List<CommonObject> getCommonObjects() {
return this.commonObjects;
}
}
#Entity(name = "CommonObject")
public class CommonObject {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
I didn't know how to describe my question in the title but I hope it will do.
So here is my situation.
I use hibernate to map my entities to db tables.
I got one entity like this:
#Entity
#Table(name = "EX.EXAMPLE")
public abstract class Entity
{
private CustomEntity customEntity;
public static final String CUSTOM_ENTITY = "customEntity";
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity()
{
return this.customEntity;
}
}
And my CustomEntity
#Entity
#Table(name = "EX.EXAMPLE2")
public class CustomEntity
{
private Entity entity;
public static final String ENTITY = "entity";
#OneToOne
#JoinColumn(name = "ID_ENTITY", nullable = true)
public Entity getEntity()
{
return this.ntity;
}
}
So here is my question: Is it possible to add another CustomEntity relation to Entity? And how do I map it?
Example what I mean:
#Entity
#Table(name = "EX.EXAMPLE")
public abstract class Entity
{
private CustomEntity customEntity;
public static final String CUSTOM_ENTITY = "customEntity";
private CustomEntity customEntity2;
public static final String CUSTOM_ENTITY2 = "customEntity2";
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity()
{
return this.customEntity;
}
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity2()
{
return this.customEntity2;
}
}
I only managed it by changing customEntity to a list in Entity.
Greetings
Yes, that is perfectly normal situation. You just need two fields with different mappedBy`, one for each relation
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY1, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity()
{
return this.customEntity;
}
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY2, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
#JoinColumn(name = "entity_2_id")
public CustomEntity getCustomEntity2()
{
return this.customEntity2;
}
And two fields in CustomEntity, one for each mapping
#OneToOne
#JoinColumn(name = "ID_ENTITY_1", nullable = true)
public Entity getEntity1()
{
return this.entity1;
}
#OneToOne
#JoinColumn(name = "ID_ENTITY_2", nullable = true)
public Entity getEntity2()
{
return this.entity2;
}