hibernate:是关系对象映射框架,对jdbc进行了轻量级的封装。简单说就是将表数据映射到POJO上。流程如下(借图说话):
既然是映射,当然需要配置数据库表与实体类的映射关系,配置文件中当指定表中字段与实体属性如何对应。
配置文件方式
映射关系配置文件如下:(路径可以随便放,只需在hibernate主配置文件中指明映射文件的路径即可)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- package 指明以下配置映射关系的实体类的路径,在此包下的实体都可以一起配置映射 -->
<hibernate-mapping auto-import="true" package="com.muchen.entity">
<!--
必须配置id,hibernate要区分两条数据的不同
name:即package下实体类的名称
table:映射到数据库里面的那个表的名称
catalog:数据库的名称 -->
<class name="Xzqh" table="XZQH" catalog="MASZWFW">
<id name="qhbm" column="QHBM">
<!-- increment: long、int、short的自增长,高并发时可能出现增长的id一样的情况
identity:SQLServer 、MySQL的自增长
sequence:Oracle、DB2的自增长
native:根据底层数据库对生成标识符的支持自动选择使用identity、sequence、hilo标识符生成器
uuid:由hibernate生成的字符型主键
assigned:由程序员生成指定
-->
<generator class="native"></generator>
</id>
<property name="pqhbm" column="pqhbm"></property>
<property name="qhmc" column="qhmc"></property>
<property name="qhjb" column="qhjb"></property>
</class>
<!-- 实体属性可以多于、少于、等于表字段数,只需配置所需映射的属性和字段即可 -->
<class name="Vote" table="T_VOTE" catalog="MASZWFW">
<id name="uuid" column="uuid">
<generator class="uuid"></generator>
</id>
<property name="businesseId" column="BUSINESSID"></property>
<property name="date" column="VOTE_DATE"></property>
<property name="order" column="VOTE_ORDER"></property>
<property name="type" column="VOTE_TYPE"></property>
<property name="userAccount" column="USER_ACCOUNT"></property>
</class>
</hibernate-mapping>
主配置文件:
<?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 中的元素 -->
<session-factory>
<property name="connection.driver_class">oracle.jdbc.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@100.235.111.35:1521:ceshi</property>
<property name="connection.username">masa</property>
<property name="connection.password">masa</property>
<property name="connection.autocommit">false</property>
<property name="connection.pool_size">10</property>
<property name="format_sql">true</property>
<property name="show_sql">true</property>
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<!-- 使用getCurrentSession()必须要有以下配置 值为thread或jta-->
<property name="current_session_context_class">thread</property>
<!-- 读取映射文件,在此不支持模糊匹配 -->
<mapping resource="com/muchen/util/entity.hibernate-first.xml"/>
</session-factory>
</hibernate-configuration>
获得主配置对象:
Configuration config = new Configuration().configure();// 默认配置名:hibernate.cfg.xml
Configuration config1 = new Configuration().configure("mu.hibernate.cfg.xml");// 指定配置名称
此方法底层:
// 没有指定主配置文件名时,默认为hibernate.cfg.xml
public Configuration configure()
throws HibernateException
{
configure("/hibernate.cfg.xml");
return this;
}
// 指定主配置文件名时
public Configuration configure(String resource)
throws HibernateException
{
log.info("configuring from resource: " + resource);
InputStream stream = getConfigurationInputStream(resource);
return doConfigure(stream, resource);
}
// 加载主配置文件里的内容
protected Configuration doConfigure(org.dom4j.Document doc)
throws HibernateException
{
// 读取 session-factory 标签
Element sfNode = doc.getRootElement().element("session-factory");
// 获取标签下的属性
String name = sfNode.attributeValue("name");
if (name != null) {
this.properties.setProperty("hibernate.session_factory_name", name);
}
addProperties(sfNode);
// 产生SessionFactory
parseSessionFactory(sfNode, name);
Element secNode = doc.getRootElement().element("security");
if (secNode != null) {
parseSecurity(secNode);
}
log.info("Configured SessionFactory: " + name);
log.debug("properties: " + this.properties);
return this;
}
获取Session对象:
Configuration config1 = new Configuration().configure("mu.hibernate.cfg.xml");// 指定配置名称
SessionFactory sessionFactory = config1.buildSessionFactory();
//使用openSession方式
Session session = sessionFactory.openSession();
session.beginTransaction();
Query query = session.createQuery("from Xzqh where qhjb=?");
List<Xzqh> list = query.setString(0, "3").list();
session.getTransaction().commit();
session.close();
//使用getCurrrentSession方式
Session currentSession = sessionFactory.getCurrentSession();
// getCurrrentSession 必须要在事务中
currentSession.beginTransaction();
//...
//
currentSession.getTransaction().commit();
这里获取的Session不同于HttpSession,前者是对象持久化的管理器,后者用于记录前端用户的状态。
openSession得到的session每次都是新的session,需手动关闭,而getCurrentSession获得的session是绑定到当前线程上的,只要此session或者该线程未关闭,获得的都是同一个session,当食物commit或rollback会自动关闭此session,不需要手动显式关闭。
注解方式
用注解方式代替hibernate-mapping配置:
@Entity // 声明实体
@Table(name="T_VOTE",catalog="MASZWFW")// 实体对应的表名和数据库名
public class Vote {
@Id
// 通用主键生成器
@GenericGenerator(name="myuuid",strategy="uuid")
// 主键生成值
@GeneratedValue(generator="myuuid")
@Column(name="UUID")
private String uuid;
@Column(name="BUSINESSID")
private String businesseId;
@Column(name="USER_ACCOUNT")
private String userAccount;
@Column(name="VOTE_TYPE")
private String type;
@Column(name="VOTE_DATE")
private String date;
@Column(name="VOTE_ORDER")
private Integer order;
@Transient // 此属性不映射到表中
private String something;
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getBusinesseId() {
return businesseId;
}
public void setBusinesseId(String businesseId) {
this.businesseId = businesseId;
}
public String getUserAccount() {
return userAccount;
}
public void setUserAccount(String userAccount) {
this.userAccount = userAccount;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public Integer getOrder() {
return order;
}
public void setOrder(Integer order) {
this.order = order;
}
public String getSomething() {
return something;
}
public void setSomething(String something) {
this.something = something;
}
}
注意:属性映射注解要么全部放在属性上,要么全部放在get()方法上,否则会报错。基于面向对象的思想,最好还是放在方法上。
使用注解的方式取代配置映射文件,加上了映射注解的pojo也就相当于此pojo的映射配置文件,只需将此pojo加入hibernate主配置文件中即可:
<hibernate-configuration>
<!-- 读取此配置文件时,会读取 session-factory 中的元素 -->
<session-factory>
<property name="connection.driver_class">oracle.jdbc.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@10.235.11.35:1521:ceshiku</property>
<property name="connection.username">maszwfw</property>
<property name="connection.password">maszwfw</property>
<property name="connection.autocommit">false</property>
<property name="connection.pool_size">10</property>
<property name="format_sql">true</property>
<property name="show_sql">true</property>
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<!-- 使用getCurrentSession()必须要有以下配置 值为thread或jta-->
<property name="current_session_context_class">thread</property>
<!-- 读取映射文件,在此不支持模糊匹配 -->
<!-- <mapping resource="com/muchen/util/entity.hibernate-first.xml"/> -->
<mapping class="com.muchen.entity.Vote"/>
</session-factory>
</hibernate-configuration>
映射是配置文件时:mapping 指定resource=配置文件路径;
映射是基于注解的pojo时:mapping指定class=该pojo完整类名。
读取配置文件:
Configuration config = new Configuration().configure("mu.hibernate.cfg.xml");
Configuration config = new AnnotationConfiguration().configure("mu.hibernate.cfg.xml");
使用注解时,hibernate3.0以前需使用AnnotationConfiguration()方式,才能解析hibernate注解,在3.0以后使用Configuration()就可以了,同样实现了解析注解的功能。
常见数据库操作
Hql:Query 针对实体操作
// 增删改
session.save(Object)/update(Object)/delete(Object)
// 查询
Query query = session.createQuery("from Vote where userAccount=?");
query.setString(0,"12364").list();
Sql:SQLQuery
SQLQuery是Query的子接口 public interface SQLQuery extends Query,所以Query有的方法SQLQuery都有,执行的是普通的sql语句
// 增删改
SQLQuery sql = session.createSQLQuery("delete from T_VOTE where USER_ACCOUNT=?");
sql.setString(0, "mn2527651").executeUpdate();
查询
SQLQuery sql = session.createSQLQuery
("select uuid qhbm,BUSINESSID pqhbm,VOTE_DATE hmc,VOTE_TYPE qhjb,VOTE_ORDER from T_VOTE where USER_ACCOUNT=?");
sql.setString(0, "songzhili1");
List<Object[]> sd = sql.list();// 返回的集合中的元素为Object[]数组
/* 指定查询的字段中取用哪些字段并指定这些字段的类型:addScalar */ 未指定字段将不返回在list中
sql.addScalar("qhbm", StandardBasicTypes.STRING).addScalar("pqhbm", StandardBasicTypes.STRING).
addScalar("qhmc", StandardBasicTypes.STRING).addScalar("qhjb", StandardBasicTypes.STRING);
/* 将查询的记录绑定到实体:addEntity */ 返回的记录的字段要能与实体属性对应上,
返回的记录字段数不能比要对应的实体属性少,可以相等,也可以多于 */
List<Xzqh> aa = sql.addEntity(Xzqh.class).list(); // 必须使用addEntity 才能强转成功
除了Query和SQLQuery使用外,结合Spring框架在Service中 extends HibernateDaoSupport 可以获得 getHibernateTemplate(),类似Spring框架的 SimpleJdbcTemplate模板,不同的是前者通过extends获得,后者通过DataSource注入获得。
分页:
sqlQuery.setFirstResult(int);设置起始行数
sqlQuery.setMaxResults(int);设置要取的行数(pageSize)
Criteria:以面向对象的方式查询
Criteria c = session.createCriteria(Vote.class);// 创建一个Criteria对象
c.add(Restrictions.eq("userAccount", "songzhili1"));// 相当于 where userAccount='songzhili1'
List<Vote> aa = c.list();
与Query相比,适合查询指定对象,限制条件多的,简单的,不需要怎么写sql的,Query功能更强大。
缓存
缓存介于应用程序与物理数据库之间,介质一般为内存或硬盘(数据量大时),能有效降低应用程序对数据库的访问频率,提高运行性能。
hibernate缓存分一级和二级缓存,一级缓存是session级别的缓存,是事务级别的缓存,二级缓存是sessionFactory级别的缓存,是进程级别的缓存。
一级缓存
在session级别,默认开启,无法卸载,在session关闭或事务关闭后缓存消失。
只有使用session的load() / get() 和query.iterator()等方法时才会将对象载入缓存,使用query其他方法并没有将结果对象加入缓存,由于session开闭比较频繁,所以在session级的缓存命中率很低。
VoteInfo v1= (VoteInfo) session.get(VoteInfo.class, 21);
VoteInfo v2= (VoteInfo) session.load(VoteInfo.class, 21);
上述代码执行过程只会发送一次sql,第二次从缓存中取:第一次查询在缓存中保存了此对象及该对象的持久化OID,第二次查询先从一级缓存中查询。
Iterator<VoteInfo> list2 = session.createQuery("from VoteInfo WHERE id=?").setInteger(0, 21).iterate();
while(list2.hasNext()){
System.out.println(list2.next());
}
N+1问题:使用query.itrator()会先查询返回符合查询条件的OID,当需要使用对象时如果缓存中有此持久化对象则直接从缓存取,没有才会去数据库查询对象。
二级缓存
由于一级缓存频繁开闭,导致一级缓存使用生命周期很短,真正使用命中率很低,所以出现了二级缓存,也称sessionFactory缓存,属于进程级共享缓存,而hibernate本身并没有自己去实现二级缓存,而是通过第三方的方式实现的二级缓存。(二级缓存默认关闭)
开启二级缓存:既然是sessionFactory缓存,那在配置文件生成SessionFactory时就已经配置好SessionFactory的缓存机制,在hibernate.cfg.xml中添加缓存配置
1. 开启二级缓存
2. 指明二级缓存供应方
<hibernate-configuration>
<!-- 读取此配置文件时,会读取 session-factory 中的元素 -->
<session-factory>
<property name="connection.driver_class">oracle.jdbc.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@localhost:1521:xzsp</property>
<property name="connection.username">xzsp</property>
<property name="connection.password">xzsp</property>
<property name="connection.autocommit">false</property>
<property name="connection.pool_size">10</property>
<property name="format_sql">true</property>
<property name="show_sql">true</property>
<!-- SessionFactory开启二级缓存-->
<property name="cache.use_second_level_cache">true</property>
<!-- SessionFactory二级缓存实现方-->
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<!-- 使用getCurrentSession()必须要有以下配置 值为thread或jta-->
<property name="current_session_context_class">thread</property>
<mapping class="com.muchen.entity.Xzqh"/>
<mapping class="com.muchen.entity.VoteInfo"/>
</session-factory>
</hibernate-configuration>
3. 指定缓存对象
3.1 配置映射文件的方式
添加:<cache usage="read-only"/>
<class name="Vote" table="T_VOTE" catalog="MASZWFW">
<cache usage="read-only"/>
<id name="uuid" column="uuid">
<generator class="uuid"></generator>
</id>
<property name="businesseId" column="BUSINESSID"></property>
<property name="date" column="VOTE_DATE"></property>
<property name="order" column="VOTE_ORDER"></property>
<property name="type" column="VOTE_TYPE"></property>
<property name="userAccount" column="USER_ACCOUNT"></property>
</class>
3.2 注解方式
实体上添加缓存注解:@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
@Entity
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
@Table(name="T_JC_VOTEINFO",catalog="XZSP")
public class VoteInfo {
private Integer id;
private String voteName;
private Integer voteKindId;
private String voteObject;
private String type;
private String year;
@Id
@Column(name="ID")
@GeneratedValue(strategy=GenerationType.SEQUENCE)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name="VOTENAME")
public String getVoteName() {
return voteName;
}
public void setVoteName(String voteName) {
this.voteName = voteName;
}
}
注:read-only方式用的多,因为缓存中适合放置的对象基本为以下几种
a. 不怎么需要变动(写操作很少)
b. 不考虑并发的不是很重要的数据
c. 数据量不是很大的情况(多了反而占用资源,性能下降)
4. 配置缓存文件ehcache.xml
放置在classpath路径下,Configuration.config加载hibernate配置文件解析session-factory元素时加载ehcache.xml文件。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" >
<!-- 指定磁盘缓存路径
* user.home - 用户的主目录
* user.dir - 用户当前工作目录
* java.io.tmpdir - 默认临时文件路径 -->
<diskStore path="e:\\tempCache"/>
<!-- 设置缓存的默认数据过期策略 -->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<!--
设定具体的命名缓存的数据过期策略。每个命名缓存代表一个缓存区域
缓存区域(region):一个具有名称的缓存块,可以给每一个缓存块设置不同的缓存策略。
如果没有设置任何的缓存区域,则所有被缓存的对象,都将使用默认的缓存策略。即:<defaultCache.../>
Hibernate 在不同的缓存区域保存不同的类/集合。
对于类而言,区域的名称是类名。如:com.atguigu.domain.Customer
对于集合而言,区域的名称是类名加属性名。如com.atguigu.domain.Customer.orders
-->
<!--
name: 设置缓存的名字,它的取值为类的全限定名或类的集合的名字
maxElementsInMemory: 设置基于内存的缓存中可存放的对象最大数目
eternal: 设置对象是否为永久的, true表示永不过期,
此时将忽略timeToIdleSeconds 和 timeToLiveSeconds属性; 默认值是false
timeToIdleSeconds:设置对象空闲最长时间,以秒为单位, 超过这个时间,对象过期。
当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。
timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。
如果此值为0,表示对象可以无限期地存在于缓存中. 该属性值必须大于或等于 timeToIdleSeconds 属性值
overflowToDisk:设置基于内存的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中
diskPersistent: 在重新启动虚拟机之间,磁盘存储是否持久化,默认值为false。
diskExpiryThreadIntervalSeconds:运行磁盘过期线程之间的秒数。默认值是120秒
diskSpoolBufferSizeMB: 这是为spool缓冲区分配磁盘存储的大小。写的是然后异步写入磁盘。默认大小为30MB。
memoryStoreEvictionPolicy:缓存对象清除策略。有三种:
1 FIFO ,first in first out ,先进先出
2 LFU , Less Frequently Used ,一直以来最少被使用的。缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存。
3 LRU ,Least Recently Used ,(默认)最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
<cache name="com.muchen.entity.VoteInfo"
maxElementsInMemory="1"
eternal="false"
timeToIdleSeconds="60"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
</ehcache>
注意:ehcache.xml放在项目中时,里面不能有中文,是中文注释也不行,会报错。
查看二级缓存:
Map cacheMap = sessionFactory.getStatistics().getSecondLevelCacheStatistics("com.muchen.entity.VoteInfo").getEntries();
只要是对com.muchen.entity.VoteInfo的查询操作都会进入二级缓存,不管是get(),load(),iterator()还是Query的方法,只要SessionFactory不关闭,缓存的对象在配置的有效时间内会一直存在,除非被手动清除。
清除指定缓存对象:
清除指定对象: sessionFactory.evict(VoteInfo.class,指定id);
清除所有某对象: sessionFactory.evict(VoteInfo.class);
hibernate缓存对象的3种状态
transient:瞬时态,一般是通过new 获得的对象,尚未与session取得关联,随时被jvm回收;
persistent:持久态,在数据库有对应的记录,通过查询或保存瞬时态获得的对象,此时存在session中;
detached:游离态,有OID,由于session关闭失去与session的关联,变成游离态。