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;
}
Related
I have defined a many-to-one relationship between my three entity classes catalogB, tableA and tableAB. tableAB has a primary key composite of tableA.tableAId and catalogB.catalogBId, and I used the following code:
#Entity
#Table(name = "tableAB", schema = Constantes.SCHEMA_SOLICITUD,uniqueConstraints = {#UniqueConstraint(columnNames = { "tableAId ", "catalogBId" }) })
public class TableAB implements Serializable {
private static final long serialVersionUID = 6360131240770014903L;
#Id
#ManyToOne(optional = false)
#JoinColumn(name = "tableAId", referencedColumnName = "tableAId")
private TableAId tableAId;
#Id
#ManyToOne(optional = false)
#JoinColumn(name = "catalogBId", referencedColumnName = "catalogBId")
private CatalogBId catalogBId;
And that gives me an error:
Caused by: java.lang.IllegalArgumentException: This class [schema.TableAB ] does not define an IdClass
Refer this question. The accepted answer as well as other answers shed light on what you are trying to achieve.
I'm trying to implement a weird database design and I can't figure out how to do it. There is a many to one relationship between two entities having both of them a composed primary key. The foreign key is composed of a field present in the primary key of the other entitie and a field that is not in the primary key. Hope someone could help me.
#Entity
#Table(name = "A")
public class A {
#EmbeddedId
private Aid id;
#JoinColumn(name = "FOREIGN_KEY_FIELD2", referencedColumnName = "foreignKeyField2")
private B foreignKeyField2;
}
#Embeddable
public class Aid{
#JoinColumn(name = "FOREIGN_KEY_FIELD1", referencedColumnName = "foreignKeyField1")
private B foreignKeyField1;
private String otherField;
}
#Entity
#Table(name = "B")
public class B {
#EmbeddedId
private Bid id;
#OneToMany(targetEntity = A.class, fetch = FetchType.LAZY)
#JoinColumns({ #JoinColumn(name = "FOREIGN_KEY_FIELD1", referencedColumnName = "foreignKeyField1"),
#JoinColumn(name = "FOREIGN_KEY_FIELD2", referencedColumnName = "foreignKeyField2") })
private List<A> as;
}
#Embeddable
public class Bid {
private String foreignKeyField1;
private String otherField;
}
Best regards.
Thanks in advance.
You can't solve this using #EmbeddedId you must define a IdClass.
Here is an example:
#Entity #IdClass(PartPK.class)
public class Part {
#Id int partNo;
#Id #ManyToOne
Supplier supplier;
}
public class PartPK {
int partNo;
int supplier;
}
I'm using spring-boot 1.5.4 with spring-data-jpa and I'm trying to override the auto generated foreign key name during spring.jpa.hibernate.ddl-auto=create.
For simple id, I was able to override it: simple_fk
Hibernate: alter table my_entity add constraint simple_fk foreign key (simple_id) references simple
But not for foreign key with composite id: FKms12cl9ma3dk8egqok1dasnfq
Hibernate: alter table my_entity add constraint FKms12cl9ma3dk8egqok1dasnfq foreign key (composite_id1, composite_id2) references composite
What is wrong with my code? I also tried #PrimaryKeyJoinColumn.
Please see the class definitions below.
#Entity
public class Simple {
#Id
private long id;
}
#Entity
public class Composite {
#Id
private CompositeId id;
}
#Embeddable
public class CompositeId {
#Column
private long id1;
#Column
private long id2;
}
#Entity
public class MyEntity {
#ManyToOne
#JoinColumn(foreignKey = #ForeignKey(name = "simple_fk"),
name = "simple_id", referencedColumnName = "id")
private Simple simple;
#ManyToOne
#JoinColumns(foreignKey = #ForeignKey(name = "composite_fk"), value = {
#JoinColumn(name = "composite_id1", referencedColumnName = "id1"),
#JoinColumn(name = "composite_id2", referencedColumnName = "id2")
})
private Composite composite;
}
This is a known issue with the Hibernate which was fix in version 5.2.8
So there are two ways to fix it: Either you update the Hibernate to the version 5.2.8 or up by adding
<hibernate.version>5.2.10.Final</hibernate.version>
to your pom.xml, which basically will update the Hibernate to the latest version.
or if Hibernate update is not possible or is just too risky you can add legacy/deprecated #org.hibernate.annotations.ForeignKey(name = "composite_fk") annotation on your
composite field which will make you code look like
#Entity
public class MyEntity {
#ManyToOne
#JoinColumn(foreignKey = #ForeignKey(name = "simple_fk"), name = "simple_id", referencedColumnName = "id")
private Simple simple;
#ManyToOne
#JoinColumns(foreignKey = #ForeignKey(name = "composite_fk"), value = {
#JoinColumn(name = "composite_id1", referencedColumnName = "id1"),
#JoinColumn(name = "composite_id2", referencedColumnName = "id2") })
#org.hibernate.annotations.ForeignKey(name = "composite_fk")
private Composite composite;
}
i have a persit problem with elcipselink
here is my model :
#Entity
#Table(name = "TBL_ASSOC_LANGUE_CODE_BE_GARE")
public class AssocLangueCodeBeGare {
#AttributeOverrides({ #AttributeOverride(name = "id_langue", column = #Column(name = "ID_LANGUE") ),
#AttributeOverride(name = "id_gare", column = #Column(name = "ID_GARE") ) })
#EmbeddedId
private AssocLangueCodeBeGareFK key;
#Column(name = "CODE_BE", length = 4)
private String codeBe;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false, name = "ID_GARE", referencedColumnName = "ID_GARE", insertable = false, updatable = false)
private StopPoint stopPoint;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_LANGUE", referencedColumnName = "ID_LANGUE", insertable = false, updatable = false)
private Langue langue;
in StopPoint i don t reference table AssocLangueCodeBeGare, i don t need it.
then when i do :
this.serviceStopPoint.save(currentStopPoint);
for (AssocLangueCodeBeGare assoc : listAssocLangueCodeBeGare) {
assoc.setStopPoint(currentStopPoint);
this.serviceAssocLangueCodeBeGare.save(assoc);
}
save is
#Override
public void save(T entityToSave) {
this.getEntityManager().persist(entityToSave);
}
I m using batch insert for writing and sometimes when i save another entity flush is done and i get :
Caused by: org.h2.jdbc.JdbcBatchUpdateException: NULL not allowed for column "ID_GARE"; SQL statement:
INSERT INTO TBL_ASSOC_LANGUE_CODE_BE_GARE (CODE_BE, ID_GARE, ID_LANGUE) VALUES (?, ?, ?) [23502-192]
at org.h2.jdbc.JdbcPreparedStatement.executeBatch(JdbcPreparedStatement.java:1208)
at org.eclipse.persistence.internal.databaseaccess.DatabasePlatform.executeBatch(DatabasePlatform.java:2134)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeJDK12BatchStatement(DatabaseAccessor.java:871)
does i miss something in the mapping?
does i need to add something like this in stoppoint entity:
#OneToMany(mappedBy = "stopPoint", cascade = CascadeType.ALL)
List<AssocLangueCodeBeGare> assocLangueCodeBeGares;
#EDIT1
I think its caused by my EmbeddedId because it was null!! the mapping does'nt set correct value in the embedable object?
here is the embedable object :
#Embeddable
public class AssocLangueCodeBeGareFK implements Serializable {
private String id_langue;
private Long id_gare;
#Override
public int hashCode() {
thanks a lot!
The insertable = false and updatable = false prevent the value from the mapping from being written to the database. You need to have another mapping to that database column with the value set, or it won't get into the database. While making your OneToMany the ID works, so would the MapsId annotation in the current code as JPA will then set the value in your embeddable ID. Or you could have manually added the value from the referenced class into the appropriate AssocLangueCodeBeGare.key fields.
Finaly i find a solution :
I use #IdClass(AssocLangueCodeBeGareFK.class on AssocLangueCodeBeGare
And i put #id on
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false, name = "ID_GARE", referencedColumnName = "ID_GARE")
private StopPoint stopPoint;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_LANGUE", referencedColumnName = "ID_LANGUE")
private Langue langue;
I rename on my AssocLangueCodeBeGareFK class name of my property to be the same as the AssocLangueCodeBeGare class :
private Long stopPoint;
private String langue;
now all work like a charm.
if it helps...
Here's the DB design (DDL):
CREATE TABLE Countries
(
iso_code CHAR(2) NOT NULL,
name VARCHAR(50) NOT NULL,
PRIMARY KEY (iso_code)
);
CREATE TABLE Zips
(
country_code CHAR(2) NOT NULL,
code VARCHAR(10) NOT NULL,
PRIMARY KEY (country_code, code),
FOREIGN KEY (country_code) REFERENCES Countries (iso_code)
);
Here's the Zip class + composite primary key class:
#Entity
#Table(name = "Zips")
public class Zip implements Serializable
{
#EmbeddedId
private ZipId embeddedId;
#ManyToOne
#JoinColumn(name = "country_code", referencedColumnName = "iso_code")
private Country country = null;
...
}
#Embeddable
public class ZipId implements Serializable
{
#Column(name = "country_code", insertable = false, updatable = false)
private String countryCode;
#Column(name = "code")
private String code;
...
}
Hibernate stack trace:
Exception in thread "main" javax.persistence.PersistenceException: [PersistenceUnit: zips] Unable to build EntityManagerFactory
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:911)
at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:57)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:48)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:32)
at tld.zips.Main.main(Main.java:27)
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: tld.zips.model.Zip column: country_code (should be mapped with insert="false" update="false")
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:675)
at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:697)
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:719)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:473)
at org.hibernate.mapping.RootClass.validate(RootClass.java:235)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1332)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1835)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:902)
... 4 more
What's this? country_code is mapped as read-only (insertable = false, updatable = false) in the composite primary key class. This works perfectly with EclipseLink! IIRC #Embeddable classes allow #Basic, #Column, #Enumerated, #Temporal, #Lob, and #Embedded on its columns, so this should work. Note the code is JPA 1.0 compatible.
The exception vanishes when putting the insertable = false, updatable = false on the #JoinColumn, but this is not what I want. I prefer my associations to be writable...
Is this a Hibernate bug? I'm using Hibernate 3.6 stable.
Looks like a bug. As a workaround you can place country into ZipId instead of countryCode:
#Entity
#Table(name = "Zips")
public class Zip implements Serializable
{
#EmbeddedId
private ZipId embeddedId;
...
}
#Embeddable
public class ZipId implements Serializable
{
#ManyToOne
#JoinColumn(name = "country_code", referencedColumnName = "iso_code")
private Country country = null;
#Column(name = "code")
private String code;
...
}
Yeah, seems like a bug. Anyway, you can do it like this, I suppose. Haven't tried it myself, though.
#Entity
#Table(name = "Zips")
public class Zip implements Serializable
{
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name="countryCode", column=#Column(name="country_code", insertable = false, updatable = false))
#AttributeOverride(name="code", column=#Column("code"))
})
private ZipId embeddedId;
#ManyToOne
#JoinColumn(name = "country_code", referencedColumnName = "iso_code")
private Country country;
...
}
#Embeddable
public class ZipId implements Serializable
{
private String countryCode;
private String code;
...
}