JPA Model:
#Getter
#Setter
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "BASE_RECORD")
public class BaseRecord {
#Id
#Column(name = "DB_ID")
public Long id;
#Embedded
public DeclarantRecord declarant;
}
#Getter
#Setter
#Embeddable
#AllArgsConstructor
#NoArgsConstructor
public class DeclarantRecord {
#Column(name = "DECLARANT_ID")
public String declarantId;
#Column(name = "DECLARANT_IDE")
public String identificationNumber;
#Column(name = "DECLARANT_NAME")
public String name;
#OneToMany(mappedBy = "baseRecord", fetch = FetchType.EAGER)
public List<DeclarantAddress> addresses;
}
#Getter
#Setter
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "ADDRESS_RECORD")
public class DeclarantAddress {
#Id
#Column(name = "DB_ID")
public Long id;
#ManyToOne
#JoinColumn(name = "PERS_ID", referencedColumnName = "DECLARANT_ID")
public BaseRecord baseRecord;
}
When fetching a BaseRecord from DB by using a Spring Boot JPARepository, I receive the following exception:
Caused by: org.hibernate.AnnotationException: referencedColumnNames(DECLARANT_ID) of DeclarantAddress.baseRecord referencing BaseRecord not mapped to a single property
I cannot understand what's wrong and moreover, if I inline the DeclarantRecord in the BaseRecord entity by declaring all the properties in there and moving the exact same relationship declaration in the BaseRecord class, everything works perfectly.
The answer that worked for me:
Move
#Column(name = "DECLARANT_ID") public String declarantId; to BaseRecord class.
The rest can stay the same.
Related
I'm auto generating entities within the database which works great using:
spring.jpa.hibernate.ddl-auto=update
Now I need an additional attribute within the association table and I can't figure it out without creating an association entity by hand. The current classes listed below generate an association table without the additional attribute.
The tables I need:
persons:
id
search_requests:
id
search_requests_persons:
person_id
search_request_id
study_id
The classes I currently have (simplified):
#NoArgsConstructor
#RequiredArgsConstructor(staticName = "of")
#Getter
#Setter
#Entity
#EqualsAndHashCode
#Table(name = "persons")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Long id;
// The following line is what I would need
// private List<Integer> studyIds;
}
#NoArgsConstructor
#RequiredArgsConstructor(staticName = "of")
#Getter
#Setter
#Entity
#EqualsAndHashCode
#Table(name = "search_requests")
public class SearchRequest {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Long id;
#NonNull
#ManyToMany
private List<Person> persons;
}
I'm using Lombok and Javax Persistence for the annotations.
In order to add additional attributes in your JoinTable, you could manually create your JoinTable entity. e.g:
PersonSearchRequest.java
#Entity
#Table(name = "person_search_request)
#Getter
#Setter
#NoArgsConstructor
public class PersonSearchRequest {
#EmbeddedId
private PersonSearchRequestPK id;
// put your additional attribute here, e.g:
private String attribute1;
private Long attribute2;
#ElementCollection
private List<String> attribute3s;
#Embeddable
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public static class PersonSearchRequestPK implements Serializable {
#ManyToOne(fetch = FetchType.LAZY)
private Person person;
#ManyToOne(fetch = FetchType.LAZY)
private SearchRequest searchRequest;
}
}
Person.java
#NoArgsConstructor
#RequiredArgsConstructor(staticName = "of")
#Getter
#Setter
#Entity
#EqualsAndHashCode
#Table(name = "persons")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Long id;
#OneToMany(mappedBy = "id.person")
private List<PersonSearchRequest> personSearchRequests;
}
SearchRequest.java
#NoArgsConstructor
#RequiredArgsConstructor(staticName = "of")
#Getter
#Setter
#Entity
#EqualsAndHashCode
#Table(name = "search_requests")
public class SearchRequest {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Long id;
#OneToMany(mappedBy = "id.searchRequest")
private List<PersonSearchRequest> personSearchRequests;
}
I have two tables, the first one is TB_RECIPE_DATA, where the PK is the ID_RECIPE field. The second table is TB_RECIPE_ITEM, where the PK is composed of three fields: ID_RECIPE, CD_LOT and CD_PRODUCT. These two tables are related so that a recipe can have multiple items. The problem I'm facing is that when I try to register a recipe with more than one item, I get an error message "InvalidDataAccessApiUsageException: Multiple representations of the same entity". When I register a recipe with just one item, it works.
In the research I've done, many indicate that it's because of Cascade, I've already tried switching to cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH} and it didn't work. The mapping of the tables was done this way:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "TB_RECIPE_DATA", schema = "A_SAMPLE")
public class Recipe {
#Id
#Column(name = "ID_RECIPE")
private Long id;
#Fetch(FetchMode.SUBSELECT)
#OneToMany(mappedBy = "id", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<RecipeItem> items;
}
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "TB_RECIPE_ITEM", schema = "A_SAMPLE")
public class RecipeItem {
#Id
#Column(name = "ID_RECIPE")
private Long id;
#Column(name = "CD_LOT")
private String lot;
#Column(name = "CD_PRODUCT")
private Long code;
#Column(name = "QT_PURCHASE")
private Long purchaseQuantity;
#Column(name = "FL_AVAILABLE")
private Boolean available;
}
The error was happening because when changing, for example, recipe A with its respective items, each item has recipe A as part of the Primary Key, so I would be changing the recipe twice. The solution would be to work the bi-direction for this case. I will share my solution in case anyone experiences a similar problem.
Main class:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "TB_RECIPE_DATA", schema = "A_SAMPLE")
public class Recipe {
#Id
#Column(name = "ID_RECIPE")
private Long id;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private List<RecipeItem> items;
}
Child class:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "TB_RECIPE_ITEM", schema = "A_SAMPLE")
public class RecipeItem {
#EmbeddedId
private RecipeItemPk id;
#Column(name = "QT_PURCHASE")
private Long purchaseQuantity;
#Column(name = "FL_AVAILABLE")
private Boolean available;
#ManyToOne
#JoinColumn(name = "ID_RECIPE", insertable = false, updatable = false)
private Recipe recipe;
}
Primary Key class:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(of = { "id", "lot", "code" })
#Embeddable
public class RecipeItemPk {
#Column(name = "ID_RECIPE")
private Long id;
#Column(name = "CD_LOT")
private String lot;
#Column(name = "CD_PRODUCT")
private Long code;
}
I have this problem where trying to delete a parent object via crud repository, I'm getting this error message:
"collection owner not associated with session: ClassA.things"
This error appeared after splitting one class into three (two classes extend the main classA), but I haven't done any other changes, I haven't touched this OneToMany relationship. So right now it looks approximately like this:
Class A:
#Table
#Entity(name = "ClassA")
#Inheritance(strategy = InheritanceType.JOINED)
#Getter #Setter #NoArgsConstructor #ToString
public class ClassA {
#Column(name = "ClassA_id")
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#OneToMany(mappedBy = "classA", orphanRemoval = true)
private Collection<Thing> things;
...etc, other fields
}
Class B and C (both extend class A):
#Table
#Entity(name = "ClassB")
#Getter #Setter #NoArgsConstructor
public class ClassB extends ClassA {
#Column(name = "ClassB_id")
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
...etc, other fields
}
#Table
#Entity(name = "ClassC")
#Getter #Setter #NoArgsConstructor
public class ClassC extends ClassA {
#Column(name = "ClassC_id")
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
...etc, other fields
}
And here's the Thing class, which the ClassA has a collection of with #OneToMany relationship:
#Table
#Entity(name = "Ratings")
#Getter #Setter #NoArgsConstructor
public class Thing {
#Column(name = "Thing_id")
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Setter(value = AccessLevel.PRIVATE)
private int id;
#ManyToOne
#JoinColumn(name = "ClassA_id")
private ClassA classA;
...etc, other fields
}
Finally, here's the controller method where DELETE is being called to delete ClassB, for example:
#Transactional
#DeleteMapping("/delete/{id}")
public ResponseEntity<String> deleteClassB(#PathVariable(name = "id") int id) {
if (classBservice.delete(id)) {
return ResponseEntity.ok().build();
} else {
return ResponseEntity.badRequest().build();
}
}
try to remove the id members from ClassB and ClassC since they are inherited from ClassA.
I have the following embeddable
#Data
#Embeddable
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class BaseEntity {
#CreatedDate
#Column(name = "created_date")
private LocalDateTime createdDate;
}
Which I embedded into another entity like this
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "room")
#Builder
public class room {
#Id
#GeneratedValue
private UUID id;
#JsonUnwrapped
#Embedded
private BaseEntity baseEntity;
#Column(length = 80, nullable = false)
private String name;
}
Now when I save a room entity the embedded BaseEntity is null and that is why nothing is audited. According to https://docs.spring.io/spring-data/commons/docs/2.4.4/reference/html/#auditing.annotations it should just work like this. How can I fix this behaviour ?
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "room")
#Builder
public class room {
...
#JsonUnwrapped
#Embedded
private BaseEntity baseEntity = new BaseEntity();
...
}
I develop a browser game on spring boot and I need a unidirectional OneToMany mapping. I have inventories entity:
#Entity
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Table(name = "inventories")
public class Inventory {
#Id
private String id;
#OneToMany(targetEntity = Equipment.class, cascade = CascadeType.ALL)
#JoinColumn(name = "equipment_id", referencedColumnName = "id")
private List<Equipment> equipments;
public static Inventory getDefaultInventory(String id){
return Inventory.builder()
.id(id)
.equipments(new ArrayList<>())
.build();
}
}
and Equipment entity
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "equipments")
public class Equipment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Enumerated(EnumType.STRING)
private EquipmentType type;
private Integer strength;
private Integer endurance;
private Integer agility;
private Integer intuition;
private Integer reaction;
private Integer rage;
private Integer wisdom;
}
All one-to-one mappings work fine and empty entities saves ok.
When I am trying to put Equipment into inventory and try to save data I catch 500 error.
2021-02-27 17:44:08.548 ERROR 11108 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: column equipments5_.equipment_id not exist.
Why hibernate think that eqipment_id is the Equipment column?