ORM是对象关系映射,实现面向对象编程语言里不同类型系统的数据之间的转换
2.XML版
每一个model对应一个.hbm.xml文件,这个文件可以帮我们自动的建表,插入字段数据等
比如新建一个Student的model,就要对应写一个Student.hbm.xml(映射文件的配置)的文件,里面的配置如下:
package表示我这个Student类所在的包名
class对应于我的类名,table表示我这个类名映射在哪张表下
<id>标签配置的是主键,其他字段用<property>标签配置
<id>里面的name表示我类里面的变量名字,column表示我类里面的变量名对应的表里面的字段的名字,如果name和column名字是一致的可以不写column
比如<property>的配置,如果name和column不一致,则需要加上column的名字,比如id的配置
主键下面的<generator>表示生成模式,这里的native表示这个主键是自增模式
<hibernate-mapping package="model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="native"></generator>
</id>
<property name="name"></property>
</class>
</hibernate-mapping>
3.注解版
注解版不需要每一个model都写一个对应的配置文件
(1)设置类的:
@Entity:表示一个实体
@Table(name="t_teacher"):表示这个model映射到t_teacher这个表
(2)设置主键的:
@Id
@GeneratedValue(generator="_native")
@GenericGenerator(name="_native",strategy="native")
注解版的配置在hibernate.cfg.xml中添加一句就可以: <mapping class="model.Teacher"/>
4.hibernate用对象标识符OID来区分对象,已经取到的对象会在session的缓存中,每个对象有唯一的OID,再取对象时会先到session缓存中看看有没有这个对象,如果有则直接获取。
5.hibernate对象标识符生成策略(主键)
主键:业务主键和代理主键
代理主键就是单纯的标志这一条唯一的记录的,不具有任何的业务
当这条记录总是变化的则选择代理主键,否则使用业务主键
(1)increment:hibernate(不是数据库)以递增的方式生成标志,适合代理主键
(2)identity :底层数据库生成标志,适合代理主键
(3)sequence: 由hibernate根据底层数据库的序列来生成标识符,适合代理主键【mysql不支持】
(4)hilo:hibernate根绝hilo算法来生成标识符(了解)
(5)native:根据底层数据库对自动生成标识符的支持能力来选择identity,sequence,或者hilo,适合代理主键
6.关联关系:一对多
例子【班级1,学生多】
下面是单向的
相当于是给学生表添加一个外键classId,这个ClassId是班级的主键
因此需要在Student的model中添加一个班级c的属性
在Student.hbm.xml中配置一下外键关联:<many-to-one name="c" column="classId" class="model.Class"></many-to-one>
name对应的是新添加的变量,column表示的是映射在表中的字段(外键名字),以及对应的class的名字
级联保存
当班级是临时对象的时候,保存学生时会报错,说是引用了未持久化对象,因为在student的<many-to-one>的配置中cascade的配置默认是null
如果我们想在不保存班级的情况下,保存上学生,并且保存上相应的班级,则需要配置cascade="save-update",
即在持久化多的一端,自动级联保存和更新一的那一端,则需要配置cascade="save-update"
如下:
<many-to-one name="c" column="classId" class="model.Class" cascade="save-update"></many-to-one>
双向的既能从学生端获取班级,也能从班级端获取学生
需要在Class2中声明一个Set集合来存储获取的所有的学生
然后在Class2.hbm.xml中添加如下配置:
"set"表示在班级中定义的获取学生集合的那个集合的名字,key表示Student2中定义的外键,同样需要注意设置级联保存的问题,不设置级联保存的话会报引用临时变量的错误
<set name="set" cascade="save-update">
<key column="classId"></key>
<one-to-many class="model.Student2"/>
</set>
7.没用session保存的称为临时对象,session保存的称为持久化对象,是保存在磁盘里面的。
8.inverse
我们一般添加inverse是在一的那一端
inverse就是为了消除冗余(冗余就是指一对多关系中双向都会进行update,但是结果是一样的,因此操作就冗余了)
因此在学生班级关系中,在班级端加上inverse,但是这个inverse要加在班级中的set下,因为学生时经常操作的,这样能提高性能,如下:
<set name="set" cascade="save-update" inverse="true">
<key column="classId"></key>
<one-to-many class="model.Student2"/>
</set>
9.自身关联映射Node
一是本身,多也是本身
因此需要在类里面定义一的方面:Node parentNode
也需要定义多的方面:Set<Node> childNodes=new HashSet<Node>()
<many-to-one>中column就代表的是主键
Node.hbm.xml配置如下:
<class name="Node" table="t_node">
<id name="id" column="nodeId">
<generator class="native"></generator>
</id>
<property name="name" column="nodeName"></property>
<many-to-one name="parentNode" column="parentId" class="model.Node" cascade="save-update"></many-to-one>
<set name="childNodes" inverse="true">
<key column="parentId"></key>
<one-to-many class="model.Node"/>
</set>
</class>
10.hibernate中四种对象状态【这些状态针对的都是数据库里面的对象的状态】
(1)临时状态(transient):刚用new语句创建,还没有持久化,并且不处于session缓存当中,处于临时状态的java对象被称为临时对象。
(2)持久化状态(persistent):已经被持久化,并且加入到session缓存当中,处于持久化状态的java对象被称为是持久化对象
(3)删除状态(removed):不再处于session缓存当中,并且session已经计划将其从数据库中删除,处于删除状态的java对象被称为是删除对象
(4)游离状态(detached):已经被持久化,但是不再处于session缓存当中。处于游离状态的java对象被称为游离对象。比如session被关闭了
临时状态、删除状态、游离状态都会被垃圾回收的。
11.session常用方法
(1)save是把临时对象转变为持久化对象
(2)load方法和get方法
都是根据OID从数据库中加载一个持久化对象
区别1:如果数据库中不存在与OID相对应的记录,load方法会抛出异常,get方法返回null
区别2:load方法默认采用延迟加载策略,get方法采取立即搜索策略
延迟加载指的是通过load取到数据,只有在用到的时候才有,用不到的时候是空的。
所以如果是为了删除,则选择load来获取对象,如果是为了获取属性则选择get来获取对象
(3)update是把游离对象转变为持久化对象
就是关闭session以后,把对象变为游离状态再进行update,此时对象又会变为持久化对象。
(4)saveOrUpdate方法包含了save和update方法
临时变量会自动调用save方法,游离变量会自动调用update方法
(5)merge方法,合并对象
在更新的时候,发现在session缓存中已经有了这个要更新的OID,merge方法就会合并。
把这两个属性合并再更新
(6)delete操作
12.一些基本类型的映射
(1)要记着的一个是如何保存图片:
LobHelper lobHelper=session.getLobHelper();//用这个来存储图片,这是hibernate封装的
InputStream in=new FileInputStream("D:/java.jpg");//定义输入流
Blob blob = lobHelper.createBlob(in,in.available());//第一个参数是输入流,第二个参数是长度
book.setBookPic(blob);
(2)String类型对应的hibernate的类型是string(小写!)
13.集合类型的映射
(1)set:无序 元素不可重复
set集合映射到表中
在Student3中添加属性Set<String> images=new HashSet<>();
在Student3.hbn.xml中配置:
<set name="images" table="t_image">
<key column="studentId"></key>
<element column="imageName" type="string"></element>
</set>
把images映射到t_image表中,<key>中设置的是外键的名字,这个外键映射的是t_student的主键
<element>里面是Set中包含的元素
2)List:有序 元素可重复
基本上和Set一样,但是需要在配置上添加一个索引<list-index>,如下:
<list name="images" table="t_image2">
<key column="studentId"></key>
<list-index column="imageIndex"></list-index>
<element column="imageName" type="string"></element>
</list>
(3)Bag:无序 元素可重复
Bag使用List来模拟的,配置也基本相同,但是需要额外添加一点配置,如下:
<collection-id>里面的imageid是主键,前面是主键的类型
<idbag name="images" table="t_image3">
<collection-id type="long" column="imageid">
<generator class="increment"></generator>
</collection-id>
<key column="studentId"></key>
<element column="imageName" type="string"></element>
</idbag>
(4)Map:键值对
把studentId和map的key形成联合主键,配置如下:
多了一个设置map的key值,element就对应的value
<map name="images" table="t_image4">
<key column="studentId"></key>
<map-key column="imageKey" type="string"></map-key>
<element column="imageName" type="string"></element>
</map>
集合类型映射只是数据的映射,不在session缓存当中
14.hibernate映射继承
(1)每个具体类对应一张表
根类Image是abstract,因此三张表,两个子类对应两张表,一个person类对应一张表。person和Image的关系是一对多的关系,但是因为Image是抽象类的,所以Person的配置如下:
很简单的配置主键,以及另一个属性
<class name="Person" table="t_person">
<id name="id" column="personId">
<generator class="native"></generator>
</id>
<property name="name" column="personName"></property>
</class>
两个子类的配置如下,另一个是一样的,只拿一个作为例子:
<class name="WorkImage" table="t_workimage">
<id name="id" column="workImageId">
<generator class="native"></generator>
</id>
<property name="imageName" column="workImageName"></property>
<many-to-one name="person" column="pId" class="model.Person" ></many-to-one>
</class>
(2)根类对应一张表
根类不是抽象类,是具体的类,则根类对应一张表,子类就不需要了
此时person的配置如下:
<class name="Person2" table="t_person2">
<id name="id" column="personId">
<generator class="native"></generator>
</id>
<property name="name" column="personName"></property>
<set name="images">
<key column="pId"></key>
<one-to-many class="model.Image2"/>
</set>
</class>
注意增添的set的那部分
Image2的配置如下:
<class name="Image2" table="t_image2">
<id name="id" column="imageId">
<generator class="native"></generator>
</id>
//需要加上标志来判断是哪个子类的
<discriminator column="imageType" type="string"></discriminator>
<property name="imageName" column="imageName"></property>
<many-to-one name="person" column="pId" class="model.Person2"></many-to-one>
//设置子类的标志,即往数据库里面写内容时,如果定义好类型以后,有了下面的语句后,数据库会自动的根据类别添加上imageType是li还是wi
<subclass name="model.LifeImage2" discriminator-value="li"></subclass>
<subclass name="model.WorkImage2" discriminator-value="wi"></subclass>
</class>
(3)每个类对应一张表
即父类一张,两个子类个一张,Person一张
person的配置如下:
<class name="Person3" table="t_person3">
<id name="id" column="personId">
<generator class="native"></generator>
</id>
<property name="name" column="personName"></property>
<set name="images">
<key column="pId"></key>
<one-to-many class="model.Image3"/>
</set>
</class>
父类的配置如下:
<class name="Image3" table="t_image3">
<id name="id" column="imageId">
<generator class="native"></generator>
</id>
<property name="imageName" column="imageName"></property>
<many-to-one name="person" column="pId" class="model.Person3"></many-to-one>
//需要注意的这里,key标签里面设置的column即是这个表的主键,又是外键,关联了外边表t_image3的主键
<joined-subclass name="model.WorkImage3" table="t_workimage3">
<key column="workImageId"></key>
</joined-subclass>
<joined-subclass name="model.LifeImage3" table="t_lifeimage3">
<key column="lifeImageId"></key>
</joined-subclass>
</class>
15.hibernate映射关系一对一
【user和Address的关系】
实现一对一映射包括两种方式:根据主键映射和根据外键映射
(1)根据主键映射实现1对1
首先model:对于User需要添加1对1的另一个属性:private Address address
对于Address也需要添加1对1的另一个属性:private User user
然后配置文件:
User:主键和其他属性都一样,唯一要添加的是1对1的映射:
<one-to-one name="address" class="model.Address" cascade="all"></one-to-one>
根据用户进行级联保存删除等。
Address:在这里需要配置共享主键,即Address的主键和User的主键一致,而且Address的主键还是外键,关联User的主键
对于外键的配置:
<id name="id" column="addressId">
<generator class="foreign">
<param name="property">user</param>
</generator>
共享主键的配置:
<one-to-one name="user" class="model.User" constrained="true"></one-to-one>
2)根据外键映射实现1对1
model是一样的
配置:
User:设置外键,因为外键可以重复,但是要实现一对一,所以需要添加上属性 unique="true",这样外键不能重复,就实现一对一了
<many-to-one name="address" class="model.Address2" column="addressId" cascade="all" unique="true"></many-to-one>
Address:主键和其他属性正常配置,增添下面一行即可,property-ref表示引用另一个对象
<one-to-one name="user" class="model.User2" property-ref="address"></one-to-one>
16.Hibernate多对多映射关系【学生和课程】
单向就是只能从一端取到另一端的数据。
所以只需要在一端添加集合即可,我们是通过学生获取课程,所以只需要在学生的model中添课程集合就可以了
private Set<Course> courses=new HashSet<Course>();
配置文件:
Student:
因为有set集合,所以需要配置set,key中的是主键也是外键,关联的是外边student表的主键,many-to-many中的column也是主键,也是外键,关联的是前面course中的主键
table="student_course"这张表里面是联合主键
<set name="courses" table="student_course" cascade="save-update">
<key column="student_id"></key>
<many-to-many class="model.Course" column="course_id"></many-to-many>
</set>
(二)多对多映射双向
双向的意思就是两端都能获取到另一端的数据
因此两端的model中都要添加集合
private Set<Course2> courses=new HashSet<Course2>();
private Set<StudentNew2> students=new HashSet<StudentNew2>();
配置文件:
student中的和上面的一样
course的需要加set的配置:注意的是添加上inverse=true消除冗余,表示由student来管理
<set name="students" table="student_course2" inverse="true">
<key column="course_id"></key>
<many-to-many class="model.StudentNew2" column="student_id"></many-to-many>
</set>
17.hibernate检索策略
(一)检索策略属性Lazy
默认为true:延迟检索,set端,一对多。即只有在用到多的那部分的时候才会搜索,否则不会执行,只会有一个sql生成
false:立即检索,set端,一对多。这样的话执行会生成两个sql,和一关联的多的部分也会被搜索出来
extra:增强延迟检索,set端,一对多
默认proxy:延迟检索,many-to-one 多对一
no-proxy:无代理延迟检索 many-to-one 多对一
(二)检索策略属性batch-size
注意:批量需要在set端设置上batch-size=number,这样一条语句就可以检索出number调,否则需要每条语句检索一个,就是需要number条语句
1.批量延迟检索:lazy=true,batch-size=number
2.批量立即检索:lazy=false,batch-size=number
(三)检索策略属性Fetch
默认:select 查询方式
subselect 子查询方式,数据量大的时候可以选择子查询
join 迫切左外连接查询方式
18.hibernate查询方式
数据model:StudentSelectModel
(1)导航对象图查询方式:在查询学生的时候可以把关联的班级也查询出来
(2)OID查询方式:get或者load这种
(3)本地sql查询方式
(4)HQL查询方式,使用最广的
(5)QBC查询方式(Query by criteria)
19.HQL查询方式【面向对象的】
(1)普通查询,直接from 类名
String hql="from StudentSelectModel";
List<StudentSelectModel> list = session.createQuery(hql).list();
(2)带条件查询,加上where,并且要设置值
String hql="from StudentSelectModel where name like :stuName and age=:stuAge";
Query query=session.createQuery(hql);
query.setString("stuName", "%t%");
query.setInteger("stuAge", 1);
(3)使用别名
String hql="from StudentSelectModel as s where s.name like :stuName and s.age=:stuAge";
(4)对结果排序,使用order by 字段名 desc(降序)
String hql="from StudentSelectModel order by age desc";
(5)分页查询
String hql="from StudentSelectModel";
Query query=session.createQuery(hql);
query.setFirstResult(0);//设置从第几个结果开始
query.setMaxResults(2);//查询输出几个结果
(6)查询单个对象
Query query=session.createQuery(hql);
query.setFirstResult(1);
query.setMaxResults(1);
StudentSelectModel s =(StudentSelectModel) query.uniqueResult();
(7)链式写法
List<StudentSelectModel> list=query.setString("stuName", "%t%")
.setInteger("stuAge", 1).list()
20.QBC查询方式【一套用接口是实现的查询方式】
(1)普通条件查询
Criteria criteria = session.createCriteria(StudentSelectModel.class);//写好要查询的类
List<StudentSelectModel> list = criteria.list();
(2)按照条件查询
Criteria criteria = session.createCriteria(StudentSelectModel.class);//写好要查询的类
Criterion c1=Restrictions.like("name", "%t%");//设置查询条件
Criterion c2=Restrictions.eq("age", 1);//设置查询条件
criteria.add(c1);//添加上查询条件
criteria.add(c2);
(3)对结果排序
criteria.addOrder(Order.desc("age"));
(4)分页查询,和HQL的设置方式基本一致
criteria.setFirstResult(0);
criteria.setMaxResults(2);
(5)查询的单个对象,和hql的也基本一致
criteria.setFirstResult(1);
criteria.setMaxResults(1);
StudentSelectModel s = (StudentSelectModel) criteria.uniqueResult();
21.hibernate高级配置
(1)配置数据库连接池
推荐使用C3P0数据库连接池,hibernate的连接池有bug
用hibernate配置C3P0
首先要导入C3P0的jar包(3个)
需要在hibernate.cfg.xml中添加如下配置:
<!-- 最小连接数 -->
<property name="c3p0.min_size">7</property>
<!-- 最大连接数 -->
<property name="c3p0.max_size">42</property>
<!-- 获得连接的超时时间,如果超过这个时间,会抛出异常,单位毫秒 -->
<property name="c3p0.timeout">1800</property>
<!-- 最大的PreparedStatement的数量 -->
<property name="c3p0.max_statements">50</property>
(2)配置日志文件Log4J
hibernate默认使用的是简单配置框架
首先引入log4j的jar包,然后在src中引入log4j.properties
配置log4j.properties(简单配置,具体的可以看百度百科)
/*
配置根logger,第一个参数是日志记录的优先级别,后面的两个参数为日志的输出地址
需要注意的是优先级别从高到低:ERROR>WARN>INFO>DEBUG,只有等于或者高于定义的这个级别才进行处理
比如你在log.properties中配置的是INFO,你在应用程序中只能输出ERROR、WARN、INFO,不能输出DEBUG的相关信息
*/
log4j.rootLogger=debug,appender1,appender2
/*
配置信息的输出地方
第一个是控制台输出
第二个是文件的输出
*/
log4j.appender.appender1=org.apache.log4j.ConsoleAppender
log4j.appender.appender2=org.apache.log4j.FileAppender
log4j.appender.appender2.File=d:/logFile.txt
/*
下面的是对于布局的配置,即产生什么样子的信息之类的
*/
log4j.appender.appender1.layout=org.apache.log4j.TTCCLayout
log4j.appender.appender2.layout=org.apache.log4j.TTCCLayout
在应用程序中测试log4j
private Logger logger=Logger.getLogger(StudentSelectModel.class);//针对哪个类进行日志输出
logger.debug("这是一个dubug信息");
logger.info("这是一个info信息");
logger.error("这是一个error信息");
(3)hibernate二级缓存
缓存是介于物理数据源与应用程序之间,是对数据库中的数据复制一份放在内存或者硬盘中的容器
其作用是为了减少应用程序对物理数据源的访问次数,hibernate在进行读取数据的时候,如果在缓存中找到了需要的数据(缓存命中)
则直接把命中的额数据作为结果加以利用。
hibernate缓存分类
1.session缓存(事物缓存),一级缓存。 hibernate内置的,不能卸除,缓存只能被当前session对象访问,缓存声明周期依赖于session的生命周期,session关闭则缓存也结束生命周期。
一级缓存只存在于同一个session同一个事物当中
2.SessionFactory缓存(应用缓存),使用第三方插件,可插拔。不同的session可以共享,应用结束时,缓存才结束生命周期,二级缓存存在于应用程序范围。
什么数据适合放在二级缓存:
经常被访问,改动不大、数量有限、不是很重要的数据
配置二级缓存
1.首先引入jar包
2.复制ehcache.xml到src下
3.在hibernate.cfg.xml中配置二级缓存
<!-- 启用二级缓存 -->
<property name="cache.use_second_level_cache">true</property>
<!-- 配置使用的二级缓存的产品 -->
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
<!-- 配置启用查询缓存 -->
<property name="cache.use_query_cache">true</property>
4.还需要在测试的model中配置