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
}
Related
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.
I have what I thought was a straight forward relation in JPA. Looks like this. CompanyGroup:
#Entity
#Table
public class CompanyGroup implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
#Column(name = "name")
private String name;
#JoinColumn(name = "companies")
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Company> companies;
}
Company:
#Entity
#Table
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "name")
private String name;
#JoinColumn(name = "users")
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<User> users;
#Id
#GeneratedValue
private Long id;
}
User:
#Entity
#Table
public class User {
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
#Column(name = "email")
private String email;
#Id
#GeneratedValue
private Long id;
}
I have omitted setters, getters, etc.
This is not working. I'm trying to save a CompanyGroup(Has 2 companies, each company has 2 users, all entities are unique) to a fully empty database.
I persist this using Spring-Data, accessed in a service like this:
#Service
public class ConcreteCompanyGroupService implements CompanyGroupService {
#Autowired
private CompanyGroupRepository repository;
#Transactional
#Override
public void save(CompanyGroup group) {
repository.save(Collections.singleton(group));
}
}
When I try to call this method I receive this:
org.postgresql.util.PSQLException: ERROR: syntax error at or near "User"
Position: 13
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2458)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2158)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:291)
Hopefully I have done something stupid that someone can find quickly. I don't know how to solve this.
EDIT:
The driver in my pom.xml:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4.1211</version>
</dependency>
Your entity maps across to a table name that is an SQL reserved keyword (User). Sadly for you, your chosen JPA provider does not automatically quote the table name identifier, and so you get exceptions when referring to the table.
Solution is either to quote the table name yourself in the #Table annotation, or change the table name to not be a reserved keyword. Alternatively use a JPA provider that auto-quotes such reserved keywords for you (e.g DataNucleus)
Solution 1: As Pascal mentioned, you have to escape the table name with backslash like:
#Entity
#Table(name="\"User\"")
public class User {
...
}
Solution 2: Rename your table's anme with another name (Users)
#Entity
#Table(name="Users")
public class User {
...
}
Solution 3: Add a suffix to the table's name:
#Entity
#Table(name="APP_User")
public class User {
...
}
Solution 4: Change the entity name, e.g. ApplicationUser
#Entity
public class ApplicationUser {
...
}
The reason
PostgreSQL as some reserved SQL Key Words. For example: ABORT, ALL, ARRAY, CACHE, CUBE, USER, ... Those tokens are in the SQL standard or specific to PostgreSQL
Use the #Table annotation or change your class name from User to something else as User is a reserved keyword in sql.
I'm new here and I'm also new in JPA! I developed a little JPA Application with the following entities:
A Customer(Name, Prename, ID) has got an Address(ZIP_Code, city, ID). The relation between these entities is ManyToOne (Customer's perpective) and OneToMany (Address' perspective).
The code (parts) are the following: (without getters/setters)
#Entity
public class AddressEntity implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String city;
private int zipCode;
#Column
#ElementCollection(targetClass=CustomerEntity.class)
private List<CustomerEntity> customers;
public AddressEntity() {}
#OneToMany(cascade=CascadeType.ALL,
fetch=FetchType.EAGER,
mappedBy="addressentity")
public List<CustomerEntity> getCustomers() {
return customers;
}
#Entity
public class CustomerEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int primaryKey;
private String preName, surName;
#ManyToOne(targetEntity=AddressEntity.class, cascade=CascadeType.ALL)
#JoinColumn(name="Address_ID")
private AddressEntity address;
public CustomerEntity() {}
}
Now i want to add two persons:
1) Bart Simpson 1234 Springfield
2) Homer Simpson 1234 Springfield
The problem is that in the address table Springfield appears twice. But that's not the sense of normalized database! How can I realize that an AddressEntity is only added when it does not exists before!
Thanks for answering,
eniac
Persisting new addresses
It is possible to have many AddressEntities with the same City and Zipcode. If you require that a given CustomerEntity is related to the same address, then you must use that specific entity. So use the same AddressEntity that you used for Bart for Homer also
CustomerEntity bart = new CustomerEntity();
//Set Barts’s fields.
CustomerEntity homer = new CustomerEntity();
//Set Homers’s fields.
AddressEntity simpsonsPlace = new AddressEntity();
//set 1234 and Springfield.
bart.setAdress(simpsonsPlace);
homer.setAdress(simpsonsPlace);
List<CustomerEntity> simpsons = new ArrayList<CustomerEntity>();
simpsons.add(bart);
simpsons.add(homer);
simpsonsPlace.setCustomers(simpsons);
yourEntityManager.persist(bart);
yourEntityManager.persist(homer);
Associating with existing addresses.
If you are creating homer sometime after bart then you will want to associate bart to an existing address. It maybe that you will pass in the address from the client. This will have the id field populated and will be a detached entity. You can use this entity with the em.merge() operation;
CustomerEntity homer = new CustomerEntity();
homer.setAdress(detachedAddress);
yourEntityManager.merge(homer);
A better way might be to form a compound PK of Zip/HouseNumber for example. This ensures that any AddressEntity with Zip/HouseNumber combination already in the DB will be treated as a detached object, and will of course only appear once in the DB.
Embeddables
You have both #ElementCollection and #OneToMany targeted to your CustomerEntity Entity.
As per JPA Spec 11.1.14;
The ElementCollection annotation defines a collection of instances of
a basic type or embeddable class.
It is therefore incorrect to target an entity with #ElementCollection.
You require a relation between your two entities, therefore, in you your AddressEntity you just need to use the #OneToMany and drop the #ElementCollection. You also need to map (with mappedBy) to the field of the target class and not the target class itself;
#Entity
public class AddressEntity implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String city;
private int zipCode;
#OneToMany(cascade=CascadeType.ALL,
fetch=FetchType.EAGER,
mappedBy="address") //Not addressentity
private List<CustomerEntity> customers;
public AddressEntity() {}
public List<CustomerEntity> getCustomers() {
return customers;
}
Alternatively you can define your CustomerEntity as an #Embeddable and not an #entity. In this way you can use #ElementCollection to target CustomerEntity from AddressEntity, but CustomerEntity would not be an entity in its own right and cannot be processed independently as it is dependent on AddressEntity
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
Suppose i want to have a composite key as street, city for purchase order entity.
Below is how i identify doing it,
#Embeddable
public class BillingAddress implements Serializable {
private String street;
private String city;
public BillingAddress(){
}
public BillingAddress(String street, String city) {
this.street = street;
this.city = city;
}
//with getters and setters
}
#Entity
#IdClass(BillingAddress.class)
public class PurchaseOrder {
public PurchaseOrder(BillingAddress billingAddress) {
street = billingAddress.getStreet();
city = billingAddress.getCity();
}
#Id
#AttributeOverrides({
#AttributeOverride(name = "street", column = #Column(name = "STREET")),
#AttributeOverride(name = "city", column = #Column(name = "CITY")) })
private String street;
private String city;
private String itemName;
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
}
I want to understand what is really #AttributeOverrides annotation do? Even i change to colomn name to something STREET1 i still see the table created with column name STREET. So what is column = #Column(name = "STREET")) doing here.
Also instead of constructore taking the BillingAddress i can have it like a field of PurchaseOrder class right like,
public class PurchaseOrder {
BillingAddress billingAddress;
}
In this case how this going to change?
Do i still need to have private String street; private String city; in PurchaseOrder?
Finally i read that using composite keys should be avoided in new data base system design which using composite primary key is applicable a situation where in order to map the legacy data base tables with out changing the data base table structure right? Is that statement a valid one?
//Edit question
Saving purchase order which billing address is in the field,
PurchaseOrder purchaseOrder = new PurchaseOrder();
purchaseOrder.setItemName("name");
BillingAddress billingAddress = new BillingAddress();
billingAddress.setCity("c1"); billingAddress.setStreet("s1"); purchaseOrder.setBillingAddress(billingAddress);
session.save(purchaseOrder);
There's are few question you asked, I tried to go through all of them and answer each one:
What does #AnnotationOverride do?
answer here: What does #AttributeOverride mean?
The second question is a bit unclear to me but I presume you're asking whether you have to include all the fields from the composite key in the PurchaseOrder class.
No, I don't think so. Here's an example I've put together real fast:
#Entity
#Table(name = "PURCHASE_ORDER")
public class PurchaseOrder{
#Id
private BillingAddress billingAddress;
//getters & setters
#Embeddable
public static class BillingAddress implements Serializable {
#Column(name = "street")
private String street;
#Column(name = "city")
private String city;
#Column(name = "itemName")
private String itemName;
//getters & setters
}
}
Don't worry about the syntax, just the structure. You can even add extra field into PurchaseOrder which isn't an id.
Should I use composite keys or not?
answer here: Should I use composite primary keys or not?
Well, your PurchaseOrder class does not extend from a mapped entity of any kind, and neither do the properties that you are (currently) applying the #AttributeOverrides to. So, there is nothing to actually override and your JPA provider is simply ignoring the annotations. What I think you are trying to do is define an embedded id for an entity, while overriding some of the column mappings for that id. You can do this with some modifications to your current code:
#Entity
public class PurchaseOrder {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "street", column = #Column(name = "BILLING_STREET")),
#AttributeOverride(name = "city", column = #Column(name = "BILLING_CITY")) })
private BillingAddress billingAddress;
private String itemName;
// Constructors, Getters/Setters
}
Note that I've changed the names of the overridden attributes, since with your current example, the embedded id name and overridden names are the same.