JPA #SecondaryTable foreign key violation - java

I want to map two entities in a one to many fashion.
A->[B, B]
I want to add to the join table more fields. Pojos looks like:
#Entity
#Table(name = "A", schema = "examples")
#SecondaryTable(name = "A_B", pkJoinColumns = #PrimaryKeyJoinColumn(name = "a_id", referencedColumnName = "id"))
public class A
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Basic
private String name;
#Basic
private Integer field1;
#Column(table = "A_B", name = "field2")
private Integer field2;
#OneToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "A_B", joinColumns = {#JoinColumn(name = "a_id")}, inverseJoinColumns = {#JoinColumn(name = "b_id")})
private List<B> datastores;
}
#Entity
#Table(name = "B", schema = "examples")
#SecondaryTable(name = "A_B", pkJoinColumns = #PrimaryKeyJoinColumn(name = "b_id", referencedColumnName = "id"))
public class B
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Basic
private String field1;
#Basic
private int field2;
#Column(table = "A_B", name = "field3")
private int field3;
}
Thing is that in order to add I had to remove the foreign key on A_B table. How do I solve the mapping to allow the foreign keys ?
Thanks.

I am missing something, but I don't see why both Entity A and Entity B are mapping to table "A_B". By adding it to Entity A as a secondary table, you are stating that every time an insert to Table a occurs, an insert to table A_B must also occur - creating a strict 1:1 relation between rows in the two tables. Except that you do the same thing to entity B, so you will end up with rows in A_B with A_id=somevalue, and B_id= null and others with a_id=null while b_id=somevalue. Table "A_B" looks like a relation table, so this probably isn't what you want.
If A_B is a relationtable you should map it using a ManyToMany as you have for the "A_B" table. If there are extra fields that need to be populated, create a AB Entity, and create a OneToMany from A->AB and B->AB, and ManyToOne from AB->A and AB->B.

Related

correct way to create relationship in hibernate

i am having a mysql table relation with following criteria.
id - auto_increment, primary
a_id - FK to test_table primary key, index present
a_parent_id - FK to test_table primary key, index present
(a_id + a_parent_id) has unique constrain
table entries example
a_id a_parent_id
1 null
2 null
3 1
4 1
5 2
6 5
6 4
currently, i have mapped test_table in hibernate
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false)
private Long id;
#Column
private String comment;
what is the correct way to map relation table in hibernate?
As you specify that you can have multiple children/parents and from the looks of the example data, I chose to go with Lists of parents/children instead of just chaining the relationship.
#Entity
#Table(name = "test_table")
public class Test {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false)
private Long id;
#Column
private String comment;
#ManyToMany(targetEntity = Test.class)
#JoinTable(name = "relation", joinColumns = {
#JoinColumn(referencedColumnName = "a_id") }, inverseJoinColumns = {
#JoinColumn(referencedColumnName = "a_parent_id") })
private List<Test> parents;
#ManyToMany(targetEntity = Test.class)
#JoinTable(name = "relation", joinColumns = {
#JoinColumn(referencedColumnName = "a_parent_id") }, inverseJoinColumns = {
#JoinColumn(referencedColumnName = "a_id") })
private List<Test> children;
}
To find the parents, you look at the relation table with an SQL like
SELECT a_parent_id FROM relation WHERE a_id = ?
For the children you switch the columns like this:
SELECT a_id FROM relation WHERE a_parent_id = ?
This behavior should be represented by the #JoinTable annotations.

How to do One to One association in hibernate with foreign key and join table and unidirectional

I wish to have a one to one association with a join table in unidirectional way. -
Tables :
A (A_id, D_id, A_Data)
B (A_id, C_id) // Join table just contain relation between A and C
C (C_id, C_Data)
Class A {
.
.
#OneToOne(cascade = CascadeType.ALL)
#JoinTable(name = "B",
joinColumns = #JoinColumn(name = "A_id", referencedColumnName = "A_id"),
inverseJoinColumns = #JoinColumn(name = "C_id", referencedColumnName = "C_id"))
private C c;
}
I am using hibernate with jpa 2.0.
Entity D is not important in the model hence ignored.
I only wish to read data ,hence insert/update/delete use cases should not be concern, but one can suggest best practice in that case also.
This setup does not work. Can some one suggest how to do it in correct way?
It gives following exception
org.hibernate.MappingException: Unable to find column with logical name: A_id in org.hibernate.mapping.Table(A) and its related supertables and secondary tables
In order to get your desired schema:
// Given the following C entity
#Entity
public class C {
#Id
#Column(name = "C_ID")
private long id;
private String C_Data;
//...
}
// A Entity should be
#Entity
public class A {
#Id
#Column(name = "A_ID")
private long id;
private String A_Data;
#OneToOne(cascade = CascadeType.ALL )
#JoinTable(name = "B", joinColumns = #JoinColumn(name = "A_id"), inverseJoinColumns = #JoinColumn(name = "C_id", unique = true))
private C c;
//...
}
I've omitted referencedColumnName, so hibernate will map it to the entity primary key.
Note also that A_id column of B table will be the primary key.

