https://github.com/altafjava/hibernate-interview-preparation
Best Hibernate interview preparation material with examples and sql outputs
https://github.com/altafjava/hibernate-interview-preparation
hibernate hibernate-interview hibernate-interview-question hibernate-interview-question-answers hibernate-jpa hibernate-orm hibernate6 interview-practice interview-preparation interview-questions orm
Last synced: 2 months ago
JSON representation
Best Hibernate interview preparation material with examples and sql outputs
- Host: GitHub
- URL: https://github.com/altafjava/hibernate-interview-preparation
- Owner: altafjava
- Created: 2022-10-19T05:52:27.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2022-11-06T12:53:37.000Z (over 3 years ago)
- Last Synced: 2025-10-15T18:20:34.649Z (8 months ago)
- Topics: hibernate, hibernate-interview, hibernate-interview-question, hibernate-interview-question-answers, hibernate-jpa, hibernate-orm, hibernate6, interview-practice, interview-preparation, interview-questions, orm
- Homepage:
- Size: 176 KB
- Stars: 2
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
1. JDBC vs Hibernate.
2. Session vs SessionFactory.
3. How to create Session & SessionFactory?
4. SessionFactory vs EntityManagerFactory.
5. What is Dialect?
6. What is an Entity Lifecycle?
Transient, Persistent/Managed, Detached, Removed
| | |
| --------------------- | ------------------------------------------------------ |
| Transient->Persistent | save(), persist(), saveOrUpdate() |
| Persistent->Transient | delete() |
| Detached->Persistent | merge(), update(), saveOrUpdate(), replicate(), lock() |
| Persistent->Removed | remove() |
| DB->Persistent | find(), get(), load() |
| Removed->Persistent | save(), persist() |
7. Can we create an Entity class without primary key columns?
8. What are the rules for creating an Entity class?
1. No-Arg Constructor
2. Provide an identifier properly
3. Declare getters/setters
4. Entity class should not be final because Hibernate uses proxies concept of lazy fetching
9. Is SessionFactory thread safe? Yes
10. Is Session thread safe? No
11. What is the CREATE TABLE query generated by Hibernate?
```sql
create table Employee (employeeId integer not null auto_increment, email varchar(255), firstName varchar(255), lastName varchar(255), primary key (employeeId)) engine=MyISAM
```
12. What is the INSERT query generated by Hibernate?
```sql
insert into Employee (email, firstName, lastName) values (?, ?, ?)
```
13. What are Entity Lifecycle events & callbacks?
Whenever we call methods in the Session interface to persist, update or delete the entities, the session generates an appropriate event based on the executed method and passes it to the configured event listener(s) for that type. The event types are declared as enum values on org.hibernate.event.spi.EventType. For example, when we persist an entity using the session.persist() method then an event EventType.PERSIST is generated. If there are any PersistEventListener implementation registered for that Entity then the event is passed to that listener for processing.
14. How to create EventListener?
Create a class and implements any the of the EventListerner interface and override its methods. For example PersistEventListener is the interface for PERSIST event.
```java
public void onPersist(PersistEvent persistEvent) throws HibernateException
public void onPersist(PersistEvent event, PersistContext createdAlready) throws HibernateException
```
15. How to register EventListener?
To register the event listeners, we need to create our own implementation of `org.hibernate.integrator.spi.Integrator` interface. The main use of Integrator is to register the event listeners. Create a class and implements `org.hibernate.integrator.spi.Integrator` and override its methods.
```java
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry)
public void integrate(Metadata metadata, BootstrapContext bootstrapContext, SessionFactoryImplementor sessionFactory)
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry)
```
The Integrator is then registered with the persistent context with the help of `BootstrapServiceRegistryBuilder`. Note that `BootstrapServiceRegistry` is intended to hold mainly 3 services that Hibernate needs at both bootstrap and run time.
1. ClassLoaderService
2. IntegratorService
3. StrategySelector.
After creating the `BootstrapServiceRegistery`, Now supply the custom `BootstrapServiceRegistry` instance to the `StandardServiceRegistry` and build the SessionFactory.
```java
BootstrapServiceRegistryBuilder bootstrapServiceRegistryBuilder = new BootstrapServiceRegistryBuilder();
bootstrapServiceRegistryBuilder.applyIntegrator(new AppIntegrator());
BootstrapServiceRegistry bootstrapServiceRegistry = bootstrapServiceRegistryBuilder.build();
StandardServiceRegistry standardServiceRegistry = new StandardServiceRegistryBuilder(bootstrapServiceRegistry).configure().build();
Metadata metadata = new MetadataSources(standardServiceRegistry).getMetadataBuilder().build();
SessionFactory sessionFactory = metadata.getSessionFactoryBuilder().build();
```
16. How to check Entity equality between Sessions?
Requesting a persistent/managed object again from the same Hibernate session returns the “same instance” of a class.
Requesting a persistent object from the different Hibernate sessions returns the "different instances" of a class.
Hence as a best practice, always implement equals() and hashCode() methods in the hibernate entities; and always compare them using equals() method only.
17. save()
This method is used to save Entity into DB and returns the primary key. This makes a TRANSIENT entity to PERSISTENT. We can use this method outside a transaction. Means without transaction also we can save the entity. If we have multiple entities with mapping among them(OTO, OTM, MTO, MTM) and if we are saving outside the transaction then only primary(parent) entity will be saved. Lets say We have Cart entity mapped with Item entity as One to Many. Cart can have multiple items. If we are saving cart without transaction then only Cart entity will be saved. Item entity will not be saved. Even we start the transaction and don’t flush or commit then still Item will not be saved. We must either flush or commit then mapped entity will be saved. Hence we should avoid save outside transaction boundary. This will led the data inconsistency problem.
We can store a DETACHED entity using this method. It will create a new primary key for that Detached entity but the associated entity will not create a new record(new primary key) ; rather for the associated entity it will execute an update query which does nothing.
```java
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
session.save(employee);
transaction.commit();
transaction = session.beginTransaction();
session.evict(employee);
session.save(employee);
transaction.commit();
```
```sql
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: insert into Address (city, zipcode, id) values (?, ?, ?)
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: update Address set city=?, zipcode=? where id=?
```
18. persist()
This method is also used to save the entity into DB(Transient to Persistent) but it does not return anything and it works only inside the transaction. If we have mapped entities and we have begun the transaction but did not flush or commit then only the parent entity will be saved not the child entity. To save all the mapped entities we must commit or flush the session. We cannot store a DETACHED entity using this method. We shall get exception
19. saveOrUpdate()
This method is used for either insert or update queries based on the provided data. If the data is present in the database(Means it is a Persistent entity) then an update query is executed. If the data is not present in the database(means it is a Transient entity) then insert query will be executed. If we have a Persistent entity and we did not change anything in those entities then nothing will happen(neither insert or update).
We can use this method without transaction too, but again we shall face the issues with mapped objects. Only the primary(parent) entity will be saved. If we want to save all the mapped entities then we must start the transaction and either flush or commit. It does not return anything.
We cannot store a DETACHED entity using this method. If we try to do so then it will execute an update query which does nothing.
20. update()
This method should be used where we know that we are only updating the entity information. This will only work with Persistent entities. If we are trying to update a Transient entity and a transaction is started and we are doing either flush or commit then we shall get an exception. If a transaction has started and we are doing flush/commit then nothing will happen.
If the entity is in Persistent state and we are modifying anything then nothing will happen. This method only works with transactions. We must start the transaction and either flush or commit. Otherwise nothing will happen. If we are updating the Persistent entity inside the transaction and the updated data is the same with the Persistent entity then it will not execute the update query. It will remain the same.
21. merge()
The main purpose of the merge method is to make a DETACHED entity to PERSISTENT. Without a transaction it does not work. We must start the transaction. This method is almost the same as saveOrUpdate except it returns the PERSISTENT entity and does not throw NonUniqueObjectException.
If we have a DETACHED entity Employee and we want to make this as PERSISTENT entity and that session already has and entity with the same id(primary key) with that DETACHED entity, we shall get NonUniqueObjectException if we shall use update() or saveOrUpdate() methods. But if we use the merge() method then it will work perfectly without any exception.
```java
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Employee employee = session.get(Employee.class, 2);
session.evict(employee);
employee.setFirstName("James");
Employee employee2 = session.get(Employee.class, 2);
// session.saveOrUpdate(employee); // NonUniqueObjectException
// session.update(employee); // NonUniqueObjectException
session.merge(employee);
Employee mergedEmployee = session.merge(employee);
```
If we are saving an TRANSIENT Employee entity which has an associated entity Address and we did not flush/commit then only the Employee entity will be saved. If we do either flush or commit then the associated entity Address will be saved.
If we want to update the Persistent entity then we must start the transaction and flush/commit then will work. If we are updating the Persistent entity inside the transaction and the updated data is the same with the Persistent entity then it will not execute the update query. It will remain the same. If we are updating the Persistent entity inside the transaction and the updated data is different with the Persistent entity then it will execute the update query.
22. Get vs Load
The entity loaded with `get()` method is eager loaded and contains information from the database. Whereas the entity loaded from `load()` method is a lazy load proxy object that queries the database when any field information is accessed. Hibernate internally uses ByteBuddy to create proxy classes at runtime.
In the case of the `get()` method, we will get the return value as NULL if the identifier is absent. But in the case of `load()` method, we will get an `ObjectNotFoundException`.
`load()` method is deprecated since Hibernate 6.0. It is recommended to use the getReference() method instead of the `load()` method.
```sql
Hibernate uses LEFT JOIN to fetch the single entity.
select e1_0.id,a1_0.id,a1_0.city,a1_0.zipcode,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0 left join Address a1_0 on e1_0.id=a1_0.id where e1_0.id=?
```
23. refresh()
Sometimes we face a situation when our application database is modified with some external application/agent and thus corresponding hibernate entity in our application actually becomes out of sync with its database representation i.e. having old data. In this case, we can use the session.refresh() method to re-populate the entity with the latest data available in the database.
24. delete()/remove()
Both methods are used to delete a single entity. If the passed entity is not present in the database then we shall get an ObjectNotFoundException. Both methods work on DETACHED entities as well if we are using Hibernate Session. But in the case of JPA, the EntityManager delete() method cannot delete the DETACHED entity. This will give IllegalArgumentException.
```java
EmployeeEntity employee = entityManager.find(Employee.class, 1L);
entityManager.detach(employee);
entityManager.remove(employee);
```
If there is any associated entity, then first associated entity will be deleted then the parent entity. delete() method is deprecated since Hibernate 6.0. It is recommended to use the remove() method.
```sql
Hibernate: delete from Address where id=?
Hibernate: delete from Employee where id=?
```
The EntityManager.remove() and Session.remove() methods are a better fit when we want to delete a single Entity but inefficient if we want to remove a list of entities. For each remove() call, hibernate loads the entity, performs the lifecycle transition to REMOVED and triggers the SQL DELETE operation. Executing N different SQL DELETE queries for N entities will lead to very inefficient performance. It’s often better to remove such a list of entities with a JPQL query.
We should flush all the pending changes to the database before executing the query so that we are making changes to the latest data. And we also minimize the risk of stale first-level cache.
```java
session.flush();
session.clear();
//Get list of Ids
List ids = getIdsToDelete();
//Remove all entities
Query query = em.createQuery("DELETE EmployeeEntity e WHERE id IN (:ids)");
query.setParameter("ids", ids);
query.executeUpdate();
```
25. @Entity
This annotation is used to make the POJO class as JPA Entity.
Entity class must have a no-argument constructor without private access modifier, otherwise we shall get NoSuchMethodException.
Entity class must not be an abstract class because Hibernate creates the object of Entity class by using newInstance() method and as we know Abstract class cannot be instantiated.
We can declare Entity class as final but this is not recommended. Hibernate uses the proxy pattern for performance improvement during lazy association. By making an entity final, Hibernate will no longer be able to use a proxy as Java doesn't allow the final class to be extended.
26. @Id
Each entity bean has to have a primary key, which we annotate on the class with the @Id annotation. We can use this annotation either in a field or in a setter method.
27. @GeneratedValue
This annotation is used when we want Hibernate to assign the entity identifier automatically using either AUTO, IDENTITY, SEQUENCE, TABLE. If we don’t use this annotation, then the identifier must be manually assigned before saving the entity.
_AUTO:_ Indicates that the persistence provider should pick an appropriate strategy for the particular database.
_IDENTITY:_ Indicates that the persistence provider must assign the primary key for the entity using a database identity column. Ex: MySQL
_SEQUENCE:_ Indicates that the persistence provider must assign primary keys for the entity using a database sequence. It uses @SequenceGenerator. Ex: Oracle
_TABLE:_ Indicates that the persistence provider must assign primary keys for the entity using an underlying database table to ensure uniqueness. In this case, one extra table is created by Hibernate with the name hibernate_sequences and there will be two columns sequence_name and next_val. It internally uses @TableGenerator.
28. @SequenceGenerator
A sequence is a database object that can be used as a source of primary key values. It is similar to the use of an identity column type, except that a sequence is independent of any particular table and can therefore be used by multiple tables. Some of the databases don’t support sequences. In this case the database sequence concept will not be utilized and it creates a new table like @TableGenerator.
```java
@Id
@SequenceGenerator(name="myseq",sequenceName="HIB_SEQ")
@GeneratedValue(strategy=SEQUENCE,generator="seq")
private Integer eid;
```
In this annotation name attribute is mandatory. A new table will be created with the name `hib_seq`. If we don’t provide the sequenceName attribute then myseq table will be created.
29. @TableGenerator
This annotation is used in a very similar way to the @SequenceGenerator annotation, but because it manipulates a standard database table to obtain its primary key values, instead of using a vendor-specific sequence object, it is guaranteed to be portable between database platforms. It is the same as `@GeneratedValue(strategy = GenerationType.TABLE)`. It creates a table with two columns `sequence_name` and `next_val`.
```java
@Id
@GeneratedValue(strategy=GenerationType.TABLE,generator="employee_generator")
@TableGenerator(name="employee_generator", table="pk_table",
pkColumnName="name", valueColumnName="value", allocationSize=100)
private Integer eid;
```
Only the name attribute is mandatory. Rest are optional.
30. @Embeddable/@Embedded
We can use these annotations to embed one entity inside another entity, so they are mapped to a single table. @Embeddable is used to mark a class to be eligible as an embedded class. @Embedded is used in the field/attribute inside the parent class. We can change the column name of the embeddable class inside the parent class using these annotations. `@AttributeOverrides` & `@AttributeOverride`.
```java
@Entity
Class Employee{
@Embedded
@AttributeOverrides(value = @AttributeOverride(name = "city",
column = @Column(name = "village")))
private Address address;
}
@Embeddable
Class Address{
}
```
## Association/Mappings
31. @OneToOne
There are primarily 4 ways to create one-to-one relationships between two entities.
1. The first technique is widely used and uses a foreign key column in one of the tables.
2. The second technique uses a rather known solution of having a join table to store the mapping between the first two tables.
3. The third technique is something new that uses a common primary key in both tables.
4. Using @MapsId annotation that uses common primary key in both the tables but different from 3rd approach. It copies primary key from child table and put into parent table as foreign key.
**Using a Foreign Key Association/Join Column:** In this kind of association, a foreign key column is created in the owner entity. For example, we have made `Employee` entity as an owner, then an extra column `accountId` will be created in the `employee` table. This column will store the foreign key for the Account table.
```java
@Table
@Entity
public class Employee implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int eid;
private String firstName;
private String lastName;
private double salary;
@OneToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "accountId") // accountId will become foreign in employee table
private Account account;
}
```
```java
@Table
@Entity
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int aid;
private String accountNo;
private String branch;
}
```
```java
Employee employee = new Employee();
employee.setFirstName("David");
employee.setLastName("Warner");
employee.setSalary(56789);
Account account=new Account();
account.setAccountNo("ACC123");
account.setBranch("Australia");
employee.setAccount(account);
session.persist(employee);
```
In the `Employee` class if don't cascade the `Account` class then we shall get an exception saying:
```java
Exception in thread "main" java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.altafjava.entity.Account
```
If we want no cascading then 1st we need to persist the Account entity then persist the Employee entity.
```java
Account account=new Account();
account.setAccountNo("ACC123");
account.setBranch("Australia");
session.persist(account);
Employee employee = new Employee();
employee.setFirstName("David");
employee.setLastName("Warner");
employee.setSalary(56789);
employee.setAccount(account);
session.persist(employee);
```
Generated Query:
```sql
Hibernate: create table Account (aid integer not null auto_increment, accountNo varchar(255), branch varchar(255), primary key (aid)) engine=MyISAM
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, accountId integer, primary key (eid)) engine=MyISAM
Hibernate: alter table Employee add constraint FKcs7jiub5myswnmgqtnp1uj1fi foreign key (accountId) references Account (aid)
Hibernate: insert into Account (accountNo, branch) values (?, ?)
Hibernate: insert into Employee (accountId, firstName, lastName, salary) values (?, ?, ?, ?)
Hibernate: select e1_0.eid,a1_0.aid,a1_0.accountNo,a1_0.branch,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0 left join Account a1_0 on a1_0.aid=e1_0.accountId where e1_0.eid=?
```
If we don't write `@JoinColumn` in the owner entity `Employee` then defaults apply. A new column will be created in the parent table `Employee` with name `account_aid`. Means concatenation of child table name(account), underscore(\_) and child table primary key(aid).
```sql
Hibernate: alter table Employee add constraint FKf5cbit5cfn86kiuergvpbidcr foreign key (account_aid) references Account (aid)
```
In a bidirectional association, only one of the sides has to be the owner. The owner is responsible for the association column(s) update. The child entity who is not responsible of managing the relationship uses `mappedBy` attribute in @OneToOne annotation. The `mappedBy` refers to the property name which is declared in the owner's side.
```java
public class Account {
private int aid;
private String accountNo;
private String branch;
@OneToOne(mappedBy = "account")
private Employee employee;
}
```
If we don't use this `mappedBy` attribute then both the table will become owner table and stores the foreign key of each others. Employee will store Account's primary and Account will store Employee's primary key.
```sql
Hibernate: create table Account (aid integer not null auto_increment, accountNo varchar(255), branch varchar(255), employee_eid integer, primary key (aid)) engine=MyISAM
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, accountId integer, primary key (eid)) engine=MyISAM
Hibernate: alter table Account add constraint FKehpisc8myx4mq7peeu83gl5c4 foreign key (employee_eid) references Employee (eid)
Hibernate: alter table Employee add constraint FKrwcsuf6gqdo5nbhro5avjqg6y foreign key (accountId) references Account (aid)
Hibernate: insert into Account (accountNo, branch, employee_eid) values (?, ?, ?)
Hibernate: insert into Employee (accountId, firstName, lastName, salary) values (?, ?, ?, ?)
```
**Using a link table/Join Table:** In this approach, Hibernate will create a new table/link table that will store the primary key values from both the entities. In this technique `@JoinTable` is used. This annotation is used to define the new table and foreign keys from both of the tables.
```java
@Table
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int eid;
private String firstName;
private String lastName;
private double salary;
@OneToOne(cascade = CascadeType.PERSIST)
@JoinTable(name = "employee_account", joinColumns = @JoinColumn(name = "employeeId"), inverseJoinColumns = @JoinColumn(name = "accountId"))
private Account account;
}
```
```java
@Table
@Entity
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int aid;
private String accountNo;
private String branch;
}
```
```sql
Hibernate: create table Account (aid integer not null auto_increment, accountNo varchar(255), branch varchar(255), primary key (aid)) engine=MyISAM
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, primary key (eid)) engine=MyISAM
Hibernate: create table employee_account (accountId integer, employeeId integer not null, primary key (employeeId)) engine=MyISAM
Hibernate: alter table employee_account add constraint FKcf0q6b690ussmm11u4s08nixj foreign key (accountId) references Account (aid)
Hibernate: alter table employee_account add constraint FKnvb8g9m5qlhk1752cq9muifk7 foreign key (employeeId) references Employee (eid)
Hibernate: insert into Account (accountNo, branch) values (?, ?)
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: insert into employee_account (accountId, employeeId) values (?, ?)
```
`@JoinTable` annotation is used in `Employee` entity class. It declares that a new table `employee_account` will be created with two columns `employeeId` (primary key of employee table) and `accountId` (primary key of account table). In this annotation `name` attribute is mandatory, rest are optional. So, if we write only
```java
@JoinTable(name="employee_account")
private Account account;
```
then still it will work. It will create column names as `account_aid`(concatenation of child table name, underscore and primary key of its table) & `eid`(primary key of the owner entity).
If we want bidirectional association then in the child entity we can use either `@OneToOne(mappedBy = "account")` or `@JoinTable` with `@OneToOne`. If we want to use `@JoinTable` then we must use `@OneToOne` with or without `mappedBy`. If we only use `@JoinTable` without `@OneToOne` then one extra column `employee` will be created in the account table with empty value.
```java
public class Account {
private int aid;
private String accountNo;
private String branch;
@OneToOne(mappedBy = "account")
// @JoinTable(name = "employee_account", joinColumns = @JoinColumn(name = "accountId"), inverseJoinColumns = @JoinColumn(name = "employeeId"))
private Employee employee;
}
```
If we don't write `mappedBy` attribute then `Account` entity will create an extra column `employee_eid` as foreign key and store Employee's primary key.
```sql
Hibernate: create table Account (aid integer not null auto_increment, accountNo varchar(255), branch varchar(255), employee_eid integer, primary key (aid)) engine=MyISAM
Hibernate: alter table Account add constraint FKejw579y4swv5plx8wfxh4bxk5 foreign key (employee_eid) references Employee (eid)
```
**Using a Shared Primary Key:** In this technique, Hibernate will ensure that it will use a common primary key value in both tables. This way primary key of `Employee` Entity can safely be assumed the primary key of `Account` Entity also. In this approach, `@PrimaryKeyJoinColumn` is the main annotation to be used in the owner's entity.
```java
@Table
@Entity
public class Employee implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int eid;
private String firstName;
private String lastName;
private double salary;
@OneToOne(cascade = CascadeType.PERSIST)
@PrimaryKeyJoinColumn
private Account account;
}
```
```java
@Table
@Entity
public class Account implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int aid;
private String accountNo;
private String branch;
}
```
```sql
Hibernate: create table Account (aid integer not null auto_increment, accountNo varchar(255), branch varchar(255), primary key (aid)) engine=MyISAM
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, primary key (eid)) engine=MyISAM
Hibernate: insert into Account (accountNo, branch) values (?, ?)
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
```
If we want bidirectional association then in the child entity we need to use `@OneToOne(mappedBy = "account")`.
```java
public class Account {
private int aid;
private String accountNo;
private String branch;
@OneToOne(mappedBy = "account")
private Employee employee;
}
```
```java
Employee employee = new Employee();
employee.setFirstName("David");
employee.setLastName("Warner");
employee.setSalary(56789);
Account account = new Account();
account.setAccountNo("ACC123");
account.setBranch("Australia");
employee.setAccount(account); // this is enough for bidirectional association
// account.setEmployee(employee); // this line is optional
session.persist(employee);
```
If we don't write `mappedBy` attribute then `Account` entity will create an extra column `employee_eid` as foreign key and store Employee's primary key.
```sql
Hibernate: create table Account (aid integer not null auto_increment, accountNo varchar(255), branch varchar(255), employee_eid integer, primary key (aid)) engine=MyISAM
Hibernate: alter table Account add constraint FKejw579y4swv5plx8wfxh4bxk5 foreign key (employee_eid) references Employee (eid)
```
**Using a Shared Primary Key:** In this technique, Hibernate assumes both the source and target share the same primary key values. For that we use `@MapsId` annotation. The parent-side association becomes redundant(its primary key will no longer be needed).
```java
@Table
@Entity
public class Employee implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int eid;
private String firstName;
private String lastName;
private double salary;
@OneToOne(cascade = CascadeType.PERSIST)
@MapsId
private Account account;
}
```
```java
@Table
@Entity
public class Account implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int aid;
private String accountNo;
private String branch;
@OneToOne(mappedBy = "account")
private Employee employee;
}
```
```sql
Hibernate: create table Account (aid integer not null auto_increment, accountNo varchar(255), branch varchar(255), primary key (aid)) engine=MyISAM
Hibernate: create table Employee (firstName varchar(255), lastName varchar(255), salary float(53) not null, account_aid integer not null, primary key (account_aid)) engine=MyISAM
Hibernate: alter table Employee add constraint FKf5cbit5cfn86kiuergvpbidcr foreign key (account_aid) references Account (aid)
Hibernate: insert into Account (accountNo, branch) values (?, ?)
Hibernate: insert into Employee (firstName, lastName, salary, account_aid) values (?, ?, ?, ?)
```
**Note:** For bidirectional association any of the technique it is must to write `@OneToOne` annotation either with or without `mappedBy` attribute. If not then we shall get exception:
```java
Exception in thread "main" jakarta.persistence.PersistenceException: Converting `org.hibernate.exception.DataException` to JPA `PersistenceException` : could not execute statement
Caused by: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'employee' at row 1
```
32. @OneToMany
We can implement one to many association in 2 ways.
1. Using foreign key column.
2. Using link table.
**Using foreign key column:** In this approach child table(Account) column will refer to the primary key of parent table(Employee).
```java
@Table
@Entity
public class Employee implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int eid;
private String firstName;
private String lastName;
private double salary;
@OneToMany(cascade = CascadeType.PERSIST, mappedBy = "employee")
private List accounts;
}
```
```java
@Entity
@Table
public class Account implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int aid;
private String accountNo;
private String branch;
@ManyToOne
@JoinColumn(name = "employeeId")
private Employee employee;
}
```
```java
Employee employee = new Employee();
employee.setFirstName("David");
employee.setLastName("Warner");
employee.setSalary(56789);
Account account = new Account();
account.setAccountNo("ACC123");
account.setBranch("Australia");
account.setEmployee(employee);
List accounts = new ArrayList<>();
accounts.add(account);
account = new Account();
account.setAccountNo("BCC567");
account.setBranch("New Zealand");
accounts.add(account);
employee.setAccounts(accounts);
account.setEmployee(employee);
Transaction transaction = session.beginTransaction();
session.persist(employee);
transaction.commit();
Employee employee2 = session.get(Employee.class, 1);
```
```sql
Hibernate: create table Account (aid integer not null auto_increment, accountNo varchar(255), branch varchar(255), employeeId integer, primary key (aid)) engine=MyISAM
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, primary key (eid)) engine=MyISAM
Hibernate: alter table Account add constraint FKbkyxfxqy2qresbaghdbm5xtty foreign key (employeeId) references Employee (eid)
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: insert into Account (accountNo, branch, employeeId) values (?, ?, ?)
Hibernate: insert into Account (accountNo, branch, employeeId) values (?, ?, ?)
-: SELECT QUERIES :-
Hibernate: select e1_0.eid,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0 where e1_0.eid=?
Hibernate: select a1_0.employeeId,a1_0.aid,a1_0.accountNo,a1_0.branch from Account a1_0 where a1_0.employeeId=?
```
**Using link table/Join Table:** This approach uses the `@JoinTable` annotation to create a link table that stores the foreign keys of both the tables.
```java
@Entity
public class Employee implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int eid;
private String firstName;
private String lastName;
private double salary;
@OneToMany(cascade = CascadeType.PERSIST)
@JoinTable(name = "empacc", joinColumns = @JoinColumn(name = "employeeId"), inverseJoinColumns = @JoinColumn(name = "accountId"))
private List accounts;
}
```
```java
@Entity
public class Account implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int aid;
private String accountNo;
private String branch;
@ManyToOne
@JoinTable(name = "empacc", joinColumns = @JoinColumn(name = "accountId"), inverseJoinColumns = @JoinColumn(name = "employeeId"))
private Employee employee;
}
```
```java
Employee employee = new Employee();
employee.setFirstName("David");
employee.setLastName("Warner");
employee.setSalary(56789);
Account account = new Account();
account.setAccountNo("ACC123");
account.setBranch("Australia");
List accounts = new ArrayList<>();
accounts.add(account);
account = new Account();
account.setAccountNo("BCC567");
account.setBranch("New Zealand");
accounts.add(account);
employee.setAccounts(accounts);
Transaction transaction = session.beginTransaction();
session.persist(employee);
transaction.commit();
```
```sql
Hibernate: create table Account (aid integer not null auto_increment, accountNo varchar(255), branch varchar(255), primary key (aid)) engine=MyISAM
Hibernate: create table empacc (employeeId integer, accountId integer not null, primary key (accountId)) engine=MyISAM
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, primary key (eid)) engine=MyISAM
Hibernate: alter table empacc add constraint FKp2jrrkjsg7pyp97t7xn0480yj foreign key (employeeId) references Employee (eid)
Hibernate: alter table empacc add constraint FK897b88v7cuvxmstxhqmad0dbe foreign key (accountId) references Account (aid)
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: insert into Account (accountNo, branch) values (?, ?)
Hibernate: insert into Account (accountNo, branch) values (?, ?)
Hibernate: insert into empacc (employeeId, accountId) values (?, ?)
Hibernate: insert into empacc (employeeId, accountId) values (?, ?)
```
33. @ManyToMany
A many-to-many association is made between two entities where many entities can be associated with multiple other entities. For example, for a subscription service, `Subscription` Entity and `Reader` Entity can be two types of entities. A given subscription can have multiple readers, whereas a reader can subscribe to multiple subscriptions. Many to many association requires a link table that joins two entities. It stores the foreign key of both the tables. This annotation doesn't require `@JoinTable`. `@ManyToMany` annotation is enough to create the link table.
```java
@Entity
public class Reader implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int rid;
private String readerName;
private String email;
@ManyToMany(cascade = CascadeType.PERSIST)
private List subscriptions;
}
```
```java
@Entity
public class Subscription implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int sid;
private String subscriptionName;
}
```
```java
Subscription subscription1 = new Subscription();
subscription1.setSubscriptionName("Youtube");
Subscription subscription2 = new Subscription();
subscription2.setSubscriptionName("Netflix");
List subscriptions = Arrays.asList(subscription1, subscription2);
Reader reader = new Reader();
reader.setEmail("abc@gmail.com");
reader.setReaderName("David");
reader.setSubscriptions(subscriptions);
Transaction transaction = session.beginTransaction();
session.persist(reader);
transaction.commit();
```
```sql
Hibernate: create table Reader (rid integer not null auto_increment, email varchar(255), readerName varchar(255), primary key (rid)) engine=MyISAM
Hibernate: create table Reader_Subscription (Reader_rid integer not null, subscriptions_sid integer not null) engine=MyISAM
Hibernate: create table Subscription (sid integer not null auto_increment, subscriptionName varchar(255), primary key (sid)) engine=MyISAM
Hibernate: alter table Reader_Subscription add constraint FKs066tyf8j84vwtv387hbboow9 foreign key (subscriptions_sid) references Subscription (sid)
Hibernate: alter table Reader_Subscription add constraint FKld5v1st16r4u53vufetlbodh5 foreign key (Reader_rid) references Reader (rid)
Hibernate: insert into Reader (email, readerName) values (?, ?)
Hibernate: insert into Subscription (subscriptionName) values (?)
Hibernate: insert into Subscription (subscriptionName) values (?)
Hibernate: insert into Reader_Subscription (Reader_rid, subscriptions_sid) values (?, ?)
Hibernate: insert into Reader_Subscription (Reader_rid, subscriptions_sid) values (?, ?)
```
If we want bidirectional association then in the child class we need to use `@ManyToMany` with `mappedBy` attribute. Rest everything will be same. Even generated SQL query will be same.
```java
@Entity
public class Subscription implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int sid;
private String subscriptionName;
@ManyToMany(mappedBy = "subscriptions")
List readers;
}
```
34. @ManyToOne
A many-to-one association is made between two entities where many entities can be associated with one entity. For example, Many students work on a single project. In this association, many side will be the owner and creates a foreign key column in its table.
```java
@Entity
public class Student implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int sid;
private String name;
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "projectId")
private Project project;
}
```
```java
@Entity
@Data
@ToString(exclude = "students")
public class Project implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int pid;
private String projectName;
@OneToMany(mappedBy = "project")
private List students;
}
```
```java
Transaction transaction = session.beginTransaction();
Project project = new Project();
project.setProjectName("HMS");
Student student1 = new Student();
student1.setName("Ajay");
student1.setProject(project);
session.persist(student1);
Student student2 = new Student();
student2.setName("David");
student2.setProject(project);
session.persist(student2);
transaction.commit();
```
```sql
Hibernate: create table Project (pid integer not null auto_increment, projectName varchar(255), primary key (pid)) engine=MyISAM
Hibernate: create table Student (sid integer not null auto_increment, name varchar(255), projectId integer, primary key (sid)) engine=MyISAM
Hibernate: alter table Student add constraint FKb3wn40e1o66egelusymas8fvo foreign key (projectId) references Project (pid)
Hibernate: insert into Project (projectName) values (?)
Hibernate: insert into Student (name, projectId) values (?, ?)
Hibernate: insert into Student (name, projectId) values (?, ?)
```
35. @Temporal
By default, `java.util.Date` & `java.util.Calendar` will be stored in a column with the `TIMESTAMP` data type, but this default behavior can be overridden with the `@Temporal` annotation. This annotation accepts a single value attribute from the `jakarta.persistence.TemporalType` enum. This offers 3 possible values: `DATE`, `TIME`, and `TIMESTAMP`. These correspond, respectively to `java.sql.Date`, `java.sql.Time` and `java.sql.Timestamp`. The table column is given the appropriate data type at the time of schema generation.
```java
@Entity
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String title;
private String content;
@Temporal(TemporalType.DATE)
private Date date;
@Temporal(TemporalType.TIME)
private Calendar calendar;
private LocalDate updatedDate;
private LocalTime updatedTime;
private LocalDateTime publishedTimestamp;
}
```
Since Java 8 has introduced new date/time API that can be mapped directly to SQL types. We don't need to use `@Temporal` annotation.
1. `java.time.LocalDate` can be converted into sql `DATE` type.
2. `java.time.LocalTime` can be converted into sql `TIME` type.
3. `java.time.LocalDateTime` can be converted into sql `TIMESTAMP` type.
36. @ElementCollection
This annotation is almost same as `@OneToMany`. It creates a a foreign key in the child side and stores the primary key of the owner entity but the difference is that the child side should be non-mapping class `Embeddable` or `Basic`. The child side should not be an Entity. The elements of child side are completely owned by the containing entities(Owner). They are modified when the owner entity is modified, deleted when the owner entity is deleted, etc. They can't have their own lifecycle.
```java
@Entity
public class State {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@ElementCollection
private List cities;
}
```
```java
State state = new State();
state.setName("Telangana");
List cities = List.of("Hyderabad", "Secundarabad", "Nalgonda");
state.setCities(cities);
Transaction transaction = session.beginTransaction();
session.persist(state);
transaction.commit();
```
```sql
Hibernate: create table State (id integer not null auto_increment, name varchar(255), primary key (id)) engine=MyISAM
Hibernate: create table State_cities (State_id integer not null, cities varchar(255)) engine=MyISAM
Hibernate: alter table State_cities add constraint FK8dvxj5lk3fvf14fclwj7ber1p foreign key (State_id) references State (id)
Hibernate: insert into State (name) values (?)
Hibernate: insert into State_cities (State_id, cities) values (?, ?)
Hibernate: insert into State_cities (State_id, cities) values (?, ?)
Hibernate: insert into State_cities (State_id, cities) values (?, ?)
```
```java
@Entity
public class Employee {
@Id
private long id;
@ElementCollection
private List phones;
}
@Embeddable
public class Phone {
private String type;
private String areaCode;
private String number;
}
```
37. @Lob
A persistent property or field can be marked for persistence as a database-supported large object type by applying the `@Lob` annotation. The annotation takes no attributes, but the underlying large object type to be used will be inferred from the type of the field or parameter. String- and character-based types will be stored in an appropriate character-based type i.e. `CLOB`. All other objects will be stored in a `BLOB`. This annotation can be used in combination with the `@Basic` or the `@ElementCollection` annotation.
```java
@Entity
@Data
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String title;
@Lob
private String content;
}
Transaction transaction = session.beginTransaction();
Article article = new Article();
article.setTitle("Title");
article.setContent("A very long article content");
session.persist(article);
transaction.commit();
```
```sql
Hibernate: create table Article (id integer not null auto_increment, content tinytext, title varchar(255), primary key (id)) engine=MyISAM
Hibernate: insert into Article (content, title) values (?, ?)
```
38. @OrderBy
This annotation specifies the ordering of the elements of a collection valued association or element collection at the point when the association or collection is retrieved from database.
```java
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String courseName;
@ManyToMany(cascade = CascadeType.ALL)
@OrderBy("name")
private List students;
}
```
When we fetch list of students from database then it uses `SELECT` query with `order by` clause.
```java
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@ManyToMany(mappedBy = "students")
private List courses;
}
```
```java
Student student1 = new Student();
student1.setName("David");
Student student2 = new Student();
student2.setName("Zahid");
Student student3 = new Student();
student3.setName("Ajay");
Course course = new Course();
course.setCourseName("Java");
course.setStudents(List.of(student1, student2, student3));
Transaction transaction = session.beginTransaction();
session.persist(course);
transaction.commit();
```
```sql
Hibernate: create table Course (id integer not null auto_increment, courseName varchar(255), primary key (id)) engine=MyISAM
Hibernate: create table Course_Student (courses_id integer not null, students_id integer not null) engine=MyISAM
Hibernate: create table Student (id integer not null auto_increment, name varchar(255), primary key (id)) engine=MyISAM
Hibernate: alter table Course_Student add constraint FKhsjbkfb9v2y4f2h02q14ft5pd foreign key (students_id) references Student (id)
Hibernate: alter table Course_Student add constraint FKs7yq2swrenbdpvkwx01j9cg1s foreign key (courses_id) references Course (id)
Hibernate: insert into Course (courseName) values (?)
Hibernate: insert into Student (name) values (?)
Hibernate: insert into Student (name) values (?)
Hibernate: insert into Student (name) values (?)
Hibernate: insert into Course_Student (courses_id, students_id) values (?, ?)
Hibernate: insert into Course_Student (courses_id, students_id) values (?, ?)
Hibernate: insert into Course_Student (courses_id, students_id) values (?, ?)
```
```java
Course course2 = session.get(Course.class, 1);
if (course2 == null) {
System.out.println(null);
} else {
System.out.println(course2);
List students = course2.getStudents();
for (Student student : students) {
System.out.println(student);
}
}
```
```sql
Hibernate: select c1_0.id,c1_0.courseName from Course c1_0 where c1_0.id=?
Hibernate: select s1_0.courses_id,s1_1.id,s1_1.name from Course_Student s1_0 join Student s1_1 on s1_1.id=s1_0.students_id where s1_0.courses_id=? order by s1_1.name
Course(id=1, courseName=Java, students=[Student(id=3, name=Ajay), Student(id=1, name=David), Student(id=2, name=Zahid)])
Student(id=3, name=Ajay)
Student(id=1, name=David)
Student(id=2, name=Zahid)
```
If `ASC` or `DESC` is not specified, `ASC` (ascending order) is assumed. If the ordering element is not specified for an entity association, ordering by the primary key of the associated entity is assumed. The property or field name must correspond to that of a persistent property or field of the associated class or embedded class within it. The properties or fields used in the ordering must correspond to columns for which comparison operators are supported. The `@OrderBy` annotation is not used when an `@OrderColumn` is specified.
The dot (".") notation is used to refer to an attribute within an embedded attribute. The value of each identifier used with the dot notation is the name of the respective embedded field or property.
```java
@Embeddable
public class Zipcode {
protected String zip;
protected int areaCode;
}
@Embeddable
public class Address {
protected String street;
protected String city;
protected String state;
@Embedded
protected Zipcode zipcode;
}
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@ElementCollection
@OrderBy("zipcode.zip DESC")
public Set
addresses;
}
```
```java
Zipcode zipcode1 = new Zipcode();
zipcode1.setAreaCode(51);
zipcode1.setZip("765478");
Address address1 = new Address();
address1.setCity("Hyderabad");
address1.setState("Telangana");
address1.setStreet("Tiger chowk");
address1.setZipcode(zipcode1);
Zipcode zipcode2 = new Zipcode();
zipcode2.setAreaCode(24);
zipcode2.setZip("123456");
Address address2 = new Address();
address2.setCity("Bengaluru");
address2.setState("Karnataka");
address2.setStreet("Howra bridge");
address2.setZipcode(zipcode2);
Person person = new Person();
person.setName("Ajay");
person.setAddresses(Set.of(address1, address2, address3));
Transaction transaction = session.beginTransaction();
session.persist(person);
transaction.commit();
```
```sql
Hibernate: create table Person (id integer not null auto_increment, name varchar(255), primary key (id)) engine=MyISAM
Hibernate: create table Person_addresses (Person_id integer not null, city varchar(255), state varchar(255), street varchar(255), areaCode integer not null, zip varchar(255)) engine=MyISAM
Hibernate: alter table Person_addresses add constraint FKbs25rlvs45qpe83fidi1x1nj0 foreign key (Person_id) references Person (id)
Hibernate: insert into Person (name) values (?)
Hibernate: insert into Person_addresses (Person_id, city, state, street, areaCode, zip) values (?, ?, ?, ?, ?, ?)
Hibernate: insert into Person_addresses (Person_id, city, state, street, areaCode, zip) values (?, ?, ?, ?, ?, ?)
```
```java
Person person2 = session.get(Person.class, 1);
System.out.println(person2);
```
```sql
Hibernate: select p1_0.id,p1_0.name from Person p1_0 where p1_0.id=?
Hibernate: select a1_0.Person_id,a1_0.city,a1_0.state,a1_0.street,a1_0.areaCode,a1_0.zip from Person_addresses a1_0 where a1_0.Person_id=? order by a1_0.zip
Person(id=1, name=Ajay, addresses=[Address(street=Tiger chowk, city=Hyderabad, state=Telangana, zipcode=Zipcode(zip=765478, areaCode=51)), Address(street=Howra bridge, city=Bengaluru, state=Karnataka, zipcode=Zipcode(zip=123456, areaCode=24))])
```
39. @OrderColumn
This annotation defines an additional column in the child table(many side) which stores the order of the items from zero onwards. For each parent record it starts from zero.
```java
@Entity
public class CreditCard {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int cid;
private String name;
private String number;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "creditCard")
@OrderColumn(name = "transactionsOrder")
List cardTransactions;
}
@Entity
public class CardTransaction {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int tid;
private double amount;
private String date;
@ManyToOne
@JoinColumn(name = "creditCardId")
private CreditCard creditCard;
}
```
```java
CreditCard creditCard = new CreditCard();
creditCard.setName("Samar");
creditCard.setNumber("1234567890123456");
CardTransaction cardTransaction1 = new CardTransaction();
cardTransaction1.setAmount(567);
cardTransaction1.setDate("2022-11-25");
cardTransaction1.setCreditCard(creditCard);
CardTransaction cardTransaction2 = new CardTransaction();
cardTransaction2.setAmount(984);
cardTransaction2.setDate("2021-04-18");
cardTransaction2.setCreditCard(creditCard);
creditCard.setCardTransactions(List.of(cardTransaction1, cardTransaction2));
Transaction transaction = session.beginTransaction();
session.persist(creditCard);
transaction.commit();
```
```sql
Hibernate: create table CardTransaction (tid integer not null auto_increment, amount float(53) not null, date varchar(255), creditCardId integer, transactionsOrder integer, primary key (tid)) engine=MyISAM
Hibernate: create table CreditCard (cid integer not null auto_increment, name varchar(255), number varchar(255), primary key (cid)) engine=MyISAM
Hibernate: alter table CardTransaction add constraint FK4uq1rd0tljxhx811rppbo39t9 foreign key (creditCardId) references CreditCard (cid)
Hibernate: insert into CreditCard (name, number) values (?, ?)
Hibernate: insert into CardTransaction (amount, creditCardId, date) values (?, ?, ?)
Hibernate: insert into CardTransaction (amount, creditCardId, date) values (?, ?, ?)
Hibernate: update CardTransaction set transactionsOrder=? where tid=?
Hibernate: update CardTransaction set transactionsOrder=? where tid=?
```
So it will store `0` in the 1st row & `1` in the 2nd row of the `cardtransaction` table and in the column `transactionsOrder`. And accordingly it will fetch the data.
```sql
Hibernate: select c1_0.cid,c1_0.name,c1_0.number from CreditCard c1_0 where c1_0.cid=?
Hibernate: select c1_0.creditCardId,c1_0.transactionsOrder,c1_0.tid,c1_0.amount,c1_0.date from CardTransaction c1_0 where c1_0.creditCardId=?
```
By default, the column can contain null (unordered) values. The nullability can be overridden by setting the nullable attribute to false. By default, when the schema is generated from the annotations, the column is assumed to be an integer type; however, this can be overridden by supplying a columnDefinition attribute specifying a different column definition string.
40. CascadeType
When an entity is associated with another entity in any type of association(1:1, 1:N, N:1, N:N) then any change in the owner entity will reflect to the child entity according to the cascade type. If we save an `Employee` then all associated `Accounts` will also be saved into database. If we delete an `Employee` then all `Accounts` associated with that `Employee` also be deleted.
```java
@OneToMany(cascade = CascadeType.PERSIST, mappedBy = "employee")
private List accounts;
```
JPA provides various cascade types like CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.REMOVE, CascadeType.DETATCH, CascadeType.ALL. There is no default cascade type in JPA. By default no operation is cascaded. The cascade configuration option accepts an array of `CascadeType`. Thus to include more than one cascade type we can use like:
```java
@OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "employee")
private List accounts;
```
41. CascadeType.REMOVE vs Orphan Removal
`CascadeType.REMOVE` is a database-specific thing. This is a way to delete a child entity or entities whenever the deletion of its parent happens.
```java
public class Employee implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int eid;
private String firstName;
private String lastName;
private double salary;
@OneToMany(cascade = { CascadeType.REMOVE, CascadeType.PERSIST }, mappedBy = "employee")
private List accounts;
}
public class Account implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int aid;
private String accountNo;
private String branch;
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "employeeId")
private Employee employee;
}
```
```java
Employee employee = new Employee();
employee.setFirstName("David");
employee.setLastName("Warner");
employee.setSalary(56789);
Account account = new Account();
account.setAccountNo("ACC123");
account.setBranch("Australia");
account.setEmployee(employee);
List accounts = new ArrayList<>();
accounts.add(account);
account = new Account();
account.setAccountNo("BCC567");
account.setBranch("New Zealand");
accounts.add(account);
employee.setAccounts(accounts);
account.setEmployee(employee);
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
session.persist(employee);
transaction.commit();
session.close();
Session session2 = sessionFactory.openSession();
Transaction transaction2 = session2.beginTransaction();
Employee employee2 = session2.get(Employee.class, 1);
session2.remove(employee2);
transaction2.commit();
session2.close();
```
```sql
Hibernate: create table Account (aid integer not null auto_increment, accountNo varchar(255), branch varchar(255), employeeId integer, primary key (aid)) engine=MyISAM
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, primary key (eid)) engine=MyISAM
Hibernate: alter table Account add constraint FKbkyxfxqy2qresbaghdbm5xtty foreign key (employeeId) references Employee (eid)
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: insert into Account (accountNo, branch, employeeId) values (?, ?, ?)
Hibernate: insert into Account (accountNo, branch, employeeId) values (?, ?, ?)
Hibernate: select e1_0.eid,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0 where e1_0.eid=?
Hibernate: select a1_0.employeeId,a1_0.aid,a1_0.accountNo,a1_0.branch from Account a1_0 where a1_0.employeeId=?
Hibernate: delete from Account where aid=?
Hibernate: delete from Account where aid=?
Hibernate: delete from Employee where eid=?
```
The `orphanRemoval=true` option was introduced in `JPA 2.0`. This marks child entity to be removed when it's no longer referenced from the parent entity. e.g. when we remove the child entity from the corresponding collection of the parent entity.
```java
public class Employee implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int eid;
private String firstName;
private String lastName;
private double salary;
@OneToMany(cascade = CascadeType.PERSIST, mappedBy = "employee", orphanRemoval = true)
private List accounts;
}
public class Account implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int aid;
private String accountNo;
private String branch;
@ManyToOne
@JoinColumn(name = "employeeId")
private Employee employee;
}
```
```java
Employee employee = new Employee();
employee.setFirstName("David");
employee.setLastName("Warner");
employee.setSalary(56789);
Account account = new Account();
account.setAccountNo("ACC123");
account.setBranch("Australia");
account.setEmployee(employee);
List accounts = new ArrayList<>();
accounts.add(account);
account = new Account();
account.setAccountNo("BCC567");
account.setBranch("New Zealand");
accounts.add(account);
employee.setAccounts(accounts);
account.setEmployee(employee);
Session session1 = sessionFactory.openSession();
Transaction transaction1 = session1.beginTransaction();
session1.persist(employee);
transaction1.commit();
session1.close();
Session session2 = sessionFactory.openSession();
Transaction transaction2 = session2.beginTransaction();
Employee employee2 = session2.get(Employee.class, 1);
List accounts = employee2.getAccounts();
accounts.remove(0); // remove an account from List of accounts, not from session
transaction2.commit();
session2.close();
```
```sql
Hibernate: create table Account (aid integer not null auto_increment, accountNo varchar(255), branch varchar(255), employeeId integer, primary key (aid)) engine=MyISAM
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, primary key (eid)) engine=MyISAM
Hibernate: alter table Account add constraint FKbkyxfxqy2qresbaghdbm5xtty foreign key (employeeId) references Employee (eid)
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: insert into Account (accountNo, branch, employeeId) values (?, ?, ?)
Hibernate: insert into Account (accountNo, branch, employeeId) values (?, ?, ?)
Hibernate: select e1_0.eid,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0 where e1_0.eid=?
Hibernate: select a1_0.employeeId,a1_0.aid,a1_0.accountNo,a1_0.branch from Account a1_0 where a1_0.employeeId=?
Hibernate: delete from Account where aid=?
```
42. HQL
HQL is an object-oriented query language, similar to SQL, but instead of operating on tables and columns, HQL works with persistent/entity objects and their properties. HQL is a superset of the JPQL, the Java Persistence Query Language. A JPQL query is a valid HQL query, but not all HQL queries are valid JPQL queries. HQL is a language with its own syntax and grammar. It is written as strings, like "from Product p". HQL queries are translated by Hibernate into conventional SQL queries. Note that Hibernate also provides the APIs that allow us to directly issue SQL queries as well. Hibernator’s query facilities do not allow us to alter the database schema. We can only add/update/delete the data inside tables. HQL is in case-sensitive except the class name and its attributes.
**HQL UPDATE:** `UPDATE` alters the details of existing objects in the database.
```java
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Employee employee = new Employee();
employee.setFirstName("David");
employee.setLastName("Warner");
employee.setSalary(56789);
session.persist(employee);
transaction.commit();
session.close();
Session session2 = sessionFactory.openSession();
Transaction transaction2 = session2.beginTransaction();
Query query = session2.createQuery("UPDATE Employee set firstName=:name where eid=:id");
query.setParameter("name", "Finch");
query.setParameter("id", 1);
int noOfRowsAffected = query.executeUpdate();
transaction2.commit();
session2.close();
```
**HQL DELETE:** `DELETE` removes the details of existing objects from the database.
```java
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
for (int i = 1; i <= 2; i++) {
Employee employee = new Employee();
employee.setFirstName("David" + i);
employee.setLastName("Warner" + i);
employee.setSalary(1000 * i);
session.persist(employee);
}
transaction.commit();
session.close();
Session session2 = sessionFactory.openSession();
Transaction transaction2 = session2.beginTransaction();
String qry = "deLETE from Employee where eid=:id"; // query is in case-sensitive
Query query = session2.createQuery(qry);
query.setParameter("id", 1);
int noOfRowsAffected = query.executeUpdate();
transaction2.commit();
session2.close();
```
```sql
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, primary key (eid)) engine=MyISAM
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: delete from Employee where eid=?
```
**HQL INSERT:** An HQL `INSERT` can be used to directly insert arbitrary entities as well as insert entities constructed from information obtained from SELECT queries.
```java
public class Person implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String firstName;
private String lastName;
}
Transaction transaction = session.beginTransaction();
String qry = "insert into Person(firstName, lastName)" + "values(:firstName, :lastName)";
Query query = session.createQuery(qry);
query.setParameter("firstName", "Moin");
query.setParameter("lastName", "Ali");
int noOfRowsAffected = query.executeUpdate();
transaction.commit();
System.out.println("noOfRowsAffected: " + noOfRowsAffected);
```
```sql
Hibernate: create table Person (id integer not null auto_increment, firstName varchar(255), lastName varchar(255), primary key (id)) engine=MyISAM
Hibernate: insert into Person(firstName,lastName) values (?,?)
```
_Insert with Select query:_
```java
Query query=session.createQuery("insert into purged_accounts(id, code, status) "+
"select id, code, status from account where accStatus=:status");
query.setString("accStatus", "PURGED");
int rowsCopied=query.executeUpdate();
```
**HQL SELECT:** An HQL `SELECT` is used to query the database for classes and their properties.
```sql
[SELECT [DISTINCT] property [, ...]]
FROM path [[AS] alias] [, ...] [FETCH ALL PROPERTIES]
WHERE logicalExpression
GROUP BY property [, ...]
HAVING logicalExpression
ORDER BY property [ASC | DESC] [, ...]
```
If `FETCH ALL PROPERTIES` is used then lazy loading semantics will be ignored, and all the immediate properties of the retrieved object(s) will be actively loaded (this does not apply recursively). `SELECT` clause is optional in `HQL` but mandatory for `JPQL`.
```java
Session session = sessionFactory.openSession();
Query query = session.createQuery("from Employee");
List employees = query.getResultList();
for (Employee employee : employees) {
System.out.println(employee);
}
```
```sql
Hibernate: select e1_0.eid,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0
Employee(eid=1, firstName=David1, lastName=Warner1, salary=1000.0)
Employee(eid=2, firstName=David2, lastName=Warner2, salary=2000.0)
```
```java
Query query = session3.createQuery("select firstName, salary from Employee");
// Query query = session.createQuery("select firstName from Employee");
List employeeList = query.getResultList();
if (!employeeList.isEmpty()) {
if (employeeList.get(0).getClass().isArray()) {
for (Object object : employeeList) {
for (Object cell : (Object[]) object) {
System.out.print(cell + " ");
}
System.out.println();
}
} else {
for (Object column : employeeList) {
System.out.println(column);
}
}
}
```
```sql
Hibernate: select e1_0.firstName,e1_0.salary from Employee e1_0
David1 1000.0
David2 2000.0
```
**HQL SELECT Clause & Projections:** The SELECT clause provides more control over the result set than the from clause. If we want to obtain the properties of objects in the result set, use the SELECT clause. For instance, we could run a projection query on the products in the database that only returned the names, instead of loading the full object into memory, as follows:
```sql
select product.name from Product product
```
The result set for this query will contain a List of java.lang.String objects. Additionally, we can retrieve the prices and the names for each product in the database, like so:
```sql
select product.name, product.price from Product product
```
If we are only interested in a few properties, this approach can allow us to reduce network traffic to the database server and save memory on the application's machine.
**Named Parameters:** Hibernate supports named parameters in its HQL queries. This makes writing queries that accept input from the user easyily and we do not have to defend against SQL injection attacks. We can either pass index or directly the parameter name.
```java
String hql = "from Product where price > :productPrice";
Query query = session.createQuery(hql);
// query.setDouble(0, 25.0);
query.setDouble("productPrice", 25.0);
List results = query.list();
```
**Sorting the Result:** To sort our HQL query results, we shall need to use the `order by` clause. We can order the results by any property on the objects in the result set either ascending (asc) or descending (desc).
```sql
from Product p where p.price>25.0 order by p.price desc
```
If we wanted to sort by more than one property, we would just add the additional properties to the end of the order by clause, separated by commas.
```sql
from Product p order by p.supplier.name asc, p.price asc
```
**HQL Association:** Associations allow us to use more than one class in an HQL query, just as SQL allows us to use joins between tables in a relational database. Hibernate supports five different types of joins:
- CROSS JOIN
- INNER JOIN
- LEFT OUTER JOIN
- RIGHT OUTER JOIN
- FULL OUTER JOIN
If we want to use `CROSS JOIN`, we just need to specify both classes in the from clause.
```sql
from Product p, Supplier s
```
For the other joins, use a join clause after the from clause. Specify the type of join, the object property to join on, and an alias for the other class.
```sql
from Product p left outer join p.supplier as s
select s.name, p.name, p.price from Product p inner join p.supplier as s
```
**HQL Aggregate Methods:** HQL supports a range of aggregate methods, similar to SQL. They work the same way in HQL as in SQL, so we do not have to learn any specific Hibernate terminology. The difference is that in HQL aggregate methods apply to the properties of persistent objects.
```sql
select count(*) from Product product
```
The aggregate functions available through HQL include the following:
- avg(property name): The average of a property’s value
- count(property name or \*): The number of times a property occurs in the results
- max(property name): The maximum value of the property values
- min(property name): The minimum value of the property values
- sum(property name): The sum total of the property values
43. @NamedQuery
A named query is a static HQL or SQL query with a fixed query string and defined either using `@NamedQuery` annotation or an XML file. We can refer to a named query by its name, in the runtime, when we need to execute it. Note that Hibernate's `@NamedQuery` annotation extends JPA’s `@NamedQuery` annotation with some additional features. Named queries are compiled when SessionFactory is instantiated (so, essentially, when our application starts up). So the advantage is that all our named queries are validated at that time rather than failing upon execution. The other advantage is that they are easy to maintain complex queries. These can be accessed and used from several places in the application which increases re-usability.
The disadvantage is that named queries are not customizable at runtime. We can of course define/supply parameters but beyond that what we have defined is what we shall get. We can't even change the sorting. Another disadvantage is that we shall not be able to change the named query within a running application server without reloading the SessionFactory.
```java
public final class Constant {
public static final String GET_EMPLOYEE_BY_ID_NAME = "GET_EMPLOYEE_BY_ID";
public static final String GET_EMPLOYEE_BY_ID_QUERY = "from Employee where eid=:id";
}
@Entity
@NamedQuery(name = Constant.GET_EMPLOYEE_BY_ID_NAME, query = Constant.GET_EMPLOYEE_BY_ID_QUERY)
public class Employee implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int eid;
private String firstName;
private String lastName;
private double salary;
}
```
```java
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
for (int i = 1; i <= 2; i++) {
Employee employee = new Employee();
employee.setFirstName("David" + i);
employee.setLastName("Warner" + i);
employee.setSalary(1000 * i);
session.persist(employee);
}
transaction.commit();
session.close();
Session session2 = sessionFactory.openSession();
Query query = session2.createNamedQuery(Constant.GET_EMPLOYEE_BY_ID_NAME);
query.setParameter("id", 2);
Employee employee = (Employee) query.getSingleResult();
System.out.println(employee);
session2.close();
```
```java
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: select e1_0.eid,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0 where e1_0.eid=?
Employee(eid=2, firstName=David2, lastName=Warner2, salary=2000.0)
```
**@NamedQueries:** If we have multiple named queries for an entity we can group them using the `@NamedQueries` annotation.
```java
@NamedQueries({
@NamedQuery(name = "QUERY_GET_DEPARTMENT_BY_ID",
query = "from DepartmentEntity d where d.id = :id"),
@NamedQuery(name = "QUERY_UPDATE_DEPARTMENT_BY_ID",
query = "UPDATE DepartmentEntity d SET d.name=:name where d.id = :id")
})
```
**@NamedNativeQuery:** The `@NamedNativeQuery` works very similar to `@NamedQuery` except we need to write the native SQL statements instead of HQL.
```java
@NamedNativeQueries({
@NamedNativeQuery(name = "NATIVE_QUERY_GET_DEPARTMENT_BY_ID",
query = "SELECT * FROM TBL_DEPT d WHERE d.id = :id"),
@NamedNativeQuery(name = "NATIVE_QUERY_UPDATE_DEPARTMENT_BY_ID",
query = "UPDATE TBL_DEPT d SET d.name=:name WHERE d.id = :id")
})
```
44. Criteria Queries
The criteria query API lets us build nested, structured query expressions in Java, providing a compile-time syntax checking. That is not possible with a query language like HQL or SQL.
_The Hibernate Criteria API had been deprecated back in Hibernate 5.x and these have been removed in Hibernate 6.0. Usually, all queries using the legacy API can be modeled with the JPA Criteria API that is still supported._
```java
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
for (int i = 1; i <= 2; i++) {
Employee employee = new Employee();
employee.setFirstName("David" + i);
employee.setLastName("Warner" + i);
employee.setSalary(1000 * i);
session.persist(employee);
}
transaction.commit();
session.close();
Session session2 = sessionFactory.openSession();
HibernateCriteriaBuilder criteriaBuilder = session2.getCriteriaBuilder();
JpaCriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Employee.class);
JpaRoot from = criteriaQuery.from(Employee.class);
JpaCriteriaQuery select = criteriaQuery.select(from);
Query query = session2.createQuery(select);
List employees = query.list();
for (Employee employee : employees) {
System.out.println(employee);
}
session2.close();
```
The above criteria code is equivalent to this HQL:
```java
Query query = session.createQuery("from Employee");
```
```sql
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, primary key (eid)) engine=MyISAM
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: select e1_0.eid,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0
Employee(eid=1, firstName=David1, lastName=Warner1, salary=1000.0)
Employee(eid=2, firstName=David2, lastName=Warner2, salary=2000.0)
```
**Restriction:** We can fetch partial rows from the tables by appying restriction. We can add restriction in criteria builder.
- criteriaBuilder.greaterThan()
- criteriaBuilder.greaterThanOrEqualTo()
- criteriaBuilder.lessThan
- criteriaBuilder.lessThanOrEqualTo()
- criteriaBuilder.equal()
- criteriaBuilder.notEqual()
- criteriaBuilder.between()
- criteriaBuilder.like()
- criteriaBuilder.notLike()
- criteriaBuilder.isNull()
- criteriaBuilder.isNotNull()
```java
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
for (int i = 1; i <= 2; i++) {
Employee employee = new Employee();
employee.setFirstName("David" + i);
employee.setLastName("Warner" + i);
employee.setSalary(1000 * i);
session.persist(employee);
}
transaction.commit();
session.close();
Session session2 = sessionFactory.openSession();
HibernateCriteriaBuilder criteriaBuilder = session2.getCriteriaBuilder();
JpaCriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Employee.class);
JpaRoot fromEmployee = criteriaQuery.from(Employee.class);
// criteriaQuery.where(criteriaBuilder.greaterThan(fromEmployee.get("eid"), 1));
JpaCriteriaQuery select = criteriaQuery.select(fromEmployee);
select.where(criteriaBuilder.greaterThan(fromEmployee.get("eid"), 1));
Query query = session2.createQuery(select);
List employees = query.list();
for (Employee employee : employees) {
System.out.println(employee);
}
session2.close();
```
```sql
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, primary key (eid)) engine=MyISAM
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: select e1_0.eid,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0 where e1_0.eid>?
Employee(eid=2, firstName=David2, lastName=Warner2, salary=2000.0)
```
We can combine these restrictions like:
```java
JpaPredicate jpaPredicate = criteriaBuilder.and(criteriaBuilder.greaterThan(fromEmployee.get("eid"), 0),
criteriaBuilder.notEqual(fromEmployee.get("firstName"), "David1"));
select.where(jpaPredicate);
```
For more help visit [Wikibooks JPA Criteria API documentation](https://en.wikibooks.org/wiki/Java_Persistence/Criteria#Criteria_API)
45. Stored Procedures
Stored procedures are like named functions that are stored in the database and used to execute native SQL statements to increase the reusability and take advantage of database-specific syntaxes. Stored procedures can accept input parameters and return output after executing the queries.
Hibernate provides support for executing the stored procedures and capturing their outputs using `ProcedureCall` & `StoredProcedureQuery` APIs. We can programmatically configure the procedure names and parameters or we can use the `@NamedStoredProcedureQuery` annotation to provide stored procedure details and later refer it to other places in the application.
Note that, under the hood, Hibernate executes the `JDBC CallableStatement` for fetching the procedure outputs. By default, the CallableStatement is closed upon ending the currently running database transaction, either via calling commit or rollback.
**ProcedureCall getEmployeeById(?,?,?,?):** It accepts `IN` parameter `eid` and returns the employee details using `OUT` parameters.
```sql
DELIMITER $$
CREATE PROCEDURE getEmployeeById (IN eid int, OUT firstName varchar(100), OUT lastName varchar(100), OUT salary float)
BEGIN
SELECT e.firstName, e.lastName, e.salary
INTO firstName, lastName, salary
from employee e
where e.eid=eid;
END$$
```
```java
public class Employee implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int eid;
private String firstName;
private String lastName;
private double salary;
}
```
```java
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
for (int i = 1; i <= 2; i++) {
Employee employee = new Employee();
employee.setFirstName("David" + i);
employee.setLastName("Warner" + i);
employee.setSalary(1000 * i);
session.persist(employee);
}
transaction.commit();
session.close();
Session session2 = sessionFactory.openSession();
ProcedureCall procedureCall = session2.createStoredProcedureCall("getEmployeeById");
procedureCall.registerParameter(1, Integer.class, ParameterMode.IN);
procedureCall.setParameter(1, 2); // 1st 1 is positional parameter & 2nd 1 is the employee id
procedureCall.registerParameter(2, String.class, ParameterMode.OUT);
procedureCall.registerParameter(3, String.class, ParameterMode.OUT);
procedureCall.registerParameter(4, Double.class, ParameterMode.OUT);
ProcedureOutputs procedureOutputs = procedureCall.getOutputs();
Object firstName = procedureOutputs.getOutputParameterValue(2);
Object lastName = procedureOutputs.getOutputParameterValue(3);
Object salary = procedureOutputs.getOutputParameterValue(4);
System.out.println(firstName + " " + lastName + " " + salary);
```
We can use either positional parameter or named parameter but we cannot mix both otherwise we shall encounter en exception:
```java
Exception in thread "main" java.lang.IllegalArgumentException: Cannot mix named parameter with positional parameter registrations
```
We must write either complete positional or complete named parameters.
```java
procedureCall.registerParameter("eid", Integer.class, ParameterMode.IN);
procedureCall.setParameter("eid", 2);
procedureCall.registerParameter("firstName", String.class, ParameterMode.OUT);
procedureCall.registerParameter("lastName", String.class, ParameterMode.OUT);
procedureCall.registerParameter("salary", Double.class, ParameterMode.OUT);
ProcedureOutputs procedureOutputs = procedureCall.getOutputs();
Object firstName = procedureOutputs.getOutputParameterValue("firstName");
Object lastName = procedureOutputs.getOutputParameterValue("lastName");
Object salary = procedureOutputs.getOutputParameterValue("salary");
System.out.println(firstName + " " + lastName + " " + salary);
```
```sql
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: {call getEmployeeById(?,?,?,?)}
David2 Warner2 2000.0
```
**StoredProcedureQuery getEmployeeDetailsBySalary(?):** We cannot use `OUT` parameters if we have to fetch a lot of information after the execution of stored procedures. It will create problems in code maintenance. So we can only map the `IN` parameters because they are generally limited to 1 or 2 values. And we can get the output information in form of `Object[]`.
```sql
DELIMITER //
CREATE PROCEDURE getEmployeeDetailsBySalary(IN sal FLOAT)
BEGIN
SELECT *
FROM Employee e
WHERE e.salary = sal;
END //
```
The `SELECT *` clause selects all four columns from the table so we shall have an Object[] of size 4. This will vary based on the number of columns and the SELECT clause. Also, the size of the List will depend on the number of rows returned after the execution of the stored procedure.
Here we need to create `StoredProcedureQuery` using `createStoredProcedureQuery()` method. This time we need to execute the procedure with `getResultList()` method.
```java
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
for (int i = 1; i <= 2; i++) {
Employee employee = new Employee();
employee.setFirstName("David" + i);
employee.setLastName("Warner" + i);
employee.setSalary(1200);
session.persist(employee);
}
transaction.commit();
session.close();
Session session2 = sessionFactory.openSession();
ProcedureCall procedureCall = session2.createStoredProcedureQuery("getEmployeeDetailsBySalary");
procedureCall.registerParameter("salary", Double.class, ParameterMode.IN);
procedureCall.setParameter("salary", 1200);
List procedureOutputs = procedureCall.getResultList();
for (Object[] columns : procedureOutputs) {
System.out.println(columns[0] + " " + columns[1] + " " + columns[2] + " " + columns[3]);
}
session2.close();
```
```sql
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, primary key (eid)) engine=MyISAM
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: {call getEmployeeDetailsBySalary(?)}
1 David1 Warner1 1200.0
2 David2 Warner2 1200.0
```
**@NamedStoredProcedureQuery:** This annotation is used to specify a stored procedure query that can be retrieved later by its name. This annotation can be applied to an Entity or mapped superclass. It is important to note that all parameters must be specified in the order in which they occur in the parameter list of the stored procedure in the database. As a great benefit, we can directly map a class to the procedure results.
```java
public class Constant {
public static final String NAMED_STORED_PROCEDURE_QUERY_NAME = "getEmployeeDetailsBySalaryProcedure";
public static final String DB_PROCEDURE_NAME = "getEmployeeDetailsBySalary";
public static final String COLUMN_NAME = "salary";
}
@Entity
@NamedStoredProcedureQuery(name = Constant.NAMED_STORED_PROCEDURE_QUERY_NAME, procedureName = Constant.DB_PROCEDURE_NAME, resultClasses = {
Employee.class }, parameters = {
@StoredProcedureParameter(name = Constant.COLUMN_NAME, type = Integer.class, mode = ParameterMode.IN) })
public class Employee implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int eid;
private String firstName;
private String lastName;
private double salary;
}
```
```java
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
for (int i = 1; i <= 2; i++) {
Employee employee = new Employee();
employee.setFirstName("David" + i);
employee.setLastName("Warner" + i);
employee.setSalary(1200);
session.persist(employee);
}
transaction.commit();
session.close();
Session session2 = sessionFactory.openSession();
ProcedureCall procedureCall = session2.createNamedStoredProcedureQuery(Constant.NAMED_STORED_PROCEDURE_QUERY_NAME);
List list = procedureCall.setParameter(Constant.COLUMN_NAME, 1200).getResultList();
for (Object object : list) {
System.out.println((Employee) object);
}
session2.close();
```
```sql
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: {call getEmployeeDetailsBySalary(?)}
Employee(eid=1, firstName=David1, lastName=Warner1, salary=1200.0)
Employee(eid=2, firstName=David2, lastName=Warner2, salary=1200.0)
```
46. First Level Cache
Caching is a facility provided by ORM frameworks that helps the users to get fast-running web applications while helping the framework itself to reduce the number of queries made to the database in a single transaction. Hibernate can achieve this by using `First Level Cache`.
- First level cache in hibernate is enabled by default and we do not need to do anything to get this functionality working. In fact, we can not disable it even forcefully.
- First-level cache associated with the Session object and it is available only till the session object is live.
- The first-level cache is associated with a specific "session" object and other session objects in the application can not see it.
- The scope of cache objects is of the session. Once the session is closed, cached objects are gone forever.
- When we query an entity the first time, it is retrieved from the database and stored in the first-level cache associated with hibernate session.
- If we query the same entity again with the same session object, it will be loaded from the cache and no `SQL query` will be executed.
- The loaded entity can be removed from the session using evict() method. The next loading of this entity will again make a database call if it has been removed using evict() method.
- The whole session cache can be removed using clear() method. It will remove all the entities stored in the cache.
```java
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Employee employee = new Employee();
employee.setFirstName("David");
employee.setLastName("Warner");
employee.setSalary(1200);
session.persist(employee);
transaction.commit();
session.close();
Session session2 = sessionFactory.openSession();
Employee employee2 = session2.get(Employee.class, 1);
System.out.println(employee2);
Employee employee3 = session2.getReference(Employee.class, 1); // equivalent to load() method
System.out.println(employee3);
session2.close();
```
```sql
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, primary key (eid)) engine=MyISAM
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: select e1_0.eid,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0 where e1_0.eid=?
Employee(eid=1, firstName=David, lastName=Warner, salary=1200.0)
Employee(eid=1, firstName=David, lastName=Warner, salary=1200.0)
```
Here we can clearly see that only one time of `SELECT` query has executed and we have fetched `Employee` two times. 1st time no employee is in the cache. Hence it goes to the database and store into the cache. 2nd time first it checks in the cache whether it is available or not and obviously it is available. So no any `SELECT` query.
If we fetch employee with different sessions then definately it will fetch from the database.
```java
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Employee employee = new Employee();
employee.setFirstName("David");
employee.setLastName("Warner");
employee.setSalary(1200);
session.persist(employee);
transaction.commit();
session.close();
Session session2 = sessionFactory.openSession();
Employee employee2 = session2.get(Employee.class, 1);
System.out.println(employee2);
session2.close();
Session session3 = sessionFactory.openSession();
Employee employee3 = session3.getReference(Employee.class, 1); // equivalent to load() method bcz load() is deprecated
System.out.println(employee3);
session3.close();
```
```sql
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, primary key (eid)) engine=MyISAM
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: select e1_0.eid,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0 where e1_0.eid=?
Employee(eid=1, firstName=David, lastName=Warner, salary=1200.0)
Hibernate: select e1_0.eid,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0 where e1_0.eid=?
Employee(eid=1, firstName=David, lastName=Warner, salary=1200.0)
```
**Remove cached entity:** Though we can not disable the first-level cache in hibernate, we can certainly remove some objects from it when needed. This is done using two methods:
- evict(): removes a particular object from cache associated with the session
- clear(): remove all cached objects associated with the session
```java
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Employee employee = new Employee();
employee.setFirstName("David");
employee.setLastName("Warner");
employee.setSalary(1200);
session.persist(employee);
transaction.commit();
session.close();
Session session2 = sessionFactory.openSession();
Employee employee2 = session2.get(Employee.class, 1);
System.out.println(employee2);
session2.evict(employee2);
// session2.clear();
Employee employee3 = session2.get(Employee.class, 1);
System.out.println(employee3);
session2.close();
```
```sql
Hibernate: create table Employee (eid integer not null auto_increment, firstName varchar(255), lastName varchar(255), salary float(53) not null, primary key (eid)) engine=MyISAM
Hibernate: insert into Employee (firstName, lastName, salary) values (?, ?, ?)
Hibernate: select e1_0.eid,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0 where e1_0.eid=?
Employee(eid=1, firstName=David, lastName=Warner, salary=1200.0)
Hibernate: select e1_0.eid,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0 where e1_0.eid=?
Employee(eid=1, firstName=David, lastName=Warner, salary=1200.0)
```
47. Second Level Cache
This is separate from the first-level cache and is available to be used globally in `SessionFactory` scope.
- The entities stored in the second level cache will be available to all the sessions created using that particular session factory.
- Once the SessionFactory is closed, all cache associated with it die and the cache manager also c