Hibernate


Hibernate is an open source Java persistence framework project. It performs powerful object-relational mapping and query databases using HQL and SQL. Hibernate is a great tool for ORM mappings in Java. It can cut down a lot of complexity and thus defects as well from your application, which may otherwise find a way to exist.
Initially started as an ORM framework, Hibernate has spun off into many projects, such as Hibernate SearchHibernate ValidatorHibernate OGM (for NoSQL databases), and so on.

Hibernate Architecture


Let’s understand what each block represents.
1.     Configuration: Generally written in hibernate.properties or hibernate.cfg.xml files(for xml based). For Java configuration, you may find class annotated with @Configuration. It is used by Session Factory to work with Java Application and the Database. It represents an entire set of mappings of an application Java Types to an SQL database.
2.     Session Factory: Any user application requests Session Factory for a session object. Session Factory uses configuration information from above listed files or from @Configuration java file, to instantiates the session object appropriately.
3.     Session: This represents the interaction between the application and the database at any point of time. This is represented by the org.hibernate.Session class. The instance of a session can be retrieved from the SessionFactory bean.
4.     Query: It allows applications to query the database for one or more stored objects. Hibernate provides different techniques to query database, including NamedQuery and Criteria API.
5.     First-level cache: It represents the default cache used by Hibernate Session object while interacting with the database. It is also called as session cache and caches objects within the current session. All requests from the Session object to the database must pass through the first-level cache or session cache. One must note that the first-level cache is available with the session object until the Session object is live.
6.     Transaction: enables you to achieve data consistency, and rollback in case something goes unexpected.
7.     Persistent objects: These are plain old Java objects (POJOs), which get persisted as one of the rows in the related table in the database by hibernate. They can be configured in configurations files (hibernate.cfg.xml or hibernate.properties) or annotated with @Entity annotation.
8.     Second-level cache: It is used to store objects across sessions. This needs to be explicitly enabled and one would be required to provide the cache provider for a second-level cache. One of the common second-level cache providers is EhCache.

Salient features of the Hibernate framework

·       Object/Relational Mapping

Hibernate, as an ORM framework, allows the mapping of the Java domain object with database tables and vice versa. As a result, business logic is able to access and manipulate database entities via Java objects. It helps to speed up the overall development process by taking care of aspects such as transaction management, automatic primary key generation, managing database connections and related implementations, and so on.

·       JPA provider

Hibernate does support the Java Persistence API (JPA) specification. JPA is a set of specifications for accessing, persisting, and managing data between Java objects and relational database entities.

·       Idiomatic persistence

Any class that follows object-oriented principles such as inheritance, polymorphism, and so on, can be used as a persistent class.

·       High performance and scalability

Hibernate supports techniques such as different fetching strategies, lazy initialization, optimistic locking, and so on, to achieve high performance, and it scales well in any environment.

·       Easy to maintain

Hibernate is easier to maintain as it requires no special database tables or fields. It generates SQL at system initialization time. It is much quicker and easier to maintain compared to JDBC.

Hibernate Properties

Following is the list of important properties, you will be required to configure for a databases in a standalone situation
Sr.No.
Properties & Description
1
hibernate.dialect
This property makes Hibernate generate the appropriate SQL for the chosen database.
2
hibernate.connection.driver_class
The JDBC driver class.
3
hibernate.connection.url
The JDBC URL to the database instance.
4
hibernate.connection.username
The database username.
5
hibernate.connection.password
The database password.
6
hibernate.connection.pool_size
Limits the number of connections waiting in the Hibernate database connection pool.
7
hibernate.connection.autocommit
Allows auto commit mode to be used for the JDBC connection.

If you are using a database along with an application server and JNDI, then you would have to configure the following properties −
Sr.No.
Properties & Description
1
hibernate.connection.datasource
The JNDI name defined in the application server context, which you are using for the application.
2
hibernate.jndi.class
The Initial Context class for JNDI.
3
hibernate.jndi.<JNDIpropertyname>
Passes any JNDI property you like to the JNDI InitialContext.
4
hibernate.jndi.url
Provides the URL for JNDI.
5
hibernate.connection.username
The database username.
6
hibernate.connection.password
The database password.

 

Hibernate - Sessions

