Hibernate

Hibernate框架介绍

什么是orm
Object Relational Mapping(对象关系映射),指的是将一个Java中的对象与关系型数据库的表建立一种映射关系,从而操作对象就可以操作数据库中的表

什么是POJO
Plain Ordinary Java Object(简单普通的java对象,主要用来指代那些没有遵循特定的java对象模型,约定或者框架的对象
有一些private的参数作为对象的属性,然后针对每一个参数定义get和set方法访问的接口。
没有从任何类继承、也没有实现任何接口,更没有被其它框架侵入的java对象
实际上就是一个简单的JavaBean

什么是Hibernate
Hibernate是一个开放源代码的对象关系映射框架,对JDBC进行了轻量级的对象封装,将POJO与数据库表建立映射关系,是一个全自动的持久层的orm框架;hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用

Hibernate的好处
① 对JDBC访问数据库的代码进行了轻量级封装,简化了数据访问层繁琐重复性的代码,减少了内存消耗,加快了运行效率;
② 是一个基本JDBC的主流持久化框架,很大程度上简化了DAO层的编码工作
③ 性能非常好, 映射灵活性比较好,支持多关系数据库,一对一,一对多,多对多的各种复杂关系
④ 可扩展性强,源代码及API开放,当本身功能不够用时,可以自行编码进行扩展

Hibernate的基本使用

  1. 下载框架:
    Hibernate官网

对下载下来的Hibernate介绍
在这里插入图片描述
解压后的文件包括以上内容,其中

  • document是Hibernate开发的文档

  • lib中包括① Hibernate的开放文档
    ② Hibernate开发所必须的依赖包(required)
    ③ Hibernate开发可选的jar包(optional)

  • project是Hibernate提供的参考项目

  1. 创建Java Web项目并且引入相应的jar包(hibernate必须的jar包和数据库的驱动包)(也可以是java项目)
    我用到的所有jar包(包括c3p0连接池和log4j)
    在这里插入图片描述

  2. 创建数据库创建表
    在这里插入图片描述

  3. 创建一个orm类,就是创建一个POJO类,属性设为私有对应要操作的数据库表的属性,并提供相应的get和set方法

public class Customer {
    private int cust_id;
    private String cust_name;
    private String cust_phone;
    

    public int getCust_id() {
        return cust_id;
    }

    public void setCust_id(int cust_id) {
        this.cust_id = cust_id;
    }

    public String getCust_name() {
        return cust_name;
    }

    public void setCust_name(String cust_name) {
        this.cust_name = cust_name;
    }

    public String getCust_phone() {
        return cust_phone;
    }

    public void setCust_phone(String cust_phone) {
        this.cust_phone = cust_phone;
    }
    //非必须只是方便打印
    @Override
    public String toString() {
        return "Customer{" +
                "cust_id=" + cust_id +
                ", cust_name='" + cust_name + '\'' +
                ", cust_phone='" + cust_phone + '\'' +
                '}';
    }
}

  1. 创建Hibernate核心配置文件(一般叫做hibernate.cfg.xml)
    可以在hibernate目录的project里面的ect项目中找到该配置文件来参考进行配置
<?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>
    <session-factory>
        <!-- 连接数据库的基本参数 -->
        <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/xtom?useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=GMT%2B8&amp;useSSL=false</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password"></property>
        <!-- 配置方言 -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
        <!-- 打印SQL语句 -->
        <property name="hibernate.show_sql">true</property>
        <!-- 自动建表 -->
        <property name="hibernate.hbm2ddl.auto">update</property>
        <!-- 建立映射 --> 
        <mapping resource="domain/Customer.hbm.xml"></mapping>
    </session-factory>
</hibernate-configuration>

注意:url后边不这样写可能会报一堆错误

  1. 创建映射关系
    ① 通过XML的配置文件来进行类到数据库中表的映射配置,一般叫做类名.hbm.xml
    ② 配置文件中先建立表与类的映射;
    ③ 再建立主键的映射;
    ④ 再建立普通属性的映射
    注意:可能会有can not resolve table的错,但不影响运行
<?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>
    <class name="domain.Customer" table="customer">
        <id name="cust_id" column="cust_id">
            <generator class="native"></generator>
        </id>
        <property name="cust_name" column="cust_name"></property>
        <property name="cust_phone" column="cust_phone"></property>
    </class>
</hibernate-mapping>
  1. 创建测试类进行测试(保存操作)
package test;

import domain.Customer;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class Test {
    public static void main(String[] args) {
        //加载hibernate的核心配置文件
        Configuration configure = new Configuration().configure();
        //创建SessionFactory
        SessionFactory sessionFactory = configure.buildSessionFactory();
        //通过SessionFactory获取Session对象
        Session session = sessionFactory.openSession();
        Customer customer = new Customer();
        customer.setCust_name("yzx");
        customer.setCust_phone("123");
        session.save(customer);
        //释放资源
        session.close();
        sessionFactory.close();
    }
}

Hibernate的工作流程(保存)

在这里插入图片描述
① 通过Configuration对象加载配置文件
configuration对象详解:
是启动Hibernate所遇到的第一个对象,对Hibernate进行配置,以及对他进行启动
也就是说在Hibernate 的启动过程中,Configuration 类的实例首先定位映射文档的位置,加载核心配置文件(hibernate.cfg.xml)和类的映射文件,读取这些配置

//加载hibernate的核心配置文件
Configuration configure = new Configuration().configure();

②通过Configuration实例对象创建SessionFactory
SessionFactory详解:
SessionFactory接口:SessionFactory接口负责初始化Hibernate;并负责创建Session对象(连接对象)。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。SessionFactory内部维护了Hibernate的连接池和Hibernate的二级缓存

//创建SessionFactory
SessionFactory sessionFactory = configure.buildSessionFactory();

可以把SessionFactory内部的连接池替换为别的连接池(推荐c3p0连接池)
先导入c3p0连接池的jar包再进行配置(再hibernate.cfg.xml)

<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!--在连接池中可用的数据库连接的最少数目 -->
<property name="c3p0.min_size">5</property>
<!--在连接池中所有数据库连接的最大数目  -->
<property name="c3p0.max_size">20</property>
<!--设定数据库连接的过期时间,以秒为单位,
如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
<property name="c3p0.timeout">120</property>
 <!--3000秒检查所有连接池中的空闲连接 以秒为单位-->
<property name="c3p0.idle_test_period">3000</property>

③ 使用SessionFactory创建的Session对象进行基本从数据库操作

//通过SessionFactory获取Session对象
Session session = sessionFactory.openSession();

④ 使用完要释放资源
session在每次使用完就关闭,而SessionFactory可以等到所有的数据库连接断开再关闭

//释放资源
session.close();
sessionFactory.close();

如果释放资源的时候报错

javax.net.ssl.SSLException: closing inbound before receiving peer’s close_notify

则是因为连接的url没设置

useSSL=false

Hibernate常用API

① 由于一个项目只用创建一个SessionFactory,所以可以抽取一个工具类,通过工具类只创建一次SessionFactory 之后的Session对象通过工具类直接获取

package utils;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
    public static final SessionFactory sessionFactory;
    static{
        Configuration configure = new Configuration().configure();
        sessionFactory = configure.buildSessionFactory();
    }

    public static Session openSession(){
        Session session = sessionFactory.openSession();
        return session;
    }

    public static Session getCurrentSession(){
        Session session = sessionFactory.getCurrentSession();
        return session;
    }
}

