Overriding Embedded XML attribute in Entity that has no Database field mapping - java

I have the following embeddable classes.
Email:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Email")
#Embeddable
public class Email {
#XmlElement(required = true, nillable = true, name = "etype")
private String type;
private String address;
private String source;
// getters and setters
}
Address:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "MyAddress")
#Embeddable
public class MyAddress {
#XmlElement(required = true, nillable = true, name = "atype")
private String type;
private String zip;
// getters and setters
}
Entity that embeds both of the above.
#Entity
#Table(name = "PERSON")
public class MyPerson {
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "address", column = #Column(name = "E_ADDR")),
#AttributeOverride(name = "source", column = #Column(name = "E_SRC")) })
private Email email;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "zip", column = #Column(name = "ZIP")),
private MyAddress address;
}
There is a type field in both of the Embeds. But that field is not mapped to any Database field. But I need to override it. Because hibernate throws error when running. (Compiles fine).
Is there a way to override type or give a different name to the embedded and non-database-mapped field?
Note: I am looking for solution with the field name intact. Because I cannot simply change the name.
This would also answer my another question, embedding the same Embeddable again overriding all attributes. For instance, I want to include Home Address, Business Address, etc with same MyAddress embed.

All your fields which are not mapped to the database should be annotated with #Transient. In this case Hibernate will not try to map the field type and will not complain for a duplicate name.

Related

Spring Boot Hibernate creates tables with wrong names

When I run my project, the Hibernate creates automatically tables with wrong names.
I have two tables User and Role and also three classes:
abstract class IdField.java:
#Entity
public abstract class IdField {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
//constructors and getters setters
User.java class:
#Entity
#Table(name = "user", schema = "quiz_app")
public class User extends IdField{
#Column(name = "user_name")
private String userName;
#Column(name = "password")
private String password;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#ManyToMany(fetch = FetchType.EAGER)
private Collection<Role> roles = new ArrayList<>();
//constructors and getters setters
and Role.java class:
#Entity
#Table(name = "role", schema = "quiz_app")
public class Role extends IdField{
#Enumerated(EnumType.ORDINAL)
#Column(name = "role_name")
private RoleName roleName;
//constructors and getters setters
And Hibernate creates two tables with wrong names as id_field and id_field_roles:
but I want table names as it is in #Table annotation like "user" and "role"
Get familiar with inheritance strategies:
https://thorben-janssen.com/complete-guide-inheritance-strategies-jpa-hibernate/
It seems to me you are looking for #MappedSuperclass
If you just want to share state and mapping information between your entities, the mapped superclass strategy is a good fit and easy to implement. You just have to set up your inheritance structure, annotate the mapping information for all attributes and add the #MappedSuperclass annotation to your superclass. Without the #MappedSuperclass annotation, Hibernate will ignore the mapping information of your superclass.
On top of that: If your shared part is only id field, as the name suggests, inheritance looks like overkill.

java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [XXX.title] on this ManagedType

In my front end part, I have a table in which I can sort its columns (asc,desc). These columns represent attributes of two entities Activity and Objective which have #OneToOne relation.
The front end sends to the web service the name of the attribute subject of sort. when the attribute's name equal to objective.title, I had this error :
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [objective.title] on this ManagedType [entity.Activity]
Below the code of two entities :
class Activity.java
#Entity
#Table(name = "ACTIVITY")
public class Activity{
#Id
#Column(name = "REFID")
private Long refid;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "PRINCIPAL_OBJECTIVE")
private Objective objective;
// getters + setters
}
class Objective.java
#Entity
#Table(name = "OBJECTIVE")
public class Objective{
#Column(name = "TITLE")
private String title;
#Column(name = "TYPE")
private Short objectiveType;
// getters + setters
}

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.

JPA Multiple Embedded fields with prefix?

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
}

AttributeOverride and CollectionOfElements, column annotattion is ignored

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"))
})

Categories