EDIT
Hi all.
WHAT I TRY TO GET
I want a table that represents a list of personal details, this table is called PERSON and must have this columns:
ID NAME SURNAME STREET ID_CITY
Well, i already have a table in my db that contains all municipalities of my country, this table is called MUNICIPALITY and has this columns:
ID COUNTRY PROVINCE NAME
I wrote the class Municipality.java to reepresents the table above, this class is:
#Entity
public class Municipality implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String country;
private String province;
private String name;
public Municipality() {
}
...
So, i thought a good implementation of PERSON table is to write an emebedded class Address.java that contains some info about "the address" of the person to embed in the Person.java class.
Address.java simpy contains a street and a municipality object:
#Embeddable
public class Address implements Serializable {
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="ID_CITY")
#Cascade(CascadeType.SAVE_UPDATE)
private Municipality municipality;
#Column(length=45)
private String street;
public Address() {
}
...
Address is simply emebedded in Person.java class:
#Entity
public class Person implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String name;
private String surname;
#Embedded
private Address address;
public Person() {
}
...
Well, if now i save a Person instance:
Person p=new Person();
Address a=new Address();
a.setStreet("bla bla");
Municipality mun=new Municipality();
mun.setName("ZZZ");
a.setMunicipality(mun);
p.setAddress(a);
session.save(p);
All works fine, hibernate create a table PERSON exactly how i want.
THE PROBLEM
But now, i want also the list of all address associate to a municipality object, so i do not necessary do a "select" for this.
I thougth i good thing is to add a collectin of Address to Municipality:
#CollectionOfElements
private List<Address> addressList;
But now if i save a person, hibernate create a new table MUNICIPALITY_ADDRESSLIST:
MUNICIPALITY_ID ID_CITY STREET
The table above contains always NULL values... so is totally useless, and i don't want it!
I read some docs about #CollectionOfElements and #OneToMany and i find i can define a join table. So i thought to set PERSON like join table...
#CollectionOfElements
#JoinTable(name="PERSON")
private List<Address> addressList;
First insert of Person works good, i obtain the PERSON table how i want, and MUNICIPALITY_ADDRESSLIST is no longer created.
But when i make the second insert, hibernate add a column to PERSON table: MUNICIPALITY_ID
ID STREET NAME SURNAME ID_CITY MUNICIPALITY_ID
MUNICIPALITY_ID contains always NULL values, so why this column is added?
Sure my implementations is very bad, im new to hibernate and JPA, so where is my main mistake?
I hope now is more understandable... sorry for my bad english.
Thanks.
blow, if you do not really want MUNICIPALITY_ADDRESSLIST Table, you should use #Transient instead of #CollectionOfElements
public class Municipality {
#Transient
private List<Address> addressList;
}
If you use #CollectionOfElements, Hibernate will always create MUNICIPALITY_ADDRESSLIST Table. If #Transient does not fullfil your needs, provide more info about what you want to get
UPDATE
According to info provided by yourself
Person has an #Embedded Address which has a #ManyToOne relationship with Municipality
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Embedded
private Address address;
}
Now, our Address
/*
* #Embeddable class MUST implements Serializable
*/
#Embeddable
public class Address implements Serializable {
#ManyToOne
/*
* If you are using Hibernate INSTEAD OF JPA
* Prefer to use #Cascade Hibernate annotation, as shown bellow,
* instead of JPA cascade Attribute
*
* Also notice it can not be null - see nullable Attribute
*/
#Cascade(CascadeType.SAVE_UPDATE)
#JoinColumn(name="MUNICIPALITY_ID", nullable=false)
private Municipality municipality;
}
And Municipality (without ANY List of Address or Person)
public class Municipality {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
}
As shown above, your DB structure should looks like
PERSON
ID
MUNICIPALITY_ID
MUNICIPALITY
ID
But whether Municipality has a List of Person as follows
public class Municipality {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#OneToMany
private List<Person> personList;
}
You will get The same structure shown above
But you have a bi-directional relationship Person can see Municipality (Through Address class) and Municipality can see Person (Through List of Person)
Now let's see The following scenario
Person person = new Person();
Municipality municipality = new Municipality();
Address address = new Address();
address.setMunicipality(person);
person.setAddress(address);
session.save(person);
You will see
INSERT INTO PERSON blah, blah, blah...
Because of #Cascade(SAVE_UPDATE)
INSERT INTO MUNICIPALITY blah, blah, blah...
But because of The bi-directional relationship, You should set up which property is The owner of The relationship. Owner of The relationship ? What is that ?
Suppose
public class A {
#ManyToOne
private B b;
}
And
public class B {
#OneToMany
#JoinColumn(name="B_ID")
#Cascade(SAVE_UPDATE)
private List<A> aList;
}
And you do
/*
* a.getB() is null, right ?
A a = new A();
B b = new B();
b.getAList().add(a);
SQL outputs
/* Let's suppose B_ID generated value is 5
INSERT INTO B
/*
* because of #Cascade(SAVE_UPDATE)
INSERT INTO A (B_ID) VALUES(5)
But because a.getB() is null
You will also see
UPDATE A SET B_ID = null
It explains why you should use mappedBy attribute when using a bi-directional relationship.
I'm not sure what the expected result is exactly (I'm too lazy to reproduce the case) but you should be able to derive the following sample:
#org.hibernate.annotations.CollectionOfElements(targetElement = java.lang.String.class)
#JoinTable( name="PHONE",joinColumns = #JoinColumn(name="STUDENT_ID"))
#Column(name="PHONE_NO")
public Set<String> getPhones() {
Finally i think to find a solution... i do this:
#Entity
public class Municipality implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String country;
private String province;
private String name;
#OneToMany(mappedBy="address.municipality")
private List<Person> persons;
public Municipality() {
}
...
I have not changed other classes.
Is this correct or work only by chance?
Thanks.
Related
Let's say I have two entities:
#Entity
public class Phone {
#Id
private Long id;
private String number;
}
#Entity
public class Person {
#Id
private Long id;
private String name;
}
The relationship between a person and a phone is one to one.
How could I access only the phone's number in the Person entity mapped by the phone's id
#Entity
public class Person {
#Id
private Long id;
private String name;
// ???
private String phoneNumber;
}
The reason for not mapping the whole entity is because in some more realistic entities there are too many properties.
I don't think you can, but something like this might be acceptable:
public class Person {
#OneToOne
#JoinColumn(name = "phone_id")
private Phone phone;
public String getPhoneNumber() {
return phone.getNumber();
}
}
Although you have mapped the whole object, not just the single property, you have only exposed the single property you want. The other stuff is hidden.
Alternatively, do it at the DB layer using a View:
create view person_with_phone as
select p.id, p.name,f.number
from person p
join phone f on f.id=p.phone_id
and then have an entity class to match the view. You'll need to turn off schema creation in your JPA implementation.
i'm curious about how HQL would assert equality between an entity instances.
Let's say I have a Entity called Person
#Entity
public class Person{
#Id
private Long id;
private String name;
}
and Department
#Entity
public class Department {
#Id
private Long id;
#ManyToOne
private Person person;
}
then it's fine if I do the following statement:
Query query = getSession().createQuery("from Department d where d.person = ?");
query.setProperty(0,new Person(1L));
but, what if I have an Embedded entity and no pk defined? like
#Embeddable
public class Adress {
private String email;
private String street;
private Long identifier;
}
#Entity
public class Person{
#Id
private Long id;
private String name;
#Embedded
private Address address;
}
would have any way so I could tell JPA to make it work:
Query query = getSession().createQuery("from Person p where p.address = ?");
query.setProperty(0,new Address(1L));
even though it's not exactly a primary key?
For sure i know i'd work if I tried p.adress.identifier, and then passed just the Long value, but the point is, can I tell JPA provider how it's gonna kind of 'implement' equality my way?
Thank you all
No, it is not supported and it would be difficult in general or would not make sense in some situations, like when there are collections in the Embeddable.
If you find that you need this often though, consider converting such Embeddables to custom user types. Then you can perform comparisons the way you described.
I want to create an #Index using hibernate. The index should consist of 3 fields, where some of them are nested within other entities. How can I achieve this?
#Table(name = "my_entity", indexes = {
#Index(name = "my_index", columnList = "id, person.firstname, person.lastname")
})
#Entity
public class MyEntity {
private Long id;
#ManyToOne
private Person person;
}
#Entity
public class Person {
#Id private Long id;
private String firstname;
private String lastname;
}
Result:
Caused by: org.hibernate.AnnotationException: Unable to create unique key constraint (id, person.firstname, person.lastname) on table my_entity: database column 'person.firstname', 'person.lastname' not found. Make sure that you use the correct column name which depends on the naming strategy in use (it may not be the same as the property name in the entity, especially for relational types)
at org.hibernate.cfg.Configuration.buildUniqueKeyFromColumnNames(Configuration.java:1684)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1459)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857)
... 46 more
columnList takes an array of column names. Not an array of entity properties.
So you just need to provide the names of the columns in the test table, as you would do if you defined the index using SQL.
Documentation
What you try to do here is to create an index over 2 different tables (MyEntity and Person), thus it's impossible.
If you want to do that you have to embed the Person class like this :
#Table(name = "test", indexes = {
#Index(name = "my_index", columnList = "id, person.firstname, person.lastname")
})
#Entity
public class MyEntity {
private Long id;
private Person person;
}
#Embeddable
public class Person {
private String firstname;
private String lastname;
}
Note that I've remplaced #Entity by #Embeddable on the Person class, this way in the database you'll have one table named Test with the columns id, firstname and lastname
I am using hibernate for database communication. I have one class as:
#Table(name="Person")
public class Person {
#Column(name="name")
private String name;
#OneToMany
#JoinColumn(name="Address_id)
private Set<Address> address;
... <other filed similarly>
}
Now I want to get this object using its primary key , but object should have only specific columns populate?
I tried using criteria and projection, it is returning a result but it is not mapped to Object i expect (Person Object)
Any idea how to solve this problem using hibernate query/criteria?
Thanks
The hibernate annotations used have to be correctly written.
Let assume that you have two Entities: Person and Address.
#Table(name="Person")
public class Person {
#Column(name="name")
private String name;
#OneToMany
#JoinColumn(name="Address_id")
private Set<Address> address;
... <other filed similarly>
}
And on the other side you have Address class
#Table(name="addresses")
public class Address{
#Column(name="name")
private String addressName;
#ManyToOne
private Person person;
}
Using this mapping when you have a method like:
public Encounter getAddressById(int idAddress) {
session = sf.getCurrentSession();
session.beginTransaction();
Address address = (Address ) session.load(Address .class, idAddress);
return address ;
}
This should return the address mapped with person; and the display of some columns will choosen by you because hibernate here returned the entire object.
I am trying to figure out the best way to accomplish a relationship in hibernate. I have a Customer object. Each customer has a technical contact, a billing contact, and a sales contact. Each type of contact has the exact same data structure (phone, email, address, etc).
My first thought was to create a Contact table, and then have three columns in the Customer table - sales_contact, billing_contact, technical_contact. That would make three distinct foreign key one-to-one relationships between the same two tables. However, I have found that this is very difficult to map in Hibernate, at least using annotations.
Another thought was to make it a many to many relationship, and have a type flag in the mapping table. So, any Customer can have multiple Contacts (though no more than three, in this case) and any Contact can belong to multiple Customers. I was not sure how to map that one either, though. Would tere be a type field on the map table? Would this attribute show up on the Contact java model object? Would the Customer model have a Set of Contact objects. or three different individual Contact objects?
So I am really looking for two things here - 1. What is the best way to implement this in the database, and 2. How do I make Hibernate map that using annotations?
It can be as simple as :
#Entity
public class Contact {
#Id
private String id;
private String phome;
private String email;
private String address;
// ... Getters and Setters
}
#Entity
public class Customer {
#Id
#GeneratedValue
private String id;
#ManyToOne
#JoinColumn(name = "ID")
private Contact billingContact;
#ManyToOne
#JoinColumn(name = "ID")
private Contact salesContact;
#ManyToOne
#JoinColumn(name = "ID")
private Contact technicalContact;
public Customer() {
}
// ... Getters and Setters
}
Now, if you want to make the difference between a BillingContact and a SalesContact at the object level, you can make Contact abstract, and implement it with each type of contact. You will have to annotate the parent class with #Inheritance to specify the inheritance strategy of your choice (SINGLE_TABLE sounds appropriate here, it will use a technical discriminator column - see http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#d0e1168).
How about using #OneToOne and just naming the #JoinColumn differently for each type:
#Entity
public class Contact {
#Id
private String id;
private String phone;
private String email;
private String address;
// ... Getters and Setters
}
#Entity
public class Customer {
#Id
#GeneratedValue
private String id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="billingContact_ID")
private Contact billingContact;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="salesContact_ID")
private Contact salesContact;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="technicalContact_ID")
private Contact technicalContact;
public Customer() {
}
// ....
}
For each row in Customer table should create three rows in Contact table