I shall cover the foreign and guid classes of generators in this post.
The foreign generator is used with objects that do not have their own key assigned to them.These use the identifier of another associated object. They are generally used along with <one-to-one> primary key association.
Consider the Person class.Each person has a PersonDetails object associated with it. The PersonDetails being in a one -to -one relationship with person, it can use the same identifier as the Person object it is associated to.
Java Classes:
On executing the create call:
Foreign:
The foreign generator is used with objects that do not have their own key assigned to them.These use the identifier of another associated object. They are generally used along with <one-to-one> primary key association.
Consider the Person class.Each person has a PersonDetails object associated with it. The PersonDetails being in a one -to -one relationship with person, it can use the same identifier as the Person object it is associated to.
Java Classes:
public class Person { private Integer id; private String name; private Integer age; private PersonDetails personDetails; //setter getter methods
}
public class PersonDetails { private Integer personId; private String fullName; private Person person; private String panCode; private BigDecimal weight; private BigDecimal height; //setter getter methods }
Mapping documents:
The mapping document for Person is more or less same as the previous example. Only the association with person details has been added here.
The mapping document for Person is more or less same as the previous example. Only the association with person details has been added here.
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.model"> <class name="Person" table="PERSON"> <id name="id" type="integer"> <column name="ID" /> <generator class="native" /> </id> <property name="name" type="string"> <column name="NAME" /> </property> <property name="age" type="integer"> <column name="AGE" /> </property> <one-to-one name="personDetails" class="PersonDetails" cascade="all"/> </class> </hibernate-mapping>
The cascade property is set to "all". This means both the entities are related in all the operations. Adding, updating and deleting Person will also do same on related PersonDetails entity.
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.model"> <class name="PersonDetails" table="Person_Details"> <id name="personId" type="integer"> <column name="PERSON_ID" /> <generator class="foreign"> <param name="property">person</param> </generator> </id> <one-to-one name="person" class="Person" constrained="true"> </one-to-one> <property name="fullName" type="string"> <column name="FULL_NAME" /> </property> <property name="panCode" type="string"> <column name="PAN_CODE" /> </property> <property name="weight" type="big_decimal"> <column name="WEIGHT" precision="10" /> </property> <property name="height" type="big_decimal"> <column name="HEIGHT" precision="10" /> </property> </class> </hibernate-mapping>
The foreign generator takes the name of the entity whose identifier will be used as a part of the <param> element. In this case the id attribute of Person object forms the identifier of the PersonDetails object. The one to one relation is also established here.
The sql tables are as follows:
The sql tables are as follows:
create table PERSON ( ID integer not null auto_increment, NAME varchar(255), AGE integer, primary key (ID) ) create table Person_Details ( PERSON_ID integer not null, FULL_NAME varchar(255), PAN_CODE varchar(255), WEIGHT numeric(10,2), HEIGHT numeric(10,2), primary key (PERSON_ID) ) alter table Person_Details add index PERSON_DETAILS_FK1 (PERSON_ID), add constraint PERSON_DETAILS_FK1 foreign key (PERSON_ID) references PERSON (ID)As can be seen from the sql, the primary key in the PERSON_DETAILS has a foreign key constraint on the associate PERSON table. To test the relationship the below code was used.
static SessionFactory sessionFactory; public static void main(String[] args) { Configuration configuration = new Configuration(); configuration = configuration.configure(); sessionFactory = configuration.buildSessionFactory(); testCreatePerson(); testLoadPersonDetails(); testPerson(); } public static void testCreatePerson() { Person p = new Person(); p.setAge(12); p.setName("Ramesh"); PersonDetails personDetails = new PersonDetails(); personDetails.setFullName("Ramesh Bhatia"); personDetails.setHeight(BigDecimal.ZERO); personDetails.setPanCode("Aku786I"); personDetails.setWeight(BigDecimal.ZERO); personDetails.setPerson(p); p.setPersonDetails(personDetails); Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); session.save(p); transaction.commit(); } public static void testPerson() { Session session = sessionFactory.openSession(); Person person = (Person) session.load(Person.class, 2); System.out.println(person.getPersonDetails().getFullName()); } public static void testLoadPersonDetails() { Session session = sessionFactory.openSession(); PersonDetails personDetails = (PersonDetails) session.load(PersonDetails.class, 2); System.out.println(personDetails.getPersonId()); System.out.println(personDetails.getPerson().getName()); }
On executing the create call:
2766 [main] DEBUG org.hibernate.event.def.AbstractSaveEventListener - executing insertions 2781 [main] DEBUG org.hibernate.engine.Cascade - processing cascade ACTION_SAVE _UPDATE for: com.model.Person 2781 [main] DEBUG org.hibernate.engine.Cascade - done processing cascade ACTION _SAVE_UPDATE for: com.model.Person 2781 [main] DEBUG org.hibernate.event.def.AbstractSaveEventListener - executing identity-insert immediately 2781 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister - Inse rting entity: com.model.Person (native id) 2781 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedSt atement (open PreparedStatements: 0, globally: 0) 2797 [main] DEBUG org.hibernate.SQL - insert into PERSON (NAME, AGE) values (?, ?) 2797 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - preparing statement 2875 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister - Dehy drating entity: [com.model.Person#<null>] 2875 [main] DEBUG org.hibernate.type.StringType - binding 'Ramesh' to parameter : 1 2875 [main] DEBUG org.hibernate.type.IntegerType - binding '12' to parameter: 2 2922 [main] DEBUG org.hibernate.id.IdentifierGeneratorFactory - Natively genera ted identity: 2 ... 2922 [main] DEBUG org.hibernate.event.def.AbstractSaveEventListener - generated identifier: 2, using strategy: org.hibernate.id.ForeignGenerator 2922 [main] DEBUG org.hibernate.event.def.AbstractSaveEventListener - saving [c om.model.PersonDetails#2] 2922 [main] DEBUG org.hibernate.engine.Cascade - done processing cascade ACTION _SAVE_UPDATE for: com.model.Person 2922 [main] DEBUG org.hibernate.transaction.JDBCTransaction - commit 2922 [main] DEBUG org.hibernate.impl.SessionImpl - automatically flushing sessi on ... 2953 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister - Inse rting entity: [com.model.PersonDetails#2] 2953 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedSt atement (open PreparedStatements: 0, globally: 0) 2953 [main] DEBUG org.hibernate.SQL - insert into Person_Details (FULL_NAME, PAN_CODE, WEIGHT, HEIGHT, PERSON_ID) values (?, ?, ?, ?, ?)
As can be seen from the logs,
The method tries to load the newly created Person object and access the PersonDetails association. the logs generated on execution are below:
- first the person object was created, then attempt was made to create the PersonDetails as part of the cascade operation.
- The id of Person object was passed as a part of the insert call for the PersonDetails object.
The method tries to load the newly created Person object and access the PersonDetails association. the logs generated on execution are below:
2250 [main] DEBUG org.hibernate.loader.Loader - loading entity: [com.model.Pers
on#2]
2250 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedSt
atement (open PreparedStatements: 0, globally: 0)
2250 [main] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connectio
n
2250 [main] DEBUG org.hibernate.connection.DriverManagerConnectionProvider - to
tal checked-out connections: 0
2250 [main] DEBUG org.hibernate.connection.DriverManagerConnectionProvider - us
ing pooled JDBC connection, pool size: 0
2250 [main] DEBUG org.hibernate.SQL -
select
person0_.ID as ID1_1_,
person0_.NAME as NAME1_1_,
person0_.AGE as AGE1_1_,
persondeta1_.PERSON_ID as PERSON1_2_0_,
persondeta1_.FULL_NAME as FULL2_2_0_,
persondeta1_.PAN_CODE as PAN3_2_0_,
persondeta1_.WEIGHT as WEIGHT2_0_,
persondeta1_.HEIGHT as HEIGHT2_0_
from
PERSON person0_
left outer join
Person_Details persondeta1_
on person0_.ID=persondeta1_.PERSON_ID
where
person0_.ID=?
loading the record...testLoadPersonDetails()
3750 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister - Fetc hing entity: [com.model.PersonDetails#2] 3750 [main] DEBUG org.hibernate.loader.Loader - loading entity: [com.model.Pers onDetails#2] 3750 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedSt atement (open PreparedStatements: 0, globally: 0) 3750 [main] DEBUG org.hibernate.SQL - select persondeta0_.PERSON_ID as PERSON1_1_0_, persondeta0_.FULL_NAME as FULL2_1_0_, persondeta0_.PAN_CODE as PAN3_1_0_, persondeta0_.WEIGHT as WEIGHT1_0_, persondeta0_.HEIGHT as HEIGHT1_0_ from Person_Details persondeta0_ where persondeta0_.PERSON_ID=?
When trying to load the person object, Hibernate is not aware if the PersonDetail association is valid. Hence it would have to do an additional select query to check if the id exists in PERSON_DETAIL table. As it is anyways doing a query, it makes sense to load the relation. Hence it does a left outer join (thereby accounting for the case when personDetails is null)
To load the PersonDetails object no such complexity is needed as the PersonDetails object exists as a part of the association. The foreign key ensures that if person_id is valid then there has to be a Person object satisfying the association. Thus the association is lazy here.
To load the PersonDetails object no such complexity is needed as the PersonDetails object exists as a part of the association. The foreign key ensures that if person_id is valid then there has to be a Person object satisfying the association. Thus the association is lazy here.
guid:
In this method a database-generated GUID string is used.To understand what are guids, this article provides a good starting point. I used a simple class called Town to test this generator.public class Town { private String id; private String name; //setter getter methods }
The mapping document is as follows:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.model"> <class name="Town" table="TOWN"> <id name="id" column="ID" type="string"> <generator class="guid" /> </id> <property name="name" type="string"> <column name="NAME" /> </property> </class> </hibernate-mapping>
The database sql is for a simple table with varchar as the primary key:
create table TOWN ( ID varchar(255) not null, NAME varchar(255) not null, primary key (ID) )
On executing the below code to create a Town object the results are as follows:
public static void testCreateState() { Town town = new Town(); town.setName("New Town"); Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); session.save(town); transaction.commit(); }
Logs:
2110 [main] DEBUG org.hibernate.jdbc.JDBCContext - after transaction begin 2125 [main] DEBUG org.hibernate.event.def.DefaultSaveOrUpdateEventListener - sa ving transient instance 2125 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedSt atement (open PreparedStatements: 0, globally: 0) 2125 [main] DEBUG org.hibernate.SQL - select uuid() 2125 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - preparing statement 2203 [main] DEBUG org.hibernate.id.GUIDGenerator - GUID identifier generated: f bd47f3e-cb57-11e0-8efc-c437c8460442 2203 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close PreparedS tatement (open PreparedStatements: 1, globally: 1) 2203 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - closing statement 2203 [main] DEBUG org.hibernate.event.def.AbstractSaveEventListener - generated identifier: fbd47f3e-cb57-11e0-8efc-c437c8460442, using strategy: org.hibernate .id.GUIDGenerator 2203 [main] DEBUG org.hibernate.event.def.AbstractSaveEventListener - saving [c om.model.Town#fbd47f3e-cb57-11e0-8efc-c437c8460442] ... 2235 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister - Inse rting entity: [com.model.Town#fbd47f3e-cb57-11e0-8efc-c437c8460442] 2250 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedSt atement (open PreparedStatements: 0, globally: 0) 2250 [main] DEBUG org.hibernate.SQL - insert into TOWN (NAME, ID) values (?, ?) 2250 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - preparing statement 2250 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister - Dehy drating entity: [com.model.Town#fbd47f3e-cb57-11e0-8efc-c437c8460442]
As can be seen from the above logs, at the beginning of the transaction, Hibernate issues a call to the MySql uuid(). This function returns a unique id. The id is then used to save the new town object. The above created object can be fetched from the database with load call:
Town town = (Town) session.load(Town.class, "fbd47f3e-cb57-11e0-8efc-c437c8460442");
Town town = (Town) session.load(Town.class, "fbd47f3e-cb57-11e0-8efc-c437c8460442");
No comments:
Post a Comment