Hibernate 캐시
캐싱은 애플리케이션 성능 최적화에 관한 것이며 애플리케이션과 데이터베이스 사이에 위치하여 다중 데이터베이스 액세스를 방지하고 성능이 중요한 애플리케이션의 성능을 향상시킵니다.
캐싱은 아래 설명된 것처럼 다중 레벨 캐싱 방식을 사용하는 Hibernate에 매우 중요합니다.
1차 레벨 캐시:
1차 레벨 캐시 이는 세션 캐시이며 모든 요청이 통과해야 하는 필수 캐시입니다. 세션 개체는 데이터베이스에 제출되기 전에 자체 전원 개체를 유지합니다.
객체에 대해 여러 업데이트가 실행되는 경우 Hibernate는 실행되는 업데이트 SQL 문 수를 줄이기 위해 가능한 한 오랫동안 업데이트를 지연하려고 시도합니다. 세션을 닫으면 캐시된 모든 개체가 손실되거나 데이터베이스에서 유지되거나 업데이트됩니다.
L2 캐시:
L2 캐시는 선택적 캐시이며 L2 캐시에서 개체를 찾으려고 시도하기 전에 항상 L1 캐시를 참조합니다. 두 번째 수준 캐시는 클래스별 및 컬렉션별로 구성할 수 있으며 세션 내에서 개체 캐싱을 담당합니다.
모든 타사 캐시는 Hibernate를 사용할 수 있습니다. org.hibernate.cache.CacheProvider 인터페이스는 Hibernate를 제공하기 위해 구현되어야 하는 핸들 캐시 구현을 제공합니다.
쿼리 레벨 캐시:
Hibernate는 또한 쿼리 결과 세트 캐시와 보조 캐시의 긴밀한 통합을 구현합니다.
이것은 캐시된 쿼리 결과와 테이블이 마지막으로 업데이트된 타임스탬프를 보관하기 위해 두 개의 추가 물리적 캐시가 필요한 선택적 기능입니다. 이는 동일한 매개변수를 사용하여 자주 실행되는 쿼리에만 유용합니다.
레벨 2 캐시:
Hibernate는 첫 번째 레벨 캐시를 사용하며, 기본적으로 첫 번째 레벨 캐시를 사용하기 위해 아무것도 하지 않습니다. 선택적 두 번째 수준 캐시로 바로 이동해 보겠습니다. 모든 클래스가 캐싱의 이점을 누리는 것은 아니므로 두 번째 수준 캐시를 비활성화할 수 있는 것이 중요합니다.
Hibernate 두 번째 수준 캐시는 2단계 프로세스로 설정됩니다. 먼저 사용할 동시성 전략을 결정해야 합니다. 그런 다음 캐시 만료를 구성하고 캐시 프로비저닝 물리적 캐시 속성을 사용할 수 있습니다.
동시성 전략:
동시성 전략은 데이터 항목을 캐시에 저장하고 캐시에서 검색하는 역할을 담당하는 중개자입니다. 두 번째 수준 캐싱을 활성화하려면 각 지속성 클래스 및 컬렉션에 대해 사용할 캐시 동시성 전략을 결정해야 합니다.
트랜잭션: 기본 읽기 데이터가 오래된 데이터의 동시 트랜잭션을 방지하는 것인 경우 이 전략을 사용합니다. 이는 드물게 업데이트되는 경우에 매우 중요합니다.
읽기-쓰기: 드물게 업데이트되는 경우 오래된 데이터로부터 동시 트랜잭션을 방지하는 것이 중요한 데이터를 주로 읽는 데 이 전략을 다시 사용합니다.
비엄격 읽기-쓰기: 이 전략은 캐시와 데이터베이스 간의 일관성을 보장하지 않습니다. 데이터가 거의 변경되지 않고 오래된 데이터가 발생할 가능성이 작은 경우에는 이 전략을 사용하는 것이 중요하지 않습니다.
읽기 전용: 동시성 전략은 데이터에 적용되며 변경되지 않습니다. 사용량 데이터는 참고용입니다.
Employee 클래스에 두 번째 레벨 캐싱을 사용하려면 Hibernate가 Employee 인스턴스에 대해 읽기-쓰기 캐싱 전략을 사용하도록 지시하는 데 필요한 매핑 요소를 추가해 보겠습니다.
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="Employee" table="EMPLOYEE"> <meta attribute="class-description"> This class contains the employee detail. </meta> <cache usage="read-write"/> <id name="id" type="int" column="id"> <generator class="native"/> </id> <property name="firstName" column="first_name" type="string"/> <property name="lastName" column="last_name" type="string"/> <property name="salary" column="salary" type="int"/> </class> </hibernate-mapping>
Usage="read-write" 속성은 Hibernate에게 읽기-쓰기 동시성 전략에 의해 정의된 캐시를 사용하도록 지시합니다.
캐시 공급자:
캐시 후보 클래스를 사용할 동시성 전략을 고려한 후 다음 단계는 캐시 공급자를 선택하는 것입니다. Hibernate는 전체 애플리케이션을 서비스하기 위해 하나의 캐시를 강제로 선택합니다.
지정된 hibernate.cfg.xml 구성 파일에 제공되는 캐시입니다. 두 번째 수준 캐시 공급자로 EHCache를 선택합니다.
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-configuration SYSTEM "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <!-- Assume students is the database name --> <property name="hibernate.connection.url"> jdbc:mysql://localhost/test </property> <property name="hibernate.connection.username"> root </property> <property name="hibernate.connection.password"> root123 </property> <property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </property> <!-- List of XML mapping files --> <mapping resource="Employee.hbm.xml"/> </session-factory> </hibernate-configuration>
이제 캐시 영역의 속성을 지정해야 합니다. EHCache에는 애플리케이션의 CLASSPATH에 있는 자체 구성 파일 ehcache.xml이 있습니다. ehcache.xml의 Employee 클래스 캐시 구성은 다음과 같을 수 있습니다:
<diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" /> <cache name="Employee" maxElementsInMemory="500" eternal="true" timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="false" />
이제 Employee 클래스에 대해 2차 레벨 캐시를 활성화하고 Hibernate는 이제 직원을 탐색하거나 전달할 때마다 2차 레벨 캐시를 갖게 됩니다. 식별자 로딩 직원.
은 모든 클래스를 분석하고 각 클래스에 적합한 캐싱 전략을 선택해야 합니다. 경우에 따라 두 번째 수준 캐시로 인해 애플리케이션 성능이 저하될 수 있습니다. 따라서 캐싱을 활성화하지 않고 처음으로 애플리케이션을 벤치마킹하는 것이 좋습니다. 캐싱 및 성능 확인에 적합합니다. 캐싱이 시스템 성능을 향상시키지 못한다면 어떤 종류의 캐시도 사용할 수 없게 만드는 것은 의미가 없습니다.
쿼리 레벨 캐싱:
쿼리 캐시를 사용하려면 먼저 hibernate.cache.use_query_cache="true" 속성을 사용하여 구성 파일에서 이를 활성화해야 합니다. 이 속성을 true로 설정하면 Hibernate가 쿼리와 식별자 세트를 보관하기 위해 메모리에 필요한 캐시를 생성하도록 합니다.
다음으로 쿼리 캐시를 사용하려면 Query 클래스의 setCacheable(Boolean) 메서드를 사용하면 됩니다. 예:
Session session = SessionFactory.openSession(); Query query = session.createQuery("FROM EMPLOYEE"); query.setCacheable(true); List users = query.list(); SessionFactory.closeSession();
Hibernate也支持通过一个缓存区域的概念非常细粒度的缓存支持。缓存区是这是给定一个名称缓存的一部分。
Session session = SessionFactory.openSession(); Query query = session.createQuery("FROM EMPLOYEE"); query.setCacheable(true); query.setCacheRegion("employee"); List users = query.list(); SessionFactory.closeSession();
此代码使用方法告诉Hibernate来存储和查找在缓存中的员工方面的查询。
Hibernate原生SQL
可以使用原生SQL来表达数据库查询,如果想利用数据库特有的功能,如查询提示或者Oracle中的CONNECT关键字。 Hibernate3.x允许使用手写SQL语句,包括存储过程,所有的创建,更新,删除和load操作。
应用程序将从会话创建一个原生SQL查询(Session接口上)createSQLQuery()方法:
public SQLQuery createSQLQuery(String sqlString) throws HibernateException
当传递一个包含SQL查询到createSQLQuery()方法,可以将SQL结果与任何现有的Hibernate实体,联接,或者一个标量结果使用addEntity()方法,addJoin(),和addScalar()方法关联的字符串。
标量查询:
最基本的SQL查询是从一个或多个表中得到标量(数值)的列表。以下是语法使用原生SQL标量的值:
String sql = "SELECT first_name, salary FROM EMPLOYEE"; SQLQuery query = session.createSQLQuery(sql); query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP); List results = query.list();
实体的查询:
上面的查询都是返回标量值,也就是从resultset中返回的“裸”数据。以下是语法通过addEntity()方法来从原生SQL查询获得实体对象作为一个整体。
String sql = "SELECT * FROM EMPLOYEE"; SQLQuery query = session.createSQLQuery(sql); query.addEntity(Employee.class); List results = query.list();
命名SQL查询:
以下是语法通过addEntity()方法来从原生SQL查询获得实体对象和使用命名SQL查询。
String sql = "SELECT * FROM EMPLOYEE WHERE id = :employee_id"; SQLQuery query = session.createSQLQuery(sql); query.addEntity(Employee.class); query.setParameter("employee_id", 10); List results = query.list();
Native SQL 例子:
考虑下面的POJO类:
public class Employee { private int id; private String firstName; private String lastName; private int salary; public Employee() {} public Employee(String fname, String lname, int salary) { this.firstName = fname; this.lastName = lname; this.salary = salary; } public int getId() { return id; } public void setId( int id ) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName( String first_name ) { this.firstName = first_name; } public String getLastName() { return lastName; } public void setLastName( String last_name ) { this.lastName = last_name; } public int getSalary() { return salary; } public void setSalary( int salary ) { this.salary = salary; } }
让我们创建下面的EMPLOYEE表来存储Employee对象:
create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) );
以下将被映射文件。
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="Employee" table="EMPLOYEE"> <meta attribute="class-description"> This class contains the employee detail. </meta> <id name="id" type="int" column="id"> <generator class="native"/> </id> <property name="firstName" column="first_name" type="string"/> <property name="lastName" column="last_name" type="string"/> <property name="salary" column="salary" type="int"/> </class> </hibernate-mapping>
最后,我们将创建应用程序类的main()方法来运行,我们将使用原生SQL查询的应用程序:
import java.util.*; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.SessionFactory; import org.hibernate.SQLQuery; import org.hibernate.Criteria; import org.hibernate.Hibernate; import org.hibernate.cfg.Configuration; public class ManageEmployee { private static SessionFactory factory; public static void main(String[] args) { try{ factory = new Configuration().configure().buildSessionFactory(); }catch (Throwable ex) { System.err.println("Failed to create sessionFactory object." + ex); throw new ExceptionInInitializerError(ex); } ManageEmployee ME = new ManageEmployee(); /* Add few employee records in database */ Integer empID1 = ME.addEmployee("Zara", "Ali", 2000); Integer empID2 = ME.addEmployee("Daisy", "Das", 5000); Integer empID3 = ME.addEmployee("John", "Paul", 5000); Integer empID4 = ME.addEmployee("Mohd", "Yasee", 3000); /* List down employees and their salary using Scalar Query */ ME.listEmployeesScalar(); /* List down complete employees information using Entity Query */ ME.listEmployeesEntity(); } /* Method to CREATE an employee in the database */ public Integer addEmployee(String fname, String lname, int salary){ Session session = factory.openSession(); Transaction tx = null; Integer employeeID = null; try{ tx = session.beginTransaction(); Employee employee = new Employee(fname, lname, salary); employeeID = (Integer) session.save(employee); tx.commit(); }catch (HibernateException e) { if (tx!=null) tx.rollback(); e.printStackTrace(); }finally { session.close(); } return employeeID; } /* Method to READ all the employees using Scalar Query */ public void listEmployeesScalar( ){ Session session = factory.openSession(); Transaction tx = null; try{ tx = session.beginTransaction(); String sql = "SELECT first_name, salary FROM EMPLOYEE"; SQLQuery query = session.createSQLQuery(sql); query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP); List data = query.list(); for(Object object : data) { Map row = (Map)object; System.out.print("First Name: " + row.get("first_name")); System.out.println(", Salary: " + row.get("salary")); } tx.commit(); }catch (HibernateException e) { if (tx!=null) tx.rollback(); e.printStackTrace(); }finally { session.close(); } } /* Method to READ all the employees using Entity Query */ public void listEmployeesEntity( ){ Session session = factory.openSession(); Transaction tx = null; try{ tx = session.beginTransaction(); String sql = "SELECT * FROM EMPLOYEE"; SQLQuery query = session.createSQLQuery(sql); query.addEntity(Employee.class); List employees = query.list(); for (Iterator iterator = employees.iterator(); iterator.hasNext();){ Employee employee = (Employee) iterator.next(); System.out.print("First Name: " + employee.getFirstName()); System.out.print(" Last Name: " + employee.getLastName()); System.out.println(" Salary: " + employee.getSalary()); } tx.commit(); }catch (HibernateException e) { if (tx!=null) tx.rollback(); e.printStackTrace(); }finally { session.close(); } } }
编译和执行:
下面是步骤来编译并运行上述应用程序。请确保在进行的编译和执行之前,适当地设置PATH和CLASSPATH。
执行ManageEmployee二进制文件来运行程序。
会得到以下结果,并记录将在EMPLOYEE表中创建。
$java ManageEmployee
.......VARIOUS LOG MESSAGES WILL DISPLAY HERE........ First Name: Zara, Salary: 2000 First Name: Daisy, Salary: 5000 First Name: John, Salary: 5000 First Name: Mohd, Salary: 3000 First Name: Zara Last Name: Ali Salary: 2000 First Name: Daisy Last Name: Das Salary: 5000 First Name: John Last Name: Paul Salary: 5000 First Name: Mohd Last Name: Yasee Salary: 3000
如果检查EMPLOYEE表,它应该记录下已:
mysql> select * from EMPLOYEE;
+----+------------+-----------+--------+ | id | first_name | last_name | salary | +----+------------+-----------+--------+ | 26 | Zara | Ali | 2000 | | 27 | Daisy | Das | 5000 | | 28 | John | Paul | 5000 | | 29 | Mohd | Yasee | 3000 | +----+------------+-----------+--------+ 4 rows in set (0.00 sec)
更多Java의 Hibernate 프레임워크에서 캐싱 및 기본 SQL 문 사용에 대한 자세한 설명相关文章请关注PHP中文网!