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 .
Related
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 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.
I am modeling a model class in java with Hibernate. But the table in the DB already exists and I want to use it.
The problem is that the table do not have an ID. Besides, the worse is that the tuple in the table are formed by four columns and their combination makes a "kind of identification/ID". Also, the columns are not FK to others tables.
Is there a way to model this with java, hibernate, and so on, without "fix/alter" the table?
Thanks
You have to define your class with an #EmbeddedId Example:
Your Entity that already exists :
#Entity
#Table(name = "ENTITY_EXAMPLE")
public class EntityExample {
// this is your new class Embeddable
#EmbeddedId
private EntityExampleId id;
//other fields
//geters y seters
//////////////////////////
}
And have to create other Class #Embeddable that contains your 4 columns of your composite ID
#Embeddable
public class EntityExampleId implements Serializable{
private static final long serialVersionUID = 1L;
#Column(name = "COLUMN1", nullable = false)
private Integer col1;
#Column(name = "COLUMN2", nullable = false)
private Integer col2;
#Column(name = "COLUMN3", nullable = false)
private Date col3;
#Column(name = "COLUMN4", nullable = false)
private Date col4;
//geters y seters
//////////////////////////
}
For more info see the references
reference 1
reference 2
reference 3
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
}
Code from my Entity Role
#Embedded
#LazyCollection(LazyCollectionOption.FALSE)
#CollectionOfElements
#JoinTable(name = "TEST_TABLE", joinColumns = #JoinColumn(name = "ROLE_ID"))
#AttributeOverrides({
#AttributeOverride(name = "code", column = #Column(name = "TSTCODE")),
#AttributeOverride(name = "work", column = #Column(name = "TSTWRK"))
})
private List<TestID> tests;
}
TestID class
#Embeddable
#AccessType("field")
public class TestID implements Serializable
{
private String code;
private String work;
// getters, setters
}
Get exception SQLGrammarException
Caused by: java.sql.SQLException: ORA-00904: "TESTS0_"."WORK": invalid identifier
Entity manager create query that trying get access to columns CODE and WORK instead of TSTCODE and TSTWRk that are in #Column annotations.
Any ideas?
Hibernate-annotation 3.2.1.ga
persistance-api 1.0
jboss-4.2.3.GA
UPDATE:
if rename fields in TestID class to table's columns names, then all warks normally
#Embeddable
#AccessType("field")
public class TestID implements Serializable
{
private String tstcode;
private String tstwks;
Remove #CollectionOfElements and just use #Embedded. I think you're double-mapping it as it is right now. Also, JPA's #ElementCollection is recommended over Hibernate's #CollectionOfElements.
Update: I kinda missed the fact that you're mapping a collection of components. You'll want to add an #Column(name="...") to the fields in your TestID to make it map correctly in that case. Even though it's somewhat contrary to the way embedded components are supposed to work, that's the only way I know to do it.
According to the hibernate recommendations (2.2.5.3.3. Collection of basic types or embeddable objects) you should use
#ElementCollection
#CollectionTable(name="TEST_TABLE", joinColumns = #JoinColumn(name = "ROLE_ID"))
#AttributeOverrides({
#AttributeOverride(name = "code", column = #Column(name = "TSTCODE")),
#AttributeOverride(name = "work", column = #Column(name = "TSTWRK"))
})