hibernate(一)
hibernate介绍
jdbc缺点
1代码结构防繁琐,面向纯sql语句的编程,对于查询而言只要查询数据库的一张表,不需有如下编码
2有connection缓存,没有数据缓存
3.事务自动开启,有安全性的隐患
jdbc优点
1.是最底层的数据库操作,所以效率比较高
2.sql语句可以自己选择写,采用效率最高的
Hibernate的优点
1是一个ormaping框架,是一个操作数据库的框架,面向对象编程
2.代码编程比较简单
3做到了数据缓存
4用的最多的场合是企业中的中小型项目
缺点
1.改框架程序员是没有办法干预sql语句的生成。如果项目对sql语句的优化要求高,不能用hibernate
2。表关系之间很复杂的情况下,不能hibenate来操作
3.如果一张表超过千万级别 也不适合用hibernate来操作
hibernate的组成
持久化话类
映射文件
数据库有多少张表,至少应该有多少个映射文件
配置文件
完成数据库的链接信息的填写,一般有一个
hibernate入门操作
创建javaproject
准备lib包
创建配置文件
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <!-- 一个session-factory代表一个数据库 --> <session-factory> <!-- 链接数据库的用户名 --> <property name="connection.username">root</property> <!-- 链接数据库的密码 --> <property name="connection.password">root</property> <!-- 链接数据库的驱动 --> <property name="connection.driver_class"> com.mysql.jdbc.Driver </property> <!-- 链接数据库的url --> <property name="connection.url"> jdbc:mysql://localhost:3306/itheima09_hibernate </property> <!-- 方言 告诉hibernate用什么样的数据库 --> <property name="dialect"> org.hibernate.dialect.MySQLDialect </property> <!-- validate 加载hibernate时,验证数据库的结构 update 加载hibernate时,检查数据库,如果表不存在,则创建,如果存在,则更新 create 每次加载hiberante,都会创建表 create-drop 每次加载hiberante,创建,卸载hiberante时,销毁 --> <property name="hbm2ddl.auto">update</property> <!-- 显示sql语句 --> <property name="show_sql">true</property> <!-- 格式化sql语句 --> <property name="format_sql">true</property> <!-- 加载映射文件 --> <mapping resource="com/itheima09/hibernate/domain/Person.hbm.xml" /> </session-factory> </hibernate-configuration>
创建持久化类
package com.heima.domain; public class Person { private Long pid; private String name; private String description; public Person(){ } public Person(String name){ this.name = name; } public Long getPid() { return pid; } public void setPid(Long pid) { this.pid = pid; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
说明:持久化类中必须有一个默认的构造器
创建映射文件
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- 描述一个持久化类 name属性为持久化类的全名 table 该持久化类对应的表名 默认情况下为类名 catalog 为数据库的名称 --> <class name="com.itheima09.hibernate.domain.Person" table="pereson"> <!-- id对应表中的主键 name为持久化类中属性的名称 length 为对应数据库表中相应字段的长度 column 属性的名称对应的表的字段名称 不写则默认和属性的名称一致 --> <id name="pid" length="5" type="java.lang.Long" column="pid"> <!-- 主键的生成器 --> <generator class="increment"></generator> </id> <property name="name" column="name" type="java.lang.String" length="20"> </property> <property name="description" column="description" type="java.lang.String" length="50"> </property> </class> </hibernate-mapping>
创建映射文件
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- 描述一个持久化类 name属性为持久化类的全名 table 该持久化类对应的表名 默认情况下为类名 catalog 为数据库的名称 --> <class name="com.itheima09.hibernate.domain.Person" table="pereson"> <!-- id对应表中的主键 name为持久化类中属性的名称 length 为对应数据库表中相应字段的长度 column 属性的名称对应的表的字段名称 不写则默认和属性的名称一致 --> <id name="pid" length="5" type="java.lang.Long" column="pid"> <!-- 主键的生成器 --> <generator class="increment"></generator> </id> <property name="name" column="name" type="java.lang.String" length="20"> </property> <property name="description" column="description" type="java.lang.String" length="50"> </property> </class> </hibernate-mapping>
完成从类到表中的字段的对应关系
增删改查dao
hibernate流程
说明:
1、 crud操作是由session来完成的
2、 在hibernate中事务不是自动提交的
入门例子过程中的类详细解析
configuration类
利用该类加载hibernate的配置文件
sessionFactory类
1.hibernate配置文件的信息 持久化类的信息 映射文件的信息全在该类中
2sessionFactory对象有且只有一个
3生命周期是整个hibernate实例
4sessionFactory本身就是线程安全的
5二级缓存在sessionFactory中存放
6.sessionFactory和数据库的链接没有直接关系
session类
1.crud操作由session来完成
2.一个session代表数据库的一个链接
内部执行流程
Hibernate反向工程
在help文档中也有说明
先存在数据表,根据表生成持久化类, Dao , session, 映射文件 的过程叫hibernate反向工程
反向完成后生成的文件,personDao自动生成多个数据方法
hibernate中的类型
在hibernate中有两种类型:java type,hibernate type
java类型
Hibernate内部直接提供了java类型到数据库的对照表
说明:用java类型可以直接完成从java类型到数据库类型的映射
hibernate类型
映射类型 java 类型 标准 sql 类型 integer int or Integer INTEGER long long or java.lang.Long BIGINT short short or java.lang.Short SMALLINT float float or java.lang.Float FLOAT double double or java.lang.Double DOUBLE big_decimal java.math.BigDecimal NUMERIC character java.lang.String CHAR(1) string java.lang.String VARCHAR byte byte or java.lang.Byte TINYINT boolean boolean or java.lang.Boolean BIT yes_no boolean or java.lang.Boolean CHAR(1)('Y' or 'N') true_false boolean or java.lang.Boolean CHAR(1)('Y' or 'N') date java.util.Date or java.sql.Date DATE time java.util.Date or java.sql.Time TIME timestamp java.util.Date or java.sql.TimeStamp TIMESTAMP calendar java.util.Calendar TIMESTAMP calendar_date java.util.Calendar DATE binary byte[] VARBINARY( or BLOB) text java.lang.String CLOB serializable java.io.Serializable VARBINARY (or BLOB) clob java.sql.Clob CLOB blob java.sql.Blob BLOB class java.lang.Class VARCHAR locale java.util.Locale VARCHAR timezone java.util.TimeZone VARCHAR currency java.util.Currency VARCHAR
从上面可以看出如果选择hibernate类型,需要查找该hibernate类型对应的java类型,从而再找到数据库类型,所以java类型效率比较高
主键的产生器
increment生成器:查找最大值,在最大值的 基础上加1
效率比较低
assigned 手动赋值
identity 支持主动自动增长
需要数据库表勾选设置 自动增长,否则出现错误
uuid
唯一的字符串
native
Hibernate会根据数据库的不同,选择合适的主键的生成策略
sequence
是oracle内部特有的内容,相当于uuid,所以是字符串类型
对象的状态
案例一
应用场景: 如有一个班的所有学生成绩同步到数据库的表中,如果每一个学生和数据交互一次,这样效率比较低。
临时状态
持久化状态
脱管状态
1、 把16,17,18这三行代码的对象的状态称为临时对象,其特征是该对象与hibernate没有关系。
2、 当执行19行代码的时候,person对象由临时状态转化成持久化状态,这个时候数据库没有对应的数据,但是该对象在hibernate内部
3、 当执行20行代码的时候,事务提交了,该对象还是持久化状态的对象,数据库中有对应的值了
4、 当执行完21行代码的时候,session关闭了,该对象成为脱管状态的对象
案例二
说明:
1、 session.get方法提取出来的是一个持久化状态的对象
2、 当事务提交的时候,hibernate内部自动更新
案例三
建立副本 减少数据库操作
案例四
不更新
案例五
说明:
该例子对象的状态由持久化à脱管à持久化
当事务提交的时候,hibernate内部只管持久化状态的对象,对于临时状态和脱管状态是不管的。
案例六
说明:
利用session.clear方法把hibernate中所有的持久化状态转化为脱管状态。
案例七
总结
临时状态
刚刚创建的对象
持久化状态
经过session.load,session.get,session.save,session.update方法都可以把一个对象变成持久化状态
脱管状态
经过session.clear,session.evict,session.close方法可以把一个对象变成脱管状态
hibernate的一级缓存
数据缓存
把数据存储在一定的数据结构中,这样的做法
Map<String,Object> map结构
String是缓存中数据的唯一标示 (sql语句或者其他)
Object是key所对应的值
对象缓存
1、 对象缓存是一个集合,而不是一个map
2、 每一个对象中都有一个唯一的标示符
3、 在hibernate中,该标示符为主键
缓存研究对象
缓存的生命周期
把一个对象放入到缓存中
把一个对象从缓存中提取出来
把一个对象从缓存中清除
把一些对象从缓存中清除
把缓存中的数据同步到数据库中
把数据库中的数据同步到缓存中
一级缓存
一级缓存是session缓存
当session开启的时候,一级缓存起作用,当session关闭的时候,一级缓存销毁了
Session的缓存存放的是私有数据
把一个对象放到缓存中
get方法
第二次执行时候 从缓存中取出, 控制台没有发出sql语句
说明:
Session.get方法把一个对象放入到了一级缓存中
Statictis方法:统计方法
Save方法
说明:session.save方法把对象放入到一级缓存中了
把一个对象从一级缓存中清除
evict()方法
clear()方法
把数据库的数据同步到缓存中
refresh()方法
把缓存中的数据同步到数据库中
flush()
当执行94行代码的时候,hibernate会检查一级缓存中所有的持久化状态的对象,
如果该持久化状态的对象没有标示符的值,则会发出insert语句,如果该持久化状态的对象有标示符的值,则会对照副本,看是否和副本一致,如果一致,则什么都不做,如果不一致,则发出update语句。
一级缓存总结
一级缓存提供了一个临时存放对象的一个内存结构,当hibernate对对象进行操作的时候,仅仅改变的是对象的属性,改变的是一级缓存中的对象的属性,在session.flush之前的代码可以任意写,因为这个时候,并没有和数据库交互。当执行session.flush的时候,hibernate会检查一级缓存中的对象的情况,发出insert或者update语句
session的产生方式
需求
实现
产生方式
sessionFaction.openSession
sessionFacton.getcurrentsession
原理
1、 从当前线程中(ThreadLocal)中,把session提取出来
2、 如果第一步失败,则调用openSession方法创建一个session
3、 接着第二步,把session放入到threadlocal中
4、 走第一步
好处
不管有几个类完全松耦合,如果这几个类要用到同一个session,在运行的时候,如果这几个类在同一个线程下运行,利用threadlocal就能够保证是同一个session。
步骤
1、 在hibernate.cfg.xml文件中
<property name="hibernate.current_session_context_class">
thread
</property>
2、 在客户端
说明:
1、 crud操作必须在事务的环境下运行
2、 事务提交,session自动关闭。
3、 这种方式强制把事务和session绑定在一起了。
关系操作
一对多的单项
保存学生
保存班级和学生
这种做法不好,因为在客户端需要操作session.save这个代码很多次。
级联操作
保存班级级联保存学生
<!-- cascade 级联 save-update 当保存或者更新classes的时候,级联操作student --> <set name="students" cascade="save-update"> <!-- key代表外键 用来关联classes表和student表,用于在hibernate低层生成sql语句 --> <key> <column name="cid"></column> </key> <!-- 建立类与类之间的关联,用于客户端的编码 --> <one-to-many class="com.heima.domain.Student"/> </set>
@Test public void testsaveclasses_cascade_savestudent(){ Session session = sessionFactory.getCurrentSession(); Transaction t = session.beginTransaction(); Classes c1 = new Classes(); c1.setName("java"); Set<Student> students = new HashSet<Student>(); Student s1 = new Student(); s1.setName("王天才1111"); Student s2 = new Student(); s2.setName("s222222"); students.add(s1); students.add(s2); c1.setStudents(students); session.save(c1); t.commit();
classes中的set<student>在session在保存class的时候,自动保存在student表中,并有外键(映射文件设置one to many的关系)
步骤
1.设置级联关系,映射文件中
2.代码中
说明:
1、 在hibernate中,通过session.save方法保存一个持久化对象这种方式称为显示保存。
2、 在hibernate中,通过级联的方式来操作一个对象,这样的方式称为隐式操作。
3、 对student对象进行了隐式的保存操作,是因为student是一个临时状态的对象,在数据库中没有对应的记录,所以应该对student执行insert语句
更新班级级联保存学生
@Test //更新班级级联更新学生 public void testUpdateClasses_cascade_updateStudent(){ Session session = sessionFactory.getCurrentSession(); Transaction t = session.beginTransaction(); Classes c=session.get(Classes.class, 5L); Set<Student> students=c.getStudents(); for(Student student:students){ student.setDescription("被改变了"); } session.save(c); t.commit(); }
关系操作
让一个新的学生加入到已经存在的班级
说明:
1、 当执行127行代码的时候,发出了加载classes的sql语句
2、 当执行129行代码的时候,发出了加载classes班级中的所有的学生的sql语句
3、 因为在Classes.hbm.xml文件中set元素的cascade属性设置了save-update,所以在更新classes的时候,要检查classes中的students集合,发现集合中多了一个对象,所以要对该对象(student)执行insert语句。
4、 当执行session.flush的时候,会发出维护关系的update语句,因为classes负责维护classes与student之间的关系。
转移班级
说明:
1、 执行141行的时候,发出加载student的sql语句
2、 执行143行的时候,发出加载cid为1的班级的sql语句
3、 执行144行的时候,发出加载cid为2的班级的sql语句
4、 执行146行的时候,发出维护关系的sql语句
执行148行的时候
因为建立关系了,所以更新了外键。
分析:从上面的代码和sql语句可以看出,外键变了两次,但是不需要改变两次
优化后
解除一个学生和一个班级之间的关系
解除该班级和所有的学生之间的关系
说明:当执行classes.setStudents(null)的时候,不需要根据班级加载学生,所以这样做效率比较高
删除班级,解除student关系
删除class,同时设置student中的cid为null
说明
1、 classes负责维护关系
2、 在执行session.flush的时候,在删除班级之前,解除该班级和所有的学生之间的关系
3、 解除关系以后,再删除班级
4、 先查询班级,再查询该班级中的每一个学生,再根据每一个学生的sid删除每一个学生,这样效率比较低
删除班级级联删除学生
两种删除class的区别 cascade ="delete"会级联删除student表, cascade="savd-update"时候,不会删除对应student,只会设置外键为null
Cascade:
Save-update
在session.save/update一个对象的时候,级联操作关联对象,关联对象或者执行save语句或者执行update语句或者什么都不执行
Delete
在session.delete一个对象的时候,级联删除关联对象
All
Save-update和delete的结合
Cascade与inverse的区别
1、 cascade描述的是对象与对象之间的关系
cascade和外键没有关系,在student表中,sid、name、description和cascade有关系,但是cid和cascade没有关系。
2、 inverse描述的是对象与外键之间的关系
inverse只和cid有关系,如果维护,则发出update语句(更新外键的sql语句),如果不维护,则不管。
inverse默认值为false 表示维护关系.
一对多单项总结:
1、 只能通过classes操作student
2、 只要classes维护关系,就会发出维护关系的update语句,所以让classes维护关系效率比较低
3、 让student维护关系效率比较高
4、 Session.flush的时候,hibernate内部做的事情
检查一级缓存中所有的持久化对象,决定发出insert语句还是update语句
检查持久化对象的关联对象,看持久化对象的映射文件中的cascade,决定关联对象是否发出insert语句或者update语句或者delete语句
检查持久化对象的映射文件的针对关联对象的inverse属性,来决定是否维护关系。如果维护关系,则发出update语句(维护关系的语句)
再次讨论session.flush
1、 检查session一级缓存中所有的持久化对象的状态,决定发出insert语句或者update语句。
会检查所有的持久化对象的关联对象,如果有级联操作,则对关联对象进行insert语句或者update语句
总结
1、 只能通过classes操作student
2、 只要classes维护关系,就会发出维护关系的update语句,所以让classes维护关系效率比较低
3、 让student维护关系效率比较高
4、 Session.flush的时候,hibernate内部做的事情
1、 检查一级缓存中所有的持久化对象,决定发出insert语句还是update语句
2、 检查持久化对象的关联对象,看持久化对象的映射文件中的cascade,决定关联对象是否发出insert语句或者update语句或者delete语句
3、 检查持久化对象的映射文件的针对关联对象的inverse属性,来决定是否维护关系。如果维护关系,则发出update语句(维护关系的语句)
一对多的双向
持久化类和映射文件
操作
新建学生级联保存班级
说明:
1、36行代码是通过student建立student与classes之间的关联,所以看student.hbm.xml文件
2、在Student.hbm.xml文件中,设置了针对classes的级联
3、在保存学生的时候级联保存了班级
4、保存学生相当于维护了关系
classes 和student级联文件相互独立 根据级联
新建一个学生,并且和一个已经的班级关联
学生转班
说明:从上述的sql语句可以看出不仅更新了外键,而且把其他属性也更新了,所以更新关系指的就是更新student对象本身的操作。
解除一个班级和一个学生之间的关系
解除该班级和所有的学生的关系
这样做是通过学生解除学生和班级之间的关系,但是效率不高。
classes.setStudent(null) 效率高
一对多总结
一般情况下,一对多,多的一方维护关系效率要比较高。发出的sql语句越少,效率越高
多对多的双向
持久化类和映射文件
让一个新的学生和一个已经存在的课程发生关联
说明:
1、65行代码是通过课程建立课程与学生之间的关联,course.getStudents()的时候会发出根据课程查找该学生的sql语句,这样做效率比较低
解除一个学生和一门课程之间的关系
解除该课程和所有的学生之间的关联
一个学生从一门课程转移到另外一门课程
关系操作总结
1、 关系的角度
一对多反映的是类与集合的关系
多对一反映的是类与类之间的关系
多对多反映的是类与集合之间的关系
2、 一对多,多的一方维护关系,效率比较高
3、 多对多,谁维护效率都一样
4、 通过谁建立关系,看谁的映射文件
Cascade与inverse之间的区别
一对一的双向
所以上面的代码是不对的,因为上面的代码会导致student2和student1的外键都为6L。
一对一双向总结
1、 关系的角度
一对多反映的是类与集合的关系
多对一反映的是类与类之间的关系
多对多反映的是类与集合之间的关系
2、 一对多,多的一方维护关系,效率比较高
3、 多对多,谁维护效率都一样
4、 通过谁建立关系,看谁的映射文件
5、 Cascade与inverse之间的区别
重点
1.内部执行流程
2.错误分析熟练掌握
错误分析
1、 把16,17,18这三行代码的对象的状态称为临时对象,其特征是该对象与hibernate没有关系。
2、 当执行19行代码的时候,person对象由临时状态转化成持久化状态,这个时候数据库没有对应的数据,但是该对象在hibernate内部
3、 当执行20行代码的时候,事务提交了,该对象还是持久化状态的对象,数据库中有对应的值了
4、 当执行完21行代码的时候,session关闭了,该对象成为脱管状态的对象