How to give composite unique keys ( address1 and address2) as Map key. Like #MapKey(name = "address1", name = "address2") instead of single unique key #MapKey(name = "address1").
#Entity
public class Person {
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "person")
#MapKey(name = "address1")
public Map<String, Address> getAddressMap() {
return addressMap;
}
}
#Entity
public class Address {
private Integer id;
private String address1;
private String address2;
private Person person;
}
Use Embeddable type.
Create an embeddable class (i.e. PersonAddress) to encapsulate your address1 and address2 properties. Then use that embeddable class as a Map key.
#Entity
public class Person {
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "person")
public Map<PersonAddress, Address> getAddressMap() {
return addressMap;
}
}
#Entity
public class Address {
private Integer id;
#Embedded
private PersonAddress;
#ManyToOne
private Person person;
}
#Embeddable
public class PersonAddress {
private String address1;
private String address2;
}
Related
I have defined several JPA entities in a Spring Boot application.
#Entity
public class EntityA {
#Id
#Column(name = "id")
private String id
// ...
//....other fields
}
#Entity
public class EntityB {
#EmbeddedId
private MyEmbeddedId id;
// ....other fields
}
public class MyEmbeddedId {
#Column(name = "id_a")
private String idA;
#Column(name = "color")
private String color;
}
EntityA is self-sufifcient, EntityB has a composite key, one of whose constituent is primary key of EntityA(represented by column id_a). So I use embedded key feature here. There is a one-to-many relationship between EntityA and EntityB. An instance of EntityA can be mapped to multiple instances of EntityB.
Now, my objective is to fetch EntityA with all its associated EntityB.
How to go about mapping for this?
Try below Entity mappings,
#Entity
public class EntityA {
#Id
#Column(name = "id")
private String id
#OneToMany(mappedBy = "myEmbeddedId.idA",
cascade = CascadeType.ALL)
private Set<EntityB> entityB;
// ...
//....other fields
}
#Entity
public class EntityB {
#EmbeddedId
private MyEmbeddedId myEmbeddedId;
// ....other fields
}
public class MyEmbeddedId {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="id_a")
private EntityA idA;
#Column(name = "color")
private String color;
}
When you load EntityA, it will load all associated EntityB.
We have tables,
'Lin_Code_Groups' with fields,
Project_ID (PK),
CG_ID(PK),
CG_Name
Corresponding entity class,
public class Lin_Code_Groups implements Serializable {
#EmbeddedId
private LinCodeGroupPK pk;
private String CG_name;
#Embeddable
public static class LinCodeGroupPK implements Serializable {
private Integer Project_ID;
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer CG_ID;
}
#OneToMany(mappedBy = "lin_Code_Groups")
private List<Lin_CG_Params> lin_CG_Params;
}
table Lin_CG_Params with fields,
Project_ID (PK)..FK to Lin_Code_Groups,
CG_ID(PK)...FK to Lin_Code_Groups,
Param_name(PK),
Param_value
Corresponding entity class,
public class Lin_CG_Params implements Serializable {
#EmbeddedId
private LinCodeGroupParamPK pk;
private String Param_value;
#Embeddable
public static class LinCodeGroupParamPK implements Serializable {
private String Param_name;
private LinCodeGroupPK linCodeGroupPK;
}
#MapsId("linCodeGroupPK")
#ManyToOne
#JoinColumns( {
#JoinColumn(name = "Project_ID",referencedColumnName= "Project_ID"),
#JoinColumn(name = "CG_ID",referencedColumnName= "CG_ID")
})
private Lin_Code_Groups lin_Code_Groups;
}
in controller class, i am using JPA's .Save method to save the data in to the tables.
#PostMapping(value = {"/hello"}, consumes = "application/json", produces = "application/json")
public ResponseEntity<Object> saveNewCodeGroupsDetails(#RequestBody Lin_Code_Groups objLin_Code_Groups ) {
respository.save(objLin_Code_Groups);
}
but getting an error 'Unable to find Lin_CG_Params with id Lin_CG_Params.LinCodeGroupParamPK'
Can anyone is please help ?
COUNTRY and ADDRESS table has one-to-many relationship and ADDRESS and COUNTRY has many-to-one relationship. COUNTRY table has Id and Country Names. Address table has Country(has country codes from COUNTRY table) and NAME.
NAME and COUNTRY are unique keys in the ADDRESS table.
How to add Address Entity's NAME to CountryAddress Embeddable class? ( embeddable attributes are from different entities)
#Entity
public class Country {
#Column(name = "COUNTRY")
private String countryName;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "country")
public Map<CountryAddress, Address> getAddressMap() {
return addressMap;
}
#Embeddable
public class CountryAddress {
#Column(name = "COUNTRY")
private String countryName;
// Need to add NAME from Address
}
}
#Entity
public class Address {
private Integer id;
#Column(name = "NAME")
private String name;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "COUNTRY")
private Country country;
}
I got a problem with JPA and ManyToMany association.
I got two class FOA_PARAM_EMPLOYE and FOA_PARAM_POSITION, and an association table FOA_PARAM_EMPLOYE_POSITION.
Class FoaParamEmploye :
#Entity
#Table(name = "FOA_PARAM_EMPLOYE")
#NamedQuery(name = "FoaParamEmploye.findAll", query = "SELECT f FROM FoaParamEmploye f")
public class FoaParamEmploye implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private FoaParamEmployePK id;
#Column(name = "ACTEUR_MAJ_OCCUR")
private String acteurMajOccur;
#Column(name = "ADRESSE_EMAIL")
private String adresseEmail;
// bi-directional many-to-many association to FoaParamPosition
#ManyToMany
#JoinTable(
name = "FOA_PARAM_EMPLOYE_POSITION",
joinColumns = { #JoinColumn(name = "ID_EMPLOYE"),
#JoinColumn(name = "COD_ENTREP") },
inverseJoinColumns = { #JoinColumn(name = "ID_POSITION")
})
private List<FoaParamPosition> foaParamPositions;
public FoaParamEmployePK getId() {
return this.id;
}
public void setId(FoaParamEmployePK id) {
this.id = id;
}
public String getActeurMajOccur() {
return this.acteurMajOccur;
}
public void setActeurMajOccur(String acteurMajOccur) {
this.acteurMajOccur = acteurMajOccur;
}
public String getAdresseEmail() {
return this.adresseEmail;
}
public void setAdresseEmail(String adresseEmail) {
this.adresseEmail = adresseEmail;
}
public List<FoaParamPosition> getFoaParamPositions() {
return foaParamPositions;
}
public void setFoaParamPositions(List<FoaParamPosition> pFoaParamPositions) {
this.foaParamPositions = pFoaParamPositions;
}
}
Class FoaParamPosition :
#Entity
#Table(name="FOA_PARAM_POSITION")
#NamedQuery(name="FoaParamPosition.findAll", query="SELECT f FROM FoaParamPosition f")
public class FoaParamPosition implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private FoaParamPositionPK id;
#Column(name="ACTEUR_MAJ_OCCUR")
private String acteurMajOccur;
#Column(name="CD_PROFIL_AFFECTATION")
private String cdProfilAffectation;
// bi-directional many-to-many association to FoaParamEmploye
#ManyToMany
private List<FoaParamEmploye> foaParamEmployes;
public FoaParamPositionPK getId() {
return this.id;
}
public void setId(FoaParamPositionPK id) {
this.id = id;
}
public String getActeurMajOccur() {
return this.acteurMajOccur;
}
public void setActeurMajOccur(String acteurMajOccur) {
this.acteurMajOccur = acteurMajOccur;
}
public String getCdProfilAffectation() {
return this.cdProfilAffectation;
}
public void setCdProfilAffectation(String cdProfilAffectation) {
this.cdProfilAffectation = cdProfilAffectation;
}
public List<FoaParamEmploye> getFoaParamEmployes() {
return foaParamEmployes;
}
public void setFoaParamEmployes(List<FoaParamEmploye> pFoaParamEmployes) {
this.foaParamEmployes = pFoaParamEmployes;
}
}
Table FOA_PARAM_EMPLOYE_POSITION has this columns :
COD_ENTREP
ID_EMPLOYE
ID_POSITION
XQCIF
ACTEUR_MAJ_OCCUR
DATE_HEURE_MAJ_OCCUR
I got this exception :
A Foreign key refering com.groupama.middlgan.entities.FoaParamPosition from
com.groupama.middlgan.entities.FoaParamEmploye has the wrong number of column.
should be 2
If I add COD_ENTREP on inverseJoinColumns in my FoaParamEmploye entity, I got this exception :
Repeated column in mapping for collection:
com.groupama.middlgan.entities.FoaParamEmploye.foaParamPositions column: COD_ENTREP
Any idee ?
[I am assuming you are listing the columns for the association table FOA_PARAM_EMPLOYE_POSITION, not FOA_PARAM_EMPLOYE, as you state in your question.]
Your mappings imply FOA_PARAM_EMPLOYE_POSITION.COD_ENTREP is part of two foreign keys, one referencing FOA_PARAM_EMPLOYE and one referencing FOA_PARAM_POSITION. In practice these two foreign keys might contain the same value for COD_ENTREP, but this cannot be enforced by the database or JPA.
You should probably model the relationship differently, possibly add another, container-like, object that has bi-directional one-to-many relationships with both FoaParamEmploye and FoaParamPosition and shares parts of its primary key with the other two primary keys.
If you want to keep your inverseJoinColumn mapping you can do as follows,
#ManyToMany
#JoinTable(
name = "FOA_PARAM_EMPLOYE_POSITION",
joinColumns = { #JoinColumn(name = "ID_EMPLOYE"),
#JoinColumn(name = "COD_ENTREP") }
,
inverseJoinColumns = {#JoinColumn(name = "FOAPARAMPOSITION_COD_ENTREP"), #JoinColumn(name = "FOAPARAMPOSITION_ID_POSITION")}
)
private List<FoaParamPosition> foaParamPosition;
and
#ManyToMany
#JoinTable(
name = "FOA_PARAM_EMPLOYE_POSITION",
joinColumns = { #JoinColumn(name = "ID_POSITION"),
#JoinColumn(name = "COD_ENTREP") }
,
inverseJoinColumns = {#JoinColumn(name = "FOAPARAMEMPLOYE_COD_ENTREP"), #JoinColumn(name = "FOAPARAMEMPLOYE_ID_EMPLOYE")}
)
private List<FoaParamEmploye> foaParamEmploye;
the mappings are aligned with the code you've posted in your other question JPA Many To Many Select
I have an existing JPA entity ("Reference") with an ID column as its primary key that it inherits from a base class "BaseEntity" (using the #MappedSuperclass annotation on the superclass).
I also have a 1-M relationship between a Reference and another entity called Violation. Violation was previously defined with a foreign key "REFERENCE_ID" to the "ID" column of the Reference entity.
Recently, I tried to add an unrelated composite key to the Reference entity. This should not have affected the 1-M relationship between Reference and Violation. However, when I run the code in my tomcat server, I see the following stack trace:
Caused by: org.hibernate.AnnotationException: referencedColumnNames(ID) of org.qcri.copydetection.sdk.metastore.entity.Violation.reference referencing org.qcri.copydetection.sdk.metastore.entity.Reference not mapped to a single property
at org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:205) ~[hibernate-annotations-3.5.6-Final.jar:3.5.6-Final]
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:110) ~[hibernate-annotations-3.5.6-Final.jar:3.5.6-Final]
at org.hibernate.cfg.AnnotationConfiguration.processEndOfQueue(AnnotationConfiguration.java:541) ~[hibernate-annotations-3.5.6-Final.jar:3.5.6-Final]
at org.hibernate.cfg.AnnotationConfiguration.processFkSecondPassInOrder(AnnotationConfiguration.java:523) ~[hibernate-annotations-3.5.6-Final.jar:3.5.6-Final]
at org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:380) ~[hibernate-annotations-3.5.6-Final.jar:3.5.6-Final]
at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1206) ~[hibernate-core-3.5.6-Final.jar:3.5.6-Final]
at org.hibernate.ejb.Ejb3Configuration.buildMappings(Ejb3Configuration.java:1459) ~[hibernate-entitymanager-3.5.6-Final.jar:3.5.6-Final]
at org.hibernate.ejb.EventListenerConfigurator.configure(EventListenerConfigurator.java:193) ~[hibernate-entitymanager-3.5.6-Final.jar:3.5.6-Final]
at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:1086) ~[hibernate-entitymanager-3.5.6-Final.jar:3.5.6-Final]
at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:685) ~[hibernate-entitymanager-3.5.6-Final.jar:3.5.6-Final]
at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:73) ~[hibernate-entitymanager-3.5.6-Final.jar:3.5.6-Final]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:268) ~[spring-orm-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:310) ~[spring-orm-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514) ~[spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452) ~[spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE]
... 39 common frames omitted
Here is the code for the 3 classes involved:
#Entity
#Table(name = "REFERENCE")
#XmlRootElement
#XmlAccessorType(XmlAccessType.PROPERTY)
#IdClass(Reference.ContextualName.class)
public class Reference extends BaseEntity {
#Column(name= "LOCATION", unique=true)
#XmlElement
private String location;
#Id
#AttributeOverrides({
#AttributeOverride(name = "name", column = #Column(name = "NAME")),
#AttributeOverride(name = "account", column = #Column(name = "ACCOUNT_ID"))
})
#Column(name = "NAME")
#XmlElement
private String name;
#ManyToOne(optional=false)
#XmlTransient
#JoinColumn(name = "ACCOUNT_ID", referencedColumnName = "ID")
private Account account;
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public Reference() {}
public Reference(String name) {
setName(name);
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public Account getAccount() {
return this.account;
}
public void setAccount(Account account) {
this.account = account;
}
#Embeddable
private class ContextualName implements Serializable {
private static final long serialVersionUID = -3687389984589209378L;
#Basic(optional = false)
#Column(name = "NAME")
#XmlElement
private String name;
#ManyToOne(optional=false)
#XmlTransient
#JoinColumn(name = "ACCOUNT_ID", referencedColumnName = "ID")
private Account account;
ContextualName() {}
}
}
#MappedSuperclass
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
#XmlElement
private Long id;
#Basic(optional = true)
#Column(name = "CREATED", insertable = false, updatable = false, columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
#Temporal(TemporalType.TIMESTAMP)
#XmlElement
private Date creationDate;
protected BaseEntity() {}
public Long getId() {
return id;
}
public void setId(Long id) {
if(this.id==null) {
this.id = id;
} else if (this.id!=id) {
throw new IllegalArgumentException("Cannot change the id after it has been set, as it is a generated field.");
}
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
if(this.creationDate==null) {
this.creationDate = creationDate;
} else if (this.creationDate!=creationDate) {
throw new IllegalArgumentException("Cannot change the creation-date after it has been set, as it is a generated field.");
}
}
}
#Entity
#Table(name = "VIOLATION")
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Violation extends BaseEntity {
#ManyToOne (optional=false, fetch= FetchType.EAGER)
#JoinColumn(name = "REFERENCE_ID", referencedColumnName = "ID")
private Reference reference;
#ManyToOne (optional=false, fetch= FetchType.EAGER)
#JoinColumn(name = "SUSPECT_ID", referencedColumnName = "ID")
private Suspect suspect;
#ManyToOne (optional=false, fetch= FetchType.EAGER)
#XmlTransient
#JoinColumn(name = "SEARCH_ID", referencedColumnName = "ID")
private Search search;
#Basic(optional = false)
#Column(name = "SCORE")
#XmlElement
private double score;
public Violation() {}
public Violation(Search search, Reference ref, Suspect sus, double score) {
this.search = search;
this.reference = ref;
this.suspect = sus;
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public Reference getReference() {
return reference;
}
public void setReference(Reference reference) {
this.reference = reference;
}
public Suspect getSuspect() {
return suspect;
}
public void setSuspect(Suspect suspect) {
this.suspect = suspect;
}
public Search getSearch() {
return search;
}
public void setSearch(Search search) {
if(this.search!=null && this.search!=search) {
this.search.removeViolation(this);
}
this.search = search;
if(search!=null) {
if(!search.getViolations().contains(this)) {
search.addViolation(this);
}
}
}
}
To cut a long story short, I'm totally confused how to go about adding a composite key to an existing (legacy) entity that already has an ID column. I can't remove the ID column, nor can I change the 1-M relationship between Reference and Violation. I can't for the life of me understand the error message because the "REFERENCE_ID" foreign key column of the Violation entity is being mapped to a single "ID" column of the Reference entity.
Many thanks in advance!