Hibernate_03
A.Hibernate缓存
1.一级缓存
一级缓存是Session缓存,属于事务范围的缓存,由hibernate管理的
只要应用程序通过Session接口来执行CRUD操作
Hibernate就会启用一级缓存,把数据库中的数据以对象的形式拷贝到缓存中
对于批量更新和批量删除操作,如果不希望启用第一级缓存
可以绕过Hibernate API,直接使用JDBC API操作
一级缓存中对象的生命周期为:当session关闭后,就自动销毁
2.二级缓存
二级缓存是SessionFactory级别的缓存,属于进程或群集范围的缓存
二级缓存是可选插件,默认不启用
就是查询的时候会把结果缓存到二级缓存中
如果同一个sessionFactory创建的其他session执行了相同的操作
hibernate就会从二级缓存中拿结果,而不会再连接数据库
主要使用第三方缓存插件,如使用Ehcache二级缓存实现
使用步骤:
a.导入Hibernate和mysql数据库驱动以及druid的jar包
b.导入二级缓存的插件包:optional下ehcache中三个jar
c.在主配置文件中hibernate.cfg.xml 添加配置信息
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 数据库信息要改 --> <property name="diverClassName">com.mysql.jdbc.Driver</property> <property name="url">jdbc:mysql://localhost:3306/test</property> <property name="username">root</property> <property name="password">root</property> <!-- 添加durid驱动 --> <property name="hibernate.connection.provider_class">com.alibaba.druid.support.hibernate.DruidConnectionProvider</property> <!-- mysql方言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL57Dialect</property> <!-- 创建表方式为自动更新 --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 是否显示sql语句 --> <property name="hibernate.show_sql">true</property> <!-- 是否格式化 --> <property name="hibernate.format_sql">true</property> <!-- 配置连接池初始化大小 --> <property name="initialSize">2</property> <!-- 最小空闲连接数 --> <property name="minIdle">1</property> <!-- 最大连接数 --> <property name="maxActive">300</property> <!-- 获取连接等待超时的时间,单位:毫秒 --> <property name="maxWait">60000</property> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis">60000</property> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis">300000</property> <!-- 开启二级缓存 --> <property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property> <!-- 映射位置 --> <mapping resource="org/xxxx/pojo/UserInfo.hbm.xml" /> </session-factory> </hibernate-configuration>
d.在src根目录下配置ehcache.xml
name:cache唯一标识
eternal:缓存是否永久有效
maxElementsInMemory:内存中最大缓存对象数
overflowToDisk(true,false):缓存对象到最大数后,写到硬盘中
diskPersistent:硬盘持久化
timeToIdleSeconds:缓存清除时间
timeToLiveSeconds:缓存存活时间
memoryStoreEvictionPolicy:缓存清空策略
FIFO:first in first out 先进先出
LFU: Less Frequently Used 一直以来最少被使用的
LRU:Least Recently Used 最近最少使用的
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <defaultCache maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"></defaultCache> <cache name="userCache" eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="3600" timeToLiveSeconds="3600" memoryStoreEvictionPolicy="LFU"></cache> </ehcache>
e.在需要被缓存的对象中hbm文件中的<class>标签下添加<cache>子标签
UserInfo.java
package org.xxxx.pojo; import java.io.Serializable; public class UserInfo implements Serializable { private static final long serialVersionUID = 1L; private int id; private String username; private String password; public UserInfo() { super(); // TODO Auto-generated constructor stub } public UserInfo(String username, String password) { super(); this.username = username; this.password = password; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "UserInfo [id=" + id + ", username=" + username + ", password=" + password + "]"; } }
UserInfo.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2018-1-13 20:10:19 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping package="org.xxxx.pojo"> <class name="UserInfo" table="USERINFO"> <!-- 配置缓存权限为只读 --> <cache usage="read-only"></cache> <id name="id" type="int"> <column name="UID" /> <!-- 自增长 --> <generator class="native" /> </id> <property name="username" type="java.lang.String"> <column name="USERNAME" /> </property> <property name="password" type="java.lang.String"> <column name="PASSWORD" /> </property> </class> </hibernate-mapping>
f.测试
package org.xxxx.pojo; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class Test_01 { public static void main(String[] args) { // 加载配置文件 Configuration config = new Configuration().configure(); // 创建Session工厂 SessionFactory factory = config.buildSessionFactory(); // 获取Session Session session = factory.openSession(); // 获取id为3的UserInfo,默认从Session缓存中尝试加载 UserInfo uInfo = session.get(UserInfo.class, 3); System.out.println(uInfo); System.out.println("-------------------------------------"); // 再次查询 UserInfo uInfo2 = session.get(UserInfo.class, 3); System.out.println(uInfo2); System.out.println("-------------------------------------"); // 释放资源 session.close(); // 再次开启 // 获取Session Session session2 = factory.openSession(); UserInfo uInfo3 = session2.get(UserInfo.class, 3); System.out.println(uInfo3); session2.close(); } }
第一次查询会生成查询语句,并查询数据库再次查询都会从cache中获取数据
3.查询缓存
Hibernate还为查询结果提供了一个查询缓存,它依赖于第二级缓存
在配置文件中开启查询缓存
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 数据库信息要改 --> <property name="diverClassName">com.mysql.jdbc.Driver</property> <property name="url">jdbc:mysql://localhost:3306/test</property> <property name="username">root</property> <property name="password">root</property> <!-- 添加durid驱动 --> <property name="hibernate.connection.provider_class">com.alibaba.druid.support.hibernate.DruidConnectionProvider</property> <!-- mysql方言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL57Dialect</property> <!-- 创建表方式为自动更新 --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 是否显示sql语句 --> <property name="hibernate.show_sql">true</property> <!-- 是否格式化 --> <property name="hibernate.format_sql">true</property> <!-- 配置连接池初始化大小 --> <property name="initialSize">2</property> <!-- 最小空闲连接数 --> <property name="minIdle">1</property> <!-- 最大连接数 --> <property name="maxActive">300</property> <!-- 获取连接等待超时的时间,单位:毫秒 --> <property name="maxWait">60000</property> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis">60000</property> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis">300000</property> <!-- 开启查询缓存 --> <property name="hibernate.cache.use_query_cache">true</property> <!-- 开启二级缓存 --> <property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property> <!-- 映射位置 --> <mapping resource="org/xxxx/pojo/UserInfo.hbm.xml" /> </session-factory> </hibernate-configuration>
需要在所有的查询方法中设置setCacheable(true)
测试
package org.xxxx.pojo; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import org.hibernate.query.Query; public class Test_01 { public static void main(String[] args) { // 加载配置文件 Configuration config = new Configuration().configure(); // 创建Session工厂 SessionFactory factory = config.buildSessionFactory(); // 获取Session Session session = factory.openSession(); // 定义hql语句,获取id为1的信息 String hql = "from UserInfo where id=1"; // 执行 @SuppressWarnings("unchecked") Query<UserInfo> query = session.createQuery(hql).setCacheable(true); // 获取结果 UserInfo uInfo = query.uniqueResult(); System.out.println(uInfo); // 释放资源 session.close(); System.out.println("-----------------------------------------------------"); // 再次执行上面代码 // 获取Session Session session1 = factory.openSession(); // 执行 @SuppressWarnings("unchecked") Query<UserInfo> query1 = session1.createQuery(hql).setCacheable(true); // 获取结果 UserInfo uInfo1 = query1.uniqueResult(); System.out.println(uInfo1); // 释放资源 session1.close(); } }
可以看出,第二次查询没有连接数据库,直接从缓存中拿到
B.通用HibernateDAO工具类(配置文件和UserInfo参照上面的)
1.获取Session工具类
HibernateUtil类,简化获取和关闭Session,使用到了单例模式
package org.xxxx.util; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtil { // 线程本地变量,存放线程对象session private static final ThreadLocal<Session> threadLocal = new ThreadLocal<>(); private static final SessionFactory factory; // 初始化,保证只有一个factory,防止出现多线程问题 static { Configuration config = new Configuration().configure(); factory = config.buildSessionFactory(); } // 私有构造,不让创建对象 private HibernateUtil() { } // 获取Session public static Session getSession() { // 从线程本地变量中获取当前线程对象 Session session = threadLocal.get(); // 判断是否存在 if (session == null) { // 为空,创建一个 session = factory.openSession(); // 放置到线程变量中 threadLocal.set(session); } return session; } // 关闭Session public static void closeSession() { // 获取session Session session = threadLocal.get(); // 非空判断 if (session != null) { session.close(); // 将线程变量置空 threadLocal.set(null); } } }
2.通用DAO接口
package org.xxxx.util; import java.io.Serializable; import java.util.List; public interface BaseDAO<T> { // 保存 public Serializable save(final T entity); // 更新,必须带有主键 public void update(final T entity); // 保存或更新 public void saveOrUpdate(final T entity); // 删除,必须带有主键 public void delete(final T entity); // 通过对象标识符获取对象 public T findById(final Serializable oid); // 返回所有对象 public List<T> findAll(); // 获取分页记录 List<T> findByPage(String hql, int start, int pageSize); // 通用查询方法:queryName是命名查询名字 public List<T> executeQuery(String hql, Object... params); // 通用查询方法:queryName是命名查询名字 public List<T> executeNamedQuery(String queryName, Object... params); // 通用更新方法:queryName是命名查询名字 public int executeUpdate(String hql, Object... params); // 通用更新方法:queryName是命名查询名字 public int executeNamedUpdate(String hqlName, Object... params); }
3.通用接口子类
具体抽象实现类可以实现自己的接口,同时继承该子类
使用抽象类不能被实例化,要使用该实现类必须继承
子实现类
package org.xxxx.util; import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import org.hibernate.query.Query; // 抽象类,不能被实例化,要使用只能继承 public abstract class BaseDAPOImpl<T> implements BaseDAO<T> { // 存储泛型的实际参数,即子类所指定的T所对应的类型 // Hibernate底层通过反射实现,所以T即为所对应的类的类型 private Class<T> clazz; @SuppressWarnings("unchecked") public BaseDAPOImpl() { // 返回实际泛型类型 Type type = getClass().getGenericSuperclass(); ParameterizedType types = (ParameterizedType) type; Type[] typeArr = types.getActualTypeArguments(); this.clazz = (Class<T>) typeArr[0].getClass(); } @Override public Serializable save(T entity) { Serializable uid = HibernateUtil.getSession().save(entity); return uid; } @Override public void update(T entity) { HibernateUtil.getSession().update(entity); } @Override public void saveOrUpdate(T entity) { HibernateUtil.getSession().saveOrUpdate(entity); } @Override public void delete(T entity) { HibernateUtil.getSession().delete(entity); } @Override public T findById(Serializable oid) { return HibernateUtil.getSession().get(clazz, oid); } @Override public List<T> findAll() { // 通过反射获取类名 @SuppressWarnings("unchecked") Query<T> query = HibernateUtil.getSession().createQuery("from" + clazz.getSimpleName()); return query.getResultList(); } @Override public List<T> findByPage(String hql, int start, int pageSize) { @SuppressWarnings("unchecked") Query<T> query = HibernateUtil.getSession().createQuery(hql); // 设置开始id以及每页显示数量 query.setFirstResult(start); query.setMaxResults(pageSize); return query.getResultList(); } @Override public List<T> executeQuery(String hql, Object... params) { @SuppressWarnings("unchecked") Query<T> query = HibernateUtil.getSession().createQuery(hql); // 获取参数长度 int len = params.length; // 非空判断 if (len != 0) { for (int i = 0; i < len; i++) { // 设置第i个参数 query.setParameter(i, params[i]); } } return query.getResultList(); } @Override public List<T> executeNamedQuery(String queryName, Object... params) { @SuppressWarnings("unchecked") Query<T> query = HibernateUtil.getSession().getNamedQuery(queryName); // 设置参数 int len = params.length; if (len != 0) { for (int i = 0; i < len; i++) { query.setParameter(i, params[i]); } } return query.getResultList(); } @Override public int executeUpdate(String hql, Object... params) { @SuppressWarnings("unchecked") Query<T> query = HibernateUtil.getSession().createQuery(hql); int len = params.length; if (len != 0) { for (int i = 0; i < len; i++) { query.setParameter(i, params[i]); } } return query.executeUpdate(); } @Override public int executeNamedUpdate(String hqlName, Object... params) { @SuppressWarnings("unchecked") Query<T> query = HibernateUtil.getSession().getNamedQuery(hqlName); int len = params.length; if (len != 0) { for (int i = 0; i < len; i++) { query.setParameter(i, params[i]); } } return query.executeUpdate(); } }
子实现类的子类
package org.xxxx.util; // 可以扩展父类的功能 public class UserInfoDaoImpl<UserInfo> extends BaseDAPOImpl<UserInfo> { }
4.测试
package org.xxxx.util; import org.hibernate.Transaction; import org.xxxx.pojo.UserInfo; public class TestDao { public static void main(String[] args) { // 创建对象 UserInfoDaoImpl<UserInfo> impl = new UserInfoDaoImpl<>(); // 开启事物 Transaction action = HibernateUtil.getSession().beginTransaction(); // 添加数据 UserInfo uInfo = new UserInfo("yangqi", "123456"); impl.save(uInfo); // 提交事物 action.commit(); // 关闭 HibernateUtil.closeSession(); } }
查询数据库