1:Hibernate是什么?
Hibernate是对JDBC进行轻量级封装的ORM框架,在项目中处于持久层的位置;即便不是在web项目中,java application程序也可以使用;也可以使用Hibernate,它跟Struts2不一样,Struts2是基于MVC的web层框架,必须用在web项目中
ORM解释:object relationship mapping(java程序中都是对象,数据库是关系类型的,两者类型不匹配,要想把对象存进关系数据库,就得出现一种新技术,把对象转换为关系型数据,存进数据库,Hibernate只是ORM技术的一种,还有iBatis)
PO类:persistent Objects 持久化类
持久化:就是将数据保存起来,要么存在文件中,要么存在数据库中;Hibernate的持久化就是将java对象保存到数据库中;
2:为什么要使用Hibernate?
如果现在没有Hibernate,那么之前的项目要更换数据库(从mysql-àOracle),那么业务逻辑层的代码就得重新编写SQL语句,另外JDBC使用PreparedStatement来预编译SQL语句,其中参数用?表示,然后set,这样字段多的话,比较麻烦;第三点:一般的程序员希望只关心业务逻辑层,而不去关心你的项目中到底使用了什么类型的数据库
现在出现了Hibernate的好处:
(1) 使用HQL语句代替SQL语句
(2) 使用QueryByParams()方法,将要操作的字段存放在String[]params中,操作更方便
(3) 分层更清晰,使得程序的耦合性更低
(4) 使程序员专注于业务流程,不用考虑底层的数据库
(5) 将关系数据库转化为java对象,让程序员将更多的经历放在面向对象的研究上
(6) 使用Hibernate之后,即便项目想更换数据库,那么只需要修改配置文件中5个属性,DriverClassName,Url,user,password,Dialect即可
**hibernate虽然与数据库无关,但是切换数据库的时候,DriverClassName url user password ,Dialect都得在配置文件中进行修改
3:如何使用(学习)Hibernate?
(1) Hibernate的两个配置文件
hibernate.cfg.xml---àhibernate连接数据库的配置文件
user.xml -àhibernate的对象关系映射文件
hibernate.properties也是一种配置文件,但是现在常用的还是xml文件
(2) PO类的编写
(3) 处于DAO层的6个接口与类
Configuration---Class
sessionRegistry ----Interface
sessionFactory ---Interface
session --Interface
Transaction ---interface
Query --Interface
4:Hibernate的创建步骤
(1) 先写PO类(domain类,javabean,POJO)-àmapping文件-àDB
(2) 先创建DB,用工具生成PO类跟Mapping文件;
小项目使用第一种就可以,大项目,一定得先设计好数据库
5:使用Hibernate的9个核心组件
(1) 导入Hibernate的jar包
(2) 编写PO类
(3) 写Hibernate.cfg.xml文件跟use.xml文件
(4) 编写测试类,使用6个接口跟类
(1) 先写PO类,User.java
Po类的条件
(a) 要 implements Serializable接口
(b) 类名跟表名一致,首字母大写
(c) 表中的字段要跟类中的成员属性想对应,且都是private的,id要用Integer类型,或者Long类型(大项目中的,小项目就有Integer就行)
(d) 无参的构造函数
(e) Setter getter 方法
import java.io.Serializable;
@SuppressWarnings("serial")
publicclass User implements Serializable {
private Integer id;
private String username;
private String password;
public User() {
}
public User(Integer id, String username, String password) {
super();
this.id = id;
this.username = username;
this.password = password;
}
Setter,getter略。。。。。。。
}
hibernate.cfg.xml文件【用来配置连接数据库的参数,以及配置映射文件】(Hibernate默认的文件名,也可以另取它名,单是在Configurationconfiguration = new Configuration().configure();)中的configure()必须指定
<?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>
<!--连接mysql的配置文件,这里使用mysql-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///hibernate_test</property>
<property name="hibernate.connection.user">root</property>
<property name="hibernate.connection.password">system</property>
<!-- 连接Oracle的配置属性-->
<!--
<propertyname="dialect">org.hibernate.dialect.OracleDialect</property>
<propertyname="connection.url">jdbc:oracle:thin:@127.0.0.1:1521:orclhsp</property>
<propertyname="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.username">scott</property>
<propertyname="connection.password">tiger</property>
-->
<!-- 在控制台输出sql语句-->
<property name="hibernate.show_sql">true</property>
<!--格式化sql语句-->
<property name="hibernate.format_sql">true</property>
<!--自动建表,有uapdate,create等属性,
Create:创建并替换原有的表
Update:不替换原有的表,只更新数据
Crate-drop:在sessionFactory关闭的时候,表也会被删除
Validate:验证数据库表结构跟mapping文件的结构是否一致
-->
<property name="hibernate.hbm2ddl.auto">update</property>
<mapping resource="user.xml"/>
</session-factory>
</hibernate-configuration>
User.xml文件【对象关系的映射文件,指定java对象跟数据库的映射关系】
<?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">
<hibernate-mapping package="test.entity">
<class name="User" ><!--类名与表名一致,不需要再指定table属性-->
<id name="id">
<!--主键生成策略,此处使用native通用的,还有序列、自增等其他类型 -->
<generator class="native"/>
</id>
<property name="username"/>
<property name="password"length="32" />
</class>
</hibernate-mapping>
Test.java文件
publicclass Test {
publicstaticvoid main(String[] args) {
//读取hibernate.cfg.xml的配置文件,并完成初始化
Configuration configuration = new Configuration().configure();
//创建serviceRegistry对象
ServiceRegistry serviceRegistry = newServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
//创建sessionFactory,比较耗费资源,应该设计成单例模式
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
//创建session,用来操作save,update,delete
//在后面的SSH框架整合中,session的创建使用sessionFactory.getCurrentSession()来创建,原因:
//openSession()打开session,需要手动关闭,getCurrentSession()自动关闭,不需要手动关闭
Session session = sessionFactory.openSession();
//开始一个事务,Hibernate的事务是需要自动commit的,一执行beginTransaction(),就会调用JDBC的setAutoCommit(false)方法 Transaction transaction =session.beginTransaction();
User user = new User(null, "张三", "123");
session.save(user);
transaction.commit();
session.close();
//sessionFactory.close();不应该关闭,每次打开耗费资源
}
}
*******在最后的项目中,使用的是Annotation的方式来替代Hibernate的两个配置文件
Hibernate.cfg.xml文件内容放在application.xml文件中,对象映射文件User.xml则删除,在
PO类中用Annotation代替;
(1) hibernate.cfg.xml:配置与数据库的连接参数跟对象关系文件的映射
(2) user.xml指定PO类与表的对应关系
(3) PO类含义,上面提到过
(4) Configurationà读取hibernate.cfg.xml的配置信息,以及加载驱动,连接数据库
(5) ServiceRegistry()与configuration一起创建sessionFactory
(6) sessionFactory
(用来生产session,一个session实例代表与数据库的一次操作;一个DB对应一个sessionFactory,要设计成单例)
(a) sessionFactory可以缓存SQL语句
(b) sessionFactory获取session的两种方式,openSession(),getCurrentSession();两者区别
1. 通过 getCurrentSession() 获取的session在事务提交后,会自动关闭,通过openSession()获取的session则必须手动关闭,session.commit();
2. openSession() 是获取一个新的session;getCurrentSession () 获取的是在同一个线程中的session,这样可以利于事务控制;SSH整合中的声明式事务处理,用到的就是getCurrentSession();
3. 对数据库进行查询,openSession()方式不需要提交事务,getCurrentSession()进行数据查询必须得开启事务,而且需要transaction.commit();
4. Session是线程不同步的(不安全),因此要保证在同一线程中使用,就需要用getCurrentSessiong()。
(7) Session接口
(a) Session一个实例代表与数据库的一次操作(crud)
(b) Session是持久化管理器,与持久化操作相关的接口(save,delete,update,get/load);
*******session.get()跟session.load()的区别
(1) 如果查询不到数据,get 会返回 null,但是不会报错, load 如果查询不到数据,则报错ObjectNotFoundException;
(2) 使用load()去查询数据,先去一级缓存跟二级缓存中去查询,如果查询到了,则返回一个实体类的代理对象;如果在缓存中没有查询到数据,而用户也不去使用数据,那么load()方法也不会去DB中查询,就这样等着,如果用户要使用这个对象了,load()方法再去DB查询(这时会发select语句),并将数据保存在缓存中;这样的方式称之为lazy懒加载的方式;
****(取消lazy的方式,在配置文件中将值设为false)*********
(3) 使用get()方式去查询,也是先到一级缓存,二级缓存中查询,如果有,就返回实体对象;没有的话,就立即向DB发送select语句去查询,查询到了,返回给用户,没有查询到,则返回NULL;
(8) Transaction-à接口【悲观锁,乐观锁(见后面)】
Hibernate的事务是由session跟Transaction接口完成的
Transactiontx=session.beginTransaction();
Tx.commit();
Tx.rollback();
更改上面的Test.java文件
public classTest {
@SuppressWarnings("unchecked")
publicstatic void main(String[] args) {
//读取hibernate.cfg.xml的配置文件,并完成初始化
Configurationconfiguration = new Configuration().configure();
//创建serviceRegistry对象
ServiceRegistryserviceRegistry = newServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
SessionFactorysessionFactory=null;
Sessionsession=null;
Transactiontransaction=null;
try{
sessionFactory=configuration.buildSessionFactory(serviceRegistry);
session=sessionFactory.getCurrentSession();
transaction=session.beginTransaction();
//增强for循环取
Query query=session.createQuery("from User");
List<User>list=query.list();
for(Useruser:list){
System.out.println(user.getUsername());
}
//迭代器取
Iterator<User>iterator=query.iterate();
while(iterator.hasNext()){
Useruser=iterator.next();
System.out.println(user.getUsername());
}
transaction.commit();
}catch (Exception e) {
e.printStackTrace();
if(transaction!=null) {
transaction.rollback();
}
thrownew RuntimeException(e.getMessage());
}finally{
if(session!=null && session.isOpen()) {
session.close();
}
}
}
}
(9) Query --à接口【HQL语句-à实体对象关联映射(见后面)】
Queryquery=session.createQuery(“HQL语句”);
query.list();query.iterator()区别
session,get(),session.load()的区别;后者根据id进行查询
HQL语句
(A) 查询某个实体对象的全部属性HQL--àfrom User
//增强for循环方式
List<User> list=session.createQuery("from User").list();
for (User user : list) {
System.out.println(user.getUsername());
}
//Iterator方式
Iterator<User>iterator=session.createQuery("fromUser").iterate();
while(iterator.hasNext()){
User user=iterator.next();
System.out.println(user.getUsername());
}
(B) 查询某个实体对象的部分属性【投影查询】
HQL-àselect username,useraddr from User;
//下面的代码会报错,username,userid,不能转换成一个User对象
List<User> list2=session.createQuery("select username,userid from User").list();
for (User user:list) {
System.out.println(user.getUsername());
}
//下面的可以
List list3=session.createQuery("select username,useraddr from User").list();
for (int i = 0; i <list3.size(); i++) {
//将数据封装成对象数组,此时的list3中存放的都是object[]对象。
Object[] objs=(Object[]) list3.get(i);
System.out.println(objs[0].toString());
}
//要将数据进行二次封装才可以使用,没有第一种方式好;尤其在实体对象关联查询的时候,应当将全部属性都查出;
对象的三种状态
主要的依据是:
1. 看该对象是否处于session的管理之下
2, 看在数据库中有没有对应的记录
瞬时态: 没有session管理,同时数据库没有对应记录
持久态: 有session管理,同时在数据库中有记录
脱管态/游离态: 没有session管理,但是在数据库中有记录.
实体对象
单向一对多
class Channel{
@onetomany
@joincolumn(name=”channel_id”)
private set<Article> articles=new HashSet<Aritcle>();
}
单向多对一【韩顺平第一讲,多对一没有set<>集合,get()方法取出的只是多端中的普通成员属性值,而关联的另一个实体对象的值是无法取到的,若想取到关联的那一端,必须关闭lazy】
class Aricle{
private Channel channel;
}
双向一对多【韩顺平第二讲,两个实体类都关联了对应的entity,只要根据id就可以查询出全部】
Class Channel{
Privateset<Article> articles=new HashSet<Article>();
}
class Article{
privateChannel channel;
privateAdmin admin;
}
基于主键的一对一关系
基于外键的一对一关系(外键要unique)
多对多关系【分解成一对多跟多对一的关系】
级联操作
(10)缓存与优化
1:懒加载:---àload()方法中,单向多对一中:代理机制
2:缓存:一级缓存(session),二级缓存(sessionFactory),查询缓存
缓存的好处:减少对数据库的访问???
一级缓存(session缓存)
get()获取数据的时候,先到一级缓存中查找,如果有的话,就返回给客户,没有的话,就会发出select语句,去查询DB,并将结果放进一级缓存中;
save()方法为什么还有缓存呢?因为以后的程序可能会用带save()保存的对象,所以为了方便就放进一级缓存中,然后transaction.commit();事务提交的时候,才会把数据添加到数据库中
----à什么样的操作会向一级缓存放入数据?
Save(),upadate(),saveorUpdate(),list(),iterator(),get(),load()
----à什么样的操作会向一级缓存取出数据?get load list uniqueResult
get load会从数据库中取出数据
list(),uniqueResult()不会从缓存中取出数据
证明方法:show_sql,查看select语句
Session缓存的生命周期
getCurrentSession()或者openSession()开始,到session.close()结束
session缓存的清除
session.evict(对象名)
session.clear();
二级缓存(sessionFactory)
为什么要有二级缓存?
一级缓存的内存有限以及生命周期短,使用二级缓存来拟补这个问题
二级缓存生命周期长,可以存在于内存中,也可以存在于硬盘上
二级缓存的配置(hibernate.cfg.xml文件中配置)
<!-- 启动二级缓存 -->
<propertyname="cache.use_second_level_cache">true</property>
<!-- 指定使用哪种二级缓存 -->
<propertyname="cache.provider_class">org.hibernate.cache.OSCacheProvider</property>
特别说明二级缓存策略:
1. read-only
2. read-write
3. nonstrict-read-write
4. transcational
-->
<class-cacheclass="com.hsp.domain.Student" usage="read-write"/>
主键增长策略
increment 自增
identity 自增
sequence (Oracle)
native 通用的
foreign 外键
uuid(主键只能是String类型)
assigned
什么情况下不适合使用Hibernate??
在大型项目中,数据访问量大的情况下不适用
对Hibernate不熟悉的情况下不适用