初识Hibernate

来源:互联网 发布:体彩开奖网络视频直播 编辑:程序博客网 时间:2024/06/06 01:27

Hibernate

JDBC和Hibernate的异同点

  • 相同点
    • 两者都是java数据库操作中间件
    • 两者对于数据库进行直接操作对象都不是线程安全的,需要及时的关闭
    • 两者都可以对数据库的更新操作进行显式的事务处理
  • 不同点
    • 使用的语言不同,JDBC使用的是Sql语言,而Hibernate是HQL语言
    • 操作的对象不一样,JDBC操作的是数据,将数据直接传送到数据库中;而Hibernate操作的是持久化对象,由底层的持久化对象的数据更新到数据库中。
    • 数据状态不同:JDBC操作的数据是“瞬时的”,变量的值没法与数据库中的值保持一致;而Hibernate操作的数据是可持久化的,持久化对象的数据和数据库中的数据是保持一致的。
    • 对于事务的控制不同,JDBC默认事务是开启的,数据插入修改删除会自动提交;而Hibernate是默认关闭的,在进行插入,修改或者删除时,需要开启事务。

ORM 对象关系映射

ORM(对象-关系映射):完成对象数据到关系型数据映射的机制称为对象-关系映射,简称ORM。

Hibernate中的三种状态

区别三种状态的一个方法是看是否拥有自增的主键,内存中是否有该对象,缓存中是否有该对象,数据库中是否有该对象的信息。
1. 瞬时态(transient)
- ==介绍==:数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器回收,一般是new出来且与session没有关联的对象。
- 对象没有自增主键
- 对象存在内存当中,session缓存和数据库都没有对象信息
2. 持久态(persistent)
- ==介绍==:数据库中有数据与之对应,当前与session有关联,并且相关联的session没有关闭,事务没有提交;持久对象状态发生改变,在事务提交时会影响到数据库(hibernate能检测到)。
- 内存,session缓存和数据库都有对象的信息
- 拥有自增主键
3. 游离态(detached)
- ==介绍==:数据库中有数据与之对应,但当前没有session与之关联;托管对象状态发生改变,hibernate不能检测到。
- 内存当中存在存在对象,session缓存中没有,数据库中有对象记录
- 拥有自增主键


Hibernate中三个重要的类

Configuration

Configuration类负责管理Hibernate的配置信息,它包括如下内容:Hibernate运行的底层信息:数据库的URL、用户名、密码、JDBC驱动类、数据库Dialect(方言),数据库连接池等。
Hibernate映射文件(*.hbm.xml)

从属性文件读取配置:hibernate.properties调用代码:Configuration cfg=new Configuration();从XML文件读取配置:hibernate.cfg.xml调用代码: Configuration cfg=new Configuration().configure();

SessionFactory

应用程序从SessionFactory里获得Session。它在多个应用线程间进行共享。通常情况下,整个应用只有唯一的一个SessionFactory——例如在应用初始化时被创建。然而,如果你使用Hibernate访问多个数据库,你需要对每一个数据库使用一个SessionFactory。
SessionFactory缓存了生成的SQL语句和Hibernate在运行时使用的映射元数据。

SessionFactory factory = cfg.buildSessionFactory();

Session

  • session不是线程安全的
  • session也称作是持久化管理器
  • session通过sessionfactory创建,在所有的工作完成了之后,需要关闭

关于session的几个重要的方法
- save 保存数据。
- delete,删除对象
- update,更新对象,如果数据库中没有记录,会出现异常。
- get,根据ID查,会立刻访问数据库。
- Load,根据ID查,(返回的是代理,不会立即访问数据库)。
- saveOrUpdate, merge(根据ID和version的值来确定是save或update),调用merge你的对象还是脱管的。
- lock(把对象变成持久对象,但不会同步对象的状态)。



session对象的获取

  • 通过 new Configuration().configure().buildSessionFactory(),得到SessionFactory对象,然后调用这个对象的openSession方法就可以获取session了
SessionFactory factory = new Configuration().configure().buildSessionFactory();Session session = factory.openSession();
  • 通过HibernateSessionFactory.getSession()可以获取到Session对象
Session session = HibernateSessionFactory.getSession();...HibernateSessionFactory.closeSession(); //关闭会话

主键自增native在mysql和oracle中的差异

generator中class如果指定了native,则会根据数据库自动匹配合适的方式来对主键自增
- 因为在mysql中,支持auto_increment,所以generator中class的native会自动递增。

