Problem with ManyToMany relations in spring data. It changed my table - java

I had a db with tables SPEC and PARTS.Also I had a table for MANY TO MANY relations. In my project I used spring jdbs template and all works good. Then I decide to change jdbc on SPring data jpa.
My Entities:
#Entity
#Table(name = "PARTS")
public class PartsJpa {
#Id
private int id;
#Column(name = "NAME")
private String name;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "ID_EXPORT", unique = false, nullable = false, updatable = true)
private ExportJpa exportJpa;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "ID_TYPE", unique = false, nullable = false, updatable = true)
private TypesJpa typesJpa;
#Column(name = "DESCRIPTION")
private String description;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name="SPEC_PARTS",
joinColumns = #JoinColumn(name="ID_SPEC", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name="ID_PARTS", referencedColumnName="id")
)
private Set<SpecJpa> specJpa;
////////
}
And Spec:
#Entity
#Table(name = "SPEC")
public class SpecJpa {
#Id
private int id;
#Column(name = "NAME")
private String name;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "Creator_ID", unique = false, nullable = false, updatable = true)
private UsersJpa usersJpa;
#Column(name = "DESCRIPTION")
private String description;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name="SPEC_PARTS",
joinColumns = #JoinColumn(name="ID_SPEC", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name="ID_PARTS", referencedColumnName="id")
)
private Set<PartsJpa> partsJpa;
////////////////
}
I don't show getters and setters.
It works, but when I start a programm, something in my table was changed and now I can't add in table spec_parts values like(1,3)(1,2).
Mistake:
FK_123: PUBLIC.SPEC_PARTS FOREIGN KEY(ID_PARTS) REFERENCES PUBLIC.SPEC(ID) (3)" Referential integrity constraint violation: "FK_123: PUBLIC.SPEC_PARTS FOREIGN KEY(ID_PARTS) REFERENCES PUBLIC.SPEC(ID) (3)"; SQL statement: INSERT INTO "PUBLIC"."SPEC_PARTS"("ID_SPEC","ID_PARTS")VALUES(?,?)
Maybe I have mistake with creating relations between spec and parts? What problem it can be?
data in spec
ID NAME CREATOR_ID DESCRIPTION CHANGER_ID
1 pc 1 description 1
2 pc2 2 description2 2
data in parts
ID ▼ NAME ID_EXPORT ID_TYPE DESCRIPTION
1 intel core i5 1 1 d1
2 intel core i7 1 1 d2
3 ddr3 2 2 d3
4 ddr4 2 2 d4
5 asus 3 3 d5
data in spec_parts now:
ID_SPEC ID_PARTS
1 1
2 2
so I can't add 1,3 or 2,4
I find a problem, spring date change something and now in table SPEC_PARTS ID_SPEC mapping on PARTS.ID. Why?

As you are using ManyToMany relation, there is a mapping table created named SPEC_PARTS which have referenced columns ID_SPEC and ID_PARTS.These columns value come from SPEC.ID and PARTS.ID. So you can't insert in SPEC_PARTS without creating referenced value because you are trying to do foreign key constraint violation.
Suppose if there is a row in SPEC with id value 1 and there is a row in PARTS with id value 2. Then you can insert in SPEC_PARTS with value like (1,2).
So, first, add data in SPEC and PARTS then map them in SPEC_PARTS.
And you can remove #JoinTable from one side, you don't need to define it both side.
Update:
Problem is SpecJpa class relation. Here you are using SPEC_PARTS.ID_SPEC as foriegn key for PARTS.ID and SPEC_PARTS.ID_PARTS as foriegn key for SPEC.ID which is fully reversed what you do in PartsJpa class.
#JoinTable(name="SPEC_PARTS",
joinColumns = #JoinColumn(name="ID_SPEC", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name="ID_PARTS", referencedColumnName="id")
)
That's why this error say
SPEC_PARTS FOREIGN KEY(ID_PARTS) REFERENCES PUBLIC.SPEC(ID) (3)";
There is no SPEC.ID value 3 exist in the database.
Solution:
Remove #JoinTable from SpecJpa class as you don't need to specify both side.
And remove the wrong relation of the foreign key from database also.

Related

Many to many relationship with itself and combine results

I've got the next database relation
This table defines the id of an entity
ID
1
2
3
4
And this the relation into this table and it self
ID_A ID_B
1 2
2 1
3 1
I've got this mapping in my entity and it works properly
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "AGRUPACION_MOTIVOS_INDEBIDO", joinColumns = #JoinColumn(name = "ID_MOTIVO_INDEBIDO_A", referencedColumnName = "ID"), inverseJoinColumns = #JoinColumn(name = "ID_MOTIVO_INDEBIDO_B", referencedColumnName = "ID"))
private List<MotivoIndebido> motivosA = new ArrayList<>();
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "AGRUPACION_MOTIVOS_INDEBIDO", joinColumns = #JoinColumn(name = "ID_MOTIVO_INDEBIDO_B", referencedColumnName = "ID"), inverseJoinColumns = #JoinColumn(name = "ID_MOTIVO_INDEBIDO_A", referencedColumnName = "ID"))
private List<MotivoIndebido> motivosB = new ArrayList<>();
But i need to combine this two list in one with all results with no duplicate cases. I mean:
The result list should contain something like:
ID_A IB_B
1 2
1 3
Can i accomplish this with hibernate or jpa or i need to combine this list manually in a java method?.
And then i should save and delete data manually too?
Sorry for my english and thanks in advice.

