zoukankan      html  css  js  c++  java
  • Hibernate3 第一天

    Hibernate3 第一天

    课程安排:4天(框架的使用+理论知识)

    1. 第一天Hibernate快速入门(单表的CRUD)+核心api讲解(配置+接口)
    2. 第二天:Hibernate一级缓存,快照,对象持久化状态,多表映射操作
    3. 第三天Hibernate各种查询的使用(三种方法),抓取策略优化(lazy加载等)
    4. 第四天 Hibernate二级缓存查询缓存,jpa注解的使用

    内容安排:

    1. Hibernate概述、有什么优缺点等
    2. Hibernate的快速入门:环境搭建、CRUD(增删改查)Create-Read-Update-Delete
    3. Hibernate核心配置常用属性详解
    4. Hibernate核心API常用操作详解
    5. 持久化对象的POOID的说明
    6. hbm映射文件配置常用参数详解

    学习目标:

    快速入门,可应用Hibernate的常见的CRUD进行单表。

    1. Hibernate概述

      1. Hibernate的来历

    关键词:开源框架,属于jboss公司产品,时间久远。可以不写SQL就可以操作数据库。

    (EJB的CMP技术)

    1. 什么是Hibernate

    Hibernate轻量级JavaEE应用的持久层解决方案,是一个关系数据库ORM框架。

    • 轻量级:使用方便(比Apache DbUtils 复杂很多倍)这个概念是个相对概念。(主要是对比EJB来说的,ejb是重量级的(内建组件很多,启动很慢,东西很多))
    • 持久层: JavaEE分为表现层、业务层(service)、持久层(dao)

      Struts2 是一个表现层框架

      Hibernate 是一个持久层框架,底层就是封装了JDBC,也就说,HIbernate最终进入数据库执行的还是sql语句(操作数据库)--ssh

    • 关系数据库: mysql、oracle 都是关系型数据库(主流数据库)

      最近 NOSQL 非关系型数据库开始流行(mongodb、 redis)

    • ORM :Object Relational Mapping对象关系映射(一种思想)

    Java--面向对象的语言

    Mysql--关系型数据库

    将数据表的关系,映射为类和对象之间关系:

    数据库(关系型)

    Java(面向对象的编程语言)

    表的字段

    类的属性

    表的一行数据

    类的一个实例对象

    ORM好处: java程序员只需要在程序中操作对象,因为存在映射关系,自动生成SQL语句。不需要你写sql语句,就可以操作数据库。

    允许你以面向对象的方式直接操作sql数据库。

    Java中常见的ORM实现(持久层框架):

    • JPA( Java Persistence API).Sun公司的规范,JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系(只有接口规范)
    • Hibernate 最流行ORM框架,通过对象-关系映射配置,可以完全脱离底层SQL(全自动)
    • MyBatis 本是apache的一个开源项目 iBatis,支持普通 SQL查询,存储过程和高级映射的优秀持久层框架(半自动)
    • 其他的持久层框架(非ORM):Apache DBUtils 、Spring JDBCTemplate

    【提示】

    Jpa是一种规范,里面只有接口,而Hibernate是它的一种实现,那么hibernate必然去实现了jpa中的接口。

    使用Jpa的一个好处是,可以更换实现而不必改动太多代码。Hibernate框架是基于JPA接口的实现,但又对其做了一些补充,因此,可以理解为Hibernate中有两套规范(JPA和自己的,事实上只是补充)。

    1. Hibernate能做什么

    1. Hibernate的优缺点

    优点:

    • Hibernate完全是ORM,自动生成sql语句,完全不需要懂sql,极大简化jdbc的操作。
    • 具有很强的反射和代理机制,生成代理对象,(可以代码增强)在数据获取可以进行延迟抓取、缓存的使用、达到优化的目的。

    缺点:

    • hibernate完全orm,自动生成sql,所以很多企业用户对hibernate性能存在质疑(任何持久层框架都不如jdbc速度快,开发效率和性能的平衡)。在hibernate框架中,提供了很多优化手段
    1. hibernate的快速入门

    开发工具:MyEclipse 10.0(自带很多插件)

    数据库层面:mysql5

    1. hibernate的jar包下载和导入

      1. 开发包的下载:

    百度搜索:

    本次课程使用3.x系列的经典版本3.6.10

    【版本使用说明】

    框架的版本并不是越高越好,适合的最好;

    不同的版本对jdk环境、不同框架的整合方面都有一定要求。

    Hibernate开发包内容:

    另外:Hibernate开发包中只是提供了一个日志接口的包slf4j,并没有提供日志的具体实现,因此,需要额外下载slf4j和log4j的开发包。

    1. Jar包的导入(开发环境的搭建)

    新建web工程Hibernate3_day01

    导入jar包到工程:共11

    • 核心jar:hibernate3.jar
    • 必须jar:lib equired*
    • jpa规范jar:libjpa---后面会用到jpa的注解。
    • jdbc驱动包:mysql-connector-java-5.0.8-bin.jar—连接数据库
    • 使用日志系统-slf4j系列(slf4j核心+slf4j和log4j的整合包+log4j)—使用静态日志绑定体系+在src中添加log4j的核心配置文件:log4j.properties

    【什么是slf4j】

    slf4j-api只是接口包(解耦合的思想,静态绑定思想),需要日志系统的实现log4j

    • slf4j整合log4j的jar:slf4j-log4j12-1.7.2.jar
    • log4j的开发jar:log4j-1.2.16.jar

    【导入完成的包】共11个

    1

    antlr-2.7.6.jar

    一个语言转换工具,Hibernate利用它实现HQLSQL的转换

    2

    commons-collections-3.1.jar

    Collections Apache的工具集,用来增强Java对集合的处理能力

    3

    dom4j-1.6.1.jar

    Xml解析器

    4

    hibernate3.jar

    核心包

    5

    hibernate-jpa-2.0-api-1.0.1.Final.jar

    Jpa接口开发包

    6

    javassist-3.12.0.GA.jar

    代理类生成工具包

    7

    jta-1.1.jar

    标准的JAVA事务处理接口(跨数据库)

    8

    slf4j-api-1.6.1.jar

    简单日志对象包

    9

    slf4j-log4j12-1.7.2.jar

    连接包

    10

    log4j-1.2.16.jar

    Log4j日志实现包

    11

    mysql-connector-java-5.0.8-bin.jar

    mysql驱动包

    1. 基于hibernate实现数据库表CRUD的操作(重点)

      1. 开发准备和配置(三个准备,7个步骤)

    【第一个准备】 :创建数据库itcast33,准备数据库的表:

    创建数据库:

    在数据库中建表:

    创建一个t_customer表,表中设计如下字段:id(编号)、name(姓名)、age(年龄)、city(城市)

    【第二个准备】:编写实体类和ORM映射文件

    【实体类和映射的编写前需要知道的】

    作为ORM框架,

    Java和数据库之间的对应关系:类(POJO)--表, 属性--字段,对象--记录,

    类就是普通的POJO实体类,必须提供getter和setter方法。

    数据类型之间的对应关系:Java实体类、Hibernate、sql之间有数据类型的对应

    对应关系:

    【编写Customer实体类】

    创建名为cn.itcast.a_quickstart包,在包中创建Customer.java类,类中代码如下:

    【编写hbm(hibernate mapping )映射】:

    习惯上,实体类所在目录,创建类名.hbm.xml数据表映射配置文件.

    作用,让实体和数据库的具体字段和表进行对应映射。

    引入dtd的头信息:

    <!DOCTYPE hibernate-mapping PUBLIC

    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

    配置本地提示:

    【请注意】在配置完catalog之后,需要重新打开Customer.hbm.xml文件

    【编写Customer.hbm.xml映射文件】

    <?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>

        <!-- 配置java类与表之间的对应关系 -->

        <!--

         name:类名:类对应的完整的包路径

         table:表名

         -->

        <class name="cn.itcast.a_quickstart.Customer" table="t_customer">

            <!-- 配置主键

                 name:java类中的属性

                 column:表中的字段,列名,当name和column一致的时候,column可以省略

             -->

            <id name="id" column="id">

                <!-- 主键生成策略

                 mysql的自增长:identity

                -->

                <generator class="identity"></generator>

            </id>

            <!-- 其他属性

                 name:java中的属性

                 column:表中字段名

                 当name和column一致的时候,column可以省略

            -->

            <property name="name" column="name"></property>

            <!-- age :-->

            <property name="age"></property>

            <property name="city"></property>

            

        </class>

    </hibernate-mapping>

    【第三个准备】: 配置Hibernate的灵魂文件

    编写hibernate核心配置.—jdbc连接数据库、配置hbm映射文件等等

    习惯上在src下创建hibernate.cfg.xml(必须叫这个名字,虽然说可以改,但是不建议改)

    引入头部信息:

    <!DOCTYPE hibernate-configuration PUBLIC

        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

    【请注意】需要重新打开hibernate.cfg.xml这个灵魂文件

    在hibernate.cfg.xml文件中进行如下配置:

    <?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>

        <!-- 生产Connection的工厂,hibernate中的session可以直接看成是Connection -->

        <session-factory>

            <!-- 先配置jdbc需要的四个值 -->

            <!-- 驱动 -->

            <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

            <!-- URL -->

            <property name="hibernate.connection.url">jdbc:mysql:///itcast32</property>

            <!-- 用户名 -->

            <property name="hibernate.connection.username">root</property>

            <!-- 密码 -->

            <property name="hibernate.connection.password">123456</property>

            <!-- 要告诉hibernate接下来连接那个数据,主要是告知连接的方言 -->

            <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>

            

            <!-- 上面的5个配置,可以说是hibernate的最低配置 -->

            <property name="hibernate.show_sql">true</property>

            <property name="hibernate.format_sql">true</property>

            <!-- 配置一个自动建表 -->

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

              

            

            <!-- 读取资源文件

             -->

            <mapping resource="cn/itcast/a_quickstart/Customer.hbm.xml"/>

            

        

        </session-factory>

        

    </hibernate-configuration>

    【提示1】方言的位置

    【7个步骤】 : java编程实现CRUD,基本示例如下:

    1. 保存(插入)数据

    在cn.itcast.a_quickstart中创建TestCustomer类,在类中编写testSave方法用来保存数据,代码如下:

    @Test

        public void testSave(){

            //1 加载配置文件:hibernate.cfg.xml

            //Configuration().configure() 会自动加载src路径下的hibernate.cfg.xml

            Configuration cfg = new Configuration().configure();

            //2 创建会话工厂:此处的sessionFactory可以理解成ConnectionFactory

            SessionFactory sessionFactory = cfg.buildSessionFactory();

            //3 创建会话

            //这里的session跟jsp九大内置对象的session不是同一个session

            //这里的session可以理解为connection,session的底层封装了connection

            Session session = sessionFactory.openSession();

            //4 开启事务

            Transaction tran = session.beginTransaction();

            

            //5 进行CRUD操作:保存操作

            Customer customer = new Customer();

            customer.setName("rose");

            customer.setAge(18);

            customer.setCity("上海");

            //执行保存操作:save操作的时候,会自动拼装sql语句

            session.save(customer);

            

            //6 提交事务

            tran.commit();

            //7 关闭连接

            session.close();

            sessionFactory.close();

        }

        

    当看到控制台打印如下语句的时候,表明所有的配置都是正确的

    1. 修改(更新)数据

    更新数据,通过session的update方法

    @Test

        public void testUpdate(){

            //1 加载配置文件

            Configuration cfg = new Configuration().configure();

            //2 创建会话工厂

            SessionFactory sessionFactory = cfg.buildSessionFactory();

            //3 获取会话

            Session session = sessionFactory.openSession();

            //4 开启事务

            Transaction tran = session.beginTransaction();

            //5 执行操作

            Customer customer = new Customer();

            //更新操作必须设置ID属性

            customer.setId(1);

            customer.setName("lucy");

            //在更新需要注意:由于hibernate执行更新操作,更新的是所有的字段

            //所以,不需要更新的字段需要赋值

            customer.setCity("上海");

            customer.setAge(18);

            

            //操作:底层自动拼装update语句

            session.update(customer);

            

            //6 提交事务

            tran.commit();

            //7 关闭连接

            session.close();

            sessionFactory.close();

        }

        

    更新前表中数据:

    更新后表中数据

    【补充】:注意update默认会更新所有字段.

    注意:在使用update的时候,一定要证属性都有值。没值的会被赋默认值

    1. 删除数据    

    删除数据,通过session的delete方法,hibernate的删除是根据主键删除的,所以删除的时候,你必须要传递拥有主键的对象(具有id的对象)

        @Test

        public void testDelete()

        {

            // 1 加载灵魂文件

            Configuration cfg = new Configuration().configure();

            // 2 创建会话工厂

            SessionFactory sessionFactory = cfg.buildSessionFactory();

            // 3 获取连接

            Session session = sessionFactory.openSession();

            // 4 开启事务

            Transaction tran = session.beginTransaction();

            // 5 操作:删除

            Customer customer = new Customer();

            //id必须要设置的

            customer.setId(3);

            //其余属性没必要

            

            session.delete(customer);

            

            // 6 提交事务

            tran.commit();

            // 7 关闭连接

            session.close();

            sessionFactory.close();

        }

          

        

     

     

    【注意】:删除是根据主键删除的,与其他字段没关系

     

    1. 通过主键来查询数据:

    根据主键查找数据,通过session的get或load

        @Test

        public void testQueryById(){

            //1 加载配置文件

            Configuration cfg = new Configuration().configure();

            //2 创建会话工厂

            SessionFactory sessionFactory = cfg.buildSessionFactory();

            //3 获取会话

            Session session = sessionFactory.openSession();

            //4 开启事务

            Transaction tran = session.beginTransaction();

            //5 各种操作

            //get:根据主键查询数据

            //第一个参数:查询的数据封装的类型

    //        Customer customer = (Customer)session.get(Customer.class, 20);

            

            //load:根据主键查询数据,方法参数跟get一致

            Customer customer = (Customer)session.load(Customer.class, 20);

            

            //get和load有区别吗?

            //区别1 :当主键不存在的时候,get方法直接返回null,load报错

            //

            

            System.out.println(customer);

            //6 提交事务

            tran.commit();

            //7 关闭事务

            session.close();

            sessionFactory.close();

            

            

        }

    【注意点1 】当使用junit单元测试的时候,如果不加@Test,报错:

    当load获取的时候,主键不存在,报错

     

     

    1. 查询所有数据

    使用hibernate提供的Query接口来完成,使用HQL(hibernate Query language)语句完成

    Hibernate提供了两种查询所有数据的方式

    【提示】为了方便学习,建议大家关联源码,关联方式:

    采用两种方式查询所有:

        @Test

        public void testQueryAll(){

            //1 加载配置文件

            Configuration cfg = new Configuration().configure();

            //2 创建会话工厂

            SessionFactory sessionFactory = cfg.buildSessionFactory();

            //3 获取会话

            Session session = sessionFactory.openSession();

            //4 开启事务

            Transaction tran = session.beginTransaction();

            //5 各种操作

            //方式一:采用HQL语句查询所有?何为HQL,类似sql

            //但是在HQL中缩写的内容要么是类,要么是类中的属性,不能写跟表相关的内容

            //HQL是面向对象的查询语句

    //        Query query = session.createQuery("from Customer");

            //查询

    //        List<Customer> list = query.list();

            

            //方式二:继续采用SQL查询所有

            SQLQuery sqlQuery = session.createSQLQuery("select * from t_customer");

            //sqlQuery查出来的对象是数组类型,所以要转成Customer对象

            //在使用SQLQuery查询之前,一定先封装实体(addEntity),查询

            List list = sqlQuery.addEntity(Customer.class).list();

            

            System.out.println(list);

            //6 提交事务

            tran.commit();

            //7 关闭事务

            session.close();

            sessionFactory.close();

            

        }

        

    hql执行的查询语句

    Sql执行的查询语句

    1. hibernate的基本运行流程分析(图解)

    1. hibernate核心配置(灵魂配置)

      1. Hibernate的基本体系结构

    通过上面的体系结构图得知:应用程序通过持久化对象(po)间接的完成对数据库的操作(crud)。

    持久化对象(persistent object),简写为PO,这里就是Customer这个实体类

    1. Hibernate核心配置文件分类

    Hibernate框架支持properties和xml两种方式的Hibernate属性的配置,对应的两种核心配置文件为:

    • hibernate.properties 配置常用的属性,必须手动编程加载hbm文件或持久化类。(了解)
    • hibernate.cfg.xml配置常用的属性,配置加载hbm映射,配置缓存策略等。(推荐)

    【提示】配置Hibernate的属性很多,可以参考hibernate解压包的project/etc/hibernate.properties。

    1. hibernate.properties配置示例(了解)

    目的:通过properties格式配置最小化的hibernate属性(连接和方言)

    【配置示例】

    hibernate.properties:

    hibernate.connection.driver_class com.mysql.jdnc.Driver

    hibernate.connection.url jdbc:mysql:///itcast32

    hibernate.connection.username root

    hibernate.connection.password 123456

    hibernate.dialect org.hibernate.dialect.MySQL5Dialect

    提示:上述配置是Hibernate的最小化配置。

    【注意】使用hibernate.properties文件的时候,一定知道,这种配置的缺陷是:无法动态的加载*.hbm.xml文件

    ,正是因为这种缺陷,导致了这种配置方式基本不用,那如果使用这种方式,未来项目中,如何加载配置文件呢?

    答:在代码中手动的加载

    【示例测试】

    建立包:cn.itcast.b_corecfg,创建类TestCoreCFG类

    @Test

        public void testCoreConfigProperties(){

            //1 加载配置文件

            //读取hibernate.properties属性文件

            Configuration cfg = new Configuration();

            //手动读取hbm文件

            cfg.addResource("cn/itcast/a_quickstart/Customer.hbm.xml");

            //2 创建会话工厂

            SessionFactory sessionFactory = cfg.buildSessionFactory();

            //3 获取会话

            Session session = sessionFactory.openSession();

            //4 开启事务

            Transaction tran = session.beginTransaction();

            //5 各种操作

            List<Customer> list = session.createQuery("from Customer").list();    

            

            System.out.println(list);

            //6 提交事务

            tran.commit();

            //7 关闭事务

            session.close();

            sessionFactory.close();

            

            

        }

    打印结果,由于没有配置show_sql,所以说,不会显示sql语句

    【提示】

    properties方式,主要缺点:不能配置加载hbm映射文件,因此后面都使用xml的方式进行配置和使用。

    【思考】

    如果两个配置文件都存在,hibernate会加载使用哪个?

    new Configuration();只加载properties文件

    Configuration configuration = new Configuration().configure();会先加载properties文件,再加载cfg.xml文件。

    后加载的会覆盖先加载的属性的值

    1. hibernate.cfg.xml配置示例+框架常用属性

      1. 数据库连接参数和数据库方言

    connection连接参数、数据库方言的配置

    方言是用来将实体类的映射转换为不同数据库的sql的一种配置策略.

    操作hibernate必须配置的5个属性—最小化配置

    方言:

    【了解】

    在配置hibernate.cfg.xml时,hibernate属性:hibernate.dialect中的"hibernate"可以省略.

    1. 连接池

    • Hibernate默认连接池(了解):

    默认情况下,我们有没有用连接池呢?

    查看日志发现Hibernate使用了一个默认的连接池:

    【提示】

    不建议在生产环境使用。

    默认的连接池大小配置(了解):

    • c3p0连接池的配置:---生产环境

    更换C3P0的连接池:

    【第一步】解压目录的lib中

    导入c3p0的jar

    【第二步】配置更改连接池提供者(插件机制,可以随时换,可插拔)

    切换到c3p0连接池:

    准备代码:

            @Test

        public void testConnectionPool(){

            //1 加载配置文件

            Configuration cfg = new Configuration().configure();

            //2 创建会话工厂

            SessionFactory sessionFactory = cfg.buildSessionFactory();

            //3 获取连接

            Session session = sessionFactory.openSession();

            //4 开启事务

            Transaction transaction = session.beginTransaction();

            //5 查询所有操作

            Query query = session.createQuery("from Customer");

            List<Customer> list = query.list();

            

            System.out.println(list);

            //6 提交事务

            transaction.commit();

            //7 关闭连接

            session.close();

            sessionFactory.close();

        }

        

    再次执行查看结果:

    【报错的原因】没有导入C3P0的jar包:直接去项目中找项目导入

    Jar包的位置

    在运行,就OK啦

    1. 控制台打印sql

    建议在测试环境下,都将sql打开,便于调试。

    1. 自动建表

    该配置是让hibernate是否自动建立相应的表。也就是说,你数据库可以没有表,hibernate可以自动给你建立一个。

    • create-drop:在程序运行时,(创建session工厂的时候)会自动建表,在程序停止时(关闭sessionFactory时),表删除

     

    如何证明 create-drop的功能?

    直接使用debug的方式,断点查看

     

    第一步,观察表是否创建成功(标志:原先的表被删除,数据丢失)

    第二步,实现保存操作,观察表中是否有数据

    第三步,程序运行结束,观察表是否被删除

     

    编写建表的代码:

    @Test

        public void testSave(){

            //1 加载配置文件

            Configuration cfg = new Configuration().configure();

            //2 创建会话工厂

            SessionFactory sessionFactory = cfg.buildSessionFactory();

            //3 获取连接: 此处的session可以看成是Connection

            Session session = sessionFactory.openSession();

            //4 开启事务

            Transaction transaction = session.beginTransaction();

            //5 各种CRUD 操作

            //准备数据

            Customer customer = new Customer();

            customer.setName("jack");

            customer.setAge(18);

            customer.setCity("北京");

            //保存

            session.save(customer);

            

            //6 提交事务

            transaction.commit();

            //7 关闭连接

            session.close();

            sessionFactory.close();

            

        }

    • create:在程序运行时,如果表不存在,自动建表,如果存在,则不进行任何操作(mysql会先删除再创建)

     

     

    • update:在程序运行时,如果表不存在,自动建表,如果表存在,则检查表和类的结构是否一致,如果不一致,则更新表结构.(推荐)

    update:

    如果当表中的字段比配置文件多的时候,那么表中多的字段,hibernate将无法维护。(因为没有映射)

    • validate:在程序运行时,如果表结构和类不一致,则报错!(表中没有相应的字段,则报错,表中有冗余字段,不报错)

     

    【自动建表小结】

     

     

     

    1. jdbc的其他相关设置(不建议使用,了解)

    我们发现,在使用hibernate的时候,都需要手动提交事务,原因是hibernate默认是关闭自动提交事务功能的。

    所以我们需要手动提交。

    如果开启自动提交功能呢,当前,也只有mysql支持,oracle不支持

    @Test

        public void testSave(){

            //1 加载配置文件

            Configuration cfg = new Configuration().configure();

            //2 创建会话工厂

            SessionFactory sessionFactory = cfg.buildSessionFactory();

            //3 获取回话

            Session session = sessionFactory.openSession();

            //4 开启事务

    //        Transaction tran = session.beginTransaction();

            //5 操作

            Customer customer = new Customer();

            customer.setName("rose");

            customer.setAge(19);

            customer.setCity("北京");

            session.save(customer);

            

            //6 提交事务

    //        tran.commit();

            //7 关闭连接

            session.close();

            sessionFactory.close();

            

            

        }

    【扩展1】

    没有提交的事务,数据库中这条记录是否存在?(默认情况下,是不会存在的,数据只是暂时的在内存中存在过)

    【扩展2】

    oracle默认是false,即使改成true也没效果!

    原因:Oracle不支持自动提交事务,不能自动提交(Oracle数据库必须手动提交,即使你在hibernate中设置了自动提交,也没用。),但msyql是可以自动提交的.

    1. Hibernate核心API

      1. 概述

    通过该体系结构图,可以看到Hibernate的核心API接口,它们之间的关系:

    Hibernate通过Configuration来读取核心配置文件,SessionFactory缓存配置并用来提供Session,而Session是用来操作PO的,让Hibernate生成crud的sql语句,在操作过程中使用Transaction来管理事务。Query和Criteria是Hibernate提供的查询的两种不同方式。

    下面详解:

    1. Configuration配置对象

    作用:加载Hibernate的相关配置(Hibernate.cfg.xml配置Hibernate.properties配置[不支持配置hbm映射])

    第一种:加载Hibernate.cfg.xml

    Configuration configuration=new Configuration().configure();//加载默认src类路径的hibernate.cfg.xml

    也可以加载其他命名的配置文件new Configuration().configure("文件路径");

    第二种:加载Hibernate.properties

    Configuration configuration=new Configuration();//加载默认的Hibernate.properties

    需要手动加载hbm映射

    configuration.addResource(xxx.hbm.xml)

    1. SessionFactory会话工厂

    作用:用来缓存一些配置和数据的

    可以缓存:

    • Hibernate常用属性(包括连接池)
    • hbm类和数据表映射信息
    • 预定义SQL语句(命名查询语句)
    • 二级缓存

    获取SessionFactory的方式: configuration.buildSessionFacotry()获取对象

    【提示】

    SessionFactory是线程安全的.当多个线程访问同一个SessionFactory是不存在线程问题的。构造SessionFactory很消耗资源,一般情况下,一个应用(项目)只需要初始化一个,即操作一个全局的SessionFactory对象。

    SessionFactory和Session的关系有点像连接池和连接的关系,我们操作数据库时主要使用的连接和session,连接池和SessionFactory只需要创建后从里面获取连接和session即可。

    【练习示例】

    创建包:cn.itcast.utils,抽取HibernateUtils来提供Session对象。

    package cn.itcast.utils;

    import org.hibernate.Session;

    import org.hibernate.SessionFactory;

    import org.hibernate.cfg.Configuration;

    public class HibernateUtils {

        

        private static SessionFactory sessionFactory;

        

        //在静态块中加载SessionFactory

        static{

            sessionFactory = new Configuration().configure().buildSessionFactory();

            //关闭:系统停止运行的时候关闭

            //JVM停止运行的时候:为JVM增加一个监听事件, 在JVM停止工作的时候,关闭sessionFactory

            //addShutdownHook:添加关闭事件

            Runtime.getRuntime().addShutdownHook(new Thread(){

                @Override

                public void run() {

                    sessionFactory.close();

                    System.out.println("sessionFactory关闭了....");

                }

            });

        }

        //获得连接工厂

        public static SessionFactory getSessionFactory(){

            return sessionFactory;

        }

        //获得连接

        public static Session openSession(){

            return sessionFactory.openSession();

        }

        

    }

    创建包:cn.itcast.c_coreapi,在包中创建类:TestCoreAPI,使用工具类来进行操作数据(查询示例):

    @Test

        public void createTable(){

            //自动建表

            HibernateUtils.getSessionFactory();

        }

        @Test

        public void testSave(){

            //1 获取连接

            Session session = HibernateUtils.openSession();

            //2 开启事务

            Transaction tran = session.beginTransaction();

            //3 操作

            Customer customer = new Customer();

            customer.setName("jack");

            customer.setAge(11);

            customer.setCity("上海");

            

            session.save(customer);

            

            //4 提交事务

            tran.commit();

            //5 关闭连接

            session.close();

            

        }

    1. session会话

    Session是hibernate的核心,所有的CRUD操作都要依靠session完成。

    作用:Session对象相当于JDBC的Connection,主要用于操作数据的(CRUD)。
    但是功能比Connection强大NN倍,

    Session底层封装了Connection

    • 其内部实际上也确实持久化了一个Connection对象,即一个Session对应一个Connection。
    • Session是应用程序与数据库之间交互操作的一个单线程对象,是Hibernate的运作中心
    • Session是线程不安全的。

     

    【问题】:如何解决Session线程不安全问题?

    不要将Session定义为成员变量,只在方法中的局部变量使用.

    (方法内部为什么不存在线程安全问题:用的私有栈空间,不存在线程共享问题)

    (每个方法使用栈空间是线程私有空间,不存在多线程并发问题)

     

     

    另外:Session还提供了一级缓存和持久化对象的常用操作。

    Session的常用方法:

    1. Transaction事务

    hibernate默认不自动提交事务,而且oracle数据库也不支持自动提交事务,所以我们推荐,每次都手动提交事务

    作用:用于事务的管理(提交或回滚等)

    常用操作:

     

    代码中操作事务:

    @Test

        public void testTransaction(){

            //获取连接

            Session session = HibernateUtils.openSession();

            //开启事务

    //        Transaction transaction = session.beginTransaction();

            //开启事务的方式二

            session.beginTransaction();

            

            Customer customer = new Customer();

            customer.setName("rose");

            customer.setAge(18);

            customer.setCity("上海");

            session.save(customer);

            

    //         /提交事务

    //        transaction.commit();

            session.getTransaction().commit();

            

            //关闭连接

            session.close();

        }

     

    【注意】

    如果没有手动开启事务,那么每个Session的操作,都相当于一个独立的事务。

    默认情况下,JDBC的事务是自动提交的,而hibernate的事务默认是不提交的,但可以通过配置的值为true,变成自动事务提交(mysql)

    【思考题面试题】:

    问题:数据库中保存了几条数据?

    【分析】运行结果是啥?

    分情况讨论

    如果是oracle,0条

    如果是mysql,开启了自动事务提交,1条

    没有开启自动事务提交,0条

     

     

    1. Query查询接口

    作用:Query对象主要用于执行查询操作。

     

    Query接口用于接收HQl语句执行查询。

    HQL是Hibernate Query Language缩写,语法很SQL语法,但是完全面向对象的。

    Hibernate为照顾和兼容原生的SQL,Hibernate提供了Query的子接口SqlQuery来执行SQL语句,用法和query接口的一样。

     

    【示例】

    通过Query接口执行查询,分别查询所有数据列表、根据条件查询一条数据(分别不使用和使用占位符)、...

    @Test

        public void testQuery(){

            Session session = HibernateUtils.openSession();

            session.beginTransaction();

            

            //HQL

    //        /方式一

    //        List<Customer> list = session.createQuery("from Customer").list();

            //方式二:返回的结果是多条的时候,使用list

    //        List<Customer> list = session.createQuery("select c from Customer c ").list();

            //根据条件查询 方式一(查询name=rose的客户的信息),返回的结果是0条或者1条用uniqueResult

    //        Customer customer = (Customer) session.createQuery("from Customer where name = 'rose'").uniqueResult();

            //根据条件查询 方式二:匿名的方式注入参数

    //        Customer customer = (Customer) session.createQuery("from Customer where name = ?")

    //                    .setString(0, "rose").uniqueResult();

            

            //根据条件查询 方式三:匿名的方式注入参数

    //        Customer customer = (Customer) session.createQuery("from Customer where name = ?")

    //                    .setParameter(0, "rose").uniqueResult();

            

    //        /根据条件你查下方式四:命名的方式注入参数

    //        Customer customer = (Customer) session.createQuery("from Customer where name = :name")

    //                    .setString("name", "rose").uniqueResult();

            

            //根据条件查询方式五

    //        Customer customer = (Customer) session.createQuery("from Customer where name = :name")

    //                .setParameter("name", "rose").uniqueResult();

              

            

            

    //        System.out.println(list);

            //SQL

            //条件查询方式一

    //        Customer customer = (Customer) session.createSQLQuery("select * from t_customer where name = ?")

    //                    .addEntity(Customer.class).setString(0, "rose").uniqueResult();

            

            //方式二

    //        Customer customer = (Customer) session.createSQLQuery("select * from t_customer where name = ?")

    //                .addEntity(Customer.class).setParameter(0, "rose").uniqueResult();

            

            //方式三:命名的方式

    //        Customer customer = (Customer) session.createSQLQuery("select * from t_customer where name = :name")

    //                .addEntity(Customer.class).setString("name", "rose").uniqueResult();

            

            //方式四:命名的方式

            Customer customer = (Customer) session.createSQLQuery("select * from t_customer where name = :name")

                    .addEntity(Customer.class).setParameter("name", "rose").uniqueResult();

            

            System.out.println(customer);

            session.getTransaction().commit();

            session.close();

        }

        

     

    【注意】

    SqlQuery.list 默认是生成的object[],必须使用绑定实体后,Hibernate才会将结果自动封装成实体对象。

     

    1. Criteria查询接口

    作用:用于执行查询操作。

    特点:完全面向对象的方式添加条件,不需要写hql或sql

     

    【示例】

    使用Criteria接口对象执行查询,分别查询所有数据列表、根据条件查询一条数据

    @Test

        public void testCriteria(){

            Session session = HibernateUtils.openSession();

            session.beginTransaction();

            //创建Criteria

    //        Criteria criteria = session.createCriteria(Customer.class);

    //        /查询所有

    //        List list = criteria.list();

            

    //        System.out.println(list);

            

            //根据条件查询rose

            Criteria criteria = session.createCriteria(Customer.class);

            //加条件

            criteria.add(Restrictions.eq("name", "rose"));

            //拼命的加条件

            criteria.add(Restrictions.eq("age", 19));

            

            //...

            

            //执行

            Customer customer = (Customer) criteria.uniqueResult();

            

            System.out.println(customer);

            

            session.getTransaction().commit();

            session.close();

            

        }

     

    1. 小结回顾

    常用的Hibernate核心API:

    • Configuration核心配置对象,用来加载Hibernate配置文件,用来创建会话工厂
    • SessionFactory 会话工厂,用来创建Hibernate会话Session,缓存了一些配置。(连接池等)
    • Session 会话,相当于一个数据库连接(持久了一个connection),进行数据库表CRUD操作,它是hibernate的运作核心。
    • Transaction 事务,对操作进行事务管理
    • Query查询接口,接收HQL语句,子接口SQLQuery接受sql语句
    • Criteria条件查询(QBC:query by Criteria),完全面向对象方式进行多条件的组合查询,无需关心sql的写法。
    1. 持久化对象PO

      1. 理解持久化对象PO

        1. PO对象是什么

    全称:Persistent Object。

    持久化(Persistence),即把数据(内存中的对象)保存到可永久保存的存储设备中(如硬盘)。

    持久化的主要应用是将内存中的对象存储在关系型的数据库中,当然也可以存储在磁盘文件中、XML数据文件中等等

     

    1. 编写规则

    POJO:一个干净(没有父类、没有接口的一个类)的类

    在hibernate中pojo=po(其他技术中有没有po这个概念?没有。Po是持久化对象,而hibernate正好是持久化的框架)

    PO类的要求如下

    • 必须提供一个public的空参构造器
    • 提供一个标识,与数据库的主键进行对应
    • 所有的私有属性都要有getter和setter方法
    • 属性的类型建议使用包装类型
    • PO类一定不能使用final关键字修饰(如果使用final修饰,将无法生成代理子对象)

     

     

    【示例】修改Customer类,具体代码如下:

     

    1. 属性类型的问题讨论

    问题思考:PO属性的数据类型使用包装类型还是原始类型?

    【场景模拟阅读】

    学生考试:

     

    全部答错

     

    根本就没来

     

    double:全部答错0分,没来 0分 , 87分

     

    Double:全部答错 0分,没来null , 87分

     

     

     

     

    //需求:有个表存放学生的分数,假如有个人没有参加考试,那么这个分数的字段该是什么值?这个字段值是0还是null合适呢?看需求,但用null更合适.

    基本数据类型double默认值是0Double默认值是null

        //如果说,使用基本的数据类型,,无法通过这个字段来区分是否参加了考试(假如有个人考试了但是0.)。假如要求参加考试的学生必须有分数,而没参加考试的学生没有分数(为空),double无法识别了一般我们都用包装类

    尽量使用包装类型,不要使用原始类型

     

     

    1. 理解OID-持久化对象的唯一标识

      1. 什么是OID

    OID(Object identifier),即对象标识,用来标识唯一对象的,因此,OID在PO中被称为持久化对象的唯一标识。

    Hibernate框架根据OID标识,是否为同一个对象

    【扩展知识】

    在关系数据库中,主键用来识别记录,并保证每条记录的唯一性。数据库中的主键最重要的3个基本要素就是不允许为null,不允许有重复值,主键永远不会改变. 所以通常我们设计表都会设计主键的值为自动增加,没有业务逻辑含义的一组数字,当然针对每个数据库,设置的方法也不同.

    在Java语言中,通过比较两个变量所引用对象的内存地址是否相同,或者比较两变量引用的对象是否相等。

    Hibernate为了解决两者之间的不同,使用对象标识符(OID)来标识对象的唯一性。OID是关系数据库中主键在Java对象模型中的等价物在运行时,Hibernate根据OID来维持Java对象和数据库中的对应关系。

    OID具有唯一性和不变性,一般是由HIBERNATE或数据库来给其赋值。

    OID(Object ID)应当没有任何业务相关含义,OID绝对不应当具有任何业务含义。

     

        /**

         * 什么样的对象,hibernate在操作的时候,会认为是同一个对象?

         */

        public void testSame()

        {

            //java中如何判断是否是同一个对象的?

            //判断hashcode

            Customer customer = new Customer();

            customer.setId(1);

            customer.setName("rose");

            customer.setAge(18);

            customer.setCity("北京");

              

            

            Customer customer1 = new Customer();

            customer1.setId(1);//OID

            customer1.setName("rose1");

            customer1.setAge(181);

            customer1.setCity("北京1");

            

            //在java中,customer和customer1肯定不是同一个对象

            //但是在Hibernate比较同一个对象的方式,是比较OID,如果OID一致,他们就是同一个对象

             //Customer 和Customer1的OID一致吗?一致,所以hibernate会把这两个对象认为是同一个对象        

        }

     

    【总结】

    Po:持久化对象,在本章中,就是指Customer

    Po的唯一标识叫OID

     

    1. hbm映射配置详解

      1. 准备工作(全天复习)

    目标:建立PO和hbm映射(加入到核心配置文件中)、建表(构建工厂)。

     

    创建包:cn.itcast.d_hbm,在包中创建Book的po类:

     

    建立hbm映射文件:

    填充基本的映射文件内容:

    <?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>

        <!-- 配置java类和表之间的映射

         name:java类对饮的完整的包路径

         table:表名

         -->

        <class name="cn.itcast.d_hbm.Book" table="t_book">

            <!-- 主键 -->

            <id name="id">

                <!-- 主键生成策略 -->

                <generator class="identity"></generator>

            </id>

            

            <!-- 配置其他属性 -->

            <property name="name"></property>

            <property name="price"></property>

            

        </class>

     

    </hibernate-mapping>

     

     

    加载hbm映射

    <!-- 配置hbm映射文件 -->

    <mapping resource="cn/itcast/d_hbm/Book.hbm.xml"/>

     

     

    建表测试:

    @Test

        //建表测试

        publicvoid createTabele(){

            HibernateUtils.getSessionFactory();    

            

        }

     

    1. class元素

    作用:指定类与表的映射,用来映射表的一些属性。

    • name:类名
    • table:表名,table也是可以省略的,如果省略,那么表名就是类名

     

    1. id元素

    作用:主键策略,设定持久化类的OID和表主键的映射

    属性:

    • name: 标识持久化类 OID 的属性名
    • column: 设置标识属性所映射的数据列的列名(主键字段的名字),可省略。
    • length:字段的长度,如果不指定,则使用数据库字段的默认最大长度。
    • type:指定Hibernate 映射类型. Hibernate 映射类型是 Java 类型与 SQL 类型的桥梁。如果没有为某个属性显式设定映射类型, Hibernate 会运用反射机制先识别出持久化类的特定属性的 Java 类型, 然后自动使用与之对应的默认的 Hibernate 映射类型。

      Java 的基本数据类型和包装类型对应相同的 Hibernate 映射类型. 基本数据类型无法表达 null, 所以对于持久化类的 OID 推荐使用包装类型(integer,long,string等)。

     

    1. generator元素

    作用:持久化类的标识符(OID)生成器,用来指定主键的生成策略。

     

    【主键策略知识】

    数据库主键分为两种:代理主键和自然主键

    两者的区别为:

    • 代理主键:不具有业务含义的字段作为主键的,---例如自增长id,uuid(在数据库的设计中,推荐使用不具备实际意义的字段作为主键)
    • 自然主键:具有业务含义的字段作为主键的, ---例如订单编号(有些表的设计中订单编号是有规则.2015011002998)

    两者的选择:企业开发中,会根据业务需要选择,但使用代理主键居多

    属性:

    • class属性:指定使用的标识符生成器全限定类名或其缩写名。

     

    选择:最常用的:native,其次:identity,sequence,uuid,assigned

     

    1. native

    标识符生成器依据底层数据库对自动生成标识符的支持能力, 底层其实自动选择使用 identity, sequence 标识符生成器.

     

    如果数据库 mysql ---- identity

    如果数据库 oracle ---- sequence (hibernate 会自动创建一个新的序列

    1. Identity--mysql演示

    切换到mysql: (前提是要导入mysql的jar包)

    依赖数据表底层自增长,用于DB2, Mysql, MSSQLServer, Sybase

    创建表后,生成的sql:

    【主键的生成时机】

    在mysql中:在保存的时候,直接发出sql语句,抢占主键,而此时数据库中是没有数据的,

    如果没有提交呢,这个主键直接废弃,接下来如果继续完成保存操作并提交,这时候,主键已经越过了

     

    在oracle中:由于oracle的主键生成策略跟mysql不一样,所以,oracle在commit的时候,才会发出sql语句,去抢占主键

     

    【示例代码】

        @Test

        public void testSave(){

            Session session = HibernateUtils.openSession();

            session.beginTransaction();

            

            Book book = new Book();

            book.setName("九阴真经");

            book.setPrice(9.9d);

            //对于mysql而言,在save的时候就会发出sql语句,抢占主键

            //而oracle,只有当commit的时候,才会发出sql语句 (两个数据库生成主键的方式不同 )

            session.save(book);

            

            session.getTransaction().commit();

            session.close();

            

        }

     

     

     

    插入数据:

     

    采取抢占的形式进行id的赋值。测试.

     

    1. sequence—oracle演示

    依赖于底层数据库,采用序列的机制,主要用于oracle,db2等。

    可以指定自定义的序列;

    修改策略:

    测试:

    结果:

    经过测试,使用了自定义的序列。

     

    如果不指定自定义的序列,会使用默认的序列(hibernate生成)

     

     

    1. Increment(不科学)

    由hibernate来生成OID和维护的,原理是select max(id) +1

     

    如果数据表中没有数据,则初始的时候,hibernate给值是1,再次给值2

     

    表主键没有自增长了

     

    问题:如果手动将主键值改大了,会如何?

    再次保存会:

    适用场景:

    问题:可能出现多线程冲突问题两个线程同时查询max(id),同时+1 ,insert

     

     

    1. uuid

    用于String类型,生成代理主键,采用uuid (32位)作为主键值

    Hibernate会产生不重复的32位字符串作为主键

    【示例】

    增加一个string类型的字段:

    更改映射的主键:

    1. Assigned

    唯一的一个自然主键设置方式,手动设置主键的值

     

    如果不指定会出现:

    错误如下:

    指定示例:

     

     

    1. 小结

     

     

    1. property元素

    作用:配置非主键的属性和表字段的映射

    属性(和id元素一样):

    • name:类中属性名
    • column: 设置标识属性所映射的数据列的列名(字段的名字),可省略。
    • length:字段的长度,如果不指定,则使用数据库字段的默认最大长度。
    • type:指定Hibernate 映射类型. Hibernate 映射类型是 Java 类型与 SQL 类型的桥梁。如果没有为某个属性显式设定映射类型, Hibernate 会运用反射机制先识别出持久化类的特定属性的 Java 类型, 然后自动使用与之对应的默认的 Hibernate 映射类型。

      Java 的基本数据类型和包装类型对应相同的 Hibernate 映射类型. 基本数据类型无法表达 null, 所以对于持久化类的 OID 推荐使用包装类型(integer,long,string等)。

        另外了解:也可以使用子元素<column>来配置数据库中的字段类型(不推荐):

    【扩展】type:字段类型(是hibernate类型):扩展(如果想写数据库类型,则需要子元素column:)

    1. 其他:jee6的bean校验错误

    如果你用myeclise2014版本的时候,默认建立的web工程是用jee6,那么在运行hibernate的时候,会报错:bean校验的错误.

    解决方案:在核心配置文件(hibernate.cfg.xml)中,配置一个阻止bean校验的一个属性

    <!-- 阻止bean校验 -->

    <property name="javax.persistence.validation.mode">none</property>

     

     

     

    1. 小结+重点

     

    1. 作业

     

    【作业一】

    课程中的快速入门,即基本的增删改查(CRUD),写一遍。

     

    【作业二】

    编写一个登录小程序。

    业务逻辑:页面输入用户名和密码,在后台验证,是否在数据库中存在,如果存在,则提示登录成功,否则,提示登录失败。

    技术要点:将以前的练习中的dbutil改造为Hibernate,用Hibernate作为dao(持久层)的技术与数据库打交道。

    参考步骤:

    1. 搭建环境:导入hb的jar,核心配置文件,工具类
    2. 编写PO和hbm映射
    3. 直接在程序中编写crud

     

     

  • 相关阅读:
    kuangbin带你飞 并查集 题解
    kuangbin带你飞 最短路 题解
    kuangbin带你飞 后缀数组 题解
    Kuangbin 带你飞-线段树专题 题解
    HDU 4578 Transformation
    Tarjan & LCA 套题题目题解
    Dancing Links [Kuangbin带你飞] 模版及题解
    二分匹配 大白例题虽有代码
    编程范式:响应式编程
    编程结构:Promise和Future
  • 原文地址:https://www.cnblogs.com/beyondcj/p/6271013.html
Copyright © 2011-2022 走看看