<class name="com.etc.domain.Student" table="STUDENT" schema="SCOTT">    <id name="sid" type="java.lang.Short">        <column name="SID" precision="4" scale="0" />        <generator class="native" />    </id>    <property name="name" type="java.lang.String">        <column name="NAME" length="10" />    </property></class>
  • 在oracle中,使用的是序列来进行主键的自增,所以当使用了native时,需要在generator中指定你的序列,否则hibernate会自动去寻找hibernate_seq这个序列对象。
<class name="com.etc.domain.Student" table="STUDENT" schema="SCOTT">    <id name="sid" type="java.lang.Short">        <column name="SID" precision="4" scale="0" />        <generator class="native">            <param name="sequence">HSTUD_SEQ</param> //指定了oracle中的序列            </generator>    </id>    <property name="name" type="java.lang.String">        <column name="NAME" length="10" />    </property></class>

手动添加主键,那么就是assigned

<class name="com.etc.domain.Student" table="STUDENT" schema="SCOTT">    <id name="sid" type="java.lang.Integer"> //这里要记得修改type,否则会一直报类型错误        <column name="SID" precision="4" scale="0" />        <generator class="assigned"></generator>    </id>    <property name="name" type="java.lang.String">        <column name="NAME" length="10" />    </property></class>

HQL语句

HQL语句操作的是对象,这点一定要记住,而不是数据库中的字段名。

如果Hibernate查询的不是一个完整的对象,而是某几个字段,那么得到的list是List

// 查询不完整的对象 单个属性String hql = "select name from Emp where id<8";List<Object> list = session.createQuery(hql).list();for ( Object o : list) {    System.out.println(o);}// 查询多个属性String hql = "select id,name from Emp where id<8";List<Object[]> list = session.createQuery(hql).list();for ( Object[] obj : list) {    for ( Object o : obj ) {        System.out.println(o);    }}// 使用内连接来进行多表的关联查询String hql = "from Emp as e inner join e.dept as d where d.deptName='技术部' ";List<Object[]> list = session.createQuery(hql).list();for ( Object[] obj : list ) {    for ( Object o : obj) {        System.out.println(o);    }}// 隐蔽的关联对象,在使用的时候才执行查询(懒加载) 推荐使用String hql = "from Emp where dept.deptno=10 ";List<Emp> list = session.createQuery(hql).list();for ( Emp e : list ) {    System.out.println(e);    System.out.println(e.getDept().getDeptName()); //这个时候查找部门}

Hibernate中的增删改查

HQL SQL save insert update update delete 需要指定主键 delete “from Obj” select XX from Obj对应的表格 get(class, iD) 获取单个对象

示例

  • 插入
    session.beginTransaction();    Student s = new Student("张三", 44);    session.save(s);    session.getTransaction().commit();    session.close();
  • 修改
    Student s = new Student("王小二", 77, new Date());    s.setSid(3);    session.beginTransaction();    session.saveOrUpdate(s);    session.getTransaction().commit();
  • 删除
    Student s = new Student();    s.setSid(1); //只需要设置主键就可以了    session.beginTransaction();    session.delete(s);    session.getTransaction().commit();    //虽然可以这么做,但一般会先查找有没有,再删除    Student s = (Student) session.get(Student.class, 99);    if ( s != null) {        session.delete(s);    }
  • 查找单个对象
