Embedded JPA Entities Under Spring Boot and Hibernate Naming
2 CommentsLast Updated on October 21, 2024 by jt
Embedded JPA Entities are nothing new to the JPA standard. By defining Embedded JPA Entities, you can define a common data type for your application. Unlike regular JPA Entities which generally follow a table per entity mapping strategy. Embedded JPA Entities are stored as additional columns in the underlying relational database table.
If you’re using Hibernate as your JPA provider under Spring Boot, and allowing Hibernate to generate the DDL for the database using the default Hibernate naming strategy provided in the default Spring Boot autoconfiguration, you may encounter exceptions when using more than Embedded JPA Entity property in a parent JPA Entity. The Spring Boot default Hibernate naming strategy does not support this. (As of Spring Boot 1.3.0)
I was coding an example for my Spring Core online course, when I ran into this problem.
Here is the exception I received when starting up the embedded Spring Boot Tomcat container.
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: guru.springframework.domain.Customer column: address_line1 (should be mapped with insert="false" update="false") at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:709) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final] at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:731) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final] at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:727) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
In a nutshell, the Hibernate Mapping Exception is caused by non-unique column names for mapped to the properties of the Embedded JPA Entities.
One solution to this issue could be to use the @AttributeOverride
 annotation to manually provide unique column names for my Embedded JPA Entities. But, looking at the examples in the Oracle documentation, this becomes kind of an eyesore of Annotations on my classes.
AttributeOverride Annotation Example
@Entity public class Customer { @Id protected Integer id; protected String name; @AttributeOverrides({ @AttributeOverride(name="state", column=@Column(name="ADDR_STATE")), @AttributeOverride(name="zipcode.zip", column=@Column(name="ADDR_ZIP")) }) @Embedded protected Address address; ... }
A More Elegant Solution to Support Multiple JPA Embedded Entities
Looking to escape annotation hell, and Google being my friend, I found a solution on Stackoverflow was to use Hibernate’s DefaultComponentSafeNamingStrategy
. This will prepend the column name to the property names of the JPA Embedded Entities.
Spring Boot by default uses SpringNamingStrategy, which overrides HIbernate’s ImprovedNamingStrategy, but adds better support of foreign key naming.
To override the default Spring Boot Hibernate Naming Strategy, you just need to provide the full class name of the Hibernate Naming strategy you wish to use in your Spring Boot application.properties
as follows:
application.properties
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.DefaultComponentSafeNamingStrategy
By overriding the default Spring Boot Hibernate naming strategy I was able to reduce the annotation hell of my JPA mappings.
Embedded JPA Entities Under Spring Boot Example
Here is the complete example of Embedded JPA Entities from my Spring Core Course.
Embedded JPA Entity
Address.java
package guru.springframework.domain; import javax.persistence.Embeddable; @Embeddable public class Address { private String addressLine1; private String addressLine2; private String city; private String state; private String zipCode; public String getAddressLine1() { return addressLine1; } public void setAddressLine1(String addressLine1) { this.addressLine1 = addressLine1; } public String getAddressLine2() { return addressLine2; } public void setAddressLine2(String addressLine2) { this.addressLine2 = addressLine2; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getState() { return state; } public void setState(String state) { this.state = state; } public String getZipCode() { return zipCode; } public void setZipCode(String zipCode) { this.zipCode = zipCode; } }
JPA Entity
Customer.java
package guru.springframework.domain; import javax.persistence.*; /** * Created by jt on 11/14/15. */ @Entity public class Customer implements DomainObject { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; @Version private Integer version; private String firstName; private String lastName; private String email; private String phoneNumber; @Embedded private Address billingAddress; @Embedded private Address shippingAddress; @Override public Integer getId() { return id; } @Override public void setId(Integer id) { this.id = id; } public Integer getVersion() { return version; } public void setVersion(Integer version) { this.version = version; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPhoneNumber() { return phoneNumber; } public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; } public Address getBillingAddress() { return billingAddress; } public void setBillingAddress(Address billingAddress) { this.billingAddress = billingAddress; } public Address getShippingAddress() { return shippingAddress; } public void setShippingAddress(Address shippingAddress) { this.shippingAddress = shippingAddress; } }
Spring Boot Configuration
- Spring Boot 1.3.0.RELEASE
- H2 Database (Embedded using Spring Boot defaults)
- Hibernate 4.3.11 (via Spring Boot Starter Data JPA Maven dependency)
application.properties
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.DefaultComponentSafeNamingStrategy
Resulting Database Table
Conclusion
I personally feel JPA has enough annotations to make it work. Some days it feels like we traded XML hell for annotation hell. (The more I work with JPA, the more I miss working with GORM in Grails.)
I’m glad I found this cleaner solution for supporting multiple Embedded JPA Entities without wearing out my fingers typing annotations. Hope this helps you along the way!
Lana Rohdes
Java is everywhere and evergreen cross platform programming language with it’s diversity. Jvm is the most powerful element behind the java, which makes it more powerful. there is another technology based on java called aem so to read more about aem visit:
what is aem
John
How to do update query on this if I want to update some address fields??