之后获取session只需要

Session currentSession = HibernateUtil.getCurrentSession();

② 保存方法

save(Object obj)

③ 查询方法

  1. get(T.class,id)
    1.1 查询之后,返回的是真实对象本身
    1.2 没有查询到指定的id,返回的是一个空值
//查询get
        Session session = HibernateUtil.getCurrentSession();
        Customer customer = session.get(Customer.class,1);
        System.out.println(customer);
  1. load(T.class,id)(不推荐)
    2.1 查询之后返回的是一个代理对象,使用的是第三方的代理机制,javassist.jar
    2.2 没有查询到结果直接报一个异常

④ 修改

  1. void update(Object obj)(不推荐)
    1.1 直接创建对象修改
    1.2 如果没有指定其它的字段,会把其它的字段设置为null

  2. 查询之后再修改
    2.1 修改了某一个字段,不会把其它的字段设置为null

//查询后再修改
//使用c3p0要自己提交事务
Session session = HibernateUtil.openSession();
Transaction transaction = session.beginTransaction();
Customer customer = session.get(Customer.class,1);
customer.setCust_phone("111");
session.update(customer);
transaction.commit();

⑤ 删除

  1. void delete(Object obj)
    1.1 直接创建对象删除
    1.2 不支持级联删除

  2. 查询之后再删除
    2.1 支持级联删除

