package com.oreilly.jent.hibernate;

/**
 * In general, you may use the code in this book in your programs and 
 * documentation. You do not need to contact us for permission unless 
 * you're reproducing a significant portion of the code. For example, 
 * writing a program that uses several chunks of code from this book does 
 * not require permission. Selling or distributing a CD-ROM of examples 
 * from O'Reilly books does require permission. Answering a question by 
 * citing this book and quoting example code does not require permission. 
 * Incorporating a significant amount of example code from this book into 
 * your product's documentation does require permission.
 * 
 * We appreciate, but do not require, attribution. An attribution usually 
 * includes the title, author, publisher, and ISBN. For example: 
 * 
 *   "Java Enterprise in a Nutshell, Third Edition, 
 *    by Jim Farley and William Crawford 
 *    with Prakash Malani, John G. Norman, and Justin Gehtland. 
 *    Copyright 2006 O'Reilly Media, Inc., 0-596-10142-2."
 *  
 *  If you feel your use of code examples falls outside fair use or the 
 *  permission given above, feel free to contact us at 
 *  permissions@oreilly.com.
 */

import java.util.List;

import org.hibernate.Hibernate;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class Facade {

   private static SessionFactory factory;

   /**
    * Creates the configuration, which loads Hibernate.cfg.xml from the classpath
    * (I've added /config to the classpath).  Then, adds the persistent classes
    * by classname, and builds the SessionFactory.
    */
   static {
      Configuration config = new Configuration();
      config.configure();
      config.addClass(UniversityClass.class);
      config.addClass(Syllabus.class);
      config.addClass(Person.class);
      config.addClass(Department.class);
      factory = config.buildSessionFactory();
   }

   /**
    * Utility method for getting an open Session.  This allows a central entry point
    * for adding Interceptors or Connections to the Session.
    * @return
    */
   private static Session getSession() {
      return factory.openSession();
   }

   /**
    * Load a Professor object. When using the polymorphic mappings, this will only load
    * rows that match the Professor's discriminator value, no matter what ID you give it.
    * @param id
    * @return
    */
   public static Professor getProfessor(Long id) {
      Session s = null;
      Professor result = null;
      try {
         s = getSession();
         result = (Professor)s.load(Professor.class, id);
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         s.close();
      }
      return result;
   }

   /**
    * Returns a List of all Professors in the database. If you are
    * using the polymorphic mapping, it will distinguish between
    * Professors and Students.
    * @return
    */
   public static List getProfessors() {
      Session s = null;
      List results = null;
      try {
         s = getSession();
         results = s.createCriteria(Professor.class).list();
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         s.close();
      }
      return results;
   }

   /**
    * Takes any persistable object and saves it to the db, using the
    * indiscriminate saveOrUpdate method, which will either create new records
    * or update existing records according to the object being saved.
    * @param o
    */
   public static void save(Object o) {
      Session s = null;
      try {
         s = getSession();
         s.saveOrUpdate(o);
         s.flush();
         s.connection().commit();
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         s.close();
      }
   }

   /**
    * Deletes any persistable object from the database.
    * @param o
    */
   public static void delete(Object o) {
      Session s = null;
      try {
         s = getSession();
         s.delete(o);
         s.flush();
         s.connection().commit();
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         s.close();
      }
   }

   /**
    * Eagerly initializes the otherwise lazily loaded emails collection of Professor
    * before returning it.
    * @param id
    * @return
    */
   public static Professor getProfessorWithEmails(Long id) {
      Session s = null;
      Professor result = null;
      try {
         s = getSession();
         result = (Professor)s.load(Professor.class, id);
         Hibernate.initialize(result.getEmails());
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         s.close();
      }
      return result;
   }

   /**
    * Loads a UniversityClass and eagerly initializes the Syllabus relationship.
    * @param id
    * @return
    */
   public static UniversityClass getUClass(Long id) {
      Session s = null;
      UniversityClass result = null;
      try {
         s = getSession();
         result = (UniversityClass)s.load(UniversityClass.class, id);
         Hibernate.initialize(result.getSyllabus());
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         s.close();
      }
      return result;
   }

   /**
    * Loads a Syllabus and eagerly initializes the UniversityClass relationship.
    * @param id
    * @return
    */
   public static Syllabus getSyllabus(Long id) {
      Session s = null;
      Syllabus result = null;
      try {
         s = getSession();
         result = (Syllabus)s.load(Syllabus.class, id);
         Hibernate.initialize(result.getUclass());
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         s.close();
      }
      return result;
   }

   /**
    * Loads a Student, and eagerly initializes the UniversityClass collection.
    * @param id
    * @return
    */
   public static Student getStudent(Long id) {
      Session s = null;
      Student result = null;
      try {
         s = getSession();
         result = (Student)s.load(Student.class, id);
         Hibernate.initialize(result.getClasses());
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         s.close();
      }
      return result;
   }

   /**
    * Loads all Studnets in the database. When using the polymorphic mapping,
    * this will distinguish between Professors and Students.
    * @return
    */
   public static List getStudents() {
      Session s = null;
      List results = null;
      try {
         s = getSession();
         results = s.createCriteria(Student.class).list();
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         s.close();
      }
      return results;
   }

   /**
    * Loads all UniversityClasses in the database.
    * @return
    */
   public static List getClasses() {
      Session s = null;
      List results = null;
      try {
         s = getSession();
         results = s.createCriteria(UniversityClass.class).list();
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         s.close();
      }
      return results;
   }

   /**
    * Only useful when using the polymorphic mappings. Loads all implementations of the Person
    * interface, regardless of concrete type.  All items returned retain full type fidelity;
    * Professors can be cast to Person or Professor, Students to Person or Student.
    * @return
    */
   public static List getPeople() {
      Session s = null;
      List results = null;
      try {
         s = getSession();
         results = s.createCriteria(Person.class).list();
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         s.close();
      }
      return results;
   }

   /**
    * Uses HQL paramaterized query to select a subset of available Professors.
    * @param degree
    * @return
    */
   public static List getProfessorsByDegree(String degree) {
      Session s = null;
      List results = null;
      try {
         s = getSession();
         Query q = s.createQuery("from Professor where degree = :degree");
         q.setString("degree", degree);
         results = q.list();
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         s.close();
      }
      return results;
   }

   /**
    * Uses an HQL query to return a List of Arrays, each one containing the first and last names of a Person.
    * @return
    */
   public static List getPeopleNames() {
      Session s = null;
      List results = null;
      try {
         s = getSession();
         results = s.createQuery("SELECT person.firstName, person.lastName FROM Person person").list();
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         s.close();
      }
      return results;
   }

   /**
    * Simple department load.
    * @param id
    * @return
    */
   public static Department getDepartment(Long id) {
      Session s = null;
      Department d = null;
      try {
         s = getSession();
         d = (Department)s.load(Department.class, id);
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         s.close();
      }
      return d;
   }
}
