# Hibernate API

* JPA specification for all interfaces are present in `javax.persistance` and it sub-packages.

## Introduction

* `org.hibernate.Session` is an interface.
* Session is associated implicitly with L1 cache _(having same scope as the session lifetime)_, also referred to as **Persistence context**.
*   Methods for doing CRUD operations on a session are as follows,

    | Method           | Description                                                                                                  |
    | ---------------- | ------------------------------------------------------------------------------------------------------------ |
    | `save()`         | Inserting the record                                                                                         |
    | `get()`/`load()` | Retrieving the record                                                                                        |
    | `update()`       | Updating the record                                                                                          |
    | `delete()`       | Deleting the record `public void delete(Object ref)` reference either persistent or detached pojo reference. |

### SessionFactory

* It represents a provider factory of session objects. _(provider/supplier of session objects)_.
* We use instance of `org.hibernate.SessionFactory` to create session object _(via `openSession()` or `getCurrentSession()`)_.
* `SessionFactory` is singleton _(1 instance per DB/application)_, immutable, inherently thread safe.
* It is a heavy weight object, therefore it has to be created only once for an application at the very beginning of application life cycle.
* `org.hibernate.SessionFactory` has two apis `getCurrentSession` and `openSession()`.

```java
    public Session openSession() throws HibernateException
    public Session getCurrentSession() throws HibernateException
```

| `openSession`                                                                            | `getCurrentSession`                                                                                                                                                                                           |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| opens NEW session from Session Factory, which has to be explicitly closed by programmer. | Opens new session, if one doesn't exist, otherwise continues with the exisitng one.                                                                                                                           |
| Have to close explicitly                                                                 | Gets automatically closed upon Transaction boundary or thread over _(since current session is bound to current thread)_ mentioned in `hibernate.cfg.xml` property _(current\_session\_context\_class thread)_ |

### Configuration

* Comes under `org.hibernate.cfg.Configuration` package, used before hibernate `4.x.`
* Configuration object is used to create the SessionFactory object.
* Object Oriented Representation of Hibernate configuration file and mapping file is nothing but Configuration object.
* When we call `configure()` method on configuration object, hibernate configuration file _(hibernate.cfg.xml placed in run time classpath)_ and mapping files are loaded in the memory.

### Entity Life cycle

1. Transient State

* An object is said to be in transient state, if it is not associated with the session, and has no matching record in the database table.

2. Persistent State

*   An object is said to be in persistent state, if it is associated with session object _(L1 cache)_ and will result into a matching record in the database table upon commit.

    ```java
        session.save(account);
        tx.commit();
        Account account=session.get(Account.class, 102);
    ```

    * Note
      * When the POJO is in persistent state it will be in synchronization with the matching record in database i.e if we make any changes to the state of the persistent POJO it will be reflected in the database only after commiting transaction \_ i.e automatic dirty checking will be performed _(resulting in to insert/update/delete)_.

3. Detached state

* Object is not associated with session but has matching record in the database table.
* If we make any changes to the state of detached object it will not be reflected in the database.
*   Following methods are used to make a persistent object, detached.

    ```java
      session.clear();
      session.evict(Object);
      session.close();
    ```

    * Note :
      * By calling `update()` method on session object it will go from detached state to persistent state.
      * By calling `delete()` method on session object it will go from persistent state to transient state.

#### State Transition Diagram