//查询后再删除
//使用c3p0要自己提交事务
Session session = HibernateUtil.openSession();
Transaction transaction = session.beginTransaction();
Customer customer = session.get(Customer.class,1);
session.delete(customer);
transaction.commit();

HQL和QBC

OID查询
根据对象的OID主键进行检索

//查询get
Session session = HibernateUtil.getCurrentSession();
Customer customer = session.get(Customer.class,1);
System.out.println(customer);

对象导航查询
Hibernate根据一个已经查询到的对象,获得其关联的对象的一种查询方式

什么是HQL
HQL查询:Hibernate Query Language,是一种面向对象的方式的查询语言,语法类似SQL;通过session.createQuery(),用于接收一个HQL进行查询方式。

  1. 简单查询
Session session = HibernateUtil.openSession();
Transaction transaction = session.beginTransaction();
//HQL不支持*的写法
//createQuery("from" 类)
Query from_customer = session.createQuery("from Customer");
List<Customer> list = from_customer.list();
for (Customer customer : list) {
    System.out.println(customer);
}
transaction.commit();
session.close();
  1. 条件查询
Session session = HibernateUtil.openSession();
Transaction transaction = session.beginTransaction();
//HQL不支持*的写法
//createQuery("from" 类)
Query from_customer = session.createQuery("from Customer where cust_name=:cust_name and cust_phone=:cust_phone ");
from_customer.setParameter("cust_name","aaa");
from_customer.setParameter("cust_phone","123");
List<Customer> list = from_customer.list();
for (Customer customer : list) {
	System.out.println(customer);
}
transaction.commit();
session.close();

多表操作

一对多
一个部门有多个员工,一个员工只属于一个部门

一对多的建表原则
在多的一方创建一个外键,指向1的一方的主键
在这里插入图片描述
例子:一个联系人只能属于某一个客户,一个客户可以有多个联系人,想在查询一方的时候把另一份也查出来

  1. 建表
    在这里插入图片描述
  2. 创建实体类
    ① Customer类 在客户类(一)里面要使用set集合存放对应的Linkman(多)
package domain;

import java.util.HashSet;
import java.util.Set;

public class Customer {
    private Long cust_id;
    private String cust_name;
    private String cust_source;
    private String cust_industry;
    private String cust_level;
    private String cust_phone;
    private String cust_mobile;
    //存放多的一方
 	private Set<Linkman> linkmen = new HashSet<>();

    public Long getCust_id() {
        return cust_id;
    }

    public void setCust_id(Long cust_id) {
        this.cust_id = cust_id;
    }

    public String getCust_name() {
        return cust_name;
    }

    public void setCust_name(String cust_name) {
        this.cust_name = cust_name;
    }

    public String getCust_source() {
        return cust_source;
    }

    public void setCust_source(String cust_source) {
        this.cust_source = cust_source;
    }

    public String getCust_industry() {
        return cust_industry;
    }

    public void setCust_industry(String cust_industry) {
        this.cust_industry = cust_industry;
    }

    public String getCust_level() {
        return cust_level;
    }

    public void setCust_level(String cust_level) {
        this.cust_level = cust_level;
    }

    public String getCust_phone() {
        return cust_phone;
    }

    public void setCust_phone(String cust_phone) {
        this.cust_phone = cust_phone;
    }

    public String getCust_mobile() {
        return cust_mobile;
    }

    public void setCust_mobile(String cust_mobile) {
        this.cust_mobile = cust_mobile;
    }

    public Set<Linkman> getLinkmen() {
        return linkmen;
    }

    public void setLinkmen(Set<Linkman> linkmen) {
        this.linkmen = linkmen;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "cust_id=" + cust_id +
                ", cust_name='" + cust_name + '\'' +
                ", cust_source='" + cust_source + '\'' +
                ", cust_industry='" + cust_industry + '\'' +
                ", cust_level='" + cust_level + '\'' +
                ", cust_phone='" + cust_phone + '\'' +
                ", cust_mobile='" + cust_mobile + '\'' +
                '}';
    }
}

② Linkman类,创建一个属性对应1的一方(客户)

package domain;

public class Linkman {
    private Long   link_id;
    private String link_name;
    private String link_gender;
    private String link_phone;
    private String link_mobile;
    private String link_email;
    private String link_qq;
    private String link_position;
    private String link_memo;
    private String link_cust_id;
    //存放1的一方
 	private Customer customer;

    public Long getLink_id() {
        return link_id;
    }

    public void setLink_id(Long link_id) {
        this.link_id = link_id;
    }

