Hibernate

来源:互联网 发布:sql2008新建数据库 编辑:程序博客网 时间:2024/06/09 23:26
1.hibernate是一个orm框架

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中配置



原创粉丝点击