Showing posts with label Hibernate. Show all posts
Showing posts with label Hibernate. Show all posts

Thursday, May 23, 2013

Hibernate - FetchType.LAZY and FetchType.EAGER

When we use @ElementCollection to persist collection, the default fetch type is LAZY and  we can optionally set fetch = FetchType.EAGER. So, what is the difference between LAZY and EAGER fetch?

In hibernate, we use session.get(Student.class, 1) to retrieve the data previously persisted. Hibernate uses proxy objects to achieve this goal, and it only gets all simple type of data immediately. It is more efficient to get collection data when needed since it may has a pretty long list of collection for the object. Therefore, the default behavior of collection retrieval is LAZY, and you can tell the framework by setting fetch = FetchType.EAGER when you want to get all collection data immediately.

Default - LAZY
        
        session = sessionFactory.openSession();
        student = null;
        student = (Student) session.get(Student.class, 1);
        session.close();
        System.out.println(student.getFirstName() + " " + student.getLastName());
        System.out.println(student.getParentList().size());

Output
Hibernate: select student0_.STUDENT_ID as STUDENT1_0_0_, student0_.FIRST_NAME as FIRST2_0_0_, student0_.LAST_NAME as LAST3_0_0_ from STUDENT_INFO student0_ where student0_.STUDENT_ID=?
James Bond
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.mqin.hibernate.demo.Student.parentList, could not initialize proxy - no Session
 at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:566)
 at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:186)
 at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:137)
 at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:242)
 at com.mqin.hibernate.demo.HibernateTest.main(HibernateTest.java:55)

String firstName and lastName is accessible since we had already initialized the value before session was closed. However, we failed to lazily initialize a collection parentList.

EAGER fetch
    @ElementCollection(fetch = FetchType.EAGER)
    @JoinTable(name = "STUDENT_PARENTS", joinColumns = @JoinColumn(name = "STUDENT_ID"))
    private Collection<parent> parentList = new ArrayList<parent>();

Output
Hibernate: select student0_.STUDENT_ID as STUDENT1_0_0_, student0_.FIRST_NAME as FIRST2_0_0_, student0_.LAST_NAME as LAST3_0_0_, parentlist1_.STUDENT_ID as STUDENT1_0_2_, parentlist1_.Parent_Name as Parent2_1_2_, parentlist1_.Parent_PhoneNo as Parent3_1_2_ from STUDENT_INFO student0_ left outer join STUDENT_PARENTS parentlist1_ on student0_.STUDENT_ID=parentlist1_.STUDENT_ID where student0_.STUDENT_ID=?
James Bond
2

We can get all information even after session was closed.

Wednesday, May 22, 2013

Hibernate - @ElementCollection, @JoinTable and @JoinColumn

Another way to persist multiple Parent objects in Student is to use Collection. JPA persists all Parent objects in a separate table with student_id as its' foreign key.

Student code
    @ElementCollection
    @JoinTable(name = "STUDENT_PARENTS", joinColumns = @JoinColumn(name = "STUDENT_ID"))
    private Collection<parent> parentList = new ArrayList<parent>();

Test code
        Student student = new Student();
        student.setFirstName("James");
        student.setLastName("Bond");
        
        Parent father = new Parent();
        father.setName("Bruce Bond");
        father.setPhoneNo("8888 8888");
        
        Parent mother = new Parent();
        mother.setName("Jolie Bond");
        mother.setPhoneNo("6666 6666");
        
        student.getParentList().add(father);
        student.getParentList().add(mother);

Console output
Hibernate: alter table STUDENT_PARENTS drop foreign key FK3A2502C5B2F00DA3
Hibernate: drop table if exists STUDENT_INFO
Hibernate: drop table if exists STUDENT_PARENTS
Hibernate: create table STUDENT_INFO (STUDENT_ID integer not null auto_increment, FIRST_NAME varchar(255), LAST_NAME varchar(255), primary key (STUDENT_ID))
Hibernate: create table STUDENT_PARENTS (STUDENT_ID integer not null, Parent_Name varchar(255), Parent_PhoneNo varchar(255))
Hibernate: alter table STUDENT_PARENTS add index FK3A2502C5B2F00DA3 (STUDENT_ID), add constraint FK3A2502C5B2F00DA3 foreign key (STUDENT_ID) references STUDENT_INFO (STUDENT_ID)
Hibernate: insert into STUDENT_INFO (FIRST_NAME, LAST_NAME) values (?, ?)
Hibernate: insert into STUDENT_PARENTS (STUDENT_ID, Parent_Name, Parent_PhoneNo) values (?, ?, ?)
Hibernate: insert into STUDENT_PARENTS (STUDENT_ID, Parent_Name, Parent_PhoneNo) values (?, ?, ?)