![Entity life cycle transition](https://574639531-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FJDnjsv52I3fJ56WbLgHu%2Fuploads%2Fgit-blob-0ecf778b83d361724369304a67065cb0b98d90d4%2Fpojo-life-cycle-image.png?alt=media)

### Session API

```java
    public void persist(Object ref)  
```

* Persists specified transient POJO on underlying DB, upon committing the transaction.
* In comparison to `save()`, this method does not return the generated identifier.

```java
    public Serializable save(Object o) throws HibernateException
```

* I/P transient POJO reference
* `save()` method auto persists transient POJO in the DB upon committing transaction & returns unique serializable ID generated by hibernate framework, if `@GeneratedValue` is used for Id.

```java
    public <T> T  get(Class<T> c, Serializable id) throws HibernateException
```

* For data retrieval by Primary Key i.e Id
* Returns null, if id is not found
* Returns PERSISTENT pojo reference if id is found.
*   Usage of Hibernate Session API's `get()`,

    ```java
        int id=101;
        BookPOJO b1=hibSession.get(BookPOJO.class, id);
        //int -----Integer(auto boxing) ---upcasting------Serializable
        //OR 
        BookPOJO b1=(BookPOJO)hibSession
                        .get(Class.forName("pojos.BookPOJO"), id);
    ```

```java
  void clear() 
```

* When `clear()` is called on session object all the objects associated with the session object become detached.
* Note that the databse connection is not closed. _(Completely clears the session. Evicts all loaded instances and cancel all pending saves, updates and deletions)_.

```java
  void close()
```

* When `close()` is called on session object all the objects associated with the session object become detached and also closes the database connection.

```java
public void evict(Object ref)
```

* It detaches a particular persistent object or disassociates from the session. _(Remove this instance from the session cache. Changes to the instance will not be synchronized with the database)._

```java
  void flush()
```

* When the object is in persistent state, whatever changes we made to the object state will be reflected in the database only at the end of transaction.
* If we want to reflect the changes before the end of transaction _(i.e before commiting the transaction)_ call the flush method. _(Flushing is the process of synchronizing the underlying DB state with persistable state of session cache)_.
* Whether the transaction will be committed on flush depends on `org.hibernate.FlushMode` value.

```java
  boolean contains(Object ref)
```

* The method indicates whether the object is associated with session or not.

```java
  void refresh(Object ref)
```

* This method is used to get the latest data from database and make corresponding modifications to the persistent object state. _(Re-read the state of the given instance from the underlying database)_.

```java
  public void update(Object ref)
```

* If object is in persistent state no need of calling the update method.
* As the object is in sync with the database, whatever changes made to the object will be reflected to database at the end of transaction.
* When the object is in detached state, record is present in the table but object is not in sync with database, therefore `update()` method can be called to update the record in the table.
* Which exceptions update method can raise?
  1. StaleStateException
     1. If you are trying to update a record (using `session.update(ref)`), whose id doesn't exist, i.e update can't transition from transient to persistent state.
     2. It can only transition from detached to persistent.
  2. `NonUniqueObjectException`
     * If there is already persistence instance with same id in session.

```java
  public Object merge(Object ref)
```

* Can Transition from transient, persistent & detached to persistent. Regarding Hibernate merge,
  1. The state of a transient or detached instance may also be made persistent as a new persistent instance by calling merge().
  2. Copies the state of the given object _(can be passed as transient or detached)_ onto the persistent object with the same identifier.
  3. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance.
  4. If the given instance is unsaved, save a copy of it and return it as a newly persistent instance. The given instance does not become associated with the session.
  5. It will not throw `NonUniqueObjectException`, even if there is already persistence instance with same id in session.

```java
  public void saveOrUpdate(Object ref)
```

* The method persists the object _(insert)_ if matching record is not found with id initialized to default value or fires update query.
* If you supply Object, with non-existing ID throws `StaleStateException`.
* This method is different from merge because, for this method the reference should be part of persistence context.

```java
  lock(Object object, LockMode mode)
```

* When `lock()` method is called on the session object for a persistent object, untill the transaction is commited in the hibernate application. Externally the matching record in the table cannot be modified.

```java
        // i/p --persistent POJO ref
        void delete(Object o) throws HibernateException 
```

* POJO is marked for removal, corresponding row from DB will be deleted after comitting transaction & closing of session.
*   One can use directly "delete HQL" & perform deletions, for eg,

    ```java
        int deletedRows = hibSession.createQuery ("delete Subscription s  where s.subscriptionDate < :today").setParameter ("today", new Date ()).executeUpdate ();
    ```

## Some interesting API behavior and differences

### Differences

*   Difference in `get()` & `load()`

    ```java
     T<T> load(Class c, Serializable id)
     T<T> get(Class c, Serializable id)
    ```

    | get(...)                                                                                                                                                                                                                                                                                           | load(...)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
    | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
    | If id doesn't exist it returns null.                                                                                                                                                                                                                                                               | If id doesn't exist & you are accessing it from within hibernate session it throws ObjectNotFoundException.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
    | Hibernate uses eager fetching policy, meaning it will generate select query always & load the state from DB in persistent POJO reference. So, even if you access the same from within the session(persistent pojo) or outside (detached) the hibernate session throws NO EXCEPTION(proxy + state). | <p>Hibernate uses lazy fetching policy, meaning it will, by default NOT generate any select query, so what you have is ONLY PROXY <em>(i.e., wrapper with no state loaded from DB)</em>.<br>On such a proxy if you access anything outside the hibernate session <em>(i.e in detached state)</em> one will get <code>LazyInitializationException</code><br>How to fix this?<br>1. Change fetch type to eager <em>(NOT AT ALL recommended)</em> as this will lead to no caching, hence disabling L1 cache)<br>2. If you want to access any POJO in detached manner <em>(i.e outside hibernate session scope)</em> fire non-id get method from within session &#x26; then hibernate has to load entire state from DB hence NO <code>LazyInitializationException</code></p> |

    * Lazy fetching becomes important in relationships or in `load()` v/s `get()`.
    * When a client requests an entity _(eg - Course POJO)_ and its associated graph of objects _(eg -Student POJO)_ from the database, it isn't usually necessary to retrieve the whole graph of every _(indirectly)_ associated object.
    * You wouldn't want to load the whole database into memory at once. For example, loading a single Category shouldn't trigger the loading of all Items in that category _(one to many relationship)_.
