Reputation: 603
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
Upvotes: 39
Views: 20791
Reputation: 111
@AttributeOverride defeats purpose of multiple same Embeddable instances (contactAddress, billingAddress, balanceFrom, balanceTo, coordinates, gpsLocation etc..).
For each Embedded object you have to write down AttributeOverwrites for all fields of embeddable object.
Then it is then simpler just not to use @AttributeOverride and @Embedded and directly write down the fields with the prefixes - much lesser boilercode to maintain.
My current workaround: I am using my custom naming strategy by extending ImplicitNamingStrategyJpaCompliantImpl in hibernate 6.2.6 and overwriting transformAttributePath
allProperties.put("hibernate.implicit_naming_strategy", CustomImplicitNamingStrategy.class.getName());
@Override
protected String transformAttributePath(AttributePath attributePath) {
String fullPath = attributePath.getFullPath();
String withprefixMarker = "prefixed_";
String result = attributePath.getProperty();
if (fullPath.contains(withprefixMarker)) {
List<String> prefixes = new ArrayList<>();
String[] split = fullPath.split("\\.");
for (int i = 0; i < split.length; i++) {
if (split[i].startsWith(withprefixMarker)) {
String prefxFieldName = split[i].replace(withprefixMarker, "");
prefixes.add(prefxFieldName);
}
}
result = String.join("_", prefixes) + "_" + result.replace(withprefixMarker, "");
}
return result;
}
Now I can use same @Embedded with different magic prefix string ("prefixed_") and also nest it :
The table:
create table person
( ....
mail varchar(20),
street varchar(20),
city varchar(20),
previous_street varchar(20),
previous_city varchar(20),
billing_mail varchar(20),
billing_street varchar(20),
billing_city varchar(20),
billing_previous_street varchar(20),
billing_previous_city varchar(20),
shipping_mail varchar(20),
shipping_street varchar(20),
shipping_city varchar(20),
shipping_previous_street varchar(20),
shipping_previous_city varchar(20),
);
JPA classes
@Embeddable
@ToString
@Getter
@Setter
public class Contact implements Serializable {
String mail
@Embedded
Address address = new Address();
@Embedded
Address prefixed_previous = new Address();
}
@Embeddable
@ToString
@Getter
@Setter
public class Address implements Serializable {
String street;
String city;
//lots of more fields.... here
}
@Entity
@Table(name = "Person")
public class PersonDb {
....
@Embedded
Contact u1 = new Contact();
@Embedded
Contact prefixed_billing = new Contact();
@Embedded
Contact prefixed_shipping = new Contact();
}
Open Issue in GitHub since 2012 ("add a prefixing mechanism for @Embedded ): https://github.com/jakartaee/persistence/issues/23
Upvotes: 1
Reputation: 14755
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
}
Upvotes: 1
Reputation: 4135
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" />
Upvotes: 7
Reputation: 9935
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
Upvotes: 16