A Session is used to get a physical connection with a database. The Session object is lightweight and designed to be instantiated each time an interaction is needed with the database. Persistent objects are saved and retrieved through a Session object.
The session objects should not be kept open for a long time because they are not usually thread safe and they should be created and destroyed them as needed. The main function of the Session is to offer, create, read, and delete operations for instances of mapped entity classes.
Instances may exist in one of the following three states at a given point in time −
·      transient − A new instance of a persistent class, which is not associated with a Session and has no representation in the database and no identifier value is considered transient by Hibernate.
·      persistent − You can make a transient instance persistent by associating it with a Session. A persistent instance has a representation in the database, an identifier value and is associated with a Session.
·      detached − Once we close the Hibernate Session, the persistent instance will become a detached instance.

Association Mappings

The mapping of associations between entity classes and the relationships between tables is the soul of ORM.
Sr.No.
Mapping type & Description
1
Mapping many-to-one relationship using Hibernate
2
Mapping one-to-one relationship using Hibernate
3
Mapping one-to-many relationship using Hibernate
4
Mapping many-to-many relationship using Hibernate

 

Hibernate - Annotations

import javax.persistence.*;

@Entity
@Table(name = "EMPLOYEE")
public class Employee {
   @Id @GeneratedValue
   @Column(name = "id")
   private int id;

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

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

   @Column(name = "salary")
   private int salary; 

   public Employee() {}
  
   public int getId() {
      return id;
   }
  
   public void setId( int id ) {
      this.id = id;
   }
  
   public String getFirstName() {
      return firstName;
   }
  
   public void setFirstName( String first_name ) {
      this.firstName = first_name;
   }
  
   public String getLastName() {
      return lastName;
   }
  
   public void setLastName( String last_name ) {
      this.lastName = last_name;
   }
  
   public int getSalary() {
      return salary;
   }
  
   public void setSalary( int salary ) {
      this.salary = salary;
   }
}

@Entity Annotation

@Entity annotation to the Employee class, which marks this class as an entity bean, so it must have a no-argument constructor that is visible with at least protected scope.

@Table Annotation

The @Table annotation allows you to specify the details of the table that will be used to persist the entity in the database.
The @Table annotation provides four attributes, allowing you to override the name of the table, its catalogue, and its schema, and enforce unique constraints on columns in the table.

@Id and @GeneratedValue Annotations

Each entity bean will have a primary key, which you annotate on the class with the @Id annotation. The primary key can be a single field or a combination of multiple fields depending on your table structure.
By default, the @Id annotation will automatically determine the most appropriate primary key generation strategy to be used but you can override this by applying the @GeneratedValue annotation, which takes two parameters strategy and generator.

@Column Annotation

The @Column annotation is used to specify the details of the column to which a field or property will be mapped. You can use column annotation with the following most commonly used attributes −
·      name attribute permits the name of the column to be explicitly specified.
·      length attribute permits the size of the column used to map a value particularly for a String value.
·      nullable attribute permits the column to be marked NOT NULL when the schema is generated.
·      unique attribute permits the column to be marked as containing only unique values.

Generator classes in Hibernate

The <generator> class is a sub-element of id. It is used to generate the unique identifier for the objects of persistent class. There are many generator classes defined in the Hibernate Framework. Some of them are listed below.

1) assigned

It is the default generator strategy if there is no <generator> element. In this case, application assigns the id

2) increment

It generates the unique id only if no other process is inserting data into this table. It generates. If a table contains an identifier then the application considers its maximum value else the application consider that the first generated identifier is 1. For each attribute value, hibernate increment the identifier by 1.

3) sequence

It uses the sequence of the database. if there is no sequence defined, it creates a sequence automatically e.g. in case of Oracle database, it creates a sequence named HIBERNATE_SEQUENCE.  And many more….

 

Hibernate - Caching

Caching is a mechanism to enhance the performance of a system. It is a buffer memory that lies between the application and the database. Cache memory stores recently used data items in order to reduce the number of database hits as much as possible.


First-level Cache

The first-level cache is the Session cache and is a mandatory cache through which all requests must pass. The Session object keeps an object under its own power before committing it to the database.
If you issue multiple updates to an object, Hibernate tries to delay doing the update as long as possible to reduce the number of update SQL statements issued. If you close the session, all the objects being cached are lost and either persisted or updated in the database.

Second-level Cache

Second level cache is an optional cache and first-level cache will always be consulted before any attempt is made to locate an object in the second-level cache. The second level cache can be configured on a per-class and per-collection basis and mainly responsible for caching objects across sessions.
Any third-party cache can be used with Hibernate. An org.hibernate.cache.CacheProvider interface is provided, which must be implemented to provide Hibernate with a handle to the cache implementation.

 

