I have the following relation in database:
I have one strong table.
I have one weak table that has one to one relation with strong table. Really it's 0 to 1 relation, because strong table doesn't have always one line in weak table. To identify this weak table is enough the Id of strong table.
And finally I have another weak table, with ManyToOne relation with first weak entity. It needs the id of OneToOneWeakEntity (that also is id of strong table), and his own id. It's like an historical of OneToOneWeakTable.
I want to map in Hibernate, but I don't know how to do it.
Now I have the following code:
#Entity
#Table(name="table")
public class Table {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id_table")
private Integer idTable;
private String otherAtributes;
....
}
#Entity
#Table(name="oneToOneWeakTable")
public class OneToOneWeakEntity {
#OneToOne(cascade = CascadeType.ALL, optional=false)
#Id
#JoinColumn(name="table_id_table")
private Table table;
private String otherAtributes;
....
}
#Entity
#Table(name="oneToManyWeakTable")
#IdClass(EntityPk.class)
public class OneToManyWeakTable {
#Id
#ManyToOne
#JoinColumn(name="table_id_table")
private OneToOneWeakEntity oneToOneWeakEntity;
#Id
#Column(name="own_id")
private String ownId;
private String otherAtributes;
....
}
class EntityPk {
#Id
#ManyToOne
#JoinColumn(name="table_id_table")
private OneToOneWeakEntity oneToOneWeakEntity;
#Id
#Column(name="own_id")
private String ownId;
private String otherAtributes;
....
}
My problem is when I try to run my application, because I have this deployment error:
Caused by: org.hibernate.AnnotationException: A Foreign key refering package.OneToOneWeakEntity from package.OneToManyWeakTable has the wrong number of column. should be 0
at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:502)
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:117)
at org.hibernate.cfg.Configuration.processFkSecondPassInOrder(Configuration.java:1518)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1422)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1930)
How can I map this relation??
Thanks!
Edit: I also try with this to map OneToOneWeakTable:
#OneToOne(cascade = CascadeType.ALL, optional=false)
#PrimaryKeyJoinColumn
#Id
#JoinColumn(name="table_id_table")
private Table table;
In this case, I also have an error when I try to deploy, but a different exception:
Caused by: java.lang.NullPointerException
at org.hibernate.cfg.Ejb3JoinColumn.checkReferencedColumnsType(Ejb3JoinColumn.java:568)
at org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:258)
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:116)
at org.hibernate.cfg.Configuration.processFkSecondPassInOrder(Configuration.java:1518)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1422)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846)
You don't have to repeat the annotations in the EntityPK class, you just need to match simple primitive attribute that represent the compound key of OneToManyWeakTable, you should get something like this (note insertable and updatable attributes, it has no sense modify the association because it is part of entity instance's pk),
#Entity
#Table(name="oneToManyWeakTable")
#IdClass(EntityPk.class)
public class OneToManyWeakTable {
#Id
#Column(name="table_id_table")
private long weakEntity
#ManyToOne
#JoinColumn(name="table_id_table", insertable=false, updatable=false)
private OneToOneWeakEntity oneToOneWeakEntity;
#Id
#Column(name="own_id")
private String ownId;
private String otherAtributes;
....
}
class EntityPk {
private long weakEntity;
private String ownId;
....
}
#Entity
#Table(name="oneToOneWeakTable")
public class OneToOneWeakEntity {
#OneToOne
#Id
#JoinColumn(name="table_id_table")
private Table table;
private String otherAtributes;
....
}
Also take a look at some official doc of compound primary keys
Edit: add the OneToOneWeakTable assuming that you are using JPA 2, in your edit you are mixing annotation. Check the id fileds and attribute that share the entities, must be same type (note that I use long for weakEntity attribute just as an example).
Related
i have the empty database in mysql, and two java entites. One of those have unidirectional relation. When hibernate tryes to create tables, i got the error:
Error executing DDL "alter table entry add constraint FK6ov2k83sx3crs9v3q8nvjuf1j foreign key (category_name) references category (name)" via JDBC Statement
There are my entites:
#Entity
public class Entry {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY)
private int id;
#Column
private String myfio;
private String descr;
#OneToOne(cascade = CascadeType.ALL)
private Category category;
}
And the second:
#Entity
#Table(name="category")
public class Category {
#Id
#Column
private String name;
}
How to create tables without errors?
OneToOne relationship shares the same id. So it should be the same type, but the first one is int (actually it should be Integer to allow null value for the transient (not stored) entities) and the second one is String. It seems you simply missed a line. Also, it worths to mention Vlad Mihalchea’s article https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/
I am using eclipselink 2.5.1.
Let's say I have these two class.
JAVA
#Entity
public class Car implements Serializable {
#EmbeddedId
protected CarPK carPK;
private String color;
#ManyToOne(fetch = FetchType.LAZY)
private Manufacturor manufacturor;
//constructors, getters & setters...
}
#Embeddable
public class CarPK implements Serializable {
#NotNull
private int idManufacturor;
#Temporal(javax.persistence.TemporalType.DATE)
private Date date;
//constructors, getters & setters...
}
Car has a composite primary key (idManufacturor and date) and idManufacturor is also a foreign key referencing the class Manufacturor.
I'm having issue with the mapping. EclipseLink understand the manufacturor object as a column in my Car table.
Error
Internal Exception: com.microsoft.sqlserver.jdbc.SQLServerException: invalid column name : 'manufacturor'.
I know the problem will be solved if I add a column manufacturor FK but it would be repeating.
Please feel free to ask for any precision if I'm not clear enough.
Thank you for your help.
Add the JoinColumn Annotation
#JoinColumn(name = "id_manufacturor", referencedColumnName = "id")
Name is the FK column name in your database (not entity).
The referencedColumnName "id" must correspond to the defined id in manufacturer table.
So in this example scenario I have an attendance DTO, and a worker DTO, workers in this context are separated by department, and a worker can only ever be inside of one department. It is important to note that Worker {id='123-123-123', department='a'} is different to Worker {id='123-123-123', department='b'}, despite them both sharing the same Id.
I have the following class setup to try and separate functions by id and department
public class IdAndDepartmentPK implements Serializable {
private String id;
private String department;
public IdAndDepartmentPK() {}
...
}
This key class is shared between DTOs that require both the Worker's id and department, below are the two DTOs that are causing a problem.
#Entity
#IdClass(IdAndDepartmentPK.class)
public class AttendencetDto {
#Id private String id; // This is a departmentally unique attendenceId
#Id private String department;
#Column private String workerId;
#JoinColumns({
#JoinColumn(name = "workerId"),
#JoinColumn(name = "department")
})
#ManyToOne(fetch = FetchType.EAGER)
private WorkerDto workerDto;
....
}
#Entity
#IdClass(IdAndDepartmentPK.class)
public class WorkerDto {
#Id
private String id;
#Id
private String department;
...
}
WorkerDto does not need to have knowledge of AttendencetDto, but AttendencetDto, does need to have access to WorkerDto and the other data it contains.
Hibernate complains that fields like workerId should be mapped with insert="false" update="false", but if I was to do this then I wouldn't be able to persist those values to the database.
I essentially want to have those fields available whilst also having the WorkerDto available, is this possible?
You should remove #Column private String workerId; because you already map it by relation to WorkerDto.
If you want to create relation between that you should use setWorkerDto method in your AttendencetDto and just save. After transaction ends you will have your relation in DB.
I have a following error stating
Exception [EclipseLink-7220] (Eclipse Persistence Services -
2.6.3.v20160428-59c81c5): org.eclipse.persistence.exceptions.ValidationException Exception
Description: The #JoinColumns on the annotated element [field
specificationSubset] from the entity class [class
com.jlr.vista.business.rule.model.Rule] 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.
I have found many solutions regarding this error but none of them have addressed this kind of issue in every case both classes have same number of composite keys but in my case I have a one PK in entity class and as FK in child class but that child class also has one more PK which makes them composite key.
In this case how should be the #OneToOne mapping is achieved ?
I'm using Eclipse link version 2.6
Rule.java
public class Rule
{
private static final long serialVersionUID = 1L;
#Id
#Column(name="RULE_ID")
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="RUL_SEQ1")
#SequenceGenerator(name="_RUL_SEQ1",sequenceName="RUL_SEQ1",allocationSize=1)
protected Integer ruleId;
#Column(name="PARENT_RULE_ID")
protected Integer parentRuleId;
#Column(name="RULE_TYPE_CODE")
protected String ruleTypeCode;
#Column(name="BRAND")
protected String brand;
#Column(name="RULE_DESCRIPTION")
protected String ruleDesscription;
#Column(name="RULE_REFERENCE")
protected String ruleReference;
#Column(name="CONSTRAINT_TYPE")
protected String constraintType;
#Column(name="ARCHIVE_STATUS")
protected String archiveStatus;
#ManyToMany(targetEntity=VistaRulePartner.class, fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="RULE_ID", referencedColumnName="RULE_ID")
})
protected List<VistaRulePartner> partners;
#OneToMany(targetEntity=OrderTypeSubset.class,fetch=FetchType.LAZY,cascade=CascadeType.ALL)
#JoinColumn(name="RULE_ID", referencedColumnName="RULE_ID",insertable=false,updatable=false)
protected List orderTypeSubsets;
#OneToOne(targetEntity=SpecificationSubset.class,cascade=CascadeType.ALL,fetch=FetchType.LAZY,optional=false)
#JoinColumn(name="RULE_ID",referencedColumnName="RULE_ID",insertable=false,updatable=false, nullable=false)
private SpecificationSubset specificationSubset;
}
SpecificationSubset.java
#IdClass(SpecificationSubsetPK.class)
#Entity
#Table(name="RULE_SPECIFICATION")
#InstantiationCopyPolicy
#Cache( refreshOnlyIfNewer=true,
coordinationType = CacheCoordinationType.SEND_NEW_OBJECTS_WITH_CHANGES)
public class SpecificationSubset extends ModelArchivable
{
#Id
#Column(name="RULE_ID")
private Integer ruleId;
#Id
#Column(name="RULE_SPEC_ID")
private Integer ruleSpecId = 1;
#Column(name="SPEC_MARKET")
private String specMarket;
#Column(name="DESC_GROUP_ID")
private String descGroupId;
#OneToMany(targetEntity=FeatureSubset.class, fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="RULE_ID", referencedColumnName="RULE_ID", insertable=false, updatable=false),
#JoinColumn(name="RULE_SPEC_ID", referencedColumnName="RULE_SPEC_ID", insertable=false, updatable=false)
})
private List featureSubsets;
#OneToOne(targetEntity=Rule.class, fetch=FetchType.LAZY)
#JoinColumn(name="RULE_ID", referencedColumnName="RULE_ID")
private Rule ruleHolder;
}
FeatureSubset.java
#Entity
#Table(name="RULE_FEATURE")
#NamedQuery(
name="GetFeatureCodesForVehicleConfig",
query="SELECT f FROM FeatureSubset f WHERE f.ruleId = :ruleId AND f.featureType = 'YEAR'")
#InstantiationCopyPolicy
#Cache( coordinationType = CacheCoordinationType.SEND_NEW_OBJECTS_WITH_CHANGES)
public class FeatureSubset
{
#Id
#Column(name="FEATURE_TYPE_NO")
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="VISTA_RUF_SEQ1")
#SequenceGenerator(name="RUF_SEQ1",sequenceName="RUF_SEQ1",allocationSize=1)
private Integer featureTypeNo; //Sequence no for the vista_rule_feature table
#Column(name="RULE_SPEC_ID")
private Integer ruleSpecId = 1;
#Column(name="RULE_ID")
private Integer ruleId;
#Column(name="FEATURE_TYPE_ID")
private String featureType;
#Column(name="FEATURE_CODE")
private String featureCode;
#OneToOne(targetEntity=SpecificationSubset.class, fetch=FetchType.LAZY)
/*#JoinColumns({
#JoinColumn(name="RULE_ID", referencedColumnName="RULE_ID"),
#JoinColumn(name="RULE_SPEC_ID", referencedColumnName="RULE_SPEC_ID")
})*/
private SpecificationSubset specificationSubset;
}
You do not need to define mapping information in both sides of relation - one is sufficient, typically it's the owner of association.
So, since your SpecificationSubset contains mapping information to Rule, in Rule you can just reference this relation:
#OneToOne(targetEntity=SpecificationSubset.class,
cascade=CascadeType.ALL,
fetch=FetchType.LAZY,
optional=false,
mappedBy="ruleHolder") // name of the property in an owning class
private SpecificationSubset specificationSubset;
It seems you need to fix owning side as well, for you already have a #Column with RULE_ID name:
#OneToOne(targetEntity=Rule.class, fetch=FetchType.LAZY)
#JoinColumn(name="RULE_ID", referencedColumnName="RULE_ID", insertable=false, updatable=false)
private Rule ruleHolder;
I'm having a problem with JPA when trying to create some models to my database.
I have these three classes (I'll just put part of the code here):
GuideVersionLang
#Entity
public class GuideVersionLang implements LangItem {
...
#ManyToOne
#JoinColumns({
#JoinColumn(name="GUIDE_VERSION_NUMBER", referencedColumnName="VERSION_NUMBER"),
#JoinColumn(name="GUIDE_ID", referencedColumnName="GUIDE_ID")
})
#JsonIgnore
private GuideVersion guideVersion;
...
}
GuideVersion
#Entity
#IdClass(value=GuideVersionKey.class)
public class GuideVersion {
...
#OneToMany(mappedBy="guideVersion", orphanRemoval=true, cascade=CascadeType.PERSIST)
private LangsCollection<GuideVersionLang> guideVersionLangs;
#Id
#ManyToOne
#JoinColumn(nullable = false, name="GUIDE_ID")
#JsonIgnore
private Guides guide;
#Id
#Column(name = "VERSION_NUMBER")
private long versionNumber;
...
}
And GuideVersionKey
#Embeddable
public class GuideVersionKey {
private long versionNumber;
private long guide;
...
}
So, I have a GuideVersion class and this class has a composite key. Its composite key is composed by the id of a Guide and a versionNumber, both long numbers.
I just want to make a relation between GuideVersion and GuideVersionLang, as you can see in the code. However, I'm having problems on the #JoinColumns annotation:
#JoinColumns({
#JoinColumn(name="GUIDE_VERSION_NUMBER", referencedColumnName="VERSION_NUMBER"),
#JoinColumn(name="GUIDE_ID", referencedColumnName="GUIDE_ID")
})
I don't know why but the #JoinColumns is not working. I'm getting this error:
The #JoinColumns on the annotated element [field guideVersion] from
the entity class [class com.model.GuideVersionLang] 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.
As you can see in the code, I am specifying both #Id columns inside the #JoinColumns annotation. What am I missing here?
Thank you VERY much!
There is some tips about the code in question:
1.as you have an embedded id for GuideVersion (GuideVersionKey ) so you really don't need to specify Ids for it (just use #EmbeddedId annoation).
2.you can map the Guid_id with #MapsId.
#Entity
public class GuideVersion {
...
#EmbeddedId
GuideVersionKey IdKey;
#ManyToOne
#MapsId("guide")
#JoinColumn(name = "GUIDE_ID")
private Guides guide;
...
}