#ManyToMany Referential integrity constraint violation ON DELETION
I got this manytomany relationship
#ManyToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER)
#JoinTable(
name="tbl_settings_employees",
joinColumns = #JoinColumn(name = "id"),
inverseJoinColumns = #JoinColumn( name = "employee_id")
)
private Set<Employee> employeeSet;
which doesn't allow me to delete the primary object. with other words, i cant delete an employee from the database in my case.
I got 4 tables for this example, a user table, a settings table a settings_edit_employee_set table and a employee table.
These are the tables and the relations:
|---------| |----------------------------|
| tbl_user| | tbl_settings |
| --------| | ---------------------------|
| Id (PK) | | Id (PK)| user_id (PK ref tbl_user)
|---------| |----------------------------|
| 1 | | 1 | 1 |
| 2 | | 2 | 2 |
| 3 | | 1 | 2 |
| 4 | | 2 | 1 |
| etc | |etc |etc |
|---------| |----------------------------|
|-------------------------------|
| tbl_settings_employees (pk=composite)
| ------------------------------|
| Id (PK ref tbl_settings) |
| | Employee_ID(PK ref tbl_employee)
|-------------------------------|
| 1 | 150 |
| 1 | 149 |
| 2 | 150 |
| 2 | 151 |
| etc | etc |
|-------------------------------|
|-------------|
| tbl_employee|
| ------------|
| Id PK |
|-------------|
| 149 |
| 150 |
| 151 |
| etc |
|-------------|
short:
I want to delete an object from my database but I can't right now; because it gives a Referential integrity constraint violation exception :
org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation:
"FKE5EF0384FB556B15: PUBLIC.TBL_SETTINGS FOREIGN KEY(EMPLOYEE_EMPLOYEE_ID) REFERENCES
PUBLIC.TBL_EMPLOYEE(EMPLOYEE_ID) (2)"; SQL statement:
delete from tbl_employee where bsn='234234234' [23503-166]
Try with: cascade = CascadeType.DETACH
Had a similar problem
#ManyToMany(targetEntity = DdoMaster.class)
#Cascade(value = {CascadeType.DELETE, CascadeType.SAVE_UPDATE})
#JoinTable(name = "acct_account_ddo_mapping", joinColumns = #JoinColumn(name = "fk_accountid", referencedColumnName = "pk_accountname"), inverseJoinColumns = #JoinColumn(name = "fk_ddoid", referencedColumnName = "pk_ddoid"))
#RemoteProperty
public Set<DdoMaster> getDdoMaster() {
return ddoMaster;
}
public void setDdoMaster(Set<DdoMaster> ddoMaster) {
this.ddoMaster = ddoMaster;
}
now while removing the row from the table it was throwing Dataintegrity exception , Simply cleared the set first and deleted the row later.... .
Related
I am working on a simple personal project in order to learn SpringBoot.
Project Overview: Spring Boot aplication that gets data and inserts data through RestController endpoint into a MariaDb instance on a docker.
My MariaDb table is this:
\+------------------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
\+------------------+------------+------+-----+---------+----------------+
| ACCOUNT_ID | int(11) | NO | PRI | NULL | auto_increment |
| ACCOUNT_VALUE | int(11) | YES | | 0 | |
| ACCOUNT_CURRENCY | varchar(5) | YES | | NULL | |
\+------------------+------------+------+-----+---------+----------------+
My table has data (I inserted it manualy from the terminal):
MariaDB \[dev\]\> select \* from CUSTOMER_ACCOUNT;
\+------------+---------------+------------------+
| ACCOUNT_ID | ACCOUNT_VALUE | ACCOUNT_CURRENCY |
\+------------+---------------+------------------+
| 1 | 300 | RON |
| 2 | 300 | RON |
| 3 | 300 | RON |
| 4 | 300 | RON |
| 5 | 300 | RON |
| 6 | 300 | RON |
| 7 | 300 | RON |
| 8 | 300 | RON |
| 9 | 300 | RON |
| 10 | 300 | RON |
| 11 | 300 | RON |
| 12 | 300 | RON |
| 13 | 300 | RON |
| 14 | 300 | RON |
| 15 | 300 | RON |
| 16 | 300 | RON |
| 17 | 300 | RON |
| 18 | 300 | RON |
\+------------+---------------+------------------+
My Entity class is this:
#Entity
#Table(name = "CUSTOMER_ACCOUNT", schema = "dev", catalog = "")
public class CustomerAccountEntity {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
#Column(name = "ACCOUNT_ID")
private int accountId;
#Basic
#Column(name = "ACCOUNT_VALUE")
private Integer accountValue;
#Basic
#Column(name = "ACCOUNT_CURRENCY")
private String accountCurrency;
public int getAccountId() {
return accountId;
}
public void setAccountId(int accountId) {
this.accountId = accountId;
}
public Integer getAccountValue() {
return accountValue;
}
public void setAccountValue(Integer accountValue) {
this.accountValue = accountValue;
}
public String getAccountCurrency() {
return accountCurrency;
}
public void setAccountCurrency(String accountCurrency) {
this.accountCurrency = accountCurrency;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CustomerAccountEntity that = (CustomerAccountEntity) o;
return accountId == that.accountId && Objects.equals(accountValue, that.accountValue) && Objects.equals(accountCurrency, that.accountCurrency);
}
#Override
public int hashCode() {
return Objects.hash(accountId, accountValue, accountCurrency);
}
}
My repository class is this :
#Repository
public interface CustomerAccountRepository extends JpaRepository\<CustomerAccountEntity,Long\> {
#Query("Select ca.accountId, ca.accountValue, ca.accountCurrency from CustomerAccountEntity ca")
List<CustomerAccountEntity> findAllCA();
}
My settings is this:
# connect via localhost on port 3306
spring.datasource.url=jdbc:mariadb://localhost:3306/dev
spring.datasource.username=root
spring.datasource.password=mypass
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
spring.datasource.driver=cdata.jdbc.mariadb.MariaDBDriver
When I hit the controller to retrieve all the data it returns empty in browser.
I attempted to play with the settings to change the driver and hibernate dialect. Note: I do have a succesfull conection to the DB
When I start the aplication there pops up a new table in mariadb
| Tables_in_dev |
+------------------+
| CUSTOMER_ACCOUNT |
| customer_account |
+------------------+
2 rows in set (0.000 sec)
Hibernate calls goes to the lower case ones, but this one is empty. It seems it is deleted when I stop the app.
I am still learning... but can someone explain what is happening.
spring.jpa.hibernate.ddl-auto=create-drop;
having this in your properties file will drop all existing content in your table each time you restart or run your spring-boot application.
To keep all records in your tables change to spring.jpa.hibernate.ddl-auto=update;
Your repo has Long as id type while entity has int. Try to make them both of the same type Long
To avoid data loss while using mariadb in docker, you should create a docker volume and plug it to /var/lib/mysql/data when you create your container
I have below table structure. I just need to read data from these tables. I don't have control over table structure.
There is foreign key on other tables to join with Transaction table. But TABLE_A to TABLE_D is having related data that is having common X_ID.
+-------------+
| Transaction |
+-------------+
| T_ID |
| Colls |
+-------------+
+-------------+
| TABLE_A |
+-------------+
| A_ID |
| T_ID | FK to Transaction Table
| X_ID |
+-------------+
+-------------+
| TABLE_B |
+-------------+
| B_ID |
| T_ID | FK to Transaction Table
| X_ID |
+-------------+
+-------------+
| TABLE_C |
+-------------+
| C_ID |
| T_ID | FK to Transaction Table
| X_ID |
+-------------+
+-------------+
| TABLE_D |
+-------------+
| D_ID |
| T_ID | FK to Transaction Table
| X_ID |
+-------------+
My current solution is like below
#Entity
public class TransactionEntity {
#Id
#Column(name = "transaction_id")
private Long id;
#OneToMany
#JoinColumn(name = "t_id")
#MapKey(name = "x_id")
private Map<Long, TableA> tablea;
#OneToMany
#JoinColumn(name = "t_id")
#MapKey(name = "x_id")
private Map<Long, TableB> tableb;
#OneToMany
#JoinColumn(name = "t_id")
#MapKey(name = "x_id")
private Map<Long, TableC> tablec;
#OneToMany
#JoinColumn(name = "t_id")
#MapKey(name = "x_id")
private Map<Long, TableD> tabled;
}
Then process TABLE_* details in java code to group them together based on X_ID. I need to represent these data in blow json.
{
"t_id": 1,
"x_data": [
"x_id": 1234,
"table_a": {
},
"table_b": {
},
"table_c": {
},
"table_d": {
}
]
}
Is there any better way to achieve this JPA?
I have discovered a In my opinion a strange behaver from jpa. First here is the a simplified version of my code ( If I missed something let me know)
#Data
#Entity
#Table(name = "parent")
Parent {
#Id
#GeneratedValue
#Column(name = "id")
private Long id;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(
name = "parent_child_mapping",
joinColumns = #JoinColumn(name = "parent_id"),
inverseJoinColumns = #JoinColumn(name = "Child_id"))
private Set<Child> childs;
#Column(name = "name", unique = true, nullable = false)
private String name;
}
#Data
#Entity
#Table(name = "child")
Child {
#Id
#GeneratedValue
#Column(name = "id")
private Long id;
#ManyToMany(mappedBy = "keywords")
private Set<Parent> parents;
#Column(name = "name", unique = true, nullable = false)
private String name;
public Child() {
this.set = new HashSet<>();
}
public Child(String name, Parent p) {
this();
this.name = name;
this.parents.add(p);
}
public Child(String name, Set<Parent> parents) {
this();
this.name = name;
this.parents.addAll(parents);
}
}
interface ChildRpo extends extends JpaRepository<Child , Long> {
Optional<Child> findByName(String name);
}
interface ParentRepo extends extends JpaRepository<Parent, Long> {
Optional<Parent> findByName(String name);
}
DB:
| Parent | | parent_child_mapping | | Child |
| id | name | | child_id | parent_id | | id | name |
| 1 | pa1 | | 1 | 1 | | 1 | child1 |
| 2 | pa2 | | 2 | 1 | | 2 | child2 |
Parent p1 = ParentRepo.findById(1).get() // Parent has Child 1 & 2
Parent p2 = ParentRepo.findById(2).get() // Parent has no Childs
So now I get Data from a Rest Interface to add p2 a probably new child with a name
#Autowired
ChildRpo childRepo;
#Autowired
ParentRepo parentRepo;
#PostMapping("/example/{parentName}/{childName}")
public void add(#PathVar("parentName") String pName,
#PathVar("childName") String cName) {
// Here is the Problem I think
Parent p = parentRepo.findByName(pName).get();
p.getChilds.add(new Child(cName,p);
this.parentRepo.save(p);
}
Case1: pName = pa2 & cName = child3 (works as expected)
| Parent | | parent_child_mapping | | Child |
| id | name | | child_id | parent_id | | id | name |
| 1 | pa1 | | 1 | 1 | | 1 | child1 |
| 2 | pa2 | | 2 | 1 | | 2 | child2 |
| 3 | 2 | | 3 | child3 |
Case2: pName = pa2 & cName = child1 (not quite what I expected) but how it is
| Parent | | parent_child_mapping | | Child |
| id | name | | child_id | parent_id | | id | name |
| 1 | pa1 | | 1 | 1 | | 1 | child1 |
| 2 | pa2 | | 2 | 1 | | 2 | child2 |
| 3 | 2 | | 3 | child1 |
Case3: pName = pa2 & cName = child1 what I expected but how it isn't is
| Parent | | parent_child_mapping | | Child |
| id | name | | child_id | parent_id | | id | name |
| 1 | pa1 | | 1 | 1 | | 1 | child1 |
| 2 | pa2 | | 2 | 1 | | 2 | child2 |
| 1 | 2 |
I have to issues with this:
I thought that the unique attribute make a column unique so two strings with the same Content can't co exists in this row 'name' but It works. I would expect In case2 an exception.
And the second one:
How can I configuration it that it works like I wants them to work?
Do I have to load childOne's id? and Attach them to the new child1 entity?
I thought because the filed is unique that spring can decide:
The field isn't in the column so I add a new child.
The field is in the column so I will not create a new child instead I attach the parent to the old child with identical name. - but this isn't the way it works by default - is there a way that it works like described?
P.S:
Here is the domain Model from the current code:
Book -- oneToMany --> Page -- oneToMany --> Version -- ManyToOne --> Author
Book -- oneToMany --> Locations
..
How can I add 'Quick' a new Version to page with id = 5 and author with Id = 4 without loading all relationships.
EDIT: The Database is created by hibernate and here are the properties:
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto= update
Unique attribute in a #column works only if the database was created automatically by JPA provider. Let the provider to recreate your db, if possible. You can do this from the properties.
spring.jpa.hibernate.ddl-auto=create-drop
You have to use a method in your jpa interface which finds (or not) a child using the name. Then check a result and create a new child, if it doesn't exist yet. This should work fine:
Parent p = parentRepo.findByName(pName).get();
Child newChild;
Optional<Child> result = childRpo.findByName(cName);
if(result != null) {
newChild = result.get();
}
else {
newChild = new Child(cName);
}
p.getChilds.add(newChild);
newChild.getParents().add(p);
parentRepo.save(p);
Exception:
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [model.pojo.CarPrice]
Have 2 tables
+------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+--------------+------+-----+---------+----------------+
| car_id | int(11) | NO | PRI | NULL | auto_increment |
| car_brand | varchar(255) | NO | | NULL | |
| car_model | varchar(255) | YES | | NULL | |
| car_plate_number | varchar(255) | NO | | NULL | |
| car_type | varchar(255) | YES | | NULL | |
+------------------+--------------+------+-----+---------+----------------+
and
+---------------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+-------------+------+-----+---------+----------------+
| car_rent_details_id | int(11) | NO | PRI | NULL | auto_increment |
| cost_per_day | double | YES | | NULL | |
| rent_start_date | varchar(20) | YES | | NULL | |
| rent_end_date | varchar(20) | YES | | NULL | |
| car_info_id | int(11) | NO | UNI | NULL | |
| avaliable_to_rent | tinyint(1) | YES | | NULL | |
+---------------------+-------------+------+-----+---------+----------------+
I trying to get whole data from Car and part of CarRentDetails
in my carRepository
#Repository
public interface CarRepository extends JpaRepository<Car, Integer> {
String ALL_AVAILIABLE_CAR_LIST_PRICE = " SELECT c.car_id, c.car_brand, c.car_model,
c.car_plate_number, c.car_type, crd.cost_per_day, crd.avaliable_to_rent " +
" FROM car_info c " +
" JOIN car_rent_details crd" +
" ON c.car_id = crd.car_rent_details_id" +
" AND crd.avaliable_to_rent = true ";
#Query(value = ALL_AVAILIABLE_CAR_LIST_PRICE, nativeQuery = true)
List<CarPrice> findAllByAvaliablePrice();
Trying to map this to my POJO class:
public class CarPrice {
private Integer carId;
private String carBrand;
private String carModel;
private String carPlateNumber;
private CarType carType;
private double costPerDay;
private boolean avaliableToRent;
+ getters, setters, to string
How can I map Objects from this Query ?
Create projection interface instead of POJO
public interface CarPrice {
Integer getCarId();
String getCarBrand();
String getCarModel();
String getCarPlateNumber();
CarType getCarType();
double getCostPerDay();
boolean isAvaliableToRent();
}
And format the query that returns the fields(map with alias) that can fits the getters of projection interface
String ALL_AVAILIABLE_CAR_LIST_PRICE = " SELECT c.car_id AS carId, c.car_brand AS carBrand, c.car_model AS carModel,
c.car_plate_number AS carPlateNumber, c.car_type AS carType, crd.cost_per_day AS costPerDay, crd.avaliable_to_rent AS avaliableToRent" +
" FROM car_info c " +
" JOIN car_rent_details crd" +
" ON c.car_id = crd.car_rent_details_id" +
" AND crd.avaliable_to_rent = true ";
#Query(value = ALL_AVAILIABLE_CAR_LIST_PRICE, nativeQuery = true)
List<CarPrice> findAllByAvaliablePrice();
Hope this helps!
I have two tables in a database:
Grade
+--------+--------------+-----------------+
| int id | varchar name | varchar teacher |
+--------+--------------+-----------------+
| 1 | CL-2A | E. Wright |
| 2 | CL-2B | B. Springsteen |
+--------+--------------+-----------------+
Student
+--------+------------------+-----------+
| int id | varchar name | int grade |
+--------+------------------+-----------+
| 1 | Michael Wallings | 1 |
| 2 | Rowen Tress | 1 |
| 3 | Alys Harverd | 2 |
| 4 | Jeff Bass | 1 |
| 5 | Harry Farewell | 2 |
+--------+------------------+-----------+
As you've probably seen already, Student.grade refers to Grade.id with a foreign key.
Now I've made some entity objects for both tables. But how do I implement to relation between Student and Grade?
public class Student {
private int id;
private String name;
private int grade; // Keep the grade as an int.
...
or
public class Student {
private int id;
private String name;
private Grade grade; // Hold a reference to the corresponding entity object Grade.
...
or something?
The second code snippet requires me to also get the grade entity object derived from the database.