Hibernate入门

发布时间 2023-06-25 09:17:05作者: 纤

Hibernate

加入maven依赖

        <!-- hibernate -->
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.12.0.GA</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>3.6.7.Final</version>
        </dependency>
使用javassist是因为

安装IDEA插件

JPA Buddy

image-20230624205726091

配置数据库

创建文件 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/xsd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql:///db1?serverTimezone=GMT%2B8&useUnico</property>
        <property name="connection.username">root</property>
        <property name="connection.password">password</property>
    </session-factory>
</hibernate-configuration>

配置方言等其他配置

        <!-- 数据库⽅⾔ -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <!-- 打印SQL -->
        <property name="show_sql">true</property>
        <!-- 格式化SQL -->
        <property name="format_sql">true</property>
        <!-- 是否⾃动⽣成数据库 -->
        <property name="hibernate.hbm2ddl.auto">update</property>

配置一个实体类的CRUD

创建实体类A

@Data
public class A implements Serializable {
    private Integer id;
    private String value;

//    private static final long serialVersionUID = 1L;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

同级包下,配置mapping

SensorData.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>
    <class name="com.jmu.design.domain.SensorData" table="sensor_data">
        <id name="id">
            <generator class="native"/>
        </id>
    <property name="type" column="type"/>
    <property name="updateTime" column="update_time"/>
    <property name="value" column="value"/>
    </class>
</hibernate-mapping>

在hibernate配置文件中添加映射关系

<!-- 关联对象配置文件 -->
        <mapping resource="com/jmu/design/domain/SensorData.hbm.xml"/>

image-20230623154029549

编写测试代码

    @Test
    public void init(){
        //初始化注册服务对象
        Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
        //获取Session工厂
        SessionFactory sessionFactory = configuration.buildSessionFactory();

        //获取Session
        Session session = sessionFactory.openSession();

        //开启事务
        Transaction transaction = session.beginTransaction();
        SensorData sensorData = new SensorData();
        sensorData.setType("type1");
        sensorData.setValue(123);
        session.save(sensorData);
        session.close();
    }

然后出现未找到配置文件,maven中配置将配置文件自动构建打包

image-20230623155921785

 	<build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

然后还需要添加一个依赖,否则3.x版本会出现异常

        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.12.0.GA</version>
        </dependency>

CRUD测试

@Test
public void init(){
    //初始化注册服务对象
    Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
    //获取Session工厂
    SessionFactory sessionFactory = configuration.buildSessionFactory();

    //获取Session
    Session session = sessionFactory.openSession();

    //开启事务
    Transaction transaction = session.beginTransaction();
    //增
    A a = new A();
    a.setValue("123");
    session.save(a);
    
    //删
    session.delete(a);
    
    //查
    List from_a = session.createQuery("from A").list();
    for (Object o : from_a) {
        System.out.println(o);
    }

    //改
    A a1 = (A) session.get(A.class, 2);
    System.out.println(a1.toString());
    a1.setValue(String.valueOf(111111));
    session.update(a1);
    transaction.commit();
    session.close();
}

Get和load的区别

get请求是立即加载的过程,先查询缓存,缓存没有查询数据库

load是懒加载机制的,只有它被使用的时候,才会去查询数据库

清理缓存

session.clear();

数据库的更新方式

一般使用update

image-20230624210503998

封装Hibernate的工具类

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.springframework.data.redis.core.index.PathBasedRedisIndexDefinition;

public class HibernateUtil {
    private static Configuration configuration;
    private static SessionFactory sessionFactory;
    private static ThreadLocal<Session> session = new ThreadLocal<>();

    static {
        configuration = new Configuration().configure("hibernate.cfg.xml");
        sessionFactory = configuration.buildSessionFactory();
    }

    public static Session getSession(){
        Session session = HibernateUtil.session.get();
        if (session==null || !session.isOpen()) {
            if(sessionFactory == null){
                buildSessionFactory();
            }
            //获取一个session
            session = sessionFactory==null?null:sessionFactory.openSession();
            //放入线程
            HibernateUtil.session.set(session);
        }
        return session;
    }


    private static void buildSessionFactory() {
        sessionFactory = configuration.buildSessionFactory();
    }

    public static void closeSession() {
        Session session = HibernateUtil.session.get();
        HibernateUtil.session.set(null);
        if(session!=null && session.isOpen()){
            session.close();
        }
    }
}

Hibernate对象的生命周期

对象状态及生命周期

Hibernate中对象有三种状态:瞬时状态(Transient)、持久状态(Persistent)、游离状态(Detached)。

瞬时状态:刚刚使用new语句创建,还没有被持久化,不处于Session的缓存中。处于临时状态的Java对象被称为临时对象。Session中没有,数据库中没有。
持久化状态:已经被持久化,加入到Session的缓存中。处于持久化状态的Java对象被称为持久化对象。Session中有,数据库中有。
游离状态:已经被持久化,但不处于Session的缓存中。处于游离状态的Java对象被称为游离对象。Session中没有,数据库中有。(session中的缓存,一级缓存)

image-20230624211235138

单向多对一

HIbernate映射

image-20230624215629802

自动生成表

image-20230624220153329

也可以将xml配置文件中的update的构建表的方式注解打开