Hibernate querying on ghost column

I have two entities that used to be linked by a one to many relation but now they are linked by a many to many relation declared as follow :
SalesTeam entity :
#Entity
#Table(name = "SALES_TEAMS")
public class SalesTeam {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.REFRESH, CascadeType.PERSIST})
#JoinTable(name = "WORKFLOW_FOR_SALESTEAM", inverseJoinColumns = {
#JoinColumn(name = "WFC_ID")
})
private List<WorkFlowCode> workFlowCodes = new ArrayList<>();
}
And the WorkFlowCode entity :
#Entity
#Table(name = "WORK_FLOW_CODE")
public class WorkFlowCode {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.REFRESH, CascadeType.PERSIST})
#JoinTable(name = "WORKFLOW_FOR_SALESTEAM", inverseJoinColumns = {
#JoinColumn(name = "ST_ID")
})
private List<SalesTeam> salesteam = new ArrayList<>();
}
As I said the relation use to be one SalesTeam for several workflow codes but the requirement change and now it need to be a many to many relation. So I had a relation table and remove the former SALES_TEAM_ID column from the WORK_FLOW_CODE table. The problem is that now I always get an error when I try to get the WorkFlowCode from a SalesTeam. It appears that hibernate still adds the removed column to the query thus the relation had changed and nothing is left from the former relation description.
Here is the hibernate generated query :
select workflowco0_.SALES_TEAMS_ID as SALES_TE3_13_0_, workflowco0_.WFC_ID as WFC_ID4_16_0_, workflowco1_.ID as ID1_17_1_ from WORKFLOW_FOR_SALESTEAM workflowco0_ inner join WORK_FLOW_CODE workflowco1_ on workflowco0_.WFC_ID=workflowco1_.ID where workflowco0_.SALES_TEAMS_ID=?
As you can see the former SALES_TEAM_ID from WORK_FLOW_CODE table is still there.
How can I remove it ?
Thx

Joining tables in hibernate

