Hibernate框架学习(三)

1、持久化类的编写
概述

持久化:将内存中对象持久化到数据库中的过程。Hibernate就是用来持久化的框架。
持久化类:一个Java对象与数据库中的表建立的关系。
持久化类 = Java类 + 映射文件。

持久化类的编写的规则

(1)提供一个无参的构造方法,因为Hibernate底层要使用反射生成实例。
(2)对属性进行私有,对私有的属性提供get和set的方法,因为Hibernate中要获取或者设置属性的值。
(3)提供一个OID与数据库中的主键对应,因为java中通过对象的地址来区分是否是通过是否是同一个对象,在Hibernate中通过持久化的OID的属性来进行区分是否是同一个对象。
(4)持久化类属性的数据类型使用包装类型,因为基本的数据类型的默认的值会产生歧义。
(5)持久化类不要使用final修饰(延迟加载返回的是代理的对象,可以对一个没有实现接口的类产生代理,使用了非常底层的字节码增强的技术产生了代理,继承这个类实现的代理,如果使用了final就不能被继承,也就不能产生代理对象,就不能使用延迟加载,hibernate的优化也就不能使用。)

2、主键生成策略

主键的分类:
自然主键:主键本身是表中的字段(是实体类中的一个属性)
代理主键:主键本身不是表中的字段(不是实体类的一个属性)
在实际的开发中尽量使用代理主键,因为一旦自然主键参与到了业务的逻辑中,就有可能会修改项目的源代码(主键是不允许修改的)。

Hibernate的主键的生成策略(不允许自己设置主键,自动生长),Hibernate中提供了多种主键生成的策略。

(1)increment:Hibernate种提供的自动生长的主键(long short int),在单个线程中可以使用,在多线程种就不要使用,使用Hibernate中的主键自动生长的机制。工作的原理:先把主键的id最大的值查出来,再将id + 1之后就是新插入的主键的值,所以多线程的时候就会出现问题。
(2)identity:自动增长(short int long),使用的是数据库底层的自动增长,可以适用于有自动增长机制的数据库
(3)sequence:自动增长(short int long),采用的是序列的方式,Oracle等。MySQL时不能使用的。
(4)uuid:适用于字符串类型的主键,Hibernate中随机的方式来产生主键。
(5)native:本地策略,可以在identity和sequence中之间进行切换。
(6)assigned:Hibernate放弃主键的管理,用户自己设置。
(7)foreigner:外部的,会在一对一的关联映射的情况下使用,主键对应外部的表的主键。

主键生成策略演示

(1)increment在多线程的条件下的问题:
编写一个测试类:两个测试的方法代表的是两个线程。

import com.hibernate.pojo.Person;
import com.hibernate.util.MyHibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

public class TestHibernate {
    /**
     * 保存记录
     */
    @Test
    public void test1(){
        Session session = MyHibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        Person person = new Person();
        person.setName("hello");
        person.setMoney(9999d);
        session.save(person);
        tx.commit();
        session.close();
    }
    @Test
    public void test2(){
        Session session = MyHibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        Person person = new Person();
        person.setName("hello2");
        person.setMoney(9999d);
        session.save(person);
        tx.commit();
        session.close();
    }
}

线程一打印的日志(线程一是进行提交完成,可以看出increment的主键生成的策略是先将最大的主键的值查出来,最后进行加1的操作,这是Hibernate自己提供的主键生成的测类):
在这里插入图片描述
线程二打印的日志:
在这里插入图片描述
通过上面的演示可以看出,在多线程的时候,通过这种查询最大的主键之后进行自增的操作是不合理的,只有在单个线程的时候才可以使用。
(2)identity生成主键的策略(利用的是数据库底层的主键自增):
在这里插入图片描述
通过日志可以看出使用的是数据库自己本身的主键增长
(3)uuid自增
日志打印:
在这里插入图片描述
查看数据库结果:
在这里插入图片描述
(4)assigned(自己负责生成主键)
如果自己没有给主键的值设置的话,就会报错。

3、持久化类三种状态
(1)瞬时态

没有唯一的标识,没有被Session对象所管理。

(2)持久态

有唯一的标识,被Session对象管理

(3)脱管态

有唯一的标识,但没有别Session对象所管理。

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import java.io.Serializable;

/**
 * 持久化类的三种状态
 */
public class TestHibernate2 {
    @Test
    public void test1(){
        Session session = MyHibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        //瞬时态对象(没有唯一的标识,而且没有被Session管理)
        Person person = new Person();
        person.setName("hello java");
        Serializable id = session.save(person);
        //有唯一的id而且被Session管理
        Person p = session.get(Person.class,id);
        tx.commit();
        session.close();
        //这里是一个脱管态的对象(有唯一的表识,Session被销毁,所以处于脱管态)
        System.out.println(person.getName());
    }
}

重点研究的是持久化类的持久化对象(特点:自动的更新数据库)。

(4)持久化类状态之间的转换
瞬时态

瞬时态对象的创建:Person person = new Person();
瞬时态转换为持久态:save(Object obj) 或者是 saveOrUpdate(Object obj);
瞬时态转换为脱管态:给瞬时态设置一个唯一的id

持久态

持久态的创建:get() ,load(),find(),iterater()(参数省略)
持久态转换为瞬时态:delete()
持久态变为脱管: close(),clear(),evict(Object obj)

脱管态

托管态的创建:new ,再进行设置id
脱管态转换为持久:update(Object obj),saveOrUpdate(Object obj)
脱管变为瞬时:id设置为null

