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&useSSL=false&useServerPrepStmts=true&cachePrepStmts=true&rewriteBatchedStatements=true&useCursorFetch=true&defaultFetchSize=100&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();
}