    public String getLink_name() {
        return link_name;
    }

    public void setLink_name(String link_name) {
        this.link_name = link_name;
    }

    public String getLink_gender() {
        return link_gender;
    }

    public void setLink_gender(String link_gender) {
        this.link_gender = link_gender;
    }

    public String getLink_phone() {
        return link_phone;
    }

    public void setLink_phone(String link_phone) {
        this.link_phone = link_phone;
    }

    public String getLink_mobile() {
        return link_mobile;
    }

    public void setLink_mobile(String link_mobile) {
        this.link_mobile = link_mobile;
    }

    public String getLink_email() {
        return link_email;
    }

    public void setLink_email(String link_email) {
        this.link_email = link_email;
    }

    public String getLink_qq() {
        return link_qq;
    }

    public void setLink_qq(String link_qq) {
        this.link_qq = link_qq;
    }

    public String getLink_position() {
        return link_position;
    }

    public void setLink_position(String link_position) {
        this.link_position = link_position;
    }

    public String getLink_memo() {
        return link_memo;
    }

    public void setLink_memo(String link_memo) {
        this.link_memo = link_memo;
    }

    public String getLink_cust_id() {
        return link_cust_id;
    }

    public void setLink_cust_id(String link_cust_id) {
        this.link_cust_id = link_cust_id;
    }

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    @Override
    public String toString() {
        return "Linkman{" +
                "link_id=" + link_id +
                ", link_name='" + link_name + '\'' +
                ", link_gender='" + link_gender + '\'' +
                ", link_phone='" + link_phone + '\'' +
                ", link_mobile='" + link_mobile + '\'' +
                ", link_email='" + link_email + '\'' +
                ", link_qq='" + link_qq + '\'' +
                ", link_position='" + link_position + '\'' +
                ", link_memo='" + link_memo + '\'' +
                ", link_cust_id='" + link_cust_id + '\'' +
                ", customer=" + customer +
                '}';
    }
}

③ 创建Customer的关系映射,使用set配置一对多关系

<?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>
    <class name="domain.Customer" table="customer">
        <id name="cust_id" column="cust_id">
            <generator class="native"></generator>

        </id>
        <property name="cust_name" column="cust_name" />
        <property name="cust_source" column="cust_source"/>
        <property name="cust_industry" column="cust_industry"/>
        <property name="cust_level" column="cust_level"/>
        <property name="cust_phone" column="cust_phone"/>
        <property name="cust_mobile" column="cust_mobile"/>
		<!-- name是类里面属性的名称 -->
        <set name="linkmen" cascade="save-update">
        <!-- key是数据库中外键的名称 -->
            <key column="link_cust_id"></key>
        <!-- class是对应的多的一方 -->
            <one-to-many class="domain.Linkman"></one-to-many>
        </set>
    </class>
</hibernate-mapping>

④ 创建Linkman的关系映射,设置一对多关系

<?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>
    <class name="domain.Linkman" table="linkman">
        <id name="link_id" column="link_id">
            <generator class="native"></generator>
        </id>
        <property name="link_name" column="link_name" />
        <property name="link_gender" column="link_gender"/>
        <property name="link_phone" column="link_phone"/>
        <property name="link_mobile" column="link_mobile"/>
        <property name="link_email" column="link_email"/>
        <property name="link_qq" column="link_qq"/>
        <property name="link_position" column="link_position"/>
        <property name="link_memo" column="link_memo"/>
        <many-to-one name="customer" class="domain.Customer" column="link_cust_id" lazy="false"></many-to-one>


    </class>
</hibernate-mapping>
  1. 测试能否通过一方把另外一方查出来
package HibernateTest;

import domain.Customer;
import domain.Linkman;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import utils.HibernateUtil;

public class HTest {
    @Test
    public void test(){
        //创建客户和联系人
        Session currentSession = HibernateUtil.getCurrentSession();
        Transaction transaction = currentSession.beginTransaction();
        //创建客户
        Customer customer1 = new Customer();
        customer1.setCust_name("customer1");
        Customer customer2 = new Customer();
        customer2.setCust_name("customer2");
        Customer customer3 = new Customer();
        customer3.setCust_name("customer3");
        //创建联系人
        Linkman linkman1 = new Linkman();
        linkman1.setLink_name("linkman1");
        Linkman linkman2 = new Linkman();
        linkman2.setLink_name("linkman2");
        Linkman linkman3 = new Linkman();
        linkman3.setLink_name("linkman3");
        //设置关系
        customer1.getLinkmen().add(linkman1);
        customer1.getLinkmen().add(linkman2);
        customer2.getLinkmen().add(linkman3);

        linkman1.setCustomer(customer1);
        linkman2.setCustomer(customer1);
        linkman3.setCustomer(customer2);
        //保存
        currentSession.save(customer1);
        currentSession.save(customer2);
        currentSession.save(customer3);
        currentSession.save(linkman1);
        currentSession.save(linkman2);
        currentSession.save(linkman3);

        transaction.commit();

    }

