I have two entities with a OneToOne relation:
#Entity
#Table(name = "APPLICATION_DEVICE")
public class ApplicationDevice implements Serializable {
[...]
#Id
public ApplicationDeviceKey getApplicationDeviceKey()
{
return applicationDeviceKey;
}
#NotFound(action = NotFoundAction.IGNORE)
#OneToOne(cascade=CascadeType.ALL)
#JoinColumns( {
#JoinColumn(name = "applicationId", referencedColumnName = "applicationId",insertable=false,updatable=false, nullable = true),
#JoinColumn(name = "deviceId", referencedColumnName = "deviceId",insertable=false,updatable=false, nullable = true), }
)
public ApplicationDevicePushInfo getDevicePushInfo() {
return devicePushInfo;
}
and the other entity:
#Entity
#Table(name = "APPLICATION_DEVICE_PUSHINFO")
public class ApplicationDevicePushInfo implements Serializable {
[...]
#Id
public ApplicationDeviceKey getApplicationDeviceKey()
{
return applicationDeviceKey;
}
#OneToOne
#JoinColumns( {
#JoinColumn(name = "applicationId", referencedColumnName = "applicationId",insertable=false,updatable=false, nullable = false),
#JoinColumn(name = "deviceId", referencedColumnName = "deviceId",insertable=false,updatable=false, nullable = false)}
)
public ApplicationDevice getApplicationDevice() {
return applicationDevice;
}
The second entity could be null, and when I try to store my first entity I get an:
Cannot add or update a child row: a foreign key constraint fails (`malcom_dev`.`application_device`, CONSTRAINT `FK16A0C0451DC7C799` FOREIGN KEY (`applicationId`, `deviceId`) REFERENCES `APPLICATION_DEVICE_PUSHINFO` (`applicationId`, `deviceId`))
By default nullable is to true, so I thought it would be possible this relation.
There are another way to have to entities with a oneToOne relation without create new columns or tables, and allow nullable the second entity ?
This is not Hibernate but database exception. Looks like there is foreign key constraint on the table.
It will work after you remove foreign key constraint.
Related
the problems is when #ManyToOne make a #Joincolumn ID_REPORT (it´s a primary key ) and #Joincolumn ID_TEMPLATE_DEFAULT
Repeated column in mapping for entity: CurReport column: id_report (should be mapped with insert="false" update="false")
Code
First table CUR_TEMPLATE
CREATE TABLE CUR_TEMPLATE
(
ID_REPORT NUMBER(5,0) NOT NULL,
ID_TEMPLATE NUMBER(5,0) NOT NULL,
-- Other fields
);
ALTER TABLE CUR_TEMPLATE ADD CONSTRAINT PK_CUR_TEMPLATE PRIMARY KEY (ID_REPORT, ID_TEMPLATE)
-- CUR_TEMPLATE foreign keys
ALTER TABLE CUR_TEMPLATE ADD CONSTRAINT FK_CUR_PLAN_REFERENCE_CUR_REPO FOREIGN KEY (ID_REPORT)
REFERENCES CUR_REPORTS (ID_REPORT);
Second table CUR_REPORTS
-- CUR_REPORTS definition
CREATE TABLE CUR_REPORTS
(
ID_REPORT NUMBER(3,0) NOT NULL,
NAME_REPORT VARCHAR2(100) NOT NULL,
-- other fields
ID_TEMPLATE_DEFAULT NUMBER(5,0),
-- other fields
) ;
ALTER TABLE CUR_REPORTS ADD CONSTRAINT PK_CUR_REPORTS PRIMARY KEY (ID_REPORT)
ALTER TABLE CUR_REPORTS CONSTRAINT FK_CUR_REPO_REFERENCE_CUR_PLAN FOREIGN KEY (ID_REPORT, ID_TEMPLATE_DEFAULT)
REFERENCES CUR_TEMPLATE (ID_REPORT, ID_TEMPLATE)
First table CUR_REPORTS Entity CurReport
#Entity
#Table(name = "CUR_REPORTS")
#IdClass(CurPlantillaPK.class)
#Getter
#Setter
public class CurReport {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID_REPORT", nullable = false)
private Long id;
#Column(name = "NAME_REPORT", nullable = false, length = 100)
private String nombreReporte;
#ManyToOne(fetch = FetchType.LAZY) <---WHERE IS THE PROBLEM
#JoinColumn(name = "ID_REPORT", referencedColumnName = "ID_REPORTE")
#JoinColumn(name = "ID_TEMPLATE_DEFAULT", referencedColumnName = "ID_TEMPLATE")
private CurTemplate curTemplate;
#OneToMany(mappedBy = "curReport")
private Set<CurTemplate> curTemplates= new LinkedHashSet<>();
}
Second table CUR_TEMPLATE Entity CurReport
#Entity
#Table(name = "CUR_TEMPLATE")
#IdClass(CurPlantillaPK.class)
#Getter
#Setter
public class CurTemplate {
#Id
#Column(name = "ID_REPORT", nullable = false)
private Long idReport;
#Id
#Column(name = "ID_TEMPLATE", nullable = false)
private Long idTemplate;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "ID_REPORT", foreignKey = #ForeignKey(name = "FK_CUR_PLAN_REFERENCE_CUR_REPO"), referencedColumnName = "ID_REPORT", insertable = false, updatable = false)
private CurReport curReport;
}
When i add insertable=false, updatable=false
#JoinColumn(name = "ID_REPORT", referencedColumnName = "ID_REPORT", insertable=false, updatable=false)
said
Mixing insertable and non insertable columns in a property is not allowed: CurTemplate
How could i map those relationships?
How resolve the #JoinColumn when one field of the FK are column PK?
You can use a derived identity and map CurTemplate like this:
#Entity
#Table(name = "CUR_TEMPLATE")
#IdClass(CurTemplatePK.class)
#Getter
#Setter
public class CurTemplate {
#Id
#Column(name = "ID_TEMPLATE", nullable = false)
private Long idTemplate;
#Id
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "ID_REPORT", foreignKey = #ForeignKey(name = "FK_CUR_PLAN_REFERENCE_CUR_REPO"), referencedColumnName = "ID_REPORT", insertable = false, updatable = false)
private CurReport curReport;
}
Then you will need an #IdClass like this:
public class CurTemplatePK {
Long idTemplate; // matches name of #Id attribute
Long curReport; // matches name of #Id attribute and type of CurReport PK
}
Then you should use a basic mapping for the default template key and provide a getter for the default template object:
#Entity
#Table(name = "CUR_REPORTS")
#IdClass(CurPlantillaPK.class)
#Getter
#Setter
public class CurReport {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID_REPORT", nullable = false)
private Long id;
#Column(name = "NAME_REPORT", nullable = false, length = 100)
private String nombreReporte;
#Column(name = "ID_TEMPLATE_DEFAULT")
private Long idDefaultTemplate;
#OneToMany(mappedBy = "curReport")
private Set<CurTemplate> curTemplates= new LinkedHashSet<>();
public CurTemplate getDefaultTemplate() {
return this.curTemplates.stream()
.filter(template -> template.getIdTemplate().equals(idDefaultTemplate))
.findFirst()
.orElse(null);
{
}
If you want to allow clients to set the default template, you will need to implement a setter that first verifies that the new default template is already in the set curTemplates.
I am trying to create a OneToMany mapping with a LinkedHashMap for my main entity B, that contains the entities VC and P, but I am getting the following error:
Repeated column in mapping for collection:
com.test.model.B.pricing column: b_name
I could be wrong, but I believe that it has something to do with the #JoinColumns or #MapKeyJoinColumn annotation, as I have not done anything like this before, so I am quite sure that I am doing this part incorrectly.
My goal is that I should be able to provide the three fields:
b_name pc and c_id e.g. the VC/VCId
in order to get the a and d_a e.g. P.
Also, if there is a better way to structure things, then I am all ears, as I personally do not really like how I have set up my tables tbh (would be nice if I could just have the b and b_p tables, where the b_p could just have all five fields (key and value) from the p map).
Here is my main entity
#Setter
#Getter
#Entity
#Table(name = "b")
public class B implements Serializable {
#Id
#Column(nullable = false)
private String name;
#OneToMany(cascade = CascadeType.PERSIST)
#JoinTable(
name = "b_p",
joinColumns = #JoinColumn(name = "b_name", referencedColumnName = "name"))
#MapKeyJoinColumns({
#MapKeyJoinColumn(name = "b_name"),
#MapKeyJoinColumn(name = "p_c"),
#MapKeyJoinColumn(name = "c_id")
})
private Map<VC, Price> pricing = new LinkedHashMap<>();
...
}
The Key to the map
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "v_c")
public class VC implements Serializable {
#EmbeddedId private VCId vcId;
}
The key's PK/Composite Key
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Embeddable
public class VCId implements Serializable {
#Column(name = "b_name")
private String bName;
#Column(name = "p_c")
private SomeEnum pc;
#Column(name = "c_id")
private String cId;
}
The value for the map
#Setter
#Getter
#NoArgsConstructor
#Embeddable
#Entity
#Table(name = "price")
public class Price implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "amount")
private BigDecimal amount;
#Column(name = "discount_amount")
private BigDecimal discountAmount;
}
DB tables
CREATE TABLE b
(
name VARCHAR(100) NOT NULL PRIMARY KEY
...
);
CREATE TABLE v_c
(
bundle_name VARCHAR(100) NOT NULL,
physical_currency TEXT NOT NULL,
coin_id VARCHAR(50) NOT NULL,
FOREIGN KEY (b_name) REFERENCES b (name) ON DELETE CASCADE,
PRIMARY KEY (b_name, p_c, c_id)
);
CREATE TABLE p
(
id BIGSERIAL NOT NULL PRIMARY KEY,
amount NUMERIC,
discount_amount NUMERIC DEFAULT 0.00
);
CREATE TABLE b_p
(
bname VARCHAR(100) NOT NULL,
p_c TEXT NOT NULL,
c_id VARCHAR(50) NOT NULL,
price_id BIGSERIAL NOT NULL,
FOREIGN KEY (b_name, p_c, c_id) REFERENCES v_c (b_name, p_c, c_id) ON DELETE CASCADE,
FOREIGN KEY (price_id) REFERENCES price (id) ON DELETE CASCADE,
PRIMARY KEY (b_name, p_c, c_id)
);
To prevent repeated mapping error, you just have to specify which join should update the column like such:
#OneToMany(cascade = CascadeType.PERSIST)
#JoinTable(name = "bundle_pricing",
joinColumns = #JoinColumn(name = "bundle_name",
referencedColumnName = "name"))
#MapKeyJoinColumns({
#MapKeyJoinColumn(name = "bundle_name", insertable = false, updatable = false),
#MapKeyJoinColumn(name = "physical_currency"), #MapKeyJoinColumn(name = "coin_id")})
private Map<VirtualCurrency, Price> pricing = new LinkedHashMap<>();
NOTE the insertable = false, updatable = false for bundle_name in the MapKeyJoinColumn
remove the #MapKeyJoinColumn(name = "bundle_name") from MapKeyJoinColumns, because when we are creating the JoinColumn in JoinTable it will create the column we don't need to mention it again.
#JoinTable(
name = "bundle_pricing",
joinColumns = #JoinColumn(name = "bundle_name", referencedColumnName = "name"))
#MapKeyJoinColumns({
#MapKeyJoinColumn(name = "physical_currency"),
#MapKeyJoinColumn(name = "coin_id")
})
I have an existing database schema and I try to make one to many relationship in JPA when PK is a composite of multiple fields and just one of them is FK in the other entity:
DemandId: PK class that consist of two fields
#Embeddable
public class DemandId implements Serializable {
#Column(name = "\"ORDER\"", nullable = false)
private String order;
#Column(name = "SNRP", nullable = false)
private String number;
}
DemandEntity: The entity itself
#Entity
#Table(name = "DEMAND")
public class DemandEntity implements Serializable {
#EmbeddedId
private DemandId id;
#OneToMany(fetch = EAGER, cascade = ALL, mappedBy = "demand")
private Set<PartEntity> parts = new HashSet<>();
}
PartEntity:
#Entity
#Table(name = "PART")
public class PartEntity implements Serializable {
#Column(name = "SNRP")
private String number;
#ManyToOne
#JoinColumn(name = "SNRP", referencedColumnName = "SNRP", insertable = false, updatable = false)
private DemandEntity demand;
}
This approach leads to an exception:
Exception Description: The #JoinColumns on the annotated element
[field demand] from the entity class [class PartEntity] is incomplete.
When the source entity class uses a composite primary key, a
#JoinColumn must be specified for each join column using the
#JoinColumns. Both the name and the referencedColumnName elements must
be specified in each such #JoinColumn.
Unfortunatelly I cannot add another join column
#JoinColumn(name = "\"ORDER\"", referencedColumnName = "\"ORDER\"", insertable = false, updatable = false)
Because the PART table doesn't contain the ORDER field and the structure of the database cannot be changed.
Is there a way to perform such mapping?
Regards
If you have composite primary keys and you want to have one to many
mapping, I would suggest rather than keeping those keys as composite
primary keys, make them composite unique keys.
And make a auto-generated sequence as a primary key.
It is better and more convenient. By the way its my personnel opinion.
I even don't know, if that is possible or not which you are trying to do.
I have an entity with a composite primary key consisting of two fields, one of which is also part of a composite foreign key.
Background: I have entities Person,Area, and Session.
Person has many-to-many relationships with Area and Session, using join entities called PersonArea and PersonSession.
So, I have PersonSession, with primary key of (personId, sessionId).
PersonId and SessionId are themselves foreign keys to Person and Session.
PersonSession also has a field areaId.
I want (personId, areaId) to be a composite foreign key to PersonArea.
My code for PersonSession:
#Entity
#Table(name="person_session")
#IdClass(PersonSession.ID.class)
public class PersonSession {
#Id
private int personId ;
#Id
private int sessionId;
#ManyToOne
#JoinColumn(name = "personId", updatable = false, insertable = false, referencedColumnName = "id")
private Person person;
#ManyToOne
#JoinColumn(name = "sessionId", updatable = false, insertable = false, referencedColumnName = "id")
private Session session;
#ManyToOne//(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "personId", updatable = false, insertable = false),
#JoinColumn(name = "areaId", updatable = false, insertable = false),
})
private PersonArea personArea;
}
Code for PersonSession.Id
public static class ID implements Serializable {
private int personId;
private int sessionId;
}
This seems OK, it creates all the correct relationships in the database. The problem comes when I try to insert PersonSession objects - the areaId column is always null, I think that is because it's defined a updatable=false, insertable=false.
However, if I try and make it updatable and insertable, I get an exception complaining the personId is a repeated column:
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: foo.bar.PersonSession column: personId (should be mapped with insert="false" update="false")
How can I have the required relationships AND have areaId updatable and insertable?
I should be able to do what I want with this:
#ManyToOne//(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "personId"),
#JoinColumn(name = "areaId", updatable = false, insertable = false),
})
private PersonArea personArea;
But Hibernate does not support mixing updatable and non updatable Join Columns. The accepted answer to this question indicates that it might be supported at some time, but it seems the developers aren't very worried about that shortcoming.
I have how ditched Hibernate in favour of Eclipselink and it works!
I know I am late but I faced the same problem and I used #JoinColumnsOrFormulas to resolve it. Here is what you could do:
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(column = #JoinColumn(name="personId", referencedColumnName = "personId")),
#JoinColumnOrFormula(formula = #JoinFormula(value="areaId", referencedColumnName = "areaId"))})
private PersonArea personArea;
I'm having a problem that I don't know if it's possible to solve just by using hibernate/jpa annotations. The problem is that I have a composite key that has the same column as one of my foreignkey composite id, and I would like to share this same column on the table. For example:
#Entity
class Id {
#Id
#Column(name = "idPessoa")
public Integer idShared;
}
#Embeddable
class APK {
#ManyToOne
#JoinColumn(name = "idShared")
public Id idShared;
public String nKey;
}
#Entity
class A {
#EmbeddedId
public APK id;
}
#Embeddable
class BPK {
#ManyToOne
#JoinColumn(name = "idShared")
public Id idShared;
public Integer nCode;
}
#Entity
class B {
#EmbeddedId
public BPK id;
#ManyToOne
#JoinColumns({ #JoinColumn(name = "idShared", nullable = false, insertable = false, updatable = false), #JoinColumn(name = "nKey", nullable = false) })
public A a;
}
The question is how I can share the column idShared between A and B and use it in the #ManyToOne for the foreign key?
I already tried to use #JoinColumn inside #JoinColumns with the name idShared but I get an error saying that I need to use insert = false and update = false, I already put insertable = false and updateable = false, but then I get another error saying that I can't mix things.
I found a possible solution saying to use:
#ManyToOne
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(formula = #JoinFormula(value = "idShared", referencedColumnName = "idShared")),
#JoinColumnOrFormula(column = #JoinColumn(name = "nKey", nullable = false)) })
public A a;
But it gives me the error:
Unable to find column with logical name in table A
It appears that the "name" property of the column it has to find is blank someway.
Need some help please!
Please have a look at Official Java EE 6 Tutorial about composite primary key. You can use #EmbeddedId #Embeddable and/or #IdClass annotations.
For example
// File: APK.java ---------------------
#Embeddable
public class APK implements Serializable {
public Integer idShared;
public String nKey;
}
// File: A.java ---------------------
#Entity
public class A {
#EmbeddedId public APK id;
}