In database









Please notice that table STUDENT_PARENTS has no primary key.

Hibernate - @AttributeOverrides

In hibernate-embeddable-and-embedded, we explained how to persist an object attribute. Let me take the Student class for example again, how we should do if we want to persist two Parent objects in Student, such as father and mother.

We can count on @AttributeOverrides
    @Embedded
    @AttributeOverrides({
            @AttributeOverride(name = "name", column = @Column(name = "Father_Name")),
            @AttributeOverride(name = "phoneNo", column = @Column(name = "Father_PhoneNo")) })
    private Parent father;

    @Embedded
    @AttributeOverrides({
            @AttributeOverride(name = "name", column = @Column(name = "Mother_Name")),
            @AttributeOverride(name = "phoneNo", column = @Column(name = "Mother_PhoneNo")) })
    private Parent mother;

Test code
        Student student = new Student();
        student.setFirstName("James");
        student.setLastName("Bond");
        
        Parent father = new Parent();
        father.setName("Bruce Bond");
        father.setPhoneNo("8888 8888");
        
        Parent mother = new Parent();
        mother.setName("Jolie Bond");
        mother.setPhoneNo("6666 6666");
        
        student.setFather(father);
        student.setMother(mother);


Console output
Hibernate: drop table if exists STUDENT_INFO
Hibernate: create table STUDENT_INFO (STUDENT_ID integer not null auto_increment, Father_Name varchar(255), Father_PhoneNo varchar(255), FIRST_NAME varchar(255), LAST_NAME varchar(255), Mother_Name varchar(255), Mother_PhoneNo varchar(255), primary key (STUDENT_ID))
Hibernate: insert into STUDENT_INFO (Father_Name, Father_PhoneNo, FIRST_NAME, LAST_NAME, Mother_Name, Mother_PhoneNo) values (?, ?, ?, ?, ?, ?)

See data in MySQL





Hibernate - @Embeddable, @Embedded and @EmbeddedId

In last post, all the attributes we persisted for Student object were quite simple, like int, String and Date. How we persist object attribute for Student class like Parent? Suppose Parent has two fields: name and phone number.

JPA provides @Embeddable and @Embeded to help us achieve this goal.

Create a new Parent class
package com.mqin.hibernate.demo;

import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
public class Parent {
    @Column(name="Parent_Name")
    private String name;
    
    @Column(name="Parent_PhoneNo")
    private String phoneNo;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhoneNo() {
        return phoneNo;
    }

    public void setPhoneNo(String phoneNo) {
        this.phoneNo = phoneNo;
    }
    
}



Student code
@Entity
@Table(name = "STUDENT_INFO")
public class Student {

    @Id
    @GeneratedValue
    @Column(name = "STUDENT_ID")
    private int ID;

    @Column(name = "FIRST_NAME")
    private String firstName;

    @Column(name = "LAST_NAME")
    private String lastName;
    
    @Embedded
    private Parent parent;

Test code
        Student student = new Student();
        student.setFirstName("James");
        student.setLastName("Bond");
        
        Parent parent = new Parent();
        parent.setName("Bruce Bond");
        parent.setPhoneNo("8888 8888");
        
        student.setParent(parent);

Console output
Hibernate: drop table if exists STUDENT_INFO
Hibernate: create table STUDENT_INFO (STUDENT_ID integer not null auto_increment, FIRST_NAME varchar(255), LAST_NAME varchar(255), Parent_Name varchar(255), Parent_PhoneNo varchar(255), primary key (STUDENT_ID))
Hibernate: insert into STUDENT_INFO (FIRST_NAME, LAST_NAME, Parent_Name, Parent_PhoneNo) values (?, ?, ?, ?)

See what's in database






Note: Use @EmbeddedId if you would like some object to be your primary key.

Monday, May 20, 2013

Hibernate - hbm2ddl.auto

You may remember this parameter in hibernate.cfg.xml
  