Cache Provider

Your next step after considering the concurrency strategies, you will use your cache candidate classes to pick a cache provider. Hibernate forces you to choose a single cache provider for the whole application.
Sr.No.
Cache Name & Description
1
EHCache
It can cache in memory or on disk and clustered caching and it supports the optional Hibernate query result cache.
2
OSCache
Supports caching to memory and disk in a single JVM with a rich set of expiration policies and query cache support.
3
warmCache
A cluster cache based on JGroups. It uses clustered invalidation, but doesn't support the Hibernate query cache.
4
JBoss Cache
A fully transactional replicated clustered cache also based on the JGroups multicast library. It supports replication or invalidation, synchronous or asynchronous communication, and optimistic and pessimistic locking. The Hibernate query cache is supported.

The following compatibility matrix will help you choose an appropriate combination.
Strategy/Provider
Read-only
Nonstrictread-write
Read-write
Transactional
EHCache
X
X
X

OSCache
X
X
X

SwarmCache
X
X


JBoss Cache
X


X

Concurrency Strategies

A concurrency strategy is a mediator, which is responsible for storing items of data in the cache and retrieving them from the cache. If you are going to enable a second-level cache, you will have to decide, for each persistent class and collection, which cache concurrency strategy to use.
·      Transactional − Use this strategy for read-mostly data where it is critical to prevent stale data in concurrent transactions, in the rare case of an update.
·      Read-write − Again use this strategy for read-mostly data where it is critical to prevent stale data in concurrent transactions, in the rare case of an update.
·      Nonstrict-read-write − This strategy makes no guarantee of consistency between the cache and the database. Use this strategy if data hardly ever changes and a small likelihood of stale data is not of critical concern.
·      Read-only − A concurrency strategy suitable for data, which never changes. Use it for reference data only.

SpringBoot-Hibernate-Transactions
Spring Hibernate Transactions example
Ways of Managing Transactions
A transaction can be managed in the following ways:
1. Programmatically manage by writing custom code
This is the legacy way of managing transaction.
EntityManagerFactory factory = Persistence.createEntityManagerFactory("PERSISTENCE_UNIT_NAME");
EntityManager entityManager = entityManagerFactory.createEntityManager();
Transaction transaction = entityManager.getTransaction()
try
{
transaction.begin();
someBusinessCode();
transaction.commit();
}
catch(Exception ex)
{
transaction.rollback();
throw ex;
}
Pros:
The scope of the transaction is very clear in the code.
Cons:
It's repetitive and error-prone. Any error can have a very high impact. A lot of boilerplate needs to be written and if you want to call another method from this method then again you need to manage it in the code.
2. Use Spring to manage the transaction
Spring supports two types of transaction management:
1. Programmatic transaction management: This means that you have to manage the transaction with the help of programming. That gives you extreme flexibility, but it is difficult to maintain.
2. Declarative transaction management: This means you separate transaction management from the business code. You only use annotations or XML-based configuration to manage the transactions.
Choosing between Programmatic and Declarative Transaction Management:
  • Programmatic transaction management is good only if you have a small number of transactional operations. (This is not a good best practice)
  • Transaction name can be explicitly set only using Programmatic transaction management.
  • Programmatic transaction management should be used when you want explicit control over managing transactions.
  • On the other hand, if your application has numerous transactional operations, declarative transaction management is worthwhile.
  • Declarative Transaction management keeps transaction management out of business logic, and is not difficult to configure.
2.1 Programmatic transaction management
The Spring Framework provides two means of programmatic transaction management.
a. Using the TransactionTemplate (Recommended by Spring Team)
b. Using a PlatformTransactionManager implementation directly
2.2. Declarative Transaction Step 1: Define a transaction manager in your spring application
@Autowired
@Bean(name = "transactionManager")
public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager(sessionFactory);
return transacationManager;
}
Step 2: Turn on support for transaction annotations by adding below entry to your spring application @EnableTransactionManagement public class AppConfig { ... }
Step 3: Add the @Transactional annotation to the Class (or method in a class) or Interface (or method in an interface).
  • The @Transactional annotation may be placed before an interface definition, a method on an interface, a class definition, or a public method on a class.
  • If you want some methods in the class (annotated with @Transactional) to have different attributes settings like isolation or propagation level then put annotation at method level which will override class level attribute settings.