4、持久态对象的特性
可以对持久态对象通过修改来对数据库中的表进行修改
  @Test
    public void test2(){
        Session session = MyHibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        Person person = session.get(Person.class,"1uinuiniuni");
        person.setName("jack");
        tx.commit();
        session.close();
    }

日志打印信息:
在这里插入图片描述
如果是持久态的对象发生了变化的话,那么Hibernate就会执行update的操作。但是如果是持久态的对象没有发生变化的话,那么就不会执行修改的操作,只会执行一个查询的操作。
底层实现的原理: Hibernate的一级缓存。

5、Hibernate中的缓存的机制
(1)优点:将我们经常使用的数据存入缓存(内存),直接从内存中获取。
(2)一级缓存

Session级别的缓存,一级缓存的声明周期是与Session的生命周期是一致的,一级缓存是自带的。先去在一级缓存查找OID,如果没有查到的话,就才会从数据库中进行查找,减少了数据库的访问,利用java集合来实现缓存的机制。

证明:
@Test
    public void test(){
        Session session = MyHibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        //只会发送一次Sql语句
        Person person = session.get(Person.class,"1uinuiniuni");
        //第二次只会在缓存中进行查询
        Person person1 = session.get(Person.class,"1uinuiniuni");
        System.out.println(person == person1);
        tx.commit();
        session.close();
    }

在这里插入图片描述

Hibernate中一级缓存的结构

(1)快照区:当事务提交的时候,会将对象与快照区的对象进行比较,如果发生了变化的话,就会进行修改。
主要有以下的两个部分组成
在这里插入图片描述
在这里插入图片描述
执行完查询之后,会将数据存入。
在这里插入图片描述
清除缓存:
clear() 清除所有的缓存
evict(Object obj) 清除一个缓存

(3)二级缓存

SessionFactory级别的缓存,需要手动的配置,但是企业中使用redis替代。

6、hibernate中设置事务的隔离的级别

在核心的配置

<!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/hibernate?serverTimezone=GMT%2B8&amp;useSSL=false&amp;useServerPrepStmts=true&amp;cachePrepStmts=true&amp;rewriteBatchedStatements=true&amp;useCursorFetch=true&amp;defaultFetchSize=100&amp;allowPublicKeyRetrieval=true</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">123</property>
        <!--方言的配置-->
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        <!--update:如果原来有表的话,会在原有的基础上进行修改,如果没有表格的话,会创建表格-->
        <!--none 不适用自动的建表-->
        <!--create:总会重新建表-->
        <!--create-drop  总是建表然后删除表格  测试-->
        <!--validate  只会使用数据库中原有的表,但是会校验映射文件的正确性-->
        <property name="hibernate.hbm2ddl.auto">update</property>
        <!--
        Hibernate中的事务的隔离的级别
        0:TRANSACTION_NONE
			1:TRANSACTION_READ_UNCOMMITTED
			2:TRANSACTION_READ_COMMITTED
			4:TRANSACTION_REPEATABLE_READ
			8:TRANSACTION_SERIALIZABLE
-->
        <property name="hibernate.connection.isolation">4</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <!--配置文件的位置-->
        <mapping resource="/mapper/user.hbm.xml"/>
    </session-factory>
</hibernate-configuration>
7、线程绑定的Session

业务层:当使使用线程绑定的时候,就不用自己关闭的Session了。
在核心的配置的文件中:

<property name="hibernate.current_session_context_class">thread</property>

在工具类中使用的Session为另一个Session


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

public class HibernateUtil {
    private static Configuration cfg;
    private static SessionFactory factory;
    static {
        cfg = new Configuration().configure();
        factory = cfg.buildSessionFactory();
    }
    public static Session openSession(){
        return factory.openSession();
    }

    /**
     * 默认的方法是不能用的
     * @return
     */
    public static Session getCurrentSession(){
        return factory.getCurrentSession();
    }
}

8、其他的Hibernate的API
(1)query

用于接收的是HQL :

 @Test
    public void test1(){
        Session session = HibernateUtil.getCurrentSession();
        Transaction tx = session.beginTransaction();
        String hql = " from Person" ;
        Query query = session.createQuery(hql);
        List<Person> list = query.list();
        for (Person person : list) {
            System.out.println(person.toString());
        }
        tx.commit();
    }

    /**
     * 条件的查询
     */
    @Test
    public void test2(){
        Session session = HibernateUtil.getCurrentSession();
        Transaction tx = session.beginTransaction();
        String hql = " from Person where id = ?0";
        Query query = session.createQuery(hql);
        Query parameter = query.setParameter(0, 1);
        Person o = (Person)query.uniqueResult();
        System.out.println(o.toString());
    }

    /**
     * 分页查询
     */
    @Test
    public void test3(){
        Session session = HibernateUtil.getCurrentSession();
        Transaction tx = session.beginTransaction();
        String hql = "from com.hibernate.domain.Person";
        Query query = session.createQuery(hql);
        query.setFirstResult(0);
        query.setMaxResults(3);
        List list = query.list();
        tx.commit();
    }
(2)criteria
 /**
     * 利用Criteria的对象进行查询
     */
    @Test
    public void test4(){
        Session session = HibernateUtil.getCurrentSession();
        Transaction tx = session.beginTransaction();
        Criteria criteria = session.createCriteria(Person.class);
//        List<Person> list = criteria.list();
//        for (Person person : list) {
//            System.out.println(person);
//        }
        //利用Criteria作为条件的查询
        Criteria id = criteria.add(Restrictions.between("id", 1, 3));
        List<Person> list = id.list();
        for (Person person : list) {
            System.out.println(person);
        }
        tx.commit();

    }
(3)SQLQuery(平时的开发中一般不适用)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值