  create-drop

This option is only for developers and not recommend for production at any time.

It has four possible values:
  • validate - makes no changes to the database
  • update - update the database
  • create - destroy previous data, and create new schema every time
  • create-drop - the database scheme will be destroyed when SessionFactory is closed explicitly.

Code in org.hibernate.cfg.SettingFactory
  String autoSchemaExport = properties.getProperty( AvailableSettings.HBM2DDL_AUTO );
  if ( "validate".equals(autoSchemaExport) ) {
   settings.setAutoValidateSchema( true );
  }
  if ( "update".equals(autoSchemaExport) ) {
   settings.setAutoUpdateSchema( true );
  }
  if ( "create".equals(autoSchemaExport) ) {
   settings.setAutoCreateSchema( true );
  }
  if ( "create-drop".equals( autoSchemaExport ) ) {
   settings.setAutoCreateSchema( true );
   settings.setAutoDropSchema( true );
  }



Hibernate - @Table, @GeneratedValue, @Column, @Temporal, @Transient and @Lob

See more annotations we use in entity bean class.
@Entity
@Table(name = "STUDENT_INFO") // change name only for the table, but not entity
public class Student {

    @Id
    @GeneratedValue // no need to visibly set ID
    @Column(name = "STUDENT_ID")
    private int ID;

    @Column(name = "FIRST_NAME")
    private String firstName;

    @Column(name = "LAST_NAME")
    private String lastName;

    @Temporal(TemporalType.DATE) // persisted as date yyyy-mm-dd
    @Column(name = "JOIN_DATE")
    private Date joinDate;

    @Transient // not persisted in db
    @Column(name = "ADDRESS")
    private String address;

    @Lob
    @Column(name = "DESCRIPTION")
    private String description;
    ...

Test Code
        Student student = new Student();
        student.setFirstName("James");
        student.setLastName("Bond");
        student.setJoinDate(new Date());
        student.setAddress("Adelaide");
        student.setDescription("Long Description");
        ...

Console output
Hibernate: drop table if exists STUDENT_INFO
Hibernate: create table STUDENT_INFO (STUDENT_ID integer not null auto_increment, DESCRIPTION longtext, FIRST_NAME varchar(255), JOIN_DATE date, LAST_NAME varchar(255), primary key (STUDENT_ID))
Hibernate: insert into STUDENT_INFO (DESCRIPTION, FIRST_NAME, JOIN_DATE, LAST_NAME) values (?, ?, ?, ?)

Execution result in DB










Hibernate - First Application

Download Hibernate 4 from hibernate4. Unzip to local file system, for example, D:\hibernate-release-4.1.12.Final.

Create a Java Project in Eclipse, add User library Hibernate with jars under D:\hibernate-release-4.1.12.Final\lib\required.

Since Hibernate is built on top of JDBC, we still need related JDBC driver. So add the jar to the project as well.

Take a look at the project structure.



















To use Hibernate, at least we need -
  • hibernate.cfg.xml
  • Object class with Annotation
  • Session, main runtime interface between Java and Hibernate

Snip of hibernate.cfg.xml


 

  
  com.mysql.jdbc.Driver
  jdbc:mysql://localhost:3306/hibernatedb
  root
  root

  
  1

  
  org.hibernate.dialect.MySQLDialect

  
  org.hibernate.cache.internal.NoCacheProvider

  
  true

  
  create

  
  

 



We need to set JDBC connection parameters, and select one proper SQL dialect. I am using MySQLDialect, and you can find all dialects at hibernate-core/org.hibernate.dialect.

Mapping object class
package com.mqin.hibernate.demo;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Student {

    @Id
    private int ID;
    private String firstName;
    private String lastName;

    public int getID() {
        return ID;
    }

    public void setID(int iD) {
        ID = iD;
    }

    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;
    }

}

Test Code
package com.mqin.hibernate.demo;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

public class HibernateTest {

    public static void main(String[] args) {
        Student student = new Student();
        student.setID(1);
        student.setFirstName("James");
        student.setLastName("Bond");

        Configuration configuration;
        ServiceRegistry serviceRegistry;
        SessionFactory sessionFactory;
        Session session;

        configuration = new Configuration();
        configuration.configure();

        serviceRegistry = new ServiceRegistryBuilder()
                              .applySettings(configuration.getProperties())
                              .buildServiceRegistry();
        
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        // sessionFactory = configuration.buildSessionFactory();
        // deprecated in Hibernate 4
        
        session = sessionFactory.openSession();
        session.beginTransaction();
        session.save(student);
        session.getTransaction().commit();

    }

}


Console ouput
Hibernate: drop table if exists Student
Hibernate: create table Student (ID integer not null, firstName varchar(255), lastName varchar(255), primary key (ID))
Hibernate: insert into Student (firstName, lastName, ID) values (?, ?, ?)

@Entity table created in database and @Id is primary key.