Accessing Data Through Hibernate; What DBAs Should Tell Developers and Vice Versa
Marco Tusa and Francisco Bordenave Percona
Accessing Data Through Hibernate; What DBAs Should Tell Developers - - PowerPoint PPT Presentation
Accessing Data Through Hibernate; What DBAs Should Tell Developers and Vice Versa Marco Tusa and Francisco Bordenave Percona Who Are We? Francisco Marco 2 Agenda 1. Basic description of Hibernate architecture 2. Basic description of MySQl
Marco Tusa and Francisco Bordenave Percona
2
3
10.What is a composite object and what is the impact? 11.Can we do better?
4
the important … is to have clear ideas and coordinated direction
5
6
7
8
9
MySQL is the RDBMS InnoDB is the engine Data is organized in 16kb pages Clustered Primary Key and then secondary indexes
1
MySQL is the RDBMS InnoDB is the engine Data is organized in 16kb pages Clustered Primary Key and then secondary indexes
InnoDB supports transactions (i.e. BEGIN/COMMIT) and has ACID capabilities:
Isolation means transactions should not affect other transactions when running concurrently.
1 1
and one for Application or by data store)
Open-close it inside nuclear operations
1 2
1 3
Simple example with One table employees
<hibernate-mapping package="net.tc.employees"> <class name="Employees" table="employees" catalog="employees"
<id name="empNo" type="int"> <column name="emp_no" /> <generator class="assigned" /> </id> <property name="birthDate" type="date" > <column name="birth_date" length="10" not-null="true" /> <property name="firstName" type="string" > <column name="first_name" length="14" not-null="true" />
public class Employees implements java.io.Serializable { private int empNo; private Date birthDate; private String firstName; private String lastName; private char gender; private Date hireDate; private Set titleses = new HashSet(0); private Set salarieses = new HashSet(0); private Set deptEmps = new HashSet(0); private Set deptManagers = new HashSet(0); public Employees() { } public Employees(int empNo, Date birthDate, String firstName, String lastName, char gender, Date hireDate) { this.empNo = empNo; this.birthDate = birthDate; this.firstName = firstName; this.lastName = lastName; this.gender = gender; this.hireDate = hireDate; }
Simple: it is a row in the table
show create table employees\G *************************** 1. row *************************** Table: employees Create Table: CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` enum('M','F') NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 1 row in set (0.01 sec) show create table salaries\G *************************** 1. row *************************** Table: salaries Create Table: CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL, `salary` int(11) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`from_date`), KEY `emp_no` (`emp_no`), CONSTRAINT `salaries_ibfk_1` FOREIGN KEY (`emp_no`) REFERENCES `employees` (`emp_no`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=latin1 1 row in set (0.00 sec)
1 6
Important to know that a connection=thread in MySQL. Thread allocates buffers (tmp_table_size, sort_buffer_size, etc) --> Thread = CPU and Memory work. The more the threads, the more the CPU and Memory pressure.
1 7
Inside MySQL 2 ways of handling threads (thread_handling variable):
Rule of thumb:
very short queries.
1 8
Out of MySQL JDBC
1 9
Connection pool as external concept VS internal Connection pool In MySQL: group of connections (threads) constantly opened and re used. Out of MySQL: group of connections handled by framework which may or not be kept opened.
Simple connection without optimization
/* mysql-connector-java-8.0.12 (Revision: 24766725dc6e017025532146d94c6e6c488fb8f1) */SELECT @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_connection, @@character_set_results AS character_set_results, @@character_set_server AS character_set_server, @@collation_server AS collation_server, @@init_connect AS init_connect, @@interactive_timeout AS interactive_timeout, @@license AS license, @@lower_case_table_names AS lower_case_table_names, @@max_allowed_packet AS max_allowed_packet, @@net_write_timeout AS net_write_timeout, @@query_cache_size AS query_cache_size, @@query_cache_type AS query_cache_type, @@sql_mode AS sql_mode, @@system_time_zone AS system_time_zone, @@time_zone AS time_zone, @@transaction_isolation AS transaction_isolation, @@wait_timeout AS wait_timeout;
2
Noise exists, but can be reduced:
hibernate.hikari.dataSource.cachePrepStmts">true hibernate.hikari.dataSource.defaultFetchSize">100 hibernate.hikari.dataSource.prepStmtCacheSize">250 hibernate.hikari.dataSource.prepStmtCacheSqlLimit">2048 hibernate.hikari.dataSource.useServerPrepStmts">true hibernate.hikari.dataSource.useLocalSessionState">true hibernate.hikari.dataSource.rewriteBatchedStatements">true hibernate.hikari.dataSource.cacheResultSetMetadata">true hibernate.hikari.dataSource.cacheServerConfiguration">true hibernate.hikari.dataSource.elideSetAutoCommits">true hibernate.hikari.dataSource.maintainTimeStats">false
For Hibernate you must add in URL
jdbc:mysql://127.0.0.1:3306/employees?prepStmtCacheSize=250&am p;prepStmtCacheSqlLimit=2048&useServerPrepStmts=YES&us eLocalSessionState=YES&useSSL=false&defaultFetchSize=1 00&rewriteBatchedStatements=YES&cacheResultSetMetadata =YES&cacheServerConfiguration=YES&elideSetAutoCommits= YES&maintainTimeStats=false
2 2
Insert
Session se = sessionFactoryEmp2.openSession(); se.setJdbcBatchSize(1); se.beginTransaction(); int i=1; while(++i < 500) { Employees employee = new Employees(); employee.setBirthDate(new Date()); employee.setHireDate(new Date()); employee.setGender('M'); employee.setEmpNo(i); employee.setFirstName("Franco" + 1); employee.setLastName("Castagna"+i); se.save(employee); } se.getTransaction().commit(); se.close();
Read
Session se = sessionFactoryEmp2.openSession(); se.setJdbcBatchSize(1); se.beginTransaction(); List<Employees> employees = se.createQuery("from Employees where emp_no <999 " ).list(); se.disconnect(); se.close();
Update
se.beginTransaction(); List<Employees> employees = se.createQuery("from Employees where emp_no <999 " ).list(); int i = 0; Iterator it = employees.iterator(); while(it.hasNext()) { Employees myEmp = (Employees) it.next(); try{myEmp.setHireDate(this.getSimpleDateFormat().parse("2015-"+ getRandomNumberInRange(1,12) +"-10"));}catch(Exception ae ) {ae.printStackTrace();} se.update(myEmp); } se.getTransaction().commit(); se.disconnect(); se.close();
2 3
Insert
Statement stmt = conn.createStatement(); int i=0; StringBuffer sb =new StringBuffer(); String sqlHead="INSERT INTO employees (emp_no,birth_date,first_name,last_name,gender,hire_date) VALUES ("; stmt.execute("START TRANSACTION"); while(++i < 500) { sb.append(i); sb.append(",’”+ this.getSimpleDateFormat().format(new Date()) +"'"); sb.append(",'Franco"+ i +"'"); sb.append(",'Castagna"+ i +"'"); sb.append(",'M'"); sb.append(",’”+ this.getSimpleDateFormat().format(new Date()) +"'"); sb.append(")"); stmt.execute(sqlHead+sb.toString()); sb.delete(0,sb.length()); } conn.commit();
Read
StringBuffer sb =new StringBuffer(); String sqlHead="SELECT emp_no,birth_date,first_name,last_name,gender,hire_date FROM employees where emp_no="; int rowReturned=0; while(++i < 500) { sb.append(i); ResultSet rs = stmt.executeQuery(sqlHead+sb.toString()); HashSet employees = new HashSet(); while(rs.next()) { Employees employee = new Employees(); employee.setBirthDate(rs.getDate("birth_date")); employee.setHireDate(rs.getDate("hire_date")); employee.setGender( rs.getString("gender").charAt(0)); employee.setEmpNo(rs.getInt("emp_no")); employee.setFirstName(rs.getString("first_name")); employee.setLastName(rs.getString("last_name")); employees.add(employee); } }
Delete
Statement stmt = conn.createStatement(); int i=0; String sqlHead="Delete from employees where emp_no="; stmt.execute("START TRANSACTION"); while(++i < 500) { stmt.execute(sqlHead+i); } conn.commit();
# Time: 2019-05-14T11:27:20.481014Z # User@Host: hibernatee[hibernatee] @ [127.0.0.1] Id: 1919 # Query_time: 0.000008 Lock_time: 0.000000 Rows_sent: 0 Rows_examined: 0 SET timestamp=1557833240; # administrator command: Close stmt; # Time: 2019-05-14T11:27:20.481484Z # User@Host: hibernatee[hibernatee] @ [127.0.0.1] Id: 1919 # Query_time: 0.000059 Lock_time: 0.000000 Rows_sent: 0 Rows_examined: 0 SET timestamp=1557833240; # administrator command: Prepare; # Time: 2019-05-14T11:27:20.482054Z # User@Host: hibernatee[hibernatee] @ [127.0.0.1] Id: 1919 # Query_time: 0.000091 Lock_time: 0.000029 Rows_sent: 0 Rows_examined: 0 SET timestamp=1557833240; insert into employees.employees (birth_date, first_name, last_name, gender, hire_date, emp_no) values ('2019-05-14', 'Franco1', 'Castagna2', 'M', '2019-05-14', 2);
SET timestamp=1557833376; INSERT INTO employees (emp_no,birth_date,first_name,last_name,gender,hire_date) VALUES (1,'2019-05-14','Franco1','Castagna1','M','2019-05-14'); # Time: 2019-05-14T11:29:36.974274Z # User@Host: hibernatee[hibernatee] @ [127.0.0.1] Id: 1934 # Query_time: 0.000082 Lock_time: 0.000033 Rows_sent: 0 Rows_examined: 0 SET timestamp=1557833376; INSERT INTO employees (emp_no,birth_date,first_name,last_name,gender,hire_date) VALUES (2,'2019-05-14','Franco2','Castagna2','M','2019-05-14');
2 7
<hibernate-configuration> <session-factory > <property name="sessionFactoryName">Hikari</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.jdbc.batch_size">20</property>
Session se = sessionFactoryEmp2.openSession(); se.setJdbcBatchSize(20); se.beginTransaction(); while(i < 500) { Employees employee = new Employees(); employee.setBirthDate(new Date()); ... se.save(employee); if ( ++i % 20 == 0 ) { //20, same as the JDBC batch sizeflush a batch of inserts and release memory: se.flush(); se.clear(); } }
SET timestamp=1557833655; insert into employees.employees (birth_date, first_name, last_name, gender, hire_date, emp_no) values ('2019-05-14', 'Franco1', 'Castagna1', 'M', '2019-05-14', 1),...,('2019-05-14', 'Franco1', 'Castagna19', 'M', '2019-05-14', 19); # Time: 2019-05-14T11:34:15.998326Z # User@Host: hibernatee[hibernatee] @ [127.0.0.1] Id: 1945 # Query_time: 0.000024 Lock_time: 0.000000 Rows_sent: 0 Rows_examined: 0 SET timestamp=1557833655; # administrator command: Close stmt; # Time: 2019-05-14T11:34:15.998609Z # User@Host: hibernatee[hibernatee] @ [127.0.0.1] Id: 1945 # Query_time: 0.000008 Lock_time: 0.000000 Rows_sent: 0 Rows_examined: 0
3
3 1
3 3
3 4
Session se = sessionFactoryEmp2.openSession(); se.beginTransaction(); Employees myEmp = se.find(Employees.class,10001, LockModeType.PESSIMISTIC_WRITE); Set salaries = null; salaries = myEmp.getSalarieses(); Iterator itS = salaries.iterator(); while(itS.hasNext()) { Salaries mySal = (Salaries) itS.next(); if(mySal.getToDate().toString().equals("9999-01-01")){ mySal.setSalary(mySal.getSalary() + 100); } } se.saveOrUpdate(myEmp); se.getTransaction().commit(); se.disconnect(); se.close(); Performance ALERT
3 5
Employees myEmp = se.find(Employees.class,10001, LockModeType.PESSIMISTIC_WRITE);
where employees0_.emp_no=? for update salaries = myEmp.getSalarieses();
salarieses0_.emp_no=? se.saveOrUpdate(myEmp);
3 6
3 7
<set name="salarieses" table="salaries" lazy="true|false" fetch="select" > <key> <column name="emp_no" not-null="true" /> </key> <one-to-many class="net.tc.employees.Salaries"/> </set> LAZY List<Employees> employees = se.createQuery("from EmployeesSummary where emp_no >10000 and emp_no < 20020 " ).list(); Iterator it = employees.iterator(); while(it.hasNext()) { Employees myEmp = (Employees) it.next(); logger.info("EmployeeSummary name = " + myEmp.getFirstName()+" "+ myEmp.getLastName()); Iterator it2 = myEmp.getTitleses().iterator(); ← HERE I retrieve the info Iterator it3 = myEmp.getSalarieses().iterator(); ← HERE I retrieve the info Iterator it4 = myEmp.getDeptEmps().iterator(); ← HERE I retrieve the info } NOT LAZY List<Employees> employees = se.createQuery("from EmployeesSummary where emp_no >10000 and emp_no < 20020 " ).list(); ← HERE Iterator it = employees.iterator(); while(it.hasNext()) { Employees myEmp = (Employees) it.next(); logger.info("EmployeeSummary name = " + myEmp.getFirstName()+" "+ myEmp.getLastName()); Iterator it2 = myEmp.getTitleses().iterator(); Iterator it3 = myEmp.getSalarieses().iterator(); Iterator it4 = myEmp.getDeptEmps().iterator(); }
Lazy SQL
Hibernate: select ... from employees.employees employeess0_ where emp_no>10000 and emp_no<20020 19/05/15 16:34:57 INFO [PL20192]: EmployeeSummary name = Georgi Fucelli Hibernate: select ... from employees.titles titleses0_ where titleses0_.emp_no=? Hibernate: select ... from employees.salaries salarieses0_ where salarieses0_.emp_no=? Hibernate: select ... from employees.dept_emp deptemps0_ where deptemps0_.emp_no=? 19/05/15 16:35:06 INFO [PL20192]: EmployeeSummary name = Bezalel Simmel
Not Lazy SQL
Hibernate: select ... from employees.employees employeess0_ where emp_no>10000 and emp_no<10003 Hibernate: select ... from employees.dept_manager deptmanage0_ where deptmanage0_.emp_no=? Hibernate: select ... from employees.dept_emp deptemps0_ where deptemps0_.emp_no=? Hibernate: select ... from employees.salaries salarieses0_ where salarieses0_.emp_no=? Hibernate: select ... from employees.titles titleses0_ where titleses0_.emp_no=? Hibernate: select ... from employees.dept_manager deptmanage0_ where deptmanage0_.emp_no=? Hibernate: select ... from employees.dept_emp deptemps0_ where deptemps0_.emp_no=? Hibernate: select ... from employees.salaries salarieses0_ where salarieses0_.emp_no=? Hibernate: select ... from employees.titles titleses0_ where titleses0_.emp_no=? 19/05/15 16:40:54 INFO [PL20192]: EmployeeSummary name = Georgi Fucelli 19/05/15 16:41:01 INFO [PL20192]: EmployeeSummary name = Bezalel Simmel
4
4 1
4 2
4 4
TRX 1
logger.info("I am the 1st TRX ");
EmployeesSummary myEmp = se.find(EmployeesSummary.class,10001, LockModeType.PESSIMISTIC_WRITE); Set salaries = null; salaries = myEmp.getSalarieses(); Iterator itS = salaries.iterator(); while(itS.hasNext()) { Salaries mySal = (Salaries) itS.next(); if(mySal.getToDate().toString().equals("9999-01-01")){ logger.info("1TRX Employee name Before = " + myEmp.getFirstName()+" " + myEmp.getLastName() +" " + mySal.getSalary()); mySal.setSalary(mySal.getSalary() + 1000); logger.info("1TRX Employee name After = " + myEmp.getFirstName()+" " + myEmp.getLastName() +" " + mySal.getSalary()); } } logger.info("Another Transaction is modifying the same value "); se.saveOrUpdate(myEmp); se.getTransaction().commit(); se.disconnect(); se.close(); logger.info("1TRX COmplete");
TRX 2
logger.info("I am the 2nd TRX "); EmployeesSummary myEmp = se.find(EmployeesSummary.class,10001); Set salaries = null; salaries = myEmp.getSalarieses(); Iterator itS = salaries.iterator(); while(itS.hasNext()) { Salaries mySal = (Salaries) itS.next(); if(mySal.getToDate().toString().equals("9999-01-01")){ logger.info("2TRX Employee name Before = " + myEmp.getFirstName()+" " + myEmp.getLastName() +" " + mySal.getSalary()); mySal.setSalary(mySal.getSalary() - 400); logger.info("2TRX Employee name After = " + myEmp.getFirstName()+" " + myEmp.getLastName() +" " + mySal.getSalary()); } } se.saveOrUpdate(myEmp); se.getTransaction().commit(); se.disconnect(); se.close(); logger.info("2TRX COmplete");
4 5
4 6
I am the 1st TRX Hibernate: select ... from employees.employees employees0_ where employees0_.emp_no=? for update Hibernate: select ... from employees.salaries salarieses0_ where salarieses0_.emp_no=? Hibernate: update employees.salaries set salary=?, to_date=? where emp_no=? and from_date=? I am the 2nd TRX Hibernate: select ... from employees.employees employees0_ where employees0_.emp_no=? Hibernate: select ... from employees.salaries salarieses0_ where salarieses0_.emp_no=? Hibernate: update employees.salaries set salary=?, to_date=? where emp_no=? and from_date=?
4 7
Because the select for update is on the main object -> Employees, but I am actually updating a Salaries record so ... no lock Hibernate: select employees0_.emp_no as emp_no1_3_0_... from employees.employees employees0_ where employees0_.emp_no=? for update If using lock also on the second TRX 19/05/14 12:16:57 ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper2]: Lock wait timeout exceeded; try restarting transaction Exception in thread "main" javax.persistence.PessimisticLockException: could not extract ResultSet
4 8
5
5 5