        <!-- 是否⾃动⽣成数据库 -->
        <property name="hibernate.hbm2ddl.auto">update</property>

配置实体类对象

设有一个student表,以及一个grade表

image-20230624220430480

Student.java

import lombok.Data;

import java.io.Serializable;

@Data
public class Student implements Serializable {
    private Integer id;
    private String name;
    private Integer age;
    private Grade grade;
}

Student.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 package="com.jmu.design.domain">
    <class name="com.jmu.design.domain.Student" table="student">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name= "name"/>
        <property name="age"/>

        <!--
            多对一
            name字段名称
            class 对应的类型

        -->
        <many-to-one name="grade" class="Grade" not-null="true" column="grade_id"/>
    </class>
</hibernate-mapping>

Grade.java

import lombok.Data;

import java.io.Serializable;

@Data
public class Grade implements Serializable {
    private Integer id;
    private String name;
}

Grade.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 package="com.jmu.design.domain">
    <class name="com.jmu.design.domain.Grade" table="grade">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name= "name"/>
    </class>
</hibernate-mapping>

要记得mapping的映射也得加上

        <mapping resource="com/jmu/design/domain/Student.hbm.xml"/>
        <mapping resource="com/jmu/design/domain/Grade.hbm.xml"/>

测试文件

public void manyToOneTest(){
    Session session = null;
    Transaction tx = null;

    try{
        session = HibernateUtil.getSession();
        tx = session.beginTransaction();

        //创建实例对象
        Grade grade1 = new Grade();
        grade1.setName("基础");
        Grade grade2 = new Grade();
        grade2.setName("高级");

        session.save(grade1);
        session.save(grade2);

        Student student1 = new Student();
        student1.setName("张三");
        student1.setAge(10);
        student1.setGrade(grade1);

        Student student2 = new Student();
        student2.setName("李四");
        student2.setAge(20);
        student2.setGrade(grade2);

        Student student3 = new Student();
        student3.setName("王五");
        student3.setAge(30);
        student3.setGrade(grade1);

        session.save(student1);
        session.save(student2);
        session.save(student3);


        tx.commit();
    }catch (Exception e){
        e.printStackTrace();
        tx.rollback();
    }finally {
        HibernateUtil.closeSession();
    }
}

单向一对多

配置实体类

Student.java

import lombok.Data;

import java.io.Serializable;

@Data
public class Student implements Serializable {
    private Integer id;
    private String name;
    private Integer age;
//    private Grade grade;
}

Grade.java

import lombok.Data;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

@Data
public class Grade implements Serializable {
    private Integer id;
    private String name;
    private Set<Student> students = new HashSet<>();

}

Student.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 package="com.jmu.design.domain">
    <class name="com.jmu.design.domain.Grade" table="grade">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name= "name"/>

        <set name="students">
            <key column="grade_id" not-null="true"/>
            <one-to-many class="Student"/>
        </set>
    </class>
</hibernate-mapping>

Student.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 package="com.jmu.design.domain">
    <class name="com.jmu.design.domain.Student" table="student">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name= "name"/>
        <property name="age"/>

        <!--
            多对一
            name字段名称
            class 对应的类型

        -->
<!--        <many-to-one name="grade" class="Grade" not-null="true" column="grade_id"/>-->
    </class>
</hibernate-mapping>

测试代码

插入的话和上面的一样

将student加入到grade的students集合中,就会自动映射到关系

查询:

@Test
public void oneToMany(){
    Session session = null;
    Transaction tx = null;

    try{
        session = HibernateUtil.getSession();
        tx = session.beginTransaction();



        Grade grade = (Grade) session.get(Grade.class, 3);
        System.out.println(grade.toString());

        tx.commit();
    }catch (Exception e){
        e.printStackTrace();
        tx.rollback();
    }finally {
        HibernateUtil.closeSession();
    }
}

一对多在存储时,会先插入grade,然后插入student,之后发现这个student在grade的集合中,他们两个有关系,才会用update语句来更改student表中的grade_id,因此效率上来说是不如多对一的

双向多对一

就是把之前注释掉的配置文件和对应的实体类的字段打开

然后存的时候,用单向一对多或者单向多对一的方式都行

双向关系的时候记得把not-null删了

建议使用多对一,不用使用update语句,效率高

级联操作

在双向一对多的学习中,我们发现每次保存对象时,学生对象和年纪对象都需要我们持久化至session,既然它们两者有关联关系,可不可以只持久化其中一端,另一端就会自动的被持久化呢,这个属性就是Hibernate的cascade。 cascade是多对一、一对多、一对一、多对多各种关系之间的一种级联操作行为。

image-20230625000004881

一般使用save-update,其他选项一般较少使用,级联插入

在存储的时候只需要存一方就行

image-20230625000515785

维护关系

加上 inverse="true"由对方来维护关系,此时一端不再维护,当使用一对多来插入时,不会再更改grade_id字段

image-20230625000852646

强制让多方来维护关系,因为多对一的插入较为高效