I have two tables
Customer Rate
------------- -----------
res_number product
strategy name
fname type
lname rate
..... ......
And I created two beans
1. 2.
#Entity #Entity
#Table(name="customer") #Table(name="rates")
EmployeeDetails{ CardDetails{
#Col(name="res_number") #col(name="product")
String resNum; String product;
..... ....
} }
Now the query I have is
hql = "from CardDetails cd, EmployeeDetails ed where ed.strategy = cd.product".
But it gives me reference problems saying hibernate.QueryException: could not resolve property:
I have tried adding
#OneToOne(mappedBy = "strategy")
#Cascade(value = CascadeType.ALL)
private EmployeeDetails empDetails;
in CardDetails but it gives me error saying no OneToOne is possible ani... tried changing to ManyToOne and OneToMany but doesnot work. Can anyone please tell me how to map the beans for join using annotations? Note: The database is not designed correctly and there is no common field in both tables(like a foreign key). So any help is greatly appreciated.
EDIT:
Adding the beans:
#Entity
#Table(name="rates")
public class CardDetails {
#Id
#Column(name="CARD_NAME")
String cardName;
#Column(name="CARD_TYPE")
String cardType;
#Column(name="FAQ_PAGE")
String faqPage;
#Column(name="GEN_INTRO_DISCL")
String genIntroDiscl;
#Column(name="GEN_REGULAR_RATE")
String genRegularRate;
#Column(name="BT_FEE")
String btFee;
#Column(name="BONUS")
String bonus;
#Column(name="ART_WORK")
String artWork;
#Column(name="DISCLOSURE_LINK")
String disclosureLink;
#Column(name="TERMS_LINK")
String termsLink;
#Column(name="PRODUCT")
String product;
#Entity
#Table(name="CUSTOMER")
#SecondaryTable(name="APPLICANT")
public class EmployeeDetails {
#Id
#Column(name="RESERVATION_NUMBER")
String reservNumber;
#Column(name="SSN")
int ssnNumber;
#Column(name="BANK_NUMBER")
String bankNumber;
#Column(name="BRANCH_NUMBER")
String branchNumber;
#Column(name="EMPLOYEE_ID")
String empId;
#Column(name="STRATEGY")
String strategy;
From the looks of your HQL, you'd like to join the two tables using stratety in the Customer table and product in the Rate table. This would imply that strategy is a foreign key.
If this is indeed a one-to-one relationship, then inside of CardDetails, try this:
#OneToOne
#JoinColumn(name = "product", referencedColumnName = "strategy")
private EmployeeDetails employeeDetails;
This assumes you don't already have product mapped as a property in CardDetails, if you do, you'll need to do it this way, otherwise Hibernate will complain about duplicate field mappings.
#Column(name = "product", columnDefinition = "char")
private String product;
#OneToOne
#JoinColumn(name = "product", referencedColumnName = "strategy", insertable = false, updatable = false)
private EmployeeDetails employeeDetails;
If it needs to be a one-to-many relationship, then do it this way:
#Column(name = "product", columnDefinition = "char")
private String product;
#OneToMany
#JoinColumn(name = "product", referencedColumnName = "strategy", insertable = false, updatable = false)
private List<EmployeeDetails> employeeDetails;

How do I join tables on non-primary key columns?

I have an issue with join tables on an object in an ORM class hierarchy where the join column is NOT the primary key of the base class due a lagacy database structure.
Here is an example of the table design:
CREATE TABLE "SCH"."FOO"
(
"OWNERID" NUMBER(10,0) NOT NULL ENABLE,
"FOOID" NUMBER(10,0) NOT NULL ENABLE,
CONSTRAINT "FOO_PK" PRIMARY KEY ("OWNERID", "FOOID")
CONSTRAINT "FOO_FK1" FOREIGN KEY ("OWNERID") REFERENCES "SCH"."OWNERS" ("OWNERID") ENABLE
)
CREATE TABLE "SCH"."BAR"
(
"BARID" NUMBER(10,0) NOT NULL ENABLE,
"FOOID" NUMBER(10,0)
CONSTRAINT "BAR_PK" PRIMARY KEY ("BARID")
)
And here are the mappings (unesessary infomation removed)
#Entity
#IdClass(FooId.class)
#Table(name = "FOO")
public class Foo implements java.io.Serializable
{
#Id
#Column(name = "OWNERID")
private BigInteger ownerId;
#Id
#SequenceGenerator(name = "FOO_GENERATOR", sequenceName = "SEQ_FOO")
#GeneratedValue(generator = "FOO_GENERATOR")
#Column(name = "FOOID")
private BigInteger id;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "FOOID", referencedColumnName = "FOOID")
#Fetch(value = FetchMode.SUBSELECT)
#Cascade(value = {CascadeType.ALL})
private Set<Bar> bar = new LinkedHashSet<Bar>(0);
}
#Entity
#Table(name = "BAR")
public class Bar implements java.io.Serializable
{
#Id
#Column(name = "BARID")
private BigInteger id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FOOID", referencedColumnName = "FOOID")
private Foo foo;
}
This fails with an exception:
Caused by: org.hibernate.AnnotationException: referencedColumnNames(FOOID) of com.package.Bar.foo referencing com.package.Foo not mapped to a single property
at org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:204)
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:114)
at org.hibernate.cfg.Configuration.processEndOfQueue(Configuration.java:1580)
at org.hibernate.cfg.Configuration.processFkSecondPassInOrder(Configuration.java:1503)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1419)
at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1375)
Could you please help with a solution?
You must not map the bidirectional association twice. The One side must be marked as the inverse of the Many side, using the mappedBy attribute:
#OneToMany(fetch = FetchType.LAZY, mappedBy = "foo")
#Fetch(value = FetchMode.SUBSELECT)
#Cascade(value = {CascadeType.ALL})
private Set<Bar> bar = new LinkedHashSet<Bar>(0);
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FOOID", referencedColumnName = "FOOID")
private Foo foo;
There is no reason to tell Hibernate twice that the association is mapped by the join column FOOID. And doing it is actually an error, because it defines two different unidirectional associations rather than one bidirectional association.
EDIT
The above should work, but doesn't due to the following Hibernate bug: It's a Hibernate bug. See HHH-4284.
To circumvent this problem, since the FOOID is enough to ensure uniqueness, a solution would be to remove the #Id annotation from the owner ID and the #IdClass annotation.
U can do as follows.... it should work -
#Entity
#IdClass(FooId.class)
#Table(name = "FOO")
public class Foo implements java.io.Serializable
{
#Id
#Column(name = "OWNERID")
private BigInteger ownerId;
#Id
#SequenceGenerator(name = "FOO_GENERATOR", sequenceName = "SEQ_FOO")
#GeneratedValue(generator = "FOO_GENERATOR")
#Column(name = "FOOID")
private BigInteger id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "FOOID",nullable=false)
#ForeignKey(name = "fk")
private Set<Bar> bar = new LinkedHashSet<Bar>(0);
}
#Entity
#Table(name = "BAR")
public class Bar implements java.io.Serializable
{
#Id
#Column(name = "BARID")
private BigInteger id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "FOOID", updatable = false, insertable = false, nullable=false)
private Foo foo;
}
You used composite primary key in FOO table. So you should try #EmbeddedId property and you should need two columns "OWNER_ID" and "FOO_ID" in BAR entity that join with FOO entity.

Categories

Resources