public static void getById(Session session){    Student s = (Student) session.get(Student.class, 3); //这里比如固定查找id3的学生    System.out.println(s);}
  • 查找一系列对象
public static void queryAllStudent(Session session){    Query query = session.createQuery("from Student"); //需要用到HQL语句    List<Student> list = query.list();    for ( Student s : list ){        System.out.println(s);    }}

动态查询

hibernate 的动态查询和JDBC中的十分类似,需要设置参数可以使用?来代替

// 使用类似JDBC的方式来进行,?是从0开始,而不是1String hql = "from Emp where id=?";Query query = session.createQuery(hql);query.setInteger(0, 3);List<Emp> list = query.list();for ( Emp e : list ) {    System.out.println(e);}// 也可以去使用别名String hql = "from Emp where id=:eid";Query query = session.createQuery(hql);query.setParameter("eid", 3);List<Emp> list = query.list();for ( Emp e : list ) {    System.out.println(e);}// 也可以使用关联对象来进行查询String hql = "from Emp where dept=:d";Query query = session.createQuery(hql);Dept d = new Dept();d.setDeptno(40);query.setEntity("d", d);List<Emp> list = query.list();for ( Emp e : list ) {    System.out.println(e);}

命名查询

hibernate支持在配置文件中写如HQL语句或者SQL语句,写的位置是你要查询的那个对象的hbm.xml中。如果是HQL的话,节点是query,如果是SQL语句的话,就是sql-query。
配置在class外.

<!-- HQL语句 --><query name="myhql_for_userinfo">    <![CDATA[select id,name from Emp where id=:eid]]> <!-- 这里要注意的是必须要加上!CDATA,否则会按照xml来解析,那么一些符号就会出问题了 --></query><!-- SQL语句 --><sql-query name="myhql_for_userinfo1">    <![CDATA[select uname,uid from Userinfo where uname=:name]]></sql-query>

以下是HQL的查询简单例子

Query query = session.getNamedQuery("myhql_for_userinfo");query.setParameter("eid", 3); // 设置配置文件中的动态参数List<Object[]> list = query.list();for ( Object[] e : list ){    for (Object o : e) {        System.out.println(o);    }}

以下是SQL的查询 和JDBC中的sql一样

Query query = session.getNamedQuery("myhql_for_userinfo1");query.setParameter("d", 40);List<Object[]> list = query.list();for(Object[] o : list) {    for (Object e : o) {        System.out.println(e);    }}

Criteria查询

QBC(Query By Criteria) 查询方式,QBC更是面向对象的查询方式,不用编写任何的语句,仅仅是对于Restrictions,Order,Projections三个对象来进行设置属性就可以了。

特点
- 完全面向对象,无需任何的语句。
- 支持“链式风格”。
- 支持从离线查询作转换

一般的使用步骤是这样的
首先需要活得Criteria对象,通过session.createCriteria()方法
使用Restrictions设置查询条件,使用Order设置排序条件,Projections来进行统计和分组

  • Restrictions类常用的方法(省略了Restrictions前缀)
方法名称 描述 eq 相等 allEq 一系列的等于条件,map key value gt 大于 lt 小于 le 小于等于 between 在什么之间 like like操作符 in 在范围内 and 且 or 或 sqlRestriction SQL限定查询

- Order常用方法

方法名称 描述 Order.asc 升序 Order.desc 降序

- Projections类(省略了Projections前缀)

方法名称 描述 avg 均值 count 统计某个属性的记录数 countDistinct 统计某个属性不重复的记录数 sum 统计某个值 max 某个字段的最大值 min 某个值的最小值 rowCount 统计结果集中的记录数 groupProperty 以某个属性进行分组 projectionList 创建一个projectionList对象

例子

Criteria cri = session.createCriteria(Emp.class);cri.add(Restrictions.ge("id", 9)); // 选取id大于9的记录cri.addOrder(Order.desc("id"));  //按照id属性进行降序排列cri.add(Restrictions.like("name", "%王"));  //找出名字中以王结尾的名List<Emp> list = cri.list();for (Emp e : list) {    System.out.println(e);}

如果要使用关联的对象来进行查询也是可以的,方法是创建一个子Criteria,然后按照普通的Criteria设置属性,然后查找的时候,用父Criteria来进行查找。
一个简单的例子如下

Criteria cri = session.createCriteria(Emp.class);Criteria subCri = cri.createCriteria("dept");subCri.add(Restrictions.eq("deptno", 40));List<Emp> list = cri.list();for (Emp e : list) {    System.out.println(e);}

Criteria的一个特点之一就是支持离线查询,在没有连接的时候,先都设置好查询条件,当获取连接后,就可以查询得到结果。

DetachedCriteria dc = DetachedCriteria.forClass(Emp.class);dc.add(Restrictions.gt("id", 3));Criteria cri = dc.getExecutableCriteria(session);List<Emp> list = cri.list();for(Emp e : list){    System.out.println(e);}

分页

Hibernate中的分页使用起来十分简单,与具体的数据库无关

Query query = session.createQuery("from Emp");query.setFirstResult(2); // 设置从第几条开始 0 1 2query.setMaxResults(5);List<Emp> list = query.list();for(Emp e : list ) {    System.out.println(e);}

Hibernate中的懒加载

在节点上设置的lazy会对load起作用,属于类级别的加载;
在节点上,同样允许使用懒加载,会在访问set中的元素时,执行查询。对于get/load都是有效的。

懒加载中涉及到主控对象和关联对象,简单说,你要先通过查找谁,然后再去找谁。前一个就是主控对象,后一个是关联对象。关闭懒加载会同时查找二者,而懒加载只会先找出主控对象。

Fetch=”select” 表示采用SQL的查询方式,当属性值为join时,会采用1条关联语句,实现两张表的查询。此时即便是设置了懒加载,是不起作用的。

必需同时满足下面三个条件时才能实现懒加载
1. lazy!=false 2)constrained=true 3)fetch=select(主表不能有constrained=true,所以主表没有懒加载)
1. 3.one-to-many (元素)懒加载:1)lazy!=false 2)fetch=select
1. 4.many-to-one (元素) :1)lazy!=false 2)fetch=select
1. 5.many-to-many (元素) :1)lazy!=false 2)fetch=select

