When I use an #Embedded class in Java / JPA, how is this mirrored in the database?
For instance, if I have an #Entity User with an #Embedded #Entity Address:
#Entity
#Table(name="User")
public class User {
#ID
private Long u_id;
#Column(name="name")
private String name;
#Embedded
private Address address;
}
#Entity
#Table(name="Address")
public class Address {
#Column(name="street")
private String street;
#Column(name="zip")
private String zipcode;
....
}
Will this result in two tables referenced using a foreign key or will there be just ONE table containing aLL columns of both entities (what would be not well normalized)?
First of all, you can't use #Embedded and #Entity together. Your class Address must be marked as #Embeddable.
Then.
If you are using #Embedded and #Embeddable - it will one table with columns of both classes.
If you are using #Entity and #OneToMany (or #OneToOne, #ManyToOne, #ManyToMany) it will be different tables;
Related
This title is closest I could get to what I am trying to do. :)
Let's start with how it should look in database (tables with columns):
Email
- id : PK
- email : String
CompanyEmail
- email_id : FK
- company_id : FK
PersonEmail
- email_id : FK
- person_id : FK
Company
- id : PK
Person
- id : PK
Now let's look at model:
#Entity
public class Company
{
#Id
#GeneratedValue
private Long id;
#OneToMany
private List<CompanyEmail> emails = new ArrayList<>();
}
#Entity
public class Person
{
#Id
#GeneratedValue
private Long id;
#OneToMany
private List<PersonEmail> emails = new ArrayList<>();
}
// Should this be #Entity? Maybe #MappedSuperclass? What strategy to use to be able to extend it?
public class Email // This is base email class/entity
{
#Id
#GeneratedValue
private Long id;
#javax.validation.constraints.Email
private String email;
}
#Entity
public class PersonEmail // This needs to somehow extend Email, so all Email data is also accessible here.
{
#ManyToOne
private Person person;
}
#Entity
public class ComanyEmail // This needs to somehow extend Email, so all Email data is also accessible here.
{
#ManyToOne
private Company company;
}
Now my question is - is it possible in Hibernate (latest) to achieve such structure?
Key points when designing above (what drove me):
Keep ALL emails in one table, for sanity checks (uniqueness).
Have smallest database footprint - above gives just one table for Email and then 2 join tables with 2 FK.
Still be able to keep the model design Hibernate-friendly (basically don't use any special queries, just JPA annotations). This means that both Company and Person can easily LAZY load their specific emails (subclasses), and also those subclassed emails can correspond to them (PersonEmail to Person, and CompanyEmail to Company) - making model bidirectional.
Note: I've also considered creating something like Contactable base class for Comapny and Person which would have Email list, but that doesn't suit my needs (neither class per table, nor same table designs).
My question is - is it possible? Even if I don't get answer with example based on classes I gave, just the fact will give me hope and I will find it.
Edit 1
Should I maybe use discriminator in Email table and then keep FK there?
Email
- id : PK
- email : String
- companyOrPerson : FK
- discriminator : Decides what FK points at
Company
- id : PK
Person
- id : PK
Here I am grabbing straws - I have no idea if such thing is possible, can discriminator decide about parent table (companyOrPerson)? Does bidirectional work here (like I mentioned, maybe I should make some base class Contactable)?
I am open to suggestions on how to do it well.
Favour composition over inheritence. It tends to simplify things.
If you had main entities of
Person
Company
Email
Then a couple of composites
PersonEmail (contains a Person and an Email)
CompanyEmail (contains a Company and an Email)
Would that not fit what you need?
e.g.
#Entity
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#OneToMany
#JoinColumn(name="person_id")
private List<PersonEmail> emails = new ArrayList<>();
}
#Entity
public class Company {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#OneToMany()
#JoinColumn(name="company_id")
private List<CompanyEmail> emails = new ArrayList<>();
}
#Entity
public class Email {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#javax.validation.constraints.Email
private String email;
}
#Entity
public class PersonEmail {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="person_id", referencedColumnName="id")
private Person person;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="email_id", referencedColumnName="id")
private Email email;
}
#Entity
public class CompanyEmail
{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="company_id", referencedColumnName="id")
private Company company;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="email_id", referencedColumnName="id")
private Email email;
}
I'm trying to create an entity, User who has two Addresses, a home address and a work address.
Instead of storing the address info directly in the User class, I want to normalize it and store all addresses in one table and then link them to the user. Like so:
#Entity
public class User {
#Id
private Integer id;
private Address homeAddress;
private Address workAddress;
// getters and setters
}
#Entity
public class Address {
#Id
#GeneratedValue (strategy = GenerationType.AUTO)
private Integer id;
private String streetNumberAndName;
private String apartmentOrSuiteNumber;
private String city;
private String state;
private String zipcode;
// getters and setters
}
How do I do this using Spring JPA? I understand this is a ManyToOne relationship but I'm not sure how to map two ManyToOne relationships to one entity. Is this even possible?
Any help much appreciated.
Thanks!
That's really simple. Just map your User class like:
#Entity
public class User {
#Id
private Integer id;
#ManyToOne
#JoinColumn(name = "fk_home_address")
private Address homeAddress;
#ManyToOne
#JoinColumn(name = "fk_work_address")
private Address workAddress;
// getters and setters
}
The table structure would be like this:
user(id, fk_home_address, fk_work_address)
Note that this is a unidirectional relationship.
The best place to look for examples if you want to learn more is here.
If you're looking for a bidirectional relation, learn here.
private Integer id;
private Address homeAddress;
private Address workAddress;
with first situation, your structure table will be
user(id,home_address_id,work_address_id)
You might consider about second structure
private Integer id;
private List<Address> userddress;//using one to many
your table structure will be
address(id,user_id)
It depend how do you want to organize the structure.
Hibernate Mapping
How to implement such a code?
Each company has two properties, they are company name and estimated annual earnings.
There are two types of companies: 1- Main company, 2 - Subsidiary company.
The company can belong only to one company but can have a few child companies.
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String companyName;
private double estimatedAnnualEarnings;
private Company company; // here need to do a #OneToOne
private List<Company> subsidiaryCompany; // here need to do a #OneToMany
}
In your Implementation you should use :
The #Entity annotation in your class level, so the entity can be persisted to database.
The #Column annotation with the companyName and estimatedAnnualEarnings properties, so they can be persisted as columns in the database.
#ManyToOne annotation with the company field, so it can be mapped with a self-reference relationship.
The same goes with the subsidiaryCompany List which needs to be mapped with #OneToMany annotation to have a relationship too.
This is how should be your code:
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column
private String companyName;
#Column
private double estimatedAnnualEarnings;
#ManyToOne(cascade={CascadeType.ALL})
#JoinColumn(name="mainCompanyId")
private Company mainCompany;
#OneToMany(mappedBy="mainCompany")
private List<Company> subsidiaryCompanies;
//getters and setters goes here
}
Note
I changed the name of company field to mainCompany and
subsidiaryCompaniy to subsidiaryCompanies for better readability
and to make it fit the logic better.
If you want to give your entity a different name in the database you
should use #Table(name="differentName") in the class level with
#Entity annotation, the smae thing with the columns you can add
name property to the #Column annotation i.e
#Column(name="company_name") if you want different names.
I am studying for the Spring Core certification and I have some doubts about how works the Embeddables JPA class.
So I have this example:
1) A Customer entity class that map the T_CUSTOMER DB table
#Entity
#Table(name= “T_CUSTOMER”)
public class Customer {
#Id
#Column (name=“cust_id”)
private Long id;
#Embedded
#AttributeOverride
(name="postcode", column=#Column(name="ZIP"))
private Address office;
…
…
…
}
2) An Address entity class that simply represent an address (and that not map any DB table):
#Embeddable
public class Address {
private String street;
private String suburb;
private String city;
private String postcode;
private String country;
}
As you can see in the Customer entity class there is this field:
#Embedded
#AttributeOverride
(name="postcode", column=#Column(name="ZIP"))
private Address office;
What exactly are the rules and the meaning of the #Embedded and #AttributeOverride annotations?
I think that this works in the following way:
1) Some column of the T_CUSTOMER DB table are mapped not directly into the Customer entity class but are mapped and stored into the field of the Address class that is a field of the the Customer class.
2) The table column named ZIP is mapped with the postcode property of the Address class.
Is it right or am I missing something?
Tnx
I am a JPA newbie. I have what I believe is a very simple example, but I am having trouble implementing it. Basically, I have an entity that I want to "reuse" in other entities.
I have a User having two fields - Home and Office. Each of these in turn refers to an Address, like so:
#Entity
public class User extends Model {
#Column
private String name;
#OneToOne
private Home home;
#OneToOne
private Office office;
}
#Entity
public class Home extends Model {
#OneToOne
private Address address;
#OneToOne(mappedBy="home")
private User user;
// ...
}
#Entity
public class Office extends Model {
#OneToOne
private Address address;
#OneToOne(mappedBy = "office")
private User user;
// ...
}
#Entity
public class Address extends Model {
#Column
private String line1;
#Column
private String line2;
#Column
private String city;
/*
* Is this possible? Being owned by two entities?
*/
#OneToOne(mappedBy="address")
private Home home;
#OneToOne(mappedBy="address")
private Office office;
// ...
}
How do I achieve this?
EDIT
I was originally running into this exception:
org.hibernate.AnnotationException: #OneToOne or #ManyToOne on example.Home.address references an unknown entity: example.Address
It turns out that one of the classes had imported the #Entity annotation from org.hibernate instead of from javax.persistence. After fixing this, the original exception went away (this helped me pin point the issue)
However, I now have a new exception:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: example.User.home -> example.Home
I don't quite understand this exactly. Should I call save the Home object before I do so on the User instance?
I fixed the problem by adding cascade=CascadeType.ALL attribute to all the #OnetoOne annotations in all the entities.
Without this annotation, I would have to save each owned entity before saving the owning one. For example, I'd have to save a Home before saving the User that the home belongs to.
Adding the cascade attribute to the annotation takes care of this for you. You only need to save the User and the Home and Addresses are automatically saved for you.
This post helped me solve the problem.
Can you make only 1 class User:
#Entity public class User extends Model
{
#Column
private String name;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "line1", column = #Column(name="column11")),
#AttributeOverride(name = "line2", column = #Column(name="column21")),
#AttributeOverride(name = "city", column = #Column(name="column31"))
})
private Address homeAddress;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "line1", column = #Column(name="column12")),
#AttributeOverride(name = "line2", column = #Column(name="column22")),
#AttributeOverride(name = "city", column = #Column(name="column32"))
})
private Address officeAddress;
// .. setters, getters
}
#Embeddable
public class Address extends Model
{
private String line1;
private String line2;
private String city;
// .. getters, setters
}