* `update()` v/s `merge()`
  *   Both methods transition detached object to persistent state.

      | Update                                                                                                                                                                                                                                                                                                                                                                                | Merge                                                                                                                              |
      | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
      | <p>If you are sure that the session does not contain an already persistent instance with the same identifier then use update to save the data in hibernate.<br>If session has an object with same id , then it throws <code>org.hibernate.NonUniqueObjectException</code> because a different object with the same identifier value was already been associated with the session.</p> | If you want to save your modifications at any time with out knowing about the state of an session then use `merge()` in hibernate. |
* `persist()` v/s `save()` `public void persist(Object transientRef)`
  * If you set id _(primary key)_ before calling `persist(ref)`, that `Id` may be existing or not existing in the persistence store, this api gives exception `org.hibernate.PersistentObjectException` with the following error message `detached entity passed to persist`. This is because when you set an Id, effectively you make it detached. Hence, the error. `public Serializable save(Object ref)`
  * if you set some non-null id _(primary key)_ which may be existing or non-existing, while calling `save(ref)` this api doesn't give any exception. It ignores your passed id & creates its own id & inserts a row.

### Interesting Behaviors

* `public void saveOrUpdate(Object ref)`
  * Either inserts/updates or throws exception
    * null id, fires insert _(works as save)_
    * non-null id, but existing id, fires update _(works as update)_
    * **non-null id, but non existing id in persistent context i.e detached, then it throws `StaleStateException`, to indicate that we are trying to delete or update a row that does not exist**.
* `public Object merge(Object ref)`
  * Instance to be merged must be either transient or detached POJO reference
  * It returns persistent pojo reference.
  * null id, fires insert (works as save)
  * non-null but existing id, fires update _(select , update)_.
  * **non-null but non existing id, no exception is thrown. It just ignores the passed id & creates its own id & inserts a row&#x20;**_**(select,insert)**_.
* `public void update(Object object)`
  * Update the persistent instance with the identifier of the given detached instance.
  * Input argument object should be detached pojo containing updated state.
  * Detached pojo becomes persistent after this operation.
  * Exception associated :
    * Throws `org.hibernate.TransientObjectException`, when given object has a null identifier. When calling update if you give null id. It fails as input object is not in detached state. _(transient ----X---> persistent via update)_.
    * Throws `org.hibernate.StaleStateException` to indicate that we are trying to delete or update a row that does not exist.
    * `org.hibernate.NonUniqueObjectException` a different object with the same identifier value was already associated with the session.