    @Test
    public void test2(){
        //查询
        Session currentSession = HibernateUtil.getCurrentSession();
        Transaction transaction = currentSession.beginTransaction();
        Linkman linkman = currentSession.get(Linkman.class,3L);
        transaction.commit();
        System.out.print(linkman);
    }
}

多对多
一个学生可以选择多门课程,一门课程可以被多个学生选择

多对多的建表原则
创建一个中间表,中间表至少有两个字段,分别作为外键执行多对多双方的主键
在这里插入图片描述

主键的生成策略

主键可以分为自然主键和代理主键
① 自然主键:主键本身就是表中的一个字段,实体中一个具体的属性,对象本身唯一的特性
② 代理主键:主键本身不是表中必须的一个字段

开发中一般都使用代理主键而不使用自然主键,因为一旦自然主键参与到业务逻辑当中,后期有可能修改源代码,比如,设计以学生身份证号为主键,在业务上添加学生身份证号,不小心录入错误,是不允许对主键进行修改的
ocp的思想:对程序的扩展时开放的,对源码的修改是关闭的

主键的生成策略
在使用代理主键的过程当中,尽量要做到自动生成主键,不能让用户手动设置主键,一般交给数据库自动增长,让程序生成唯一的标识,在hibernate当中,为了减少程序的编写,内部提供了多种的主键生成策略
在这里插入图片描述

Hibernate事务管理

设置事务的隔离级别
在这里插入图片描述
开发中一般使用第三个,因为第四个的效率较低,默认为第三个(配置要写在映射前面)

事务
在service层开启/提交事务(可以在service中把dao的小的逻辑单元罗列在一起)
在这里插入图片描述
注意:在service处理事务的时候,要保证和dao中的连接(session)是同一个,才可以使用事务
解决方法1:(向下传递)(比较麻烦)
在service里面new出来session并且传递到dao层
在这里插入图片描述

解决方法2:使用ThreadLocal(推荐使用)
在service中把创建的连接绑定到对应的ThreadLocal(每个请求就是一个线程),在dao层通过当前线程获取连接对象
在这里插入图片描述
在这里插入图片描述
以后使用可以不用openSession而使用getCurrentSession

public class HibernateUtil {
    public static final SessionFactory sessionFactory;
    static{
        Configuration configure = new Configuration().configure();
        sessionFactory = configure.buildSessionFactory();
    }

    public static Session openSession(){
        Session session = sessionFactory.openSession();
        return session;
    }

    public static Session getCurrentSession(){
        Session session = sessionFactory.getCurrentSession();
        return session;
    }
}

 @Test
    public void Test3(){
        Session currentSession = HibernateUtil.getCurrentSession();
        Transaction transaction = currentSession.beginTransaction();
        Query query = currentSession.createQuery("from Customer where cust_name=:aaa and cust_source=:bbb");
        query.setParameter("aaa","customer1");
        query.setParameter("bbb","1");
        List<Customer> list = query.list();
        for (Customer customer : list) {
            System.out.println(customer);
        }
        transaction.commit();
    }

一级缓存和二级缓存

缓存是一种优化方式,将数据存到缓存里面,使用的时候直接从缓存中获取,不用再直接到存储源里面取数据

二级缓存现在通常不使用hibernate自带的二级缓存而用redis

一级缓存
是session级别的缓存,就是在session内部维护的一个java集合,生命周期和session一样,是自带不可卸载的
当应用程序用Session接口的Save(),update(),saveOrUpdate()时,如果session缓存中没有相应的对象,就会自动的从数据库查询相应的信息,写到缓存当中
当调用Session接口的load,get()方法,以及Query接口的list iterator方法时, 会判断缓存中是否存在该对象,有则返回, 不会查询数据库,如果缓存中没有要查询的对象,再到数据库当中查询对应的对象,并添加到一级缓存中
当调用session.close方法时,缓存会被清空

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值