jdbc:面向sql语句
hibernate:面向对象
jdbc缺点:
1、写sql语句比较繁琐
2、把sql语句的操作封装成面向对象的操作比较麻烦
3、关闭资源的操作特别繁琐
4、查询的操作代码太多(冗余太多)
5、没有做到数据的缓存(向数据库频繁的要数据,性能很差)
6、sql语句的移植性太差(存储过程、触发器)
jdbc优点:
1、有连接池,所以获取connection的速度更快了
2、最低层的数据库的操作
hibernate:
优点:
1、面向对象的操作
2、关闭资源操作很简单
3、查询变得很简单了
4、一级缓存、二级缓存、查询缓存
5、移植性很好
缺点:
1、sql语句是由hibernate内部生成的,如果要对sql语句做优化,几乎不能
2、如果一张表中的数据集达到千万级别,hibernate不适合
总结:hibernate适合一些中小型的项目开发
hibernate学习
1、完成crud
2、一对多、多对多的关系怎么样转变成hibernate的面向对象的操作
3、hibernate的hql语句:类似于sql语句
4、性能的问题
1、延迟加载
2、数据缓存
3、抓取策略
5、二级缓存(不是重点,面试重点)
hibernate的组成部分:
1、持久化类
2、映射文件:描述了表到类的映射关系
3、hibernate的配置文件:用于产生链接的
4、客户端
*.hbm.xml(映射文件)
建立表到类的映射
1、表明和类名映射
2、表里字段的名称和类中属性的名称映射
3、表里的字段类型和类中的属性类型映射
1、导包
2、写bean
3、导入配置文件 bean名.hbm.xml(详细看源文件)
<hibernate-mapping>
<!--
class是用来描述一个持久化类
name属性就是累的全名
table表明如果不写,默认就是类名
catalog数据库的名称 一般不写
-->
<class name="" table="" catalog="">
<!--
id代表标示符属性
name为属性呢的名称 提示来自于属性
column表中的列名 默认就是属性的名称
length指的是数据中的类型长度,如果不写,默认最大长度
<id name="pid" column="pid" length="5" [type=""]>
<!--
generator 主键的产生机制
-->
<generator class="increment"></generator>
</id>
<property name="pname" length="20" [type=""]></property>
<property name="sex" length="5" [type=""]></property>
</class>
4、导入hibernate.cfg.xml并配置
5、类实现
1、加载解析配置文件
默认方法:Configuration configuration = new Configuration();
configuration.configure()-->configure("/hibernate.cfg.xml");-->配置文件放在src下
按名称:Configuration configuration = new Configuration();
configuration.configure("")-->configure(String path);
2、连接数据库,动态生成表
configuration.buildSessionFactory
SessionFactory:
1、一个SessionFactory只能连接一个数据库
2、配置文件的信息、映射文件的信息会存到sessionFactory中
3、sessionFactory是一个重量级类
4、sessionFactory是一个单例类
5、sessionFactory是线程安全的
3、sessionFactory.optionSession 打开session
4、session.beginTransaction
1、打开事务
2、完成cud的操作
3、commit()
5、关闭session.close();数据库链路断开
1、添加数据save
Person person = new Person();
person.setPname("老方");
person.setPsex("不详");
session.save(person);
2、更新数据update
*先把数据提取出来
*修改
*更新update
session.get(要取的表,主键的属性值)
Person person = (Person)session.get(Person.class, 1L);
person.setPsex("纯爷们");
session.update(person);
3、删除数据delete
//Person person = (Person)session.get(Person.class, 1L);//Person person = new Person();
//person.setPid(1L);
session.delete(person);
4、查询createQuery
List<Person> personList = session.createQuery("from Person").list();
for(Person person:personList){
}
字段类型的映射
hibernate java类型 sql类型
integer java.lang.Integerint/number
long java.lang.Longlong/number
配置的时候写java类型,映射快
详细java类型和hibernate类型对比参照:http://blog.csdn.net/kuyuyingzi/article/details/10012367
id生成器
1、increment,主键类型是数字,不自动增加,由hibernate(先获取max,再加1)自动增加
注意:increment时,不能手动设置id,否则会报错
2、identity,主键类型是数字,由sql自动增加,因此效率比increment高
用increment优点,删除后,id没有断片
3、uuid,企业里面一般用这个
*主键类型是字符串
*由hibernate生成
4、assigned手动指定主键值
持久化状态
1、Person person = new Person();
2、person.setPname("laofng");
3、perso.setPsex("sdf");
4、session.save(person);
5、transaction.commit();
6、session.close();
1,2,3person对象时临时状态的对象
4、person由临时状态转化成持久化状态
5、person也是持久化状态,数据库中相对应的值了
6、session关闭,hibernate与数据库通信断开,变成脱管状态
总结:持久化状态:与数据库有关联和交互,脱管状态,有过交互但停了
session.save(person),如果是新对象则执行insert,如果是更新操作则执行update
session.evict(person)将对象从session中移除,变成脱管状态
session.update(person)变成持久化状态,如果同一个对象进行多个更新,则只更新最近的一个
session.clear()把session中的所有对象全部清空了
注意:
session.save,update,get方法都可以把一个对象变成持久化状态,并都能产生一个副本
如果没有上述方法,session.commit的时候,hibernate会比较session中的副本中的值,不一样才与数据库发生相应交互
hibernate的多表:
一对多:
<set name="students" cascade="save-update" inverse="false">
<key>
<!-- 外键-->
<column name="cid"></column>
</key>
<!-- -->
<one-to-many class="..student"/>
</set>
另一方是多对一,应配置<!-- 引入外键 -->
<many-to-one name="classes" class="..." column="cid" cascade="save-update"></many-to-one>
类配置文件:
<!--
cascade(默认为none)级联:在保存或者更新一个对象的时候保存或者更新级联对象
即session.save(classs)时级联保存set<student>
inverse(默认为false):表示对关系的不维护
true 不维护关系
false 维护关系
default false
表示class是否维护class与student的关系
-->
<set name="students" cascade="save-update" inverse="false">
<key>
<!-- 用来描述class和student的关系-->
<column name="cid"></column>
</key>
<!-- -->
<one-to-many class="..student"/>
</set>
总结:
1、一对多的单项关联,维护关系指的就是发出update语句
2、维护关系的操作,如果update cid=null肯定就是解除关系的操作
如果update cid=4就是建立关系或者更改关系
3、在维护关系的代码中,是通过classes维护关系的,所以应该看classes.hbm.xml文件
4、级联指的是对象和对象之间的操作与关系无关,inverse指的是对象和关系之间的操作与对象无关
多对多:
多对多谁维护关系,效率都一样,都要对第三种表进行操作
多对多维护关系体现在:
1、建立关系指的是在第三张表中插入一行数据
2、解除关系指的是在第三张表中删除一行数据
3、更新数据
执行update语句
先删除,后增加
<set name="students" table="student_course" cascade="save-update">
<key>
<column name="cid"></column>
</key>
<many-to-many class="" column="sid">
</set>
总结:一对多和多对多
1、两边都是可以连通的
2、inverse和cascade
inverse指的是对象与关系的操作(对象与外键的操作)
cascade指的是对象与对象之间的操作(与外键没有关系)
一班默认情况下:
一对多
多的一方维护效率比较高
维护关系指的是发出update语句
update set 关系=null 解除关系
update set 关系=值 建立关系
多对多
哪一方维护都行
维护关系
建立关系:在关系表中插入一行数据 insert
解除关系:在关系表中删除数据 delete
更新关系:在关系表中先删除后增加
3、在客户端session.save/update操作谁,看谁的映射文件
4、在写客户端代码的时候,一定要考虑这几个方面的因素
1、当session执行save/update方法的时候,是否发生对象的关联
2、当session执行save/update方法的时候,是否维护关系
3、发生级联的时候,什么情况下对于级联对象执行save/update操作
4、客户端代码中持久化对象的转换状态:临时状态-持久化
hibernate性能的提高 :延迟加载、抓取策略、缓存、内连接-外连接
延迟加载:关键词load
懒加载lazy:要配合session.load方法使用
true(默认)
alse
extra;用count/sum/avg等函数计算数值的时候,这个时候并不需要查
1、概念:该加载时加载
2、类型
类的延迟加载
类的基本属性
1、classes = session.load("");
当classes.getCname()、setCname()的时候才发出sql语句
2、<class lazy="false">,取消session.load()的延迟加载
经典错误:
classes = session.load("");
session.close();
classes.getCname();发生no session错误
集合的延迟加载
在关联对象时集合的情况
一对多 多对多
1、students = classes.getStudents();并没有发出sql语句
for(Student student:students){}才发出sql语句
2、<set lazy="false"> 取消集合的延迟加载,获取的时候就发出sql语句
单端关联的延迟加载
通过多的一方加载一的一方的策略
<many-to-one>中lazy的值有
false
proxy 延迟加载
no-proxy
通过多的一方加载一的一方,就一条数据,保持默认值就可以了,不会特别影响效率
抓取策略:关键词createQuery
根据一个对象提取关联对象的策略
抓取策略的情况:
集合的提取
<set fetch="">
fetch
"join"外连接 left outer join
"subselect"子查询:查询效率最高 select+“batch-size"==subselect
后面可跟:session.createQuery("from classes where cid in(1,2)");
"select"默认的
batch-size: 一次性查询出集合的数量
根据多的 一段加载一的一端
说明:
1、如果把雪球分析翻译成sql语句,sql语句存在子查询,这个时候,首选子查询
2、如果没有子查询,但是想把数据一次性的提取出来,这个时候选择”join"
3、如果没有子查询,但是不想把数据一次性提取出来,这个时候选择默认“select”
抓取策略和懒加载结合:结合面试重点内容
lazy fetchsql
true/extra join 采用左外连接,lazy不起作用
false join采用左外连接
true/extra select 先加载一的一段,再加载多的一端,多的一端在迭代时候加载
false select在session.get方法时,把集合加载出来了,但是发出两条sql
false select在session.createQuery时,就已经发出sql语句
session:
1、session的产生方式
2、session的缓存
3、session与实务的关系
openSession:每次执行该方法都会新建一个session;
getCurrentSession的执行过程
1、先从当前线程中查找session
2、如果找到,则直接使用,如果偶找不到执行第三步
3、当事务提交transaction.commit,getCurrestSession中的session会自动关闭
4、需要配置<property name="current_session_context_class">thread</property>(可以通过界面配置)
5、所有的操作必须在事务环境下进行
优点:安全性、自动关闭session
session缓存:一级缓存 私有缓存
1、缓存的生命周期
生命周期开始:当session打开的时候
生命周期结束:session.close(),session为当前线程,则程序执行完以后session自动关闭,缓存结束
2、缓存中数据的安全性
如果是当前线程打开session,session本身在threadLocal中,所以session中的数据时安全的(其他线程访问不到)
3、数据怎么样放入到一级缓存中的
当执行session.get、load、save、update方法时,把对象放入到了一级缓存中
4、怎么样从一级缓存中取出数据
session.get、load取数据
5、怎么样把缓存中的数据同步到数据库中
transaction.commit其实默认执行session.flush
session.flush该语句包含作用:
1、save/update数据
2、有关系,更新关系
3、有级联,更新级联操作
一般配合session.clear()使用
6、怎么样把数据库中的数据同步到缓存中
session.refresh()只能把一个数据同步到缓存中
7、怎么样把一个对象从缓存中移除
session.evict(Object obj)
8、怎么样清空缓存中所有的数据
session.clear();
二级缓存
1、缓存的生命周期
sessionFactory级别:当hibernate容器启动的时候,二级缓存就有了
2、缓存中数据的安全性
*sessionFactory线程安全的
3、二级缓存存在的意义
*提高查询速度
*如果数据更新特别频繁,不适合做二级缓存
*一些重要的数据不能放到二级缓存中
*存放到二级缓存中的数据是不经常改变的,所以hibernate3以上的版本相对于一级缓存,有一些区别:
二级缓存并不建议清除(把一个对象清除获取把所有的对象清除)
*二级缓存分为类的二级缓存和集合的二级缓存,用法一样
4、hibernate本身没有二级缓存,是借助第三方框架来实现的(面试亮点)
5、二级缓存默认是关闭的
6、二级缓存配置
二级缓存的提供商
<property name="cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
开启二级缓存
<property name="cache.use_second_level_cache">true</property>
类级别的二级缓存:usage读取策略
1、在hibernate.cfg.xml<class-cache usage="read-write" class="..'/> 我用着不行哪
2、一般在相应对的映射文件中配置<class name="..">
<cache usage="read-only"/>
不配置就不会放到二级缓存
7、如果利用二级缓存
session.get、session.load方法:把数据拿出来,存到一级缓存和二级缓存
8、当二级缓存中的数据量比较大事,hibernate提供了一种机制,可以在内存缓存一部分数据,另一部分存在磁盘上
ehcache.xml
将缓存放到磁盘的配置文件,必须放在src目录下
需要修改的项目
<diskStore path=""/>
<defaultCache
maxElementsInMemory="12"
maxElementsOnDisk="10000000"
查询缓存:
*查询缓存是建立在二级缓存基础之上的
*查询缓存默认也不是开启的
*打开查询缓存
<property name="cache.use_query_cache">true</property>
*在程序中的用法
Query query= session.createQuery("from Classes")
query.setCacheable(true);//开启查询缓存
query.list();//把数据写入到查询缓存中
query = session.createQuery("from Classes")
query.setCacheable(true);//开启查询缓存
query.list();//从查询缓存中取数据
二级缓存:针对ID的查询缓存
查询缓存:针对数据的查询
session.createQuery("from Classes").list()
session.createQuery().list()与session.createQuery().iterator()方法的区别(面试点)
list:利用查询缓存,不利用一级缓存和二级缓存
iterator:n+1查询
利用一级缓存、二级缓存,不利用查询缓存
hql语句(重点):得到合适的结果集,单表查询尽量用select,多表用fetch
*语法规则:
from持久化类 where 属性
sql语句中的函数count,avg,sum等在hal语句中都适合
order by,group by,having,子查询,in等在hql语句中都适合
投影查询:只查询某些属性
*单表的查询
*select X X from 表,取出来的元素是Object
*如果要求select查出来的是对象元素:select new com.....(类的路径)Classes(X,X) from 表
该中查询方法,要求bean中要有有参构造函数,而且查询的参数个数与构造函数个数一致
*from 表取出来的元素是对象
总结:
1、如果页面上的字段和数据库中的字段的个数差不多,直接”from 持久化类“
2、如果页面上的字段和数据库中的字段相差很多,则利用到构造函数的查询
3、如果是第一种情况,查询出来的Object[]中的顺序和select后的顺序保持一致
迫切内连接:fetct规范取出去的数据
from Student s inner join fetch s.classes c
from Classes c inner join fetch c.students s
from Classes c left outer join fetch c.students s
外连接:可以在条件不成立的时候,仍然可以包含某些不成立的记录
左外连接: 在条件不成立的时候,等号左边的表仍然被包含 left outer join
右外连接: 在条件不成立的时候,等号右边的表仍然被包含 right outer join
总结:无论是内连接还是左外连接,如果查询出来的结构和持久化的结构保持一致,要在join后面加fetch
需求:查询表一的一部分和表二的一部分
需要建一个bean来接收
select new com.....(类的路径)Classes(c.X,s.X) from Classes c inner join c.students s
*关联表的查询
一对多
多对多
一对多和多对多的结合(难点)
关键:找核心表(能同时联系其他的表),表的设计要用星型设计
最终的目的:好取数据
from Student s inner join fetch s.classes inner join fetch s.course
问题:因为内连接用的是笛卡尔积,有重复数据
解决:利用hashset不重复元素原理
Set<Studnet>students = new HashSet<Student>(StudentList);
studentList = new ArrayList<Student>(Students);
附录:
小细节1:Hibernate 中 HQL问号与冒号的区别:
在网上搜资料,得到的答案是基本相同,既然是基本相同并不是完全相同,所以肯定有区别。
目前发现的区别就是用了 ? 则用变量的序号来赋值,从0 开始。
如果用了:变量名的则用变量名来赋值。