"Welcome To Ashok IT" "Spring Boot and MicroServices" Topic : Spring Data JPA - Generators Date : 10/10/2024 (Session - 42) _____________________________________________________________________________________________________________________________ Today Session ============== Generators ========== * Basically in application development will have multiple database tables. * Each Database table will have mandatorly primary key column which is combination of two constraints i.e.,Unique + not null constraint. * While inserting records into database table, If end user supplied primary key value might have chance for entering duplicate value for primary key column. * To Overcome this problem ORM Framework provided the generators concept. * Generators are used in application for generating the Primary key column value based on some mechanism supported by database vendor. * When we use generators in application before saving record primary key value will be generated with in application then record will be inserted into table along with primary key. * ORM Will support's the four types of Generators are as below 1) AUTO 2) SEQUENCE 3) IDENTITY 4) TABLE * We can use the following annotation @GeneratedValue helps for generating value for primary key based on strategy defined. * @GeneratedValue annotation should be defined on top of primary key column field in entity class. ************************ 1) GenerationType.AUTO ************************ >> This is the default generation type strategy for generating the primary key value of Database column. >> We can define the auto strategy on top of the primary key field in Entity Class with below annotation @Id @GeneratedValue(strategy=GenerationType.AUTO) private Integer productId; >> If we are using Oracle database internally Hibernate will create sequence database object with the name of hibernate_sequence. Ex: create sequence hibernate_sequence starts with 1 and increment by 1; >>>> SpringBoot 2.7.X version create sequence shopping_customer_seq start with 1 increment by 50 >>>> Spring Boot 3.X Table specific Sequence. create sequence mahesh_customers_seq start with 1 increment by 50 >>>>>> Spring Boot 3.X Table Specific Sequence. >> Before inserting the record into database table Hibernate will select value for hibernate_sequence/table specific sequence and insert into primary key column value. Ex: select hibernate_sequence.nextval from dual; >>>>>>> SpringBoot 2.7.x Version select shopping_customers_seq.nextval from dual; >>>>>>> SpringBoot 3.x Version insert into shopping_customers(?,?,?,?,?); >> If we are using MYSQL database internally Hibernate Will create table per entity class because MySQL doesn't supports the sequences concept and inorder to generate the primary key column value in MYSQL will have auto increment facility. create table hibernate_sequence( next_val bigint; ); insert into hibernate_sequence values(1); NOTE ==== Hibernate: create table shopping_customers ( customer_id integer not null, contact_no varchar(255), email varchar(255), location varchar(255), name varchar(255), primary key (customer_id) ) engine=InnoDB Hibernate: create table shopping_customers_seq ( next_val bigint ) engine=InnoDB Hibernate: insert into shopping_customers_seq values ( 1 ) Hibernate: select next_val as id_val from shopping_customers_seq for update Hibernate: update shopping_customers_seq set next_val= ? where next_val=? Hibernate: insert into shopping_customers (contact_no, email, location, name, customer_id) values (?, ?, ?, ?, ?) >> Before inserting the record into database table Hibernate will select value for hibernate_sequence table and insert into primary column value. Example Application =================== Customer.java ============= package com.ashokit.enities; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="mahesh_cutomers") public class Customer { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer customerId; private String customerName; private String customerLocation; public Integer getCustomerId() { return customerId; } public void setCustomerId(Integer customerId) { this.customerId = customerId; } public String getCustomerName() { return customerName; } public void setCustomerName(String customerName) { this.customerName = customerName; } public String getCustomerLocation() { return customerLocation; } public void setCustomerLocation(String customerLocation) { this.customerLocation = customerLocation; } @Override public String toString() { return "Customer [customerId=" + customerId + ", customerName=" + customerName + ", customerLocation=" + customerLocation + "]"; } } CustomerDao.java ================ package com.ashokit.dao; import org.springframework.data.repository.CrudRepository; import com.ashokit.enities.Customer; public interface CustomerDao extends CrudRepository { } Application.java ================ package com.ashokit; import java.util.Arrays; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import com.ashokit.dao.CustomerDao; import com.ashokit.enities.Customer; @SpringBootApplication public class Application implements CommandLineRunner{ @Autowired private CustomerDao customerDao; public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Override public void run(String... args) throws Exception { //inserting the record for an customer Customer c = new Customer(); c.setCustomerName("Mahesh"); c.setCustomerLocation("Hyderabad"); Customer c2 = new Customer(); c2.setCustomerName("Ashok"); c2.setCustomerLocation("Bangalore"); Iterable savedCustomers = customerDao.saveAll(Arrays.asList(c,c2)); savedCustomers.forEach(cust -> System.out.println(cust)); } } application.properties ====================== #configuring the Database properties spring.datasource.driver-class-name=oracle.jdbc.OracleDriver spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe spring.datasource.username=system spring.datasource.password=manager #Hibernate Configuration spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true ******************************** Problem With hibernate_sequence ******************************** ShoppingCustomers >>>>>> shopping_customers(DBTable) >>>>>> hibernate_sequence >>>>> Oracle Database ShoppingEmployee >>>>>> shopping_employees(DBTable) >>>>>> hibernate_sequence >>>>> Oracle Database 1) You Performed 5 insertions into shopping_customers DB Table >>>>>> CustomerIds(1,2,3,4,5) 2) You Performed 5 inserstion into shopping_employees DB Table >>>>>> employeeids(6,7,8,9,10) 3) You Performed 5 insertions into shopping_customers DB Table >>>>>> customerIds(11,12,13,14,15) 4) You Performed 5 insertions into shopping_employees DB Table >>>>>> employeeids(16,17,18,19,20) 5) Viewing the data for shopping_customers :1,2,3,4,5,11,12,13,14,15 6) Viewing the data for shopping_employees :6,7,8,9,10,15,16,17,18,19,20 If you observe the data for above two tables primary key column values are not in sync i.e.,Data Inconsistency. Inorder to overcome this problem we need to create independent sequence per database table. shopping_customers >>>>> shopping_customers_sequence >>>> GenerationType.SEQUENCE shopping_employees >>>>> shopping_employees_sequence >>>> GenerationType.SEQUENCE *************************** 2) GenerationType.SEQUENCE *************************** * In Order to overcome the problem of hibernate_sequence we need to came for this type of strategy with respective to Hibernate 5.x version. * From Hibernate 6.x version onwards we know that table specific sequence generation is happening in that default sequence initial value as 1 and increment by 50. * In this strategy Hibernate provided the facility to generate our own custom sequence name and we can customize the sequence initial value and increment value per table automatically with help of the following annotation i.e.,@SequenceGenerator. Example ======= @Id @GeneratedValue( strategy = GenerationType.SEQUENCE, generator = "customer_seq" ) @SequenceGenerator( name = "customer_seq", sequenceName ="shopping_customers_seq", initialValue = 1000, allocationSize = 1 ) private Integer customerId; * This type of generator is used to generate primary key values and uses a database sequence to generate unique values. * Inorder to generate primary key column we required additional select statement along with insert statement. * @SequenceGenerator annotation will takecare of creating sequence with name as "shopping_customers_seq" and defined the name of the generator i.e.,customer_seq and required allocation size of the sequence for incrementation. Example Application =================== Product.java ============ package com.ashokit.enities; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.SequenceGenerator; import javax.persistence.Table; @Entity @Table(name="mahesh_products") public class Product { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "product_seq") @SequenceGenerator(name = "product_seq", sequenceName = "mahesh_products_seq", initialValue = 1000, allocationSize = 1) private Integer productId; private String productName; private float productPrice; public Integer getProductId() { return productId; } public void setProductId(Integer productId) { this.productId = productId; } public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } public float getProductPrice() { return productPrice; } public void setProductPrice(float productPrice) { this.productPrice = productPrice; } @Override public String toString() { return "Product [productId=" + productId + ", productName=" + productName + ", productPrice=" + productPrice + "]"; } } ProductDao.java =============== public interface ProductDao extends CrudRepoistory{ } Vehicle.java ============ package com.ashokit.enities; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.SequenceGenerator; import javax.persistence.Table; @Entity @Table(name = "mahesh_vehicles") public class Vehicle { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "vehicle_seq") @SequenceGenerator(name = "vehicle_seq", sequenceName = "mahesh_vehicles_seq", initialValue = 1, allocationSize = 1) private Integer vehicleId; private String vehicleName; private float vehicleCost; public Integer getVehicleId() { return vehicleId; } public void setVehicleId(Integer vehicleId) { this.vehicleId = vehicleId; } public String getVehicleName() { return vehicleName; } public void setVehicleName(String vehicleName) { this.vehicleName = vehicleName; } public float getVehicleCost() { return vehicleCost; } public void setVehicleCost(float vehicleCost) { this.vehicleCost = vehicleCost; } @Override public String toString() { return "Vehicle [vehicleId=" + vehicleId + ", vehicleName=" + vehicleName + ", vehicleCost=" + vehicleCost + "]"; } } VehicleDao.java =============== public interface VehicleDao extends CrudRepoistory{ } Application.java ================ package com.ashokit; import java.util.Arrays; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import com.ashokit.dao.BikeDao; import com.ashokit.dao.CustomerDao; import com.ashokit.dao.EmployeeDao; import com.ashokit.dao.ProductDao; import com.ashokit.dao.VehicleDao; import com.ashokit.enities.Bike; import com.ashokit.enities.Employee; import com.ashokit.enities.Product; import com.ashokit.enities.Vehicle; @SpringBootApplication public class Application implements CommandLineRunner{ @Autowired private ProductDao productDao; @Autowired private VehicleDao vehicleDao; public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Override public void run(String... args) throws Exception { Product p = new Product(); p.setProductName("Laptop"); p.setProductPrice(50000.00f); Product p1 =new Product(); p1.setProductName("Mobilephone"); p1.setProductPrice(10000.00f); Iterable savedProducts = productDao.saveAll(Arrays.asList(p,p1)); savedProducts.forEach(prod -> System.out.println(prod)); System.out.println("*****************************************************************"); Vehicle v1 = new Vehicle(); v1.setVehicleName("Tata"); v1.setVehicleCost(50000.00f); Vehicle v2 = new Vehicle(); v2.setVehicleName("Hyundai"); v2.setVehicleCost(60000.00f); Iterable savedVehicles = vehicleDao.saveAll(Arrays.asList(v1,v2)); savedVehicles.forEach(veh -> System.out.println(veh)); } } NOTE ==== Observe the Output of the Spring Boot Application Console.