1.orm框架
对象/关系映射,将关系数据库表中的记录映射称为对象
2.Hibernate的优点:
非常优秀的开源和免费的orm实现框架,轻量级,有可拓展性
3.Hibernate事务
1)通过begintransaction()方法打开,由transactionfactory实例产生
2)直接使用底层的jdbc连接
3)线程不是安全的
4)事务由session的方法开启
4.Hibernate需要的配置文件
1)hibernate.properties文件或者hibernate.cfg.xml
2)xxxx.hbm.xml
备注:需要导入的jar
5.Hibernate的核心接口
1)Configuration:配置hibernate,根启动hibernate,创建sessionfactory对象
2)Sessionfactory:初始化hibernate,充当数据存储的代理,创建session对象,这是一个线程安全的,重量级的(因为有很大的缓存,所以有相应的缓存插件),只初始一次
3)Session:负责保存、更新、删除、加载和查询对象,不是线程安全的,应根据要求设置相应的隔离级别
4)Transaction:管理事物
5)Query和criteria:执行数据库查询
6)开启session和关闭session详细过程说明
开启方式1无需在cfg.xml文件在配置mapping
// new Configuration()获取src下的hibernate.properties文件 new Configuration().configu//re(); 获取src下的hibernate.cfg.xml文件
Configuration configure = new Configuration().configure();
configure.addClass(Person.class);
//创建服务注册对象
ServiceRegistry serviceRegistry=new StandardServiceRegistryBuilder().applySettings(configure.getProperties()).build();
//根据服务注册对象创建sessionfactory,建立数据库连接,内部有一个线程池,线程安全
sessionFactory = configure.buildSessionFactory(serviceRegistry);
//随机从连接池中获取一个连接,创建session对象,对事物进行操作,线程不安全
session = sessionFactory.openSession();
//开启一个事物,设置为手动提交
transaction = session.beginTransaction();
开启方式2需要在cfg.xml中配置mapping更有利于维护
Configuration configure = new Configuration().configure();
//按读取hibernate.cfg.xml文件配置sessionfactory对象
sessionFactory = configure.buildSessionFactory();
session = sessionFactory.openSession();
transaction = session.beginTransaction();
关闭
//关闭一个事物
transaction.commit();
//关闭session
session.close();
//关闭factory
sessionFactory.close();
配置文件说明
<session-factory>
<!-- 关联数据库 -->
<property name="connection.username">root</property>
<property name="connection.password">cgz12345678</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/table?characterEncoding=UTF-8</property>
<!-- 配置hibernate的基本信息 -->
<!-- hibernate所使用的数据库方言 -->
<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!-- 执行操作时是否再控制台打印sql 是否再控制台打印 -->
<property name="show_sql">true</property>
<!-- 是否对sql进行格式化 否则在一行中输出-->
<property name="format_sql">true</property>
<!-- 指定自动生成表的策略 -->
<!-- 这其中的值一共有四个 create 每次都会删除上一次的表,创建新的表| update 更新表结构 不会删除已有的行和列 | create-drop 当sessionfactory关闭后表会删除 | validate 会和数据库中的表进行比较,若.hbmxml文件的列在数据表中不存在则报异常-->
<property name="hbm2ddl.auto">update</property>
<!-- 设置隔离级别 1 2 4 8
1 读未提交 脏读 幻读 不可重复读都存在
2 读已提交 幻读 不可重复读存在
3 可重复读 幻读依然存在
4 串行化 解决上述问题,变成了线程安全的,效率低下
-->
<property name="connection.isolation">2</property>
<!-- 将删除后的oid置为空 -->
<property name="use_identifier_rollback">true</property>
<!-- 设定 JDBC 的 Statement 读取数据的时候每次从数据库中取出的记录条数 -->
<property name="hibernate.jdbc.fetch_size">100</property>
<!-- 设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小 -->
<property name="jdbc.batch_size">8</property>
//配置.hbm.xml文件,也可以不写,在configure.addClass(Person.class);进行设置
<mapping resource="_1_1/Person.hbm.xml"/>
</session-factory>
详情见hibernate-release-4.2.16.Final/hibernate-release-4.2.16.Final/documentation/manual/en-US/html/index.html的List of Tables
6.Flush,refresh,clean说明
Flush:将缓存中的数据更新数据库,
Refresh:将数据库的数据更新到缓存
Clear:清除缓存
7.Flush与commit的区别
Flush;执行一系列的sql语句,但是不会提交事物
Commit:先执行flush然后提交事物
8.Person.hbm.xml配置文件说明
<hibernate-mapping>
<!-- discriminator-value="person"这个值可以随便写 -->
<class name="hibernate.Person" table="PERSON" discriminator-value="person">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<!—class 主键生成策略见说明—>
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<property name="age" type="int">
<column name="AGE" />
</property>
</class>
</hibernate-mapping>
主键生成策略
9.持久化的四种状态
临时对象:在数据库没有相应的记录,不在session缓存中
托管(持久化对象):在数据库中有相应对象, 位于session缓存中
删除对象:不在缓存中,也不在数据库中
游离对象(脱管)iod不为空,不在session缓存中,可能在数据库中
10.对象状态的转换
临时对象:new
临时对象---持久化对象 save saveorupdate persist merge
持久化对象---游离对象 evict close clear
游离对象---持久化对象 update saveorupdate merge
游离状态—删除状态 delete
持久化状态—删除状态 delete
Persist与save的区别
Save方法之前可以设置oid,会把该对象以一个新的iod(这个不一定是设置的oid,与主键生成方式有关)保存在数据库中,但是persist会抛出异常
Get和load方法
Get方法采用立即检索策略 load方法采用延迟检索策略,延迟检索存在安全问题(假如在提交在前关闭session,会出现异常)
对jdbc api的访问
Work接口
执行work对象指定的操作可以采用session.dowork(work)方法
12.关系映射
基于主键的单向1对1
配置文件如下
<generator class="foreign" >
<!—主键的生成方式基于address属性生成表的主键一致 -->
<param name="property">address</param>//两表的主键生成策略一致
</generator>
<!-- cascade指定实体之间的级联行为,all表示自动的插入所需的级联风格-->
<one-to-one name="address" cascade="all" ></one-to-one>//由于主键生成策略一致,主键对主键,所以没有必要写column,关联关系是以键的形式连接
<!-- -->
基于外键的单向1对1
<!-- unique=true 添加唯一的约束 unique-key="ID"设置添加唯一的属性-->
<many-to-one name="address" class="_1_1.Address" unique-key="ID" cascade="all" unique="true">//由于指定了unique的,所以关联的外键是unique指定的键
<column name="ADDRESS" />//自己的行address,对用表的栏ADDRESS,跟随unique-key="ID"的id来建立外键
</many-to-one>
基于外连接表的单向1对1
<!—强制添加表 key必须要写 -->
<join table="persion_address">
<key column="person_ID"></key>
<many-to-one name="address" class="n_1_2.Address" cascade="all" unique-key="ID" unique="true">
<column name="ID" />
</many-to-one>
</join>
基于外键双向1对1
<!— property-ref 依赖关系person需要依赖address属性,address中需要有person属性及相应的get和set方法 -->
<one-to-one name="person" property-ref="address"></one-to-one>
<many-to-one name="address" class="Address" unique-key="ID" cascade="all" unique="true">
<column name="ADDRESS" />//查看sql语句就可以知道ADDRESS作为外键对应person表的id
</many-to-one>
基于外表双向1-1
<!—inverse设置为true的一方负责维护关联关系 一般设置在one的一端,两端都没有设置,那么两端都维护,会多出update语句-,一端用inverse另一端用options->
<join table="person_address" inverse="true">
<key column="ADDRESS" unique="true"></key>//建立管理的外键,在双向中,一般来说外键对应是一致的,否则找不到栏
<many-to-one name="person" column="person_id" unique="true"></many-to-one>//查看sql可以知道person_id作为外键对应person的主键
<join table="person_address" optional="true">
<key column="person_id" unique="true"></key>
<many-to-one name="address" class="Address" unique-key="ID" cascade="all" unique="true">//查看sql语句就可以知道ADDRESS作为外键对应address表的id
<column name="ADDRESS" />
</many-to-one>
</join>
基于主键的双向1对1
<one-to-one name="person" ></one-to-one>
<generator class="foreign" >
<param name="property">address</param>
</generator>
<one-to-one name="address" ></one-to-one>
基于外键的单向1对多
可以是set map list
<set name="address" cascade="all">
<key column="person_id"></key>
<one-to-many class="Address" />
</set>
基于外表的单向一对多
<set name="address" cascade="all" table="person_address">
<key column="person_id" unique="true"></key>
<many-to-many class="Address" column="adress_id" ></many-to-many>
</set>
基于外键的双向1对多
<many-to-one name="person" class="Person" column="person_id"></many-to-one>
<set name="address" cascade="all" inverse="true">
<key column="person_id"></key>
<one-to-many class="Address" />
</set>
基于外表的双向1对1
<join table="person_address">
<key column="address_id"></key>
<many-to-one name="person" class="Person" column="person_id"></many-to-one>
</join>
<set name="address" cascade="all" inverse="true" table="person_address">
<key column="person_id"></key>
<many-to-many class="Address" column="address_id" unique="true"/>
</set>
基于外键的单向多对1
<many-to-one name="address" class="n_1.Address" cascade="all" >
<column name="ADDRESS" />
</many-to-one>
基于外表的单向多对1
<join table="persion_address">
<key column="person_ID"></key>
<many-to-one name="address" class="n_1_2.Address" cascade="all" >
<column name="ID" />
</many-to-one>
</join>
基于外表的单向多对多
<set name="address" cascade="all" table="person_address1">
<key column="person_id" ></key>
<many-to-many class="Address" column="adress_id" ></many-to-many>
</set>
基于外表的双向多对多,必须将一端的inverse设置为true
<set name="address" cascade="all" table="person_address1">
<key column="person_id" ></key>
<many-to-many class="Address" column="adress_id" ></many-to-many>
</set>
<set name="persons" table="person_address1" inverse=true>
<key column="adress_id"></key>
<many-to-many column="person_id" class="Person></many-to-many>
</set>
组件属性包含关联关系
<component name="address" class="Address">
<!-- 映射组件指向自己的实体本生 -->
<parent name="person" />
<property name="addressDetail" type="java.lang.String" column="addressDetail"></property>
<set name="persons">
<key column="persons"></key>
<one-to-many class="Person"/>
</set>
</component>
</class>
继承映射
Subclass:组合到一张表中
<subclass name="Employee" discriminator-value="雇员">
<property name="title"></property>
<property name="salary"></property>
</subclass>
Join-subclass:字表只有父表没有的属性
<!—辨别者列 -->
discriminator-value="人"
<discriminator column="pers" type="string"></discriminator>
<joined-subclass name="Employee" table="employee">
<!-- key 和父主键id一致 -->
<key column="id"></key>
<property name="title"></property>
<property name="salary"></property>
</joined-subclass>
Union-subclass:子表中用于父属性
<union-subclass name="Employee" table="employee1">
<property name="title"></property>
<property name="salary"></property>
</union-subclass>
其中常用的属性说明
Class下的属性
unsaved-value:若设定了该属性, Hibernate 会通过比较持久化类的 OID 值和该属性值来区分当前持久化类的对象是否为临时对象
type:指定 Hibernate 映射类型. Hibernate 映射类型是 Java 类型与 SQL 类型的桥梁. 如果没有为某个属性显式设定映射类型, Hibernate 会运用反射机制先识别出持久化类的特定属性的 Java 类型, 然后自动使用与之对应的默认的 Hibernate 映射类型
generator:设定持久化类设定标识符生成器
generato下的class: 指定使用的标识符生成器全限定类名或其缩写名
property
column:指定与类的属性映射的表的字段名. 如果没有设置该属性, Hibernate 将直接使用类的属性名作为字段名.
name:指定该持久化类的属性的名字
type:指定 Hibernate 映射类型. Hibernate 映射类型是 Java 类型与 SQL 类型的桥梁. 如果没有为某个属性显式设定映射类型, Hibernate 会运用反射机制先识别出持久化类的特定属性的 Java 类型, 然后自动使用与之对应的默认的 Hibernate 映射类型.
not-null:若该属性值为 true, 表明不允许为 null, 默认为 false
access:指定 Hibernate 的默认的属性访问策略。默认值为 property, 即使用 getter, setter 方法来访问属性. 若指定 field, 则 Hibernate 会忽略
unique: 设置是否为该属性所映射的数据列添加唯一约束.
index: 指定一个字符串的索引名称. 当系统需要 Hibernate 自动建表时, 用于为该属性所映射的数据列创建索引, 从而加快该数据列的查询.
length: 指定该属性所映射数据列的字段的长度
scale: 指定该属性所映射数据列的小数位数, 对 double, float, decimal 等类型的数据列有效
formula:设置一个 SQL 表达式, Hibernate 将根据它来计算出派生属性的值.
派生属性: 并不是持久化类的所有属性都直接和表的字段匹配, 持久化类的有些属性的值必须在运行时通过计算才能得出来, 这种属性称为派生属性,使用 formula 属性时formula=“(sql)” 的英文括号不能少,Sql 表达式中的列名和表名都应该和数据库对应, 而不是和持久化对象的属性对应,如果需要在 formula 属性中使用参数, 这直接使用 where cur.id=id 形式, 其中 id 就是参数, 和当前持久化对象的 id 属性对应的列的 id 值将作为参数传入.
Cascade属性的值
Subclass,joined-subclass,union-subclass继承映射的区别
subclass:将域模型中的每一个实体对象映射到一个独立的表中,也就是说不用在关系数据模型中考虑域模型中的继承关系和多态
joined-subclass : 对于继承关系中的子类使用同一个表,这就需要在数据库表中增加额外的区分子类类型的字段
union-subclass:域模型中的每个类映射到一个表,通过关系数据模型中的外键来描述表之间的继承关系。这也就相当于按照域模型的结构来建立数据库中的表,并通过外键来建立表之间的继承关系。
14.类级别的检索策略
立即检索:不论是持久化还是游离态,应用程序都可以方便的从一个对象到与它关联的对象,但是select语句较多,占用空间,性能大
延迟检索:仅加载需要的对象,节省空间,提高性能,但是希望访问游离态的代理类对象,必须保证它在持久化状态已经被初始化 多对多 一对多
迫切左外:多对一 一对一,:不论是持久化还是游离态,应用程序都可以方便的从一个对象到与它关联的对象,相对立即检索少,可能存在重复元素,需要去重
lazy 和 fetch 属性(一对多 多对多检索策略)
进一步说明:Lazy:true表示立即检索,会立即相应的加所有相关联的对象,假如设置为false,那么会用代理对象会采用一个代理模式,实际上里面是空的,只有在用的时候才会相应的加载,例如客服有100个订单,立即检索会相应的全部初始化,延迟检索返回的是一个代理对象,只有在用的时候才会加载,假如设置为extra,那么是增强延迟检索,与延迟检索的区别在于,也就是说当访问size,isempty时不会初始化代理实例
Set中的Batch-size属性,减少select语句的数量,提高性能,在set中设置,采用延迟检索策略后,当检索相应的集合时,只立即检索相应的语句的对象,设置为3~10之间较为合适
fetch 属性: 若取值为”join”, 则决定 orders 集合被初始化的时机,若把 fetch 设置为 “join”, lazy 属性将被, 取值为 “select” 或 “subselect” 时, 决定初始化 orders 的查询语句的形式; 若取值为”join”, lazy 属性将被忽略,采用迫切左外连接(通过左外连接加载与检索指定的对象关联的对象)策略来检索所有关联的 Order 对象,Query 的list() 方法会忽略映射文件中配置的迫切左外连接检索策略, 而依旧采用延迟加载策略,默认值为 select,当 fetch 属性为 “subselect” 时,假定 Session 缓存中有 n 个 orders 集合代理类实例没有被初始化, Hibernate 能够通过带子查询的 select 语句, 来批量初始化 n 个 orders 集合代理类实例,batch-size 属性将被忽略,子查询中的 select 语句为查询 CUSTOMERS 表 OID 的 SELECT 语句
15检索方式
Oid hql qbc
16 HQL 的参数绑定由两种形式:
按参数名字绑定: 在 HQL 查询语句中定义命名参数, 命名参数以 “:” 开头.
按参数位置绑定: 在 HQL 查询语句中用 “?” 来定义参数位置
17检索的hql语句和qbc语句
方式1:在java中书写
基于hql
//方法1 根据位置写 基于位置参数
String sql="from employee e where e.salary >? and eamil like ?";
Query query = session.createQuery(sql);
query.setParameter(0, "5500").setParameter(1, "%q%");
List list = query.list();
System.out.println(list.size());
//方法2 根据名称写 基于命名参数
String sql="from employee e where e.salary >:sa and eamil like :ea";
Query query = session.createQuery(sql);
query.setParameter("sa", "5500").setParameter("ea", "%q%");
List list = query.list();
System.out.println(list.size());
基于qbc
Criteria criteria = session.createCriteria(Employee.class);
Conjunction conjunction = Restrictions.conjunction();
conjunction.add(Restrictions.like("name", "a", MatchMode.ANYWHERE));
Department dept = new Department();
dept.setId(10);
conjunction.add(Restrictions.eq("dept", dept));
System.out.println(conjunction);
方式2:在相应的hbm.xml中书写,更加有利于维护
<query name="salaryEmps"><![CDATA[FROM Employee e WHERE e.salary > :minSal AND e.salary < :maxSal]]></query>
采用
Query query = session.getNamedQuery("salaryEmps");
List<Employee> emps = query.setFloat("minSal", 5000).setFloat("maxSal", 10000).list();
17.hql检索
分页查询
Setfirstresult 设置页首
Setmaxresult:一次最多检索到的数目
query.setFirstResult(2).setMaxResults(5).list();
单个对象检索
Uniqueresult:单个对象检索
按主键逐个检索
Iterate:按主键逐个检索 在开启二级缓存后性能稍微有所提升(前提条件要查询的数据表中包含大量字段,启用了二级缓存, 且二级缓存中可能已经包含了待查询的对象,否则更多,并不建议使用)
投影检索
投影查询: 查询结果仅包含实体的部分属性. 通过 SELECT 关键字实现.,Query 的 list() 方法返回的集合中包含的是数组类型的元素, 每个对象数组代表查询结果的一条记录,可以在持久化类中定义一个对象的构造器来包装投影查询返回的记录, 使程序代码能完全运用面向对象的语义来访问查询结果集 ,可以通过 DISTINCT 关键字来保证查询结果不会返回重复元素
String hql = "SELECT new Employee(e.email, e.salary, e.dept) FROM Employee e
WHERE e.dept = :dept";
Department dept = new Department();
dept.setId(10);
List<Employee> result = query.setEntity("dept", dept).list();
for(Employee emp: result){
System.out.println(emp.getId() + ", " + emp.getEmail() + ", " + emp.getSalary() + ", " + emp.getDept());}
报表查询
GROUP BY 关键字对数据分组, 用 HAVING 关键字对分组数据设定约束条件.
可以调用的聚集函数count() min() max() sum() avg()
String hql = "SELECT min(e.salary), max(e.salary) FROM Employee e GROUP BY e.dept HAVING min(salary) > :minSal";
说明:HAVING min(salary) > :minSa分组约束 GROUP BY e.dept根据同名的dept分组 min(e.salary), max(e.salary)组里面的最大和最小
迫切左外连接:
LEFT JOIN FETCH 关键字表示迫切左外连接检索策略.,list() 方法返回的集合中存放实体对象的引用, 每个 Department 对象关联的 Employee 集合都被初始化, 存放所有关联的 Employee 的实体对象. ,查询结果中可能会包含重复元素, 可以通过一个 HashSet 来过滤重复元素,HQL 会忽略映射文件中设置的迫切左外连接检索策略, 如果希望 HQL 采用迫切左外连接策略, 就必须在 HQL 查询语句中显式的指定它,若在 HQL 代码中显式指定了检索策略, 就会覆盖映射文件中配置的检索策略
String hql = "SELECT DISTINCT d FROM Employee d left JOIN FETCH d. dept” DISTINCT去重 左外和迫切左外推荐迫切左外 含没有部门的人
左外连接:
LEFT JOIN 关键字表示左外连接查询,list() 方法返回的集合中存放的是对象数组类型
根据配置文件来决定 Employee 集合的检索策略. ,如果希望 list() 方法返回的集合中仅包含 Department 对象, 可以在HQL 查询语句中使用 SELECT 关键字
String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN d.emps"
迫切内连接:
INNER JOIN FETCH 关键字表示迫切内连接, 也可以省略 INNER 关键字,list() 方法返回的集合中存放 Department 对象的引用, 每个 Department 对象的 Employee 集合都被初始化, 存放所有关联的 Employee 对象
String hql = "SELECT DISTINCT d FROM Employee d inner JOIN FETCH d. dept” DISTINCT去重 不含没有部门的人
内连接:
INNER JOIN 关键字表示内连接, 也可以省略 INNER 关键字,list() 方法的集合中存放的每个元素对应查询结果的一条记录, 每个元素都是对象数组类型,如果希望 list() 方法的返回的集合仅包含 Department 对象, 可以在 HQL 查询语句中使用 SELECT 关键字
String hql = "SELECT e FROM Employee e INNER JOIN e.dept";
说明:迫切表示集合已经初始化,查询的时候可以连续输出,而没有迫切表示集合没有被初始化,集合中放的是引用,所以每个对象中内容会连续输出,不同的不会连续输出
18.HQL运算符QBC 运算符含义
= Restrictions.eq() 等于
<> Restrictions.ne()不等于
> Restrictions.gt()大于
>=Restrictions.ge()大于等于
< Restrictions.lt()小于
<=Restrictions.le()小于等于
is null Restrictions.isnull()等于空值
is not null Restrictions.isNotNull()非空值
like Restrictions.like() 字符串模式匹配
and Restrictions.and() 逻辑与
and Restrictions.conjunction() 逻辑与
or Restrictions.or() 逻辑或
or Restrictions.disjunction() 逻辑或
not Restrictions.not() 逻辑非
in(列表) Restrictions.in() 等于列表中的某一个值
not in(列表) Restrictions.not(Restrictions.in()) 不等于列表中任意一个值
between x and y Restrictions.between() 闭区间xy中的任意值
not between x and y Restrictions.not(Restrictions..between()) 小于值X或者大于值y
字符串匹配模式
% 任意长度 长度可以为0
_ 单个任意字符
Hql除了上述还有去重,排序等这些类似sql
Qbc除了上面以外还可以如下操作
Restrictions.like(”name”,“t”,matchmode.start),以t开头语
相应的还有.end 以t结尾 .anywhere 包含t .exact必须为t
Projections.max("salary")最大salary 使用setProjection方法 还有min,avg,sum,count等
排序addorder等
19.Session的一级缓存和二级缓存
缓存可以减少hibernate应用程序访问数据库的频率,默认情况下开启的是一级缓存,第一级别的缓存是 Session 级别的缓存,它是属于事务范围的缓存。这一级别的缓存由 hibernate 管理的,第二级别的缓存是 SessionFactory 级别的缓存,它是属于进程范围的缓存,适合放入二级缓存中的数据:很少被修改,不是很重要的数据, 允许出现偶尔的并发问题,不适合放入二级缓存中的数据:经常被修改,财务数据, 绝对不允许出现并发问题,与其他应用程序共享的数据
20.二级缓存的4 种类型的并发访问策略
非严格读写(Nonstrict-read-write): 不保证缓存与数据库中数据的一致性. 提供 Read Uncommited 事务隔离级别, 对于极少被修改, 而且允许脏读的数据, 可以采用这种策略 读未提交
读写型(Read-write): 提供 Read Commited 数据隔离级别.对于经常读但是很少被修改的数据, 可以采用这种隔离类型, 因为它可以防止脏读 推荐使用 读已提交
事务型(Transactional): 仅在受管理环境下适用. 它提供了 Repeatable Read 事务隔离级别. 对于经常读但是很少被修改的数据, 可以采用这种隔离类型, 因为它可以防止脏读和不可重复读 可重复读
只读型(Read-Only):提供 Serializable 数据隔离级别, 对于从来不会被修改的数据, 可以采用这种访问策略 串行化
21.Hibernate主要三种模式
自动版本化 hibernate能够自动进行乐观并发控制,如果在用户思考的过程中持久化实体发生并发修改,hibernate能够自动检测到
托管对象:采用每次用户请求对应一次session的模式,,前面载入的实例在用户思考的过程中,始终与session脱离,处于托管状态,hibernate运行把托管对象重新关联到session上,并且对修改进行持久化
长生命周期的session:session在数据库提交之后,断开和底层的jdbc连接,当新的客户端请求到来时,有重新连上
22.配置二级缓存
1)配置二级缓存需要的jar包
slf4j-api-xxxjar ,ehcache-core-xxxjar,hibernate-ehcache-xxxFinal.jar
2)cgf.xml添加的配置
<!-- 启用二级缓存 -->
<property name="cache.use_second_level_cache">true</property>
<!-- 配置使用的二级缓存的产品 -->
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCa<!-- 配置启用查询缓存 -->
<property name="cache.use_query_cache">true</property>
<!—对类启用二级缓存 -->
<class-cache usage="read-write" class="entities.Employee"/>
<!—对集合启用二级缓存 假如只写这个那么效率会很低,所以不但要缓存emps集合,还需要缓存里面的元素,这样sql语句才会变少,否则更多-->
<collection-cache usage="read-write" collection="entities.Department.emps"/>
相应的也可以在hbm.xml文件中配置
<!—对类启用二级缓存 -->
<cache usage="read-write"/>
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<!—对集合启用二级缓存 假如只写这个那么效率会很低,所以不但要缓存emps集合,还需要缓存里面的元素,这样sql语句才会变少,否则更多-->
<set name="emps" table="dd" inverse="true" lazy="true">
<cache usage="read-only"/>
3)Cache.xml配置
<diskStore path="d:\\tempDirectory"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
<cache name="entities.Employee"
maxElementsInMemory="1"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
说明
Maxelementsinmemory :设置缓存中最多可放多少个对象
Eternal:设置缓存是否永久有效
Timetoldleseconds:设置缓存的对象多少秒没有被使用就会被清理
Timetoliveseconds:设置缓存的对象在过期之前可以缓存多少秒
Diskpersistent:设置缓存是否持久化到硬盘中
diskStore:写入磁盘路径
overflowToDisk:内存溢出时写入磁盘
备注:hql是不走二级缓存的,但是可以查询二级缓存,但是需要相应的设置
23时间戳缓存
时间戳缓存区域存放了对于查询结果相关的表进行插入, 更新或删除操作的时间戳. Hibernate 通过时间戳缓存区域来判断被缓存的查询结果是否过期, 其运行过程如下:
T1 时刻执行查询操作, 把查询结果存放在 QueryCache 区域, 记录该区域的时间戳为 T1
T2 时刻对查询结果相关的表进行更新操作, Hibernate 把 T2 时刻存放在 UpdateTimestampCache 区域.
T3 时刻执行查询结果前, 先比较 QueryCache 区域的时间戳和 UpdateTimestampCache 区域的时间戳, 若 T2 >T1, 那么就丢弃原先存放在 QueryCache 区域的查询结果, 重新到数据库中查询数据, 再把结果存放到 QueryCache 区域; 若 T2 < T1, 直接从 QueryCache 中获得查询结果
24session本地线程绑定
Cfg.xml文件中的配置
<!-- 配置管理 Session 的方式,绑定当前线程这样去管理session -->
<property name="current_session_context_class">thread</property>
说明:当一个线程(threadA)第一次调用 SessionFactory 对象的 getCurrentSession() 方法时, 该方法会创建一个新的 Session(sessionA) 对象, 把该对象与 threadA 绑定, 并将 sessionA 返回 ,当 threadA 再次调用 SessionFactory 对象的 getCurrentSession() 方法时, 该方法将返回 sessionA 对象,当 threadA 提交 sessionA 对象关联的事务时, Hibernate 会自动flush sessionA 对象的缓存, 然后提交事务, 关闭 sessionA 对象. 当 threadA 撤销 sessionA 对象关联的事务时, 也会自动关闭 sessionA 对象,若 threadA 再次调用 SessionFactory 对象的 getCurrentSession() 方法时, 该方法会又创建一个新的 Session(sessionB) 对象, 把该对象与 threadA 绑定, 并将 sessionB 返回
26 批量操作
利用可回滚的较为合适
但是推荐使用jdbcapi的方式
27 悲观锁和乐观锁
悲观锁:悲观锁假定访问当前事务操作的数据资源是,肯定还有有其他事务同时访问该数据资源,为了避免当前事务的操作受到干扰,先锁定资源,虽然悲观锁可以防止丢失更新和不可重复读的并发问题,但是影响并发性能
乐观锁:刚好与上相反,但是不同的是,乐观锁完全有数据库的隔离级别来控制工作
注解
略
28.Spring对hibernate的整合
Spring是以ioc为核心对action和管理业务逻辑层
配置applicationContext.xml文件
<context:property-placeholder location="classpath:db.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property> <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
</bean>
<!-- 配置 Hibernate: 通过 Spring 提供的 LocalSessionFactoryBean 进行配置 --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <!—platformtransactionmananger接口,针对采用数据库连接的特定实现 --> <property name="dataSource" ref="dataSource"></property> <!-- 配置 hibernate 配置文件的位置及名称 --> <!-- <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> --> <!-- 使用 hibernateProperties 属性来配置 Hibernate 原生属性 --> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> <!—用mappinglocation属性来配置 hibernate 映射文件的位置及名称, 可以使用通配符 <property name="mappingLocations" value="*.hbm.xml"></property>--> <!—用 mappingresources属性来来配置 hibernate 映射文件的位置及名称--> <property name=” mappingResources”> <list> <!—以下用来列出所有的po映射文件 --> <value>Person.hbm.xml</value> </list> </property> </bean>
Hibernatetemplate对象
常用构造方法
Hibernatetemplate()需要用setsessionfactory方法指定sessionfactory或者Hibernatetemplate(sessionfactory)
方法:delete 删除持久化对象
Deleteall(collection)删除集合内的所以持久化对象
Find(string )根据hql查询字段返回实例集合的系列操作方法
Findbynamequery(string)根据命名查询返回实例集合的系列重载方法
Save 保存
Saveorupdate 保存或者更新
Update 更新
Setmaxresult 设置分页的大小,不能担任分页查询
备注:Hibernatetemplate对数据库的访问较为方便,但是灵活性不够,所以添加了hibernatecallback接口,实现该方法必须重写Doinhibernate方法,在方法体类可以获得hibernate session的引用,获得hibernate的引用后可以对数据库进行相应的操作
Hibernatetemplate 灵活的访问方法,但是都需要实现Hibernatecallback的实例
Execute(hibernatecallback action)
Executefind(hibernatecallback action)
Spring为hibernate的dao提供的工具类,hibernatedaosupport,该工具类主要提供了两个简化dao的实现
Public final hibernatetemplate gethibernatetemplate()获取hibernatetemplate对象
Public final void setsessionfactory(sessionfactory)用于接受spring的依赖注入
所以继承hibernatedaosupport后可以利用上述的工具方法,是的代码相对更为简洁
说明:setSessionFactory() 和 setHibernateTemplate() 方法,如果在配置文件中注入了相应属性名与hibernatedaosupport相同的属性名那么不需要设置setSessionFactory() 或者 setHibernateTemplate() ,这两个方法是放置不能自动依赖注入hibernatedaosupport的手动输入方法
说明:不推荐使用hibernatedaosupport和hibernatetemplate,因为这样会使dao和spring的api耦合,可移植性变差,一般如下使用
@autowired
Private SessionFactory sessionFactory;
Private Session getSession(){
return sessionFactory.getCurrentSession;
}
说明:Hibernate 通过 CurrentSessionContext 接口的实现类和 配置参数hibernate.current_session_context_class定义 “上下文”,也就是设置为thread,绑定本地线程,详情见hibernate总结的绑定本地线程