您的位置:首页 > 编程语言 > Java开发

【JavaEE学习笔记】Hibernate_03_缓存机制,自定义通用HibernateDAO工具类

2018-01-14 21:50 736 查看
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();
}
}



查询数据库

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