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?
Related
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.
I have two Entities in relation #ManyToOne:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "TRIPS")
public class Trip {
#Id
#GeneratedValue
#NotNull
#Column(name = "ID", unique = true)
int tripId;
#OneToMany(
targetEntity = Audit.class,
mappedBy = "tripId",
fetch = FetchType.LAZY
)
private List<Audit> audits = new ArrayList<>();
}
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table
public class Audit {
#Id
#GeneratedValue
#NotNull
#Column(unique = true)
public int auditId;
#ManyToOne
#JoinColumn(name = "TRIP_ID")
public Trip tripId;
}
When I'm trying to:
Audit audit = new Audit();
auditDao.save(audit);
List<Audit> audits = new ArrayList<>();
audits.add(audit);
Trip trip = new Trip(audits);
I get exception: detached entity passed to persist.
I tried to change CascadeType to MERGE, but it didn't help.
Where's the problem. Is there maybe other way?
I'm a beginner in spring boot, try to make an app with class Department & Employee. I make the relation between these two classes, Department can have many Employee whereas Employee can only have one Department. Every time I ended with an error:
com.learning.model.Employee cannot be converted to java.lang.Integer
Also, I've found two ways to inserting data into DB via API. First through the service layer, 2nd directly through the controller. Thankful if you could advise as to what is the most authentic method among the two above.
Department.java
#Entity
#Table(name = "Department")
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "dept_seq")
#SequenceGenerator(initialValue = 1, name = "dept_seq", sequenceName = "dept_sequence")
#Column(name = "id")
private Integer id;
#Column(name = "deptName")
private String deptName;
#OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonIgnore
private List<Employee> employees;
}
Employee.java
#Entity
#Table(name = "Employee_Dtls")
#ToString
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "emp_seq")
#SequenceGenerator(initialValue = 1, name = "emp_seq", sequenceName = "employee_sequence")
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "dept_id")
private Department department;
}
DepartmentService.java
#Service
public class DepartmentService {
#Autowired
private EmployeeRepository employeeRepository;
#Autowired
private DepartmentRepository departmentRepository;
//Get Department
public List<Department> getAllDepartments() {
return departmentRepository.findAll();
}
//Add Department
public Department addDepartment(Department department) {
Employee emp = employeeRepository.findById(department.getEmployees().get(department.getId())).orElse(null);
if (null == emp) {
emp = new Employee();
}
emp.setName(department.getEmployees().get(emp.getId()));
department.setEmployees(emp);
return departmentRepository.save(department);
}
}
DepartmentController.java
public class DepartmentController {
#Autowired
private DepartmentService departmentService;
#GetMapping("/get-departments")
public ResponseEntity<List<Department>> getAllDepartments() {
List<Department> departments = departmentService.getAllDepartments();
return new ResponseEntity<>(departments, HttpStatus.OK);
}
#PostMapping("/department")
public ResponseEntity<Department> saveDepartment(#RequestBody Department department) {
Department dept = departmentService.addDepartment(department);
return new ResponseEntity<>(dept, HttpStatus.OK);
}
}
I think you must go over these stacks, these will probably help you to understand how #onetomany annotation works in spring
One to Many Relationship in spring boot REST Api
POSTing oneToMany in a REST call via Spring Boot API
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 the following entity 'User' where the field 'companyId' is a foreign key:
#Entity
#Table(name = "Users")
#Getter #Setter #ToString
public class User {
#Id
#GeneratedValue
private long id;
#Column(name = "company_id")
private Long companyId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "company_id", insertable = false, updatable = false)
private Company company;
The Company entity:
#Entity
#Table(name = "Companies")
#Getter #Setter #ToString
public class Company {
#Id
#GeneratedValue
private long id;
#OneToMany(mappedBy = "company", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
private List<User> users;
I removed other irrelevant fields from the classes.
I'm using spring boot data jpa.
My question is how to remove the field 'companyId' and use the company id inside the field 'company' for CRUD functions with the DB.
Simply remove the companyId and make Company writable
#Entity
#Table(name = "Users")
#Getter #Setter #ToString
public class User {
#Id
#GeneratedValue
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "company_id")
private Company company;