Hello I had a problem with Hibernate and #AttributeOverrides annotation.
I had embedable class money
#Embeddable
public class Money implements Serializable {
private BigDecimal denomination;
private String currencyCode;
}
And entity Loan that contains two money object: Value and Interests
#Entity
public class Loan {
#AttributeOverrides({
#AttributeOverride(name = "valuedenomination", column = #Column(name = "valuedenomination")),
#AttributeOverride(name = "valuecurrencycode", column = #Column(name = "valuecurrencycode")) })
private Money value;
#AttributeOverrides({
#AttributeOverride(name = "interestdenomination", column = #Column(name = "interestdenomination")),
#AttributeOverride(name = "interestcurrencycode", column = #Column(name = "interestcurrencycode")) })
private Money interest;
}
When I trying to package application I got follow cause exception:
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: org.finance.app.core.domain.common.loan.Loan column: currencyCode (should be mapped with insert="false" update="false")
When I removed One Money entity from Loam in dataBase I got two fields:
currencyCode and denomination instead of valuecurrencyCode and valuedenomination as I put in AttributeOverrides.
Do I miss something with AttributeOverrides annotation?
AttributeOverride::name should be the name of property inside embedded class Money. So your mapping should looks like:
#Entity
public class Loan {
#AttributeOverrides({
#AttributeOverride(name = "denomination",
column = #Column(name = "valuedenomination")),
#AttributeOverride(name = "currencyCode",
column = #Column(name = "valuecurrencycode")) })
private Money value;
#AttributeOverrides({
#AttributeOverride(name = "denomination",
column = #Column(name = "interestdenomination")),
#AttributeOverride(name = "currencyCode",
column = #Column(name = "interestcurrencycode")) })
private Money interest;
}
Related
I have this sql query I am running in my database:
Select ce.external_id, ce.isid, ji.name, ji.controlled_status, ji.jurisdiction_name, ji.result_comments, ji.code_name, s.corporate_id
from cs_jurisdiction_information ji
Join substance s on ji.substance_id = s.id
Join controlled_event ce on ce.id = s.controlled_event_id and ce.calling_system_id = 402
and ce.external_id = 'EXP-raoan-000011774';
Which produces the above results. Everything following the first two fields are compounds that I would like to map to my model, so when I run this query from my repository:
public interface ExperimentDetailsRepository extends JpaRepository<ExperimentDetails, String> {
#Query(value = "Select ce.external_id, ce.isid, ji.name, ji.controlled_status, " +
"ji.jurisdiction_name, ji.result_comments," +
"ji.code_name, s.corporate_id " +
"from cs_jurisdiction_information ji " +
"Join substance s on ji.substance_id = s.id " +
"Join controlled_event ce on ce.id = s.controlled_event_id " +
"and ce.calling_system_id = 402 " +
"and ce.external_id = :externalId", nativeQuery = true)
List<ExperimentDetails> findByExternalId(#Param("externalId") String externalId);
}
I first tried this model:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
public class ExperimentDetails {
#Id
private String externalId;
private String isid;
private String name;
private String controlledStatus;
private String jurisdictionName;
private String resultComments;
private String codeName;
private String corporateId;
}
Calling findByExternalId(externalId) in my service class produces the incorrect results, the method returns 3 objects which is fine, but only maps the first compound Cathine, and duplicates the result for the next two objects, which makes sense because my structure here is flat, when it actually should be an array for anything after name attribute.
So I tried using #Embeddable annotation to modify the model to the correct structure as below:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
public class ExperimentDetails {
#Id
private String externalId;
private String isid;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "name", column = #Column(name = "name")),
#AttributeOverride(name = "controlledStatus", column = #Column(name = "controlled_status")),
#AttributeOverride(name = "jurisdictionName", column = #Column(name = "jurisdiction_name")),
#AttributeOverride(name = "resultComments", column = #Column(name = "result_comments")),
#AttributeOverride(name = "codeName", column = #Column(name = "code_name")),
#AttributeOverride(name = "corporateId", column = #Column(name = "corporate_id")),
})
private Compound compounds;
}
and I created the Embeddable class:
#Embeddable
#Data
public class Compound {
private String name;
private String controlledStatus;
private String jurisdictionName;
private String resultComments;
private String codeName;
private String corporateId;
}
This duplicates the same error in results as the previous model, which I then realized still doesn't produce the array of compounds, so I modified the model again:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
public class ExperimentDetails {
#Id
private String externalId;
private String isid;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "name", column = #Column(name = "name")),
#AttributeOverride(name = "controlledStatus", column = #Column(name = "controlled_status")),
#AttributeOverride(name = "jurisdictionName", column = #Column(name = "jurisdiction_name")),
#AttributeOverride(name = "resultComments", column = #Column(name = "result_comments")),
#AttributeOverride(name = "codeName", column = #Column(name = "code_name")),
#AttributeOverride(name = "corporateId", column = #Column(name = "corporate_id")),
})
private List<Compound> compounds;
}
This time making Compound a list. However, running findByExternalId(externalId) now produces null for the embedded class, so no compounds are actually mapped.
How can I map my model correctly to produce the correct results for my query?
Given A class
#Entity
#Table(name = "ATABLE")
public class A implements Serializable {
public static final String DB_ID = "AID";
public static final String DB_MARKET = "AMARKET";
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "id", column = #Column(name = DB_ID)),
#AttributeOverride(name = "market", column = #Column(name = DB_MARKET))
})
public AIdClass id;
#OneToMany
#JoinColumn(name = B.DB_MARKET, referencedColumnName = DB_MARKET, insertable = false, updatable = false)
public List<B> bs;
}
and B class
#Entity
#Table(name = "BTABLE")
public class B implements Serializable {
public static final String DB_ID = "BID";
public static final String DB_MARKET = "BMARKET";
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "id", column = #Column(name = DB_ID)),
#AttributeOverride(name = "market", column = #Column(name = DB_MARKET))
})
public BIdClass id;
}
Each entities might be listed, but using that #OneToMany relation does throw the following error
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory'
Caused by: org.hibernate.AnnotationException: Unable to map collection fr.zzz.domain.A.bs
Caused by: org.hibernate.AnnotationException: referencedColumnNames(AMARKET) of fr.zzz.domain.A.bs referencing fr.zzz.domain.B not mapped to a single property
An A entity relates to multiple B on A.AMARKET = B.BMARKET
You are having this issue because there is be a possibility that composite keys (AID,AMARKET) and (BID,BMARKET) will not be unique when doing a join on keys AMARKET = BMARKET. Therefore you are getting the error not mapped to a single property. Please bear with me, use the following sample data to analyze the issue;
For table A
AID AMARKET
1 1
2 1
3 2
For table B
BID BMARKET
1 1
2 2
3 2
The above scenario is absolutely possible (at least on a database level) and just using AMARKET and BMARKET to make the join #OneToMany is not possible. What is possible though is to use #ManyToMany, this will immediately solve the issue if the table structures are correct.
But what if it is required to use #OneToMany due to some business constraint. Then you must update the table B to include A.AID and add a foreign key constraint to ensure data integrity. Then only the result set will be valid for the relationship #OneToMany. And the join will be as follows;
#OneToMany
#JoinColumn(name = B.DB_AID, referencedColumnName = DB_ID)
#JoinColumn(name = B.DB_MARKET, referencedColumnName = DB_MARKET)
public List<B> bs;
In B:
#Entity
#Table(name = "BTABLE")
public class B implements Serializable {
public static final String DB_ID = "BID";
public static final String DB_MARKET = "BMARKET";
public static final String DB_AID = "AID";
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "id", column = #Column(name = DB_ID)),
#AttributeOverride(name = "market", column = #Column(name = DB_MARKET))
})
public BIdClass id;
#Column(name = DB_AID)
private Long aid; // assuming aid is a Long
}
Now the join is being done on the composite primary key of A.
I have a couple of entities mixed with Embedded attributes. I've been able to override embedded column names using #AttributeOverrides and #AttributeOverride but it doesn't seem to work for nested entities.
see below:
#Entity
#Table(name = "branch")
public class Branch {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String phoneNumber;
private String email;
#Embedded
#AttributeOverrides({
#AttributeOverride( name = "streeNumber", column = #Column(name = "branch_street_number")),
#AttributeOverride( name = "streetName", column = #Column(name = "branch_street_name")),
#AttributeOverride( name = "lga", column = #Column(name = "branch_lga_id")),
#AttributeOverride( name = "city", column = #Column(name = "branch_city"))
})
private Location location;
Location entity:
#Embeddable
public class Location {
private String streetNumber;
private String streetName;
#ManyToOne
#JoinColumn(referencedColumnName = "id", nullable = false)
private LocalGovernmentArea lga;
private String city;
the lga column still generates as lga_id instead of branch_lga_id
Is there any solution to this?
Thank you.
Found a solution. I can override the entity name using #AssociationOverrides
#Embedded
#AttributeOverrides({
#AttributeOverride( name = "streetNumber", column = #Column(name = "branch_street_number")),
#AttributeOverride( name = "streetName", column = #Column(name = "branch_street_name")),
#AttributeOverride( name = "lga", column = #Column(name = "branch_lga_id")),
#AttributeOverride( name = "city", column = #Column(name = "branch_city"))
})
#AssociationOverrides({
#AssociationOverride(
name = "lga", joinColumns = #JoinColumn(name = "branch_lga_id"))
})
private Location location;
That does the trick!
As you can see in this topic, JPA how to supply inheritance relationships for embeddable objects, I have embeddable classes for each unit type.
I have an entity A with following definition
#Entity
#Table(name = "class_x")
#PrimaryKeyJoinColumn(name = "class_x_id")
public class ClassX {
/**
*
*/
private static final long serialVersionUID = -4924441463209260247L;
public ClassX () {
super();
}
#Column
private String name;
/** rated Apparent Power. */
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "value", column = #Column(name = "aColumn_value")),
#AttributeOverride(name = "unit", column = #Column(name = "aColumn_unit")),
#AttributeOverride(name = "multiplier", column = #Column(name = "aColumn_multiplier"))
})
private Temperature aColumn;
/** Reference short circuit voltage. */
#AttributeOverrides({
#AttributeOverride(name = "value", column = #Column(name = "bColumn_value")),
#AttributeOverride(name = "unit", column = #Column(name = "bColumn_unit")),
#AttributeOverride(name = "multiplier", column = #Column(name = "bColumn_multiplier"))
})
private Percentage bColumn;
}
I configured hibernate to create tables from my entities with create-drop.class_x table is created , it has only have class_x_id and name columns. Columns of embeddable object are not created. I could not find any problem/solution about this.
The problem is about my embeddable class definitions. I was using inherited embeddable objects. This does not work in JPA. Now, I have defined base abstract unit as MappedSuperClass. super classes are embeddable. So as a result embeddable objects are inherited from #mappedsuperclass. Actually I read that Jpa also does not suport this. But it works now .
With JPA annoations, I want to reuse same embedded object like this :
#Entity
public class User {
#Embedded
public Address homeAddress;
#Embedded
public Address workAddress;
}
#Embeddable
public class Address {
public String code;
public String city;
...
}
I can specify SQL column names with #AttributeOverrides, #AttributeOverride and #Column, but it's verbos. Is it possible to specify just a prefix to add to each column for homeAddress and workAddress ?
Thanks,
Xavier
If you would like to use multiple same Embedded class. You have to do #AttributeOverrides for all columns.
Try as below;
Reference JPA AttributeOverrides
#Embeddable
public class Address {
private String state;
#Column(name = "zip_code")
private String zip;
}
#Entity(name = "Employee")
public class Employee implements Serializable {
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "state", column = #Column(name = "province_1")),
#AttributeOverride(name = "zip", column = #Column(name = "postal_code_2"))
})
private Address address_1;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "state", column = #Column(name = "province_2")),
#AttributeOverride(name = "zip", column = #Column(name = "postal_code_2"))
})
private Address address_2;
}
My suggestion, if there are one or more Embedded value in your Entity. Try to use #CollectionTable.
#CollectionTable(name = "EMPLOYEE_ADDRESS", joinColumns = #JoinColumn(name = "ADDRESS_ID"))
private List<Address> addressList;
Reference JPA CollectionTable
adding this works for me (i'm using hibernate as JPA provider though)
<property name="hibernate.implicit_naming_strategy" value="org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl" />
In 2022 (after 9 Years) #zaw-than-oo -s answer is still valid :-( .
This "Duplicate" answer is for reference if sombody wants to improve jpa-embedded.
Here is a working crossplatform example based on #zaw-than-oo -s answer
with verbose java-jpa and easy to use android-room
#androidx.room.Entity
#javax.persistence.Entity
#javax.persistence.Inheritance(strategy = javax.persistence.InheritanceType.SINGLE_TABLE)
public class AppHardware {
#javax.persistence.Id
#javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.AUTO)
#androidx.room.PrimaryKey(autoGenerate = true)
private int id;
// max is embedded without column renaming
#androidx.room.Embedded
#javax.persistence.Embedded
private ProfileCommon max;
// min is embedded with column renaming
#androidx.room.Embedded(prefix = "min")
#javax.persistence.Embedded
#javax.persistence.AttributeOverrides({
// Verbose: every persisted property in ProfileCommon needs an entry
// see https://stackoverflow.com/questions/12912063/jpa-multiple-embedded-fields-with-prefix
#AttributeOverride(name = "added", column = #Column(name = "minadded")),
#AttributeOverride(name = "apkName", column = #Column(name = "minapkName")),
#AttributeOverride(name = "versionCode", column = #Column(name = "minversionCode")),
#AttributeOverride(name = "versionName", column = #Column(name = "minversionName")),
#AttributeOverride(name = "size", column = #Column(name = "minsize"))
})
private ProfileCommon min;
// getter and setter onmitted
}
#javax.persistence.MappedSuperclass
public class ProfileCommon {
private long added; // date
private String apkName;
private long versionCode;
private String versionName;
private long size;
// getter and setter onmitted
}