Hibernate中的关联

one-to-one

ont-to-many

一对多的情况十分常见,例如一个学生有多门课程,一个公司有多个部门,one-to-many常常和many-to-one一起用的,因为这仅仅是角度的问题。
在myEclipse中,选中一对多的两张表,然后反向工程,自动去生成配置文件,大致的内容如下

// 在学生当中有一个成绩set对象<set name="scores" inverse="true">    <key>        <column name="sid" not-null="true" />    </key>    <one-to-many class="com.etc.domain.Score" /></set>// 在成绩当中,有一个学生对象<many-to-one name="student" class="com.etc.domain.Student" fetch="select">    <column name="sid" not-null="true" /></many-to-one>

many-to-many

多对多的关系按照中间的联系分为2中情况
- 联系存在额外的数据,称之为不纯洁的的多对多
- 联系不存在额外的数据,称之为纯洁的的多对多

不纯洁的多对多

不纯洁的多对多在myEclipse中的反向工程中,要选定三张表(A,B,以及AB联系表)建立他们的实体。
在使用的时候和一般的一对多关联没有太大的区别,这里主要贴出三个配置文件。两个使用的是一对多的关系,联系表使用的是多对多的关系。

//学生hbm<set name="scores" inverse="true">    <key>        <column name="cid" not-null="true" />    </key>    <one-to-many class="com.etc.domain.Score" /></set>//课程的hbm<set name="scores" inverse="true">    <key>        <column name="sid" not-null="true" />    </key>    <one-to-many class="com.etc.domain.Score" /></set>//成绩hbm<many-to-one name="student" class="com.etc.domain.Student" fetch="select">    <column name="sid" not-null="true" /></many-to-one><many-to-one name="course" class="com.etc.domain.Course" fetch="select">    <column name="cid" not-null="true" /></many-to-one>
纯洁的多对多

纯洁的多对多相比于不纯洁的多对多要稍微麻烦一点。
1. 在反向工程中不选择AB联系表
1. 在实体A中,保留Set Bs对象,删除联系表对象。
1. 同理,在实体B中,保留Set As,删除联系表对象。
1. toString() 方法最好不要加入Set集合,可能会有问题
1. 修改A.hbm.xml和B.hbm.xml中的set节点,参考如下

<set name="students" inverse="true" table="stud_role_ralation" lazy="false">    <key>        <column name="rid" not-null="true" />    </key>    <many-to-many class="com.etc.domain.Student" column="sid"/></set>

name 是实体中拥有的set对象,table是联系表,lazy默认是true,false表示不懒加载。key是本类的主键,节点要从原来的one-to-many修改为many-to-many,class为与哪一个类是多对多的关系,column表示关联的字段。

表结构不存在 hbm2ddl.auto

当数据库中不存在表,而我们又需要写入时,可以使用hbm2ddl.auto,这个需要在hibernate.cfg.xml中进行配置。

<property name="hbm2ddl.auto">update</property>

对应实体类的hbm.xml配置文件和普通的POJO一样,这里给一个最基本的。

<class name="com.etc.domain.Animal" table="animal" catalog="mytest" >    <id name="aid" type="java.lang.Integer">        <column name="rid" />        <generator class="native" />    </id>    <property name="name" type="java.lang.String">        <column name="name" length="20" not-null="true" />    </property></class>

级联增删改

  • 增改
    在many-to-one的对象中的many-to-one节点,设置cascade=”save-update”属性
<many-to-one name="dept" class="com.etc.domain.Dept" fetch="select" cascade="save-update">    <column name="deptNo" precision="2" scale="0" /></many-to-one>
  • 删除
    多的一端,hbm.xml的many-to-one里面设置级联为删除,则在删除的时候,会尝试去删除主表中的信息,这个时候如果从表里有其他数据与主表关联,则会删除失败。
    以员工和部门为例
<many-to-one name="dept" class="com.etc.domain.Dept" fetch="select" cascade="delete">    <column name="deptNo" precision="2" scale="0" /></many-to-one>

在部门的hbm.xml中的配置

<set name="emps" inverse="true" cascade="delete">    <key>        <column name="deptNo" />    </key>    <one-to-many class="com.etc.domain.Emp" /></set>
  • 级联有一点需要注意的是,如果新增的属性是有自增或者序列的,那么在创建对象的时候,就不要设置属性,会出现异常。
  • 关联表的增删改操作,往往需要用到级联属性。

缓存

Hibernate中的缓存分为一级缓存,二级缓存和查询缓存。

0 0
原创粉丝点击