#JoinColumn gives error "column with logical name ID not found" in Hibernate 4

I am having below annotation on column in my entity class.
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "INQUIRYID", referencedColumnName="ID", updatable=false, insertable=false)
private MyTable myInquiry;
It was giving below error on runtime.
column with logical name ID not found in entity class
As the referenced column is a primary key, I removed the referencedColumnName attribute and updated to below
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "INQUIRYID", updatable=false, insertable=false)
private MyTable myInquiry;
This works perfectly with Hibernate 5.3 but as soon as I go to hibernate 4 , I see some anomalies.
In hibernate 5 I get this issue only with columns which are referring some ID(PK) of another class. However, in hibernate 4 I see this error for non-pk columns as well.
I start to get the same error for columns which are referring non primary keys.
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "AB", referencedColumnName = "AB")
private MyTable someData;
Above code gives me error in hibernate 4
Column with logical name AB not found in entity class.
Here, AB is a non primary key column .So I can't remove referencedColumnName attribute.
Is it purely because of hibernate version or there is a different reason for such behavior ?
Referred this : Similar Issue
There is a bug for hibernate 4.1.7
A workaround for this issue is surrounding the column name with gave accents.
Unable to find column with logical name: id in
org.hibernate.mapping.Table(template) and its related supertables and
secondary tables
#ManyToOne #JoinColumnsOrFormulas({ #JoinColumnOrFormula(column =
#JoinColumn(name = "template", referencedColumnName = "id")),
#JoinColumnOrFormula(formula = #JoinFormula(value = "'custom'",
referencedColumnName = "type")) }) This is caused by identifying the
column names within the logicalToPhysical map of
TableColumnNameBinding. Within this map the column names are
surrounded by grave accents (`id`) while the check do a lookup to the
map with plain column name (id).
A workaround for this issue is surrounding the column name with gave
accents.
#ManyToOne #JoinColumnsOrFormulas({ #JoinColumnOrFormula(column =
#JoinColumn(name = "template", referencedColumnName = "`id`")),
#JoinColumnOrFormula(formula = #JoinFormula(value = "'custom'",
referencedColumnName = "`type`")) })

Join 3 tables using javax.persistence in a OneToMany relationship