@Transactional attributes.
@Transactional (isolation=Isolation.READ_COMMITTED)
The default is Isolation.DEFAULT Most of the times, you will use default unless and until you have specific requirements. Informs the transaction (tx) manager that the following isolation level should be used for the current tx. Should be set at the point from where the tx starts because we cannot change the isolation level after starting a tx. DEFAULT: Use the default isolation level of the underlying database.
READ_COMMITTED: A constant indicating that dirty reads are prevented; non-repeatable reads and phantom reads can occur.
READ_UNCOMMITTED: This isolation level states that a transaction may read data that is still uncommitted by other transactions.
REPEATABLE_READ: A constant indicating that dirty reads and non-repeatable reads are prevented; phantom reads can occur.
SERIALIZABLE: A constant indicating that dirty reads, non-repeatable reads, and phantom reads are prevented.
What do these Jargons dirty reads, phantom reads, or repeatable reads mean?
Dirty Reads: Transaction "A" writes a record. Meanwhile, Transaction "B" reads that same record before Transaction A commits. Later, Transaction A decides to rollback and now we have changes in Transaction B that are inconsistent. This is a dirty read. Transaction B was running in READ_UNCOMMITTED isolation level so it was able to read Transaction A changes before a commit occurred.
Non-Repeatable Reads: Transaction "A" reads some record. Then Transaction "B" writes that same record and commits. Later Transaction A reads that same record again and may get different values because Transaction B made changes to that record and committed. This is a non-repeatable read.
Phantom Reads: Transaction "A" reads a range of records. Meanwhile, Transaction "B" inserts a new record in the same range that Transaction A initially fetched and commits. Later Transaction A reads the same range again and will also get the record that Transaction B just inserted. This is a phantom read: a transaction fetched a range of records multiple times from the database and obtained different result sets (containing phantom records).
@Transactional(propagation=Propagation.REQUIRED)
If not specified, the default propagational behavior is REQUIRED.
Other options are REQUIRES_NEW , MANDATORY , SUPPORTS , NOT_SUPPORTED , NEVER , and NESTED .
REQUIRED
Indicates that the target method cannot run without an active tx. If a tx has already been started before the invocation of this method, then it will continue in the same tx or a new tx would begin soon as this method is called.
REQUIRES_NEW
Indicates that a new tx has to start every time the target method is called. If already a tx is going on, it will be suspended before starting a new one.
MANDATORY
Indicates that the target method requires an active tx to be running. If a tx is not going on, it will fail by throwing an exception.
SUPPORTS
Indicates that the target method can execute irrespective of a tx. If a tx is running, it will participate in the same tx. If executed without a tx it will still execute if no errors. Methods which fetch data are the best candidates for this option.
NOT_SUPPORTED
Indicates that the target method doesn’t require the transaction context to be propagated. Mostly those methods which run in a transaction but perform in-memory operations are the best candidates for this option.
NEVER
Indicates that the target method will raise an exception if executed in a transactional process. This option is mostly not used in projects.
@Transactional (rollbackFor=Exception.class)
  • Default is rollbackFor=RunTimeException.class
  • In Spring, all API classes throw RuntimeException, which means if any method fails, the container will always rollback the ongoing transaction.
  • The problem is only with checked exceptions. So this option can be used to declaratively rollback a transaction if Checked Exception occurs.
@Transactional (noRollbackFor=IllegalStateException.class)
Indicates that a rollback should not be issued if the target method raises this exception.
@Transactional(timeout=60)
Defaults to the default timeout of the underlying transaction system.
Informs the tx manager about the time duration to wait for an idle tx before a decision is taken to rollback non-responsive transactions.
@Transactional: Service or DAO Layer?
The Service is the best place for putting @Transactional, service layer should hold the detail-level use case behavior for a user interaction that would logically go in a transaction.
There are a lot of CRUD applications that don't have any significant business logic for them having a service layer that just passes data through between the controllers and data access objects is not useful. In these cases we can put transaction annotation on Dao.
So in practice you can put them in either place, it's up to you.
Also if you put @Transactional in DAO layer and if your DAO layer is getting reused by different services then it will be difficult to put it on DAO layer as different services may have different requirements.
If your service layer is retrieving objects using Hibernate and let's say you have lazy initializations in your domain object definition then you need to have a transaction open in service layer else you will face LazyInitializationException thrown by the ORM.


1 comment: