JPA Multiple Embedded fields with prefix? - java

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
}

Related

Spring JPA, OneToMany referencing part of primary key

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.

Spring boot and JPA override mapped entity name/id

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!

Is there any way to audit , with hibernate-envers, an entity having an #Embedded in its EmbeddedId

I have an entity BlocRecord having a composite code BlocRecordId, and in its composite code there is an #Embedded (relation code ManyToOne) pointing to another entiy Record and want to Audit the entity BlocRecord.
The entity BlocRecord
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "blocRecord")
#Access(value = AccessType.FIELD)
#Audited
public class BlocRecord {
#EmbeddedId
private BlocRecordId blocRecordId = new BlocRecordId();
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumns({
#JoinColumn(name = "record_identifier_", referencedColumnName = "identifier_", unique = false, nullable = false),
#JoinColumn(name = "record_recordType_", referencedColumnName = "recordType_", unique = false, nullable = false)})
#MapsId("record")
private Record record;
...
}
The id class BlocRecordID
#Embeddable
public class BlocRecordId implements Serializable {
#Embedded
private RecordId record;
#Column(name = "source_")
String source ;
#Column(name = "messageType_")
String messageType ;
The entity Record
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "records")
#Access(value = AccessType.FIELD)
#Audited
public class Record {
#EmbeddedId
private RecordId recordId = new RecordId();
#OneToMany(targetEntity = BlocRecord.class, fetch = FetchType.LAZY, mappedBy = "record")
private Set<BlocRecord> blocRecord = new java.util.HashSet<>();
...
}
The idClass of the entity Record
#Embeddable
public class RecordId implements Serializable{
#Column(name = "identifier_")
String identifier ;
#Column(name = "recordType_")
String recordType ;
}
Hibernate-envers fails when trying to generate the metadata of the field record in the embeddable BlocRecordId, the The flowing exception is thrown
org.hibernate.MappingException: Type not supported: org.hibernate.type.ComponentType
at org.hibernate.envers.configuration.internal.metadata.IdMetadataGenerator.addIdProperties(IdMetadataGenerator.java:121)
at org.hibernate.envers.configuration.internal.metadata.IdMetadataGenerator.addId(IdMetadataGenerator.java:230)
at org.hibernate.envers.configuration.internal.metadata.AuditMetadataGenerator.generateFirstPass(AuditMetadataGenerator.java:642)
at org.hibernate.envers.configuration.internal.EntitiesConfigurator.configure(EntitiesConfigurator.java:95)
at org.hibernate.envers.boot.internal.EnversServiceImpl.doInitialize(EnversServiceImpl.java:154)
at org.hibernate.envers.boot.internal.EnversServiceImpl.initialize(EnversServiceImpl.java:118)
at org.hibernate.envers.boot.internal.AdditionalJaxbMappingProducerImpl.produceAdditionalMappings(AdditionalJaxbMappingProducerImpl.java:99)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:288)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:83)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:417)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:86)
at org.hibernate.boot.MetadataSources.buildMetadata(MetadataSources.java:179)
Do you have any idea how to resolve the issue ?
Thanks
At the moment, Envers does not support the idea of nesting an embeddable inside an embeddable when we map the identifier columns like your example illustrates. The only valid mappings that Envers presently does support is if the attribute in the embeddable is a #ManyToOne or a #Basic type.
You can work around this problem but it involves being a bit more explicit and not using RecordId. What I mean is rewrite BlocRecordId to be the following:
#Embeddable
public class BlocRecordId implements Serializable {
#Column(name = "identifier_")
String identifier;
#Column(name = "recordType_")
String recordType;
#Column(name = "source_")
String source;
#Column(name = "messageType_")
String messageType;
#Transient
private RecordId recordId;
/** Helper method to assign the values from an existing RecordId */
public void setRecordId(RecordId recordId) {
this.identifier = recordId.getIdentifier();
this.recordType = recordId.getRecordType();
}
/** Helper method to get the RecordId, caching it to avoid multiple allocations */
public RecordId getRecordId() {
if ( recordId == null ) {
this.recordId = new RecordId( identifier, recordType );
}
return this.recordId;
}
}
I agree this is less than ideal but it does at least work around the current limitation of the code. I have gone added and added HHH-13361 as an open issue to support this. You're welcomed to contribute if you wish or I'll work in getting this supported for Envers 6.0.

AttributeOverrides annotation dont create right names of column

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;
}

Hibernate / JPA returns list of nulls

I have an entity like this:
#Entity
#Table(name = "MY_TABLE")
public class OrderPackage implements Serializable {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "pId", column = #Column(name = "P_ID")),
#AttributeOverride(name = "prId", column = #Column(name = "PR_ID")),
#AttributeOverride(name = "pin", column = #Column(name = "PIN")),
#AttributeOverride(name = "Id", column = #Column(name = "MN_ID"))
})
private PackageId id;
#Column(name = "MEM_ID")
private Long memberId;
.....
.....
// more fields and getters and setters
}
PackageId.class
#Embeddable
public class PackageId implements Serializable {
private String pId;
private String prId;
private String pin;
private Long Id;
// getters and setters
}
Mytable has the following fields matching for my query:
SELECT c FROM OrderPackage c where <condition> order by c.prId;
Matching records like this:
MN_ID MEM_ID P_ID PR_ID PIN ///some other fields
6217 402022795 A1000005361 B1000077615 (null)
6213 402022795 A1000005361 B1000037615 (null)
6218 402022795 A1000005361 B1000087615 (null)
6215 402022795 A1000005361 B1000057615 (null)
MN_ID is unique here.
I get the count 4. But all null rows in the list.
What could be causing this?
jBoss 4.2.x
JPA
Hibernate
ORACLE
UPDATE:
Is this still the case? (When there a null value for any one of the fields in the Composite Key, then it returns null for that entire row)
http://thelittlefellow.blogspot.com/2011/07/hibernate-gethibernatetemplatefind.html
https://hibernate.atlassian.net/browse/HHH-177

Categories