The 3 tables are "analyticalgroups", "labinstructions", "observedproperties". Each table has an "id" primary key column.
I'd like to use a 4th table ("analyticalgroups_observedproperties_labinstructions") to store the OneToMany relationship. Ultimately I'd like the output to be structured something like this:
analyticalGroup: {
id: "...",
observedPropertyLabInstructions: [
{observedProperty, labInstruction},
{observedProperty, labInstruction},
{observedProperty, labInstruction},
...etc...
]
}
I've followed some examples online, but can't get this to work. The problem is when I try this I get the following error:
"message" : "Error occurred at repository: PSQLException: ERROR: column observedpr0_.observedpropertyentitylabinstructionentitymap_id does not exist\n Position: 6550",
"errorCode" : "gaia.domain.exceptions.RepositoryException",
Here's the structure for the join table.
CREATE TABLE analyticalgroups_observedproperties_labinstructions
(
analyticalgroupid character varying(36) NOT NULL,
labinstructionid character varying(36) NOT NULL,
observedpropertyid character varying(36) NOT NULL,
CONSTRAINT fk_analyticalgroups_observedproperties_labinstructions_groupid FOREIGN KEY (analyticalgroupid)
REFERENCES analyticalgroups (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT fk_analyticalgroups_observedproperties_labinstructions_labinstr FOREIGN KEY (labinstructionid)
REFERENCES labinstructions (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT fk_analyticalgroups_observedproperties_labinstructions_observed FOREIGN KEY (observedpropertyid)
REFERENCES observedproperties (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE
)
#Entity
#Data
public class AnalyticalGroupEntity {
public static final String ENTITY_NAME = "analyticalGroups";
public static final String JOIN_OBSERVEDPROPERTIES_LABINSTRUCTIONS_TABLE_NAME =
ENTITY_NAME +
IDomainEntity.UNDERSCORE +
ObservedPropertyEntity.ENTITY_NAME +
IDomainEntity.UNDERSCORE +
LabInstructionEntity.ENTITY_NAME;
#Id
#Column(name = IDomainEntity.ID_KEY, nullable = false, columnDefinition = IDomainEntity.COLUMN_TYPE_UUID)
private String id;
#OneToMany
#JoinTable(
name = JOIN_OBSERVEDPROPERTIES_LABINSTRUCTIONS_TABLE_NAME,
joinColumns = #JoinColumn(name = LabInstructionEntity.ID_KEY, referencedColumnName = IDomainEntity.ID_KEY, table = "labinstructions")
)
#MapKeyJoinColumn(name = ObservedPropertyEntity.ID_KEY, referencedColumnName = IDomainEntity.ID_KEY, table = "observedproperties")
private Map<ObservedPropertyEntity, LabInstructionEntity> observedPropertyLabInstructions;
}
Hopefully I've laid this all out as clearly as necessary.
Your help is much appreciated. Thanks for reading!
edit Actually... it turns out this doesn't work. It successfully gets the data I want, buuuuut it also deletes every row in the join table whenever I make a GET request *flip table*
So bizarre!
#OneToMany
#JoinTable(
name = JOIN_OBSERVEDPROPERTIES_LABINSTRUCTIONS_TABLE_NAME,
joinColumns = #JoinColumn(name = "analyticalgroupid", referencedColumnName = IDomainEntity.ID_KEY, table = "labinstructions"),
inverseJoinColumns = #JoinColumn(name = LabInstructionEntity.ID_KEY, referencedColumnName = IDomainEntity.ID_KEY, table = "labinstructions")
)
#MapKeyJoinColumn(name = ObservedPropertyEntity.ID_KEY, referencedColumnName = IDomainEntity.ID_KEY, table = "observedproperties")
private Map<ObservedPropertyEntity, LabInstructionEntity> observedPropertyEntityLabInstructionEntityMap;

HQL for getting min

I have following entities:
Company.class:
public class Company {
#JoinTable(name = "company_employee", joinColumns = #JoinColumn(name = "company_id") , inverseJoinColumns = #JoinColumn(name = "employee_id") )
#ManyToMany(fetch = FetchType.LAZY)
private Set<Employee> employees;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "company")
private List<Score> scores;
#JoinTable(name = "company_factor", joinColumns = #JoinColumn(name = "company_id") , inverseJoinColumns = #JoinColumn(name = "factor_id") )
#ManyToOne(fetch = FetchType.LAZY)
private Factor factors;
}
and Employee.class
public class Employee {
#ManyToMany(fetch = FetchType.EAGER, mappedBy="employees")
private Set<Company> companies;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "company")
private List<Score> scores;
#JoinTable(name = "employee_factor", joinColumns = #JoinColumn(name = "employee_id") , inverseJoinColumns = #JoinColumn(name = "factor_id") )
#ManyToMany(fetch = FetchType.LAZY)
private Set<Factor> factors;
#Transient
private int score;
}
Factor.class doesn't contain any relationships.
Also, I have some entity Score that is unique for each combination Company-Employee.
It looks like that:
Score.class:
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "company_id", insertable = false, updatable = false)
private Company company;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "employee_id", insertable = false, updatable = false)
private Employee employee;
#Column(name = "score")
private BigDecimal score;
If I get List, it will be a list of combinations Company and Employee instances, and sometimes Company or Employee can be repeated.
The goal is to get List, filtered by Factor in Employee and showing only minimal score for each employee ordered in ascending order.
Say, if exist combinations
employee1-company1, score=1
employee1-company2, score=2
employee2-company1, score=3
employee2-company4, score=5
employee3-company4, score6
ResultList should look like that:
employee1-company1, score=1
employee2-company1, score=3
employee3-company4, score=6
So employee should not repeat, but company could repeat in the List.
I am not quite sure, how to do that. What I achieved is showing unique results in ascending order, but they don't show min score. I used HQL:
select distinct e from Score e
left outer join fetch e.company
left outer join fetch e.company.factors
left outer join fetch e.employee
left outer join fetch e.employee.factors ef
where ef.factor_id=:factor_id
group by e.employee.employee_id
order by e.score asc
Could anybody help how to achieve what I need? Thank you.
UPDATE1:
I decided to go another way.
Now I am getting it via Employee using this query:
select distinct e from Employee e join e.scores es order by es.score asc
It seems it's exactly what I need, but how to put in the query the minimum es.score to the field score of Employee object? Maybe there is some way to substitute e.score by es.score?
You can use the following:
Select a from (Select b from Score b order by score) as a group by a.employee
Explanation:
Select b from Score b order by score : gets the results in asc (default) order of score
Group by on above result will give the unique employees
As a solution I switched to entityManager.createNativeQuery("some native sql string").
I am satisfied with the result. Just for the case, question about SQL query is here
The only drawback is that it's impossible to use join fetch, hence N+1 select problem is here, but I plan to fetch quite small chunk of data, so it's bearable.

#ManyToOne, Can one column reference to two other Columns?

Can I have a relationship as below:
#Entity Table1{
#ManyToOne
#JoinColumn(name = "Column1",
referencedColumnName = "t2id",
insertable = false,
updatable = false)
private Table2 table2_col;
#ManyToOne
#JoinColumn(name = "Column1",
referencedColumnName = "t3id",
insertable = false,
updatable = false)
private Table3 table3_col;
}
Yes, the mapping looks valid.
The column Column1 in both cases belong to different tables (Table2.column1 and Table3.column1). So I don't see any collision here. It is not the case as the tittle says "one column references to two other columns".
In this case you have two many-to-one relations: Table1<--->Table2 and Table1<--->Table3. So the column1 in both tables (2 and 3) is a Foreign Key to the Table1. So you have 2 different foreign keys.

Categories